diff options
Diffstat (limited to 'drivers/char')
49 files changed, 5798 insertions, 2773 deletions
diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index 3141dd3b6e53..f09fc0e2062d 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig @@ -276,11 +276,19 @@ config N_HDLC Allows synchronous HDLC communications with tty device drivers that support synchronous HDLC such as the Microgate SyncLink adapter. - This driver can only be built as a module ( = code which can be + This driver can be built as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module will be called n_hdlc. If you want to do that, say M here. +config N_GSM + tristate "GSM MUX line discipline support (EXPERIMENTAL)" + depends on EXPERIMENTAL + depends on NET + help + This line discipline provides support for the GSM MUX protocol and + presents the mux as a set of 61 individual tty devices. + config RISCOM8 tristate "SDL RISCom/8 card support" depends on SERIAL_NONSTANDARD @@ -1113,5 +1121,12 @@ config DEVPORT source "drivers/s390/char/Kconfig" +config RAMOOPS + tristate "Log panic/oops to a RAM buffer" + default n + help + This enables panic and oops messages to be logged to a circular + buffer in RAM where it can be read back at some later point. + endmenu diff --git a/drivers/char/Makefile b/drivers/char/Makefile index f957edf7e45d..88d6eac69754 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -40,6 +40,7 @@ obj-$(CONFIG_SYNCLINK) += synclink.o obj-$(CONFIG_SYNCLINKMP) += synclinkmp.o obj-$(CONFIG_SYNCLINK_GT) += synclink_gt.o obj-$(CONFIG_N_HDLC) += n_hdlc.o +obj-$(CONFIG_N_GSM) += n_gsm.o obj-$(CONFIG_AMIGA_BUILTIN_SERIAL) += amiserial.o obj-$(CONFIG_SX) += sx.o generic_serial.o obj-$(CONFIG_RIO) += rio/ generic_serial.o @@ -107,6 +108,7 @@ obj-$(CONFIG_HANGCHECK_TIMER) += hangcheck-timer.o obj-$(CONFIG_TCG_TPM) += tpm/ obj-$(CONFIG_PS3_FLASH) += ps3flash.o +obj-$(CONFIG_RAMOOPS) += ramoops.o obj-$(CONFIG_JS_RTC) += js-rtc.o js-rtc-y = rtc.o diff --git a/drivers/char/agp/agp.h b/drivers/char/agp/agp.h index 870f12cfed93..120490949997 100644 --- a/drivers/char/agp/agp.h +++ b/drivers/char/agp/agp.h @@ -178,86 +178,6 @@ struct agp_bridge_data { #define PGE_EMPTY(b, p) (!(p) || (p) == (unsigned long) (b)->scratch_page) -/* Intel registers */ -#define INTEL_APSIZE 0xb4 -#define INTEL_ATTBASE 0xb8 -#define INTEL_AGPCTRL 0xb0 -#define INTEL_NBXCFG 0x50 -#define INTEL_ERRSTS 0x91 - -/* Intel i830 registers */ -#define I830_GMCH_CTRL 0x52 -#define I830_GMCH_ENABLED 0x4 -#define I830_GMCH_MEM_MASK 0x1 -#define I830_GMCH_MEM_64M 0x1 -#define I830_GMCH_MEM_128M 0 -#define I830_GMCH_GMS_MASK 0x70 -#define I830_GMCH_GMS_DISABLED 0x00 -#define I830_GMCH_GMS_LOCAL 0x10 -#define I830_GMCH_GMS_STOLEN_512 0x20 -#define I830_GMCH_GMS_STOLEN_1024 0x30 -#define I830_GMCH_GMS_STOLEN_8192 0x40 -#define I830_RDRAM_CHANNEL_TYPE 0x03010 -#define I830_RDRAM_ND(x) (((x) & 0x20) >> 5) -#define I830_RDRAM_DDT(x) (((x) & 0x18) >> 3) - -/* This one is for I830MP w. an external graphic card */ -#define INTEL_I830_ERRSTS 0x92 - -/* Intel 855GM/852GM registers */ -#define I855_GMCH_GMS_MASK 0xF0 -#define I855_GMCH_GMS_STOLEN_0M 0x0 -#define I855_GMCH_GMS_STOLEN_1M (0x1 << 4) -#define I855_GMCH_GMS_STOLEN_4M (0x2 << 4) -#define I855_GMCH_GMS_STOLEN_8M (0x3 << 4) -#define I855_GMCH_GMS_STOLEN_16M (0x4 << 4) -#define I855_GMCH_GMS_STOLEN_32M (0x5 << 4) -#define I85X_CAPID 0x44 -#define I85X_VARIANT_MASK 0x7 -#define I85X_VARIANT_SHIFT 5 -#define I855_GME 0x0 -#define I855_GM 0x4 -#define I852_GME 0x2 -#define I852_GM 0x5 - -/* Intel i845 registers */ -#define INTEL_I845_AGPM 0x51 -#define INTEL_I845_ERRSTS 0xc8 - -/* Intel i860 registers */ -#define INTEL_I860_MCHCFG 0x50 -#define INTEL_I860_ERRSTS 0xc8 - -/* Intel i810 registers */ -#define I810_GMADDR 0x10 -#define I810_MMADDR 0x14 -#define I810_PTE_BASE 0x10000 -#define I810_PTE_MAIN_UNCACHED 0x00000000 -#define I810_PTE_LOCAL 0x00000002 -#define I810_PTE_VALID 0x00000001 -#define I830_PTE_SYSTEM_CACHED 0x00000006 -#define I810_SMRAM_MISCC 0x70 -#define I810_GFX_MEM_WIN_SIZE 0x00010000 -#define I810_GFX_MEM_WIN_32M 0x00010000 -#define I810_GMS 0x000000c0 -#define I810_GMS_DISABLE 0x00000000 -#define I810_PGETBL_CTL 0x2020 -#define I810_PGETBL_ENABLED 0x00000001 -#define I965_PGETBL_SIZE_MASK 0x0000000e -#define I965_PGETBL_SIZE_512KB (0 << 1) -#define I965_PGETBL_SIZE_256KB (1 << 1) -#define I965_PGETBL_SIZE_128KB (2 << 1) -#define I965_PGETBL_SIZE_1MB (3 << 1) -#define I965_PGETBL_SIZE_2MB (4 << 1) -#define I965_PGETBL_SIZE_1_5MB (5 << 1) -#define G33_PGETBL_SIZE_MASK (3 << 8) -#define G33_PGETBL_SIZE_1M (1 << 8) -#define G33_PGETBL_SIZE_2M (2 << 8) - -#define I810_DRAM_CTL 0x3000 -#define I810_DRAM_ROW_0 0x00000001 -#define I810_DRAM_ROW_0_SDRAM 0x00000001 - struct agp_device_ids { unsigned short device_id; /* first, to make table easier to read */ enum chipset_type chipset; diff --git a/drivers/char/agp/ali-agp.c b/drivers/char/agp/ali-agp.c index d2ce68f27e4b..fd793519ea2b 100644 --- a/drivers/char/agp/ali-agp.c +++ b/drivers/char/agp/ali-agp.c @@ -204,6 +204,7 @@ static const struct agp_bridge_driver ali_generic_bridge = { .aperture_sizes = ali_generic_sizes, .size_type = U32_APER_SIZE, .num_aperture_sizes = 7, + .needs_scratch_page = true, .configure = ali_configure, .fetch_size = ali_fetch_size, .cleanup = ali_cleanup, diff --git a/drivers/char/agp/amd-k7-agp.c b/drivers/char/agp/amd-k7-agp.c index a7637d72cef6..b6b1568314c8 100644 --- a/drivers/char/agp/amd-k7-agp.c +++ b/drivers/char/agp/amd-k7-agp.c @@ -142,6 +142,7 @@ static int amd_create_gatt_table(struct agp_bridge_data *bridge) { struct aper_size_info_lvl2 *value; struct amd_page_map page_dir; + unsigned long __iomem *cur_gatt; unsigned long addr; int retval; u32 temp; @@ -178,6 +179,13 @@ static int amd_create_gatt_table(struct agp_bridge_data *bridge) readl(page_dir.remapped+GET_PAGE_DIR_OFF(addr)); /* PCI Posting. */ } + for (i = 0; i < value->num_entries; i++) { + addr = (i * PAGE_SIZE) + agp_bridge->gart_bus_addr; + cur_gatt = GET_GATT(addr); + writel(agp_bridge->scratch_page, cur_gatt+GET_GATT_OFF(addr)); + readl(cur_gatt+GET_GATT_OFF(addr)); /* PCI Posting. */ + } + return 0; } @@ -375,6 +383,7 @@ static const struct agp_bridge_driver amd_irongate_driver = { .aperture_sizes = amd_irongate_sizes, .size_type = LVL2_APER_SIZE, .num_aperture_sizes = 7, + .needs_scratch_page = true, .configure = amd_irongate_configure, .fetch_size = amd_irongate_fetch_size, .cleanup = amd_irongate_cleanup, diff --git a/drivers/char/agp/amd64-agp.c b/drivers/char/agp/amd64-agp.c index fd50ead59c79..70312da4c968 100644 --- a/drivers/char/agp/amd64-agp.c +++ b/drivers/char/agp/amd64-agp.c @@ -210,6 +210,7 @@ static const struct agp_bridge_driver amd_8151_driver = { .aperture_sizes = amd_8151_sizes, .size_type = U32_APER_SIZE, .num_aperture_sizes = 7, + .needs_scratch_page = true, .configure = amd_8151_configure, .fetch_size = amd64_fetch_size, .cleanup = amd64_cleanup, @@ -383,7 +384,7 @@ static int __devinit uli_agp_init(struct pci_dev *pdev) { u32 httfea,baseaddr,enuscr; struct pci_dev *dev1; - int i; + int i, ret; unsigned size = amd64_fetch_size(); dev_info(&pdev->dev, "setting up ULi AGP\n"); @@ -399,15 +400,18 @@ static int __devinit uli_agp_init(struct pci_dev *pdev) if (i == ARRAY_SIZE(uli_sizes)) { dev_info(&pdev->dev, "no ULi size found for %d\n", size); - return -ENODEV; + ret = -ENODEV; + goto put; } /* shadow x86-64 registers into ULi registers */ pci_read_config_dword (k8_northbridges[0], AMD64_GARTAPERTUREBASE, &httfea); /* if x86-64 aperture base is beyond 4G, exit here */ - if ((httfea & 0x7fff) >> (32 - 25)) - return -ENODEV; + if ((httfea & 0x7fff) >> (32 - 25)) { + ret = -ENODEV; + goto put; + } httfea = (httfea& 0x7fff) << 25; @@ -419,9 +423,10 @@ 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); - + ret = 0; +put: pci_dev_put(dev1); - return 0; + return ret; } @@ -440,7 +445,7 @@ static int nforce3_agp_init(struct pci_dev *pdev) { u32 tmp, apbase, apbar, aplimit; struct pci_dev *dev1; - int i; + int i, ret; unsigned size = amd64_fetch_size(); dev_info(&pdev->dev, "setting up Nforce3 AGP\n"); @@ -457,7 +462,8 @@ static int nforce3_agp_init(struct pci_dev *pdev) if (i == ARRAY_SIZE(nforce3_sizes)) { dev_info(&pdev->dev, "no NForce3 size found for %d\n", size); - return -ENODEV; + ret = -ENODEV; + goto put; } pci_read_config_dword(dev1, NVIDIA_X86_64_1_APSIZE, &tmp); @@ -471,7 +477,8 @@ static int nforce3_agp_init(struct pci_dev *pdev) /* if x86-64 aperture base is beyond 4G, exit here */ if ( (apbase & 0x7fff) >> (32 - 25) ) { dev_info(&pdev->dev, "aperture base > 4G\n"); - return -ENODEV; + ret = -ENODEV; + goto put; } apbase = (apbase & 0x7fff) << 25; @@ -487,9 +494,11 @@ static int 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); + ret = 0; +put: pci_dev_put(dev1); - return 0; + return ret; } static int __devinit agp_amd64_probe(struct pci_dev *pdev, @@ -499,6 +508,10 @@ static int __devinit agp_amd64_probe(struct pci_dev *pdev, u8 cap_ptr; int err; + /* The Highlander principle */ + if (agp_bridges_found) + return -ENODEV; + cap_ptr = pci_find_capability(pdev, PCI_CAP_ID_AGP); if (!cap_ptr) return -ENODEV; @@ -562,6 +575,8 @@ static void __devexit agp_amd64_remove(struct pci_dev *pdev) amd64_aperture_sizes[bridge->aperture_size_idx].size); agp_remove_bridge(bridge); agp_put_bridge(bridge); + + agp_bridges_found--; } #ifdef CONFIG_PM @@ -709,6 +724,11 @@ static struct pci_device_id agp_amd64_pci_table[] = { MODULE_DEVICE_TABLE(pci, agp_amd64_pci_table); +static DEFINE_PCI_DEVICE_TABLE(agp_amd64_pci_promisc_table) = { + { PCI_DEVICE_CLASS(0, 0) }, + { } +}; + static struct pci_driver agp_amd64_pci_driver = { .name = "agpgart-amd64", .id_table = agp_amd64_pci_table, @@ -734,7 +754,6 @@ int __init agp_amd64_init(void) return err; if (agp_bridges_found == 0) { - struct pci_dev *dev; if (!agp_try_unsupported && !agp_try_unsupported_boot) { printk(KERN_INFO PFX "No supported AGP bridge found.\n"); #ifdef MODULE @@ -750,17 +769,10 @@ int __init agp_amd64_init(void) return -ENODEV; /* Look for any AGP bridge */ - dev = NULL; - err = -ENODEV; - for_each_pci_dev(dev) { - if (!pci_find_capability(dev, PCI_CAP_ID_AGP)) - continue; - /* Only one bridge supported right now */ - if (agp_amd64_probe(dev, NULL) == 0) { - err = 0; - break; - } - } + agp_amd64_pci_driver.id_table = agp_amd64_pci_promisc_table; + err = driver_attach(&agp_amd64_pci_driver.driver); + if (err == 0 && agp_bridges_found == 0) + err = -ENODEV; } return err; } diff --git a/drivers/char/agp/ati-agp.c b/drivers/char/agp/ati-agp.c index 3b2ecbe86ebe..dc30e2243494 100644 --- a/drivers/char/agp/ati-agp.c +++ b/drivers/char/agp/ati-agp.c @@ -341,6 +341,7 @@ static int ati_create_gatt_table(struct agp_bridge_data *bridge) { struct aper_size_info_lvl2 *value; struct ati_page_map page_dir; + unsigned long __iomem *cur_gatt; unsigned long addr; int retval; u32 temp; @@ -395,6 +396,12 @@ static int ati_create_gatt_table(struct agp_bridge_data *bridge) readl(page_dir.remapped+GET_PAGE_DIR_OFF(addr)); /* PCI Posting. */ } + for (i = 0; i < value->num_entries; i++) { + addr = (i * PAGE_SIZE) + agp_bridge->gart_bus_addr; + cur_gatt = GET_GATT(addr); + writel(agp_bridge->scratch_page, cur_gatt+GET_GATT_OFF(addr)); + } + return 0; } @@ -415,6 +422,7 @@ static const struct agp_bridge_driver ati_generic_bridge = { .aperture_sizes = ati_generic_sizes, .size_type = LVL2_APER_SIZE, .num_aperture_sizes = 7, + .needs_scratch_page = true, .configure = ati_configure, .fetch_size = ati_fetch_size, .cleanup = ati_cleanup, diff --git a/drivers/char/agp/efficeon-agp.c b/drivers/char/agp/efficeon-agp.c index 793f39ea9618..aa109cbe0e6e 100644 --- a/drivers/char/agp/efficeon-agp.c +++ b/drivers/char/agp/efficeon-agp.c @@ -28,6 +28,7 @@ #include <linux/page-flags.h> #include <linux/mm.h> #include "agp.h" +#include "intel-agp.h" /* * The real differences to the generic AGP code is diff --git a/drivers/char/agp/intel-agp.c b/drivers/char/agp/intel-agp.c index aa4248efc5d8..d836a71bf06d 100644 --- a/drivers/char/agp/intel-agp.c +++ b/drivers/char/agp/intel-agp.c @@ -11,1531 +11,13 @@ #include <linux/agp_backend.h> #include <asm/smp.h> #include "agp.h" +#include "intel-agp.h" + +#include "intel-gtt.c" int intel_agp_enabled; EXPORT_SYMBOL(intel_agp_enabled); -/* - * If we have Intel graphics, we're not going to have anything other than - * an Intel IOMMU. So make the correct use of the PCI DMA API contingent - * on the Intel IOMMU support (CONFIG_DMAR). - * Only newer chipsets need to bother with this, of course. - */ -#ifdef CONFIG_DMAR -#define USE_PCI_DMA_API 1 -#endif - -#define PCI_DEVICE_ID_INTEL_E7221_HB 0x2588 -#define PCI_DEVICE_ID_INTEL_E7221_IG 0x258a -#define PCI_DEVICE_ID_INTEL_82946GZ_HB 0x2970 -#define PCI_DEVICE_ID_INTEL_82946GZ_IG 0x2972 -#define PCI_DEVICE_ID_INTEL_82G35_HB 0x2980 -#define PCI_DEVICE_ID_INTEL_82G35_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 PCI_DEVICE_ID_INTEL_82965GM_HB 0x2A00 -#define PCI_DEVICE_ID_INTEL_82965GM_IG 0x2A02 -#define PCI_DEVICE_ID_INTEL_82965GME_HB 0x2A10 -#define PCI_DEVICE_ID_INTEL_82965GME_IG 0x2A12 -#define PCI_DEVICE_ID_INTEL_82945GME_HB 0x27AC -#define PCI_DEVICE_ID_INTEL_82945GME_IG 0x27AE -#define PCI_DEVICE_ID_INTEL_PINEVIEW_M_HB 0xA010 -#define PCI_DEVICE_ID_INTEL_PINEVIEW_M_IG 0xA011 -#define PCI_DEVICE_ID_INTEL_PINEVIEW_HB 0xA000 -#define PCI_DEVICE_ID_INTEL_PINEVIEW_IG 0xA001 -#define PCI_DEVICE_ID_INTEL_G33_HB 0x29C0 -#define PCI_DEVICE_ID_INTEL_G33_IG 0x29C2 -#define PCI_DEVICE_ID_INTEL_Q35_HB 0x29B0 -#define PCI_DEVICE_ID_INTEL_Q35_IG 0x29B2 -#define PCI_DEVICE_ID_INTEL_Q33_HB 0x29D0 -#define PCI_DEVICE_ID_INTEL_Q33_IG 0x29D2 -#define PCI_DEVICE_ID_INTEL_B43_HB 0x2E40 -#define PCI_DEVICE_ID_INTEL_B43_IG 0x2E42 -#define PCI_DEVICE_ID_INTEL_GM45_HB 0x2A40 -#define PCI_DEVICE_ID_INTEL_GM45_IG 0x2A42 -#define PCI_DEVICE_ID_INTEL_EAGLELAKE_HB 0x2E00 -#define PCI_DEVICE_ID_INTEL_EAGLELAKE_IG 0x2E02 -#define PCI_DEVICE_ID_INTEL_Q45_HB 0x2E10 -#define PCI_DEVICE_ID_INTEL_Q45_IG 0x2E12 -#define PCI_DEVICE_ID_INTEL_G45_HB 0x2E20 -#define PCI_DEVICE_ID_INTEL_G45_IG 0x2E22 -#define PCI_DEVICE_ID_INTEL_G41_HB 0x2E30 -#define PCI_DEVICE_ID_INTEL_G41_IG 0x2E32 -#define PCI_DEVICE_ID_INTEL_IRONLAKE_D_HB 0x0040 -#define PCI_DEVICE_ID_INTEL_IRONLAKE_D_IG 0x0042 -#define PCI_DEVICE_ID_INTEL_IRONLAKE_M_HB 0x0044 -#define PCI_DEVICE_ID_INTEL_IRONLAKE_MA_HB 0x0062 -#define PCI_DEVICE_ID_INTEL_IRONLAKE_MC2_HB 0x006a -#define PCI_DEVICE_ID_INTEL_IRONLAKE_M_IG 0x0046 -#define PCI_DEVICE_ID_INTEL_SANDYBRIDGE_HB 0x0100 -#define PCI_DEVICE_ID_INTEL_SANDYBRIDGE_IG 0x0102 -#define PCI_DEVICE_ID_INTEL_SANDYBRIDGE_M_HB 0x0104 -#define PCI_DEVICE_ID_INTEL_SANDYBRIDGE_M_IG 0x0106 - -/* cover 915 and 945 variants */ -#define IS_I915 (agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_E7221_HB || \ - 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_82945GME_HB) - -#define IS_I965 (agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82946GZ_HB || \ - agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82G35_HB || \ - agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82965Q_HB || \ - agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82965G_HB || \ - agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82965GM_HB || \ - agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82965GME_HB) - -#define IS_G33 (agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_G33_HB || \ - agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_Q35_HB || \ - agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_Q33_HB || \ - agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_PINEVIEW_M_HB || \ - agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_PINEVIEW_HB) - -#define IS_PINEVIEW (agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_PINEVIEW_M_HB || \ - agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_PINEVIEW_HB) - -#define IS_SNB (agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_SANDYBRIDGE_HB || \ - agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_SANDYBRIDGE_M_HB) - -#define IS_G4X (agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_EAGLELAKE_HB || \ - agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_Q45_HB || \ - agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_G45_HB || \ - agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_GM45_HB || \ - agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_G41_HB || \ - agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_B43_HB || \ - agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_IRONLAKE_D_HB || \ - agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_IRONLAKE_M_HB || \ - agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_IRONLAKE_MA_HB || \ - agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_IRONLAKE_MC2_HB || \ - IS_SNB) - -extern int agp_memory_reserved; - - -/* Intel 815 register */ -#define INTEL_815_APCONT 0x51 -#define INTEL_815_ATTBASE_MASK ~0x1FFFFFFF - -/* Intel i820 registers */ -#define INTEL_I820_RDCR 0x51 -#define INTEL_I820_ERRSTS 0xc8 - -/* Intel i840 registers */ -#define INTEL_I840_MCHCFG 0x50 -#define INTEL_I840_ERRSTS 0xc8 - -/* Intel i850 registers */ -#define INTEL_I850_MCHCFG 0x50 -#define INTEL_I850_ERRSTS 0xc8 - -/* intel 915G registers */ -#define I915_GMADDR 0x18 -#define I915_MMADDR 0x10 -#define I915_PTEADDR 0x1C -#define I915_GMCH_GMS_STOLEN_48M (0x6 << 4) -#define I915_GMCH_GMS_STOLEN_64M (0x7 << 4) -#define G33_GMCH_GMS_STOLEN_128M (0x8 << 4) -#define G33_GMCH_GMS_STOLEN_256M (0x9 << 4) -#define INTEL_GMCH_GMS_STOLEN_96M (0xa << 4) -#define INTEL_GMCH_GMS_STOLEN_160M (0xb << 4) -#define INTEL_GMCH_GMS_STOLEN_224M (0xc << 4) -#define INTEL_GMCH_GMS_STOLEN_352M (0xd << 4) - -#define I915_IFPADDR 0x60 - -/* Intel 965G registers */ -#define I965_MSAC 0x62 -#define I965_IFPADDR 0x70 - -/* Intel 7505 registers */ -#define INTEL_I7505_APSIZE 0x74 -#define INTEL_I7505_NCAPID 0x60 -#define INTEL_I7505_NISTAT 0x6c -#define INTEL_I7505_ATTBASE 0x78 -#define INTEL_I7505_ERRSTS 0x42 -#define INTEL_I7505_AGPCTRL 0x70 -#define INTEL_I7505_MCHCFG 0x50 - -#define SNB_GMCH_CTRL 0x50 -#define SNB_GMCH_GMS_STOLEN_MASK 0xF8 -#define SNB_GMCH_GMS_STOLEN_32M (1 << 3) -#define SNB_GMCH_GMS_STOLEN_64M (2 << 3) -#define SNB_GMCH_GMS_STOLEN_96M (3 << 3) -#define SNB_GMCH_GMS_STOLEN_128M (4 << 3) -#define SNB_GMCH_GMS_STOLEN_160M (5 << 3) -#define SNB_GMCH_GMS_STOLEN_192M (6 << 3) -#define SNB_GMCH_GMS_STOLEN_224M (7 << 3) -#define SNB_GMCH_GMS_STOLEN_256M (8 << 3) -#define SNB_GMCH_GMS_STOLEN_288M (9 << 3) -#define SNB_GMCH_GMS_STOLEN_320M (0xa << 3) -#define SNB_GMCH_GMS_STOLEN_352M (0xb << 3) -#define SNB_GMCH_GMS_STOLEN_384M (0xc << 3) -#define SNB_GMCH_GMS_STOLEN_416M (0xd << 3) -#define SNB_GMCH_GMS_STOLEN_448M (0xe << 3) -#define SNB_GMCH_GMS_STOLEN_480M (0xf << 3) -#define SNB_GMCH_GMS_STOLEN_512M (0x10 << 3) -#define SNB_GTT_SIZE_0M (0 << 8) -#define SNB_GTT_SIZE_1M (1 << 8) -#define SNB_GTT_SIZE_2M (2 << 8) -#define SNB_GTT_SIZE_MASK (3 << 8) - -static const struct aper_size_info_fixed intel_i810_sizes[] = -{ - {64, 16384, 4}, - /* The 32M mode still requires a 64k gatt */ - {32, 8192, 4} -}; - -#define AGP_DCACHE_MEMORY 1 -#define AGP_PHYS_MEMORY 2 -#define INTEL_AGP_CACHED_MEMORY 3 - -static struct gatt_mask intel_i810_masks[] = -{ - {.mask = I810_PTE_VALID, .type = 0}, - {.mask = (I810_PTE_VALID | I810_PTE_LOCAL), .type = AGP_DCACHE_MEMORY}, - {.mask = I810_PTE_VALID, .type = 0}, - {.mask = I810_PTE_VALID | I830_PTE_SYSTEM_CACHED, - .type = INTEL_AGP_CACHED_MEMORY} -}; - -static struct _intel_private { - struct pci_dev *pcidev; /* device one */ - u8 __iomem *registers; - u32 __iomem *gtt; /* I915G */ - int num_dcache_entries; - /* gtt_entries is the number of gtt entries that are already mapped - * to stolen memory. Stolen memory is larger than the memory mapped - * through gtt_entries, as it includes some reserved space for the BIOS - * popup and for the GTT. - */ - int gtt_entries; /* i830+ */ - int gtt_total_size; - union { - void __iomem *i9xx_flush_page; - void *i8xx_flush_page; - }; - struct page *i8xx_page; - struct resource ifp_resource; - int resource_valid; -} intel_private; - -#ifdef USE_PCI_DMA_API -static int intel_agp_map_page(struct page *page, dma_addr_t *ret) -{ - *ret = pci_map_page(intel_private.pcidev, page, 0, - PAGE_SIZE, PCI_DMA_BIDIRECTIONAL); - if (pci_dma_mapping_error(intel_private.pcidev, *ret)) - return -EINVAL; - return 0; -} - -static void intel_agp_unmap_page(struct page *page, dma_addr_t dma) -{ - pci_unmap_page(intel_private.pcidev, dma, - PAGE_SIZE, PCI_DMA_BIDIRECTIONAL); -} - -static void intel_agp_free_sglist(struct agp_memory *mem) -{ - struct sg_table st; - - st.sgl = mem->sg_list; - st.orig_nents = st.nents = mem->page_count; - - sg_free_table(&st); - - mem->sg_list = NULL; - mem->num_sg = 0; -} - -static int intel_agp_map_memory(struct agp_memory *mem) -{ - struct sg_table st; - struct scatterlist *sg; - int i; - - DBG("try mapping %lu pages\n", (unsigned long)mem->page_count); - - if (sg_alloc_table(&st, mem->page_count, GFP_KERNEL)) - return -ENOMEM; - - mem->sg_list = sg = st.sgl; - - for (i = 0 ; i < mem->page_count; i++, sg = sg_next(sg)) - sg_set_page(sg, mem->pages[i], PAGE_SIZE, 0); - - mem->num_sg = pci_map_sg(intel_private.pcidev, mem->sg_list, - mem->page_count, PCI_DMA_BIDIRECTIONAL); - if (unlikely(!mem->num_sg)) { - intel_agp_free_sglist(mem); - return -ENOMEM; - } - return 0; -} - -static void intel_agp_unmap_memory(struct agp_memory *mem) -{ - DBG("try unmapping %lu pages\n", (unsigned long)mem->page_count); - - pci_unmap_sg(intel_private.pcidev, mem->sg_list, - mem->page_count, PCI_DMA_BIDIRECTIONAL); - intel_agp_free_sglist(mem); -} - -static void intel_agp_insert_sg_entries(struct agp_memory *mem, - off_t pg_start, int mask_type) -{ - struct scatterlist *sg; - int i, j; - - j = pg_start; - - WARN_ON(!mem->num_sg); - - if (mem->num_sg == mem->page_count) { - for_each_sg(mem->sg_list, sg, mem->page_count, i) { - writel(agp_bridge->driver->mask_memory(agp_bridge, - sg_dma_address(sg), mask_type), - intel_private.gtt+j); - j++; - } - } else { - /* sg may merge pages, but we have to separate - * per-page addr for GTT */ - unsigned int len, m; - - for_each_sg(mem->sg_list, sg, mem->num_sg, i) { - len = sg_dma_len(sg) / PAGE_SIZE; - for (m = 0; m < len; m++) { - writel(agp_bridge->driver->mask_memory(agp_bridge, - sg_dma_address(sg) + m * PAGE_SIZE, - mask_type), - intel_private.gtt+j); - j++; - } - } - } - readl(intel_private.gtt+j-1); -} - -#else - -static void intel_agp_insert_sg_entries(struct agp_memory *mem, - off_t pg_start, int mask_type) -{ - int i, j; - u32 cache_bits = 0; - - if (agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_SANDYBRIDGE_HB || - agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_SANDYBRIDGE_M_HB) - { - cache_bits = I830_PTE_SYSTEM_CACHED; - } - - for (i = 0, j = pg_start; i < mem->page_count; i++, j++) { - writel(agp_bridge->driver->mask_memory(agp_bridge, - page_to_phys(mem->pages[i]), mask_type), - intel_private.gtt+j); - } - - readl(intel_private.gtt+j-1); -} - -#endif - -static int intel_i810_fetch_size(void) -{ - u32 smram_miscc; - struct aper_size_info_fixed *values; - - pci_read_config_dword(agp_bridge->dev, I810_SMRAM_MISCC, &smram_miscc); - values = A_SIZE_FIX(agp_bridge->driver->aperture_sizes); - - if ((smram_miscc & I810_GMS) == I810_GMS_DISABLE) { - dev_warn(&agp_bridge->dev->dev, "i810 is disabled\n"); - return 0; - } - if ((smram_miscc & I810_GFX_MEM_WIN_SIZE) == I810_GFX_MEM_WIN_32M) { - agp_bridge->previous_size = - agp_bridge->current_size = (void *) (values + 1); - agp_bridge->aperture_size_idx = 1; - return values[1].size; - } else { - agp_bridge->previous_size = - agp_bridge->current_size = (void *) (values); - agp_bridge->aperture_size_idx = 0; - return values[0].size; - } - - return 0; -} - -static int intel_i810_configure(void) -{ - struct aper_size_info_fixed *current_size; - u32 temp; - int i; - - current_size = A_SIZE_FIX(agp_bridge->current_size); - - if (!intel_private.registers) { - pci_read_config_dword(intel_private.pcidev, I810_MMADDR, &temp); - temp &= 0xfff80000; - - intel_private.registers = ioremap(temp, 128 * 4096); - if (!intel_private.registers) { - dev_err(&intel_private.pcidev->dev, - "can't remap memory\n"); - return -ENOMEM; - } - } - - if ((readl(intel_private.registers+I810_DRAM_CTL) - & I810_DRAM_ROW_0) == I810_DRAM_ROW_0_SDRAM) { - /* This will need to be dynamically assigned */ - dev_info(&intel_private.pcidev->dev, - "detected 4MB dedicated video ram\n"); - intel_private.num_dcache_entries = 1024; - } - pci_read_config_dword(intel_private.pcidev, I810_GMADDR, &temp); - agp_bridge->gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK); - writel(agp_bridge->gatt_bus_addr | I810_PGETBL_ENABLED, intel_private.registers+I810_PGETBL_CTL); - readl(intel_private.registers+I810_PGETBL_CTL); /* PCI Posting. */ - - if (agp_bridge->driver->needs_scratch_page) { - for (i = 0; i < current_size->num_entries; i++) { - writel(agp_bridge->scratch_page, intel_private.registers+I810_PTE_BASE+(i*4)); - } - readl(intel_private.registers+I810_PTE_BASE+((i-1)*4)); /* PCI posting. */ - } - global_cache_flush(); - return 0; -} - -static void intel_i810_cleanup(void) -{ - writel(0, intel_private.registers+I810_PGETBL_CTL); - readl(intel_private.registers); /* PCI Posting. */ - iounmap(intel_private.registers); -} - -static void intel_i810_tlbflush(struct agp_memory *mem) -{ - return; -} - -static void intel_i810_agp_enable(struct agp_bridge_data *bridge, u32 mode) -{ - return; -} - -/* Exists to support ARGB cursors */ -static struct page *i8xx_alloc_pages(void) -{ - struct page *page; - - page = alloc_pages(GFP_KERNEL | GFP_DMA32, 2); - if (page == NULL) - return NULL; - - if (set_pages_uc(page, 4) < 0) { - set_pages_wb(page, 4); - __free_pages(page, 2); - return NULL; - } - get_page(page); - atomic_inc(&agp_bridge->current_memory_agp); - return page; -} - -static void i8xx_destroy_pages(struct page *page) -{ - if (page == NULL) - return; - - set_pages_wb(page, 4); - put_page(page); - __free_pages(page, 2); - atomic_dec(&agp_bridge->current_memory_agp); -} - -static int intel_i830_type_to_mask_type(struct agp_bridge_data *bridge, - int type) -{ - if (type < AGP_USER_TYPES) - return type; - else if (type == AGP_USER_CACHED_MEMORY) - return INTEL_AGP_CACHED_MEMORY; - else - return 0; -} - -static int intel_i810_insert_entries(struct agp_memory *mem, off_t pg_start, - int type) -{ - int i, j, num_entries; - void *temp; - int ret = -EINVAL; - int mask_type; - - if (mem->page_count == 0) - goto out; - - temp = agp_bridge->current_size; - num_entries = A_SIZE_FIX(temp)->num_entries; - - if ((pg_start + mem->page_count) > num_entries) - goto out_err; - - - for (j = pg_start; j < (pg_start + mem->page_count); j++) { - if (!PGE_EMPTY(agp_bridge, readl(agp_bridge->gatt_table+j))) { - ret = -EBUSY; - goto out_err; - } - } - - if (type != mem->type) - goto out_err; - - mask_type = agp_bridge->driver->agp_type_to_mask_type(agp_bridge, type); - - switch (mask_type) { - case AGP_DCACHE_MEMORY: - if (!mem->is_flushed) - global_cache_flush(); - for (i = pg_start; i < (pg_start + mem->page_count); i++) { - writel((i*4096)|I810_PTE_LOCAL|I810_PTE_VALID, - intel_private.registers+I810_PTE_BASE+(i*4)); - } - readl(intel_private.registers+I810_PTE_BASE+((i-1)*4)); - break; - case AGP_PHYS_MEMORY: - case AGP_NORMAL_MEMORY: - if (!mem->is_flushed) - global_cache_flush(); - for (i = 0, j = pg_start; i < mem->page_count; i++, j++) { - writel(agp_bridge->driver->mask_memory(agp_bridge, - page_to_phys(mem->pages[i]), mask_type), - intel_private.registers+I810_PTE_BASE+(j*4)); - } - readl(intel_private.registers+I810_PTE_BASE+((j-1)*4)); - break; - default: - goto out_err; - } - - agp_bridge->driver->tlb_flush(mem); -out: - ret = 0; -out_err: - mem->is_flushed = true; - return ret; -} - -static int intel_i810_remove_entries(struct agp_memory *mem, off_t pg_start, - int type) -{ - int i; - - if (mem->page_count == 0) - return 0; - - for (i = pg_start; i < (mem->page_count + pg_start); i++) { - writel(agp_bridge->scratch_page, intel_private.registers+I810_PTE_BASE+(i*4)); - } - readl(intel_private.registers+I810_PTE_BASE+((i-1)*4)); - - agp_bridge->driver->tlb_flush(mem); - return 0; -} - -/* - * The i810/i830 requires a physical address to program its mouse - * pointer into hardware. - * However the Xserver still writes to it through the agp aperture. - */ -static struct agp_memory *alloc_agpphysmem_i8xx(size_t pg_count, int type) -{ - struct agp_memory *new; - struct page *page; - - switch (pg_count) { - case 1: page = agp_bridge->driver->agp_alloc_page(agp_bridge); - break; - case 4: - /* kludge to get 4 physical pages for ARGB cursor */ - page = i8xx_alloc_pages(); - break; - default: - return NULL; - } - - if (page == NULL) - return NULL; - - new = agp_create_memory(pg_count); - if (new == NULL) - return NULL; - - new->pages[0] = page; - if (pg_count == 4) { - /* kludge to get 4 physical pages for ARGB cursor */ - new->pages[1] = new->pages[0] + 1; - new->pages[2] = new->pages[1] + 1; - new->pages[3] = new->pages[2] + 1; - } - new->page_count = pg_count; - new->num_scratch_pages = pg_count; - new->type = AGP_PHYS_MEMORY; - new->physical = page_to_phys(new->pages[0]); - return new; -} - -static struct agp_memory *intel_i810_alloc_by_type(size_t pg_count, int type) -{ - struct agp_memory *new; - - if (type == AGP_DCACHE_MEMORY) { - if (pg_count != intel_private.num_dcache_entries) - return NULL; - - new = agp_create_memory(1); - if (new == NULL) - return NULL; - - new->type = AGP_DCACHE_MEMORY; - new->page_count = pg_count; - new->num_scratch_pages = 0; - agp_free_page_array(new); - return new; - } - if (type == AGP_PHYS_MEMORY) - return alloc_agpphysmem_i8xx(pg_count, type); - return NULL; -} - -static void intel_i810_free_by_type(struct agp_memory *curr) -{ - agp_free_key(curr->key); - if (curr->type == AGP_PHYS_MEMORY) { - if (curr->page_count == 4) - i8xx_destroy_pages(curr->pages[0]); - else { - agp_bridge->driver->agp_destroy_page(curr->pages[0], - AGP_PAGE_DESTROY_UNMAP); - agp_bridge->driver->agp_destroy_page(curr->pages[0], - AGP_PAGE_DESTROY_FREE); - } - agp_free_page_array(curr); - } - kfree(curr); -} - -static unsigned long intel_i810_mask_memory(struct agp_bridge_data *bridge, - dma_addr_t addr, int type) -{ - /* Type checking must be done elsewhere */ - return addr | bridge->driver->masks[type].mask; -} - -static struct aper_size_info_fixed intel_i830_sizes[] = -{ - {128, 32768, 5}, - /* The 64M mode still requires a 128k gatt */ - {64, 16384, 5}, - {256, 65536, 6}, - {512, 131072, 7}, -}; - -static void intel_i830_init_gtt_entries(void) -{ - u16 gmch_ctrl; - int gtt_entries = 0; - u8 rdct; - int local = 0; - static const int ddt[4] = { 0, 16, 32, 64 }; - int size; /* reserved space (in kb) at the top of stolen memory */ - - pci_read_config_word(agp_bridge->dev, I830_GMCH_CTRL, &gmch_ctrl); - - if (IS_I965) { - u32 pgetbl_ctl; - pgetbl_ctl = readl(intel_private.registers+I810_PGETBL_CTL); - - /* The 965 has a field telling us the size of the GTT, - * which may be larger than what is necessary to map the - * aperture. - */ - switch (pgetbl_ctl & I965_PGETBL_SIZE_MASK) { - case I965_PGETBL_SIZE_128KB: - size = 128; - break; - case I965_PGETBL_SIZE_256KB: - size = 256; - break; - case I965_PGETBL_SIZE_512KB: - size = 512; - break; - case I965_PGETBL_SIZE_1MB: - size = 1024; - break; - case I965_PGETBL_SIZE_2MB: - size = 2048; - break; - case I965_PGETBL_SIZE_1_5MB: - size = 1024 + 512; - break; - default: - dev_info(&intel_private.pcidev->dev, - "unknown page table size, assuming 512KB\n"); - size = 512; - } - size += 4; /* add in BIOS popup space */ - } else if (IS_G33 && !IS_PINEVIEW) { - /* G33's GTT size defined in gmch_ctrl */ - switch (gmch_ctrl & G33_PGETBL_SIZE_MASK) { - case G33_PGETBL_SIZE_1M: - size = 1024; - break; - case G33_PGETBL_SIZE_2M: - size = 2048; - break; - default: - dev_info(&agp_bridge->dev->dev, - "unknown page table size 0x%x, assuming 512KB\n", - (gmch_ctrl & G33_PGETBL_SIZE_MASK)); - size = 512; - } - size += 4; - } else if (IS_G4X || IS_PINEVIEW) { - /* On 4 series hardware, GTT stolen is separate from graphics - * stolen, ignore it in stolen gtt entries counting. However, - * 4KB of the stolen memory doesn't get mapped to the GTT. - */ - size = 4; - } else { - /* On previous hardware, the GTT size was just what was - * required to map the aperture. - */ - 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) { - switch (gmch_ctrl & I830_GMCH_GMS_MASK) { - case I830_GMCH_GMS_STOLEN_512: - gtt_entries = KB(512) - KB(size); - break; - case I830_GMCH_GMS_STOLEN_1024: - gtt_entries = MB(1) - KB(size); - break; - case I830_GMCH_GMS_STOLEN_8192: - gtt_entries = MB(8) - KB(size); - break; - case I830_GMCH_GMS_LOCAL: - rdct = readb(intel_private.registers+I830_RDRAM_CHANNEL_TYPE); - gtt_entries = (I830_RDRAM_ND(rdct) + 1) * - MB(ddt[I830_RDRAM_DDT(rdct)]); - local = 1; - break; - default: - gtt_entries = 0; - break; - } - } else if (agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_SANDYBRIDGE_HB || - agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_SANDYBRIDGE_M_HB) { - /* - * SandyBridge has new memory control reg at 0x50.w - */ - u16 snb_gmch_ctl; - pci_read_config_word(intel_private.pcidev, SNB_GMCH_CTRL, &snb_gmch_ctl); - switch (snb_gmch_ctl & SNB_GMCH_GMS_STOLEN_MASK) { - case SNB_GMCH_GMS_STOLEN_32M: - gtt_entries = MB(32) - KB(size); - break; - case SNB_GMCH_GMS_STOLEN_64M: - gtt_entries = MB(64) - KB(size); - break; - case SNB_GMCH_GMS_STOLEN_96M: - gtt_entries = MB(96) - KB(size); - break; - case SNB_GMCH_GMS_STOLEN_128M: - gtt_entries = MB(128) - KB(size); - break; - case SNB_GMCH_GMS_STOLEN_160M: - gtt_entries = MB(160) - KB(size); - break; - case SNB_GMCH_GMS_STOLEN_192M: - gtt_entries = MB(192) - KB(size); - break; - case SNB_GMCH_GMS_STOLEN_224M: - gtt_entries = MB(224) - KB(size); - break; - case SNB_GMCH_GMS_STOLEN_256M: - gtt_entries = MB(256) - KB(size); - break; - case SNB_GMCH_GMS_STOLEN_288M: - gtt_entries = MB(288) - KB(size); - break; - case SNB_GMCH_GMS_STOLEN_320M: - gtt_entries = MB(320) - KB(size); - break; - case SNB_GMCH_GMS_STOLEN_352M: - gtt_entries = MB(352) - KB(size); - break; - case SNB_GMCH_GMS_STOLEN_384M: - gtt_entries = MB(384) - KB(size); - break; - case SNB_GMCH_GMS_STOLEN_416M: - gtt_entries = MB(416) - KB(size); - break; - case SNB_GMCH_GMS_STOLEN_448M: - gtt_entries = MB(448) - KB(size); - break; - case SNB_GMCH_GMS_STOLEN_480M: - gtt_entries = MB(480) - KB(size); - break; - case SNB_GMCH_GMS_STOLEN_512M: - gtt_entries = MB(512) - KB(size); - break; - } - } else { - switch (gmch_ctrl & I855_GMCH_GMS_MASK) { - case I855_GMCH_GMS_STOLEN_1M: - gtt_entries = MB(1) - KB(size); - break; - case I855_GMCH_GMS_STOLEN_4M: - gtt_entries = MB(4) - KB(size); - break; - case I855_GMCH_GMS_STOLEN_8M: - gtt_entries = MB(8) - KB(size); - break; - case I855_GMCH_GMS_STOLEN_16M: - gtt_entries = MB(16) - KB(size); - break; - case I855_GMCH_GMS_STOLEN_32M: - gtt_entries = MB(32) - KB(size); - break; - case I915_GMCH_GMS_STOLEN_48M: - /* Check it's really I915G */ - if (IS_I915 || IS_I965 || IS_G33 || IS_G4X) - gtt_entries = MB(48) - KB(size); - else - gtt_entries = 0; - break; - case I915_GMCH_GMS_STOLEN_64M: - /* Check it's really I915G */ - if (IS_I915 || IS_I965 || IS_G33 || IS_G4X) - gtt_entries = MB(64) - KB(size); - else - gtt_entries = 0; - break; - case G33_GMCH_GMS_STOLEN_128M: - if (IS_G33 || IS_I965 || IS_G4X) - gtt_entries = MB(128) - KB(size); - else - gtt_entries = 0; - break; - case G33_GMCH_GMS_STOLEN_256M: - if (IS_G33 || IS_I965 || IS_G4X) - gtt_entries = MB(256) - KB(size); - else - gtt_entries = 0; - break; - case INTEL_GMCH_GMS_STOLEN_96M: - if (IS_I965 || IS_G4X) - gtt_entries = MB(96) - KB(size); - else - gtt_entries = 0; - break; - case INTEL_GMCH_GMS_STOLEN_160M: - if (IS_I965 || IS_G4X) - gtt_entries = MB(160) - KB(size); - else - gtt_entries = 0; - break; - case INTEL_GMCH_GMS_STOLEN_224M: - if (IS_I965 || IS_G4X) - gtt_entries = MB(224) - KB(size); - else - gtt_entries = 0; - break; - case INTEL_GMCH_GMS_STOLEN_352M: - if (IS_I965 || IS_G4X) - gtt_entries = MB(352) - KB(size); - else - gtt_entries = 0; - break; - default: - gtt_entries = 0; - break; - } - } - if (gtt_entries > 0) { - dev_info(&agp_bridge->dev->dev, "detected %dK %s memory\n", - gtt_entries / KB(1), local ? "local" : "stolen"); - gtt_entries /= KB(4); - } else { - dev_info(&agp_bridge->dev->dev, - "no pre-allocated video memory detected\n"); - gtt_entries = 0; - } - - intel_private.gtt_entries = gtt_entries; -} - -static void intel_i830_fini_flush(void) -{ - kunmap(intel_private.i8xx_page); - intel_private.i8xx_flush_page = NULL; - unmap_page_from_agp(intel_private.i8xx_page); - - __free_page(intel_private.i8xx_page); - intel_private.i8xx_page = NULL; -} - -static void intel_i830_setup_flush(void) -{ - /* return if we've already set the flush mechanism up */ - if (intel_private.i8xx_page) - return; - - intel_private.i8xx_page = alloc_page(GFP_KERNEL | __GFP_ZERO | GFP_DMA32); - if (!intel_private.i8xx_page) - return; - - intel_private.i8xx_flush_page = kmap(intel_private.i8xx_page); - if (!intel_private.i8xx_flush_page) - intel_i830_fini_flush(); -} - -/* The chipset_flush interface needs to get data that has already been - * flushed out of the CPU all the way out to main memory, because the GPU - * doesn't snoop those buffers. - * - * The 8xx series doesn't have the same lovely interface for flushing the - * chipset write buffers that the later chips do. According to the 865 - * specs, it's 64 octwords, or 1KB. So, to get those previous things in - * that buffer out, we just fill 1KB and clflush it out, on the assumption - * that it'll push whatever was in there out. It appears to work. - */ -static void intel_i830_chipset_flush(struct agp_bridge_data *bridge) -{ - unsigned int *pg = intel_private.i8xx_flush_page; - - memset(pg, 0, 1024); - - if (cpu_has_clflush) - clflush_cache_range(pg, 1024); - else if (wbinvd_on_all_cpus() != 0) - printk(KERN_ERR "Timed out waiting for cache flush.\n"); -} - -/* The intel i830 automatically initializes the agp aperture during POST. - * Use the memory already set aside for in the GTT. - */ -static int intel_i830_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_private.pcidev, I810_MMADDR, &temp); - temp &= 0xfff80000; - - intel_private.registers = ioremap(temp, 128 * 4096); - if (!intel_private.registers) - return -ENOMEM; - - temp = readl(intel_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; -} - -/* Return the gatt table to a sane state. Use the top of stolen - * memory for the GTT. - */ -static int intel_i830_free_gatt_table(struct agp_bridge_data *bridge) -{ - return 0; -} - -static int intel_i830_fetch_size(void) -{ - u16 gmch_ctrl; - struct aper_size_info_fixed *values; - - values = A_SIZE_FIX(agp_bridge->driver->aperture_sizes); - - if (agp_bridge->dev->device != PCI_DEVICE_ID_INTEL_82830_HB && - agp_bridge->dev->device != PCI_DEVICE_ID_INTEL_82845G_HB) { - /* 855GM/852GM/865G has 128MB aperture size */ - agp_bridge->previous_size = agp_bridge->current_size = (void *) values; - agp_bridge->aperture_size_idx = 0; - return values[0].size; - } - - pci_read_config_word(agp_bridge->dev, I830_GMCH_CTRL, &gmch_ctrl); - - if ((gmch_ctrl & I830_GMCH_MEM_MASK) == I830_GMCH_MEM_128M) { - agp_bridge->previous_size = agp_bridge->current_size = (void *) values; - agp_bridge->aperture_size_idx = 0; - return values[0].size; - } else { - agp_bridge->previous_size = agp_bridge->current_size = (void *) (values + 1); - agp_bridge->aperture_size_idx = 1; - return values[1].size; - } - - return 0; -} - -static int intel_i830_configure(void) -{ - struct aper_size_info_fixed *current_size; - u32 temp; - u16 gmch_ctrl; - int i; - - current_size = A_SIZE_FIX(agp_bridge->current_size); - - pci_read_config_dword(intel_private.pcidev, I810_GMADDR, &temp); - agp_bridge->gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK); - - pci_read_config_word(agp_bridge->dev, I830_GMCH_CTRL, &gmch_ctrl); - gmch_ctrl |= I830_GMCH_ENABLED; - pci_write_config_word(agp_bridge->dev, I830_GMCH_CTRL, gmch_ctrl); - - writel(agp_bridge->gatt_bus_addr|I810_PGETBL_ENABLED, intel_private.registers+I810_PGETBL_CTL); - readl(intel_private.registers+I810_PGETBL_CTL); /* PCI Posting. */ - - if (agp_bridge->driver->needs_scratch_page) { - for (i = intel_private.gtt_entries; i < current_size->num_entries; i++) { - writel(agp_bridge->scratch_page, intel_private.registers+I810_PTE_BASE+(i*4)); - } - readl(intel_private.registers+I810_PTE_BASE+((i-1)*4)); /* PCI Posting. */ - } - - global_cache_flush(); - - intel_i830_setup_flush(); - return 0; -} - -static void intel_i830_cleanup(void) -{ - iounmap(intel_private.registers); -} - -static int intel_i830_insert_entries(struct agp_memory *mem, off_t pg_start, - int type) -{ - int i, j, num_entries; - void *temp; - int ret = -EINVAL; - int mask_type; - - if (mem->page_count == 0) - goto out; - - temp = agp_bridge->current_size; - num_entries = A_SIZE_FIX(temp)->num_entries; - - if (pg_start < intel_private.gtt_entries) { - dev_printk(KERN_DEBUG, &intel_private.pcidev->dev, - "pg_start == 0x%.8lx, intel_private.gtt_entries == 0x%.8x\n", - pg_start, intel_private.gtt_entries); - - dev_info(&intel_private.pcidev->dev, - "trying to insert into local/stolen memory\n"); - goto out_err; - } - - if ((pg_start + mem->page_count) > num_entries) - goto out_err; - - /* The i830 can't check the GTT for entries since its read only, - * depend on the caller to make the correct offset decisions. - */ - - if (type != mem->type) - goto out_err; - - mask_type = agp_bridge->driver->agp_type_to_mask_type(agp_bridge, type); - - if (mask_type != 0 && mask_type != AGP_PHYS_MEMORY && - mask_type != INTEL_AGP_CACHED_MEMORY) - goto out_err; - - if (!mem->is_flushed) - global_cache_flush(); - - for (i = 0, j = pg_start; i < mem->page_count; i++, j++) { - writel(agp_bridge->driver->mask_memory(agp_bridge, - page_to_phys(mem->pages[i]), mask_type), - intel_private.registers+I810_PTE_BASE+(j*4)); - } - readl(intel_private.registers+I810_PTE_BASE+((j-1)*4)); - agp_bridge->driver->tlb_flush(mem); - -out: - ret = 0; -out_err: - mem->is_flushed = true; - return ret; -} - -static int intel_i830_remove_entries(struct agp_memory *mem, off_t pg_start, - int type) -{ - int i; - - if (mem->page_count == 0) - return 0; - - if (pg_start < intel_private.gtt_entries) { - dev_info(&intel_private.pcidev->dev, - "trying to disable local/stolen memory\n"); - return -EINVAL; - } - - for (i = pg_start; i < (mem->page_count + pg_start); i++) { - writel(agp_bridge->scratch_page, intel_private.registers+I810_PTE_BASE+(i*4)); - } - readl(intel_private.registers+I810_PTE_BASE+((i-1)*4)); - - agp_bridge->driver->tlb_flush(mem); - return 0; -} - -static struct agp_memory *intel_i830_alloc_by_type(size_t pg_count, int type) -{ - if (type == AGP_PHYS_MEMORY) - return alloc_agpphysmem_i8xx(pg_count, type); - /* always return NULL for other allocation types for now */ - return NULL; -} - -static int intel_alloc_chipset_flush_resource(void) -{ - int ret; - ret = pci_bus_alloc_resource(agp_bridge->dev->bus, &intel_private.ifp_resource, PAGE_SIZE, - PAGE_SIZE, PCIBIOS_MIN_MEM, 0, - pcibios_align_resource, agp_bridge->dev); - - return ret; -} - -static void intel_i915_setup_chipset_flush(void) -{ - int ret; - u32 temp; - - pci_read_config_dword(agp_bridge->dev, I915_IFPADDR, &temp); - if (!(temp & 0x1)) { - intel_alloc_chipset_flush_resource(); - intel_private.resource_valid = 1; - pci_write_config_dword(agp_bridge->dev, I915_IFPADDR, (intel_private.ifp_resource.start & 0xffffffff) | 0x1); - } else { - temp &= ~1; - - intel_private.resource_valid = 1; - intel_private.ifp_resource.start = temp; - intel_private.ifp_resource.end = temp + PAGE_SIZE; - ret = request_resource(&iomem_resource, &intel_private.ifp_resource); - /* some BIOSes reserve this area in a pnp some don't */ - if (ret) - intel_private.resource_valid = 0; - } -} - -static void intel_i965_g33_setup_chipset_flush(void) -{ - u32 temp_hi, temp_lo; - int ret; - - pci_read_config_dword(agp_bridge->dev, I965_IFPADDR + 4, &temp_hi); - pci_read_config_dword(agp_bridge->dev, I965_IFPADDR, &temp_lo); - - if (!(temp_lo & 0x1)) { - - intel_alloc_chipset_flush_resource(); - - intel_private.resource_valid = 1; - pci_write_config_dword(agp_bridge->dev, I965_IFPADDR + 4, - upper_32_bits(intel_private.ifp_resource.start)); - pci_write_config_dword(agp_bridge->dev, I965_IFPADDR, (intel_private.ifp_resource.start & 0xffffffff) | 0x1); - } else { - u64 l64; - - temp_lo &= ~0x1; - l64 = ((u64)temp_hi << 32) | temp_lo; - - intel_private.resource_valid = 1; - intel_private.ifp_resource.start = l64; - intel_private.ifp_resource.end = l64 + PAGE_SIZE; - ret = request_resource(&iomem_resource, &intel_private.ifp_resource); - /* some BIOSes reserve this area in a pnp some don't */ - if (ret) - intel_private.resource_valid = 0; - } -} - -static void intel_i9xx_setup_flush(void) -{ - /* return if already configured */ - if (intel_private.ifp_resource.start) - return; - - if (IS_SNB) - return; - - /* setup a resource for this object */ - intel_private.ifp_resource.name = "Intel Flush Page"; - intel_private.ifp_resource.flags = IORESOURCE_MEM; - - /* Setup chipset flush for 915 */ - if (IS_I965 || IS_G33 || IS_G4X) { - intel_i965_g33_setup_chipset_flush(); - } else { - intel_i915_setup_chipset_flush(); - } - - if (intel_private.ifp_resource.start) { - intel_private.i9xx_flush_page = ioremap_nocache(intel_private.ifp_resource.start, PAGE_SIZE); - if (!intel_private.i9xx_flush_page) - dev_info(&intel_private.pcidev->dev, "can't ioremap flush page - no chipset flushing"); - } -} - -static int intel_i915_configure(void) -{ - struct aper_size_info_fixed *current_size; - u32 temp; - u16 gmch_ctrl; - int i; - - current_size = A_SIZE_FIX(agp_bridge->current_size); - - pci_read_config_dword(intel_private.pcidev, I915_GMADDR, &temp); - - agp_bridge->gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK); - - pci_read_config_word(agp_bridge->dev, I830_GMCH_CTRL, &gmch_ctrl); - gmch_ctrl |= I830_GMCH_ENABLED; - pci_write_config_word(agp_bridge->dev, I830_GMCH_CTRL, gmch_ctrl); - - writel(agp_bridge->gatt_bus_addr|I810_PGETBL_ENABLED, intel_private.registers+I810_PGETBL_CTL); - readl(intel_private.registers+I810_PGETBL_CTL); /* PCI Posting. */ - - if (agp_bridge->driver->needs_scratch_page) { - for (i = intel_private.gtt_entries; i < intel_private.gtt_total_size; i++) { - writel(agp_bridge->scratch_page, intel_private.gtt+i); - } - readl(intel_private.gtt+i-1); /* PCI Posting. */ - } - - global_cache_flush(); - - intel_i9xx_setup_flush(); - - return 0; -} - -static void intel_i915_cleanup(void) -{ - if (intel_private.i9xx_flush_page) - iounmap(intel_private.i9xx_flush_page); - if (intel_private.resource_valid) - release_resource(&intel_private.ifp_resource); - intel_private.ifp_resource.start = 0; - intel_private.resource_valid = 0; - iounmap(intel_private.gtt); - iounmap(intel_private.registers); -} - -static void intel_i915_chipset_flush(struct agp_bridge_data *bridge) -{ - if (intel_private.i9xx_flush_page) - writel(1, intel_private.i9xx_flush_page); -} - -static int intel_i915_insert_entries(struct agp_memory *mem, off_t pg_start, - int type) -{ - int num_entries; - void *temp; - int ret = -EINVAL; - int mask_type; - - if (mem->page_count == 0) - goto out; - - temp = agp_bridge->current_size; - num_entries = A_SIZE_FIX(temp)->num_entries; - - if (pg_start < intel_private.gtt_entries) { - dev_printk(KERN_DEBUG, &intel_private.pcidev->dev, - "pg_start == 0x%.8lx, intel_private.gtt_entries == 0x%.8x\n", - pg_start, intel_private.gtt_entries); - - dev_info(&intel_private.pcidev->dev, - "trying to insert into local/stolen memory\n"); - goto out_err; - } - - if ((pg_start + mem->page_count) > num_entries) - goto out_err; - - /* The i915 can't check the GTT for entries since it's read only; - * depend on the caller to make the correct offset decisions. - */ - - if (type != mem->type) - goto out_err; - - mask_type = agp_bridge->driver->agp_type_to_mask_type(agp_bridge, type); - - if (mask_type != 0 && mask_type != AGP_PHYS_MEMORY && - mask_type != INTEL_AGP_CACHED_MEMORY) - goto out_err; - - if (!mem->is_flushed) - global_cache_flush(); - - intel_agp_insert_sg_entries(mem, pg_start, mask_type); - agp_bridge->driver->tlb_flush(mem); - - out: - ret = 0; - out_err: - mem->is_flushed = true; - return ret; -} - -static int intel_i915_remove_entries(struct agp_memory *mem, off_t pg_start, - int type) -{ - int i; - - if (mem->page_count == 0) - return 0; - - if (pg_start < intel_private.gtt_entries) { - dev_info(&intel_private.pcidev->dev, - "trying to disable local/stolen memory\n"); - return -EINVAL; - } - - for (i = pg_start; i < (mem->page_count + pg_start); i++) - writel(agp_bridge->scratch_page, intel_private.gtt+i); - - readl(intel_private.gtt+i-1); - - agp_bridge->driver->tlb_flush(mem); - return 0; -} - -/* Return the aperture size by just checking the resource length. The effect - * described in the spec of the MSAC registers is just changing of the - * resource size. - */ -static int intel_i9xx_fetch_size(void) -{ - int num_sizes = ARRAY_SIZE(intel_i830_sizes); - int aper_size; /* size in megabytes */ - int i; - - aper_size = pci_resource_len(intel_private.pcidev, 2) / MB(1); - - for (i = 0; i < num_sizes; i++) { - if (aper_size == intel_i830_sizes[i].size) { - agp_bridge->current_size = intel_i830_sizes + i; - agp_bridge->previous_size = agp_bridge->current_size; - return aper_size; - } - } - - return 0; -} - -/* The intel i915 automatically initializes the agp aperture during POST. - * Use the memory already set aside for in the GTT. - */ -static int intel_i915_create_gatt_table(struct agp_bridge_data *bridge) -{ - int page_order; - struct aper_size_info_fixed *size; - int num_entries; - u32 temp, temp2; - int gtt_map_size = 256 * 1024; - - 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_private.pcidev, I915_MMADDR, &temp); - pci_read_config_dword(intel_private.pcidev, I915_PTEADDR, &temp2); - - if (IS_G33) - gtt_map_size = 1024 * 1024; /* 1M on G33 */ - intel_private.gtt = ioremap(temp2, gtt_map_size); - if (!intel_private.gtt) - return -ENOMEM; - - intel_private.gtt_total_size = gtt_map_size / 4; - - temp &= 0xfff80000; - - intel_private.registers = ioremap(temp, 128 * 4096); - if (!intel_private.registers) { - iounmap(intel_private.gtt); - return -ENOMEM; - } - - temp = readl(intel_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; -} - -/* - * The i965 supports 36-bit physical addresses, but to keep - * the format of the GTT the same, the bits that don't fit - * in a 32-bit word are shifted down to bits 4..7. - * - * Gcc is smart enough to notice that "(addr >> 28) & 0xf0" - * is always zero on 32-bit architectures, so no need to make - * this conditional. - */ -static unsigned long intel_i965_mask_memory(struct agp_bridge_data *bridge, - dma_addr_t addr, int type) -{ - /* Shift high bits down */ - addr |= (addr >> 28) & 0xf0; - - /* Type checking must be done elsewhere */ - return addr | bridge->driver->masks[type].mask; -} - -static void intel_i965_get_gtt_range(int *gtt_offset, int *gtt_size) -{ - u16 snb_gmch_ctl; - - switch (agp_bridge->dev->device) { - case PCI_DEVICE_ID_INTEL_GM45_HB: - case PCI_DEVICE_ID_INTEL_EAGLELAKE_HB: - case PCI_DEVICE_ID_INTEL_Q45_HB: - case PCI_DEVICE_ID_INTEL_G45_HB: - case PCI_DEVICE_ID_INTEL_G41_HB: - case PCI_DEVICE_ID_INTEL_B43_HB: - case PCI_DEVICE_ID_INTEL_IRONLAKE_D_HB: - case PCI_DEVICE_ID_INTEL_IRONLAKE_M_HB: - case PCI_DEVICE_ID_INTEL_IRONLAKE_MA_HB: - case PCI_DEVICE_ID_INTEL_IRONLAKE_MC2_HB: - *gtt_offset = *gtt_size = MB(2); - break; - case PCI_DEVICE_ID_INTEL_SANDYBRIDGE_HB: - case PCI_DEVICE_ID_INTEL_SANDYBRIDGE_M_HB: - *gtt_offset = MB(2); - - pci_read_config_word(intel_private.pcidev, SNB_GMCH_CTRL, &snb_gmch_ctl); - switch (snb_gmch_ctl & SNB_GTT_SIZE_MASK) { - default: - case SNB_GTT_SIZE_0M: - printk(KERN_ERR "Bad GTT size mask: 0x%04x.\n", snb_gmch_ctl); - *gtt_size = MB(0); - break; - case SNB_GTT_SIZE_1M: - *gtt_size = MB(1); - break; - case SNB_GTT_SIZE_2M: - *gtt_size = MB(2); - break; - } - break; - default: - *gtt_offset = *gtt_size = KB(512); - } -} - -/* 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; - int gtt_offset, gtt_size; - - 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_private.pcidev, I915_MMADDR, &temp); - - temp &= 0xfff00000; - - intel_i965_get_gtt_range(>t_offset, >t_size); - - intel_private.gtt = ioremap((temp + gtt_offset) , gtt_size); - - if (!intel_private.gtt) - return -ENOMEM; - - intel_private.gtt_total_size = gtt_size / 4; - - intel_private.registers = ioremap(temp, 128 * 4096); - if (!intel_private.registers) { - iounmap(intel_private.gtt); - return -ENOMEM; - } - - temp = readl(intel_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) { int i; @@ -1982,6 +464,7 @@ static const struct agp_bridge_driver intel_generic_driver = { .aperture_sizes = intel_generic_sizes, .size_type = U16_APER_SIZE, .num_aperture_sizes = 7, + .needs_scratch_page = true, .configure = intel_configure, .fetch_size = intel_fetch_size, .cleanup = intel_cleanup, @@ -2003,38 +486,12 @@ static const struct agp_bridge_driver intel_generic_driver = { .agp_type_to_mask_type = agp_generic_type_to_mask_type, }; -static const struct agp_bridge_driver intel_810_driver = { - .owner = THIS_MODULE, - .aperture_sizes = intel_i810_sizes, - .size_type = FIXED_APER_SIZE, - .num_aperture_sizes = 2, - .needs_scratch_page = true, - .configure = intel_i810_configure, - .fetch_size = intel_i810_fetch_size, - .cleanup = intel_i810_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 = agp_generic_create_gatt_table, - .free_gatt_table = agp_generic_free_gatt_table, - .insert_memory = intel_i810_insert_entries, - .remove_memory = intel_i810_remove_entries, - .alloc_by_type = intel_i810_alloc_by_type, - .free_by_type = intel_i810_free_by_type, - .agp_alloc_page = agp_generic_alloc_page, - .agp_alloc_pages = agp_generic_alloc_pages, - .agp_destroy_page = agp_generic_destroy_page, - .agp_destroy_pages = agp_generic_destroy_pages, - .agp_type_to_mask_type = agp_generic_type_to_mask_type, -}; - static const struct agp_bridge_driver intel_815_driver = { .owner = THIS_MODULE, .aperture_sizes = intel_815_sizes, .size_type = U8_APER_SIZE, .num_aperture_sizes = 2, + .needs_scratch_page = true, .configure = intel_815_configure, .fetch_size = intel_815_fetch_size, .cleanup = intel_8xx_cleanup, @@ -2056,39 +513,12 @@ static const struct agp_bridge_driver intel_815_driver = { .agp_type_to_mask_type = agp_generic_type_to_mask_type, }; -static const struct agp_bridge_driver intel_830_driver = { - .owner = THIS_MODULE, - .aperture_sizes = intel_i830_sizes, - .size_type = FIXED_APER_SIZE, - .num_aperture_sizes = 4, - .needs_scratch_page = true, - .configure = intel_i830_configure, - .fetch_size = intel_i830_fetch_size, - .cleanup = intel_i830_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_i830_create_gatt_table, - .free_gatt_table = intel_i830_free_gatt_table, - .insert_memory = intel_i830_insert_entries, - .remove_memory = intel_i830_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_alloc_pages = agp_generic_alloc_pages, - .agp_destroy_page = agp_generic_destroy_page, - .agp_destroy_pages = agp_generic_destroy_pages, - .agp_type_to_mask_type = intel_i830_type_to_mask_type, - .chipset_flush = intel_i830_chipset_flush, -}; - static const struct agp_bridge_driver intel_820_driver = { .owner = THIS_MODULE, .aperture_sizes = intel_8xx_sizes, .size_type = U8_APER_SIZE, .num_aperture_sizes = 7, + .needs_scratch_page = true, .configure = intel_820_configure, .fetch_size = intel_8xx_fetch_size, .cleanup = intel_820_cleanup, @@ -2115,6 +545,7 @@ static const struct agp_bridge_driver intel_830mp_driver = { .aperture_sizes = intel_830mp_sizes, .size_type = U8_APER_SIZE, .num_aperture_sizes = 4, + .needs_scratch_page = true, .configure = intel_830mp_configure, .fetch_size = intel_8xx_fetch_size, .cleanup = intel_8xx_cleanup, @@ -2141,6 +572,7 @@ static const struct agp_bridge_driver intel_840_driver = { .aperture_sizes = intel_8xx_sizes, .size_type = U8_APER_SIZE, .num_aperture_sizes = 7, + .needs_scratch_page = true, .configure = intel_840_configure, .fetch_size = intel_8xx_fetch_size, .cleanup = intel_8xx_cleanup, @@ -2167,6 +599,7 @@ static const struct agp_bridge_driver intel_845_driver = { .aperture_sizes = intel_8xx_sizes, .size_type = U8_APER_SIZE, .num_aperture_sizes = 7, + .needs_scratch_page = true, .configure = intel_845_configure, .fetch_size = intel_8xx_fetch_size, .cleanup = intel_8xx_cleanup, @@ -2193,6 +626,7 @@ static const struct agp_bridge_driver intel_850_driver = { .aperture_sizes = intel_8xx_sizes, .size_type = U8_APER_SIZE, .num_aperture_sizes = 7, + .needs_scratch_page = true, .configure = intel_850_configure, .fetch_size = intel_8xx_fetch_size, .cleanup = intel_8xx_cleanup, @@ -2219,6 +653,7 @@ static const struct agp_bridge_driver intel_860_driver = { .aperture_sizes = intel_8xx_sizes, .size_type = U8_APER_SIZE, .num_aperture_sizes = 7, + .needs_scratch_page = true, .configure = intel_860_configure, .fetch_size = intel_8xx_fetch_size, .cleanup = intel_8xx_cleanup, @@ -2240,79 +675,12 @@ static const struct agp_bridge_driver intel_860_driver = { .agp_type_to_mask_type = agp_generic_type_to_mask_type, }; -static const struct agp_bridge_driver intel_915_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_i9xx_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_i915_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_alloc_pages = agp_generic_alloc_pages, - .agp_destroy_page = agp_generic_destroy_page, - .agp_destroy_pages = agp_generic_destroy_pages, - .agp_type_to_mask_type = intel_i830_type_to_mask_type, - .chipset_flush = intel_i915_chipset_flush, -#ifdef USE_PCI_DMA_API - .agp_map_page = intel_agp_map_page, - .agp_unmap_page = intel_agp_unmap_page, - .agp_map_memory = intel_agp_map_memory, - .agp_unmap_memory = intel_agp_unmap_memory, -#endif -}; - -static const 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_i9xx_fetch_size, - .cleanup = intel_i915_cleanup, - .tlb_flush = intel_i810_tlbflush, - .mask_memory = intel_i965_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_alloc_pages = agp_generic_alloc_pages, - .agp_destroy_page = agp_generic_destroy_page, - .agp_destroy_pages = agp_generic_destroy_pages, - .agp_type_to_mask_type = intel_i830_type_to_mask_type, - .chipset_flush = intel_i915_chipset_flush, -#ifdef USE_PCI_DMA_API - .agp_map_page = intel_agp_map_page, - .agp_unmap_page = intel_agp_unmap_page, - .agp_map_memory = intel_agp_map_memory, - .agp_unmap_memory = intel_agp_unmap_memory, -#endif -}; - static const struct agp_bridge_driver intel_7505_driver = { .owner = THIS_MODULE, .aperture_sizes = intel_8xx_sizes, .size_type = U8_APER_SIZE, .num_aperture_sizes = 7, + .needs_scratch_page = true, .configure = intel_7505_configure, .fetch_size = intel_8xx_fetch_size, .cleanup = intel_8xx_cleanup, @@ -2334,40 +702,6 @@ static const struct agp_bridge_driver intel_7505_driver = { .agp_type_to_mask_type = agp_generic_type_to_mask_type, }; -static const struct agp_bridge_driver intel_g33_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_i9xx_fetch_size, - .cleanup = intel_i915_cleanup, - .tlb_flush = intel_i810_tlbflush, - .mask_memory = intel_i965_mask_memory, - .masks = intel_i810_masks, - .agp_enable = intel_i810_agp_enable, - .cache_flush = global_cache_flush, - .create_gatt_table = intel_i915_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_alloc_pages = agp_generic_alloc_pages, - .agp_destroy_page = agp_generic_destroy_page, - .agp_destroy_pages = agp_generic_destroy_pages, - .agp_type_to_mask_type = intel_i830_type_to_mask_type, - .chipset_flush = intel_i915_chipset_flush, -#ifdef USE_PCI_DMA_API - .agp_map_page = intel_agp_map_page, - .agp_unmap_page = intel_agp_unmap_page, - .agp_map_memory = intel_agp_map_memory, - .agp_unmap_memory = intel_agp_unmap_memory, -#endif -}; - static int find_gmch(u16 device) { struct pci_dev *gmch_device; @@ -2392,103 +726,137 @@ static int find_gmch(u16 device) static const struct intel_driver_description { unsigned int chip_id; unsigned int gmch_chip_id; - unsigned int multi_gmch_chip; /* if we have more gfx chip type on this HB. */ char *name; const struct agp_bridge_driver *driver; const struct agp_bridge_driver *gmch_driver; } intel_agp_chipsets[] = { - { PCI_DEVICE_ID_INTEL_82443LX_0, 0, 0, "440LX", &intel_generic_driver, NULL }, - { PCI_DEVICE_ID_INTEL_82443BX_0, 0, 0, "440BX", &intel_generic_driver, NULL }, - { PCI_DEVICE_ID_INTEL_82443GX_0, 0, 0, "440GX", &intel_generic_driver, NULL }, - { PCI_DEVICE_ID_INTEL_82810_MC1, PCI_DEVICE_ID_INTEL_82810_IG1, 0, "i810", + { PCI_DEVICE_ID_INTEL_82443LX_0, 0, "440LX", &intel_generic_driver, NULL }, + { PCI_DEVICE_ID_INTEL_82443BX_0, 0, "440BX", &intel_generic_driver, NULL }, + { PCI_DEVICE_ID_INTEL_82443GX_0, 0, "440GX", &intel_generic_driver, NULL }, + { PCI_DEVICE_ID_INTEL_82810_MC1, PCI_DEVICE_ID_INTEL_82810_IG1, "i810", NULL, &intel_810_driver }, - { PCI_DEVICE_ID_INTEL_82810_MC3, PCI_DEVICE_ID_INTEL_82810_IG3, 0, "i810", + { PCI_DEVICE_ID_INTEL_82810_MC3, PCI_DEVICE_ID_INTEL_82810_IG3, "i810", NULL, &intel_810_driver }, - { PCI_DEVICE_ID_INTEL_82810E_MC, PCI_DEVICE_ID_INTEL_82810E_IG, 0, "i810", + { PCI_DEVICE_ID_INTEL_82810E_MC, PCI_DEVICE_ID_INTEL_82810E_IG, "i810", NULL, &intel_810_driver }, - { PCI_DEVICE_ID_INTEL_82815_MC, PCI_DEVICE_ID_INTEL_82815_CGC, 0, "i815", + { PCI_DEVICE_ID_INTEL_82815_MC, PCI_DEVICE_ID_INTEL_82815_CGC, "i815", &intel_815_driver, &intel_810_driver }, - { PCI_DEVICE_ID_INTEL_82820_HB, 0, 0, "i820", &intel_820_driver, NULL }, - { PCI_DEVICE_ID_INTEL_82820_UP_HB, 0, 0, "i820", &intel_820_driver, NULL }, - { PCI_DEVICE_ID_INTEL_82830_HB, PCI_DEVICE_ID_INTEL_82830_CGC, 0, "830M", + { PCI_DEVICE_ID_INTEL_82820_HB, 0, "i820", &intel_820_driver, NULL }, + { PCI_DEVICE_ID_INTEL_82820_UP_HB, 0, "i820", &intel_820_driver, NULL }, + { PCI_DEVICE_ID_INTEL_82830_HB, PCI_DEVICE_ID_INTEL_82830_CGC, "830M", &intel_830mp_driver, &intel_830_driver }, - { PCI_DEVICE_ID_INTEL_82840_HB, 0, 0, "i840", &intel_840_driver, NULL }, - { PCI_DEVICE_ID_INTEL_82845_HB, 0, 0, "845G", &intel_845_driver, NULL }, - { PCI_DEVICE_ID_INTEL_82845G_HB, PCI_DEVICE_ID_INTEL_82845G_IG, 0, "830M", + { PCI_DEVICE_ID_INTEL_82840_HB, 0, "i840", &intel_840_driver, NULL }, + { PCI_DEVICE_ID_INTEL_82845_HB, 0, "845G", &intel_845_driver, NULL }, + { PCI_DEVICE_ID_INTEL_82845G_HB, PCI_DEVICE_ID_INTEL_82845G_IG, "830M", &intel_845_driver, &intel_830_driver }, - { PCI_DEVICE_ID_INTEL_82850_HB, 0, 0, "i850", &intel_850_driver, NULL }, - { PCI_DEVICE_ID_INTEL_82854_HB, PCI_DEVICE_ID_INTEL_82854_IG, 0, "854", + { PCI_DEVICE_ID_INTEL_82850_HB, 0, "i850", &intel_850_driver, NULL }, + { PCI_DEVICE_ID_INTEL_82854_HB, PCI_DEVICE_ID_INTEL_82854_IG, "854", &intel_845_driver, &intel_830_driver }, - { PCI_DEVICE_ID_INTEL_82855PM_HB, 0, 0, "855PM", &intel_845_driver, NULL }, - { PCI_DEVICE_ID_INTEL_82855GM_HB, PCI_DEVICE_ID_INTEL_82855GM_IG, 0, "855GM", + { PCI_DEVICE_ID_INTEL_82855PM_HB, 0, "855PM", &intel_845_driver, NULL }, + { PCI_DEVICE_ID_INTEL_82855GM_HB, PCI_DEVICE_ID_INTEL_82855GM_IG, "855GM", &intel_845_driver, &intel_830_driver }, - { PCI_DEVICE_ID_INTEL_82860_HB, 0, 0, "i860", &intel_860_driver, NULL }, - { PCI_DEVICE_ID_INTEL_82865_HB, PCI_DEVICE_ID_INTEL_82865_IG, 0, "865", + { PCI_DEVICE_ID_INTEL_82860_HB, 0, "i860", &intel_860_driver, NULL }, + { PCI_DEVICE_ID_INTEL_82865_HB, PCI_DEVICE_ID_INTEL_82865_IG, "865", &intel_845_driver, &intel_830_driver }, - { PCI_DEVICE_ID_INTEL_82875_HB, 0, 0, "i875", &intel_845_driver, NULL }, - { PCI_DEVICE_ID_INTEL_E7221_HB, PCI_DEVICE_ID_INTEL_E7221_IG, 0, "E7221 (i915)", + { PCI_DEVICE_ID_INTEL_82875_HB, 0, "i875", &intel_845_driver, NULL }, + { PCI_DEVICE_ID_INTEL_E7221_HB, PCI_DEVICE_ID_INTEL_E7221_IG, "E7221 (i915)", NULL, &intel_915_driver }, - { PCI_DEVICE_ID_INTEL_82915G_HB, PCI_DEVICE_ID_INTEL_82915G_IG, 0, "915G", + { PCI_DEVICE_ID_INTEL_82915G_HB, PCI_DEVICE_ID_INTEL_82915G_IG, "915G", NULL, &intel_915_driver }, - { PCI_DEVICE_ID_INTEL_82915GM_HB, PCI_DEVICE_ID_INTEL_82915GM_IG, 0, "915GM", + { PCI_DEVICE_ID_INTEL_82915GM_HB, PCI_DEVICE_ID_INTEL_82915GM_IG, "915GM", NULL, &intel_915_driver }, - { PCI_DEVICE_ID_INTEL_82945G_HB, PCI_DEVICE_ID_INTEL_82945G_IG, 0, "945G", + { PCI_DEVICE_ID_INTEL_82945G_HB, PCI_DEVICE_ID_INTEL_82945G_IG, "945G", NULL, &intel_915_driver }, - { PCI_DEVICE_ID_INTEL_82945GM_HB, PCI_DEVICE_ID_INTEL_82945GM_IG, 0, "945GM", + { PCI_DEVICE_ID_INTEL_82945GM_HB, PCI_DEVICE_ID_INTEL_82945GM_IG, "945GM", NULL, &intel_915_driver }, - { PCI_DEVICE_ID_INTEL_82945GME_HB, PCI_DEVICE_ID_INTEL_82945GME_IG, 0, "945GME", + { PCI_DEVICE_ID_INTEL_82945GME_HB, PCI_DEVICE_ID_INTEL_82945GME_IG, "945GME", NULL, &intel_915_driver }, - { PCI_DEVICE_ID_INTEL_82946GZ_HB, PCI_DEVICE_ID_INTEL_82946GZ_IG, 0, "946GZ", + { PCI_DEVICE_ID_INTEL_82946GZ_HB, PCI_DEVICE_ID_INTEL_82946GZ_IG, "946GZ", NULL, &intel_i965_driver }, - { PCI_DEVICE_ID_INTEL_82G35_HB, PCI_DEVICE_ID_INTEL_82G35_IG, 0, "G35", + { PCI_DEVICE_ID_INTEL_82G35_HB, PCI_DEVICE_ID_INTEL_82G35_IG, "G35", NULL, &intel_i965_driver }, - { PCI_DEVICE_ID_INTEL_82965Q_HB, PCI_DEVICE_ID_INTEL_82965Q_IG, 0, "965Q", + { PCI_DEVICE_ID_INTEL_82965Q_HB, PCI_DEVICE_ID_INTEL_82965Q_IG, "965Q", NULL, &intel_i965_driver }, - { PCI_DEVICE_ID_INTEL_82965G_HB, PCI_DEVICE_ID_INTEL_82965G_IG, 0, "965G", + { PCI_DEVICE_ID_INTEL_82965G_HB, PCI_DEVICE_ID_INTEL_82965G_IG, "965G", NULL, &intel_i965_driver }, - { PCI_DEVICE_ID_INTEL_82965GM_HB, PCI_DEVICE_ID_INTEL_82965GM_IG, 0, "965GM", + { PCI_DEVICE_ID_INTEL_82965GM_HB, PCI_DEVICE_ID_INTEL_82965GM_IG, "965GM", NULL, &intel_i965_driver }, - { PCI_DEVICE_ID_INTEL_82965GME_HB, PCI_DEVICE_ID_INTEL_82965GME_IG, 0, "965GME/GLE", + { PCI_DEVICE_ID_INTEL_82965GME_HB, PCI_DEVICE_ID_INTEL_82965GME_IG, "965GME/GLE", NULL, &intel_i965_driver }, - { PCI_DEVICE_ID_INTEL_7505_0, 0, 0, "E7505", &intel_7505_driver, NULL }, - { PCI_DEVICE_ID_INTEL_7205_0, 0, 0, "E7205", &intel_7505_driver, NULL }, - { PCI_DEVICE_ID_INTEL_G33_HB, PCI_DEVICE_ID_INTEL_G33_IG, 0, "G33", + { PCI_DEVICE_ID_INTEL_7505_0, 0, "E7505", &intel_7505_driver, NULL }, + { PCI_DEVICE_ID_INTEL_7205_0, 0, "E7205", &intel_7505_driver, NULL }, + { PCI_DEVICE_ID_INTEL_G33_HB, PCI_DEVICE_ID_INTEL_G33_IG, "G33", NULL, &intel_g33_driver }, - { PCI_DEVICE_ID_INTEL_Q35_HB, PCI_DEVICE_ID_INTEL_Q35_IG, 0, "Q35", + { PCI_DEVICE_ID_INTEL_Q35_HB, PCI_DEVICE_ID_INTEL_Q35_IG, "Q35", NULL, &intel_g33_driver }, - { PCI_DEVICE_ID_INTEL_Q33_HB, PCI_DEVICE_ID_INTEL_Q33_IG, 0, "Q33", + { PCI_DEVICE_ID_INTEL_Q33_HB, PCI_DEVICE_ID_INTEL_Q33_IG, "Q33", NULL, &intel_g33_driver }, - { PCI_DEVICE_ID_INTEL_PINEVIEW_M_HB, PCI_DEVICE_ID_INTEL_PINEVIEW_M_IG, 0, "GMA3150", + { PCI_DEVICE_ID_INTEL_PINEVIEW_M_HB, PCI_DEVICE_ID_INTEL_PINEVIEW_M_IG, "GMA3150", NULL, &intel_g33_driver }, - { PCI_DEVICE_ID_INTEL_PINEVIEW_HB, PCI_DEVICE_ID_INTEL_PINEVIEW_IG, 0, "GMA3150", + { PCI_DEVICE_ID_INTEL_PINEVIEW_HB, PCI_DEVICE_ID_INTEL_PINEVIEW_IG, "GMA3150", NULL, &intel_g33_driver }, - { PCI_DEVICE_ID_INTEL_GM45_HB, PCI_DEVICE_ID_INTEL_GM45_IG, 0, + { PCI_DEVICE_ID_INTEL_GM45_HB, PCI_DEVICE_ID_INTEL_GM45_IG, "GM45", NULL, &intel_i965_driver }, - { PCI_DEVICE_ID_INTEL_EAGLELAKE_HB, PCI_DEVICE_ID_INTEL_EAGLELAKE_IG, 0, + { PCI_DEVICE_ID_INTEL_EAGLELAKE_HB, PCI_DEVICE_ID_INTEL_EAGLELAKE_IG, "Eaglelake", NULL, &intel_i965_driver }, - { PCI_DEVICE_ID_INTEL_Q45_HB, PCI_DEVICE_ID_INTEL_Q45_IG, 0, + { PCI_DEVICE_ID_INTEL_Q45_HB, PCI_DEVICE_ID_INTEL_Q45_IG, "Q45/Q43", NULL, &intel_i965_driver }, - { PCI_DEVICE_ID_INTEL_G45_HB, PCI_DEVICE_ID_INTEL_G45_IG, 0, + { PCI_DEVICE_ID_INTEL_G45_HB, PCI_DEVICE_ID_INTEL_G45_IG, "G45/G43", NULL, &intel_i965_driver }, - { PCI_DEVICE_ID_INTEL_B43_HB, PCI_DEVICE_ID_INTEL_B43_IG, 0, + { PCI_DEVICE_ID_INTEL_B43_HB, PCI_DEVICE_ID_INTEL_B43_IG, "B43", NULL, &intel_i965_driver }, - { PCI_DEVICE_ID_INTEL_G41_HB, PCI_DEVICE_ID_INTEL_G41_IG, 0, + { PCI_DEVICE_ID_INTEL_G41_HB, PCI_DEVICE_ID_INTEL_G41_IG, "G41", NULL, &intel_i965_driver }, - { PCI_DEVICE_ID_INTEL_IRONLAKE_D_HB, PCI_DEVICE_ID_INTEL_IRONLAKE_D_IG, 0, + { PCI_DEVICE_ID_INTEL_IRONLAKE_D_HB, PCI_DEVICE_ID_INTEL_IRONLAKE_D_IG, "HD Graphics", NULL, &intel_i965_driver }, - { PCI_DEVICE_ID_INTEL_IRONLAKE_M_HB, PCI_DEVICE_ID_INTEL_IRONLAKE_M_IG, 0, + { PCI_DEVICE_ID_INTEL_IRONLAKE_M_HB, PCI_DEVICE_ID_INTEL_IRONLAKE_M_IG, "HD Graphics", NULL, &intel_i965_driver }, - { PCI_DEVICE_ID_INTEL_IRONLAKE_MA_HB, PCI_DEVICE_ID_INTEL_IRONLAKE_M_IG, 0, + { PCI_DEVICE_ID_INTEL_IRONLAKE_MA_HB, PCI_DEVICE_ID_INTEL_IRONLAKE_M_IG, "HD Graphics", NULL, &intel_i965_driver }, - { PCI_DEVICE_ID_INTEL_IRONLAKE_MC2_HB, PCI_DEVICE_ID_INTEL_IRONLAKE_M_IG, 0, + { PCI_DEVICE_ID_INTEL_IRONLAKE_MC2_HB, PCI_DEVICE_ID_INTEL_IRONLAKE_M_IG, "HD Graphics", NULL, &intel_i965_driver }, - { PCI_DEVICE_ID_INTEL_SANDYBRIDGE_HB, PCI_DEVICE_ID_INTEL_SANDYBRIDGE_IG, 0, + { PCI_DEVICE_ID_INTEL_SANDYBRIDGE_HB, PCI_DEVICE_ID_INTEL_SANDYBRIDGE_IG, "Sandybridge", NULL, &intel_i965_driver }, - { PCI_DEVICE_ID_INTEL_SANDYBRIDGE_M_HB, PCI_DEVICE_ID_INTEL_SANDYBRIDGE_M_IG, 0, + { PCI_DEVICE_ID_INTEL_SANDYBRIDGE_M_HB, PCI_DEVICE_ID_INTEL_SANDYBRIDGE_M_IG, "Sandybridge", NULL, &intel_i965_driver }, - { 0, 0, 0, NULL, NULL, NULL } + { 0, 0, NULL, NULL, NULL } }; +static int __devinit intel_gmch_probe(struct pci_dev *pdev, + struct agp_bridge_data *bridge) +{ + int i; + bridge->driver = NULL; + + for (i = 0; intel_agp_chipsets[i].name != NULL; i++) { + if ((intel_agp_chipsets[i].gmch_chip_id != 0) && + find_gmch(intel_agp_chipsets[i].gmch_chip_id)) { + bridge->driver = + intel_agp_chipsets[i].gmch_driver; + break; + } + } + + if (!bridge->driver) + return 0; + + bridge->dev_private_data = &intel_private; + bridge->dev = pdev; + + dev_info(&pdev->dev, "Intel %s Chipset\n", intel_agp_chipsets[i].name); + + if (bridge->driver->mask_memory == intel_i965_mask_memory) { + if (pci_set_dma_mask(intel_private.pcidev, DMA_BIT_MASK(36))) + dev_err(&intel_private.pcidev->dev, + "set gfx device dma mask 36bit failed!\n"); + else + pci_set_consistent_dma_mask(intel_private.pcidev, + DMA_BIT_MASK(36)); + } + + return 1; +} + static int __devinit agp_intel_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { @@ -2503,22 +871,18 @@ static int __devinit agp_intel_probe(struct pci_dev *pdev, if (!bridge) return -ENOMEM; + bridge->capndx = cap_ptr; + + if (intel_gmch_probe(pdev, bridge)) + goto found_gmch; + for (i = 0; intel_agp_chipsets[i].name != NULL; i++) { /* In case that multiple models of gfx chip may stand on same host bridge type, this can be sure we detect the right IGD. */ if (pdev->device == intel_agp_chipsets[i].chip_id) { - if ((intel_agp_chipsets[i].gmch_chip_id != 0) && - find_gmch(intel_agp_chipsets[i].gmch_chip_id)) { - bridge->driver = - intel_agp_chipsets[i].gmch_driver; - break; - } else if (intel_agp_chipsets[i].multi_gmch_chip) { - continue; - } else { - bridge->driver = intel_agp_chipsets[i].driver; - break; - } + bridge->driver = intel_agp_chipsets[i].driver; + break; } } @@ -2530,18 +894,16 @@ static int __devinit agp_intel_probe(struct pci_dev *pdev, return -ENODEV; } - if (bridge->driver == NULL) { - /* bridge has no AGP and no IGD detected */ + if (!bridge->driver) { if (cap_ptr) dev_warn(&pdev->dev, "can't find bridge device (chip_id: %04x)\n", - intel_agp_chipsets[i].gmch_chip_id); + intel_agp_chipsets[i].gmch_chip_id); agp_put_bridge(bridge); return -ENODEV; } bridge->dev = pdev; - bridge->capndx = cap_ptr; - bridge->dev_private_data = &intel_private; + bridge->dev_private_data = NULL; dev_info(&pdev->dev, "Intel %s Chipset\n", intel_agp_chipsets[i].name); @@ -2577,15 +939,7 @@ static int __devinit agp_intel_probe(struct pci_dev *pdev, &bridge->mode); } - if (bridge->driver->mask_memory == intel_i965_mask_memory) { - if (pci_set_dma_mask(intel_private.pcidev, DMA_BIT_MASK(36))) - dev_err(&intel_private.pcidev->dev, - "set gfx device dma mask 36bit failed!\n"); - else - pci_set_consistent_dma_mask(intel_private.pcidev, - DMA_BIT_MASK(36)); - } - +found_gmch: pci_set_drvdata(pdev, bridge); err = agp_add_bridge(bridge); if (!err) @@ -2611,22 +965,7 @@ static int agp_intel_resume(struct pci_dev *pdev) struct agp_bridge_data *bridge = pci_get_drvdata(pdev); int ret_val; - if (bridge->driver == &intel_generic_driver) - intel_configure(); - else if (bridge->driver == &intel_850_driver) - intel_850_configure(); - else if (bridge->driver == &intel_845_driver) - intel_845_configure(); - else if (bridge->driver == &intel_830mp_driver) - intel_830mp_configure(); - else if (bridge->driver == &intel_915_driver) - intel_i915_configure(); - else if (bridge->driver == &intel_830_driver) - intel_i830_configure(); - else if (bridge->driver == &intel_810_driver) - intel_i810_configure(); - else if (bridge->driver == &intel_i965_driver) - intel_i915_configure(); + bridge->driver->configure(); ret_val = agp_rebind_memory(); if (ret_val != 0) diff --git a/drivers/char/agp/intel-agp.h b/drivers/char/agp/intel-agp.h new file mode 100644 index 000000000000..2547465d4658 --- /dev/null +++ b/drivers/char/agp/intel-agp.h @@ -0,0 +1,239 @@ +/* + * Common Intel AGPGART and GTT definitions. + */ + +/* Intel registers */ +#define INTEL_APSIZE 0xb4 +#define INTEL_ATTBASE 0xb8 +#define INTEL_AGPCTRL 0xb0 +#define INTEL_NBXCFG 0x50 +#define INTEL_ERRSTS 0x91 + +/* Intel i830 registers */ +#define I830_GMCH_CTRL 0x52 +#define I830_GMCH_ENABLED 0x4 +#define I830_GMCH_MEM_MASK 0x1 +#define I830_GMCH_MEM_64M 0x1 +#define I830_GMCH_MEM_128M 0 +#define I830_GMCH_GMS_MASK 0x70 +#define I830_GMCH_GMS_DISABLED 0x00 +#define I830_GMCH_GMS_LOCAL 0x10 +#define I830_GMCH_GMS_STOLEN_512 0x20 +#define I830_GMCH_GMS_STOLEN_1024 0x30 +#define I830_GMCH_GMS_STOLEN_8192 0x40 +#define I830_RDRAM_CHANNEL_TYPE 0x03010 +#define I830_RDRAM_ND(x) (((x) & 0x20) >> 5) +#define I830_RDRAM_DDT(x) (((x) & 0x18) >> 3) + +/* This one is for I830MP w. an external graphic card */ +#define INTEL_I830_ERRSTS 0x92 + +/* Intel 855GM/852GM registers */ +#define I855_GMCH_GMS_MASK 0xF0 +#define I855_GMCH_GMS_STOLEN_0M 0x0 +#define I855_GMCH_GMS_STOLEN_1M (0x1 << 4) +#define I855_GMCH_GMS_STOLEN_4M (0x2 << 4) +#define I855_GMCH_GMS_STOLEN_8M (0x3 << 4) +#define I855_GMCH_GMS_STOLEN_16M (0x4 << 4) +#define I855_GMCH_GMS_STOLEN_32M (0x5 << 4) +#define I85X_CAPID 0x44 +#define I85X_VARIANT_MASK 0x7 +#define I85X_VARIANT_SHIFT 5 +#define I855_GME 0x0 +#define I855_GM 0x4 +#define I852_GME 0x2 +#define I852_GM 0x5 + +/* Intel i845 registers */ +#define INTEL_I845_AGPM 0x51 +#define INTEL_I845_ERRSTS 0xc8 + +/* Intel i860 registers */ +#define INTEL_I860_MCHCFG 0x50 +#define INTEL_I860_ERRSTS 0xc8 + +/* Intel i810 registers */ +#define I810_GMADDR 0x10 +#define I810_MMADDR 0x14 +#define I810_PTE_BASE 0x10000 +#define I810_PTE_MAIN_UNCACHED 0x00000000 +#define I810_PTE_LOCAL 0x00000002 +#define I810_PTE_VALID 0x00000001 +#define I830_PTE_SYSTEM_CACHED 0x00000006 +#define I810_SMRAM_MISCC 0x70 +#define I810_GFX_MEM_WIN_SIZE 0x00010000 +#define I810_GFX_MEM_WIN_32M 0x00010000 +#define I810_GMS 0x000000c0 +#define I810_GMS_DISABLE 0x00000000 +#define I810_PGETBL_CTL 0x2020 +#define I810_PGETBL_ENABLED 0x00000001 +#define I965_PGETBL_SIZE_MASK 0x0000000e +#define I965_PGETBL_SIZE_512KB (0 << 1) +#define I965_PGETBL_SIZE_256KB (1 << 1) +#define I965_PGETBL_SIZE_128KB (2 << 1) +#define I965_PGETBL_SIZE_1MB (3 << 1) +#define I965_PGETBL_SIZE_2MB (4 << 1) +#define I965_PGETBL_SIZE_1_5MB (5 << 1) +#define G33_PGETBL_SIZE_MASK (3 << 8) +#define G33_PGETBL_SIZE_1M (1 << 8) +#define G33_PGETBL_SIZE_2M (2 << 8) + +#define I810_DRAM_CTL 0x3000 +#define I810_DRAM_ROW_0 0x00000001 +#define I810_DRAM_ROW_0_SDRAM 0x00000001 + +/* Intel 815 register */ +#define INTEL_815_APCONT 0x51 +#define INTEL_815_ATTBASE_MASK ~0x1FFFFFFF + +/* Intel i820 registers */ +#define INTEL_I820_RDCR 0x51 +#define INTEL_I820_ERRSTS 0xc8 + +/* Intel i840 registers */ +#define INTEL_I840_MCHCFG 0x50 +#define INTEL_I840_ERRSTS 0xc8 + +/* Intel i850 registers */ +#define INTEL_I850_MCHCFG 0x50 +#define INTEL_I850_ERRSTS 0xc8 + +/* intel 915G registers */ +#define I915_GMADDR 0x18 +#define I915_MMADDR 0x10 +#define I915_PTEADDR 0x1C +#define I915_GMCH_GMS_STOLEN_48M (0x6 << 4) +#define I915_GMCH_GMS_STOLEN_64M (0x7 << 4) +#define G33_GMCH_GMS_STOLEN_128M (0x8 << 4) +#define G33_GMCH_GMS_STOLEN_256M (0x9 << 4) +#define INTEL_GMCH_GMS_STOLEN_96M (0xa << 4) +#define INTEL_GMCH_GMS_STOLEN_160M (0xb << 4) +#define INTEL_GMCH_GMS_STOLEN_224M (0xc << 4) +#define INTEL_GMCH_GMS_STOLEN_352M (0xd << 4) + +#define I915_IFPADDR 0x60 + +/* Intel 965G registers */ +#define I965_MSAC 0x62 +#define I965_IFPADDR 0x70 + +/* Intel 7505 registers */ +#define INTEL_I7505_APSIZE 0x74 +#define INTEL_I7505_NCAPID 0x60 +#define INTEL_I7505_NISTAT 0x6c +#define INTEL_I7505_ATTBASE 0x78 +#define INTEL_I7505_ERRSTS 0x42 +#define INTEL_I7505_AGPCTRL 0x70 +#define INTEL_I7505_MCHCFG 0x50 + +#define SNB_GMCH_CTRL 0x50 +#define SNB_GMCH_GMS_STOLEN_MASK 0xF8 +#define SNB_GMCH_GMS_STOLEN_32M (1 << 3) +#define SNB_GMCH_GMS_STOLEN_64M (2 << 3) +#define SNB_GMCH_GMS_STOLEN_96M (3 << 3) +#define SNB_GMCH_GMS_STOLEN_128M (4 << 3) +#define SNB_GMCH_GMS_STOLEN_160M (5 << 3) +#define SNB_GMCH_GMS_STOLEN_192M (6 << 3) +#define SNB_GMCH_GMS_STOLEN_224M (7 << 3) +#define SNB_GMCH_GMS_STOLEN_256M (8 << 3) +#define SNB_GMCH_GMS_STOLEN_288M (9 << 3) +#define SNB_GMCH_GMS_STOLEN_320M (0xa << 3) +#define SNB_GMCH_GMS_STOLEN_352M (0xb << 3) +#define SNB_GMCH_GMS_STOLEN_384M (0xc << 3) +#define SNB_GMCH_GMS_STOLEN_416M (0xd << 3) +#define SNB_GMCH_GMS_STOLEN_448M (0xe << 3) +#define SNB_GMCH_GMS_STOLEN_480M (0xf << 3) +#define SNB_GMCH_GMS_STOLEN_512M (0x10 << 3) +#define SNB_GTT_SIZE_0M (0 << 8) +#define SNB_GTT_SIZE_1M (1 << 8) +#define SNB_GTT_SIZE_2M (2 << 8) +#define SNB_GTT_SIZE_MASK (3 << 8) + +/* pci devices ids */ +#define PCI_DEVICE_ID_INTEL_E7221_HB 0x2588 +#define PCI_DEVICE_ID_INTEL_E7221_IG 0x258a +#define PCI_DEVICE_ID_INTEL_82946GZ_HB 0x2970 +#define PCI_DEVICE_ID_INTEL_82946GZ_IG 0x2972 +#define PCI_DEVICE_ID_INTEL_82G35_HB 0x2980 +#define PCI_DEVICE_ID_INTEL_82G35_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 PCI_DEVICE_ID_INTEL_82965GM_HB 0x2A00 +#define PCI_DEVICE_ID_INTEL_82965GM_IG 0x2A02 +#define PCI_DEVICE_ID_INTEL_82965GME_HB 0x2A10 +#define PCI_DEVICE_ID_INTEL_82965GME_IG 0x2A12 +#define PCI_DEVICE_ID_INTEL_82945GME_HB 0x27AC +#define PCI_DEVICE_ID_INTEL_82945GME_IG 0x27AE +#define PCI_DEVICE_ID_INTEL_PINEVIEW_M_HB 0xA010 +#define PCI_DEVICE_ID_INTEL_PINEVIEW_M_IG 0xA011 +#define PCI_DEVICE_ID_INTEL_PINEVIEW_HB 0xA000 +#define PCI_DEVICE_ID_INTEL_PINEVIEW_IG 0xA001 +#define PCI_DEVICE_ID_INTEL_G33_HB 0x29C0 +#define PCI_DEVICE_ID_INTEL_G33_IG 0x29C2 +#define PCI_DEVICE_ID_INTEL_Q35_HB 0x29B0 +#define PCI_DEVICE_ID_INTEL_Q35_IG 0x29B2 +#define PCI_DEVICE_ID_INTEL_Q33_HB 0x29D0 +#define PCI_DEVICE_ID_INTEL_Q33_IG 0x29D2 +#define PCI_DEVICE_ID_INTEL_B43_HB 0x2E40 +#define PCI_DEVICE_ID_INTEL_B43_IG 0x2E42 +#define PCI_DEVICE_ID_INTEL_GM45_HB 0x2A40 +#define PCI_DEVICE_ID_INTEL_GM45_IG 0x2A42 +#define PCI_DEVICE_ID_INTEL_EAGLELAKE_HB 0x2E00 +#define PCI_DEVICE_ID_INTEL_EAGLELAKE_IG 0x2E02 +#define PCI_DEVICE_ID_INTEL_Q45_HB 0x2E10 +#define PCI_DEVICE_ID_INTEL_Q45_IG 0x2E12 +#define PCI_DEVICE_ID_INTEL_G45_HB 0x2E20 +#define PCI_DEVICE_ID_INTEL_G45_IG 0x2E22 +#define PCI_DEVICE_ID_INTEL_G41_HB 0x2E30 +#define PCI_DEVICE_ID_INTEL_G41_IG 0x2E32 +#define PCI_DEVICE_ID_INTEL_IRONLAKE_D_HB 0x0040 +#define PCI_DEVICE_ID_INTEL_IRONLAKE_D_IG 0x0042 +#define PCI_DEVICE_ID_INTEL_IRONLAKE_M_HB 0x0044 +#define PCI_DEVICE_ID_INTEL_IRONLAKE_MA_HB 0x0062 +#define PCI_DEVICE_ID_INTEL_IRONLAKE_MC2_HB 0x006a +#define PCI_DEVICE_ID_INTEL_IRONLAKE_M_IG 0x0046 +#define PCI_DEVICE_ID_INTEL_SANDYBRIDGE_HB 0x0100 +#define PCI_DEVICE_ID_INTEL_SANDYBRIDGE_IG 0x0102 +#define PCI_DEVICE_ID_INTEL_SANDYBRIDGE_M_HB 0x0104 +#define PCI_DEVICE_ID_INTEL_SANDYBRIDGE_M_IG 0x0106 + +/* cover 915 and 945 variants */ +#define IS_I915 (agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_E7221_HB || \ + 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_82945GME_HB) + +#define IS_I965 (agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82946GZ_HB || \ + agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82G35_HB || \ + agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82965Q_HB || \ + agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82965G_HB || \ + agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82965GM_HB || \ + agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82965GME_HB) + +#define IS_G33 (agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_G33_HB || \ + agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_Q35_HB || \ + agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_Q33_HB || \ + agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_PINEVIEW_M_HB || \ + agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_PINEVIEW_HB) + +#define IS_PINEVIEW (agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_PINEVIEW_M_HB || \ + agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_PINEVIEW_HB) + +#define IS_SNB (agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_SANDYBRIDGE_HB || \ + agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_SANDYBRIDGE_M_HB) + +#define IS_G4X (agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_EAGLELAKE_HB || \ + agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_Q45_HB || \ + agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_G45_HB || \ + agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_GM45_HB || \ + agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_G41_HB || \ + agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_B43_HB || \ + agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_IRONLAKE_D_HB || \ + agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_IRONLAKE_M_HB || \ + agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_IRONLAKE_MA_HB || \ + agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_IRONLAKE_MC2_HB || \ + IS_SNB) diff --git a/drivers/char/agp/intel-gtt.c b/drivers/char/agp/intel-gtt.c new file mode 100644 index 000000000000..e8ea6825822c --- /dev/null +++ b/drivers/char/agp/intel-gtt.c @@ -0,0 +1,1516 @@ +/* + * Intel GTT (Graphics Translation Table) routines + * + * Caveat: This driver implements the linux agp interface, but this is far from + * a agp driver! GTT support ended up here for purely historical reasons: The + * old userspace intel graphics drivers needed an interface to map memory into + * the GTT. And the drm provides a default interface for graphic devices sitting + * on an agp port. So it made sense to fake the GTT support as an agp port to + * avoid having to create a new api. + * + * With gem this does not make much sense anymore, just needlessly complicates + * the code. But as long as the old graphics stack is still support, it's stuck + * here. + * + * /fairy-tale-mode off + */ + +/* + * If we have Intel graphics, we're not going to have anything other than + * an Intel IOMMU. So make the correct use of the PCI DMA API contingent + * on the Intel IOMMU support (CONFIG_DMAR). + * Only newer chipsets need to bother with this, of course. + */ +#ifdef CONFIG_DMAR +#define USE_PCI_DMA_API 1 +#endif + +static const struct aper_size_info_fixed intel_i810_sizes[] = +{ + {64, 16384, 4}, + /* The 32M mode still requires a 64k gatt */ + {32, 8192, 4} +}; + +#define AGP_DCACHE_MEMORY 1 +#define AGP_PHYS_MEMORY 2 +#define INTEL_AGP_CACHED_MEMORY 3 + +static struct gatt_mask intel_i810_masks[] = +{ + {.mask = I810_PTE_VALID, .type = 0}, + {.mask = (I810_PTE_VALID | I810_PTE_LOCAL), .type = AGP_DCACHE_MEMORY}, + {.mask = I810_PTE_VALID, .type = 0}, + {.mask = I810_PTE_VALID | I830_PTE_SYSTEM_CACHED, + .type = INTEL_AGP_CACHED_MEMORY} +}; + +static struct _intel_private { + struct pci_dev *pcidev; /* device one */ + u8 __iomem *registers; + u32 __iomem *gtt; /* I915G */ + int num_dcache_entries; + /* gtt_entries is the number of gtt entries that are already mapped + * to stolen memory. Stolen memory is larger than the memory mapped + * through gtt_entries, as it includes some reserved space for the BIOS + * popup and for the GTT. + */ + int gtt_entries; /* i830+ */ + int gtt_total_size; + union { + void __iomem *i9xx_flush_page; + void *i8xx_flush_page; + }; + struct page *i8xx_page; + struct resource ifp_resource; + int resource_valid; +} intel_private; + +#ifdef USE_PCI_DMA_API +static int intel_agp_map_page(struct page *page, dma_addr_t *ret) +{ + *ret = pci_map_page(intel_private.pcidev, page, 0, + PAGE_SIZE, PCI_DMA_BIDIRECTIONAL); + if (pci_dma_mapping_error(intel_private.pcidev, *ret)) + return -EINVAL; + return 0; +} + +static void intel_agp_unmap_page(struct page *page, dma_addr_t dma) +{ + pci_unmap_page(intel_private.pcidev, dma, + PAGE_SIZE, PCI_DMA_BIDIRECTIONAL); +} + +static void intel_agp_free_sglist(struct agp_memory *mem) +{ + struct sg_table st; + + st.sgl = mem->sg_list; + st.orig_nents = st.nents = mem->page_count; + + sg_free_table(&st); + + mem->sg_list = NULL; + mem->num_sg = 0; +} + +static int intel_agp_map_memory(struct agp_memory *mem) +{ + struct sg_table st; + struct scatterlist *sg; + int i; + + DBG("try mapping %lu pages\n", (unsigned long)mem->page_count); + + if (sg_alloc_table(&st, mem->page_count, GFP_KERNEL)) + return -ENOMEM; + + mem->sg_list = sg = st.sgl; + + for (i = 0 ; i < mem->page_count; i++, sg = sg_next(sg)) + sg_set_page(sg, mem->pages[i], PAGE_SIZE, 0); + + mem->num_sg = pci_map_sg(intel_private.pcidev, mem->sg_list, + mem->page_count, PCI_DMA_BIDIRECTIONAL); + if (unlikely(!mem->num_sg)) { + intel_agp_free_sglist(mem); + return -ENOMEM; + } + return 0; +} + +static void intel_agp_unmap_memory(struct agp_memory *mem) +{ + DBG("try unmapping %lu pages\n", (unsigned long)mem->page_count); + + pci_unmap_sg(intel_private.pcidev, mem->sg_list, + mem->page_count, PCI_DMA_BIDIRECTIONAL); + intel_agp_free_sglist(mem); +} + +static void intel_agp_insert_sg_entries(struct agp_memory *mem, + off_t pg_start, int mask_type) +{ + struct scatterlist *sg; + int i, j; + + j = pg_start; + + WARN_ON(!mem->num_sg); + + if (mem->num_sg == mem->page_count) { + for_each_sg(mem->sg_list, sg, mem->page_count, i) { + writel(agp_bridge->driver->mask_memory(agp_bridge, + sg_dma_address(sg), mask_type), + intel_private.gtt+j); + j++; + } + } else { + /* sg may merge pages, but we have to separate + * per-page addr for GTT */ + unsigned int len, m; + + for_each_sg(mem->sg_list, sg, mem->num_sg, i) { + len = sg_dma_len(sg) / PAGE_SIZE; + for (m = 0; m < len; m++) { + writel(agp_bridge->driver->mask_memory(agp_bridge, + sg_dma_address(sg) + m * PAGE_SIZE, + mask_type), + intel_private.gtt+j); + j++; + } + } + } + readl(intel_private.gtt+j-1); +} + +#else + +static void intel_agp_insert_sg_entries(struct agp_memory *mem, + off_t pg_start, int mask_type) +{ + int i, j; + u32 cache_bits = 0; + + if (agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_SANDYBRIDGE_HB || + agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_SANDYBRIDGE_M_HB) + { + cache_bits = I830_PTE_SYSTEM_CACHED; + } + + for (i = 0, j = pg_start; i < mem->page_count; i++, j++) { + writel(agp_bridge->driver->mask_memory(agp_bridge, + page_to_phys(mem->pages[i]), mask_type), + intel_private.gtt+j); + } + + readl(intel_private.gtt+j-1); +} + +#endif + +static int intel_i810_fetch_size(void) +{ + u32 smram_miscc; + struct aper_size_info_fixed *values; + + pci_read_config_dword(agp_bridge->dev, I810_SMRAM_MISCC, &smram_miscc); + values = A_SIZE_FIX(agp_bridge->driver->aperture_sizes); + + if ((smram_miscc & I810_GMS) == I810_GMS_DISABLE) { + dev_warn(&agp_bridge->dev->dev, "i810 is disabled\n"); + return 0; + } + if ((smram_miscc & I810_GFX_MEM_WIN_SIZE) == I810_GFX_MEM_WIN_32M) { + agp_bridge->current_size = (void *) (values + 1); + agp_bridge->aperture_size_idx = 1; + return values[1].size; + } else { + agp_bridge->current_size = (void *) (values); + agp_bridge->aperture_size_idx = 0; + return values[0].size; + } + + return 0; +} + +static int intel_i810_configure(void) +{ + struct aper_size_info_fixed *current_size; + u32 temp; + int i; + + current_size = A_SIZE_FIX(agp_bridge->current_size); + + if (!intel_private.registers) { + pci_read_config_dword(intel_private.pcidev, I810_MMADDR, &temp); + temp &= 0xfff80000; + + intel_private.registers = ioremap(temp, 128 * 4096); + if (!intel_private.registers) { + dev_err(&intel_private.pcidev->dev, + "can't remap memory\n"); + return -ENOMEM; + } + } + + if ((readl(intel_private.registers+I810_DRAM_CTL) + & I810_DRAM_ROW_0) == I810_DRAM_ROW_0_SDRAM) { + /* This will need to be dynamically assigned */ + dev_info(&intel_private.pcidev->dev, + "detected 4MB dedicated video ram\n"); + intel_private.num_dcache_entries = 1024; + } + pci_read_config_dword(intel_private.pcidev, I810_GMADDR, &temp); + agp_bridge->gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK); + writel(agp_bridge->gatt_bus_addr | I810_PGETBL_ENABLED, intel_private.registers+I810_PGETBL_CTL); + readl(intel_private.registers+I810_PGETBL_CTL); /* PCI Posting. */ + + if (agp_bridge->driver->needs_scratch_page) { + for (i = 0; i < current_size->num_entries; i++) { + writel(agp_bridge->scratch_page, intel_private.registers+I810_PTE_BASE+(i*4)); + } + readl(intel_private.registers+I810_PTE_BASE+((i-1)*4)); /* PCI posting. */ + } + global_cache_flush(); + return 0; +} + +static void intel_i810_cleanup(void) +{ + writel(0, intel_private.registers+I810_PGETBL_CTL); + readl(intel_private.registers); /* PCI Posting. */ + iounmap(intel_private.registers); +} + +static void intel_i810_agp_enable(struct agp_bridge_data *bridge, u32 mode) +{ + return; +} + +/* Exists to support ARGB cursors */ +static struct page *i8xx_alloc_pages(void) +{ + struct page *page; + + page = alloc_pages(GFP_KERNEL | GFP_DMA32, 2); + if (page == NULL) + return NULL; + + if (set_pages_uc(page, 4) < 0) { + set_pages_wb(page, 4); + __free_pages(page, 2); + return NULL; + } + get_page(page); + atomic_inc(&agp_bridge->current_memory_agp); + return page; +} + +static void i8xx_destroy_pages(struct page *page) +{ + if (page == NULL) + return; + + set_pages_wb(page, 4); + put_page(page); + __free_pages(page, 2); + atomic_dec(&agp_bridge->current_memory_agp); +} + +static int intel_i830_type_to_mask_type(struct agp_bridge_data *bridge, + int type) +{ + if (type < AGP_USER_TYPES) + return type; + else if (type == AGP_USER_CACHED_MEMORY) + return INTEL_AGP_CACHED_MEMORY; + else + return 0; +} + +static int intel_i810_insert_entries(struct agp_memory *mem, off_t pg_start, + int type) +{ + int i, j, num_entries; + void *temp; + int ret = -EINVAL; + int mask_type; + + if (mem->page_count == 0) + goto out; + + temp = agp_bridge->current_size; + num_entries = A_SIZE_FIX(temp)->num_entries; + + if ((pg_start + mem->page_count) > num_entries) + goto out_err; + + + for (j = pg_start; j < (pg_start + mem->page_count); j++) { + if (!PGE_EMPTY(agp_bridge, readl(agp_bridge->gatt_table+j))) { + ret = -EBUSY; + goto out_err; + } + } + + if (type != mem->type) + goto out_err; + + mask_type = agp_bridge->driver->agp_type_to_mask_type(agp_bridge, type); + + switch (mask_type) { + case AGP_DCACHE_MEMORY: + if (!mem->is_flushed) + global_cache_flush(); + for (i = pg_start; i < (pg_start + mem->page_count); i++) { + writel((i*4096)|I810_PTE_LOCAL|I810_PTE_VALID, + intel_private.registers+I810_PTE_BASE+(i*4)); + } + readl(intel_private.registers+I810_PTE_BASE+((i-1)*4)); + break; + case AGP_PHYS_MEMORY: + case AGP_NORMAL_MEMORY: + if (!mem->is_flushed) + global_cache_flush(); + for (i = 0, j = pg_start; i < mem->page_count; i++, j++) { + writel(agp_bridge->driver->mask_memory(agp_bridge, + page_to_phys(mem->pages[i]), mask_type), + intel_private.registers+I810_PTE_BASE+(j*4)); + } + readl(intel_private.registers+I810_PTE_BASE+((j-1)*4)); + break; + default: + goto out_err; + } + +out: + ret = 0; +out_err: + mem->is_flushed = true; + return ret; +} + +static int intel_i810_remove_entries(struct agp_memory *mem, off_t pg_start, + int type) +{ + int i; + + if (mem->page_count == 0) + return 0; + + for (i = pg_start; i < (mem->page_count + pg_start); i++) { + writel(agp_bridge->scratch_page, intel_private.registers+I810_PTE_BASE+(i*4)); + } + readl(intel_private.registers+I810_PTE_BASE+((i-1)*4)); + + return 0; +} + +/* + * The i810/i830 requires a physical address to program its mouse + * pointer into hardware. + * However the Xserver still writes to it through the agp aperture. + */ +static struct agp_memory *alloc_agpphysmem_i8xx(size_t pg_count, int type) +{ + struct agp_memory *new; + struct page *page; + + switch (pg_count) { + case 1: page = agp_bridge->driver->agp_alloc_page(agp_bridge); + break; + case 4: + /* kludge to get 4 physical pages for ARGB cursor */ + page = i8xx_alloc_pages(); + break; + default: + return NULL; + } + + if (page == NULL) + return NULL; + + new = agp_create_memory(pg_count); + if (new == NULL) + return NULL; + + new->pages[0] = page; + if (pg_count == 4) { + /* kludge to get 4 physical pages for ARGB cursor */ + new->pages[1] = new->pages[0] + 1; + new->pages[2] = new->pages[1] + 1; + new->pages[3] = new->pages[2] + 1; + } + new->page_count = pg_count; + new->num_scratch_pages = pg_count; + new->type = AGP_PHYS_MEMORY; + new->physical = page_to_phys(new->pages[0]); + return new; +} + +static struct agp_memory *intel_i810_alloc_by_type(size_t pg_count, int type) +{ + struct agp_memory *new; + + if (type == AGP_DCACHE_MEMORY) { + if (pg_count != intel_private.num_dcache_entries) + return NULL; + + new = agp_create_memory(1); + if (new == NULL) + return NULL; + + new->type = AGP_DCACHE_MEMORY; + new->page_count = pg_count; + new->num_scratch_pages = 0; + agp_free_page_array(new); + return new; + } + if (type == AGP_PHYS_MEMORY) + return alloc_agpphysmem_i8xx(pg_count, type); + return NULL; +} + +static void intel_i810_free_by_type(struct agp_memory *curr) +{ + agp_free_key(curr->key); + if (curr->type == AGP_PHYS_MEMORY) { + if (curr->page_count == 4) + i8xx_destroy_pages(curr->pages[0]); + else { + agp_bridge->driver->agp_destroy_page(curr->pages[0], + AGP_PAGE_DESTROY_UNMAP); + agp_bridge->driver->agp_destroy_page(curr->pages[0], + AGP_PAGE_DESTROY_FREE); + } + agp_free_page_array(curr); + } + kfree(curr); +} + +static unsigned long intel_i810_mask_memory(struct agp_bridge_data *bridge, + dma_addr_t addr, int type) +{ + /* Type checking must be done elsewhere */ + return addr | bridge->driver->masks[type].mask; +} + +static struct aper_size_info_fixed intel_i830_sizes[] = +{ + {128, 32768, 5}, + /* The 64M mode still requires a 128k gatt */ + {64, 16384, 5}, + {256, 65536, 6}, + {512, 131072, 7}, +}; + +static void intel_i830_init_gtt_entries(void) +{ + u16 gmch_ctrl; + int gtt_entries = 0; + u8 rdct; + int local = 0; + static const int ddt[4] = { 0, 16, 32, 64 }; + int size; /* reserved space (in kb) at the top of stolen memory */ + + pci_read_config_word(agp_bridge->dev, I830_GMCH_CTRL, &gmch_ctrl); + + if (IS_I965) { + u32 pgetbl_ctl; + pgetbl_ctl = readl(intel_private.registers+I810_PGETBL_CTL); + + /* The 965 has a field telling us the size of the GTT, + * which may be larger than what is necessary to map the + * aperture. + */ + switch (pgetbl_ctl & I965_PGETBL_SIZE_MASK) { + case I965_PGETBL_SIZE_128KB: + size = 128; + break; + case I965_PGETBL_SIZE_256KB: + size = 256; + break; + case I965_PGETBL_SIZE_512KB: + size = 512; + break; + case I965_PGETBL_SIZE_1MB: + size = 1024; + break; + case I965_PGETBL_SIZE_2MB: + size = 2048; + break; + case I965_PGETBL_SIZE_1_5MB: + size = 1024 + 512; + break; + default: + dev_info(&intel_private.pcidev->dev, + "unknown page table size, assuming 512KB\n"); + size = 512; + } + size += 4; /* add in BIOS popup space */ + } else if (IS_G33 && !IS_PINEVIEW) { + /* G33's GTT size defined in gmch_ctrl */ + switch (gmch_ctrl & G33_PGETBL_SIZE_MASK) { + case G33_PGETBL_SIZE_1M: + size = 1024; + break; + case G33_PGETBL_SIZE_2M: + size = 2048; + break; + default: + dev_info(&agp_bridge->dev->dev, + "unknown page table size 0x%x, assuming 512KB\n", + (gmch_ctrl & G33_PGETBL_SIZE_MASK)); + size = 512; + } + size += 4; + } else if (IS_G4X || IS_PINEVIEW) { + /* On 4 series hardware, GTT stolen is separate from graphics + * stolen, ignore it in stolen gtt entries counting. However, + * 4KB of the stolen memory doesn't get mapped to the GTT. + */ + size = 4; + } else { + /* On previous hardware, the GTT size was just what was + * required to map the aperture. + */ + 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) { + switch (gmch_ctrl & I830_GMCH_GMS_MASK) { + case I830_GMCH_GMS_STOLEN_512: + gtt_entries = KB(512) - KB(size); + break; + case I830_GMCH_GMS_STOLEN_1024: + gtt_entries = MB(1) - KB(size); + break; + case I830_GMCH_GMS_STOLEN_8192: + gtt_entries = MB(8) - KB(size); + break; + case I830_GMCH_GMS_LOCAL: + rdct = readb(intel_private.registers+I830_RDRAM_CHANNEL_TYPE); + gtt_entries = (I830_RDRAM_ND(rdct) + 1) * + MB(ddt[I830_RDRAM_DDT(rdct)]); + local = 1; + break; + default: + gtt_entries = 0; + break; + } + } else if (agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_SANDYBRIDGE_HB || + agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_SANDYBRIDGE_M_HB) { + /* + * SandyBridge has new memory control reg at 0x50.w + */ + u16 snb_gmch_ctl; + pci_read_config_word(intel_private.pcidev, SNB_GMCH_CTRL, &snb_gmch_ctl); + switch (snb_gmch_ctl & SNB_GMCH_GMS_STOLEN_MASK) { + case SNB_GMCH_GMS_STOLEN_32M: + gtt_entries = MB(32) - KB(size); + break; + case SNB_GMCH_GMS_STOLEN_64M: + gtt_entries = MB(64) - KB(size); + break; + case SNB_GMCH_GMS_STOLEN_96M: + gtt_entries = MB(96) - KB(size); + break; + case SNB_GMCH_GMS_STOLEN_128M: + gtt_entries = MB(128) - KB(size); + break; + case SNB_GMCH_GMS_STOLEN_160M: + gtt_entries = MB(160) - KB(size); + break; + case SNB_GMCH_GMS_STOLEN_192M: + gtt_entries = MB(192) - KB(size); + break; + case SNB_GMCH_GMS_STOLEN_224M: + gtt_entries = MB(224) - KB(size); + break; + case SNB_GMCH_GMS_STOLEN_256M: + gtt_entries = MB(256) - KB(size); + break; + case SNB_GMCH_GMS_STOLEN_288M: + gtt_entries = MB(288) - KB(size); + break; + case SNB_GMCH_GMS_STOLEN_320M: + gtt_entries = MB(320) - KB(size); + break; + case SNB_GMCH_GMS_STOLEN_352M: + gtt_entries = MB(352) - KB(size); + break; + case SNB_GMCH_GMS_STOLEN_384M: + gtt_entries = MB(384) - KB(size); + break; + case SNB_GMCH_GMS_STOLEN_416M: + gtt_entries = MB(416) - KB(size); + break; + case SNB_GMCH_GMS_STOLEN_448M: + gtt_entries = MB(448) - KB(size); + break; + case SNB_GMCH_GMS_STOLEN_480M: + gtt_entries = MB(480) - KB(size); + break; + case SNB_GMCH_GMS_STOLEN_512M: + gtt_entries = MB(512) - KB(size); + break; + } + } else { + switch (gmch_ctrl & I855_GMCH_GMS_MASK) { + case I855_GMCH_GMS_STOLEN_1M: + gtt_entries = MB(1) - KB(size); + break; + case I855_GMCH_GMS_STOLEN_4M: + gtt_entries = MB(4) - KB(size); + break; + case I855_GMCH_GMS_STOLEN_8M: + gtt_entries = MB(8) - KB(size); + break; + case I855_GMCH_GMS_STOLEN_16M: + gtt_entries = MB(16) - KB(size); + break; + case I855_GMCH_GMS_STOLEN_32M: + gtt_entries = MB(32) - KB(size); + break; + case I915_GMCH_GMS_STOLEN_48M: + /* Check it's really I915G */ + if (IS_I915 || IS_I965 || IS_G33 || IS_G4X) + gtt_entries = MB(48) - KB(size); + else + gtt_entries = 0; + break; + case I915_GMCH_GMS_STOLEN_64M: + /* Check it's really I915G */ + if (IS_I915 || IS_I965 || IS_G33 || IS_G4X) + gtt_entries = MB(64) - KB(size); + else + gtt_entries = 0; + break; + case G33_GMCH_GMS_STOLEN_128M: + if (IS_G33 || IS_I965 || IS_G4X) + gtt_entries = MB(128) - KB(size); + else + gtt_entries = 0; + break; + case G33_GMCH_GMS_STOLEN_256M: + if (IS_G33 || IS_I965 || IS_G4X) + gtt_entries = MB(256) - KB(size); + else + gtt_entries = 0; + break; + case INTEL_GMCH_GMS_STOLEN_96M: + if (IS_I965 || IS_G4X) + gtt_entries = MB(96) - KB(size); + else + gtt_entries = 0; + break; + case INTEL_GMCH_GMS_STOLEN_160M: + if (IS_I965 || IS_G4X) + gtt_entries = MB(160) - KB(size); + else + gtt_entries = 0; + break; + case INTEL_GMCH_GMS_STOLEN_224M: + if (IS_I965 || IS_G4X) + gtt_entries = MB(224) - KB(size); + else + gtt_entries = 0; + break; + case INTEL_GMCH_GMS_STOLEN_352M: + if (IS_I965 || IS_G4X) + gtt_entries = MB(352) - KB(size); + else + gtt_entries = 0; + break; + default: + gtt_entries = 0; + break; + } + } + if (gtt_entries > 0) { + dev_info(&agp_bridge->dev->dev, "detected %dK %s memory\n", + gtt_entries / KB(1), local ? "local" : "stolen"); + gtt_entries /= KB(4); + } else { + dev_info(&agp_bridge->dev->dev, + "no pre-allocated video memory detected\n"); + gtt_entries = 0; + } + + intel_private.gtt_entries = gtt_entries; +} + +static void intel_i830_fini_flush(void) +{ + kunmap(intel_private.i8xx_page); + intel_private.i8xx_flush_page = NULL; + unmap_page_from_agp(intel_private.i8xx_page); + + __free_page(intel_private.i8xx_page); + intel_private.i8xx_page = NULL; +} + +static void intel_i830_setup_flush(void) +{ + /* return if we've already set the flush mechanism up */ + if (intel_private.i8xx_page) + return; + + intel_private.i8xx_page = alloc_page(GFP_KERNEL | __GFP_ZERO | GFP_DMA32); + if (!intel_private.i8xx_page) + return; + + intel_private.i8xx_flush_page = kmap(intel_private.i8xx_page); + if (!intel_private.i8xx_flush_page) + intel_i830_fini_flush(); +} + +/* The chipset_flush interface needs to get data that has already been + * flushed out of the CPU all the way out to main memory, because the GPU + * doesn't snoop those buffers. + * + * The 8xx series doesn't have the same lovely interface for flushing the + * chipset write buffers that the later chips do. According to the 865 + * specs, it's 64 octwords, or 1KB. So, to get those previous things in + * that buffer out, we just fill 1KB and clflush it out, on the assumption + * that it'll push whatever was in there out. It appears to work. + */ +static void intel_i830_chipset_flush(struct agp_bridge_data *bridge) +{ + unsigned int *pg = intel_private.i8xx_flush_page; + + memset(pg, 0, 1024); + + if (cpu_has_clflush) + clflush_cache_range(pg, 1024); + else if (wbinvd_on_all_cpus() != 0) + printk(KERN_ERR "Timed out waiting for cache flush.\n"); +} + +/* The intel i830 automatically initializes the agp aperture during POST. + * Use the memory already set aside for in the GTT. + */ +static int intel_i830_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_private.pcidev, I810_MMADDR, &temp); + temp &= 0xfff80000; + + intel_private.registers = ioremap(temp, 128 * 4096); + if (!intel_private.registers) + return -ENOMEM; + + temp = readl(intel_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; +} + +/* Return the gatt table to a sane state. Use the top of stolen + * memory for the GTT. + */ +static int intel_i830_free_gatt_table(struct agp_bridge_data *bridge) +{ + return 0; +} + +static int intel_i830_fetch_size(void) +{ + u16 gmch_ctrl; + struct aper_size_info_fixed *values; + + values = A_SIZE_FIX(agp_bridge->driver->aperture_sizes); + + if (agp_bridge->dev->device != PCI_DEVICE_ID_INTEL_82830_HB && + agp_bridge->dev->device != PCI_DEVICE_ID_INTEL_82845G_HB) { + /* 855GM/852GM/865G has 128MB aperture size */ + agp_bridge->current_size = (void *) values; + agp_bridge->aperture_size_idx = 0; + return values[0].size; + } + + pci_read_config_word(agp_bridge->dev, I830_GMCH_CTRL, &gmch_ctrl); + + if ((gmch_ctrl & I830_GMCH_MEM_MASK) == I830_GMCH_MEM_128M) { + agp_bridge->current_size = (void *) values; + agp_bridge->aperture_size_idx = 0; + return values[0].size; + } else { + agp_bridge->current_size = (void *) (values + 1); + agp_bridge->aperture_size_idx = 1; + return values[1].size; + } + + return 0; +} + +static int intel_i830_configure(void) +{ + struct aper_size_info_fixed *current_size; + u32 temp; + u16 gmch_ctrl; + int i; + + current_size = A_SIZE_FIX(agp_bridge->current_size); + + pci_read_config_dword(intel_private.pcidev, I810_GMADDR, &temp); + agp_bridge->gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK); + + pci_read_config_word(agp_bridge->dev, I830_GMCH_CTRL, &gmch_ctrl); + gmch_ctrl |= I830_GMCH_ENABLED; + pci_write_config_word(agp_bridge->dev, I830_GMCH_CTRL, gmch_ctrl); + + writel(agp_bridge->gatt_bus_addr|I810_PGETBL_ENABLED, intel_private.registers+I810_PGETBL_CTL); + readl(intel_private.registers+I810_PGETBL_CTL); /* PCI Posting. */ + + if (agp_bridge->driver->needs_scratch_page) { + for (i = intel_private.gtt_entries; i < current_size->num_entries; i++) { + writel(agp_bridge->scratch_page, intel_private.registers+I810_PTE_BASE+(i*4)); + } + readl(intel_private.registers+I810_PTE_BASE+((i-1)*4)); /* PCI Posting. */ + } + + global_cache_flush(); + + intel_i830_setup_flush(); + return 0; +} + +static void intel_i830_cleanup(void) +{ + iounmap(intel_private.registers); +} + +static int intel_i830_insert_entries(struct agp_memory *mem, off_t pg_start, + int type) +{ + int i, j, num_entries; + void *temp; + int ret = -EINVAL; + int mask_type; + + if (mem->page_count == 0) + goto out; + + temp = agp_bridge->current_size; + num_entries = A_SIZE_FIX(temp)->num_entries; + + if (pg_start < intel_private.gtt_entries) { + dev_printk(KERN_DEBUG, &intel_private.pcidev->dev, + "pg_start == 0x%.8lx, intel_private.gtt_entries == 0x%.8x\n", + pg_start, intel_private.gtt_entries); + + dev_info(&intel_private.pcidev->dev, + "trying to insert into local/stolen memory\n"); + goto out_err; + } + + if ((pg_start + mem->page_count) > num_entries) + goto out_err; + + /* The i830 can't check the GTT for entries since its read only, + * depend on the caller to make the correct offset decisions. + */ + + if (type != mem->type) + goto out_err; + + mask_type = agp_bridge->driver->agp_type_to_mask_type(agp_bridge, type); + + if (mask_type != 0 && mask_type != AGP_PHYS_MEMORY && + mask_type != INTEL_AGP_CACHED_MEMORY) + goto out_err; + + if (!mem->is_flushed) + global_cache_flush(); + + for (i = 0, j = pg_start; i < mem->page_count; i++, j++) { + writel(agp_bridge->driver->mask_memory(agp_bridge, + page_to_phys(mem->pages[i]), mask_type), + intel_private.registers+I810_PTE_BASE+(j*4)); + } + readl(intel_private.registers+I810_PTE_BASE+((j-1)*4)); + +out: + ret = 0; +out_err: + mem->is_flushed = true; + return ret; +} + +static int intel_i830_remove_entries(struct agp_memory *mem, off_t pg_start, + int type) +{ + int i; + + if (mem->page_count == 0) + return 0; + + if (pg_start < intel_private.gtt_entries) { + dev_info(&intel_private.pcidev->dev, + "trying to disable local/stolen memory\n"); + return -EINVAL; + } + + for (i = pg_start; i < (mem->page_count + pg_start); i++) { + writel(agp_bridge->scratch_page, intel_private.registers+I810_PTE_BASE+(i*4)); + } + readl(intel_private.registers+I810_PTE_BASE+((i-1)*4)); + + return 0; +} + +static struct agp_memory *intel_i830_alloc_by_type(size_t pg_count, int type) +{ + if (type == AGP_PHYS_MEMORY) + return alloc_agpphysmem_i8xx(pg_count, type); + /* always return NULL for other allocation types for now */ + return NULL; +} + +static int intel_alloc_chipset_flush_resource(void) +{ + int ret; + ret = pci_bus_alloc_resource(agp_bridge->dev->bus, &intel_private.ifp_resource, PAGE_SIZE, + PAGE_SIZE, PCIBIOS_MIN_MEM, 0, + pcibios_align_resource, agp_bridge->dev); + + return ret; +} + +static void intel_i915_setup_chipset_flush(void) +{ + int ret; + u32 temp; + + pci_read_config_dword(agp_bridge->dev, I915_IFPADDR, &temp); + if (!(temp & 0x1)) { + intel_alloc_chipset_flush_resource(); + intel_private.resource_valid = 1; + pci_write_config_dword(agp_bridge->dev, I915_IFPADDR, (intel_private.ifp_resource.start & 0xffffffff) | 0x1); + } else { + temp &= ~1; + + intel_private.resource_valid = 1; + intel_private.ifp_resource.start = temp; + intel_private.ifp_resource.end = temp + PAGE_SIZE; + ret = request_resource(&iomem_resource, &intel_private.ifp_resource); + /* some BIOSes reserve this area in a pnp some don't */ + if (ret) + intel_private.resource_valid = 0; + } +} + +static void intel_i965_g33_setup_chipset_flush(void) +{ + u32 temp_hi, temp_lo; + int ret; + + pci_read_config_dword(agp_bridge->dev, I965_IFPADDR + 4, &temp_hi); + pci_read_config_dword(agp_bridge->dev, I965_IFPADDR, &temp_lo); + + if (!(temp_lo & 0x1)) { + + intel_alloc_chipset_flush_resource(); + + intel_private.resource_valid = 1; + pci_write_config_dword(agp_bridge->dev, I965_IFPADDR + 4, + upper_32_bits(intel_private.ifp_resource.start)); + pci_write_config_dword(agp_bridge->dev, I965_IFPADDR, (intel_private.ifp_resource.start & 0xffffffff) | 0x1); + } else { + u64 l64; + + temp_lo &= ~0x1; + l64 = ((u64)temp_hi << 32) | temp_lo; + + intel_private.resource_valid = 1; + intel_private.ifp_resource.start = l64; + intel_private.ifp_resource.end = l64 + PAGE_SIZE; + ret = request_resource(&iomem_resource, &intel_private.ifp_resource); + /* some BIOSes reserve this area in a pnp some don't */ + if (ret) + intel_private.resource_valid = 0; + } +} + +static void intel_i9xx_setup_flush(void) +{ + /* return if already configured */ + if (intel_private.ifp_resource.start) + return; + + if (IS_SNB) + return; + + /* setup a resource for this object */ + intel_private.ifp_resource.name = "Intel Flush Page"; + intel_private.ifp_resource.flags = IORESOURCE_MEM; + + /* Setup chipset flush for 915 */ + if (IS_I965 || IS_G33 || IS_G4X) { + intel_i965_g33_setup_chipset_flush(); + } else { + intel_i915_setup_chipset_flush(); + } + + if (intel_private.ifp_resource.start) { + intel_private.i9xx_flush_page = ioremap_nocache(intel_private.ifp_resource.start, PAGE_SIZE); + if (!intel_private.i9xx_flush_page) + dev_info(&intel_private.pcidev->dev, "can't ioremap flush page - no chipset flushing"); + } +} + +static int intel_i915_configure(void) +{ + struct aper_size_info_fixed *current_size; + u32 temp; + u16 gmch_ctrl; + int i; + + current_size = A_SIZE_FIX(agp_bridge->current_size); + + pci_read_config_dword(intel_private.pcidev, I915_GMADDR, &temp); + + agp_bridge->gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK); + + pci_read_config_word(agp_bridge->dev, I830_GMCH_CTRL, &gmch_ctrl); + gmch_ctrl |= I830_GMCH_ENABLED; + pci_write_config_word(agp_bridge->dev, I830_GMCH_CTRL, gmch_ctrl); + + writel(agp_bridge->gatt_bus_addr|I810_PGETBL_ENABLED, intel_private.registers+I810_PGETBL_CTL); + readl(intel_private.registers+I810_PGETBL_CTL); /* PCI Posting. */ + + if (agp_bridge->driver->needs_scratch_page) { + for (i = intel_private.gtt_entries; i < intel_private.gtt_total_size; i++) { + writel(agp_bridge->scratch_page, intel_private.gtt+i); + } + readl(intel_private.gtt+i-1); /* PCI Posting. */ + } + + global_cache_flush(); + + intel_i9xx_setup_flush(); + + return 0; +} + +static void intel_i915_cleanup(void) +{ + if (intel_private.i9xx_flush_page) + iounmap(intel_private.i9xx_flush_page); + if (intel_private.resource_valid) + release_resource(&intel_private.ifp_resource); + intel_private.ifp_resource.start = 0; + intel_private.resource_valid = 0; + iounmap(intel_private.gtt); + iounmap(intel_private.registers); +} + +static void intel_i915_chipset_flush(struct agp_bridge_data *bridge) +{ + if (intel_private.i9xx_flush_page) + writel(1, intel_private.i9xx_flush_page); +} + +static int intel_i915_insert_entries(struct agp_memory *mem, off_t pg_start, + int type) +{ + int num_entries; + void *temp; + int ret = -EINVAL; + int mask_type; + + if (mem->page_count == 0) + goto out; + + temp = agp_bridge->current_size; + num_entries = A_SIZE_FIX(temp)->num_entries; + + if (pg_start < intel_private.gtt_entries) { + dev_printk(KERN_DEBUG, &intel_private.pcidev->dev, + "pg_start == 0x%.8lx, intel_private.gtt_entries == 0x%.8x\n", + pg_start, intel_private.gtt_entries); + + dev_info(&intel_private.pcidev->dev, + "trying to insert into local/stolen memory\n"); + goto out_err; + } + + if ((pg_start + mem->page_count) > num_entries) + goto out_err; + + /* The i915 can't check the GTT for entries since it's read only; + * depend on the caller to make the correct offset decisions. + */ + + if (type != mem->type) + goto out_err; + + mask_type = agp_bridge->driver->agp_type_to_mask_type(agp_bridge, type); + + if (mask_type != 0 && mask_type != AGP_PHYS_MEMORY && + mask_type != INTEL_AGP_CACHED_MEMORY) + goto out_err; + + if (!mem->is_flushed) + global_cache_flush(); + + intel_agp_insert_sg_entries(mem, pg_start, mask_type); + + out: + ret = 0; + out_err: + mem->is_flushed = true; + return ret; +} + +static int intel_i915_remove_entries(struct agp_memory *mem, off_t pg_start, + int type) +{ + int i; + + if (mem->page_count == 0) + return 0; + + if (pg_start < intel_private.gtt_entries) { + dev_info(&intel_private.pcidev->dev, + "trying to disable local/stolen memory\n"); + return -EINVAL; + } + + for (i = pg_start; i < (mem->page_count + pg_start); i++) + writel(agp_bridge->scratch_page, intel_private.gtt+i); + + readl(intel_private.gtt+i-1); + + return 0; +} + +/* Return the aperture size by just checking the resource length. The effect + * described in the spec of the MSAC registers is just changing of the + * resource size. + */ +static int intel_i9xx_fetch_size(void) +{ + int num_sizes = ARRAY_SIZE(intel_i830_sizes); + int aper_size; /* size in megabytes */ + int i; + + aper_size = pci_resource_len(intel_private.pcidev, 2) / MB(1); + + for (i = 0; i < num_sizes; i++) { + if (aper_size == intel_i830_sizes[i].size) { + agp_bridge->current_size = intel_i830_sizes + i; + return aper_size; + } + } + + return 0; +} + +/* The intel i915 automatically initializes the agp aperture during POST. + * Use the memory already set aside for in the GTT. + */ +static int intel_i915_create_gatt_table(struct agp_bridge_data *bridge) +{ + int page_order; + struct aper_size_info_fixed *size; + int num_entries; + u32 temp, temp2; + int gtt_map_size = 256 * 1024; + + 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_private.pcidev, I915_MMADDR, &temp); + pci_read_config_dword(intel_private.pcidev, I915_PTEADDR, &temp2); + + if (IS_G33) + gtt_map_size = 1024 * 1024; /* 1M on G33 */ + intel_private.gtt = ioremap(temp2, gtt_map_size); + if (!intel_private.gtt) + return -ENOMEM; + + intel_private.gtt_total_size = gtt_map_size / 4; + + temp &= 0xfff80000; + + intel_private.registers = ioremap(temp, 128 * 4096); + if (!intel_private.registers) { + iounmap(intel_private.gtt); + return -ENOMEM; + } + + temp = readl(intel_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; +} + +/* + * The i965 supports 36-bit physical addresses, but to keep + * the format of the GTT the same, the bits that don't fit + * in a 32-bit word are shifted down to bits 4..7. + * + * Gcc is smart enough to notice that "(addr >> 28) & 0xf0" + * is always zero on 32-bit architectures, so no need to make + * this conditional. + */ +static unsigned long intel_i965_mask_memory(struct agp_bridge_data *bridge, + dma_addr_t addr, int type) +{ + /* Shift high bits down */ + addr |= (addr >> 28) & 0xf0; + + /* Type checking must be done elsewhere */ + return addr | bridge->driver->masks[type].mask; +} + +static void intel_i965_get_gtt_range(int *gtt_offset, int *gtt_size) +{ + u16 snb_gmch_ctl; + + switch (agp_bridge->dev->device) { + case PCI_DEVICE_ID_INTEL_GM45_HB: + case PCI_DEVICE_ID_INTEL_EAGLELAKE_HB: + case PCI_DEVICE_ID_INTEL_Q45_HB: + case PCI_DEVICE_ID_INTEL_G45_HB: + case PCI_DEVICE_ID_INTEL_G41_HB: + case PCI_DEVICE_ID_INTEL_B43_HB: + case PCI_DEVICE_ID_INTEL_IRONLAKE_D_HB: + case PCI_DEVICE_ID_INTEL_IRONLAKE_M_HB: + case PCI_DEVICE_ID_INTEL_IRONLAKE_MA_HB: + case PCI_DEVICE_ID_INTEL_IRONLAKE_MC2_HB: + *gtt_offset = *gtt_size = MB(2); + break; + case PCI_DEVICE_ID_INTEL_SANDYBRIDGE_HB: + case PCI_DEVICE_ID_INTEL_SANDYBRIDGE_M_HB: + *gtt_offset = MB(2); + + pci_read_config_word(intel_private.pcidev, SNB_GMCH_CTRL, &snb_gmch_ctl); + switch (snb_gmch_ctl & SNB_GTT_SIZE_MASK) { + default: + case SNB_GTT_SIZE_0M: + printk(KERN_ERR "Bad GTT size mask: 0x%04x.\n", snb_gmch_ctl); + *gtt_size = MB(0); + break; + case SNB_GTT_SIZE_1M: + *gtt_size = MB(1); + break; + case SNB_GTT_SIZE_2M: + *gtt_size = MB(2); + break; + } + break; + default: + *gtt_offset = *gtt_size = KB(512); + } +} + +/* 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; + int gtt_offset, gtt_size; + + 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_private.pcidev, I915_MMADDR, &temp); + + temp &= 0xfff00000; + + intel_i965_get_gtt_range(>t_offset, >t_size); + + intel_private.gtt = ioremap((temp + gtt_offset) , gtt_size); + + if (!intel_private.gtt) + return -ENOMEM; + + intel_private.gtt_total_size = gtt_size / 4; + + intel_private.registers = ioremap(temp, 128 * 4096); + if (!intel_private.registers) { + iounmap(intel_private.gtt); + return -ENOMEM; + } + + temp = readl(intel_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 const struct agp_bridge_driver intel_810_driver = { + .owner = THIS_MODULE, + .aperture_sizes = intel_i810_sizes, + .size_type = FIXED_APER_SIZE, + .num_aperture_sizes = 2, + .needs_scratch_page = true, + .configure = intel_i810_configure, + .fetch_size = intel_i810_fetch_size, + .cleanup = intel_i810_cleanup, + .mask_memory = intel_i810_mask_memory, + .masks = intel_i810_masks, + .agp_enable = intel_i810_agp_enable, + .cache_flush = global_cache_flush, + .create_gatt_table = agp_generic_create_gatt_table, + .free_gatt_table = agp_generic_free_gatt_table, + .insert_memory = intel_i810_insert_entries, + .remove_memory = intel_i810_remove_entries, + .alloc_by_type = intel_i810_alloc_by_type, + .free_by_type = intel_i810_free_by_type, + .agp_alloc_page = agp_generic_alloc_page, + .agp_alloc_pages = agp_generic_alloc_pages, + .agp_destroy_page = agp_generic_destroy_page, + .agp_destroy_pages = agp_generic_destroy_pages, + .agp_type_to_mask_type = agp_generic_type_to_mask_type, +}; + +static const struct agp_bridge_driver intel_830_driver = { + .owner = THIS_MODULE, + .aperture_sizes = intel_i830_sizes, + .size_type = FIXED_APER_SIZE, + .num_aperture_sizes = 4, + .needs_scratch_page = true, + .configure = intel_i830_configure, + .fetch_size = intel_i830_fetch_size, + .cleanup = intel_i830_cleanup, + .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_i830_create_gatt_table, + .free_gatt_table = intel_i830_free_gatt_table, + .insert_memory = intel_i830_insert_entries, + .remove_memory = intel_i830_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_alloc_pages = agp_generic_alloc_pages, + .agp_destroy_page = agp_generic_destroy_page, + .agp_destroy_pages = agp_generic_destroy_pages, + .agp_type_to_mask_type = intel_i830_type_to_mask_type, + .chipset_flush = intel_i830_chipset_flush, +}; + +static const struct agp_bridge_driver intel_915_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_i9xx_fetch_size, + .cleanup = intel_i915_cleanup, + .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_i915_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_alloc_pages = agp_generic_alloc_pages, + .agp_destroy_page = agp_generic_destroy_page, + .agp_destroy_pages = agp_generic_destroy_pages, + .agp_type_to_mask_type = intel_i830_type_to_mask_type, + .chipset_flush = intel_i915_chipset_flush, +#ifdef USE_PCI_DMA_API + .agp_map_page = intel_agp_map_page, + .agp_unmap_page = intel_agp_unmap_page, + .agp_map_memory = intel_agp_map_memory, + .agp_unmap_memory = intel_agp_unmap_memory, +#endif +}; + +static const 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_i9xx_fetch_size, + .cleanup = intel_i915_cleanup, + .mask_memory = intel_i965_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_alloc_pages = agp_generic_alloc_pages, + .agp_destroy_page = agp_generic_destroy_page, + .agp_destroy_pages = agp_generic_destroy_pages, + .agp_type_to_mask_type = intel_i830_type_to_mask_type, + .chipset_flush = intel_i915_chipset_flush, +#ifdef USE_PCI_DMA_API + .agp_map_page = intel_agp_map_page, + .agp_unmap_page = intel_agp_unmap_page, + .agp_map_memory = intel_agp_map_memory, + .agp_unmap_memory = intel_agp_unmap_memory, +#endif +}; + +static const struct agp_bridge_driver intel_g33_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_i9xx_fetch_size, + .cleanup = intel_i915_cleanup, + .mask_memory = intel_i965_mask_memory, + .masks = intel_i810_masks, + .agp_enable = intel_i810_agp_enable, + .cache_flush = global_cache_flush, + .create_gatt_table = intel_i915_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_alloc_pages = agp_generic_alloc_pages, + .agp_destroy_page = agp_generic_destroy_page, + .agp_destroy_pages = agp_generic_destroy_pages, + .agp_type_to_mask_type = intel_i830_type_to_mask_type, + .chipset_flush = intel_i915_chipset_flush, +#ifdef USE_PCI_DMA_API + .agp_map_page = intel_agp_map_page, + .agp_unmap_page = intel_agp_unmap_page, + .agp_map_memory = intel_agp_map_memory, + .agp_unmap_memory = intel_agp_unmap_memory, +#endif +}; diff --git a/drivers/char/agp/nvidia-agp.c b/drivers/char/agp/nvidia-agp.c index 10f24e349a26..b9734a978186 100644 --- a/drivers/char/agp/nvidia-agp.c +++ b/drivers/char/agp/nvidia-agp.c @@ -310,6 +310,7 @@ static const struct agp_bridge_driver nvidia_driver = { .aperture_sizes = nvidia_generic_sizes, .size_type = U8_APER_SIZE, .num_aperture_sizes = 5, + .needs_scratch_page = true, .configure = nvidia_configure, .fetch_size = nvidia_fetch_size, .cleanup = nvidia_cleanup, diff --git a/drivers/char/agp/sis-agp.c b/drivers/char/agp/sis-agp.c index 6c3837a0184d..29aacd81de78 100644 --- a/drivers/char/agp/sis-agp.c +++ b/drivers/char/agp/sis-agp.c @@ -125,6 +125,7 @@ static struct agp_bridge_driver sis_driver = { .aperture_sizes = sis_generic_sizes, .size_type = U8_APER_SIZE, .num_aperture_sizes = 7, + .needs_scratch_page = true, .configure = sis_configure, .fetch_size = sis_fetch_size, .cleanup = sis_cleanup, @@ -415,14 +416,6 @@ static struct pci_device_id agp_sis_pci_table[] = { .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, }, - { - .class = (PCI_CLASS_BRIDGE_HOST << 8), - .class_mask = ~0, - .vendor = PCI_VENDOR_ID_SI, - .device = PCI_DEVICE_ID_SI_760, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - }, { } }; diff --git a/drivers/char/agp/uninorth-agp.c b/drivers/char/agp/uninorth-agp.c index 6f48931ac1ce..95db71360d24 100644 --- a/drivers/char/agp/uninorth-agp.c +++ b/drivers/char/agp/uninorth-agp.c @@ -28,6 +28,7 @@ */ static int uninorth_rev; static int is_u3; +static u32 scratch_value; #define DEFAULT_APERTURE_SIZE 256 #define DEFAULT_APERTURE_STRING "256" @@ -172,7 +173,7 @@ static int uninorth_insert_memory(struct agp_memory *mem, off_t pg_start, int ty gp = (u32 *) &agp_bridge->gatt_table[pg_start]; for (i = 0; i < mem->page_count; ++i) { - if (gp[i]) { + if (gp[i] != scratch_value) { dev_info(&agp_bridge->dev->dev, "uninorth_insert_memory: entry 0x%x occupied (%x)\n", i, gp[i]); @@ -214,8 +215,9 @@ int uninorth_remove_memory(struct agp_memory *mem, off_t pg_start, int type) return 0; gp = (u32 *) &agp_bridge->gatt_table[pg_start]; - for (i = 0; i < mem->page_count; ++i) - gp[i] = 0; + for (i = 0; i < mem->page_count; ++i) { + gp[i] = scratch_value; + } mb(); uninorth_tlbflush(mem); @@ -421,8 +423,13 @@ static int uninorth_create_gatt_table(struct agp_bridge_data *bridge) bridge->gatt_bus_addr = virt_to_phys(table); + if (is_u3) + scratch_value = (page_to_phys(agp_bridge->scratch_page_page) >> PAGE_SHIFT) | 0x80000000UL; + else + scratch_value = cpu_to_le32((page_to_phys(agp_bridge->scratch_page_page) & 0xFFFFF000UL) | + 0x1UL); for (i = 0; i < num_entries; i++) - bridge->gatt_table[i] = 0; + bridge->gatt_table[i] = scratch_value; return 0; @@ -519,6 +526,7 @@ const struct agp_bridge_driver uninorth_agp_driver = { .agp_destroy_pages = agp_generic_destroy_pages, .agp_type_to_mask_type = agp_generic_type_to_mask_type, .cant_use_aperture = true, + .needs_scratch_page = true, }; const struct agp_bridge_driver u3_agp_driver = { diff --git a/drivers/char/agp/via-agp.c b/drivers/char/agp/via-agp.c index d3bd243867fc..df67e80019d2 100644 --- a/drivers/char/agp/via-agp.c +++ b/drivers/char/agp/via-agp.c @@ -175,6 +175,7 @@ static const struct agp_bridge_driver via_agp3_driver = { .aperture_sizes = agp3_generic_sizes, .size_type = U8_APER_SIZE, .num_aperture_sizes = 10, + .needs_scratch_page = true, .configure = via_configure_agp3, .fetch_size = via_fetch_size_agp3, .cleanup = via_cleanup_agp3, @@ -201,6 +202,7 @@ static const struct agp_bridge_driver via_driver = { .aperture_sizes = via_generic_sizes, .size_type = U8_APER_SIZE, .num_aperture_sizes = 9, + .needs_scratch_page = true, .configure = via_configure, .fetch_size = via_fetch_size, .cleanup = via_cleanup, diff --git a/drivers/char/amiserial.c b/drivers/char/amiserial.c index 56b27671adc4..4f8d60c25a98 100644 --- a/drivers/char/amiserial.c +++ b/drivers/char/amiserial.c @@ -84,6 +84,7 @@ static char *serial_version = "4.30"; #include <linux/smp_lock.h> #include <linux/init.h> #include <linux/bitops.h> +#include <linux/platform_device.h> #include <asm/setup.h> @@ -1954,29 +1955,16 @@ static const struct tty_operations serial_ops = { /* * The serial driver boot-time initialization code! */ -static int __init rs_init(void) +static int __init amiga_serial_probe(struct platform_device *pdev) { unsigned long flags; struct serial_state * state; int error; - if (!MACH_IS_AMIGA || !AMIGAHW_PRESENT(AMI_SERIAL)) - return -ENODEV; - serial_driver = alloc_tty_driver(1); if (!serial_driver) return -ENOMEM; - /* - * We request SERDAT and SERPER only, because the serial registers are - * too spreaded over the custom register space - */ - if (!request_mem_region(CUSTOM_PHYSADDR+0x30, 4, - "amiserial [Paula]")) { - error = -EBUSY; - goto fail_put_tty_driver; - } - IRQ_ports = NULL; show_serial_version(); @@ -1998,7 +1986,7 @@ static int __init rs_init(void) error = tty_register_driver(serial_driver); if (error) - goto fail_release_mem_region; + goto fail_put_tty_driver; state = rs_table; state->magic = SSTATE_MAGIC; @@ -2050,23 +2038,24 @@ static int __init rs_init(void) ciab.ddra |= (SER_DTR | SER_RTS); /* outputs */ ciab.ddra &= ~(SER_DCD | SER_CTS | SER_DSR); /* inputs */ + platform_set_drvdata(pdev, state); + return 0; fail_free_irq: free_irq(IRQ_AMIGA_TBE, state); fail_unregister: tty_unregister_driver(serial_driver); -fail_release_mem_region: - release_mem_region(CUSTOM_PHYSADDR+0x30, 4); fail_put_tty_driver: put_tty_driver(serial_driver); return error; } -static __exit void rs_exit(void) +static int __exit amiga_serial_remove(struct platform_device *pdev) { int error; - struct async_struct *info = rs_table[0].info; + struct serial_state *state = platform_get_drvdata(pdev); + struct async_struct *info = state->info; /* printk("Unloading %s: version %s\n", serial_name, serial_version); */ tasklet_kill(&info->tlet); @@ -2075,19 +2064,38 @@ static __exit void rs_exit(void) error); put_tty_driver(serial_driver); - if (info) { - rs_table[0].info = NULL; - kfree(info); - } + rs_table[0].info = NULL; + kfree(info); free_irq(IRQ_AMIGA_TBE, rs_table); free_irq(IRQ_AMIGA_RBF, rs_table); - release_mem_region(CUSTOM_PHYSADDR+0x30, 4); + platform_set_drvdata(pdev, NULL); + + return error; +} + +static struct platform_driver amiga_serial_driver = { + .remove = __exit_p(amiga_serial_remove), + .driver = { + .name = "amiga-serial", + .owner = THIS_MODULE, + }, +}; + +static int __init amiga_serial_init(void) +{ + return platform_driver_probe(&amiga_serial_driver, amiga_serial_probe); +} + +module_init(amiga_serial_init); + +static void __exit amiga_serial_exit(void) +{ + platform_driver_unregister(&amiga_serial_driver); } -module_init(rs_init) -module_exit(rs_exit) +module_exit(amiga_serial_exit); #if defined(CONFIG_SERIAL_CONSOLE) && !defined(MODULE) @@ -2154,3 +2162,4 @@ console_initcall(amiserial_console_init); #endif /* CONFIG_SERIAL_CONSOLE && !MODULE */ MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:amiga-serial"); diff --git a/drivers/char/apm-emulation.c b/drivers/char/apm-emulation.c index 4f568cb9af3f..033e1505fca9 100644 --- a/drivers/char/apm-emulation.c +++ b/drivers/char/apm-emulation.c @@ -265,8 +265,8 @@ static unsigned int apm_poll(struct file *fp, poll_table * wait) * Only when everyone who has opened /dev/apm_bios with write permission * has acknowledge does the actual suspend happen. */ -static int -apm_ioctl(struct inode * inode, struct file *filp, u_int cmd, u_long arg) +static long +apm_ioctl(struct file *filp, u_int cmd, u_long arg) { struct apm_user *as = filp->private_data; int err = -EINVAL; @@ -274,6 +274,7 @@ apm_ioctl(struct inode * inode, struct file *filp, u_int cmd, u_long arg) if (!as->suser || !as->writer) return -EPERM; + lock_kernel(); switch (cmd) { case APM_IOC_SUSPEND: mutex_lock(&state_lock); @@ -334,6 +335,7 @@ apm_ioctl(struct inode * inode, struct file *filp, u_int cmd, u_long arg) mutex_unlock(&state_lock); break; } + unlock_kernel(); return err; } @@ -397,7 +399,7 @@ static const struct file_operations apm_bios_fops = { .owner = THIS_MODULE, .read = apm_read, .poll = apm_poll, - .ioctl = apm_ioctl, + .unlocked_ioctl = apm_ioctl, .open = apm_open, .release = apm_release, }; diff --git a/drivers/char/applicom.c b/drivers/char/applicom.c index a7424bf7eacf..f4ae0e0fb631 100644 --- a/drivers/char/applicom.c +++ b/drivers/char/applicom.c @@ -26,6 +26,7 @@ #include <linux/sched.h> #include <linux/slab.h> #include <linux/errno.h> +#include <linux/smp_lock.h> #include <linux/miscdevice.h> #include <linux/pci.h> #include <linux/wait.h> @@ -106,8 +107,7 @@ static unsigned int DeviceErrorCount; /* number of device error */ static ssize_t ac_read (struct file *, char __user *, size_t, loff_t *); static ssize_t ac_write (struct file *, const char __user *, size_t, loff_t *); -static int ac_ioctl(struct inode *, struct file *, unsigned int, - unsigned long); +static long ac_ioctl(struct file *, unsigned int, unsigned long); static irqreturn_t ac_interrupt(int, void *); static const struct file_operations ac_fops = { @@ -115,7 +115,7 @@ static const struct file_operations ac_fops = { .llseek = no_llseek, .read = ac_read, .write = ac_write, - .ioctl = ac_ioctl, + .unlocked_ioctl = ac_ioctl, }; static struct miscdevice ac_miscdev = { @@ -689,7 +689,7 @@ static irqreturn_t ac_interrupt(int vec, void *dev_instance) -static int ac_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +static long ac_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { /* @ ADG ou ATO selon le cas */ int i; @@ -703,15 +703,11 @@ static int ac_ioctl(struct inode *inode, struct file *file, unsigned int cmd, un /* In general, the device is only openable by root anyway, so we're not particularly concerned that bogus ioctls can flood the console. */ - adgl = kmalloc(sizeof(struct st_ram_io), GFP_KERNEL); - if (!adgl) - return -ENOMEM; + adgl = memdup_user(argp, sizeof(struct st_ram_io)); + if (IS_ERR(adgl)) + return PTR_ERR(adgl); - if (copy_from_user(adgl, argp, sizeof(struct st_ram_io))) { - kfree(adgl); - return -EFAULT; - } - + lock_kernel(); IndexCard = adgl->num_card-1; if(cmd != 6 && ((IndexCard >= MAX_BOARD) || !apbs[IndexCard].RamIO)) { @@ -721,6 +717,7 @@ static int ac_ioctl(struct inode *inode, struct file *file, unsigned int cmd, un warncount--; } kfree(adgl); + unlock_kernel(); return -EINVAL; } @@ -838,6 +835,7 @@ static int ac_ioctl(struct inode *inode, struct file *file, unsigned int cmd, un } Dummy = readb(apbs[IndexCard].RamIO + VERS); kfree(adgl); + unlock_kernel(); return 0; } diff --git a/drivers/char/ds1620.c b/drivers/char/ds1620.c index 61f0146e215d..dbee8688f75c 100644 --- a/drivers/char/ds1620.c +++ b/drivers/char/ds1620.c @@ -232,7 +232,7 @@ ds1620_read(struct file *file, char __user *buf, size_t count, loff_t *ptr) } static int -ds1620_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +ds1620_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { struct therm therm; union { @@ -316,6 +316,18 @@ ds1620_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned return 0; } +static long +ds1620_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + int ret; + + lock_kernel(); + ret = ds1620_ioctl(file, cmd, arg); + unlock_kernel(); + + return ret; +} + #ifdef THERM_USE_PROC static int proc_therm_ds1620_read(char *buf, char **start, off_t offset, @@ -344,7 +356,7 @@ static const struct file_operations ds1620_fops = { .owner = THIS_MODULE, .open = ds1620_open, .read = ds1620_read, - .ioctl = ds1620_ioctl, + .unlocked_ioctl = ds1620_unlocked_ioctl, }; static struct miscdevice ds1620_miscdev = { diff --git a/drivers/char/dtlk.c b/drivers/char/dtlk.c index 045c930e6320..e3859d4eaead 100644 --- a/drivers/char/dtlk.c +++ b/drivers/char/dtlk.c @@ -93,8 +93,8 @@ static ssize_t dtlk_write(struct file *, const char __user *, static unsigned int dtlk_poll(struct file *, poll_table *); static int dtlk_open(struct inode *, struct file *); static int dtlk_release(struct inode *, struct file *); -static int dtlk_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg); +static long dtlk_ioctl(struct file *file, + unsigned int cmd, unsigned long arg); static const struct file_operations dtlk_fops = { @@ -102,7 +102,7 @@ static const struct file_operations dtlk_fops = .read = dtlk_read, .write = dtlk_write, .poll = dtlk_poll, - .ioctl = dtlk_ioctl, + .unlocked_ioctl = dtlk_ioctl, .open = dtlk_open, .release = dtlk_release, }; @@ -263,10 +263,9 @@ static void dtlk_timer_tick(unsigned long data) wake_up_interruptible(&dtlk_process_list); } -static int dtlk_ioctl(struct inode *inode, - struct file *file, - unsigned int cmd, - unsigned long arg) +static long dtlk_ioctl(struct file *file, + unsigned int cmd, + unsigned long arg) { char __user *argp = (char __user *)arg; struct dtlk_settings *sp; @@ -276,7 +275,9 @@ static int dtlk_ioctl(struct inode *inode, switch (cmd) { case DTLK_INTERROGATE: + lock_kernel(); sp = dtlk_interrogate(); + unlock_kernel(); if (copy_to_user(argp, sp, sizeof(struct dtlk_settings))) return -EINVAL; return 0; diff --git a/drivers/char/generic_nvram.c b/drivers/char/generic_nvram.c index fda4181b5e67..82b5a88a82d7 100644 --- a/drivers/char/generic_nvram.c +++ b/drivers/char/generic_nvram.c @@ -19,6 +19,7 @@ #include <linux/miscdevice.h> #include <linux/fcntl.h> #include <linux/init.h> +#include <linux/smp_lock.h> #include <asm/uaccess.h> #include <asm/nvram.h> #ifdef CONFIG_PPC_PMAC @@ -84,8 +85,7 @@ static ssize_t write_nvram(struct file *file, const char __user *buf, return p - buf; } -static int nvram_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) +static int nvram_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { switch(cmd) { #ifdef CONFIG_PPC_PMAC @@ -116,12 +116,23 @@ static int nvram_ioctl(struct inode *inode, struct file *file, return 0; } +static long nvram_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + int ret; + + lock_kernel(); + ret = nvram_ioctl(file, cmd, arg); + unlock_kernel(); + + return ret; +} + const struct file_operations nvram_fops = { .owner = THIS_MODULE, .llseek = nvram_llseek, .read = read_nvram, .write = write_nvram, - .ioctl = nvram_ioctl, + .unlocked_ioctl = nvram_unlocked_ioctl, }; static struct miscdevice nvram_dev = { diff --git a/drivers/char/genrtc.c b/drivers/char/genrtc.c index 31e7c91c2d9d..b6c2cc167c11 100644 --- a/drivers/char/genrtc.c +++ b/drivers/char/genrtc.c @@ -262,7 +262,7 @@ static inline int gen_set_rtc_irq_bit(unsigned char bit) #endif } -static int gen_rtc_ioctl(struct inode *inode, struct file *file, +static int gen_rtc_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { struct rtc_time wtime; @@ -332,6 +332,18 @@ static int gen_rtc_ioctl(struct inode *inode, struct file *file, return -EINVAL; } +static long gen_rtc_unlocked_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + int ret; + + lock_kernel(); + ret = gen_rtc_ioctl(file, cmd, arg); + unlock_kernel(); + + return ret; +} + /* * We enforce only one user at a time here with the open/close. * Also clear the previous interrupt data on an open, and clean @@ -482,7 +494,7 @@ static const struct file_operations gen_rtc_fops = { .read = gen_rtc_read, .poll = gen_rtc_poll, #endif - .ioctl = gen_rtc_ioctl, + .unlocked_ioctl = gen_rtc_unlocked_ioctl, .open = gen_rtc_open, .release = gen_rtc_release, }; diff --git a/drivers/char/hangcheck-timer.c b/drivers/char/hangcheck-timer.c index 712d9f271aa6..e0249722d25f 100644 --- a/drivers/char/hangcheck-timer.c +++ b/drivers/char/hangcheck-timer.c @@ -49,8 +49,9 @@ #include <asm/uaccess.h> #include <linux/sysrq.h> #include <linux/timer.h> +#include <linux/time.h> -#define VERSION_STR "0.9.0" +#define VERSION_STR "0.9.1" #define DEFAULT_IOFENCE_MARGIN 60 /* Default fudge factor, in seconds */ #define DEFAULT_IOFENCE_TICK 180 /* Default timer timeout, in seconds */ @@ -119,10 +120,8 @@ __setup("hcheck_dump_tasks", hangcheck_parse_dump_tasks); #if defined(CONFIG_S390) # define HAVE_MONOTONIC # define TIMER_FREQ 1000000000ULL -#elif defined(CONFIG_IA64) -# define TIMER_FREQ ((unsigned long long)local_cpu_data->itc_freq) #else -# define TIMER_FREQ (HZ*loops_per_jiffy) +# define TIMER_FREQ 1000000000ULL #endif #ifdef HAVE_MONOTONIC @@ -130,7 +129,9 @@ extern unsigned long long monotonic_clock(void); #else static inline unsigned long long monotonic_clock(void) { - return get_cycles(); + struct timespec ts; + getrawmonotonic(&ts); + return timespec_to_ns(&ts); } #endif /* HAVE_MONOTONIC */ @@ -168,6 +169,13 @@ static void hangcheck_fire(unsigned long data) printk(KERN_CRIT "Hangcheck: hangcheck value past margin!\n"); } } +#if 0 + /* + * Enable to investigate delays in detail + */ + printk("Hangcheck: called %Ld ns since last time (%Ld ns overshoot)\n", + tsc_diff, tsc_diff - hangcheck_tick*TIMER_FREQ); +#endif mod_timer(&hangcheck_ticktock, jiffies + (hangcheck_tick*HZ)); hangcheck_tsc = monotonic_clock(); } @@ -180,7 +188,7 @@ static int __init hangcheck_init(void) #if defined (HAVE_MONOTONIC) printk("Hangcheck: Using monotonic_clock().\n"); #else - printk("Hangcheck: Using get_cycles().\n"); + printk("Hangcheck: Using getrawmonotonic().\n"); #endif /* HAVE_MONOTONIC */ hangcheck_tsc_margin = (unsigned long long)(hangcheck_margin + hangcheck_tick); diff --git a/drivers/char/hpet.c b/drivers/char/hpet.c index 9ded667625ac..a0a1829d3198 100644 --- a/drivers/char/hpet.c +++ b/drivers/char/hpet.c @@ -431,14 +431,18 @@ static int hpet_release(struct inode *inode, struct file *file) static int hpet_ioctl_common(struct hpet_dev *, int, unsigned long, int); -static int -hpet_ioctl(struct inode *inode, struct file *file, unsigned int cmd, - unsigned long arg) +static long hpet_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) { struct hpet_dev *devp; + int ret; devp = file->private_data; - return hpet_ioctl_common(devp, cmd, arg, 0); + lock_kernel(); + ret = hpet_ioctl_common(devp, cmd, arg, 0); + unlock_kernel(); + + return ret; } static int hpet_ioctl_ieon(struct hpet_dev *devp) @@ -654,7 +658,7 @@ static const struct file_operations hpet_fops = { .llseek = no_llseek, .read = hpet_read, .poll = hpet_poll, - .ioctl = hpet_ioctl, + .unlocked_ioctl = hpet_ioctl, .open = hpet_open, .release = hpet_release, .fasync = hpet_fasync, diff --git a/drivers/char/hvsi.c b/drivers/char/hvsi.c index 793b236c9266..d4b14ff1c4c1 100644 --- a/drivers/char/hvsi.c +++ b/drivers/char/hvsi.c @@ -194,10 +194,8 @@ static inline void print_state(struct hvsi_struct *hp) "HVSI_WAIT_FOR_MCTRL_RESPONSE", "HVSI_FSP_DIED", }; - const char *name = state_names[hp->state]; - - if (hp->state > ARRAY_SIZE(state_names)) - name = "UNKNOWN"; + const char *name = (hp->state < ARRAY_SIZE(state_names)) + ? state_names[hp->state] : "UNKNOWN"; pr_debug("hvsi%i: state = %s\n", hp->index, name); #endif /* DEBUG */ diff --git a/drivers/char/hw_random/n2-drv.c b/drivers/char/hw_random/n2-drv.c index 10f868eefaa6..0f9cbf1aaf15 100644 --- a/drivers/char/hw_random/n2-drv.c +++ b/drivers/char/hw_random/n2-drv.c @@ -660,7 +660,7 @@ static int __devinit n2rng_probe(struct of_device *op, np->hvapi_major); goto out_hvapi_unregister; } - np->num_units = of_getintprop_default(op->node, + np->num_units = of_getintprop_default(op->dev.of_node, "rng-#units", 0); if (!np->num_units) { dev_err(&op->dev, "VF RNG lacks rng-#units property\n"); @@ -751,8 +751,11 @@ static const struct of_device_id n2rng_match[] = { MODULE_DEVICE_TABLE(of, n2rng_match); static struct of_platform_driver n2rng_driver = { - .name = "n2rng", - .match_table = n2rng_match, + .driver = { + .name = "n2rng", + .owner = THIS_MODULE, + .of_match_table = n2rng_match, + }, .probe = n2rng_probe, .remove = __devexit_p(n2rng_remove), }; diff --git a/drivers/char/hw_random/nomadik-rng.c b/drivers/char/hw_random/nomadik-rng.c index a8b4c4010144..a348c7e9aa0b 100644 --- a/drivers/char/hw_random/nomadik-rng.c +++ b/drivers/char/hw_random/nomadik-rng.c @@ -15,6 +15,10 @@ #include <linux/amba/bus.h> #include <linux/hw_random.h> #include <linux/io.h> +#include <linux/clk.h> +#include <linux/err.h> + +static struct clk *rng_clk; static int nmk_rng_read(struct hwrng *rng, void *data, size_t max, bool wait) { @@ -40,6 +44,15 @@ static int nmk_rng_probe(struct amba_device *dev, struct amba_id *id) void __iomem *base; int ret; + rng_clk = clk_get(&dev->dev, NULL); + if (IS_ERR(rng_clk)) { + dev_err(&dev->dev, "could not get rng clock\n"); + ret = PTR_ERR(rng_clk); + return ret; + } + + clk_enable(rng_clk); + ret = amba_request_regions(dev, dev->dev.init_name); if (ret) return ret; @@ -57,6 +70,8 @@ out_unmap: iounmap(base); out_release: amba_release_regions(dev); + clk_disable(rng_clk); + clk_put(rng_clk); return ret; } @@ -66,6 +81,8 @@ static int nmk_rng_remove(struct amba_device *dev) hwrng_unregister(&nmk_rng); iounmap(base); amba_release_regions(dev); + clk_disable(rng_clk); + clk_put(rng_clk); return 0; } diff --git a/drivers/char/hw_random/pasemi-rng.c b/drivers/char/hw_random/pasemi-rng.c index 7fa61dd1d9d9..261ba8f22b8b 100644 --- a/drivers/char/hw_random/pasemi-rng.c +++ b/drivers/char/hw_random/pasemi-rng.c @@ -98,7 +98,7 @@ static int __devinit rng_probe(struct of_device *ofdev, const struct of_device_id *match) { void __iomem *rng_regs; - struct device_node *rng_np = ofdev->node; + struct device_node *rng_np = ofdev->dev.of_node; struct resource res; int err = 0; @@ -140,8 +140,11 @@ static struct of_device_id rng_match[] = { }; static struct of_platform_driver rng_driver = { - .name = "pasemi-rng", - .match_table = rng_match, + .driver = { + .name = "pasemi-rng", + .owner = THIS_MODULE, + .of_match_table = rng_match, + }, .probe = rng_probe, .remove = rng_remove, }; diff --git a/drivers/char/hw_random/virtio-rng.c b/drivers/char/hw_random/virtio-rng.c index 64fe0a793efd..75f1cbd61c17 100644 --- a/drivers/char/hw_random/virtio-rng.c +++ b/drivers/char/hw_random/virtio-rng.c @@ -32,7 +32,7 @@ static bool busy; static void random_recv_done(struct virtqueue *vq) { /* We can get spurious callbacks, e.g. shared IRQs + virtio_pci. */ - if (!vq->vq_ops->get_buf(vq, &data_avail)) + if (!virtqueue_get_buf(vq, &data_avail)) return; complete(&have_data); @@ -46,10 +46,10 @@ static void register_buffer(u8 *buf, size_t size) sg_init_one(&sg, buf, size); /* There should always be room for one buffer. */ - if (vq->vq_ops->add_buf(vq, &sg, 0, 1, buf) < 0) + if (virtqueue_add_buf(vq, &sg, 0, 1, buf) < 0) BUG(); - vq->vq_ops->kick(vq); + virtqueue_kick(vq); } static int virtio_read(struct hwrng *rng, void *buf, size_t size, bool wait) diff --git a/drivers/char/ipmi/ipmi_devintf.c b/drivers/char/ipmi/ipmi_devintf.c index 65545de3dbf4..d8ec92a38980 100644 --- a/drivers/char/ipmi/ipmi_devintf.c +++ b/drivers/char/ipmi/ipmi_devintf.c @@ -228,8 +228,7 @@ static int handle_send_req(ipmi_user_t user, return rv; } -static int ipmi_ioctl(struct inode *inode, - struct file *file, +static int ipmi_ioctl(struct file *file, unsigned int cmd, unsigned long data) { @@ -630,6 +629,23 @@ static int ipmi_ioctl(struct inode *inode, return rv; } +/* + * Note: it doesn't make sense to take the BKL here but + * not in compat_ipmi_ioctl. -arnd + */ +static long ipmi_unlocked_ioctl(struct file *file, + unsigned int cmd, + unsigned long data) +{ + int ret; + + lock_kernel(); + ret = ipmi_ioctl(file, cmd, data); + unlock_kernel(); + + return ret; +} + #ifdef CONFIG_COMPAT /* @@ -802,7 +818,7 @@ static long compat_ipmi_ioctl(struct file *filep, unsigned int cmd, if (copy_to_user(precv64, &recv64, sizeof(recv64))) return -EFAULT; - rc = ipmi_ioctl(filep->f_path.dentry->d_inode, filep, + rc = ipmi_ioctl(filep, ((cmd == COMPAT_IPMICTL_RECEIVE_MSG) ? IPMICTL_RECEIVE_MSG : IPMICTL_RECEIVE_MSG_TRUNC), @@ -819,14 +835,14 @@ static long compat_ipmi_ioctl(struct file *filep, unsigned int cmd, return rc; } default: - return ipmi_ioctl(filep->f_path.dentry->d_inode, filep, cmd, arg); + return ipmi_ioctl(filep, cmd, arg); } } #endif static const struct file_operations ipmi_fops = { .owner = THIS_MODULE, - .ioctl = ipmi_ioctl, + .unlocked_ioctl = ipmi_unlocked_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl = compat_ipmi_ioctl, #endif diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c index c6ad4234378d..4f3f8c9ec262 100644 --- a/drivers/char/ipmi/ipmi_msghandler.c +++ b/drivers/char/ipmi/ipmi_msghandler.c @@ -2505,12 +2505,11 @@ static int ipmi_bmc_register(ipmi_smi_t intf, int ifnum, return rv; } - printk(KERN_INFO - "ipmi: Found new BMC (man_id: 0x%6.6x, " - " prod_id: 0x%4.4x, dev_id: 0x%2.2x)\n", - bmc->id.manufacturer_id, - bmc->id.product_id, - bmc->id.device_id); + dev_info(intf->si_dev, "Found new BMC (man_id: 0x%6.6x, " + "prod_id: 0x%4.4x, dev_id: 0x%2.2x)\n", + bmc->id.manufacturer_id, + bmc->id.product_id, + bmc->id.device_id); } /* @@ -4037,8 +4036,8 @@ static void ipmi_request_event(void) static struct timer_list ipmi_timer; -/* Call every ~100 ms. */ -#define IPMI_TIMEOUT_TIME 100 +/* Call every ~1000 ms. */ +#define IPMI_TIMEOUT_TIME 1000 /* How many jiffies does it take to get to the timeout time. */ #define IPMI_TIMEOUT_JIFFIES ((IPMI_TIMEOUT_TIME * HZ) / 1000) diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c index 4462b113ba3f..35603dd4e6c5 100644 --- a/drivers/char/ipmi/ipmi_si_intf.c +++ b/drivers/char/ipmi/ipmi_si_intf.c @@ -107,6 +107,14 @@ enum si_type { }; static char *si_to_str[] = { "kcs", "smic", "bt" }; +enum ipmi_addr_src { + SI_INVALID = 0, SI_HOTMOD, SI_HARDCODED, SI_SPMI, SI_ACPI, SI_SMBIOS, + SI_PCI, SI_DEVICETREE, SI_DEFAULT +}; +static char *ipmi_addr_src_to_str[] = { NULL, "hotmod", "hardcoded", "SPMI", + "ACPI", "SMBIOS", "PCI", + "device-tree", "default" }; + #define DEVICE_NAME "ipmi_si" static struct platform_driver ipmi_driver = { @@ -188,7 +196,7 @@ struct smi_info { int (*irq_setup)(struct smi_info *info); void (*irq_cleanup)(struct smi_info *info); unsigned int io_size; - char *addr_source; /* ACPI, PCI, SMBIOS, hardcode, default. */ + enum ipmi_addr_src addr_source; /* ACPI, PCI, SMBIOS, hardcode, etc. */ void (*addr_source_cleanup)(struct smi_info *info); void *addr_source_data; @@ -300,6 +308,7 @@ static int num_max_busy_us; static int unload_when_empty = 1; +static int add_smi(struct smi_info *smi); static int try_smi_init(struct smi_info *smi); static void cleanup_one_si(struct smi_info *to_clean); @@ -314,9 +323,14 @@ static void deliver_recv_msg(struct smi_info *smi_info, { /* Deliver the message to the upper layer with the lock released. */ - spin_unlock(&(smi_info->si_lock)); - ipmi_smi_msg_received(smi_info->intf, msg); - spin_lock(&(smi_info->si_lock)); + + if (smi_info->run_to_completion) { + ipmi_smi_msg_received(smi_info->intf, msg); + } else { + spin_unlock(&(smi_info->si_lock)); + ipmi_smi_msg_received(smi_info->intf, msg); + spin_lock(&(smi_info->si_lock)); + } } static void return_hosed_msg(struct smi_info *smi_info, int cCode) @@ -445,6 +459,9 @@ static inline void disable_si_irq(struct smi_info *smi_info) if ((smi_info->irq) && (!smi_info->interrupt_disabled)) { start_disable_irq(smi_info); smi_info->interrupt_disabled = 1; + if (!atomic_read(&smi_info->stop_operation)) + mod_timer(&smi_info->si_timer, + jiffies + SI_TIMEOUT_JIFFIES); } } @@ -576,9 +593,8 @@ static void handle_transaction_done(struct smi_info *smi_info) smi_info->handlers->get_result(smi_info->si_sm, msg, 3); if (msg[2] != 0) { /* Error clearing flags */ - printk(KERN_WARNING - "ipmi_si: Error clearing flags: %2.2x\n", - msg[2]); + dev_warn(smi_info->dev, + "Error clearing flags: %2.2x\n", msg[2]); } if (smi_info->si_state == SI_CLEARING_FLAGS_THEN_SET_IRQ) start_enable_irq(smi_info); @@ -670,9 +686,8 @@ static void handle_transaction_done(struct smi_info *smi_info) /* We got the flags from the SMI, now handle them. */ smi_info->handlers->get_result(smi_info->si_sm, msg, 4); if (msg[2] != 0) { - printk(KERN_WARNING - "ipmi_si: Could not enable interrupts" - ", failed get, using polled mode.\n"); + dev_warn(smi_info->dev, "Could not enable interrupts" + ", failed get, using polled mode.\n"); smi_info->si_state = SI_NORMAL; } else { msg[0] = (IPMI_NETFN_APP_REQUEST << 2); @@ -693,11 +708,11 @@ static void handle_transaction_done(struct smi_info *smi_info) /* We got the flags from the SMI, now handle them. */ smi_info->handlers->get_result(smi_info->si_sm, msg, 4); - if (msg[2] != 0) { - printk(KERN_WARNING - "ipmi_si: Could not enable interrupts" - ", failed set, using polled mode.\n"); - } + if (msg[2] != 0) + dev_warn(smi_info->dev, "Could not enable interrupts" + ", failed set, using polled mode.\n"); + else + smi_info->interrupt_disabled = 0; smi_info->si_state = SI_NORMAL; break; } @@ -709,9 +724,8 @@ static void handle_transaction_done(struct smi_info *smi_info) /* We got the flags from the SMI, now handle them. */ smi_info->handlers->get_result(smi_info->si_sm, msg, 4); if (msg[2] != 0) { - printk(KERN_WARNING - "ipmi_si: Could not disable interrupts" - ", failed get.\n"); + dev_warn(smi_info->dev, "Could not disable interrupts" + ", failed get.\n"); smi_info->si_state = SI_NORMAL; } else { msg[0] = (IPMI_NETFN_APP_REQUEST << 2); @@ -733,9 +747,8 @@ static void handle_transaction_done(struct smi_info *smi_info) /* We got the flags from the SMI, now handle them. */ smi_info->handlers->get_result(smi_info->si_sm, msg, 4); if (msg[2] != 0) { - printk(KERN_WARNING - "ipmi_si: Could not disable interrupts" - ", failed set.\n"); + dev_warn(smi_info->dev, "Could not disable interrupts" + ", failed set.\n"); } smi_info->si_state = SI_NORMAL; break; @@ -877,6 +890,11 @@ static void sender(void *send_info, printk("**Enqueue: %d.%9.9d\n", t.tv_sec, t.tv_usec); #endif + mod_timer(&smi_info->si_timer, jiffies + SI_TIMEOUT_JIFFIES); + + if (smi_info->thread) + wake_up_process(smi_info->thread); + if (smi_info->run_to_completion) { /* * If we are running to completion, then throw it in @@ -997,6 +1015,8 @@ static int ipmi_thread(void *data) ; /* do nothing */ else if (smi_result == SI_SM_CALL_WITH_DELAY && busy_wait) schedule(); + else if (smi_result == SI_SM_IDLE) + schedule_timeout_interruptible(100); else schedule_timeout_interruptible(0); } @@ -1039,6 +1059,7 @@ static void smi_timeout(unsigned long data) unsigned long flags; unsigned long jiffies_now; long time_diff; + long timeout; #ifdef DEBUG_TIMING struct timeval t; #endif @@ -1059,9 +1080,9 @@ static void smi_timeout(unsigned long data) if ((smi_info->irq) && (!smi_info->interrupt_disabled)) { /* Running with interrupts, only do long timeouts. */ - smi_info->si_timer.expires = jiffies + SI_TIMEOUT_JIFFIES; + timeout = jiffies + SI_TIMEOUT_JIFFIES; smi_inc_stat(smi_info, long_timeouts); - goto do_add_timer; + goto do_mod_timer; } /* @@ -1070,14 +1091,15 @@ static void smi_timeout(unsigned long data) */ if (smi_result == SI_SM_CALL_WITH_DELAY) { smi_inc_stat(smi_info, short_timeouts); - smi_info->si_timer.expires = jiffies + 1; + timeout = jiffies + 1; } else { smi_inc_stat(smi_info, long_timeouts); - smi_info->si_timer.expires = jiffies + SI_TIMEOUT_JIFFIES; + timeout = jiffies + SI_TIMEOUT_JIFFIES; } - do_add_timer: - add_timer(&(smi_info->si_timer)); + do_mod_timer: + if (smi_result != SI_SM_IDLE) + mod_timer(&(smi_info->si_timer), timeout); } static irqreturn_t si_irq_handler(int irq, void *data) @@ -1144,10 +1166,10 @@ static int smi_start_processing(void *send_info, new_smi->thread = kthread_run(ipmi_thread, new_smi, "kipmi%d", new_smi->intf_num); if (IS_ERR(new_smi->thread)) { - printk(KERN_NOTICE "ipmi_si_intf: Could not start" - " kernel thread due to error %ld, only using" - " timers to drive the interface\n", - PTR_ERR(new_smi->thread)); + dev_notice(new_smi->dev, "Could not start" + " kernel thread due to error %ld, only using" + " timers to drive the interface\n", + PTR_ERR(new_smi->thread)); new_smi->thread = NULL; } } @@ -1308,14 +1330,13 @@ static int std_irq_setup(struct smi_info *info) DEVICE_NAME, info); if (rv) { - printk(KERN_WARNING - "ipmi_si: %s unable to claim interrupt %d," - " running polled\n", - DEVICE_NAME, info->irq); + dev_warn(info->dev, "%s unable to claim interrupt %d," + " running polled\n", + DEVICE_NAME, info->irq); info->irq = 0; } else { info->irq_cleanup = std_irq_cleanup; - printk(" Using irq %d\n", info->irq); + dev_info(info->dev, "Using irq %d\n", info->irq); } return rv; @@ -1406,8 +1427,8 @@ static int port_setup(struct smi_info *info) info->io.outputb = port_outl; break; default: - printk(KERN_WARNING "ipmi_si: Invalid register size: %d\n", - info->io.regsize); + dev_warn(info->dev, "Invalid register size: %d\n", + info->io.regsize); return -EINVAL; } @@ -1529,8 +1550,8 @@ static int mem_setup(struct smi_info *info) break; #endif default: - printk(KERN_WARNING "ipmi_si: Invalid register size: %d\n", - info->io.regsize); + dev_warn(info->dev, "Invalid register size: %d\n", + info->io.regsize); return -EINVAL; } @@ -1755,7 +1776,7 @@ static int hotmod_handler(const char *val, struct kernel_param *kp) goto out; } - info->addr_source = "hotmod"; + info->addr_source = SI_HOTMOD; info->si_type = si_type; info->io.addr_data = addr; info->io.addr_type = addr_space; @@ -1777,7 +1798,9 @@ static int hotmod_handler(const char *val, struct kernel_param *kp) info->irq_setup = std_irq_setup; info->slave_addr = ipmb; - try_smi_init(info); + if (!add_smi(info)) + if (try_smi_init(info)) + cleanup_one_si(info); } else { /* remove */ struct smi_info *e, *tmp_e; @@ -1813,7 +1836,8 @@ static __devinit void hardcode_find_bmc(void) if (!info) return; - info->addr_source = "hardcoded"; + info->addr_source = SI_HARDCODED; + printk(KERN_INFO PFX "probing via hardcoded address\n"); if (!si_type[i] || strcmp(si_type[i], "kcs") == 0) { info->si_type = SI_KCS; @@ -1822,8 +1846,7 @@ static __devinit void hardcode_find_bmc(void) } else if (strcmp(si_type[i], "bt") == 0) { info->si_type = SI_BT; } else { - printk(KERN_WARNING - "ipmi_si: Interface type specified " + printk(KERN_WARNING PFX "Interface type specified " "for interface %d, was invalid: %s\n", i, si_type[i]); kfree(info); @@ -1841,11 +1864,9 @@ static __devinit void hardcode_find_bmc(void) info->io.addr_data = addrs[i]; info->io.addr_type = IPMI_MEM_ADDR_SPACE; } else { - printk(KERN_WARNING - "ipmi_si: Interface type specified " - "for interface %d, " - "but port and address were not set or " - "set to zero.\n", i); + printk(KERN_WARNING PFX "Interface type specified " + "for interface %d, but port and address were " + "not set or set to zero.\n", i); kfree(info); continue; } @@ -1863,7 +1884,9 @@ static __devinit void hardcode_find_bmc(void) info->irq_setup = std_irq_setup; info->slave_addr = slave_addrs[i]; - try_smi_init(info); + if (!add_smi(info)) + if (try_smi_init(info)) + cleanup_one_si(info); } } @@ -1923,15 +1946,13 @@ static int acpi_gpe_irq_setup(struct smi_info *info) &ipmi_acpi_gpe, info); if (status != AE_OK) { - printk(KERN_WARNING - "ipmi_si: %s unable to claim ACPI GPE %d," - " running polled\n", - DEVICE_NAME, info->irq); + dev_warn(info->dev, "%s unable to claim ACPI GPE %d," + " running polled\n", DEVICE_NAME, info->irq); info->irq = 0; return -EINVAL; } else { info->irq_cleanup = acpi_gpe_irq_cleanup; - printk(" Using ACPI GPE %d\n", info->irq); + dev_info(info->dev, "Using ACPI GPE %d\n", info->irq); return 0; } } @@ -1989,8 +2010,8 @@ static __devinit int try_init_spmi(struct SPMITable *spmi) u8 addr_space; if (spmi->IPMIlegacy != 1) { - printk(KERN_INFO "IPMI: Bad SPMI legacy %d\n", spmi->IPMIlegacy); - return -ENODEV; + printk(KERN_INFO PFX "Bad SPMI legacy %d\n", spmi->IPMIlegacy); + return -ENODEV; } if (spmi->addr.space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) @@ -2000,11 +2021,12 @@ static __devinit int try_init_spmi(struct SPMITable *spmi) info = kzalloc(sizeof(*info), GFP_KERNEL); if (!info) { - printk(KERN_ERR "ipmi_si: Could not allocate SI data (3)\n"); + printk(KERN_ERR PFX "Could not allocate SI data (3)\n"); return -ENOMEM; } - info->addr_source = "SPMI"; + info->addr_source = SI_SPMI; + printk(KERN_INFO PFX "probing via SPMI\n"); /* Figure out the interface type. */ switch (spmi->InterfaceType) { @@ -2018,8 +2040,8 @@ static __devinit int try_init_spmi(struct SPMITable *spmi) info->si_type = SI_BT; break; default: - printk(KERN_INFO "ipmi_si: Unknown ACPI/SPMI SI type %d\n", - spmi->InterfaceType); + printk(KERN_INFO PFX "Unknown ACPI/SPMI SI type %d\n", + spmi->InterfaceType); kfree(info); return -EIO; } @@ -2055,13 +2077,12 @@ static __devinit int try_init_spmi(struct SPMITable *spmi) info->io.addr_type = IPMI_IO_ADDR_SPACE; } else { kfree(info); - printk(KERN_WARNING - "ipmi_si: Unknown ACPI I/O Address type\n"); + printk(KERN_WARNING PFX "Unknown ACPI I/O Address type\n"); return -EIO; } info->io.addr_data = spmi->addr.address; - try_smi_init(info); + add_smi(info); return 0; } @@ -2093,6 +2114,7 @@ static int __devinit ipmi_pnp_probe(struct pnp_dev *dev, { struct acpi_device *acpi_dev; struct smi_info *info; + struct resource *res; acpi_handle handle; acpi_status status; unsigned long long tmp; @@ -2105,7 +2127,8 @@ static int __devinit ipmi_pnp_probe(struct pnp_dev *dev, if (!info) return -ENOMEM; - info->addr_source = "ACPI"; + info->addr_source = SI_ACPI; + printk(KERN_INFO PFX "probing via ACPI\n"); handle = acpi_dev->handle; @@ -2125,22 +2148,26 @@ static int __devinit ipmi_pnp_probe(struct pnp_dev *dev, info->si_type = SI_BT; break; default: - dev_info(&dev->dev, "unknown interface type %lld\n", tmp); + dev_info(&dev->dev, "unknown IPMI type %lld\n", tmp); goto err_free; } - if (pnp_port_valid(dev, 0)) { + res = pnp_get_resource(dev, IORESOURCE_IO, 0); + if (res) { info->io_setup = port_setup; info->io.addr_type = IPMI_IO_ADDR_SPACE; - info->io.addr_data = pnp_port_start(dev, 0); - } else if (pnp_mem_valid(dev, 0)) { - info->io_setup = mem_setup; - info->io.addr_type = IPMI_MEM_ADDR_SPACE; - info->io.addr_data = pnp_mem_start(dev, 0); } else { + res = pnp_get_resource(dev, IORESOURCE_MEM, 0); + if (res) { + info->io_setup = mem_setup; + info->io.addr_type = IPMI_MEM_ADDR_SPACE; + } + } + if (!res) { dev_err(&dev->dev, "no I/O or memory address\n"); goto err_free; } + info->io.addr_data = res->start; info->io.regspacing = DEFAULT_REGSPACING; info->io.regsize = DEFAULT_REGSPACING; @@ -2156,10 +2183,14 @@ static int __devinit ipmi_pnp_probe(struct pnp_dev *dev, info->irq_setup = std_irq_setup; } - info->dev = &acpi_dev->dev; + info->dev = &dev->dev; pnp_set_drvdata(dev, info); - return try_smi_init(info); + dev_info(info->dev, "%pR regsize %d spacing %d irq %d\n", + res, info->io.regsize, info->io.regspacing, + info->irq); + + return add_smi(info); err_free: kfree(info); @@ -2264,12 +2295,12 @@ static __devinit void try_init_dmi(struct dmi_ipmi_data *ipmi_data) info = kzalloc(sizeof(*info), GFP_KERNEL); if (!info) { - printk(KERN_ERR - "ipmi_si: Could not allocate SI data\n"); + printk(KERN_ERR PFX "Could not allocate SI data\n"); return; } - info->addr_source = "SMBIOS"; + info->addr_source = SI_SMBIOS; + printk(KERN_INFO PFX "probing via SMBIOS\n"); switch (ipmi_data->type) { case 0x01: /* KCS */ @@ -2299,8 +2330,7 @@ static __devinit void try_init_dmi(struct dmi_ipmi_data *ipmi_data) default: kfree(info); - printk(KERN_WARNING - "ipmi_si: Unknown SMBIOS I/O Address type: %d.\n", + printk(KERN_WARNING PFX "Unknown SMBIOS I/O Address type: %d\n", ipmi_data->addr_space); return; } @@ -2318,7 +2348,7 @@ static __devinit void try_init_dmi(struct dmi_ipmi_data *ipmi_data) if (info->irq) info->irq_setup = std_irq_setup; - try_smi_init(info); + add_smi(info); } static void __devinit dmi_find_bmc(void) @@ -2368,7 +2398,8 @@ static int __devinit ipmi_pci_probe(struct pci_dev *pdev, if (!info) return -ENOMEM; - info->addr_source = "PCI"; + info->addr_source = SI_PCI; + dev_info(&pdev->dev, "probing via PCI"); switch (class_type) { case PCI_ERMC_CLASSCODE_TYPE_SMIC: @@ -2385,15 +2416,13 @@ static int __devinit ipmi_pci_probe(struct pci_dev *pdev, default: kfree(info); - printk(KERN_INFO "ipmi_si: %s: Unknown IPMI type: %d\n", - pci_name(pdev), class_type); + dev_info(&pdev->dev, "Unknown IPMI type: %d\n", class_type); return -ENOMEM; } rv = pci_enable_device(pdev); if (rv) { - printk(KERN_ERR "ipmi_si: %s: couldn't enable PCI device\n", - pci_name(pdev)); + dev_err(&pdev->dev, "couldn't enable PCI device\n"); kfree(info); return rv; } @@ -2421,7 +2450,11 @@ static int __devinit ipmi_pci_probe(struct pci_dev *pdev, info->dev = &pdev->dev; pci_set_drvdata(pdev, info); - return try_smi_init(info); + dev_info(&pdev->dev, "%pR regsize %d spacing %d irq %d\n", + &pdev->resource[0], info->io.regsize, info->io.regspacing, + info->irq); + + return add_smi(info); } static void __devexit ipmi_pci_remove(struct pci_dev *pdev) @@ -2469,11 +2502,11 @@ static int __devinit ipmi_of_probe(struct of_device *dev, struct smi_info *info; struct resource resource; const int *regsize, *regspacing, *regshift; - struct device_node *np = dev->node; + struct device_node *np = dev->dev.of_node; int ret; int proplen; - dev_info(&dev->dev, PFX "probing via device tree\n"); + dev_info(&dev->dev, "probing via device tree\n"); ret = of_address_to_resource(np, 0, &resource); if (ret) { @@ -2503,12 +2536,12 @@ static int __devinit ipmi_of_probe(struct of_device *dev, if (!info) { dev_err(&dev->dev, - PFX "could not allocate memory for OF probe\n"); + "could not allocate memory for OF probe\n"); return -ENOMEM; } info->si_type = (enum si_type) match->data; - info->addr_source = "device-tree"; + info->addr_source = SI_DEVICETREE; info->irq_setup = std_irq_setup; if (resource.flags & IORESOURCE_IO) { @@ -2525,16 +2558,16 @@ static int __devinit ipmi_of_probe(struct of_device *dev, info->io.regspacing = regspacing ? *regspacing : DEFAULT_REGSPACING; info->io.regshift = regshift ? *regshift : 0; - info->irq = irq_of_parse_and_map(dev->node, 0); + info->irq = irq_of_parse_and_map(dev->dev.of_node, 0); info->dev = &dev->dev; - dev_dbg(&dev->dev, "addr 0x%lx regsize %d spacing %d irq %x\n", + dev_dbg(&dev->dev, "addr 0x%lx regsize %d spacing %d irq %d\n", info->io.addr_data, info->io.regsize, info->io.regspacing, info->irq); dev_set_drvdata(&dev->dev, info); - return try_smi_init(info); + return add_smi(info); } static int __devexit ipmi_of_remove(struct of_device *dev) @@ -2555,8 +2588,11 @@ static struct of_device_id ipmi_match[] = }; static struct of_platform_driver ipmi_of_platform_driver = { - .name = "ipmi", - .match_table = ipmi_match, + .driver = { + .name = "ipmi", + .owner = THIS_MODULE, + .of_match_table = ipmi_match, + }, .probe = ipmi_of_probe, .remove = __devexit_p(ipmi_of_remove), }; @@ -2640,9 +2676,8 @@ static int try_enable_event_buffer(struct smi_info *smi_info) rv = wait_for_msg_done(smi_info); if (rv) { - printk(KERN_WARNING - "ipmi_si: Error getting response from get global," - " enables command, the event buffer is not" + printk(KERN_WARNING PFX "Error getting response from get" + " global enables command, the event buffer is not" " enabled.\n"); goto out; } @@ -2654,10 +2689,8 @@ static int try_enable_event_buffer(struct smi_info *smi_info) resp[0] != (IPMI_NETFN_APP_REQUEST | 1) << 2 || resp[1] != IPMI_GET_BMC_GLOBAL_ENABLES_CMD || resp[2] != 0) { - printk(KERN_WARNING - "ipmi_si: Invalid return from get global" - " enables command, cannot enable the event" - " buffer.\n"); + printk(KERN_WARNING PFX "Invalid return from get global" + " enables command, cannot enable the event buffer.\n"); rv = -EINVAL; goto out; } @@ -2673,9 +2706,8 @@ static int try_enable_event_buffer(struct smi_info *smi_info) rv = wait_for_msg_done(smi_info); if (rv) { - printk(KERN_WARNING - "ipmi_si: Error getting response from set global," - " enables command, the event buffer is not" + printk(KERN_WARNING PFX "Error getting response from set" + " global, enables command, the event buffer is not" " enabled.\n"); goto out; } @@ -2686,10 +2718,8 @@ static int try_enable_event_buffer(struct smi_info *smi_info) if (resp_len < 3 || resp[0] != (IPMI_NETFN_APP_REQUEST | 1) << 2 || resp[1] != IPMI_SET_BMC_GLOBAL_ENABLES_CMD) { - printk(KERN_WARNING - "ipmi_si: Invalid return from get global," - "enables command, not enable the event" - " buffer.\n"); + printk(KERN_WARNING PFX "Invalid return from get global," + "enables command, not enable the event buffer.\n"); rv = -EINVAL; goto out; } @@ -2948,7 +2978,7 @@ static __devinit void default_find_bmc(void) if (!info) return; - info->addr_source = NULL; + info->addr_source = SI_DEFAULT; info->si_type = ipmi_defaults[i].type; info->io_setup = port_setup; @@ -2960,14 +2990,16 @@ static __devinit void default_find_bmc(void) info->io.regsize = DEFAULT_REGSPACING; info->io.regshift = 0; - if (try_smi_init(info) == 0) { - /* Found one... */ - printk(KERN_INFO "ipmi_si: Found default %s state" - " machine at %s address 0x%lx\n", - si_to_str[info->si_type], - addr_space_to_str[info->io.addr_type], - info->io.addr_data); - return; + if (add_smi(info) == 0) { + if ((try_smi_init(info)) == 0) { + /* Found one... */ + printk(KERN_INFO PFX "Found default %s" + " state machine at %s address 0x%lx\n", + si_to_str[info->si_type], + addr_space_to_str[info->io.addr_type], + info->io.addr_data); + } else + cleanup_one_si(info); } } } @@ -2986,34 +3018,48 @@ static int is_new_interface(struct smi_info *info) return 1; } -static int try_smi_init(struct smi_info *new_smi) +static int add_smi(struct smi_info *new_smi) { - int rv; - int i; - - if (new_smi->addr_source) { - printk(KERN_INFO "ipmi_si: Trying %s-specified %s state" - " machine at %s address 0x%lx, slave address 0x%x," - " irq %d\n", - new_smi->addr_source, - si_to_str[new_smi->si_type], - addr_space_to_str[new_smi->io.addr_type], - new_smi->io.addr_data, - new_smi->slave_addr, new_smi->irq); - } + int rv = 0; + printk(KERN_INFO PFX "Adding %s-specified %s state machine", + ipmi_addr_src_to_str[new_smi->addr_source], + si_to_str[new_smi->si_type]); mutex_lock(&smi_infos_lock); if (!is_new_interface(new_smi)) { - printk(KERN_WARNING "ipmi_si: duplicate interface\n"); + printk(KERN_CONT PFX "duplicate interface\n"); rv = -EBUSY; goto out_err; } + printk(KERN_CONT "\n"); + /* So we know not to free it unless we have allocated one. */ new_smi->intf = NULL; new_smi->si_sm = NULL; new_smi->handlers = NULL; + list_add_tail(&new_smi->link, &smi_infos); + +out_err: + mutex_unlock(&smi_infos_lock); + return rv; +} + +static int try_smi_init(struct smi_info *new_smi) +{ + int rv = 0; + int i; + + printk(KERN_INFO PFX "Trying %s-specified %s state" + " machine at %s address 0x%lx, slave address 0x%x," + " irq %d\n", + ipmi_addr_src_to_str[new_smi->addr_source], + si_to_str[new_smi->si_type], + addr_space_to_str[new_smi->io.addr_type], + new_smi->io.addr_data, + new_smi->slave_addr, new_smi->irq); + switch (new_smi->si_type) { case SI_KCS: new_smi->handlers = &kcs_smi_handlers; @@ -3036,7 +3082,8 @@ static int try_smi_init(struct smi_info *new_smi) /* Allocate the state machine's data and initialize it. */ new_smi->si_sm = kmalloc(new_smi->handlers->size(), GFP_KERNEL); if (!new_smi->si_sm) { - printk(KERN_ERR "Could not allocate state machine memory\n"); + printk(KERN_ERR PFX + "Could not allocate state machine memory\n"); rv = -ENOMEM; goto out_err; } @@ -3046,7 +3093,7 @@ static int try_smi_init(struct smi_info *new_smi) /* Now that we know the I/O size, we can set up the I/O. */ rv = new_smi->io_setup(new_smi); if (rv) { - printk(KERN_ERR "Could not set up I/O space\n"); + printk(KERN_ERR PFX "Could not set up I/O space\n"); goto out_err; } @@ -3056,8 +3103,7 @@ static int try_smi_init(struct smi_info *new_smi) /* Do low-level detection first. */ if (new_smi->handlers->detect(new_smi->si_sm)) { if (new_smi->addr_source) - printk(KERN_INFO "ipmi_si: Interface detection" - " failed\n"); + printk(KERN_INFO PFX "Interface detection failed\n"); rv = -ENODEV; goto out_err; } @@ -3069,7 +3115,7 @@ static int try_smi_init(struct smi_info *new_smi) rv = try_get_dev_id(new_smi); if (rv) { if (new_smi->addr_source) - printk(KERN_INFO "ipmi_si: There appears to be no BMC" + printk(KERN_INFO PFX "There appears to be no BMC" " at this location\n"); goto out_err; } @@ -3085,7 +3131,7 @@ static int try_smi_init(struct smi_info *new_smi) for (i = 0; i < SI_NUM_STATS; i++) atomic_set(&new_smi->stats[i], 0); - new_smi->interrupt_disabled = 0; + new_smi->interrupt_disabled = 1; atomic_set(&new_smi->stop_operation, 0); new_smi->intf_num = smi_num; smi_num++; @@ -3111,9 +3157,8 @@ static int try_smi_init(struct smi_info *new_smi) new_smi->pdev = platform_device_alloc("ipmi_si", new_smi->intf_num); if (!new_smi->pdev) { - printk(KERN_ERR - "ipmi_si_intf:" - " Unable to allocate platform device\n"); + printk(KERN_ERR PFX + "Unable to allocate platform device\n"); goto out_err; } new_smi->dev = &new_smi->pdev->dev; @@ -3121,9 +3166,8 @@ static int try_smi_init(struct smi_info *new_smi) rv = platform_device_add(new_smi->pdev); if (rv) { - printk(KERN_ERR - "ipmi_si_intf:" - " Unable to register system interface device:" + printk(KERN_ERR PFX + "Unable to register system interface device:" " %d\n", rv); goto out_err; @@ -3138,9 +3182,8 @@ static int try_smi_init(struct smi_info *new_smi) "bmc", new_smi->slave_addr); if (rv) { - printk(KERN_ERR - "ipmi_si: Unable to register device: error %d\n", - rv); + dev_err(new_smi->dev, "Unable to register device: error %d\n", + rv); goto out_err_stop_timer; } @@ -3148,9 +3191,7 @@ static int try_smi_init(struct smi_info *new_smi) type_file_read_proc, new_smi); if (rv) { - printk(KERN_ERR - "ipmi_si: Unable to create proc entry: %d\n", - rv); + dev_err(new_smi->dev, "Unable to create proc entry: %d\n", rv); goto out_err_stop_timer; } @@ -3158,9 +3199,7 @@ static int try_smi_init(struct smi_info *new_smi) stat_file_read_proc, new_smi); if (rv) { - printk(KERN_ERR - "ipmi_si: Unable to create proc entry: %d\n", - rv); + dev_err(new_smi->dev, "Unable to create proc entry: %d\n", rv); goto out_err_stop_timer; } @@ -3168,18 +3207,12 @@ static int try_smi_init(struct smi_info *new_smi) param_read_proc, new_smi); if (rv) { - printk(KERN_ERR - "ipmi_si: Unable to create proc entry: %d\n", - rv); + dev_err(new_smi->dev, "Unable to create proc entry: %d\n", rv); goto out_err_stop_timer; } - list_add_tail(&new_smi->link, &smi_infos); - - mutex_unlock(&smi_infos_lock); - - printk(KERN_INFO "IPMI %s interface initialized\n", - si_to_str[new_smi->si_type]); + dev_info(new_smi->dev, "IPMI %s interface initialized\n", + si_to_str[new_smi->si_type]); return 0; @@ -3188,11 +3221,17 @@ static int try_smi_init(struct smi_info *new_smi) wait_for_timer_and_thread(new_smi); out_err: - if (new_smi->intf) + new_smi->interrupt_disabled = 1; + + if (new_smi->intf) { ipmi_unregister_smi(new_smi->intf); + new_smi->intf = NULL; + } - if (new_smi->irq_cleanup) + if (new_smi->irq_cleanup) { new_smi->irq_cleanup(new_smi); + new_smi->irq_cleanup = NULL; + } /* * Wait until we know that we are out of any interrupt @@ -3205,18 +3244,21 @@ static int try_smi_init(struct smi_info *new_smi) if (new_smi->handlers) new_smi->handlers->cleanup(new_smi->si_sm); kfree(new_smi->si_sm); + new_smi->si_sm = NULL; } - if (new_smi->addr_source_cleanup) + if (new_smi->addr_source_cleanup) { new_smi->addr_source_cleanup(new_smi); - if (new_smi->io_cleanup) + new_smi->addr_source_cleanup = NULL; + } + if (new_smi->io_cleanup) { new_smi->io_cleanup(new_smi); + new_smi->io_cleanup = NULL; + } - if (new_smi->dev_registered) + if (new_smi->dev_registered) { platform_device_unregister(new_smi->pdev); - - kfree(new_smi); - - mutex_unlock(&smi_infos_lock); + new_smi->dev_registered = 0; + } return rv; } @@ -3226,6 +3268,8 @@ static __devinit int init_ipmi_si(void) int i; char *str; int rv; + struct smi_info *e; + enum ipmi_addr_src type = SI_INVALID; if (initialized) return 0; @@ -3234,9 +3278,7 @@ static __devinit int init_ipmi_si(void) /* Register the device drivers. */ rv = driver_register(&ipmi_driver.driver); if (rv) { - printk(KERN_ERR - "init_ipmi_si: Unable to register driver: %d\n", - rv); + printk(KERN_ERR PFX "Unable to register driver: %d\n", rv); return rv; } @@ -3260,38 +3302,81 @@ static __devinit int init_ipmi_si(void) hardcode_find_bmc(); -#ifdef CONFIG_DMI - dmi_find_bmc(); -#endif + /* If the user gave us a device, they presumably want us to use it */ + mutex_lock(&smi_infos_lock); + if (!list_empty(&smi_infos)) { + mutex_unlock(&smi_infos_lock); + return 0; + } + mutex_unlock(&smi_infos_lock); -#ifdef CONFIG_ACPI - spmi_find_bmc(); +#ifdef CONFIG_PCI + rv = pci_register_driver(&ipmi_pci_driver); + if (rv) + printk(KERN_ERR PFX "Unable to register PCI driver: %d\n", rv); #endif + #ifdef CONFIG_ACPI pnp_register_driver(&ipmi_pnp_driver); #endif -#ifdef CONFIG_PCI - rv = pci_register_driver(&ipmi_pci_driver); - if (rv) - printk(KERN_ERR - "init_ipmi_si: Unable to register PCI driver: %d\n", - rv); +#ifdef CONFIG_DMI + dmi_find_bmc(); +#endif + +#ifdef CONFIG_ACPI + spmi_find_bmc(); #endif #ifdef CONFIG_PPC_OF of_register_platform_driver(&ipmi_of_platform_driver); #endif + /* We prefer devices with interrupts, but in the case of a machine + with multiple BMCs we assume that there will be several instances + of a given type so if we succeed in registering a type then also + try to register everything else of the same type */ + + mutex_lock(&smi_infos_lock); + list_for_each_entry(e, &smi_infos, link) { + /* Try to register a device if it has an IRQ and we either + haven't successfully registered a device yet or this + device has the same type as one we successfully registered */ + if (e->irq && (!type || e->addr_source == type)) { + if (!try_smi_init(e)) { + type = e->addr_source; + } + } + } + + /* type will only have been set if we successfully registered an si */ + if (type) { + mutex_unlock(&smi_infos_lock); + return 0; + } + + /* Fall back to the preferred device */ + + list_for_each_entry(e, &smi_infos, link) { + if (!e->irq && (!type || e->addr_source == type)) { + if (!try_smi_init(e)) { + type = e->addr_source; + } + } + } + mutex_unlock(&smi_infos_lock); + + if (type) + return 0; + if (si_trydefaults) { mutex_lock(&smi_infos_lock); if (list_empty(&smi_infos)) { /* No BMC was found, try defaults. */ mutex_unlock(&smi_infos_lock); default_find_bmc(); - } else { + } else mutex_unlock(&smi_infos_lock); - } } mutex_lock(&smi_infos_lock); @@ -3305,8 +3390,8 @@ static __devinit int init_ipmi_si(void) of_unregister_platform_driver(&ipmi_of_platform_driver); #endif driver_unregister(&ipmi_driver.driver); - printk(KERN_WARNING - "ipmi_si: Unable to find any System Interface(s)\n"); + printk(KERN_WARNING PFX + "Unable to find any System Interface(s)\n"); return -ENODEV; } else { mutex_unlock(&smi_infos_lock); @@ -3317,7 +3402,7 @@ module_init(init_ipmi_si); static void cleanup_one_si(struct smi_info *to_clean) { - int rv; + int rv = 0; unsigned long flags; if (!to_clean) @@ -3361,14 +3446,16 @@ static void cleanup_one_si(struct smi_info *to_clean) schedule_timeout_uninterruptible(1); } - rv = ipmi_unregister_smi(to_clean->intf); + if (to_clean->intf) + rv = ipmi_unregister_smi(to_clean->intf); + if (rv) { - printk(KERN_ERR - "ipmi_si: Unable to unregister device: errno=%d\n", + printk(KERN_ERR PFX "Unable to unregister device: errno=%d\n", rv); } - to_clean->handlers->cleanup(to_clean->si_sm); + if (to_clean->handlers) + to_clean->handlers->cleanup(to_clean->si_sm); kfree(to_clean->si_sm); diff --git a/drivers/char/ipmi/ipmi_watchdog.c b/drivers/char/ipmi/ipmi_watchdog.c index a4d57e31f713..82bcdb262a3a 100644 --- a/drivers/char/ipmi/ipmi_watchdog.c +++ b/drivers/char/ipmi/ipmi_watchdog.c @@ -659,7 +659,7 @@ static struct watchdog_info ident = { .identity = "IPMI" }; -static int ipmi_ioctl(struct inode *inode, struct file *file, +static int ipmi_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { void __user *argp = (void __user *)arg; @@ -730,6 +730,19 @@ static int ipmi_ioctl(struct inode *inode, struct file *file, } } +static long ipmi_unlocked_ioctl(struct file *file, + unsigned int cmd, + unsigned long arg) +{ + int ret; + + lock_kernel(); + ret = ipmi_ioctl(file, cmd, arg); + unlock_kernel(); + + return ret; +} + static ssize_t ipmi_write(struct file *file, const char __user *buf, size_t len, @@ -880,7 +893,7 @@ static const struct file_operations ipmi_wdog_fops = { .read = ipmi_read, .poll = ipmi_poll, .write = ipmi_write, - .ioctl = ipmi_ioctl, + .unlocked_ioctl = ipmi_unlocked_ioctl, .open = ipmi_open, .release = ipmi_close, .fasync = ipmi_fasync, diff --git a/drivers/char/isicom.c b/drivers/char/isicom.c index c1ab303455cf..98310e1aae30 100644 --- a/drivers/char/isicom.c +++ b/drivers/char/isicom.c @@ -1573,11 +1573,16 @@ static int __devinit isicom_probe(struct pci_dev *pdev, dev_info(&pdev->dev, "ISI PCI Card(Device ID 0x%x)\n", ent->device); /* allot the first empty slot in the array */ - for (index = 0; index < BOARD_COUNT; index++) + for (index = 0; index < BOARD_COUNT; index++) { if (isi_card[index].base == 0) { board = &isi_card[index]; break; } + } + if (index == BOARD_COUNT) { + retval = -ENODEV; + goto err_disable; + } board->index = index; board->base = pci_resource_start(pdev, 3); @@ -1624,6 +1629,7 @@ errunrr: errdec: board->base = 0; card_count--; +err_disable: pci_disable_device(pdev); err: return retval; diff --git a/drivers/char/misc.c b/drivers/char/misc.c index 92ab03d28294..cd650ca8c679 100644 --- a/drivers/char/misc.c +++ b/drivers/char/misc.c @@ -144,6 +144,7 @@ static int misc_open(struct inode * inode, struct file * file) old_fops = file->f_op; file->f_op = new_fops; if (file->f_op->open) { + file->private_data = c; err=file->f_op->open(inode,file); if (err) { fops_put(file->f_op); diff --git a/drivers/char/n_gsm.c b/drivers/char/n_gsm.c new file mode 100644 index 000000000000..c4161d5e053d --- /dev/null +++ b/drivers/char/n_gsm.c @@ -0,0 +1,2763 @@ +/* + * n_gsm.c GSM 0710 tty multiplexor + * Copyright (c) 2009/10 Intel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * * THIS IS A DEVELOPMENT SNAPSHOT IT IS NOT A FINAL RELEASE * + * + * TO DO: + * Mostly done: ioctls for setting modes/timing + * Partly done: hooks so you can pull off frames to non tty devs + * Restart DLCI 0 when it closes ? + * Test basic encoding + * Improve the tx engine + * Resolve tx side locking by adding a queue_head and routing + * all control traffic via it + * General tidy/document + * Review the locking/move to refcounts more (mux now moved to an + * alloc/free model ready) + * Use newest tty open/close port helpers and install hooks + * What to do about power functions ? + * Termios setting and negotiation + * Do we need a 'which mux are you' ioctl to correlate mux and tty sets + * + */ + +#include <linux/types.h> +#include <linux/major.h> +#include <linux/errno.h> +#include <linux/signal.h> +#include <linux/fcntl.h> +#include <linux/sched.h> +#include <linux/interrupt.h> +#include <linux/tty.h> +#include <linux/timer.h> +#include <linux/ctype.h> +#include <linux/mm.h> +#include <linux/string.h> +#include <linux/slab.h> +#include <linux/poll.h> +#include <linux/bitops.h> +#include <linux/file.h> +#include <linux/uaccess.h> +#include <linux/module.h> +#include <linux/timer.h> +#include <linux/tty_flip.h> +#include <linux/tty_driver.h> +#include <linux/serial.h> +#include <linux/kfifo.h> +#include <linux/skbuff.h> +#include <linux/gsmmux.h> + +static int debug; +module_param(debug, int, 0600); + +#define T1 (HZ/10) +#define T2 (HZ/3) +#define N2 3 + +/* Use long timers for testing at low speed with debug on */ +#ifdef DEBUG_TIMING +#define T1 HZ +#define T2 (2 * HZ) +#endif + +/* Semi-arbitary buffer size limits. 0710 is normally run with 32-64 byte + limits so this is plenty */ +#define MAX_MRU 512 +#define MAX_MTU 512 + +/* + * Each block of data we have queued to go out is in the form of + * a gsm_msg which holds everything we need in a link layer independant + * format + */ + +struct gsm_msg { + struct gsm_msg *next; + u8 addr; /* DLCI address + flags */ + u8 ctrl; /* Control byte + flags */ + unsigned int len; /* Length of data block (can be zero) */ + unsigned char *data; /* Points into buffer but not at the start */ + unsigned char buffer[0]; +}; + +/* + * Each active data link has a gsm_dlci structure associated which ties + * the link layer to an optional tty (if the tty side is open). To avoid + * complexity right now these are only ever freed up when the mux is + * shut down. + * + * At the moment we don't free DLCI objects until the mux is torn down + * this avoid object life time issues but might be worth review later. + */ + +struct gsm_dlci { + struct gsm_mux *gsm; + int addr; + int state; +#define DLCI_CLOSED 0 +#define DLCI_OPENING 1 /* Sending SABM not seen UA */ +#define DLCI_OPEN 2 /* SABM/UA complete */ +#define DLCI_CLOSING 3 /* Sending DISC not seen UA/DM */ + + /* Link layer */ + spinlock_t lock; /* Protects the internal state */ + struct timer_list t1; /* Retransmit timer for SABM and UA */ + int retries; + /* Uplink tty if active */ + struct tty_port port; /* The tty bound to this DLCI if there is one */ + struct kfifo *fifo; /* Queue fifo for the DLCI */ + struct kfifo _fifo; /* For new fifo API porting only */ + int adaption; /* Adaption layer in use */ + u32 modem_rx; /* Our incoming virtual modem lines */ + u32 modem_tx; /* Our outgoing modem lines */ + int dead; /* Refuse re-open */ + /* Flow control */ + int throttled; /* Private copy of throttle state */ + int constipated; /* Throttle status for outgoing */ + /* Packetised I/O */ + struct sk_buff *skb; /* Frame being sent */ + struct sk_buff_head skb_list; /* Queued frames */ + /* Data handling callback */ + void (*data)(struct gsm_dlci *dlci, u8 *data, int len); +}; + +/* DLCI 0, 62/63 are special or reseved see gsmtty_open */ + +#define NUM_DLCI 64 + +/* + * DLCI 0 is used to pass control blocks out of band of the data + * flow (and with a higher link priority). One command can be outstanding + * at a time and we use this structure to manage them. They are created + * and destroyed by the user context, and updated by the receive paths + * and timers + */ + +struct gsm_control { + u8 cmd; /* Command we are issuing */ + u8 *data; /* Data for the command in case we retransmit */ + int len; /* Length of block for retransmission */ + int done; /* Done flag */ + int error; /* Error if any */ +}; + +/* + * Each GSM mux we have is represented by this structure. If we are + * operating as an ldisc then we use this structure as our ldisc + * state. We need to sort out lifetimes and locking with respect + * to the gsm mux array. For now we don't free DLCI objects that + * have been instantiated until the mux itself is terminated. + * + * To consider further: tty open versus mux shutdown. + */ + +struct gsm_mux { + struct tty_struct *tty; /* The tty our ldisc is bound to */ + spinlock_t lock; + + /* Events on the GSM channel */ + wait_queue_head_t event; + + /* Bits for GSM mode decoding */ + + /* Framing Layer */ + unsigned char *buf; + int state; +#define GSM_SEARCH 0 +#define GSM_START 1 +#define GSM_ADDRESS 2 +#define GSM_CONTROL 3 +#define GSM_LEN 4 +#define GSM_DATA 5 +#define GSM_FCS 6 +#define GSM_OVERRUN 7 + unsigned int len; + unsigned int address; + unsigned int count; + int escape; + int encoding; + u8 control; + u8 fcs; + u8 *txframe; /* TX framing buffer */ + + /* Methods for the receiver side */ + void (*receive)(struct gsm_mux *gsm, u8 ch); + void (*error)(struct gsm_mux *gsm, u8 ch, u8 flag); + /* And transmit side */ + int (*output)(struct gsm_mux *mux, u8 *data, int len); + + /* Link Layer */ + unsigned int mru; + unsigned int mtu; + int initiator; /* Did we initiate connection */ + int dead; /* Has the mux been shut down */ + struct gsm_dlci *dlci[NUM_DLCI]; + int constipated; /* Asked by remote to shut up */ + + spinlock_t tx_lock; + unsigned int tx_bytes; /* TX data outstanding */ +#define TX_THRESH_HI 8192 +#define TX_THRESH_LO 2048 + struct gsm_msg *tx_head; /* Pending data packets */ + struct gsm_msg *tx_tail; + + /* Control messages */ + struct timer_list t2_timer; /* Retransmit timer for commands */ + int cretries; /* Command retry counter */ + struct gsm_control *pending_cmd;/* Our current pending command */ + spinlock_t control_lock; /* Protects the pending command */ + + /* Configuration */ + int adaption; /* 1 or 2 supported */ + u8 ftype; /* UI or UIH */ + int t1, t2; /* Timers in 1/100th of a sec */ + int n2; /* Retry count */ + + /* Statistics (not currently exposed) */ + unsigned long bad_fcs; + unsigned long malformed; + unsigned long io_error; + unsigned long bad_size; + unsigned long unsupported; +}; + + +/* + * Mux objects - needed so that we can translate a tty index into the + * relevant mux and DLCI. + */ + +#define MAX_MUX 4 /* 256 minors */ +static struct gsm_mux *gsm_mux[MAX_MUX]; /* GSM muxes */ +static spinlock_t gsm_mux_lock; + +/* + * This section of the driver logic implements the GSM encodings + * both the basic and the 'advanced'. Reliable transport is not + * supported. + */ + +#define CR 0x02 +#define EA 0x01 +#define PF 0x10 + +/* I is special: the rest are ..*/ +#define RR 0x01 +#define UI 0x03 +#define RNR 0x05 +#define REJ 0x09 +#define DM 0x0F +#define SABM 0x2F +#define DISC 0x43 +#define UA 0x63 +#define UIH 0xEF + +/* Channel commands */ +#define CMD_NSC 0x09 +#define CMD_TEST 0x11 +#define CMD_PSC 0x21 +#define CMD_RLS 0x29 +#define CMD_FCOFF 0x31 +#define CMD_PN 0x41 +#define CMD_RPN 0x49 +#define CMD_FCON 0x51 +#define CMD_CLD 0x61 +#define CMD_SNC 0x69 +#define CMD_MSC 0x71 + +/* Virtual modem bits */ +#define MDM_FC 0x01 +#define MDM_RTC 0x02 +#define MDM_RTR 0x04 +#define MDM_IC 0x20 +#define MDM_DV 0x40 + +#define GSM0_SOF 0xF9 +#define GSM1_SOF 0x7E +#define GSM1_ESCAPE 0x7D +#define GSM1_ESCAPE_BITS 0x20 +#define XON 0x11 +#define XOFF 0x13 + +static const struct tty_port_operations gsm_port_ops; + +/* + * CRC table for GSM 0710 + */ + +static const u8 gsm_fcs8[256] = { + 0x00, 0x91, 0xE3, 0x72, 0x07, 0x96, 0xE4, 0x75, + 0x0E, 0x9F, 0xED, 0x7C, 0x09, 0x98, 0xEA, 0x7B, + 0x1C, 0x8D, 0xFF, 0x6E, 0x1B, 0x8A, 0xF8, 0x69, + 0x12, 0x83, 0xF1, 0x60, 0x15, 0x84, 0xF6, 0x67, + 0x38, 0xA9, 0xDB, 0x4A, 0x3F, 0xAE, 0xDC, 0x4D, + 0x36, 0xA7, 0xD5, 0x44, 0x31, 0xA0, 0xD2, 0x43, + 0x24, 0xB5, 0xC7, 0x56, 0x23, 0xB2, 0xC0, 0x51, + 0x2A, 0xBB, 0xC9, 0x58, 0x2D, 0xBC, 0xCE, 0x5F, + 0x70, 0xE1, 0x93, 0x02, 0x77, 0xE6, 0x94, 0x05, + 0x7E, 0xEF, 0x9D, 0x0C, 0x79, 0xE8, 0x9A, 0x0B, + 0x6C, 0xFD, 0x8F, 0x1E, 0x6B, 0xFA, 0x88, 0x19, + 0x62, 0xF3, 0x81, 0x10, 0x65, 0xF4, 0x86, 0x17, + 0x48, 0xD9, 0xAB, 0x3A, 0x4F, 0xDE, 0xAC, 0x3D, + 0x46, 0xD7, 0xA5, 0x34, 0x41, 0xD0, 0xA2, 0x33, + 0x54, 0xC5, 0xB7, 0x26, 0x53, 0xC2, 0xB0, 0x21, + 0x5A, 0xCB, 0xB9, 0x28, 0x5D, 0xCC, 0xBE, 0x2F, + 0xE0, 0x71, 0x03, 0x92, 0xE7, 0x76, 0x04, 0x95, + 0xEE, 0x7F, 0x0D, 0x9C, 0xE9, 0x78, 0x0A, 0x9B, + 0xFC, 0x6D, 0x1F, 0x8E, 0xFB, 0x6A, 0x18, 0x89, + 0xF2, 0x63, 0x11, 0x80, 0xF5, 0x64, 0x16, 0x87, + 0xD8, 0x49, 0x3B, 0xAA, 0xDF, 0x4E, 0x3C, 0xAD, + 0xD6, 0x47, 0x35, 0xA4, 0xD1, 0x40, 0x32, 0xA3, + 0xC4, 0x55, 0x27, 0xB6, 0xC3, 0x52, 0x20, 0xB1, + 0xCA, 0x5B, 0x29, 0xB8, 0xCD, 0x5C, 0x2E, 0xBF, + 0x90, 0x01, 0x73, 0xE2, 0x97, 0x06, 0x74, 0xE5, + 0x9E, 0x0F, 0x7D, 0xEC, 0x99, 0x08, 0x7A, 0xEB, + 0x8C, 0x1D, 0x6F, 0xFE, 0x8B, 0x1A, 0x68, 0xF9, + 0x82, 0x13, 0x61, 0xF0, 0x85, 0x14, 0x66, 0xF7, + 0xA8, 0x39, 0x4B, 0xDA, 0xAF, 0x3E, 0x4C, 0xDD, + 0xA6, 0x37, 0x45, 0xD4, 0xA1, 0x30, 0x42, 0xD3, + 0xB4, 0x25, 0x57, 0xC6, 0xB3, 0x22, 0x50, 0xC1, + 0xBA, 0x2B, 0x59, 0xC8, 0xBD, 0x2C, 0x5E, 0xCF +}; + +#define INIT_FCS 0xFF +#define GOOD_FCS 0xCF + +/** + * gsm_fcs_add - update FCS + * @fcs: Current FCS + * @c: Next data + * + * Update the FCS to include c. Uses the algorithm in the specification + * notes. + */ + +static inline u8 gsm_fcs_add(u8 fcs, u8 c) +{ + return gsm_fcs8[fcs ^ c]; +} + +/** + * gsm_fcs_add_block - update FCS for a block + * @fcs: Current FCS + * @c: buffer of data + * @len: length of buffer + * + * Update the FCS to include c. Uses the algorithm in the specification + * notes. + */ + +static inline u8 gsm_fcs_add_block(u8 fcs, u8 *c, int len) +{ + while (len--) + fcs = gsm_fcs8[fcs ^ *c++]; + return fcs; +} + +/** + * gsm_read_ea - read a byte into an EA + * @val: variable holding value + * c: byte going into the EA + * + * Processes one byte of an EA. Updates the passed variable + * and returns 1 if the EA is now completely read + */ + +static int gsm_read_ea(unsigned int *val, u8 c) +{ + /* Add the next 7 bits into the value */ + *val <<= 7; + *val |= c >> 1; + /* Was this the last byte of the EA 1 = yes*/ + return c & EA; +} + +/** + * gsm_encode_modem - encode modem data bits + * @dlci: DLCI to encode from + * + * Returns the correct GSM encoded modem status bits (6 bit field) for + * the current status of the DLCI and attached tty object + */ + +static u8 gsm_encode_modem(const struct gsm_dlci *dlci) +{ + u8 modembits = 0; + /* FC is true flow control not modem bits */ + if (dlci->throttled) + modembits |= MDM_FC; + if (dlci->modem_tx & TIOCM_DTR) + modembits |= MDM_RTC; + if (dlci->modem_tx & TIOCM_RTS) + modembits |= MDM_RTR; + if (dlci->modem_tx & TIOCM_RI) + modembits |= MDM_IC; + if (dlci->modem_tx & TIOCM_CD) + modembits |= MDM_DV; + return modembits; +} + +/** + * gsm_print_packet - display a frame for debug + * @hdr: header to print before decode + * @addr: address EA from the frame + * @cr: C/R bit from the frame + * @control: control including PF bit + * @data: following data bytes + * @dlen: length of data + * + * Displays a packet in human readable format for debugging purposes. The + * style is based on amateur radio LAP-B dump display. + */ + +static void gsm_print_packet(const char *hdr, int addr, int cr, + u8 control, const u8 *data, int dlen) +{ + if (!(debug & 1)) + return; + + printk(KERN_INFO "%s %d) %c: ", hdr, addr, "RC"[cr]); + + switch (control & ~PF) { + case SABM: + printk(KERN_CONT "SABM"); + break; + case UA: + printk(KERN_CONT "UA"); + break; + case DISC: + printk(KERN_CONT "DISC"); + break; + case DM: + printk(KERN_CONT "DM"); + break; + case UI: + printk(KERN_CONT "UI"); + break; + case UIH: + printk(KERN_CONT "UIH"); + break; + default: + if (!(control & 0x01)) { + printk(KERN_CONT "I N(S)%d N(R)%d", + (control & 0x0E) >> 1, (control & 0xE)>> 5); + } else switch (control & 0x0F) { + case RR: + printk("RR(%d)", (control & 0xE0) >> 5); + break; + case RNR: + printk("RNR(%d)", (control & 0xE0) >> 5); + break; + case REJ: + printk("REJ(%d)", (control & 0xE0) >> 5); + break; + default: + printk(KERN_CONT "[%02X]", control); + } + } + + if (control & PF) + printk(KERN_CONT "(P)"); + else + printk(KERN_CONT "(F)"); + + if (dlen) { + int ct = 0; + while (dlen--) { + if (ct % 8 == 0) + printk(KERN_CONT "\n "); + printk(KERN_CONT "%02X ", *data++); + ct++; + } + } + printk(KERN_CONT "\n"); +} + + +/* + * Link level transmission side + */ + +/** + * gsm_stuff_packet - bytestuff a packet + * @ibuf: input + * @obuf: output + * @len: length of input + * + * Expand a buffer by bytestuffing it. The worst case size change + * is doubling and the caller is responsible for handing out + * suitable sized buffers. + */ + +static int gsm_stuff_frame(const u8 *input, u8 *output, int len) +{ + int olen = 0; + while (len--) { + if (*input == GSM1_SOF || *input == GSM1_ESCAPE + || *input == XON || *input == XOFF) { + *output++ = GSM1_ESCAPE; + *output++ = *input++ ^ GSM1_ESCAPE_BITS; + olen++; + } else + *output++ = *input++; + olen++; + } + return olen; +} + +static void hex_packet(const unsigned char *p, int len) +{ + int i; + for (i = 0; i < len; i++) { + if (i && (i % 16) == 0) + printk("\n"); + printk("%02X ", *p++); + } + printk("\n"); +} + +/** + * gsm_send - send a control frame + * @gsm: our GSM mux + * @addr: address for control frame + * @cr: command/response bit + * @control: control byte including PF bit + * + * Format up and transmit a control frame. These do not go via the + * queueing logic as they should be transmitted ahead of data when + * they are needed. + * + * FIXME: Lock versus data TX path + */ + +static void gsm_send(struct gsm_mux *gsm, int addr, int cr, int control) +{ + int len; + u8 cbuf[10]; + u8 ibuf[3]; + + switch (gsm->encoding) { + case 0: + cbuf[0] = GSM0_SOF; + cbuf[1] = (addr << 2) | (cr << 1) | EA; + cbuf[2] = control; + cbuf[3] = EA; /* Length of data = 0 */ + cbuf[4] = 0xFF - gsm_fcs_add_block(INIT_FCS, cbuf + 1, 3); + cbuf[5] = GSM0_SOF; + len = 6; + break; + case 1: + case 2: + /* Control frame + packing (but not frame stuffing) in mode 1 */ + ibuf[0] = (addr << 2) | (cr << 1) | EA; + ibuf[1] = control; + ibuf[2] = 0xFF - gsm_fcs_add_block(INIT_FCS, ibuf, 2); + /* Stuffing may double the size worst case */ + len = gsm_stuff_frame(ibuf, cbuf + 1, 3); + /* Now add the SOF markers */ + cbuf[0] = GSM1_SOF; + cbuf[len + 1] = GSM1_SOF; + /* FIXME: we can omit the lead one in many cases */ + len += 2; + break; + default: + WARN_ON(1); + return; + } + gsm->output(gsm, cbuf, len); + gsm_print_packet("-->", addr, cr, control, NULL, 0); +} + +/** + * gsm_response - send a control response + * @gsm: our GSM mux + * @addr: address for control frame + * @control: control byte including PF bit + * + * Format up and transmit a link level response frame. + */ + +static inline void gsm_response(struct gsm_mux *gsm, int addr, int control) +{ + gsm_send(gsm, addr, 0, control); +} + +/** + * gsm_command - send a control command + * @gsm: our GSM mux + * @addr: address for control frame + * @control: control byte including PF bit + * + * Format up and transmit a link level command frame. + */ + +static inline void gsm_command(struct gsm_mux *gsm, int addr, int control) +{ + gsm_send(gsm, addr, 1, control); +} + +/* Data transmission */ + +#define HDR_LEN 6 /* ADDR CTRL [LEN.2] DATA FCS */ + +/** + * gsm_data_alloc - allocate data frame + * @gsm: GSM mux + * @addr: DLCI address + * @len: length excluding header and FCS + * @ctrl: control byte + * + * Allocate a new data buffer for sending frames with data. Space is left + * at the front for header bytes but that is treated as an implementation + * detail and not for the high level code to use + */ + +static struct gsm_msg *gsm_data_alloc(struct gsm_mux *gsm, u8 addr, int len, + u8 ctrl) +{ + struct gsm_msg *m = kmalloc(sizeof(struct gsm_msg) + len + HDR_LEN, + GFP_ATOMIC); + if (m == NULL) + return NULL; + m->data = m->buffer + HDR_LEN - 1; /* Allow for FCS */ + m->len = len; + m->addr = addr; + m->ctrl = ctrl; + m->next = NULL; + return m; +} + +/** + * gsm_data_kick - poke the queue + * @gsm: GSM Mux + * + * The tty device has called us to indicate that room has appeared in + * the transmit queue. Ram more data into the pipe if we have any + * + * FIXME: lock against link layer control transmissions + */ + +static void gsm_data_kick(struct gsm_mux *gsm) +{ + struct gsm_msg *msg = gsm->tx_head; + int len; + int skip_sof = 0; + + /* FIXME: We need to apply this solely to data messages */ + if (gsm->constipated) + return; + + while (gsm->tx_head != NULL) { + msg = gsm->tx_head; + if (gsm->encoding != 0) { + gsm->txframe[0] = GSM1_SOF; + len = gsm_stuff_frame(msg->data, + gsm->txframe + 1, msg->len); + gsm->txframe[len + 1] = GSM1_SOF; + len += 2; + } else { + gsm->txframe[0] = GSM0_SOF; + memcpy(gsm->txframe + 1 , msg->data, msg->len); + gsm->txframe[msg->len + 1] = GSM0_SOF; + len = msg->len + 2; + } + + if (debug & 4) { + printk("gsm_data_kick: \n"); + hex_packet(gsm->txframe, len); + } + + if (gsm->output(gsm, gsm->txframe + skip_sof, + len - skip_sof) < 0) + break; + /* FIXME: Can eliminate one SOF in many more cases */ + gsm->tx_head = msg->next; + if (gsm->tx_head == NULL) + gsm->tx_tail = NULL; + gsm->tx_bytes -= msg->len; + kfree(msg); + /* For a burst of frames skip the extra SOF within the + burst */ + skip_sof = 1; + } +} + +/** + * __gsm_data_queue - queue a UI or UIH frame + * @dlci: DLCI sending the data + * @msg: message queued + * + * Add data to the transmit queue and try and get stuff moving + * out of the mux tty if not already doing so. The Caller must hold + * the gsm tx lock. + */ + +static void __gsm_data_queue(struct gsm_dlci *dlci, struct gsm_msg *msg) +{ + struct gsm_mux *gsm = dlci->gsm; + u8 *dp = msg->data; + u8 *fcs = dp + msg->len; + + /* Fill in the header */ + if (gsm->encoding == 0) { + if (msg->len < 128) + *--dp = (msg->len << 1) | EA; + else { + *--dp = (msg->len >> 6) | EA; + *--dp = (msg->len & 127) << 1; + } + } + + *--dp = msg->ctrl; + if (gsm->initiator) + *--dp = (msg->addr << 2) | 2 | EA; + else + *--dp = (msg->addr << 2) | EA; + *fcs = gsm_fcs_add_block(INIT_FCS, dp , msg->data - dp); + /* Ugly protocol layering violation */ + if (msg->ctrl == UI || msg->ctrl == (UI|PF)) + *fcs = gsm_fcs_add_block(*fcs, msg->data, msg->len); + *fcs = 0xFF - *fcs; + + gsm_print_packet("Q> ", msg->addr, gsm->initiator, msg->ctrl, + msg->data, msg->len); + + /* Move the header back and adjust the length, also allow for the FCS + now tacked on the end */ + msg->len += (msg->data - dp) + 1; + msg->data = dp; + + /* Add to the actual output queue */ + if (gsm->tx_tail) + gsm->tx_tail->next = msg; + else + gsm->tx_head = msg; + gsm->tx_tail = msg; + gsm->tx_bytes += msg->len; + gsm_data_kick(gsm); +} + +/** + * gsm_data_queue - queue a UI or UIH frame + * @dlci: DLCI sending the data + * @msg: message queued + * + * Add data to the transmit queue and try and get stuff moving + * out of the mux tty if not already doing so. Take the + * the gsm tx lock and dlci lock. + */ + +static void gsm_data_queue(struct gsm_dlci *dlci, struct gsm_msg *msg) +{ + unsigned long flags; + spin_lock_irqsave(&dlci->gsm->tx_lock, flags); + __gsm_data_queue(dlci, msg); + spin_unlock_irqrestore(&dlci->gsm->tx_lock, flags); +} + +/** + * gsm_dlci_data_output - try and push data out of a DLCI + * @gsm: mux + * @dlci: the DLCI to pull data from + * + * Pull data from a DLCI and send it into the transmit queue if there + * is data. Keep to the MRU of the mux. This path handles the usual tty + * interface which is a byte stream with optional modem data. + * + * Caller must hold the tx_lock of the mux. + */ + +static int gsm_dlci_data_output(struct gsm_mux *gsm, struct gsm_dlci *dlci) +{ + struct gsm_msg *msg; + u8 *dp; + int len, size; + int h = dlci->adaption - 1; + + len = kfifo_len(dlci->fifo); + if (len == 0) + return 0; + + /* MTU/MRU count only the data bits */ + if (len > gsm->mtu) + len = gsm->mtu; + + size = len + h; + + msg = gsm_data_alloc(gsm, dlci->addr, size, gsm->ftype); + /* FIXME: need a timer or something to kick this so it can't + get stuck with no work outstanding and no buffer free */ + if (msg == NULL) + return -ENOMEM; + dp = msg->data; + switch (dlci->adaption) { + case 1: /* Unstructured */ + break; + case 2: /* Unstructed with modem bits. Always one byte as we never + send inline break data */ + *dp += gsm_encode_modem(dlci); + len--; + break; + } + WARN_ON(kfifo_out_locked(dlci->fifo, dp , len, &dlci->lock) != len); + __gsm_data_queue(dlci, msg); + /* Bytes of data we used up */ + return size; +} + +/** + * gsm_dlci_data_output_framed - try and push data out of a DLCI + * @gsm: mux + * @dlci: the DLCI to pull data from + * + * Pull data from a DLCI and send it into the transmit queue if there + * is data. Keep to the MRU of the mux. This path handles framed data + * queued as skbuffs to the DLCI. + * + * Caller must hold the tx_lock of the mux. + */ + +static int gsm_dlci_data_output_framed(struct gsm_mux *gsm, + struct gsm_dlci *dlci) +{ + struct gsm_msg *msg; + u8 *dp; + int len, size; + int last = 0, first = 0; + int overhead = 0; + + /* One byte per frame is used for B/F flags */ + if (dlci->adaption == 4) + overhead = 1; + + /* dlci->skb is locked by tx_lock */ + if (dlci->skb == NULL) { + dlci->skb = skb_dequeue(&dlci->skb_list); + if (dlci->skb == NULL) + return 0; + first = 1; + } + len = dlci->skb->len + overhead; + + /* MTU/MRU count only the data bits */ + if (len > gsm->mtu) { + if (dlci->adaption == 3) { + /* Over long frame, bin it */ + kfree_skb(dlci->skb); + dlci->skb = NULL; + return 0; + } + len = gsm->mtu; + } else + last = 1; + + size = len + overhead; + msg = gsm_data_alloc(gsm, dlci->addr, size, gsm->ftype); + + /* FIXME: need a timer or something to kick this so it can't + get stuck with no work outstanding and no buffer free */ + if (msg == NULL) + return -ENOMEM; + dp = msg->data; + + if (dlci->adaption == 4) { /* Interruptible framed (Packetised Data) */ + /* Flag byte to carry the start/end info */ + *dp++ = last << 7 | first << 6 | 1; /* EA */ + len--; + } + memcpy(dp, skb_pull(dlci->skb, len), len); + __gsm_data_queue(dlci, msg); + if (last) + dlci->skb = NULL; + return size; +} + +/** + * gsm_dlci_data_sweep - look for data to send + * @gsm: the GSM mux + * + * Sweep the GSM mux channels in priority order looking for ones with + * data to send. We could do with optimising this scan a bit. We aim + * to fill the queue totally or up to TX_THRESH_HI bytes. Once we hit + * TX_THRESH_LO we get called again + * + * FIXME: We should round robin between groups and in theory you can + * renegotiate DLCI priorities with optional stuff. Needs optimising. + */ + +static void gsm_dlci_data_sweep(struct gsm_mux *gsm) +{ + int len; + /* Priority ordering: We should do priority with RR of the groups */ + int i = 1; + unsigned long flags; + + spin_lock_irqsave(&gsm->tx_lock, flags); + while (i < NUM_DLCI) { + struct gsm_dlci *dlci; + + if (gsm->tx_bytes > TX_THRESH_HI) + break; + dlci = gsm->dlci[i]; + if (dlci == NULL || dlci->constipated) { + i++; + continue; + } + if (dlci->adaption < 3) + len = gsm_dlci_data_output(gsm, dlci); + else + len = gsm_dlci_data_output_framed(gsm, dlci); + if (len < 0) + return; + /* DLCI empty - try the next */ + if (len == 0) + i++; + } + spin_unlock_irqrestore(&gsm->tx_lock, flags); +} + +/** + * gsm_dlci_data_kick - transmit if possible + * @dlci: DLCI to kick + * + * Transmit data from this DLCI if the queue is empty. We can't rely on + * a tty wakeup except when we filled the pipe so we need to fire off + * new data ourselves in other cases. + */ + +static void gsm_dlci_data_kick(struct gsm_dlci *dlci) +{ + unsigned long flags; + + spin_lock_irqsave(&dlci->gsm->tx_lock, flags); + /* If we have nothing running then we need to fire up */ + if (dlci->gsm->tx_bytes == 0) + gsm_dlci_data_output(dlci->gsm, dlci); + else if (dlci->gsm->tx_bytes < TX_THRESH_LO) + gsm_dlci_data_sweep(dlci->gsm); + spin_unlock_irqrestore(&dlci->gsm->tx_lock, flags); +} + +/* + * Control message processing + */ + + +/** + * gsm_control_reply - send a response frame to a control + * @gsm: gsm channel + * @cmd: the command to use + * @data: data to follow encoded info + * @dlen: length of data + * + * Encode up and queue a UI/UIH frame containing our response. + */ + +static void gsm_control_reply(struct gsm_mux *gsm, int cmd, u8 *data, + int dlen) +{ + struct gsm_msg *msg; + msg = gsm_data_alloc(gsm, 0, dlen + 2, gsm->ftype); + msg->data[0] = (cmd & 0xFE) << 1 | EA; /* Clear C/R */ + msg->data[1] = (dlen << 1) | EA; + memcpy(msg->data + 2, data, dlen); + gsm_data_queue(gsm->dlci[0], msg); +} + +/** + * gsm_process_modem - process received modem status + * @tty: virtual tty bound to the DLCI + * @dlci: DLCI to affect + * @modem: modem bits (full EA) + * + * Used when a modem control message or line state inline in adaption + * layer 2 is processed. Sort out the local modem state and throttles + */ + +static void gsm_process_modem(struct tty_struct *tty, struct gsm_dlci *dlci, + u32 modem) +{ + int mlines = 0; + u8 brk = modem >> 6; + + /* Flow control/ready to communicate */ + if (modem & MDM_FC) { + /* Need to throttle our output on this device */ + dlci->constipated = 1; + } + if (modem & MDM_RTC) { + mlines |= TIOCM_DSR | TIOCM_DTR; + dlci->constipated = 0; + gsm_dlci_data_kick(dlci); + } + /* Map modem bits */ + if (modem & MDM_RTR) + mlines |= TIOCM_RTS | TIOCM_CTS; + if (modem & MDM_IC) + mlines |= TIOCM_RI; + if (modem & MDM_DV) + mlines |= TIOCM_CD; + + /* Carrier drop -> hangup */ + if (tty) { + if ((mlines & TIOCM_CD) == 0 && (dlci->modem_rx & TIOCM_CD)) + if (!(tty->termios->c_cflag & CLOCAL)) + tty_hangup(tty); + if (brk & 0x01) + tty_insert_flip_char(tty, 0, TTY_BREAK); + } + dlci->modem_rx = mlines; +} + +/** + * gsm_control_modem - modem status received + * @gsm: GSM channel + * @data: data following command + * @clen: command length + * + * We have received a modem status control message. This is used by + * the GSM mux protocol to pass virtual modem line status and optionally + * to indicate break signals. Unpack it, convert to Linux representation + * and if need be stuff a break message down the tty. + */ + +static void gsm_control_modem(struct gsm_mux *gsm, u8 *data, int clen) +{ + unsigned int addr = 0; + unsigned int modem = 0; + struct gsm_dlci *dlci; + int len = clen; + u8 *dp = data; + struct tty_struct *tty; + + while (gsm_read_ea(&addr, *dp++) == 0) { + len--; + if (len == 0) + return; + } + /* Must be at least one byte following the EA */ + len--; + if (len <= 0) + return; + + addr >>= 1; + /* Closed port, or invalid ? */ + if (addr == 0 || addr >= NUM_DLCI || gsm->dlci[addr] == NULL) + return; + dlci = gsm->dlci[addr]; + + while (gsm_read_ea(&modem, *dp++) == 0) { + len--; + if (len == 0) + return; + } + tty = tty_port_tty_get(&dlci->port); + gsm_process_modem(tty, dlci, modem); + if (tty) { + tty_wakeup(tty); + tty_kref_put(tty); + } + gsm_control_reply(gsm, CMD_MSC, data, clen); +} + +/** + * gsm_control_rls - remote line status + * @gsm: GSM channel + * @data: data bytes + * @clen: data length + * + * The modem sends us a two byte message on the control channel whenever + * it wishes to send us an error state from the virtual link. Stuff + * this into the uplink tty if present + */ + +static void gsm_control_rls(struct gsm_mux *gsm, u8 *data, int clen) +{ + struct tty_struct *tty; + unsigned int addr = 0 ; + u8 bits; + int len = clen; + u8 *dp = data; + + while (gsm_read_ea(&addr, *dp++) == 0) { + len--; + if (len == 0) + return; + } + /* Must be at least one byte following ea */ + len--; + if (len <= 0) + return; + addr >>= 1; + /* Closed port, or invalid ? */ + if (addr == 0 || addr >= NUM_DLCI || gsm->dlci[addr] == NULL) + return; + /* No error ? */ + bits = *dp; + if ((bits & 1) == 0) + return; + /* See if we have an uplink tty */ + tty = tty_port_tty_get(&gsm->dlci[addr]->port); + + if (tty) { + if (bits & 2) + tty_insert_flip_char(tty, 0, TTY_OVERRUN); + if (bits & 4) + tty_insert_flip_char(tty, 0, TTY_PARITY); + if (bits & 8) + tty_insert_flip_char(tty, 0, TTY_FRAME); + tty_flip_buffer_push(tty); + tty_kref_put(tty); + } + gsm_control_reply(gsm, CMD_RLS, data, clen); +} + +static void gsm_dlci_begin_close(struct gsm_dlci *dlci); + +/** + * gsm_control_message - DLCI 0 control processing + * @gsm: our GSM mux + * @command: the command EA + * @data: data beyond the command/length EAs + * @clen: length + * + * Input processor for control messages from the other end of the link. + * Processes the incoming request and queues a response frame or an + * NSC response if not supported + */ + +static void gsm_control_message(struct gsm_mux *gsm, unsigned int command, + u8 *data, int clen) +{ + u8 buf[1]; + switch (command) { + case CMD_CLD: { + struct gsm_dlci *dlci = gsm->dlci[0]; + /* Modem wishes to close down */ + if (dlci) { + dlci->dead = 1; + gsm->dead = 1; + gsm_dlci_begin_close(dlci); + } + } + break; + case CMD_TEST: + /* Modem wishes to test, reply with the data */ + gsm_control_reply(gsm, CMD_TEST, data, clen); + break; + case CMD_FCON: + /* Modem wants us to STFU */ + gsm->constipated = 1; + gsm_control_reply(gsm, CMD_FCON, NULL, 0); + break; + case CMD_FCOFF: + /* Modem can accept data again */ + gsm->constipated = 0; + gsm_control_reply(gsm, CMD_FCOFF, NULL, 0); + /* Kick the link in case it is idling */ + gsm_data_kick(gsm); + break; + case CMD_MSC: + /* Out of band modem line change indicator for a DLCI */ + gsm_control_modem(gsm, data, clen); + break; + case CMD_RLS: + /* Out of band error reception for a DLCI */ + gsm_control_rls(gsm, data, clen); + break; + case CMD_PSC: + /* Modem wishes to enter power saving state */ + gsm_control_reply(gsm, CMD_PSC, NULL, 0); + break; + /* Optional unsupported commands */ + case CMD_PN: /* Parameter negotiation */ + case CMD_RPN: /* Remote port negotation */ + case CMD_SNC: /* Service negotation command */ + default: + /* Reply to bad commands with an NSC */ + buf[0] = command; + gsm_control_reply(gsm, CMD_NSC, buf, 1); + break; + } +} + +/** + * gsm_control_response - process a response to our control + * @gsm: our GSM mux + * @command: the command (response) EA + * @data: data beyond the command/length EA + * @clen: length + * + * Process a response to an outstanding command. We only allow a single + * control message in flight so this is fairly easy. All the clean up + * is done by the caller, we just update the fields, flag it as done + * and return + */ + +static void gsm_control_response(struct gsm_mux *gsm, unsigned int command, + u8 *data, int clen) +{ + struct gsm_control *ctrl; + unsigned long flags; + + spin_lock_irqsave(&gsm->control_lock, flags); + + ctrl = gsm->pending_cmd; + /* Does the reply match our command */ + command |= 1; + if (ctrl != NULL && (command == ctrl->cmd || command == CMD_NSC)) { + /* Our command was replied to, kill the retry timer */ + del_timer(&gsm->t2_timer); + gsm->pending_cmd = NULL; + /* Rejected by the other end */ + if (command == CMD_NSC) + ctrl->error = -EOPNOTSUPP; + ctrl->done = 1; + wake_up(&gsm->event); + } + spin_unlock_irqrestore(&gsm->control_lock, flags); +} + +/** + * gsm_control_transmit - send control packet + * @gsm: gsm mux + * @ctrl: frame to send + * + * Send out a pending control command (called under control lock) + */ + +static void gsm_control_transmit(struct gsm_mux *gsm, struct gsm_control *ctrl) +{ + struct gsm_msg *msg = gsm_data_alloc(gsm, 0, ctrl->len + 1, + gsm->ftype|PF); + if (msg == NULL) + return; + msg->data[0] = (ctrl->cmd << 1) | 2 | EA; /* command */ + memcpy(msg->data + 1, ctrl->data, ctrl->len); + gsm_data_queue(gsm->dlci[0], msg); +} + +/** + * gsm_control_retransmit - retransmit a control frame + * @data: pointer to our gsm object + * + * Called off the T2 timer expiry in order to retransmit control frames + * that have been lost in the system somewhere. The control_lock protects + * us from colliding with another sender or a receive completion event. + * In that situation the timer may still occur in a small window but + * gsm->pending_cmd will be NULL and we just let the timer expire. + */ + +static void gsm_control_retransmit(unsigned long data) +{ + struct gsm_mux *gsm = (struct gsm_mux *)data; + struct gsm_control *ctrl; + unsigned long flags; + spin_lock_irqsave(&gsm->control_lock, flags); + ctrl = gsm->pending_cmd; + if (ctrl) { + gsm->cretries--; + if (gsm->cretries == 0) { + gsm->pending_cmd = NULL; + ctrl->error = -ETIMEDOUT; + ctrl->done = 1; + spin_unlock_irqrestore(&gsm->control_lock, flags); + wake_up(&gsm->event); + return; + } + gsm_control_transmit(gsm, ctrl); + mod_timer(&gsm->t2_timer, jiffies + gsm->t2 * HZ / 100); + } + spin_unlock_irqrestore(&gsm->control_lock, flags); +} + +/** + * gsm_control_send - send a control frame on DLCI 0 + * @gsm: the GSM channel + * @command: command to send including CR bit + * @data: bytes of data (must be kmalloced) + * @len: length of the block to send + * + * Queue and dispatch a control command. Only one command can be + * active at a time. In theory more can be outstanding but the matching + * gets really complicated so for now stick to one outstanding. + */ + +static struct gsm_control *gsm_control_send(struct gsm_mux *gsm, + unsigned int command, u8 *data, int clen) +{ + struct gsm_control *ctrl = kzalloc(sizeof(struct gsm_control), + GFP_KERNEL); + unsigned long flags; + if (ctrl == NULL) + return NULL; +retry: + wait_event(gsm->event, gsm->pending_cmd == NULL); + spin_lock_irqsave(&gsm->control_lock, flags); + if (gsm->pending_cmd != NULL) { + spin_unlock_irqrestore(&gsm->control_lock, flags); + goto retry; + } + ctrl->cmd = command; + ctrl->data = data; + ctrl->len = clen; + gsm->pending_cmd = ctrl; + gsm->cretries = gsm->n2; + mod_timer(&gsm->t2_timer, jiffies + gsm->t2 * HZ / 100); + gsm_control_transmit(gsm, ctrl); + spin_unlock_irqrestore(&gsm->control_lock, flags); + return ctrl; +} + +/** + * gsm_control_wait - wait for a control to finish + * @gsm: GSM mux + * @control: control we are waiting on + * + * Waits for the control to complete or time out. Frees any used + * resources and returns 0 for success, or an error if the remote + * rejected or ignored the request. + */ + +static int gsm_control_wait(struct gsm_mux *gsm, struct gsm_control *control) +{ + int err; + wait_event(gsm->event, control->done == 1); + err = control->error; + kfree(control); + return err; +} + + +/* + * DLCI level handling: Needs krefs + */ + +/* + * State transitions and timers + */ + +/** + * gsm_dlci_close - a DLCI has closed + * @dlci: DLCI that closed + * + * Perform processing when moving a DLCI into closed state. If there + * is an attached tty this is hung up + */ + +static void gsm_dlci_close(struct gsm_dlci *dlci) +{ + del_timer(&dlci->t1); + if (debug & 8) + printk("DLCI %d goes closed.\n", dlci->addr); + dlci->state = DLCI_CLOSED; + if (dlci->addr != 0) { + struct tty_struct *tty = tty_port_tty_get(&dlci->port); + if (tty) { + tty_hangup(tty); + tty_kref_put(tty); + } + kfifo_reset(dlci->fifo); + } else + dlci->gsm->dead = 1; + wake_up(&dlci->gsm->event); + /* A DLCI 0 close is a MUX termination so we need to kick that + back to userspace somehow */ +} + +/** + * gsm_dlci_open - a DLCI has opened + * @dlci: DLCI that opened + * + * Perform processing when moving a DLCI into open state. + */ + +static void gsm_dlci_open(struct gsm_dlci *dlci) +{ + /* Note that SABM UA .. SABM UA first UA lost can mean that we go + open -> open */ + del_timer(&dlci->t1); + /* This will let a tty open continue */ + dlci->state = DLCI_OPEN; + if (debug & 8) + printk("DLCI %d goes open.\n", dlci->addr); + wake_up(&dlci->gsm->event); +} + +/** + * gsm_dlci_t1 - T1 timer expiry + * @dlci: DLCI that opened + * + * The T1 timer handles retransmits of control frames (essentially of + * SABM and DISC). We resend the command until the retry count runs out + * in which case an opening port goes back to closed and a closing port + * is simply put into closed state (any further frames from the other + * end will get a DM response) + */ + +static void gsm_dlci_t1(unsigned long data) +{ + struct gsm_dlci *dlci = (struct gsm_dlci *)data; + struct gsm_mux *gsm = dlci->gsm; + + switch (dlci->state) { + case DLCI_OPENING: + dlci->retries--; + if (dlci->retries) { + gsm_command(dlci->gsm, dlci->addr, SABM|PF); + mod_timer(&dlci->t1, jiffies + gsm->t1 * HZ / 100); + } else + gsm_dlci_close(dlci); + break; + case DLCI_CLOSING: + dlci->retries--; + if (dlci->retries) { + gsm_command(dlci->gsm, dlci->addr, DISC|PF); + mod_timer(&dlci->t1, jiffies + gsm->t1 * HZ / 100); + } else + gsm_dlci_close(dlci); + break; + } +} + +/** + * gsm_dlci_begin_open - start channel open procedure + * @dlci: DLCI to open + * + * Commence opening a DLCI from the Linux side. We issue SABM messages + * to the modem which should then reply with a UA, at which point we + * will move into open state. Opening is done asynchronously with retry + * running off timers and the responses. + */ + +static void gsm_dlci_begin_open(struct gsm_dlci *dlci) +{ + struct gsm_mux *gsm = dlci->gsm; + if (dlci->state == DLCI_OPEN || dlci->state == DLCI_OPENING) + return; + dlci->retries = gsm->n2; + dlci->state = DLCI_OPENING; + gsm_command(dlci->gsm, dlci->addr, SABM|PF); + mod_timer(&dlci->t1, jiffies + gsm->t1 * HZ / 100); +} + +/** + * gsm_dlci_begin_close - start channel open procedure + * @dlci: DLCI to open + * + * Commence closing a DLCI from the Linux side. We issue DISC messages + * to the modem which should then reply with a UA, at which point we + * will move into closed state. Closing is done asynchronously with retry + * off timers. We may also receive a DM reply from the other end which + * indicates the channel was already closed. + */ + +static void gsm_dlci_begin_close(struct gsm_dlci *dlci) +{ + struct gsm_mux *gsm = dlci->gsm; + if (dlci->state == DLCI_CLOSED || dlci->state == DLCI_CLOSING) + return; + dlci->retries = gsm->n2; + dlci->state = DLCI_CLOSING; + gsm_command(dlci->gsm, dlci->addr, DISC|PF); + mod_timer(&dlci->t1, jiffies + gsm->t1 * HZ / 100); +} + +/** + * gsm_dlci_data - data arrived + * @dlci: channel + * @data: block of bytes received + * @len: length of received block + * + * A UI or UIH frame has arrived which contains data for a channel + * other than the control channel. If the relevant virtual tty is + * open we shovel the bits down it, if not we drop them. + */ + +static void gsm_dlci_data(struct gsm_dlci *dlci, u8 *data, int len) +{ + /* krefs .. */ + struct tty_port *port = &dlci->port; + struct tty_struct *tty = tty_port_tty_get(port); + unsigned int modem = 0; + + if (debug & 16) + printk("%d bytes for tty %p\n", len, tty); + if (tty) { + switch (dlci->adaption) { + /* Unsupported types */ + /* Packetised interruptible data */ + case 4: + break; + /* Packetised uininterruptible voice/data */ + case 3: + break; + /* Asynchronous serial with line state in each frame */ + case 2: + while (gsm_read_ea(&modem, *data++) == 0) { + len--; + if (len == 0) + return; + } + gsm_process_modem(tty, dlci, modem); + /* Line state will go via DLCI 0 controls only */ + case 1: + default: + tty_insert_flip_string(tty, data, len); + tty_flip_buffer_push(tty); + } + tty_kref_put(tty); + } +} + +/** + * gsm_dlci_control - data arrived on control channel + * @dlci: channel + * @data: block of bytes received + * @len: length of received block + * + * A UI or UIH frame has arrived which contains data for DLCI 0 the + * control channel. This should contain a command EA followed by + * control data bytes. The command EA contains a command/response bit + * and we divide up the work accordingly. + */ + +static void gsm_dlci_command(struct gsm_dlci *dlci, u8 *data, int len) +{ + /* See what command is involved */ + unsigned int command = 0; + while (len-- > 0) { + if (gsm_read_ea(&command, *data++) == 1) { + int clen = *data++; + len--; + /* FIXME: this is properly an EA */ + clen >>= 1; + /* Malformed command ? */ + if (clen > len) + return; + if (command & 1) + gsm_control_message(dlci->gsm, command, + data, clen); + else + gsm_control_response(dlci->gsm, command, + data, clen); + return; + } + } +} + +/* + * Allocate/Free DLCI channels + */ + +/** + * gsm_dlci_alloc - allocate a DLCI + * @gsm: GSM mux + * @addr: address of the DLCI + * + * Allocate and install a new DLCI object into the GSM mux. + * + * FIXME: review locking races + */ + +static struct gsm_dlci *gsm_dlci_alloc(struct gsm_mux *gsm, int addr) +{ + struct gsm_dlci *dlci = kzalloc(sizeof(struct gsm_dlci), GFP_ATOMIC); + if (dlci == NULL) + return NULL; + spin_lock_init(&dlci->lock); + dlci->fifo = &dlci->_fifo; + if (kfifo_alloc(&dlci->_fifo, 4096, GFP_KERNEL) < 0) { + kfree(dlci); + return NULL; + } + + skb_queue_head_init(&dlci->skb_list); + init_timer(&dlci->t1); + dlci->t1.function = gsm_dlci_t1; + dlci->t1.data = (unsigned long)dlci; + tty_port_init(&dlci->port); + dlci->port.ops = &gsm_port_ops; + dlci->gsm = gsm; + dlci->addr = addr; + dlci->adaption = gsm->adaption; + dlci->state = DLCI_CLOSED; + if (addr) + dlci->data = gsm_dlci_data; + else + dlci->data = gsm_dlci_command; + gsm->dlci[addr] = dlci; + return dlci; +} + +/** + * gsm_dlci_free - release DLCI + * @dlci: DLCI to destroy + * + * Free up a DLCI. Currently to keep the lifetime rules sane we only + * clean up DLCI objects when the MUX closes rather than as the port + * is closed down on both the tty and mux levels. + * + * Can sleep. + */ +static void gsm_dlci_free(struct gsm_dlci *dlci) +{ + struct tty_struct *tty = tty_port_tty_get(&dlci->port); + if (tty) { + tty_vhangup(tty); + tty_kref_put(tty); + } + del_timer_sync(&dlci->t1); + dlci->gsm->dlci[dlci->addr] = NULL; + kfifo_free(dlci->fifo); + kfree(dlci); +} + + +/* + * LAPBish link layer logic + */ + +/** + * gsm_queue - a GSM frame is ready to process + * @gsm: pointer to our gsm mux + * + * At this point in time a frame has arrived and been demangled from + * the line encoding. All the differences between the encodings have + * been handled below us and the frame is unpacked into the structures. + * The fcs holds the header FCS but any data FCS must be added here. + */ + +static void gsm_queue(struct gsm_mux *gsm) +{ + struct gsm_dlci *dlci; + u8 cr; + int address; + /* We have to sneak a look at the packet body to do the FCS. + A somewhat layering violation in the spec */ + + if ((gsm->control & ~PF) == UI) + gsm->fcs = gsm_fcs_add_block(gsm->fcs, gsm->buf, gsm->len); + if (gsm->fcs != GOOD_FCS) { + gsm->bad_fcs++; + if (debug & 4) + printk("BAD FCS %02x\n", gsm->fcs); + return; + } + address = gsm->address >> 1; + if (address >= NUM_DLCI) + goto invalid; + + cr = gsm->address & 1; /* C/R bit */ + + gsm_print_packet("<--", address, cr, gsm->control, gsm->buf, gsm->len); + + cr ^= 1 - gsm->initiator; /* Flip so 1 always means command */ + dlci = gsm->dlci[address]; + + switch (gsm->control) { + case SABM|PF: + if (cr == 0) + goto invalid; + if (dlci == NULL) + dlci = gsm_dlci_alloc(gsm, address); + if (dlci == NULL) + return; + if (dlci->dead) + gsm_response(gsm, address, DM); + else { + gsm_response(gsm, address, UA); + gsm_dlci_open(dlci); + } + break; + case DISC|PF: + if (cr == 0) + goto invalid; + if (dlci == NULL || dlci->state == DLCI_CLOSED) { + gsm_response(gsm, address, DM); + return; + } + /* Real close complete */ + gsm_response(gsm, address, UA); + gsm_dlci_close(dlci); + break; + case UA: + case UA|PF: + if (cr == 0 || dlci == NULL) + break; + switch (dlci->state) { + case DLCI_CLOSING: + gsm_dlci_close(dlci); + break; + case DLCI_OPENING: + gsm_dlci_open(dlci); + break; + } + break; + case DM: /* DM can be valid unsolicited */ + case DM|PF: + if (cr) + goto invalid; + if (dlci == NULL) + return; + gsm_dlci_close(dlci); + break; + case UI: + case UI|PF: + case UIH: + case UIH|PF: +#if 0 + if (cr) + goto invalid; +#endif + if (dlci == NULL || dlci->state != DLCI_OPEN) { + gsm_command(gsm, address, DM|PF); + return; + } + dlci->data(dlci, gsm->buf, gsm->len); + break; + default: + goto invalid; + } + return; +invalid: + gsm->malformed++; + return; +} + + +/** + * gsm0_receive - perform processing for non-transparency + * @gsm: gsm data for this ldisc instance + * @c: character + * + * Receive bytes in gsm mode 0 + */ + +static void gsm0_receive(struct gsm_mux *gsm, unsigned char c) +{ + switch (gsm->state) { + case GSM_SEARCH: /* SOF marker */ + if (c == GSM0_SOF) { + gsm->state = GSM_ADDRESS; + gsm->address = 0; + gsm->len = 0; + gsm->fcs = INIT_FCS; + } + break; /* Address EA */ + case GSM_ADDRESS: + gsm->fcs = gsm_fcs_add(gsm->fcs, c); + if (gsm_read_ea(&gsm->address, c)) + gsm->state = GSM_CONTROL; + break; + case GSM_CONTROL: /* Control Byte */ + gsm->fcs = gsm_fcs_add(gsm->fcs, c); + gsm->control = c; + gsm->state = GSM_LEN; + break; + case GSM_LEN: /* Length EA */ + gsm->fcs = gsm_fcs_add(gsm->fcs, c); + if (gsm_read_ea(&gsm->len, c)) { + if (gsm->len > gsm->mru) { + gsm->bad_size++; + gsm->state = GSM_SEARCH; + break; + } + gsm->count = 0; + gsm->state = GSM_DATA; + } + break; + case GSM_DATA: /* Data */ + gsm->buf[gsm->count++] = c; + if (gsm->count == gsm->len) + gsm->state = GSM_FCS; + break; + case GSM_FCS: /* FCS follows the packet */ + gsm->fcs = c; + gsm_queue(gsm); + /* And then back for the next frame */ + gsm->state = GSM_SEARCH; + break; + } +} + +/** + * gsm0_receive - perform processing for non-transparency + * @gsm: gsm data for this ldisc instance + * @c: character + * + * Receive bytes in mode 1 (Advanced option) + */ + +static void gsm1_receive(struct gsm_mux *gsm, unsigned char c) +{ + if (c == GSM1_SOF) { + /* EOF is only valid in frame if we have got to the data state + and received at least one byte (the FCS) */ + if (gsm->state == GSM_DATA && gsm->count) { + /* Extract the FCS */ + gsm->count--; + gsm->fcs = gsm_fcs_add(gsm->fcs, gsm->buf[gsm->count]); + gsm->len = gsm->count; + gsm_queue(gsm); + gsm->state = GSM_START; + return; + } + /* Any partial frame was a runt so go back to start */ + if (gsm->state != GSM_START) { + gsm->malformed++; + gsm->state = GSM_START; + } + /* A SOF in GSM_START means we are still reading idling or + framing bytes */ + return; + } + + if (c == GSM1_ESCAPE) { + gsm->escape = 1; + return; + } + + /* Only an unescaped SOF gets us out of GSM search */ + if (gsm->state == GSM_SEARCH) + return; + + if (gsm->escape) { + c ^= GSM1_ESCAPE_BITS; + gsm->escape = 0; + } + switch (gsm->state) { + case GSM_START: /* First byte after SOF */ + gsm->address = 0; + gsm->state = GSM_ADDRESS; + gsm->fcs = INIT_FCS; + /* Drop through */ + case GSM_ADDRESS: /* Address continuation */ + gsm->fcs = gsm_fcs_add(gsm->fcs, c); + if (gsm_read_ea(&gsm->address, c)) + gsm->state = GSM_CONTROL; + break; + case GSM_CONTROL: /* Control Byte */ + gsm->fcs = gsm_fcs_add(gsm->fcs, c); + gsm->control = c; + gsm->count = 0; + gsm->state = GSM_DATA; + break; + case GSM_DATA: /* Data */ + if (gsm->count > gsm->mru ) { /* Allow one for the FCS */ + gsm->state = GSM_OVERRUN; + gsm->bad_size++; + } else + gsm->buf[gsm->count++] = c; + break; + case GSM_OVERRUN: /* Over-long - eg a dropped SOF */ + break; + } +} + +/** + * gsm_error - handle tty error + * @gsm: ldisc data + * @data: byte received (may be invalid) + * @flag: error received + * + * Handle an error in the receipt of data for a frame. Currently we just + * go back to hunting for a SOF. + * + * FIXME: better diagnostics ? + */ + +static void gsm_error(struct gsm_mux *gsm, + unsigned char data, unsigned char flag) +{ + gsm->state = GSM_SEARCH; + gsm->io_error++; +} + +/** + * gsm_cleanup_mux - generic GSM protocol cleanup + * @gsm: our mux + * + * Clean up the bits of the mux which are the same for all framing + * protocols. Remove the mux from the mux table, stop all the timers + * and then shut down each device hanging up the channels as we go. + */ + +void gsm_cleanup_mux(struct gsm_mux *gsm) +{ + int i; + struct gsm_dlci *dlci = gsm->dlci[0]; + struct gsm_msg *txq; + + gsm->dead = 1; + + spin_lock(&gsm_mux_lock); + for (i = 0; i < MAX_MUX; i++) { + if (gsm_mux[i] == gsm) { + gsm_mux[i] = NULL; + break; + } + } + spin_unlock(&gsm_mux_lock); + WARN_ON(i == MAX_MUX); + + del_timer_sync(&gsm->t2_timer); + /* Now we are sure T2 has stopped */ + if (dlci) { + dlci->dead = 1; + gsm_dlci_begin_close(dlci); + wait_event_interruptible(gsm->event, + dlci->state == DLCI_CLOSED); + } + /* Free up any link layer users */ + for (i = 0; i < NUM_DLCI; i++) + if (gsm->dlci[i]) + gsm_dlci_free(gsm->dlci[i]); + /* Now wipe the queues */ + for (txq = gsm->tx_head; txq != NULL; txq = gsm->tx_head) { + gsm->tx_head = txq->next; + kfree(txq); + } + gsm->tx_tail = NULL; +} +EXPORT_SYMBOL_GPL(gsm_cleanup_mux); + +/** + * gsm_activate_mux - generic GSM setup + * @gsm: our mux + * + * Set up the bits of the mux which are the same for all framing + * protocols. Add the mux to the mux table so it can be opened and + * finally kick off connecting to DLCI 0 on the modem. + */ + +int gsm_activate_mux(struct gsm_mux *gsm) +{ + struct gsm_dlci *dlci; + int i = 0; + + init_timer(&gsm->t2_timer); + gsm->t2_timer.function = gsm_control_retransmit; + gsm->t2_timer.data = (unsigned long)gsm; + init_waitqueue_head(&gsm->event); + spin_lock_init(&gsm->control_lock); + spin_lock_init(&gsm->tx_lock); + + if (gsm->encoding == 0) + gsm->receive = gsm0_receive; + else + gsm->receive = gsm1_receive; + gsm->error = gsm_error; + + spin_lock(&gsm_mux_lock); + for (i = 0; i < MAX_MUX; i++) { + if (gsm_mux[i] == NULL) { + gsm_mux[i] = gsm; + break; + } + } + spin_unlock(&gsm_mux_lock); + if (i == MAX_MUX) + return -EBUSY; + + dlci = gsm_dlci_alloc(gsm, 0); + if (dlci == NULL) + return -ENOMEM; + gsm->dead = 0; /* Tty opens are now permissible */ + return 0; +} +EXPORT_SYMBOL_GPL(gsm_activate_mux); + +/** + * gsm_free_mux - free up a mux + * @mux: mux to free + * + * Dispose of allocated resources for a dead mux. No refcounting + * at present so the mux must be truely dead. + */ +void gsm_free_mux(struct gsm_mux *gsm) +{ + kfree(gsm->txframe); + kfree(gsm->buf); + kfree(gsm); +} +EXPORT_SYMBOL_GPL(gsm_free_mux); + +/** + * gsm_alloc_mux - allocate a mux + * + * Creates a new mux ready for activation. + */ + +struct gsm_mux *gsm_alloc_mux(void) +{ + struct gsm_mux *gsm = kzalloc(sizeof(struct gsm_mux), GFP_KERNEL); + if (gsm == NULL) + return NULL; + gsm->buf = kmalloc(MAX_MRU + 1, GFP_KERNEL); + if (gsm->buf == NULL) { + kfree(gsm); + return NULL; + } + gsm->txframe = kmalloc(2 * MAX_MRU + 2, GFP_KERNEL); + if (gsm->txframe == NULL) { + kfree(gsm->buf); + kfree(gsm); + return NULL; + } + spin_lock_init(&gsm->lock); + + gsm->t1 = T1; + gsm->t2 = T2; + gsm->n2 = N2; + gsm->ftype = UIH; + gsm->initiator = 0; + gsm->adaption = 1; + gsm->encoding = 1; + gsm->mru = 64; /* Default to encoding 1 so these should be 64 */ + gsm->mtu = 64; + gsm->dead = 1; /* Avoid early tty opens */ + + return gsm; +} +EXPORT_SYMBOL_GPL(gsm_alloc_mux); + + + + +/** + * gsmld_output - write to link + * @gsm: our mux + * @data: bytes to output + * @len: size + * + * Write a block of data from the GSM mux to the data channel. This + * will eventually be serialized from above but at the moment isn't. + */ + +static int gsmld_output(struct gsm_mux *gsm, u8 *data, int len) +{ + if (tty_write_room(gsm->tty) < len) { + set_bit(TTY_DO_WRITE_WAKEUP, &gsm->tty->flags); + return -ENOSPC; + } + if (debug & 4) { + printk("-->%d bytes out\n", len); + hex_packet(data, len); + } + gsm->tty->ops->write(gsm->tty, data, len); + return len; +} + +/** + * gsmld_attach_gsm - mode set up + * @tty: our tty structure + * @gsm: our mux + * + * Set up the MUX for basic mode and commence connecting to the + * modem. Currently called from the line discipline set up but + * will need moving to an ioctl path. + */ + +static int gsmld_attach_gsm(struct tty_struct *tty, struct gsm_mux *gsm) +{ + int ret; + + gsm->tty = tty_kref_get(tty); + gsm->output = gsmld_output; + ret = gsm_activate_mux(gsm); + if (ret != 0) + tty_kref_put(gsm->tty); + return ret; +} + + +/** + * gsmld_detach_gsm - stop doing 0710 mux + * @tty: tty atttached to the mux + * @gsm: mux + * + * Shutdown and then clean up the resources used by the line discipline + */ + +static void gsmld_detach_gsm(struct tty_struct *tty, struct gsm_mux *gsm) +{ + WARN_ON(tty != gsm->tty); + gsm_cleanup_mux(gsm); + tty_kref_put(gsm->tty); + gsm->tty = NULL; +} + +static void gsmld_receive_buf(struct tty_struct *tty, const unsigned char *cp, + char *fp, int count) +{ + struct gsm_mux *gsm = tty->disc_data; + const unsigned char *dp; + char *f; + int i; + char buf[64]; + char flags; + + if (debug & 4) { + printk("Inbytes %dd\n", count); + hex_packet(cp, count); + } + + for (i = count, dp = cp, f = fp; i; i--, dp++) { + flags = *f++; + switch (flags) { + case TTY_NORMAL: + gsm->receive(gsm, *dp); + break; + case TTY_OVERRUN: + case TTY_BREAK: + case TTY_PARITY: + case TTY_FRAME: + gsm->error(gsm, *dp, flags); + break; + default: + printk(KERN_ERR "%s: unknown flag %d\n", + tty_name(tty, buf), flags); + break; + } + } + /* FASYNC if needed ? */ + /* If clogged call tty_throttle(tty); */ +} + +/** + * gsmld_chars_in_buffer - report available bytes + * @tty: tty device + * + * Report the number of characters buffered to be delivered to user + * at this instant in time. + * + * Locking: gsm lock + */ + +static ssize_t gsmld_chars_in_buffer(struct tty_struct *tty) +{ + return 0; +} + +/** + * gsmld_flush_buffer - clean input queue + * @tty: terminal device + * + * Flush the input buffer. Called when the line discipline is + * being closed, when the tty layer wants the buffer flushed (eg + * at hangup). + */ + +static void gsmld_flush_buffer(struct tty_struct *tty) +{ +} + +/** + * gsmld_close - close the ldisc for this tty + * @tty: device + * + * Called from the terminal layer when this line discipline is + * being shut down, either because of a close or becsuse of a + * discipline change. The function will not be called while other + * ldisc methods are in progress. + */ + +static void gsmld_close(struct tty_struct *tty) +{ + struct gsm_mux *gsm = tty->disc_data; + + gsmld_detach_gsm(tty, gsm); + + gsmld_flush_buffer(tty); + /* Do other clean up here */ + gsm_free_mux(gsm); +} + +/** + * gsmld_open - open an ldisc + * @tty: terminal to open + * + * Called when this line discipline is being attached to the + * terminal device. Can sleep. Called serialized so that no + * other events will occur in parallel. No further open will occur + * until a close. + */ + +static int gsmld_open(struct tty_struct *tty) +{ + struct gsm_mux *gsm; + + if (tty->ops->write == NULL) + return -EINVAL; + + /* Attach our ldisc data */ + gsm = gsm_alloc_mux(); + if (gsm == NULL) + return -ENOMEM; + + tty->disc_data = gsm; + tty->receive_room = 65536; + + /* Attach the initial passive connection */ + gsm->encoding = 1; + return gsmld_attach_gsm(tty, gsm); +} + +/** + * gsmld_write_wakeup - asynchronous I/O notifier + * @tty: tty device + * + * Required for the ptys, serial driver etc. since processes + * that attach themselves to the master and rely on ASYNC + * IO must be woken up + */ + +static void gsmld_write_wakeup(struct tty_struct *tty) +{ + struct gsm_mux *gsm = tty->disc_data; + + /* Queue poll */ + clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); + gsm_data_kick(gsm); + if (gsm->tx_bytes < TX_THRESH_LO) + gsm_dlci_data_sweep(gsm); +} + +/** + * gsmld_read - read function for tty + * @tty: tty device + * @file: file object + * @buf: userspace buffer pointer + * @nr: size of I/O + * + * Perform reads for the line discipline. We are guaranteed that the + * line discipline will not be closed under us but we may get multiple + * parallel readers and must handle this ourselves. We may also get + * a hangup. Always called in user context, may sleep. + * + * This code must be sure never to sleep through a hangup. + */ + +static ssize_t gsmld_read(struct tty_struct *tty, struct file *file, + unsigned char __user *buf, size_t nr) +{ + return -EOPNOTSUPP; +} + +/** + * gsmld_write - write function for tty + * @tty: tty device + * @file: file object + * @buf: userspace buffer pointer + * @nr: size of I/O + * + * Called when the owner of the device wants to send a frame + * itself (or some other control data). The data is transferred + * as-is and must be properly framed and checksummed as appropriate + * by userspace. Frames are either sent whole or not at all as this + * avoids pain user side. + */ + +static ssize_t gsmld_write(struct tty_struct *tty, struct file *file, + const unsigned char *buf, size_t nr) +{ + int space = tty_write_room(tty); + if (space >= nr) + return tty->ops->write(tty, buf, nr); + set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); + return -ENOBUFS; +} + +/** + * gsmld_poll - poll method for N_GSM0710 + * @tty: terminal device + * @file: file accessing it + * @wait: poll table + * + * Called when the line discipline is asked to poll() for data or + * for special events. This code is not serialized with respect to + * other events save open/close. + * + * This code must be sure never to sleep through a hangup. + * Called without the kernel lock held - fine + */ + +static unsigned int gsmld_poll(struct tty_struct *tty, struct file *file, + poll_table *wait) +{ + unsigned int mask = 0; + struct gsm_mux *gsm = tty->disc_data; + + poll_wait(file, &tty->read_wait, wait); + poll_wait(file, &tty->write_wait, wait); + if (tty_hung_up_p(file)) + mask |= POLLHUP; + if (!tty_is_writelocked(tty) && tty_write_room(tty) > 0) + mask |= POLLOUT | POLLWRNORM; + if (gsm->dead) + mask |= POLLHUP; + return mask; +} + +static int gsmld_config(struct tty_struct *tty, struct gsm_mux *gsm, + struct gsm_config *c) +{ + int need_close = 0; + int need_restart = 0; + + /* Stuff we don't support yet - UI or I frame transport, windowing */ + if ((c->adaption !=1 && c->adaption != 2) || c->k) + return -EOPNOTSUPP; + /* Check the MRU/MTU range looks sane */ + if (c->mru > MAX_MRU || c->mtu > MAX_MTU || c->mru < 8 || c->mtu < 8) + return -EINVAL; + if (c->n2 < 3) + return -EINVAL; + if (c->encapsulation > 1) /* Basic, advanced, no I */ + return -EINVAL; + if (c->initiator > 1) + return -EINVAL; + if (c->i == 0 || c->i > 2) /* UIH and UI only */ + return -EINVAL; + /* + * See what is needed for reconfiguration + */ + + /* Timing fields */ + if (c->t1 != 0 && c->t1 != gsm->t1) + need_restart = 1; + if (c->t2 != 0 && c->t2 != gsm->t2) + need_restart = 1; + if (c->encapsulation != gsm->encoding) + need_restart = 1; + if (c->adaption != gsm->adaption) + need_restart = 1; + /* Requires care */ + if (c->initiator != gsm->initiator) + need_close = 1; + if (c->mru != gsm->mru) + need_restart = 1; + if (c->mtu != gsm->mtu) + need_restart = 1; + + /* + * Close down what is needed, restart and initiate the new + * configuration + */ + + if (need_close || need_restart) { + gsm_dlci_begin_close(gsm->dlci[0]); + /* This will timeout if the link is down due to N2 expiring */ + wait_event_interruptible(gsm->event, + gsm->dlci[0]->state == DLCI_CLOSED); + if (signal_pending(current)) + return -EINTR; + } + if (need_restart) + gsm_cleanup_mux(gsm); + + gsm->initiator = c->initiator; + gsm->mru = c->mru; + gsm->encoding = c->encapsulation; + gsm->adaption = c->adaption; + + if (c->i == 1) + gsm->ftype = UIH; + else if (c->i == 2) + gsm->ftype = UI; + + if (c->t1) + gsm->t1 = c->t1; + if (c->t2) + gsm->t2 = c->t2; + + /* FIXME: We need to separate activation/deactivation from adding + and removing from the mux array */ + if (need_restart) + gsm_activate_mux(gsm); + if (gsm->initiator && need_close) + gsm_dlci_begin_open(gsm->dlci[0]); + return 0; +} + +static int gsmld_ioctl(struct tty_struct *tty, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct gsm_config c; + struct gsm_mux *gsm = tty->disc_data; + + switch (cmd) { + case GSMIOC_GETCONF: + memset(&c, 0, sizeof(c)); + c.adaption = gsm->adaption; + c.encapsulation = gsm->encoding; + c.initiator = gsm->initiator; + c.t1 = gsm->t1; + c.t2 = gsm->t2; + c.t3 = 0; /* Not supported */ + c.n2 = gsm->n2; + if (gsm->ftype == UIH) + c.i = 1; + else + c.i = 2; + printk("Ftype %d i %d\n", gsm->ftype, c.i); + c.mru = gsm->mru; + c.mtu = gsm->mtu; + c.k = 0; + if (copy_to_user((void *)arg, &c, sizeof(c))) + return -EFAULT; + return 0; + case GSMIOC_SETCONF: + if (copy_from_user(&c, (void *)arg, sizeof(c))) + return -EFAULT; + return gsmld_config(tty, gsm, &c); + default: + return n_tty_ioctl_helper(tty, file, cmd, arg); + } +} + + +/* Line discipline for real tty */ +struct tty_ldisc_ops tty_ldisc_packet = { + .owner = THIS_MODULE, + .magic = TTY_LDISC_MAGIC, + .name = "n_gsm", + .open = gsmld_open, + .close = gsmld_close, + .flush_buffer = gsmld_flush_buffer, + .chars_in_buffer = gsmld_chars_in_buffer, + .read = gsmld_read, + .write = gsmld_write, + .ioctl = gsmld_ioctl, + .poll = gsmld_poll, + .receive_buf = gsmld_receive_buf, + .write_wakeup = gsmld_write_wakeup +}; + +/* + * Virtual tty side + */ + +#define TX_SIZE 512 + +static int gsmtty_modem_update(struct gsm_dlci *dlci, u8 brk) +{ + u8 modembits[5]; + struct gsm_control *ctrl; + int len = 2; + + if (brk) + len++; + + modembits[0] = len << 1 | EA; /* Data bytes */ + modembits[1] = dlci->addr << 2 | 3; /* DLCI, EA, 1 */ + modembits[2] = gsm_encode_modem(dlci) << 1 | EA; + if (brk) + modembits[3] = brk << 4 | 2 | EA; /* Valid, EA */ + ctrl = gsm_control_send(dlci->gsm, CMD_MSC, modembits, len + 1); + if (ctrl == NULL) + return -ENOMEM; + return gsm_control_wait(dlci->gsm, ctrl); +} + +static int gsm_carrier_raised(struct tty_port *port) +{ + struct gsm_dlci *dlci = container_of(port, struct gsm_dlci, port); + /* Not yet open so no carrier info */ + if (dlci->state != DLCI_OPEN) + return 0; + if (debug & 2) + return 1; + return dlci->modem_rx & TIOCM_CD; +} + +static void gsm_dtr_rts(struct tty_port *port, int onoff) +{ + struct gsm_dlci *dlci = container_of(port, struct gsm_dlci, port); + unsigned int modem_tx = dlci->modem_tx; + if (onoff) + modem_tx |= TIOCM_DTR | TIOCM_RTS; + else + modem_tx &= ~(TIOCM_DTR | TIOCM_RTS); + if (modem_tx != dlci->modem_tx) { + dlci->modem_tx = modem_tx; + gsmtty_modem_update(dlci, 0); + } +} + +static const struct tty_port_operations gsm_port_ops = { + .carrier_raised = gsm_carrier_raised, + .dtr_rts = gsm_dtr_rts, +}; + + +static int gsmtty_open(struct tty_struct *tty, struct file *filp) +{ + struct gsm_mux *gsm; + struct gsm_dlci *dlci; + struct tty_port *port; + unsigned int line = tty->index; + unsigned int mux = line >> 6; + + line = line & 0x3F; + + if (mux >= MAX_MUX) + return -ENXIO; + /* FIXME: we need to lock gsm_mux for lifetimes of ttys eventually */ + if (gsm_mux[mux] == NULL) + return -EUNATCH; + if (line == 0 || line > 61) /* 62/63 reserved */ + return -ECHRNG; + gsm = gsm_mux[mux]; + if (gsm->dead) + return -EL2HLT; + dlci = gsm->dlci[line]; + if (dlci == NULL) + dlci = gsm_dlci_alloc(gsm, line); + if (dlci == NULL) + return -ENOMEM; + port = &dlci->port; + port->count++; + tty->driver_data = dlci; + tty_port_tty_set(port, tty); + + dlci->modem_rx = 0; + /* We could in theory open and close before we wait - eg if we get + a DM straight back. This is ok as that will have caused a hangup */ + set_bit(ASYNCB_INITIALIZED, &port->flags); + /* Start sending off SABM messages */ + gsm_dlci_begin_open(dlci); + /* And wait for virtual carrier */ + return tty_port_block_til_ready(port, tty, filp); +} + +static void gsmtty_close(struct tty_struct *tty, struct file *filp) +{ + struct gsm_dlci *dlci = tty->driver_data; + if (dlci == NULL) + return; + if (tty_port_close_start(&dlci->port, tty, filp) == 0) + return; + gsm_dlci_begin_close(dlci); + tty_port_close_end(&dlci->port, tty); + tty_port_tty_set(&dlci->port, NULL); +} + +static void gsmtty_hangup(struct tty_struct *tty) +{ + struct gsm_dlci *dlci = tty->driver_data; + tty_port_hangup(&dlci->port); + gsm_dlci_begin_close(dlci); +} + +static int gsmtty_write(struct tty_struct *tty, const unsigned char *buf, + int len) +{ + struct gsm_dlci *dlci = tty->driver_data; + /* Stuff the bytes into the fifo queue */ + int sent = kfifo_in_locked(dlci->fifo, buf, len, &dlci->lock); + /* Need to kick the channel */ + gsm_dlci_data_kick(dlci); + return sent; +} + +static int gsmtty_write_room(struct tty_struct *tty) +{ + struct gsm_dlci *dlci = tty->driver_data; + return TX_SIZE - kfifo_len(dlci->fifo); +} + +static int gsmtty_chars_in_buffer(struct tty_struct *tty) +{ + struct gsm_dlci *dlci = tty->driver_data; + return kfifo_len(dlci->fifo); +} + +static void gsmtty_flush_buffer(struct tty_struct *tty) +{ + struct gsm_dlci *dlci = tty->driver_data; + /* Caution needed: If we implement reliable transport classes + then the data being transmitted can't simply be junked once + it has first hit the stack. Until then we can just blow it + away */ + kfifo_reset(dlci->fifo); + /* Need to unhook this DLCI from the transmit queue logic */ +} + +static void gsmtty_wait_until_sent(struct tty_struct *tty, int timeout) +{ + /* The FIFO handles the queue so the kernel will do the right + thing waiting on chars_in_buffer before calling us. No work + to do here */ +} + +static int gsmtty_tiocmget(struct tty_struct *tty, struct file *filp) +{ + struct gsm_dlci *dlci = tty->driver_data; + return dlci->modem_rx; +} + +static int gsmtty_tiocmset(struct tty_struct *tty, struct file *filp, + unsigned int set, unsigned int clear) +{ + struct gsm_dlci *dlci = tty->driver_data; + unsigned int modem_tx = dlci->modem_tx; + + modem_tx &= clear; + modem_tx |= set; + + if (modem_tx != dlci->modem_tx) { + dlci->modem_tx = modem_tx; + return gsmtty_modem_update(dlci, 0); + } + return 0; +} + + +static int gsmtty_ioctl(struct tty_struct *tty, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + return -ENOIOCTLCMD; +} + +static void gsmtty_set_termios(struct tty_struct *tty, struct ktermios *old) +{ + /* For the moment its fixed. In actual fact the speed information + for the virtual channel can be propogated in both directions by + the RPN control message. This however rapidly gets nasty as we + then have to remap modem signals each way according to whether + our virtual cable is null modem etc .. */ + tty_termios_copy_hw(tty->termios, old); +} + +static void gsmtty_throttle(struct tty_struct *tty) +{ + struct gsm_dlci *dlci = tty->driver_data; + if (tty->termios->c_cflag & CRTSCTS) + dlci->modem_tx &= ~TIOCM_DTR; + dlci->throttled = 1; + /* Send an MSC with DTR cleared */ + gsmtty_modem_update(dlci, 0); +} + +static void gsmtty_unthrottle(struct tty_struct *tty) +{ + struct gsm_dlci *dlci = tty->driver_data; + if (tty->termios->c_cflag & CRTSCTS) + dlci->modem_tx |= TIOCM_DTR; + dlci->throttled = 0; + /* Send an MSC with DTR set */ + gsmtty_modem_update(dlci, 0); +} + +static int gsmtty_break_ctl(struct tty_struct *tty, int state) +{ + struct gsm_dlci *dlci = tty->driver_data; + int encode = 0; /* Off */ + + if (state == -1) /* "On indefinitely" - we can't encode this + properly */ + encode = 0x0F; + else if (state > 0) { + encode = state / 200; /* mS to encoding */ + if (encode > 0x0F) + encode = 0x0F; /* Best effort */ + } + return gsmtty_modem_update(dlci, encode); +} + +static struct tty_driver *gsm_tty_driver; + +/* Virtual ttys for the demux */ +static const struct tty_operations gsmtty_ops = { + .open = gsmtty_open, + .close = gsmtty_close, + .write = gsmtty_write, + .write_room = gsmtty_write_room, + .chars_in_buffer = gsmtty_chars_in_buffer, + .flush_buffer = gsmtty_flush_buffer, + .ioctl = gsmtty_ioctl, + .throttle = gsmtty_throttle, + .unthrottle = gsmtty_unthrottle, + .set_termios = gsmtty_set_termios, + .hangup = gsmtty_hangup, + .wait_until_sent = gsmtty_wait_until_sent, + .tiocmget = gsmtty_tiocmget, + .tiocmset = gsmtty_tiocmset, + .break_ctl = gsmtty_break_ctl, +}; + + + +static int __init gsm_init(void) +{ + /* Fill in our line protocol discipline, and register it */ + int status = tty_register_ldisc(N_GSM0710, &tty_ldisc_packet); + if (status != 0) { + printk(KERN_ERR "n_gsm: can't register line discipline (err = %d)\n", status); + return status; + } + + gsm_tty_driver = alloc_tty_driver(256); + if (!gsm_tty_driver) { + tty_unregister_ldisc(N_GSM0710); + printk(KERN_ERR "gsm_init: tty allocation failed.\n"); + return -EINVAL; + } + gsm_tty_driver->owner = THIS_MODULE; + gsm_tty_driver->driver_name = "gsmtty"; + gsm_tty_driver->name = "gsmtty"; + gsm_tty_driver->major = 0; /* Dynamic */ + gsm_tty_driver->minor_start = 0; + gsm_tty_driver->type = TTY_DRIVER_TYPE_SERIAL; + gsm_tty_driver->subtype = SERIAL_TYPE_NORMAL; + gsm_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV + | TTY_DRIVER_HARDWARE_BREAK; + gsm_tty_driver->init_termios = tty_std_termios; + /* Fixme */ + gsm_tty_driver->init_termios.c_lflag &= ~ECHO; + tty_set_operations(gsm_tty_driver, &gsmtty_ops); + + spin_lock_init(&gsm_mux_lock); + + if (tty_register_driver(gsm_tty_driver)) { + put_tty_driver(gsm_tty_driver); + tty_unregister_ldisc(N_GSM0710); + printk(KERN_ERR "gsm_init: tty registration failed.\n"); + return -EBUSY; + } + printk(KERN_INFO "gsm_init: loaded as %d,%d.\n", gsm_tty_driver->major, gsm_tty_driver->minor_start); + return 0; +} + +static void __exit gsm_exit(void) +{ + int status = tty_unregister_ldisc(N_GSM0710); + if (status != 0) + printk(KERN_ERR "n_gsm: can't unregister line discipline (err = %d)\n", status); + tty_unregister_driver(gsm_tty_driver); + put_tty_driver(gsm_tty_driver); + printk(KERN_INFO "gsm_init: unloaded.\n"); +} + +module_init(gsm_init); +module_exit(gsm_exit); + + +MODULE_LICENSE("GPL"); +MODULE_ALIAS_LDISC(N_GSM0710); diff --git a/drivers/char/nvram.c b/drivers/char/nvram.c index 47e8f7b0e4c1..66d2917b003f 100644 --- a/drivers/char/nvram.c +++ b/drivers/char/nvram.c @@ -296,8 +296,8 @@ checksum_err: return -EIO; } -static int nvram_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) +static long nvram_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) { int i; @@ -308,6 +308,7 @@ static int nvram_ioctl(struct inode *inode, struct file *file, if (!capable(CAP_SYS_ADMIN)) return -EACCES; + lock_kernel(); spin_lock_irq(&rtc_lock); for (i = 0; i < NVRAM_BYTES; ++i) @@ -315,6 +316,7 @@ static int nvram_ioctl(struct inode *inode, struct file *file, __nvram_set_checksum(); spin_unlock_irq(&rtc_lock); + unlock_kernel(); return 0; case NVRAM_SETCKS: @@ -323,9 +325,11 @@ static int nvram_ioctl(struct inode *inode, struct file *file, if (!capable(CAP_SYS_ADMIN)) return -EACCES; + lock_kernel(); spin_lock_irq(&rtc_lock); __nvram_set_checksum(); spin_unlock_irq(&rtc_lock); + unlock_kernel(); return 0; default: @@ -422,7 +426,7 @@ static const struct file_operations nvram_fops = { .llseek = nvram_llseek, .read = nvram_read, .write = nvram_write, - .ioctl = nvram_ioctl, + .unlocked_ioctl = nvram_ioctl, .open = nvram_open, .release = nvram_release, }; diff --git a/drivers/char/nwflash.c b/drivers/char/nwflash.c index f80810901db6..043a1c7b86be 100644 --- a/drivers/char/nwflash.c +++ b/drivers/char/nwflash.c @@ -94,8 +94,9 @@ static int get_flash_id(void) return c2; } -static int flash_ioctl(struct inode *inodep, struct file *filep, unsigned int cmd, unsigned long arg) +static long flash_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) { + lock_kernel(); switch (cmd) { case CMD_WRITE_DISABLE: gbWriteBase64Enable = 0; @@ -113,8 +114,10 @@ static int flash_ioctl(struct inode *inodep, struct file *filep, unsigned int cm default: gbWriteBase64Enable = 0; gbWriteEnable = 0; + unlock_kernel(); return -EINVAL; } + unlock_kernel(); return 0; } @@ -631,7 +634,7 @@ static const struct file_operations flash_fops = .llseek = flash_llseek, .read = flash_read, .write = flash_write, - .ioctl = flash_ioctl, + .unlocked_ioctl = flash_ioctl, }; static struct miscdevice flash_miscdev = diff --git a/drivers/char/ppdev.c b/drivers/char/ppdev.c index fdd37543aa79..02abfddce45a 100644 --- a/drivers/char/ppdev.c +++ b/drivers/char/ppdev.c @@ -287,12 +287,10 @@ static int register_device (int minor, struct pp_struct *pp) char *name; int fl; - name = kmalloc (strlen (CHRDEV) + 3, GFP_KERNEL); + name = kasprintf(GFP_KERNEL, CHRDEV "%x", minor); if (name == NULL) return -ENOMEM; - sprintf (name, CHRDEV "%x", minor); - port = parport_find_number (minor); if (!port) { printk (KERN_WARNING "%s: no associated port!\n", name); diff --git a/drivers/char/ps3flash.c b/drivers/char/ps3flash.c index 606048b72bcf..85c004a518ee 100644 --- a/drivers/char/ps3flash.c +++ b/drivers/char/ps3flash.c @@ -305,8 +305,7 @@ static int ps3flash_flush(struct file *file, fl_owner_t id) return ps3flash_writeback(ps3flash_dev); } -static int ps3flash_fsync(struct file *file, struct dentry *dentry, - int datasync) +static int ps3flash_fsync(struct file *file, int datasync) { return ps3flash_writeback(ps3flash_dev); } diff --git a/drivers/char/ramoops.c b/drivers/char/ramoops.c new file mode 100644 index 000000000000..74f00b5ffa36 --- /dev/null +++ b/drivers/char/ramoops.c @@ -0,0 +1,162 @@ +/* + * RAM Oops/Panic logger + * + * Copyright (C) 2010 Marco Stornelli <marco.stornelli@gmail.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/kmsg_dump.h> +#include <linux/time.h> +#include <linux/io.h> +#include <linux/ioport.h> + +#define RAMOOPS_KERNMSG_HDR "====" +#define RAMOOPS_HEADER_SIZE (5 + sizeof(struct timeval)) + +#define RECORD_SIZE 4096 + +static ulong mem_address; +module_param(mem_address, ulong, 0400); +MODULE_PARM_DESC(mem_address, + "start of reserved RAM used to store oops/panic logs"); + +static ulong mem_size; +module_param(mem_size, ulong, 0400); +MODULE_PARM_DESC(mem_size, + "size of reserved RAM used to store oops/panic logs"); + +static int dump_oops = 1; +module_param(dump_oops, int, 0600); +MODULE_PARM_DESC(dump_oops, + "set to 1 to dump oopses, 0 to only dump panics (default 1)"); + +static struct ramoops_context { + struct kmsg_dumper dump; + void *virt_addr; + phys_addr_t phys_addr; + unsigned long size; + int count; + int max_count; +} oops_cxt; + +static void ramoops_do_dump(struct kmsg_dumper *dumper, + enum kmsg_dump_reason reason, const char *s1, unsigned long l1, + const char *s2, unsigned long l2) +{ + struct ramoops_context *cxt = container_of(dumper, + struct ramoops_context, dump); + unsigned long s1_start, s2_start; + unsigned long l1_cpy, l2_cpy; + int res; + char *buf; + struct timeval timestamp; + + /* Only dump oopses if dump_oops is set */ + if (reason == KMSG_DUMP_OOPS && !dump_oops) + return; + + buf = (char *)(cxt->virt_addr + (cxt->count * RECORD_SIZE)); + memset(buf, '\0', RECORD_SIZE); + res = sprintf(buf, "%s", RAMOOPS_KERNMSG_HDR); + buf += res; + do_gettimeofday(×tamp); + res = sprintf(buf, "%lu.%lu\n", (long)timestamp.tv_sec, (long)timestamp.tv_usec); + buf += res; + + l2_cpy = min(l2, (unsigned long)(RECORD_SIZE - RAMOOPS_HEADER_SIZE)); + l1_cpy = min(l1, (unsigned long)(RECORD_SIZE - RAMOOPS_HEADER_SIZE) - l2_cpy); + + s2_start = l2 - l2_cpy; + s1_start = l1 - l1_cpy; + + memcpy(buf, s1 + s1_start, l1_cpy); + memcpy(buf + l1_cpy, s2 + s2_start, l2_cpy); + + cxt->count = (cxt->count + 1) % cxt->max_count; +} + +static int __init ramoops_init(void) +{ + struct ramoops_context *cxt = &oops_cxt; + int err = -EINVAL; + + if (!mem_size) { + printk(KERN_ERR "ramoops: invalid size specification"); + goto fail3; + } + + rounddown_pow_of_two(mem_size); + + if (mem_size < RECORD_SIZE) { + printk(KERN_ERR "ramoops: size too small"); + goto fail3; + } + + cxt->max_count = mem_size / RECORD_SIZE; + cxt->count = 0; + cxt->size = mem_size; + cxt->phys_addr = mem_address; + + if (!request_mem_region(cxt->phys_addr, cxt->size, "ramoops")) { + printk(KERN_ERR "ramoops: request mem region failed"); + err = -EINVAL; + goto fail3; + } + + cxt->virt_addr = ioremap(cxt->phys_addr, cxt->size); + if (!cxt->virt_addr) { + printk(KERN_ERR "ramoops: ioremap failed"); + goto fail2; + } + + cxt->dump.dump = ramoops_do_dump; + err = kmsg_dump_register(&cxt->dump); + if (err) { + printk(KERN_ERR "ramoops: registering kmsg dumper failed"); + goto fail1; + } + + return 0; + +fail1: + iounmap(cxt->virt_addr); +fail2: + release_mem_region(cxt->phys_addr, cxt->size); +fail3: + return err; +} + +static void __exit ramoops_exit(void) +{ + struct ramoops_context *cxt = &oops_cxt; + + if (kmsg_dump_unregister(&cxt->dump) < 0) + printk(KERN_WARNING "ramoops: could not unregister kmsg_dumper"); + + iounmap(cxt->virt_addr); + release_mem_region(cxt->phys_addr, cxt->size); +} + + +module_init(ramoops_init); +module_exit(ramoops_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Marco Stornelli <marco.stornelli@gmail.com>"); +MODULE_DESCRIPTION("RAM Oops/Panic logger/driver"); diff --git a/drivers/char/random.c b/drivers/char/random.c index 2fd3d39995d5..8d85587b6d4f 100644 --- a/drivers/char/random.c +++ b/drivers/char/random.c @@ -257,6 +257,7 @@ #define INPUT_POOL_WORDS 128 #define OUTPUT_POOL_WORDS 32 #define SEC_XFER_SIZE 512 +#define EXTRACT_SIZE 10 /* * The minimum number of bits of entropy before we wake up a read on @@ -414,7 +415,7 @@ struct entropy_store { unsigned add_ptr; int entropy_count; int input_rotate; - __u8 *last_data; + __u8 last_data[EXTRACT_SIZE]; }; static __u32 input_pool_data[INPUT_POOL_WORDS]; @@ -714,8 +715,6 @@ void add_disk_randomness(struct gendisk *disk) } #endif -#define EXTRACT_SIZE 10 - /********************************************************************* * * Entropy extraction routines @@ -862,7 +861,7 @@ static ssize_t extract_entropy(struct entropy_store *r, void *buf, while (nbytes) { extract_buf(r, tmp); - if (r->last_data) { + if (fips_enabled) { spin_lock_irqsave(&r->lock, flags); if (!memcmp(tmp, r->last_data, EXTRACT_SIZE)) panic("Hardware RNG duplicated output!\n"); @@ -951,9 +950,6 @@ static void init_std_data(struct entropy_store *r) now = ktime_get_real(); mix_pool_bytes(r, &now, sizeof(now)); mix_pool_bytes(r, utsname(), sizeof(*(utsname()))); - /* Enable continuous test in fips mode */ - if (fips_enabled) - r->last_data = kmalloc(EXTRACT_SIZE, GFP_KERNEL); } static int rand_initialize(void) diff --git a/drivers/char/raw.c b/drivers/char/raw.c index 8756ab0daa8b..b38942f6bf31 100644 --- a/drivers/char/raw.c +++ b/drivers/char/raw.c @@ -121,13 +121,17 @@ static int raw_release(struct inode *inode, struct file *filp) /* * Forward ioctls to the underlying block device. */ -static int -raw_ioctl(struct inode *inode, struct file *filp, - unsigned int command, unsigned long arg) +static long +raw_ioctl(struct file *filp, unsigned int command, unsigned long arg) { struct block_device *bdev = filp->private_data; + int ret; + + lock_kernel(); + ret = blkdev_ioctl(bdev, 0, command, arg); + unlock_kernel(); - return blkdev_ioctl(bdev, 0, command, arg); + return ret; } static void bind_device(struct raw_config_request *rq) @@ -141,13 +145,14 @@ static void bind_device(struct raw_config_request *rq) * Deal with ioctls against the raw-device control interface, to bind * and unbind other raw devices. */ -static int raw_ctl_ioctl(struct inode *inode, struct file *filp, - unsigned int command, unsigned long arg) +static long raw_ctl_ioctl(struct file *filp, unsigned int command, + unsigned long arg) { struct raw_config_request rq; struct raw_device_data *rawdev; int err = 0; + lock_kernel(); switch (command) { case RAW_SETBIND: case RAW_GETBIND: @@ -240,25 +245,26 @@ static int raw_ctl_ioctl(struct inode *inode, struct file *filp, break; } out: + unlock_kernel(); return err; } static const struct file_operations raw_fops = { - .read = do_sync_read, - .aio_read = generic_file_aio_read, - .write = do_sync_write, - .aio_write = blkdev_aio_write, - .fsync = blkdev_fsync, - .open = raw_open, - .release= raw_release, - .ioctl = raw_ioctl, - .owner = THIS_MODULE, + .read = do_sync_read, + .aio_read = generic_file_aio_read, + .write = do_sync_write, + .aio_write = blkdev_aio_write, + .fsync = blkdev_fsync, + .open = raw_open, + .release = raw_release, + .unlocked_ioctl = raw_ioctl, + .owner = THIS_MODULE, }; static const struct file_operations raw_ctl_fops = { - .ioctl = raw_ctl_ioctl, - .open = raw_open, - .owner = THIS_MODULE, + .unlocked_ioctl = raw_ctl_ioctl, + .open = raw_open, + .owner = THIS_MODULE, }; static struct cdev raw_cdev; diff --git a/drivers/char/serial167.c b/drivers/char/serial167.c index 78a62ebe75c7..ecbe479c7d68 100644 --- a/drivers/char/serial167.c +++ b/drivers/char/serial167.c @@ -176,23 +176,6 @@ static void config_setup(struct cyclades_port *); static void show_status(int); #endif -#ifdef CONFIG_REMOTE_DEBUG -static void debug_setup(void); -void queueDebugChar(int c); -int getDebugChar(void); - -#define DEBUG_PORT 1 -#define DEBUG_LEN 256 - -typedef struct { - int in; - int out; - unsigned char buf[DEBUG_LEN]; -} debugq; - -debugq debugiq; -#endif - /* * I have my own version of udelay(), as it is needed when initialising * the chip, before the delay loop has been calibrated. Should probably @@ -515,11 +498,6 @@ static irqreturn_t cd2401_tx_interrupt(int irq, void *dev_id) /* determine the channel and change to that context */ channel = (u_short) (base_addr[CyLICR] >> 2); -#ifdef CONFIG_REMOTE_DEBUG - if (channel == DEBUG_PORT) { - panic("TxInt on debug port!!!"); - } -#endif /* validate the port number (as configured and open) */ if ((channel < 0) || (NR_PORTS <= channel)) { base_addr[CyIER] &= ~(CyTxMpty | CyTxRdy); @@ -634,14 +612,6 @@ static irqreturn_t cd2401_rx_interrupt(int irq, void *dev_id) info->last_active = jiffies; save_cnt = char_count = base_addr[CyRFOC]; -#ifdef CONFIG_REMOTE_DEBUG - if (channel == DEBUG_PORT) { - while (char_count--) { - data = base_addr[CyRDR]; - queueDebugChar(data); - } - } else -#endif /* if there is nowhere to put the data, discard it */ if (info->tty == 0) { while (char_count--) { @@ -2195,9 +2165,7 @@ static int __init serial167_init(void) port_num++; info++; } -#ifdef CONFIG_REMOTE_DEBUG - debug_setup(); -#endif + ret = request_irq(MVME167_IRQ_SER_ERR, cd2401_rxerr_interrupt, 0, "cd2401_errors", cd2401_rxerr_interrupt); if (ret) { @@ -2518,193 +2486,4 @@ static int __init serial167_console_init(void) console_initcall(serial167_console_init); -#ifdef CONFIG_REMOTE_DEBUG -void putDebugChar(int c) -{ - volatile unsigned char *base_addr = (u_char *) BASE_ADDR; - unsigned long flags; - volatile u_char sink; - u_char ier; - int port; - - local_irq_save(flags); - - /* Ensure transmitter is enabled! */ - - port = DEBUG_PORT; - base_addr[CyCAR] = (u_char) port; - while (base_addr[CyCCR]) - ; - base_addr[CyCCR] = CyENB_XMTR; - - ier = base_addr[CyIER]; - base_addr[CyIER] = CyTxMpty; - - while (1) { - if (pcc2chip[PccSCCTICR] & 0x20) { - /* We have a Tx int. Acknowledge it */ - sink = pcc2chip[PccTPIACKR]; - if ((base_addr[CyLICR] >> 2) == port) { - base_addr[CyTDR] = c; - base_addr[CyTEOIR] = 0; - break; - } else - base_addr[CyTEOIR] = CyNOTRANS; - } - } - - base_addr[CyIER] = ier; - - local_irq_restore(flags); -} - -int getDebugChar() -{ - volatile unsigned char *base_addr = (u_char *) BASE_ADDR; - unsigned long flags; - volatile u_char sink; - u_char ier; - int port; - int i, c; - - i = debugiq.out; - if (i != debugiq.in) { - c = debugiq.buf[i]; - if (++i == DEBUG_LEN) - i = 0; - debugiq.out = i; - return c; - } - /* OK, nothing in queue, wait in poll loop */ - - local_irq_save(flags); - - /* Ensure receiver is enabled! */ - - port = DEBUG_PORT; - base_addr[CyCAR] = (u_char) port; -#if 0 - while (base_addr[CyCCR]) - ; - base_addr[CyCCR] = CyENB_RCVR; -#endif - ier = base_addr[CyIER]; - base_addr[CyIER] = CyRxData; - - while (1) { - if (pcc2chip[PccSCCRICR] & 0x20) { - /* We have a Rx int. Acknowledge it */ - sink = pcc2chip[PccRPIACKR]; - if ((base_addr[CyLICR] >> 2) == port) { - int cnt = base_addr[CyRFOC]; - while (cnt-- > 0) { - c = base_addr[CyRDR]; - if (c == 0) - printk - ("!! debug char is null (cnt=%d) !!", - cnt); - else - queueDebugChar(c); - } - base_addr[CyREOIR] = 0; - i = debugiq.out; - if (i == debugiq.in) - panic("Debug input queue empty!"); - c = debugiq.buf[i]; - if (++i == DEBUG_LEN) - i = 0; - debugiq.out = i; - break; - } else - base_addr[CyREOIR] = CyNOTRANS; - } - } - - base_addr[CyIER] = ier; - - local_irq_restore(flags); - - return (c); -} - -void queueDebugChar(int c) -{ - int i; - - i = debugiq.in; - debugiq.buf[i] = c; - if (++i == DEBUG_LEN) - i = 0; - if (i != debugiq.out) - debugiq.in = i; -} - -static void debug_setup() -{ - unsigned long flags; - volatile unsigned char *base_addr = (u_char *) BASE_ADDR; - int i, cflag; - - cflag = B19200; - - local_irq_save(flags); - - for (i = 0; i < 4; i++) { - base_addr[CyCAR] = i; - base_addr[CyLICR] = i << 2; - } - - debugiq.in = debugiq.out = 0; - - base_addr[CyCAR] = DEBUG_PORT; - - /* baud rate */ - i = cflag & CBAUD; - - base_addr[CyIER] = 0; - - base_addr[CyCMR] = CyASYNC; - base_addr[CyLICR] = DEBUG_PORT << 2; - base_addr[CyLIVR] = 0x5c; - - /* tx and rx baud rate */ - - base_addr[CyTCOR] = baud_co[i]; - base_addr[CyTBPR] = baud_bpr[i]; - base_addr[CyRCOR] = baud_co[i] >> 5; - base_addr[CyRBPR] = baud_bpr[i]; - - /* set line characteristics according configuration */ - - base_addr[CySCHR1] = 0; - base_addr[CySCHR2] = 0; - base_addr[CySCRL] = 0; - base_addr[CySCRH] = 0; - base_addr[CyCOR1] = Cy_8_BITS | CyPARITY_NONE; - base_addr[CyCOR2] = 0; - base_addr[CyCOR3] = Cy_1_STOP; - base_addr[CyCOR4] = baud_cor4[i]; - base_addr[CyCOR5] = 0; - base_addr[CyCOR6] = 0; - base_addr[CyCOR7] = 0; - - write_cy_cmd(base_addr, CyINIT_CHAN); - write_cy_cmd(base_addr, CyENB_RCVR); - - base_addr[CyCAR] = DEBUG_PORT; /* !!! Is this needed? */ - - base_addr[CyRTPRL] = 2; - base_addr[CyRTPRH] = 0; - - base_addr[CyMSVR1] = CyRTS; - base_addr[CyMSVR2] = CyDTR; - - base_addr[CyIER] = CyRxData; - - local_irq_restore(flags); - -} /* debug_setup */ - -#endif - MODULE_LICENSE("GPL"); diff --git a/drivers/char/tty_buffer.c b/drivers/char/tty_buffer.c index 7ee52164d474..cc1e9850d655 100644 --- a/drivers/char/tty_buffer.c +++ b/drivers/char/tty_buffer.c @@ -238,7 +238,7 @@ EXPORT_SYMBOL_GPL(tty_buffer_request_room); * @size: size * * Queue a series of bytes to the tty buffering. All the characters - * passed are marked as without error. Returns the number added. + * passed are marked with the supplied flag. Returns the number added. * * Locking: Called functions may take tty->buf.lock */ diff --git a/drivers/char/viotape.c b/drivers/char/viotape.c index 1144a04cda6e..42f7fa442ff8 100644 --- a/drivers/char/viotape.c +++ b/drivers/char/viotape.c @@ -866,7 +866,7 @@ static int viotape_probe(struct vio_dev *vdev, const struct vio_device_id *id) { int i = vdev->unit_address; int j; - struct device_node *node = vdev->dev.archdata.of_node; + struct device_node *node = vdev->dev.of_node; if (i >= VIOTAPE_MAX_TAPE) return -ENODEV; diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index 196428c2287a..8c99bf1b5e9f 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -33,35 +33,6 @@ #include <linux/workqueue.h> #include "hvc_console.h" -/* Moved here from .h file in order to disable MULTIPORT. */ -#define VIRTIO_CONSOLE_F_MULTIPORT 1 /* Does host provide multiple ports? */ - -struct virtio_console_multiport_conf { - struct virtio_console_config config; - /* max. number of ports this device can hold */ - __u32 max_nr_ports; - /* number of ports added so far */ - __u32 nr_ports; -} __attribute__((packed)); - -/* - * A message that's passed between the Host and the Guest for a - * particular port. - */ -struct virtio_console_control { - __u32 id; /* Port number */ - __u16 event; /* The kind of control event (see below) */ - __u16 value; /* Extra information for the key */ -}; - -/* Some events for control messages */ -#define VIRTIO_CONSOLE_PORT_READY 0 -#define VIRTIO_CONSOLE_CONSOLE_PORT 1 -#define VIRTIO_CONSOLE_RESIZE 2 -#define VIRTIO_CONSOLE_PORT_OPEN 3 -#define VIRTIO_CONSOLE_PORT_NAME 4 -#define VIRTIO_CONSOLE_PORT_REMOVE 5 - /* * This is a global struct for storing common data for all the devices * this driver handles. @@ -107,6 +78,9 @@ struct console { /* The hvc device associated with this console port */ struct hvc_struct *hvc; + /* The size of the console */ + struct winsize ws; + /* * This number identifies the number that we used to register * with hvc in hvc_instantiate() and hvc_alloc(); this is the @@ -139,7 +113,6 @@ struct ports_device { * notification */ struct work_struct control_work; - struct work_struct config_work; struct list_head ports; @@ -150,7 +123,7 @@ struct ports_device { spinlock_t cvq_lock; /* The current config space is stored here */ - struct virtio_console_multiport_conf config; + struct virtio_console_config config; /* The virtio device we're associated with */ struct virtio_device *vdev; @@ -189,6 +162,9 @@ struct port { */ spinlock_t inbuf_lock; + /* Protect the operations on the out_vq. */ + spinlock_t outvq_lock; + /* The IO vqs for this port */ struct virtqueue *in_vq, *out_vq; @@ -214,6 +190,8 @@ struct port { /* The 'id' to identify the port with the Host */ u32 id; + bool outvq_full; + /* Is the host device open */ bool host_connected; @@ -328,7 +306,7 @@ static void *get_inbuf(struct port *port) unsigned int len; vq = port->in_vq; - buf = vq->vq_ops->get_buf(vq, &len); + buf = virtqueue_get_buf(vq, &len); if (buf) { buf->len = len; buf->offset = 0; @@ -349,8 +327,8 @@ static int add_inbuf(struct virtqueue *vq, struct port_buffer *buf) sg_init_one(sg, buf->buf, buf->size); - ret = vq->vq_ops->add_buf(vq, sg, 0, 1, buf); - vq->vq_ops->kick(vq); + ret = virtqueue_add_buf(vq, sg, 0, 1, buf); + virtqueue_kick(vq); return ret; } @@ -366,7 +344,7 @@ static void discard_port_data(struct port *port) if (port->inbuf) buf = port->inbuf; else - buf = vq->vq_ops->get_buf(vq, &len); + buf = virtqueue_get_buf(vq, &len); ret = 0; while (buf) { @@ -374,7 +352,7 @@ static void discard_port_data(struct port *port) ret++; free_buf(buf); } - buf = vq->vq_ops->get_buf(vq, &len); + buf = virtqueue_get_buf(vq, &len); } port->inbuf = NULL; if (ret) @@ -403,57 +381,96 @@ out: return ret; } -static ssize_t send_control_msg(struct port *port, unsigned int event, - unsigned int value) +static ssize_t __send_control_msg(struct ports_device *portdev, u32 port_id, + unsigned int event, unsigned int value) { struct scatterlist sg[1]; struct virtio_console_control cpkt; struct virtqueue *vq; unsigned int len; - if (!use_multiport(port->portdev)) + if (!use_multiport(portdev)) return 0; - cpkt.id = port->id; + cpkt.id = port_id; cpkt.event = event; cpkt.value = value; - vq = port->portdev->c_ovq; + vq = portdev->c_ovq; sg_init_one(sg, &cpkt, sizeof(cpkt)); - if (vq->vq_ops->add_buf(vq, sg, 1, 0, &cpkt) >= 0) { - vq->vq_ops->kick(vq); - while (!vq->vq_ops->get_buf(vq, &len)) + if (virtqueue_add_buf(vq, sg, 1, 0, &cpkt) >= 0) { + virtqueue_kick(vq); + while (!virtqueue_get_buf(vq, &len)) cpu_relax(); } return 0; } -static ssize_t send_buf(struct port *port, void *in_buf, size_t in_count) +static ssize_t send_control_msg(struct port *port, unsigned int event, + unsigned int value) +{ + return __send_control_msg(port->portdev, port->id, event, value); +} + +/* Callers must take the port->outvq_lock */ +static void reclaim_consumed_buffers(struct port *port) +{ + void *buf; + unsigned int len; + + while ((buf = virtqueue_get_buf(port->out_vq, &len))) { + kfree(buf); + port->outvq_full = false; + } +} + +static ssize_t send_buf(struct port *port, void *in_buf, size_t in_count, + bool nonblock) { struct scatterlist sg[1]; struct virtqueue *out_vq; ssize_t ret; + unsigned long flags; unsigned int len; out_vq = port->out_vq; + spin_lock_irqsave(&port->outvq_lock, flags); + + reclaim_consumed_buffers(port); + sg_init_one(sg, in_buf, in_count); - ret = out_vq->vq_ops->add_buf(out_vq, sg, 1, 0, in_buf); + ret = virtqueue_add_buf(out_vq, sg, 1, 0, in_buf); /* Tell Host to go! */ - out_vq->vq_ops->kick(out_vq); + virtqueue_kick(out_vq); if (ret < 0) { in_count = 0; - goto fail; + goto done; } - /* Wait till the host acknowledges it pushed out the data we sent. */ - while (!out_vq->vq_ops->get_buf(out_vq, &len)) + if (ret == 0) + port->outvq_full = true; + + if (nonblock) + goto done; + + /* + * Wait till the host acknowledges it pushed out the data we + * sent. This is done for ports in blocking mode or for data + * from the hvc_console; the tty operations are performed with + * spinlocks held so we can't sleep here. + */ + while (!virtqueue_get_buf(out_vq, &len)) cpu_relax(); -fail: - /* We're expected to return the amount of data we wrote */ +done: + spin_unlock_irqrestore(&port->outvq_lock, flags); + /* + * We're expected to return the amount of data we wrote -- all + * of it + */ return in_count; } @@ -503,9 +520,28 @@ static ssize_t fill_readbuf(struct port *port, char *out_buf, size_t out_count, } /* The condition that must be true for polling to end */ -static bool wait_is_over(struct port *port) +static bool will_read_block(struct port *port) +{ + return !port_has_data(port) && port->host_connected; +} + +static bool will_write_block(struct port *port) { - return port_has_data(port) || !port->host_connected; + bool ret; + + if (!port->host_connected) + return true; + + spin_lock_irq(&port->outvq_lock); + /* + * Check if the Host has consumed any buffers since we last + * sent data (this is only applicable for nonblocking ports). + */ + reclaim_consumed_buffers(port); + ret = port->outvq_full; + spin_unlock_irq(&port->outvq_lock); + + return ret; } static ssize_t port_fops_read(struct file *filp, char __user *ubuf, @@ -528,7 +564,7 @@ static ssize_t port_fops_read(struct file *filp, char __user *ubuf, return -EAGAIN; ret = wait_event_interruptible(port->waitqueue, - wait_is_over(port)); + !will_read_block(port)); if (ret < 0) return ret; } @@ -554,9 +590,22 @@ static ssize_t port_fops_write(struct file *filp, const char __user *ubuf, struct port *port; char *buf; ssize_t ret; + bool nonblock; port = filp->private_data; + nonblock = filp->f_flags & O_NONBLOCK; + + if (will_write_block(port)) { + if (nonblock) + return -EAGAIN; + + ret = wait_event_interruptible(port->waitqueue, + !will_write_block(port)); + if (ret < 0) + return ret; + } + count = min((size_t)(32 * 1024), count); buf = kmalloc(count, GFP_KERNEL); @@ -569,9 +618,14 @@ static ssize_t port_fops_write(struct file *filp, const char __user *ubuf, goto free_buf; } - ret = send_buf(port, buf, count); + ret = send_buf(port, buf, count, nonblock); + + if (nonblock && ret > 0) + goto out; + free_buf: kfree(buf); +out: return ret; } @@ -586,7 +640,7 @@ static unsigned int port_fops_poll(struct file *filp, poll_table *wait) ret = 0; if (port->inbuf) ret |= POLLIN | POLLRDNORM; - if (port->host_connected) + if (!will_write_block(port)) ret |= POLLOUT; if (!port->host_connected) ret |= POLLHUP; @@ -610,6 +664,10 @@ static int port_fops_release(struct inode *inode, struct file *filp) spin_unlock_irq(&port->inbuf_lock); + spin_lock_irq(&port->outvq_lock); + reclaim_consumed_buffers(port); + spin_unlock_irq(&port->outvq_lock); + return 0; } @@ -638,6 +696,15 @@ static int port_fops_open(struct inode *inode, struct file *filp) port->guest_connected = true; spin_unlock_irq(&port->inbuf_lock); + spin_lock_irq(&port->outvq_lock); + /* + * There might be a chance that we missed reclaiming a few + * buffers in the window of the port getting previously closed + * and opening now. + */ + reclaim_consumed_buffers(port); + spin_unlock_irq(&port->outvq_lock); + /* Notify host of port being opened */ send_control_msg(filp->private_data, VIRTIO_CONSOLE_PORT_OPEN, 1); @@ -676,9 +743,9 @@ static int put_chars(u32 vtermno, const char *buf, int count) port = find_port_by_vtermno(vtermno); if (!port) - return 0; + return -EPIPE; - return send_buf(port, (void *)buf, count); + return send_buf(port, (void *)buf, count, false); } /* @@ -692,9 +759,13 @@ static int get_chars(u32 vtermno, char *buf, int count) { struct port *port; + /* If we've not set up the port yet, we have no input to give. */ + if (unlikely(early_put_chars)) + return 0; + port = find_port_by_vtermno(vtermno); if (!port) - return 0; + return -EPIPE; /* If we don't have an input queue yet, we can't get input. */ BUG_ON(!port->in_vq); @@ -705,22 +776,14 @@ static int get_chars(u32 vtermno, char *buf, int count) static void resize_console(struct port *port) { struct virtio_device *vdev; - struct winsize ws; /* The port could have been hot-unplugged */ - if (!port) + if (!port || !is_console_port(port)) return; vdev = port->portdev->vdev; - if (virtio_has_feature(vdev, VIRTIO_CONSOLE_F_SIZE)) { - vdev->config->get(vdev, - offsetof(struct virtio_console_config, cols), - &ws.ws_col, sizeof(u16)); - vdev->config->get(vdev, - offsetof(struct virtio_console_config, rows), - &ws.ws_row, sizeof(u16)); - hvc_resize(port->cons.hvc, ws); - } + if (virtio_has_feature(vdev, VIRTIO_CONSOLE_F_SIZE)) + hvc_resize(port->cons.hvc, port->cons.ws); } /* We set the configuration at this point, since we now have a tty */ @@ -804,6 +867,13 @@ int init_port_console(struct port *port) spin_unlock_irq(&pdrvdata_lock); port->guest_connected = true; + /* + * Start using the new console output if this is the first + * console to come up. + */ + if (early_put_chars) + early_put_chars = NULL; + /* Notify host of port being opened */ send_control_msg(port, VIRTIO_CONSOLE_PORT_OPEN, 1); @@ -859,6 +929,8 @@ static ssize_t debugfs_read(struct file *filp, char __user *ubuf, out_offset += snprintf(buf + out_offset, out_count - out_offset, "host_connected: %d\n", port->host_connected); out_offset += snprintf(buf + out_offset, out_count - out_offset, + "outvq_full: %d\n", port->outvq_full); + out_offset += snprintf(buf + out_offset, out_count - out_offset, "is_console: %s\n", is_console_port(port) ? "yes" : "no"); out_offset += snprintf(buf + out_offset, out_count - out_offset, @@ -875,6 +947,153 @@ static const struct file_operations port_debugfs_ops = { .read = debugfs_read, }; +static void set_console_size(struct port *port, u16 rows, u16 cols) +{ + if (!port || !is_console_port(port)) + return; + + port->cons.ws.ws_row = rows; + port->cons.ws.ws_col = cols; +} + +static unsigned int fill_queue(struct virtqueue *vq, spinlock_t *lock) +{ + struct port_buffer *buf; + unsigned int nr_added_bufs; + int ret; + + nr_added_bufs = 0; + do { + buf = alloc_buf(PAGE_SIZE); + if (!buf) + break; + + spin_lock_irq(lock); + ret = add_inbuf(vq, buf); + if (ret < 0) { + spin_unlock_irq(lock); + free_buf(buf); + break; + } + nr_added_bufs++; + spin_unlock_irq(lock); + } while (ret > 0); + + return nr_added_bufs; +} + +static int add_port(struct ports_device *portdev, u32 id) +{ + char debugfs_name[16]; + struct port *port; + struct port_buffer *buf; + dev_t devt; + unsigned int nr_added_bufs; + int err; + + port = kmalloc(sizeof(*port), GFP_KERNEL); + if (!port) { + err = -ENOMEM; + goto fail; + } + + port->portdev = portdev; + port->id = id; + + port->name = NULL; + port->inbuf = NULL; + port->cons.hvc = NULL; + + port->cons.ws.ws_row = port->cons.ws.ws_col = 0; + + port->host_connected = port->guest_connected = false; + + port->outvq_full = false; + + port->in_vq = portdev->in_vqs[port->id]; + port->out_vq = portdev->out_vqs[port->id]; + + cdev_init(&port->cdev, &port_fops); + + devt = MKDEV(portdev->chr_major, id); + err = cdev_add(&port->cdev, devt, 1); + if (err < 0) { + dev_err(&port->portdev->vdev->dev, + "Error %d adding cdev for port %u\n", err, id); + goto free_port; + } + port->dev = device_create(pdrvdata.class, &port->portdev->vdev->dev, + devt, port, "vport%up%u", + port->portdev->drv_index, id); + if (IS_ERR(port->dev)) { + err = PTR_ERR(port->dev); + dev_err(&port->portdev->vdev->dev, + "Error %d creating device for port %u\n", + err, id); + goto free_cdev; + } + + spin_lock_init(&port->inbuf_lock); + spin_lock_init(&port->outvq_lock); + init_waitqueue_head(&port->waitqueue); + + /* Fill the in_vq with buffers so the host can send us data. */ + nr_added_bufs = fill_queue(port->in_vq, &port->inbuf_lock); + if (!nr_added_bufs) { + dev_err(port->dev, "Error allocating inbufs\n"); + err = -ENOMEM; + goto free_device; + } + + /* + * If we're not using multiport support, this has to be a console port + */ + if (!use_multiport(port->portdev)) { + err = init_port_console(port); + if (err) + goto free_inbufs; + } + + spin_lock_irq(&portdev->ports_lock); + list_add_tail(&port->list, &port->portdev->ports); + spin_unlock_irq(&portdev->ports_lock); + + /* + * Tell the Host we're set so that it can send us various + * configuration parameters for this port (eg, port name, + * caching, whether this is a console port, etc.) + */ + send_control_msg(port, VIRTIO_CONSOLE_PORT_READY, 1); + + if (pdrvdata.debugfs_dir) { + /* + * Finally, create the debugfs file that we can use to + * inspect a port's state at any time + */ + sprintf(debugfs_name, "vport%up%u", + port->portdev->drv_index, id); + port->debugfs_file = debugfs_create_file(debugfs_name, 0444, + pdrvdata.debugfs_dir, + port, + &port_debugfs_ops); + } + return 0; + +free_inbufs: + while ((buf = virtqueue_detach_unused_buf(port->in_vq))) + free_buf(buf); +free_device: + device_destroy(pdrvdata.class, port->dev->devt); +free_cdev: + cdev_del(&port->cdev); +free_port: + kfree(port); +fail: + /* The host might want to notify management sw about port add failure */ + __send_control_msg(portdev, id, VIRTIO_CONSOLE_PORT_READY, 0); + return err; +} + /* Remove all port-specific data. */ static int remove_port(struct port *port) { @@ -888,7 +1107,18 @@ static int remove_port(struct port *port) spin_lock_irq(&pdrvdata_lock); list_del(&port->cons.list); spin_unlock_irq(&pdrvdata_lock); +#if 0 + /* + * hvc_remove() not called as removing one hvc port + * results in other hvc ports getting frozen. + * + * Once this is resolved in hvc, this functionality + * will be enabled. Till that is done, the -EPIPE + * return from get_chars() above will help + * hvc_console.c to clean up on ports we remove here. + */ hvc_remove(port->cons.hvc); +#endif } if (port->guest_connected) send_control_msg(port, VIRTIO_CONSOLE_PORT_OPEN, 0); @@ -900,8 +1130,10 @@ static int remove_port(struct port *port) /* Remove unused data this port might have received. */ discard_port_data(port); + reclaim_consumed_buffers(port); + /* Remove buffers we queued up for the Host to send us data in. */ - while ((buf = port->in_vq->vq_ops->detach_unused_buf(port->in_vq))) + while ((buf = virtqueue_detach_unused_buf(port->in_vq))) free_buf(buf); kfree(port->name); @@ -924,7 +1156,7 @@ static void handle_control_message(struct ports_device *portdev, cpkt = (struct virtio_console_control *)(buf->buf + buf->offset); port = find_port_by_id(portdev, cpkt->id); - if (!port) { + if (!port && cpkt->event != VIRTIO_CONSOLE_PORT_ADD) { /* No valid header at start of buffer. Drop it. */ dev_dbg(&portdev->vdev->dev, "Invalid index %u in control packet\n", cpkt->id); @@ -932,6 +1164,24 @@ static void handle_control_message(struct ports_device *portdev, } switch (cpkt->event) { + case VIRTIO_CONSOLE_PORT_ADD: + if (port) { + dev_dbg(&portdev->vdev->dev, + "Port %u already added\n", port->id); + send_control_msg(port, VIRTIO_CONSOLE_PORT_READY, 1); + break; + } + if (cpkt->id >= portdev->config.max_nr_ports) { + dev_warn(&portdev->vdev->dev, + "Request for adding port with out-of-bound id %u, max. supported id: %u\n", + cpkt->id, portdev->config.max_nr_ports - 1); + break; + } + add_port(portdev, cpkt->id); + break; + case VIRTIO_CONSOLE_PORT_REMOVE: + remove_port(port); + break; case VIRTIO_CONSOLE_CONSOLE_PORT: if (!cpkt->value) break; @@ -944,15 +1194,34 @@ static void handle_control_message(struct ports_device *portdev, * have to notify the host first. */ break; - case VIRTIO_CONSOLE_RESIZE: + case VIRTIO_CONSOLE_RESIZE: { + struct { + __u16 rows; + __u16 cols; + } size; + if (!is_console_port(port)) break; + + memcpy(&size, buf->buf + buf->offset + sizeof(*cpkt), + sizeof(size)); + set_console_size(port, size.rows, size.cols); + port->cons.hvc->irq_requested = 1; resize_console(port); break; + } case VIRTIO_CONSOLE_PORT_OPEN: port->host_connected = cpkt->value; wake_up_interruptible(&port->waitqueue); + /* + * If the host port got closed and the host had any + * unconsumed buffers, we'll be able to reclaim them + * now. + */ + spin_lock_irq(&port->outvq_lock); + reclaim_consumed_buffers(port); + spin_unlock_irq(&port->outvq_lock); break; case VIRTIO_CONSOLE_PORT_NAME: /* @@ -990,32 +1259,6 @@ static void handle_control_message(struct ports_device *portdev, kobject_uevent(&port->dev->kobj, KOBJ_CHANGE); } break; - case VIRTIO_CONSOLE_PORT_REMOVE: - /* - * Hot unplug the port. We don't decrement nr_ports - * since we don't want to deal with extra complexities - * of using the lowest-available port id: We can just - * pick up the nr_ports number as the id and not have - * userspace send it to us. This helps us in two - * ways: - * - * - We don't need to have a 'port_id' field in the - * config space when a port is hot-added. This is a - * good thing as we might queue up multiple hotplug - * requests issued in our workqueue. - * - * - Another way to deal with this would have been to - * use a bitmap of the active ports and select the - * lowest non-active port from that map. That - * bloats the already tight config space and we - * would end up artificially limiting the - * max. number of ports to sizeof(bitmap). Right - * now we can support 2^32 ports (as the port id is - * stored in a u32 type). - * - */ - remove_port(port); - break; } } @@ -1030,7 +1273,7 @@ static void control_work_handler(struct work_struct *work) vq = portdev->c_ivq; spin_lock(&portdev->cvq_lock); - while ((buf = vq->vq_ops->get_buf(vq, &len))) { + while ((buf = virtqueue_get_buf(vq, &len))) { spin_unlock(&portdev->cvq_lock); buf->len = len; @@ -1092,204 +1335,29 @@ static void config_intr(struct virtio_device *vdev) struct ports_device *portdev; portdev = vdev->priv; - if (use_multiport(portdev)) { - /* Handle port hot-add */ - schedule_work(&portdev->config_work); - } - /* - * We'll use this way of resizing only for legacy support. - * For newer userspace (VIRTIO_CONSOLE_F_MULTPORT+), use - * control messages to indicate console size changes so that - * it can be done per-port - */ - resize_console(find_port_by_id(portdev, 0)); -} - -static unsigned int fill_queue(struct virtqueue *vq, spinlock_t *lock) -{ - struct port_buffer *buf; - unsigned int nr_added_bufs; - int ret; - - nr_added_bufs = 0; - do { - buf = alloc_buf(PAGE_SIZE); - if (!buf) - break; - - spin_lock_irq(lock); - ret = add_inbuf(vq, buf); - if (ret < 0) { - spin_unlock_irq(lock); - free_buf(buf); - break; - } - nr_added_bufs++; - spin_unlock_irq(lock); - } while (ret > 0); - - return nr_added_bufs; -} - -static int add_port(struct ports_device *portdev, u32 id) -{ - char debugfs_name[16]; - struct port *port; - struct port_buffer *buf; - dev_t devt; - unsigned int nr_added_bufs; - int err; - - port = kmalloc(sizeof(*port), GFP_KERNEL); - if (!port) { - err = -ENOMEM; - goto fail; - } - - port->portdev = portdev; - port->id = id; - - port->name = NULL; - port->inbuf = NULL; - port->cons.hvc = NULL; - - port->host_connected = port->guest_connected = false; - - port->in_vq = portdev->in_vqs[port->id]; - port->out_vq = portdev->out_vqs[port->id]; - - cdev_init(&port->cdev, &port_fops); - - devt = MKDEV(portdev->chr_major, id); - err = cdev_add(&port->cdev, devt, 1); - if (err < 0) { - dev_err(&port->portdev->vdev->dev, - "Error %d adding cdev for port %u\n", err, id); - goto free_port; - } - port->dev = device_create(pdrvdata.class, &port->portdev->vdev->dev, - devt, port, "vport%up%u", - port->portdev->drv_index, id); - if (IS_ERR(port->dev)) { - err = PTR_ERR(port->dev); - dev_err(&port->portdev->vdev->dev, - "Error %d creating device for port %u\n", - err, id); - goto free_cdev; - } - - spin_lock_init(&port->inbuf_lock); - init_waitqueue_head(&port->waitqueue); - - /* Fill the in_vq with buffers so the host can send us data. */ - nr_added_bufs = fill_queue(port->in_vq, &port->inbuf_lock); - if (!nr_added_bufs) { - dev_err(port->dev, "Error allocating inbufs\n"); - err = -ENOMEM; - goto free_device; - } - - /* - * If we're not using multiport support, this has to be a console port - */ - if (!use_multiport(port->portdev)) { - err = init_port_console(port); - if (err) - goto free_inbufs; - } - - spin_lock_irq(&portdev->ports_lock); - list_add_tail(&port->list, &port->portdev->ports); - spin_unlock_irq(&portdev->ports_lock); - - /* - * Tell the Host we're set so that it can send us various - * configuration parameters for this port (eg, port name, - * caching, whether this is a console port, etc.) - */ - send_control_msg(port, VIRTIO_CONSOLE_PORT_READY, 1); - - if (pdrvdata.debugfs_dir) { - /* - * Finally, create the debugfs file that we can use to - * inspect a port's state at any time - */ - sprintf(debugfs_name, "vport%up%u", - port->portdev->drv_index, id); - port->debugfs_file = debugfs_create_file(debugfs_name, 0444, - pdrvdata.debugfs_dir, - port, - &port_debugfs_ops); - } - return 0; - -free_inbufs: - while ((buf = port->in_vq->vq_ops->detach_unused_buf(port->in_vq))) - free_buf(buf); -free_device: - device_destroy(pdrvdata.class, port->dev->devt); -free_cdev: - cdev_del(&port->cdev); -free_port: - kfree(port); -fail: - return err; -} -/* - * The workhandler for config-space updates. - * - * This is called when ports are hot-added. - */ -static void config_work_handler(struct work_struct *work) -{ - struct virtio_console_multiport_conf virtconconf; - struct ports_device *portdev; - struct virtio_device *vdev; - int err; + if (!use_multiport(portdev)) { + struct port *port; + u16 rows, cols; - portdev = container_of(work, struct ports_device, config_work); + vdev->config->get(vdev, + offsetof(struct virtio_console_config, cols), + &cols, sizeof(u16)); + vdev->config->get(vdev, + offsetof(struct virtio_console_config, rows), + &rows, sizeof(u16)); - vdev = portdev->vdev; - vdev->config->get(vdev, - offsetof(struct virtio_console_multiport_conf, - nr_ports), - &virtconconf.nr_ports, - sizeof(virtconconf.nr_ports)); + port = find_port_by_id(portdev, 0); + set_console_size(port, rows, cols); - if (portdev->config.nr_ports == virtconconf.nr_ports) { /* - * Port 0 got hot-added. Since we already did all the - * other initialisation for it, just tell the Host - * that the port is ready if we find the port. In - * case the port was hot-removed earlier, we call - * add_port to add the port. + * We'll use this way of resizing only for legacy + * support. For newer userspace + * (VIRTIO_CONSOLE_F_MULTPORT+), use control messages + * to indicate console size changes so that it can be + * done per-port. */ - struct port *port; - - port = find_port_by_id(portdev, 0); - if (!port) - add_port(portdev, 0); - else - send_control_msg(port, VIRTIO_CONSOLE_PORT_READY, 1); - return; - } - if (virtconconf.nr_ports > portdev->config.max_nr_ports) { - dev_warn(&vdev->dev, - "More ports specified (%u) than allowed (%u)", - portdev->config.nr_ports + 1, - portdev->config.max_nr_ports); - return; - } - if (virtconconf.nr_ports < portdev->config.nr_ports) - return; - - /* Hot-add ports */ - while (virtconconf.nr_ports - portdev->config.nr_ports) { - err = add_port(portdev, portdev->config.nr_ports); - if (err) - break; - portdev->config.nr_ports++; + resize_console(port); } } @@ -1414,7 +1482,6 @@ static const struct file_operations portdev_fops = { static int __devinit virtcons_probe(struct virtio_device *vdev) { struct ports_device *portdev; - u32 i; int err; bool multiport; @@ -1443,37 +1510,19 @@ static int __devinit virtcons_probe(struct virtio_device *vdev) } multiport = false; - portdev->config.nr_ports = 1; portdev->config.max_nr_ports = 1; -#if 0 /* Multiport is not quite ready yet --RR */ if (virtio_has_feature(vdev, VIRTIO_CONSOLE_F_MULTIPORT)) { multiport = true; vdev->features[0] |= 1 << VIRTIO_CONSOLE_F_MULTIPORT; - vdev->config->get(vdev, - offsetof(struct virtio_console_multiport_conf, - nr_ports), - &portdev->config.nr_ports, - sizeof(portdev->config.nr_ports)); - vdev->config->get(vdev, - offsetof(struct virtio_console_multiport_conf, - max_nr_ports), + vdev->config->get(vdev, offsetof(struct virtio_console_config, + max_nr_ports), &portdev->config.max_nr_ports, sizeof(portdev->config.max_nr_ports)); - if (portdev->config.nr_ports > portdev->config.max_nr_ports) { - dev_warn(&vdev->dev, - "More ports (%u) specified than allowed (%u). Will init %u ports.", - portdev->config.nr_ports, - portdev->config.max_nr_ports, - portdev->config.max_nr_ports); - - portdev->config.nr_ports = portdev->config.max_nr_ports; - } } /* Let the Host know we support multiple ports.*/ vdev->config->finalize_features(vdev); -#endif err = init_vqs(portdev); if (err < 0) { @@ -1489,7 +1538,6 @@ static int __devinit virtcons_probe(struct virtio_device *vdev) spin_lock_init(&portdev->cvq_lock); INIT_WORK(&portdev->control_work, &control_work_handler); - INIT_WORK(&portdev->config_work, &config_work_handler); nr_added_bufs = fill_queue(portdev->c_ivq, &portdev->cvq_lock); if (!nr_added_bufs) { @@ -1498,16 +1546,22 @@ static int __devinit virtcons_probe(struct virtio_device *vdev) err = -ENOMEM; goto free_vqs; } + } else { + /* + * For backward compatibility: Create a console port + * if we're running on older host. + */ + add_port(portdev, 0); } - for (i = 0; i < portdev->config.nr_ports; i++) - add_port(portdev, i); - - /* Start using the new console output. */ - early_put_chars = NULL; + __send_control_msg(portdev, VIRTIO_CONSOLE_BAD_ID, + VIRTIO_CONSOLE_DEVICE_READY, 1); return 0; free_vqs: + /* The host might want to notify mgmt sw about device add failure */ + __send_control_msg(portdev, VIRTIO_CONSOLE_BAD_ID, + VIRTIO_CONSOLE_DEVICE_READY, 0); vdev->config->del_vqs(vdev); kfree(portdev->in_vqs); kfree(portdev->out_vqs); @@ -1529,17 +1583,16 @@ static void virtcons_remove(struct virtio_device *vdev) portdev = vdev->priv; cancel_work_sync(&portdev->control_work); - cancel_work_sync(&portdev->config_work); list_for_each_entry_safe(port, port2, &portdev->ports, list) remove_port(port); unregister_chrdev(portdev->chr_major, "virtio-portsdev"); - while ((buf = portdev->c_ivq->vq_ops->get_buf(portdev->c_ivq, &len))) + while ((buf = virtqueue_get_buf(portdev->c_ivq, &len))) free_buf(buf); - while ((buf = portdev->c_ivq->vq_ops->detach_unused_buf(portdev->c_ivq))) + while ((buf = virtqueue_detach_unused_buf(portdev->c_ivq))) free_buf(buf); vdev->config->del_vqs(vdev); @@ -1556,6 +1609,7 @@ static struct virtio_device_id id_table[] = { static unsigned int features[] = { VIRTIO_CONSOLE_F_SIZE, + VIRTIO_CONSOLE_F_MULTIPORT, }; static struct virtio_driver virtio_console = { diff --git a/drivers/char/vt.c b/drivers/char/vt.c index bd1d1164fec5..7cdb6ee569cd 100644 --- a/drivers/char/vt.c +++ b/drivers/char/vt.c @@ -3967,13 +3967,9 @@ static int con_font_set(struct vc_data *vc, struct console_font_op *op) font.charcount = op->charcount; font.height = op->height; font.width = op->width; - font.data = kmalloc(size, GFP_KERNEL); - if (!font.data) - return -ENOMEM; - if (copy_from_user(font.data, op->data, size)) { - kfree(font.data); - return -EFAULT; - } + font.data = memdup_user(op->data, size); + if (IS_ERR(font.data)) + return PTR_ERR(font.data); acquire_console_sem(); if (vc->vc_sw->con_font_set) rc = vc->vc_sw->con_font_set(vc, &font, op->flags); diff --git a/drivers/char/xilinx_hwicap/xilinx_hwicap.c b/drivers/char/xilinx_hwicap/xilinx_hwicap.c index 7261b8d9087c..ed8a9cec2a05 100644 --- a/drivers/char/xilinx_hwicap/xilinx_hwicap.c +++ b/drivers/char/xilinx_hwicap/xilinx_hwicap.c @@ -772,18 +772,18 @@ hwicap_of_probe(struct of_device *op, const struct of_device_id *match) dev_dbg(&op->dev, "hwicap_of_probe(%p, %p)\n", op, match); - rc = of_address_to_resource(op->node, 0, &res); + rc = of_address_to_resource(op->dev.of_node, 0, &res); if (rc) { dev_err(&op->dev, "invalid address\n"); return rc; } - id = of_get_property(op->node, "port-number", NULL); + id = of_get_property(op->dev.of_node, "port-number", NULL); /* It's most likely that we're using V4, if the family is not specified */ regs = &v4_config_registers; - family = of_get_property(op->node, "xlnx,family", NULL); + family = of_get_property(op->dev.of_node, "xlnx,family", NULL); if (family) { if (!strcmp(family, "virtex2p")) { @@ -812,13 +812,12 @@ static const struct of_device_id __devinitconst hwicap_of_match[] = { MODULE_DEVICE_TABLE(of, hwicap_of_match); static struct of_platform_driver hwicap_of_driver = { - .owner = THIS_MODULE, - .name = DRIVER_NAME, - .match_table = hwicap_of_match, .probe = hwicap_of_probe, .remove = __devexit_p(hwicap_of_remove), .driver = { .name = DRIVER_NAME, + .owner = THIS_MODULE, + .of_match_table = hwicap_of_match, }, }; |