diff options
author | David Woodhouse <dwmw2@infradead.org> | 2006-10-01 17:55:53 +0100 |
---|---|---|
committer | David Woodhouse <dwmw2@infradead.org> | 2006-10-01 17:55:53 +0100 |
commit | 8a84fc15ae5cafcc366dd85cf8e1ab2040679abc (patch) | |
tree | 5d8dce194c9667fa92e9ec9f545cec867a9a1e0d /drivers/char | |
parent | 28b79ff9661b22e4c41c0d00d4ab8503e810f13d (diff) | |
parent | 82965addad66fce61a92c5f03104ea90b0b87124 (diff) |
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6
Manually resolve conflict in include/mtd/Kbuild
Signed-off-by: David Woodhouse <dwmw2@infradead.org>
Diffstat (limited to 'drivers/char')
101 files changed, 4601 insertions, 3003 deletions
diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index c40e487d9f5..bde1c665d9f 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig @@ -439,6 +439,14 @@ config SGI_MBCS If you have an SGI Altix with an attached SABrick say Y or M here, otherwise say N. +config MSPEC + tristate "Memory special operations driver" + depends on IA64 + help + If you have an ia64 and you want to enable memory special + operations support (formerly known as fetchop), say Y here, + otherwise say N. + source "drivers/serial/Kconfig" config UNIX98_PTYS @@ -495,6 +503,21 @@ config LEGACY_PTY_COUNT When not in use, each legacy PTY occupies 12 bytes on 32-bit architectures and 24 bytes on 64-bit architectures. +config BRIQ_PANEL + tristate 'Total Impact briQ front panel driver' + depends on PPC_CHRP + ---help--- + The briQ is a small footprint CHRP computer with a frontpanel VFD, a + tristate led and two switches. It is the size of a CDROM drive. + + If you have such one and want anything showing on the VFD then you + must answer Y here. + + To compile this driver as a module, choose M here: the + module will be called briq_panel. + + It's safe to say N here. + config PRINTER tristate "Parallel printer support" depends on PARPORT @@ -596,6 +619,13 @@ config HVC_CONSOLE console. This driver allows each pSeries partition to have a console which is accessed via the HMC. +config HVC_ISERIES + bool "iSeries Hypervisor Virtual Console support" + depends on PPC_ISERIES && !VIOCONS + select HVC_DRIVER + help + iSeries machines support a hypervisor virtual console. + config HVC_RTAS bool "IBM RTAS Console support" depends on PPC_RTAS @@ -717,7 +747,7 @@ config NVRAM config RTC tristate "Enhanced Real Time Clock Support" - depends on !PPC && !PARISC && !IA64 && !M68K && (!SPARC || PCI) && !FRV && !ARM + depends on !PPC && !PARISC && !IA64 && !M68K && (!SPARC || PCI) && !FRV && !ARM && !SUPERH ---help--- If you say Y here and create a character special file /dev/rtc with major number 10 and minor number 135 using mknod ("man mknod"), you @@ -801,14 +831,6 @@ config DS1302 will get access to the real time clock (or hardware clock) built into your computer. -config S3C2410_RTC - bool "S3C2410 RTC Driver" - depends on ARCH_S3C2410 - help - RTC (Realtime Clock) driver for the clock inbuilt into the - Samsung S3C2410. This can provide periodic interrupt rates - from 1Hz to 64Hz for user programs, and wakeup from Alarm. - config COBALT_LCD bool "Support for Cobalt LCD" depends on MIPS_COBALT @@ -984,6 +1006,7 @@ config GPIO_VR41XX config RAW_DRIVER tristate "RAW driver (/dev/raw/rawN) (OBSOLETE)" + depends on BLOCK help The raw driver permits block devices to be bound to /dev/raw/rawN. Once bound, I/O against /dev/raw/rawN uses efficient zero-copy I/O. diff --git a/drivers/char/Makefile b/drivers/char/Makefile index 6e0f4469d8b..19114df59bb 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -42,15 +42,18 @@ obj-$(CONFIG_AMIGA_BUILTIN_SERIAL) += amiserial.o obj-$(CONFIG_SX) += sx.o generic_serial.o obj-$(CONFIG_RIO) += rio/ generic_serial.o obj-$(CONFIG_HVC_CONSOLE) += hvc_vio.o hvsi.o +obj-$(CONFIG_HVC_ISERIES) += hvc_iseries.o obj-$(CONFIG_HVC_RTAS) += hvc_rtas.o obj-$(CONFIG_HVC_DRIVER) += hvc_console.o obj-$(CONFIG_RAW_DRIVER) += raw.o obj-$(CONFIG_SGI_SNSC) += snsc.o snsc_event.o +obj-$(CONFIG_MSPEC) += mspec.o obj-$(CONFIG_MMTIMER) += mmtimer.o obj-$(CONFIG_VIOCONS) += viocons.o obj-$(CONFIG_VIOTAPE) += viotape.o obj-$(CONFIG_HVCS) += hvcs.o obj-$(CONFIG_SGI_MBCS) += mbcs.o +obj-$(CONFIG_BRIQ_PANEL) += briq_panel.o obj-$(CONFIG_PRINTER) += lp.o obj-$(CONFIG_TIPAR) += tipar.o @@ -66,7 +69,6 @@ obj-$(CONFIG_EFI_RTC) += efirtc.o obj-$(CONFIG_SGI_DS1286) += ds1286.o obj-$(CONFIG_SGI_IP27_RTC) += ip27-rtc.o obj-$(CONFIG_DS1302) += ds1302.o -obj-$(CONFIG_S3C2410_RTC) += s3c2410-rtc.o ifeq ($(CONFIG_GENERIC_NVRAM),y) obj-$(CONFIG_NVRAM) += generic_nvram.o else diff --git a/drivers/char/agp/agp.h b/drivers/char/agp/agp.h index 3c623b67ea1..8b3317fd46c 100644 --- a/drivers/char/agp/agp.h +++ b/drivers/char/agp/agp.h @@ -117,7 +117,7 @@ struct agp_bridge_driver { }; struct agp_bridge_data { - struct agp_version *version; + const struct agp_version *version; struct agp_bridge_driver *driver; struct vm_operations_struct *vm_ops; void *previous_size; diff --git a/drivers/char/agp/amd64-agp.c b/drivers/char/agp/amd64-agp.c index 8cd52984cda..00b17ae3973 100644 --- a/drivers/char/agp/amd64-agp.c +++ b/drivers/char/agp/amd64-agp.c @@ -409,7 +409,7 @@ static int __devinit uli_agp_init(struct pci_dev *pdev) int i; unsigned size = amd64_fetch_size(); printk(KERN_INFO "Setting up ULi AGP.\n"); - dev1 = pci_find_slot ((unsigned int)pdev->bus->number,PCI_DEVFN(0,0)); + dev1 = pci_get_slot (pdev->bus,PCI_DEVFN(0,0)); if (dev1 == NULL) { printk(KERN_INFO PFX "Detected a ULi chipset, " "but could not fine the secondary device.\n"); @@ -442,6 +442,8 @@ static int __devinit uli_agp_init(struct pci_dev *pdev) enuscr= httfea+ (size * 1024 * 1024) - 1; pci_write_config_dword(dev1, ULI_X86_64_HTT_FEA_REG, httfea); pci_write_config_dword(dev1, ULI_X86_64_ENU_SCR_REG, enuscr); + + pci_dev_put(dev1); return 0; } @@ -466,7 +468,7 @@ static int __devinit nforce3_agp_init(struct pci_dev *pdev) printk(KERN_INFO PFX "Setting up Nforce3 AGP.\n"); - dev1 = pci_find_slot((unsigned int)pdev->bus->number, PCI_DEVFN(11, 0)); + dev1 = pci_get_slot(pdev->bus, PCI_DEVFN(11, 0)); if (dev1 == NULL) { printk(KERN_INFO PFX "agpgart: Detected an NVIDIA " "nForce3 chipset, but could not find " @@ -510,6 +512,8 @@ static int __devinit nforce3_agp_init(struct pci_dev *pdev) pci_write_config_dword(dev1, NVIDIA_X86_64_1_APBASE2, apbase); pci_write_config_dword(dev1, NVIDIA_X86_64_1_APLIMIT2, aplimit); + pci_dev_put(dev1); + return 0; } diff --git a/drivers/char/agp/backend.c b/drivers/char/agp/backend.c index 509adc40325..d59e037ddd1 100644 --- a/drivers/char/agp/backend.c +++ b/drivers/char/agp/backend.c @@ -44,7 +44,7 @@ * past 0.99 at all due to some boolean logic error. */ #define AGPGART_VERSION_MAJOR 0 #define AGPGART_VERSION_MINOR 101 -static struct agp_version agp_current_version = +static const struct agp_version agp_current_version = { .major = AGPGART_VERSION_MAJOR, .minor = AGPGART_VERSION_MINOR, diff --git a/drivers/char/agp/efficeon-agp.c b/drivers/char/agp/efficeon-agp.c index b788b0a3bbf..30f730ff81c 100644 --- a/drivers/char/agp/efficeon-agp.c +++ b/drivers/char/agp/efficeon-agp.c @@ -337,13 +337,6 @@ static struct agp_bridge_driver efficeon_driver = { .agp_destroy_page = agp_generic_destroy_page, }; - -static int agp_efficeon_resume(struct pci_dev *pdev) -{ - printk(KERN_DEBUG PFX "agp_efficeon_resume()\n"); - return efficeon_configure(); -} - static int __devinit agp_efficeon_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { @@ -414,11 +407,18 @@ static void __devexit agp_efficeon_remove(struct pci_dev *pdev) agp_put_bridge(bridge); } +#ifdef CONFIG_PM static int agp_efficeon_suspend(struct pci_dev *dev, pm_message_t state) { return 0; } +static int agp_efficeon_resume(struct pci_dev *pdev) +{ + printk(KERN_DEBUG PFX "agp_efficeon_resume()\n"); + return efficeon_configure(); +} +#endif static struct pci_device_id agp_efficeon_pci_table[] = { { @@ -439,8 +439,10 @@ static struct pci_driver agp_efficeon_pci_driver = { .id_table = agp_efficeon_pci_table, .probe = agp_efficeon_probe, .remove = agp_efficeon_remove, +#ifdef CONFIG_PM .suspend = agp_efficeon_suspend, .resume = agp_efficeon_resume, +#endif }; static int __init agp_efficeon_init(void) diff --git a/drivers/char/agp/frontend.c b/drivers/char/agp/frontend.c index d9c5a9142ad..0f2ed2aa2d8 100644 --- a/drivers/char/agp/frontend.c +++ b/drivers/char/agp/frontend.c @@ -151,35 +151,12 @@ static void agp_add_seg_to_client(struct agp_client *client, client->segments = seg; } -/* Originally taken from linux/mm/mmap.c from the array - * protection_map. - * The original really should be exported to modules, or - * some routine which does the conversion for you - */ - -static const pgprot_t my_protect_map[16] = -{ - __P000, __P001, __P010, __P011, __P100, __P101, __P110, __P111, - __S000, __S001, __S010, __S011, __S100, __S101, __S110, __S111 -}; - static pgprot_t agp_convert_mmap_flags(int prot) { -#define _trans(x,bit1,bit2) \ -((bit1==bit2)?(x&bit1):(x&bit1)?bit2:0) - unsigned long prot_bits; - pgprot_t temp; - - prot_bits = _trans(prot, PROT_READ, VM_READ) | - _trans(prot, PROT_WRITE, VM_WRITE) | - _trans(prot, PROT_EXEC, VM_EXEC); - - prot_bits |= VM_SHARED; - temp = my_protect_map[prot_bits & 0x0000000f]; - - return temp; + prot_bits = calc_vm_prot_bits(prot) | VM_SHARED; + return vm_get_page_prot(prot_bits); } static int agp_create_segment(struct agp_client *client, struct agp_region *region) diff --git a/drivers/char/agp/generic.c b/drivers/char/agp/generic.c index cc5ea347a8a..c3920016168 100644 --- a/drivers/char/agp/generic.c +++ b/drivers/char/agp/generic.c @@ -568,25 +568,37 @@ static void agp_v3_parse_one(u32 *requested_mode, u32 *bridge_agpstat, u32 *vga_ *bridge_agpstat &= ~(AGPSTAT3_4X | AGPSTAT3_RSVD); goto done; + } else if (*requested_mode & AGPSTAT3_4X) { + *bridge_agpstat &= ~(AGPSTAT3_8X | AGPSTAT3_RSVD); + *bridge_agpstat |= AGPSTAT3_4X; + goto done; + } else { /* - * If we didn't specify AGPx8, we can only do x4. - * If the hardware can't do x4, we're up shit creek, and never - * should have got this far. + * If we didn't specify an AGP mode, we see if both + * the graphics card, and the bridge can do x8, and use if so. + * If not, we fall back to x4 mode. */ - *bridge_agpstat &= ~(AGPSTAT3_8X | AGPSTAT3_RSVD); - if ((*bridge_agpstat & AGPSTAT3_4X) && (*vga_agpstat & AGPSTAT3_4X)) - *bridge_agpstat |= AGPSTAT3_4X; - else { - printk(KERN_INFO PFX "Badness. Don't know which AGP mode to set. " - "[bridge_agpstat:%x vga_agpstat:%x fell back to:- bridge_agpstat:%x vga_agpstat:%x]\n", - origbridge, origvga, *bridge_agpstat, *vga_agpstat); - if (!(*bridge_agpstat & AGPSTAT3_4X)) - printk(KERN_INFO PFX "Bridge couldn't do AGP x4.\n"); - if (!(*vga_agpstat & AGPSTAT3_4X)) - printk(KERN_INFO PFX "Graphic card couldn't do AGP x4.\n"); - return; + if ((*bridge_agpstat & AGPSTAT3_8X) && (*vga_agpstat & AGPSTAT3_8X)) { + printk(KERN_INFO PFX "No AGP mode specified. Setting to highest mode " + "supported by bridge & card (x8).\n"); + *bridge_agpstat &= ~(AGPSTAT3_4X | AGPSTAT3_RSVD); + *vga_agpstat &= ~(AGPSTAT3_4X | AGPSTAT3_RSVD); + } else { + printk(KERN_INFO PFX "Fell back to AGPx4 mode because"); + if (!(*bridge_agpstat & AGPSTAT3_8X)) { + printk(KERN_INFO PFX "bridge couldn't do x8. bridge_agpstat:%x (orig=%x)\n", + *bridge_agpstat, origbridge); + *bridge_agpstat &= ~(AGPSTAT3_8X | AGPSTAT3_RSVD); + *bridge_agpstat |= AGPSTAT3_4X; + } + if (!(*vga_agpstat & AGPSTAT3_8X)) { + printk(KERN_INFO PFX "graphics card couldn't do x8. vga_agpstat:%x (orig=%x)\n", + *vga_agpstat, origvga); + *vga_agpstat &= ~(AGPSTAT3_8X | AGPSTAT3_RSVD); + *vga_agpstat |= AGPSTAT3_4X; + } } } diff --git a/drivers/char/agp/intel-agp.c b/drivers/char/agp/intel-agp.c index 61ac3809f99..d1ede7db5a1 100644 --- a/drivers/char/agp/intel-agp.c +++ b/drivers/char/agp/intel-agp.c @@ -2,14 +2,6 @@ * Intel AGPGART routines. */ -/* - * Intel(R) 855GM/852GM and 865G support added by David Dawes - * <dawes@tungstengraphics.com>. - * - * Intel(R) 915G/915GM support added by Alan Hourihane - * <alanh@tungstengraphics.com>. - */ - #include <linux/module.h> #include <linux/pci.h> #include <linux/init.h> @@ -17,6 +9,21 @@ #include <linux/agp_backend.h> #include "agp.h" +#define PCI_DEVICE_ID_INTEL_82946GZ_HB 0x2970 +#define PCI_DEVICE_ID_INTEL_82946GZ_IG 0x2972 +#define PCI_DEVICE_ID_INTEL_82965G_1_HB 0x2980 +#define PCI_DEVICE_ID_INTEL_82965G_1_IG 0x2982 +#define PCI_DEVICE_ID_INTEL_82965Q_HB 0x2990 +#define PCI_DEVICE_ID_INTEL_82965Q_IG 0x2992 +#define PCI_DEVICE_ID_INTEL_82965G_HB 0x29A0 +#define PCI_DEVICE_ID_INTEL_82965G_IG 0x29A2 + +#define IS_I965 (agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82946GZ_HB || \ + agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82965G_1_HB || \ + agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82965Q_HB || \ + agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82965G_HB) + + /* Intel 815 register */ #define INTEL_815_APCONT 0x51 #define INTEL_815_ATTBASE_MASK ~0x1FFFFFFF @@ -40,6 +47,8 @@ #define I915_GMCH_GMS_STOLEN_48M (0x6 << 4) #define I915_GMCH_GMS_STOLEN_64M (0x7 << 4) +/* Intel 965G registers */ +#define I965_MSAC 0x62 /* Intel 7505 registers */ #define INTEL_I7505_APSIZE 0x74 @@ -354,6 +363,7 @@ static struct aper_size_info_fixed intel_i830_sizes[] = /* The 64M mode still requires a 128k gatt */ {64, 16384, 5}, {256, 65536, 6}, + {512, 131072, 7}, }; static struct _intel_i830_private { @@ -377,7 +387,11 @@ static void intel_i830_init_gtt_entries(void) /* We obtain the size of the GTT, which is also stored (for some * reason) at the top of stolen memory. Then we add 4KB to that * for the video BIOS popup, which is also stored in there. */ - size = agp_bridge->driver->fetch_size() + 4; + + if (IS_I965) + size = 512 + 4; + else + size = agp_bridge->driver->fetch_size() + 4; if (agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82830_HB || agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82845G_HB) { @@ -423,7 +437,7 @@ static void intel_i830_init_gtt_entries(void) if (agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82915G_HB || agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82915GM_HB || agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82945G_HB || - agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82945GM_HB) + agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82945GM_HB || IS_I965 ) gtt_entries = MB(48) - KB(size); else gtt_entries = 0; @@ -433,7 +447,7 @@ static void intel_i830_init_gtt_entries(void) if (agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82915G_HB || agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82915GM_HB || agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82945G_HB || - agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82945GM_HB) + agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82945GM_HB || IS_I965) gtt_entries = MB(64) - KB(size); else gtt_entries = 0; @@ -791,6 +805,77 @@ static int intel_i915_create_gatt_table(struct agp_bridge_data *bridge) return 0; } +static int intel_i965_fetch_size(void) +{ + struct aper_size_info_fixed *values; + u32 offset = 0; + u8 temp; + +#define I965_512MB_ADDRESS_MASK (3<<1) + + values = A_SIZE_FIX(agp_bridge->driver->aperture_sizes); + + pci_read_config_byte(intel_i830_private.i830_dev, I965_MSAC, &temp); + temp &= I965_512MB_ADDRESS_MASK; + switch (temp) { + case 0x00: + offset = 0; /* 128MB */ + break; + case 0x06: + offset = 3; /* 512MB */ + break; + default: + case 0x02: + offset = 2; /* 256MB */ + break; + } + + agp_bridge->previous_size = agp_bridge->current_size = (void *)(values + offset); + + return values[offset].size; +} + +/* The intel i965 automatically initializes the agp aperture during POST. ++ * Use the memory already set aside for in the GTT. ++ */ +static int intel_i965_create_gatt_table(struct agp_bridge_data *bridge) +{ + int page_order; + struct aper_size_info_fixed *size; + int num_entries; + u32 temp; + + size = agp_bridge->current_size; + page_order = size->page_order; + num_entries = size->num_entries; + agp_bridge->gatt_table_real = NULL; + + pci_read_config_dword(intel_i830_private.i830_dev, I915_MMADDR, &temp); + + temp &= 0xfff00000; + intel_i830_private.gtt = ioremap((temp + (512 * 1024)) , 512 * 1024); + + if (!intel_i830_private.gtt) + return -ENOMEM; + + + intel_i830_private.registers = ioremap(temp,128 * 4096); + if (!intel_i830_private.registers) + return -ENOMEM; + + temp = readl(intel_i830_private.registers+I810_PGETBL_CTL) & 0xfffff000; + global_cache_flush(); /* FIXME: ? */ + + /* we have to call this as early as possible after the MMIO base address is known */ + intel_i830_init_gtt_entries(); + + agp_bridge->gatt_table = NULL; + + agp_bridge->gatt_bus_addr = temp; + + return 0; +} + static int intel_fetch_size(void) { @@ -1307,7 +1392,7 @@ static struct agp_bridge_driver intel_830_driver = { .owner = THIS_MODULE, .aperture_sizes = intel_i830_sizes, .size_type = FIXED_APER_SIZE, - .num_aperture_sizes = 3, + .num_aperture_sizes = 4, .needs_scratch_page = TRUE, .configure = intel_i830_configure, .fetch_size = intel_i830_fetch_size, @@ -1469,7 +1554,7 @@ static struct agp_bridge_driver intel_915_driver = { .owner = THIS_MODULE, .aperture_sizes = intel_i830_sizes, .size_type = FIXED_APER_SIZE, - .num_aperture_sizes = 3, + .num_aperture_sizes = 4, .needs_scratch_page = TRUE, .configure = intel_i915_configure, .fetch_size = intel_i915_fetch_size, @@ -1489,6 +1574,29 @@ static struct agp_bridge_driver intel_915_driver = { .agp_destroy_page = agp_generic_destroy_page, }; +static struct agp_bridge_driver intel_i965_driver = { + .owner = THIS_MODULE, + .aperture_sizes = intel_i830_sizes, + .size_type = FIXED_APER_SIZE, + .num_aperture_sizes = 4, + .needs_scratch_page = TRUE, + .configure = intel_i915_configure, + .fetch_size = intel_i965_fetch_size, + .cleanup = intel_i915_cleanup, + .tlb_flush = intel_i810_tlbflush, + .mask_memory = intel_i810_mask_memory, + .masks = intel_i810_masks, + .agp_enable = intel_i810_agp_enable, + .cache_flush = global_cache_flush, + .create_gatt_table = intel_i965_create_gatt_table, + .free_gatt_table = intel_i830_free_gatt_table, + .insert_memory = intel_i915_insert_entries, + .remove_memory = intel_i915_remove_entries, + .alloc_by_type = intel_i830_alloc_by_type, + .free_by_type = intel_i810_free_by_type, + .agp_alloc_page = agp_generic_alloc_page, + .agp_destroy_page = agp_generic_destroy_page, +}; static struct agp_bridge_driver intel_7505_driver = { .owner = THIS_MODULE, @@ -1684,6 +1792,35 @@ static int __devinit agp_intel_probe(struct pci_dev *pdev, bridge->driver = &intel_845_driver; name = "945GM"; break; + case PCI_DEVICE_ID_INTEL_82946GZ_HB: + if (find_i830(PCI_DEVICE_ID_INTEL_82946GZ_IG)) + bridge->driver = &intel_i965_driver; + else + bridge->driver = &intel_845_driver; + name = "946GZ"; + break; + case PCI_DEVICE_ID_INTEL_82965G_1_HB: + if (find_i830(PCI_DEVICE_ID_INTEL_82965G_1_IG)) + bridge->driver = &intel_i965_driver; + else + bridge->driver = &intel_845_driver; + name = "965G"; + break; + case PCI_DEVICE_ID_INTEL_82965Q_HB: + if (find_i830(PCI_DEVICE_ID_INTEL_82965Q_IG)) + bridge->driver = &intel_i965_driver; + else + bridge->driver = &intel_845_driver; + name = "965Q"; + break; + case PCI_DEVICE_ID_INTEL_82965G_HB: + if (find_i830(PCI_DEVICE_ID_INTEL_82965G_IG)) + bridge->driver = &intel_i965_driver; + else + bridge->driver = &intel_845_driver; + name = "965G"; + break; + case PCI_DEVICE_ID_INTEL_7505_0: bridge->driver = &intel_7505_driver; name = "E7505"; @@ -1766,6 +1903,7 @@ static void __devexit agp_intel_remove(struct pci_dev *pdev) agp_put_bridge(bridge); } +#ifdef CONFIG_PM static int agp_intel_resume(struct pci_dev *pdev) { struct agp_bridge_data *bridge = pci_get_drvdata(pdev); @@ -1786,9 +1924,12 @@ static int agp_intel_resume(struct pci_dev *pdev) intel_i830_configure(); else if (bridge->driver == &intel_810_driver) intel_i810_configure(); + else if (bridge->driver == &intel_i965_driver) + intel_i915_configure(); return 0; } +#endif static struct pci_device_id agp_intel_pci_table[] = { #define ID(x) \ @@ -1825,6 +1966,10 @@ static struct pci_device_id agp_intel_pci_table[] = { ID(PCI_DEVICE_ID_INTEL_82915GM_HB), ID(PCI_DEVICE_ID_INTEL_82945G_HB), ID(PCI_DEVICE_ID_INTEL_82945GM_HB), + ID(PCI_DEVICE_ID_INTEL_82946GZ_HB), + ID(PCI_DEVICE_ID_INTEL_82965G_1_HB), + ID(PCI_DEVICE_ID_INTEL_82965Q_HB), + ID(PCI_DEVICE_ID_INTEL_82965G_HB), { } }; @@ -1835,7 +1980,9 @@ static struct pci_driver agp_intel_pci_driver = { .id_table = agp_intel_pci_table, .probe = agp_intel_probe, .remove = __devexit_p(agp_intel_remove), +#ifdef CONFIG_PM .resume = agp_intel_resume, +#endif }; static int __init agp_intel_init(void) diff --git a/drivers/char/agp/uninorth-agp.c b/drivers/char/agp/uninorth-agp.c index 1de1b12043b..91b71e750ee 100644 --- a/drivers/char/agp/uninorth-agp.c +++ b/drivers/char/agp/uninorth-agp.c @@ -601,8 +601,8 @@ static int __devinit agp_uninorth_probe(struct pci_dev *pdev, uninorth_node = of_find_node_by_name(NULL, "u3"); } if (uninorth_node) { - int *revprop = (int *) - get_property(uninorth_node, "device-rev", NULL); + const int *revprop = get_property(uninorth_node, + "device-rev", NULL); if (revprop != NULL) uninorth_rev = *revprop & 0x3f; of_node_put(uninorth_node); diff --git a/drivers/char/agp/via-agp.c b/drivers/char/agp/via-agp.c index b8ec25d1747..c149ac9ce9a 100644 --- a/drivers/char/agp/via-agp.c +++ b/drivers/char/agp/via-agp.c @@ -9,7 +9,7 @@ #include <linux/agp_backend.h> #include "agp.h" -static struct pci_device_id agp_via_pci_table[]; +static const struct pci_device_id agp_via_pci_table[]; #define VIA_GARTCTRL 0x80 #define VIA_APSIZE 0x84 @@ -485,7 +485,7 @@ static int agp_via_resume(struct pci_dev *pdev) #endif /* CONFIG_PM */ /* must be the same order as name table above */ -static struct pci_device_id agp_via_pci_table[] = { +static const struct pci_device_id agp_via_pci_table[] = { #define ID(x) \ { \ .class = (PCI_CLASS_BRIDGE_HOST << 8), \ diff --git a/drivers/char/briq_panel.c b/drivers/char/briq_panel.c new file mode 100644 index 00000000000..b8c22255f6a --- /dev/null +++ b/drivers/char/briq_panel.c @@ -0,0 +1,271 @@ +/* + * Drivers for the Total Impact PPC based computer "BRIQ" + * by Dr. Karsten Jeppesen + * + */ + +#include <linux/module.h> + +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/tty.h> +#include <linux/timer.h> +#include <linux/config.h> +#include <linux/kernel.h> +#include <linux/wait.h> +#include <linux/string.h> +#include <linux/slab.h> +#include <linux/ioport.h> +#include <linux/delay.h> +#include <linux/miscdevice.h> +#include <linux/fs.h> +#include <linux/mm.h> +#include <linux/init.h> + +#include <asm/uaccess.h> +#include <asm/io.h> +#include <asm/prom.h> + +#define BRIQ_PANEL_MINOR 156 +#define BRIQ_PANEL_VFD_IOPORT 0x0390 +#define BRIQ_PANEL_LED_IOPORT 0x0398 +#define BRIQ_PANEL_VER "1.1 (04/20/2002)" +#define BRIQ_PANEL_MSG0 "Loading Linux" + +static int vfd_is_open; +static unsigned char vfd[40]; +static int vfd_cursor; +static unsigned char ledpb, led; + +static void update_vfd(void) +{ + int i; + + /* cursor home */ + outb(0x02, BRIQ_PANEL_VFD_IOPORT); + for (i=0; i<20; i++) + outb(vfd[i], BRIQ_PANEL_VFD_IOPORT + 1); + + /* cursor to next line */ + outb(0xc0, BRIQ_PANEL_VFD_IOPORT); + for (i=20; i<40; i++) + outb(vfd[i], BRIQ_PANEL_VFD_IOPORT + 1); + +} + +static void set_led(char state) +{ + if (state == 'R') + led = 0x01; + else if (state == 'G') + led = 0x02; + else if (state == 'Y') + led = 0x03; + else if (state == 'X') + led = 0x00; + outb(led, BRIQ_PANEL_LED_IOPORT); +} + +static int briq_panel_open(struct inode *ino, struct file *filep) +{ + /* enforce single access */ + if (vfd_is_open) + return -EBUSY; + vfd_is_open = 1; + + return 0; +} + +static int briq_panel_release(struct inode *ino, struct file *filep) +{ + if (!vfd_is_open) + return -ENODEV; + + vfd_is_open = 0; + + return 0; +} + +static ssize_t briq_panel_read(struct file *file, char __user *buf, size_t count, + loff_t *ppos) +{ + unsigned short c; + unsigned char cp; + +#if 0 /* Can't seek (pread) on this device */ + if (ppos != &file->f_pos) + return -ESPIPE; +#endif + + if (!vfd_is_open) + return -ENODEV; + + c = (inb(BRIQ_PANEL_LED_IOPORT) & 0x000c) | (ledpb & 0x0003); + set_led(' '); + /* upper button released */ + if ((!(ledpb & 0x0004)) && (c & 0x0004)) { + cp = ' '; + ledpb = c; + if (copy_to_user(buf, &cp, 1)) + return -EFAULT; + return 1; + } + /* lower button released */ + else if ((!(ledpb & 0x0008)) && (c & 0x0008)) { + cp = '\r'; + ledpb = c; + if (copy_to_user(buf, &cp, 1)) + return -EFAULT; + return 1; + } else { + ledpb = c; + return 0; + } +} + +static void scroll_vfd( void ) +{ + int i; + + for (i=0; i<20; i++) { + vfd[i] = vfd[i+20]; + vfd[i+20] = ' '; + } + vfd_cursor = 20; +} + +static ssize_t briq_panel_write(struct file *file, const char __user *buf, size_t len, + loff_t *ppos) +{ + size_t indx = len; + int i, esc = 0; + +#if 0 /* Can't seek (pwrite) on this device */ + if (ppos != &file->f_pos) + return -ESPIPE; +#endif + + if (!vfd_is_open) + return -EBUSY; + + for (;;) { + char c; + if (!indx) + break; + if (get_user(c, buf)) + return -EFAULT; + if (esc) { + set_led(c); + esc = 0; + } else if (c == 27) { + esc = 1; + } else if (c == 12) { + /* do a form feed */ + for (i=0; i<40; i++) + vfd[i] = ' '; + vfd_cursor = 0; + } else if (c == 10) { + if (vfd_cursor < 20) + vfd_cursor = 20; + else if (vfd_cursor < 40) + vfd_cursor = 40; + else if (vfd_cursor < 60) + vfd_cursor = 60; + if (vfd_cursor > 59) + scroll_vfd(); + } else { + /* just a character */ + if (vfd_cursor > 39) + scroll_vfd(); + vfd[vfd_cursor++] = c; + } + indx--; + buf++; + } + update_vfd(); + + return len; +} + +static struct file_operations briq_panel_fops = { + .owner = THIS_MODULE, + .read = briq_panel_read, + .write = briq_panel_write, + .open = briq_panel_open, + .release = briq_panel_release, +}; + +static struct miscdevice briq_panel_miscdev = { + BRIQ_PANEL_MINOR, + "briq_panel", + &briq_panel_fops +}; + +static int __init briq_panel_init(void) +{ + struct device_node *root = find_path_device("/"); + const char *machine; + int i; + + machine = get_property(root, "model", NULL); + if (!machine || strncmp(machine, "TotalImpact,BRIQ-1", 18) != 0) + return -ENODEV; + + printk(KERN_INFO + "briq_panel: v%s Dr. Karsten Jeppesen (kj@totalimpact.com)\n", + BRIQ_PANEL_VER); + + if (!request_region(BRIQ_PANEL_VFD_IOPORT, 4, "BRIQ Front Panel")) + return -EBUSY; + + if (!request_region(BRIQ_PANEL_LED_IOPORT, 2, "BRIQ Front Panel")) { + release_region(BRIQ_PANEL_VFD_IOPORT, 4); + return -EBUSY; + } + ledpb = inb(BRIQ_PANEL_LED_IOPORT) & 0x000c; + + if (misc_register(&briq_panel_miscdev) < 0) { + release_region(BRIQ_PANEL_VFD_IOPORT, 4); + release_region(BRIQ_PANEL_LED_IOPORT, 2); + return -EBUSY; + } + + outb(0x38, BRIQ_PANEL_VFD_IOPORT); /* Function set */ + outb(0x01, BRIQ_PANEL_VFD_IOPORT); /* Clear display */ + outb(0x0c, BRIQ_PANEL_VFD_IOPORT); /* Display on */ + outb(0x06, BRIQ_PANEL_VFD_IOPORT); /* Entry normal */ + for (i=0; i<40; i++) + vfd[i]=' '; +#ifndef MODULE + vfd[0] = 'L'; + vfd[1] = 'o'; + vfd[2] = 'a'; + vfd[3] = 'd'; + vfd[4] = 'i'; + vfd[5] = 'n'; + vfd[6] = 'g'; + vfd[7] = ' '; + vfd[8] = '.'; + vfd[9] = '.'; + vfd[10] = '.'; +#endif /* !MODULE */ + + update_vfd(); + + return 0; +} + +static void __exit briq_panel_exit(void) +{ + misc_deregister(&briq_panel_miscdev); + release_region(BRIQ_PANEL_VFD_IOPORT, 4); + release_region(BRIQ_PANEL_LED_IOPORT, 2); +} + +module_init(briq_panel_init); +module_exit(briq_panel_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Karsten Jeppesen <karsten@jeppesens.com>"); +MODULE_DESCRIPTION("Driver for the Total Impact briQ front panel"); diff --git a/drivers/char/drm/Kconfig b/drivers/char/drm/Kconfig index 5278c388d3e..ef833a1c27e 100644 --- a/drivers/char/drm/Kconfig +++ b/drivers/char/drm/Kconfig @@ -60,7 +60,9 @@ config DRM_I830 Choose this option if you have a system that has Intel 830M, 845G, 852GM, 855GM or 865G integrated graphics. If M is selected, the module will be called i830. AGP support is required for this driver - to work. This driver will eventually be replaced by the i915 one. + to work. This driver is used by the older X releases X.org 6.7 and + XFree86 4.3. If unsure, build this and i915 as modules and the X server + will load the correct one. config DRM_I915 tristate "i915 driver" @@ -68,8 +70,9 @@ config DRM_I915 Choose this option if you have a system that has Intel 830M, 845G, 852GM, 855GM 865G or 915G integrated graphics. If M is selected, the module will be called i915. AGP support is required for this driver - to work. This driver will eventually replace the I830 driver, when - later release of X start to use the new DDX and DRI. + to work. This driver is used by the Intel driver in X.org 6.8 and + XFree86 4.4 and above. If unsure, build this and i830 as modules and + the X server will load the correct one. endchoice diff --git a/drivers/char/drm/Makefile b/drivers/char/drm/Makefile index 9d180c42816..3ad0f648c6b 100644 --- a/drivers/char/drm/Makefile +++ b/drivers/char/drm/Makefile @@ -6,7 +6,7 @@ drm-objs := drm_auth.o drm_bufs.o drm_context.o drm_dma.o drm_drawable.o \ drm_drv.o drm_fops.o drm_ioctl.o drm_irq.o \ drm_lock.o drm_memory.o drm_proc.o drm_stub.o drm_vm.o \ drm_agpsupport.o drm_scatter.o ati_pcigart.o drm_pci.o \ - drm_sysfs.o + drm_sysfs.o drm_hashtab.o drm_sman.o drm_mm.o tdfx-objs := tdfx_drv.o r128-objs := r128_drv.o r128_cce.o r128_state.o r128_irq.o @@ -16,9 +16,9 @@ i830-objs := i830_drv.o i830_dma.o i830_irq.o i915-objs := i915_drv.o i915_dma.o i915_irq.o i915_mem.o radeon-objs := radeon_drv.o radeon_cp.o radeon_state.o radeon_mem.o radeon_irq.o r300_cmdbuf.o ffb-objs := ffb_drv.o ffb_context.o -sis-objs := sis_drv.o sis_ds.o sis_mm.o +sis-objs := sis_drv.o sis_mm.o savage-objs := savage_drv.o savage_bci.o savage_state.o -via-objs := via_irq.o via_drv.o via_ds.o via_map.o via_mm.o via_dma.o via_verifier.o via_video.o via_dmablit.o +via-objs := via_irq.o via_drv.o via_map.o via_mm.o via_dma.o via_verifier.o via_video.o via_dmablit.o ifeq ($(CONFIG_COMPAT),y) drm-objs += drm_ioc32.o diff --git a/drivers/char/drm/drmP.h b/drivers/char/drm/drmP.h index d2a56182bc3..7690a59ace0 100644 --- a/drivers/char/drm/drmP.h +++ b/drivers/char/drm/drmP.h @@ -79,6 +79,7 @@ #define __OS_HAS_MTRR (defined(CONFIG_MTRR)) #include "drm_os_linux.h" +#include "drm_hashtab.h" /***********************************************************************/ /** \name DRM template customization defaults */ @@ -104,7 +105,7 @@ #define DRM_DEBUG_CODE 2 /**< Include debugging code if > 1, then also include looping detection. */ -#define DRM_HASH_SIZE 16 /**< Size of key hash table. Must be power of 2. */ +#define DRM_MAGIC_HASH_ORDER 4 /**< Size of key hash table. Must be power of 2. */ #define DRM_KERNEL_CONTEXT 0 /**< Change drm_resctx if changed */ #define DRM_RESERVED_CONTEXTS 1 /**< Change drm_resctx if changed */ #define DRM_LOOPING_LIMIT 5000000 @@ -134,19 +135,12 @@ #define DRM_MEM_CTXBITMAP 18 #define DRM_MEM_STUB 19 #define DRM_MEM_SGLISTS 20 -#define DRM_MEM_CTXLIST 21 +#define DRM_MEM_CTXLIST 21 +#define DRM_MEM_MM 22 +#define DRM_MEM_HASHTAB 23 #define DRM_MAX_CTXBITMAP (PAGE_SIZE * 8) - -/*@}*/ - -/***********************************************************************/ -/** \name Backward compatibility section */ -/*@{*/ - -#define DRM_RPR_ARG(vma) vma, - -#define VM_OFFSET(vma) ((vma)->vm_pgoff << PAGE_SHIFT) +#define DRM_MAP_HASH_OFFSET 0x10000000 /*@}*/ @@ -211,8 +205,6 @@ /*@{*/ #define DRM_ARRAY_SIZE(x) ARRAY_SIZE(x) -#define DRM_MIN(a,b) min(a,b) -#define DRM_MAX(a,b) max(a,b) #define DRM_LEFTCOUNT(x) (((x)->rp + (x)->count - (x)->wp) % ((x)->count + 1)) #define DRM_BUFCOUNT(x) ((x)->count - DRM_LEFTCOUNT(x)) @@ -286,7 +278,8 @@ typedef struct drm_devstate { } drm_devstate_t; typedef struct drm_magic_entry { - drm_magic_t magic; + drm_hash_item_t hash_item; + struct list_head head; struct drm_file *priv; struct drm_magic_entry *next; } drm_magic_entry_t; @@ -493,6 +486,7 @@ typedef struct drm_sigdata { */ typedef struct drm_map_list { struct list_head head; /**< list head */ + drm_hash_item_t hash; drm_map_t *map; /**< mapping */ unsigned int user_token; } drm_map_list_t; @@ -527,6 +521,22 @@ typedef struct ati_pcigart_info { drm_local_map_t mapping; } drm_ati_pcigart_info; +/* + * Generic memory manager structs + */ +typedef struct drm_mm_node { + struct list_head fl_entry; + struct list_head ml_entry; + int free; + unsigned long start; + unsigned long size; + void *private; +} drm_mm_node_t; + +typedef struct drm_mm { + drm_mm_node_t root_node; +} drm_mm_t; + /** * DRM driver structure. This structure represent the common code for * a family of cards. There will one drm_device for each card present @@ -646,13 +656,15 @@ typedef struct drm_device { /*@{ */ drm_file_t *file_first; /**< file list head */ drm_file_t *file_last; /**< file list tail */ - drm_magic_head_t magiclist[DRM_HASH_SIZE]; /**< magic hash table */ + drm_open_hash_t magiclist; /**< magic hash table */ + struct list_head magicfree; /*@} */ /** \name Memory management */ /*@{ */ drm_map_list_t *maplist; /**< Linked list of regions */ int map_count; /**< Number of mappable regions */ + drm_open_hash_t map_hash; /**< User token hash table for maps */ /** \name Context handle management */ /*@{ */ @@ -711,10 +723,8 @@ typedef struct drm_device { drm_agp_head_t *agp; /**< AGP data */ struct pci_dev *pdev; /**< PCI device structure */ - int pci_domain; /**< PCI bus domain number */ - int pci_bus; /**< PCI bus number */ - int pci_slot; /**< PCI slot number */ - int pci_func; /**< PCI function number */ + int pci_vendor; /**< PCI vendor id */ + int pci_device; /**< PCI device id */ #ifdef __alpha__ struct pci_controller *hose; #endif @@ -736,6 +746,12 @@ static __inline__ int drm_core_check_feature(struct drm_device *dev, return ((dev->driver->driver_features & feature) ? 1 : 0); } +#ifdef __alpha__ +#define drm_get_pci_domain(dev) dev->hose->bus->number +#else +#define drm_get_pci_domain(dev) 0 +#endif + #if __OS_HAS_AGP static inline int drm_core_has_AGP(struct drm_device *dev) { @@ -1011,6 +1027,18 @@ extern struct class_device *drm_sysfs_device_add(struct class *cs, drm_head_t *head); extern void drm_sysfs_device_remove(struct class_device *class_dev); +/* + * Basic memory manager support (drm_mm.c) + */ +extern drm_mm_node_t *drm_mm_get_block(drm_mm_node_t * parent, + unsigned long size, + unsigned alignment); +extern void drm_mm_put_block(drm_mm_t *mm, drm_mm_node_t *cur); +extern drm_mm_node_t *drm_mm_search_free(const drm_mm_t *mm, unsigned long size, + unsigned alignment, int best_match); +extern int drm_mm_init(drm_mm_t *mm, unsigned long start, unsigned long size); +extern void drm_mm_takedown(drm_mm_t *mm); + /* Inline replacements for DRM_IOREMAP macros */ static __inline__ void drm_core_ioremap(struct drm_map *map, struct drm_device *dev) diff --git a/drivers/char/drm/drm_auth.c b/drivers/char/drm/drm_auth.c index 2a37586a7ee..c7b19d35bcd 100644 --- a/drivers/char/drm/drm_auth.c +++ b/drivers/char/drm/drm_auth.c @@ -36,20 +36,6 @@ #include "drmP.h" /** - * Generate a hash key from a magic. - * - * \param magic magic. - * \return hash key. - * - * The key is the modulus of the hash table size, #DRM_HASH_SIZE, which must be - * a power of 2. - */ -static int drm_hash_magic(drm_magic_t magic) -{ - return magic & (DRM_HASH_SIZE - 1); -} - -/** * Find the file with the given magic number. * * \param dev DRM device. @@ -63,14 +49,12 @@ static drm_file_t *drm_find_file(drm_device_t * dev, drm_magic_t magic) { drm_file_t *retval = NULL; drm_magic_entry_t *pt; - int hash = drm_hash_magic(magic); + drm_hash_item_t *hash; mutex_lock(&dev->struct_mutex); - for (pt = dev->magiclist[hash].head; pt; pt = pt->next) { - if (pt->magic == magic) { - retval = pt->priv; - break; - } + if (!drm_ht_find_item(&dev->magiclist, (unsigned long)magic, &hash)) { + pt = drm_hash_entry(hash, drm_magic_entry_t, hash_item); + retval = pt->priv; } mutex_unlock(&dev->struct_mutex); return retval; @@ -90,28 +74,20 @@ static drm_file_t *drm_find_file(drm_device_t * dev, drm_magic_t magic) static int drm_add_magic(drm_device_t * dev, drm_file_t * priv, drm_magic_t magic) { - int hash; drm_magic_entry_t *entry; DRM_DEBUG("%d\n", magic); - hash = drm_hash_magic(magic); entry = drm_alloc(sizeof(*entry), DRM_MEM_MAGIC); if (!entry) return -ENOMEM; memset(entry, 0, sizeof(*entry)); - entry->magic = magic; entry->priv = priv; - entry->next = NULL; + entry->hash_item.key = (unsigned long)magic; mutex_lock(&dev->struct_mutex); - if (dev->magiclist[hash].tail) { - dev->magiclist[hash].tail->next = entry; - dev->magiclist[hash].tail = entry; - } else { - dev->magiclist[hash].head = entry; - dev->magiclist[hash].tail = entry; - } + drm_ht_insert_item(&dev->magiclist, &entry->hash_item); + list_add_tail(&entry->head, &dev->magicfree); mutex_unlock(&dev->struct_mutex); return 0; @@ -128,34 +104,24 @@ static int drm_add_magic(drm_device_t * dev, drm_file_t * priv, */ static int drm_remove_magic(drm_device_t * dev, drm_magic_t magic) { - drm_magic_entry_t *prev = NULL; drm_magic_entry_t *pt; - int hash; + drm_hash_item_t *hash; DRM_DEBUG("%d\n", magic); - hash = drm_hash_magic(magic); mutex_lock(&dev->struct_mutex); - for (pt = dev->magiclist[hash].head; pt; prev = pt, pt = pt->next) { - if (pt->magic == magic) { - if (dev->magiclist[hash].head == pt) { - dev->magiclist[hash].head = pt->next; - } - if (dev->magiclist[hash].tail == pt) { - dev->magiclist[hash].tail = prev; - } - if (prev) { - prev->next = pt->next; - } - mutex_unlock(&dev->struct_mutex); - return 0; - } + if (drm_ht_find_item(&dev->magiclist, (unsigned long)magic, &hash)) { + mutex_unlock(&dev->struct_mutex); + return -EINVAL; } + pt = drm_hash_entry(hash, drm_magic_entry_t, hash_item); + drm_ht_remove_item(&dev->magiclist, hash); + list_del(&pt->head); mutex_unlock(&dev->struct_mutex); drm_free(pt, sizeof(*pt), DRM_MEM_MAGIC); - return -EINVAL; + return 0; } /** diff --git a/drivers/char/drm/drm_bufs.c b/drivers/char/drm/drm_bufs.c index 006b06d2972..029baea33b6 100644 --- a/drivers/char/drm/drm_bufs.c +++ b/drivers/char/drm/drm_bufs.c @@ -65,43 +65,29 @@ static drm_map_list_t *drm_find_matching_map(drm_device_t *dev, return NULL; } -/* - * Used to allocate 32-bit handles for mappings. - */ -#define START_RANGE 0x10000000 -#define END_RANGE 0x40000000 - -#ifdef _LP64 -static __inline__ unsigned int HandleID(unsigned long lhandle, - drm_device_t *dev) +static int drm_map_handle(drm_device_t *dev, drm_hash_item_t *hash, + unsigned long user_token, int hashed_handle) { - static unsigned int map32_handle = START_RANGE; - unsigned int hash; - - if (lhandle & 0xffffffff00000000) { - hash = map32_handle; - map32_handle += PAGE_SIZE; - if (map32_handle > END_RANGE) - map32_handle = START_RANGE; - } else - hash = lhandle; - - while (1) { - drm_map_list_t *_entry; - list_for_each_entry(_entry, &dev->maplist->head, head) { - if (_entry->user_token == hash) - break; - } - if (&_entry->head == &dev->maplist->head) - return hash; + int use_hashed_handle; +#if (BITS_PER_LONG == 64) + use_hashed_handle = ((user_token & 0xFFFFFFFF00000000UL) || hashed_handle); +#elif (BITS_PER_LONG == 32) + use_hashed_handle = hashed_handle; +#else +#error Unsupported long size. Neither 64 nor 32 bits. +#endif - hash += PAGE_SIZE; - map32_handle += PAGE_SIZE; + if (!use_hashed_handle) { + int ret; + hash->key = user_token; + ret = drm_ht_insert_item(&dev->map_hash, hash); + if (ret != -EINVAL) + return ret; } + return drm_ht_just_insert_please(&dev->map_hash, hash, + user_token, 32 - PAGE_SHIFT - 3, + PAGE_SHIFT, DRM_MAP_HASH_OFFSET); } -#else -# define HandleID(x,dev) (unsigned int)(x) -#endif /** * Ioctl to specify a range of memory that is available for mapping by a non-root process. @@ -123,6 +109,8 @@ static int drm_addmap_core(drm_device_t * dev, unsigned int offset, drm_map_t *map; drm_map_list_t *list; drm_dma_handle_t *dmah; + unsigned long user_token; + int ret; map = drm_alloc(sizeof(*map), DRM_MEM_MAPS); if (!map) @@ -257,11 +245,20 @@ static int drm_addmap_core(drm_device_t * dev, unsigned int offset, mutex_lock(&dev->struct_mutex); list_add(&list->head, &dev->maplist->head); + /* Assign a 32-bit handle */ /* We do it here so that dev->struct_mutex protects the increment */ - list->user_token = HandleID(map->type == _DRM_SHM - ? (unsigned long)map->handle - : map->offset, dev); + user_token = (map->type == _DRM_SHM) ? (unsigned long)map->handle : + map->offset; + ret = drm_map_handle(dev, &list->hash, user_token, 0); + if (ret) { + drm_free(map, sizeof(*map), DRM_MEM_MAPS); + drm_free(list, sizeof(*list), DRM_MEM_MAPS); + mutex_unlock(&dev->struct_mutex); + return ret; + } + + list->user_token = list->hash.key; mutex_unlock(&dev->struct_mutex); *maplist = list; @@ -346,6 +343,7 @@ int drm_rmmap_locked(drm_device_t *dev, drm_local_map_t *map) if (r_list->map == map) { list_del(list); + drm_ht_remove_key(&dev->map_hash, r_list->user_token); drm_free(list, sizeof(*list), DRM_MEM_MAPS); break; } @@ -441,8 +439,10 @@ int drm_rmmap_ioctl(struct inode *inode, struct file *filp, return -EINVAL; } - if (!map) + if (!map) { + mutex_unlock(&dev->struct_mutex); return -EINVAL; + } /* Register and framebuffer maps are permanent */ if ((map->type == _DRM_REGISTERS) || (map->type == _DRM_FRAME_BUFFER)) { diff --git a/drivers/char/drm/drm_drv.c b/drivers/char/drm/drm_drv.c index 3c0b882a8e7..b366c5b1bd1 100644 --- a/drivers/char/drm/drm_drv.c +++ b/drivers/char/drm/drm_drv.c @@ -118,7 +118,7 @@ static drm_ioctl_desc_t drm_ioctls[] = { [DRM_IOCTL_NR(DRM_IOCTL_WAIT_VBLANK)] = {drm_wait_vblank, 0}, }; -#define DRIVER_IOCTL_COUNT DRM_ARRAY_SIZE( drm_ioctls ) +#define DRIVER_IOCTL_COUNT ARRAY_SIZE( drm_ioctls ) /** * Take down the DRM device. @@ -155,12 +155,13 @@ int drm_lastclose(drm_device_t * dev) del_timer(&dev->timer); /* Clear pid list */ - for (i = 0; i < DRM_HASH_SIZE; i++) { - for (pt = dev->magiclist[i].head; pt; pt = next) { - next = pt->next; + if (dev->magicfree.next) { + list_for_each_entry_safe(pt, next, &dev->magicfree, head) { + list_del(&pt->head); + drm_ht_remove_item(&dev->magiclist, &pt->hash_item); drm_free(pt, sizeof(*pt), DRM_MEM_MAGIC); } - dev->magiclist[i].head = dev->magiclist[i].tail = NULL; + drm_ht_remove(&dev->magiclist); } /* Clear AGP information */ @@ -299,6 +300,7 @@ static void drm_cleanup(drm_device_t * dev) if (dev->maplist) { drm_free(dev->maplist, sizeof(*dev->maplist), DRM_MEM_MAPS); dev->maplist = NULL; + drm_ht_remove(&dev->map_hash); } drm_ctxbitmap_cleanup(dev); diff --git a/drivers/char/drm/drm_fops.c b/drivers/char/drm/drm_fops.c index b7f7951c458..898f47dafec 100644 --- a/drivers/char/drm/drm_fops.c +++ b/drivers/char/drm/drm_fops.c @@ -53,6 +53,8 @@ static int drm_setup(drm_device_t * dev) return ret; } + dev->magicfree.next = NULL; + /* prebuild the SAREA */ i = drm_addmap(dev, 0, SAREA_MAX, _DRM_SHM, _DRM_CONTAINS_LOCK, &map); if (i != 0) @@ -69,13 +71,11 @@ static int drm_setup(drm_device_t * dev) return i; } - for (i = 0; i < DRM_ARRAY_SIZE(dev->counts); i++) + for (i = 0; i < ARRAY_SIZE(dev->counts); i++) atomic_set(&dev->counts[i], 0); - for (i = 0; i < DRM_HASH_SIZE; i++) { - dev->magiclist[i].head = NULL; - dev->magiclist[i].tail = NULL; - } + drm_ht_create(&dev->magiclist, DRM_MAGIC_HASH_ORDER); + INIT_LIST_HEAD(&dev->magicfree); dev->ctxlist = drm_alloc(sizeof(*dev->ctxlist), DRM_MEM_CTXLIST); if (dev->ctxlist == NULL) diff --git a/drivers/char/drm/drm_hashtab.c b/drivers/char/drm/drm_hashtab.c new file mode 100644 index 00000000000..a0b2d6802ae --- /dev/null +++ b/drivers/char/drm/drm_hashtab.c @@ -0,0 +1,190 @@ +/************************************************************************** + * + * Copyright 2006 Tungsten Graphics, Inc., Bismarck, ND. USA. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * + **************************************************************************/ +/* + * Simple open hash tab implementation. + * + * Authors: + * Thomas Hellström <thomas-at-tungstengraphics-dot-com> + */ + +#include "drmP.h" +#include "drm_hashtab.h" +#include <linux/hash.h> + +int drm_ht_create(drm_open_hash_t *ht, unsigned int order) +{ + unsigned int i; + + ht->size = 1 << order; + ht->order = order; + ht->fill = 0; + ht->table = vmalloc(ht->size*sizeof(*ht->table)); + if (!ht->table) { + DRM_ERROR("Out of memory for hash table\n"); + return -ENOMEM; + } + for (i=0; i< ht->size; ++i) { + INIT_HLIST_HEAD(&ht->table[i]); + } + return 0; +} + +void drm_ht_verbose_list(drm_open_hash_t *ht, unsigned long key) +{ + drm_hash_item_t *entry; + struct hlist_head *h_list; + struct hlist_node *list; + unsigned int hashed_key; + int count = 0; + + hashed_key = hash_long(key, ht->order); + DRM_DEBUG("Key is 0x%08lx, Hashed key is 0x%08x\n", key, hashed_key); + h_list = &ht->table[hashed_key]; + hlist_for_each(list, h_list) { + entry = hlist_entry(list, drm_hash_item_t, head); + DRM_DEBUG("count %d, key: 0x%08lx\n", count++, entry->key); + } +} + +static struct hlist_node *drm_ht_find_key(drm_open_hash_t *ht, + unsigned long key) +{ + drm_hash_item_t *entry; + struct hlist_head *h_list; + struct hlist_node *list; + unsigned int hashed_key; + + hashed_key = hash_long(key, ht->order); + h_list = &ht->table[hashed_key]; + hlist_for_each(list, h_list) { + entry = hlist_entry(list, drm_hash_item_t, head); + if (entry->key == key) + return list; + if (entry->key > key) + break; + } + return NULL; +} + + +int drm_ht_insert_item(drm_open_hash_t *ht, drm_hash_item_t *item) +{ + drm_hash_item_t *entry; + struct hlist_head *h_list; + struct hlist_node *list, *parent; + unsigned int hashed_key; + unsigned long key = item->key; + + hashed_key = hash_long(key, ht->order); + h_list = &ht->table[hashed_key]; + parent = NULL; + hlist_for_each(list, h_list) { + entry = hlist_entry(list, drm_hash_item_t, head); + if (entry->key == key) + return -EINVAL; + if (entry->key > key) + break; + parent = list; + } + if (parent) { + hlist_add_after(parent, &item->head); + } else { + hlist_add_head(&item->head, h_list); + } + return 0; +} + +/* + * Just insert an item and return any "bits" bit key that hasn't been + * used before. + */ +int drm_ht_just_insert_please(drm_open_hash_t *ht, drm_hash_item_t *item, + unsigned long seed, int bits, int shift, + unsigned long add) +{ + int ret; + unsigned long mask = (1 << bits) - 1; + unsigned long first, unshifted_key; + + unshifted_key = hash_long(seed, bits); + first = unshifted_key; + do { + item->key = (unshifted_key << shift) + add; + ret = drm_ht_insert_item(ht, item); + if (ret) + unshifted_key = (unshifted_key + 1) & mask; + } while(ret && (unshifted_key != first)); + + if (ret) { + DRM_ERROR("Available key bit space exhausted\n"); + return -EINVAL; + } + return 0; +} + +int drm_ht_find_item(drm_open_hash_t *ht, unsigned long key, + drm_hash_item_t **item) +{ + struct hlist_node *list; + + list = drm_ht_find_key(ht, key); + if (!list) + return -EINVAL; + + *item = hlist_entry(list, drm_hash_item_t, head); + return 0; +} + +int drm_ht_remove_key(drm_open_hash_t *ht, unsigned long key) +{ + struct hlist_node *list; + + list = drm_ht_find_key(ht, key); + if (list) { + hlist_del_init(list); + ht->fill--; + return 0; + } + return -EINVAL; +} + +int drm_ht_remove_item(drm_open_hash_t *ht, drm_hash_item_t *item) +{ + hlist_del_init(&item->head); + ht->fill--; + return 0; +} + +void drm_ht_remove(drm_open_hash_t *ht) +{ + if (ht->table) { + vfree(ht->table); + ht->table = NULL; + } +} + diff --git a/drivers/char/drm/drm_hashtab.h b/drivers/char/drm/drm_hashtab.h new file mode 100644 index 00000000000..40afec05bff --- /dev/null +++ b/drivers/char/drm/drm_hashtab.h @@ -0,0 +1,67 @@ +/************************************************************************** + * + * Copyright 2006 Tungsten Graphics, Inc., Bismack, ND. USA. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * + **************************************************************************/ +/* + * Simple open hash tab implementation. + * + * Authors: + * Thomas Hellström <thomas-at-tungstengraphics-dot-com> + */ + +#ifndef DRM_HASHTAB_H +#define DRM_HASHTAB_H + +#define drm_hash_entry(_ptr, _type, _member) container_of(_ptr, _type, _member) + +typedef struct drm_hash_item{ + struct hlist_node head; + unsigned long key; +} drm_hash_item_t; + +typedef struct drm_open_hash{ + unsigned int size; + unsigned int order; + unsigned int fill; + struct hlist_head *table; +} drm_open_hash_t; + + +extern int drm_ht_create(drm_open_hash_t *ht, unsigned int order); +extern int drm_ht_insert_item(drm_open_hash_t *ht, drm_hash_item_t *item); +extern int drm_ht_just_insert_please(drm_open_hash_t *ht, drm_hash_item_t *item, + unsigned long seed, int bits, int shift, + unsigned long add); +extern int drm_ht_find_item(drm_open_hash_t *ht, unsigned long key, drm_hash_item_t **item); + +extern void drm_ht_verbose_list(drm_open_hash_t *ht, unsigned long key); +extern int drm_ht_remove_key(drm_open_hash_t *ht, unsigned long key); +extern int drm_ht_remove_item(drm_open_hash_t *ht, drm_hash_item_t *item); +extern void drm_ht_remove(drm_open_hash_t *ht); + + +#endif + diff --git a/drivers/char/drm/drm_ioc32.c b/drivers/char/drm/drm_ioc32.c index e9e2db18952..d4f87452008 100644 --- a/drivers/char/drm/drm_ioc32.c +++ b/drivers/char/drm/drm_ioc32.c @@ -1051,7 +1051,7 @@ long drm_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) drm_ioctl_compat_t *fn; int ret; - if (nr >= DRM_ARRAY_SIZE(drm_compat_ioctls)) + if (nr >= ARRAY_SIZE(drm_compat_ioctls)) return -ENOTTY; fn = drm_compat_ioctls[nr]; diff --git a/drivers/char/drm/drm_ioctl.c b/drivers/char/drm/drm_ioctl.c index 555f323b8a3..565895547d7 100644 --- a/drivers/char/drm/drm_ioctl.c +++ b/drivers/char/drm/drm_ioctl.c @@ -127,9 +127,10 @@ int drm_setunique(struct inode *inode, struct file *filp, domain = bus >> 8; bus &= 0xff; - if ((domain != dev->pci_domain) || - (bus != dev->pci_bus) || - (slot != dev->pci_slot) || (func != dev->pci_func)) + if ((domain != drm_get_pci_domain(dev)) || + (bus != dev->pdev->bus->number) || + (slot != PCI_SLOT(dev->pdev->devfn)) || + (func != PCI_FUNC(dev->pdev->devfn))) return -EINVAL; return 0; @@ -140,15 +141,17 @@ static int drm_set_busid(drm_device_t * dev) int len; if (dev->unique != NULL) - return EBUSY; + return 0; dev->unique_len = 40; dev->unique = drm_alloc(dev->unique_len + 1, DRM_MEM_DRIVER); if (dev->unique == NULL) - return ENOMEM; + return -ENOMEM; len = snprintf(dev->unique, dev->unique_len, "pci:%04x:%02x:%02x.%d", - dev->pci_domain, dev->pci_bus, dev->pci_slot, dev->pci_func); + drm_get_pci_domain(dev), dev->pdev->bus->number, + PCI_SLOT(dev->pdev->devfn), + PCI_FUNC(dev->pdev->devfn)); if (len > dev->unique_len) DRM_ERROR("Unique buffer overflowed\n"); @@ -157,7 +160,7 @@ static int drm_set_busid(drm_device_t * dev) drm_alloc(strlen(dev->driver->pci_driver.name) + dev->unique_len + 2, DRM_MEM_DRIVER); if (dev->devname == NULL) - return ENOMEM; + return -ENOMEM; sprintf(dev->devname, "%s@%s", dev->driver->pci_driver.name, dev->unique); @@ -330,27 +333,32 @@ int drm_setversion(DRM_IOCTL_ARGS) drm_set_version_t retv; int if_version; drm_set_version_t __user *argp = (void __user *)data; + int ret; - DRM_COPY_FROM_USER_IOCTL(sv, argp, sizeof(sv)); + if (copy_from_user(&sv, argp, sizeof(sv))) + return -EFAULT; retv.drm_di_major = DRM_IF_MAJOR; retv.drm_di_minor = DRM_IF_MINOR; retv.drm_dd_major = dev->driver->major; retv.drm_dd_minor = dev->driver->minor; - DRM_COPY_TO_USER_IOCTL(argp, retv, sizeof(sv)); + if (copy_to_user(argp, &retv, sizeof(retv))) + return -EFAULT; if (sv.drm_di_major != -1) { if (sv.drm_di_major != DRM_IF_MAJOR || sv.drm_di_minor < 0 || sv.drm_di_minor > DRM_IF_MINOR) - return EINVAL; + return -EINVAL; if_version = DRM_IF_VERSION(sv.drm_di_major, sv.drm_di_minor); - dev->if_version = DRM_MAX(if_version, dev->if_version); + dev->if_version = max(if_version, dev->if_version); if (sv.drm_di_minor >= 1) { /* * Version 1.1 includes tying of DRM to specific device */ - drm_set_busid(dev); + ret = drm_set_busid(dev); + if (ret) + return ret; } } @@ -358,7 +366,7 @@ int drm_setversion(DRM_IOCTL_ARGS) if (sv.drm_dd_major != dev->driver->major || sv.drm_dd_minor < 0 || sv.drm_dd_minor > dev->driver->minor) - return EINVAL; + return -EINVAL; if (dev->driver->set_version) dev->driver->set_version(dev, &sv); diff --git a/drivers/char/drm/drm_irq.c b/drivers/char/drm/drm_irq.c index ebdb7182c4f..4553a3a1e49 100644 --- a/drivers/char/drm/drm_irq.c +++ b/drivers/char/drm/drm_irq.c @@ -64,9 +64,9 @@ int drm_irq_by_busid(struct inode *inode, struct file *filp, if (copy_from_user(&p, argp, sizeof(p))) return -EFAULT; - if ((p.busnum >> 8) != dev->pci_domain || - (p.busnum & 0xff) != dev->pci_bus || - p.devnum != dev->pci_slot || p.funcnum != dev->pci_func) + if ((p.busnum >> 8) != drm_get_pci_domain(dev) || + (p.busnum & 0xff) != dev->pdev->bus->number || + p.devnum != PCI_SLOT(dev->pdev->devfn) || p.funcnum != PCI_FUNC(dev->pdev->devfn)) return -EINVAL; p.irq = dev->irq; @@ -255,7 +255,8 @@ int drm_wait_vblank(DRM_IOCTL_ARGS) if (!dev->irq) return -EINVAL; - DRM_COPY_FROM_USER_IOCTL(vblwait, argp, sizeof(vblwait)); + if (copy_from_user(&vblwait, argp, sizeof(vblwait))) + return -EFAULT; switch (vblwait.request.type & ~_DRM_VBLANK_FLAGS_MASK) { case _DRM_VBLANK_RELATIVE: @@ -329,7 +330,8 @@ int drm_wait_vblank(DRM_IOCTL_ARGS) } done: - DRM_COPY_TO_USER_IOCTL(argp, vblwait, sizeof(vblwait)); + if (copy_to_user(argp, &vblwait, sizeof(vblwait))) + return -EFAULT; return ret; } diff --git a/drivers/char/drm/drm_mm.c b/drivers/char/drm/drm_mm.c new file mode 100644 index 00000000000..617526bd5b0 --- /dev/null +++ b/drivers/char/drm/drm_mm.c @@ -0,0 +1,201 @@ +/************************************************************************** + * + * Copyright 2006 Tungsten Graphics, Inc., Bismarck, ND., USA. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * + **************************************************************************/ + +/* + * Generic simple memory manager implementation. Intended to be used as a base + * class implementation for more advanced memory managers. + * + * Note that the algorithm used is quite simple and there might be substantial + * performance gains if a smarter free list is implemented. Currently it is just an + * unordered stack of free regions. This could easily be improved if an RB-tree + * is used instead. At least if we expect heavy fragmentation. + * + * Aligned allocations can also see improvement. + * + * Authors: + * Thomas Hellström <thomas-at-tungstengraphics-dot-com> + */ + +#include "drmP.h" + +drm_mm_node_t *drm_mm_get_block(drm_mm_node_t * parent, + unsigned long size, unsigned alignment) +{ + + drm_mm_node_t *child; + + if (alignment) + size += alignment - 1; + + if (parent->size == size) { + list_del_init(&parent->fl_entry); + parent->free = 0; + return parent; + } else { + child = (drm_mm_node_t *) drm_alloc(sizeof(*child), DRM_MEM_MM); + if (!child) + return NULL; + + INIT_LIST_HEAD(&child->ml_entry); + INIT_LIST_HEAD(&child->fl_entry); + + child->free = 0; + child->size = size; + child->start = parent->start; + + list_add_tail(&child->ml_entry, &parent->ml_entry); + parent->size -= size; + parent->start += size; + } + return child; +} + +/* + * Put a block. Merge with the previous and / or next block if they are free. + * Otherwise add to the free stack. + */ + +void drm_mm_put_block(drm_mm_t * mm, drm_mm_node_t * cur) +{ + + drm_mm_node_t *list_root = &mm->root_node; + struct list_head *cur_head = &cur->ml_entry; + struct list_head *root_head = &list_root->ml_entry; + drm_mm_node_t *prev_node = NULL; + drm_mm_node_t *next_node; + + int merged = 0; + + if (cur_head->prev != root_head) { + prev_node = list_entry(cur_head->prev, drm_mm_node_t, ml_entry); + if (prev_node->free) { + prev_node->size += cur->size; + merged = 1; + } + } + if (cur_head->next != root_head) { + next_node = list_entry(cur_head->next, drm_mm_node_t, ml_entry); + if (next_node->free) { + if (merged) { + prev_node->size += next_node->size; + list_del(&next_node->ml_entry); + list_del(&next_node->fl_entry); + drm_free(next_node, sizeof(*next_node), + DRM_MEM_MM); + } else { + next_node->size += cur->size; + next_node->start = cur->start; + merged = 1; + } + } + } + if (!merged) { + cur->free = 1; + list_add(&cur->fl_entry, &list_root->fl_entry); + } else { + list_del(&cur->ml_entry); + drm_free(cur, sizeof(*cur), DRM_MEM_MM); + } +} + +drm_mm_node_t *drm_mm_search_free(const drm_mm_t * mm, + unsigned long size, + unsigned alignment, int best_match) +{ + struct list_head *list; + const struct list_head *free_stack = &mm->root_node.fl_entry; + drm_mm_node_t *entry; + drm_mm_node_t *best; + unsigned long best_size; + + best = NULL; + best_size = ~0UL; + + if (alignment) + size += alignment - 1; + + list_for_each(list, free_stack) { + entry = list_entry(list, drm_mm_node_t, fl_entry); + if (entry->size >= size) { + if (!best_match) + return entry; + if (size < best_size) { + best = entry; + best_size = entry->size; + } + } + } + + return best; +} + +int drm_mm_init(drm_mm_t * mm, unsigned long start, unsigned long size) +{ + drm_mm_node_t *child; + + INIT_LIST_HEAD(&mm->root_node.ml_entry); + INIT_LIST_HEAD(&mm->root_node.fl_entry); + child = (drm_mm_node_t *) drm_alloc(sizeof(*child), DRM_MEM_MM); + if (!child) + return -ENOMEM; + + INIT_LIST_HEAD(&child->ml_entry); + INIT_LIST_HEAD(&child->fl_entry); + + child->start = start; + child->size = size; + child->free = 1; + + list_add(&child->fl_entry, &mm->root_node.fl_entry); + list_add(&child->ml_entry, &mm->root_node.ml_entry); + + return 0; +} + +EXPORT_SYMBOL(drm_mm_init); + +void drm_mm_takedown(drm_mm_t * mm) +{ + struct list_head *bnode = mm->root_node.fl_entry.next; + drm_mm_node_t *entry; + + entry = list_entry(bnode, drm_mm_node_t, fl_entry); + + if (entry->ml_entry.next != &mm->root_node.ml_entry || + entry->fl_entry.next != &mm->root_node.fl_entry) { + DRM_ERROR("Memory manager not clean. Delaying takedown\n"); + return; + } + + list_del(&entry->fl_entry); + list_del(&entry->ml_entry); + + drm_free(entry, sizeof(*entry), DRM_MEM_MM); +} + +EXPORT_SYMBOL(drm_mm_takedown); diff --git a/drivers/char/drm/drm_pciids.h b/drivers/char/drm/drm_pciids.h index b1bb3c7b568..09398d5fbd3 100644 --- a/drivers/char/drm/drm_pciids.h +++ b/drivers/char/drm/drm_pciids.h @@ -3,13 +3,13 @@ Please contact dri-devel@lists.sf.net to add new cards to this list */ #define radeon_PCI_IDS \ - {0x1002, 0x3150, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|CHIP_IS_MOBILITY}, \ - {0x1002, 0x3152, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|CHIP_IS_MOBILITY|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x3154, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|CHIP_IS_MOBILITY|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x3E50, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x3E54, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x4136, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS100|CHIP_IS_IGP}, \ - {0x1002, 0x4137, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS200|CHIP_IS_IGP}, \ + {0x1002, 0x3150, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|RADEON_IS_MOBILITY}, \ + {0x1002, 0x3152, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x3154, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x3E50, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x3E54, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x4136, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS100|RADEON_IS_IGP}, \ + {0x1002, 0x4137, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS200|RADEON_IS_IGP}, \ {0x1002, 0x4144, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R300}, \ {0x1002, 0x4145, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R300}, \ {0x1002, 0x4146, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R300}, \ @@ -25,35 +25,35 @@ {0x1002, 0x4154, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350}, \ {0x1002, 0x4155, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350}, \ {0x1002, 0x4156, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350}, \ - {0x1002, 0x4237, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS200|CHIP_IS_IGP}, \ + {0x1002, 0x4237, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS200|RADEON_IS_IGP}, \ {0x1002, 0x4242, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R200}, \ {0x1002, 0x4243, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R200}, \ - {0x1002, 0x4336, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS100|CHIP_IS_IGP|CHIP_IS_MOBILITY}, \ - {0x1002, 0x4337, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS200|CHIP_IS_IGP|CHIP_IS_MOBILITY}, \ - {0x1002, 0x4437, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS200|CHIP_IS_IGP|CHIP_IS_MOBILITY}, \ + {0x1002, 0x4336, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS100|RADEON_IS_IGP|RADEON_IS_MOBILITY}, \ + {0x1002, 0x4337, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS200|RADEON_IS_IGP|RADEON_IS_MOBILITY}, \ + {0x1002, 0x4437, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS200|RADEON_IS_IGP|RADEON_IS_MOBILITY}, \ {0x1002, 0x4966, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV250}, \ {0x1002, 0x4967, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV250}, \ - {0x1002, 0x4A48, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x4A49, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x4A4A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x4A4B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x4A4C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x4A4D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x4A4E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_IS_MOBILITY|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x4A4F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x4A50, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x4A54, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x4B49, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x4B4A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x4B4B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x4B4C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x4C57, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV200|CHIP_IS_MOBILITY}, \ - {0x1002, 0x4C58, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV200|CHIP_IS_MOBILITY}, \ - {0x1002, 0x4C59, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV100|CHIP_IS_MOBILITY}, \ - {0x1002, 0x4C5A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV100|CHIP_IS_MOBILITY}, \ - {0x1002, 0x4C64, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV250|CHIP_IS_MOBILITY}, \ - {0x1002, 0x4C66, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV250|CHIP_IS_MOBILITY}, \ - {0x1002, 0x4C67, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV250|CHIP_IS_MOBILITY}, \ + {0x1002, 0x4A48, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x4A49, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x4A4A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x4A4B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x4A4C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x4A4D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x4A4E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x4A4F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x4A50, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x4A54, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x4B49, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x4B4A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x4B4B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x4B4C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x4C57, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV200|RADEON_IS_MOBILITY}, \ + {0x1002, 0x4C58, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV200|RADEON_IS_MOBILITY}, \ + {0x1002, 0x4C59, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV100|RADEON_IS_MOBILITY}, \ + {0x1002, 0x4C5A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV100|RADEON_IS_MOBILITY}, \ + {0x1002, 0x4C64, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV250|RADEON_IS_MOBILITY}, \ + {0x1002, 0x4C66, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV250|RADEON_IS_MOBILITY}, \ + {0x1002, 0x4C67, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV250|RADEON_IS_MOBILITY}, \ {0x1002, 0x4E44, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R300}, \ {0x1002, 0x4E45, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R300}, \ {0x1002, 0x4E46, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R300}, \ @@ -62,16 +62,16 @@ {0x1002, 0x4E49, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R350}, \ {0x1002, 0x4E4A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R350}, \ {0x1002, 0x4E4B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R350}, \ - {0x1002, 0x4E50, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350|CHIP_IS_MOBILITY}, \ - {0x1002, 0x4E51, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350|CHIP_IS_MOBILITY}, \ - {0x1002, 0x4E52, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350|CHIP_IS_MOBILITY}, \ - {0x1002, 0x4E53, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350|CHIP_IS_MOBILITY}, \ - {0x1002, 0x4E54, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350|CHIP_IS_MOBILITY}, \ - {0x1002, 0x4E56, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350|CHIP_IS_MOBILITY}, \ - {0x1002, 0x5144, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R100|CHIP_SINGLE_CRTC}, \ - {0x1002, 0x5145, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R100|CHIP_SINGLE_CRTC}, \ - {0x1002, 0x5146, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R100|CHIP_SINGLE_CRTC}, \ - {0x1002, 0x5147, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R100|CHIP_SINGLE_CRTC}, \ + {0x1002, 0x4E50, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350|RADEON_IS_MOBILITY}, \ + {0x1002, 0x4E51, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350|RADEON_IS_MOBILITY}, \ + {0x1002, 0x4E52, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350|RADEON_IS_MOBILITY}, \ + {0x1002, 0x4E53, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350|RADEON_IS_MOBILITY}, \ + {0x1002, 0x4E54, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350|RADEON_IS_MOBILITY}, \ + {0x1002, 0x4E56, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350|RADEON_IS_MOBILITY}, \ + {0x1002, 0x5144, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R100|RADEON_SINGLE_CRTC}, \ + {0x1002, 0x5145, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R100|RADEON_SINGLE_CRTC}, \ + {0x1002, 0x5146, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R100|RADEON_SINGLE_CRTC}, \ + {0x1002, 0x5147, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R100|RADEON_SINGLE_CRTC}, \ {0x1002, 0x5148, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R200}, \ {0x1002, 0x514C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R200}, \ {0x1002, 0x514D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R200}, \ @@ -80,59 +80,59 @@ {0x1002, 0x5159, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV100}, \ {0x1002, 0x515A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV100}, \ {0x1002, 0x515E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV100}, \ - {0x1002, 0x5460, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|CHIP_IS_MOBILITY}, \ - {0x1002, 0x5462, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|CHIP_IS_MOBILITY}, \ - {0x1002, 0x5464, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|CHIP_IS_MOBILITY}, \ - {0x1002, 0x5548, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x5549, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x554A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x554B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x554C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x554D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x554E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x554F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x5550, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x5551, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x5552, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x5554, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x564A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|CHIP_IS_MOBILITY|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x564B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|CHIP_IS_MOBILITY|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x564F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|CHIP_IS_MOBILITY|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x5652, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|CHIP_IS_MOBILITY|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x5653, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|CHIP_IS_MOBILITY|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x5834, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS300|CHIP_IS_IGP}, \ - {0x1002, 0x5835, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS300|CHIP_IS_IGP|CHIP_IS_MOBILITY}, \ + {0x1002, 0x5460, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|RADEON_IS_MOBILITY}, \ + {0x1002, 0x5462, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|RADEON_IS_MOBILITY}, \ + {0x1002, 0x5464, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|RADEON_IS_MOBILITY}, \ + {0x1002, 0x5548, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x5549, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x554A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x554B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x554C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x554D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x554E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x554F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x5550, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x5551, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x5552, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x5554, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x564A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x564B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x564F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x5652, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x5653, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x5834, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS300|RADEON_IS_IGP}, \ + {0x1002, 0x5835, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS300|RADEON_IS_IGP|RADEON_IS_MOBILITY}, \ {0x1002, 0x5960, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV280}, \ {0x1002, 0x5961, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV280}, \ {0x1002, 0x5962, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV280}, \ {0x1002, 0x5964, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV280}, \ {0x1002, 0x5965, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV280}, \ {0x1002, 0x5969, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV100}, \ - {0x1002, 0x5b60, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x5b62, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x5b63, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x5b64, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x5b65, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x5c61, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV280|CHIP_IS_MOBILITY}, \ - {0x1002, 0x5c63, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV280|CHIP_IS_MOBILITY}, \ - {0x1002, 0x5d48, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_IS_MOBILITY|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x5d49, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_IS_MOBILITY|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x5d4a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_IS_MOBILITY|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x5d4c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x5d4d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x5d4e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x5d4f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x5d50, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x5d52, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x5d57, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x5e48, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x5e4a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x5e4b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x5e4c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x5e4d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x5e4f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x7834, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS300|CHIP_IS_IGP|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x7835, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS300|CHIP_IS_IGP|CHIP_IS_MOBILITY|CHIP_NEW_MEMMAP}, \ + {0x1002, 0x5b60, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x5b62, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x5b63, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x5b64, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x5b65, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x5c61, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV280|RADEON_IS_MOBILITY}, \ + {0x1002, 0x5c63, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV280|RADEON_IS_MOBILITY}, \ + {0x1002, 0x5d48, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x5d49, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x5d4a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x5d4c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x5d4d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x5d4e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x5d4f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x5d50, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x5d52, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x5d57, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x5e48, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x5e4a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x5e4b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x5e4c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x5e4d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x5e4f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x7834, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS300|RADEON_IS_IGP|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x7835, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS300|RADEON_IS_IGP|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \ {0, 0, 0} #define r128_PCI_IDS \ @@ -209,6 +209,7 @@ {0x1039, 0x0300, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ {0x1039, 0x5300, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ {0x1039, 0x6300, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1039, 0x6330, PCI_ANY_ID, PCI_ANY_ID, 0, 0, SIS_CHIP_315}, \ {0x1039, 0x7300, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ {0, 0, 0} @@ -227,6 +228,10 @@ {0x1106, 0x3122, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ {0x1106, 0x7205, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ {0x1106, 0x3108, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1106, 0x3304, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1106, 0x3157, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1106, 0x3344, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1106, 0x7204, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ {0, 0, 0} #define i810_PCI_IDS \ @@ -285,5 +290,9 @@ {0x8086, 0x2592, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ {0x8086, 0x2772, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ {0x8086, 0x27a2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x8086, 0x2972, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x8086, 0x2982, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x8086, 0x2992, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x8086, 0x29a2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ {0, 0, 0} diff --git a/drivers/char/drm/drm_proc.c b/drivers/char/drm/drm_proc.c index 362a270af0f..62d5fe15f04 100644 --- a/drivers/char/drm/drm_proc.c +++ b/drivers/char/drm/drm_proc.c @@ -510,7 +510,7 @@ static int drm__vma_info(char *buf, char **start, off_t offset, int request, vma->vm_flags & VM_MAYSHARE ? 's' : 'p', vma->vm_flags & VM_LOCKED ? 'l' : '-', vma->vm_flags & VM_IO ? 'i' : '-', - VM_OFFSET(vma)); + vma->vm_pgoff << PAGE_SHIFT); #if defined(__i386__) pgprot = pgprot_val(vma->vm_page_prot); diff --git a/drivers/char/drm/drm_sman.c b/drivers/char/drm/drm_sman.c new file mode 100644 index 00000000000..425c82336ee --- /dev/null +++ b/drivers/char/drm/drm_sman.c @@ -0,0 +1,352 @@ +/************************************************************************** + * + * Copyright 2006 Tungsten Graphics, Inc., Bismarck., ND., USA. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * + **************************************************************************/ +/* + * Simple memory manager interface that keeps track on allocate regions on a + * per "owner" basis. All regions associated with an "owner" can be released + * with a simple call. Typically if the "owner" exists. The owner is any + * "unsigned long" identifier. Can typically be a pointer to a file private + * struct or a context identifier. + * + * Authors: + * Thomas Hellström <thomas-at-tungstengraphics-dot-com> + */ + +#include "drm_sman.h" + +typedef struct drm_owner_item { + drm_hash_item_t owner_hash; + struct list_head sman_list; + struct list_head mem_blocks; +} drm_owner_item_t; + +void drm_sman_takedown(drm_sman_t * sman) +{ + drm_ht_remove(&sman->user_hash_tab); + drm_ht_remove(&sman->owner_hash_tab); + if (sman->mm) + drm_free(sman->mm, sman->num_managers * sizeof(*sman->mm), + DRM_MEM_MM); +} + +EXPORT_SYMBOL(drm_sman_takedown); + +int +drm_sman_init(drm_sman_t * sman, unsigned int num_managers, + unsigned int user_order, unsigned int owner_order) +{ + int ret = 0; + + sman->mm = (drm_sman_mm_t *) drm_calloc(num_managers, sizeof(*sman->mm), + DRM_MEM_MM); + if (!sman->mm) { + ret = -ENOMEM; + goto out; + } + sman->num_managers = num_managers; + INIT_LIST_HEAD(&sman->owner_items); + ret = drm_ht_create(&sman->owner_hash_tab, owner_order); + if (ret) + goto out1; + ret = drm_ht_create(&sman->user_hash_tab, user_order); + if (!ret) + goto out; + + drm_ht_remove(&sman->owner_hash_tab); +out1: + drm_free(sman->mm, num_managers * sizeof(*sman->mm), DRM_MEM_MM); +out: + return ret; +} + +EXPORT_SYMBOL(drm_sman_init); + +static void *drm_sman_mm_allocate(void *private, unsigned long size, + unsigned alignment) +{ + drm_mm_t *mm = (drm_mm_t *) private; + drm_mm_node_t *tmp; + + tmp = drm_mm_search_free(mm, size, alignment, 1); + if (!tmp) { + return NULL; + } + tmp = drm_mm_get_block(tmp, size, alignment); + return tmp; +} + +static void drm_sman_mm_free(void *private, void *ref) +{ + drm_mm_t *mm = (drm_mm_t *) private; + drm_mm_node_t *node = (drm_mm_node_t *) ref; + + drm_mm_put_block(mm, node); +} + +static void drm_sman_mm_destroy(void *private) +{ + drm_mm_t *mm = (drm_mm_t *) private; + drm_mm_takedown(mm); + drm_free(mm, sizeof(*mm), DRM_MEM_MM); +} + +static unsigned long drm_sman_mm_offset(void *private, void *ref) +{ + drm_mm_node_t *node = (drm_mm_node_t *) ref; + return node->start; +} + +int +drm_sman_set_range(drm_sman_t * sman, unsigned int manager, + unsigned long start, unsigned long size) +{ + drm_sman_mm_t *sman_mm; + drm_mm_t *mm; + int ret; + + BUG_ON(manager >= sman->num_managers); + + sman_mm = &sman->mm[manager]; + mm = drm_calloc(1, sizeof(*mm), DRM_MEM_MM); + if (!mm) { + return -ENOMEM; + } + sman_mm->private = mm; + ret = drm_mm_init(mm, start, size); + + if (ret) { + drm_free(mm, sizeof(*mm), DRM_MEM_MM); + return ret; + } + + sman_mm->allocate = drm_sman_mm_allocate; + sman_mm->free = drm_sman_mm_free; + sman_mm->destroy = drm_sman_mm_destroy; + sman_mm->offset = drm_sman_mm_offset; + + return 0; +} + +EXPORT_SYMBOL(drm_sman_set_range); + +int +drm_sman_set_manager(drm_sman_t * sman, unsigned int manager, + drm_sman_mm_t * allocator) +{ + BUG_ON(manager >= sman->num_managers); + sman->mm[manager] = *allocator; + + return 0; +} + +static drm_owner_item_t *drm_sman_get_owner_item(drm_sman_t * sman, + unsigned long owner) +{ + int ret; + drm_hash_item_t *owner_hash_item; + drm_owner_item_t *owner_item; + + ret = drm_ht_find_item(&sman->owner_hash_tab, owner, &owner_hash_item); + if (!ret) { + return drm_hash_entry(owner_hash_item, drm_owner_item_t, + owner_hash); + } + + owner_item = drm_calloc(1, sizeof(*owner_item), DRM_MEM_MM); + if (!owner_item) + goto out; + + INIT_LIST_HEAD(&owner_item->mem_blocks); + owner_item->owner_hash.key = owner; + if (drm_ht_insert_item(&sman->owner_hash_tab, &owner_item->owner_hash)) + goto out1; + + list_add_tail(&owner_item->sman_list, &sman->owner_items); + return owner_item; + +out1: + drm_free(owner_item, sizeof(*owner_item), DRM_MEM_MM); +out: + return NULL; +} + +drm_memblock_item_t *drm_sman_alloc(drm_sman_t *sman, unsigned int manager, + unsigned long size, unsigned alignment, + unsigned long owner) +{ + void *tmp; + drm_sman_mm_t *sman_mm; + drm_owner_item_t *owner_item; + drm_memblock_item_t *memblock; + + BUG_ON(manager >= sman->num_managers); + + sman_mm = &sman->mm[manager]; + tmp = sman_mm->allocate(sman_mm->private, size, alignment); + + if (!tmp) { + return NULL; + } + + memblock = drm_calloc(1, sizeof(*memblock), DRM_MEM_MM); + + if (!memblock) + goto out; + + memblock->mm_info = tmp; + memblock->mm = sman_mm; + memblock->sman = sman; + + if (drm_ht_just_insert_please + (&sman->user_hash_tab, &memblock->user_hash, + (unsigned long)memblock, 32, 0, 0)) + goto out1; + + owner_item = drm_sman_get_owner_item(sman, owner); + if (!owner_item) + goto out2; + + list_add_tail(&memblock->owner_list, &owner_item->mem_blocks); + + return memblock; + +out2: + drm_ht_remove_item(&sman->user_hash_tab, &memblock->user_hash); +out1: + drm_free(memblock, sizeof(*memblock), DRM_MEM_MM); +out: + sman_mm->free(sman_mm->private, tmp); + + return NULL; +} + +EXPORT_SYMBOL(drm_sman_alloc); + +static void drm_sman_free(drm_memblock_item_t *item) +{ + drm_sman_t *sman = item->sman; + + list_del(&item->owner_list); + drm_ht_remove_item(&sman->user_hash_tab, &item->user_hash); + item->mm->free(item->mm->private, item->mm_info); + drm_free(item, sizeof(*item), DRM_MEM_MM); +} + +int drm_sman_free_key(drm_sman_t *sman, unsigned int key) +{ + drm_hash_item_t *hash_item; + drm_memblock_item_t *memblock_item; + + if (drm_ht_find_item(&sman->user_hash_tab, key, &hash_item)) + return -EINVAL; + + memblock_item = drm_hash_entry(hash_item, drm_memblock_item_t, user_hash); + drm_sman_free(memblock_item); + return 0; +} + +EXPORT_SYMBOL(drm_sman_free_key); + +static void drm_sman_remove_owner(drm_sman_t *sman, + drm_owner_item_t *owner_item) +{ + list_del(&owner_item->sman_list); + drm_ht_remove_item(&sman->owner_hash_tab, &owner_item->owner_hash); + drm_free(owner_item, sizeof(*owner_item), DRM_MEM_MM); +} + +int drm_sman_owner_clean(drm_sman_t *sman, unsigned long owner) +{ + + drm_hash_item_t *hash_item; + drm_owner_item_t *owner_item; + + if (drm_ht_find_item(&sman->owner_hash_tab, owner, &hash_item)) { + return -1; + } + + owner_item = drm_hash_entry(hash_item, drm_owner_item_t, owner_hash); + if (owner_item->mem_blocks.next == &owner_item->mem_blocks) { + drm_sman_remove_owner(sman, owner_item); + return -1; + } + + return 0; +} + +EXPORT_SYMBOL(drm_sman_owner_clean); + +static void drm_sman_do_owner_cleanup(drm_sman_t *sman, + drm_owner_item_t *owner_item) +{ + drm_memblock_item_t *entry, *next; + + list_for_each_entry_safe(entry, next, &owner_item->mem_blocks, + owner_list) { + drm_sman_free(entry); + } + drm_sman_remove_owner(sman, owner_item); +} + +void drm_sman_owner_cleanup(drm_sman_t *sman, unsigned long owner) +{ + + drm_hash_item_t *hash_item; + drm_owner_item_t *owner_item; + + if (drm_ht_find_item(&sman->owner_hash_tab, owner, &hash_item)) { + + return; + } + + owner_item = drm_hash_entry(hash_item, drm_owner_item_t, owner_hash); + drm_sman_do_owner_cleanup(sman, owner_item); +} + +EXPORT_SYMBOL(drm_sman_owner_cleanup); + +void drm_sman_cleanup(drm_sman_t *sman) +{ + drm_owner_item_t *entry, *next; + unsigned int i; + drm_sman_mm_t *sman_mm; + + list_for_each_entry_safe(entry, next, &sman->owner_items, sman_list) { + drm_sman_do_owner_cleanup(sman, entry); + } + if (sman->mm) { + for (i = 0; i < sman->num_managers; ++i) { + sman_mm = &sman->mm[i]; + if (sman_mm->private) { + sman_mm->destroy(sman_mm->private); + sman_mm->private = NULL; + } + } + } +} + +EXPORT_SYMBOL(drm_sman_cleanup); diff --git a/drivers/char/drm/drm_sman.h b/drivers/char/drm/drm_sman.h new file mode 100644 index 00000000000..ddc732a1bf2 --- /dev/null +++ b/drivers/char/drm/drm_sman.h @@ -0,0 +1,176 @@ +/************************************************************************** + * + * Copyright 2006 Tungsten Graphics, Inc., Bismarck, ND., USA. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * + **************************************************************************/ +/* + * Simple memory MANager interface that keeps track on allocate regions on a + * per "owner" basis. All regions associated with an "owner" can be released + * with a simple call. Typically if the "owner" exists. The owner is any + * "unsigned long" identifier. Can typically be a pointer to a file private + * struct or a context identifier. + * + * Authors: + * Thomas Hellström <thomas-at-tungstengraphics-dot-com> + */ + +#ifndef DRM_SMAN_H +#define DRM_SMAN_H + +#include "drmP.h" +#include "drm_hashtab.h" + +/* + * A class that is an abstration of a simple memory allocator. + * The sman implementation provides a default such allocator + * using the drm_mm.c implementation. But the user can replace it. + * See the SiS implementation, which may use the SiS FB kernel module + * for memory management. + */ + +typedef struct drm_sman_mm { + /* private info. If allocated, needs to be destroyed by the destroy + function */ + void *private; + + /* Allocate a memory block with given size and alignment. + Return an opaque reference to the memory block */ + + void *(*allocate) (void *private, unsigned long size, + unsigned alignment); + + /* Free a memory block. "ref" is the opaque reference that we got from + the "alloc" function */ + + void (*free) (void *private, void *ref); + + /* Free all resources associated with this allocator */ + + void (*destroy) (void *private); + + /* Return a memory offset from the opaque reference returned from the + "alloc" function */ + + unsigned long (*offset) (void *private, void *ref); +} drm_sman_mm_t; + +typedef struct drm_memblock_item { + struct list_head owner_list; + drm_hash_item_t user_hash; + void *mm_info; + drm_sman_mm_t *mm; + struct drm_sman *sman; +} drm_memblock_item_t; + +typedef struct drm_sman { + drm_sman_mm_t *mm; + int num_managers; + drm_open_hash_t owner_hash_tab; + drm_open_hash_t user_hash_tab; + struct list_head owner_items; +} drm_sman_t; + +/* + * Take down a memory manager. This function should only be called after a + * successful init and after a call to drm_sman_cleanup. + */ + +extern void drm_sman_takedown(drm_sman_t * sman); + +/* + * Allocate structures for a manager. + * num_managers are the number of memory pools to manage. (VRAM, AGP, ....) + * user_order is the log2 of the number of buckets in the user hash table. + * set this to approximately log2 of the max number of memory regions + * that will be allocated for _all_ pools together. + * owner_order is the log2 of the number of buckets in the owner hash table. + * set this to approximately log2 of + * the number of client file connections that will + * be using the manager. + * + */ + +extern int drm_sman_init(drm_sman_t * sman, unsigned int num_managers, + unsigned int user_order, unsigned int owner_order); + +/* + * Initialize a drm_mm.c allocator. Should be called only once for each + * manager unless a customized allogator is used. + */ + +extern int drm_sman_set_range(drm_sman_t * sman, unsigned int manager, + unsigned long start, unsigned long size); + +/* + * Initialize a customized allocator for one of the managers. + * (See the SiS module). The object pointed to by "allocator" is copied, + * so it can be destroyed after this call. + */ + +extern int drm_sman_set_manager(drm_sman_t * sman, unsigned int mananger, + drm_sman_mm_t * allocator); + +/* + * Allocate a memory block. Aligment is not implemented yet. + */ + +extern drm_memblock_item_t *drm_sman_alloc(drm_sman_t * sman, + unsigned int manager, + unsigned long size, + unsigned alignment, + unsigned long owner); +/* + * Free a memory block identified by its user hash key. + */ + +extern int drm_sman_free_key(drm_sman_t * sman, unsigned int key); + +/* + * returns 1 iff there are no stale memory blocks associated with this owner. + * Typically called to determine if we need to idle the hardware and call + * drm_sman_owner_cleanup. If there are no stale memory blocks, it removes all + * resources associated with owner. + */ + +extern int drm_sman_owner_clean(drm_sman_t * sman, unsigned long owner); + +/* + * Frees all stale memory blocks associated with this owner. Note that this + * requires that the hardware is finished with all blocks, so the graphics engine + * should be idled before this call is made. This function also frees + * any resources associated with "owner" and should be called when owner + * is not going to be referenced anymore. + */ + +extern void drm_sman_owner_cleanup(drm_sman_t * sman, unsigned long owner); + +/* + * Frees all stale memory blocks associated with the memory manager. + * See idling above. + */ + +extern void drm_sman_cleanup(drm_sman_t * sman); + +#endif diff --git a/drivers/char/drm/drm_stub.c b/drivers/char/drm/drm_stub.c index 9a842a36bb2..7b1d4e8659b 100644 --- a/drivers/char/drm/drm_stub.c +++ b/drivers/char/drm/drm_stub.c @@ -65,22 +65,22 @@ static int drm_fill_in_dev(drm_device_t * dev, struct pci_dev *pdev, mutex_init(&dev->ctxlist_mutex); dev->pdev = pdev; + dev->pci_device = pdev->device; + dev->pci_vendor = pdev->vendor; #ifdef __alpha__ dev->hose = pdev->sysdata; - dev->pci_domain = dev->hose->bus->number; -#else - dev->pci_domain = 0; #endif - dev->pci_bus = pdev->bus->number; - dev->pci_slot = PCI_SLOT(pdev->devfn); - dev->pci_func = PCI_FUNC(pdev->devfn); dev->irq = pdev->irq; dev->maplist = drm_calloc(1, sizeof(*dev->maplist), DRM_MEM_MAPS); if (dev->maplist == NULL) return -ENOMEM; INIT_LIST_HEAD(&dev->maplist->head); + if (drm_ht_create(&dev->map_hash, 12)) { + drm_free(dev->maplist, sizeof(*dev->maplist), DRM_MEM_MAPS); + return -ENOMEM; + } /* the DRM has 6 basic counters */ dev->counters = 6; diff --git a/drivers/char/drm/drm_vm.c b/drivers/char/drm/drm_vm.c index ffd0800ed60..b40ae438f53 100644 --- a/drivers/char/drm/drm_vm.c +++ b/drivers/char/drm/drm_vm.c @@ -59,7 +59,7 @@ static __inline__ struct page *drm_do_vm_nopage(struct vm_area_struct *vma, drm_device_t *dev = priv->head->dev; drm_map_t *map = NULL; drm_map_list_t *r_list; - struct list_head *list; + drm_hash_item_t *hash; /* * Find the right map @@ -70,14 +70,11 @@ static __inline__ struct page *drm_do_vm_nopage(struct vm_area_struct *vma, if (!dev->agp || !dev->agp->cant_use_aperture) goto vm_nopage_error; - list_for_each(list, &dev->maplist->head) { - r_list = list_entry(list, drm_map_list_t, head); - map = r_list->map; - if (!map) - continue; - if (r_list->user_token == VM_OFFSET(vma)) - break; - } + if (drm_ht_find_item(&dev->map_hash, vma->vm_pgoff << PAGE_SHIFT, &hash)) + goto vm_nopage_error; + + r_list = drm_hash_entry(hash, drm_map_list_t, hash); + map = r_list->map; if (map && map->type == _DRM_AGP) { unsigned long offset = address - vma->vm_start; @@ -467,7 +464,7 @@ static int drm_mmap_dma(struct file *filp, struct vm_area_struct *vma) dev = priv->head->dev; dma = dev->dma; DRM_DEBUG("start = 0x%lx, end = 0x%lx, offset = 0x%lx\n", - vma->vm_start, vma->vm_end, VM_OFFSET(vma)); + vma->vm_start, vma->vm_end, vma->vm_pgoff << PAGE_SHIFT); /* Length must match exact page count */ if (!dma || (length >> PAGE_SHIFT) != dma->page_count) { @@ -521,12 +518,11 @@ int drm_mmap(struct file *filp, struct vm_area_struct *vma) drm_file_t *priv = filp->private_data; drm_device_t *dev = priv->head->dev; drm_map_t *map = NULL; - drm_map_list_t *r_list; unsigned long offset = 0; - struct list_head *list; + drm_hash_item_t *hash; DRM_DEBUG("start = 0x%lx, end = 0x%lx, offset = 0x%lx\n", - vma->vm_start, vma->vm_end, VM_OFFSET(vma)); + vma->vm_start, vma->vm_end, vma->vm_pgoff << PAGE_SHIFT); if (!priv->authenticated) return -EACCES; @@ -535,7 +531,7 @@ int drm_mmap(struct file *filp, struct vm_area_struct *vma) * the AGP mapped at physical address 0 * --BenH. */ - if (!VM_OFFSET(vma) + if (!(vma->vm_pgoff << PAGE_SHIFT) #if __OS_HAS_AGP && (!dev->agp || dev->agp->agp_info.device->vendor != PCI_VENDOR_ID_APPLE) @@ -543,23 +539,12 @@ int drm_mmap(struct file *filp, struct vm_area_struct *vma) ) return drm_mmap_dma(filp, vma); - /* A sequential search of a linked list is - fine here because: 1) there will only be - about 5-10 entries in the list and, 2) a - DRI client only has to do this mapping - once, so it doesn't have to be optimized - for performance, even if the list was a - bit longer. */ - list_for_each(list, &dev->maplist->head) { - - r_list = list_entry(list, drm_map_list_t, head); - map = r_list->map; - if (!map) - continue; - if (r_list->user_token == VM_OFFSET(vma)) - break; + if (drm_ht_find_item(&dev->map_hash, vma->vm_pgoff << PAGE_SHIFT, &hash)) { + DRM_ERROR("Could not find map\n"); + return -EINVAL; } + map = drm_hash_entry(hash, drm_map_list_t, hash)->map; if (!map || ((map->flags & _DRM_RESTRICTED) && !capable(CAP_SYS_ADMIN))) return -EPERM; @@ -620,7 +605,7 @@ int drm_mmap(struct file *filp, struct vm_area_struct *vma) offset = dev->driver->get_reg_ofs(dev); #ifdef __sparc__ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); - if (io_remap_pfn_range(DRM_RPR_ARG(vma) vma->vm_start, + if (io_remap_pfn_range(vma, vma->vm_start, (map->offset + offset) >> PAGE_SHIFT, vma->vm_end - vma->vm_start, vma->vm_page_prot)) diff --git a/drivers/char/drm/i810_dma.c b/drivers/char/drm/i810_dma.c index c658dde3633..fa2de70f740 100644 --- a/drivers/char/drm/i810_dma.c +++ b/drivers/char/drm/i810_dma.c @@ -106,7 +106,7 @@ static int i810_mmap_buffers(struct file *filp, struct vm_area_struct *vma) unlock_kernel(); if (io_remap_pfn_range(vma, vma->vm_start, - VM_OFFSET(vma) >> PAGE_SHIFT, + vma->vm_pgoff, vma->vm_end - vma->vm_start, vma->vm_page_prot)) return -EAGAIN; return 0; @@ -141,10 +141,10 @@ static int i810_map_buffer(drm_buf_t * buf, struct file *filp) MAP_SHARED, buf->bus_address); dev_priv->mmap_buffer = NULL; filp->f_op = old_fops; - if ((unsigned long)buf_priv->virtual > -1024UL) { + if (IS_ERR(buf_priv->virtual)) { /* Real error */ DRM_ERROR("mmap error\n"); - retcode = (signed int)buf_priv->virtual; + retcode = PTR_ERR(buf_priv->virtual); buf_priv->virtual = NULL; } up_write(¤t->mm->mmap_sem); @@ -808,7 +808,7 @@ static void i810_dma_dispatch_vertex(drm_device_t * dev, ((GFX_OP_PRIMITIVE | prim | ((used / 4) - 2))); if (used & 4) { - *(u32 *) ((u32) buf_priv->kernel_virtual + used) = 0; + *(u32 *) ((char *) buf_priv->kernel_virtual + used) = 0; used += 4; } @@ -1166,7 +1166,7 @@ static void i810_dma_dispatch_mc(drm_device_t * dev, drm_buf_t * buf, int used, if (buf_priv->currently_mapped == I810_BUF_MAPPED) { if (used & 4) { - *(u32 *) ((u32) buf_priv->virtual + used) = 0; + *(u32 *) ((char *) buf_priv->virtual + used) = 0; used += 4; } diff --git a/drivers/char/drm/i830_dma.c b/drivers/char/drm/i830_dma.c index b0f815d8cea..4f0e5746ab3 100644 --- a/drivers/char/drm/i830_dma.c +++ b/drivers/char/drm/i830_dma.c @@ -108,7 +108,7 @@ static int i830_mmap_buffers(struct file *filp, struct vm_area_struct *vma) unlock_kernel(); if (io_remap_pfn_range(vma, vma->vm_start, - VM_OFFSET(vma) >> PAGE_SHIFT, + vma->vm_pgoff, vma->vm_end - vma->vm_start, vma->vm_page_prot)) return -EAGAIN; return 0; @@ -146,7 +146,7 @@ static int i830_map_buffer(drm_buf_t * buf, struct file *filp) if (IS_ERR((void *)virtual)) { /* ugh */ /* Real error */ DRM_ERROR("mmap error\n"); - retcode = virtual; + retcode = PTR_ERR((void *)virtual); buf_priv->virtual = NULL; } else { buf_priv->virtual = (void __user *)virtual; diff --git a/drivers/char/drm/i915_dma.c b/drivers/char/drm/i915_dma.c index a94233bdbc0..fb7913ff528 100644 --- a/drivers/char/drm/i915_dma.c +++ b/drivers/char/drm/i915_dma.c @@ -31,6 +31,11 @@ #include "i915_drm.h" #include "i915_drv.h" +#define IS_I965G(dev) (dev->pci_device == 0x2972 || \ + dev->pci_device == 0x2982 || \ + dev->pci_device == 0x2992 || \ + dev->pci_device == 0x29A2) + /* Really want an OS-independent resettable timer. Would like to have * this loop run for (eg) 3 sec, but have the timer reset every time * the head pointer changes, so that EBUSY only happens if the ring @@ -255,7 +260,7 @@ static int i915_dma_init(DRM_IOCTL_ARGS) retcode = i915_dma_resume(dev); break; default: - retcode = -EINVAL; + retcode = DRM_ERR(EINVAL); break; } @@ -347,7 +352,7 @@ static int i915_emit_cmds(drm_device_t * dev, int __user * buffer, int dwords) if ((dwords+1) * sizeof(int) >= dev_priv->ring.Size - 8) return DRM_ERR(EINVAL); - BEGIN_LP_RING(((dwords+1)&~1)); + BEGIN_LP_RING((dwords+1)&~1); for (i = 0; i < dwords;) { int cmd, sz; @@ -386,7 +391,7 @@ static int i915_emit_box(drm_device_t * dev, RING_LOCALS; if (DRM_COPY_FROM_USER_UNCHECKED(&box, &boxes[i], sizeof(box))) { - return EFAULT; + return DRM_ERR(EFAULT); } if (box.y2 <= box.y1 || box.x2 <= box.x1 || box.y2 <= 0 || box.x2 <= 0) { @@ -395,24 +400,40 @@ static int i915_emit_box(drm_device_t * dev, return DRM_ERR(EINVAL); } - BEGIN_LP_RING(6); - OUT_RING(GFX_OP_DRAWRECT_INFO); - OUT_RING(DR1); - OUT_RING((box.x1 & 0xffff) | (box.y1 << 16)); - OUT_RING(((box.x2 - 1) & 0xffff) | ((box.y2 - 1) << 16)); - OUT_RING(DR4); - OUT_RING(0); - ADVANCE_LP_RING(); + if (IS_I965G(dev)) { + BEGIN_LP_RING(4); + OUT_RING(GFX_OP_DRAWRECT_INFO_I965); + OUT_RING((box.x1 & 0xffff) | (box.y1 << 16)); + OUT_RING(((box.x2 - 1) & 0xffff) | ((box.y2 - 1) << 16)); + OUT_RING(DR4); + ADVANCE_LP_RING(); + } else { + BEGIN_LP_RING(6); + OUT_RING(GFX_OP_DRAWRECT_INFO); + OUT_RING(DR1); + OUT_RING((box.x1 & 0xffff) | (box.y1 << 16)); + OUT_RING(((box.x2 - 1) & 0xffff) | ((box.y2 - 1) << 16)); + OUT_RING(DR4); + OUT_RING(0); + ADVANCE_LP_RING(); + } return 0; } +/* XXX: Emitting the counter should really be moved to part of the IRQ + * emit. For now, do it in both places: + */ + static void i915_emit_breadcrumb(drm_device_t *dev) { drm_i915_private_t *dev_priv = dev->dev_private; RING_LOCALS; - dev_priv->sarea_priv->last_enqueue = dev_priv->counter++; + dev_priv->sarea_priv->last_enqueue = ++dev_priv->counter; + + if (dev_priv->counter > 0x7FFFFFFFUL) + dev_priv->sarea_priv->last_enqueue = dev_priv->counter = 1; BEGIN_LP_RING(4); OUT_RING(CMD_STORE_DWORD_IDX); diff --git a/drivers/char/drm/i915_drm.h b/drivers/char/drm/i915_drm.h index 5aa3e0e3bb4..6af83e613f2 100644 --- a/drivers/char/drm/i915_drm.h +++ b/drivers/char/drm/i915_drm.h @@ -98,6 +98,12 @@ typedef struct _drm_i915_sarea { int rotated_size; int rotated_pitch; int virtualX, virtualY; + + unsigned int front_tiled; + unsigned int back_tiled; + unsigned int depth_tiled; + unsigned int rotated_tiled; + unsigned int rotated2_tiled; } drm_i915_sarea_t; /* Flags for perf_boxes diff --git a/drivers/char/drm/i915_drv.h b/drivers/char/drm/i915_drv.h index 2d565031c00..fdc2bf19271 100644 --- a/drivers/char/drm/i915_drv.h +++ b/drivers/char/drm/i915_drv.h @@ -146,9 +146,9 @@ extern void i915_mem_release(drm_device_t * dev, #define BEGIN_LP_RING(n) do { \ if (I915_VERBOSE) \ DRM_DEBUG("BEGIN_LP_RING(%d) in %s\n", \ - n, __FUNCTION__); \ - if (dev_priv->ring.space < n*4) \ - i915_wait_ring(dev, n*4, __FUNCTION__); \ + (n), __FUNCTION__); \ + if (dev_priv->ring.space < (n)*4) \ + i915_wait_ring(dev, (n)*4, __FUNCTION__); \ outcount = 0; \ outring = dev_priv->ring.tail; \ ringmask = dev_priv->ring.tail_mask; \ @@ -157,7 +157,7 @@ extern void i915_mem_release(drm_device_t * dev, #define OUT_RING(n) do { \ if (I915_VERBOSE) DRM_DEBUG(" OUT_RING %x\n", (int)(n)); \ - *(volatile unsigned int *)(virt + outring) = n; \ + *(volatile unsigned int *)(virt + outring) = (n); \ outcount++; \ outring += 4; \ outring &= ringmask; \ @@ -254,6 +254,8 @@ extern int i915_wait_ring(drm_device_t * dev, int n, const char *caller); #define GFX_OP_DESTBUFFER_VARS ((0x3<<29)|(0x1d<<24)|(0x85<<16)|0x0) #define GFX_OP_DRAWRECT_INFO ((0x3<<29)|(0x1d<<24)|(0x80<<16)|(0x3)) +#define GFX_OP_DRAWRECT_INFO_I965 ((0x7900<<16)|0x2) + #define MI_BATCH_BUFFER ((0x30<<23)|1) #define MI_BATCH_BUFFER_START (0x31<<23) #define MI_BATCH_BUFFER_END (0xA<<23) diff --git a/drivers/char/drm/i915_irq.c b/drivers/char/drm/i915_irq.c index cd96cfa430d..0d4a162aa38 100644 --- a/drivers/char/drm/i915_irq.c +++ b/drivers/char/drm/i915_irq.c @@ -71,21 +71,27 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) static int i915_emit_irq(drm_device_t * dev) { drm_i915_private_t *dev_priv = dev->dev_private; - u32 ret; RING_LOCALS; i915_kernel_lost_context(dev); DRM_DEBUG("%s\n", __FUNCTION__); - ret = dev_priv->counter; + dev_priv->sarea_priv->last_enqueue = ++dev_priv->counter; - BEGIN_LP_RING(2); + if (dev_priv->counter > 0x7FFFFFFFUL) + dev_priv->sarea_priv->last_enqueue = dev_priv->counter = 1; + + BEGIN_LP_RING(6); + OUT_RING(CMD_STORE_DWORD_IDX); + OUT_RING(20); + OUT_RING(dev_priv->counter); + OUT_RING(0); OUT_RING(0); OUT_RING(GFX_OP_USER_INTERRUPT); ADVANCE_LP_RING(); - - return ret; + + return dev_priv->counter; } static int i915_wait_irq(drm_device_t * dev, int irq_nr) diff --git a/drivers/char/drm/radeon_cp.c b/drivers/char/drm/radeon_cp.c index 5ad43ba7b5a..5ed96568829 100644 --- a/drivers/char/drm/radeon_cp.c +++ b/drivers/char/drm/radeon_cp.c @@ -864,13 +864,13 @@ static int radeon_do_pixcache_flush(drm_radeon_private_t * dev_priv) dev_priv->stats.boxes |= RADEON_BOX_WAIT_IDLE; - tmp = RADEON_READ(RADEON_RB2D_DSTCACHE_CTLSTAT); - tmp |= RADEON_RB2D_DC_FLUSH_ALL; - RADEON_WRITE(RADEON_RB2D_DSTCACHE_CTLSTAT, tmp); + tmp = RADEON_READ(RADEON_RB3D_DSTCACHE_CTLSTAT); + tmp |= RADEON_RB3D_DC_FLUSH_ALL; + RADEON_WRITE(RADEON_RB3D_DSTCACHE_CTLSTAT, tmp); for (i = 0; i < dev_priv->usec_timeout; i++) { - if (!(RADEON_READ(RADEON_RB2D_DSTCACHE_CTLSTAT) - & RADEON_RB2D_DC_BUSY)) { + if (!(RADEON_READ(RADEON_RB3D_DSTCACHE_CTLSTAT) + & RADEON_RB3D_DC_BUSY)) { return 0; } DRM_UDELAY(1); @@ -1130,7 +1130,7 @@ static void radeon_cp_init_ring_buffer(drm_device_t * dev, | (dev_priv->fb_location >> 16)); #if __OS_HAS_AGP - if (dev_priv->flags & CHIP_IS_AGP) { + if (dev_priv->flags & RADEON_IS_AGP) { RADEON_WRITE(RADEON_AGP_BASE, (unsigned int)dev->agp->base); RADEON_WRITE(RADEON_MC_AGP_LOCATION, (((dev_priv->gart_vm_start - 1 + @@ -1158,7 +1158,7 @@ static void radeon_cp_init_ring_buffer(drm_device_t * dev, dev_priv->ring.tail = cur_read_ptr; #if __OS_HAS_AGP - if (dev_priv->flags & CHIP_IS_AGP) { + if (dev_priv->flags & RADEON_IS_AGP) { RADEON_WRITE(RADEON_CP_RB_RPTR_ADDR, dev_priv->ring_rptr->offset - dev->agp->base + dev_priv->gart_vm_start); @@ -1258,6 +1258,13 @@ static void radeon_test_writeback(drm_radeon_private_t * dev_priv) dev_priv->writeback_works = 0; DRM_INFO("writeback forced off\n"); } + + if (!dev_priv->writeback_works) { + /* Disable writeback to avoid unnecessary bus master transfer */ + RADEON_WRITE(RADEON_CP_RB_CNTL, RADEON_READ(RADEON_CP_RB_CNTL) | + RADEON_RB_NO_UPDATE); + RADEON_WRITE(RADEON_SCRATCH_UMSK, 0); + } } /* Enable or disable PCI-E GART on the chip */ @@ -1295,7 +1302,7 @@ static void radeon_set_pcigart(drm_radeon_private_t * dev_priv, int on) { u32 tmp; - if (dev_priv->flags & CHIP_IS_PCIE) { + if (dev_priv->flags & RADEON_IS_PCIE) { radeon_set_pciegart(dev_priv, on); return; } @@ -1333,20 +1340,22 @@ static int radeon_do_init_cp(drm_device_t * dev, drm_radeon_init_t * init) DRM_DEBUG("\n"); /* if we require new memory map but we don't have it fail */ - if ((dev_priv->flags & CHIP_NEW_MEMMAP) && !dev_priv->new_memmap) - { - DRM_ERROR("Cannot initialise DRM on this card\nThis card requires a new X.org DDX\n"); + if ((dev_priv->flags & RADEON_NEW_MEMMAP) && !dev_priv->new_memmap) { + DRM_ERROR("Cannot initialise DRM on this card\nThis card requires a new X.org DDX for 3D\n"); radeon_do_cleanup_cp(dev); return DRM_ERR(EINVAL); } - if (init->is_pci && (dev_priv->flags & CHIP_IS_AGP)) - { + if (init->is_pci && (dev_priv->flags & RADEON_IS_AGP)) { DRM_DEBUG("Forcing AGP card to PCI mode\n"); - dev_priv->flags &= ~CHIP_IS_AGP; + dev_priv->flags &= ~RADEON_IS_AGP; + } else if (!(dev_priv->flags & (RADEON_IS_AGP | RADEON_IS_PCI | RADEON_IS_PCIE)) + && !init->is_pci) { + DRM_DEBUG("Restoring AGP flag\n"); + dev_priv->flags |= RADEON_IS_AGP; } - if ((!(dev_priv->flags & CHIP_IS_AGP)) && !dev->sg) { + if ((!(dev_priv->flags & RADEON_IS_AGP)) && !dev->sg) { DRM_ERROR("PCI GART memory not allocated!\n"); radeon_do_cleanup_cp(dev); return DRM_ERR(EINVAL); @@ -1489,7 +1498,7 @@ static int radeon_do_init_cp(drm_device_t * dev, drm_radeon_init_t * init) init->sarea_priv_offset); #if __OS_HAS_AGP - if (dev_priv->flags & CHIP_IS_AGP) { + if (dev_priv->flags & RADEON_IS_AGP) { drm_core_ioremap(dev_priv->cp_ring, dev); drm_core_ioremap(dev_priv->ring_rptr, dev); drm_core_ioremap(dev->agp_buffer_map, dev); @@ -1548,7 +1557,7 @@ static int radeon_do_init_cp(drm_device_t * dev, drm_radeon_init_t * init) * align it down. */ #if __OS_HAS_AGP - if (dev_priv->flags & CHIP_IS_AGP) { + if (dev_priv->flags & RADEON_IS_AGP) { base = dev->agp->base; /* Check if valid */ if ((base + dev_priv->gart_size) > dev_priv->fb_location && @@ -1578,7 +1587,7 @@ static int radeon_do_init_cp(drm_device_t * dev, drm_radeon_init_t * init) } #if __OS_HAS_AGP - if (dev_priv->flags & CHIP_IS_AGP) + if (dev_priv->flags & RADEON_IS_AGP) dev_priv->gart_buffers_offset = (dev->agp_buffer_map->offset - dev->agp->base + dev_priv->gart_vm_start); @@ -1604,7 +1613,7 @@ static int radeon_do_init_cp(drm_device_t * dev, drm_radeon_init_t * init) dev_priv->ring.high_mark = RADEON_RING_HIGH_MARK; #if __OS_HAS_AGP - if (dev_priv->flags & CHIP_IS_AGP) { + if (dev_priv->flags & RADEON_IS_AGP) { /* Turn off PCI GART */ radeon_set_pcigart(dev_priv, 0); } else @@ -1624,7 +1633,7 @@ static int radeon_do_init_cp(drm_device_t * dev, drm_radeon_init_t * init) dev_priv->gart_info.mapping.handle; dev_priv->gart_info.is_pcie = - !!(dev_priv->flags & CHIP_IS_PCIE); + !!(dev_priv->flags & RADEON_IS_PCIE); dev_priv->gart_info.gart_table_location = DRM_ATI_GART_FB; @@ -1636,7 +1645,7 @@ static int radeon_do_init_cp(drm_device_t * dev, drm_radeon_init_t * init) DRM_ATI_GART_MAIN; dev_priv->gart_info.addr = NULL; dev_priv->gart_info.bus_addr = 0; - if (dev_priv->flags & CHIP_IS_PCIE) { + if (dev_priv->flags & RADEON_IS_PCIE) { DRM_ERROR ("Cannot use PCI Express without GART in FB memory\n"); radeon_do_cleanup_cp(dev); @@ -1678,7 +1687,7 @@ static int radeon_do_cleanup_cp(drm_device_t * dev) drm_irq_uninstall(dev); #if __OS_HAS_AGP - if (dev_priv->flags & CHIP_IS_AGP) { + if (dev_priv->flags & RADEON_IS_AGP) { if (dev_priv->cp_ring != NULL) { drm_core_ioremapfree(dev_priv->cp_ring, dev); dev_priv->cp_ring = NULL; @@ -1733,7 +1742,7 @@ static int radeon_do_resume_cp(drm_device_t * dev) DRM_DEBUG("Starting radeon_do_resume_cp()\n"); #if __OS_HAS_AGP - if (dev_priv->flags & CHIP_IS_AGP) { + if (dev_priv->flags & RADEON_IS_AGP) { /* Turn off PCI GART */ radeon_set_pcigart(dev_priv, 0); } else @@ -2177,13 +2186,15 @@ int radeon_driver_load(struct drm_device *dev, unsigned long flags) dev->dev_private = (void *)dev_priv; dev_priv->flags = flags; - switch (flags & CHIP_FAMILY_MASK) { + switch (flags & RADEON_FAMILY_MASK) { case CHIP_R100: case CHIP_RV200: case CHIP_R200: case CHIP_R300: + case CHIP_R350: case CHIP_R420: - dev_priv->flags |= CHIP_HAS_HIERZ; + case CHIP_RV410: + dev_priv->flags |= RADEON_HAS_HIERZ; break; default: /* all other chips have no hierarchical z buffer */ @@ -2191,13 +2202,14 @@ int radeon_driver_load(struct drm_device *dev, unsigned long flags) } if (drm_device_is_agp(dev)) - dev_priv->flags |= CHIP_IS_AGP; - - if (drm_device_is_pcie(dev)) - dev_priv->flags |= CHIP_IS_PCIE; + dev_priv->flags |= RADEON_IS_AGP; + else if (drm_device_is_pcie(dev)) + dev_priv->flags |= RADEON_IS_PCIE; + else + dev_priv->flags |= RADEON_IS_PCI; DRM_DEBUG("%s card detected\n", - ((dev_priv->flags & CHIP_IS_AGP) ? "AGP" : (((dev_priv->flags & CHIP_IS_PCIE) ? "PCIE" : "PCI")))); + ((dev_priv->flags & RADEON_IS_AGP) ? "AGP" : (((dev_priv->flags & RADEON_IS_PCIE) ? "PCIE" : "PCI")))); return ret; } diff --git a/drivers/char/drm/radeon_drv.c b/drivers/char/drm/radeon_drv.c index eb985c2a31e..2eb652ec674 100644 --- a/drivers/char/drm/radeon_drv.c +++ b/drivers/char/drm/radeon_drv.c @@ -44,7 +44,7 @@ module_param_named(no_wb, radeon_no_wb, int, 0444); static int dri_library_name(struct drm_device *dev, char *buf) { drm_radeon_private_t *dev_priv = dev->dev_private; - int family = dev_priv->flags & CHIP_FAMILY_MASK; + int family = dev_priv->flags & RADEON_FAMILY_MASK; return snprintf(buf, PAGE_SIZE, "%s\n", (family < CHIP_R200) ? "radeon" : diff --git a/drivers/char/drm/radeon_drv.h b/drivers/char/drm/radeon_drv.h index e5a256f5429..f45cd7f147a 100644 --- a/drivers/char/drm/radeon_drv.h +++ b/drivers/char/drm/radeon_drv.h @@ -133,15 +133,16 @@ enum radeon_cp_microcode_version { * Chip flags */ enum radeon_chip_flags { - CHIP_FAMILY_MASK = 0x0000ffffUL, - CHIP_FLAGS_MASK = 0xffff0000UL, - CHIP_IS_MOBILITY = 0x00010000UL, - CHIP_IS_IGP = 0x00020000UL, - CHIP_SINGLE_CRTC = 0x00040000UL, - CHIP_IS_AGP = 0x00080000UL, - CHIP_HAS_HIERZ = 0x00100000UL, - CHIP_IS_PCIE = 0x00200000UL, - CHIP_NEW_MEMMAP = 0x00400000UL, + RADEON_FAMILY_MASK = 0x0000ffffUL, + RADEON_FLAGS_MASK = 0xffff0000UL, + RADEON_IS_MOBILITY = 0x00010000UL, + RADEON_IS_IGP = 0x00020000UL, + RADEON_SINGLE_CRTC = 0x00040000UL, + RADEON_IS_AGP = 0x00080000UL, + RADEON_HAS_HIERZ = 0x00100000UL, + RADEON_IS_PCIE = 0x00200000UL, + RADEON_NEW_MEMMAP = 0x00400000UL, + RADEON_IS_PCI = 0x00800000UL, }; #define GET_RING_HEAD(dev_priv) (dev_priv->writeback_works ? \ @@ -424,6 +425,8 @@ extern int r300_do_cp_cmdbuf(drm_device_t * dev, DRMFILE filp, #define RADEON_RB3D_COLOROFFSET 0x1c40 #define RADEON_RB3D_COLORPITCH 0x1c48 +#define RADEON_SRC_X_Y 0x1590 + #define RADEON_DP_GUI_MASTER_CNTL 0x146c # define RADEON_GMC_SRC_PITCH_OFFSET_CNTL (1 << 0) # define RADEON_GMC_DST_PITCH_OFFSET_CNTL (1 << 1) @@ -441,6 +444,7 @@ extern int r300_do_cp_cmdbuf(drm_device_t * dev, DRMFILE filp, # define RADEON_ROP3_S 0x00cc0000 # define RADEON_ROP3_P 0x00f00000 #define RADEON_DP_WRITE_MASK 0x16cc +#define RADEON_SRC_PITCH_OFFSET 0x1428 #define RADEON_DST_PITCH_OFFSET 0x142c #define RADEON_DST_PITCH_OFFSET_C 0x1c80 # define RADEON_DST_TILE_LINEAR (0 << 30) @@ -545,6 +549,11 @@ extern int r300_do_cp_cmdbuf(drm_device_t * dev, DRMFILE filp, # define RADEON_RB3D_ZC_FREE (1 << 2) # define RADEON_RB3D_ZC_FLUSH_ALL 0x5 # define RADEON_RB3D_ZC_BUSY (1 << 31) +#define RADEON_RB3D_DSTCACHE_CTLSTAT 0x325c +# define RADEON_RB3D_DC_FLUSH (3 << 0) +# define RADEON_RB3D_DC_FREE (3 << 2) +# define RADEON_RB3D_DC_FLUSH_ALL 0xf +# define RADEON_RB3D_DC_BUSY (1 << 31) #define RADEON_RB3D_ZSTENCILCNTL 0x1c2c # define RADEON_Z_TEST_MASK (7 << 4) # define RADEON_Z_TEST_ALWAYS (7 << 4) @@ -681,6 +690,7 @@ extern int r300_do_cp_cmdbuf(drm_device_t * dev, DRMFILE filp, #define RADEON_CP_RB_BASE 0x0700 #define RADEON_CP_RB_CNTL 0x0704 # define RADEON_BUF_SWAP_32BIT (2 << 16) +# define RADEON_RB_NO_UPDATE (1 << 27) #define RADEON_CP_RB_RPTR_ADDR 0x070c #define RADEON_CP_RB_RPTR 0x0710 #define RADEON_CP_RB_WPTR 0x0714 @@ -986,13 +996,13 @@ do { \ } while (0) #define RADEON_FLUSH_CACHE() do { \ - OUT_RING( CP_PACKET0( RADEON_RB2D_DSTCACHE_CTLSTAT, 0 ) ); \ - OUT_RING( RADEON_RB2D_DC_FLUSH ); \ + OUT_RING( CP_PACKET0( RADEON_RB3D_DSTCACHE_CTLSTAT, 0 ) ); \ + OUT_RING( RADEON_RB3D_DC_FLUSH ); \ } while (0) #define RADEON_PURGE_CACHE() do { \ - OUT_RING( CP_PACKET0( RADEON_RB2D_DSTCACHE_CTLSTAT, 0 ) ); \ - OUT_RING( RADEON_RB2D_DC_FLUSH_ALL ); \ + OUT_RING( CP_PACKET0( RADEON_RB3D_DSTCACHE_CTLSTAT, 0 ) ); \ + OUT_RING( RADEON_RB3D_DC_FLUSH_ALL ); \ } while (0) #define RADEON_FLUSH_ZCACHE() do { \ diff --git a/drivers/char/drm/radeon_state.c b/drivers/char/drm/radeon_state.c index 39a7f685e3f..feac5f005d4 100644 --- a/drivers/char/drm/radeon_state.c +++ b/drivers/char/drm/radeon_state.c @@ -42,7 +42,11 @@ static __inline__ int radeon_check_and_fixup_offset(drm_radeon_private_t * drm_file_t * filp_priv, u32 *offset) { - u32 off = *offset; + u64 off = *offset; + u32 fb_start = dev_priv->fb_location; + u32 fb_end = fb_start + dev_priv->fb_size - 1; + u32 gart_start = dev_priv->gart_vm_start; + u32 gart_end = gart_start + dev_priv->gart_size - 1; struct drm_radeon_driver_file_fields *radeon_priv; /* Hrm ... the story of the offset ... So this function converts @@ -62,10 +66,8 @@ static __inline__ int radeon_check_and_fixup_offset(drm_radeon_private_t * /* First, the best case, the offset already lands in either the * framebuffer or the GART mapped space */ - if ((off >= dev_priv->fb_location && - off < (dev_priv->fb_location + dev_priv->fb_size)) || - (off >= dev_priv->gart_vm_start && - off < (dev_priv->gart_vm_start + dev_priv->gart_size))) + if ((off >= fb_start && off <= fb_end) || + (off >= gart_start && off <= gart_end)) return 0; /* Ok, that didn't happen... now check if we have a zero based @@ -78,16 +80,13 @@ static __inline__ int radeon_check_and_fixup_offset(drm_radeon_private_t * } /* Finally, assume we aimed at a GART offset if beyond the fb */ - if (off > (dev_priv->fb_location + dev_priv->fb_size)) - off = off - (dev_priv->fb_location + dev_priv->fb_size) + - dev_priv->gart_vm_start; + if (off > fb_end) + off = off - fb_end - 1 + gart_start; /* Now recheck and fail if out of bounds */ - if ((off >= dev_priv->fb_location && - off < (dev_priv->fb_location + dev_priv->fb_size)) || - (off >= dev_priv->gart_vm_start && - off < (dev_priv->gart_vm_start + dev_priv->gart_size))) { - DRM_DEBUG("offset fixed up to 0x%x\n", off); + if ((off >= fb_start && off <= fb_end) || + (off >= gart_start && off <= gart_end)) { + DRM_DEBUG("offset fixed up to 0x%x\n", (unsigned int)off); *offset = off; return 0; } @@ -869,7 +868,7 @@ static void radeon_cp_dispatch_clear(drm_device_t * dev, */ dev_priv->sarea_priv->ctx_owner = 0; - if ((dev_priv->flags & CHIP_HAS_HIERZ) + if ((dev_priv->flags & RADEON_HAS_HIERZ) && (flags & RADEON_USE_HIERZ)) { /* FIXME : reverse engineer that for Rx00 cards */ /* FIXME : the mask supposedly contains low-res z values. So can't set @@ -914,7 +913,7 @@ static void radeon_cp_dispatch_clear(drm_device_t * dev, for (i = 0; i < nbox; i++) { int tileoffset, nrtilesx, nrtilesy, j; /* it looks like r200 needs rv-style clears, at least if hierz is not enabled? */ - if ((dev_priv->flags & CHIP_HAS_HIERZ) + if ((dev_priv->flags & RADEON_HAS_HIERZ) && !(dev_priv->microcode_version == UCODE_R200)) { /* FIXME : figure this out for r200 (when hierz is enabled). Or maybe r200 actually doesn't need to put the low-res z value into @@ -998,7 +997,7 @@ static void radeon_cp_dispatch_clear(drm_device_t * dev, } /* TODO don't always clear all hi-level z tiles */ - if ((dev_priv->flags & CHIP_HAS_HIERZ) + if ((dev_priv->flags & RADEON_HAS_HIERZ) && (dev_priv->microcode_version == UCODE_R200) && (flags & RADEON_USE_HIERZ)) /* r100 and cards without hierarchical z-buffer have no high-level z-buffer */ @@ -1270,9 +1269,9 @@ static void radeon_cp_dispatch_swap(drm_device_t * dev) DRM_DEBUG("dispatch swap %d,%d-%d,%d\n", x, y, w, h); - BEGIN_RING(7); + BEGIN_RING(9); - OUT_RING(CP_PACKET3(RADEON_CNTL_BITBLT_MULTI, 5)); + OUT_RING(CP_PACKET0(RADEON_DP_GUI_MASTER_CNTL, 0)); OUT_RING(RADEON_GMC_SRC_PITCH_OFFSET_CNTL | RADEON_GMC_DST_PITCH_OFFSET_CNTL | RADEON_GMC_BRUSH_NONE | @@ -1284,6 +1283,7 @@ static void radeon_cp_dispatch_swap(drm_device_t * dev) /* Make this work even if front & back are flipped: */ + OUT_RING(CP_PACKET0(RADEON_SRC_PITCH_OFFSET, 1)); if (dev_priv->current_page == 0) { OUT_RING(dev_priv->back_pitch_offset); OUT_RING(dev_priv->front_pitch_offset); @@ -1292,6 +1292,7 @@ static void radeon_cp_dispatch_swap(drm_device_t * dev) OUT_RING(dev_priv->back_pitch_offset); } + OUT_RING(CP_PACKET0(RADEON_SRC_X_Y, 2)); OUT_RING((x << 16) | y); OUT_RING((x << 16) | y); OUT_RING((w << 16) | h); @@ -2987,16 +2988,21 @@ static int radeon_cp_getparam(DRM_IOCTL_ARGS) case RADEON_PARAM_GART_TEX_HANDLE: value = dev_priv->gart_textures_offset; break; - + case RADEON_PARAM_SCRATCH_OFFSET: + if (!dev_priv->writeback_works) + return DRM_ERR(EINVAL); + value = RADEON_SCRATCH_REG_OFFSET; + break; case RADEON_PARAM_CARD_TYPE: - if (dev_priv->flags & CHIP_IS_PCIE) + if (dev_priv->flags & RADEON_IS_PCIE) value = RADEON_CARD_PCIE; - else if (dev_priv->flags & CHIP_IS_AGP) + else if (dev_priv->flags & RADEON_IS_AGP) value = RADEON_CARD_AGP; else value = RADEON_CARD_PCI; break; default: + DRM_DEBUG("Invalid parameter %d\n", param.param); return DRM_ERR(EINVAL); } diff --git a/drivers/char/drm/sis_drv.c b/drivers/char/drm/sis_drv.c index 5e9dc86f295..3d5b3218b6f 100644 --- a/drivers/char/drm/sis_drv.c +++ b/drivers/char/drm/sis_drv.c @@ -35,11 +35,44 @@ static struct pci_device_id pciidlist[] = { sisdrv_PCI_IDS }; +static int sis_driver_load(drm_device_t *dev, unsigned long chipset) +{ + drm_sis_private_t *dev_priv; + int ret; + + dev_priv = drm_calloc(1, sizeof(drm_sis_private_t), DRM_MEM_DRIVER); + if (dev_priv == NULL) + return DRM_ERR(ENOMEM); + + dev->dev_private = (void *)dev_priv; + dev_priv->chipset = chipset; + ret = drm_sman_init(&dev_priv->sman, 2, 12, 8); + if (ret) { + drm_free(dev_priv, sizeof(dev_priv), DRM_MEM_DRIVER); + } + + return ret; +} + +static int sis_driver_unload(drm_device_t *dev) +{ + drm_sis_private_t *dev_priv = dev->dev_private; + + drm_sman_takedown(&dev_priv->sman); + drm_free(dev_priv, sizeof(*dev_priv), DRM_MEM_DRIVER); + + return 0; +} + static struct drm_driver driver = { .driver_features = DRIVER_USE_AGP | DRIVER_USE_MTRR, - .context_ctor = sis_init_context, - .context_dtor = sis_final_context, - .reclaim_buffers = drm_core_reclaim_buffers, + .load = sis_driver_load, + .unload = sis_driver_unload, + .context_dtor = NULL, + .dma_quiescent = sis_idle, + .reclaim_buffers = NULL, + .reclaim_buffers_locked = sis_reclaim_buffers_locked, + .lastclose = sis_lastclose, .get_map_ofs = drm_core_get_map_ofs, .get_reg_ofs = drm_core_get_reg_ofs, .ioctls = sis_ioctls, diff --git a/drivers/char/drm/sis_drv.h b/drivers/char/drm/sis_drv.h index e218e526950..2b8d6f6ed7c 100644 --- a/drivers/char/drm/sis_drv.h +++ b/drivers/char/drm/sis_drv.h @@ -31,23 +31,39 @@ /* General customization: */ -#define DRIVER_AUTHOR "SIS" +#define DRIVER_AUTHOR "SIS, Tungsten Graphics" #define DRIVER_NAME "sis" #define DRIVER_DESC "SIS 300/630/540" -#define DRIVER_DATE "20030826" +#define DRIVER_DATE "20060704" #define DRIVER_MAJOR 1 -#define DRIVER_MINOR 1 -#define DRIVER_PATCHLEVEL 0 +#define DRIVER_MINOR 2 +#define DRIVER_PATCHLEVEL 1 -#include "sis_ds.h" +enum sis_family { + SIS_OTHER = 0, + SIS_CHIP_315 = 1, +}; + +#include "drm_sman.h" + +#define SIS_BASE (dev_priv->mmio) +#define SIS_READ(reg) DRM_READ32(SIS_BASE, reg); +#define SIS_WRITE(reg, val) DRM_WRITE32(SIS_BASE, reg, val); typedef struct drm_sis_private { - memHeap_t *AGPHeap; - memHeap_t *FBHeap; + drm_local_map_t *mmio; + unsigned int idle_fault; + drm_sman_t sman; + unsigned int chipset; + int vram_initialized; + int agp_initialized; + unsigned long vram_offset; + unsigned long agp_offset; } drm_sis_private_t; -extern int sis_init_context(drm_device_t * dev, int context); -extern int sis_final_context(drm_device_t * dev, int context); +extern int sis_idle(drm_device_t *dev); +extern void sis_reclaim_buffers_locked(drm_device_t *dev, struct file *filp); +extern void sis_lastclose(drm_device_t *dev); extern drm_ioctl_desc_t sis_ioctls[]; extern int sis_max_ioctl; diff --git a/drivers/char/drm/sis_ds.c b/drivers/char/drm/sis_ds.c deleted file mode 100644 index 2e485d48294..00000000000 --- a/drivers/char/drm/sis_ds.c +++ /dev/null @@ -1,299 +0,0 @@ -/* sis_ds.c -- Private header for Direct Rendering Manager -*- linux-c -*- - * Created: Mon Jan 4 10:05:05 1999 by sclin@sis.com.tw - * - * Copyright 2000 Silicon Integrated Systems Corp, Inc., HsinChu, Taiwan. - * All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Authors: - * Sung-Ching Lin <sclin@sis.com.tw> - * - */ - -#include "drmP.h" -#include "drm.h" -#include "sis_ds.h" - -/* Set Data Structure, not check repeated value - * temporarily used - */ - -set_t *setInit(void) -{ - int i; - set_t *set; - - set = (set_t *) drm_alloc(sizeof(set_t), DRM_MEM_DRIVER); - if (set != NULL) { - for (i = 0; i < SET_SIZE; i++) { - set->list[i].free_next = i + 1; - set->list[i].alloc_next = -1; - } - set->list[SET_SIZE - 1].free_next = -1; - set->free = 0; - set->alloc = -1; - set->trace = -1; - } - return set; -} - -int setAdd(set_t * set, ITEM_TYPE item) -{ - int free = set->free; - - if (free != -1) { - set->list[free].val = item; - set->free = set->list[free].free_next; - } else { - return 0; - } - - set->list[free].alloc_next = set->alloc; - set->alloc = free; - set->list[free].free_next = -1; - - return 1; -} - -int setDel(set_t * set, ITEM_TYPE item) -{ - int alloc = set->alloc; - int prev = -1; - - while (alloc != -1) { - if (set->list[alloc].val == item) { - if (prev != -1) - set->list[prev].alloc_next = - set->list[alloc].alloc_next; - else - set->alloc = set->list[alloc].alloc_next; - break; - } - prev = alloc; - alloc = set->list[alloc].alloc_next; - } - - if (alloc == -1) - return 0; - - set->list[alloc].free_next = set->free; - set->free = alloc; - set->list[alloc].alloc_next = -1; - - return 1; -} - -/* setFirst -> setAdd -> setNext is wrong */ - -int setFirst(set_t * set, ITEM_TYPE * item) -{ - if (set->alloc == -1) - return 0; - - *item = set->list[set->alloc].val; - set->trace = set->list[set->alloc].alloc_next; - - return 1; -} - -int setNext(set_t * set, ITEM_TYPE * item) -{ - if (set->trace == -1) - return 0; - - *item = set->list[set->trace].val; - set->trace = set->list[set->trace].alloc_next; - - return 1; -} - -int setDestroy(set_t * set) -{ - drm_free(set, sizeof(set_t), DRM_MEM_DRIVER); - - return 1; -} - -/* - * GLX Hardware Device Driver common code - * Copyright (C) 1999 Wittawat Yamwong - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * WITTAWAT YAMWONG, OR ANY OTHER CONTRIBUTORS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR - * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE - * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ - -#define ISFREE(bptr) ((bptr)->free) - -memHeap_t *mmInit(int ofs, int size) -{ - PMemBlock blocks; - - if (size <= 0) - return NULL; - - blocks = (TMemBlock *) drm_calloc(1, sizeof(TMemBlock), DRM_MEM_DRIVER); - if (blocks != NULL) { - blocks->ofs = ofs; - blocks->size = size; - blocks->free = 1; - return (memHeap_t *) blocks; - } else - return NULL; -} - -/* Checks if a pointer 'b' is part of the heap 'heap' */ -int mmBlockInHeap(memHeap_t * heap, PMemBlock b) -{ - TMemBlock *p; - - if (heap == NULL || b == NULL) - return 0; - - p = heap; - while (p != NULL && p != b) { - p = p->next; - } - if (p == b) - return 1; - else - return 0; -} - -static TMemBlock *SliceBlock(TMemBlock * p, - int startofs, int size, - int reserved, int alignment) -{ - TMemBlock *newblock; - - /* break left */ - if (startofs > p->ofs) { - newblock = (TMemBlock *) drm_calloc(1, sizeof(TMemBlock), - DRM_MEM_DRIVER); - newblock->ofs = startofs; - newblock->size = p->size - (startofs - p->ofs); - newblock->free = 1; - newblock->next = p->next; - p->size -= newblock->size; - p->next = newblock; - p = newblock; - } - - /* break right */ - if (size < p->size) { - newblock = (TMemBlock *) drm_calloc(1, sizeof(TMemBlock), - DRM_MEM_DRIVER); - newblock->ofs = startofs + size; - newblock->size = p->size - size; - newblock->free = 1; - newblock->next = p->next; - p->size = size; - p->next = newblock; - } - - /* p = middle block */ - p->align = alignment; - p->free = 0; - p->reserved = reserved; - return p; -} - -PMemBlock mmAllocMem(memHeap_t * heap, int size, int align2, int startSearch) -{ - int mask, startofs, endofs; - TMemBlock *p; - - if (heap == NULL || align2 < 0 || size <= 0) - return NULL; - - mask = (1 << align2) - 1; - startofs = 0; - p = (TMemBlock *) heap; - while (p != NULL) { - if (ISFREE(p)) { - startofs = (p->ofs + mask) & ~mask; - if (startofs < startSearch) { - startofs = startSearch; - } - endofs = startofs + size; - if (endofs <= (p->ofs + p->size)) - break; - } - p = p->next; - } - if (p == NULL) - return NULL; - p = SliceBlock(p, startofs, size, 0, mask + 1); - p->heap = heap; - return p; -} - -static __inline__ int Join2Blocks(TMemBlock * p) -{ - if (p->free && p->next && p->next->free) { - TMemBlock *q = p->next; - p->size += q->size; - p->next = q->next; - drm_free(q, sizeof(TMemBlock), DRM_MEM_DRIVER); - return 1; - } - return 0; -} - -int mmFreeMem(PMemBlock b) -{ - TMemBlock *p, *prev; - - if (b == NULL) - return 0; - if (b->heap == NULL) - return -1; - - p = b->heap; - prev = NULL; - while (p != NULL && p != b) { - prev = p; - p = p->next; - } - if (p == NULL || p->free || p->reserved) - return -1; - - p->free = 1; - Join2Blocks(p); - if (prev) - Join2Blocks(prev); - return 0; -} diff --git a/drivers/char/drm/sis_ds.h b/drivers/char/drm/sis_ds.h deleted file mode 100644 index 94f2b4728b6..00000000000 --- a/drivers/char/drm/sis_ds.h +++ /dev/null @@ -1,146 +0,0 @@ -/* sis_ds.h -- Private header for Direct Rendering Manager -*- linux-c -*- - * Created: Mon Jan 4 10:05:05 1999 by sclin@sis.com.tw - */ -/* - * Copyright 2000 Silicon Integrated Systems Corp, Inc., HsinChu, Taiwan. - * All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Authors: - * Sung-Ching Lin <sclin@sis.com.tw> - * - */ - -#ifndef __SIS_DS_H__ -#define __SIS_DS_H__ - -/* Set Data Structure */ - -#define SET_SIZE 5000 - -typedef unsigned long ITEM_TYPE; - -typedef struct { - ITEM_TYPE val; - int alloc_next, free_next; -} list_item_t; - -typedef struct { - int alloc; - int free; - int trace; - list_item_t list[SET_SIZE]; -} set_t; - -set_t *setInit(void); -int setAdd(set_t * set, ITEM_TYPE item); -int setDel(set_t * set, ITEM_TYPE item); -int setFirst(set_t * set, ITEM_TYPE * item); -int setNext(set_t * set, ITEM_TYPE * item); -int setDestroy(set_t * set); - -/* - * GLX Hardware Device Driver common code - * Copyright (C) 1999 Wittawat Yamwong - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * WITTAWAT YAMWONG, OR ANY OTHER CONTRIBUTORS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR - * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE - * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ - -struct mem_block_t { - struct mem_block_t *next; - struct mem_block_t *heap; - int ofs, size; - int align; - unsigned int free:1; - unsigned int reserved:1; -}; -typedef struct mem_block_t TMemBlock; -typedef struct mem_block_t *PMemBlock; - -/* a heap is just the first block in a chain */ -typedef struct mem_block_t memHeap_t; - -static __inline__ int mmBlockSize(PMemBlock b) -{ - return b->size; -} - -static __inline__ int mmOffset(PMemBlock b) -{ - return b->ofs; -} - -static __inline__ void mmMarkReserved(PMemBlock b) -{ - b->reserved = 1; -} - -/* - * input: total size in bytes - * return: a heap pointer if OK, NULL if error - */ -memHeap_t *mmInit(int ofs, int size); - -/* - * Allocate 'size' bytes with 2^align2 bytes alignment, - * restrict the search to free memory after 'startSearch' - * depth and back buffers should be in different 4mb banks - * to get better page hits if possible - * input: size = size of block - * align2 = 2^align2 bytes alignment - * startSearch = linear offset from start of heap to begin search - * return: pointer to the allocated block, 0 if error - */ -PMemBlock mmAllocMem(memHeap_t * heap, int size, int align2, int startSearch); - -/* - * Returns 1 if the block 'b' is part of the heap 'heap' - */ -int mmBlockInHeap(PMemBlock heap, PMemBlock b); - -/* - * Free block starts at offset - * input: pointer to a block - * return: 0 if OK, -1 if error - */ -int mmFreeMem(PMemBlock b); - -/* For debuging purpose. */ -void mmDumpMemInfo(memHeap_t * mmInit); - -#endif /* __SIS_DS_H__ */ diff --git a/drivers/char/drm/sis_mm.c b/drivers/char/drm/sis_mm.c index 5e9936bc307..d26f5dbb785 100644 --- a/drivers/char/drm/sis_mm.c +++ b/drivers/char/drm/sis_mm.c @@ -1,414 +1,348 @@ -/* sis_mm.c -- Private header for Direct Rendering Manager -*- linux-c -*- - * Created: Mon Jan 4 10:05:05 1999 by sclin@sis.com.tw +/************************************************************************** * - * Copyright 2000 Silicon Integrated Systems Corp, Inc., HsinChu, Taiwan. - * All rights reserved. + * Copyright 2006 Tungsten Graphics, Inc., Bismarck, ND., USA. + * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. * - * Authors: - * Sung-Ching Lin <sclin@sis.com.tw> * + **************************************************************************/ + +/* + * Authors: + * Thomas Hellström <thomas-at-tungstengraphics-dot-com> */ #include "drmP.h" #include "sis_drm.h" #include "sis_drv.h" -#include "sis_ds.h" -#if defined(__linux__) && defined(CONFIG_FB_SIS) + #include <video/sisfb.h> -#endif -#define MAX_CONTEXT 100 #define VIDEO_TYPE 0 #define AGP_TYPE 1 -typedef struct { - int used; - int context; - set_t *sets[2]; /* 0 for video, 1 for AGP */ -} sis_context_t; -static sis_context_t global_ppriv[MAX_CONTEXT]; +#if defined(CONFIG_FB_SIS) +/* fb management via fb device */ -static int add_alloc_set(int context, int type, unsigned int val) -{ - int i, retval = 0; +#define SIS_MM_ALIGN_SHIFT 0 +#define SIS_MM_ALIGN_MASK 0 - for (i = 0; i < MAX_CONTEXT; i++) { - if (global_ppriv[i].used && global_ppriv[i].context == context) { - retval = setAdd(global_ppriv[i].sets[type], val); - break; - } - } - return retval; -} - -static int del_alloc_set(int context, int type, unsigned int val) +static void *sis_sman_mm_allocate(void *private, unsigned long size, + unsigned alignment) { - int i, retval = 0; + struct sis_memreq req; - for (i = 0; i < MAX_CONTEXT; i++) { - if (global_ppriv[i].used && global_ppriv[i].context == context) { - retval = setDel(global_ppriv[i].sets[type], val); - break; - } - } - return retval; + req.size = size; + sis_malloc(&req); + if (req.size == 0) + return NULL; + else + return (void *)~req.offset; } -/* fb management via fb device */ -#if defined(__linux__) && defined(CONFIG_FB_SIS) - -static int sis_fb_init(DRM_IOCTL_ARGS) +static void sis_sman_mm_free(void *private, void *ref) { - return 0; + sis_free(~((unsigned long)ref)); } -static int sis_fb_alloc(DRM_IOCTL_ARGS) +static void sis_sman_mm_destroy(void *private) { - drm_sis_mem_t fb; - struct sis_memreq req; - drm_sis_mem_t __user *argp = (drm_sis_mem_t __user *)data; - int retval = 0; - - DRM_COPY_FROM_USER_IOCTL(fb, argp, sizeof(fb)); - - req.size = fb.size; - sis_malloc(&req); - if (req.offset) { - /* TODO */ - fb.offset = req.offset; - fb.free = req.offset; - if (!add_alloc_set(fb.context, VIDEO_TYPE, fb.free)) { - DRM_DEBUG("adding to allocation set fails\n"); - sis_free(req.offset); - retval = DRM_ERR(EINVAL); - } - } else { - fb.offset = 0; - fb.size = 0; - fb.free = 0; - } - - DRM_COPY_TO_USER_IOCTL(argp, fb, sizeof(fb)); - - DRM_DEBUG("alloc fb, size = %d, offset = %d\n", fb.size, req.offset); - - return retval; + ; } -static int sis_fb_free(DRM_IOCTL_ARGS) +static unsigned long sis_sman_mm_offset(void *private, void *ref) { - drm_sis_mem_t fb; - int retval = 0; - - DRM_COPY_FROM_USER_IOCTL(fb, (drm_sis_mem_t __user *) data, sizeof(fb)); - - if (!fb.free) - return DRM_ERR(EINVAL); + return ~((unsigned long)ref); +} - if (!del_alloc_set(fb.context, VIDEO_TYPE, fb.free)) - retval = DRM_ERR(EINVAL); - sis_free(fb.free); +#else /* CONFIG_FB_SIS */ - DRM_DEBUG("free fb, offset = 0x%lx\n", fb.free); +#define SIS_MM_ALIGN_SHIFT 4 +#define SIS_MM_ALIGN_MASK ( (1 << SIS_MM_ALIGN_SHIFT) - 1) - return retval; -} +#endif /* CONFIG_FB_SIS */ -#else - -/* Called by the X Server to initialize the FB heap. Allocations will fail - * unless this is called. Offset is the beginning of the heap from the - * framebuffer offset (MaxXFBMem in XFree86). - * - * Memory layout according to Thomas Winischofer: - * |------------------|DDDDDDDDDDDDDDDDDDDDDDDDDDDDD|HHHH|CCCCCCCCCCC| - * - * X driver/sisfb HW- Command- - * framebuffer memory DRI heap Cursor queue - */ static int sis_fb_init(DRM_IOCTL_ARGS) { DRM_DEVICE; drm_sis_private_t *dev_priv = dev->dev_private; drm_sis_fb_t fb; + int ret; DRM_COPY_FROM_USER_IOCTL(fb, (drm_sis_fb_t __user *) data, sizeof(fb)); - if (dev_priv == NULL) { - dev->dev_private = drm_calloc(1, sizeof(drm_sis_private_t), - DRM_MEM_DRIVER); - dev_priv = dev->dev_private; - if (dev_priv == NULL) - return ENOMEM; + mutex_lock(&dev->struct_mutex); +#if defined(CONFIG_FB_SIS) + { + drm_sman_mm_t sman_mm; + sman_mm.private = (void *)0xFFFFFFFF; + sman_mm.allocate = sis_sman_mm_allocate; + sman_mm.free = sis_sman_mm_free; + sman_mm.destroy = sis_sman_mm_destroy; + sman_mm.offset = sis_sman_mm_offset; + ret = + drm_sman_set_manager(&dev_priv->sman, VIDEO_TYPE, &sman_mm); } +#else + ret = drm_sman_set_range(&dev_priv->sman, VIDEO_TYPE, 0, + fb.size >> SIS_MM_ALIGN_SHIFT); +#endif - if (dev_priv->FBHeap != NULL) - return DRM_ERR(EINVAL); + if (ret) { + DRM_ERROR("VRAM memory manager initialisation error\n"); + mutex_unlock(&dev->struct_mutex); + return ret; + } - dev_priv->FBHeap = mmInit(fb.offset, fb.size); + dev_priv->vram_initialized = 1; + dev_priv->vram_offset = fb.offset; + mutex_unlock(&dev->struct_mutex); DRM_DEBUG("offset = %u, size = %u", fb.offset, fb.size); return 0; } -static int sis_fb_alloc(DRM_IOCTL_ARGS) +static int sis_drm_alloc(drm_device_t * dev, drm_file_t * priv, + unsigned long data, int pool) { - DRM_DEVICE; drm_sis_private_t *dev_priv = dev->dev_private; - drm_sis_mem_t __user *argp = (drm_sis_mem_t __user *)data; - drm_sis_mem_t fb; - PMemBlock block; + drm_sis_mem_t __user *argp = (drm_sis_mem_t __user *) data; + drm_sis_mem_t mem; int retval = 0; + drm_memblock_item_t *item; + + DRM_COPY_FROM_USER_IOCTL(mem, argp, sizeof(mem)); - if (dev_priv == NULL || dev_priv->FBHeap == NULL) + mutex_lock(&dev->struct_mutex); + + if (0 == ((pool == 0) ? dev_priv->vram_initialized : + dev_priv->agp_initialized)) { + DRM_ERROR + ("Attempt to allocate from uninitialized memory manager.\n"); return DRM_ERR(EINVAL); + } - DRM_COPY_FROM_USER_IOCTL(fb, argp, sizeof(fb)); - - block = mmAllocMem(dev_priv->FBHeap, fb.size, 0, 0); - if (block) { - /* TODO */ - fb.offset = block->ofs; - fb.free = (unsigned long)block; - if (!add_alloc_set(fb.context, VIDEO_TYPE, fb.free)) { - DRM_DEBUG("adding to allocation set fails\n"); - mmFreeMem((PMemBlock) fb.free); - retval = DRM_ERR(EINVAL); - } + mem.size = (mem.size + SIS_MM_ALIGN_MASK) >> SIS_MM_ALIGN_SHIFT; + item = drm_sman_alloc(&dev_priv->sman, pool, mem.size, 0, + (unsigned long)priv); + + mutex_unlock(&dev->struct_mutex); + if (item) { + mem.offset = ((pool == 0) ? + dev_priv->vram_offset : dev_priv->agp_offset) + + (item->mm-> + offset(item->mm, item->mm_info) << SIS_MM_ALIGN_SHIFT); + mem.free = item->user_hash.key; + mem.size = mem.size << SIS_MM_ALIGN_SHIFT; } else { - fb.offset = 0; - fb.size = 0; - fb.free = 0; + mem.offset = 0; + mem.size = 0; + mem.free = 0; + retval = DRM_ERR(ENOMEM); } - DRM_COPY_TO_USER_IOCTL(argp, fb, sizeof(fb)); + DRM_COPY_TO_USER_IOCTL(argp, mem, sizeof(mem)); - DRM_DEBUG("alloc fb, size = %d, offset = %d\n", fb.size, fb.offset); + DRM_DEBUG("alloc %d, size = %d, offset = %d\n", pool, mem.size, + mem.offset); return retval; } -static int sis_fb_free(DRM_IOCTL_ARGS) +static int sis_drm_free(DRM_IOCTL_ARGS) { DRM_DEVICE; drm_sis_private_t *dev_priv = dev->dev_private; - drm_sis_mem_t fb; + drm_sis_mem_t mem; + int ret; - if (dev_priv == NULL || dev_priv->FBHeap == NULL) - return DRM_ERR(EINVAL); + DRM_COPY_FROM_USER_IOCTL(mem, (drm_sis_mem_t __user *) data, + sizeof(mem)); - DRM_COPY_FROM_USER_IOCTL(fb, (drm_sis_mem_t __user *) data, sizeof(fb)); + mutex_lock(&dev->struct_mutex); + ret = drm_sman_free_key(&dev_priv->sman, mem.free); + mutex_unlock(&dev->struct_mutex); + DRM_DEBUG("free = 0x%lx\n", mem.free); - if (!mmBlockInHeap(dev_priv->FBHeap, (PMemBlock) fb.free)) - return DRM_ERR(EINVAL); - - if (!del_alloc_set(fb.context, VIDEO_TYPE, fb.free)) - return DRM_ERR(EINVAL); - mmFreeMem((PMemBlock) fb.free); - - DRM_DEBUG("free fb, free = 0x%lx\n", fb.free); - - return 0; + return ret; } -#endif - -/* agp memory management */ +static int sis_fb_alloc(DRM_IOCTL_ARGS) +{ + DRM_DEVICE; + return sis_drm_alloc(dev, priv, data, VIDEO_TYPE); +} static int sis_ioctl_agp_init(DRM_IOCTL_ARGS) { DRM_DEVICE; drm_sis_private_t *dev_priv = dev->dev_private; drm_sis_agp_t agp; - - if (dev_priv == NULL) { - dev->dev_private = drm_calloc(1, sizeof(drm_sis_private_t), - DRM_MEM_DRIVER); - dev_priv = dev->dev_private; - if (dev_priv == NULL) - return ENOMEM; - } - - if (dev_priv->AGPHeap != NULL) - return DRM_ERR(EINVAL); + int ret; + dev_priv = dev->dev_private; DRM_COPY_FROM_USER_IOCTL(agp, (drm_sis_agp_t __user *) data, sizeof(agp)); + mutex_lock(&dev->struct_mutex); + ret = drm_sman_set_range(&dev_priv->sman, AGP_TYPE, 0, + agp.size >> SIS_MM_ALIGN_SHIFT); + + if (ret) { + DRM_ERROR("AGP memory manager initialisation error\n"); + mutex_unlock(&dev->struct_mutex); + return ret; + } - dev_priv->AGPHeap = mmInit(agp.offset, agp.size); + dev_priv->agp_initialized = 1; + dev_priv->agp_offset = agp.offset; + mutex_unlock(&dev->struct_mutex); DRM_DEBUG("offset = %u, size = %u", agp.offset, agp.size); - return 0; } static int sis_ioctl_agp_alloc(DRM_IOCTL_ARGS) { DRM_DEVICE; - drm_sis_private_t *dev_priv = dev->dev_private; - drm_sis_mem_t __user *argp = (drm_sis_mem_t __user *)data; - drm_sis_mem_t agp; - PMemBlock block; - int retval = 0; - if (dev_priv == NULL || dev_priv->AGPHeap == NULL) - return DRM_ERR(EINVAL); + return sis_drm_alloc(dev, priv, data, AGP_TYPE); +} - DRM_COPY_FROM_USER_IOCTL(agp, argp, sizeof(agp)); - - block = mmAllocMem(dev_priv->AGPHeap, agp.size, 0, 0); - if (block) { - /* TODO */ - agp.offset = block->ofs; - agp.free = (unsigned long)block; - if (!add_alloc_set(agp.context, AGP_TYPE, agp.free)) { - DRM_DEBUG("adding to allocation set fails\n"); - mmFreeMem((PMemBlock) agp.free); - retval = -1; +static drm_local_map_t *sis_reg_init(drm_device_t *dev) +{ + drm_map_list_t *entry; + drm_local_map_t *map; + + list_for_each_entry(entry, &dev->maplist->head, head) { + map = entry->map; + if (!map) + continue; + if (map->type == _DRM_REGISTERS) { + return map; } - } else { - agp.offset = 0; - agp.size = 0; - agp.free = 0; } - - DRM_COPY_TO_USER_IOCTL(argp, agp, sizeof(agp)); - - DRM_DEBUG("alloc agp, size = %d, offset = %d\n", agp.size, agp.offset); - - return retval; + return NULL; } -static int sis_ioctl_agp_free(DRM_IOCTL_ARGS) +int sis_idle(drm_device_t *dev) { - DRM_DEVICE; drm_sis_private_t *dev_priv = dev->dev_private; - drm_sis_mem_t agp; - - if (dev_priv == NULL || dev_priv->AGPHeap == NULL) - return DRM_ERR(EINVAL); + uint32_t idle_reg; + unsigned long end; + int i; - DRM_COPY_FROM_USER_IOCTL(agp, (drm_sis_mem_t __user *) data, - sizeof(agp)); + if (dev_priv->idle_fault) + return 0; - if (!mmBlockInHeap(dev_priv->AGPHeap, (PMemBlock) agp.free)) - return DRM_ERR(EINVAL); + if (dev_priv->mmio == NULL) { + dev_priv->mmio = sis_reg_init(dev); + if (dev_priv->mmio == NULL) { + DRM_ERROR("Could not find register map.\n"); + return 0; + } + } + + /* + * Implement a device switch here if needed + */ + + if (dev_priv->chipset != SIS_CHIP_315) + return 0; + + /* + * Timeout after 3 seconds. We cannot use DRM_WAIT_ON here + * because its polling frequency is too low. + */ + + end = jiffies + (DRM_HZ * 3); + + for (i=0; i<4; ++i) { + do { + idle_reg = SIS_READ(0x85cc); + } while ( !time_after_eq(jiffies, end) && + ((idle_reg & 0x80000000) != 0x80000000)); + } - mmFreeMem((PMemBlock) agp.free); - if (!del_alloc_set(agp.context, AGP_TYPE, agp.free)) - return DRM_ERR(EINVAL); + if (time_after_eq(jiffies, end)) { + DRM_ERROR("Graphics engine idle timeout. " + "Disabling idle check\n"); + dev_priv->idle_fault = 1; + } - DRM_DEBUG("free agp, free = 0x%lx\n", agp.free); + /* + * The caller never sees an error code. It gets trapped + * in libdrm. + */ return 0; } -int sis_init_context(struct drm_device *dev, int context) -{ - int i; - for (i = 0; i < MAX_CONTEXT; i++) { - if (global_ppriv[i].used && - (global_ppriv[i].context == context)) - break; - } +void sis_lastclose(struct drm_device *dev) +{ + drm_sis_private_t *dev_priv = dev->dev_private; - if (i >= MAX_CONTEXT) { - for (i = 0; i < MAX_CONTEXT; i++) { - if (!global_ppriv[i].used) { - global_ppriv[i].context = context; - global_ppriv[i].used = 1; - global_ppriv[i].sets[0] = setInit(); - global_ppriv[i].sets[1] = setInit(); - DRM_DEBUG("init allocation set, socket=%d, " - "context = %d\n", i, context); - break; - } - } - if ((i >= MAX_CONTEXT) || (global_ppriv[i].sets[0] == NULL) || - (global_ppriv[i].sets[1] == NULL)) { - return 0; - } - } + if (!dev_priv) + return; - return 1; + mutex_lock(&dev->struct_mutex); + drm_sman_cleanup(&dev_priv->sman); + dev_priv->vram_initialized = 0; + dev_priv->agp_initialized = 0; + dev_priv->mmio = NULL; + mutex_unlock(&dev->struct_mutex); } -int sis_final_context(struct drm_device *dev, int context) +void sis_reclaim_buffers_locked(drm_device_t * dev, struct file *filp) { - int i; + drm_sis_private_t *dev_priv = dev->dev_private; + drm_file_t *priv = filp->private_data; - for (i = 0; i < MAX_CONTEXT; i++) { - if (global_ppriv[i].used && - (global_ppriv[i].context == context)) - break; + mutex_lock(&dev->struct_mutex); + if (drm_sman_owner_clean(&dev_priv->sman, (unsigned long)priv)) { + mutex_unlock(&dev->struct_mutex); + return; } - if (i < MAX_CONTEXT) { - set_t *set; - ITEM_TYPE item; - int retval; - - DRM_DEBUG("find socket %d, context = %d\n", i, context); - - /* Video Memory */ - set = global_ppriv[i].sets[0]; - retval = setFirst(set, &item); - while (retval) { - DRM_DEBUG("free video memory 0x%lx\n", item); -#if defined(__linux__) && defined(CONFIG_FB_SIS) - sis_free(item); -#else - mmFreeMem((PMemBlock) item); -#endif - retval = setNext(set, &item); - } - setDestroy(set); - - /* AGP Memory */ - set = global_ppriv[i].sets[1]; - retval = setFirst(set, &item); - while (retval) { - DRM_DEBUG("free agp memory 0x%lx\n", item); - mmFreeMem((PMemBlock) item); - retval = setNext(set, &item); - } - setDestroy(set); - - global_ppriv[i].used = 0; + if (dev->driver->dma_quiescent) { + dev->driver->dma_quiescent(dev); } - return 1; + drm_sman_owner_cleanup(&dev_priv->sman, (unsigned long)priv); + mutex_unlock(&dev->struct_mutex); + return; } drm_ioctl_desc_t sis_ioctls[] = { [DRM_IOCTL_NR(DRM_SIS_FB_ALLOC)] = {sis_fb_alloc, DRM_AUTH}, - [DRM_IOCTL_NR(DRM_SIS_FB_FREE)] = {sis_fb_free, DRM_AUTH}, - [DRM_IOCTL_NR(DRM_SIS_AGP_INIT)] = {sis_ioctl_agp_init, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY}, + [DRM_IOCTL_NR(DRM_SIS_FB_FREE)] = {sis_drm_free, DRM_AUTH}, + [DRM_IOCTL_NR(DRM_SIS_AGP_INIT)] = + {sis_ioctl_agp_init, DRM_AUTH | DRM_MASTER | DRM_ROOT_ONLY}, [DRM_IOCTL_NR(DRM_SIS_AGP_ALLOC)] = {sis_ioctl_agp_alloc, DRM_AUTH}, - [DRM_IOCTL_NR(DRM_SIS_AGP_FREE)] = {sis_ioctl_agp_free, DRM_AUTH}, - [DRM_IOCTL_NR(DRM_SIS_FB_INIT)] = {sis_fb_init, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY} + [DRM_IOCTL_NR(DRM_SIS_AGP_FREE)] = {sis_drm_free, DRM_AUTH}, + [DRM_IOCTL_NR(DRM_SIS_FB_INIT)] = + {sis_fb_init, DRM_AUTH | DRM_MASTER | DRM_ROOT_ONLY} }; int sis_max_ioctl = DRM_ARRAY_SIZE(sis_ioctls); diff --git a/drivers/char/drm/via_dmablit.c b/drivers/char/drm/via_dmablit.c index 78a81a4a99c..60c1695db30 100644 --- a/drivers/char/drm/via_dmablit.c +++ b/drivers/char/drm/via_dmablit.c @@ -41,9 +41,9 @@ #include <linux/pagemap.h> -#define VIA_PGDN(x) (((unsigned long)(x)) & PAGE_MASK) -#define VIA_PGOFF(x) (((unsigned long)(x)) & ~PAGE_MASK) -#define VIA_PFN(x) ((unsigned long)(x) >> PAGE_SHIFT) +#define VIA_PGDN(x) (((unsigned long)(x)) & PAGE_MASK) +#define VIA_PGOFF(x) (((unsigned long)(x)) & ~PAGE_MASK) +#define VIA_PFN(x) ((unsigned long)(x) >> PAGE_SHIFT) typedef struct _drm_via_descriptor { uint32_t mem_addr; @@ -121,19 +121,19 @@ via_map_blit_for_device(struct pci_dev *pdev, while (line_len > 0) { - remaining_len = min(PAGE_SIZE-VIA_PGOFF(cur_mem), line_len); + remaining_len = min(PAGE_SIZE-VIA_PGOFF(cur_mem), line_len); line_len -= remaining_len; if (mode == 1) { - desc_ptr->mem_addr = + desc_ptr->mem_addr = dma_map_page(&pdev->dev, vsg->pages[VIA_PFN(cur_mem) - VIA_PFN(first_addr)], VIA_PGOFF(cur_mem), remaining_len, vsg->direction); - desc_ptr->dev_addr = cur_fb; + desc_ptr->dev_addr = cur_fb; - desc_ptr->size = remaining_len; + desc_ptr->size = remaining_len; desc_ptr->next = (uint32_t) next; next = dma_map_single(&pdev->dev, desc_ptr, sizeof(*desc_ptr), DMA_TO_DEVICE); @@ -162,7 +162,7 @@ via_map_blit_for_device(struct pci_dev *pdev, /* * Function that frees up all resources for a blit. It is usable even if the - * blit info has only be partially built as long as the status enum is consistent + * blit info has only been partially built as long as the status enum is consistent * with the actual status of the used resources. */ @@ -238,8 +238,11 @@ via_lock_all_dma_pages(drm_via_sg_info_t *vsg, drm_via_dmablit_t *xfer) return DRM_ERR(ENOMEM); memset(vsg->pages, 0, sizeof(struct page *) * vsg->num_pages); down_read(¤t->mm->mmap_sem); - ret = get_user_pages(current, current->mm, (unsigned long) xfer->mem_addr, - vsg->num_pages, vsg->direction, 0, vsg->pages, NULL); + ret = get_user_pages(current, current->mm, + (unsigned long)xfer->mem_addr, + vsg->num_pages, + (vsg->direction == DMA_FROM_DEVICE), + 0, vsg->pages, NULL); up_read(¤t->mm->mmap_sem); if (ret != vsg->num_pages) { @@ -475,9 +478,15 @@ via_dmablit_timer(unsigned long data) if (!timer_pending(&blitq->poll_timer)) { blitq->poll_timer.expires = jiffies+1; add_timer(&blitq->poll_timer); - } - via_dmablit_handler(dev, engine, 0); + /* + * Rerun handler to delete timer if engines are off, and + * to shorten abort latency. This is a little nasty. + */ + + via_dmablit_handler(dev, engine, 0); + + } } @@ -597,15 +606,27 @@ via_build_sg_info(drm_device_t *dev, drm_via_sg_info_t *vsg, drm_via_dmablit_t * * (Not a big limitation anyway.) */ - if (((xfer->mem_stride - xfer->line_length) >= PAGE_SIZE) || - (xfer->mem_stride > 2048*4)) { + if ((xfer->mem_stride - xfer->line_length) >= PAGE_SIZE) { DRM_ERROR("Too large system memory stride. Stride: %d, " "Length: %d\n", xfer->mem_stride, xfer->line_length); return DRM_ERR(EINVAL); } - if (xfer->num_lines > 2048) { - DRM_ERROR("Too many PCI DMA bitblt lines.\n"); + if ((xfer->mem_stride == xfer->line_length) && + (xfer->fb_stride == xfer->line_length)) { + xfer->mem_stride *= xfer->num_lines; + xfer->line_length = xfer->mem_stride; + xfer->fb_stride = xfer->mem_stride; + xfer->num_lines = 1; + } + + /* + * Don't lock an arbitrary large number of pages, since that causes a + * DOS security hole. + */ + + if (xfer->num_lines > 2048 || (xfer->num_lines*xfer->mem_stride > (2048*2048*4))) { + DRM_ERROR("Too large PCI DMA bitblt.\n"); return DRM_ERR(EINVAL); } @@ -628,16 +649,17 @@ via_build_sg_info(drm_device_t *dev, drm_via_sg_info_t *vsg, drm_via_dmablit_t * #ifdef VIA_BUGFREE if ((((unsigned long)xfer->mem_addr & 3) != ((unsigned long)xfer->fb_addr & 3)) || - ((xfer->mem_stride & 3) != (xfer->fb_stride & 3))) { + ((xfer->num_lines > 1) && ((xfer->mem_stride & 3) != (xfer->fb_stride & 3)))) { DRM_ERROR("Invalid DRM bitblt alignment.\n"); - return DRM_ERR(EINVAL); + return DRM_ERR(EINVAL); } #else if ((((unsigned long)xfer->mem_addr & 15) || - ((unsigned long)xfer->fb_addr & 3)) || (xfer->mem_stride & 15) || - (xfer->fb_stride & 3)) { + ((unsigned long)xfer->fb_addr & 3)) || + ((xfer->num_lines > 1) && + ((xfer->mem_stride & 15) || (xfer->fb_stride & 3)))) { DRM_ERROR("Invalid DRM bitblt alignment.\n"); - return DRM_ERR(EINVAL); + return DRM_ERR(EINVAL); } #endif @@ -715,7 +737,7 @@ via_dmablit(drm_device_t *dev, drm_via_dmablit_t *xfer) drm_via_private_t *dev_priv = (drm_via_private_t *)dev->dev_private; drm_via_sg_info_t *vsg; drm_via_blitq_t *blitq; - int ret; + int ret; int engine; unsigned long irqsave; @@ -756,7 +778,7 @@ via_dmablit(drm_device_t *dev, drm_via_dmablit_t *xfer) /* * Sync on a previously submitted blit. Note that the X server use signals extensively, and - * that there is a very big proability that this IOCTL will be interrupted by a signal. In that + * that there is a very big probability that this IOCTL will be interrupted by a signal. In that * case it returns with -EAGAIN for the signal to be delivered. * The caller should then reissue the IOCTL. This is similar to what is being done for drmGetLock(). */ diff --git a/drivers/char/drm/via_drm.h b/drivers/char/drm/via_drm.h index 47f0b5b2637..e4ee97d7156 100644 --- a/drivers/char/drm/via_drm.h +++ b/drivers/char/drm/via_drm.h @@ -250,6 +250,12 @@ typedef struct drm_via_blitsync { unsigned engine; } drm_via_blitsync_t; +/* - * Below,"flags" is currently unused but will be used for possible future + * extensions like kernel space bounce buffers for bad alignments and + * blit engine busy-wait polling for better latency in the absence of + * interrupts. + */ + typedef struct drm_via_dmablit { uint32_t num_lines; uint32_t line_length; @@ -260,7 +266,7 @@ typedef struct drm_via_dmablit { unsigned char *mem_addr; uint32_t mem_stride; - int bounce_buffer; + uint32_t flags; int to_fb; drm_via_blitsync_t sync; diff --git a/drivers/char/drm/via_drv.c b/drivers/char/drm/via_drv.c index b3d364d793d..bb9dde8b191 100644 --- a/drivers/char/drm/via_drv.c +++ b/drivers/char/drm/via_drv.c @@ -43,7 +43,6 @@ static struct drm_driver driver = { DRIVER_IRQ_SHARED | DRIVER_IRQ_VBL, .load = via_driver_load, .unload = via_driver_unload, - .context_ctor = via_init_context, .context_dtor = via_final_context, .vblank_wait = via_driver_vblank_wait, .irq_preinstall = via_driver_irq_preinstall, @@ -53,6 +52,8 @@ static struct drm_driver driver = { .dma_quiescent = via_driver_dma_quiescent, .dri_library_name = dri_library_name, .reclaim_buffers = drm_core_reclaim_buffers, + .reclaim_buffers_locked = via_reclaim_buffers_locked, + .lastclose = via_lastclose, .get_map_ofs = drm_core_get_map_ofs, .get_reg_ofs = drm_core_get_reg_ofs, .ioctls = via_ioctls, diff --git a/drivers/char/drm/via_drv.h b/drivers/char/drm/via_drv.h index 52bcc7b1ba4..d21b5b75da0 100644 --- a/drivers/char/drm/via_drv.h +++ b/drivers/char/drm/via_drv.h @@ -24,15 +24,16 @@ #ifndef _VIA_DRV_H_ #define _VIA_DRV_H_ +#include "drm_sman.h" #define DRIVER_AUTHOR "Various" #define DRIVER_NAME "via" #define DRIVER_DESC "VIA Unichrome / Pro" -#define DRIVER_DATE "20051116" +#define DRIVER_DATE "20060529" #define DRIVER_MAJOR 2 -#define DRIVER_MINOR 7 -#define DRIVER_PATCHLEVEL 4 +#define DRIVER_MINOR 10 +#define DRIVER_PATCHLEVEL 0 #include "via_verifier.h" @@ -85,6 +86,12 @@ typedef struct drm_via_private { uint32_t irq_enable_mask; uint32_t irq_pending_mask; int *irq_map; + unsigned int idle_fault; + drm_sman_t sman; + int vram_initialized; + int agp_initialized; + unsigned long vram_offset; + unsigned long agp_offset; drm_via_blitq_t blit_queues[VIA_NUM_BLIT_ENGINES]; } drm_via_private_t; @@ -135,6 +142,9 @@ extern void via_init_futex(drm_via_private_t * dev_priv); extern void via_cleanup_futex(drm_via_private_t * dev_priv); extern void via_release_futex(drm_via_private_t * dev_priv, int context); +extern void via_reclaim_buffers_locked(drm_device_t *dev, struct file *filp); +extern void via_lastclose(drm_device_t *dev); + extern void via_dmablit_handler(drm_device_t *dev, int engine, int from_irq); extern void via_init_dmablit(drm_device_t *dev); diff --git a/drivers/char/drm/via_ds.c b/drivers/char/drm/via_ds.c deleted file mode 100644 index 9429736b3b9..00000000000 --- a/drivers/char/drm/via_ds.c +++ /dev/null @@ -1,273 +0,0 @@ -/* - * Copyright 1998-2003 VIA Technologies, Inc. All Rights Reserved. - * Copyright 2001-2003 S3 Graphics, Inc. All Rights Reserved. - * Copyright 2000 Silicon Integrated Systems Corp, Inc., HsinChu, Taiwan. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sub license, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the - * next paragraph) shall be included in all copies or substantial portions - * of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL - * VIA, S3 GRAPHICS, AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - */ -#include "drmP.h" - -#include "via_ds.h" -extern unsigned int VIA_DEBUG; - -set_t *via_setInit(void) -{ - int i; - set_t *set; - set = (set_t *) drm_alloc(sizeof(set_t), DRM_MEM_DRIVER); - for (i = 0; i < SET_SIZE; i++) { - set->list[i].free_next = i + 1; - set->list[i].alloc_next = -1; - } - set->list[SET_SIZE - 1].free_next = -1; - set->free = 0; - set->alloc = -1; - set->trace = -1; - return set; -} - -int via_setAdd(set_t * set, ITEM_TYPE item) -{ - int free = set->free; - if (free != -1) { - set->list[free].val = item; - set->free = set->list[free].free_next; - } else { - return 0; - } - set->list[free].alloc_next = set->alloc; - set->alloc = free; - set->list[free].free_next = -1; - return 1; -} - -int via_setDel(set_t * set, ITEM_TYPE item) -{ - int alloc = set->alloc; - int prev = -1; - - while (alloc != -1) { - if (set->list[alloc].val == item) { - if (prev != -1) - set->list[prev].alloc_next = - set->list[alloc].alloc_next; - else - set->alloc = set->list[alloc].alloc_next; - break; - } - prev = alloc; - alloc = set->list[alloc].alloc_next; - } - - if (alloc == -1) - return 0; - - set->list[alloc].free_next = set->free; - set->free = alloc; - set->list[alloc].alloc_next = -1; - - return 1; -} - -/* setFirst -> setAdd -> setNext is wrong */ - -int via_setFirst(set_t * set, ITEM_TYPE * item) -{ - if (set->alloc == -1) - return 0; - - *item = set->list[set->alloc].val; - set->trace = set->list[set->alloc].alloc_next; - - return 1; -} - -int via_setNext(set_t * set, ITEM_TYPE * item) -{ - if (set->trace == -1) - return 0; - - *item = set->list[set->trace].val; - set->trace = set->list[set->trace].alloc_next; - - return 1; -} - -int via_setDestroy(set_t * set) -{ - drm_free(set, sizeof(set_t), DRM_MEM_DRIVER); - - return 1; -} - -#define ISFREE(bptr) ((bptr)->free) - -#define fprintf(fmt, arg...) do{}while(0) - -memHeap_t *via_mmInit(int ofs, int size) -{ - PMemBlock blocks; - - if (size <= 0) - return NULL; - - blocks = (TMemBlock *) drm_calloc(1, sizeof(TMemBlock), DRM_MEM_DRIVER); - - if (blocks) { - blocks->ofs = ofs; - blocks->size = size; - blocks->free = 1; - return (memHeap_t *) blocks; - } else - return NULL; -} - -static TMemBlock *SliceBlock(TMemBlock * p, - int startofs, int size, - int reserved, int alignment) -{ - TMemBlock *newblock; - - /* break left */ - if (startofs > p->ofs) { - newblock = - (TMemBlock *) drm_calloc(1, sizeof(TMemBlock), - DRM_MEM_DRIVER); - newblock->ofs = startofs; - newblock->size = p->size - (startofs - p->ofs); - newblock->free = 1; - newblock->next = p->next; - p->size -= newblock->size; - p->next = newblock; - p = newblock; - } - - /* break right */ - if (size < p->size) { - newblock = - (TMemBlock *) drm_calloc(1, sizeof(TMemBlock), - DRM_MEM_DRIVER); - newblock->ofs = startofs + size; - newblock->size = p->size - size; - newblock->free = 1; - newblock->next = p->next; - p->size = size; - p->next = newblock; - } - - /* p = middle block */ - p->align = alignment; - p->free = 0; - p->reserved = reserved; - return p; -} - -PMemBlock via_mmAllocMem(memHeap_t * heap, int size, int align2, - int startSearch) -{ - int mask, startofs, endofs; - TMemBlock *p; - - if (!heap || align2 < 0 || size <= 0) - return NULL; - - mask = (1 << align2) - 1; - startofs = 0; - p = (TMemBlock *) heap; - - while (p) { - if (ISFREE(p)) { - startofs = (p->ofs + mask) & ~mask; - - if (startofs < startSearch) - startofs = startSearch; - - endofs = startofs + size; - - if (endofs <= (p->ofs + p->size)) - break; - } - - p = p->next; - } - - if (!p) - return NULL; - - p = SliceBlock(p, startofs, size, 0, mask + 1); - p->heap = heap; - - return p; -} - -static __inline__ int Join2Blocks(TMemBlock * p) -{ - if (p->free && p->next && p->next->free) { - TMemBlock *q = p->next; - p->size += q->size; - p->next = q->next; - drm_free(q, sizeof(TMemBlock), DRM_MEM_DRIVER); - - return 1; - } - - return 0; -} - -int via_mmFreeMem(PMemBlock b) -{ - TMemBlock *p, *prev; - - if (!b) - return 0; - - if (!b->heap) { - fprintf(stderr, "no heap\n"); - - return -1; - } - - p = b->heap; - prev = NULL; - - while (p && p != b) { - prev = p; - p = p->next; - } - - if (!p || p->free || p->reserved) { - if (!p) - fprintf(stderr, "block not found in heap\n"); - else if (p->free) - fprintf(stderr, "block already free\n"); - else - fprintf(stderr, "block is reserved\n"); - - return -1; - } - - p->free = 1; - Join2Blocks(p); - - if (prev) - Join2Blocks(prev); - - return 0; -} diff --git a/drivers/char/drm/via_ds.h b/drivers/char/drm/via_ds.h deleted file mode 100644 index d2bb9f37ca3..00000000000 --- a/drivers/char/drm/via_ds.h +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright 1998-2003 VIA Technologies, Inc. All Rights Reserved. - * Copyright 2001-2003 S3 Graphics, Inc. All Rights Reserved. - * Copyright 2000 Silicon Integrated Systems Corp, Inc., HsinChu, Taiwan. - * All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sub license, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the - * next paragraph) shall be included in all copies or substantial portions - * of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL - * VIA, S3 GRAPHICS, AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - */ -#ifndef _via_ds_h_ -#define _via_ds_h_ - -#include "drmP.h" - -/* Set Data Structure */ -#define SET_SIZE 5000 -typedef unsigned long ITEM_TYPE; - -typedef struct { - ITEM_TYPE val; - int alloc_next, free_next; -} list_item_t; - -typedef struct { - int alloc; - int free; - int trace; - list_item_t list[SET_SIZE]; -} set_t; - -set_t *via_setInit(void); -int via_setAdd(set_t * set, ITEM_TYPE item); -int via_setDel(set_t * set, ITEM_TYPE item); -int via_setFirst(set_t * set, ITEM_TYPE * item); -int via_setNext(set_t * set, ITEM_TYPE * item); -int via_setDestroy(set_t * set); - -#endif - -#ifndef MM_INC -#define MM_INC - -struct mem_block_t { - struct mem_block_t *next; - struct mem_block_t *heap; - int ofs, size; - int align; - unsigned int free:1; - unsigned int reserved:1; -}; -typedef struct mem_block_t TMemBlock; -typedef struct mem_block_t *PMemBlock; - -/* a heap is just the first block in a chain */ -typedef struct mem_block_t memHeap_t; - -static __inline__ int mmBlockSize(PMemBlock b) -{ - return b->size; -} - -static __inline__ int mmOffset(PMemBlock b) -{ - return b->ofs; -} - -static __inline__ void mmMarkReserved(PMemBlock b) -{ - b->reserved = 1; -} - -/* - * input: total size in bytes - * return: a heap pointer if OK, NULL if error - */ -memHeap_t *via_mmInit(int ofs, int size); - -PMemBlock via_mmAllocMem(memHeap_t * heap, int size, int align2, - int startSearch); - -/* - * Free block starts at offset - * input: pointer to a block - * return: 0 if OK, -1 if error - */ -int via_mmFreeMem(PMemBlock b); - -#endif diff --git a/drivers/char/drm/via_map.c b/drivers/char/drm/via_map.c index c6a08e96285..782011e0a58 100644 --- a/drivers/char/drm/via_map.c +++ b/drivers/char/drm/via_map.c @@ -98,6 +98,7 @@ int via_map_init(DRM_IOCTL_ARGS) int via_driver_load(drm_device_t *dev, unsigned long chipset) { drm_via_private_t *dev_priv; + int ret = 0; dev_priv = drm_calloc(1, sizeof(drm_via_private_t), DRM_MEM_DRIVER); if (dev_priv == NULL) @@ -108,13 +109,19 @@ int via_driver_load(drm_device_t *dev, unsigned long chipset) if (chipset == VIA_PRO_GROUP_A) dev_priv->pro_group_a = 1; - return 0; + ret = drm_sman_init(&dev_priv->sman, 2, 12, 8); + if (ret) { + drm_free(dev_priv, sizeof(*dev_priv), DRM_MEM_DRIVER); + } + return ret; } int via_driver_unload(drm_device_t *dev) { drm_via_private_t *dev_priv = dev->dev_private; + drm_sman_takedown(&dev_priv->sman); + drm_free(dev_priv, sizeof(drm_via_private_t), DRM_MEM_DRIVER); return 0; diff --git a/drivers/char/drm/via_mm.c b/drivers/char/drm/via_mm.c index 33e0cb12e4c..2fcf0577a7a 100644 --- a/drivers/char/drm/via_mm.c +++ b/drivers/char/drm/via_mm.c @@ -1,6 +1,6 @@ /* - * Copyright 1998-2003 VIA Technologies, Inc. All Rights Reserved. - * Copyright 2001-2003 S3 Graphics, Inc. All Rights Reserved. + * Copyright 2006 Tungsten Graphics Inc., Bismarck, ND., USA. + * All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -16,347 +16,194 @@ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL - * VIA, S3 GRAPHICS, AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * THE AUTHORS OR COPYRIGHT HOLDERS AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ +/* + * Authors: Thomas Hellström <thomas-at-tungstengraphics-dot-com> + */ + #include "drmP.h" #include "via_drm.h" #include "via_drv.h" -#include "via_ds.h" -#include "via_mm.h" - -#define MAX_CONTEXT 100 - -typedef struct { - int used; - int context; - set_t *sets[2]; /* 0 for frame buffer, 1 for AGP , 2 for System */ -} via_context_t; - -static via_context_t global_ppriv[MAX_CONTEXT]; +#include "drm_sman.h" -static int via_agp_alloc(drm_via_mem_t * mem); -static int via_agp_free(drm_via_mem_t * mem); -static int via_fb_alloc(drm_via_mem_t * mem); -static int via_fb_free(drm_via_mem_t * mem); - -static int add_alloc_set(int context, int type, unsigned long val) -{ - int i, retval = 0; - - for (i = 0; i < MAX_CONTEXT; i++) { - if (global_ppriv[i].used && global_ppriv[i].context == context) { - retval = via_setAdd(global_ppriv[i].sets[type], val); - break; - } - } - - return retval; -} - -static int del_alloc_set(int context, int type, unsigned long val) -{ - int i, retval = 0; - - for (i = 0; i < MAX_CONTEXT; i++) - if (global_ppriv[i].used && global_ppriv[i].context == context) { - retval = via_setDel(global_ppriv[i].sets[type], val); - break; - } - - return retval; -} - -/* agp memory management */ -static memHeap_t *AgpHeap = NULL; +#define VIA_MM_ALIGN_SHIFT 4 +#define VIA_MM_ALIGN_MASK ( (1 << VIA_MM_ALIGN_SHIFT) - 1) int via_agp_init(DRM_IOCTL_ARGS) { + DRM_DEVICE; drm_via_agp_t agp; + drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private; + int ret; DRM_COPY_FROM_USER_IOCTL(agp, (drm_via_agp_t __user *) data, sizeof(agp)); + mutex_lock(&dev->struct_mutex); + ret = drm_sman_set_range(&dev_priv->sman, VIA_MEM_AGP, 0, + agp.size >> VIA_MM_ALIGN_SHIFT); + + if (ret) { + DRM_ERROR("AGP memory manager initialisation error\n"); + mutex_unlock(&dev->struct_mutex); + return ret; + } - AgpHeap = via_mmInit(agp.offset, agp.size); - - DRM_DEBUG("offset = %lu, size = %lu", (unsigned long)agp.offset, - (unsigned long)agp.size); + dev_priv->agp_initialized = 1; + dev_priv->agp_offset = agp.offset; + mutex_unlock(&dev->struct_mutex); + DRM_DEBUG("offset = %u, size = %u", agp.offset, agp.size); return 0; } -/* fb memory management */ -static memHeap_t *FBHeap = NULL; - int via_fb_init(DRM_IOCTL_ARGS) { + DRM_DEVICE; drm_via_fb_t fb; + drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private; + int ret; DRM_COPY_FROM_USER_IOCTL(fb, (drm_via_fb_t __user *) data, sizeof(fb)); - FBHeap = via_mmInit(fb.offset, fb.size); + mutex_lock(&dev->struct_mutex); + ret = drm_sman_set_range(&dev_priv->sman, VIA_MEM_VIDEO, 0, + fb.size >> VIA_MM_ALIGN_SHIFT); - DRM_DEBUG("offset = %lu, size = %lu", (unsigned long)fb.offset, - (unsigned long)fb.size); + if (ret) { + DRM_ERROR("VRAM memory manager initialisation error\n"); + mutex_unlock(&dev->struct_mutex); + return ret; + } - return 0; -} + dev_priv->vram_initialized = 1; + dev_priv->vram_offset = fb.offset; -int via_init_context(struct drm_device *dev, int context) -{ - int i; - - for (i = 0; i < MAX_CONTEXT; i++) - if (global_ppriv[i].used && - (global_ppriv[i].context == context)) - break; - - if (i >= MAX_CONTEXT) { - for (i = 0; i < MAX_CONTEXT; i++) { - if (!global_ppriv[i].used) { - global_ppriv[i].context = context; - global_ppriv[i].used = 1; - global_ppriv[i].sets[0] = via_setInit(); - global_ppriv[i].sets[1] = via_setInit(); - DRM_DEBUG("init allocation set, socket=%d," - " context = %d\n", i, context); - break; - } - } - - if ((i >= MAX_CONTEXT) || (global_ppriv[i].sets[0] == NULL) || - (global_ppriv[i].sets[1] == NULL)) { - return 0; - } - } + mutex_unlock(&dev->struct_mutex); + DRM_DEBUG("offset = %u, size = %u", fb.offset, fb.size); + + return 0; - return 1; } int via_final_context(struct drm_device *dev, int context) { - int i; drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private; - for (i = 0; i < MAX_CONTEXT; i++) - if (global_ppriv[i].used && - (global_ppriv[i].context == context)) - break; - - if (i < MAX_CONTEXT) { - set_t *set; - ITEM_TYPE item; - int retval; - - DRM_DEBUG("find socket %d, context = %d\n", i, context); - - /* Video Memory */ - set = global_ppriv[i].sets[0]; - retval = via_setFirst(set, &item); - while (retval) { - DRM_DEBUG("free video memory 0x%lx\n", item); - via_mmFreeMem((PMemBlock) item); - retval = via_setNext(set, &item); - } - via_setDestroy(set); - - /* AGP Memory */ - set = global_ppriv[i].sets[1]; - retval = via_setFirst(set, &item); - while (retval) { - DRM_DEBUG("free agp memory 0x%lx\n", item); - via_mmFreeMem((PMemBlock) item); - retval = via_setNext(set, &item); - } - via_setDestroy(set); - global_ppriv[i].used = 0; - } via_release_futex(dev_priv, context); -#if defined(__linux__) /* Linux specific until context tracking code gets ported to BSD */ /* Last context, perform cleanup */ if (dev->ctx_count == 1 && dev->dev_private) { DRM_DEBUG("Last Context\n"); if (dev->irq) drm_irq_uninstall(dev); - via_cleanup_futex(dev_priv); via_do_cleanup_map(dev); } -#endif - return 1; } +void via_lastclose(struct drm_device *dev) +{ + drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private; + + if (!dev_priv) + return; + + mutex_lock(&dev->struct_mutex); + drm_sman_cleanup(&dev_priv->sman); + dev_priv->vram_initialized = 0; + dev_priv->agp_initialized = 0; + mutex_unlock(&dev->struct_mutex); +} + int via_mem_alloc(DRM_IOCTL_ARGS) { + DRM_DEVICE; + drm_via_mem_t mem; + int retval = 0; + drm_memblock_item_t *item; + drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private; + unsigned long tmpSize; DRM_COPY_FROM_USER_IOCTL(mem, (drm_via_mem_t __user *) data, sizeof(mem)); - switch (mem.type) { - case VIA_MEM_VIDEO: - if (via_fb_alloc(&mem) < 0) - return -EFAULT; - DRM_COPY_TO_USER_IOCTL((drm_via_mem_t __user *) data, mem, - sizeof(mem)); - return 0; - case VIA_MEM_AGP: - if (via_agp_alloc(&mem) < 0) - return -EFAULT; - DRM_COPY_TO_USER_IOCTL((drm_via_mem_t __user *) data, mem, - sizeof(mem)); - return 0; + if (mem.type > VIA_MEM_AGP) { + DRM_ERROR("Unknown memory type allocation\n"); + return DRM_ERR(EINVAL); } - - return -EFAULT; -} - -static int via_fb_alloc(drm_via_mem_t * mem) -{ - drm_via_mm_t fb; - PMemBlock block; - int retval = 0; - - if (!FBHeap) - return -1; - - fb.size = mem->size; - fb.context = mem->context; - - block = via_mmAllocMem(FBHeap, fb.size, 5, 0); - if (block) { - fb.offset = block->ofs; - fb.free = (unsigned long)block; - if (!add_alloc_set(fb.context, VIA_MEM_VIDEO, fb.free)) { - DRM_DEBUG("adding to allocation set fails\n"); - via_mmFreeMem((PMemBlock) fb.free); - retval = -1; - } - } else { - fb.offset = 0; - fb.size = 0; - fb.free = 0; - retval = -1; + mutex_lock(&dev->struct_mutex); + if (0 == ((mem.type == VIA_MEM_VIDEO) ? dev_priv->vram_initialized : + dev_priv->agp_initialized)) { + DRM_ERROR + ("Attempt to allocate from uninitialized memory manager.\n"); + mutex_unlock(&dev->struct_mutex); + return DRM_ERR(EINVAL); } - mem->offset = fb.offset; - mem->index = fb.free; - - DRM_DEBUG("alloc fb, size = %d, offset = %d\n", fb.size, - (int)fb.offset); - - return retval; -} - -static int via_agp_alloc(drm_via_mem_t * mem) -{ - drm_via_mm_t agp; - PMemBlock block; - int retval = 0; - - if (!AgpHeap) - return -1; - - agp.size = mem->size; - agp.context = mem->context; - - block = via_mmAllocMem(AgpHeap, agp.size, 5, 0); - if (block) { - agp.offset = block->ofs; - agp.free = (unsigned long)block; - if (!add_alloc_set(agp.context, VIA_MEM_AGP, agp.free)) { - DRM_DEBUG("adding to allocation set fails\n"); - via_mmFreeMem((PMemBlock) agp.free); - retval = -1; - } + tmpSize = (mem.size + VIA_MM_ALIGN_MASK) >> VIA_MM_ALIGN_SHIFT; + item = drm_sman_alloc(&dev_priv->sman, mem.type, tmpSize, 0, + (unsigned long)priv); + mutex_unlock(&dev->struct_mutex); + if (item) { + mem.offset = ((mem.type == VIA_MEM_VIDEO) ? + dev_priv->vram_offset : dev_priv->agp_offset) + + (item->mm-> + offset(item->mm, item->mm_info) << VIA_MM_ALIGN_SHIFT); + mem.index = item->user_hash.key; } else { - agp.offset = 0; - agp.size = 0; - agp.free = 0; + mem.offset = 0; + mem.size = 0; + mem.index = 0; + DRM_DEBUG("Video memory allocation failed\n"); + retval = DRM_ERR(ENOMEM); } + DRM_COPY_TO_USER_IOCTL((drm_via_mem_t __user *) data, mem, sizeof(mem)); - mem->offset = agp.offset; - mem->index = agp.free; - - DRM_DEBUG("alloc agp, size = %d, offset = %d\n", agp.size, - (unsigned int)agp.offset); return retval; } int via_mem_free(DRM_IOCTL_ARGS) { + DRM_DEVICE; + drm_via_private_t *dev_priv = dev->dev_private; drm_via_mem_t mem; + int ret; DRM_COPY_FROM_USER_IOCTL(mem, (drm_via_mem_t __user *) data, sizeof(mem)); - switch (mem.type) { + mutex_lock(&dev->struct_mutex); + ret = drm_sman_free_key(&dev_priv->sman, mem.index); + mutex_unlock(&dev->struct_mutex); + DRM_DEBUG("free = 0x%lx\n", mem.index); - case VIA_MEM_VIDEO: - if (via_fb_free(&mem) == 0) - return 0; - break; - case VIA_MEM_AGP: - if (via_agp_free(&mem) == 0) - return 0; - break; - } - - return -EFAULT; + return ret; } -static int via_fb_free(drm_via_mem_t * mem) -{ - drm_via_mm_t fb; - int retval = 0; - - if (!FBHeap) { - return -1; - } - - fb.free = mem->index; - fb.context = mem->context; - - if (!fb.free) { - return -1; - - } - - via_mmFreeMem((PMemBlock) fb.free); - - if (!del_alloc_set(fb.context, VIA_MEM_VIDEO, fb.free)) { - retval = -1; - } - - DRM_DEBUG("free fb, free = %ld\n", fb.free); - return retval; -} - -static int via_agp_free(drm_via_mem_t * mem) +void via_reclaim_buffers_locked(drm_device_t * dev, struct file *filp) { - drm_via_mm_t agp; - - int retval = 0; + drm_via_private_t *dev_priv = dev->dev_private; + drm_file_t *priv = filp->private_data; - agp.free = mem->index; - agp.context = mem->context; - - if (!agp.free) - return -1; - - via_mmFreeMem((PMemBlock) agp.free); - - if (!del_alloc_set(agp.context, VIA_MEM_AGP, agp.free)) { - retval = -1; + mutex_lock(&dev->struct_mutex); + if (drm_sman_owner_clean(&dev_priv->sman, (unsigned long)priv)) { + mutex_unlock(&dev->struct_mutex); + return; } - DRM_DEBUG("free agp, free = %ld\n", agp.free); + if (dev->driver->dma_quiescent) { + dev->driver->dma_quiescent(dev); + } - return retval; + drm_sman_owner_cleanup(&dev_priv->sman, (unsigned long)priv); + mutex_unlock(&dev->struct_mutex); + return; } diff --git a/drivers/char/ds1286.c b/drivers/char/ds1286.c index 21c8229f544..6d58b037080 100644 --- a/drivers/char/ds1286.c +++ b/drivers/char/ds1286.c @@ -104,7 +104,7 @@ static int ds1286_ioctl(struct inode *inode, struct file *file, switch (cmd) { case RTC_AIE_OFF: /* Mask alarm int. enab. bit */ { - unsigned int flags; + unsigned long flags; unsigned char val; if (!capable(CAP_SYS_TIME)) @@ -120,7 +120,7 @@ static int ds1286_ioctl(struct inode *inode, struct file *file, } case RTC_AIE_ON: /* Allow alarm interrupts. */ { - unsigned int flags; + unsigned long flags; unsigned char val; if (!capable(CAP_SYS_TIME)) @@ -136,7 +136,7 @@ static int ds1286_ioctl(struct inode *inode, struct file *file, } case RTC_WIE_OFF: /* Mask watchdog int. enab. bit */ { - unsigned int flags; + unsigned long flags; unsigned char val; if (!capable(CAP_SYS_TIME)) @@ -152,7 +152,7 @@ static int ds1286_ioctl(struct inode *inode, struct file *file, } case RTC_WIE_ON: /* Allow watchdog interrupts. */ { - unsigned int flags; + unsigned long flags; unsigned char val; if (!capable(CAP_SYS_TIME)) @@ -434,7 +434,7 @@ static inline unsigned char ds1286_is_updating(void) static void ds1286_get_time(struct rtc_time *rtc_tm) { unsigned char save_control; - unsigned int flags; + unsigned long flags; unsigned long uip_watchdog = jiffies; /* @@ -494,7 +494,8 @@ static int ds1286_set_time(struct rtc_time *rtc_tm) { unsigned char mon, day, hrs, min, sec, leap_yr; unsigned char save_control; - unsigned int yrs, flags; + unsigned int yrs; + unsigned long flags; yrs = rtc_tm->tm_year + 1900; @@ -552,7 +553,7 @@ static int ds1286_set_time(struct rtc_time *rtc_tm) static void ds1286_get_alm_time(struct rtc_time *alm_tm) { unsigned char cmd; - unsigned int flags; + unsigned long flags; /* * Only the values that we read from the RTC are set. That diff --git a/drivers/char/generic_serial.c b/drivers/char/generic_serial.c index 5e59c0b4273..4711d9b3a59 100644 --- a/drivers/char/generic_serial.c +++ b/drivers/char/generic_serial.c @@ -746,11 +746,9 @@ void gs_set_termios (struct tty_struct * tty, gs_dprintk (GS_DEBUG_TERMIOS, "termios structure (%p):\n", tiosp); } -#if 0 /* This is an optimization that is only allowed for dumb cards */ /* Smart cards require knowledge of iflags and oflags too: that might change hardware cooking mode.... */ -#endif if (old_termios) { if( (tiosp->c_iflag == old_termios->c_iflag) && (tiosp->c_oflag == old_termios->c_oflag) @@ -774,14 +772,7 @@ void gs_set_termios (struct tty_struct * tty, if(!memcmp(tiosp->c_cc, old_termios->c_cc, NCC)) printk("c_cc changed\n"); } - baudrate = tiosp->c_cflag & CBAUD; - if (baudrate & CBAUDEX) { - baudrate &= ~CBAUDEX; - if ((baudrate < 1) || (baudrate > 4)) - tiosp->c_cflag &= ~CBAUDEX; - else - baudrate += 15; - } + baudrate = tty_get_baud_rate(tty); baudrate = gs_baudrates[baudrate]; if ((tiosp->c_cflag & CBAUD) == B38400) { diff --git a/drivers/char/hpet.c b/drivers/char/hpet.c index 8afba339f05..58b0eb58111 100644 --- a/drivers/char/hpet.c +++ b/drivers/char/hpet.c @@ -868,8 +868,8 @@ int hpet_alloc(struct hpet_data *hdp) do_div(temp, period); hpetp->hp_tick_freq = temp; /* ticks per second */ - printk(KERN_INFO "hpet%d: at MMIO 0x%lx (virtual 0x%p), IRQ%s", - hpetp->hp_which, hdp->hd_phys_address, hdp->hd_address, + printk(KERN_INFO "hpet%d: at MMIO 0x%lx, IRQ%s", + hpetp->hp_which, hdp->hd_phys_address, hpetp->hp_ntimer > 1 ? "s" : ""); for (i = 0; i < hpetp->hp_ntimer; i++) printk("%s %d", i > 0 ? "," : "", hdp->hd_irq[i]); diff --git a/drivers/char/hvc_console.c b/drivers/char/hvc_console.c index 613d67f1c7f..a76d2c40dd5 100644 --- a/drivers/char/hvc_console.c +++ b/drivers/char/hvc_console.c @@ -80,7 +80,8 @@ struct hvc_struct { struct tty_struct *tty; unsigned int count; int do_wakeup; - char outbuf[N_OUTBUF] __ALIGNED__; + char *outbuf; + int outbuf_size; int n_outbuf; uint32_t vtermno; struct hv_ops *ops; @@ -319,10 +320,8 @@ static int hvc_open(struct tty_struct *tty, struct file * filp) struct kobject *kobjp; /* Auto increments kobject reference if found. */ - if (!(hp = hvc_get_by_index(tty->index))) { - printk(KERN_WARNING "hvc_console: tty open failed, no vty associated with tty.\n"); + if (!(hp = hvc_get_by_index(tty->index))) return -ENODEV; - } spin_lock_irqsave(&hp->lock, flags); /* Check and then increment for fast path open. */ @@ -505,7 +504,7 @@ static int hvc_write(struct tty_struct *tty, const unsigned char *buf, int count if (hp->n_outbuf > 0) hvc_push(hp); - while (count > 0 && (rsize = N_OUTBUF - hp->n_outbuf) > 0) { + while (count > 0 && (rsize = hp->outbuf_size - hp->n_outbuf) > 0) { if (rsize > count) rsize = count; memcpy(hp->outbuf + hp->n_outbuf, buf, rsize); @@ -538,7 +537,7 @@ static int hvc_write_room(struct tty_struct *tty) if (!hp) return -1; - return N_OUTBUF - hp->n_outbuf; + return hp->outbuf_size - hp->n_outbuf; } static int hvc_chars_in_buffer(struct tty_struct *tty) @@ -729,12 +728,13 @@ static struct kobj_type hvc_kobj_type = { }; struct hvc_struct __devinit *hvc_alloc(uint32_t vtermno, int irq, - struct hv_ops *ops) + struct hv_ops *ops, int outbuf_size) { struct hvc_struct *hp; int i; - hp = kmalloc(sizeof(*hp), GFP_KERNEL); + hp = kmalloc(ALIGN(sizeof(*hp), sizeof(long)) + outbuf_size, + GFP_KERNEL); if (!hp) return ERR_PTR(-ENOMEM); @@ -743,6 +743,8 @@ struct hvc_struct __devinit *hvc_alloc(uint32_t vtermno, int irq, hp->vtermno = vtermno; hp->irq = irq; hp->ops = ops; + hp->outbuf_size = outbuf_size; + hp->outbuf = &((char *)hp)[ALIGN(sizeof(*hp), sizeof(long))]; kobject_init(&hp->kobj); hp->kobj.ktype = &hvc_kobj_type; diff --git a/drivers/char/hvc_console.h b/drivers/char/hvc_console.h index 96b7401319c..8c59818050e 100644 --- a/drivers/char/hvc_console.h +++ b/drivers/char/hvc_console.h @@ -56,7 +56,7 @@ extern int hvc_instantiate(uint32_t vtermno, int index, struct hv_ops *ops); /* register a vterm for hvc tty operation (module_init or hotplug add) */ extern struct hvc_struct * __devinit hvc_alloc(uint32_t vtermno, int irq, - struct hv_ops *ops); + struct hv_ops *ops, int outbuf_size); /* remove a vterm from hvc tty operation (modele_exit or hotplug remove) */ extern int __devexit hvc_remove(struct hvc_struct *hp); diff --git a/drivers/char/hvc_iseries.c b/drivers/char/hvc_iseries.c new file mode 100644 index 00000000000..4747729459c --- /dev/null +++ b/drivers/char/hvc_iseries.c @@ -0,0 +1,594 @@ +/* + * iSeries vio driver interface to hvc_console.c + * + * This code is based heavily on hvc_vio.c and viocons.c + * + * Copyright (C) 2006 Stephen Rothwell, IBM Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <stdarg.h> +#include <linux/types.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/spinlock.h> +#include <linux/console.h> + +#include <asm/hvconsole.h> +#include <asm/vio.h> +#include <asm/prom.h> +#include <asm/iseries/vio.h> +#include <asm/iseries/hv_call.h> +#include <asm/iseries/hv_lp_config.h> +#include <asm/iseries/hv_lp_event.h> + +#include "hvc_console.h" + +#define VTTY_PORTS 10 + +static DEFINE_SPINLOCK(consolelock); +static DEFINE_SPINLOCK(consoleloglock); + +static const char hvc_driver_name[] = "hvc_console"; + +#define IN_BUF_SIZE 200 + +/* + * Our port information. + */ +static struct port_info { + HvLpIndex lp; + u64 seq; /* sequence number of last HV send */ + u64 ack; /* last ack from HV */ + struct hvc_struct *hp; + int in_start; + int in_end; + unsigned char in_buf[IN_BUF_SIZE]; +} port_info[VTTY_PORTS] = { + [ 0 ... VTTY_PORTS - 1 ] = { + .lp = HvLpIndexInvalid + } +}; + +#define viochar_is_console(pi) ((pi) == &port_info[0]) + +static struct vio_device_id hvc_driver_table[] __devinitdata = { + {"serial", "IBM,iSeries-vty"}, + { "", "" } +}; +MODULE_DEVICE_TABLE(vio, hvc_driver_table); + +static void hvlog(char *fmt, ...) +{ + int i; + unsigned long flags; + va_list args; + static char buf[256]; + + spin_lock_irqsave(&consoleloglock, flags); + va_start(args, fmt); + i = vscnprintf(buf, sizeof(buf) - 1, fmt, args); + va_end(args); + buf[i++] = '\r'; + HvCall_writeLogBuffer(buf, i); + spin_unlock_irqrestore(&consoleloglock, flags); +} + +/* + * Initialize the common fields in a charLpEvent + */ +static void init_data_event(struct viocharlpevent *viochar, HvLpIndex lp) +{ + struct HvLpEvent *hev = &viochar->event; + + memset(viochar, 0, sizeof(struct viocharlpevent)); + + hev->flags = HV_LP_EVENT_VALID | HV_LP_EVENT_DEFERRED_ACK | + HV_LP_EVENT_INT; + hev->xType = HvLpEvent_Type_VirtualIo; + hev->xSubtype = viomajorsubtype_chario | viochardata; + hev->xSourceLp = HvLpConfig_getLpIndex(); + hev->xTargetLp = lp; + hev->xSizeMinus1 = sizeof(struct viocharlpevent); + hev->xSourceInstanceId = viopath_sourceinst(lp); + hev->xTargetInstanceId = viopath_targetinst(lp); +} + +static int get_chars(uint32_t vtermno, char *buf, int count) +{ + struct port_info *pi; + int n = 0; + unsigned long flags; + + if (vtermno >= VTTY_PORTS) + return -EINVAL; + if (count == 0) + return 0; + + pi = &port_info[vtermno]; + spin_lock_irqsave(&consolelock, flags); + + if (pi->in_end == 0) + goto done; + + n = pi->in_end - pi->in_start; + if (n > count) + n = count; + memcpy(buf, &pi->in_buf[pi->in_start], n); + pi->in_start += n; + if (pi->in_start == pi->in_end) { + pi->in_start = 0; + pi->in_end = 0; + } +done: + spin_unlock_irqrestore(&consolelock, flags); + return n; +} + +static int put_chars(uint32_t vtermno, const char *buf, int count) +{ + struct viocharlpevent *viochar; + struct port_info *pi; + HvLpEvent_Rc hvrc; + unsigned long flags; + int sent = 0; + + if (vtermno >= VTTY_PORTS) + return -EINVAL; + + pi = &port_info[vtermno]; + + spin_lock_irqsave(&consolelock, flags); + + if (viochar_is_console(pi) && !viopath_isactive(pi->lp)) { + spin_lock_irqsave(&consoleloglock, flags); + HvCall_writeLogBuffer(buf, count); + spin_unlock_irqrestore(&consoleloglock, flags); + sent = count; + goto done; + } + + viochar = vio_get_event_buffer(viomajorsubtype_chario); + if (viochar == NULL) { + hvlog("\n\rviocons: Can't get viochar buffer."); + goto done; + } + + while ((count > 0) && ((pi->seq - pi->ack) < VIOCHAR_WINDOW)) { + int len; + + len = (count > VIOCHAR_MAX_DATA) ? VIOCHAR_MAX_DATA : count; + + if (viochar_is_console(pi)) { + spin_lock_irqsave(&consoleloglock, flags); + HvCall_writeLogBuffer(buf, len); + spin_unlock_irqrestore(&consoleloglock, flags); + } + + init_data_event(viochar, pi->lp); + + viochar->len = len; + viochar->event.xCorrelationToken = pi->seq++; + viochar->event.xSizeMinus1 = + offsetof(struct viocharlpevent, data) + len; + + memcpy(viochar->data, buf, len); + + hvrc = HvCallEvent_signalLpEvent(&viochar->event); + if (hvrc) + hvlog("\n\rerror sending event! return code %d\n\r", + (int)hvrc); + sent += len; + count -= len; + buf += len; + } + + vio_free_event_buffer(viomajorsubtype_chario, viochar); +done: + spin_unlock_irqrestore(&consolelock, flags); + return sent; +} + +static struct hv_ops hvc_get_put_ops = { + .get_chars = get_chars, + .put_chars = put_chars, +}; + +static int __devinit hvc_vio_probe(struct vio_dev *vdev, + const struct vio_device_id *id) +{ + struct hvc_struct *hp; + struct port_info *pi; + + /* probed with invalid parameters. */ + if (!vdev || !id) + return -EPERM; + + if (vdev->unit_address >= VTTY_PORTS) + return -ENODEV; + + pi = &port_info[vdev->unit_address]; + + hp = hvc_alloc(vdev->unit_address, vdev->irq, &hvc_get_put_ops, + VIOCHAR_MAX_DATA); + if (IS_ERR(hp)) + return PTR_ERR(hp); + pi->hp = hp; + dev_set_drvdata(&vdev->dev, pi); + + return 0; +} + +static int __devexit hvc_vio_remove(struct vio_dev *vdev) +{ + struct port_info *pi = dev_get_drvdata(&vdev->dev); + struct hvc_struct *hp = pi->hp; + + return hvc_remove(hp); +} + +static struct vio_driver hvc_vio_driver = { + .id_table = hvc_driver_table, + .probe = hvc_vio_probe, + .remove = hvc_vio_remove, + .driver = { + .name = hvc_driver_name, + .owner = THIS_MODULE, + } +}; + +static void hvc_open_event(struct HvLpEvent *event) +{ + unsigned long flags; + struct viocharlpevent *cevent = (struct viocharlpevent *)event; + u8 port = cevent->virtual_device; + struct port_info *pi; + int reject = 0; + + if (hvlpevent_is_ack(event)) { + if (port >= VTTY_PORTS) + return; + + spin_lock_irqsave(&consolelock, flags); + + pi = &port_info[port]; + if (event->xRc == HvLpEvent_Rc_Good) { + pi->seq = pi->ack = 0; + /* + * This line allows connections from the primary + * partition but once one is connected from the + * primary partition nothing short of a reboot + * of linux will allow access from the hosting + * partition again without a required iSeries fix. + */ + pi->lp = event->xTargetLp; + } + + spin_unlock_irqrestore(&consolelock, flags); + if (event->xRc != HvLpEvent_Rc_Good) + printk(KERN_WARNING + "hvc: handle_open_event: event->xRc == (%d).\n", + event->xRc); + + if (event->xCorrelationToken != 0) { + atomic_t *aptr= (atomic_t *)event->xCorrelationToken; + atomic_set(aptr, 1); + } else + printk(KERN_WARNING + "hvc: weird...got open ack without atomic\n"); + return; + } + + /* This had better require an ack, otherwise complain */ + if (!hvlpevent_need_ack(event)) { + printk(KERN_WARNING "hvc: viocharopen without ack bit!\n"); + return; + } + + spin_lock_irqsave(&consolelock, flags); + + /* Make sure this is a good virtual tty */ + if (port >= VTTY_PORTS) { + event->xRc = HvLpEvent_Rc_SubtypeError; + cevent->subtype_result_code = viorc_openRejected; + /* + * Flag state here since we can't printk while holding + * the consolelock spinlock. + */ + reject = 1; + } else { + pi = &port_info[port]; + if ((pi->lp != HvLpIndexInvalid) && + (pi->lp != event->xSourceLp)) { + /* + * If this is tty is already connected to a different + * partition, fail. + */ + event->xRc = HvLpEvent_Rc_SubtypeError; + cevent->subtype_result_code = viorc_openRejected; + reject = 2; + } else { + pi->lp = event->xSourceLp; + event->xRc = HvLpEvent_Rc_Good; + cevent->subtype_result_code = viorc_good; + pi->seq = pi->ack = 0; + } + } + + spin_unlock_irqrestore(&consolelock, flags); + + if (reject == 1) + printk(KERN_WARNING "hvc: open rejected: bad virtual tty.\n"); + else if (reject == 2) + printk(KERN_WARNING "hvc: open rejected: console in exclusive " + "use by another partition.\n"); + + /* Return the acknowledgement */ + HvCallEvent_ackLpEvent(event); +} + +/* + * Handle a close charLpEvent. This should ONLY be an Interrupt because the + * virtual console should never actually issue a close event to the hypervisor + * because the virtual console never goes away. A close event coming from the + * hypervisor simply means that there are no client consoles connected to the + * virtual console. + */ +static void hvc_close_event(struct HvLpEvent *event) +{ + unsigned long flags; + struct viocharlpevent *cevent = (struct viocharlpevent *)event; + u8 port = cevent->virtual_device; + + if (!hvlpevent_is_int(event)) { + printk(KERN_WARNING + "hvc: got unexpected close acknowlegement\n"); + return; + } + + if (port >= VTTY_PORTS) { + printk(KERN_WARNING + "hvc: close message from invalid virtual device.\n"); + return; + } + + /* For closes, just mark the console partition invalid */ + spin_lock_irqsave(&consolelock, flags); + + if (port_info[port].lp == event->xSourceLp) + port_info[port].lp = HvLpIndexInvalid; + + spin_unlock_irqrestore(&consolelock, flags); +} + +static void hvc_data_event(struct HvLpEvent *event) +{ + unsigned long flags; + struct viocharlpevent *cevent = (struct viocharlpevent *)event; + struct port_info *pi; + int n; + u8 port = cevent->virtual_device; + + if (port >= VTTY_PORTS) { + printk(KERN_WARNING "hvc: data on invalid virtual device %d\n", + port); + return; + } + if (cevent->len == 0) + return; + + /* + * Change 05/01/2003 - Ryan Arnold: If a partition other than + * the current exclusive partition tries to send us data + * events then just drop them on the floor because we don't + * want his stinking data. He isn't authorized to receive + * data because he wasn't the first one to get the console, + * therefore he shouldn't be allowed to send data either. + * This will work without an iSeries fix. + */ + pi = &port_info[port]; + if (pi->lp != event->xSourceLp) + return; + + spin_lock_irqsave(&consolelock, flags); + + n = IN_BUF_SIZE - pi->in_end; + if (n > cevent->len) + n = cevent->len; + if (n > 0) { + memcpy(&pi->in_buf[pi->in_end], cevent->data, n); + pi->in_end += n; + } + spin_unlock_irqrestore(&consolelock, flags); + if (n == 0) + printk(KERN_WARNING "hvc: input buffer overflow\n"); +} + +static void hvc_ack_event(struct HvLpEvent *event) +{ + struct viocharlpevent *cevent = (struct viocharlpevent *)event; + unsigned long flags; + u8 port = cevent->virtual_device; + + if (port >= VTTY_PORTS) { + printk(KERN_WARNING "hvc: data on invalid virtual device\n"); + return; + } + + spin_lock_irqsave(&consolelock, flags); + port_info[port].ack = event->xCorrelationToken; + spin_unlock_irqrestore(&consolelock, flags); +} + +static void hvc_config_event(struct HvLpEvent *event) +{ + struct viocharlpevent *cevent = (struct viocharlpevent *)event; + + if (cevent->data[0] == 0x01) + printk(KERN_INFO "hvc: window resized to %d: %d: %d: %d\n", + cevent->data[1], cevent->data[2], + cevent->data[3], cevent->data[4]); + else + printk(KERN_WARNING "hvc: unknown config event\n"); +} + +static void hvc_handle_event(struct HvLpEvent *event) +{ + int charminor; + + if (event == NULL) + return; + + charminor = event->xSubtype & VIOMINOR_SUBTYPE_MASK; + switch (charminor) { + case viocharopen: + hvc_open_event(event); + break; + case viocharclose: + hvc_close_event(event); + break; + case viochardata: + hvc_data_event(event); + break; + case viocharack: + hvc_ack_event(event); + break; + case viocharconfig: + hvc_config_event(event); + break; + default: + if (hvlpevent_is_int(event) && hvlpevent_need_ack(event)) { + event->xRc = HvLpEvent_Rc_InvalidSubtype; + HvCallEvent_ackLpEvent(event); + } + } +} + +static int send_open(HvLpIndex remoteLp, void *sem) +{ + return HvCallEvent_signalLpEventFast(remoteLp, + HvLpEvent_Type_VirtualIo, + viomajorsubtype_chario | viocharopen, + HvLpEvent_AckInd_DoAck, HvLpEvent_AckType_ImmediateAck, + viopath_sourceinst(remoteLp), + viopath_targetinst(remoteLp), + (u64)(unsigned long)sem, VIOVERSION << 16, + 0, 0, 0, 0); +} + +static int hvc_vio_init(void) +{ + atomic_t wait_flag; + int rc; + + /* +2 for fudge */ + rc = viopath_open(HvLpConfig_getPrimaryLpIndex(), + viomajorsubtype_chario, VIOCHAR_WINDOW + 2); + if (rc) + printk(KERN_WARNING "hvc: error opening to primary %d\n", rc); + + if (viopath_hostLp == HvLpIndexInvalid) + vio_set_hostlp(); + + /* + * And if the primary is not the same as the hosting LP, open to the + * hosting lp + */ + if ((viopath_hostLp != HvLpIndexInvalid) && + (viopath_hostLp != HvLpConfig_getPrimaryLpIndex())) { + printk(KERN_INFO "hvc: open path to hosting (%d)\n", + viopath_hostLp); + rc = viopath_open(viopath_hostLp, viomajorsubtype_chario, + VIOCHAR_WINDOW + 2); /* +2 for fudge */ + if (rc) + printk(KERN_WARNING + "error opening to partition %d: %d\n", + viopath_hostLp, rc); + } + + if (vio_setHandler(viomajorsubtype_chario, hvc_handle_event) < 0) + printk(KERN_WARNING + "hvc: error seting handler for console events!\n"); + + /* + * First, try to open the console to the hosting lp. + * Wait on a semaphore for the response. + */ + atomic_set(&wait_flag, 0); + if ((viopath_isactive(viopath_hostLp)) && + (send_open(viopath_hostLp, &wait_flag) == 0)) { + printk(KERN_INFO "hvc: hosting partition %d\n", viopath_hostLp); + while (atomic_read(&wait_flag) == 0) + mb(); + atomic_set(&wait_flag, 0); + } + + /* + * If we don't have an active console, try the primary + */ + if ((!viopath_isactive(port_info[0].lp)) && + (viopath_isactive(HvLpConfig_getPrimaryLpIndex())) && + (send_open(HvLpConfig_getPrimaryLpIndex(), &wait_flag) == 0)) { + printk(KERN_INFO "hvc: opening console to primary partition\n"); + while (atomic_read(&wait_flag) == 0) + mb(); + } + + /* Register as a vio device to receive callbacks */ + rc = vio_register_driver(&hvc_vio_driver); + + return rc; +} +module_init(hvc_vio_init); /* after drivers/char/hvc_console.c */ + +static void hvc_vio_exit(void) +{ + vio_unregister_driver(&hvc_vio_driver); +} +module_exit(hvc_vio_exit); + +/* the device tree order defines our numbering */ +static int hvc_find_vtys(void) +{ + struct device_node *vty; + int num_found = 0; + + for (vty = of_find_node_by_name(NULL, "vty"); vty != NULL; + vty = of_find_node_by_name(vty, "vty")) { + uint32_t *vtermno; + + /* We have statically defined space for only a certain number + * of console adapters. + */ + if ((num_found >= MAX_NR_HVC_CONSOLES) || + (num_found >= VTTY_PORTS)) + break; + + vtermno = (uint32_t *)get_property(vty, "reg", NULL); + if (!vtermno) + continue; + + if (!device_is_compatible(vty, "IBM,iSeries-vty")) + continue; + + if (num_found == 0) + add_preferred_console("hvc", 0, NULL); + hvc_instantiate(*vtermno, num_found, &hvc_get_put_ops); + ++num_found; + } + + return num_found; +} +console_initcall(hvc_find_vtys); diff --git a/drivers/char/hvc_rtas.c b/drivers/char/hvc_rtas.c index 57106e02fd2..4b97eaf1860 100644 --- a/drivers/char/hvc_rtas.c +++ b/drivers/char/hvc_rtas.c @@ -94,7 +94,7 @@ static int hvc_rtas_init(void) /* Allocate an hvc_struct for the console device we instantiated * earlier. Save off hp so that we can return it on exit */ - hp = hvc_alloc(hvc_rtas_cookie, NO_IRQ, &hvc_rtas_get_put_ops); + hp = hvc_alloc(hvc_rtas_cookie, NO_IRQ, &hvc_rtas_get_put_ops, 16); if (IS_ERR(hp)) return PTR_ERR(hp); diff --git a/drivers/char/hvc_vio.c b/drivers/char/hvc_vio.c index 9add81ceb44..cc95941148f 100644 --- a/drivers/char/hvc_vio.c +++ b/drivers/char/hvc_vio.c @@ -90,7 +90,8 @@ static int __devinit hvc_vio_probe(struct vio_dev *vdev, if (!vdev || !id) return -EPERM; - hp = hvc_alloc(vdev->unit_address, vdev->irq, &hvc_get_put_ops); + hp = hvc_alloc(vdev->unit_address, vdev->irq, &hvc_get_put_ops, + MAX_VIO_PUT_CHARS); if (IS_ERR(hp)) return PTR_ERR(hp); dev_set_drvdata(&vdev->dev, hp); @@ -140,7 +141,7 @@ static int hvc_find_vtys(void) for (vty = of_find_node_by_name(NULL, "vty"); vty != NULL; vty = of_find_node_by_name(vty, "vty")) { - uint32_t *vtermno; + const uint32_t *vtermno; /* We have statically defined space for only a certain number * of console adapters. @@ -148,7 +149,7 @@ static int hvc_find_vtys(void) if (num_found >= MAX_NR_HVC_CONSOLES) break; - vtermno = (uint32_t *)get_property(vty, "reg", NULL); + vtermno = get_property(vty, "reg", NULL); if (!vtermno) continue; diff --git a/drivers/char/hvsi.c b/drivers/char/hvsi.c index 017f755632a..a89a95fb5e4 100644 --- a/drivers/char/hvsi.c +++ b/drivers/char/hvsi.c @@ -1274,11 +1274,10 @@ static int __init hvsi_console_init(void) vty != NULL; vty = of_find_compatible_node(vty, "serial", "hvterm-protocol")) { struct hvsi_struct *hp; - uint32_t *vtermno; - uint32_t *irq; + const uint32_t *vtermno, *irq; - vtermno = (uint32_t *)get_property(vty, "reg", NULL); - irq = (uint32_t *)get_property(vty, "interrupts", NULL); + vtermno = get_property(vty, "reg", NULL); + irq = get_property(vty, "interrupts", NULL); if (!vtermno || !irq) continue; diff --git a/drivers/char/hw_random/intel-rng.c b/drivers/char/hw_random/intel-rng.c index ccd7e710223..8efbc9c0e54 100644 --- a/drivers/char/hw_random/intel-rng.c +++ b/drivers/char/hw_random/intel-rng.c @@ -50,6 +50,43 @@ #define INTEL_RNG_ADDR_LEN 3 /* + * LPC bridge PCI config space registers + */ +#define FWH_DEC_EN1_REG_OLD 0xe3 +#define FWH_DEC_EN1_REG_NEW 0xd9 /* high byte of 16-bit register */ +#define FWH_F8_EN_MASK 0x80 + +#define BIOS_CNTL_REG_OLD 0x4e +#define BIOS_CNTL_REG_NEW 0xdc +#define BIOS_CNTL_WRITE_ENABLE_MASK 0x01 +#define BIOS_CNTL_LOCK_ENABLE_MASK 0x02 + +/* + * Magic address at which Intel Firmware Hubs get accessed + */ +#define INTEL_FWH_ADDR 0xffff0000 +#define INTEL_FWH_ADDR_LEN 2 + +/* + * Intel Firmware Hub command codes (write to any address inside the device) + */ +#define INTEL_FWH_RESET_CMD 0xff /* aka READ_ARRAY */ +#define INTEL_FWH_READ_ID_CMD 0x90 + +/* + * Intel Firmware Hub Read ID command result addresses + */ +#define INTEL_FWH_MANUFACTURER_CODE_ADDRESS 0x000000 +#define INTEL_FWH_DEVICE_CODE_ADDRESS 0x000001 + +/* + * Intel Firmware Hub Read ID command result values + */ +#define INTEL_FWH_MANUFACTURER_CODE 0x89 +#define INTEL_FWH_DEVICE_CODE_8M 0xac +#define INTEL_FWH_DEVICE_CODE_4M 0xad + +/* * Data for PCI driver interface * * This data only exists for exporting the supported @@ -58,12 +95,50 @@ * want to register another driver on the same PCI id. */ static const struct pci_device_id pci_tbl[] = { - { 0x8086, 0x2418, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, - { 0x8086, 0x2428, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, - { 0x8086, 0x2430, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, - { 0x8086, 0x2448, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, - { 0x8086, 0x244e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, - { 0x8086, 0x245e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, +/* AA + { 0x8086, 0x2418, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, */ + { 0x8086, 0x2410, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* AA */ +/* AB + { 0x8086, 0x2428, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, */ + { 0x8086, 0x2420, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* AB */ +/* ?? + { 0x8086, 0x2430, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, */ +/* BAM, CAM, DBM, FBM, GxM + { 0x8086, 0x2448, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, */ + { 0x8086, 0x244c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* BAM */ + { 0x8086, 0x248c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* CAM */ + { 0x8086, 0x24cc, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* DBM */ + { 0x8086, 0x2641, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* FBM */ + { 0x8086, 0x27b9, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* GxM */ + { 0x8086, 0x27bd, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* GxM DH */ +/* BA, CA, DB, Ex, 6300, Fx, 631x/632x, Gx + { 0x8086, 0x244e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, */ + { 0x8086, 0x2440, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* BA */ + { 0x8086, 0x2480, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* CA */ + { 0x8086, 0x24c0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* DB */ + { 0x8086, 0x24d0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* Ex */ + { 0x8086, 0x25a1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* 6300 */ + { 0x8086, 0x2640, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* Fx */ + { 0x8086, 0x2670, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* 631x/632x */ + { 0x8086, 0x2671, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* 631x/632x */ + { 0x8086, 0x2672, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* 631x/632x */ + { 0x8086, 0x2673, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* 631x/632x */ + { 0x8086, 0x2674, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* 631x/632x */ + { 0x8086, 0x2675, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* 631x/632x */ + { 0x8086, 0x2676, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* 631x/632x */ + { 0x8086, 0x2677, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* 631x/632x */ + { 0x8086, 0x2678, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* 631x/632x */ + { 0x8086, 0x2679, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* 631x/632x */ + { 0x8086, 0x267a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* 631x/632x */ + { 0x8086, 0x267b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* 631x/632x */ + { 0x8086, 0x267c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* 631x/632x */ + { 0x8086, 0x267d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* 631x/632x */ + { 0x8086, 0x267e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* 631x/632x */ + { 0x8086, 0x267f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* 631x/632x */ + { 0x8086, 0x27b8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* Gx */ +/* E + { 0x8086, 0x245e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, */ + { 0x8086, 0x2450, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* E */ { 0, }, /* terminate list */ }; MODULE_DEVICE_TABLE(pci, pci_tbl); @@ -138,22 +213,115 @@ static struct hwrng intel_rng = { }; +#ifdef CONFIG_SMP +static char __initdata waitflag; + +static void __init intel_init_wait(void *unused) +{ + while (waitflag) + cpu_relax(); +} +#endif + static int __init mod_init(void) { int err = -ENODEV; + unsigned i; + struct pci_dev *dev = NULL; void __iomem *mem; - u8 hw_status; + unsigned long flags; + u8 bios_cntl_off, fwh_dec_en1_off; + u8 bios_cntl_val = 0xff, fwh_dec_en1_val = 0xff; + u8 hw_status, mfc, dvc; - if (!pci_dev_present(pci_tbl)) + for (i = 0; !dev && pci_tbl[i].vendor; ++i) + dev = pci_get_device(pci_tbl[i].vendor, pci_tbl[i].device, NULL); + + if (!dev) goto out; /* Device not found. */ + /* Check for Intel 82802 */ + if (dev->device < 0x2640) { + fwh_dec_en1_off = FWH_DEC_EN1_REG_OLD; + bios_cntl_off = BIOS_CNTL_REG_OLD; + } else { + fwh_dec_en1_off = FWH_DEC_EN1_REG_NEW; + bios_cntl_off = BIOS_CNTL_REG_NEW; + } + + pci_read_config_byte(dev, fwh_dec_en1_off, &fwh_dec_en1_val); + pci_read_config_byte(dev, bios_cntl_off, &bios_cntl_val); + + mem = ioremap_nocache(INTEL_FWH_ADDR, INTEL_FWH_ADDR_LEN); + if (mem == NULL) { + pci_dev_put(dev); + err = -EBUSY; + goto out; + } + + /* + * Since the BIOS code/data is going to disappear from its normal + * location with the Read ID command, all activity on the system + * must be stopped until the state is back to normal. + */ +#ifdef CONFIG_SMP + set_mb(waitflag, 1); + if (smp_call_function(intel_init_wait, NULL, 1, 0) != 0) { + set_mb(waitflag, 0); + pci_dev_put(dev); + printk(KERN_ERR PFX "cannot run on all processors\n"); + err = -EAGAIN; + goto err_unmap; + } +#endif + local_irq_save(flags); + + if (!(fwh_dec_en1_val & FWH_F8_EN_MASK)) + pci_write_config_byte(dev, + fwh_dec_en1_off, + fwh_dec_en1_val | FWH_F8_EN_MASK); + if (!(bios_cntl_val & + (BIOS_CNTL_LOCK_ENABLE_MASK|BIOS_CNTL_WRITE_ENABLE_MASK))) + pci_write_config_byte(dev, + bios_cntl_off, + bios_cntl_val | BIOS_CNTL_WRITE_ENABLE_MASK); + + writeb(INTEL_FWH_RESET_CMD, mem); + writeb(INTEL_FWH_READ_ID_CMD, mem); + mfc = readb(mem + INTEL_FWH_MANUFACTURER_CODE_ADDRESS); + dvc = readb(mem + INTEL_FWH_DEVICE_CODE_ADDRESS); + writeb(INTEL_FWH_RESET_CMD, mem); + + if (!(bios_cntl_val & + (BIOS_CNTL_LOCK_ENABLE_MASK|BIOS_CNTL_WRITE_ENABLE_MASK))) + pci_write_config_byte(dev, bios_cntl_off, bios_cntl_val); + if (!(fwh_dec_en1_val & FWH_F8_EN_MASK)) + pci_write_config_byte(dev, fwh_dec_en1_off, fwh_dec_en1_val); + + local_irq_restore(flags); +#ifdef CONFIG_SMP + /* Tell other CPUs to resume. */ + set_mb(waitflag, 0); +#endif + + iounmap(mem); + pci_dev_put(dev); + + if (mfc != INTEL_FWH_MANUFACTURER_CODE || + (dvc != INTEL_FWH_DEVICE_CODE_8M && + dvc != INTEL_FWH_DEVICE_CODE_4M)) { + printk(KERN_ERR PFX "FWH not detected\n"); + err = -ENODEV; + goto out; + } + err = -ENOMEM; mem = ioremap(INTEL_RNG_ADDR, INTEL_RNG_ADDR_LEN); if (!mem) goto out; intel_rng.priv = (unsigned long)mem; - /* Check for Intel 82802 */ + /* Check for Random Number Generator */ err = -ENODEV; hw_status = hwstatus_get(mem); if ((hw_status & INTEL_RNG_PRESENT) == 0) diff --git a/drivers/char/ip2/ip2main.c b/drivers/char/ip2/ip2main.c index 7907ae88c2f..331f447e622 100644 --- a/drivers/char/ip2/ip2main.c +++ b/drivers/char/ip2/ip2main.c @@ -436,6 +436,7 @@ cleanup_module(void) #ifdef CONFIG_PCI if (ip2config.type[i] == PCI && ip2config.pci_dev[i]) { pci_disable_device(ip2config.pci_dev[i]); + pci_dev_put(ip2config.pci_dev[i]); ip2config.pci_dev[i] = NULL; } #endif @@ -505,6 +506,7 @@ ip2_loadmain(int *iop, int *irqp, unsigned char *firmware, int firmsize) static int loaded; i2eBordStrPtr pB = NULL; int rc = -1; + static struct pci_dev *pci_dev_i = NULL; ip2trace (ITRC_NO_PORT, ITRC_INIT, ITRC_ENTER, 0 ); @@ -588,8 +590,7 @@ ip2_loadmain(int *iop, int *irqp, unsigned char *firmware, int firmsize) case PCI: #ifdef CONFIG_PCI { - struct pci_dev *pci_dev_i = NULL; - pci_dev_i = pci_find_device(PCI_VENDOR_ID_COMPUTONE, + pci_dev_i = pci_get_device(PCI_VENDOR_ID_COMPUTONE, PCI_DEVICE_ID_COMPUTONE_IP2EX, pci_dev_i); if (pci_dev_i != NULL) { unsigned int addr; @@ -600,7 +601,7 @@ ip2_loadmain(int *iop, int *irqp, unsigned char *firmware, int firmsize) break; } ip2config.type[i] = PCI; - ip2config.pci_dev[i] = pci_dev_i; + ip2config.pci_dev[i] = pci_dev_get(pci_dev_i); status = pci_read_config_dword(pci_dev_i, PCI_BASE_ADDRESS_1, &addr); if ( addr & 1 ) { @@ -641,6 +642,9 @@ ip2_loadmain(int *iop, int *irqp, unsigned char *firmware, int firmsize) break; } /* switch */ } /* for */ + if (pci_dev_i) + pci_dev_put(pci_dev_i); + for ( i = 0; i < IP2_MAX_BOARDS; ++i ) { if ( ip2config.addr[i] ) { pB = kmalloc( sizeof(i2eBordStr), GFP_KERNEL); diff --git a/drivers/char/ipmi/ipmi_devintf.c b/drivers/char/ipmi/ipmi_devintf.c index 68d7c61a864..81fcf0ce21d 100644 --- a/drivers/char/ipmi/ipmi_devintf.c +++ b/drivers/char/ipmi/ipmi_devintf.c @@ -377,7 +377,8 @@ static int ipmi_ioctl(struct inode *inode, break; } - rv = ipmi_register_for_cmd(priv->user, val.netfn, val.cmd); + rv = ipmi_register_for_cmd(priv->user, val.netfn, val.cmd, + IPMI_CHAN_ALL); break; } @@ -390,7 +391,36 @@ static int ipmi_ioctl(struct inode *inode, break; } - rv = ipmi_unregister_for_cmd(priv->user, val.netfn, val.cmd); + rv = ipmi_unregister_for_cmd(priv->user, val.netfn, val.cmd, + IPMI_CHAN_ALL); + break; + } + + case IPMICTL_REGISTER_FOR_CMD_CHANS: + { + struct ipmi_cmdspec_chans val; + + if (copy_from_user(&val, arg, sizeof(val))) { + rv = -EFAULT; + break; + } + + rv = ipmi_register_for_cmd(priv->user, val.netfn, val.cmd, + val.chans); + break; + } + + case IPMICTL_UNREGISTER_FOR_CMD_CHANS: + { + struct ipmi_cmdspec_chans val; + + if (copy_from_user(&val, arg, sizeof(val))) { + rv = -EFAULT; + break; + } + + rv = ipmi_unregister_for_cmd(priv->user, val.netfn, val.cmd, + val.chans); break; } diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c index 843d34c8627..2455e8d478a 100644 --- a/drivers/char/ipmi/ipmi_msghandler.c +++ b/drivers/char/ipmi/ipmi_msghandler.c @@ -96,6 +96,7 @@ struct cmd_rcvr ipmi_user_t user; unsigned char netfn; unsigned char cmd; + unsigned int chans; /* * This is used to form a linked lised during mass deletion. @@ -953,24 +954,41 @@ int ipmi_set_gets_events(ipmi_user_t user, int val) static struct cmd_rcvr *find_cmd_rcvr(ipmi_smi_t intf, unsigned char netfn, - unsigned char cmd) + unsigned char cmd, + unsigned char chan) { struct cmd_rcvr *rcvr; list_for_each_entry_rcu(rcvr, &intf->cmd_rcvrs, link) { - if ((rcvr->netfn == netfn) && (rcvr->cmd == cmd)) + if ((rcvr->netfn == netfn) && (rcvr->cmd == cmd) + && (rcvr->chans & (1 << chan))) return rcvr; } return NULL; } +static int is_cmd_rcvr_exclusive(ipmi_smi_t intf, + unsigned char netfn, + unsigned char cmd, + unsigned int chans) +{ + struct cmd_rcvr *rcvr; + + list_for_each_entry_rcu(rcvr, &intf->cmd_rcvrs, link) { + if ((rcvr->netfn == netfn) && (rcvr->cmd == cmd) + && (rcvr->chans & chans)) + return 0; + } + return 1; +} + int ipmi_register_for_cmd(ipmi_user_t user, unsigned char netfn, - unsigned char cmd) + unsigned char cmd, + unsigned int chans) { ipmi_smi_t intf = user->intf; struct cmd_rcvr *rcvr; - struct cmd_rcvr *entry; int rv = 0; @@ -979,12 +997,12 @@ int ipmi_register_for_cmd(ipmi_user_t user, return -ENOMEM; rcvr->cmd = cmd; rcvr->netfn = netfn; + rcvr->chans = chans; rcvr->user = user; mutex_lock(&intf->cmd_rcvrs_mutex); /* Make sure the command/netfn is not already registered. */ - entry = find_cmd_rcvr(intf, netfn, cmd); - if (entry) { + if (!is_cmd_rcvr_exclusive(intf, netfn, cmd, chans)) { rv = -EBUSY; goto out_unlock; } @@ -1001,24 +1019,39 @@ int ipmi_register_for_cmd(ipmi_user_t user, int ipmi_unregister_for_cmd(ipmi_user_t user, unsigned char netfn, - unsigned char cmd) + unsigned char cmd, + unsigned int chans) { ipmi_smi_t intf = user->intf; struct cmd_rcvr *rcvr; + struct cmd_rcvr *rcvrs = NULL; + int i, rv = -ENOENT; mutex_lock(&intf->cmd_rcvrs_mutex); - /* Make sure the command/netfn is not already registered. */ - rcvr = find_cmd_rcvr(intf, netfn, cmd); - if ((rcvr) && (rcvr->user == user)) { - list_del_rcu(&rcvr->link); - mutex_unlock(&intf->cmd_rcvrs_mutex); - synchronize_rcu(); + for (i = 0; i < IPMI_NUM_CHANNELS; i++) { + if (((1 << i) & chans) == 0) + continue; + rcvr = find_cmd_rcvr(intf, netfn, cmd, i); + if (rcvr == NULL) + continue; + if (rcvr->user == user) { + rv = 0; + rcvr->chans &= ~chans; + if (rcvr->chans == 0) { + list_del_rcu(&rcvr->link); + rcvr->next = rcvrs; + rcvrs = rcvr; + } + } + } + mutex_unlock(&intf->cmd_rcvrs_mutex); + synchronize_rcu(); + while (rcvrs) { + rcvr = rcvrs; + rcvrs = rcvr->next; kfree(rcvr); - return 0; - } else { - mutex_unlock(&intf->cmd_rcvrs_mutex); - return -ENOENT; } + return rv; } void ipmi_user_set_run_to_completion(ipmi_user_t user, int val) @@ -2548,6 +2581,7 @@ static int handle_ipmb_get_msg_cmd(ipmi_smi_t intf, int rv = 0; unsigned char netfn; unsigned char cmd; + unsigned char chan; ipmi_user_t user = NULL; struct ipmi_ipmb_addr *ipmb_addr; struct ipmi_recv_msg *recv_msg; @@ -2568,9 +2602,10 @@ static int handle_ipmb_get_msg_cmd(ipmi_smi_t intf, netfn = msg->rsp[4] >> 2; cmd = msg->rsp[8]; + chan = msg->rsp[3] & 0xf; rcu_read_lock(); - rcvr = find_cmd_rcvr(intf, netfn, cmd); + rcvr = find_cmd_rcvr(intf, netfn, cmd, chan); if (rcvr) { user = rcvr->user; kref_get(&user->refcount); @@ -2728,6 +2763,7 @@ static int handle_lan_get_msg_cmd(ipmi_smi_t intf, int rv = 0; unsigned char netfn; unsigned char cmd; + unsigned char chan; ipmi_user_t user = NULL; struct ipmi_lan_addr *lan_addr; struct ipmi_recv_msg *recv_msg; @@ -2748,9 +2784,10 @@ static int handle_lan_get_msg_cmd(ipmi_smi_t intf, netfn = msg->rsp[6] >> 2; cmd = msg->rsp[10]; + chan = msg->rsp[3] & 0xf; rcu_read_lock(); - rcvr = find_cmd_rcvr(intf, netfn, cmd); + rcvr = find_cmd_rcvr(intf, netfn, cmd, chan); if (rcvr) { user = rcvr->user; kref_get(&user->refcount); diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c index abca98beac1..908521e36d8 100644 --- a/drivers/char/ipmi/ipmi_si_intf.c +++ b/drivers/char/ipmi/ipmi_si_intf.c @@ -916,7 +916,11 @@ static int smi_start_processing(void *send_info, new_smi->last_timeout_jiffies = jiffies; mod_timer(&new_smi->si_timer, jiffies + SI_TIMEOUT_JIFFIES); - if (new_smi->si_type != SI_BT) { + /* + * The BT interface is efficient enough to not need a thread, + * and there is no need for a thread if we have interrupts. + */ + if ((new_smi->si_type != SI_BT) && (!new_smi->irq)) { new_smi->thread = kthread_run(ipmi_thread, new_smi, "kipmi%d", new_smi->intf_num); if (IS_ERR(new_smi->thread)) { diff --git a/drivers/char/isicom.c b/drivers/char/isicom.c index 913be23e0a2..2e1da632aee 100644 --- a/drivers/char/isicom.c +++ b/drivers/char/isicom.c @@ -1756,9 +1756,12 @@ static int __devinit load_firmware(struct pci_dev *pdev, if (retval) goto end; + retval = -EIO; + for (frame = (struct stframe *)fw->data; frame < (struct stframe *)(fw->data + fw->size); - frame++) { + frame = (struct stframe *)((u8 *)(frame + 1) + + frame->count)) { if (WaitTillCardIsFree(base)) goto errrelfw; @@ -1797,23 +1800,12 @@ static int __devinit load_firmware(struct pci_dev *pdev, } } - retval = -EIO; - - if (WaitTillCardIsFree(base)) - goto errrelfw; - - outw(0xf2, base); - outw(0x800, base); - outw(0x0, base); - outw(0x0, base); - InterruptTheCard(base); - outw(0x0, base + 0x4); /* for ISI4608 cards */ - /* XXX: should we test it by reading it back and comparing with original like * in load firmware package? */ - for (frame = (struct stframe*)fw->data; - frame < (struct stframe*)(fw->data + fw->size); - frame++) { + for (frame = (struct stframe *)fw->data; + frame < (struct stframe *)(fw->data + fw->size); + frame = (struct stframe *)((u8 *)(frame + 1) + + frame->count)) { if (WaitTillCardIsFree(base)) goto errrelfw; @@ -1863,6 +1855,17 @@ static int __devinit load_firmware(struct pci_dev *pdev, } } + /* xfer ctrl */ + if (WaitTillCardIsFree(base)) + goto errrelfw; + + outw(0xf2, base); + outw(0x800, base); + outw(0x0, base); + outw(0x0, base); + InterruptTheCard(base); + outw(0x0, base + 0x4); /* for ISI4608 cards */ + board->status |= FIRMWARE_LOADED; retval = 0; diff --git a/drivers/char/istallion.c b/drivers/char/istallion.c index 84dfc427813..6b4d82a4565 100644 --- a/drivers/char/istallion.c +++ b/drivers/char/istallion.c @@ -612,16 +612,6 @@ MODULE_DEVICE_TABLE(pci, istallion_pci_tbl); #define MINOR2BRD(min) (((min) & 0xc0) >> 6) #define MINOR2PORT(min) ((min) & 0x3f) -/* - * Define a baud rate table that converts termios baud rate selector - * into the actual baud rate value. All baud rate calculations are based - * on the actual baud rate required. - */ -static unsigned int stli_baudrates[] = { - 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, - 9600, 19200, 38400, 57600, 115200, 230400, 460800, 921600 -}; - /*****************************************************************************/ /* @@ -2747,15 +2737,7 @@ static void stli_mkasyport(stliport_t *portp, asyport_t *pp, struct termios *tio /* * Start of by setting the baud, char size, parity and stop bit info. */ - pp->baudout = tiosp->c_cflag & CBAUD; - if (pp->baudout & CBAUDEX) { - pp->baudout &= ~CBAUDEX; - if ((pp->baudout < 1) || (pp->baudout > 4)) - tiosp->c_cflag &= ~CBAUDEX; - else - pp->baudout += 15; - } - pp->baudout = stli_baudrates[pp->baudout]; + pp->baudout = tty_get_baud_rate(portp->tty); if ((tiosp->c_cflag & CBAUD) == B38400) { if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) pp->baudout = 57600; @@ -3488,7 +3470,7 @@ static int stli_initecp(stlibrd_t *brdp) */ EBRDENABLE(brdp); sigsp = (cdkecpsig_t __iomem *) EBRDGETMEMPTR(brdp, CDK_SIGADDR); - memcpy(&sig, sigsp, sizeof(cdkecpsig_t)); + memcpy_fromio(&sig, sigsp, sizeof(cdkecpsig_t)); EBRDDISABLE(brdp); if (sig.magic != cpu_to_le32(ECP_MAGIC)) diff --git a/drivers/char/lp.c b/drivers/char/lp.c index f875fda3b08..1ecea7d448f 100644 --- a/drivers/char/lp.c +++ b/drivers/char/lp.c @@ -906,7 +906,7 @@ static int __init lp_init (void) lp_class = class_create(THIS_MODULE, "printer"); if (IS_ERR(lp_class)) { err = PTR_ERR(lp_class); - goto out_devfs; + goto out_reg; } if (parport_register_driver (&lp_driver)) { @@ -927,7 +927,7 @@ static int __init lp_init (void) out_class: class_destroy(lp_class); -out_devfs: +out_reg: unregister_chrdev(LP_MAJOR, "lp"); return err; } diff --git a/drivers/char/mbcs.c b/drivers/char/mbcs.c index 0385650f607..63635472265 100644 --- a/drivers/char/mbcs.c +++ b/drivers/char/mbcs.c @@ -22,6 +22,7 @@ #include <linux/delay.h> #include <linux/device.h> #include <linux/mm.h> +#include <linux/fs.h> #include <linux/uio.h> #include <asm/io.h> #include <asm/uaccess.h> @@ -447,15 +448,15 @@ loff_t mbcs_sram_llseek(struct file * filp, loff_t off, int whence) loff_t newpos; switch (whence) { - case 0: /* SEEK_SET */ + case SEEK_SET: newpos = off; break; - case 1: /* SEEK_CUR */ + case SEEK_CUR: newpos = filp->f_pos + off; break; - case 2: /* SEEK_END */ + case SEEK_END: newpos = MBCS_SRAM_SIZE + off; break; diff --git a/drivers/char/mem.c b/drivers/char/mem.c index 917b2040266..6511012cbdc 100644 --- a/drivers/char/mem.c +++ b/drivers/char/mem.c @@ -238,6 +238,32 @@ static pgprot_t phys_mem_access_prot(struct file *file, unsigned long pfn, } #endif +#ifndef CONFIG_MMU +static unsigned long get_unmapped_area_mem(struct file *file, + unsigned long addr, + unsigned long len, + unsigned long pgoff, + unsigned long flags) +{ + if (!valid_mmap_phys_addr_range(pgoff, len)) + return (unsigned long) -EINVAL; + return pgoff; +} + +/* can't do an in-place private mapping if there's no MMU */ +static inline int private_mapping_ok(struct vm_area_struct *vma) +{ + return vma->vm_flags & VM_MAYSHARE; +} +#else +#define get_unmapped_area_mem NULL + +static inline int private_mapping_ok(struct vm_area_struct *vma) +{ + return 1; +} +#endif + static int mmap_mem(struct file * file, struct vm_area_struct * vma) { size_t size = vma->vm_end - vma->vm_start; @@ -245,6 +271,9 @@ static int mmap_mem(struct file * file, struct vm_area_struct * vma) if (!valid_mmap_phys_addr_range(vma->vm_pgoff, size)) return -EINVAL; + if (!private_mapping_ok(vma)) + return -ENOSYS; + vma->vm_page_prot = phys_mem_access_prot(file, vma->vm_pgoff, size, vma->vm_page_prot); @@ -522,7 +551,7 @@ static ssize_t write_kmem(struct file * file, const char __user * buf, return virtr + wrote; } -#if defined(CONFIG_ISA) || !defined(__mc68000__) +#if (defined(CONFIG_ISA) || defined(CONFIG_PCI)) && !defined(__mc68000__) static ssize_t read_port(struct file * file, char __user * buf, size_t count, loff_t *ppos) { @@ -782,6 +811,7 @@ static const struct file_operations mem_fops = { .write = write_mem, .mmap = mmap_mem, .open = open_mem, + .get_unmapped_area = get_unmapped_area_mem, }; static const struct file_operations kmem_fops = { @@ -790,6 +820,7 @@ static const struct file_operations kmem_fops = { .write = write_kmem, .mmap = mmap_kmem, .open = open_kmem, + .get_unmapped_area = get_unmapped_area_mem, }; static const struct file_operations null_fops = { @@ -799,7 +830,7 @@ static const struct file_operations null_fops = { .splice_write = splice_write_null, }; -#if defined(CONFIG_ISA) || !defined(__mc68000__) +#if (defined(CONFIG_ISA) || defined(CONFIG_PCI)) && !defined(__mc68000__) static const struct file_operations port_fops = { .llseek = memory_lseek, .read = read_port, @@ -815,6 +846,10 @@ static const struct file_operations zero_fops = { .mmap = mmap_zero, }; +/* + * capabilities for /dev/zero + * - permits private mappings, "copies" are taken of the source of zeros + */ static struct backing_dev_info zero_bdi = { .capabilities = BDI_CAP_MAP_COPY, }; @@ -862,14 +897,18 @@ static int memory_open(struct inode * inode, struct file * filp) switch (iminor(inode)) { case 1: filp->f_op = &mem_fops; + filp->f_mapping->backing_dev_info = + &directly_mappable_cdev_bdi; break; case 2: filp->f_op = &kmem_fops; + filp->f_mapping->backing_dev_info = + &directly_mappable_cdev_bdi; break; case 3: filp->f_op = &null_fops; break; -#if defined(CONFIG_ISA) || !defined(__mc68000__) +#if (defined(CONFIG_ISA) || defined(CONFIG_PCI)) && !defined(__mc68000__) case 4: filp->f_op = &port_fops; break; @@ -916,7 +955,7 @@ static const struct { {1, "mem", S_IRUSR | S_IWUSR | S_IRGRP, &mem_fops}, {2, "kmem", S_IRUSR | S_IWUSR | S_IRGRP, &kmem_fops}, {3, "null", S_IRUGO | S_IWUGO, &null_fops}, -#if defined(CONFIG_ISA) || !defined(__mc68000__) +#if (defined(CONFIG_ISA) || defined(CONFIG_PCI)) && !defined(__mc68000__) {4, "port", S_IRUSR | S_IWUSR | S_IRGRP, &port_fops}, #endif {5, "zero", S_IRUGO | S_IWUGO, &zero_fops}, diff --git a/drivers/char/moxa.c b/drivers/char/moxa.c index a369dd6877d..c1a6d3c48da 100644 --- a/drivers/char/moxa.c +++ b/drivers/char/moxa.c @@ -260,7 +260,7 @@ static void MoxaPortEnable(int); static void MoxaPortDisable(int); static long MoxaPortGetMaxBaud(int); static long MoxaPortSetBaud(int, long); -static int MoxaPortSetTermio(int, struct termios *); +static int MoxaPortSetTermio(int, struct termios *, speed_t); static int MoxaPortGetLineOut(int, int *, int *); static void MoxaPortLineCtrl(int, int, int); static void MoxaPortFlowCtrl(int, int, int, int, int, int); @@ -986,7 +986,7 @@ static void set_tty_param(struct tty_struct *tty) if (ts->c_iflag & IXANY) xany = 1; MoxaPortFlowCtrl(ch->port, rts, cts, txflow, rxflow, xany); - MoxaPortSetTermio(ch->port, ts); + MoxaPortSetTermio(ch->port, ts, tty_get_baud_rate(tty)); } static int block_till_ready(struct tty_struct *tty, struct file *filp, @@ -1900,9 +1900,10 @@ int MoxaPortsOfCard(int cardno) * * Function 12: Configure the port. * Syntax: - * int MoxaPortSetTermio(int port, struct termios *termio); + * int MoxaPortSetTermio(int port, struct termios *termio, speed_t baud); * int port : port number (0 - 127) * struct termios * termio : termio structure pointer + * speed_t baud : baud rate * * return: -1 : this port is invalid or termio == NULL * 0 : setting O.K. @@ -2182,11 +2183,10 @@ long MoxaPortSetBaud(int port, long baud) return (baud); } -int MoxaPortSetTermio(int port, struct termios *termio) +int MoxaPortSetTermio(int port, struct termios *termio, speed_t baud) { void __iomem *ofsAddr; tcflag_t cflag; - long baud; tcflag_t mode = 0; if (moxaChkPort[port] == 0 || termio == 0) @@ -2222,77 +2222,9 @@ int MoxaPortSetTermio(int port, struct termios *termio) moxafunc(ofsAddr, FC_SetDataMode, (ushort) mode); - cflag &= (CBAUD | CBAUDEX); -#ifndef B921600 -#define B921600 (B460800+1) -#endif - switch (cflag) { - case B921600: - baud = 921600L; - break; - case B460800: - baud = 460800L; - break; - case B230400: - baud = 230400L; - break; - case B115200: - baud = 115200L; - break; - case B57600: - baud = 57600L; - break; - case B38400: - baud = 38400L; - break; - case B19200: - baud = 19200L; - break; - case B9600: - baud = 9600L; - break; - case B4800: - baud = 4800L; - break; - case B2400: - baud = 2400L; - break; - case B1800: - baud = 1800L; - break; - case B1200: - baud = 1200L; - break; - case B600: - baud = 600L; - break; - case B300: - baud = 300L; - break; - case B200: - baud = 200L; - break; - case B150: - baud = 150L; - break; - case B134: - baud = 134L; - break; - case B110: - baud = 110L; - break; - case B75: - baud = 75L; - break; - case B50: - baud = 50L; - break; - default: - baud = 0; - } if ((moxa_boards[port / MAX_PORTS_PER_BOARD].boardType == MOXA_BOARD_C320_ISA) || (moxa_boards[port / MAX_PORTS_PER_BOARD].boardType == MOXA_BOARD_C320_PCI)) { - if (baud == 921600L) + if (baud >= 921600L) return (-1); } MoxaPortSetBaud(port, baud); diff --git a/drivers/char/mspec.c b/drivers/char/mspec.c new file mode 100644 index 00000000000..5426b1e5595 --- /dev/null +++ b/drivers/char/mspec.c @@ -0,0 +1,421 @@ +/* + * Copyright (C) 2001-2006 Silicon Graphics, Inc. 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. + */ + +/* + * SN Platform Special Memory (mspec) Support + * + * This driver exports the SN special memory (mspec) facility to user + * processes. + * There are three types of memory made available thru this driver: + * fetchops, uncached and cached. + * + * Fetchops are atomic memory operations that are implemented in the + * memory controller on SGI SN hardware. + * + * Uncached are used for memory write combining feature of the ia64 + * cpu. + * + * Cached are used for areas of memory that are used as cached addresses + * on our partition and used as uncached addresses from other partitions. + * Due to a design constraint of the SN2 Shub, you can not have processors + * on the same FSB perform both a cached and uncached reference to the + * same cache line. These special memory cached regions prevent the + * kernel from ever dropping in a TLB entry and therefore prevent the + * processor from ever speculating a cache line from this page. + */ + +#include <linux/config.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/errno.h> +#include <linux/miscdevice.h> +#include <linux/spinlock.h> +#include <linux/mm.h> +#include <linux/vmalloc.h> +#include <linux/string.h> +#include <linux/slab.h> +#include <linux/numa.h> +#include <asm/page.h> +#include <asm/system.h> +#include <asm/pgtable.h> +#include <asm/atomic.h> +#include <asm/tlbflush.h> +#include <asm/uncached.h> +#include <asm/sn/addrs.h> +#include <asm/sn/arch.h> +#include <asm/sn/mspec.h> +#include <asm/sn/sn_cpuid.h> +#include <asm/sn/io.h> +#include <asm/sn/bte.h> +#include <asm/sn/shubio.h> + + +#define FETCHOP_ID "SGI Fetchop," +#define CACHED_ID "Cached," +#define UNCACHED_ID "Uncached" +#define REVISION "4.0" +#define MSPEC_BASENAME "mspec" + +/* + * Page types allocated by the device. + */ +enum { + MSPEC_FETCHOP = 1, + MSPEC_CACHED, + MSPEC_UNCACHED +}; + +static int is_sn2; + +/* + * One of these structures is allocated when an mspec region is mmaped. The + * structure is pointed to by the vma->vm_private_data field in the vma struct. + * This structure is used to record the addresses of the mspec pages. + */ +struct vma_data { + atomic_t refcnt; /* Number of vmas sharing the data. */ + spinlock_t lock; /* Serialize access to the vma. */ + int count; /* Number of pages allocated. */ + int type; /* Type of pages allocated. */ + unsigned long maddr[0]; /* Array of MSPEC addresses. */ +}; + +/* used on shub2 to clear FOP cache in the HUB */ +static unsigned long scratch_page[MAX_NUMNODES]; +#define SH2_AMO_CACHE_ENTRIES 4 + +static inline int +mspec_zero_block(unsigned long addr, int len) +{ + int status; + + if (is_sn2) { + if (is_shub2()) { + int nid; + void *p; + int i; + + nid = nasid_to_cnodeid(get_node_number(__pa(addr))); + p = (void *)TO_AMO(scratch_page[nid]); + + for (i=0; i < SH2_AMO_CACHE_ENTRIES; i++) { + FETCHOP_LOAD_OP(p, FETCHOP_LOAD); + p += FETCHOP_VAR_SIZE; + } + } + + status = bte_copy(0, addr & ~__IA64_UNCACHED_OFFSET, len, + BTE_WACQUIRE | BTE_ZERO_FILL, NULL); + } else { + memset((char *) addr, 0, len); + status = 0; + } + return status; +} + +/* + * mspec_open + * + * Called when a device mapping is created by a means other than mmap + * (via fork, etc.). Increments the reference count on the underlying + * mspec data so it is not freed prematurely. + */ +static void +mspec_open(struct vm_area_struct *vma) +{ + struct vma_data *vdata; + + vdata = vma->vm_private_data; + atomic_inc(&vdata->refcnt); +} + +/* + * mspec_close + * + * Called when unmapping a device mapping. Frees all mspec pages + * belonging to the vma. + */ +static void +mspec_close(struct vm_area_struct *vma) +{ + struct vma_data *vdata; + int i, pages, result, vdata_size; + + vdata = vma->vm_private_data; + if (!atomic_dec_and_test(&vdata->refcnt)) + return; + + pages = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT; + vdata_size = sizeof(struct vma_data) + pages * sizeof(long); + for (i = 0; i < pages; i++) { + if (vdata->maddr[i] == 0) + continue; + /* + * Clear the page before sticking it back + * into the pool. + */ + result = mspec_zero_block(vdata->maddr[i], PAGE_SIZE); + if (!result) + uncached_free_page(vdata->maddr[i]); + else + printk(KERN_WARNING "mspec_close(): " + "failed to zero page %i\n", + result); + } + + if (vdata_size <= PAGE_SIZE) + kfree(vdata); + else + vfree(vdata); +} + + +/* + * mspec_nopfn + * + * Creates a mspec page and maps it to user space. + */ +static unsigned long +mspec_nopfn(struct vm_area_struct *vma, unsigned long address) +{ + unsigned long paddr, maddr; + unsigned long pfn; + int index; + struct vma_data *vdata = vma->vm_private_data; + + index = (address - vma->vm_start) >> PAGE_SHIFT; + maddr = (volatile unsigned long) vdata->maddr[index]; + if (maddr == 0) { + maddr = uncached_alloc_page(numa_node_id()); + if (maddr == 0) + return NOPFN_OOM; + + spin_lock(&vdata->lock); + if (vdata->maddr[index] == 0) { + vdata->count++; + vdata->maddr[index] = maddr; + } else { + uncached_free_page(maddr); + maddr = vdata->maddr[index]; + } + spin_unlock(&vdata->lock); + } + + if (vdata->type == MSPEC_FETCHOP) + paddr = TO_AMO(maddr); + else + paddr = __pa(TO_CAC(maddr)); + + pfn = paddr >> PAGE_SHIFT; + + return pfn; +} + +static struct vm_operations_struct mspec_vm_ops = { + .open = mspec_open, + .close = mspec_close, + .nopfn = mspec_nopfn +}; + +/* + * mspec_mmap + * + * Called when mmaping the device. Initializes the vma with a fault handler + * and private data structure necessary to allocate, track, and free the + * underlying pages. + */ +static int +mspec_mmap(struct file *file, struct vm_area_struct *vma, int type) +{ + struct vma_data *vdata; + int pages, vdata_size; + + if (vma->vm_pgoff != 0) + return -EINVAL; + + if ((vma->vm_flags & VM_SHARED) == 0) + return -EINVAL; + + if ((vma->vm_flags & VM_WRITE) == 0) + return -EPERM; + + pages = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT; + vdata_size = sizeof(struct vma_data) + pages * sizeof(long); + if (vdata_size <= PAGE_SIZE) + vdata = kmalloc(vdata_size, GFP_KERNEL); + else + vdata = vmalloc(vdata_size); + if (!vdata) + return -ENOMEM; + memset(vdata, 0, vdata_size); + + vdata->type = type; + spin_lock_init(&vdata->lock); + vdata->refcnt = ATOMIC_INIT(1); + vma->vm_private_data = vdata; + + vma->vm_flags |= (VM_IO | VM_LOCKED | VM_RESERVED | VM_PFNMAP); + if (vdata->type == MSPEC_FETCHOP || vdata->type == MSPEC_UNCACHED) + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + vma->vm_ops = &mspec_vm_ops; + + return 0; +} + +static int +fetchop_mmap(struct file *file, struct vm_area_struct *vma) +{ + return mspec_mmap(file, vma, MSPEC_FETCHOP); +} + +static int +cached_mmap(struct file *file, struct vm_area_struct *vma) +{ + return mspec_mmap(file, vma, MSPEC_CACHED); +} + +static int +uncached_mmap(struct file *file, struct vm_area_struct *vma) +{ + return mspec_mmap(file, vma, MSPEC_UNCACHED); +} + +static struct file_operations fetchop_fops = { + .owner = THIS_MODULE, + .mmap = fetchop_mmap +}; + +static struct miscdevice fetchop_miscdev = { + .minor = MISC_DYNAMIC_MINOR, + .name = "sgi_fetchop", + .fops = &fetchop_fops +}; + +static struct file_operations cached_fops = { + .owner = THIS_MODULE, + .mmap = cached_mmap +}; + +static struct miscdevice cached_miscdev = { + .minor = MISC_DYNAMIC_MINOR, + .name = "mspec_cached", + .fops = &cached_fops +}; + +static struct file_operations uncached_fops = { + .owner = THIS_MODULE, + .mmap = uncached_mmap +}; + +static struct miscdevice uncached_miscdev = { + .minor = MISC_DYNAMIC_MINOR, + .name = "mspec_uncached", + .fops = &uncached_fops +}; + +/* + * mspec_init + * + * Called at boot time to initialize the mspec facility. + */ +static int __init +mspec_init(void) +{ + int ret; + int nid; + + /* + * The fetchop device only works on SN2 hardware, uncached and cached + * memory drivers should both be valid on all ia64 hardware + */ + if (ia64_platform_is("sn2")) { + is_sn2 = 1; + if (is_shub2()) { + ret = -ENOMEM; + for_each_online_node(nid) { + int actual_nid; + int nasid; + unsigned long phys; + + scratch_page[nid] = uncached_alloc_page(nid); + if (scratch_page[nid] == 0) + goto free_scratch_pages; + phys = __pa(scratch_page[nid]); + nasid = get_node_number(phys); + actual_nid = nasid_to_cnodeid(nasid); + if (actual_nid != nid) + goto free_scratch_pages; + } + } + + ret = misc_register(&fetchop_miscdev); + if (ret) { + printk(KERN_ERR + "%s: failed to register device %i\n", + FETCHOP_ID, ret); + goto free_scratch_pages; + } + } + ret = misc_register(&cached_miscdev); + if (ret) { + printk(KERN_ERR "%s: failed to register device %i\n", + CACHED_ID, ret); + if (is_sn2) + misc_deregister(&fetchop_miscdev); + goto free_scratch_pages; + } + ret = misc_register(&uncached_miscdev); + if (ret) { + printk(KERN_ERR "%s: failed to register device %i\n", + UNCACHED_ID, ret); + misc_deregister(&cached_miscdev); + if (is_sn2) + misc_deregister(&fetchop_miscdev); + goto free_scratch_pages; + } + + printk(KERN_INFO "%s %s initialized devices: %s %s %s\n", + MSPEC_BASENAME, REVISION, is_sn2 ? FETCHOP_ID : "", + CACHED_ID, UNCACHED_ID); + + return 0; + + free_scratch_pages: + for_each_node(nid) { + if (scratch_page[nid] != 0) + uncached_free_page(scratch_page[nid]); + } + return ret; +} + +static void __exit +mspec_exit(void) +{ + int nid; + + misc_deregister(&uncached_miscdev); + misc_deregister(&cached_miscdev); + if (is_sn2) { + misc_deregister(&fetchop_miscdev); + + for_each_node(nid) { + if (scratch_page[nid] != 0) + uncached_free_page(scratch_page[nid]); + } + } +} + +module_init(mspec_init); +module_exit(mspec_exit); + +MODULE_AUTHOR("Silicon Graphics, Inc. <linux-altix@sgi.com>"); +MODULE_DESCRIPTION("Driver for SGI SN special memory operations"); +MODULE_LICENSE("GPL"); diff --git a/drivers/char/mwave/mwavedd.c b/drivers/char/mwave/mwavedd.c index 39a2e661ff5..8d14823b051 100644 --- a/drivers/char/mwave/mwavedd.c +++ b/drivers/char/mwave/mwavedd.c @@ -297,7 +297,7 @@ static int mwave_ioctl(struct inode *inode, struct file *file, " ipcnum %x, usIntCount %x\n", ipcnum, pDrvData->IPCs[ipcnum].usIntCount); - if (ipcnum > ARRAY_SIZE(pDrvData->IPCs)) { + if (ipcnum >= ARRAY_SIZE(pDrvData->IPCs)) { PRINTK_ERROR(KERN_ERR_MWAVE "mwavedd::mwave_ioctl:" " IOCTL_MW_GET_IPC: Error:" @@ -355,7 +355,7 @@ static int mwave_ioctl(struct inode *inode, struct file *file, "mwavedd::mwave_ioctl IOCTL_MW_UNREGISTER_IPC" " ipcnum %x\n", ipcnum); - if (ipcnum > ARRAY_SIZE(pDrvData->IPCs)) { + if (ipcnum >= ARRAY_SIZE(pDrvData->IPCs)) { PRINTK_ERROR(KERN_ERR_MWAVE "mwavedd::mwave_ioctl:" " IOCTL_MW_UNREGISTER_IPC:" diff --git a/drivers/char/mxser.c b/drivers/char/mxser.c index 556abd3e0d0..27a65377204 100644 --- a/drivers/char/mxser.c +++ b/drivers/char/mxser.c @@ -2554,71 +2554,7 @@ static int mxser_change_speed(struct mxser_struct *info, struct termios *old_ter #define B921600 (B460800 +1) #endif if (mxser_set_baud_method[info->port] == 0) { - switch (cflag & (CBAUD | CBAUDEX)) { - case B921600: - baud = 921600; - break; - case B460800: - baud = 460800; - break; - case B230400: - baud = 230400; - break; - case B115200: - baud = 115200; - break; - case B57600: - baud = 57600; - break; - case B38400: - baud = 38400; - break; - case B19200: - baud = 19200; - break; - case B9600: - baud = 9600; - break; - case B4800: - baud = 4800; - break; - case B2400: - baud = 2400; - break; - case B1800: - baud = 1800; - break; - case B1200: - baud = 1200; - break; - case B600: - baud = 600; - break; - case B300: - baud = 300; - break; - case B200: - baud = 200; - break; - case B150: - baud = 150; - break; - case B134: - baud = 134; - break; - case B110: - baud = 110; - break; - case B75: - baud = 75; - break; - case B50: - baud = 50; - break; - default: - baud = 0; - break; - } + baud = tty_get_baud_rate(info->tty); mxser_set_baud(info, baud); } diff --git a/drivers/char/pc8736x_gpio.c b/drivers/char/pc8736x_gpio.c index 84e5a68635f..ecfaf180e5b 100644 --- a/drivers/char/pc8736x_gpio.c +++ b/drivers/char/pc8736x_gpio.c @@ -188,16 +188,6 @@ static void pc8736x_gpio_set(unsigned minor, int val) pc8736x_gpio_shadow[port] = val; } -static void pc8736x_gpio_set_high(unsigned index) -{ - pc8736x_gpio_set(index, 1); -} - -static void pc8736x_gpio_set_low(unsigned index) -{ - pc8736x_gpio_set(index, 0); -} - static int pc8736x_gpio_current(unsigned minor) { int port, bit; diff --git a/drivers/char/random.c b/drivers/char/random.c index 4c3a5ca9d8f..b430a12eb81 100644 --- a/drivers/char/random.c +++ b/drivers/char/random.c @@ -655,6 +655,7 @@ void add_interrupt_randomness(int irq) add_timer_randomness(irq_timer_state[irq], 0x100 + irq); } +#ifdef CONFIG_BLOCK void add_disk_randomness(struct gendisk *disk) { if (!disk || !disk->random) @@ -667,6 +668,7 @@ void add_disk_randomness(struct gendisk *disk) } EXPORT_SYMBOL(add_disk_randomness); +#endif #define EXTRACT_SIZE 10 @@ -918,6 +920,7 @@ void rand_initialize_irq(int irq) } } +#ifdef CONFIG_BLOCK void rand_initialize_disk(struct gendisk *disk) { struct timer_rand_state *state; @@ -932,6 +935,7 @@ void rand_initialize_disk(struct gendisk *disk) disk->random = state; } } +#endif static ssize_t random_read(struct file * file, char __user * buf, size_t nbytes, loff_t *ppos) diff --git a/drivers/char/raw.c b/drivers/char/raw.c index 579868af4a5..89b718e326e 100644 --- a/drivers/char/raw.c +++ b/drivers/char/raw.c @@ -238,39 +238,14 @@ out: return err; } -static ssize_t raw_file_write(struct file *file, const char __user *buf, - size_t count, loff_t *ppos) -{ - struct iovec local_iov = { - .iov_base = (char __user *)buf, - .iov_len = count - }; - - return generic_file_write_nolock(file, &local_iov, 1, ppos); -} - -static ssize_t raw_file_aio_write(struct kiocb *iocb, const char __user *buf, - size_t count, loff_t pos) -{ - struct iovec local_iov = { - .iov_base = (char __user *)buf, - .iov_len = count - }; - - return generic_file_aio_write_nolock(iocb, &local_iov, 1, &iocb->ki_pos); -} - - static const struct file_operations raw_fops = { - .read = generic_file_read, + .read = do_sync_read, .aio_read = generic_file_aio_read, - .write = raw_file_write, - .aio_write = raw_file_aio_write, + .write = do_sync_write, + .aio_write = generic_file_aio_write_nolock, .open = raw_open, .release= raw_release, .ioctl = raw_ioctl, - .readv = generic_file_readv, - .writev = generic_file_writev, .owner = THIS_MODULE, }; @@ -288,31 +263,34 @@ static struct cdev raw_cdev = { static int __init raw_init(void) { dev_t dev = MKDEV(RAW_MAJOR, 0); + int ret; - if (register_chrdev_region(dev, MAX_RAW_MINORS, "raw")) + ret = register_chrdev_region(dev, MAX_RAW_MINORS, "raw"); + if (ret) goto error; cdev_init(&raw_cdev, &raw_fops); - if (cdev_add(&raw_cdev, dev, MAX_RAW_MINORS)) { + ret = cdev_add(&raw_cdev, dev, MAX_RAW_MINORS); + if (ret) { kobject_put(&raw_cdev.kobj); - unregister_chrdev_region(dev, MAX_RAW_MINORS); - goto error; + goto error_region; } raw_class = class_create(THIS_MODULE, "raw"); if (IS_ERR(raw_class)) { printk(KERN_ERR "Error creating raw class.\n"); cdev_del(&raw_cdev); - unregister_chrdev_region(dev, MAX_RAW_MINORS); - goto error; + ret = PTR_ERR(raw_class); + goto error_region; } class_device_create(raw_class, NULL, MKDEV(RAW_MAJOR, 0), NULL, "rawctl"); return 0; +error_region: + unregister_chrdev_region(dev, MAX_RAW_MINORS); error: - printk(KERN_ERR "error register raw device\n"); - return 1; + return ret; } static void __exit raw_exit(void) diff --git a/drivers/char/riscom8.c b/drivers/char/riscom8.c index f1c94f771af..06b9f78a95d 100644 --- a/drivers/char/riscom8.c +++ b/drivers/char/riscom8.c @@ -675,26 +675,12 @@ static void rc_change_speed(struct riscom_board *bp, struct riscom_port *port) port->COR2 = 0; port->MSVR = MSVR_RTS; - baud = C_BAUD(tty); - - if (baud & CBAUDEX) { - baud &= ~CBAUDEX; - if (baud < 1 || baud > 2) - port->tty->termios->c_cflag &= ~CBAUDEX; - else - baud += 15; - } - if (baud == 15) { - if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) - baud ++; - if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) - baud += 2; - } + baud = tty_get_baud_rate(tty); /* Select port on the board */ rc_out(bp, CD180_CAR, port_No(port)); - if (!baud_table[baud]) { + if (!baud) { /* Drop DTR & exit */ bp->DTR |= (1u << port_No(port)); rc_out(bp, RC_DTR, bp->DTR); @@ -710,7 +696,7 @@ static void rc_change_speed(struct riscom_board *bp, struct riscom_port *port) */ /* Set baud rate for port */ - tmp = (((RC_OSCFREQ + baud_table[baud]/2) / baud_table[baud] + + tmp = (((RC_OSCFREQ + baud/2) / baud + CD180_TPC/2) / CD180_TPC); rc_out(bp, CD180_RBPRH, (tmp >> 8) & 0xff); @@ -718,7 +704,7 @@ static void rc_change_speed(struct riscom_board *bp, struct riscom_port *port) rc_out(bp, CD180_RBPRL, tmp & 0xff); rc_out(bp, CD180_TBPRL, tmp & 0xff); - baud = (baud_table[baud] + 5) / 10; /* Estimated CPS */ + baud = (baud + 5) / 10; /* Estimated CPS */ /* Two timer ticks seems enough to wakeup something like SLIP driver */ tmp = ((baud + HZ/2) / HZ) * 2 - CD180_NFIFO; diff --git a/drivers/char/rtc.c b/drivers/char/rtc.c index 6e6a7c7a7ef..656f8c0ca52 100644 --- a/drivers/char/rtc.c +++ b/drivers/char/rtc.c @@ -209,11 +209,12 @@ static const unsigned char days_in_mo[] = */ static inline unsigned char rtc_is_updating(void) { + unsigned long flags; unsigned char uip; - spin_lock_irq(&rtc_lock); + spin_lock_irqsave(&rtc_lock, flags); uip = (CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP); - spin_unlock_irq(&rtc_lock); + spin_unlock_irqrestore(&rtc_lock, flags); return uip; } @@ -1261,10 +1262,8 @@ void rtc_get_rtc_time(struct rtc_time *rtc_tm) * Once the read clears, read the RTC time (again via ioctl). Easy. */ - while (rtc_is_updating() != 0 && jiffies - uip_watchdog < 2*HZ/100) { - barrier(); + while (rtc_is_updating() != 0 && jiffies - uip_watchdog < 2*HZ/100) cpu_relax(); - } /* * Only the values that we read from the RTC are set. We leave diff --git a/drivers/char/s3c2410-rtc.c b/drivers/char/s3c2410-rtc.c deleted file mode 100644 index 5458ef1634e..00000000000 --- a/drivers/char/s3c2410-rtc.c +++ /dev/null @@ -1,591 +0,0 @@ -/* drivers/char/s3c2410_rtc.c - * - * Copyright (c) 2004 Simtec Electronics <linux@simtec.co.uk> - * http://www.simtec.co.uk/products/SWLINUX/ - * - * 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. - * - * S3C2410 Internal RTC Driver - * - * Changelog: - * 08-Nov-2004 BJD Initial creation - * 12-Nov-2004 BJD Added periodic IRQ and PM code - * 22-Nov-2004 BJD Sign-test on alarm code to check for <0 - * 10-Mar-2005 LCVR Changed S3C2410_VA_RTC to S3C24XX_VA_RTC -*/ - -#include <linux/module.h> -#include <linux/fs.h> -#include <linux/string.h> -#include <linux/init.h> -#include <linux/platform_device.h> -#include <linux/interrupt.h> -#include <linux/rtc.h> -#include <linux/bcd.h> -#include <linux/clk.h> - -#include <asm/hardware.h> -#include <asm/uaccess.h> -#include <asm/io.h> -#include <asm/irq.h> -#include <asm/rtc.h> - -#include <asm/mach/time.h> - -#include <asm/arch/regs-rtc.h> - -/* need this for the RTC_AF definitions */ -#include <linux/mc146818rtc.h> - -#undef S3C24XX_VA_RTC -#define S3C24XX_VA_RTC s3c2410_rtc_base - -static struct resource *s3c2410_rtc_mem; - -static void __iomem *s3c2410_rtc_base; -static int s3c2410_rtc_alarmno = NO_IRQ; -static int s3c2410_rtc_tickno = NO_IRQ; -static int s3c2410_rtc_freq = 1; - -static DEFINE_SPINLOCK(s3c2410_rtc_pie_lock); - -/* IRQ Handlers */ - -static irqreturn_t s3c2410_rtc_alarmirq(int irq, void *id, struct pt_regs *r) -{ - rtc_update(1, RTC_AF | RTC_IRQF); - return IRQ_HANDLED; -} - -static irqreturn_t s3c2410_rtc_tickirq(int irq, void *id, struct pt_regs *r) -{ - rtc_update(1, RTC_PF | RTC_IRQF); - return IRQ_HANDLED; -} - -/* Update control registers */ -static void s3c2410_rtc_setaie(int to) -{ - unsigned int tmp; - - pr_debug("%s: aie=%d\n", __FUNCTION__, to); - - tmp = readb(S3C2410_RTCALM); - - if (to) - tmp |= S3C2410_RTCALM_ALMEN; - else - tmp &= ~S3C2410_RTCALM_ALMEN; - - - writeb(tmp, S3C2410_RTCALM); -} - -static void s3c2410_rtc_setpie(int to) -{ - unsigned int tmp; - - pr_debug("%s: pie=%d\n", __FUNCTION__, to); - - spin_lock_irq(&s3c2410_rtc_pie_lock); - tmp = readb(S3C2410_TICNT) & ~S3C2410_TICNT_ENABLE; - - if (to) - tmp |= S3C2410_TICNT_ENABLE; - - writeb(tmp, S3C2410_TICNT); - spin_unlock_irq(&s3c2410_rtc_pie_lock); -} - -static void s3c2410_rtc_setfreq(int freq) -{ - unsigned int tmp; - - spin_lock_irq(&s3c2410_rtc_pie_lock); - tmp = readb(S3C2410_TICNT) & S3C2410_TICNT_ENABLE; - - s3c2410_rtc_freq = freq; - - tmp |= (128 / freq)-1; - - writeb(tmp, S3C2410_TICNT); - spin_unlock_irq(&s3c2410_rtc_pie_lock); -} - -/* Time read/write */ - -static int s3c2410_rtc_gettime(struct rtc_time *rtc_tm) -{ - unsigned int have_retried = 0; - - retry_get_time: - rtc_tm->tm_min = readb(S3C2410_RTCMIN); - rtc_tm->tm_hour = readb(S3C2410_RTCHOUR); - rtc_tm->tm_mday = readb(S3C2410_RTCDATE); - rtc_tm->tm_mon = readb(S3C2410_RTCMON); - rtc_tm->tm_year = readb(S3C2410_RTCYEAR); - rtc_tm->tm_sec = readb(S3C2410_RTCSEC); - - /* the only way to work out wether the system was mid-update - * when we read it is to check the second counter, and if it - * is zero, then we re-try the entire read - */ - - if (rtc_tm->tm_sec == 0 && !have_retried) { - have_retried = 1; - goto retry_get_time; - } - - pr_debug("read time %02x.%02x.%02x %02x/%02x/%02x\n", - rtc_tm->tm_year, rtc_tm->tm_mon, rtc_tm->tm_mday, - rtc_tm->tm_hour, rtc_tm->tm_min, rtc_tm->tm_sec); - - 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); - - rtc_tm->tm_year += 100; - rtc_tm->tm_mon -= 1; - - return 0; -} - - -static int s3c2410_rtc_settime(struct rtc_time *tm) -{ - /* the rtc gets round the y2k problem by just not supporting it */ - - if (tm->tm_year < 100) - return -EINVAL; - - writeb(BIN2BCD(tm->tm_sec), S3C2410_RTCSEC); - writeb(BIN2BCD(tm->tm_min), S3C2410_RTCMIN); - writeb(BIN2BCD(tm->tm_hour), S3C2410_RTCHOUR); - writeb(BIN2BCD(tm->tm_mday), S3C2410_RTCDATE); - writeb(BIN2BCD(tm->tm_mon + 1), S3C2410_RTCMON); - writeb(BIN2BCD(tm->tm_year - 100), S3C2410_RTCYEAR); - - return 0; -} - -static int s3c2410_rtc_getalarm(struct rtc_wkalrm *alrm) -{ - struct rtc_time *alm_tm = &alrm->time; - unsigned int alm_en; - - alm_tm->tm_sec = readb(S3C2410_ALMSEC); - alm_tm->tm_min = readb(S3C2410_ALMMIN); - alm_tm->tm_hour = readb(S3C2410_ALMHOUR); - alm_tm->tm_mon = readb(S3C2410_ALMMON); - alm_tm->tm_mday = readb(S3C2410_ALMDATE); - alm_tm->tm_year = readb(S3C2410_ALMYEAR); - - alm_en = readb(S3C2410_RTCALM); - - pr_debug("read alarm %02x %02x.%02x.%02x %02x/%02x/%02x\n", - alm_en, - alm_tm->tm_year, alm_tm->tm_mon, alm_tm->tm_mday, - alm_tm->tm_hour, alm_tm->tm_min, alm_tm->tm_sec); - - - /* decode the alarm enable field */ - - if (alm_en & S3C2410_RTCALM_SECEN) { - BCD_TO_BIN(alm_tm->tm_sec); - } else { - alm_tm->tm_sec = 0xff; - } - - if (alm_en & S3C2410_RTCALM_MINEN) { - BCD_TO_BIN(alm_tm->tm_min); - } else { - alm_tm->tm_min = 0xff; - } - - if (alm_en & S3C2410_RTCALM_HOUREN) { - BCD_TO_BIN(alm_tm->tm_hour); - } else { - alm_tm->tm_hour = 0xff; - } - - if (alm_en & S3C2410_RTCALM_DAYEN) { - BCD_TO_BIN(alm_tm->tm_mday); - } else { - alm_tm->tm_mday = 0xff; - } - - if (alm_en & S3C2410_RTCALM_MONEN) { - BCD_TO_BIN(alm_tm->tm_mon); - alm_tm->tm_mon -= 1; - } else { - alm_tm->tm_mon = 0xff; - } - - if (alm_en & S3C2410_RTCALM_YEAREN) { - BCD_TO_BIN(alm_tm->tm_year); - } else { - alm_tm->tm_year = 0xffff; - } - - /* todo - set alrm->enabled ? */ - - return 0; -} - -static int s3c2410_rtc_setalarm(struct rtc_wkalrm *alrm) -{ - struct rtc_time *tm = &alrm->time; - unsigned int alrm_en; - - pr_debug("s3c2410_rtc_setalarm: %d, %02x/%02x/%02x %02x.%02x.%02x\n", - alrm->enabled, - tm->tm_mday & 0xff, tm->tm_mon & 0xff, tm->tm_year & 0xff, - tm->tm_hour & 0xff, tm->tm_min & 0xff, tm->tm_sec); - - if (alrm->enabled || 1) { - alrm_en = readb(S3C2410_RTCALM) & S3C2410_RTCALM_ALMEN; - writeb(0x00, S3C2410_RTCALM); - - if (tm->tm_sec < 60 && tm->tm_sec >= 0) { - alrm_en |= S3C2410_RTCALM_SECEN; - writeb(BIN2BCD(tm->tm_sec), S3C2410_ALMSEC); - } - - if (tm->tm_min < 60 && tm->tm_min >= 0) { - alrm_en |= S3C2410_RTCALM_MINEN; - writeb(BIN2BCD(tm->tm_min), S3C2410_ALMMIN); - } - - if (tm->tm_hour < 24 && tm->tm_hour >= 0) { - alrm_en |= S3C2410_RTCALM_HOUREN; - writeb(BIN2BCD(tm->tm_hour), S3C2410_ALMHOUR); - } - - pr_debug("setting S3C2410_RTCALM to %08x\n", alrm_en); - - writeb(alrm_en, S3C2410_RTCALM); - enable_irq_wake(s3c2410_rtc_alarmno); - } else { - alrm_en = readb(S3C2410_RTCALM); - alrm_en &= ~S3C2410_RTCALM_ALMEN; - writeb(alrm_en, S3C2410_RTCALM); - disable_irq_wake(s3c2410_rtc_alarmno); - } - - return 0; -} - -static int s3c2410_rtc_ioctl(unsigned int cmd, unsigned long arg) -{ - switch (cmd) { - case RTC_AIE_OFF: - case RTC_AIE_ON: - s3c2410_rtc_setaie((cmd == RTC_AIE_ON) ? 1 : 0); - return 0; - - case RTC_PIE_OFF: - case RTC_PIE_ON: - s3c2410_rtc_setpie((cmd == RTC_PIE_ON) ? 1 : 0); - return 0; - - case RTC_IRQP_READ: - return put_user(s3c2410_rtc_freq, (unsigned long __user *)arg); - - case RTC_IRQP_SET: - if (arg < 1 || arg > 64) - return -EINVAL; - - if (!capable(CAP_SYS_RESOURCE)) - return -EACCES; - - /* check for power of 2 */ - - if ((arg & (arg-1)) != 0) - return -EINVAL; - - pr_debug("s3c2410_rtc: setting frequency %ld\n", arg); - - s3c2410_rtc_setfreq(arg); - return 0; - - case RTC_UIE_ON: - case RTC_UIE_OFF: - return -EINVAL; - } - - return -EINVAL; -} - -static int s3c2410_rtc_proc(char *buf) -{ - unsigned int rtcalm = readb(S3C2410_RTCALM); - unsigned int ticnt = readb (S3C2410_TICNT); - char *p = buf; - - p += sprintf(p, "alarm_IRQ\t: %s\n", - (rtcalm & S3C2410_RTCALM_ALMEN) ? "yes" : "no" ); - p += sprintf(p, "periodic_IRQ\t: %s\n", - (ticnt & S3C2410_TICNT_ENABLE) ? "yes" : "no" ); - p += sprintf(p, "periodic_freq\t: %d\n", s3c2410_rtc_freq); - - return p - buf; -} - -static int s3c2410_rtc_open(void) -{ - int ret; - - ret = request_irq(s3c2410_rtc_alarmno, s3c2410_rtc_alarmirq, - IRQF_DISABLED, "s3c2410-rtc alarm", NULL); - - if (ret) - printk(KERN_ERR "IRQ%d already in use\n", s3c2410_rtc_alarmno); - - ret = request_irq(s3c2410_rtc_tickno, s3c2410_rtc_tickirq, - IRQF_DISABLED, "s3c2410-rtc tick", NULL); - - if (ret) { - printk(KERN_ERR "IRQ%d already in use\n", s3c2410_rtc_tickno); - goto tick_err; - } - - return ret; - - tick_err: - free_irq(s3c2410_rtc_alarmno, NULL); - return ret; -} - -static void s3c2410_rtc_release(void) -{ - /* do not clear AIE here, it may be needed for wake */ - - s3c2410_rtc_setpie(0); - free_irq(s3c2410_rtc_alarmno, NULL); - free_irq(s3c2410_rtc_tickno, NULL); -} - -static struct rtc_ops s3c2410_rtcops = { - .owner = THIS_MODULE, - .open = s3c2410_rtc_open, - .release = s3c2410_rtc_release, - .ioctl = s3c2410_rtc_ioctl, - .read_time = s3c2410_rtc_gettime, - .set_time = s3c2410_rtc_settime, - .read_alarm = s3c2410_rtc_getalarm, - .set_alarm = s3c2410_rtc_setalarm, - .proc = s3c2410_rtc_proc, -}; - -static void s3c2410_rtc_enable(struct platform_device *pdev, int en) -{ - unsigned int tmp; - - if (s3c2410_rtc_base == NULL) - return; - - if (!en) { - tmp = readb(S3C2410_RTCCON); - writeb(tmp & ~S3C2410_RTCCON_RTCEN, S3C2410_RTCCON); - - tmp = readb(S3C2410_TICNT); - writeb(tmp & ~S3C2410_TICNT_ENABLE, S3C2410_TICNT); - } else { - /* re-enable the device, and check it is ok */ - - if ((readb(S3C2410_RTCCON) & S3C2410_RTCCON_RTCEN) == 0){ - dev_info(&pdev->dev, "rtc disabled, re-enabling\n"); - - tmp = readb(S3C2410_RTCCON); - writeb(tmp | S3C2410_RTCCON_RTCEN , S3C2410_RTCCON); - } - - if ((readb(S3C2410_RTCCON) & S3C2410_RTCCON_CNTSEL)){ - dev_info(&pdev->dev, "removing S3C2410_RTCCON_CNTSEL\n"); - - tmp = readb(S3C2410_RTCCON); - writeb(tmp& ~S3C2410_RTCCON_CNTSEL , S3C2410_RTCCON); - } - - if ((readb(S3C2410_RTCCON) & S3C2410_RTCCON_CLKRST)){ - dev_info(&pdev->dev, "removing S3C2410_RTCCON_CLKRST\n"); - - tmp = readb(S3C2410_RTCCON); - writeb(tmp & ~S3C2410_RTCCON_CLKRST, S3C2410_RTCCON); - } - } -} - -static int s3c2410_rtc_remove(struct platform_device *dev) -{ - unregister_rtc(&s3c2410_rtcops); - - s3c2410_rtc_setpie(0); - s3c2410_rtc_setaie(0); - - if (s3c2410_rtc_mem != NULL) { - pr_debug("s3c2410_rtc: releasing s3c2410_rtc_mem\n"); - iounmap(s3c2410_rtc_base); - release_resource(s3c2410_rtc_mem); - kfree(s3c2410_rtc_mem); - } - - return 0; -} - -static int s3c2410_rtc_probe(struct platform_device *pdev) -{ - struct resource *res; - int ret; - - pr_debug("%s: probe=%p\n", __FUNCTION__, pdev); - - /* find the IRQs */ - - s3c2410_rtc_tickno = platform_get_irq(pdev, 1); - if (s3c2410_rtc_tickno < 0) { - dev_err(&pdev->dev, "no irq for rtc tick\n"); - return -ENOENT; - } - - s3c2410_rtc_alarmno = platform_get_irq(pdev, 0); - if (s3c2410_rtc_alarmno < 0) { - dev_err(&pdev->dev, "no irq for alarm\n"); - return -ENOENT; - } - - pr_debug("s3c2410_rtc: tick irq %d, alarm irq %d\n", - s3c2410_rtc_tickno, s3c2410_rtc_alarmno); - - /* get the memory region */ - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (res == NULL) { - dev_err(&pdev->dev, "failed to get memory region resource\n"); - return -ENOENT; - } - - s3c2410_rtc_mem = request_mem_region(res->start, res->end-res->start+1, - pdev->name); - - if (s3c2410_rtc_mem == NULL) { - dev_err(&pdev->dev, "failed to reserve memory region\n"); - ret = -ENOENT; - goto exit_err; - } - - s3c2410_rtc_base = ioremap(res->start, res->end - res->start + 1); - if (s3c2410_rtc_base == NULL) { - dev_err(&pdev->dev, "failed ioremap()\n"); - ret = -EINVAL; - goto exit_err; - } - - s3c2410_rtc_mem = res; - pr_debug("s3c2410_rtc_base=%p\n", s3c2410_rtc_base); - - pr_debug("s3c2410_rtc: RTCCON=%02x\n", readb(S3C2410_RTCCON)); - - /* check to see if everything is setup correctly */ - - s3c2410_rtc_enable(pdev, 1); - - pr_debug("s3c2410_rtc: RTCCON=%02x\n", readb(S3C2410_RTCCON)); - - s3c2410_rtc_setfreq(s3c2410_rtc_freq); - - /* register RTC and exit */ - - register_rtc(&s3c2410_rtcops); - return 0; - - exit_err: - dev_err(&pdev->dev, "error %d during initialisation\n", ret); - - return ret; -} - -#ifdef CONFIG_PM - -/* S3C2410 RTC Power management control */ - -static struct timespec s3c2410_rtc_delta; - -static int ticnt_save; - -static int s3c2410_rtc_suspend(struct platform_device *pdev, pm_message_t state) -{ - struct rtc_time tm; - struct timespec time; - - time.tv_nsec = 0; - - /* save TICNT for anyone using periodic interrupts */ - - ticnt_save = readb(S3C2410_TICNT); - - /* calculate time delta for suspend */ - - s3c2410_rtc_gettime(&tm); - rtc_tm_to_time(&tm, &time.tv_sec); - save_time_delta(&s3c2410_rtc_delta, &time); - s3c2410_rtc_enable(pdev, 0); - - return 0; -} - -static int s3c2410_rtc_resume(struct platform_device *pdev) -{ - struct rtc_time tm; - struct timespec time; - - time.tv_nsec = 0; - - s3c2410_rtc_enable(pdev, 1); - s3c2410_rtc_gettime(&tm); - rtc_tm_to_time(&tm, &time.tv_sec); - restore_time_delta(&s3c2410_rtc_delta, &time); - - writeb(ticnt_save, S3C2410_TICNT); - return 0; -} -#else -#define s3c2410_rtc_suspend NULL -#define s3c2410_rtc_resume NULL -#endif - -static struct platform_driver s3c2410_rtcdrv = { - .probe = s3c2410_rtc_probe, - .remove = s3c2410_rtc_remove, - .suspend = s3c2410_rtc_suspend, - .resume = s3c2410_rtc_resume, - .driver = { - .name = "s3c2410-rtc", - .owner = THIS_MODULE, - }, -}; - -static char __initdata banner[] = "S3C2410 RTC, (c) 2004 Simtec Electronics\n"; - -static int __init s3c2410_rtc_init(void) -{ - printk(banner); - return platform_driver_register(&s3c2410_rtcdrv); -} - -static void __exit s3c2410_rtc_exit(void) -{ - platform_driver_unregister(&s3c2410_rtcdrv); -} - -module_init(s3c2410_rtc_init); -module_exit(s3c2410_rtc_exit); - -MODULE_DESCRIPTION("S3C24XX RTC Driver"); -MODULE_AUTHOR("Ben Dooks, <ben@simtec.co.uk>"); -MODULE_LICENSE("GPL"); diff --git a/drivers/char/scx200_gpio.c b/drivers/char/scx200_gpio.c index b956c7babd1..99e5272e3c5 100644 --- a/drivers/char/scx200_gpio.c +++ b/drivers/char/scx200_gpio.c @@ -44,7 +44,7 @@ struct nsc_gpio_ops scx200_gpio_ops = { .gpio_change = scx200_gpio_change, .gpio_current = scx200_gpio_current }; -EXPORT_SYMBOL(scx200_gpio_ops); +EXPORT_SYMBOL_GPL(scx200_gpio_ops); static int scx200_gpio_open(struct inode *inode, struct file *file) { @@ -69,7 +69,7 @@ static const struct file_operations scx200_gpio_fileops = { .release = scx200_gpio_release, }; -struct cdev scx200_gpio_cdev; /* use 1 cdev for all pins */ +static struct cdev scx200_gpio_cdev; /* use 1 cdev for all pins */ static int __init scx200_gpio_init(void) { diff --git a/drivers/char/selection.c b/drivers/char/selection.c index 71093a9fc46..74cff839c85 100644 --- a/drivers/char/selection.c +++ b/drivers/char/selection.c @@ -33,7 +33,7 @@ extern void poke_blanked_console(void); /* Variables for selection control. */ /* Use a dynamic buffer, instead of static (Dec 1994) */ -struct vc_data *sel_cons; /* must not be disallocated */ +struct vc_data *sel_cons; /* must not be deallocated */ static volatile int sel_start = -1; /* cleared by clear_selection */ static int sel_end; static int sel_buffer_lth; diff --git a/drivers/char/specialix.c b/drivers/char/specialix.c index a1d303f9a33..f52c7c31bad 100644 --- a/drivers/char/specialix.c +++ b/drivers/char/specialix.c @@ -182,7 +182,6 @@ static int sx_poll = HZ; #define RS_EVENT_WRITE_WAKEUP 0 static struct tty_driver *specialix_driver; -static unsigned char * tmp_buf; static unsigned long baud_table[] = { 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, @@ -1087,24 +1086,16 @@ static void sx_change_speed(struct specialix_board *bp, struct specialix_port *p port->MSVR = (sx_in(bp, CD186x_MSVR) & MSVR_RTS); spin_unlock_irqrestore(&bp->lock, flags); dprintk (SX_DEBUG_TERMIOS, "sx: got MSVR=%02x.\n", port->MSVR); - baud = C_BAUD(tty); + baud = tty_get_baud_rate(tty); - if (baud & CBAUDEX) { - baud &= ~CBAUDEX; - if (baud < 1 || baud > 2) - port->tty->termios->c_cflag &= ~CBAUDEX; - else - baud += 15; - } - if (baud == 15) { + if (baud == 38400) { if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) baud ++; if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) baud += 2; } - - if (!baud_table[baud]) { + if (!baud) { /* Drop DTR & exit */ dprintk (SX_DEBUG_TERMIOS, "Dropping DTR... Hmm....\n"); if (!SX_CRTSCTS (tty)) { @@ -1134,7 +1125,7 @@ static void sx_change_speed(struct specialix_board *bp, struct specialix_port *p "This is an untested option, please be carefull.\n", port_No (port), tmp); else - tmp = (((SX_OSCFREQ + baud_table[baud]/2) / baud_table[baud] + + tmp = (((SX_OSCFREQ + baud/2) / baud + CD186x_TPC/2) / CD186x_TPC); if ((tmp < 0x10) && time_before(again, jiffies)) { @@ -1682,7 +1673,7 @@ static int sx_write(struct tty_struct * tty, bp = port_Board(port); - if (!port->xmit_buf || !tmp_buf) { + if (!port->xmit_buf) { func_exit(); return 0; } @@ -2406,12 +2397,6 @@ static int sx_init_drivers(void) return 1; } - if (!(tmp_buf = (unsigned char *) get_zeroed_page(GFP_KERNEL))) { - printk(KERN_ERR "sx: Couldn't get free page.\n"); - put_tty_driver(specialix_driver); - func_exit(); - return 1; - } specialix_driver->owner = THIS_MODULE; specialix_driver->name = "ttyW"; specialix_driver->major = SPECIALIX_NORMAL_MAJOR; @@ -2425,7 +2410,6 @@ static int sx_init_drivers(void) if ((error = tty_register_driver(specialix_driver))) { put_tty_driver(specialix_driver); - free_page((unsigned long)tmp_buf); printk(KERN_ERR "sx: Couldn't register specialix IO8+ driver, error = %d\n", error); func_exit(); @@ -2451,7 +2435,6 @@ static void sx_release_drivers(void) { func_enter(); - free_page((unsigned long)tmp_buf); tty_unregister_driver(specialix_driver); put_tty_driver(specialix_driver); func_exit(); diff --git a/drivers/char/synclink_gt.c b/drivers/char/synclink_gt.c index 2f07b085536..78bc85180c8 100644 --- a/drivers/char/synclink_gt.c +++ b/drivers/char/synclink_gt.c @@ -1,5 +1,5 @@ /* - * $Id: synclink_gt.c,v 4.25 2006/02/06 21:20:33 paulkf Exp $ + * $Id: synclink_gt.c,v 4.36 2006/08/28 20:47:14 paulkf Exp $ * * Device driver for Microgate SyncLink GT serial adapters. * @@ -91,12 +91,12 @@ * module identification */ static char *driver_name = "SyncLink GT"; -static char *driver_version = "$Revision: 4.25 $"; +static char *driver_version = "$Revision: 4.36 $"; static char *tty_driver_name = "synclink_gt"; static char *tty_dev_prefix = "ttySLG"; MODULE_LICENSE("GPL"); #define MGSL_MAGIC 0x5401 -#define MAX_DEVICES 12 +#define MAX_DEVICES 32 static struct pci_device_id pci_table[] = { {PCI_VENDOR_ID_MICROGATE, SYNCLINK_GT_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,}, @@ -461,7 +461,7 @@ static int adapter_test(struct slgt_info *info); static void reset_adapter(struct slgt_info *info); static void reset_port(struct slgt_info *info); static void async_mode(struct slgt_info *info); -static void hdlc_mode(struct slgt_info *info); +static void sync_mode(struct slgt_info *info); static void rx_stop(struct slgt_info *info); static void rx_start(struct slgt_info *info); @@ -881,7 +881,9 @@ static int write(struct tty_struct *tty, if (!count) goto cleanup; - if (info->params.mode == MGSL_MODE_RAW) { + if (info->params.mode == MGSL_MODE_RAW || + info->params.mode == MGSL_MODE_MONOSYNC || + info->params.mode == MGSL_MODE_BISYNC) { unsigned int bufs_needed = (count/DMABUFSIZE); unsigned int bufs_free = free_tbuf_count(info); if (count % DMABUFSIZE) @@ -1897,6 +1899,8 @@ static void bh_handler(void* context) while(rx_get_frame(info)); break; case MGSL_MODE_RAW: + case MGSL_MODE_MONOSYNC: + case MGSL_MODE_BISYNC: while(rx_get_buf(info)); break; } @@ -2362,10 +2366,9 @@ static void program_hw(struct slgt_info *info) rx_stop(info); tx_stop(info); - if (info->params.mode == MGSL_MODE_HDLC || - info->params.mode == MGSL_MODE_RAW || + if (info->params.mode != MGSL_MODE_ASYNC || info->netcount) - hdlc_mode(info); + sync_mode(info); else async_mode(info); @@ -2564,6 +2567,10 @@ static int rx_enable(struct slgt_info *info, int enable) if (enable) { if (!info->rx_enabled) rx_start(info); + else if (enable == 2) { + /* force hunt mode (write 1 to RCR[3]) */ + wr_reg16(info, RCR, rd_reg16(info, RCR) | BIT3); + } } else { if (info->rx_enabled) rx_stop(info); @@ -3748,7 +3755,7 @@ static void tx_start(struct slgt_info *info) { if (!info->tx_enabled) { wr_reg16(info, TCR, - (unsigned short)(rd_reg16(info, TCR) | BIT1)); + (unsigned short)((rd_reg16(info, TCR) | BIT1) & ~BIT2)); info->tx_enabled = TRUE; } @@ -3775,13 +3782,18 @@ static void tx_start(struct slgt_info *info) tdma_reset(info); /* set 1st descriptor address */ wr_reg32(info, TDDAR, info->tbufs[info->tbuf_start].pdesc); - if (info->params.mode == MGSL_MODE_RAW) + switch(info->params.mode) { + case MGSL_MODE_RAW: + case MGSL_MODE_MONOSYNC: + case MGSL_MODE_BISYNC: wr_reg32(info, TDCSR, BIT2 + BIT0); /* IRQ + DMA enable */ - else + break; + default: wr_reg32(info, TDCSR, BIT0); /* DMA enable */ + } } - if (info->params.mode != MGSL_MODE_RAW) { + if (info->params.mode == MGSL_MODE_HDLC) { info->tx_timer.expires = jiffies + msecs_to_jiffies(5000); add_timer(&info->tx_timer); } @@ -3814,7 +3826,6 @@ static void tx_stop(struct slgt_info *info) /* reset and disable transmitter */ val = rd_reg16(info, TCR) & ~BIT1; /* clear enable bit */ wr_reg16(info, TCR, (unsigned short)(val | BIT2)); /* set reset bit */ - wr_reg16(info, TCR, val); /* clear reset */ slgt_irq_off(info, IRQ_TXDATA + IRQ_TXIDLE + IRQ_TXUNDER); @@ -3982,7 +3993,7 @@ static void async_mode(struct slgt_info *info) enable_loopback(info); } -static void hdlc_mode(struct slgt_info *info) +static void sync_mode(struct slgt_info *info) { unsigned short val; @@ -3992,7 +4003,7 @@ static void hdlc_mode(struct slgt_info *info) /* TCR (tx control) * - * 15..13 mode, 000=HDLC 001=raw sync + * 15..13 mode, 000=HDLC 001=raw 010=async 011=monosync 100=bisync * 12..10 encoding * 09 CRC enable * 08 CRC32 @@ -4006,8 +4017,11 @@ static void hdlc_mode(struct slgt_info *info) */ val = 0; - if (info->params.mode == MGSL_MODE_RAW) - val |= BIT13; + switch(info->params.mode) { + case MGSL_MODE_MONOSYNC: val |= BIT14 + BIT13; break; + case MGSL_MODE_BISYNC: val |= BIT15; break; + case MGSL_MODE_RAW: val |= BIT13; break; + } if (info->if_mode & MGSL_INTERFACE_RTS_EN) val |= BIT7; @@ -4058,7 +4072,7 @@ static void hdlc_mode(struct slgt_info *info) /* RCR (rx control) * - * 15..13 mode, 000=HDLC 001=raw sync + * 15..13 mode, 000=HDLC 001=raw 010=async 011=monosync 100=bisync * 12..10 encoding * 09 CRC enable * 08 CRC32 @@ -4069,8 +4083,11 @@ static void hdlc_mode(struct slgt_info *info) */ val = 0; - if (info->params.mode == MGSL_MODE_RAW) - val |= BIT13; + switch(info->params.mode) { + case MGSL_MODE_MONOSYNC: val |= BIT14 + BIT13; break; + case MGSL_MODE_BISYNC: val |= BIT15; break; + case MGSL_MODE_RAW: val |= BIT13; break; + } switch(info->params.encoding) { @@ -4309,10 +4326,15 @@ static void free_rbufs(struct slgt_info *info, unsigned int i, unsigned int last while(!done) { /* reset current buffer for reuse */ info->rbufs[i].status = 0; - if (info->params.mode == MGSL_MODE_RAW) + switch(info->params.mode) { + case MGSL_MODE_RAW: + case MGSL_MODE_MONOSYNC: + case MGSL_MODE_BISYNC: set_desc_count(info->rbufs[i], info->raw_rx_size); - else + break; + default: set_desc_count(info->rbufs[i], DMABUFSIZE); + } if (i == last) done = 1; @@ -4477,13 +4499,24 @@ cleanup: static int rx_get_buf(struct slgt_info *info) { unsigned int i = info->rbuf_current; + unsigned int count; if (!desc_complete(info->rbufs[i])) return 0; - DBGDATA(info, info->rbufs[i].buf, desc_count(info->rbufs[i]), "rx"); - DBGINFO(("rx_get_buf size=%d\n", desc_count(info->rbufs[i]))); - ldisc_receive_buf(info->tty, info->rbufs[i].buf, - info->flag_buf, desc_count(info->rbufs[i])); + count = desc_count(info->rbufs[i]); + switch(info->params.mode) { + case MGSL_MODE_MONOSYNC: + case MGSL_MODE_BISYNC: + /* ignore residue in byte synchronous modes */ + if (desc_residue(info->rbufs[i])) + count--; + break; + } + DBGDATA(info, info->rbufs[i].buf, count, "rx"); + DBGINFO(("rx_get_buf size=%d\n", count)); + if (count) + ldisc_receive_buf(info->tty, info->rbufs[i].buf, + info->flag_buf, count); free_rbufs(info, i, i); return 1; } @@ -4549,8 +4582,13 @@ static void tx_load(struct slgt_info *info, const char *buf, unsigned int size) size -= count; buf += count; - if (!size && info->params.mode != MGSL_MODE_RAW) - set_desc_eof(*d, 1); /* HDLC: set EOF of last desc */ + /* + * set EOF bit for last buffer of HDLC frame or + * for every buffer in raw mode + */ + if ((!size && info->params.mode == MGSL_MODE_HDLC) || + info->params.mode == MGSL_MODE_RAW) + set_desc_eof(*d, 1); else set_desc_eof(*d, 0); diff --git a/drivers/char/sysrq.c b/drivers/char/sysrq.c index ee3ca8f1768..6b4d4d1e343 100644 --- a/drivers/char/sysrq.c +++ b/drivers/char/sysrq.c @@ -113,6 +113,7 @@ static struct sysrq_key_op sysrq_crashdump_op = { static void sysrq_handle_reboot(int key, struct pt_regs *pt_regs, struct tty_struct *tty) { + lockdep_off(); local_irq_enable(); emergency_restart(); } @@ -208,7 +209,7 @@ static void send_sig_all(int sig) struct task_struct *p; for_each_process(p) { - if (p->mm && p->pid != 1) + if (p->mm && !is_init(p)) /* Not swapper, init nor kernel thread */ force_sig(sig, p); } diff --git a/drivers/char/tpm/tpm_atmel.h b/drivers/char/tpm/tpm_atmel.h index 2e68eeb8a2c..aefd683c60b 100644 --- a/drivers/char/tpm/tpm_atmel.h +++ b/drivers/char/tpm/tpm_atmel.h @@ -37,7 +37,7 @@ static void __iomem * atmel_get_base_addr(unsigned long *base, int *region_size) { struct device_node *dn; unsigned long address, size; - unsigned int *reg; + const unsigned int *reg; int reglen; int naddrc; int nsizec; @@ -52,7 +52,7 @@ static void __iomem * atmel_get_base_addr(unsigned long *base, int *region_size) return NULL; } - reg = (unsigned int *) get_property(dn, "reg", ®len); + reg = get_property(dn, "reg", ®len); naddrc = prom_n_addr_cells(dn); nsizec = prom_n_size_cells(dn); diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index bb0d9199e99..333741770f1 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -129,6 +129,7 @@ LIST_HEAD(tty_drivers); /* linked list of tty drivers */ /* Semaphore to protect creating and releasing a tty. This is shared with vt.c for deeply disgusting hack reasons */ DEFINE_MUTEX(tty_mutex); +EXPORT_SYMBOL(tty_mutex); #ifdef CONFIG_UNIX98_PTYS extern struct tty_driver *ptm_driver; /* Unix98 pty masters; for /dev/ptmx */ @@ -160,17 +161,11 @@ static void release_mem(struct tty_struct *tty, int idx); * been initialized in any way but has been zeroed * * Locking: none - * FIXME: use kzalloc */ static struct tty_struct *alloc_tty_struct(void) { - struct tty_struct *tty; - - tty = kmalloc(sizeof(struct tty_struct), GFP_KERNEL); - if (tty) - memset(tty, 0, sizeof(struct tty_struct)); - return tty; + return kzalloc(sizeof(struct tty_struct), GFP_KERNEL); } static void tty_buffer_free_all(struct tty_struct *); @@ -483,10 +478,9 @@ int tty_insert_flip_string(struct tty_struct *tty, const unsigned char *chars, tb->used += space; copied += space; chars += space; - } - /* There is a small chance that we need to split the data over - several buffers. If this is the case we must loop */ - while (unlikely(size > copied)); + /* There is a small chance that we need to split the data over + several buffers. If this is the case we must loop */ + } while (unlikely(size > copied)); return copied; } EXPORT_SYMBOL(tty_insert_flip_string); @@ -521,10 +515,9 @@ int tty_insert_flip_string_flags(struct tty_struct *tty, copied += space; chars += space; flags += space; - } - /* There is a small chance that we need to split the data over - several buffers. If this is the case we must loop */ - while (unlikely(size > copied)); + /* There is a small chance that we need to split the data over + several buffers. If this is the case we must loop */ + } while (unlikely(size > copied)); return copied; } EXPORT_SYMBOL(tty_insert_flip_string_flags); @@ -626,9 +619,9 @@ EXPORT_SYMBOL_GPL(tty_prepare_flip_string_flags); static void tty_set_termios_ldisc(struct tty_struct *tty, int num) { - down(&tty->termios_sem); + mutex_lock(&tty->termios_mutex); tty->termios->c_line = num; - up(&tty->termios_sem); + mutex_unlock(&tty->termios_mutex); } /* @@ -1346,9 +1339,9 @@ static void do_tty_hangup(void *data) */ if (tty->driver->flags & TTY_DRIVER_RESET_TERMIOS) { - down(&tty->termios_sem); + mutex_lock(&tty->termios_mutex); *tty->termios = tty->driver->init_termios; - up(&tty->termios_sem); + mutex_unlock(&tty->termios_mutex); } /* Defer ldisc switch */ @@ -2072,8 +2065,9 @@ fail_no_mem: /* call the tty release_mem routine to clean out this slot */ release_mem_out: - printk(KERN_INFO "init_dev: ldisc open failed, " - "clearing slot %d\n", idx); + if (printk_ratelimit()) + printk(KERN_INFO "init_dev: ldisc open failed, " + "clearing slot %d\n", idx); release_mem(tty, idx); goto end_init; } @@ -2726,6 +2720,8 @@ static int tty_fasync(int fd, struct file * filp, int on) * Locking: * Called functions take tty_ldisc_lock * current->signal->tty check is safe without locks + * + * FIXME: may race normal receive processing */ static int tiocsti(struct tty_struct *tty, char __user *p) @@ -2748,18 +2744,21 @@ static int tiocsti(struct tty_struct *tty, char __user *p) * @tty; tty * @arg: user buffer for result * - * Copies the kernel idea of the window size into the user buffer. No - * locking is done. + * Copies the kernel idea of the window size into the user buffer. * - * FIXME: Returning random values racing a window size set is wrong - * should lock here against that + * Locking: tty->termios_sem is taken to ensure the winsize data + * is consistent. */ static int tiocgwinsz(struct tty_struct *tty, struct winsize __user * arg) { - if (copy_to_user(arg, &tty->winsize, sizeof(*arg))) - return -EFAULT; - return 0; + int err; + + mutex_lock(&tty->termios_mutex); + err = copy_to_user(arg, &tty->winsize, sizeof(*arg)); + mutex_unlock(&tty->termios_mutex); + + return err ? -EFAULT: 0; } /** @@ -2772,12 +2771,11 @@ static int tiocgwinsz(struct tty_struct *tty, struct winsize __user * arg) * actually has driver level meaning and triggers a VC resize. * * Locking: - * The console_sem is used to ensure we do not try and resize - * the console twice at once. - * FIXME: Two racing size sets may leave the console and kernel - * parameters disagreeing. Is this exploitable ? - * FIXME: Random values racing a window size get is wrong - * should lock here against that + * Called function use the console_sem is used to ensure we do + * not try and resize the console twice at once. + * The tty->termios_sem is used to ensure we don't double + * resize and get confused. Lock order - tty->termios.sem before + * console sem */ static int tiocswinsz(struct tty_struct *tty, struct tty_struct *real_tty, @@ -2787,17 +2785,18 @@ static int tiocswinsz(struct tty_struct *tty, struct tty_struct *real_tty, if (copy_from_user(&tmp_ws, arg, sizeof(*arg))) return -EFAULT; + + mutex_lock(&tty->termios_mutex); if (!memcmp(&tmp_ws, &tty->winsize, sizeof(*arg))) - return 0; + goto done; + #ifdef CONFIG_VT if (tty->driver->type == TTY_DRIVER_TYPE_CONSOLE) { - int rc; - - acquire_console_sem(); - rc = vc_resize(tty->driver_data, tmp_ws.ws_col, tmp_ws.ws_row); - release_console_sem(); - if (rc) - return -ENXIO; + if (vc_lock_resize(tty->driver_data, tmp_ws.ws_col, + tmp_ws.ws_row)) { + mutex_unlock(&tty->termios_mutex); + return -ENXIO; + } } #endif if (tty->pgrp > 0) @@ -2806,6 +2805,8 @@ static int tiocswinsz(struct tty_struct *tty, struct tty_struct *real_tty, kill_pg(real_tty->pgrp, SIGWINCH, 1); tty->winsize = tmp_ws; real_tty->winsize = tmp_ws; +done: + mutex_unlock(&tty->termios_mutex); return 0; } @@ -2880,9 +2881,7 @@ static int fionbio(struct file *file, int __user *p) * Locking: * Takes tasklist lock internally to walk sessions * Takes task_lock() when updating signal->tty - * - * FIXME: tty_mutex is needed to protect signal->tty references. - * FIXME: why task_lock on the signal->tty reference ?? + * Takes tty_mutex() to protect tty instance * */ @@ -2917,9 +2916,11 @@ static int tiocsctty(struct tty_struct *tty, int arg) } else return -EPERM; } + mutex_lock(&tty_mutex); task_lock(current); current->signal->tty = tty; task_unlock(current); + mutex_unlock(&tty_mutex); current->signal->tty_old_pgrp = 0; tty->session = current->signal->session; tty->pgrp = process_group(current); @@ -2959,8 +2960,6 @@ static int tiocgpgrp(struct tty_struct *tty, struct tty_struct *real_tty, pid_t * permitted where the tty session is our session. * * Locking: None - * - * FIXME: current->signal->tty referencing is unsafe. */ static int tiocspgrp(struct tty_struct *tty, struct tty_struct *real_tty, pid_t __user *p) @@ -3039,19 +3038,20 @@ static int tiocsetd(struct tty_struct *tty, int __user *p) * timed break functionality. * * Locking: - * None + * atomic_write_lock serializes * - * FIXME: - * What if two overlap */ static int send_break(struct tty_struct *tty, unsigned int duration) { + if (mutex_lock_interruptible(&tty->atomic_write_lock)) + return -EINTR; tty->driver->break_ctl(tty, -1); if (!signal_pending(current)) { msleep_interruptible(duration); } tty->driver->break_ctl(tty, 0); + mutex_unlock(&tty->atomic_write_lock); if (signal_pending(current)) return -EINTR; return 0; @@ -3144,6 +3144,8 @@ int tty_ioctl(struct inode * inode, struct file * file, if (tty_paranoia_check(tty, inode, "tty_ioctl")) return -EINVAL; + /* CHECKME: is this safe as one end closes ? */ + real_tty = tty; if (tty->driver->type == TTY_DRIVER_TYPE_PTY && tty->driver->subtype == PTY_TYPE_MASTER) @@ -3580,7 +3582,7 @@ static void initialize_tty_struct(struct tty_struct *tty) tty_buffer_init(tty); INIT_WORK(&tty->buf.work, flush_to_ldisc, tty); init_MUTEX(&tty->buf.pty_sem); - init_MUTEX(&tty->termios_sem); + mutex_init(&tty->termios_mutex); init_waitqueue_head(&tty->write_wait); init_waitqueue_head(&tty->read_wait); INIT_WORK(&tty->hangup_work, do_tty_hangup, tty); diff --git a/drivers/char/tty_ioctl.c b/drivers/char/tty_ioctl.c index 4ad47d321bd..3b6fa7b0be8 100644 --- a/drivers/char/tty_ioctl.c +++ b/drivers/char/tty_ioctl.c @@ -20,6 +20,7 @@ #include <linux/mm.h> #include <linux/module.h> #include <linux/bitops.h> +#include <linux/mutex.h> #include <asm/io.h> #include <asm/uaccess.h> @@ -131,7 +132,7 @@ static void change_termios(struct tty_struct * tty, struct termios * new_termios /* FIXME: we need to decide on some locking/ordering semantics for the set_termios notification eventually */ - down(&tty->termios_sem); + mutex_lock(&tty->termios_mutex); *tty->termios = *new_termios; unset_locked_termios(tty->termios, &old_termios, tty->termios_locked); @@ -176,7 +177,7 @@ static void change_termios(struct tty_struct * tty, struct termios * new_termios (ld->set_termios)(tty, &old_termios); tty_ldisc_deref(ld); } - up(&tty->termios_sem); + mutex_unlock(&tty->termios_mutex); } /** @@ -284,13 +285,13 @@ static int get_sgttyb(struct tty_struct * tty, struct sgttyb __user * sgttyb) { struct sgttyb tmp; - down(&tty->termios_sem); + mutex_lock(&tty->termios_mutex); tmp.sg_ispeed = 0; tmp.sg_ospeed = 0; tmp.sg_erase = tty->termios->c_cc[VERASE]; tmp.sg_kill = tty->termios->c_cc[VKILL]; tmp.sg_flags = get_sgflags(tty); - up(&tty->termios_sem); + mutex_unlock(&tty->termios_mutex); return copy_to_user(sgttyb, &tmp, sizeof(tmp)) ? -EFAULT : 0; } @@ -345,12 +346,12 @@ static int set_sgttyb(struct tty_struct * tty, struct sgttyb __user * sgttyb) if (copy_from_user(&tmp, sgttyb, sizeof(tmp))) return -EFAULT; - down(&tty->termios_sem); + mutex_lock(&tty->termios_mutex); termios = *tty->termios; termios.c_cc[VERASE] = tmp.sg_erase; termios.c_cc[VKILL] = tmp.sg_kill; set_sgflags(&termios, tmp.sg_flags); - up(&tty->termios_sem); + mutex_unlock(&tty->termios_mutex); change_termios(tty, &termios); return 0; } @@ -422,24 +423,28 @@ static int set_ltchars(struct tty_struct * tty, struct ltchars __user * ltchars) * * Send a high priority character to the tty even if stopped * - * Locking: none - * - * FIXME: overlapping calls with start/stop tty lose state of tty + * Locking: none for xchar method, write ordering for write method. */ -static void send_prio_char(struct tty_struct *tty, char ch) +static int send_prio_char(struct tty_struct *tty, char ch) { int was_stopped = tty->stopped; if (tty->driver->send_xchar) { tty->driver->send_xchar(tty, ch); - return; + return 0; } + + if (mutex_lock_interruptible(&tty->atomic_write_lock)) + return -ERESTARTSYS; + if (was_stopped) start_tty(tty); tty->driver->write(tty, &ch, 1); if (was_stopped) stop_tty(tty); + mutex_unlock(&tty->atomic_write_lock); + return 0; } int n_tty_ioctl(struct tty_struct * tty, struct file * file, @@ -513,11 +518,11 @@ int n_tty_ioctl(struct tty_struct * tty, struct file * file, break; case TCIOFF: if (STOP_CHAR(tty) != __DISABLED_CHAR) - send_prio_char(tty, STOP_CHAR(tty)); + return send_prio_char(tty, STOP_CHAR(tty)); break; case TCION: if (START_CHAR(tty) != __DISABLED_CHAR) - send_prio_char(tty, START_CHAR(tty)); + return send_prio_char(tty, START_CHAR(tty)); break; default: return -EINVAL; @@ -592,11 +597,11 @@ int n_tty_ioctl(struct tty_struct * tty, struct file * file, case TIOCSSOFTCAR: if (get_user(arg, (unsigned int __user *) arg)) return -EFAULT; - down(&tty->termios_sem); + mutex_lock(&tty->termios_mutex); tty->termios->c_cflag = ((tty->termios->c_cflag & ~CLOCAL) | (arg ? CLOCAL : 0)); - up(&tty->termios_sem); + mutex_unlock(&tty->termios_mutex); return 0; default: return -ENOIOCTLCMD; diff --git a/drivers/char/vc_screen.c b/drivers/char/vc_screen.c index a9247b5213d..bd7a98c6ea7 100644 --- a/drivers/char/vc_screen.c +++ b/drivers/char/vc_screen.c @@ -474,14 +474,15 @@ static const struct file_operations vcs_fops = { static struct class *vc_class; -void vcs_make_devfs(struct tty_struct *tty) +void vcs_make_sysfs(struct tty_struct *tty) { class_device_create(vc_class, NULL, MKDEV(VCS_MAJOR, tty->index + 1), NULL, "vcs%u", tty->index + 1); class_device_create(vc_class, NULL, MKDEV(VCS_MAJOR, tty->index + 129), NULL, "vcsa%u", tty->index + 1); } -void vcs_remove_devfs(struct tty_struct *tty) + +void vcs_remove_sysfs(struct tty_struct *tty) { class_device_destroy(vc_class, MKDEV(VCS_MAJOR, tty->index + 1)); class_device_destroy(vc_class, MKDEV(VCS_MAJOR, tty->index + 129)); diff --git a/drivers/char/viocons.c b/drivers/char/viocons.c index 766f7864c6c..f3efeaf2826 100644 --- a/drivers/char/viocons.c +++ b/drivers/char/viocons.c @@ -43,7 +43,6 @@ #include <linux/sysrq.h> #include <asm/iseries/vio.h> - #include <asm/iseries/hv_lp_event.h> #include <asm/iseries/hv_call_event.h> #include <asm/iseries/hv_lp_config.h> @@ -67,35 +66,6 @@ static int vio_sysrq_pressed; extern int sysrq_enabled; #endif -/* - * The structure of the events that flow between us and OS/400. You can't - * mess with this unless the OS/400 side changes too - */ -struct viocharlpevent { - struct HvLpEvent event; - u32 reserved; - u16 version; - u16 subtype_result_code; - u8 virtual_device; - u8 len; - u8 data[VIOCHAR_MAX_DATA]; -}; - -#define VIOCHAR_WINDOW 10 -#define VIOCHAR_HIGHWATERMARK 3 - -enum viocharsubtype { - viocharopen = 0x0001, - viocharclose = 0x0002, - viochardata = 0x0003, - viocharack = 0x0004, - viocharconfig = 0x0005 -}; - -enum viochar_rc { - viochar_rc_ebusy = 1 -}; - #define VIOCHAR_NUM_BUF 16 /* @@ -1183,6 +1153,7 @@ static int __init viocons_init(void) port_info[i].magic = VIOTTY_MAGIC; } HvCall_setLogBufferFormatAndCodepage(HvCall_LogBuffer_ASCII, 437); + add_preferred_console("viocons", 0, NULL); register_console(&viocons_early); return 0; } diff --git a/drivers/char/viotape.c b/drivers/char/viotape.c index b72b2049aaa..73c78bf75d7 100644 --- a/drivers/char/viotape.c +++ b/drivers/char/viotape.c @@ -940,7 +940,6 @@ static void vioHandleTapeEvent(struct HvLpEvent *event) static int viotape_probe(struct vio_dev *vdev, const struct vio_device_id *id) { - char tapename[32]; int i = vdev->unit_address; int j; @@ -956,10 +955,9 @@ static int viotape_probe(struct vio_dev *vdev, const struct vio_device_id *id) "iseries!vt%d", i); class_device_create(tape_class, NULL, MKDEV(VIOTAPE_MAJOR, i | 0x80), NULL, "iseries!nvt%d", i); - sprintf(tapename, "iseries/vt%d", i); - printk(VIOTAPE_KERN_INFO "tape %s is iSeries " + printk(VIOTAPE_KERN_INFO "tape iseries/vt%d is iSeries " "resource %10.10s type %4.4s, model %3.3s\n", - tapename, viotape_unitinfo[i].rsrcname, + i, viotape_unitinfo[i].rsrcname, viotape_unitinfo[i].type, viotape_unitinfo[i].model); return 0; } diff --git a/drivers/char/vt.c b/drivers/char/vt.c index da7e66a2a38..fb75da940b5 100644 --- a/drivers/char/vt.c +++ b/drivers/char/vt.c @@ -63,6 +63,13 @@ * * Removed console_lock, enabled interrupts across all console operations * 13 March 2001, Andrew Morton + * + * Fixed UTF-8 mode so alternate charset modes always work according + * to control sequences interpreted in do_con_trol function + * preserving backward VT100 semigraphics compatibility, + * malformed UTF sequences represented as sequences of replacement glyphs, + * original codes or '?' as a last resort if replacement glyph is undefined + * by Adam Tla/lka <atlka@pg.gda.pl>, Aug 2006 */ #include <linux/module.h> @@ -128,8 +135,8 @@ const struct consw *conswitchp; #define DEFAULT_BELL_PITCH 750 #define DEFAULT_BELL_DURATION (HZ/8) -extern void vcs_make_devfs(struct tty_struct *tty); -extern void vcs_remove_devfs(struct tty_struct *tty); +extern void vcs_make_sysfs(struct tty_struct *tty); +extern void vcs_remove_sysfs(struct tty_struct *tty); extern void console_map_init(void); #ifdef CONFIG_PROM_CONSOLE @@ -730,7 +737,8 @@ int vc_allocate(unsigned int currcons) /* return 0 on success */ visual_init(vc, currcons, 1); if (!*vc->vc_uni_pagedir_loc) con_set_default_unimap(vc); - vc->vc_screenbuf = kmalloc(vc->vc_screenbuf_size, GFP_KERNEL); + if (!vc->vc_kmalloced) + vc->vc_screenbuf = kmalloc(vc->vc_screenbuf_size, GFP_KERNEL); if (!vc->vc_screenbuf) { kfree(vc); vc_cons[currcons].d = NULL; @@ -878,8 +886,17 @@ int vc_resize(struct vc_data *vc, unsigned int cols, unsigned int lines) return err; } +int vc_lock_resize(struct vc_data *vc, unsigned int cols, unsigned int lines) +{ + int rc; + + acquire_console_sem(); + rc = vc_resize(vc, cols, lines); + release_console_sem(); + return rc; +} -void vc_disallocate(unsigned int currcons) +void vc_deallocate(unsigned int currcons) { WARN_CONSOLE_UNLOCKED(); @@ -2005,17 +2022,23 @@ static int do_con_write(struct tty_struct *tty, const unsigned char *buf, int co /* Do no translation at all in control states */ if (vc->vc_state != ESnormal) { tc = c; - } else if (vc->vc_utf) { + } else if (vc->vc_utf && !vc->vc_disp_ctrl) { /* Combine UTF-8 into Unicode */ - /* Incomplete characters silently ignored */ + /* Malformed sequences as sequences of replacement glyphs */ +rescan_last_byte: if(c > 0x7f) { - if (vc->vc_utf_count > 0 && (c & 0xc0) == 0x80) { - vc->vc_utf_char = (vc->vc_utf_char << 6) | (c & 0x3f); - vc->vc_utf_count--; - if (vc->vc_utf_count == 0) - tc = c = vc->vc_utf_char; - else continue; + if (vc->vc_utf_count) { + if ((c & 0xc0) == 0x80) { + vc->vc_utf_char = (vc->vc_utf_char << 6) | (c & 0x3f); + if (--vc->vc_utf_count) { + vc->vc_npar++; + continue; + } + tc = c = vc->vc_utf_char; + } else + goto replacement_glyph; } else { + vc->vc_npar = 0; if ((c & 0xe0) == 0xc0) { vc->vc_utf_count = 1; vc->vc_utf_char = (c & 0x1f); @@ -2032,14 +2055,15 @@ static int do_con_write(struct tty_struct *tty, const unsigned char *buf, int co vc->vc_utf_count = 5; vc->vc_utf_char = (c & 0x01); } else - vc->vc_utf_count = 0; + goto replacement_glyph; continue; } } else { + if (vc->vc_utf_count) + goto replacement_glyph; tc = c; - vc->vc_utf_count = 0; } - } else { /* no utf */ + } else { /* no utf or alternate charset mode */ tc = vc->vc_translate[vc->vc_toggle_meta ? (c | 0x80) : c]; } @@ -2054,31 +2078,33 @@ static int do_con_write(struct tty_struct *tty, const unsigned char *buf, int co * direct-to-font zone in UTF-8 mode. */ ok = tc && (c >= 32 || - (!vc->vc_utf && !(((vc->vc_disp_ctrl ? CTRL_ALWAYS - : CTRL_ACTION) >> c) & 1))) + !(vc->vc_disp_ctrl ? (CTRL_ALWAYS >> c) & 1 : + vc->vc_utf || ((CTRL_ACTION >> c) & 1))) && (c != 127 || vc->vc_disp_ctrl) && (c != 128+27); if (vc->vc_state == ESnormal && ok) { /* Now try to find out how to display it */ tc = conv_uni_to_pc(vc, tc); - if ( tc == -4 ) { + if (tc & ~charmask) { + if ( tc == -4 ) { /* If we got -4 (not found) then see if we have defined a replacement character (U+FFFD) */ - tc = conv_uni_to_pc(vc, 0xfffd); - - /* One reason for the -4 can be that we just - did a clear_unimap(); - try at least to show something. */ - if (tc == -4) - tc = c; - } else if ( tc == -3 ) { - /* Bad hash table -- hope for the best */ - tc = c; - } - if (tc & ~charmask) - continue; /* Conversion failed */ +replacement_glyph: + tc = conv_uni_to_pc(vc, 0xfffd); + if (!(tc & ~charmask)) + goto display_glyph; + } else if ( tc != -3 ) + continue; /* nothing to display */ + /* no hash table or no replacement -- + * hope for the best */ + if ( c & ~charmask ) + tc = '?'; + else + tc = c; + } +display_glyph: if (vc->vc_need_wrap || vc->vc_decim) FLUSH if (vc->vc_need_wrap) { @@ -2102,6 +2128,15 @@ static int do_con_write(struct tty_struct *tty, const unsigned char *buf, int co vc->vc_x++; draw_to = (vc->vc_pos += 2); } + if (vc->vc_utf_count) { + if (vc->vc_npar) { + vc->vc_npar--; + goto display_glyph; + } + vc->vc_utf_count = 0; + c = orig; + goto rescan_last_byte; + } continue; } FLUSH @@ -2498,7 +2533,7 @@ static int con_open(struct tty_struct *tty, struct file *filp) tty->winsize.ws_col = vc_cons[currcons].d->vc_cols; } release_console_sem(); - vcs_make_devfs(tty); + vcs_make_sysfs(tty); return ret; } } @@ -2511,7 +2546,7 @@ static int con_open(struct tty_struct *tty, struct file *filp) * and taking a ref against the tty while we're in the process of forgetting * about it and cleaning things up. * - * This is because vcs_remove_devfs() can sleep and will drop the BKL. + * This is because vcs_remove_sysfs() can sleep and will drop the BKL. */ static void con_close(struct tty_struct *tty, struct file *filp) { @@ -2524,7 +2559,7 @@ static void con_close(struct tty_struct *tty, struct file *filp) vc->vc_tty = NULL; tty->driver_data = NULL; release_console_sem(); - vcs_remove_devfs(tty); + vcs_remove_sysfs(tty); mutex_unlock(&tty_mutex); /* * tty_mutex is released, but we still hold BKL, so there is @@ -3765,6 +3800,7 @@ EXPORT_SYMBOL(default_blu); EXPORT_SYMBOL(update_region); EXPORT_SYMBOL(redraw_screen); EXPORT_SYMBOL(vc_resize); +EXPORT_SYMBOL(vc_lock_resize); EXPORT_SYMBOL(fg_console); EXPORT_SYMBOL(console_blank_hook); EXPORT_SYMBOL(console_blanked); diff --git a/drivers/char/vt_ioctl.c b/drivers/char/vt_ioctl.c index a5628a8b662..a53e382cc10 100644 --- a/drivers/char/vt_ioctl.c +++ b/drivers/char/vt_ioctl.c @@ -96,7 +96,7 @@ do_kdsk_ioctl(int cmd, struct kbentry __user *user_kbe, int perm, struct kbd_str if (!perm) return -EPERM; if (!i && v == K_NOSUCHMAP) { - /* disallocate map */ + /* deallocate map */ key_map = key_maps[s]; if (s && key_map) { key_maps[s] = NULL; @@ -819,20 +819,20 @@ int vt_ioctl(struct tty_struct *tty, struct file * file, if (arg > MAX_NR_CONSOLES) return -ENXIO; if (arg == 0) { - /* disallocate all unused consoles, but leave 0 */ + /* deallocate all unused consoles, but leave 0 */ acquire_console_sem(); for (i=1; i<MAX_NR_CONSOLES; i++) if (! VT_BUSY(i)) - vc_disallocate(i); + vc_deallocate(i); release_console_sem(); } else { - /* disallocate a single console, if possible */ + /* deallocate a single console, if possible */ arg--; if (VT_BUSY(arg)) return -EBUSY; if (arg) { /* leave 0 */ acquire_console_sem(); - vc_disallocate(arg); + vc_deallocate(arg); release_console_sem(); } } @@ -847,11 +847,8 @@ int vt_ioctl(struct tty_struct *tty, struct file * file, if (get_user(ll, &vtsizes->v_rows) || get_user(cc, &vtsizes->v_cols)) return -EFAULT; - for (i = 0; i < MAX_NR_CONSOLES; i++) { - acquire_console_sem(); - vc_resize(vc_cons[i].d, cc, ll); - release_console_sem(); - } + for (i = 0; i < MAX_NR_CONSOLES; i++) + vc_lock_resize(vc_cons[i].d, cc, ll); return 0; } diff --git a/drivers/char/watchdog/Kconfig b/drivers/char/watchdog/Kconfig index fff89c2d88f..77ab7e020da 100644 --- a/drivers/char/watchdog/Kconfig +++ b/drivers/char/watchdog/Kconfig @@ -165,6 +165,13 @@ config EP93XX_WATCHDOG To compile this driver as a module, choose M here: the module will be called ep93xx_wdt. +config OMAP_WATCHDOG + tristate "OMAP Watchdog" + depends on WATCHDOG && (ARCH_OMAP16XX || ARCH_OMAP24XX) + help + Support for TI OMAP1610/OMAP1710/OMAP2420 watchdog. Say 'Y' here to + enable the OMAP1610/OMAP1710 watchdog timer. + # X86 (i386 + ia64 + x86_64) Architecture config ACQUIRE_WDT @@ -510,6 +517,14 @@ config SH_WDT To compile this driver as a module, choose M here: the module will be called shwdt. +config SH_WDT_MMAP + bool "Allow mmap of SH WDT" + default n + depends on SH_WDT + help + If you say Y here, user applications will be able to mmap the + WDT/CPG registers. +# # SPARC64 Architecture config WATCHDOG_CP1XXX diff --git a/drivers/char/watchdog/Makefile b/drivers/char/watchdog/Makefile index 6ab77b61a64..5099f8be8cc 100644 --- a/drivers/char/watchdog/Makefile +++ b/drivers/char/watchdog/Makefile @@ -24,6 +24,7 @@ obj-$(CONFIG_USBPCWATCHDOG) += pcwd_usb.o # ARM Architecture obj-$(CONFIG_AT91_WATCHDOG) += at91_wdt.o +obj-$(CONFIG_OMAP_WATCHDOG) += omap_wdt.o obj-$(CONFIG_21285_WATCHDOG) += wdt285.o obj-$(CONFIG_977_WATCHDOG) += wdt977.o obj-$(CONFIG_IXP2000_WATCHDOG) += ixp2000_wdt.o diff --git a/drivers/char/watchdog/omap_wdt.c b/drivers/char/watchdog/omap_wdt.c new file mode 100644 index 00000000000..8f90b90a502 --- /dev/null +++ b/drivers/char/watchdog/omap_wdt.c @@ -0,0 +1,391 @@ +/* + * linux/drivers/char/watchdog/omap_wdt.c + * + * Watchdog driver for the TI OMAP 16xx & 24xx 32KHz (non-secure) watchdog + * + * Author: MontaVista Software, Inc. + * <gdavis@mvista.com> or <source@mvista.com> + * + * 2003 (c) MontaVista Software, Inc. This file is licensed under the + * terms of the GNU General Public License version 2. This program is + * licensed "as is" without any warranty of any kind, whether express + * or implied. + * + * History: + * + * 20030527: George G. Davis <gdavis@mvista.com> + * Initially based on linux-2.4.19-rmk7-pxa1/drivers/char/sa1100_wdt.c + * (c) Copyright 2000 Oleg Drokin <green@crimea.edu> + * Based on SoftDog driver by Alan Cox <alan@redhat.com> + * + * Copyright (c) 2004 Texas Instruments. + * 1. Modified to support OMAP1610 32-KHz watchdog timer + * 2. Ported to 2.6 kernel + * + * Copyright (c) 2005 David Brownell + * Use the driver model and standard identifiers; handle bigger timeouts. + */ + +#include <linux/module.h> +#include <linux/config.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/fs.h> +#include <linux/mm.h> +#include <linux/miscdevice.h> +#include <linux/watchdog.h> +#include <linux/reboot.h> +#include <linux/smp_lock.h> +#include <linux/init.h> +#include <linux/err.h> +#include <linux/platform_device.h> +#include <linux/moduleparam.h> +#include <linux/clk.h> + +#include <asm/io.h> +#include <asm/uaccess.h> +#include <asm/hardware.h> +#include <asm/bitops.h> + +#include <asm/arch/prcm.h> + +#include "omap_wdt.h" + +static unsigned timer_margin; +module_param(timer_margin, uint, 0); +MODULE_PARM_DESC(timer_margin, "initial watchdog timeout (in seconds)"); + +static int omap_wdt_users; +static struct clk *armwdt_ck = NULL; +static struct clk *mpu_wdt_ick = NULL; +static struct clk *mpu_wdt_fck = NULL; + +static unsigned int wdt_trgr_pattern = 0x1234; + +static void omap_wdt_ping(void) +{ + /* wait for posted write to complete */ + while ((omap_readl(OMAP_WATCHDOG_WPS)) & 0x08) + cpu_relax(); + wdt_trgr_pattern = ~wdt_trgr_pattern; + omap_writel(wdt_trgr_pattern, (OMAP_WATCHDOG_TGR)); + /* wait for posted write to complete */ + while ((omap_readl(OMAP_WATCHDOG_WPS)) & 0x08) + cpu_relax(); + /* reloaded WCRR from WLDR */ +} + +static void omap_wdt_enable(void) +{ + /* Sequence to enable the watchdog */ + omap_writel(0xBBBB, OMAP_WATCHDOG_SPR); + while ((omap_readl(OMAP_WATCHDOG_WPS)) & 0x10) + cpu_relax(); + omap_writel(0x4444, OMAP_WATCHDOG_SPR); + while ((omap_readl(OMAP_WATCHDOG_WPS)) & 0x10) + cpu_relax(); +} + +static void omap_wdt_disable(void) +{ + /* sequence required to disable watchdog */ + omap_writel(0xAAAA, OMAP_WATCHDOG_SPR); /* TIMER_MODE */ + while (omap_readl(OMAP_WATCHDOG_WPS) & 0x10) + cpu_relax(); + omap_writel(0x5555, OMAP_WATCHDOG_SPR); /* TIMER_MODE */ + while (omap_readl(OMAP_WATCHDOG_WPS) & 0x10) + cpu_relax(); +} + +static void omap_wdt_adjust_timeout(unsigned new_timeout) +{ + if (new_timeout < TIMER_MARGIN_MIN) + new_timeout = TIMER_MARGIN_DEFAULT; + if (new_timeout > TIMER_MARGIN_MAX) + new_timeout = TIMER_MARGIN_MAX; + timer_margin = new_timeout; +} + +static void omap_wdt_set_timeout(void) +{ + u32 pre_margin = GET_WLDR_VAL(timer_margin); + + /* just count up at 32 KHz */ + while (omap_readl(OMAP_WATCHDOG_WPS) & 0x04) + cpu_relax(); + omap_writel(pre_margin, OMAP_WATCHDOG_LDR); + while (omap_readl(OMAP_WATCHDOG_WPS) & 0x04) + cpu_relax(); +} + +/* + * Allow only one task to hold it open + */ + +static int omap_wdt_open(struct inode *inode, struct file *file) +{ + if (test_and_set_bit(1, (unsigned long *)&omap_wdt_users)) + return -EBUSY; + + if (cpu_is_omap16xx()) + clk_enable(armwdt_ck); /* Enable the clock */ + + if (cpu_is_omap24xx()) { + clk_enable(mpu_wdt_ick); /* Enable the interface clock */ + clk_enable(mpu_wdt_fck); /* Enable the functional clock */ + } + + /* initialize prescaler */ + while (omap_readl(OMAP_WATCHDOG_WPS) & 0x01) + cpu_relax(); + omap_writel((1 << 5) | (PTV << 2), OMAP_WATCHDOG_CNTRL); + while (omap_readl(OMAP_WATCHDOG_WPS) & 0x01) + cpu_relax(); + + omap_wdt_set_timeout(); + omap_wdt_enable(); + return 0; +} + +static int omap_wdt_release(struct inode *inode, struct file *file) +{ + /* + * Shut off the timer unless NOWAYOUT is defined. + */ +#ifndef CONFIG_WATCHDOG_NOWAYOUT + omap_wdt_disable(); + + if (cpu_is_omap16xx()) { + clk_disable(armwdt_ck); /* Disable the clock */ + clk_put(armwdt_ck); + armwdt_ck = NULL; + } + + if (cpu_is_omap24xx()) { + clk_disable(mpu_wdt_ick); /* Disable the clock */ + clk_disable(mpu_wdt_fck); /* Disable the clock */ + clk_put(mpu_wdt_ick); + clk_put(mpu_wdt_fck); + mpu_wdt_ick = NULL; + mpu_wdt_fck = NULL; + } +#else + printk(KERN_CRIT "omap_wdt: Unexpected close, not stopping!\n"); +#endif + omap_wdt_users = 0; + return 0; +} + +static ssize_t +omap_wdt_write(struct file *file, const char __user *data, + size_t len, loff_t *ppos) +{ + /* Refresh LOAD_TIME. */ + if (len) + omap_wdt_ping(); + return len; +} + +static int +omap_wdt_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + int new_margin; + static struct watchdog_info ident = { + .identity = "OMAP Watchdog", + .options = WDIOF_SETTIMEOUT, + .firmware_version = 0, + }; + + switch (cmd) { + default: + return -ENOIOCTLCMD; + case WDIOC_GETSUPPORT: + return copy_to_user((struct watchdog_info __user *)arg, &ident, + sizeof(ident)); + case WDIOC_GETSTATUS: + return put_user(0, (int __user *)arg); + case WDIOC_GETBOOTSTATUS: + if (cpu_is_omap16xx()) + return put_user(omap_readw(ARM_SYSST), + (int __user *)arg); + if (cpu_is_omap24xx()) + return put_user(omap_prcm_get_reset_sources(), + (int __user *)arg); + case WDIOC_KEEPALIVE: + omap_wdt_ping(); + return 0; + case WDIOC_SETTIMEOUT: + if (get_user(new_margin, (int __user *)arg)) + return -EFAULT; + omap_wdt_adjust_timeout(new_margin); + + omap_wdt_disable(); + omap_wdt_set_timeout(); + omap_wdt_enable(); + + omap_wdt_ping(); + /* Fall */ + case WDIOC_GETTIMEOUT: + return put_user(timer_margin, (int __user *)arg); + } +} + +static struct file_operations omap_wdt_fops = { + .owner = THIS_MODULE, + .write = omap_wdt_write, + .ioctl = omap_wdt_ioctl, + .open = omap_wdt_open, + .release = omap_wdt_release, +}; + +static struct miscdevice omap_wdt_miscdev = { + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &omap_wdt_fops +}; + +static int __init omap_wdt_probe(struct platform_device *pdev) +{ + struct resource *res, *mem; + int ret; + + /* reserve static register mappings */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENOENT; + + mem = request_mem_region(res->start, res->end - res->start + 1, + pdev->name); + if (mem == NULL) + return -EBUSY; + + platform_set_drvdata(pdev, mem); + + omap_wdt_users = 0; + + if (cpu_is_omap16xx()) { + armwdt_ck = clk_get(&pdev->dev, "armwdt_ck"); + if (IS_ERR(armwdt_ck)) { + ret = PTR_ERR(armwdt_ck); + armwdt_ck = NULL; + goto fail; + } + } + + if (cpu_is_omap24xx()) { + mpu_wdt_ick = clk_get(&pdev->dev, "mpu_wdt_ick"); + if (IS_ERR(mpu_wdt_ick)) { + ret = PTR_ERR(mpu_wdt_ick); + mpu_wdt_ick = NULL; + goto fail; + } + mpu_wdt_fck = clk_get(&pdev->dev, "mpu_wdt_fck"); + if (IS_ERR(mpu_wdt_fck)) { + ret = PTR_ERR(mpu_wdt_fck); + mpu_wdt_fck = NULL; + goto fail; + } + } + + omap_wdt_disable(); + omap_wdt_adjust_timeout(timer_margin); + + omap_wdt_miscdev.dev = &pdev->dev; + ret = misc_register(&omap_wdt_miscdev); + if (ret) + goto fail; + + pr_info("OMAP Watchdog Timer: initial timeout %d sec\n", timer_margin); + + /* autogate OCP interface clock */ + omap_writel(0x01, OMAP_WATCHDOG_SYS_CONFIG); + return 0; + +fail: + if (armwdt_ck) + clk_put(armwdt_ck); + if (mpu_wdt_ick) + clk_put(mpu_wdt_ick); + if (mpu_wdt_fck) + clk_put(mpu_wdt_fck); + release_resource(mem); + return ret; +} + +static void omap_wdt_shutdown(struct platform_device *pdev) +{ + omap_wdt_disable(); +} + +static int omap_wdt_remove(struct platform_device *pdev) +{ + struct resource *mem = platform_get_drvdata(pdev); + misc_deregister(&omap_wdt_miscdev); + release_resource(mem); + if (armwdt_ck) + clk_put(armwdt_ck); + if (mpu_wdt_ick) + clk_put(mpu_wdt_ick); + if (mpu_wdt_fck) + clk_put(mpu_wdt_fck); + return 0; +} + +#ifdef CONFIG_PM + +/* REVISIT ... not clear this is the best way to handle system suspend; and + * it's very inappropriate for selective device suspend (e.g. suspending this + * through sysfs rather than by stopping the watchdog daemon). Also, this + * may not play well enough with NOWAYOUT... + */ + +static int omap_wdt_suspend(struct platform_device *pdev, pm_message_t state) +{ + if (omap_wdt_users) + omap_wdt_disable(); + return 0; +} + +static int omap_wdt_resume(struct platform_device *pdev) +{ + if (omap_wdt_users) { + omap_wdt_enable(); + omap_wdt_ping(); + } + return 0; +} + +#else +#define omap_wdt_suspend NULL +#define omap_wdt_resume NULL +#endif + +static struct platform_driver omap_wdt_driver = { + .probe = omap_wdt_probe, + .remove = omap_wdt_remove, + .shutdown = omap_wdt_shutdown, + .suspend = omap_wdt_suspend, + .resume = omap_wdt_resume, + .driver = { + .owner = THIS_MODULE, + .name = "omap_wdt", + }, +}; + +static int __init omap_wdt_init(void) +{ + return platform_driver_register(&omap_wdt_driver); +} + +static void __exit omap_wdt_exit(void) +{ + platform_driver_unregister(&omap_wdt_driver); +} + +module_init(omap_wdt_init); +module_exit(omap_wdt_exit); + +MODULE_AUTHOR("George G. Davis"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); diff --git a/drivers/char/watchdog/omap_wdt.h b/drivers/char/watchdog/omap_wdt.h new file mode 100644 index 00000000000..52a532a5114 --- /dev/null +++ b/drivers/char/watchdog/omap_wdt.h @@ -0,0 +1,64 @@ +/* + * linux/drivers/char/watchdog/omap_wdt.h + * + * BRIEF MODULE DESCRIPTION + * OMAP Watchdog timer register definitions + * + * Copyright (C) 2004 Texas Instruments. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef _OMAP_WATCHDOG_H +#define _OMAP_WATCHDOG_H + +#define OMAP1610_WATCHDOG_BASE 0xfffeb000 +#define OMAP2420_WATCHDOG_BASE 0x48022000 /*WDT Timer 2 */ + +#ifdef CONFIG_ARCH_OMAP24XX +#define OMAP_WATCHDOG_BASE OMAP2420_WATCHDOG_BASE +#else +#define OMAP_WATCHDOG_BASE OMAP1610_WATCHDOG_BASE +#define RM_RSTST_WKUP 0 +#endif + +#define OMAP_WATCHDOG_REV (OMAP_WATCHDOG_BASE + 0x00) +#define OMAP_WATCHDOG_SYS_CONFIG (OMAP_WATCHDOG_BASE + 0x10) +#define OMAP_WATCHDOG_STATUS (OMAP_WATCHDOG_BASE + 0x14) +#define OMAP_WATCHDOG_CNTRL (OMAP_WATCHDOG_BASE + 0x24) +#define OMAP_WATCHDOG_CRR (OMAP_WATCHDOG_BASE + 0x28) +#define OMAP_WATCHDOG_LDR (OMAP_WATCHDOG_BASE + 0x2c) +#define OMAP_WATCHDOG_TGR (OMAP_WATCHDOG_BASE + 0x30) +#define OMAP_WATCHDOG_WPS (OMAP_WATCHDOG_BASE + 0x34) +#define OMAP_WATCHDOG_SPR (OMAP_WATCHDOG_BASE + 0x48) + +/* Using the prescaler, the OMAP watchdog could go for many + * months before firing. These limits work without scaling, + * with the 60 second default assumed by most tools and docs. + */ +#define TIMER_MARGIN_MAX (24 * 60 * 60) /* 1 day */ +#define TIMER_MARGIN_DEFAULT 60 /* 60 secs */ +#define TIMER_MARGIN_MIN 1 + +#define PTV 0 /* prescale */ +#define GET_WLDR_VAL(secs) (0xffffffff - ((secs) * (32768/(1<<PTV))) + 1) + +#endif /* _OMAP_WATCHDOG_H */ diff --git a/drivers/char/watchdog/shwdt.c b/drivers/char/watchdog/shwdt.c index 1355038f104..e5b8c64f1d6 100644 --- a/drivers/char/watchdog/shwdt.c +++ b/drivers/char/watchdog/shwdt.c @@ -27,7 +27,7 @@ #include <linux/notifier.h> #include <linux/ioport.h> #include <linux/fs.h> - +#include <linux/mm.h> #include <asm/io.h> #include <asm/uaccess.h> #include <asm/watchdog.h> @@ -125,7 +125,6 @@ static void sh_wdt_start(void) /** * sh_wdt_stop - Stop the Watchdog - * * Stops the watchdog. */ static void sh_wdt_stop(void) @@ -141,22 +140,20 @@ static void sh_wdt_stop(void) /** * sh_wdt_keepalive - Keep the Userspace Watchdog Alive - * * The Userspace watchdog got a KeepAlive: schedule the next heartbeat. */ -static void sh_wdt_keepalive(void) +static inline void sh_wdt_keepalive(void) { next_heartbeat = jiffies + (heartbeat * HZ); } /** * sh_wdt_set_heartbeat - Set the Userspace Watchdog heartbeat - * * Set the Userspace Watchdog heartbeat */ static int sh_wdt_set_heartbeat(int t) { - if ((t < 1) || (t > 3600)) /* arbitrary upper limit */ + if (unlikely((t < 1) || (t > 3600))) /* arbitrary upper limit */ return -EINVAL; heartbeat = t; @@ -165,7 +162,6 @@ static int sh_wdt_set_heartbeat(int t) /** * sh_wdt_ping - Ping the Watchdog - * * @data: Unused * * Clears overflow bit, resets timer counter. @@ -182,14 +178,13 @@ static void sh_wdt_ping(unsigned long data) sh_wdt_write_cnt(0); mod_timer(&timer, next_ping_period(clock_division_ratio)); - } else { - printk(KERN_WARNING PFX "Heartbeat lost! Will not ping the watchdog\n"); - } + } else + printk(KERN_WARNING PFX "Heartbeat lost! Will not ping " + "the watchdog\n"); } /** * sh_wdt_open - Open the Device - * * @inode: inode of device * @file: file handle of device * @@ -209,7 +204,6 @@ static int sh_wdt_open(struct inode *inode, struct file *file) /** * sh_wdt_close - Close the Device - * * @inode: inode of device * @file: file handle of device * @@ -220,7 +214,8 @@ static int sh_wdt_close(struct inode *inode, struct file *file) if (shwdt_expect_close == 42) { sh_wdt_stop(); } else { - printk(KERN_CRIT PFX "Unexpected close, not stopping watchdog!\n"); + printk(KERN_CRIT PFX "Unexpected close, not " + "stopping watchdog!\n"); sh_wdt_keepalive(); } @@ -232,7 +227,6 @@ static int sh_wdt_close(struct inode *inode, struct file *file) /** * sh_wdt_write - Write to Device - * * @file: file handle of device * @buf: buffer to write * @count: length of buffer @@ -264,8 +258,56 @@ static ssize_t sh_wdt_write(struct file *file, const char *buf, } /** - * sh_wdt_ioctl - Query Device + * sh_wdt_mmap - map WDT/CPG registers into userspace + * @file: file structure for the device + * @vma: VMA to map the registers into + * + * A simple mmap() implementation for the corner cases where the counter + * needs to be mapped in userspace directly. Due to the relatively small + * size of the area, neighbouring registers not necessarily tied to the + * CPG will also be accessible through the register page, so this remains + * configurable for users that really know what they're doing. * + * Additionaly, the register page maps in the CPG register base relative + * to the nearest page-aligned boundary, which requires that userspace do + * the appropriate CPU subtype math for calculating the page offset for + * the counter value. + */ +static int sh_wdt_mmap(struct file *file, struct vm_area_struct *vma) +{ + int ret = -ENOSYS; + +#ifdef CONFIG_SH_WDT_MMAP + unsigned long addr; + + /* Only support the simple cases where we map in a register page. */ + if (((vma->vm_end - vma->vm_start) != PAGE_SIZE) || vma->vm_pgoff) + return -EINVAL; + + /* + * Pick WTCNT as the start, it's usually the first register after the + * FRQCR, and neither one are generally page-aligned out of the box. + */ + addr = WTCNT & ~(PAGE_SIZE - 1); + + vma->vm_flags |= VM_IO; + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + + if (io_remap_pfn_range(vma, vma->vm_start, addr >> PAGE_SHIFT, + PAGE_SIZE, vma->vm_page_prot)) { + printk(KERN_ERR PFX "%s: io_remap_pfn_range failed\n", + __FUNCTION__); + return -EAGAIN; + } + + ret = 0; +#endif + + return ret; +} + +/** + * sh_wdt_ioctl - Query Device * @inode: inode of device * @file: file handle of device * @cmd: watchdog command @@ -326,7 +368,6 @@ static int sh_wdt_ioctl(struct inode *inode, struct file *file, /** * sh_wdt_notify_sys - Notifier Handler - * * @this: notifier block * @code: notifier event * @unused: unused @@ -337,9 +378,8 @@ static int sh_wdt_ioctl(struct inode *inode, struct file *file, static int sh_wdt_notify_sys(struct notifier_block *this, unsigned long code, void *unused) { - if (code == SYS_DOWN || code == SYS_HALT) { + if (code == SYS_DOWN || code == SYS_HALT) sh_wdt_stop(); - } return NOTIFY_DONE; } @@ -351,10 +391,12 @@ static const struct file_operations sh_wdt_fops = { .ioctl = sh_wdt_ioctl, .open = sh_wdt_open, .release = sh_wdt_close, + .mmap = sh_wdt_mmap, }; static struct watchdog_info sh_wdt_info = { - .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE, + .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | + WDIOF_MAGICCLOSE, .firmware_version = 1, .identity = "SH WDT", }; @@ -371,7 +413,6 @@ static struct miscdevice sh_wdt_miscdev = { /** * sh_wdt_init - Initialize module - * * Registers the device and notifier handler. Actual device * initialization is handled by sh_wdt_open(). */ @@ -381,15 +422,15 @@ static int __init sh_wdt_init(void) if ((clock_division_ratio < 0x5) || (clock_division_ratio > 0x7)) { clock_division_ratio = WTCSR_CKS_4096; - printk(KERN_INFO PFX "clock_division_ratio value must be 0x5<=x<=0x7, using %d\n", - clock_division_ratio); + printk(KERN_INFO PFX "clock_division_ratio value must " + "be 0x5<=x<=0x7, using %d\n", clock_division_ratio); } - if (sh_wdt_set_heartbeat(heartbeat)) - { + rc = sh_wdt_set_heartbeat(heartbeat); + if (unlikely(rc)) { heartbeat = WATCHDOG_HEARTBEAT; - printk(KERN_INFO PFX "heartbeat value must be 1<=x<=3600, using %d\n", - heartbeat); + printk(KERN_INFO PFX "heartbeat value must " + "be 1<=x<=3600, using %d\n", heartbeat); } init_timer(&timer); @@ -397,15 +438,16 @@ static int __init sh_wdt_init(void) timer.data = 0; rc = register_reboot_notifier(&sh_wdt_notifier); - if (rc) { - printk(KERN_ERR PFX "Can't register reboot notifier (err=%d)\n", rc); + if (unlikely(rc)) { + printk(KERN_ERR PFX "Can't register reboot notifier (err=%d)\n", + rc); return rc; } rc = misc_register(&sh_wdt_miscdev); - if (rc) { - printk(KERN_ERR PFX "Can't register miscdev on minor=%d (err=%d)\n", - sh_wdt_miscdev.minor, rc); + if (unlikely(rc)) { + printk(KERN_ERR PFX "Can't register miscdev on " + "minor=%d (err=%d)\n", sh_wdt_miscdev.minor, rc); unregister_reboot_notifier(&sh_wdt_notifier); return rc; } @@ -418,7 +460,6 @@ static int __init sh_wdt_init(void) /** * sh_wdt_exit - Deinitialize module - * * Unregisters the device and notifier handler. Actual device * deinitialization is handled by sh_wdt_close(). */ @@ -434,14 +475,13 @@ MODULE_LICENSE("GPL"); MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); module_param(clock_division_ratio, int, 0); -MODULE_PARM_DESC(clock_division_ratio, "Clock division ratio. Valid ranges are from 0x5 (1.31ms) to 0x7 (5.25ms). Defaults to 0x7."); +MODULE_PARM_DESC(clock_division_ratio, "Clock division ratio. Valid ranges are from 0x5 (1.31ms) to 0x7 (5.25ms). (default=" __MODULE_STRING(clock_division_ratio) ")"); module_param(heartbeat, int, 0); MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds. (1<=heartbeat<=3600, default=" __MODULE_STRING(WATCHDOG_HEARTBEAT) ")"); module_param(nowayout, int, 0); -MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=CONFIG_WATCHDOG_NOWAYOUT)"); +MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); module_init(sh_wdt_init); module_exit(sh_wdt_exit); - |