diff options
author | James Morris <jmorris@namei.org> | 2009-09-30 07:47:33 +1000 |
---|---|---|
committer | James Morris <jmorris@namei.org> | 2009-09-30 07:47:33 +1000 |
commit | 1669b049db50fc7f1d4e694fb115a0f408c63fce (patch) | |
tree | 9b3b90b5cbff9b8f30ecf0b2a44896ce8bef0c20 /drivers/char | |
parent | 7f366784f5c2b8fc0658b5b374f4c63ee42c789f (diff) | |
parent | 17d857be649a21ca90008c6dc425d849fa83db5c (diff) | |
download | op-kernel-dev-1669b049db50fc7f1d4e694fb115a0f408c63fce.zip op-kernel-dev-1669b049db50fc7f1d4e694fb115a0f408c63fce.tar.gz |
Merge branch 'master' into next
Diffstat (limited to 'drivers/char')
66 files changed, 2316 insertions, 2331 deletions
diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index 6a06913..08a6f50 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig @@ -1087,6 +1087,14 @@ config MMTIMER The mmtimer device allows direct userspace access to the Altix system timer. +config UV_MMTIMER + tristate "UV_MMTIMER Memory mapped RTC for SGI UV" + depends on X86_UV + default m + help + The uv_mmtimer device allows direct userspace access to the + UV system timer. + source "drivers/char/tpm/Kconfig" config TELCLOCK diff --git a/drivers/char/Makefile b/drivers/char/Makefile index 66f779a..19a79dd 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -58,6 +58,7 @@ obj-$(CONFIG_RAW_DRIVER) += raw.o obj-$(CONFIG_SGI_SNSC) += snsc.o snsc_event.o obj-$(CONFIG_MSPEC) += mspec.o obj-$(CONFIG_MMTIMER) += mmtimer.o +obj-$(CONFIG_UV_MMTIMER) += uv_mmtimer.o obj-$(CONFIG_VIOTAPE) += viotape.o obj-$(CONFIG_HVCS) += hvcs.o obj-$(CONFIG_IBM_BSR) += bsr.o diff --git a/drivers/char/agp/agp.h b/drivers/char/agp/agp.h index 178e2e9..870f12c 100644 --- a/drivers/char/agp/agp.h +++ b/drivers/char/agp/agp.h @@ -107,7 +107,7 @@ struct agp_bridge_driver { void (*agp_enable)(struct agp_bridge_data *, u32); void (*cleanup)(void); void (*tlb_flush)(struct agp_memory *); - unsigned long (*mask_memory)(struct agp_bridge_data *, struct page *, int); + unsigned long (*mask_memory)(struct agp_bridge_data *, dma_addr_t, int); void (*cache_flush)(void); int (*create_gatt_table)(struct agp_bridge_data *); int (*free_gatt_table)(struct agp_bridge_data *); @@ -121,12 +121,17 @@ struct agp_bridge_driver { void (*agp_destroy_pages)(struct agp_memory *); int (*agp_type_to_mask_type) (struct agp_bridge_data *, int); void (*chipset_flush)(struct agp_bridge_data *); + + int (*agp_map_page)(struct page *page, dma_addr_t *ret); + void (*agp_unmap_page)(struct page *page, dma_addr_t dma); + int (*agp_map_memory)(struct agp_memory *mem); + void (*agp_unmap_memory)(struct agp_memory *mem); }; struct agp_bridge_data { const struct agp_version *version; const struct agp_bridge_driver *driver; - struct vm_operations_struct *vm_ops; + const struct vm_operations_struct *vm_ops; void *previous_size; void *current_size; void *dev_private_data; @@ -134,7 +139,8 @@ struct agp_bridge_data { u32 __iomem *gatt_table; u32 *gatt_table_real; unsigned long scratch_page; - unsigned long scratch_page_real; + struct page *scratch_page_page; + dma_addr_t scratch_page_dma; unsigned long gart_bus_addr; unsigned long gatt_bus_addr; u32 mode; @@ -291,7 +297,7 @@ int agp_3_5_enable(struct agp_bridge_data *bridge); void global_cache_flush(void); void get_agp_version(struct agp_bridge_data *bridge); unsigned long agp_generic_mask_memory(struct agp_bridge_data *bridge, - struct page *page, int type); + dma_addr_t phys, int type); int agp_generic_type_to_mask_type(struct agp_bridge_data *bridge, int type); struct agp_bridge_data *agp_generic_find_bridge(struct pci_dev *pdev); @@ -312,9 +318,6 @@ void agp3_generic_cleanup(void); #define AGP_GENERIC_SIZES_ENTRIES 11 extern const struct aper_size_info_16 agp3_generic_sizes[]; -#define virt_to_gart(x) (phys_to_gart(virt_to_phys(x))) -#define gart_to_virt(x) (phys_to_virt(gart_to_phys(x))) - extern int agp_off; extern int agp_try_unsupported_boot; diff --git a/drivers/char/agp/ali-agp.c b/drivers/char/agp/ali-agp.c index 201ef3f..d2ce68f 100644 --- a/drivers/char/agp/ali-agp.c +++ b/drivers/char/agp/ali-agp.c @@ -152,7 +152,7 @@ static struct page *m1541_alloc_page(struct agp_bridge_data *bridge) pci_read_config_dword(agp_bridge->dev, ALI_CACHE_FLUSH_CTRL, &temp); pci_write_config_dword(agp_bridge->dev, ALI_CACHE_FLUSH_CTRL, (((temp & ALI_CACHE_FLUSH_ADDR_MASK) | - phys_to_gart(page_to_phys(page))) | ALI_CACHE_FLUSH_EN )); + page_to_phys(page)) | ALI_CACHE_FLUSH_EN )); return page; } @@ -180,7 +180,7 @@ static void m1541_destroy_page(struct page *page, int flags) pci_read_config_dword(agp_bridge->dev, ALI_CACHE_FLUSH_CTRL, &temp); pci_write_config_dword(agp_bridge->dev, ALI_CACHE_FLUSH_CTRL, (((temp & ALI_CACHE_FLUSH_ADDR_MASK) | - phys_to_gart(page_to_phys(page))) | ALI_CACHE_FLUSH_EN)); + page_to_phys(page)) | ALI_CACHE_FLUSH_EN)); } agp_generic_destroy_page(page, flags); } diff --git a/drivers/char/agp/alpha-agp.c b/drivers/char/agp/alpha-agp.c index 5ea4da8..dd84af4 100644 --- a/drivers/char/agp/alpha-agp.c +++ b/drivers/char/agp/alpha-agp.c @@ -40,7 +40,7 @@ static struct aper_size_info_fixed alpha_core_agp_sizes[] = { 0, 0, 0 }, /* filled in by alpha_core_agp_setup */ }; -struct vm_operations_struct alpha_core_agp_vm_ops = { +static const struct vm_operations_struct alpha_core_agp_vm_ops = { .fault = alpha_core_agp_vm_fault, }; diff --git a/drivers/char/agp/amd-k7-agp.c b/drivers/char/agp/amd-k7-agp.c index ba9bde7..73dbf40 100644 --- a/drivers/char/agp/amd-k7-agp.c +++ b/drivers/char/agp/amd-k7-agp.c @@ -44,7 +44,7 @@ static int amd_create_page_map(struct amd_page_map *page_map) #ifndef CONFIG_X86 SetPageReserved(virt_to_page(page_map->real)); global_cache_flush(); - page_map->remapped = ioremap_nocache(virt_to_gart(page_map->real), + page_map->remapped = ioremap_nocache(virt_to_phys(page_map->real), PAGE_SIZE); if (page_map->remapped == NULL) { ClearPageReserved(virt_to_page(page_map->real)); @@ -160,7 +160,7 @@ static int amd_create_gatt_table(struct agp_bridge_data *bridge) agp_bridge->gatt_table_real = (u32 *)page_dir.real; agp_bridge->gatt_table = (u32 __iomem *)page_dir.remapped; - agp_bridge->gatt_bus_addr = virt_to_gart(page_dir.real); + agp_bridge->gatt_bus_addr = virt_to_phys(page_dir.real); /* Get the address for the gart region. * This is a bus address even on the alpha, b/c its @@ -173,7 +173,7 @@ static int amd_create_gatt_table(struct agp_bridge_data *bridge) /* Calculate the agp offset */ for (i = 0; i < value->num_entries / 1024; i++, addr += 0x00400000) { - writel(virt_to_gart(amd_irongate_private.gatt_pages[i]->real) | 1, + writel(virt_to_phys(amd_irongate_private.gatt_pages[i]->real) | 1, page_dir.remapped+GET_PAGE_DIR_OFF(addr)); readl(page_dir.remapped+GET_PAGE_DIR_OFF(addr)); /* PCI Posting. */ } @@ -325,7 +325,9 @@ static int amd_insert_memory(struct agp_memory *mem, off_t pg_start, int type) addr = (j * PAGE_SIZE) + agp_bridge->gart_bus_addr; cur_gatt = GET_GATT(addr); writel(agp_generic_mask_memory(agp_bridge, - mem->pages[i], mem->type), cur_gatt+GET_GATT_OFF(addr)); + page_to_phys(mem->pages[i]), + mem->type), + cur_gatt+GET_GATT_OFF(addr)); readl(cur_gatt+GET_GATT_OFF(addr)); /* PCI Posting. */ } amd_irongate_tlbflush(mem); diff --git a/drivers/char/agp/amd64-agp.c b/drivers/char/agp/amd64-agp.c index 3bf5dda..2fb2e6c 100644 --- a/drivers/char/agp/amd64-agp.c +++ b/drivers/char/agp/amd64-agp.c @@ -79,7 +79,8 @@ static int amd64_insert_memory(struct agp_memory *mem, off_t pg_start, int type) for (i = 0, j = pg_start; i < mem->page_count; i++, j++) { tmp = agp_bridge->driver->mask_memory(agp_bridge, - mem->pages[i], mask_type); + page_to_phys(mem->pages[i]), + mask_type); BUG_ON(tmp & 0xffffff0000000ffcULL); pte = (tmp & 0x000000ff00000000ULL) >> 28; @@ -177,7 +178,7 @@ static const struct aper_size_info_32 amd_8151_sizes[7] = static int amd_8151_configure(void) { - unsigned long gatt_bus = virt_to_gart(agp_bridge->gatt_table_real); + unsigned long gatt_bus = virt_to_phys(agp_bridge->gatt_table_real); int i; /* Configure AGP regs in each x86-64 host bridge. */ @@ -557,7 +558,7 @@ static void __devexit agp_amd64_remove(struct pci_dev *pdev) { struct agp_bridge_data *bridge = pci_get_drvdata(pdev); - release_mem_region(virt_to_gart(bridge->gatt_table_real), + release_mem_region(virt_to_phys(bridge->gatt_table_real), amd64_aperture_sizes[bridge->aperture_size_idx].size); agp_remove_bridge(bridge); agp_put_bridge(bridge); diff --git a/drivers/char/agp/ati-agp.c b/drivers/char/agp/ati-agp.c index 33656e1..3b2ecbe 100644 --- a/drivers/char/agp/ati-agp.c +++ b/drivers/char/agp/ati-agp.c @@ -302,7 +302,8 @@ static int ati_insert_memory(struct agp_memory * mem, addr = (j * PAGE_SIZE) + agp_bridge->gart_bus_addr; cur_gatt = GET_GATT(addr); writel(agp_bridge->driver->mask_memory(agp_bridge, - mem->pages[i], mem->type), + page_to_phys(mem->pages[i]), + mem->type), cur_gatt+GET_GATT_OFF(addr)); } readl(GET_GATT(agp_bridge->gart_bus_addr)); /* PCI posting */ @@ -359,7 +360,7 @@ static int ati_create_gatt_table(struct agp_bridge_data *bridge) agp_bridge->gatt_table_real = (u32 *)page_dir.real; agp_bridge->gatt_table = (u32 __iomem *) page_dir.remapped; - agp_bridge->gatt_bus_addr = virt_to_gart(page_dir.real); + agp_bridge->gatt_bus_addr = virt_to_phys(page_dir.real); /* Write out the size register */ current_size = A_SIZE_LVL2(agp_bridge->current_size); @@ -389,7 +390,7 @@ static int ati_create_gatt_table(struct agp_bridge_data *bridge) /* Calculate the agp offset */ for (i = 0; i < value->num_entries / 1024; i++, addr += 0x00400000) { - writel(virt_to_gart(ati_generic_private.gatt_pages[i]->real) | 1, + writel(virt_to_phys(ati_generic_private.gatt_pages[i]->real) | 1, page_dir.remapped+GET_PAGE_DIR_OFF(addr)); readl(page_dir.remapped+GET_PAGE_DIR_OFF(addr)); /* PCI Posting. */ } diff --git a/drivers/char/agp/backend.c b/drivers/char/agp/backend.c index cfa5a64..a56ca08 100644 --- a/drivers/char/agp/backend.c +++ b/drivers/char/agp/backend.c @@ -114,9 +114,9 @@ static int agp_find_max(void) long memory, index, result; #if PAGE_SHIFT < 20 - memory = num_physpages >> (20 - PAGE_SHIFT); + memory = totalram_pages >> (20 - PAGE_SHIFT); #else - memory = num_physpages << (PAGE_SHIFT - 20); + memory = totalram_pages << (PAGE_SHIFT - 20); #endif index = 1; @@ -149,9 +149,21 @@ static int agp_backend_initialize(struct agp_bridge_data *bridge) return -ENOMEM; } - bridge->scratch_page_real = phys_to_gart(page_to_phys(page)); - bridge->scratch_page = - bridge->driver->mask_memory(bridge, page, 0); + bridge->scratch_page_page = page; + if (bridge->driver->agp_map_page) { + if (bridge->driver->agp_map_page(page, + &bridge->scratch_page_dma)) { + dev_err(&bridge->dev->dev, + "unable to dma-map scratch page\n"); + rc = -ENOMEM; + goto err_out_nounmap; + } + } else { + bridge->scratch_page_dma = page_to_phys(page); + } + + bridge->scratch_page = bridge->driver->mask_memory(bridge, + bridge->scratch_page_dma, 0); } size_value = bridge->driver->fetch_size(); @@ -191,8 +203,14 @@ static int agp_backend_initialize(struct agp_bridge_data *bridge) return 0; err_out: + if (bridge->driver->needs_scratch_page && + bridge->driver->agp_unmap_page) { + bridge->driver->agp_unmap_page(bridge->scratch_page_page, + bridge->scratch_page_dma); + } +err_out_nounmap: if (bridge->driver->needs_scratch_page) { - void *va = gart_to_virt(bridge->scratch_page_real); + void *va = page_address(bridge->scratch_page_page); bridge->driver->agp_destroy_page(va, AGP_PAGE_DESTROY_UNMAP); bridge->driver->agp_destroy_page(va, AGP_PAGE_DESTROY_FREE); @@ -219,7 +237,11 @@ static void agp_backend_cleanup(struct agp_bridge_data *bridge) if (bridge->driver->agp_destroy_page && bridge->driver->needs_scratch_page) { - void *va = gart_to_virt(bridge->scratch_page_real); + void *va = page_address(bridge->scratch_page_page); + + if (bridge->driver->agp_unmap_page) + bridge->driver->agp_unmap_page(bridge->scratch_page_page, + bridge->scratch_page_dma); bridge->driver->agp_destroy_page(va, AGP_PAGE_DESTROY_UNMAP); bridge->driver->agp_destroy_page(va, AGP_PAGE_DESTROY_FREE); diff --git a/drivers/char/agp/efficeon-agp.c b/drivers/char/agp/efficeon-agp.c index 35d50f2..793f39e 100644 --- a/drivers/char/agp/efficeon-agp.c +++ b/drivers/char/agp/efficeon-agp.c @@ -67,7 +67,7 @@ static const struct gatt_mask efficeon_generic_masks[] = /* This function does the same thing as mask_memory() for this chipset... */ static inline unsigned long efficeon_mask_memory(struct page *page) { - unsigned long addr = phys_to_gart(page_to_phys(page)); + unsigned long addr = page_to_phys(page); return addr | 0x00000001; } @@ -226,7 +226,7 @@ static int efficeon_create_gatt_table(struct agp_bridge_data *bridge) efficeon_private.l1_table[index] = page; - value = virt_to_gart((unsigned long *)page) | pati | present | index; + value = virt_to_phys((unsigned long *)page) | pati | present | index; pci_write_config_dword(agp_bridge->dev, EFFICEON_ATTPAGE, value); diff --git a/drivers/char/agp/generic.c b/drivers/char/agp/generic.c index 1e8b461..c505439 100644 --- a/drivers/char/agp/generic.c +++ b/drivers/char/agp/generic.c @@ -437,6 +437,12 @@ int agp_bind_memory(struct agp_memory *curr, off_t pg_start) curr->bridge->driver->cache_flush(); curr->is_flushed = true; } + + if (curr->bridge->driver->agp_map_memory) { + ret_val = curr->bridge->driver->agp_map_memory(curr); + if (ret_val) + return ret_val; + } ret_val = curr->bridge->driver->insert_memory(curr, pg_start, curr->type); if (ret_val != 0) @@ -478,6 +484,9 @@ int agp_unbind_memory(struct agp_memory *curr) if (ret_val != 0) return ret_val; + if (curr->bridge->driver->agp_unmap_memory) + curr->bridge->driver->agp_unmap_memory(curr); + curr->is_bound = false; curr->pg_start = 0; spin_lock(&curr->bridge->mapped_lock); @@ -979,7 +988,7 @@ int agp_generic_create_gatt_table(struct agp_bridge_data *bridge) set_memory_uc((unsigned long)table, 1 << page_order); bridge->gatt_table = (void *)table; #else - bridge->gatt_table = ioremap_nocache(virt_to_gart(table), + bridge->gatt_table = ioremap_nocache(virt_to_phys(table), (PAGE_SIZE * (1 << page_order))); bridge->driver->cache_flush(); #endif @@ -992,7 +1001,7 @@ int agp_generic_create_gatt_table(struct agp_bridge_data *bridge) return -ENOMEM; } - bridge->gatt_bus_addr = virt_to_gart(bridge->gatt_table_real); + bridge->gatt_bus_addr = virt_to_phys(bridge->gatt_table_real); /* AK: bogus, should encode addresses > 4GB */ for (i = 0; i < num_entries; i++) { @@ -1132,7 +1141,9 @@ int agp_generic_insert_memory(struct agp_memory * mem, off_t pg_start, int type) } for (i = 0, j = pg_start; i < mem->page_count; i++, j++) { - writel(bridge->driver->mask_memory(bridge, mem->pages[i], mask_type), + writel(bridge->driver->mask_memory(bridge, + page_to_phys(mem->pages[i]), + mask_type), bridge->gatt_table+j); } readl(bridge->gatt_table+j-1); /* PCI Posting. */ @@ -1347,9 +1358,8 @@ void global_cache_flush(void) EXPORT_SYMBOL(global_cache_flush); unsigned long agp_generic_mask_memory(struct agp_bridge_data *bridge, - struct page *page, int type) + dma_addr_t addr, int type) { - unsigned long addr = phys_to_gart(page_to_phys(page)); /* memory type is ignored in the generic routine */ if (bridge->driver->masks) return addr | bridge->driver->masks[0].mask; diff --git a/drivers/char/agp/hp-agp.c b/drivers/char/agp/hp-agp.c index 8f3d4c1..9047b27 100644 --- a/drivers/char/agp/hp-agp.c +++ b/drivers/char/agp/hp-agp.c @@ -107,7 +107,7 @@ static int __init hp_zx1_ioc_shared(void) hp->gart_size = HP_ZX1_GART_SIZE; hp->gatt_entries = hp->gart_size / hp->io_page_size; - hp->io_pdir = gart_to_virt(readq(hp->ioc_regs+HP_ZX1_PDIR_BASE)); + hp->io_pdir = phys_to_virt(readq(hp->ioc_regs+HP_ZX1_PDIR_BASE)); hp->gatt = &hp->io_pdir[HP_ZX1_IOVA_TO_PDIR(hp->gart_base)]; if (hp->gatt[0] != HP_ZX1_SBA_IOMMU_COOKIE) { @@ -246,7 +246,7 @@ hp_zx1_configure (void) agp_bridge->mode = readl(hp->lba_regs+hp->lba_cap_offset+PCI_AGP_STATUS); if (hp->io_pdir_owner) { - writel(virt_to_gart(hp->io_pdir), hp->ioc_regs+HP_ZX1_PDIR_BASE); + writel(virt_to_phys(hp->io_pdir), hp->ioc_regs+HP_ZX1_PDIR_BASE); readl(hp->ioc_regs+HP_ZX1_PDIR_BASE); writel(hp->io_tlb_ps, hp->ioc_regs+HP_ZX1_TCNFG); readl(hp->ioc_regs+HP_ZX1_TCNFG); @@ -394,10 +394,8 @@ hp_zx1_remove_memory (struct agp_memory *mem, off_t pg_start, int type) } static unsigned long -hp_zx1_mask_memory (struct agp_bridge_data *bridge, - struct page *page, int type) +hp_zx1_mask_memory (struct agp_bridge_data *bridge, dma_addr_t addr, int type) { - unsigned long addr = phys_to_gart(page_to_phys(page)); return HP_ZX1_PDIR_VALID_BIT | addr; } @@ -478,7 +476,6 @@ zx1_gart_probe (acpi_handle obj, u32 depth, void *context, void **ret) { acpi_handle handle, parent; acpi_status status; - struct acpi_buffer buffer; struct acpi_device_info *info; u64 lba_hpa, sba_hpa, length; int match; @@ -490,13 +487,11 @@ zx1_gart_probe (acpi_handle obj, u32 depth, void *context, void **ret) /* Look for an enclosing IOC scope and find its CSR space */ handle = obj; do { - buffer.length = ACPI_ALLOCATE_LOCAL_BUFFER; - status = acpi_get_object_info(handle, &buffer); + status = acpi_get_object_info(handle, &info); if (ACPI_SUCCESS(status)) { /* TBD check _CID also */ - info = buffer.pointer; - info->hardware_id.value[sizeof(info->hardware_id)-1] = '\0'; - match = (strcmp(info->hardware_id.value, "HWP0001") == 0); + info->hardware_id.string[sizeof(info->hardware_id.length)-1] = '\0'; + match = (strcmp(info->hardware_id.string, "HWP0001") == 0); kfree(info); if (match) { status = hp_acpi_csr_space(handle, &sba_hpa, &length); diff --git a/drivers/char/agp/i460-agp.c b/drivers/char/agp/i460-agp.c index 60cc35b..e763d33 100644 --- a/drivers/char/agp/i460-agp.c +++ b/drivers/char/agp/i460-agp.c @@ -61,7 +61,7 @@ #define WR_FLUSH_GATT(index) RD_GATT(index) static unsigned long i460_mask_memory (struct agp_bridge_data *bridge, - unsigned long addr, int type); + dma_addr_t addr, int type); static struct { void *gatt; /* ioremap'd GATT area */ @@ -325,7 +325,7 @@ static int i460_insert_memory_small_io_page (struct agp_memory *mem, io_page_size = 1UL << I460_IO_PAGE_SHIFT; for (i = 0, j = io_pg_start; i < mem->page_count; i++) { - paddr = phys_to_gart(page_to_phys(mem->pages[i])); + paddr = page_to_phys(mem->pages[i]); for (k = 0; k < I460_IOPAGES_PER_KPAGE; k++, j++, paddr += io_page_size) WR_GATT(j, i460_mask_memory(agp_bridge, paddr, mem->type)); } @@ -382,7 +382,7 @@ static int i460_alloc_large_page (struct lp_desc *lp) return -ENOMEM; } - lp->paddr = phys_to_gart(page_to_phys(lp->page)); + lp->paddr = page_to_phys(lp->page); lp->refcount = 0; atomic_add(I460_KPAGES_PER_IOPAGE, &agp_bridge->current_memory_agp); return 0; @@ -546,20 +546,13 @@ static void i460_destroy_page (struct page *page, int flags) #endif /* I460_LARGE_IO_PAGES */ static unsigned long i460_mask_memory (struct agp_bridge_data *bridge, - unsigned long addr, int type) + dma_addr_t addr, int type) { /* Make sure the returned address is a valid GATT entry */ return bridge->driver->masks[0].mask | (((addr & ~((1 << I460_IO_PAGE_SHIFT) - 1)) & 0xfffff000) >> 12); } -static unsigned long i460_page_mask_memory(struct agp_bridge_data *bridge, - struct page *page, int type) -{ - unsigned long addr = phys_to_gart(page_to_phys(page)); - return i460_mask_memory(bridge, addr, type); -} - const struct agp_bridge_driver intel_i460_driver = { .owner = THIS_MODULE, .aperture_sizes = i460_sizes, @@ -569,7 +562,7 @@ const struct agp_bridge_driver intel_i460_driver = { .fetch_size = i460_fetch_size, .cleanup = i460_cleanup, .tlb_flush = i460_tlb_flush, - .mask_memory = i460_page_mask_memory, + .mask_memory = i460_mask_memory, .masks = i460_masks, .agp_enable = agp_generic_enable, .cache_flush = global_cache_flush, diff --git a/drivers/char/agp/intel-agp.c b/drivers/char/agp/intel-agp.c index c585577..4068467 100644 --- a/drivers/char/agp/intel-agp.c +++ b/drivers/char/agp/intel-agp.c @@ -10,6 +10,16 @@ #include <linux/agp_backend.h> #include "agp.h" +/* + * 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 @@ -36,6 +46,8 @@ #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_IGD_E_HB 0x2E00 @@ -81,6 +93,7 @@ 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_IGDNG_D_HB || \ agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_IGDNG_M_HB || \ agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_IGDNG_MA_HB) @@ -172,6 +185,123 @@ static struct _intel_private { 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 seperate + * 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; + + 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; @@ -345,8 +475,7 @@ static int intel_i810_insert_entries(struct agp_memory *mem, off_t pg_start, global_cache_flush(); for (i = 0, j = pg_start; i < mem->page_count; i++, j++) { writel(agp_bridge->driver->mask_memory(agp_bridge, - mem->pages[i], - mask_type), + 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)); @@ -463,9 +592,8 @@ static void intel_i810_free_by_type(struct agp_memory *curr) } static unsigned long intel_i810_mask_memory(struct agp_bridge_data *bridge, - struct page *page, int type) + dma_addr_t addr, int type) { - unsigned long addr = phys_to_gart(page_to_phys(page)); /* Type checking must be done elsewhere */ return addr | bridge->driver->masks[type].mask; } @@ -679,23 +807,39 @@ static void intel_i830_setup_flush(void) if (!intel_private.i8xx_page) return; - /* make page uncached */ - map_page_into_agp(intel_private.i8xx_page); - intel_private.i8xx_flush_page = kmap(intel_private.i8xx_page); if (!intel_private.i8xx_flush_page) intel_i830_fini_flush(); } +static void +do_wbinvd(void *null) +{ + wbinvd(); +} + +/* 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; - int i; - for (i = 0; i < 256; i += 2) - *(pg + i) = i; + memset(pg, 0, 1024); - wmb(); + if (cpu_has_clflush) { + clflush_cache_range(pg, 1024); + } else { + if (on_each_cpu(do_wbinvd, NULL, 1) != 0) + printk(KERN_ERR "Timed out waiting for cache flush.\n"); + } } /* The intel i830 automatically initializes the agp aperture during POST. @@ -853,7 +997,7 @@ static int intel_i830_insert_entries(struct agp_memory *mem, off_t pg_start, for (i = 0, j = pg_start; i < mem->page_count; i++, j++) { writel(agp_bridge->driver->mask_memory(agp_bridge, - mem->pages[i], mask_type), + 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)); @@ -1017,6 +1161,12 @@ static int intel_i915_configure(void) intel_i9xx_setup_flush(); +#ifdef USE_PCI_DMA_API + 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"); +#endif + return 0; } @@ -1041,7 +1191,7 @@ static void intel_i915_chipset_flush(struct agp_bridge_data *bridge) static int intel_i915_insert_entries(struct agp_memory *mem, off_t pg_start, int type) { - int i, j, num_entries; + int num_entries; void *temp; int ret = -EINVAL; int mask_type; @@ -1065,7 +1215,7 @@ static int intel_i915_insert_entries(struct agp_memory *mem, off_t pg_start, if ((pg_start + mem->page_count) > num_entries) goto out_err; - /* The i915 can't check the GTT for entries since its read only, + /* The i915 can't check the GTT for entries since it's read only; * depend on the caller to make the correct offset decisions. */ @@ -1081,12 +1231,7 @@ static int intel_i915_insert_entries(struct agp_memory *mem, off_t pg_start, 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, - mem->pages[i], mask_type), intel_private.gtt+j); - } - - readl(intel_private.gtt+j-1); + intel_agp_insert_sg_entries(mem, pg_start, mask_type); agp_bridge->driver->tlb_flush(mem); out: @@ -1198,9 +1343,8 @@ static int intel_i915_create_gatt_table(struct agp_bridge_data *bridge) * this conditional. */ static unsigned long intel_i965_mask_memory(struct agp_bridge_data *bridge, - struct page *page, int type) + dma_addr_t addr, int type) { - dma_addr_t addr = phys_to_gart(page_to_phys(page)); /* Shift high bits down */ addr |= (addr >> 28) & 0xf0; @@ -1216,6 +1360,7 @@ static void intel_i965_get_gtt_range(int *gtt_offset, int *gtt_size) 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_IGDNG_D_HB: case PCI_DEVICE_ID_INTEL_IGDNG_M_HB: case PCI_DEVICE_ID_INTEL_IGDNG_MA_HB: @@ -2006,6 +2151,12 @@ static const struct agp_bridge_driver intel_915_driver = { .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 = { @@ -2034,6 +2185,12 @@ static const struct agp_bridge_driver intel_i965_driver = { .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 = { @@ -2088,6 +2245,12 @@ static const struct agp_bridge_driver intel_g33_driver = { .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) @@ -2192,6 +2355,8 @@ static const struct intel_driver_description { "Q45/Q43", NULL, &intel_i965_driver }, { PCI_DEVICE_ID_INTEL_G45_HB, PCI_DEVICE_ID_INTEL_G45_IG, 0, "G45/G43", NULL, &intel_i965_driver }, + { PCI_DEVICE_ID_INTEL_B43_HB, PCI_DEVICE_ID_INTEL_B43_IG, 0, + "B43", NULL, &intel_i965_driver }, { PCI_DEVICE_ID_INTEL_G41_HB, PCI_DEVICE_ID_INTEL_G41_IG, 0, "G41", NULL, &intel_i965_driver }, { PCI_DEVICE_ID_INTEL_IGDNG_D_HB, PCI_DEVICE_ID_INTEL_IGDNG_D_IG, 0, @@ -2313,15 +2478,6 @@ static int agp_intel_resume(struct pci_dev *pdev) struct agp_bridge_data *bridge = pci_get_drvdata(pdev); int ret_val; - pci_restore_state(pdev); - - /* We should restore our graphics device's config space, - * as host bridge (00:00) resumes before graphics device (02:00), - * then our access to its pci space can work right. - */ - if (intel_private.pcidev) - pci_restore_state(intel_private.pcidev); - if (bridge->driver == &intel_generic_driver) intel_configure(); else if (bridge->driver == &intel_850_driver) @@ -2401,6 +2557,7 @@ static struct pci_device_id agp_intel_pci_table[] = { ID(PCI_DEVICE_ID_INTEL_Q45_HB), ID(PCI_DEVICE_ID_INTEL_G45_HB), ID(PCI_DEVICE_ID_INTEL_G41_HB), + ID(PCI_DEVICE_ID_INTEL_B43_HB), ID(PCI_DEVICE_ID_INTEL_IGDNG_D_HB), ID(PCI_DEVICE_ID_INTEL_IGDNG_M_HB), ID(PCI_DEVICE_ID_INTEL_IGDNG_MA_HB), diff --git a/drivers/char/agp/nvidia-agp.c b/drivers/char/agp/nvidia-agp.c index 263d71d..7e36d2b 100644 --- a/drivers/char/agp/nvidia-agp.c +++ b/drivers/char/agp/nvidia-agp.c @@ -225,7 +225,7 @@ static int nvidia_insert_memory(struct agp_memory *mem, off_t pg_start, int type } for (i = 0, j = pg_start; i < mem->page_count; i++, j++) { writel(agp_bridge->driver->mask_memory(agp_bridge, - mem->pages[i], mask_type), + page_to_phys(mem->pages[i]), mask_type), agp_bridge->gatt_table+nvidia_private.pg_offset+j); } diff --git a/drivers/char/agp/parisc-agp.c b/drivers/char/agp/parisc-agp.c index e077701..60ab751 100644 --- a/drivers/char/agp/parisc-agp.c +++ b/drivers/char/agp/parisc-agp.c @@ -32,7 +32,7 @@ #define AGP8X_MODE (1 << AGP8X_MODE_BIT) static unsigned long -parisc_agp_mask_memory(struct agp_bridge_data *bridge, unsigned long addr, +parisc_agp_mask_memory(struct agp_bridge_data *bridge, dma_addr_t addr, int type); static struct _parisc_agp_info { @@ -189,20 +189,12 @@ parisc_agp_remove_memory(struct agp_memory *mem, off_t pg_start, int type) } static unsigned long -parisc_agp_mask_memory(struct agp_bridge_data *bridge, unsigned long addr, +parisc_agp_mask_memory(struct agp_bridge_data *bridge, dma_addr_t addr, int type) { return SBA_PDIR_VALID_BIT | addr; } -static unsigned long -parisc_agp_page_mask_memory(struct agp_bridge_data *bridge, struct page *page, - int type) -{ - unsigned long addr = phys_to_gart(page_to_phys(page)); - return SBA_PDIR_VALID_BIT | addr; -} - static void parisc_agp_enable(struct agp_bridge_data *bridge, u32 mode) { diff --git a/drivers/char/agp/sgi-agp.c b/drivers/char/agp/sgi-agp.c index d3ea2e4..0d426ae 100644 --- a/drivers/char/agp/sgi-agp.c +++ b/drivers/char/agp/sgi-agp.c @@ -70,10 +70,9 @@ static void sgi_tioca_tlbflush(struct agp_memory *mem) * entry. */ static unsigned long -sgi_tioca_mask_memory(struct agp_bridge_data *bridge, - struct page *page, int type) +sgi_tioca_mask_memory(struct agp_bridge_data *bridge, dma_addr_t addr, + int type) { - unsigned long addr = phys_to_gart(page_to_phys(page)); return tioca_physpage_to_gart(addr); } @@ -190,7 +189,8 @@ static int sgi_tioca_insert_memory(struct agp_memory *mem, off_t pg_start, for (i = 0, j = pg_start; i < mem->page_count; i++, j++) { table[j] = - bridge->driver->mask_memory(bridge, mem->pages[i], + bridge->driver->mask_memory(bridge, + page_to_phys(mem->pages[i]), mem->type); } diff --git a/drivers/char/agp/sworks-agp.c b/drivers/char/agp/sworks-agp.c index b964a21..13acaaf 100644 --- a/drivers/char/agp/sworks-agp.c +++ b/drivers/char/agp/sworks-agp.c @@ -155,7 +155,7 @@ static int serverworks_create_gatt_table(struct agp_bridge_data *bridge) /* Create a fake scratch directory */ for (i = 0; i < 1024; i++) { writel(agp_bridge->scratch_page, serverworks_private.scratch_dir.remapped+i); - writel(virt_to_gart(serverworks_private.scratch_dir.real) | 1, page_dir.remapped+i); + writel(virt_to_phys(serverworks_private.scratch_dir.real) | 1, page_dir.remapped+i); } retval = serverworks_create_gatt_pages(value->num_entries / 1024); @@ -167,7 +167,7 @@ static int serverworks_create_gatt_table(struct agp_bridge_data *bridge) agp_bridge->gatt_table_real = (u32 *)page_dir.real; agp_bridge->gatt_table = (u32 __iomem *)page_dir.remapped; - agp_bridge->gatt_bus_addr = virt_to_gart(page_dir.real); + agp_bridge->gatt_bus_addr = virt_to_phys(page_dir.real); /* Get the address for the gart region. * This is a bus address even on the alpha, b/c its @@ -179,7 +179,7 @@ static int serverworks_create_gatt_table(struct agp_bridge_data *bridge) /* Calculate the agp offset */ for (i = 0; i < value->num_entries / 1024; i++) - writel(virt_to_gart(serverworks_private.gatt_pages[i]->real)|1, page_dir.remapped+i); + writel(virt_to_phys(serverworks_private.gatt_pages[i]->real)|1, page_dir.remapped+i); return 0; } @@ -349,7 +349,9 @@ static int serverworks_insert_memory(struct agp_memory *mem, for (i = 0, j = pg_start; i < mem->page_count; i++, j++) { addr = (j * PAGE_SIZE) + agp_bridge->gart_bus_addr; cur_gatt = SVRWRKS_GET_GATT(addr); - writel(agp_bridge->driver->mask_memory(agp_bridge, mem->pages[i], mem->type), cur_gatt+GET_GATT_OFF(addr)); + writel(agp_bridge->driver->mask_memory(agp_bridge, + page_to_phys(mem->pages[i]), mem->type), + cur_gatt+GET_GATT_OFF(addr)); } serverworks_tlbflush(mem); return 0; diff --git a/drivers/char/agp/uninorth-agp.c b/drivers/char/agp/uninorth-agp.c index f192c3b..703959e 100644 --- a/drivers/char/agp/uninorth-agp.c +++ b/drivers/char/agp/uninorth-agp.c @@ -7,6 +7,7 @@ #include <linux/pagemap.h> #include <linux/agp_backend.h> #include <linux/delay.h> +#include <linux/vmalloc.h> #include <asm/uninorth.h> #include <asm/pci-bridge.h> #include <asm/prom.h> @@ -27,6 +28,8 @@ static int uninorth_rev; static int is_u3; +#define DEFAULT_APERTURE_SIZE 256 +#define DEFAULT_APERTURE_STRING "256" static char *aperture = NULL; static int uninorth_fetch_size(void) @@ -55,7 +58,7 @@ static int uninorth_fetch_size(void) if (!size) { for (i = 0; i < agp_bridge->driver->num_aperture_sizes; i++) - if (values[i].size == 32) + if (values[i].size == DEFAULT_APERTURE_SIZE) break; } @@ -135,7 +138,7 @@ static int uninorth_configure(void) if (is_u3) { pci_write_config_dword(agp_bridge->dev, UNI_N_CFG_GART_DUMMY_PAGE, - agp_bridge->scratch_page_real >> 12); + page_to_phys(agp_bridge->scratch_page_page) >> 12); } return 0; @@ -179,8 +182,6 @@ static int uninorth_insert_memory(struct agp_memory *mem, off_t pg_start, } (void)in_le32((volatile u32*)&agp_bridge->gatt_table[pg_start]); mb(); - flush_dcache_range((unsigned long)&agp_bridge->gatt_table[pg_start], - (unsigned long)&agp_bridge->gatt_table[pg_start + mem->page_count]); uninorth_tlbflush(mem); return 0; @@ -224,7 +225,6 @@ static int u3_insert_memory(struct agp_memory *mem, off_t pg_start, int type) (unsigned long)__va(page_to_phys(mem->pages[i]))+0x1000); } mb(); - flush_dcache_range((unsigned long)gp, (unsigned long) &gp[i]); uninorth_tlbflush(mem); return 0; @@ -243,7 +243,6 @@ int u3_remove_memory(struct agp_memory *mem, off_t pg_start, int type) for (i = 0; i < mem->page_count; ++i) gp[i] = 0; mb(); - flush_dcache_range((unsigned long)gp, (unsigned long) &gp[i]); uninorth_tlbflush(mem); return 0; @@ -271,7 +270,7 @@ static void uninorth_agp_enable(struct agp_bridge_data *bridge, u32 mode) if ((uninorth_rev >= 0x30) && (uninorth_rev <= 0x33)) { /* - * We need to to set REQ_DEPTH to 7 for U3 versions 1.0, 2.1, + * We need to set REQ_DEPTH to 7 for U3 versions 1.0, 2.1, * 2.2 and 2.3, Darwin do so. */ if ((command >> AGPSTAT_RQ_DEPTH_SHIFT) > 7) @@ -396,6 +395,7 @@ static int uninorth_create_gatt_table(struct agp_bridge_data *bridge) int i; void *temp; struct page *page; + struct page **pages; /* We can't handle 2 level gatt's */ if (bridge->driver->size_type == LVL2_APER_SIZE) @@ -424,21 +424,39 @@ static int uninorth_create_gatt_table(struct agp_bridge_data *bridge) if (table == NULL) return -ENOMEM; + pages = kmalloc((1 << page_order) * sizeof(struct page*), GFP_KERNEL); + if (pages == NULL) + goto enomem; + table_end = table + ((PAGE_SIZE * (1 << page_order)) - 1); - for (page = virt_to_page(table); page <= virt_to_page(table_end); page++) + for (page = virt_to_page(table), i = 0; page <= virt_to_page(table_end); + page++, i++) { SetPageReserved(page); + pages[i] = page; + } bridge->gatt_table_real = (u32 *) table; - bridge->gatt_table = (u32 *)table; - bridge->gatt_bus_addr = virt_to_gart(table); + /* Need to clear out any dirty data still sitting in caches */ + flush_dcache_range((unsigned long)table, + (unsigned long)(table_end + PAGE_SIZE)); + bridge->gatt_table = vmap(pages, (1 << page_order), 0, PAGE_KERNEL_NCG); + + if (bridge->gatt_table == NULL) + goto enomem; + + bridge->gatt_bus_addr = virt_to_phys(table); for (i = 0; i < num_entries; i++) bridge->gatt_table[i] = 0; - flush_dcache_range((unsigned long)table, (unsigned long)table_end); - return 0; + +enomem: + kfree(pages); + if (table) + free_pages((unsigned long)table, page_order); + return -ENOMEM; } static int uninorth_free_gatt_table(struct agp_bridge_data *bridge) @@ -456,6 +474,7 @@ static int uninorth_free_gatt_table(struct agp_bridge_data *bridge) * from the table. */ + vunmap(bridge->gatt_table); table = (char *) bridge->gatt_table_real; table_end = table + ((PAGE_SIZE * (1 << page_order)) - 1); @@ -474,13 +493,11 @@ void null_cache_flush(void) /* Setup function */ -static const struct aper_size_info_32 uninorth_sizes[7] = +static const struct aper_size_info_32 uninorth_sizes[] = { -#if 0 /* Not sure uninorth supports that high aperture sizes */ {256, 65536, 6, 64}, {128, 32768, 5, 32}, {64, 16384, 4, 16}, -#endif {32, 8192, 3, 8}, {16, 4096, 2, 4}, {8, 2048, 1, 2}, @@ -491,7 +508,7 @@ static const struct aper_size_info_32 uninorth_sizes[7] = * Not sure that u3 supports that high aperture sizes but it * would strange if it did not :) */ -static const struct aper_size_info_32 u3_sizes[8] = +static const struct aper_size_info_32 u3_sizes[] = { {512, 131072, 7, 128}, {256, 65536, 6, 64}, @@ -507,7 +524,7 @@ const struct agp_bridge_driver uninorth_agp_driver = { .owner = THIS_MODULE, .aperture_sizes = (void *)uninorth_sizes, .size_type = U32_APER_SIZE, - .num_aperture_sizes = 4, + .num_aperture_sizes = ARRAY_SIZE(uninorth_sizes), .configure = uninorth_configure, .fetch_size = uninorth_fetch_size, .cleanup = uninorth_cleanup, @@ -534,7 +551,7 @@ const struct agp_bridge_driver u3_agp_driver = { .owner = THIS_MODULE, .aperture_sizes = (void *)u3_sizes, .size_type = U32_APER_SIZE, - .num_aperture_sizes = 8, + .num_aperture_sizes = ARRAY_SIZE(u3_sizes), .configure = uninorth_configure, .fetch_size = uninorth_fetch_size, .cleanup = uninorth_cleanup, @@ -717,7 +734,7 @@ module_param(aperture, charp, 0); MODULE_PARM_DESC(aperture, "Aperture size, must be power of two between 4MB and an\n" "\t\tupper limit specific to the UniNorth revision.\n" - "\t\tDefault: 32M"); + "\t\tDefault: " DEFAULT_APERTURE_STRING "M"); MODULE_AUTHOR("Ben Herrenschmidt & Paul Mackerras"); MODULE_LICENSE("GPL"); diff --git a/drivers/char/bfin-otp.c b/drivers/char/bfin-otp.c index 0a01329..e3dd24b 100644 --- a/drivers/char/bfin-otp.c +++ b/drivers/char/bfin-otp.c @@ -1,8 +1,7 @@ /* * Blackfin On-Chip OTP Memory Interface - * Supports BF52x/BF54x * - * Copyright 2007-2008 Analog Devices Inc. + * Copyright 2007-2009 Analog Devices Inc. * * Enter bugs at http://blackfin.uclinux.org/ * @@ -17,8 +16,10 @@ #include <linux/module.h> #include <linux/mutex.h> #include <linux/types.h> +#include <mtd/mtd-abi.h> #include <asm/blackfin.h> +#include <asm/bfrom.h> #include <asm/uaccess.h> #define stamp(fmt, args...) pr_debug("%s:%i: " fmt "\n", __func__, __LINE__, ## args) @@ -30,39 +31,6 @@ static DEFINE_MUTEX(bfin_otp_lock); -/* OTP Boot ROM functions */ -#define _BOOTROM_OTP_COMMAND 0xEF000018 -#define _BOOTROM_OTP_READ 0xEF00001A -#define _BOOTROM_OTP_WRITE 0xEF00001C - -static u32 (* const otp_command)(u32 command, u32 value) = (void *)_BOOTROM_OTP_COMMAND; -static u32 (* const otp_read)(u32 page, u32 flags, u64 *page_content) = (void *)_BOOTROM_OTP_READ; -static u32 (* const otp_write)(u32 page, u32 flags, u64 *page_content) = (void *)_BOOTROM_OTP_WRITE; - -/* otp_command(): defines for "command" */ -#define OTP_INIT 0x00000001 -#define OTP_CLOSE 0x00000002 - -/* otp_{read,write}(): defines for "flags" */ -#define OTP_LOWER_HALF 0x00000000 /* select upper/lower 64-bit half (bit 0) */ -#define OTP_UPPER_HALF 0x00000001 -#define OTP_NO_ECC 0x00000010 /* do not use ECC */ -#define OTP_LOCK 0x00000020 /* sets page protection bit for page */ -#define OTP_ACCESS_READ 0x00001000 -#define OTP_ACCESS_READWRITE 0x00002000 - -/* Return values for all functions */ -#define OTP_SUCCESS 0x00000000 -#define OTP_MASTER_ERROR 0x001 -#define OTP_WRITE_ERROR 0x003 -#define OTP_READ_ERROR 0x005 -#define OTP_ACC_VIO_ERROR 0x009 -#define OTP_DATA_MULT_ERROR 0x011 -#define OTP_ECC_MULT_ERROR 0x021 -#define OTP_PREV_WR_ERROR 0x041 -#define OTP_DATA_SB_WARN 0x100 -#define OTP_ECC_SB_WARN 0x200 - /** * bfin_otp_read - Read OTP pages * @@ -86,9 +54,11 @@ static ssize_t bfin_otp_read(struct file *file, char __user *buff, size_t count, page = *pos / (sizeof(u64) * 2); while (bytes_done < count) { flags = (*pos % (sizeof(u64) * 2) ? OTP_UPPER_HALF : OTP_LOWER_HALF); - stamp("processing page %i (%s)", page, (flags == OTP_UPPER_HALF ? "upper" : "lower")); - ret = otp_read(page, flags, &content); + stamp("processing page %i (0x%x:%s)", page, flags, + (flags & OTP_UPPER_HALF ? "upper" : "lower")); + ret = bfrom_OtpRead(page, flags, &content); if (ret & OTP_MASTER_ERROR) { + stamp("error from otp: 0x%x", ret); bytes_done = -EIO; break; } @@ -96,7 +66,7 @@ static ssize_t bfin_otp_read(struct file *file, char __user *buff, size_t count, bytes_done = -EFAULT; break; } - if (flags == OTP_UPPER_HALF) + if (flags & OTP_UPPER_HALF) ++page; bytes_done += sizeof(content); *pos += sizeof(content); @@ -108,14 +78,53 @@ static ssize_t bfin_otp_read(struct file *file, char __user *buff, size_t count, } #ifdef CONFIG_BFIN_OTP_WRITE_ENABLE +static bool allow_writes; + +/** + * bfin_otp_init_timing - setup OTP timing parameters + * + * Required before doing any write operation. Algorithms from HRM. + */ +static u32 bfin_otp_init_timing(void) +{ + u32 tp1, tp2, tp3, timing; + + tp1 = get_sclk() / 1000000; + tp2 = (2 * get_sclk() / 10000000) << 8; + tp3 = (0x1401) << 15; + timing = tp1 | tp2 | tp3; + if (bfrom_OtpCommand(OTP_INIT, timing)) + return 0; + + return timing; +} + +/** + * bfin_otp_deinit_timing - set timings to only allow reads + * + * Should be called after all writes are done. + */ +static void bfin_otp_deinit_timing(u32 timing) +{ + /* mask bits [31:15] so that any attempts to write fail */ + bfrom_OtpCommand(OTP_CLOSE, 0); + bfrom_OtpCommand(OTP_INIT, timing & ~(-1 << 15)); + bfrom_OtpCommand(OTP_CLOSE, 0); +} + /** - * bfin_otp_write - Write OTP pages + * bfin_otp_write - write OTP pages * * All writes must be in half page chunks (half page == 64 bits). */ static ssize_t bfin_otp_write(struct file *filp, const char __user *buff, size_t count, loff_t *pos) { - stampit(); + ssize_t bytes_done; + u32 timing, page, base_flags, flags, ret; + u64 content; + + if (!allow_writes) + return -EACCES; if (count % sizeof(u64)) return -EMSGSIZE; @@ -123,20 +132,96 @@ static ssize_t bfin_otp_write(struct file *filp, const char __user *buff, size_t if (mutex_lock_interruptible(&bfin_otp_lock)) return -ERESTARTSYS; - /* need otp_init() documentation before this can be implemented */ + stampit(); + + timing = bfin_otp_init_timing(); + if (timing == 0) { + mutex_unlock(&bfin_otp_lock); + return -EIO; + } + + base_flags = OTP_CHECK_FOR_PREV_WRITE; + + bytes_done = 0; + page = *pos / (sizeof(u64) * 2); + while (bytes_done < count) { + flags = base_flags | (*pos % (sizeof(u64) * 2) ? OTP_UPPER_HALF : OTP_LOWER_HALF); + stamp("processing page %i (0x%x:%s) from %p", page, flags, + (flags & OTP_UPPER_HALF ? "upper" : "lower"), buff + bytes_done); + if (copy_from_user(&content, buff + bytes_done, sizeof(content))) { + bytes_done = -EFAULT; + break; + } + ret = bfrom_OtpWrite(page, flags, &content); + if (ret & OTP_MASTER_ERROR) { + stamp("error from otp: 0x%x", ret); + bytes_done = -EIO; + break; + } + if (flags & OTP_UPPER_HALF) + ++page; + bytes_done += sizeof(content); + *pos += sizeof(content); + } + + bfin_otp_deinit_timing(timing); mutex_unlock(&bfin_otp_lock); + return bytes_done; +} + +static long bfin_otp_ioctl(struct file *filp, unsigned cmd, unsigned long arg) +{ + stampit(); + + switch (cmd) { + case OTPLOCK: { + u32 timing; + int ret = -EIO; + + if (!allow_writes) + return -EACCES; + + if (mutex_lock_interruptible(&bfin_otp_lock)) + return -ERESTARTSYS; + + timing = bfin_otp_init_timing(); + if (timing) { + u32 otp_result = bfrom_OtpWrite(arg, OTP_LOCK, NULL); + stamp("locking page %lu resulted in 0x%x", arg, otp_result); + if (!(otp_result & OTP_MASTER_ERROR)) + ret = 0; + + bfin_otp_deinit_timing(timing); + } + + mutex_unlock(&bfin_otp_lock); + + return ret; + } + + case MEMLOCK: + allow_writes = false; + return 0; + + case MEMUNLOCK: + allow_writes = true; + return 0; + } + return -EINVAL; } #else # define bfin_otp_write NULL +# define bfin_otp_ioctl NULL #endif static struct file_operations bfin_otp_fops = { - .owner = THIS_MODULE, - .read = bfin_otp_read, - .write = bfin_otp_write, + .owner = THIS_MODULE, + .unlocked_ioctl = bfin_otp_ioctl, + .read = bfin_otp_read, + .write = bfin_otp_write, }; static struct miscdevice bfin_otp_misc_device = { diff --git a/drivers/char/cyclades.c b/drivers/char/cyclades.c index 2dafc2d..df5038b 100644 --- a/drivers/char/cyclades.c +++ b/drivers/char/cyclades.c @@ -11,7 +11,7 @@ * Initially written by Randolph Bentson <bentson@grieg.seaslug.org>. * Modified and maintained by Marcio Saito <marcio@cyclades.com>. * - * Copyright (C) 2007 Jiri Slaby <jirislaby@gmail.com> + * Copyright (C) 2007-2009 Jiri Slaby <jirislaby@gmail.com> * * Much of the design and some of the code came from serial.c * which was copyright (C) 1991, 1992 Linus Torvalds. It was @@ -19,577 +19,9 @@ * and then fixed as suggested by Michael K. Johnson 12/12/92. * Converted to pci probing and cleaned up by Jiri Slaby. * - * This version supports shared IRQ's (only for PCI boards). - * - * Prevent users from opening non-existing Z ports. - * - * Revision 2.3.2.8 2000/07/06 18:14:16 ivan - * Fixed the PCI detection function to work properly on Alpha systems. - * Implemented support for TIOCSERGETLSR ioctl. - * Implemented full support for non-standard baud rates. - * - * Revision 2.3.2.7 2000/06/01 18:26:34 ivan - * Request PLX I/O region, although driver doesn't use it, to avoid - * problems with other drivers accessing it. - * Removed count for on-board buffer characters in cy_chars_in_buffer - * (Cyclades-Z only). - * - * Revision 2.3.2.6 2000/05/05 13:56:05 ivan - * Driver now reports physical instead of virtual memory addresses. - * Masks were added to some Cyclades-Z read accesses. - * Implemented workaround for PLX9050 bug that would cause a system lockup - * in certain systems, depending on the MMIO addresses allocated to the - * board. - * Changed the Tx interrupt programming in the CD1400 chips to boost up - * performance (Cyclom-Y only). - * Code is now compliant with the new module interface (module_[init|exit]). - * Make use of the PCI helper functions to access PCI resources. - * Did some code "housekeeping". - * - * Revision 2.3.2.5 2000/01/19 14:35:33 ivan - * Fixed bug in cy_set_termios on CRTSCTS flag turnoff. - * - * Revision 2.3.2.4 2000/01/17 09:19:40 ivan - * Fixed SMP locking in Cyclom-Y interrupt handler. - * - * Revision 2.3.2.3 1999/12/28 12:11:39 ivan - * Added a new cyclades_card field called nports to allow the driver to - * know the exact number of ports found by the Z firmware after its load; - * RX buffer contention prevention logic on interrupt op mode revisited - * (Cyclades-Z only); - * Revisited printk's for Z debug; - * Driver now makes sure that the constant SERIAL_XMIT_SIZE is defined; - * - * Revision 2.3.2.2 1999/10/01 11:27:43 ivan - * Fixed bug in cyz_poll that would make all ports but port 0 - * unable to transmit/receive data (Cyclades-Z only); - * Implemented logic to prevent the RX buffer from being stuck with data - * due to a driver / firmware race condition in interrupt op mode - * (Cyclades-Z only); - * Fixed bug in block_til_ready logic that would lead to a system crash; - * Revisited cy_close spinlock usage; - * - * Revision 2.3.2.1 1999/09/28 11:01:22 ivan - * Revisited CONFIG_PCI conditional compilation for PCI board support; - * Implemented TIOCGICOUNT and TIOCMIWAIT ioctl support; - * _Major_ cleanup on the Cyclades-Z interrupt support code / logic; - * Removed CTS handling from the driver -- this is now completely handled - * by the firmware (Cyclades-Z only); - * Flush RX on-board buffers on a port open (Cyclades-Z only); - * Fixed handling of ASYNC_SPD_* TTY flags; - * Module unload now unmaps all memory area allocated by ioremap; - * - * Revision 2.3.1.1 1999/07/15 16:45:53 ivan - * Removed CY_PROC conditional compilation; - * Implemented SMP-awareness for the driver; - * Implemented a new ISA IRQ autoprobe that uses the irq_probe_[on|off] - * functions; - * The driver now accepts memory addresses (maddr=0xMMMMM) and IRQs - * (irq=NN) as parameters (only for ISA boards); - * Fixed bug in set_line_char that would prevent the Cyclades-Z - * ports from being configured at speeds above 115.2Kbps; - * Fixed bug in cy_set_termios that would prevent XON/XOFF flow control - * switching from working properly; - * The driver now only prints IRQ info for the Cyclades-Z if it's - * configured to work in interrupt mode; - * - * Revision 2.2.2.3 1999/06/28 11:13:29 ivan - * Added support for interrupt mode operation for the Z cards; - * Removed the driver inactivity control for the Z; - * Added a missing MOD_DEC_USE_COUNT in the cy_open function for when - * the Z firmware is not loaded yet; - * Replaced the "manual" Z Tx flush buffer by a call to a FW command of - * same functionality; - * Implemented workaround for IRQ setting loss on the PCI configuration - * registers after a PCI bridge EEPROM reload (affects PLX9060 only); - * - * Revision 2.2.2.2 1999/05/14 17:18:15 ivan - * /proc entry location changed to /proc/tty/driver/cyclades; - * Added support to shared IRQ's (only for PCI boards); - * Added support for Cobalt Qube2 systems; - * IRQ [de]allocation scheme revisited; - * BREAK implementation changed in order to make use of the 'break_ctl' - * TTY facility; - * Fixed typo in TTY structure field 'driver_name'; - * Included a PCI bridge reset and EEPROM reload in the board - * initialization code (for both Y and Z series). - * - * Revision 2.2.2.1 1999/04/08 16:17:43 ivan - * Fixed a bug in cy_wait_until_sent that was preventing the port to be - * closed properly after a SIGINT; - * Module usage counter scheme revisited; - * Added support to the upcoming Y PCI boards (i.e., support to additional - * PCI Device ID's). - * - * Revision 2.2.1.10 1999/01/20 16:14:29 ivan - * Removed all unnecessary page-alignement operations in ioremap calls - * (ioremap is currently safe for these operations). - * - * Revision 2.2.1.9 1998/12/30 18:18:30 ivan - * Changed access to PLX PCI bridge registers from I/O to MMIO, in - * order to make PLX9050-based boards work with certain motherboards. - * - * Revision 2.2.1.8 1998/11/13 12:46:20 ivan - * cy_close function now resets (correctly) the tty->closing flag; - * JIFFIES_DIFF macro fixed. - * - * Revision 2.2.1.7 1998/09/03 12:07:28 ivan - * Fixed bug in cy_close function, which was not informing HW of - * which port should have the reception disabled before doing so; - * fixed Cyclom-8YoP hardware detection bug. - * - * Revision 2.2.1.6 1998/08/20 17:15:39 ivan - * Fixed bug in cy_close function, which causes malfunction - * of one of the first 4 ports when a higher port is closed - * (Cyclom-Y only). - * - * Revision 2.2.1.5 1998/08/10 18:10:28 ivan - * Fixed Cyclom-4Yo hardware detection bug. - * - * Revision 2.2.1.4 1998/08/04 11:02:50 ivan - * /proc/cyclades implementation with great collaboration of - * Marc Lewis <marc@blarg.net>; - * cyy_interrupt was changed to avoid occurrence of kernel oopses - * during PPP operation. - * - * Revision 2.2.1.3 1998/06/01 12:09:10 ivan - * General code review in order to comply with 2.1 kernel standards; - * data loss prevention for slow devices revisited (cy_wait_until_sent - * was created); - * removed conditional compilation for new/old PCI structure support - * (now the driver only supports the new PCI structure). - * - * Revision 2.2.1.1 1998/03/19 16:43:12 ivan - * added conditional compilation for new/old PCI structure support; - * removed kernel series (2.0.x / 2.1.x) conditional compilation. - * - * Revision 2.1.1.3 1998/03/16 18:01:12 ivan - * cleaned up the data loss fix; - * fixed XON/XOFF handling once more (Cyclades-Z); - * general review of the driver routines; - * introduction of a mechanism to prevent data loss with slow - * printers, by forcing a delay before closing the port. - * - * Revision 2.1.1.2 1998/02/17 16:50:00 ivan - * fixed detection/handling of new CD1400 in Ye boards; - * fixed XON/XOFF handling (Cyclades-Z); - * fixed data loss caused by a premature port close; - * introduction of a flag that holds the CD1400 version ID per port - * (used by the CYGETCD1400VER new ioctl). - * - * Revision 2.1.1.1 1997/12/03 17:31:19 ivan - * Code review for the module cleanup routine; - * fixed RTS and DTR status report for new CD1400's in get_modem_info; - * includes anonymous changes regarding signal_pending. - * - * Revision 2.1 1997/11/01 17:42:41 ivan - * Changes in the driver to support Alpha systems (except 8Zo V_1); - * BREAK fix for the Cyclades-Z boards; - * driver inactivity control by FW implemented; - * introduction of flag that allows driver to take advantage of - * a special CD1400 feature related to HW flow control; - * added support for the CD1400 rev. J (Cyclom-Y boards); - * introduction of ioctls to: - * - control the rtsdtr_inv flag (Cyclom-Y); - * - control the rflow flag (Cyclom-Y); - * - adjust the polling interval (Cyclades-Z); - * - * Revision 1.36.4.33 1997/06/27 19:00:00 ivan - * Fixes related to kernel version conditional - * compilation. - * - * Revision 1.36.4.32 1997/06/14 19:30:00 ivan - * Compatibility issues between kernels 2.0.x and - * 2.1.x (mainly related to clear_bit function). - * - * Revision 1.36.4.31 1997/06/03 15:30:00 ivan - * Changes to define the memory window according to the - * board type. - * - * Revision 1.36.4.30 1997/05/16 15:30:00 daniel - * Changes to support new cycladesZ boards. - * - * Revision 1.36.4.29 1997/05/12 11:30:00 daniel - * Merge of Bentson's and Daniel's version 1.36.4.28. - * Corrects bug in cy_detect_pci: check if there are more - * ports than the number of static structs allocated. - * Warning message during initialization if this driver is - * used with the new generation of cycladesZ boards. Those - * will be supported only in next release of the driver. - * Corrects bug in cy_detect_pci and cy_detect_isa that - * returned wrong number of VALID boards, when a cyclomY - * was found with no serial modules connected. - * Changes to use current (2.1.x) kernel subroutine names - * and created macros for compilation with 2.0.x kernel, - * instead of the other way around. - * - * Revision 1.36.4.28 1997/05/?? ??:00:00 bentson - * Change queue_task_irq_off to queue_task_irq. - * The inline function queue_task_irq_off (tqueue.h) - * was removed from latest releases of 2.1.x kernel. - * Use of macro __init to mark the initialization - * routines, so memory can be reused. - * Also incorporate implementation of critical region - * in function cleanup_module() created by anonymous - * linuxer. - * - * Revision 1.36.4.28 1997/04/25 16:00:00 daniel - * Change to support new firmware that solves DCD problem: - * application could fail to receive SIGHUP signal when DCD - * varying too fast. - * - * Revision 1.36.4.27 1997/03/26 10:30:00 daniel - * Changed for support linux versions 2.1.X. - * Backward compatible with linux versions 2.0.X. - * Corrected illegal use of filler field in - * CH_CTRL struct. - * Deleted some debug messages. - * - * Revision 1.36.4.26 1997/02/27 12:00:00 daniel - * Included check for NULL tty pointer in cyz_poll. - * - * Revision 1.36.4.25 1997/02/26 16:28:30 bentson - * Bill Foster at Blarg! Online services noticed that - * some of the switch elements of -Z modem control - * lacked a closing "break;" - * - * Revision 1.36.4.24 1997/02/24 11:00:00 daniel - * Changed low water threshold for buffer xmit_buf - * - * Revision 1.36.4.23 1996/12/02 21:50:16 bentson - * Marcio provided fix to modem status fetch for -Z - * - * Revision 1.36.4.22 1996/10/28 22:41:17 bentson - * improve mapping of -Z control page (thanks to Steve - * Price <stevep@fa.tdktca.com> for help on this) - * - * Revision 1.36.4.21 1996/09/10 17:00:10 bentson - * shift from CPU-bound to memcopy in cyz_polling operation - * - * Revision 1.36.4.20 1996/09/09 18:30:32 Bentson - * Added support to set and report higher speeds. - * - * Revision 1.36.4.19c 1996/08/09 10:00:00 Marcio Saito - * Some fixes in the HW flow control for the BETA release. - * Don't try to register the IRQ. - * - * Revision 1.36.4.19 1996/08/08 16:23:18 Bentson - * make sure "cyc" appears in all kernel messages; all soft interrupts - * handled by same routine; recognize out-of-band reception; comment - * out some diagnostic messages; leave RTS/CTS flow control to hardware; - * fix race condition in -Z buffer management; only -Y needs to explicitly - * flush chars; tidy up some startup messages; - * - * Revision 1.36.4.18 1996/07/25 18:57:31 bentson - * shift MOD_INC_USE_COUNT location to match - * serial.c; purge some diagnostic messages; - * - * Revision 1.36.4.17 1996/07/25 18:01:08 bentson - * enable modem status messages and fetch & process them; note - * time of last activity type for each port; set_line_char now - * supports more than line 0 and treats 0 baud correctly; - * get_modem_info senses rs_status; - * - * Revision 1.36.4.16 1996/07/20 08:43:15 bentson - * barely works--now's time to turn on - * more features 'til it breaks - * - * Revision 1.36.4.15 1996/07/19 22:30:06 bentson - * check more -Z board status; shorten boot message - * - * Revision 1.36.4.14 1996/07/19 22:20:37 bentson - * fix reference to ch_ctrl in startup; verify return - * values from cyz_issue_cmd and cyz_update_channel; - * more stuff to get modem control correct; - * - * Revision 1.36.4.13 1996/07/11 19:53:33 bentson - * more -Z stuff folded in; re-order changes to put -Z stuff - * after -Y stuff (to make changes clearer) - * - * Revision 1.36.4.12 1996/07/11 15:40:55 bentson - * Add code to poll Cyclades-Z. Add code to get & set RS-232 control. - * Add code to send break. Clear firmware ID word at startup (so - * that other code won't talk to inactive board). - * - * Revision 1.36.4.11 1996/07/09 05:28:29 bentson - * add code for -Z in set_line_char - * - * Revision 1.36.4.10 1996/07/08 19:28:37 bentson - * fold more -Z stuff (or in some cases, error messages) - * into driver; add text to "don't know what to do" messages. - * - * Revision 1.36.4.9 1996/07/08 18:38:38 bentson - * moved compile-time flags near top of file; cosmetic changes - * to narrow text (to allow 2-up printing); changed many declarations - * to "static" to limit external symbols; shuffled code order to - * coalesce -Y and -Z specific code, also to put internal functions - * in order of tty_driver structure; added code to recognize -Z - * ports (and for moment, do nothing or report error); add cy_startup - * to parse boot command line for extra base addresses for ISA probes; - * - * Revision 1.36.4.8 1996/06/25 17:40:19 bentson - * reorder some code, fix types of some vars (int vs. long), - * add cy_setup to support user declared ISA addresses - * - * Revision 1.36.4.7 1996/06/21 23:06:18 bentson - * dump ioctl based firmware load (it's now a user level - * program); ensure uninitialzed ports cannot be used - * - * Revision 1.36.4.6 1996/06/20 23:17:19 bentson - * rename vars and restructure some code - * - * Revision 1.36.4.5 1996/06/14 15:09:44 bentson - * get right status back after boot load - * - * Revision 1.36.4.4 1996/06/13 19:51:44 bentson - * successfully loads firmware - * - * Revision 1.36.4.3 1996/06/13 06:08:33 bentson - * add more of the code for the boot/load ioctls - * - * Revision 1.36.4.2 1996/06/11 21:00:51 bentson - * start to add Z functionality--starting with ioctl - * for loading firmware - * - * Revision 1.36.4.1 1996/06/10 18:03:02 bentson - * added code to recognize Z/PCI card at initialization; report - * presence, but card is not initialized (because firmware needs - * to be loaded) - * - * Revision 1.36.3.8 1996/06/07 16:29:00 bentson - * starting minor number at zero; added missing verify_area - * as noted by Heiko Eißfeldt <heiko@colossus.escape.de> - * - * Revision 1.36.3.7 1996/04/19 21:06:18 bentson - * remove unneeded boot message & fix CLOCAL hardware flow - * control (Miquel van Smoorenburg <miquels@Q.cistron.nl>); - * remove unused diagnostic statements; minor 0 is first; - * - * Revision 1.36.3.6 1996/03/13 13:21:17 marcio - * The kernel function vremap (available only in later 1.3.xx kernels) - * allows the access to memory addresses above the RAM. This revision - * of the driver supports PCI boards below 1Mb (device id 0x100) and - * above 1Mb (device id 0x101). - * - * Revision 1.36.3.5 1996/03/07 15:20:17 bentson - * Some global changes to interrupt handling spilled into - * this driver--mostly unused arguments in system function - * calls. Also added change by Marcio Saito which should - * reduce lost interrupts at startup by fast processors. - * - * Revision 1.36.3.4 1995/11/13 20:45:10 bentson - * Changes by Corey Minyard <minyard@wf-rch.cirr.com> distributed - * in 1.3.41 kernel to remove a possible race condition, extend - * some error messages, and let the driver run as a loadable module - * Change by Alan Wendt <alan@ez0.ezlink.com> to remove a - * possible race condition. - * Change by Marcio Saito <marcio@cyclades.com> to fix PCI addressing. - * - * Revision 1.36.3.3 1995/11/13 19:44:48 bentson - * Changes by Linus Torvalds in 1.3.33 kernel distribution - * required due to reordering of driver initialization. - * Drivers are now initialized *after* memory management. - * - * Revision 1.36.3.2 1995/09/08 22:07:14 bentson - * remove printk from ISR; fix typo - * - * Revision 1.36.3.1 1995/09/01 12:00:42 marcio - * Minor fixes in the PCI board support. PCI function calls in - * conditional compilation (CONFIG_PCI). Thanks to Jim Duncan - * <duncan@okay.com>. "bad serial count" message removed. - * - * Revision 1.36.3 1995/08/22 09:19:42 marcio - * Cyclom-Y/PCI support added. Changes in the cy_init routine and - * board initialization. Changes in the boot messages. The driver - * supports up to 4 boards and 64 ports by default. - * - * Revision 1.36.1.4 1995/03/29 06:14:14 bentson - * disambiguate between Cyclom-16Y and Cyclom-32Ye; - * - * Revision 1.36.1.3 1995/03/23 22:15:35 bentson - * add missing break in modem control block in ioctl switch statement - * (discovered by Michael Edward Chastain <mec@jobe.shell.portal.com>); - * - * Revision 1.36.1.2 1995/03/22 19:16:22 bentson - * make sure CTS flow control is set as soon as possible (thanks - * to note from David Lambert <lambert@chesapeake.rps.slb.com>); - * - * Revision 1.36.1.1 1995/03/13 15:44:43 bentson - * initialize defaults for receive threshold and stale data timeout; - * cosmetic changes; - * - * Revision 1.36 1995/03/10 23:33:53 bentson - * added support of chips 4-7 in 32 port Cyclom-Ye; - * fix cy_interrupt pointer dereference problem - * (Joe Portman <baron@aa.net>); - * give better error response if open is attempted on non-existent port - * (Zachariah Vaum <jchryslr@netcom.com>); - * correct command timeout (Kenneth Lerman <lerman@@seltd.newnet.com>); - * conditional compilation for -16Y on systems with fast, noisy bus; - * comment out diagnostic print function; - * cleaned up table of base addresses; - * set receiver time-out period register to correct value, - * set receive threshold to better default values, - * set chip timer to more accurate 200 Hz ticking, - * add code to monitor and modify receive parameters - * (Rik Faith <faith@cs.unc.edu> Nick Simicich - * <njs@scifi.emi.net>); - * - * Revision 1.35 1994/12/16 13:54:18 steffen - * additional patch by Marcio Saito for board detection - * Accidently left out in 1.34 - * - * Revision 1.34 1994/12/10 12:37:12 steffen - * This is the corrected version as suggested by Marcio Saito - * - * Revision 1.33 1994/12/01 22:41:18 bentson - * add hooks to support more high speeds directly; add tytso - * patch regarding CLOCAL wakeups - * - * Revision 1.32 1994/11/23 19:50:04 bentson - * allow direct kernel control of higher signalling rates; - * look for cards at additional locations - * - * Revision 1.31 1994/11/16 04:33:28 bentson - * ANOTHER fix from Corey Minyard, minyard@wf-rch.cirr.com-- - * a problem in chars_in_buffer has been resolved by some - * small changes; this should yield smoother output - * - * Revision 1.30 1994/11/16 04:28:05 bentson - * Fix from Corey Minyard, Internet: minyard@metronet.com, - * UUCP: minyard@wf-rch.cirr.com, WORK: minyardbnr.ca, to - * cy_hangup that appears to clear up much (all?) of the - * DTR glitches; also he's added/cleaned-up diagnostic messages - * - * Revision 1.29 1994/11/16 04:16:07 bentson - * add change proposed by Ralph Sims, ralphs@halcyon.com, to - * operate higher speeds in same way as other serial ports; - * add more serial ports (for up to two 16-port muxes). - * - * Revision 1.28 1994/11/04 00:13:16 root - * turn off diagnostic messages - * - * Revision 1.27 1994/11/03 23:46:37 root - * bunch of changes to bring driver into greater conformance - * with the serial.c driver (looking for missed fixes) - * - * Revision 1.26 1994/11/03 22:40:36 root - * automatic interrupt probing fixed. - * - * Revision 1.25 1994/11/03 20:17:02 root - * start to implement auto-irq - * - * Revision 1.24 1994/11/03 18:01:55 root - * still working on modem signals--trying not to drop DTR - * during the getty/login processes - * - * Revision 1.23 1994/11/03 17:51:36 root - * extend baud rate support; set receive threshold as function - * of baud rate; fix some problems with RTS/CTS; - * - * Revision 1.22 1994/11/02 18:05:35 root - * changed arguments to udelay to type long to get - * delays to be of correct duration - * - * Revision 1.21 1994/11/02 17:37:30 root - * employ udelay (after calibrating loops_per_second earlier - * in init/main.c) instead of using home-grown delay routines - * - * Revision 1.20 1994/11/02 03:11:38 root - * cy_chars_in_buffer forces a return value of 0 to let - * login work (don't know why it does); some functions - * that were returning EFAULT, now executes the code; - * more work on deciding when to disable xmit interrupts; - * - * Revision 1.19 1994/11/01 20:10:14 root - * define routine to start transmission interrupts (by enabling - * transmit interrupts); directly enable/disable modem interrupts; - * - * Revision 1.18 1994/11/01 18:40:45 bentson - * Don't always enable transmit interrupts in startup; interrupt on - * TxMpty instead of TxRdy to help characters get out before shutdown; - * restructure xmit interrupt to check for chars first and quit if - * none are ready to go; modem status (MXVRx) is upright, _not_ inverted - * (to my view); - * - * Revision 1.17 1994/10/30 04:39:45 bentson - * rename serial_driver and callout_driver to cy_serial_driver and - * cy_callout_driver to avoid linkage interference; initialize - * info->type to PORT_CIRRUS; ruggedize paranoia test; elide ->port - * from cyclades_port structure; add paranoia check to cy_close; - * - * Revision 1.16 1994/10/30 01:14:33 bentson - * change major numbers; add some _early_ return statements; - * - * Revision 1.15 1994/10/29 06:43:15 bentson - * final tidying up for clean compile; enable some error reporting - * - * Revision 1.14 1994/10/28 20:30:22 Bentson - * lots of changes to drag the driver towards the new tty_io - * structures and operation. not expected to work, but may - * compile cleanly. - * - * Revision 1.13 1994/07/21 23:08:57 Bentson - * add some diagnostic cruft; support 24 lines (for testing - * both -8Y and -16Y cards; be more thorough in servicing all - * chips during interrupt; add "volatile" a few places to - * circumvent compiler optimizations; fix base & offset - * computations in block_til_ready (was causing chip 0 to - * stop operation) - * - * Revision 1.12 1994/07/19 16:42:11 Bentson - * add some hackery for kernel version 1.1.8; expand - * error messages; refine timing for delay loops and - * declare loop params volatile - * - * Revision 1.11 1994/06/11 21:53:10 bentson - * get use of save_car right in transmit interrupt service - * - * Revision 1.10.1.1 1994/06/11 21:31:18 bentson - * add some diagnostic printing; try to fix save_car stuff - * - * Revision 1.10 1994/06/11 20:36:08 bentson - * clean up compiler warnings - * - * Revision 1.9 1994/06/11 19:42:46 bentson - * added a bunch of code to support modem signalling - * - * Revision 1.8 1994/06/11 17:57:07 bentson - * recognize break & parity error - * - * Revision 1.7 1994/06/05 05:51:34 bentson - * Reorder baud table to be monotonic; add cli to CP; discard - * incoming characters and status if the line isn't open; start to - * fold code into cy_throttle; start to port get_serial_info, - * set_serial_info, get_modem_info, set_modem_info, and send_break - * from serial.c; expand cy_ioctl; relocate and expand config_setup; - * get flow control characters from tty struct; invalidate ports w/o - * hardware; - * - * Revision 1.6 1994/05/31 18:42:21 bentson - * add a loop-breaker in the interrupt service routine; - * note when port is initialized so that it can be shut - * down under the right conditions; receive works without - * any obvious errors - * - * Revision 1.5 1994/05/30 00:55:02 bentson - * transmit works without obvious errors - * - * Revision 1.4 1994/05/27 18:46:27 bentson - * incorporated more code from lib_y.c; can now print short - * strings under interrupt control to port zero; seems to - * select ports/channels/lines correctly - * - * Revision 1.3 1994/05/25 22:12:44 bentson - * shifting from multi-port on a card to proper multiplexor - * data structures; added skeletons of most routines - * - * Revision 1.2 1994/05/19 13:21:43 bentson - * start to crib from other sources - * */ -#define CY_VERSION "2.5" +#define CY_VERSION "2.6" /* If you need to install more boards than NR_CARDS, change the constant in the definition below. No other change is necessary to support up to @@ -648,9 +80,7 @@ #include <linux/firmware.h> #include <linux/device.h> -#include <asm/system.h> #include <linux/io.h> -#include <asm/irq.h> #include <linux/uaccess.h> #include <linux/kernel.h> @@ -660,13 +90,11 @@ #include <linux/proc_fs.h> #include <linux/seq_file.h> -static void cy_throttle(struct tty_struct *tty); static void cy_send_xchar(struct tty_struct *tty, char ch); #ifndef SERIAL_XMIT_SIZE #define SERIAL_XMIT_SIZE (min(PAGE_SIZE, 4096)) #endif -#define WAKEUP_CHARS 256 #define STD_COM_FLAGS (0) @@ -756,25 +184,25 @@ static int cy_next_channel; /* next minor available */ * HI VHI * 20 */ -static int baud_table[] = { +static const int baud_table[] = { 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, 9600, 19200, 38400, 57600, 76800, 115200, 150000, 230400, 0 }; -static char baud_co_25[] = { /* 25 MHz clock option table */ +static const char baud_co_25[] = { /* 25 MHz clock option table */ /* value => 00 01 02 03 04 */ /* divide by 8 32 128 512 2048 */ 0x00, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x02, 0x02, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; -static char baud_bpr_25[] = { /* 25 MHz baud rate period table */ +static const char baud_bpr_25[] = { /* 25 MHz baud rate period table */ 0x00, 0xf5, 0xa3, 0x6f, 0x5c, 0x51, 0xf5, 0xa3, 0x51, 0xa3, 0x6d, 0x51, 0xa3, 0x51, 0xa3, 0x51, 0x36, 0x29, 0x1b, 0x15 }; -static char baud_co_60[] = { /* 60 MHz clock option table (CD1400 J) */ +static const char baud_co_60[] = { /* 60 MHz clock option table (CD1400 J) */ /* value => 00 01 02 03 04 */ /* divide by 8 32 128 512 2048 */ 0x00, 0x00, 0x00, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, @@ -782,13 +210,13 @@ static char baud_co_60[] = { /* 60 MHz clock option table (CD1400 J) */ 0x00 }; -static char baud_bpr_60[] = { /* 60 MHz baud rate period table (CD1400 J) */ +static const char baud_bpr_60[] = { /* 60 MHz baud rate period table (CD1400 J) */ 0x00, 0x82, 0x21, 0xff, 0xdb, 0xc3, 0x92, 0x62, 0xc3, 0x62, 0x41, 0xc3, 0x62, 0xc3, 0x62, 0xc3, 0x82, 0x62, 0x41, 0x32, 0x21 }; -static char baud_cor3[] = { /* receive threshold */ +static const char baud_cor3[] = { /* receive threshold */ 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x09, 0x09, 0x08, 0x08, 0x08, 0x08, 0x07, 0x07 @@ -805,7 +233,7 @@ static char baud_cor3[] = { /* receive threshold */ * cables. */ -static char rflow_thr[] = { /* rflow threshold */ +static const char rflow_thr[] = { /* rflow threshold */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a @@ -814,7 +242,7 @@ static char rflow_thr[] = { /* rflow threshold */ /* The Cyclom-Ye has placed the sequential chips in non-sequential * address order. This look-up table overcomes that problem. */ -static int cy_chip_offset[] = { 0x0000, +static const unsigned int cy_chip_offset[] = { 0x0000, 0x0400, 0x0800, 0x0C00, @@ -827,7 +255,7 @@ static int cy_chip_offset[] = { 0x0000, /* PCI related definitions */ #ifdef CONFIG_PCI -static struct pci_device_id cy_pci_dev_id[] __devinitdata = { +static const struct pci_device_id cy_pci_dev_id[] = { /* PCI < 1Mb */ { PCI_DEVICE(PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_CYCLOM_Y_Lo) }, /* PCI > 1Mb */ @@ -850,7 +278,7 @@ MODULE_DEVICE_TABLE(pci, cy_pci_dev_id); #endif static void cy_start(struct tty_struct *); -static void set_line_char(struct cyclades_port *); +static void cy_set_line_char(struct cyclades_port *, struct tty_struct *); static int cyz_issue_cmd(struct cyclades_card *, __u32, __u8, __u32); #ifdef CONFIG_ISA static unsigned detect_isa_irq(void __iomem *); @@ -869,6 +297,20 @@ static void cyz_rx_restart(unsigned long); static struct timer_list cyz_rx_full_timer[NR_PORTS]; #endif /* CONFIG_CYZ_INTR */ +static inline void cyy_writeb(struct cyclades_port *port, u32 reg, u8 val) +{ + struct cyclades_card *card = port->card; + + cy_writeb(port->u.cyy.base_addr + (reg << card->bus_index), val); +} + +static inline u8 cyy_readb(struct cyclades_port *port, u32 reg) +{ + struct cyclades_card *card = port->card; + + return readb(port->u.cyy.base_addr + (reg << card->bus_index)); +} + static inline bool cy_is_Z(struct cyclades_card *card) { return card->num_chips == (unsigned int)-1; @@ -893,7 +335,7 @@ static inline bool cyz_is_loaded(struct cyclades_card *card) } static inline int serial_paranoia_check(struct cyclades_port *info, - char *name, const char *routine) + const char *name, const char *routine) { #ifdef SERIAL_PARANOIA_CHECK if (!info) { @@ -909,7 +351,7 @@ static inline int serial_paranoia_check(struct cyclades_port *info, } #endif return 0; -} /* serial_paranoia_check */ +} /***********************************************************/ /********* Start of block of Cyclom-Y specific code ********/ @@ -921,13 +363,14 @@ static inline int serial_paranoia_check(struct cyclades_port *info, This function is only called from inside spinlock-protected code. */ -static int cyy_issue_cmd(void __iomem *base_addr, u_char cmd, int index) +static int __cyy_issue_cmd(void __iomem *base_addr, u8 cmd, int index) { + void __iomem *ccr = base_addr + (CyCCR << index); unsigned int i; /* Check to see that the previous command has completed */ for (i = 0; i < 100; i++) { - if (readb(base_addr + (CyCCR << index)) == 0) + if (readb(ccr) == 0) break; udelay(10L); } @@ -937,10 +380,16 @@ static int cyy_issue_cmd(void __iomem *base_addr, u_char cmd, int index) return -1; /* Issue the new command */ - cy_writeb(base_addr + (CyCCR << index), cmd); + cy_writeb(ccr, cmd); return 0; -} /* cyy_issue_cmd */ +} + +static inline int cyy_issue_cmd(struct cyclades_port *port, u8 cmd) +{ + return __cyy_issue_cmd(port->u.cyy.base_addr, cmd, + port->card->bus_index); +} #ifdef CONFIG_ISA /* ISA interrupt detection code */ @@ -960,12 +409,12 @@ static unsigned detect_isa_irq(void __iomem *address) irqs = probe_irq_on(); /* Wait ... */ - udelay(5000L); + msleep(5); /* Enable the Tx interrupts on the CD1400 */ local_irq_save(flags); cy_writeb(address + (CyCAR << index), 0); - cyy_issue_cmd(address, CyCHAN_CTL | CyENB_XMTR, index); + __cyy_issue_cmd(address, CyCHAN_CTL | CyENB_XMTR, index); cy_writeb(address + (CyCAR << index), 0); cy_writeb(address + (CySRER << index), @@ -973,7 +422,7 @@ static unsigned detect_isa_irq(void __iomem *address) local_irq_restore(flags); /* Wait ... */ - udelay(5000L); + msleep(5); /* Check which interrupt is in use */ irq = probe_irq_off(irqs); @@ -999,7 +448,7 @@ static void cyy_chip_rx(struct cyclades_card *cinfo, int chip, struct cyclades_port *info; struct tty_struct *tty; int len, index = cinfo->bus_index; - u8 save_xir, channel, save_car, data, char_count; + u8 ivr, save_xir, channel, save_car, data, char_count; #ifdef CY_DEBUG_INTERRUPTS printk(KERN_DEBUG "cyy_interrupt: rcvd intr, chip %d\n", chip); @@ -1008,26 +457,25 @@ static void cyy_chip_rx(struct cyclades_card *cinfo, int chip, save_xir = readb(base_addr + (CyRIR << index)); channel = save_xir & CyIRChannel; info = &cinfo->ports[channel + chip * 4]; - save_car = readb(base_addr + (CyCAR << index)); - cy_writeb(base_addr + (CyCAR << index), save_xir); + save_car = cyy_readb(info, CyCAR); + cyy_writeb(info, CyCAR, save_xir); + ivr = cyy_readb(info, CyRIVR) & CyIVRMask; + tty = tty_port_tty_get(&info->port); /* if there is nowhere to put the data, discard it */ - if (info->port.tty == NULL) { - if ((readb(base_addr + (CyRIVR << index)) & CyIVRMask) == - CyIVRRxEx) { /* exception */ - data = readb(base_addr + (CyRDSR << index)); + if (tty == NULL) { + if (ivr == CyIVRRxEx) { /* exception */ + data = cyy_readb(info, CyRDSR); } else { /* normal character reception */ - char_count = readb(base_addr + (CyRDCR << index)); + char_count = cyy_readb(info, CyRDCR); while (char_count--) - data = readb(base_addr + (CyRDSR << index)); + data = cyy_readb(info, CyRDSR); } goto end; } /* there is an open port for this data */ - tty = info->port.tty; - if ((readb(base_addr + (CyRIVR << index)) & CyIVRMask) == - CyIVRRxEx) { /* exception */ - data = readb(base_addr + (CyRDSR << index)); + if (ivr == CyIVRRxEx) { /* exception */ + data = cyy_readb(info, CyRDSR); /* For statistics only */ if (data & CyBREAK) @@ -1041,28 +489,29 @@ static void cyy_chip_rx(struct cyclades_card *cinfo, int chip, if (data & info->ignore_status_mask) { info->icount.rx++; + tty_kref_put(tty); return; } if (tty_buffer_request_room(tty, 1)) { if (data & info->read_status_mask) { if (data & CyBREAK) { tty_insert_flip_char(tty, - readb(base_addr + (CyRDSR << - index)), TTY_BREAK); + cyy_readb(info, CyRDSR), + TTY_BREAK); info->icount.rx++; if (info->port.flags & ASYNC_SAK) do_SAK(tty); } else if (data & CyFRAME) { tty_insert_flip_char(tty, - readb(base_addr + (CyRDSR << - index)), TTY_FRAME); + cyy_readb(info, CyRDSR), + TTY_FRAME); info->icount.rx++; info->idle_stats.frame_errs++; } else if (data & CyPARITY) { /* Pieces of seven... */ tty_insert_flip_char(tty, - readb(base_addr + (CyRDSR << - index)), TTY_PARITY); + cyy_readb(info, CyRDSR), + TTY_PARITY); info->icount.rx++; info->idle_stats.parity_errs++; } else if (data & CyOVERRUN) { @@ -1074,8 +523,8 @@ static void cyy_chip_rx(struct cyclades_card *cinfo, int chip, the next incoming character. */ tty_insert_flip_char(tty, - readb(base_addr + (CyRDSR << - index)), TTY_FRAME); + cyy_readb(info, CyRDSR), + TTY_FRAME); info->icount.rx++; info->idle_stats.overruns++; /* These two conditions may imply */ @@ -1099,7 +548,7 @@ static void cyy_chip_rx(struct cyclades_card *cinfo, int chip, } } else { /* normal character reception */ /* load # chars available from the chip */ - char_count = readb(base_addr + (CyRDCR << index)); + char_count = cyy_readb(info, CyRDCR); #ifdef CY_ENABLE_MONITORING ++info->mon.int_count; @@ -1110,7 +559,7 @@ static void cyy_chip_rx(struct cyclades_card *cinfo, int chip, #endif len = tty_buffer_request_room(tty, char_count); while (len--) { - data = readb(base_addr + (CyRDSR << index)); + data = cyy_readb(info, CyRDSR); tty_insert_flip_char(tty, data, TTY_NORMAL); info->idle_stats.recv_bytes++; info->icount.rx++; @@ -1121,16 +570,18 @@ static void cyy_chip_rx(struct cyclades_card *cinfo, int chip, info->idle_stats.recv_idle = jiffies; } tty_schedule_flip(tty); + tty_kref_put(tty); end: /* end of service */ - cy_writeb(base_addr + (CyRIR << index), save_xir & 0x3f); - cy_writeb(base_addr + (CyCAR << index), save_car); + cyy_writeb(info, CyRIR, save_xir & 0x3f); + cyy_writeb(info, CyCAR, save_car); } static void cyy_chip_tx(struct cyclades_card *cinfo, unsigned int chip, void __iomem *base_addr) { struct cyclades_port *info; + struct tty_struct *tty; int char_count, index = cinfo->bus_index; u8 save_xir, channel, save_car, outch; @@ -1154,9 +605,9 @@ static void cyy_chip_tx(struct cyclades_card *cinfo, unsigned int chip, goto end; } info = &cinfo->ports[channel + chip * 4]; - if (info->port.tty == NULL) { - cy_writeb(base_addr + (CySRER << index), - readb(base_addr + (CySRER << index)) & ~CyTxRdy); + tty = tty_port_tty_get(&info->port); + if (tty == NULL) { + cyy_writeb(info, CySRER, cyy_readb(info, CySRER) & ~CyTxRdy); goto end; } @@ -1165,7 +616,7 @@ static void cyy_chip_tx(struct cyclades_card *cinfo, unsigned int chip, if (info->x_char) { /* send special char */ outch = info->x_char; - cy_writeb(base_addr + (CyTDR << index), outch); + cyy_writeb(info, CyTDR, outch); char_count--; info->icount.tx++; info->x_char = 0; @@ -1173,14 +624,14 @@ static void cyy_chip_tx(struct cyclades_card *cinfo, unsigned int chip, if (info->breakon || info->breakoff) { if (info->breakon) { - cy_writeb(base_addr + (CyTDR << index), 0); - cy_writeb(base_addr + (CyTDR << index), 0x81); + cyy_writeb(info, CyTDR, 0); + cyy_writeb(info, CyTDR, 0x81); info->breakon = 0; char_count -= 2; } if (info->breakoff) { - cy_writeb(base_addr + (CyTDR << index), 0); - cy_writeb(base_addr + (CyTDR << index), 0x83); + cyy_writeb(info, CyTDR, 0); + cyy_writeb(info, CyTDR, 0x83); info->breakoff = 0; char_count -= 2; } @@ -1188,27 +639,23 @@ static void cyy_chip_tx(struct cyclades_card *cinfo, unsigned int chip, while (char_count-- > 0) { if (!info->xmit_cnt) { - if (readb(base_addr + (CySRER << index)) & CyTxMpty) { - cy_writeb(base_addr + (CySRER << index), - readb(base_addr + (CySRER << index)) & - ~CyTxMpty); + if (cyy_readb(info, CySRER) & CyTxMpty) { + cyy_writeb(info, CySRER, + cyy_readb(info, CySRER) & ~CyTxMpty); } else { - cy_writeb(base_addr + (CySRER << index), - (readb(base_addr + (CySRER << index)) & - ~CyTxRdy) | CyTxMpty); + cyy_writeb(info, CySRER, CyTxMpty | + (cyy_readb(info, CySRER) & ~CyTxRdy)); } goto done; } if (info->port.xmit_buf == NULL) { - cy_writeb(base_addr + (CySRER << index), - readb(base_addr + (CySRER << index)) & - ~CyTxRdy); + cyy_writeb(info, CySRER, + cyy_readb(info, CySRER) & ~CyTxRdy); goto done; } - if (info->port.tty->stopped || info->port.tty->hw_stopped) { - cy_writeb(base_addr + (CySRER << index), - readb(base_addr + (CySRER << index)) & - ~CyTxRdy); + if (tty->stopped || tty->hw_stopped) { + cyy_writeb(info, CySRER, + cyy_readb(info, CySRER) & ~CyTxRdy); goto done; } /* Because the Embedded Transmit Commands have been enabled, @@ -1225,15 +672,15 @@ static void cyy_chip_tx(struct cyclades_card *cinfo, unsigned int chip, info->xmit_cnt--; info->xmit_tail = (info->xmit_tail + 1) & (SERIAL_XMIT_SIZE - 1); - cy_writeb(base_addr + (CyTDR << index), outch); + cyy_writeb(info, CyTDR, outch); info->icount.tx++; } else { if (char_count > 1) { info->xmit_cnt--; info->xmit_tail = (info->xmit_tail + 1) & (SERIAL_XMIT_SIZE - 1); - cy_writeb(base_addr + (CyTDR << index), outch); - cy_writeb(base_addr + (CyTDR << index), 0); + cyy_writeb(info, CyTDR, outch); + cyy_writeb(info, CyTDR, 0); info->icount.tx++; char_count--; } @@ -1241,17 +688,19 @@ static void cyy_chip_tx(struct cyclades_card *cinfo, unsigned int chip, } done: - tty_wakeup(info->port.tty); + tty_wakeup(tty); + tty_kref_put(tty); end: /* end of service */ - cy_writeb(base_addr + (CyTIR << index), save_xir & 0x3f); - cy_writeb(base_addr + (CyCAR << index), save_car); + cyy_writeb(info, CyTIR, save_xir & 0x3f); + cyy_writeb(info, CyCAR, save_car); } static void cyy_chip_modem(struct cyclades_card *cinfo, int chip, void __iomem *base_addr) { struct cyclades_port *info; + struct tty_struct *tty; int index = cinfo->bus_index; u8 save_xir, channel, save_car, mdm_change, mdm_status; @@ -1259,13 +708,14 @@ static void cyy_chip_modem(struct cyclades_card *cinfo, int chip, save_xir = readb(base_addr + (CyMIR << index)); channel = save_xir & CyIRChannel; info = &cinfo->ports[channel + chip * 4]; - save_car = readb(base_addr + (CyCAR << index)); - cy_writeb(base_addr + (CyCAR << index), save_xir); + save_car = cyy_readb(info, CyCAR); + cyy_writeb(info, CyCAR, save_xir); - mdm_change = readb(base_addr + (CyMISR << index)); - mdm_status = readb(base_addr + (CyMSVR1 << index)); + mdm_change = cyy_readb(info, CyMISR); + mdm_status = cyy_readb(info, CyMSVR1); - if (!info->port.tty) + tty = tty_port_tty_get(&info->port); + if (!tty) goto end; if (mdm_change & CyANY_DELTA) { @@ -1279,35 +729,32 @@ static void cyy_chip_modem(struct cyclades_card *cinfo, int chip, if (mdm_change & CyRI) info->icount.rng++; - wake_up_interruptible(&info->delta_msr_wait); + wake_up_interruptible(&info->port.delta_msr_wait); } if ((mdm_change & CyDCD) && (info->port.flags & ASYNC_CHECK_CD)) { - if (!(mdm_status & CyDCD)) { - tty_hangup(info->port.tty); - info->port.flags &= ~ASYNC_NORMAL_ACTIVE; - } - wake_up_interruptible(&info->port.open_wait); + if (mdm_status & CyDCD) + wake_up_interruptible(&info->port.open_wait); + else + tty_hangup(tty); } if ((mdm_change & CyCTS) && (info->port.flags & ASYNC_CTS_FLOW)) { - if (info->port.tty->hw_stopped) { + if (tty->hw_stopped) { if (mdm_status & CyCTS) { /* cy_start isn't used because... !!! */ - info->port.tty->hw_stopped = 0; - cy_writeb(base_addr + (CySRER << index), - readb(base_addr + (CySRER << index)) | - CyTxRdy); - tty_wakeup(info->port.tty); + tty->hw_stopped = 0; + cyy_writeb(info, CySRER, + cyy_readb(info, CySRER) | CyTxRdy); + tty_wakeup(tty); } } else { if (!(mdm_status & CyCTS)) { /* cy_stop isn't used because ... !!! */ - info->port.tty->hw_stopped = 1; - cy_writeb(base_addr + (CySRER << index), - readb(base_addr + (CySRER << index)) & - ~CyTxRdy); + tty->hw_stopped = 1; + cyy_writeb(info, CySRER, + cyy_readb(info, CySRER) & ~CyTxRdy); } } } @@ -1315,10 +762,11 @@ static void cyy_chip_modem(struct cyclades_card *cinfo, int chip, } if (mdm_change & CyRI) { }*/ + tty_kref_put(tty); end: /* end of service */ - cy_writeb(base_addr + (CyMIR << index), save_xir & 0x3f); - cy_writeb(base_addr + (CyCAR << index), save_car); + cyy_writeb(info, CyMIR, save_xir & 0x3f); + cyy_writeb(info, CyCAR, save_car); } /* The real interrupt service routine is called @@ -1389,6 +837,56 @@ static irqreturn_t cyy_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } /* cyy_interrupt */ +static void cyy_change_rts_dtr(struct cyclades_port *info, unsigned int set, + unsigned int clear) +{ + struct cyclades_card *card = info->card; + int channel = info->line - card->first_line; + u32 rts, dtr, msvrr, msvrd; + + channel &= 0x03; + + if (info->rtsdtr_inv) { + msvrr = CyMSVR2; + msvrd = CyMSVR1; + rts = CyDTR; + dtr = CyRTS; + } else { + msvrr = CyMSVR1; + msvrd = CyMSVR2; + rts = CyRTS; + dtr = CyDTR; + } + if (set & TIOCM_RTS) { + cyy_writeb(info, CyCAR, channel); + cyy_writeb(info, msvrr, rts); + } + if (clear & TIOCM_RTS) { + cyy_writeb(info, CyCAR, channel); + cyy_writeb(info, msvrr, ~rts); + } + if (set & TIOCM_DTR) { + cyy_writeb(info, CyCAR, channel); + cyy_writeb(info, msvrd, dtr); +#ifdef CY_DEBUG_DTR + printk(KERN_DEBUG "cyc:set_modem_info raising DTR\n"); + printk(KERN_DEBUG " status: 0x%x, 0x%x\n", + cyy_readb(info, CyMSVR1), + cyy_readb(info, CyMSVR2)); +#endif + } + if (clear & TIOCM_DTR) { + cyy_writeb(info, CyCAR, channel); + cyy_writeb(info, msvrd, ~dtr); +#ifdef CY_DEBUG_DTR + printk(KERN_DEBUG "cyc:set_modem_info dropping DTR\n"); + printk(KERN_DEBUG " status: 0x%x, 0x%x\n", + cyy_readb(info, CyMSVR1), + cyy_readb(info, CyMSVR2)); +#endif + } +} + /***********************************************************/ /********* End of block of Cyclom-Y specific code **********/ /******** Start of block of Cyclades-Z specific code *******/ @@ -1398,15 +896,9 @@ static int cyz_fetch_msg(struct cyclades_card *cinfo, __u32 *channel, __u8 *cmd, __u32 *param) { - struct FIRM_ID __iomem *firm_id; - struct ZFW_CTRL __iomem *zfw_ctrl; - struct BOARD_CTRL __iomem *board_ctrl; + struct BOARD_CTRL __iomem *board_ctrl = cinfo->board_ctrl; unsigned long loc_doorbell; - firm_id = cinfo->base_addr + ID_ADDRESS; - zfw_ctrl = cinfo->base_addr + (readl(&firm_id->zfwctrl_addr) & 0xfffff); - board_ctrl = &zfw_ctrl->board_ctrl; - loc_doorbell = readl(&cinfo->ctl_addr.p9060->loc_doorbell); if (loc_doorbell) { *cmd = (char)(0xff & loc_doorbell); @@ -1422,19 +914,13 @@ static int cyz_issue_cmd(struct cyclades_card *cinfo, __u32 channel, __u8 cmd, __u32 param) { - struct FIRM_ID __iomem *firm_id; - struct ZFW_CTRL __iomem *zfw_ctrl; - struct BOARD_CTRL __iomem *board_ctrl; + struct BOARD_CTRL __iomem *board_ctrl = cinfo->board_ctrl; __u32 __iomem *pci_doorbell; unsigned int index; - firm_id = cinfo->base_addr + ID_ADDRESS; if (!cyz_is_loaded(cinfo)) return -1; - zfw_ctrl = cinfo->base_addr + (readl(&firm_id->zfwctrl_addr) & 0xfffff); - board_ctrl = &zfw_ctrl->board_ctrl; - index = 0; pci_doorbell = &cinfo->ctl_addr.p9060->pci_doorbell; while ((readl(pci_doorbell) & 0xff) != 0) { @@ -1449,11 +935,10 @@ cyz_issue_cmd(struct cyclades_card *cinfo, return 0; } /* cyz_issue_cmd */ -static void cyz_handle_rx(struct cyclades_port *info, - struct BUF_CTRL __iomem *buf_ctrl) +static void cyz_handle_rx(struct cyclades_port *info, struct tty_struct *tty) { + struct BUF_CTRL __iomem *buf_ctrl = info->u.cyz.buf_ctrl; struct cyclades_card *cinfo = info->card; - struct tty_struct *tty = info->port.tty; unsigned int char_count; int len; #ifdef BLOCKMOVE @@ -1542,11 +1027,10 @@ static void cyz_handle_rx(struct cyclades_port *info, } } -static void cyz_handle_tx(struct cyclades_port *info, - struct BUF_CTRL __iomem *buf_ctrl) +static void cyz_handle_tx(struct cyclades_port *info, struct tty_struct *tty) { + struct BUF_CTRL __iomem *buf_ctrl = info->u.cyz.buf_ctrl; struct cyclades_card *cinfo = info->card; - struct tty_struct *tty = info->port.tty; u8 data; unsigned int char_count; #ifdef BLOCKMOVE @@ -1621,34 +1105,24 @@ ztxdone: static void cyz_handle_cmd(struct cyclades_card *cinfo) { + struct BOARD_CTRL __iomem *board_ctrl = cinfo->board_ctrl; struct tty_struct *tty; struct cyclades_port *info; - static struct FIRM_ID __iomem *firm_id; - static struct ZFW_CTRL __iomem *zfw_ctrl; - static struct BOARD_CTRL __iomem *board_ctrl; - static struct CH_CTRL __iomem *ch_ctrl; - static struct BUF_CTRL __iomem *buf_ctrl; __u32 channel, param, fw_ver; __u8 cmd; int special_count; int delta_count; - firm_id = cinfo->base_addr + ID_ADDRESS; - zfw_ctrl = cinfo->base_addr + (readl(&firm_id->zfwctrl_addr) & 0xfffff); - board_ctrl = &zfw_ctrl->board_ctrl; fw_ver = readl(&board_ctrl->fw_version); while (cyz_fetch_msg(cinfo, &channel, &cmd, ¶m) == 1) { special_count = 0; delta_count = 0; info = &cinfo->ports[channel]; - tty = info->port.tty; + tty = tty_port_tty_get(&info->port); if (tty == NULL) continue; - ch_ctrl = &(zfw_ctrl->ch_ctrl[channel]); - buf_ctrl = &(zfw_ctrl->buf_ctrl[channel]); - switch (cmd) { case C_CM_PR_ERROR: tty_insert_flip_char(tty, 0, TTY_PARITY); @@ -1669,15 +1143,12 @@ static void cyz_handle_cmd(struct cyclades_card *cinfo) info->icount.dcd++; delta_count++; if (info->port.flags & ASYNC_CHECK_CD) { - if ((fw_ver > 241 ? ((u_long) param) : - readl(&ch_ctrl->rs_status)) & - C_RS_DCD) { + u32 dcd = fw_ver > 241 ? param : + readl(&info->u.cyz.ch_ctrl->rs_status); + if (dcd & C_RS_DCD) wake_up_interruptible(&info->port.open_wait); - } else { - tty_hangup(info->port.tty); - wake_up_interruptible(&info->port.open_wait); - info->port.flags &= ~ASYNC_NORMAL_ACTIVE; - } + else + tty_hangup(tty); } break; case C_CM_MCTS: @@ -1706,7 +1177,7 @@ static void cyz_handle_cmd(struct cyclades_card *cinfo) printk(KERN_DEBUG "cyz_interrupt: rcvd intr, card %d, " "port %ld\n", info->card, channel); #endif - cyz_handle_rx(info, buf_ctrl); + cyz_handle_rx(info, tty); break; case C_CM_TXBEMPTY: case C_CM_TXLOWWM: @@ -1716,7 +1187,7 @@ static void cyz_handle_cmd(struct cyclades_card *cinfo) printk(KERN_DEBUG "cyz_interrupt: xmit intr, card %d, " "port %ld\n", info->card, channel); #endif - cyz_handle_tx(info, buf_ctrl); + cyz_handle_tx(info, tty); break; #endif /* CONFIG_CYZ_INTR */ case C_CM_FATAL: @@ -1726,9 +1197,10 @@ static void cyz_handle_cmd(struct cyclades_card *cinfo) break; } if (delta_count) - wake_up_interruptible(&info->delta_msr_wait); + wake_up_interruptible(&info->port.delta_msr_wait); if (special_count) tty_schedule_flip(tty); + tty_kref_put(tty); } } @@ -1774,10 +1246,6 @@ static void cyz_poll(unsigned long arg) { struct cyclades_card *cinfo; struct cyclades_port *info; - struct tty_struct *tty; - struct FIRM_ID __iomem *firm_id; - struct ZFW_CTRL __iomem *zfw_ctrl; - struct BUF_CTRL __iomem *buf_ctrl; unsigned long expires = jiffies + HZ; unsigned int port, card; @@ -1789,10 +1257,6 @@ static void cyz_poll(unsigned long arg) if (!cyz_is_loaded(cinfo)) continue; - firm_id = cinfo->base_addr + ID_ADDRESS; - zfw_ctrl = cinfo->base_addr + - (readl(&firm_id->zfwctrl_addr) & 0xfffff); - /* Skip first polling cycle to avoid racing conditions with the FW */ if (!cinfo->intr_enabled) { cinfo->intr_enabled = 1; @@ -1802,13 +1266,17 @@ static void cyz_poll(unsigned long arg) cyz_handle_cmd(cinfo); for (port = 0; port < cinfo->nports; port++) { + struct tty_struct *tty; + info = &cinfo->ports[port]; - tty = info->port.tty; - buf_ctrl = &(zfw_ctrl->buf_ctrl[port]); + tty = tty_port_tty_get(&info->port); + /* OK to pass NULL to the handle functions below. + They need to drop the data in that case. */ if (!info->throttle) - cyz_handle_rx(info, buf_ctrl); - cyz_handle_tx(info, buf_ctrl); + cyz_handle_rx(info, tty); + cyz_handle_tx(info, tty); + tty_kref_put(tty); } /* poll every 'cyz_polling_cycle' period */ expires = jiffies + cyz_polling_cycle; @@ -1824,13 +1292,12 @@ static void cyz_poll(unsigned long arg) /* This is called whenever a port becomes active; interrupts are enabled and DTR & RTS are turned on. */ -static int startup(struct cyclades_port *info) +static int cy_startup(struct cyclades_port *info, struct tty_struct *tty) { struct cyclades_card *card; unsigned long flags; int retval = 0; - void __iomem *base_addr; - int chip, channel, index; + int channel; unsigned long page; card = info->card; @@ -1842,15 +1309,11 @@ static int startup(struct cyclades_port *info) spin_lock_irqsave(&card->card_lock, flags); - if (info->port.flags & ASYNC_INITIALIZED) { - free_page(page); + if (info->port.flags & ASYNC_INITIALIZED) goto errout; - } if (!info->type) { - if (info->port.tty) - set_bit(TTY_IO_ERROR, &info->port.tty->flags); - free_page(page); + set_bit(TTY_IO_ERROR, &tty->flags); goto errout; } @@ -1861,96 +1324,53 @@ static int startup(struct cyclades_port *info) spin_unlock_irqrestore(&card->card_lock, flags); - set_line_char(info); + cy_set_line_char(info, tty); if (!cy_is_Z(card)) { - chip = channel >> 2; channel &= 0x03; - index = card->bus_index; - base_addr = card->base_addr + (cy_chip_offset[chip] << index); -#ifdef CY_DEBUG_OPEN - printk(KERN_DEBUG "cyc startup card %d, chip %d, channel %d, " - "base_addr %p\n", - card, chip, channel, base_addr); -#endif spin_lock_irqsave(&card->card_lock, flags); - cy_writeb(base_addr + (CyCAR << index), (u_char) channel); + cyy_writeb(info, CyCAR, channel); - cy_writeb(base_addr + (CyRTPR << index), + cyy_writeb(info, CyRTPR, (info->default_timeout ? info->default_timeout : 0x02)); /* 10ms rx timeout */ - cyy_issue_cmd(base_addr, CyCHAN_CTL | CyENB_RCVR | CyENB_XMTR, - index); - - cy_writeb(base_addr + (CyCAR << index), (u_char) channel); - cy_writeb(base_addr + (CyMSVR1 << index), CyRTS); - cy_writeb(base_addr + (CyMSVR2 << index), CyDTR); - -#ifdef CY_DEBUG_DTR - printk(KERN_DEBUG "cyc:startup raising DTR\n"); - printk(KERN_DEBUG " status: 0x%x, 0x%x\n", - readb(base_addr + (CyMSVR1 << index)), - readb(base_addr + (CyMSVR2 << index))); -#endif - - cy_writeb(base_addr + (CySRER << index), - readb(base_addr + (CySRER << index)) | CyRxData); - info->port.flags |= ASYNC_INITIALIZED; - - if (info->port.tty) - clear_bit(TTY_IO_ERROR, &info->port.tty->flags); - info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; - info->breakon = info->breakoff = 0; - memset((char *)&info->idle_stats, 0, sizeof(info->idle_stats)); - info->idle_stats.in_use = - info->idle_stats.recv_idle = - info->idle_stats.xmit_idle = jiffies; + cyy_issue_cmd(info, CyCHAN_CTL | CyENB_RCVR | CyENB_XMTR); - spin_unlock_irqrestore(&card->card_lock, flags); + cyy_change_rts_dtr(info, TIOCM_RTS | TIOCM_DTR, 0); + cyy_writeb(info, CySRER, cyy_readb(info, CySRER) | CyRxData); } else { - struct FIRM_ID __iomem *firm_id; - struct ZFW_CTRL __iomem *zfw_ctrl; - struct BOARD_CTRL __iomem *board_ctrl; - struct CH_CTRL __iomem *ch_ctrl; + struct CH_CTRL __iomem *ch_ctrl = info->u.cyz.ch_ctrl; - base_addr = card->base_addr; - - firm_id = base_addr + ID_ADDRESS; if (!cyz_is_loaded(card)) return -ENODEV; - zfw_ctrl = card->base_addr + - (readl(&firm_id->zfwctrl_addr) & 0xfffff); - board_ctrl = &zfw_ctrl->board_ctrl; - ch_ctrl = zfw_ctrl->ch_ctrl; - #ifdef CY_DEBUG_OPEN printk(KERN_DEBUG "cyc startup Z card %d, channel %d, " - "base_addr %p\n", card, channel, base_addr); + "base_addr %p\n", card, channel, card->base_addr); #endif spin_lock_irqsave(&card->card_lock, flags); - cy_writel(&ch_ctrl[channel].op_mode, C_CH_ENABLE); + cy_writel(&ch_ctrl->op_mode, C_CH_ENABLE); #ifdef Z_WAKE #ifdef CONFIG_CYZ_INTR - cy_writel(&ch_ctrl[channel].intr_enable, + cy_writel(&ch_ctrl->intr_enable, C_IN_TXBEMPTY | C_IN_TXLOWWM | C_IN_RXHIWM | C_IN_RXNNDT | C_IN_IOCTLW | C_IN_MDCD); #else - cy_writel(&ch_ctrl[channel].intr_enable, + cy_writel(&ch_ctrl->intr_enable, C_IN_IOCTLW | C_IN_MDCD); #endif /* CONFIG_CYZ_INTR */ #else #ifdef CONFIG_CYZ_INTR - cy_writel(&ch_ctrl[channel].intr_enable, + cy_writel(&ch_ctrl->intr_enable, C_IN_TXBEMPTY | C_IN_TXLOWWM | C_IN_RXHIWM | C_IN_RXNNDT | C_IN_MDCD); #else - cy_writel(&ch_ctrl[channel].intr_enable, C_IN_MDCD); + cy_writel(&ch_ctrl->intr_enable, C_IN_MDCD); #endif /* CONFIG_CYZ_INTR */ #endif /* Z_WAKE */ @@ -1969,32 +1389,22 @@ static int startup(struct cyclades_port *info) /* set timeout !!! */ /* set RTS and DTR !!! */ - cy_writel(&ch_ctrl[channel].rs_control, - readl(&ch_ctrl[channel].rs_control) | C_RS_RTS | - C_RS_DTR); - retval = cyz_issue_cmd(card, channel, C_CM_IOCTLM, 0L); - if (retval != 0) { - printk(KERN_ERR "cyc:startup(3) retval on ttyC%d was " - "%x\n", info->line, retval); - } -#ifdef CY_DEBUG_DTR - printk(KERN_DEBUG "cyc:startup raising Z DTR\n"); -#endif + tty_port_raise_dtr_rts(&info->port); /* enable send, recv, modem !!! */ + } - info->port.flags |= ASYNC_INITIALIZED; - if (info->port.tty) - clear_bit(TTY_IO_ERROR, &info->port.tty->flags); - info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; - info->breakon = info->breakoff = 0; - memset((char *)&info->idle_stats, 0, sizeof(info->idle_stats)); - info->idle_stats.in_use = - info->idle_stats.recv_idle = - info->idle_stats.xmit_idle = jiffies; + info->port.flags |= ASYNC_INITIALIZED; - spin_unlock_irqrestore(&card->card_lock, flags); - } + clear_bit(TTY_IO_ERROR, &tty->flags); + info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; + info->breakon = info->breakoff = 0; + memset((char *)&info->idle_stats, 0, sizeof(info->idle_stats)); + info->idle_stats.in_use = + info->idle_stats.recv_idle = + info->idle_stats.xmit_idle = jiffies; + + spin_unlock_irqrestore(&card->card_lock, flags); #ifdef CY_DEBUG_OPEN printk(KERN_DEBUG "cyc startup done\n"); @@ -2003,28 +1413,20 @@ static int startup(struct cyclades_port *info) errout: spin_unlock_irqrestore(&card->card_lock, flags); + free_page(page); return retval; } /* startup */ static void start_xmit(struct cyclades_port *info) { - struct cyclades_card *card; + struct cyclades_card *card = info->card; unsigned long flags; - void __iomem *base_addr; - int chip, channel, index; + int channel = info->line - card->first_line; - card = info->card; - channel = info->line - card->first_line; if (!cy_is_Z(card)) { - chip = channel >> 2; - channel &= 0x03; - index = card->bus_index; - base_addr = card->base_addr + (cy_chip_offset[chip] << index); - spin_lock_irqsave(&card->card_lock, flags); - cy_writeb(base_addr + (CyCAR << index), channel); - cy_writeb(base_addr + (CySRER << index), - readb(base_addr + (CySRER << index)) | CyTxRdy); + cyy_writeb(info, CyCAR, channel & 0x03); + cyy_writeb(info, CySRER, cyy_readb(info, CySRER) | CyTxRdy); spin_unlock_irqrestore(&card->card_lock, flags); } else { #ifdef CONFIG_CYZ_INTR @@ -2047,12 +1449,11 @@ static void start_xmit(struct cyclades_port *info) * This routine shuts down a serial port; interrupts are disabled, * and DTR is dropped if the hangup on close termio flag is on. */ -static void shutdown(struct cyclades_port *info) +static void cy_shutdown(struct cyclades_port *info, struct tty_struct *tty) { struct cyclades_card *card; unsigned long flags; - void __iomem *base_addr; - int chip, channel, index; + int channel; if (!(info->port.flags & ASYNC_INITIALIZED)) return; @@ -2060,21 +1461,10 @@ static void shutdown(struct cyclades_port *info) card = info->card; channel = info->line - card->first_line; if (!cy_is_Z(card)) { - chip = channel >> 2; - channel &= 0x03; - index = card->bus_index; - base_addr = card->base_addr + (cy_chip_offset[chip] << index); - -#ifdef CY_DEBUG_OPEN - printk(KERN_DEBUG "cyc shutdown Y card %d, chip %d, " - "channel %d, base_addr %p\n", - card, chip, channel, base_addr); -#endif - spin_lock_irqsave(&card->card_lock, flags); /* Clear delta_msr_wait queue to avoid mem leaks. */ - wake_up_interruptible(&info->delta_msr_wait); + wake_up_interruptible(&info->port.delta_msr_wait); if (info->port.xmit_buf) { unsigned char *temp; @@ -2082,47 +1472,25 @@ static void shutdown(struct cyclades_port *info) info->port.xmit_buf = NULL; free_page((unsigned long)temp); } - cy_writeb(base_addr + (CyCAR << index), (u_char) channel); - if (!info->port.tty || (info->port.tty->termios->c_cflag & HUPCL)) { - cy_writeb(base_addr + (CyMSVR1 << index), ~CyRTS); - cy_writeb(base_addr + (CyMSVR2 << index), ~CyDTR); -#ifdef CY_DEBUG_DTR - printk(KERN_DEBUG "cyc shutdown dropping DTR\n"); - printk(KERN_DEBUG " status: 0x%x, 0x%x\n", - readb(base_addr + (CyMSVR1 << index)), - readb(base_addr + (CyMSVR2 << index))); -#endif - } - cyy_issue_cmd(base_addr, CyCHAN_CTL | CyDIS_RCVR, index); + if (tty->termios->c_cflag & HUPCL) + cyy_change_rts_dtr(info, 0, TIOCM_RTS | TIOCM_DTR); + + cyy_issue_cmd(info, CyCHAN_CTL | CyDIS_RCVR); /* it may be appropriate to clear _XMIT at some later date (after testing)!!! */ - if (info->port.tty) - set_bit(TTY_IO_ERROR, &info->port.tty->flags); + set_bit(TTY_IO_ERROR, &tty->flags); info->port.flags &= ~ASYNC_INITIALIZED; spin_unlock_irqrestore(&card->card_lock, flags); } else { - struct FIRM_ID __iomem *firm_id; - struct ZFW_CTRL __iomem *zfw_ctrl; - struct BOARD_CTRL __iomem *board_ctrl; - struct CH_CTRL __iomem *ch_ctrl; - int retval; - - base_addr = card->base_addr; #ifdef CY_DEBUG_OPEN printk(KERN_DEBUG "cyc shutdown Z card %d, channel %d, " - "base_addr %p\n", card, channel, base_addr); + "base_addr %p\n", card, channel, card->base_addr); #endif - firm_id = base_addr + ID_ADDRESS; if (!cyz_is_loaded(card)) return; - zfw_ctrl = card->base_addr + - (readl(&firm_id->zfwctrl_addr) & 0xfffff); - board_ctrl = &zfw_ctrl->board_ctrl; - ch_ctrl = zfw_ctrl->ch_ctrl; - spin_lock_irqsave(&card->card_lock, flags); if (info->port.xmit_buf) { @@ -2132,23 +1500,10 @@ static void shutdown(struct cyclades_port *info) free_page((unsigned long)temp); } - if (!info->port.tty || (info->port.tty->termios->c_cflag & HUPCL)) { - cy_writel(&ch_ctrl[channel].rs_control, - (__u32)(readl(&ch_ctrl[channel].rs_control) & - ~(C_RS_RTS | C_RS_DTR))); - retval = cyz_issue_cmd(info->card, channel, - C_CM_IOCTLM, 0L); - if (retval != 0) { - printk(KERN_ERR"cyc:shutdown retval on ttyC%d " - "was %x\n", info->line, retval); - } -#ifdef CY_DEBUG_DTR - printk(KERN_DEBUG "cyc:shutdown dropping Z DTR\n"); -#endif - } + if (tty->termios->c_cflag & HUPCL) + tty_port_lower_dtr_rts(&info->port); - if (info->port.tty) - set_bit(TTY_IO_ERROR, &info->port.tty->flags); + set_bit(TTY_IO_ERROR, &tty->flags); info->port.flags &= ~ASYNC_INITIALIZED; spin_unlock_irqrestore(&card->card_lock, flags); @@ -2165,199 +1520,6 @@ static void shutdown(struct cyclades_port *info) * ------------------------------------------------------------ */ -static int -block_til_ready(struct tty_struct *tty, struct file *filp, - struct cyclades_port *info) -{ - DECLARE_WAITQUEUE(wait, current); - struct cyclades_card *cinfo; - unsigned long flags; - int chip, channel, index; - int retval; - void __iomem *base_addr; - - cinfo = info->card; - channel = info->line - cinfo->first_line; - - /* - * If the device is in the middle of being closed, then block - * until it's done, and then try again. - */ - if (tty_hung_up_p(filp) || (info->port.flags & ASYNC_CLOSING)) { - wait_event_interruptible(info->port.close_wait, - !(info->port.flags & ASYNC_CLOSING)); - return (info->port.flags & ASYNC_HUP_NOTIFY) ? -EAGAIN: -ERESTARTSYS; - } - - /* - * If non-blocking mode is set, then make the check up front - * and then exit. - */ - if ((filp->f_flags & O_NONBLOCK) || - (tty->flags & (1 << TTY_IO_ERROR))) { - info->port.flags |= ASYNC_NORMAL_ACTIVE; - return 0; - } - - /* - * Block waiting for the carrier detect and the line to become - * free (i.e., not in use by the callout). While we are in - * this loop, info->port.count is dropped by one, so that - * cy_close() knows when to free things. We restore it upon - * exit, either normal or abnormal. - */ - retval = 0; - add_wait_queue(&info->port.open_wait, &wait); -#ifdef CY_DEBUG_OPEN - printk(KERN_DEBUG "cyc block_til_ready before block: ttyC%d, " - "count = %d\n", info->line, info->port.count); -#endif - spin_lock_irqsave(&cinfo->card_lock, flags); - if (!tty_hung_up_p(filp)) - info->port.count--; - spin_unlock_irqrestore(&cinfo->card_lock, flags); -#ifdef CY_DEBUG_COUNT - printk(KERN_DEBUG "cyc block_til_ready: (%d): decrementing count to " - "%d\n", current->pid, info->port.count); -#endif - info->port.blocked_open++; - - if (!cy_is_Z(cinfo)) { - chip = channel >> 2; - channel &= 0x03; - index = cinfo->bus_index; - base_addr = cinfo->base_addr + (cy_chip_offset[chip] << index); - - while (1) { - spin_lock_irqsave(&cinfo->card_lock, flags); - if ((tty->termios->c_cflag & CBAUD)) { - cy_writeb(base_addr + (CyCAR << index), - (u_char) channel); - cy_writeb(base_addr + (CyMSVR1 << index), - CyRTS); - cy_writeb(base_addr + (CyMSVR2 << index), - CyDTR); -#ifdef CY_DEBUG_DTR - printk(KERN_DEBUG "cyc:block_til_ready raising " - "DTR\n"); - printk(KERN_DEBUG " status: 0x%x, 0x%x\n", - readb(base_addr + (CyMSVR1 << index)), - readb(base_addr + (CyMSVR2 << index))); -#endif - } - spin_unlock_irqrestore(&cinfo->card_lock, flags); - - set_current_state(TASK_INTERRUPTIBLE); - if (tty_hung_up_p(filp) || - !(info->port.flags & ASYNC_INITIALIZED)) { - retval = ((info->port.flags & ASYNC_HUP_NOTIFY) ? - -EAGAIN : -ERESTARTSYS); - break; - } - - spin_lock_irqsave(&cinfo->card_lock, flags); - cy_writeb(base_addr + (CyCAR << index), - (u_char) channel); - if (!(info->port.flags & ASYNC_CLOSING) && (C_CLOCAL(tty) || - (readb(base_addr + - (CyMSVR1 << index)) & CyDCD))) { - spin_unlock_irqrestore(&cinfo->card_lock, flags); - break; - } - spin_unlock_irqrestore(&cinfo->card_lock, flags); - - if (signal_pending(current)) { - retval = -ERESTARTSYS; - break; - } -#ifdef CY_DEBUG_OPEN - printk(KERN_DEBUG "cyc block_til_ready blocking: " - "ttyC%d, count = %d\n", - info->line, info->port.count); -#endif - schedule(); - } - } else { - struct FIRM_ID __iomem *firm_id; - struct ZFW_CTRL __iomem *zfw_ctrl; - struct BOARD_CTRL __iomem *board_ctrl; - struct CH_CTRL __iomem *ch_ctrl; - - base_addr = cinfo->base_addr; - firm_id = base_addr + ID_ADDRESS; - if (!cyz_is_loaded(cinfo)) { - __set_current_state(TASK_RUNNING); - remove_wait_queue(&info->port.open_wait, &wait); - return -EINVAL; - } - - zfw_ctrl = base_addr + (readl(&firm_id->zfwctrl_addr) - & 0xfffff); - board_ctrl = &zfw_ctrl->board_ctrl; - ch_ctrl = zfw_ctrl->ch_ctrl; - - while (1) { - if ((tty->termios->c_cflag & CBAUD)) { - cy_writel(&ch_ctrl[channel].rs_control, - readl(&ch_ctrl[channel].rs_control) | - C_RS_RTS | C_RS_DTR); - retval = cyz_issue_cmd(cinfo, - channel, C_CM_IOCTLM, 0L); - if (retval != 0) { - printk(KERN_ERR "cyc:block_til_ready " - "retval on ttyC%d was %x\n", - info->line, retval); - } -#ifdef CY_DEBUG_DTR - printk(KERN_DEBUG "cyc:block_til_ready raising " - "Z DTR\n"); -#endif - } - - set_current_state(TASK_INTERRUPTIBLE); - if (tty_hung_up_p(filp) || - !(info->port.flags & ASYNC_INITIALIZED)) { - retval = ((info->port.flags & ASYNC_HUP_NOTIFY) ? - -EAGAIN : -ERESTARTSYS); - break; - } - if (!(info->port.flags & ASYNC_CLOSING) && (C_CLOCAL(tty) || - (readl(&ch_ctrl[channel].rs_status) & - C_RS_DCD))) { - break; - } - if (signal_pending(current)) { - retval = -ERESTARTSYS; - break; - } -#ifdef CY_DEBUG_OPEN - printk(KERN_DEBUG "cyc block_til_ready blocking: " - "ttyC%d, count = %d\n", - info->line, info->port.count); -#endif - schedule(); - } - } - __set_current_state(TASK_RUNNING); - remove_wait_queue(&info->port.open_wait, &wait); - if (!tty_hung_up_p(filp)) { - info->port.count++; -#ifdef CY_DEBUG_COUNT - printk(KERN_DEBUG "cyc:block_til_ready (%d): incrementing " - "count to %d\n", current->pid, info->port.count); -#endif - } - info->port.blocked_open--; -#ifdef CY_DEBUG_OPEN - printk(KERN_DEBUG "cyc:block_til_ready after blocking: ttyC%d, " - "count = %d\n", info->line, info->port.count); -#endif - if (retval) - return retval; - info->port.flags |= ASYNC_NORMAL_ACTIVE; - return 0; -} /* block_til_ready */ - /* * This routine is called whenever a serial port is opened. It * performs the serial-specific initialization for the tty structure. @@ -2436,7 +1598,6 @@ static int cy_open(struct tty_struct *tty, struct file *filp) printk(KERN_DEBUG "cyc:cy_open ttyC%d\n", info->line); #endif tty->driver_data = info; - info->port.tty = tty; if (serial_paranoia_check(info, tty->name, "cy_open")) return -ENODEV; @@ -2462,11 +1623,11 @@ static int cy_open(struct tty_struct *tty, struct file *filp) /* * Start up serial port */ - retval = startup(info); + retval = cy_startup(info, tty); if (retval) return retval; - retval = block_til_ready(tty, filp, info); + retval = tty_port_block_til_ready(&info->port, tty, filp); if (retval) { #ifdef CY_DEBUG_OPEN printk(KERN_DEBUG "cyc:cy_open returning after block_til_ready " @@ -2476,6 +1637,7 @@ static int cy_open(struct tty_struct *tty, struct file *filp) } info->throttle = 0; + tty_port_tty_set(&info->port, tty); #ifdef CY_DEBUG_OPEN printk(KERN_DEBUG "cyc:cy_open done\n"); @@ -2490,8 +1652,6 @@ static void cy_wait_until_sent(struct tty_struct *tty, int timeout) { struct cyclades_card *card; struct cyclades_port *info = tty->driver_data; - void __iomem *base_addr; - int chip, channel, index; unsigned long orig_jiffies; int char_time; @@ -2535,13 +1695,8 @@ static void cy_wait_until_sent(struct tty_struct *tty, int timeout) timeout, char_time, jiffies); #endif card = info->card; - channel = (info->line) - (card->first_line); if (!cy_is_Z(card)) { - chip = channel >> 2; - channel &= 0x03; - index = card->bus_index; - base_addr = card->base_addr + (cy_chip_offset[chip] << index); - while (readb(base_addr + (CySRER << index)) & CyTxRdy) { + while (cyy_readb(info, CySRER) & CyTxRdy) { #ifdef CY_DEBUG_WAIT_UNTIL_SENT printk(KERN_DEBUG "Not clean (jiff=%lu)...", jiffies); #endif @@ -2595,103 +1750,37 @@ static void cy_flush_buffer(struct tty_struct *tty) } /* cy_flush_buffer */ -/* - * This routine is called when a particular tty device is closed. - */ -static void cy_close(struct tty_struct *tty, struct file *filp) +static void cy_do_close(struct tty_port *port) { - struct cyclades_port *info = tty->driver_data; + struct cyclades_port *info = container_of(port, struct cyclades_port, + port); struct cyclades_card *card; unsigned long flags; - -#ifdef CY_DEBUG_OTHER - printk(KERN_DEBUG "cyc:cy_close ttyC%d\n", info->line); -#endif - - if (!info || serial_paranoia_check(info, tty->name, "cy_close")) - return; + int channel; card = info->card; - - spin_lock_irqsave(&card->card_lock, flags); - /* If the TTY is being hung up, nothing to do */ - if (tty_hung_up_p(filp)) { - spin_unlock_irqrestore(&card->card_lock, flags); - return; - } -#ifdef CY_DEBUG_OPEN - printk(KERN_DEBUG "cyc:cy_close ttyC%d, count = %d\n", info->line, - info->port.count); -#endif - if ((tty->count == 1) && (info->port.count != 1)) { - /* - * Uh, oh. tty->count is 1, which means that the tty - * structure will be freed. Info->count should always - * be one in these conditions. If it's greater than - * one, we've got real problems, since it means the - * serial port won't be shutdown. - */ - printk(KERN_ERR "cyc:cy_close: bad serial port count; " - "tty->count is 1, info->port.count is %d\n", info->port.count); - info->port.count = 1; - } -#ifdef CY_DEBUG_COUNT - printk(KERN_DEBUG "cyc:cy_close at (%d): decrementing count to %d\n", - current->pid, info->port.count - 1); -#endif - if (--info->port.count < 0) { -#ifdef CY_DEBUG_COUNT - printk(KERN_DEBUG "cyc:cyc_close setting count to 0\n"); -#endif - info->port.count = 0; - } - if (info->port.count) { - spin_unlock_irqrestore(&card->card_lock, flags); - return; - } - info->port.flags |= ASYNC_CLOSING; - - /* - * Now we wait for the transmit buffer to clear; and we notify - * the line discipline to only process XON/XOFF characters. - */ - tty->closing = 1; - spin_unlock_irqrestore(&card->card_lock, flags); - if (info->port.closing_wait != CY_CLOSING_WAIT_NONE) - tty_wait_until_sent(tty, info->port.closing_wait); - + channel = info->line - card->first_line; spin_lock_irqsave(&card->card_lock, flags); if (!cy_is_Z(card)) { - int channel = info->line - card->first_line; - int index = card->bus_index; - void __iomem *base_addr = card->base_addr + - (cy_chip_offset[channel >> 2] << index); /* Stop accepting input */ - channel &= 0x03; - cy_writeb(base_addr + (CyCAR << index), (u_char) channel); - cy_writeb(base_addr + (CySRER << index), - readb(base_addr + (CySRER << index)) & ~CyRxData); + cyy_writeb(info, CyCAR, channel & 0x03); + cyy_writeb(info, CySRER, cyy_readb(info, CySRER) & ~CyRxData); if (info->port.flags & ASYNC_INITIALIZED) { /* Waiting for on-board buffers to be empty before closing the port */ spin_unlock_irqrestore(&card->card_lock, flags); - cy_wait_until_sent(tty, info->timeout); + cy_wait_until_sent(port->tty, info->timeout); spin_lock_irqsave(&card->card_lock, flags); } } else { #ifdef Z_WAKE /* Waiting for on-board buffers to be empty before closing the port */ - void __iomem *base_addr = card->base_addr; - struct FIRM_ID __iomem *firm_id = base_addr + ID_ADDRESS; - struct ZFW_CTRL __iomem *zfw_ctrl = - base_addr + (readl(&firm_id->zfwctrl_addr) & 0xfffff); - struct CH_CTRL __iomem *ch_ctrl = zfw_ctrl->ch_ctrl; - int channel = info->line - card->first_line; + struct CH_CTRL __iomem *ch_ctrl = info->u.cyz.ch_ctrl; int retval; - if (readl(&ch_ctrl[channel].flow_status) != C_FS_TXIDLE) { + if (readl(&ch_ctrl->flow_status) != C_FS_TXIDLE) { retval = cyz_issue_cmd(card, channel, C_CM_IOCTLW, 0L); if (retval != 0) { printk(KERN_DEBUG "cyc:cy_close retval on " @@ -2703,32 +1792,19 @@ static void cy_close(struct tty_struct *tty, struct file *filp) } #endif } - spin_unlock_irqrestore(&card->card_lock, flags); - shutdown(info); - cy_flush_buffer(tty); - tty_ldisc_flush(tty); - spin_lock_irqsave(&card->card_lock, flags); - - tty->closing = 0; - info->port.tty = NULL; - if (info->port.blocked_open) { - spin_unlock_irqrestore(&card->card_lock, flags); - if (info->port.close_delay) { - msleep_interruptible(jiffies_to_msecs - (info->port.close_delay)); - } - wake_up_interruptible(&info->port.open_wait); - spin_lock_irqsave(&card->card_lock, flags); - } - info->port.flags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_CLOSING); - wake_up_interruptible(&info->port.close_wait); - -#ifdef CY_DEBUG_OTHER - printk(KERN_DEBUG "cyc:cy_close done\n"); -#endif + cy_shutdown(info, port->tty); +} - spin_unlock_irqrestore(&card->card_lock, flags); +/* + * This routine is called when a particular tty device is closed. + */ +static void cy_close(struct tty_struct *tty, struct file *filp) +{ + struct cyclades_port *info = tty->driver_data; + if (!info || serial_paranoia_check(info, tty->name, "cy_close")) + return; + tty_port_close(&info->port, tty, filp); } /* cy_close */ /* This routine gets called when tty_write has put something into @@ -2871,18 +1947,13 @@ static int cy_write_room(struct tty_struct *tty) static int cy_chars_in_buffer(struct tty_struct *tty) { - struct cyclades_card *card; struct cyclades_port *info = tty->driver_data; - int channel; if (serial_paranoia_check(info, tty->name, "cy_chars_in_buffer")) return 0; - card = info->card; - channel = (info->line) - (card->first_line); - #ifdef Z_EXT_CHARS_IN_BUFFER - if (!cy_is_Z(card)) { + if (!cy_is_Z(info->card)) { #endif /* Z_EXT_CHARS_IN_BUFFER */ #ifdef CY_DEBUG_IO printk(KERN_DEBUG "cyc:cy_chars_in_buffer ttyC%d %d\n", @@ -2891,20 +1962,11 @@ static int cy_chars_in_buffer(struct tty_struct *tty) return info->xmit_cnt; #ifdef Z_EXT_CHARS_IN_BUFFER } else { - static struct FIRM_ID *firm_id; - static struct ZFW_CTRL *zfw_ctrl; - static struct CH_CTRL *ch_ctrl; - static struct BUF_CTRL *buf_ctrl; + struct BUF_CTRL __iomem *buf_ctrl = info->u.cyz.buf_ctrl; int char_count; __u32 tx_put, tx_get, tx_bufsize; lock_kernel(); - firm_id = card->base_addr + ID_ADDRESS; - zfw_ctrl = card->base_addr + - (readl(&firm_id->zfwctrl_addr) & 0xfffff); - ch_ctrl = &(zfw_ctrl->ch_ctrl[channel]); - buf_ctrl = &(zfw_ctrl->buf_ctrl[channel]); - tx_get = readl(&buf_ctrl->tx_get); tx_put = readl(&buf_ctrl->tx_put); tx_bufsize = readl(&buf_ctrl->tx_bufsize); @@ -2957,48 +2019,44 @@ static void cyy_baud_calc(struct cyclades_port *info, __u32 baud) * This routine finds or computes the various line characteristics. * It used to be called config_setup */ -static void set_line_char(struct cyclades_port *info) +static void cy_set_line_char(struct cyclades_port *info, struct tty_struct *tty) { struct cyclades_card *card; unsigned long flags; - void __iomem *base_addr; - int chip, channel, index; + int channel; unsigned cflag, iflag; int baud, baud_rate = 0; int i; - if (!info->port.tty || !info->port.tty->termios) + if (!tty->termios) /* XXX can this happen at all? */ return; if (info->line == -1) return; - cflag = info->port.tty->termios->c_cflag; - iflag = info->port.tty->termios->c_iflag; + cflag = tty->termios->c_cflag; + iflag = tty->termios->c_iflag; /* * Set up the tty->alt_speed kludge */ - if (info->port.tty) { - if ((info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) - info->port.tty->alt_speed = 57600; - if ((info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) - info->port.tty->alt_speed = 115200; - if ((info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) - info->port.tty->alt_speed = 230400; - if ((info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) - info->port.tty->alt_speed = 460800; - } + if ((info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) + tty->alt_speed = 57600; + if ((info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) + tty->alt_speed = 115200; + if ((info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) + tty->alt_speed = 230400; + if ((info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) + tty->alt_speed = 460800; card = info->card; channel = info->line - card->first_line; if (!cy_is_Z(card)) { - - index = card->bus_index; + u32 cflags; /* baud rate */ - baud = tty_get_baud_rate(info->port.tty); + baud = tty_get_baud_rate(tty); if (baud == 38400 && (info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST) { if (info->custom_divisor) @@ -3107,124 +2165,68 @@ static void set_line_char(struct cyclades_port *info) cable. Contact Marcio Saito for details. ***********************************************/ - chip = channel >> 2; channel &= 0x03; - base_addr = card->base_addr + (cy_chip_offset[chip] << index); spin_lock_irqsave(&card->card_lock, flags); - cy_writeb(base_addr + (CyCAR << index), (u_char) channel); + cyy_writeb(info, CyCAR, channel); /* tx and rx baud rate */ - cy_writeb(base_addr + (CyTCOR << index), info->tco); - cy_writeb(base_addr + (CyTBPR << index), info->tbpr); - cy_writeb(base_addr + (CyRCOR << index), info->rco); - cy_writeb(base_addr + (CyRBPR << index), info->rbpr); + cyy_writeb(info, CyTCOR, info->tco); + cyy_writeb(info, CyTBPR, info->tbpr); + cyy_writeb(info, CyRCOR, info->rco); + cyy_writeb(info, CyRBPR, info->rbpr); /* set line characteristics according configuration */ - cy_writeb(base_addr + (CySCHR1 << index), - START_CHAR(info->port.tty)); - cy_writeb(base_addr + (CySCHR2 << index), STOP_CHAR(info->port.tty)); - cy_writeb(base_addr + (CyCOR1 << index), info->cor1); - cy_writeb(base_addr + (CyCOR2 << index), info->cor2); - cy_writeb(base_addr + (CyCOR3 << index), info->cor3); - cy_writeb(base_addr + (CyCOR4 << index), info->cor4); - cy_writeb(base_addr + (CyCOR5 << index), info->cor5); + cyy_writeb(info, CySCHR1, START_CHAR(tty)); + cyy_writeb(info, CySCHR2, STOP_CHAR(tty)); + cyy_writeb(info, CyCOR1, info->cor1); + cyy_writeb(info, CyCOR2, info->cor2); + cyy_writeb(info, CyCOR3, info->cor3); + cyy_writeb(info, CyCOR4, info->cor4); + cyy_writeb(info, CyCOR5, info->cor5); - cyy_issue_cmd(base_addr, CyCOR_CHANGE | CyCOR1ch | CyCOR2ch | - CyCOR3ch, index); + cyy_issue_cmd(info, CyCOR_CHANGE | CyCOR1ch | CyCOR2ch | + CyCOR3ch); /* !!! Is this needed? */ - cy_writeb(base_addr + (CyCAR << index), (u_char) channel); - cy_writeb(base_addr + (CyRTPR << index), + cyy_writeb(info, CyCAR, channel); + cyy_writeb(info, CyRTPR, (info->default_timeout ? info->default_timeout : 0x02)); /* 10ms rx timeout */ - if (C_CLOCAL(info->port.tty)) { - /* without modem intr */ - cy_writeb(base_addr + (CySRER << index), - readb(base_addr + (CySRER << index)) | CyMdmCh); - /* act on 1->0 modem transitions */ - if ((cflag & CRTSCTS) && info->rflow) { - cy_writeb(base_addr + (CyMCOR1 << index), - (CyCTS | rflow_thr[i])); - } else { - cy_writeb(base_addr + (CyMCOR1 << index), - CyCTS); - } - /* act on 0->1 modem transitions */ - cy_writeb(base_addr + (CyMCOR2 << index), CyCTS); - } else { - /* without modem intr */ - cy_writeb(base_addr + (CySRER << index), - readb(base_addr + - (CySRER << index)) | CyMdmCh); - /* act on 1->0 modem transitions */ - if ((cflag & CRTSCTS) && info->rflow) { - cy_writeb(base_addr + (CyMCOR1 << index), - (CyDSR | CyCTS | CyRI | CyDCD | - rflow_thr[i])); - } else { - cy_writeb(base_addr + (CyMCOR1 << index), - CyDSR | CyCTS | CyRI | CyDCD); - } - /* act on 0->1 modem transitions */ - cy_writeb(base_addr + (CyMCOR2 << index), - CyDSR | CyCTS | CyRI | CyDCD); - } + cflags = CyCTS; + if (!C_CLOCAL(tty)) + cflags |= CyDSR | CyRI | CyDCD; + /* without modem intr */ + cyy_writeb(info, CySRER, cyy_readb(info, CySRER) | CyMdmCh); + /* act on 1->0 modem transitions */ + if ((cflag & CRTSCTS) && info->rflow) + cyy_writeb(info, CyMCOR1, cflags | rflow_thr[i]); + else + cyy_writeb(info, CyMCOR1, cflags); + /* act on 0->1 modem transitions */ + cyy_writeb(info, CyMCOR2, cflags); - if (i == 0) { /* baud rate is zero, turn off line */ - if (info->rtsdtr_inv) { - cy_writeb(base_addr + (CyMSVR1 << index), - ~CyRTS); - } else { - cy_writeb(base_addr + (CyMSVR2 << index), - ~CyDTR); - } -#ifdef CY_DEBUG_DTR - printk(KERN_DEBUG "cyc:set_line_char dropping DTR\n"); - printk(KERN_DEBUG " status: 0x%x, 0x%x\n", - readb(base_addr + (CyMSVR1 << index)), - readb(base_addr + (CyMSVR2 << index))); -#endif - } else { - if (info->rtsdtr_inv) { - cy_writeb(base_addr + (CyMSVR1 << index), - CyRTS); - } else { - cy_writeb(base_addr + (CyMSVR2 << index), - CyDTR); - } -#ifdef CY_DEBUG_DTR - printk(KERN_DEBUG "cyc:set_line_char raising DTR\n"); - printk(KERN_DEBUG " status: 0x%x, 0x%x\n", - readb(base_addr + (CyMSVR1 << index)), - readb(base_addr + (CyMSVR2 << index))); -#endif - } + if (i == 0) /* baud rate is zero, turn off line */ + cyy_change_rts_dtr(info, 0, TIOCM_DTR); + else + cyy_change_rts_dtr(info, TIOCM_DTR, 0); - if (info->port.tty) - clear_bit(TTY_IO_ERROR, &info->port.tty->flags); + clear_bit(TTY_IO_ERROR, &tty->flags); spin_unlock_irqrestore(&card->card_lock, flags); } else { - struct FIRM_ID __iomem *firm_id; - struct ZFW_CTRL __iomem *zfw_ctrl; - struct CH_CTRL __iomem *ch_ctrl; + struct CH_CTRL __iomem *ch_ctrl = info->u.cyz.ch_ctrl; __u32 sw_flow; int retval; - firm_id = card->base_addr + ID_ADDRESS; if (!cyz_is_loaded(card)) return; - zfw_ctrl = card->base_addr + - (readl(&firm_id->zfwctrl_addr) & 0xfffff); - ch_ctrl = &(zfw_ctrl->ch_ctrl[channel]); - /* baud rate */ - baud = tty_get_baud_rate(info->port.tty); + baud = tty_get_baud_rate(tty); if (baud == 38400 && (info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST) { if (info->custom_divisor) @@ -3335,45 +2337,38 @@ static void set_line_char(struct cyclades_port *info) "was %x\n", info->line, retval); } - if (info->port.tty) - clear_bit(TTY_IO_ERROR, &info->port.tty->flags); + clear_bit(TTY_IO_ERROR, &tty->flags); } } /* set_line_char */ -static int -get_serial_info(struct cyclades_port *info, +static int cy_get_serial_info(struct cyclades_port *info, struct serial_struct __user *retinfo) { - struct serial_struct tmp; struct cyclades_card *cinfo = info->card; - - if (!retinfo) - return -EFAULT; - memset(&tmp, 0, sizeof(tmp)); - tmp.type = info->type; - tmp.line = info->line; - tmp.port = (info->card - cy_card) * 0x100 + info->line - - cinfo->first_line; - tmp.irq = cinfo->irq; - tmp.flags = info->port.flags; - tmp.close_delay = info->port.close_delay; - tmp.closing_wait = info->port.closing_wait; - tmp.baud_base = info->baud; - tmp.custom_divisor = info->custom_divisor; - tmp.hub6 = 0; /*!!! */ + struct serial_struct tmp = { + .type = info->type, + .line = info->line, + .port = (info->card - cy_card) * 0x100 + info->line - + cinfo->first_line, + .irq = cinfo->irq, + .flags = info->port.flags, + .close_delay = info->port.close_delay, + .closing_wait = info->port.closing_wait, + .baud_base = info->baud, + .custom_divisor = info->custom_divisor, + .hub6 = 0, /*!!! */ + }; return copy_to_user(retinfo, &tmp, sizeof(*retinfo)) ? -EFAULT : 0; -} /* get_serial_info */ +} static int -set_serial_info(struct cyclades_port *info, +cy_set_serial_info(struct cyclades_port *info, struct tty_struct *tty, struct serial_struct __user *new_info) { struct serial_struct new_serial; - struct cyclades_port old_info; if (copy_from_user(&new_serial, new_info, sizeof(new_serial))) return -EFAULT; - old_info = *info; if (!capable(CAP_SYS_ADMIN)) { if (new_serial.close_delay != info->port.close_delay || @@ -3403,10 +2398,10 @@ set_serial_info(struct cyclades_port *info, check_and_exit: if (info->port.flags & ASYNC_INITIALIZED) { - set_line_char(info); + cy_set_line_char(info, tty); return 0; } else { - return startup(info); + return cy_startup(info, tty); } } /* set_serial_info */ @@ -3422,24 +2417,14 @@ check_and_exit: */ static int get_lsr_info(struct cyclades_port *info, unsigned int __user *value) { - struct cyclades_card *card; - int chip, channel, index; - unsigned char status; + struct cyclades_card *card = info->card; unsigned int result; unsigned long flags; - void __iomem *base_addr; + u8 status; - card = info->card; - channel = (info->line) - (card->first_line); if (!cy_is_Z(card)) { - chip = channel >> 2; - channel &= 0x03; - index = card->bus_index; - base_addr = card->base_addr + (cy_chip_offset[chip] << index); - spin_lock_irqsave(&card->card_lock, flags); - status = readb(base_addr + (CySRER << index)) & - (CyTxRdy | CyTxMpty); + status = cyy_readb(info, CySRER) & (CyTxRdy | CyTxMpty); spin_unlock_irqrestore(&card->card_lock, flags); result = (status ? 0 : TIOCSER_TEMT); } else { @@ -3453,34 +2438,23 @@ static int cy_tiocmget(struct tty_struct *tty, struct file *file) { struct cyclades_port *info = tty->driver_data; struct cyclades_card *card; - int chip, channel, index; - void __iomem *base_addr; - unsigned long flags; - unsigned char status; - unsigned long lstatus; - unsigned int result; - struct FIRM_ID __iomem *firm_id; - struct ZFW_CTRL __iomem *zfw_ctrl; - struct BOARD_CTRL __iomem *board_ctrl; - struct CH_CTRL __iomem *ch_ctrl; + int result; if (serial_paranoia_check(info, tty->name, __func__)) return -ENODEV; - lock_kernel(); - card = info->card; - channel = info->line - card->first_line; + + lock_kernel(); if (!cy_is_Z(card)) { - chip = channel >> 2; - channel &= 0x03; - index = card->bus_index; - base_addr = card->base_addr + (cy_chip_offset[chip] << index); + unsigned long flags; + int channel = info->line - card->first_line; + u8 status; spin_lock_irqsave(&card->card_lock, flags); - cy_writeb(base_addr + (CyCAR << index), (u_char) channel); - status = readb(base_addr + (CyMSVR1 << index)); - status |= readb(base_addr + (CyMSVR2 << index)); + cyy_writeb(info, CyCAR, channel & 0x03); + status = cyy_readb(info, CyMSVR1); + status |= cyy_readb(info, CyMSVR2); spin_unlock_irqrestore(&card->card_lock, flags); if (info->rtsdtr_inv) { @@ -3495,27 +2469,22 @@ static int cy_tiocmget(struct tty_struct *tty, struct file *file) ((status & CyDSR) ? TIOCM_DSR : 0) | ((status & CyCTS) ? TIOCM_CTS : 0); } else { - base_addr = card->base_addr; - firm_id = card->base_addr + ID_ADDRESS; - if (cyz_is_loaded(card)) { - zfw_ctrl = card->base_addr + - (readl(&firm_id->zfwctrl_addr) & 0xfffff); - board_ctrl = &zfw_ctrl->board_ctrl; - ch_ctrl = zfw_ctrl->ch_ctrl; - lstatus = readl(&ch_ctrl[channel].rs_status); - result = ((lstatus & C_RS_RTS) ? TIOCM_RTS : 0) | - ((lstatus & C_RS_DTR) ? TIOCM_DTR : 0) | - ((lstatus & C_RS_DCD) ? TIOCM_CAR : 0) | - ((lstatus & C_RS_RI) ? TIOCM_RNG : 0) | - ((lstatus & C_RS_DSR) ? TIOCM_DSR : 0) | - ((lstatus & C_RS_CTS) ? TIOCM_CTS : 0); - } else { - result = 0; - unlock_kernel(); - return -ENODEV; + u32 lstatus; + + if (!cyz_is_loaded(card)) { + result = -ENODEV; + goto end; } + lstatus = readl(&info->u.cyz.ch_ctrl->rs_status); + result = ((lstatus & C_RS_RTS) ? TIOCM_RTS : 0) | + ((lstatus & C_RS_DTR) ? TIOCM_DTR : 0) | + ((lstatus & C_RS_DCD) ? TIOCM_CAR : 0) | + ((lstatus & C_RS_RI) ? TIOCM_RNG : 0) | + ((lstatus & C_RS_DSR) ? TIOCM_DSR : 0) | + ((lstatus & C_RS_CTS) ? TIOCM_CTS : 0); } +end: unlock_kernel(); return result; } /* cy_tiomget */ @@ -3526,150 +2495,53 @@ cy_tiocmset(struct tty_struct *tty, struct file *file, { struct cyclades_port *info = tty->driver_data; struct cyclades_card *card; - int chip, channel, index; - void __iomem *base_addr; unsigned long flags; - struct FIRM_ID __iomem *firm_id; - struct ZFW_CTRL __iomem *zfw_ctrl; - struct BOARD_CTRL __iomem *board_ctrl; - struct CH_CTRL __iomem *ch_ctrl; - int retval; if (serial_paranoia_check(info, tty->name, __func__)) return -ENODEV; card = info->card; - channel = (info->line) - (card->first_line); if (!cy_is_Z(card)) { - chip = channel >> 2; - channel &= 0x03; - index = card->bus_index; - base_addr = card->base_addr + (cy_chip_offset[chip] << index); + spin_lock_irqsave(&card->card_lock, flags); + cyy_change_rts_dtr(info, set, clear); + spin_unlock_irqrestore(&card->card_lock, flags); + } else { + struct CH_CTRL __iomem *ch_ctrl = info->u.cyz.ch_ctrl; + int retval, channel = info->line - card->first_line; + u32 rs; - if (set & TIOCM_RTS) { - spin_lock_irqsave(&card->card_lock, flags); - cy_writeb(base_addr + (CyCAR << index), - (u_char) channel); - if (info->rtsdtr_inv) { - cy_writeb(base_addr + (CyMSVR2 << index), - CyDTR); - } else { - cy_writeb(base_addr + (CyMSVR1 << index), - CyRTS); - } - spin_unlock_irqrestore(&card->card_lock, flags); - } - if (clear & TIOCM_RTS) { - spin_lock_irqsave(&card->card_lock, flags); - cy_writeb(base_addr + (CyCAR << index), - (u_char) channel); - if (info->rtsdtr_inv) { - cy_writeb(base_addr + (CyMSVR2 << index), - ~CyDTR); - } else { - cy_writeb(base_addr + (CyMSVR1 << index), - ~CyRTS); - } - spin_unlock_irqrestore(&card->card_lock, flags); - } + if (!cyz_is_loaded(card)) + return -ENODEV; + + spin_lock_irqsave(&card->card_lock, flags); + rs = readl(&ch_ctrl->rs_control); + if (set & TIOCM_RTS) + rs |= C_RS_RTS; + if (clear & TIOCM_RTS) + rs &= ~C_RS_RTS; if (set & TIOCM_DTR) { - spin_lock_irqsave(&card->card_lock, flags); - cy_writeb(base_addr + (CyCAR << index), - (u_char) channel); - if (info->rtsdtr_inv) { - cy_writeb(base_addr + (CyMSVR1 << index), - CyRTS); - } else { - cy_writeb(base_addr + (CyMSVR2 << index), - CyDTR); - } + rs |= C_RS_DTR; #ifdef CY_DEBUG_DTR - printk(KERN_DEBUG "cyc:set_modem_info raising DTR\n"); - printk(KERN_DEBUG " status: 0x%x, 0x%x\n", - readb(base_addr + (CyMSVR1 << index)), - readb(base_addr + (CyMSVR2 << index))); + printk(KERN_DEBUG "cyc:set_modem_info raising Z DTR\n"); #endif - spin_unlock_irqrestore(&card->card_lock, flags); } if (clear & TIOCM_DTR) { - spin_lock_irqsave(&card->card_lock, flags); - cy_writeb(base_addr + (CyCAR << index), - (u_char) channel); - if (info->rtsdtr_inv) { - cy_writeb(base_addr + (CyMSVR1 << index), - ~CyRTS); - } else { - cy_writeb(base_addr + (CyMSVR2 << index), - ~CyDTR); - } - + rs &= ~C_RS_DTR; #ifdef CY_DEBUG_DTR - printk(KERN_DEBUG "cyc:set_modem_info dropping DTR\n"); - printk(KERN_DEBUG " status: 0x%x, 0x%x\n", - readb(base_addr + (CyMSVR1 << index)), - readb(base_addr + (CyMSVR2 << index))); + printk(KERN_DEBUG "cyc:set_modem_info clearing " + "Z DTR\n"); #endif - spin_unlock_irqrestore(&card->card_lock, flags); } - } else { - base_addr = card->base_addr; - - firm_id = card->base_addr + ID_ADDRESS; - if (cyz_is_loaded(card)) { - zfw_ctrl = card->base_addr + - (readl(&firm_id->zfwctrl_addr) & 0xfffff); - board_ctrl = &zfw_ctrl->board_ctrl; - ch_ctrl = zfw_ctrl->ch_ctrl; - - if (set & TIOCM_RTS) { - spin_lock_irqsave(&card->card_lock, flags); - cy_writel(&ch_ctrl[channel].rs_control, - readl(&ch_ctrl[channel].rs_control) | - C_RS_RTS); - spin_unlock_irqrestore(&card->card_lock, flags); - } - if (clear & TIOCM_RTS) { - spin_lock_irqsave(&card->card_lock, flags); - cy_writel(&ch_ctrl[channel].rs_control, - readl(&ch_ctrl[channel].rs_control) & - ~C_RS_RTS); - spin_unlock_irqrestore(&card->card_lock, flags); - } - if (set & TIOCM_DTR) { - spin_lock_irqsave(&card->card_lock, flags); - cy_writel(&ch_ctrl[channel].rs_control, - readl(&ch_ctrl[channel].rs_control) | - C_RS_DTR); -#ifdef CY_DEBUG_DTR - printk(KERN_DEBUG "cyc:set_modem_info raising " - "Z DTR\n"); -#endif - spin_unlock_irqrestore(&card->card_lock, flags); - } - if (clear & TIOCM_DTR) { - spin_lock_irqsave(&card->card_lock, flags); - cy_writel(&ch_ctrl[channel].rs_control, - readl(&ch_ctrl[channel].rs_control) & - ~C_RS_DTR); -#ifdef CY_DEBUG_DTR - printk(KERN_DEBUG "cyc:set_modem_info clearing " - "Z DTR\n"); -#endif - spin_unlock_irqrestore(&card->card_lock, flags); - } - } else { - return -ENODEV; - } - spin_lock_irqsave(&card->card_lock, flags); + cy_writel(&ch_ctrl->rs_control, rs); retval = cyz_issue_cmd(card, channel, C_CM_IOCTLM, 0L); + spin_unlock_irqrestore(&card->card_lock, flags); if (retval != 0) { printk(KERN_ERR "cyc:set_modem_info retval on ttyC%d " "was %x\n", info->line, retval); } - spin_unlock_irqrestore(&card->card_lock, flags); } return 0; -} /* cy_tiocmset */ +} /* * cy_break() --- routine which turns the break handling on or off @@ -3734,41 +2606,18 @@ static int cy_break(struct tty_struct *tty, int break_state) return retval; } /* cy_break */ -static int get_mon_info(struct cyclades_port *info, - struct cyclades_monitor __user *mon) -{ - - if (copy_to_user(mon, &info->mon, sizeof(struct cyclades_monitor))) - return -EFAULT; - info->mon.int_count = 0; - info->mon.char_count = 0; - info->mon.char_max = 0; - info->mon.char_last = 0; - return 0; -} /* get_mon_info */ - static int set_threshold(struct cyclades_port *info, unsigned long value) { - struct cyclades_card *card; - void __iomem *base_addr; - int channel, chip, index; + struct cyclades_card *card = info->card; unsigned long flags; - card = info->card; - channel = info->line - card->first_line; if (!cy_is_Z(card)) { - chip = channel >> 2; - channel &= 0x03; - index = card->bus_index; - base_addr = - card->base_addr + (cy_chip_offset[chip] << index); - info->cor3 &= ~CyREC_FIFO; info->cor3 |= value & CyREC_FIFO; spin_lock_irqsave(&card->card_lock, flags); - cy_writeb(base_addr + (CyCOR3 << index), info->cor3); - cyy_issue_cmd(base_addr, CyCOR_CHANGE | CyCOR3ch, index); + cyy_writeb(info, CyCOR3, info->cor3); + cyy_issue_cmd(info, CyCOR_CHANGE | CyCOR3ch); spin_unlock_irqrestore(&card->card_lock, flags); } return 0; @@ -3777,55 +2626,23 @@ static int set_threshold(struct cyclades_port *info, unsigned long value) static int get_threshold(struct cyclades_port *info, unsigned long __user *value) { - struct cyclades_card *card; - void __iomem *base_addr; - int channel, chip, index; - unsigned long tmp; + struct cyclades_card *card = info->card; - card = info->card; - channel = info->line - card->first_line; if (!cy_is_Z(card)) { - chip = channel >> 2; - channel &= 0x03; - index = card->bus_index; - base_addr = card->base_addr + (cy_chip_offset[chip] << index); - - tmp = readb(base_addr + (CyCOR3 << index)) & CyREC_FIFO; + u8 tmp = cyy_readb(info, CyCOR3) & CyREC_FIFO; return put_user(tmp, value); } return 0; } /* get_threshold */ -static int set_default_threshold(struct cyclades_port *info, - unsigned long value) -{ - info->default_threshold = value & 0x0f; - return 0; -} /* set_default_threshold */ - -static int get_default_threshold(struct cyclades_port *info, - unsigned long __user *value) -{ - return put_user(info->default_threshold, value); -} /* get_default_threshold */ - static int set_timeout(struct cyclades_port *info, unsigned long value) { - struct cyclades_card *card; - void __iomem *base_addr; - int channel, chip, index; + struct cyclades_card *card = info->card; unsigned long flags; - card = info->card; - channel = info->line - card->first_line; if (!cy_is_Z(card)) { - chip = channel >> 2; - channel &= 0x03; - index = card->bus_index; - base_addr = card->base_addr + (cy_chip_offset[chip] << index); - spin_lock_irqsave(&card->card_lock, flags); - cy_writeb(base_addr + (CyRTPR << index), value & 0xff); + cyy_writeb(info, CyRTPR, value & 0xff); spin_unlock_irqrestore(&card->card_lock, flags); } return 0; @@ -3834,36 +2651,35 @@ static int set_timeout(struct cyclades_port *info, unsigned long value) static int get_timeout(struct cyclades_port *info, unsigned long __user *value) { - struct cyclades_card *card; - void __iomem *base_addr; - int channel, chip, index; - unsigned long tmp; + struct cyclades_card *card = info->card; - card = info->card; - channel = info->line - card->first_line; if (!cy_is_Z(card)) { - chip = channel >> 2; - channel &= 0x03; - index = card->bus_index; - base_addr = card->base_addr + (cy_chip_offset[chip] << index); - - tmp = readb(base_addr + (CyRTPR << index)); + u8 tmp = cyy_readb(info, CyRTPR); return put_user(tmp, value); } return 0; } /* get_timeout */ -static int set_default_timeout(struct cyclades_port *info, unsigned long value) +static int cy_cflags_changed(struct cyclades_port *info, unsigned long arg, + struct cyclades_icount *cprev) { - info->default_timeout = value & 0xff; - return 0; -} /* set_default_timeout */ + struct cyclades_icount cnow; + unsigned long flags; + int ret; -static int get_default_timeout(struct cyclades_port *info, - unsigned long __user *value) -{ - return put_user(info->default_timeout, value); -} /* get_default_timeout */ + spin_lock_irqsave(&info->card->card_lock, flags); + cnow = info->icount; /* atomic copy */ + spin_unlock_irqrestore(&info->card->card_lock, flags); + + ret = ((arg & TIOCM_RNG) && (cnow.rng != cprev->rng)) || + ((arg & TIOCM_DSR) && (cnow.dsr != cprev->dsr)) || + ((arg & TIOCM_CD) && (cnow.dcd != cprev->dcd)) || + ((arg & TIOCM_CTS) && (cnow.cts != cprev->cts)); + + *cprev = cnow; + + return ret; +} /* * This routine allows the tty driver to implement device- @@ -3875,8 +2691,7 @@ cy_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg) { struct cyclades_port *info = tty->driver_data; - struct cyclades_icount cprev, cnow; /* kernel counter temps */ - struct serial_icounter_struct __user *p_cuser; /* user space */ + struct cyclades_icount cnow; /* kernel counter temps */ int ret_val = 0; unsigned long flags; void __user *argp = (void __user *)arg; @@ -3892,7 +2707,11 @@ cy_ioctl(struct tty_struct *tty, struct file *file, switch (cmd) { case CYGETMON: - ret_val = get_mon_info(info, argp); + if (copy_to_user(argp, &info->mon, sizeof(info->mon))) { + ret_val = -EFAULT; + break; + } + memset(&info->mon, 0, sizeof(info->mon)); break; case CYGETTHRESH: ret_val = get_threshold(info, argp); @@ -3901,10 +2720,11 @@ cy_ioctl(struct tty_struct *tty, struct file *file, ret_val = set_threshold(info, arg); break; case CYGETDEFTHRESH: - ret_val = get_default_threshold(info, argp); + ret_val = put_user(info->default_threshold, + (unsigned long __user *)argp); break; case CYSETDEFTHRESH: - ret_val = set_default_threshold(info, arg); + info->default_threshold = arg & 0x0f; break; case CYGETTIMEOUT: ret_val = get_timeout(info, argp); @@ -3913,21 +2733,20 @@ cy_ioctl(struct tty_struct *tty, struct file *file, ret_val = set_timeout(info, arg); break; case CYGETDEFTIMEOUT: - ret_val = get_default_timeout(info, argp); + ret_val = put_user(info->default_timeout, + (unsigned long __user *)argp); break; case CYSETDEFTIMEOUT: - ret_val = set_default_timeout(info, arg); + info->default_timeout = arg & 0xff; break; case CYSETRFLOW: info->rflow = (int)arg; - ret_val = 0; break; case CYGETRFLOW: ret_val = info->rflow; break; case CYSETRTSDTR_INV: info->rtsdtr_inv = (int)arg; - ret_val = 0; break; case CYGETRTSDTR_INV: ret_val = info->rtsdtr_inv; @@ -3938,7 +2757,6 @@ cy_ioctl(struct tty_struct *tty, struct file *file, #ifndef CONFIG_CYZ_INTR case CYZSETPOLLCYCLE: cyz_polling_cycle = (arg * HZ) / 1000; - ret_val = 0; break; case CYZGETPOLLCYCLE: ret_val = (cyz_polling_cycle * 1000) / HZ; @@ -3946,16 +2764,15 @@ cy_ioctl(struct tty_struct *tty, struct file *file, #endif /* CONFIG_CYZ_INTR */ case CYSETWAIT: info->port.closing_wait = (unsigned short)arg * HZ / 100; - ret_val = 0; break; case CYGETWAIT: ret_val = info->port.closing_wait / (HZ / 100); break; case TIOCGSERIAL: - ret_val = get_serial_info(info, argp); + ret_val = cy_get_serial_info(info, argp); break; case TIOCSSERIAL: - ret_val = set_serial_info(info, argp); + ret_val = cy_set_serial_info(info, tty, argp); break; case TIOCSERGETLSR: /* Get line status register */ ret_val = get_lsr_info(info, argp); @@ -3971,17 +2788,8 @@ cy_ioctl(struct tty_struct *tty, struct file *file, /* note the counters on entry */ cnow = info->icount; spin_unlock_irqrestore(&info->card->card_lock, flags); - ret_val = wait_event_interruptible(info->delta_msr_wait, ({ - cprev = cnow; - spin_lock_irqsave(&info->card->card_lock, flags); - cnow = info->icount; /* atomic copy */ - spin_unlock_irqrestore(&info->card->card_lock, flags); - - ((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) || - ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) || - ((arg & TIOCM_CD) && (cnow.dcd != cprev.dcd)) || - ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts)); - })); + ret_val = wait_event_interruptible(info->port.delta_msr_wait, + cy_cflags_changed(info, arg, &cnow)); break; /* @@ -3990,46 +2798,29 @@ cy_ioctl(struct tty_struct *tty, struct file *file, * NB: both 1->0 and 0->1 transitions are counted except for * RI where only 0->1 is counted. */ - case TIOCGICOUNT: + case TIOCGICOUNT: { + struct serial_icounter_struct sic = { }; + spin_lock_irqsave(&info->card->card_lock, flags); cnow = info->icount; spin_unlock_irqrestore(&info->card->card_lock, flags); - p_cuser = argp; - ret_val = put_user(cnow.cts, &p_cuser->cts); - if (ret_val) - break; - ret_val = put_user(cnow.dsr, &p_cuser->dsr); - if (ret_val) - break; - ret_val = put_user(cnow.rng, &p_cuser->rng); - if (ret_val) - break; - ret_val = put_user(cnow.dcd, &p_cuser->dcd); - if (ret_val) - break; - ret_val = put_user(cnow.rx, &p_cuser->rx); - if (ret_val) - break; - ret_val = put_user(cnow.tx, &p_cuser->tx); - if (ret_val) - break; - ret_val = put_user(cnow.frame, &p_cuser->frame); - if (ret_val) - break; - ret_val = put_user(cnow.overrun, &p_cuser->overrun); - if (ret_val) - break; - ret_val = put_user(cnow.parity, &p_cuser->parity); - if (ret_val) - break; - ret_val = put_user(cnow.brk, &p_cuser->brk); - if (ret_val) - break; - ret_val = put_user(cnow.buf_overrun, &p_cuser->buf_overrun); - if (ret_val) - break; - ret_val = 0; + + sic.cts = cnow.cts; + sic.dsr = cnow.dsr; + sic.rng = cnow.rng; + sic.dcd = cnow.dcd; + sic.rx = cnow.rx; + sic.tx = cnow.tx; + sic.frame = cnow.frame; + sic.overrun = cnow.overrun; + sic.parity = cnow.parity; + sic.brk = cnow.brk; + sic.buf_overrun = cnow.buf_overrun; + + if (copy_to_user(argp, &sic, sizeof(sic))) + ret_val = -EFAULT; break; + } default: ret_val = -ENOIOCTLCMD; } @@ -4055,7 +2846,7 @@ static void cy_set_termios(struct tty_struct *tty, struct ktermios *old_termios) printk(KERN_DEBUG "cyc:cy_set_termios ttyC%d\n", info->line); #endif - set_line_char(info); + cy_set_line_char(info, tty); if ((old_termios->c_cflag & CRTSCTS) && !(tty->termios->c_cflag & CRTSCTS)) { @@ -4112,8 +2903,6 @@ static void cy_throttle(struct tty_struct *tty) struct cyclades_port *info = tty->driver_data; struct cyclades_card *card; unsigned long flags; - void __iomem *base_addr; - int chip, channel, index; #ifdef CY_DEBUG_THROTTLE char buf[64]; @@ -4135,24 +2924,9 @@ static void cy_throttle(struct tty_struct *tty) } if (tty->termios->c_cflag & CRTSCTS) { - channel = info->line - card->first_line; if (!cy_is_Z(card)) { - chip = channel >> 2; - channel &= 0x03; - index = card->bus_index; - base_addr = card->base_addr + - (cy_chip_offset[chip] << index); - spin_lock_irqsave(&card->card_lock, flags); - cy_writeb(base_addr + (CyCAR << index), - (u_char) channel); - if (info->rtsdtr_inv) { - cy_writeb(base_addr + (CyMSVR2 << index), - ~CyDTR); - } else { - cy_writeb(base_addr + (CyMSVR1 << index), - ~CyRTS); - } + cyy_change_rts_dtr(info, 0, TIOCM_RTS); spin_unlock_irqrestore(&card->card_lock, flags); } else { info->throttle = 1; @@ -4170,8 +2944,6 @@ static void cy_unthrottle(struct tty_struct *tty) struct cyclades_port *info = tty->driver_data; struct cyclades_card *card; unsigned long flags; - void __iomem *base_addr; - int chip, channel, index; #ifdef CY_DEBUG_THROTTLE char buf[64]; @@ -4192,24 +2964,9 @@ static void cy_unthrottle(struct tty_struct *tty) if (tty->termios->c_cflag & CRTSCTS) { card = info->card; - channel = info->line - card->first_line; if (!cy_is_Z(card)) { - chip = channel >> 2; - channel &= 0x03; - index = card->bus_index; - base_addr = card->base_addr + - (cy_chip_offset[chip] << index); - spin_lock_irqsave(&card->card_lock, flags); - cy_writeb(base_addr + (CyCAR << index), - (u_char) channel); - if (info->rtsdtr_inv) { - cy_writeb(base_addr + (CyMSVR2 << index), - CyDTR); - } else { - cy_writeb(base_addr + (CyMSVR1 << index), - CyRTS); - } + cyy_change_rts_dtr(info, TIOCM_RTS, 0); spin_unlock_irqrestore(&card->card_lock, flags); } else { info->throttle = 0; @@ -4224,8 +2981,7 @@ static void cy_stop(struct tty_struct *tty) { struct cyclades_card *cinfo; struct cyclades_port *info = tty->driver_data; - void __iomem *base_addr; - int chip, channel, index; + int channel; unsigned long flags; #ifdef CY_DEBUG_OTHER @@ -4238,16 +2994,9 @@ static void cy_stop(struct tty_struct *tty) cinfo = info->card; channel = info->line - cinfo->first_line; if (!cy_is_Z(cinfo)) { - index = cinfo->bus_index; - chip = channel >> 2; - channel &= 0x03; - base_addr = cinfo->base_addr + (cy_chip_offset[chip] << index); - spin_lock_irqsave(&cinfo->card_lock, flags); - cy_writeb(base_addr + (CyCAR << index), - (u_char)(channel & 0x0003)); /* index channel */ - cy_writeb(base_addr + (CySRER << index), - readb(base_addr + (CySRER << index)) & ~CyTxRdy); + cyy_writeb(info, CyCAR, channel & 0x03); + cyy_writeb(info, CySRER, cyy_readb(info, CySRER) & ~CyTxRdy); spin_unlock_irqrestore(&cinfo->card_lock, flags); } } /* cy_stop */ @@ -4256,8 +3005,7 @@ static void cy_start(struct tty_struct *tty) { struct cyclades_card *cinfo; struct cyclades_port *info = tty->driver_data; - void __iomem *base_addr; - int chip, channel, index; + int channel; unsigned long flags; #ifdef CY_DEBUG_OTHER @@ -4269,17 +3017,10 @@ static void cy_start(struct tty_struct *tty) cinfo = info->card; channel = info->line - cinfo->first_line; - index = cinfo->bus_index; if (!cy_is_Z(cinfo)) { - chip = channel >> 2; - channel &= 0x03; - base_addr = cinfo->base_addr + (cy_chip_offset[chip] << index); - spin_lock_irqsave(&cinfo->card_lock, flags); - cy_writeb(base_addr + (CyCAR << index), - (u_char) (channel & 0x0003)); /* index channel */ - cy_writeb(base_addr + (CySRER << index), - readb(base_addr + (CySRER << index)) | CyTxRdy); + cyy_writeb(info, CyCAR, channel & 0x03); + cyy_writeb(info, CySRER, cyy_readb(info, CySRER) | CyTxRdy); spin_unlock_irqrestore(&cinfo->card_lock, flags); } } /* cy_start */ @@ -4299,17 +3040,84 @@ static void cy_hangup(struct tty_struct *tty) return; cy_flush_buffer(tty); - shutdown(info); - info->port.count = 0; -#ifdef CY_DEBUG_COUNT - printk(KERN_DEBUG "cyc:cy_hangup (%d): setting count to 0\n", - current->pid); -#endif - info->port.tty = NULL; - info->port.flags &= ~ASYNC_NORMAL_ACTIVE; - wake_up_interruptible(&info->port.open_wait); + cy_shutdown(info, tty); + tty_port_hangup(&info->port); } /* cy_hangup */ +static int cyy_carrier_raised(struct tty_port *port) +{ + struct cyclades_port *info = container_of(port, struct cyclades_port, + port); + struct cyclades_card *cinfo = info->card; + unsigned long flags; + int channel = info->line - cinfo->first_line; + u32 cd; + + spin_lock_irqsave(&cinfo->card_lock, flags); + cyy_writeb(info, CyCAR, channel & 0x03); + cd = cyy_readb(info, CyMSVR1) & CyDCD; + spin_unlock_irqrestore(&cinfo->card_lock, flags); + + return cd; +} + +static void cyy_dtr_rts(struct tty_port *port, int raise) +{ + struct cyclades_port *info = container_of(port, struct cyclades_port, + port); + struct cyclades_card *cinfo = info->card; + unsigned long flags; + + spin_lock_irqsave(&cinfo->card_lock, flags); + cyy_change_rts_dtr(info, raise ? TIOCM_RTS | TIOCM_DTR : 0, + raise ? 0 : TIOCM_RTS | TIOCM_DTR); + spin_unlock_irqrestore(&cinfo->card_lock, flags); +} + +static int cyz_carrier_raised(struct tty_port *port) +{ + struct cyclades_port *info = container_of(port, struct cyclades_port, + port); + + return readl(&info->u.cyz.ch_ctrl->rs_status) & C_RS_DCD; +} + +static void cyz_dtr_rts(struct tty_port *port, int raise) +{ + struct cyclades_port *info = container_of(port, struct cyclades_port, + port); + struct cyclades_card *cinfo = info->card; + struct CH_CTRL __iomem *ch_ctrl = info->u.cyz.ch_ctrl; + int ret, channel = info->line - cinfo->first_line; + u32 rs; + + rs = readl(&ch_ctrl->rs_control); + if (raise) + rs |= C_RS_RTS | C_RS_DTR; + else + rs &= ~(C_RS_RTS | C_RS_DTR); + cy_writel(&ch_ctrl->rs_control, rs); + ret = cyz_issue_cmd(cinfo, channel, C_CM_IOCTLM, 0L); + if (ret != 0) + printk(KERN_ERR "%s: retval on ttyC%d was %x\n", + __func__, info->line, ret); +#ifdef CY_DEBUG_DTR + printk(KERN_DEBUG "%s: raising Z DTR\n", __func__); +#endif +} + +static const struct tty_port_operations cyy_port_ops = { + .carrier_raised = cyy_carrier_raised, + .dtr_rts = cyy_dtr_rts, + .shutdown = cy_do_close, +}; + +static const struct tty_port_operations cyz_port_ops = { + .carrier_raised = cyz_carrier_raised, + .dtr_rts = cyz_dtr_rts, + .shutdown = cy_do_close, +}; + /* * --------------------------------------------------------------------- * cy_init() and friends @@ -4321,8 +3129,7 @@ static void cy_hangup(struct tty_struct *tty) static int __devinit cy_init_card(struct cyclades_card *cinfo) { struct cyclades_port *info; - unsigned int port; - unsigned short chip_number; + unsigned int channel, port; spin_lock_init(&cinfo->card_lock); cinfo->intr_enabled = 0; @@ -4334,9 +3141,9 @@ static int __devinit cy_init_card(struct cyclades_card *cinfo) return -ENOMEM; } - for (port = cinfo->first_line; port < cinfo->first_line + cinfo->nports; - port++) { - info = &cinfo->ports[port - cinfo->first_line]; + for (channel = 0, port = cinfo->first_line; channel < cinfo->nports; + channel++, port++) { + info = &cinfo->ports[channel]; tty_port_init(&info->port); info->magic = CYCLADES_MAGIC; info->card = cinfo; @@ -4346,10 +3153,19 @@ static int __devinit cy_init_card(struct cyclades_card *cinfo) info->port.close_delay = 5 * HZ / 10; info->port.flags = STD_COM_FLAGS; init_completion(&info->shutdown_wait); - init_waitqueue_head(&info->delta_msr_wait); if (cy_is_Z(cinfo)) { + struct FIRM_ID *firm_id = cinfo->base_addr + ID_ADDRESS; + struct ZFW_CTRL *zfw_ctrl; + + info->port.ops = &cyz_port_ops; info->type = PORT_STARTECH; + + zfw_ctrl = cinfo->base_addr + + (readl(&firm_id->zfwctrl_addr) & 0xfffff); + info->u.cyz.ch_ctrl = &zfw_ctrl->ch_ctrl[channel]; + info->u.cyz.buf_ctrl = &zfw_ctrl->buf_ctrl[channel]; + if (cinfo->hw_ver == ZO_V1) info->xmit_fifo_size = CYZ_FIFO_SIZE; else @@ -4359,17 +3175,20 @@ static int __devinit cy_init_card(struct cyclades_card *cinfo) cyz_rx_restart, (unsigned long)info); #endif } else { + unsigned short chip_number; int index = cinfo->bus_index; + + info->port.ops = &cyy_port_ops; info->type = PORT_CIRRUS; info->xmit_fifo_size = CyMAX_CHAR_FIFO; info->cor1 = CyPARITY_NONE | Cy_1_STOP | Cy_8_BITS; info->cor2 = CyETC; info->cor3 = 0x08; /* _very_ small rcv threshold */ - chip_number = (port - cinfo->first_line) / 4; - info->chip_rev = readb(cinfo->base_addr + - (cy_chip_offset[chip_number] << index) + - (CyGFRCR << index)); + chip_number = channel / CyPORTS_PER_CHIP; + info->u.cyy.base_addr = cinfo->base_addr + + (cy_chip_offset[chip_number] << index); + info->chip_rev = cyy_readb(info, CyGFRCR); if (info->chip_rev >= CD1400_REV_J) { /* It is a CD1400 rev. J or later */ @@ -5060,8 +3879,14 @@ static int __devinit cy_pci_probe(struct pci_dev *pdev, } cy_card[card_no].num_chips = nchan / CyPORTS_PER_CHIP; } else { + struct FIRM_ID __iomem *firm_id = addr2 + ID_ADDRESS; + struct ZFW_CTRL __iomem *zfw_ctrl; + + zfw_ctrl = addr2 + (readl(&firm_id->zfwctrl_addr) & 0xfffff); + cy_card[card_no].hw_ver = mailbox; cy_card[card_no].num_chips = (unsigned int)-1; + cy_card[card_no].board_ctrl = &zfw_ctrl->board_ctrl; #ifdef CONFIG_CYZ_INTR /* allocate IRQ only if board has an IRQ */ if (irq != 0 && irq != 255) { @@ -5191,18 +4016,30 @@ static int cyclades_proc_show(struct seq_file *m, void *v) for (j = 0; j < cy_card[i].nports; j++) { info = &cy_card[i].ports[j]; - if (info->port.count) + if (info->port.count) { + /* XXX is the ldisc num worth this? */ + struct tty_struct *tty; + struct tty_ldisc *ld; + int num = 0; + tty = tty_port_tty_get(&info->port); + if (tty) { + ld = tty_ldisc_ref(tty); + if (ld) { + num = ld->ops->num; + tty_ldisc_deref(ld); + } + tty_kref_put(tty); + } seq_printf(m, "%3d %8lu %10lu %8lu " - "%10lu %8lu %9lu %6ld\n", info->line, + "%10lu %8lu %9lu %6d\n", info->line, (cur_jifs - info->idle_stats.in_use) / HZ, info->idle_stats.xmit_bytes, (cur_jifs - info->idle_stats.xmit_idle)/ HZ, info->idle_stats.recv_bytes, (cur_jifs - info->idle_stats.recv_idle)/ HZ, info->idle_stats.overruns, - /* FIXME: double check locking */ - (long)info->port.tty->ldisc->ops->num); - else + num); + } else seq_printf(m, "%3d %8lu %10lu %8lu " "%10lu %8lu %9lu %6ld\n", info->line, 0L, 0L, 0L, 0L, 0L, 0L, 0L); diff --git a/drivers/char/epca.c b/drivers/char/epca.c index ff647ca..9d589e3 100644 --- a/drivers/char/epca.c +++ b/drivers/char/epca.c @@ -2239,7 +2239,7 @@ static void do_softint(struct work_struct *work) struct channel *ch = container_of(work, struct channel, tqueue); /* Called in response to a modem change event */ if (ch && ch->magic == EPCA_MAGIC) { - struct tty_struct *tty = tty_port_tty_get(&ch->port);; + struct tty_struct *tty = tty_port_tty_get(&ch->port); if (tty && tty->driver_data) { if (test_and_clear_bit(EPCA_EVENT_HANGUP, &ch->event)) { diff --git a/drivers/char/esp.c b/drivers/char/esp.c index a5c59fc..b19d43c 100644 --- a/drivers/char/esp.c +++ b/drivers/char/esp.c @@ -572,7 +572,7 @@ static void check_modem_status(struct esp_struct *info) info->icount.dcd++; if (status & UART_MSR_DCTS) info->icount.cts++; - wake_up_interruptible(&info->delta_msr_wait); + wake_up_interruptible(&info->port.delta_msr_wait); } if ((info->port.flags & ASYNC_CHECK_CD) && (status & UART_MSR_DDCD)) { @@ -927,7 +927,7 @@ static void shutdown(struct esp_struct *info) * clear delta_msr_wait queue to avoid mem leaks: we may free the irq * here so the queue might never be waken up */ - wake_up_interruptible(&info->delta_msr_wait); + wake_up_interruptible(&info->port.delta_msr_wait); wake_up_interruptible(&info->break_wait); /* stop a DMA transfer on the port being closed */ @@ -1800,7 +1800,7 @@ static int rs_ioctl(struct tty_struct *tty, struct file *file, spin_unlock_irqrestore(&info->lock, flags); while (1) { /* FIXME: convert to new style wakeup */ - interruptible_sleep_on(&info->delta_msr_wait); + interruptible_sleep_on(&info->port.delta_msr_wait); /* see if a signal did it */ if (signal_pending(current)) return -ERESTARTSYS; @@ -2452,7 +2452,6 @@ static int __init espserial_init(void) info->config.flow_off = flow_off; info->config.pio_threshold = pio_threshold; info->next_port = ports; - init_waitqueue_head(&info->delta_msr_wait); init_waitqueue_head(&info->break_wait); ports = info; printk(KERN_INFO "ttyP%d at 0x%04x (irq = %d) is an ESP ", diff --git a/drivers/char/generic_nvram.c b/drivers/char/generic_nvram.c index a00869c..ef31738 100644 --- a/drivers/char/generic_nvram.c +++ b/drivers/char/generic_nvram.c @@ -2,7 +2,7 @@ * Generic /dev/nvram driver for architectures providing some * "generic" hooks, that is : * - * nvram_read_byte, nvram_write_byte, nvram_sync + * nvram_read_byte, nvram_write_byte, nvram_sync, nvram_get_size * * Note that an additional hook is supported for PowerMac only * for getting the nvram "partition" informations @@ -28,6 +28,8 @@ #define NVRAM_SIZE 8192 +static ssize_t nvram_len; + static loff_t nvram_llseek(struct file *file, loff_t offset, int origin) { lock_kernel(); @@ -36,7 +38,7 @@ static loff_t nvram_llseek(struct file *file, loff_t offset, int origin) offset += file->f_pos; break; case 2: - offset += NVRAM_SIZE; + offset += nvram_len; break; } if (offset < 0) { @@ -56,9 +58,9 @@ static ssize_t read_nvram(struct file *file, char __user *buf, if (!access_ok(VERIFY_WRITE, buf, count)) return -EFAULT; - if (*ppos >= NVRAM_SIZE) + if (*ppos >= nvram_len) return 0; - for (i = *ppos; count > 0 && i < NVRAM_SIZE; ++i, ++p, --count) + for (i = *ppos; count > 0 && i < nvram_len; ++i, ++p, --count) if (__put_user(nvram_read_byte(i), p)) return -EFAULT; *ppos = i; @@ -74,9 +76,9 @@ static ssize_t write_nvram(struct file *file, const char __user *buf, if (!access_ok(VERIFY_READ, buf, count)) return -EFAULT; - if (*ppos >= NVRAM_SIZE) + if (*ppos >= nvram_len) return 0; - for (i = *ppos; count > 0 && i < NVRAM_SIZE; ++i, ++p, --count) { + for (i = *ppos; count > 0 && i < nvram_len; ++i, ++p, --count) { if (__get_user(c, p)) return -EFAULT; nvram_write_byte(c, i); @@ -133,9 +135,20 @@ static struct miscdevice nvram_dev = { int __init nvram_init(void) { + int ret = 0; + printk(KERN_INFO "Generic non-volatile memory driver v%s\n", NVRAM_VERSION); - return misc_register(&nvram_dev); + ret = misc_register(&nvram_dev); + if (ret != 0) + goto out; + + nvram_len = nvram_get_size(); + if (nvram_len < 0) + nvram_len = NVRAM_SIZE; + +out: + return ret; } void __exit nvram_cleanup(void) diff --git a/drivers/char/hpet.c b/drivers/char/hpet.c index 4a9f349..70a770a 100644 --- a/drivers/char/hpet.c +++ b/drivers/char/hpet.c @@ -166,9 +166,8 @@ static irqreturn_t hpet_interrupt(int irq, void *data) unsigned long m, t; t = devp->hd_ireqfreq; - m = read_counter(&devp->hd_hpet->hpet_mc); - write_counter(t + m + devp->hd_hpets->hp_delta, - &devp->hd_timer->hpet_compare); + m = read_counter(&devp->hd_timer->hpet_compare); + write_counter(t + m, &devp->hd_timer->hpet_compare); } if (devp->hd_flags & HPET_SHARED_IRQ) @@ -504,21 +503,25 @@ static int hpet_ioctl_ieon(struct hpet_dev *devp) g = v | Tn_32MODE_CNF_MASK | Tn_INT_ENB_CNF_MASK; if (devp->hd_flags & HPET_PERIODIC) { - write_counter(t, &timer->hpet_compare); g |= Tn_TYPE_CNF_MASK; - v |= Tn_TYPE_CNF_MASK; - writeq(v, &timer->hpet_config); - v |= Tn_VAL_SET_CNF_MASK; + v |= Tn_TYPE_CNF_MASK | Tn_VAL_SET_CNF_MASK; writeq(v, &timer->hpet_config); local_irq_save(flags); - /* NOTE: what we modify here is a hidden accumulator + /* + * NOTE: First we modify the hidden accumulator * register supported by periodic-capable comparators. * We never want to modify the (single) counter; that - * would affect all the comparators. + * would affect all the comparators. The value written + * is the counter value when the first interrupt is due. */ m = read_counter(&hpet->hpet_mc); write_counter(t + m + hpetp->hp_delta, &timer->hpet_compare); + /* + * Then we modify the comparator, indicating the period + * for subsequent interrupt. + */ + write_counter(t, &timer->hpet_compare); } else { local_irq_save(flags); m = read_counter(&hpet->hpet_mc); diff --git a/drivers/char/hvc_console.c b/drivers/char/hvc_console.c index d97779e..a632f25 100644 --- a/drivers/char/hvc_console.c +++ b/drivers/char/hvc_console.c @@ -516,8 +516,6 @@ static void hvc_set_winsz(struct work_struct *work) struct winsize ws; hp = container_of(work, struct hvc_struct, tty_resize); - if (!hp) - return; spin_lock_irqsave(&hp->lock, hvc_flags); if (!hp->tty) { @@ -680,7 +678,7 @@ int hvc_poll(struct hvc_struct *hp) EXPORT_SYMBOL_GPL(hvc_poll); /** - * hvc_resize() - Update terminal window size information. + * __hvc_resize() - Update terminal window size information. * @hp: HVC console pointer * @ws: Terminal window size structure * @@ -689,12 +687,12 @@ EXPORT_SYMBOL_GPL(hvc_poll); * * Locking: Locking free; the function MUST be called holding hp->lock */ -void hvc_resize(struct hvc_struct *hp, struct winsize ws) +void __hvc_resize(struct hvc_struct *hp, struct winsize ws) { hp->ws = ws; schedule_work(&hp->tty_resize); } -EXPORT_SYMBOL_GPL(hvc_resize); +EXPORT_SYMBOL_GPL(__hvc_resize); /* * This kthread is either polling or interrupt driven. This is determined by diff --git a/drivers/char/hvc_console.h b/drivers/char/hvc_console.h index 3c85d78..10950ca 100644 --- a/drivers/char/hvc_console.h +++ b/drivers/char/hvc_console.h @@ -28,6 +28,7 @@ #define HVC_CONSOLE_H #include <linux/kref.h> #include <linux/tty.h> +#include <linux/spinlock.h> /* * This is the max number of console adapters that can/will be found as @@ -88,7 +89,16 @@ int hvc_poll(struct hvc_struct *hp); void hvc_kick(void); /* Resize hvc tty terminal window */ -extern void hvc_resize(struct hvc_struct *hp, struct winsize ws); +extern void __hvc_resize(struct hvc_struct *hp, struct winsize ws); + +static inline void hvc_resize(struct hvc_struct *hp, struct winsize ws) +{ + unsigned long flags; + + spin_lock_irqsave(&hp->lock, flags); + __hvc_resize(hp, ws); + spin_unlock_irqrestore(&hp->lock, flags); +} /* default notifier for irq based notification */ extern int notifier_add_irq(struct hvc_struct *hp, int data); diff --git a/drivers/char/hvc_iucv.c b/drivers/char/hvc_iucv.c index 0ecac7e..b8a5d65 100644 --- a/drivers/char/hvc_iucv.c +++ b/drivers/char/hvc_iucv.c @@ -273,7 +273,9 @@ static int hvc_iucv_write(struct hvc_iucv_private *priv, case MSG_TYPE_WINSIZE: if (rb->mbuf->datalen != sizeof(struct winsize)) break; - hvc_resize(priv->hvc, *((struct winsize *) rb->mbuf->data)); + /* The caller must ensure that the hvc is locked, which + * is the case when called from hvc_iucv_get_chars() */ + __hvc_resize(priv->hvc, *((struct winsize *) rb->mbuf->data)); break; case MSG_TYPE_ERROR: /* ignored ... */ diff --git a/drivers/char/hvc_vio.c b/drivers/char/hvc_vio.c index c72b994..10be343 100644 --- a/drivers/char/hvc_vio.c +++ b/drivers/char/hvc_vio.c @@ -120,7 +120,7 @@ static struct vio_driver hvc_vio_driver = { } }; -static int hvc_vio_init(void) +static int __init hvc_vio_init(void) { int rc; @@ -134,7 +134,7 @@ static int hvc_vio_init(void) } module_init(hvc_vio_init); /* after drivers/char/hvc_console.c */ -static void hvc_vio_exit(void) +static void __exit hvc_vio_exit(void) { vio_unregister_driver(&hvc_vio_driver); } diff --git a/drivers/char/hvsi.c b/drivers/char/hvsi.c index 2989056..793b236 100644 --- a/drivers/char/hvsi.c +++ b/drivers/char/hvsi.c @@ -1230,11 +1230,12 @@ static struct tty_driver *hvsi_console_device(struct console *console, static int __init hvsi_console_setup(struct console *console, char *options) { - struct hvsi_struct *hp = &hvsi_ports[console->index]; + struct hvsi_struct *hp; int ret; if (console->index < 0 || console->index >= hvsi_count) return -1; + hp = &hvsi_ports[console->index]; /* give the FSP a chance to change the baud rate when we re-open */ hvsi_close_protocol(hp); diff --git a/drivers/char/hw_random/Kconfig b/drivers/char/hw_random/Kconfig index ce66a70..8706026 100644 --- a/drivers/char/hw_random/Kconfig +++ b/drivers/char/hw_random/Kconfig @@ -126,6 +126,19 @@ config HW_RANDOM_OMAP If unsure, say Y. +config HW_RANDOM_OCTEON + tristate "Octeon Random Number Generator support" + depends on HW_RANDOM && CPU_CAVIUM_OCTEON + default HW_RANDOM + ---help--- + This driver provides kernel-side support for the Random Number + Generator hardware found on Octeon processors. + + To compile this driver as a module, choose M here: the + module will be called octeon-rng. + + If unsure, say Y. + config HW_RANDOM_PASEMI tristate "PA Semi HW Random Number Generator support" depends on HW_RANDOM && PPC_PASEMI diff --git a/drivers/char/hw_random/Makefile b/drivers/char/hw_random/Makefile index 676828b..5eeb130 100644 --- a/drivers/char/hw_random/Makefile +++ b/drivers/char/hw_random/Makefile @@ -17,3 +17,4 @@ obj-$(CONFIG_HW_RANDOM_PASEMI) += pasemi-rng.o obj-$(CONFIG_HW_RANDOM_VIRTIO) += virtio-rng.o obj-$(CONFIG_HW_RANDOM_TX4939) += tx4939-rng.o obj-$(CONFIG_HW_RANDOM_MXC_RNGA) += mxc-rnga.o +obj-$(CONFIG_HW_RANDOM_OCTEON) += octeon-rng.o diff --git a/drivers/char/hw_random/core.c b/drivers/char/hw_random/core.c index fc93e2f..1573aeb 100644 --- a/drivers/char/hw_random/core.c +++ b/drivers/char/hw_random/core.c @@ -153,7 +153,7 @@ static const struct file_operations rng_chrdev_ops = { static struct miscdevice rng_miscdev = { .minor = RNG_MISCDEV_MINOR, .name = RNG_MODULE_NAME, - .devnode = "hwrng", + .nodename = "hwrng", .fops = &rng_chrdev_ops, }; diff --git a/drivers/char/hw_random/octeon-rng.c b/drivers/char/hw_random/octeon-rng.c new file mode 100644 index 0000000..54b0d9b --- /dev/null +++ b/drivers/char/hw_random/octeon-rng.c @@ -0,0 +1,147 @@ +/* + * Hardware Random Number Generator support for Cavium Networks + * Octeon processor family. + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2009 Cavium Networks + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/device.h> +#include <linux/hw_random.h> +#include <linux/io.h> + +#include <asm/octeon/octeon.h> +#include <asm/octeon/cvmx-rnm-defs.h> + +struct octeon_rng { + struct hwrng ops; + void __iomem *control_status; + void __iomem *result; +}; + +static int octeon_rng_init(struct hwrng *rng) +{ + union cvmx_rnm_ctl_status ctl; + struct octeon_rng *p = container_of(rng, struct octeon_rng, ops); + + ctl.u64 = 0; + ctl.s.ent_en = 1; /* Enable the entropy source. */ + ctl.s.rng_en = 1; /* Enable the RNG hardware. */ + cvmx_write_csr((u64)p->control_status, ctl.u64); + return 0; +} + +static void octeon_rng_cleanup(struct hwrng *rng) +{ + union cvmx_rnm_ctl_status ctl; + struct octeon_rng *p = container_of(rng, struct octeon_rng, ops); + + ctl.u64 = 0; + /* Disable everything. */ + cvmx_write_csr((u64)p->control_status, ctl.u64); +} + +static int octeon_rng_data_read(struct hwrng *rng, u32 *data) +{ + struct octeon_rng *p = container_of(rng, struct octeon_rng, ops); + + *data = cvmx_read64_uint32((u64)p->result); + return sizeof(u32); +} + +static int __devinit octeon_rng_probe(struct platform_device *pdev) +{ + struct resource *res_ports; + struct resource *res_result; + struct octeon_rng *rng; + int ret; + struct hwrng ops = { + .name = "octeon", + .init = octeon_rng_init, + .cleanup = octeon_rng_cleanup, + .data_read = octeon_rng_data_read + }; + + rng = devm_kzalloc(&pdev->dev, sizeof(*rng), GFP_KERNEL); + if (!rng) + return -ENOMEM; + + res_ports = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res_ports) + goto err_ports; + + res_result = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!res_result) + goto err_ports; + + + rng->control_status = devm_ioremap_nocache(&pdev->dev, + res_ports->start, + sizeof(u64)); + if (!rng->control_status) + goto err_ports; + + rng->result = devm_ioremap_nocache(&pdev->dev, + res_result->start, + sizeof(u64)); + if (!rng->result) + goto err_r; + + rng->ops = ops; + + dev_set_drvdata(&pdev->dev, &rng->ops); + ret = hwrng_register(&rng->ops); + if (ret) + goto err; + + dev_info(&pdev->dev, "Octeon Random Number Generator\n"); + + return 0; +err: + devm_iounmap(&pdev->dev, rng->control_status); +err_r: + devm_iounmap(&pdev->dev, rng->result); +err_ports: + devm_kfree(&pdev->dev, rng); + return -ENOENT; +} + +static int __exit octeon_rng_remove(struct platform_device *pdev) +{ + struct hwrng *rng = dev_get_drvdata(&pdev->dev); + + hwrng_unregister(rng); + + return 0; +} + +static struct platform_driver octeon_rng_driver = { + .driver = { + .name = "octeon_rng", + .owner = THIS_MODULE, + }, + .probe = octeon_rng_probe, + .remove = __exit_p(octeon_rng_remove), +}; + +static int __init octeon_rng_mod_init(void) +{ + return platform_driver_register(&octeon_rng_driver); +} + +static void __exit octeon_rng_mod_exit(void) +{ + platform_driver_unregister(&octeon_rng_driver); +} + +module_init(octeon_rng_mod_init); +module_exit(octeon_rng_mod_exit); + +MODULE_AUTHOR("David Daney"); +MODULE_LICENSE("GPL"); diff --git a/drivers/char/hw_random/virtio-rng.c b/drivers/char/hw_random/virtio-rng.c index 32216b6..962968f 100644 --- a/drivers/char/hw_random/virtio-rng.c +++ b/drivers/char/hw_random/virtio-rng.c @@ -21,6 +21,7 @@ #include <linux/scatterlist.h> #include <linux/spinlock.h> #include <linux/virtio.h> +#include <linux/virtio_ids.h> #include <linux/virtio_rng.h> /* The host will fill any buffer we give it with sweet, sweet randomness. We @@ -51,7 +52,7 @@ static void register_buffer(void) sg_init_one(&sg, random_data+data_left, RANDOM_DATA_SIZE-data_left); /* There should always be room for one buffer. */ - if (vq->vq_ops->add_buf(vq, &sg, 0, 1, random_data) != 0) + if (vq->vq_ops->add_buf(vq, &sg, 0, 1, random_data) < 0) BUG(); vq->vq_ops->kick(vq); } diff --git a/drivers/char/ipmi/ipmi_poweroff.c b/drivers/char/ipmi/ipmi_poweroff.c index a261bd7..2e66b5f 100644 --- a/drivers/char/ipmi/ipmi_poweroff.c +++ b/drivers/char/ipmi/ipmi_poweroff.c @@ -691,7 +691,7 @@ static struct ctl_table_header *ipmi_table_header; /* * Startup and shutdown functions. */ -static int ipmi_poweroff_init(void) +static int __init ipmi_poweroff_init(void) { int rv; @@ -725,7 +725,7 @@ static int ipmi_poweroff_init(void) } #ifdef MODULE -static __exit void ipmi_poweroff_cleanup(void) +static void __exit ipmi_poweroff_cleanup(void) { int rv; diff --git a/drivers/char/isicom.c b/drivers/char/isicom.c index 4f1f4cd..426bfdd 100644 --- a/drivers/char/isicom.c +++ b/drivers/char/isicom.c @@ -846,37 +846,53 @@ static int isicom_carrier_raised(struct tty_port *port) return (ip->status & ISI_DCD)?1 : 0; } -static int isicom_open(struct tty_struct *tty, struct file *filp) +static struct tty_port *isicom_find_port(struct tty_struct *tty) { struct isi_port *port; struct isi_board *card; unsigned int board; - int error, line; + int line = tty->index; - line = tty->index; if (line < 0 || line > PORT_COUNT-1) - return -ENODEV; + return NULL; board = BOARD(line); card = &isi_card[board]; if (!(card->status & FIRMWARE_LOADED)) - return -ENODEV; + return NULL; /* open on a port greater than the port count for the card !!! */ if (line > ((board * 16) + card->port_count - 1)) - return -ENODEV; + return NULL; port = &isi_ports[line]; if (isicom_paranoia_check(port, tty->name, "isicom_open")) - return -ENODEV; + return NULL; + return &port->port; +} + +static int isicom_open(struct tty_struct *tty, struct file *filp) +{ + struct isi_port *port; + struct isi_board *card; + struct tty_port *tport; + int error = 0; + + tport = isicom_find_port(tty); + if (tport == NULL) + return -ENODEV; + port = container_of(tport, struct isi_port, port); + card = &isi_card[BOARD(tty->index)]; isicom_setup_board(card); /* FIXME: locking on port.count etc */ port->port.count++; tty->driver_data = port; tty_port_tty_set(&port->port, tty); - error = isicom_setup_port(tty); + /* FIXME: Locking on Initialized flag */ + if (!test_bit(ASYNCB_INITIALIZED, &tport->flags)) + error = isicom_setup_port(tty); if (error == 0) error = tty_port_block_til_ready(&port->port, tty, filp); return error; @@ -952,19 +968,12 @@ static void isicom_flush_buffer(struct tty_struct *tty) tty_wakeup(tty); } -static void isicom_close(struct tty_struct *tty, struct file *filp) +static void isicom_close_port(struct tty_port *port) { - struct isi_port *ip = tty->driver_data; - struct tty_port *port = &ip->port; - struct isi_board *card; + struct isi_port *ip = container_of(port, struct isi_port, port); + struct isi_board *card = ip->card; unsigned long flags; - BUG_ON(!ip); - - card = ip->card; - if (isicom_paranoia_check(ip, tty->name, "isicom_close")) - return; - /* indicate to the card that no more data can be received on this port */ spin_lock_irqsave(&card->card_lock, flags); @@ -974,9 +983,19 @@ static void isicom_close(struct tty_struct *tty, struct file *filp) } isicom_shutdown_port(ip); spin_unlock_irqrestore(&card->card_lock, flags); +} + +static void isicom_close(struct tty_struct *tty, struct file *filp) +{ + struct isi_port *ip = tty->driver_data; + struct tty_port *port = &ip->port; + if (isicom_paranoia_check(ip, tty->name, "isicom_close")) + return; + if (tty_port_close_start(port, tty, filp) == 0) + return; + isicom_close_port(port); isicom_flush_buffer(tty); - tty_port_close_end(port, tty); } diff --git a/drivers/char/mbcs.c b/drivers/char/mbcs.c index acd8e9e..87c67b4 100644 --- a/drivers/char/mbcs.c +++ b/drivers/char/mbcs.c @@ -15,6 +15,7 @@ #include <linux/moduleparam.h> #include <linux/types.h> #include <linux/ioport.h> +#include <linux/kernel.h> #include <linux/notifier.h> #include <linux/reboot.h> #include <linux/init.h> @@ -715,8 +716,8 @@ static ssize_t show_algo(struct device *dev, struct device_attribute *attr, char */ debug0 = *(uint64_t *) soft->debug_addr; - return sprintf(buf, "0x%lx 0x%lx\n", - (debug0 >> 32), (debug0 & 0xffffffff)); + return sprintf(buf, "0x%x 0x%x\n", + upper_32_bits(debug0), lower_32_bits(debug0)); } static ssize_t store_algo(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) diff --git a/drivers/char/mem.c b/drivers/char/mem.c index 645237b..a074fce 100644 --- a/drivers/char/mem.c +++ b/drivers/char/mem.c @@ -301,7 +301,7 @@ static inline int private_mapping_ok(struct vm_area_struct *vma) } #endif -static struct vm_operations_struct mmap_mem_ops = { +static const struct vm_operations_struct mmap_mem_ops = { #ifdef CONFIG_HAVE_IOREMAP_PROT .access = generic_access_phys #endif @@ -690,7 +690,7 @@ static ssize_t read_zero(struct file * file, char __user * buf, if (chunk > PAGE_SIZE) chunk = PAGE_SIZE; /* Just for latency reasons */ - unwritten = clear_user(buf, chunk); + unwritten = __clear_user(buf, chunk); written += chunk - unwritten; if (unwritten) break; @@ -864,71 +864,75 @@ static const struct file_operations kmsg_fops = { .write = kmsg_write, }; -static const struct { - unsigned int minor; - char *name; - umode_t mode; - const struct file_operations *fops; - struct backing_dev_info *dev_info; -} devlist[] = { /* list of minor devices */ - {1, "mem", S_IRUSR | S_IWUSR | S_IRGRP, &mem_fops, - &directly_mappable_cdev_bdi}, +static const struct memdev { + const char *name; + mode_t mode; + const struct file_operations *fops; + struct backing_dev_info *dev_info; +} devlist[] = { + [1] = { "mem", 0, &mem_fops, &directly_mappable_cdev_bdi }, #ifdef CONFIG_DEVKMEM - {2, "kmem", S_IRUSR | S_IWUSR | S_IRGRP, &kmem_fops, - &directly_mappable_cdev_bdi}, + [2] = { "kmem", 0, &kmem_fops, &directly_mappable_cdev_bdi }, #endif - {3, "null", S_IRUGO | S_IWUGO, &null_fops, NULL}, + [3] = { "null", 0666, &null_fops, NULL }, #ifdef CONFIG_DEVPORT - {4, "port", S_IRUSR | S_IWUSR | S_IRGRP, &port_fops, NULL}, + [4] = { "port", 0, &port_fops, NULL }, #endif - {5, "zero", S_IRUGO | S_IWUGO, &zero_fops, &zero_bdi}, - {7, "full", S_IRUGO | S_IWUGO, &full_fops, NULL}, - {8, "random", S_IRUGO | S_IWUSR, &random_fops, NULL}, - {9, "urandom", S_IRUGO | S_IWUSR, &urandom_fops, NULL}, - {11,"kmsg", S_IRUGO | S_IWUSR, &kmsg_fops, NULL}, + [5] = { "zero", 0666, &zero_fops, &zero_bdi }, + [7] = { "full", 0666, &full_fops, NULL }, + [8] = { "random", 0666, &random_fops, NULL }, + [9] = { "urandom", 0666, &urandom_fops, NULL }, + [11] = { "kmsg", 0, &kmsg_fops, NULL }, #ifdef CONFIG_CRASH_DUMP - {12,"oldmem", S_IRUSR | S_IWUSR | S_IRGRP, &oldmem_fops, NULL}, + [12] = { "oldmem", 0, &oldmem_fops, NULL }, #endif }; static int memory_open(struct inode *inode, struct file *filp) { - int ret = 0; - int i; + int minor; + const struct memdev *dev; + int ret = -ENXIO; lock_kernel(); - for (i = 0; i < ARRAY_SIZE(devlist); i++) { - if (devlist[i].minor == iminor(inode)) { - filp->f_op = devlist[i].fops; - if (devlist[i].dev_info) { - filp->f_mapping->backing_dev_info = - devlist[i].dev_info; - } + minor = iminor(inode); + if (minor >= ARRAY_SIZE(devlist)) + goto out; - break; - } - } + dev = &devlist[minor]; + if (!dev->fops) + goto out; - if (i == ARRAY_SIZE(devlist)) - ret = -ENXIO; - else - if (filp->f_op && filp->f_op->open) - ret = filp->f_op->open(inode, filp); + filp->f_op = dev->fops; + if (dev->dev_info) + filp->f_mapping->backing_dev_info = dev->dev_info; + if (dev->fops->open) + ret = dev->fops->open(inode, filp); + else + ret = 0; +out: unlock_kernel(); return ret; } static const struct file_operations memory_fops = { - .open = memory_open, /* just a selector for the real open */ + .open = memory_open, }; +static char *mem_devnode(struct device *dev, mode_t *mode) +{ + if (mode && devlist[MINOR(dev->devt)].mode) + *mode = devlist[MINOR(dev->devt)].mode; + return NULL; +} + static struct class *mem_class; static int __init chr_dev_init(void) { - int i; + int minor; int err; err = bdi_init(&zero_bdi); @@ -939,10 +943,13 @@ static int __init chr_dev_init(void) printk("unable to get major %d for memory devs\n", MEM_MAJOR); mem_class = class_create(THIS_MODULE, "mem"); - for (i = 0; i < ARRAY_SIZE(devlist); i++) - device_create(mem_class, NULL, - MKDEV(MEM_MAJOR, devlist[i].minor), NULL, - devlist[i].name); + mem_class->devnode = mem_devnode; + for (minor = 1; minor < ARRAY_SIZE(devlist); minor++) { + if (!devlist[minor].name) + continue; + device_create(mem_class, NULL, MKDEV(MEM_MAJOR, minor), + NULL, devlist[minor].name); + } return 0; } diff --git a/drivers/char/misc.c b/drivers/char/misc.c index 62c99fa..07fa612 100644 --- a/drivers/char/misc.c +++ b/drivers/char/misc.c @@ -91,7 +91,7 @@ static int misc_seq_show(struct seq_file *seq, void *v) } -static struct seq_operations misc_seq_ops = { +static const struct seq_operations misc_seq_ops = { .start = misc_seq_start, .next = misc_seq_next, .stop = misc_seq_stop, @@ -263,12 +263,14 @@ int misc_deregister(struct miscdevice *misc) EXPORT_SYMBOL(misc_register); EXPORT_SYMBOL(misc_deregister); -static char *misc_nodename(struct device *dev) +static char *misc_devnode(struct device *dev, mode_t *mode) { struct miscdevice *c = dev_get_drvdata(dev); - if (c->devnode) - return kstrdup(c->devnode, GFP_KERNEL); + if (mode && c->mode) + *mode = c->mode; + if (c->nodename) + return kstrdup(c->nodename, GFP_KERNEL); return NULL; } @@ -287,7 +289,7 @@ static int __init misc_init(void) err = -EIO; if (register_chrdev(MISC_MAJOR,"misc",&misc_fops)) goto fail_printk; - misc_class->nodename = misc_nodename; + misc_class->devnode = misc_devnode; return 0; fail_printk: diff --git a/drivers/char/mspec.c b/drivers/char/mspec.c index 30f095a..1997270 100644 --- a/drivers/char/mspec.c +++ b/drivers/char/mspec.c @@ -239,7 +239,7 @@ mspec_fault(struct vm_area_struct *vma, struct vm_fault *vmf) return VM_FAULT_NOPAGE; } -static struct vm_operations_struct mspec_vm_ops = { +static const struct vm_operations_struct mspec_vm_ops = { .open = mspec_open, .close = mspec_close, .fault = mspec_fault, diff --git a/drivers/char/mwave/mwavedd.c b/drivers/char/mwave/mwavedd.c index 94ad2c3..a4ec50c 100644 --- a/drivers/char/mwave/mwavedd.c +++ b/drivers/char/mwave/mwavedd.c @@ -281,12 +281,6 @@ static long mwave_ioctl(struct file *file, unsigned int iocmd, case IOCTL_MW_REGISTER_IPC: { unsigned int ipcnum = (unsigned int) ioarg; - PRINTK_3(TRACE_MWAVE, - "mwavedd::mwave_ioctl IOCTL_MW_REGISTER_IPC" - " ipcnum %x entry usIntCount %x\n", - ipcnum, - pDrvData->IPCs[ipcnum].usIntCount); - if (ipcnum >= ARRAY_SIZE(pDrvData->IPCs)) { PRINTK_ERROR(KERN_ERR_MWAVE "mwavedd::mwave_ioctl:" @@ -295,6 +289,12 @@ static long mwave_ioctl(struct file *file, unsigned int iocmd, ipcnum); return -EINVAL; } + PRINTK_3(TRACE_MWAVE, + "mwavedd::mwave_ioctl IOCTL_MW_REGISTER_IPC" + " ipcnum %x entry usIntCount %x\n", + ipcnum, + pDrvData->IPCs[ipcnum].usIntCount); + lock_kernel(); pDrvData->IPCs[ipcnum].bIsHere = FALSE; pDrvData->IPCs[ipcnum].bIsEnabled = TRUE; @@ -310,11 +310,6 @@ static long mwave_ioctl(struct file *file, unsigned int iocmd, case IOCTL_MW_GET_IPC: { unsigned int ipcnum = (unsigned int) ioarg; - PRINTK_3(TRACE_MWAVE, - "mwavedd::mwave_ioctl IOCTL_MW_GET_IPC" - " ipcnum %x, usIntCount %x\n", - ipcnum, - pDrvData->IPCs[ipcnum].usIntCount); if (ipcnum >= ARRAY_SIZE(pDrvData->IPCs)) { PRINTK_ERROR(KERN_ERR_MWAVE "mwavedd::mwave_ioctl:" @@ -322,6 +317,11 @@ static long mwave_ioctl(struct file *file, unsigned int iocmd, " Invalid ipcnum %x\n", ipcnum); return -EINVAL; } + PRINTK_3(TRACE_MWAVE, + "mwavedd::mwave_ioctl IOCTL_MW_GET_IPC" + " ipcnum %x, usIntCount %x\n", + ipcnum, + pDrvData->IPCs[ipcnum].usIntCount); lock_kernel(); if (pDrvData->IPCs[ipcnum].bIsEnabled == TRUE) { diff --git a/drivers/char/mxser.c b/drivers/char/mxser.c index dbf8d52..5e28d39 100644 --- a/drivers/char/mxser.c +++ b/drivers/char/mxser.c @@ -48,7 +48,7 @@ #include "mxser.h" -#define MXSER_VERSION "2.0.4" /* 1.12 */ +#define MXSER_VERSION "2.0.5" /* 1.14 */ #define MXSERMAJOR 174 #define MXSER_BOARDS 4 /* Max. boards */ @@ -69,6 +69,7 @@ #define PCI_DEVICE_ID_POS104UL 0x1044 #define PCI_DEVICE_ID_CB108 0x1080 #define PCI_DEVICE_ID_CP102UF 0x1023 +#define PCI_DEVICE_ID_CP112UL 0x1120 #define PCI_DEVICE_ID_CB114 0x1142 #define PCI_DEVICE_ID_CP114UL 0x1143 #define PCI_DEVICE_ID_CB134I 0x1341 @@ -139,7 +140,8 @@ static const struct mxser_cardinfo mxser_cards[] = { { "CP-138U series", 8, }, { "POS-104UL series", 4, }, { "CP-114UL series", 4, }, -/*30*/ { "CP-102UF series", 2, } +/*30*/ { "CP-102UF series", 2, }, + { "CP-112UL series", 2, }, }; /* driver_data correspond to the lines in the structure above @@ -170,6 +172,7 @@ static struct pci_device_id mxser_pcibrds[] = { { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_POS104UL), .driver_data = 28 }, { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_CP114UL), .driver_data = 29 }, { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_CP102UF), .driver_data = 30 }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_CP112UL), .driver_data = 31 }, { } }; MODULE_DEVICE_TABLE(pci, mxser_pcibrds); @@ -258,7 +261,6 @@ struct mxser_port { struct mxser_mon mon_data; spinlock_t slock; - wait_queue_head_t delta_msr_wait; }; struct mxser_board { @@ -818,7 +820,7 @@ static void mxser_check_modem_status(struct tty_struct *tty, if (status & UART_MSR_DCTS) port->icount.cts++; port->mon_data.modem_status = status; - wake_up_interruptible(&port->delta_msr_wait); + wake_up_interruptible(&port->port.delta_msr_wait); if ((port->port.flags & ASYNC_CHECK_CD) && (status & UART_MSR_DDCD)) { if (status & UART_MSR_DCD) @@ -973,7 +975,7 @@ static void mxser_shutdown(struct tty_struct *tty) * clear delta_msr_wait queue to avoid mem leaks: we may free the irq * here so the queue might never be waken up */ - wake_up_interruptible(&info->delta_msr_wait); + wake_up_interruptible(&info->port.delta_msr_wait); /* * Free the IRQ, if necessary @@ -1073,34 +1075,17 @@ static void mxser_flush_buffer(struct tty_struct *tty) } -/* - * This routine is called when the serial port gets closed. First, we - * wait for the last remaining data to be sent. Then, we unlink its - * async structure from the interrupt chain if necessary, and we free - * that IRQ if nothing is left in the chain. - */ -static void mxser_close(struct tty_struct *tty, struct file *filp) +static void mxser_close_port(struct tty_struct *tty, struct tty_port *port) { - struct mxser_port *info = tty->driver_data; - struct tty_port *port = &info->port; - + struct mxser_port *info = container_of(port, struct mxser_port, port); unsigned long timeout; - - if (tty->index == MXSER_PORTS) - return; - if (!info) - return; - - if (tty_port_close_start(port, tty, filp) == 0) - return; - /* * Save the termios structure, since this port may have * separate termios for callout and dialin. * * FIXME: Can this go ? */ - if (info->port.flags & ASYNC_NORMAL_ACTIVE) + if (port->flags & ASYNC_NORMAL_ACTIVE) info->normal_termios = *tty->termios; /* * At this point we stop accepting input. To do this, we @@ -1112,7 +1097,7 @@ static void mxser_close(struct tty_struct *tty, struct file *filp) if (info->board->chip_flag) info->IER &= ~MOXA_MUST_RECV_ISR; - if (info->port.flags & ASYNC_INITIALIZED) { + if (port->flags & ASYNC_INITIALIZED) { outb(info->IER, info->ioaddr + UART_IER); /* * Before we drop DTR, make sure the UART transmitter @@ -1127,8 +1112,26 @@ static void mxser_close(struct tty_struct *tty, struct file *filp) } } mxser_shutdown(tty); - mxser_flush_buffer(tty); +} + +/* + * This routine is called when the serial port gets closed. First, we + * wait for the last remaining data to be sent. Then, we unlink its + * async structure from the interrupt chain if necessary, and we free + * that IRQ if nothing is left in the chain. + */ +static void mxser_close(struct tty_struct *tty, struct file *filp) +{ + struct mxser_port *info = tty->driver_data; + struct tty_port *port = &info->port; + + if (tty->index == MXSER_PORTS) + return; + if (tty_port_close_start(port, tty, filp) == 0) + return; + mxser_close_port(tty, port); + mxser_flush_buffer(tty); /* Right now the tty_port set is done outside of the close_end helper as we don't yet have everyone using refcounts */ tty_port_close_end(port, tty); @@ -1761,7 +1764,7 @@ static int mxser_ioctl(struct tty_struct *tty, struct file *file, cnow = info->icount; /* note the counters on entry */ spin_unlock_irqrestore(&info->slock, flags); - return wait_event_interruptible(info->delta_msr_wait, + return wait_event_interruptible(info->port.delta_msr_wait, mxser_cflags_changed(info, arg, &cnow)); /* * Get counter of input serial line interrupts (DCD,RI,DSR,CTS) @@ -1803,7 +1806,7 @@ static int mxser_ioctl(struct tty_struct *tty, struct file *file, lock_kernel(); len = mxser_chars_in_buffer(tty); - lsr = inb(info->ioaddr + UART_LSR) & UART_LSR_TEMT; + lsr = inb(info->ioaddr + UART_LSR) & UART_LSR_THRE; len += (lsr ? 0 : 1); unlock_kernel(); @@ -2413,7 +2416,6 @@ static int __devinit mxser_initbrd(struct mxser_board *brd, info->port.close_delay = 5 * HZ / 10; info->port.closing_wait = 30 * HZ; info->normal_termios = mxvar_sdriver->init_termios; - init_waitqueue_head(&info->delta_msr_wait); memset(&info->mon_data, 0, sizeof(struct mxser_mon)); info->err_shadow = 0; spin_lock_init(&info->slock); diff --git a/drivers/char/n_tty.c b/drivers/char/n_tty.c index 4e28b35..2e50f4d 100644 --- a/drivers/char/n_tty.c +++ b/drivers/char/n_tty.c @@ -272,7 +272,8 @@ static inline int is_continuation(unsigned char c, struct tty_struct *tty) * * This is a helper function that handles one output character * (including special characters like TAB, CR, LF, etc.), - * putting the results in the tty driver's write buffer. + * doing OPOST processing and putting the results in the + * tty driver's write buffer. * * Note that Linux currently ignores TABDLY, CRDLY, VTDLY, FFDLY * and NLDLY. They simply aren't relevant in the world today. @@ -350,8 +351,9 @@ static int do_output_char(unsigned char c, struct tty_struct *tty, int space) * @c: character (or partial unicode symbol) * @tty: terminal device * - * Perform OPOST processing. Returns -1 when the output device is - * full and the character must be retried. + * Output one character with OPOST processing. + * Returns -1 when the output device is full and the character + * must be retried. * * Locking: output_lock to protect column state and space left * (also, this is called from n_tty_write under the @@ -377,8 +379,11 @@ static int process_output(unsigned char c, struct tty_struct *tty) /** * process_output_block - block post processor * @tty: terminal device - * @inbuf: user buffer - * @nr: number of bytes + * @buf: character buffer + * @nr: number of bytes to output + * + * Output a block of characters with OPOST processing. + * Returns the number of characters output. * * This path is used to speed up block console writes, among other * things when processing blocks of output data. It handles only @@ -571,33 +576,23 @@ static void process_echoes(struct tty_struct *tty) break; default: - if (iscntrl(op)) { - if (L_ECHOCTL(tty)) { - /* - * Ensure there is enough space - * for the whole ctrl pair. - */ - if (space < 2) { - no_space_left = 1; - break; - } - tty_put_char(tty, '^'); - tty_put_char(tty, op ^ 0100); - tty->column += 2; - space -= 2; - } else { - if (!space) { - no_space_left = 1; - break; - } - tty_put_char(tty, op); - space--; - } - } /* - * If above falls through, this was an - * undefined op. + * If the op is not a special byte code, + * it is a ctrl char tagged to be echoed + * as "^X" (where X is the letter + * representing the control char). + * Note that we must ensure there is + * enough space for the whole ctrl pair. + * */ + if (space < 2) { + no_space_left = 1; + break; + } + tty_put_char(tty, '^'); + tty_put_char(tty, op ^ 0100); + tty->column += 2; + space -= 2; cp += 2; nr -= 2; } @@ -605,12 +600,18 @@ static void process_echoes(struct tty_struct *tty) if (no_space_left) break; } else { - int retval; - - retval = do_output_char(c, tty, space); - if (retval < 0) - break; - space -= retval; + if (O_OPOST(tty) && + !(test_bit(TTY_HW_COOK_OUT, &tty->flags))) { + int retval = do_output_char(c, tty, space); + if (retval < 0) + break; + space -= retval; + } else { + if (!space) + break; + tty_put_char(tty, c); + space -= 1; + } cp += 1; nr -= 1; } @@ -798,8 +799,8 @@ static void echo_char_raw(unsigned char c, struct tty_struct *tty) * Echo user input back onto the screen. This must be called only when * L_ECHO(tty) is true. Called from the driver receive_buf path. * - * This variant tags control characters to be possibly echoed as - * as "^X" (where X is the letter representing the control char). + * This variant tags control characters to be echoed as "^X" + * (where X is the letter representing the control char). * * Locking: echo_lock to protect the echo buffer */ @@ -812,7 +813,7 @@ static void echo_char(unsigned char c, struct tty_struct *tty) add_echo_byte(ECHO_OP_START, tty); add_echo_byte(ECHO_OP_START, tty); } else { - if (iscntrl(c) && c != '\t') + if (L_ECHOCTL(tty) && iscntrl(c) && c != '\t') add_echo_byte(ECHO_OP_START, tty); add_echo_byte(c, tty); } diff --git a/drivers/char/pcmcia/cm4000_cs.c b/drivers/char/pcmcia/cm4000_cs.c index 881934c..c250a31 100644 --- a/drivers/char/pcmcia/cm4000_cs.c +++ b/drivers/char/pcmcia/cm4000_cs.c @@ -1017,7 +1017,7 @@ static ssize_t cmm_read(struct file *filp, __user char *buf, size_t count, } } - if (dev->proto == 0 && count > dev->rlen - dev->rpos) { + if (dev->proto == 0 && count > dev->rlen - dev->rpos && i) { DEBUGP(4, dev, "T=0 and count > buffer\n"); dev->rbuf[i] = dev->rbuf[i - 1]; dev->rbuf[i - 1] = dev->procbyte; diff --git a/drivers/char/pcmcia/synclink_cs.c b/drivers/char/pcmcia/synclink_cs.c index 77b3648..caf6e4d 100644 --- a/drivers/char/pcmcia/synclink_cs.c +++ b/drivers/char/pcmcia/synclink_cs.c @@ -4005,10 +4005,9 @@ static int hdlcdev_attach(struct net_device *dev, unsigned short encoding, * * skb socket buffer containing HDLC frame * dev pointer to network device structure - * - * returns 0 if success, otherwise error code */ -static int hdlcdev_xmit(struct sk_buff *skb, struct net_device *dev) +static netdev_tx_t hdlcdev_xmit(struct sk_buff *skb, + struct net_device *dev) { MGSLPC_INFO *info = dev_to_port(dev); unsigned long flags; @@ -4043,7 +4042,7 @@ static int hdlcdev_xmit(struct sk_buff *skb, struct net_device *dev) } spin_unlock_irqrestore(&info->lock,flags); - return 0; + return NETDEV_TX_OK; } /** diff --git a/drivers/char/pty.c b/drivers/char/pty.c index b33d668..53761ce 100644 --- a/drivers/char/pty.c +++ b/drivers/char/pty.c @@ -120,8 +120,10 @@ static int pty_write(struct tty_struct *tty, const unsigned char *buf, int c) /* Stuff the data into the input queue of the other end */ c = tty_insert_flip_string(to, buf, c); /* And shovel */ - tty_flip_buffer_push(to); - tty_wakeup(tty); + if (c) { + tty_flip_buffer_push(to); + tty_wakeup(tty); + } } return c; } diff --git a/drivers/char/random.c b/drivers/char/random.c index d8a9255..04b505e 100644 --- a/drivers/char/random.c +++ b/drivers/char/random.c @@ -1231,7 +1231,7 @@ static char sysctl_bootid[16]; * as an ASCII string in the standard UUID format. If accesses via the * sysctl system call, it is returned as 16 bytes of binary data. */ -static int proc_do_uuid(ctl_table *table, int write, struct file *filp, +static int proc_do_uuid(ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { ctl_table fake_table; @@ -1254,7 +1254,7 @@ static int proc_do_uuid(ctl_table *table, int write, struct file *filp, fake_table.data = buf; fake_table.maxlen = sizeof(buf); - return proc_dostring(&fake_table, write, filp, buffer, lenp, ppos); + return proc_dostring(&fake_table, write, buffer, lenp, ppos); } static int uuid_strategy(ctl_table *table, diff --git a/drivers/char/raw.c b/drivers/char/raw.c index 05f9d18..64acd05 100644 --- a/drivers/char/raw.c +++ b/drivers/char/raw.c @@ -246,7 +246,7 @@ static const struct file_operations raw_fops = { .read = do_sync_read, .aio_read = generic_file_aio_read, .write = do_sync_write, - .aio_write = generic_file_aio_write_nolock, + .aio_write = blkdev_aio_write, .open = raw_open, .release= raw_release, .ioctl = raw_ioctl, @@ -261,7 +261,7 @@ static const struct file_operations raw_ctl_fops = { static struct cdev raw_cdev; -static char *raw_nodename(struct device *dev) +static char *raw_devnode(struct device *dev, mode_t *mode) { return kasprintf(GFP_KERNEL, "raw/%s", dev_name(dev)); } @@ -289,7 +289,7 @@ static int __init raw_init(void) ret = PTR_ERR(raw_class); goto error_region; } - raw_class->nodename = raw_nodename; + raw_class->devnode = raw_devnode; device_create(raw_class, NULL, MKDEV(RAW_MAJOR, 0), NULL, "rawctl"); return 0; diff --git a/drivers/char/rio/rioctrl.c b/drivers/char/rio/rioctrl.c index eecee0f..7433955 100644 --- a/drivers/char/rio/rioctrl.c +++ b/drivers/char/rio/rioctrl.c @@ -873,7 +873,7 @@ int riocontrol(struct rio_info *p, dev_t dev, int cmd, unsigned long arg, int su /* ** It is important that the product code is an unsigned object! */ - if (DownLoad.ProductCode > MAX_PRODUCT) { + if (DownLoad.ProductCode >= MAX_PRODUCT) { rio_dprintk(RIO_DEBUG_CTRL, "RIO_DOWNLOAD: Bad product code %d passed\n", DownLoad.ProductCode); p->RIOError.Error = NO_SUCH_PRODUCT; return -ENXIO; diff --git a/drivers/char/riscom8.c b/drivers/char/riscom8.c index 171711a..3cfa22d 100644 --- a/drivers/char/riscom8.c +++ b/drivers/char/riscom8.c @@ -343,7 +343,7 @@ static void rc_receive_exc(struct riscom_board const *bp) if (port == NULL) return; - tty = port->port.tty; + tty = tty_port_tty_get(&port->port); #ifdef RC_REPORT_OVERRUN status = rc_in(bp, CD180_RCSR); @@ -355,18 +355,18 @@ static void rc_receive_exc(struct riscom_board const *bp) #endif ch = rc_in(bp, CD180_RDR); if (!status) - return; + goto out; if (status & RCSR_TOUT) { printk(KERN_WARNING "rc%d: port %d: Receiver timeout. " "Hardware problems ?\n", board_No(bp), port_No(port)); - return; + goto out; } else if (status & RCSR_BREAK) { printk(KERN_INFO "rc%d: port %d: Handling break...\n", board_No(bp), port_No(port)); flag = TTY_BREAK; - if (port->port.flags & ASYNC_SAK) + if (tty && (port->port.flags & ASYNC_SAK)) do_SAK(tty); } else if (status & RCSR_PE) @@ -380,8 +380,12 @@ static void rc_receive_exc(struct riscom_board const *bp) else flag = TTY_NORMAL; - tty_insert_flip_char(tty, ch, flag); - tty_flip_buffer_push(tty); + if (tty) { + tty_insert_flip_char(tty, ch, flag); + tty_flip_buffer_push(tty); + } +out: + tty_kref_put(tty); } static void rc_receive(struct riscom_board const *bp) @@ -394,7 +398,7 @@ static void rc_receive(struct riscom_board const *bp) if (port == NULL) return; - tty = port->port.tty; + tty = tty_port_tty_get(&port->port); count = rc_in(bp, CD180_RDCR); @@ -403,15 +407,14 @@ static void rc_receive(struct riscom_board const *bp) #endif while (count--) { - if (tty_buffer_request_room(tty, 1) == 0) { - printk(KERN_WARNING "rc%d: port %d: Working around " - "flip buffer overflow.\n", - board_No(bp), port_No(port)); - break; - } - tty_insert_flip_char(tty, rc_in(bp, CD180_RDR), TTY_NORMAL); + u8 ch = rc_in(bp, CD180_RDR); + if (tty) + tty_insert_flip_char(tty, ch, TTY_NORMAL); + } + if (tty) { + tty_flip_buffer_push(tty); + tty_kref_put(tty); } - tty_flip_buffer_push(tty); } static void rc_transmit(struct riscom_board const *bp) @@ -424,22 +427,22 @@ static void rc_transmit(struct riscom_board const *bp) if (port == NULL) return; - tty = port->port.tty; + tty = tty_port_tty_get(&port->port); if (port->IER & IER_TXEMPTY) { /* FIFO drained */ rc_out(bp, CD180_CAR, port_No(port)); port->IER &= ~IER_TXEMPTY; rc_out(bp, CD180_IER, port->IER); - return; + goto out; } if ((port->xmit_cnt <= 0 && !port->break_length) - || tty->stopped || tty->hw_stopped) { + || (tty && (tty->stopped || tty->hw_stopped))) { rc_out(bp, CD180_CAR, port_No(port)); port->IER &= ~IER_TXRDY; rc_out(bp, CD180_IER, port->IER); - return; + goto out; } if (port->break_length) { @@ -464,7 +467,7 @@ static void rc_transmit(struct riscom_board const *bp) rc_out(bp, CD180_CCR, CCR_CORCHG2); port->break_length = 0; } - return; + goto out; } count = CD180_NFIFO; @@ -480,8 +483,10 @@ static void rc_transmit(struct riscom_board const *bp) port->IER &= ~IER_TXRDY; rc_out(bp, CD180_IER, port->IER); } - if (port->xmit_cnt <= port->wakeup_chars) + if (tty && port->xmit_cnt <= port->wakeup_chars) tty_wakeup(tty); +out: + tty_kref_put(tty); } static void rc_check_modem(struct riscom_board const *bp) @@ -494,37 +499,43 @@ static void rc_check_modem(struct riscom_board const *bp) if (port == NULL) return; - tty = port->port.tty; + tty = tty_port_tty_get(&port->port); mcr = rc_in(bp, CD180_MCR); if (mcr & MCR_CDCHG) { if (rc_in(bp, CD180_MSVR) & MSVR_CD) wake_up_interruptible(&port->port.open_wait); - else + else if (tty) tty_hangup(tty); } #ifdef RISCOM_BRAIN_DAMAGED_CTS if (mcr & MCR_CTSCHG) { if (rc_in(bp, CD180_MSVR) & MSVR_CTS) { - tty->hw_stopped = 0; port->IER |= IER_TXRDY; - if (port->xmit_cnt <= port->wakeup_chars) - tty_wakeup(tty); + if (tty) { + tty->hw_stopped = 0; + if (port->xmit_cnt <= port->wakeup_chars) + tty_wakeup(tty); + } } else { - tty->hw_stopped = 1; + if (tty) + tty->hw_stopped = 1; port->IER &= ~IER_TXRDY; } rc_out(bp, CD180_IER, port->IER); } if (mcr & MCR_DSRCHG) { if (rc_in(bp, CD180_MSVR) & MSVR_DSR) { - tty->hw_stopped = 0; port->IER |= IER_TXRDY; - if (port->xmit_cnt <= port->wakeup_chars) - tty_wakeup(tty); + if (tty) { + tty->hw_stopped = 0; + if (port->xmit_cnt <= port->wakeup_chars) + tty_wakeup(tty); + } } else { - tty->hw_stopped = 1; + if (tty) + tty->hw_stopped = 1; port->IER &= ~IER_TXRDY; } rc_out(bp, CD180_IER, port->IER); @@ -533,6 +544,7 @@ static void rc_check_modem(struct riscom_board const *bp) /* Clear change bits */ rc_out(bp, CD180_MCR, 0); + tty_kref_put(tty); } /* The main interrupt processing routine */ @@ -632,9 +644,9 @@ static void rc_shutdown_board(struct riscom_board *bp) * Setting up port characteristics. * Must be called with disabled interrupts */ -static void rc_change_speed(struct riscom_board *bp, struct riscom_port *port) +static void rc_change_speed(struct tty_struct *tty, struct riscom_board *bp, + struct riscom_port *port) { - struct tty_struct *tty = port->port.tty; unsigned long baud; long tmp; unsigned char cor1 = 0, cor3 = 0; @@ -781,7 +793,8 @@ static void rc_change_speed(struct riscom_board *bp, struct riscom_port *port) } /* Must be called with interrupts enabled */ -static int rc_setup_port(struct riscom_board *bp, struct riscom_port *port) +static int rc_setup_port(struct tty_struct *tty, struct riscom_board *bp, + struct riscom_port *port) { unsigned long flags; @@ -793,11 +806,11 @@ static int rc_setup_port(struct riscom_board *bp, struct riscom_port *port) spin_lock_irqsave(&riscom_lock, flags); - clear_bit(TTY_IO_ERROR, &port->port.tty->flags); + clear_bit(TTY_IO_ERROR, &tty->flags); if (port->port.count == 1) bp->count++; port->xmit_cnt = port->xmit_head = port->xmit_tail = 0; - rc_change_speed(bp, port); + rc_change_speed(tty, bp, port); port->port.flags |= ASYNC_INITIALIZED; spin_unlock_irqrestore(&riscom_lock, flags); @@ -898,9 +911,9 @@ static int rc_open(struct tty_struct *tty, struct file *filp) port->port.count++; tty->driver_data = port; - port->port.tty = tty; + tty_port_tty_set(&port->port, tty); - error = rc_setup_port(bp, port); + error = rc_setup_port(tty, bp, port); if (error == 0) error = tty_port_block_til_ready(&port->port, tty, filp); return error; @@ -921,20 +934,12 @@ static void rc_flush_buffer(struct tty_struct *tty) tty_wakeup(tty); } -static void rc_close(struct tty_struct *tty, struct file *filp) +static void rc_close_port(struct tty_port *port) { - struct riscom_port *port = tty->driver_data; - struct riscom_board *bp; unsigned long flags; + struct riscom_port *rp = container_of(port, struct riscom_port, port); + struct riscom_board *bp = port_Board(rp); unsigned long timeout; - - if (!port || rc_paranoia_check(port, tty->name, "close")) - return; - - bp = port_Board(port); - - if (tty_port_close_start(&port->port, tty, filp) == 0) - return; /* * At this point we stop accepting input. To do this, we @@ -944,31 +949,37 @@ static void rc_close(struct tty_struct *tty, struct file *filp) */ spin_lock_irqsave(&riscom_lock, flags); - port->IER &= ~IER_RXD; - if (port->port.flags & ASYNC_INITIALIZED) { - port->IER &= ~IER_TXRDY; - port->IER |= IER_TXEMPTY; - rc_out(bp, CD180_CAR, port_No(port)); - rc_out(bp, CD180_IER, port->IER); + rp->IER &= ~IER_RXD; + if (port->flags & ASYNC_INITIALIZED) { + rp->IER &= ~IER_TXRDY; + rp->IER |= IER_TXEMPTY; + rc_out(bp, CD180_CAR, port_No(rp)); + rc_out(bp, CD180_IER, rp->IER); /* * Before we drop DTR, make sure the UART transmitter * has completely drained; this is especially * important if there is a transmit FIFO! */ timeout = jiffies + HZ; - while (port->IER & IER_TXEMPTY) { + while (rp->IER & IER_TXEMPTY) { spin_unlock_irqrestore(&riscom_lock, flags); - msleep_interruptible(jiffies_to_msecs(port->timeout)); + msleep_interruptible(jiffies_to_msecs(rp->timeout)); spin_lock_irqsave(&riscom_lock, flags); if (time_after(jiffies, timeout)) break; } } - rc_shutdown_port(tty, bp, port); - rc_flush_buffer(tty); + rc_shutdown_port(port->tty, bp, rp); spin_unlock_irqrestore(&riscom_lock, flags); +} + +static void rc_close(struct tty_struct *tty, struct file *filp) +{ + struct riscom_port *port = tty->driver_data; - tty_port_close_end(&port->port, tty); + if (!port || rc_paranoia_check(port, tty->name, "close")) + return; + tty_port_close(&port->port, tty, filp); } static int rc_write(struct tty_struct *tty, @@ -1170,7 +1181,7 @@ static int rc_send_break(struct tty_struct *tty, int length) return 0; } -static int rc_set_serial_info(struct riscom_port *port, +static int rc_set_serial_info(struct tty_struct *tty, struct riscom_port *port, struct serial_struct __user *newinfo) { struct serial_struct tmp; @@ -1180,17 +1191,6 @@ static int rc_set_serial_info(struct riscom_port *port, if (copy_from_user(&tmp, newinfo, sizeof(tmp))) return -EFAULT; -#if 0 - if ((tmp.irq != bp->irq) || - (tmp.port != bp->base) || - (tmp.type != PORT_CIRRUS) || - (tmp.baud_base != (RC_OSCFREQ + CD180_TPC/2) / CD180_TPC) || - (tmp.custom_divisor != 0) || - (tmp.xmit_fifo_size != CD180_NFIFO) || - (tmp.flags & ~RISCOM_LEGAL_FLAGS)) - return -EINVAL; -#endif - change_speed = ((port->port.flags & ASYNC_SPD_MASK) != (tmp.flags & ASYNC_SPD_MASK)); @@ -1212,7 +1212,7 @@ static int rc_set_serial_info(struct riscom_port *port, unsigned long flags; spin_lock_irqsave(&riscom_lock, flags); - rc_change_speed(bp, port); + rc_change_speed(tty, bp, port); spin_unlock_irqrestore(&riscom_lock, flags); } return 0; @@ -1255,7 +1255,7 @@ static int rc_ioctl(struct tty_struct *tty, struct file *filp, break; case TIOCSSERIAL: lock_kernel(); - retval = rc_set_serial_info(port, argp); + retval = rc_set_serial_info(tty, port, argp); unlock_kernel(); break; default: @@ -1350,21 +1350,12 @@ static void rc_start(struct tty_struct *tty) static void rc_hangup(struct tty_struct *tty) { struct riscom_port *port = tty->driver_data; - struct riscom_board *bp; - unsigned long flags; if (rc_paranoia_check(port, tty->name, "rc_hangup")) return; - bp = port_Board(port); - - rc_shutdown_port(tty, bp, port); - spin_lock_irqsave(&port->port.lock, flags); - port->port.count = 0; - port->port.flags &= ~ASYNC_NORMAL_ACTIVE; - port->port.tty = NULL; - wake_up_interruptible(&port->port.open_wait); - spin_unlock_irqrestore(&port->port.lock, flags); + rc_shutdown_port(tty, port_Board(port), port); + tty_port_hangup(&port->port); } static void rc_set_termios(struct tty_struct *tty, @@ -1377,7 +1368,7 @@ static void rc_set_termios(struct tty_struct *tty, return; spin_lock_irqsave(&riscom_lock, flags); - rc_change_speed(port_Board(port), port); + rc_change_speed(tty, port_Board(port), port); spin_unlock_irqrestore(&riscom_lock, flags); if ((old_termios->c_cflag & CRTSCTS) && @@ -1410,6 +1401,7 @@ static const struct tty_operations riscom_ops = { static const struct tty_port_operations riscom_port_ops = { .carrier_raised = carrier_raised, + .shutdown = rc_close_port, }; diff --git a/drivers/char/serial167.c b/drivers/char/serial167.c index 51e7a46..5942a9d 100644 --- a/drivers/char/serial167.c +++ b/drivers/char/serial167.c @@ -171,7 +171,6 @@ static int startup(struct cyclades_port *); static void cy_throttle(struct tty_struct *); static void cy_unthrottle(struct tty_struct *); static void config_setup(struct cyclades_port *); -extern void console_print(const char *); #ifdef CYCLOM_SHOW_STATUS static void show_status(int); #endif @@ -245,7 +244,7 @@ void SP(char *data) { unsigned long flags; local_irq_save(flags); - console_print(data); + printk(KERN_EMERG "%s", data); local_irq_restore(flags); } @@ -255,7 +254,7 @@ void CP(char data) unsigned long flags; local_irq_save(flags); scrn[0] = data; - console_print(scrn); + printk(KERN_EMERG "%c", scrn); local_irq_restore(flags); } /* CP */ diff --git a/drivers/char/synclink.c b/drivers/char/synclink.c index 813552f..4846b73 100644 --- a/drivers/char/synclink.c +++ b/drivers/char/synclink.c @@ -7697,10 +7697,9 @@ static int hdlcdev_attach(struct net_device *dev, unsigned short encoding, * * skb socket buffer containing HDLC frame * dev pointer to network device structure - * - * returns 0 if success, otherwise error code */ -static int hdlcdev_xmit(struct sk_buff *skb, struct net_device *dev) +static netdev_tx_t hdlcdev_xmit(struct sk_buff *skb, + struct net_device *dev) { struct mgsl_struct *info = dev_to_port(dev); unsigned long flags; @@ -7731,7 +7730,7 @@ static int hdlcdev_xmit(struct sk_buff *skb, struct net_device *dev) usc_start_transmitter(info); spin_unlock_irqrestore(&info->irq_spinlock,flags); - return 0; + return NETDEV_TX_OK; } /** diff --git a/drivers/char/synclink_gt.c b/drivers/char/synclink_gt.c index 91f20a9..8678f0c 100644 --- a/drivers/char/synclink_gt.c +++ b/drivers/char/synclink_gt.c @@ -1497,10 +1497,9 @@ static int hdlcdev_attach(struct net_device *dev, unsigned short encoding, * * skb socket buffer containing HDLC frame * dev pointer to network device structure - * - * returns 0 if success, otherwise error code */ -static int hdlcdev_xmit(struct sk_buff *skb, struct net_device *dev) +static netdev_tx_t hdlcdev_xmit(struct sk_buff *skb, + struct net_device *dev) { struct slgt_info *info = dev_to_port(dev); unsigned long flags; @@ -1529,7 +1528,7 @@ static int hdlcdev_xmit(struct sk_buff *skb, struct net_device *dev) update_tx_timer(info); spin_unlock_irqrestore(&info->lock,flags); - return 0; + return NETDEV_TX_OK; } /** diff --git a/drivers/char/synclinkmp.c b/drivers/char/synclinkmp.c index 8d4a2a8..2b18adc 100644 --- a/drivers/char/synclinkmp.c +++ b/drivers/char/synclinkmp.c @@ -1608,10 +1608,9 @@ static int hdlcdev_attach(struct net_device *dev, unsigned short encoding, * * skb socket buffer containing HDLC frame * dev pointer to network device structure - * - * returns 0 if success, otherwise error code */ -static int hdlcdev_xmit(struct sk_buff *skb, struct net_device *dev) +static netdev_tx_t hdlcdev_xmit(struct sk_buff *skb, + struct net_device *dev) { SLMP_INFO *info = dev_to_port(dev); unsigned long flags; @@ -1642,7 +1641,7 @@ static int hdlcdev_xmit(struct sk_buff *skb, struct net_device *dev) tx_start(info); spin_unlock_irqrestore(&info->lock,flags); - return 0; + return NETDEV_TX_OK; } /** diff --git a/drivers/char/sysrq.c b/drivers/char/sysrq.c index 50eecfe..44203ff 100644 --- a/drivers/char/sysrq.c +++ b/drivers/char/sysrq.c @@ -26,7 +26,7 @@ #include <linux/proc_fs.h> #include <linux/nmi.h> #include <linux/quotaops.h> -#include <linux/perf_counter.h> +#include <linux/perf_event.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/suspend.h> @@ -252,7 +252,7 @@ static void sysrq_handle_showregs(int key, struct tty_struct *tty) struct pt_regs *regs = get_irq_regs(); if (regs) show_regs(regs); - perf_counter_print_debug(); + perf_event_print_debug(); } static struct sysrq_key_op sysrq_showregs_op = { .handler = sysrq_handle_showregs, diff --git a/drivers/char/tpm/tpm.c b/drivers/char/tpm/tpm.c index f4c68ab..aeafac5 100644 --- a/drivers/char/tpm/tpm.c +++ b/drivers/char/tpm/tpm.c @@ -696,7 +696,7 @@ int __tpm_pcr_read(struct tpm_chip *chip, int pcr_idx, u8 *res_buf) cmd.header.in = pcrread_header; cmd.params.pcrread_in.pcr_idx = cpu_to_be32(pcr_idx); - BUILD_BUG_ON(cmd.header.in.length > READ_PCR_RESULT_SIZE); + BUG_ON(cmd.header.in.length > READ_PCR_RESULT_SIZE); rc = transmit_cmd(chip, &cmd, cmd.header.in.length, "attempting to read a pcr value"); @@ -742,7 +742,7 @@ EXPORT_SYMBOL_GPL(tpm_pcr_read); * the module usage count. */ #define TPM_ORD_PCR_EXTEND cpu_to_be32(20) -#define EXTEND_PCR_SIZE 34 +#define EXTEND_PCR_RESULT_SIZE 34 static struct tpm_input_header pcrextend_header = { .tag = TPM_TAG_RQU_COMMAND, .length = cpu_to_be32(34), @@ -760,10 +760,9 @@ int tpm_pcr_extend(u32 chip_num, int pcr_idx, const u8 *hash) return -ENODEV; cmd.header.in = pcrextend_header; - BUILD_BUG_ON(be32_to_cpu(cmd.header.in.length) > EXTEND_PCR_SIZE); cmd.params.pcrextend_in.pcr_idx = cpu_to_be32(pcr_idx); memcpy(cmd.params.pcrextend_in.hash, hash, TPM_DIGEST_SIZE); - rc = transmit_cmd(chip, &cmd, cmd.header.in.length, + rc = transmit_cmd(chip, &cmd, EXTEND_PCR_RESULT_SIZE, "attempting extend a PCR value"); module_put(chip->dev->driver->owner); diff --git a/drivers/char/tpm/tpm_bios.c b/drivers/char/tpm/tpm_bios.c index 0c2f55a..bf2170f 100644 --- a/drivers/char/tpm/tpm_bios.c +++ b/drivers/char/tpm/tpm_bios.c @@ -343,14 +343,14 @@ static int tpm_ascii_bios_measurements_show(struct seq_file *m, void *v) return 0; } -static struct seq_operations tpm_ascii_b_measurments_seqops = { +static const struct seq_operations tpm_ascii_b_measurments_seqops = { .start = tpm_bios_measurements_start, .next = tpm_bios_measurements_next, .stop = tpm_bios_measurements_stop, .show = tpm_ascii_bios_measurements_show, }; -static struct seq_operations tpm_binary_b_measurments_seqops = { +static const struct seq_operations tpm_binary_b_measurments_seqops = { .start = tpm_bios_measurements_start, .next = tpm_bios_measurements_next, .stop = tpm_bios_measurements_stop, diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index a3afa0c..59499ee 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -1184,6 +1184,7 @@ int tty_init_termios(struct tty_struct *tty) tty->termios->c_ospeed = tty_termios_baud_rate(tty->termios); return 0; } +EXPORT_SYMBOL_GPL(tty_init_termios); /** * tty_driver_install_tty() - install a tty entry in the driver @@ -1386,16 +1387,19 @@ EXPORT_SYMBOL(tty_shutdown); * tty_mutex - sometimes only * takes the file list lock internally when working on the list * of ttys that the driver keeps. + * + * This method gets called from a work queue so that the driver private + * cleanup ops can sleep (needed for USB at least) */ -static void release_one_tty(struct kref *kref) +static void release_one_tty(struct work_struct *work) { - struct tty_struct *tty = container_of(kref, struct tty_struct, kref); + struct tty_struct *tty = + container_of(work, struct tty_struct, hangup_work); struct tty_driver *driver = tty->driver; - if (tty->ops->shutdown) - tty->ops->shutdown(tty); - else - tty_shutdown(tty); + if (tty->ops->cleanup) + tty->ops->cleanup(tty); + tty->magic = 0; tty_driver_kref_put(driver); module_put(driver->owner); @@ -1407,6 +1411,21 @@ static void release_one_tty(struct kref *kref) free_tty_struct(tty); } +static void queue_release_one_tty(struct kref *kref) +{ + struct tty_struct *tty = container_of(kref, struct tty_struct, kref); + + if (tty->ops->shutdown) + tty->ops->shutdown(tty); + else + tty_shutdown(tty); + + /* The hangup queue is now free so we can reuse it rather than + waste a chunk of memory for each port */ + INIT_WORK(&tty->hangup_work, release_one_tty); + schedule_work(&tty->hangup_work); +} + /** * tty_kref_put - release a tty kref * @tty: tty device @@ -1418,7 +1437,7 @@ static void release_one_tty(struct kref *kref) void tty_kref_put(struct tty_struct *tty) { if (tty) - kref_put(&tty->kref, release_one_tty); + kref_put(&tty->kref, queue_release_one_tty); } EXPORT_SYMBOL(tty_kref_put); @@ -2085,7 +2104,7 @@ static int tioccons(struct file *file) * the generic functionality existed. This piece of history is preserved * in the expected tty API of posix OS's. * - * Locking: none, the open fle handle ensures it won't go away. + * Locking: none, the open file handle ensures it won't go away. */ static int fionbio(struct file *file, int __user *p) @@ -3056,11 +3075,22 @@ void __init console_init(void) } } +static char *tty_devnode(struct device *dev, mode_t *mode) +{ + if (!mode) + return NULL; + if (dev->devt == MKDEV(TTYAUX_MAJOR, 0) || + dev->devt == MKDEV(TTYAUX_MAJOR, 2)) + *mode = 0666; + return NULL; +} + static int __init tty_class_init(void) { tty_class = class_create(THIS_MODULE, "tty"); if (IS_ERR(tty_class)) return PTR_ERR(tty_class); + tty_class->devnode = tty_devnode; return 0; } diff --git a/drivers/char/tty_ioctl.c b/drivers/char/tty_ioctl.c index ad6ba4e..8e67d5c 100644 --- a/drivers/char/tty_ioctl.c +++ b/drivers/char/tty_ioctl.c @@ -393,9 +393,7 @@ void tty_termios_encode_baud_rate(struct ktermios *termios, termios->c_cflag |= (BOTHER << IBSHIFT); #else if (ifound == -1 || ofound == -1) { - static int warned; - if (!warned++) - printk(KERN_WARNING "tty: Unable to return correct " + printk_once(KERN_WARNING "tty: Unable to return correct " "speed data as your architecture needs updating.\n"); } #endif diff --git a/drivers/char/tty_ldisc.c b/drivers/char/tty_ldisc.c index e48af9f..aafdbae 100644 --- a/drivers/char/tty_ldisc.c +++ b/drivers/char/tty_ldisc.c @@ -145,48 +145,33 @@ int tty_unregister_ldisc(int disc) } EXPORT_SYMBOL(tty_unregister_ldisc); - -/** - * tty_ldisc_try_get - try and reference an ldisc - * @disc: ldisc number - * - * Attempt to open and lock a line discipline into place. Return - * the line discipline refcounted or an error. - */ - -static struct tty_ldisc *tty_ldisc_try_get(int disc) +static struct tty_ldisc_ops *get_ldops(int disc) { unsigned long flags; - struct tty_ldisc *ld; - struct tty_ldisc_ops *ldops; - int err = -EINVAL; - - ld = kmalloc(sizeof(struct tty_ldisc), GFP_KERNEL); - if (ld == NULL) - return ERR_PTR(-ENOMEM); + struct tty_ldisc_ops *ldops, *ret; spin_lock_irqsave(&tty_ldisc_lock, flags); - ld->ops = NULL; + ret = ERR_PTR(-EINVAL); ldops = tty_ldiscs[disc]; - /* Check the entry is defined */ if (ldops) { - /* If the module is being unloaded we can't use it */ - if (!try_module_get(ldops->owner)) - err = -EAGAIN; - else { - /* lock it */ + ret = ERR_PTR(-EAGAIN); + if (try_module_get(ldops->owner)) { ldops->refcount++; - ld->ops = ldops; - atomic_set(&ld->users, 1); - err = 0; + ret = ldops; } } spin_unlock_irqrestore(&tty_ldisc_lock, flags); - if (err) { - kfree(ld); - return ERR_PTR(err); - } - return ld; + return ret; +} + +static void put_ldops(struct tty_ldisc_ops *ldops) +{ + unsigned long flags; + + spin_lock_irqsave(&tty_ldisc_lock, flags); + ldops->refcount--; + module_put(ldops->owner); + spin_unlock_irqrestore(&tty_ldisc_lock, flags); } /** @@ -205,14 +190,31 @@ static struct tty_ldisc *tty_ldisc_try_get(int disc) static struct tty_ldisc *tty_ldisc_get(int disc) { struct tty_ldisc *ld; + struct tty_ldisc_ops *ldops; if (disc < N_TTY || disc >= NR_LDISCS) return ERR_PTR(-EINVAL); - ld = tty_ldisc_try_get(disc); - if (IS_ERR(ld)) { + + /* + * Get the ldisc ops - we may need to request them to be loaded + * dynamically and try again. + */ + ldops = get_ldops(disc); + if (IS_ERR(ldops)) { request_module("tty-ldisc-%d", disc); - ld = tty_ldisc_try_get(disc); + ldops = get_ldops(disc); + if (IS_ERR(ldops)) + return ERR_CAST(ldops); } + + ld = kmalloc(sizeof(struct tty_ldisc), GFP_KERNEL); + if (ld == NULL) { + put_ldops(ldops); + return ERR_PTR(-ENOMEM); + } + + ld->ops = ldops; + atomic_set(&ld->users, 1); return ld; } @@ -234,13 +236,13 @@ static void tty_ldiscs_seq_stop(struct seq_file *m, void *v) static int tty_ldiscs_seq_show(struct seq_file *m, void *v) { int i = *(loff_t *)v; - struct tty_ldisc *ld; + struct tty_ldisc_ops *ldops; - ld = tty_ldisc_try_get(i); - if (IS_ERR(ld)) + ldops = get_ldops(i); + if (IS_ERR(ldops)) return 0; - seq_printf(m, "%-10s %2d\n", ld->ops->name ? ld->ops->name : "???", i); - put_ldisc(ld); + seq_printf(m, "%-10s %2d\n", ldops->name ? ldops->name : "???", i); + put_ldops(ldops); return 0; } diff --git a/drivers/char/tty_port.c b/drivers/char/tty_port.c index 9769b11..a4bbb28 100644 --- a/drivers/char/tty_port.c +++ b/drivers/char/tty_port.c @@ -23,6 +23,7 @@ void tty_port_init(struct tty_port *port) memset(port, 0, sizeof(*port)); init_waitqueue_head(&port->open_wait); init_waitqueue_head(&port->close_wait); + init_waitqueue_head(&port->delta_msr_wait); mutex_init(&port->mutex); spin_lock_init(&port->lock); port->close_delay = (50 * HZ) / 100; @@ -96,6 +97,14 @@ void tty_port_tty_set(struct tty_port *port, struct tty_struct *tty) } EXPORT_SYMBOL(tty_port_tty_set); +static void tty_port_shutdown(struct tty_port *port) +{ + if (port->ops->shutdown && + test_and_clear_bit(ASYNCB_INITIALIZED, &port->flags)) + port->ops->shutdown(port); + +} + /** * tty_port_hangup - hangup helper * @port: tty port @@ -116,6 +125,8 @@ void tty_port_hangup(struct tty_port *port) port->tty = NULL; spin_unlock_irqrestore(&port->lock, flags); wake_up_interruptible(&port->open_wait); + wake_up_interruptible(&port->delta_msr_wait); + tty_port_shutdown(port); } EXPORT_SYMBOL(tty_port_hangup); @@ -296,15 +307,17 @@ int tty_port_close_start(struct tty_port *port, struct tty_struct *tty, struct f if (port->count) { spin_unlock_irqrestore(&port->lock, flags); + if (port->ops->drop) + port->ops->drop(port); return 0; } - port->flags |= ASYNC_CLOSING; + set_bit(ASYNCB_CLOSING, &port->flags); tty->closing = 1; spin_unlock_irqrestore(&port->lock, flags); /* Don't block on a stalled port, just pull the chain */ if (tty->flow_stopped) tty_driver_flush_buffer(tty); - if (port->flags & ASYNC_INITIALIZED && + if (test_bit(ASYNCB_INITIALIZED, &port->flags) && port->closing_wait != ASYNC_CLOSING_WAIT_NONE) tty_wait_until_sent(tty, port->closing_wait); if (port->drain_delay) { @@ -318,6 +331,9 @@ int tty_port_close_start(struct tty_port *port, struct tty_struct *tty, struct f timeout = 2 * HZ; schedule_timeout_interruptible(timeout); } + /* Don't call port->drop for the last reference. Callers will want + to drop the last active reference in ->shutdown() or the tty + shutdown path */ return 1; } EXPORT_SYMBOL(tty_port_close_start); @@ -348,3 +364,14 @@ void tty_port_close_end(struct tty_port *port, struct tty_struct *tty) spin_unlock_irqrestore(&port->lock, flags); } EXPORT_SYMBOL(tty_port_close_end); + +void tty_port_close(struct tty_port *port, struct tty_struct *tty, + struct file *filp) +{ + if (tty_port_close_start(port, tty, filp) == 0) + return; + tty_port_shutdown(port); + tty_port_close_end(port, tty); + tty_port_tty_set(port, NULL); +} +EXPORT_SYMBOL(tty_port_close); diff --git a/drivers/char/uv_mmtimer.c b/drivers/char/uv_mmtimer.c new file mode 100644 index 0000000..867b67b --- /dev/null +++ b/drivers/char/uv_mmtimer.c @@ -0,0 +1,216 @@ +/* + * Timer device implementation for SGI UV platform. + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (c) 2009 Silicon Graphics, Inc. All rights reserved. + * + */ + +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/ioctl.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/errno.h> +#include <linux/mm.h> +#include <linux/fs.h> +#include <linux/mmtimer.h> +#include <linux/miscdevice.h> +#include <linux/posix-timers.h> +#include <linux/interrupt.h> +#include <linux/time.h> +#include <linux/math64.h> +#include <linux/smp_lock.h> + +#include <asm/genapic.h> +#include <asm/uv/uv_hub.h> +#include <asm/uv/bios.h> +#include <asm/uv/uv.h> + +MODULE_AUTHOR("Dimitri Sivanich <sivanich@sgi.com>"); +MODULE_DESCRIPTION("SGI UV Memory Mapped RTC Timer"); +MODULE_LICENSE("GPL"); + +/* name of the device, usually in /dev */ +#define UV_MMTIMER_NAME "mmtimer" +#define UV_MMTIMER_DESC "SGI UV Memory Mapped RTC Timer" +#define UV_MMTIMER_VERSION "1.0" + +static long uv_mmtimer_ioctl(struct file *file, unsigned int cmd, + unsigned long arg); +static int uv_mmtimer_mmap(struct file *file, struct vm_area_struct *vma); + +/* + * Period in femtoseconds (10^-15 s) + */ +static unsigned long uv_mmtimer_femtoperiod; + +static const struct file_operations uv_mmtimer_fops = { + .owner = THIS_MODULE, + .mmap = uv_mmtimer_mmap, + .unlocked_ioctl = uv_mmtimer_ioctl, +}; + +/** + * uv_mmtimer_ioctl - ioctl interface for /dev/uv_mmtimer + * @file: file structure for the device + * @cmd: command to execute + * @arg: optional argument to command + * + * Executes the command specified by @cmd. Returns 0 for success, < 0 for + * failure. + * + * Valid commands: + * + * %MMTIMER_GETOFFSET - Should return the offset (relative to the start + * of the page where the registers are mapped) for the counter in question. + * + * %MMTIMER_GETRES - Returns the resolution of the clock in femto (10^-15) + * seconds + * + * %MMTIMER_GETFREQ - Copies the frequency of the clock in Hz to the address + * specified by @arg + * + * %MMTIMER_GETBITS - Returns the number of bits in the clock's counter + * + * %MMTIMER_MMAPAVAIL - Returns 1 if registers can be mmap'd into userspace + * + * %MMTIMER_GETCOUNTER - Gets the current value in the counter and places it + * in the address specified by @arg. + */ +static long uv_mmtimer_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + int ret = 0; + + switch (cmd) { + case MMTIMER_GETOFFSET: /* offset of the counter */ + /* + * UV RTC register is on its own page + */ + if (PAGE_SIZE <= (1 << 16)) + ret = ((UV_LOCAL_MMR_BASE | UVH_RTC) & (PAGE_SIZE-1)) + / 8; + else + ret = -ENOSYS; + break; + + case MMTIMER_GETRES: /* resolution of the clock in 10^-15 s */ + if (copy_to_user((unsigned long __user *)arg, + &uv_mmtimer_femtoperiod, sizeof(unsigned long))) + ret = -EFAULT; + break; + + case MMTIMER_GETFREQ: /* frequency in Hz */ + if (copy_to_user((unsigned long __user *)arg, + &sn_rtc_cycles_per_second, + sizeof(unsigned long))) + ret = -EFAULT; + break; + + case MMTIMER_GETBITS: /* number of bits in the clock */ + ret = hweight64(UVH_RTC_REAL_TIME_CLOCK_MASK); + break; + + case MMTIMER_MMAPAVAIL: /* can we mmap the clock into userspace? */ + ret = (PAGE_SIZE <= (1 << 16)) ? 1 : 0; + break; + + case MMTIMER_GETCOUNTER: + if (copy_to_user((unsigned long __user *)arg, + (unsigned long *)uv_local_mmr_address(UVH_RTC), + sizeof(unsigned long))) + ret = -EFAULT; + break; + default: + ret = -ENOTTY; + break; + } + return ret; +} + +/** + * uv_mmtimer_mmap - maps the clock's registers into userspace + * @file: file structure for the device + * @vma: VMA to map the registers into + * + * Calls remap_pfn_range() to map the clock's registers into + * the calling process' address space. + */ +static int uv_mmtimer_mmap(struct file *file, struct vm_area_struct *vma) +{ + unsigned long uv_mmtimer_addr; + + if (vma->vm_end - vma->vm_start != PAGE_SIZE) + return -EINVAL; + + if (vma->vm_flags & VM_WRITE) + return -EPERM; + + if (PAGE_SIZE > (1 << 16)) + return -ENOSYS; + + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + + uv_mmtimer_addr = UV_LOCAL_MMR_BASE | UVH_RTC; + uv_mmtimer_addr &= ~(PAGE_SIZE - 1); + uv_mmtimer_addr &= 0xfffffffffffffffUL; + + if (remap_pfn_range(vma, vma->vm_start, uv_mmtimer_addr >> PAGE_SHIFT, + PAGE_SIZE, vma->vm_page_prot)) { + printk(KERN_ERR "remap_pfn_range failed in uv_mmtimer_mmap\n"); + return -EAGAIN; + } + + return 0; +} + +static struct miscdevice uv_mmtimer_miscdev = { + MISC_DYNAMIC_MINOR, + UV_MMTIMER_NAME, + &uv_mmtimer_fops +}; + + +/** + * uv_mmtimer_init - device initialization routine + * + * Does initial setup for the uv_mmtimer device. + */ +static int __init uv_mmtimer_init(void) +{ + if (!is_uv_system()) { + printk(KERN_ERR "%s: Hardware unsupported\n", UV_MMTIMER_NAME); + return -1; + } + + /* + * Sanity check the cycles/sec variable + */ + if (sn_rtc_cycles_per_second < 100000) { + printk(KERN_ERR "%s: unable to determine clock frequency\n", + UV_MMTIMER_NAME); + return -1; + } + + uv_mmtimer_femtoperiod = ((unsigned long)1E15 + + sn_rtc_cycles_per_second / 2) / + sn_rtc_cycles_per_second; + + if (misc_register(&uv_mmtimer_miscdev)) { + printk(KERN_ERR "%s: failed to register device\n", + UV_MMTIMER_NAME); + return -1; + } + + printk(KERN_INFO "%s: v%s, %ld MHz\n", UV_MMTIMER_DESC, + UV_MMTIMER_VERSION, + sn_rtc_cycles_per_second/(unsigned long)1E6); + + return 0; +} + +module_init(uv_mmtimer_init); diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index c74dacf..0d328b5 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -31,6 +31,7 @@ #include <linux/err.h> #include <linux/init.h> #include <linux/virtio.h> +#include <linux/virtio_ids.h> #include <linux/virtio_console.h> #include "hvc_console.h" @@ -65,7 +66,7 @@ static int put_chars(u32 vtermno, const char *buf, int count) /* add_buf wants a token to identify this buffer: we hand it any * non-NULL pointer, since there's only ever one buffer. */ - if (out_vq->vq_ops->add_buf(out_vq, sg, 1, 0, (void *)1) == 0) { + if (out_vq->vq_ops->add_buf(out_vq, sg, 1, 0, (void *)1) >= 0) { /* Tell Host to go! */ out_vq->vq_ops->kick(out_vq); /* Chill out until it's done with the buffer. */ @@ -85,7 +86,7 @@ static void add_inbuf(void) sg_init_one(sg, inbuf, PAGE_SIZE); /* We should always be able to add one buffer to an empty queue. */ - if (in_vq->vq_ops->add_buf(in_vq, sg, 0, 1, inbuf) != 0) + if (in_vq->vq_ops->add_buf(in_vq, sg, 0, 1, inbuf) < 0) BUG(); in_vq->vq_ops->kick(in_vq); } diff --git a/drivers/char/vt.c b/drivers/char/vt.c index 404f4c1..0c80c68 100644 --- a/drivers/char/vt.c +++ b/drivers/char/vt.c @@ -252,7 +252,6 @@ static void notify_update(struct vc_data *vc) struct vt_notifier_param param = { .vc = vc }; atomic_notifier_call_chain(&vt_notifier_list, VT_UPDATE, ¶m); } - /* * Low-Level Functions */ @@ -935,6 +934,7 @@ static int vc_do_resize(struct tty_struct *tty, struct vc_data *vc, if (CON_IS_VISIBLE(vc)) update_screen(vc); + vt_event_post(VT_EVENT_RESIZE, vc->vc_num, vc->vc_num); return err; } @@ -2129,11 +2129,7 @@ static int do_con_write(struct tty_struct *tty, const unsigned char *buf, int co currcons = vc->vc_num; if (!vc_cons_allocated(currcons)) { /* could this happen? */ - static int error = 0; - if (!error) { - error = 1; - printk("con_write: tty %d not allocated\n", currcons+1); - } + printk_once("con_write: tty %d not allocated\n", currcons+1); release_console_sem(); return 0; } @@ -2910,6 +2906,9 @@ static const struct tty_operations con_ops = { .flush_chars = con_flush_chars, .chars_in_buffer = con_chars_in_buffer, .ioctl = vt_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = vt_compat_ioctl, +#endif .stop = con_stop, .start = con_start, .throttle = con_throttle, @@ -2948,9 +2947,6 @@ int __init vty_init(const struct file_operations *console_fops) panic("Couldn't register console driver\n"); kbd_init(); console_map_init(); -#ifdef CONFIG_PROM_CONSOLE - prom_con_init(); -#endif #ifdef CONFIG_MDA_CONSOLE mda_console_init(); #endif @@ -2958,7 +2954,6 @@ int __init vty_init(const struct file_operations *console_fops) } #ifndef VT_SINGLE_DRIVER -#include <linux/device.h> static struct class *vtconsole_class; @@ -3641,6 +3636,7 @@ void do_blank_screen(int entering_gfx) blank_state = blank_vesa_wait; mod_timer(&console_timer, jiffies + vesa_off_interval); } + vt_event_post(VT_EVENT_BLANK, vc->vc_num, vc->vc_num); } EXPORT_SYMBOL(do_blank_screen); @@ -3685,6 +3681,7 @@ void do_unblank_screen(int leaving_gfx) console_blank_hook(0); set_palette(vc); set_cursor(vc); + vt_event_post(VT_EVENT_UNBLANK, vc->vc_num, vc->vc_num); } EXPORT_SYMBOL(do_unblank_screen); diff --git a/drivers/char/vt_ioctl.c b/drivers/char/vt_ioctl.c index 95189f2..29c651a 100644 --- a/drivers/char/vt_ioctl.c +++ b/drivers/char/vt_ioctl.c @@ -16,6 +16,8 @@ #include <linux/tty.h> #include <linux/timer.h> #include <linux/kernel.h> +#include <linux/compat.h> +#include <linux/module.h> #include <linux/kd.h> #include <linux/vt.h> #include <linux/string.h> @@ -62,6 +64,133 @@ extern struct tty_driver *console_driver; static void complete_change_console(struct vc_data *vc); /* + * User space VT_EVENT handlers + */ + +struct vt_event_wait { + struct list_head list; + struct vt_event event; + int done; +}; + +static LIST_HEAD(vt_events); +static DEFINE_SPINLOCK(vt_event_lock); +static DECLARE_WAIT_QUEUE_HEAD(vt_event_waitqueue); + +/** + * vt_event_post + * @event: the event that occurred + * @old: old console + * @new: new console + * + * Post an VT event to interested VT handlers + */ + +void vt_event_post(unsigned int event, unsigned int old, unsigned int new) +{ + struct list_head *pos, *head; + unsigned long flags; + int wake = 0; + + spin_lock_irqsave(&vt_event_lock, flags); + head = &vt_events; + + list_for_each(pos, head) { + struct vt_event_wait *ve = list_entry(pos, + struct vt_event_wait, list); + if (!(ve->event.event & event)) + continue; + ve->event.event = event; + /* kernel view is consoles 0..n-1, user space view is + console 1..n with 0 meaning current, so we must bias */ + ve->event.old = old + 1; + ve->event.new = new + 1; + wake = 1; + ve->done = 1; + } + spin_unlock_irqrestore(&vt_event_lock, flags); + if (wake) + wake_up_interruptible(&vt_event_waitqueue); +} + +/** + * vt_event_wait - wait for an event + * @vw: our event + * + * Waits for an event to occur which completes our vt_event_wait + * structure. On return the structure has wv->done set to 1 for success + * or 0 if some event such as a signal ended the wait. + */ + +static void vt_event_wait(struct vt_event_wait *vw) +{ + unsigned long flags; + /* Prepare the event */ + INIT_LIST_HEAD(&vw->list); + vw->done = 0; + /* Queue our event */ + spin_lock_irqsave(&vt_event_lock, flags); + list_add(&vw->list, &vt_events); + spin_unlock_irqrestore(&vt_event_lock, flags); + /* Wait for it to pass */ + wait_event_interruptible(vt_event_waitqueue, vw->done); + /* Dequeue it */ + spin_lock_irqsave(&vt_event_lock, flags); + list_del(&vw->list); + spin_unlock_irqrestore(&vt_event_lock, flags); +} + +/** + * vt_event_wait_ioctl - event ioctl handler + * @arg: argument to ioctl + * + * Implement the VT_WAITEVENT ioctl using the VT event interface + */ + +static int vt_event_wait_ioctl(struct vt_event __user *event) +{ + struct vt_event_wait vw; + + if (copy_from_user(&vw.event, event, sizeof(struct vt_event))) + return -EFAULT; + /* Highest supported event for now */ + if (vw.event.event & ~VT_MAX_EVENT) + return -EINVAL; + + vt_event_wait(&vw); + /* If it occurred report it */ + if (vw.done) { + if (copy_to_user(event, &vw.event, sizeof(struct vt_event))) + return -EFAULT; + return 0; + } + return -EINTR; +} + +/** + * vt_waitactive - active console wait + * @event: event code + * @n: new console + * + * Helper for event waits. Used to implement the legacy + * event waiting ioctls in terms of events + */ + +int vt_waitactive(int n) +{ + struct vt_event_wait vw; + do { + if (n == fg_console + 1) + break; + vw.event.event = VT_EVENT_SWITCH; + vt_event_wait(&vw); + if (vw.done == 0) + return -EINTR; + } while (vw.event.new != n); + return 0; +} + +/* * these are the valid i/o ports we're allowed to change. they map all the * video ports */ @@ -360,6 +489,8 @@ do_unimap_ioctl(int cmd, struct unimapdesc __user *user_ud, int perm, struct vc_ return 0; } + + /* * We handle the console-specific ioctl's here. We allow the * capability to modify any console, not just the fg_console. @@ -842,6 +973,41 @@ int vt_ioctl(struct tty_struct *tty, struct file * file, } break; + case VT_SETACTIVATE: + { + struct vt_setactivate vsa; + + if (!perm) + goto eperm; + + if (copy_from_user(&vsa, (struct vt_setactivate __user *)arg, + sizeof(struct vt_setactivate))) + return -EFAULT; + if (vsa.console == 0 || vsa.console > MAX_NR_CONSOLES) + ret = -ENXIO; + else { + vsa.console--; + acquire_console_sem(); + ret = vc_allocate(vsa.console); + if (ret == 0) { + struct vc_data *nvc; + /* This is safe providing we don't drop the + console sem between vc_allocate and + finishing referencing nvc */ + nvc = vc_cons[vsa.console].d; + nvc->vt_mode = vsa.mode; + nvc->vt_mode.frsig = 0; + put_pid(nvc->vt_pid); + nvc->vt_pid = get_pid(task_pid(current)); + } + release_console_sem(); + if (ret) + break; + /* Commence switch and lock */ + set_console(arg); + } + } + /* * wait until the specified VT has been activated */ @@ -851,7 +1017,7 @@ int vt_ioctl(struct tty_struct *tty, struct file * file, if (arg == 0 || arg > MAX_NR_CONSOLES) ret = -ENXIO; else - ret = vt_waitactive(arg - 1); + ret = vt_waitactive(arg); break; /* @@ -1159,6 +1325,9 @@ int vt_ioctl(struct tty_struct *tty, struct file * file, ret = put_user(vc->vc_hi_font_mask, (unsigned short __user *)arg); break; + case VT_WAITEVENT: + ret = vt_event_wait_ioctl((struct vt_event __user *)arg); + break; default: ret = -ENOIOCTLCMD; } @@ -1170,54 +1339,6 @@ eperm: goto out; } -/* - * Sometimes we want to wait until a particular VT has been activated. We - * do it in a very simple manner. Everybody waits on a single queue and - * get woken up at once. Those that are satisfied go on with their business, - * while those not ready go back to sleep. Seems overkill to add a wait - * to each vt just for this - usually this does nothing! - */ -static DECLARE_WAIT_QUEUE_HEAD(vt_activate_queue); - -/* - * Sleeps until a vt is activated, or the task is interrupted. Returns - * 0 if activation, -EINTR if interrupted by a signal handler. - */ -int vt_waitactive(int vt) -{ - int retval; - DECLARE_WAITQUEUE(wait, current); - - add_wait_queue(&vt_activate_queue, &wait); - for (;;) { - retval = 0; - - /* - * Synchronize with redraw_screen(). By acquiring the console - * semaphore we make sure that the console switch is completed - * before we return. If we didn't wait for the semaphore, we - * could return at a point where fg_console has already been - * updated, but the console switch hasn't been completed. - */ - acquire_console_sem(); - set_current_state(TASK_INTERRUPTIBLE); - if (vt == fg_console) { - release_console_sem(); - break; - } - release_console_sem(); - retval = -ERESTARTNOHAND; - if (signal_pending(current)) - break; - schedule(); - } - remove_wait_queue(&vt_activate_queue, &wait); - __set_current_state(TASK_RUNNING); - return retval; -} - -#define vt_wake_waitactive() wake_up(&vt_activate_queue) - void reset_vc(struct vc_data *vc) { vc->vc_mode = KD_TEXT; @@ -1256,12 +1377,216 @@ void vc_SAK(struct work_struct *work) release_console_sem(); } +#ifdef CONFIG_COMPAT + +struct compat_consolefontdesc { + unsigned short charcount; /* characters in font (256 or 512) */ + unsigned short charheight; /* scan lines per character (1-32) */ + compat_caddr_t chardata; /* font data in expanded form */ +}; + +static inline int +compat_fontx_ioctl(int cmd, struct compat_consolefontdesc __user *user_cfd, + int perm, struct console_font_op *op) +{ + struct compat_consolefontdesc cfdarg; + int i; + + if (copy_from_user(&cfdarg, user_cfd, sizeof(struct compat_consolefontdesc))) + return -EFAULT; + + switch (cmd) { + case PIO_FONTX: + if (!perm) + return -EPERM; + op->op = KD_FONT_OP_SET; + op->flags = KD_FONT_FLAG_OLD; + op->width = 8; + op->height = cfdarg.charheight; + op->charcount = cfdarg.charcount; + op->data = compat_ptr(cfdarg.chardata); + return con_font_op(vc_cons[fg_console].d, op); + case GIO_FONTX: + op->op = KD_FONT_OP_GET; + op->flags = KD_FONT_FLAG_OLD; + op->width = 8; + op->height = cfdarg.charheight; + op->charcount = cfdarg.charcount; + op->data = compat_ptr(cfdarg.chardata); + i = con_font_op(vc_cons[fg_console].d, op); + if (i) + return i; + cfdarg.charheight = op->height; + cfdarg.charcount = op->charcount; + if (copy_to_user(user_cfd, &cfdarg, sizeof(struct compat_consolefontdesc))) + return -EFAULT; + return 0; + } + return -EINVAL; +} + +struct compat_console_font_op { + compat_uint_t op; /* operation code KD_FONT_OP_* */ + compat_uint_t flags; /* KD_FONT_FLAG_* */ + compat_uint_t width, height; /* font size */ + compat_uint_t charcount; + compat_caddr_t data; /* font data with height fixed to 32 */ +}; + +static inline int +compat_kdfontop_ioctl(struct compat_console_font_op __user *fontop, + int perm, struct console_font_op *op, struct vc_data *vc) +{ + int i; + + if (copy_from_user(op, fontop, sizeof(struct compat_console_font_op))) + return -EFAULT; + if (!perm && op->op != KD_FONT_OP_GET) + return -EPERM; + op->data = compat_ptr(((struct compat_console_font_op *)op)->data); + op->flags |= KD_FONT_FLAG_OLD; + i = con_font_op(vc, op); + if (i) + return i; + ((struct compat_console_font_op *)op)->data = (unsigned long)op->data; + if (copy_to_user(fontop, op, sizeof(struct compat_console_font_op))) + return -EFAULT; + return 0; +} + +struct compat_unimapdesc { + unsigned short entry_ct; + compat_caddr_t entries; +}; + +static inline int +compat_unimap_ioctl(unsigned int cmd, struct compat_unimapdesc __user *user_ud, + int perm, struct vc_data *vc) +{ + struct compat_unimapdesc tmp; + struct unipair __user *tmp_entries; + + if (copy_from_user(&tmp, user_ud, sizeof tmp)) + return -EFAULT; + tmp_entries = compat_ptr(tmp.entries); + if (tmp_entries) + if (!access_ok(VERIFY_WRITE, tmp_entries, + tmp.entry_ct*sizeof(struct unipair))) + return -EFAULT; + switch (cmd) { + case PIO_UNIMAP: + if (!perm) + return -EPERM; + return con_set_unimap(vc, tmp.entry_ct, tmp_entries); + case GIO_UNIMAP: + if (!perm && fg_console != vc->vc_num) + return -EPERM; + return con_get_unimap(vc, tmp.entry_ct, &(user_ud->entry_ct), tmp_entries); + } + return 0; +} + +long vt_compat_ioctl(struct tty_struct *tty, struct file * file, + unsigned int cmd, unsigned long arg) +{ + struct vc_data *vc = tty->driver_data; + struct console_font_op op; /* used in multiple places here */ + struct kbd_struct *kbd; + unsigned int console; + void __user *up = (void __user *)arg; + int perm; + int ret = 0; + + console = vc->vc_num; + + lock_kernel(); + + if (!vc_cons_allocated(console)) { /* impossible? */ + ret = -ENOIOCTLCMD; + goto out; + } + + /* + * To have permissions to do most of the vt ioctls, we either have + * to be the owner of the tty, or have CAP_SYS_TTY_CONFIG. + */ + perm = 0; + if (current->signal->tty == tty || capable(CAP_SYS_TTY_CONFIG)) + perm = 1; + + kbd = kbd_table + console; + switch (cmd) { + /* + * these need special handlers for incompatible data structures + */ + case PIO_FONTX: + case GIO_FONTX: + ret = compat_fontx_ioctl(cmd, up, perm, &op); + break; + + case KDFONTOP: + ret = compat_kdfontop_ioctl(up, perm, &op, vc); + break; + + case PIO_UNIMAP: + case GIO_UNIMAP: + ret = do_unimap_ioctl(cmd, up, perm, vc); + break; + + /* + * all these treat 'arg' as an integer + */ + case KIOCSOUND: + case KDMKTONE: +#ifdef CONFIG_X86 + case KDADDIO: + case KDDELIO: +#endif + case KDSETMODE: + case KDMAPDISP: + case KDUNMAPDISP: + case KDSKBMODE: + case KDSKBMETA: + case KDSKBLED: + case KDSETLED: + case KDSIGACCEPT: + case VT_ACTIVATE: + case VT_WAITACTIVE: + case VT_RELDISP: + case VT_DISALLOCATE: + case VT_RESIZE: + case VT_RESIZEX: + goto fallback; + + /* + * the rest has a compatible data structure behind arg, + * but we have to convert it to a proper 64 bit pointer. + */ + default: + arg = (unsigned long)compat_ptr(arg); + goto fallback; + } +out: + unlock_kernel(); + return ret; + +fallback: + unlock_kernel(); + return vt_ioctl(tty, file, cmd, arg); +} + + +#endif /* CONFIG_COMPAT */ + + /* - * Performs the back end of a vt switch + * Performs the back end of a vt switch. Called under the console + * semaphore. */ static void complete_change_console(struct vc_data *vc) { unsigned char old_vc_mode; + int old = fg_console; last_console = fg_console; @@ -1325,7 +1650,7 @@ static void complete_change_console(struct vc_data *vc) /* * Wake anyone waiting for their VT to activate */ - vt_wake_waitactive(); + vt_event_post(VT_EVENT_SWITCH, old, vc->vc_num); return; } @@ -1398,3 +1723,58 @@ void change_console(struct vc_data *new_vc) complete_change_console(new_vc); } + +/* Perform a kernel triggered VT switch for suspend/resume */ + +static int disable_vt_switch; + +int vt_move_to_console(unsigned int vt, int alloc) +{ + int prev; + + acquire_console_sem(); + /* Graphics mode - up to X */ + if (disable_vt_switch) { + release_console_sem(); + return 0; + } + prev = fg_console; + + if (alloc && vc_allocate(vt)) { + /* we can't have a free VC for now. Too bad, + * we don't want to mess the screen for now. */ + release_console_sem(); + return -ENOSPC; + } + + if (set_console(vt)) { + /* + * We're unable to switch to the SUSPEND_CONSOLE. + * Let the calling function know so it can decide + * what to do. + */ + release_console_sem(); + return -EIO; + } + release_console_sem(); + if (vt_waitactive(vt + 1)) { + pr_debug("Suspend: Can't switch VCs."); + return -EINTR; + } + return prev; +} + +/* + * Normally during a suspend, we allocate a new console and switch to it. + * When we resume, we switch back to the original console. This switch + * can be slow, so on systems where the framebuffer can handle restoration + * of video registers anyways, there's little point in doing the console + * switch. This function allows you to disable it by passing it '0'. + */ +void pm_set_vt_switch(int do_switch) +{ + acquire_console_sem(); + disable_vt_switch = !do_switch; + release_console_sem(); +} +EXPORT_SYMBOL(pm_set_vt_switch); |