diff options
Diffstat (limited to 'drivers')
47 files changed, 40 insertions, 14813 deletions
diff --git a/drivers/Kconfig b/drivers/Kconfig index 61cf4ea2c229..477d63d0364d 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -30,8 +30,6 @@ source "drivers/block/Kconfig" source "drivers/nvme/Kconfig" -# misc before ide - BLK_DEV_SGIIOC4 depends on SGI_IOC4 - source "drivers/misc/Kconfig" source "drivers/ide/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index 6d37564e783c..05be8c798c51 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -132,7 +132,6 @@ obj-y += mmc/ obj-$(CONFIG_MEMSTICK) += memstick/ obj-$(CONFIG_NEW_LEDS) += leds/ obj-$(CONFIG_INFINIBAND) += infiniband/ -obj-$(CONFIG_SGI_SN) += sn/ obj-y += firmware/ obj-$(CONFIG_CRYPTO) += crypto/ obj-$(CONFIG_SUPERH) += sh/ diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index 5f6158973289..ebe1e9e5fd81 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -323,7 +323,7 @@ config ACPI_NUMA bool "NUMA support" depends on NUMA depends on (X86 || IA64 || ARM64) - default y if IA64_GENERIC || IA64_SGI_SN2 || ARM64 + default y if IA64 || ARM64 config ACPI_CUSTOM_DSDT_FILE string "Custom DSDT Table file to include" diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index 2794f4b3f62d..df0fc997dc3e 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig @@ -26,28 +26,6 @@ config DEVKMEM kind of kernel debugging operations. When in doubt, say "N". -config SGI_SNSC - bool "SGI Altix system controller communication support" - depends on (IA64_SGI_SN2 || IA64_GENERIC) - help - If you have an SGI Altix and you want to enable system - controller communication from user space (you want this!), - say Y. Otherwise, say N. - -config SGI_TIOCX - bool "SGI TIO CX driver support" - depends on (IA64_SGI_SN2 || IA64_GENERIC) - help - If you have an SGI Altix and you have fpga devices attached - to your TIO, say Y here, otherwise say N. - -config SGI_MBCS - tristate "SGI FPGA Core Services driver support" - depends on SGI_TIOCX - help - If you have an SGI Altix with an attached SABrick - say Y or M here, otherwise say N. - source "drivers/tty/serial/Kconfig" source "drivers/tty/serdev/Kconfig" diff --git a/drivers/char/Makefile b/drivers/char/Makefile index fbea7dd12932..7c5ea6f9df14 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -9,11 +9,9 @@ obj-y += misc.o obj-$(CONFIG_ATARI_DSP56K) += dsp56k.o obj-$(CONFIG_VIRTIO_CONSOLE) += virtio_console.o obj-$(CONFIG_RAW_DRIVER) += raw.o -obj-$(CONFIG_SGI_SNSC) += snsc.o snsc_event.o obj-$(CONFIG_MSPEC) += mspec.o obj-$(CONFIG_UV_MMTIMER) += uv_mmtimer.o obj-$(CONFIG_IBM_BSR) += bsr.o -obj-$(CONFIG_SGI_MBCS) += mbcs.o obj-$(CONFIG_PRINTER) += lp.o diff --git a/drivers/char/agp/Kconfig b/drivers/char/agp/Kconfig index 6231714ef3c8..812d6aa6e013 100644 --- a/drivers/char/agp/Kconfig +++ b/drivers/char/agp/Kconfig @@ -111,14 +111,14 @@ config AGP_VIA config AGP_I460 tristate "Intel 460GX chipset support" - depends on AGP && (IA64_DIG || IA64_GENERIC) + depends on AGP && IA64 help This option gives you AGP GART support for the Intel 460GX chipset for IA64 processors. config AGP_HP_ZX1 tristate "HP ZX1 chipset AGP support" - depends on AGP && (IA64_HP_ZX1 || IA64_HP_ZX1_SWIOTLB || IA64_GENERIC) + depends on AGP && IA64 help This option gives you AGP GART support for the HP ZX1 chipset for IA64 processors. @@ -150,13 +150,6 @@ config AGP_EFFICEON This option gives you AGP support for the Transmeta Efficeon series processors with integrated northbridges. -config AGP_SGI_TIOCA - tristate "SGI TIO chipset AGP support" - depends on AGP && (IA64_SGI_SN2 || IA64_GENERIC) - help - This option gives you AGP GART support for the SGI TIO chipset - for IA64 processors. - config INTEL_GTT tristate depends on X86 && PCI diff --git a/drivers/char/agp/Makefile b/drivers/char/agp/Makefile index 4a786ffd9dee..cb2497d157f6 100644 --- a/drivers/char/agp/Makefile +++ b/drivers/char/agp/Makefile @@ -16,7 +16,6 @@ obj-$(CONFIG_AGP_I460) += i460-agp.o obj-$(CONFIG_AGP_INTEL) += intel-agp.o obj-$(CONFIG_INTEL_GTT) += intel-gtt.o obj-$(CONFIG_AGP_NVIDIA) += nvidia-agp.o -obj-$(CONFIG_AGP_SGI_TIOCA) += sgi-agp.o obj-$(CONFIG_AGP_SIS) += sis-agp.o obj-$(CONFIG_AGP_SWORKS) += sworks-agp.o obj-$(CONFIG_AGP_UNINORTH) += uninorth-agp.o diff --git a/drivers/char/agp/sgi-agp.c b/drivers/char/agp/sgi-agp.c deleted file mode 100644 index e7d5bdc02d93..000000000000 --- a/drivers/char/agp/sgi-agp.c +++ /dev/null @@ -1,338 +0,0 @@ -/* - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - * - * Copyright (C) 2003-2005 Silicon Graphics, Inc. All Rights Reserved. - */ - -/* - * SGI TIOCA AGPGART routines. - * - */ - -#include <linux/acpi.h> -#include <linux/module.h> -#include <linux/pci.h> -#include <linux/slab.h> -#include <linux/agp_backend.h> -#include <asm/sn/addrs.h> -#include <asm/sn/io.h> -#include <asm/sn/pcidev.h> -#include <asm/sn/pcibus_provider_defs.h> -#include <asm/sn/tioca_provider.h> -#include "agp.h" - -extern int agp_memory_reserved; -extern uint32_t tioca_gart_found; -extern struct list_head tioca_list; -static struct agp_bridge_data **sgi_tioca_agp_bridges; - -/* - * The aperature size and related information is set up at TIOCA init time. - * Values for this table will be extracted and filled in at - * sgi_tioca_fetch_size() time. - */ - -static struct aper_size_info_fixed sgi_tioca_sizes[] = { - {0, 0, 0}, -}; - -static struct page *sgi_tioca_alloc_page(struct agp_bridge_data *bridge) -{ - struct page *page; - int nid; - struct tioca_kernel *info = - (struct tioca_kernel *)bridge->dev_private_data; - - nid = info->ca_closest_node; - page = alloc_pages_node(nid, GFP_KERNEL, 0); - if (!page) - return NULL; - - get_page(page); - atomic_inc(&agp_bridge->current_memory_agp); - return page; -} - -/* - * Flush GART tlb's. Cannot selectively flush based on memory so the mem - * arg is ignored. - */ - -static void sgi_tioca_tlbflush(struct agp_memory *mem) -{ - tioca_tlbflush(mem->bridge->dev_private_data); -} - -/* - * Given an address of a host physical page, turn it into a valid gart - * entry. - */ -static unsigned long -sgi_tioca_mask_memory(struct agp_bridge_data *bridge, dma_addr_t addr, - int type) -{ - return tioca_physpage_to_gart(addr); -} - -static void sgi_tioca_agp_enable(struct agp_bridge_data *bridge, u32 mode) -{ - tioca_fastwrite_enable(bridge->dev_private_data); -} - -/* - * sgi_tioca_configure() doesn't have anything to do since the base CA driver - * has alreay set up the GART. - */ - -static int sgi_tioca_configure(void) -{ - return 0; -} - -/* - * Determine gfx aperature size. This has already been determined by the - * CA driver init, so just need to set agp_bridge values accordingly. - */ - -static int sgi_tioca_fetch_size(void) -{ - struct tioca_kernel *info = - (struct tioca_kernel *)agp_bridge->dev_private_data; - - sgi_tioca_sizes[0].size = info->ca_gfxap_size / MB(1); - sgi_tioca_sizes[0].num_entries = info->ca_gfxgart_entries; - - return sgi_tioca_sizes[0].size; -} - -static int sgi_tioca_create_gatt_table(struct agp_bridge_data *bridge) -{ - struct tioca_kernel *info = - (struct tioca_kernel *)bridge->dev_private_data; - - bridge->gatt_table_real = (u32 *) info->ca_gfxgart; - bridge->gatt_table = bridge->gatt_table_real; - bridge->gatt_bus_addr = info->ca_gfxgart_base; - - return 0; -} - -static int sgi_tioca_free_gatt_table(struct agp_bridge_data *bridge) -{ - return 0; -} - -static int sgi_tioca_insert_memory(struct agp_memory *mem, off_t pg_start, - int type) -{ - int num_entries; - size_t i; - off_t j; - void *temp; - struct agp_bridge_data *bridge; - u64 *table; - - bridge = mem->bridge; - if (!bridge) - return -EINVAL; - - table = (u64 *)bridge->gatt_table; - - temp = bridge->current_size; - - switch (bridge->driver->size_type) { - case U8_APER_SIZE: - num_entries = A_SIZE_8(temp)->num_entries; - break; - case U16_APER_SIZE: - num_entries = A_SIZE_16(temp)->num_entries; - break; - case U32_APER_SIZE: - num_entries = A_SIZE_32(temp)->num_entries; - break; - case FIXED_APER_SIZE: - num_entries = A_SIZE_FIX(temp)->num_entries; - break; - case LVL2_APER_SIZE: - return -EINVAL; - default: - num_entries = 0; - break; - } - - num_entries -= agp_memory_reserved / PAGE_SIZE; - if (num_entries < 0) - num_entries = 0; - - if (type != 0 || mem->type != 0) { - return -EINVAL; - } - - if ((pg_start + mem->page_count) > num_entries) - return -EINVAL; - - j = pg_start; - - while (j < (pg_start + mem->page_count)) { - if (table[j]) - return -EBUSY; - j++; - } - - if (!mem->is_flushed) { - bridge->driver->cache_flush(); - mem->is_flushed = true; - } - - for (i = 0, j = pg_start; i < mem->page_count; i++, j++) { - table[j] = - bridge->driver->mask_memory(bridge, - page_to_phys(mem->pages[i]), - mem->type); - } - - bridge->driver->tlb_flush(mem); - return 0; -} - -static int sgi_tioca_remove_memory(struct agp_memory *mem, off_t pg_start, - int type) -{ - size_t i; - struct agp_bridge_data *bridge; - u64 *table; - - bridge = mem->bridge; - if (!bridge) - return -EINVAL; - - if (type != 0 || mem->type != 0) { - return -EINVAL; - } - - table = (u64 *)bridge->gatt_table; - - for (i = pg_start; i < (mem->page_count + pg_start); i++) { - table[i] = 0; - } - - bridge->driver->tlb_flush(mem); - return 0; -} - -static void sgi_tioca_cache_flush(void) -{ -} - -/* - * Cleanup. Nothing to do as the CA driver owns the GART. - */ - -static void sgi_tioca_cleanup(void) -{ -} - -static struct agp_bridge_data *sgi_tioca_find_bridge(struct pci_dev *pdev) -{ - struct agp_bridge_data *bridge; - - list_for_each_entry(bridge, &agp_bridges, list) { - if (bridge->dev->bus == pdev->bus) - break; - } - return bridge; -} - -const struct agp_bridge_driver sgi_tioca_driver = { - .owner = THIS_MODULE, - .size_type = U16_APER_SIZE, - .configure = sgi_tioca_configure, - .fetch_size = sgi_tioca_fetch_size, - .cleanup = sgi_tioca_cleanup, - .tlb_flush = sgi_tioca_tlbflush, - .mask_memory = sgi_tioca_mask_memory, - .agp_enable = sgi_tioca_agp_enable, - .cache_flush = sgi_tioca_cache_flush, - .create_gatt_table = sgi_tioca_create_gatt_table, - .free_gatt_table = sgi_tioca_free_gatt_table, - .insert_memory = sgi_tioca_insert_memory, - .remove_memory = sgi_tioca_remove_memory, - .alloc_by_type = agp_generic_alloc_by_type, - .free_by_type = agp_generic_free_by_type, - .agp_alloc_page = sgi_tioca_alloc_page, - .agp_destroy_page = agp_generic_destroy_page, - .agp_type_to_mask_type = agp_generic_type_to_mask_type, - .cant_use_aperture = true, - .needs_scratch_page = false, - .num_aperture_sizes = 1, -}; - -static int agp_sgi_init(void) -{ - unsigned int j; - struct tioca_kernel *info; - struct pci_dev *pdev = NULL; - - if (tioca_gart_found) - printk(KERN_INFO PFX "SGI TIO CA GART driver initialized.\n"); - else - return 0; - - sgi_tioca_agp_bridges = kmalloc_array(tioca_gart_found, - sizeof(struct agp_bridge_data *), - GFP_KERNEL); - if (!sgi_tioca_agp_bridges) - return -ENOMEM; - - j = 0; - list_for_each_entry(info, &tioca_list, ca_list) { - if (list_empty(info->ca_devices)) - continue; - list_for_each_entry(pdev, info->ca_devices, bus_list) { - u8 cap_ptr; - - if (pdev->class != (PCI_CLASS_DISPLAY_VGA << 8)) - continue; - cap_ptr = pci_find_capability(pdev, PCI_CAP_ID_AGP); - if (!cap_ptr) - continue; - } - sgi_tioca_agp_bridges[j] = agp_alloc_bridge(); - printk(KERN_INFO PFX "bridge %d = 0x%p\n", j, - sgi_tioca_agp_bridges[j]); - if (sgi_tioca_agp_bridges[j]) { - sgi_tioca_agp_bridges[j]->dev = pdev; - sgi_tioca_agp_bridges[j]->dev_private_data = info; - sgi_tioca_agp_bridges[j]->driver = &sgi_tioca_driver; - sgi_tioca_agp_bridges[j]->gart_bus_addr = - info->ca_gfxap_base; - sgi_tioca_agp_bridges[j]->mode = (0x7D << 24) | /* 126 requests */ - (0x1 << 9) | /* SBA supported */ - (0x1 << 5) | /* 64-bit addresses supported */ - (0x1 << 4) | /* FW supported */ - (0x1 << 3) | /* AGP 3.0 mode */ - 0x2; /* 8x transfer only */ - sgi_tioca_agp_bridges[j]->current_size = - sgi_tioca_agp_bridges[j]->previous_size = - (void *)&sgi_tioca_sizes[0]; - agp_add_bridge(sgi_tioca_agp_bridges[j]); - } - j++; - } - - agp_find_bridge = &sgi_tioca_find_bridge; - return 0; -} - -static void agp_sgi_cleanup(void) -{ - kfree(sgi_tioca_agp_bridges); - sgi_tioca_agp_bridges = NULL; -} - -module_init(agp_sgi_init); -module_exit(agp_sgi_cleanup); - -MODULE_LICENSE("GPL and additional rights"); diff --git a/drivers/char/mbcs.c b/drivers/char/mbcs.c deleted file mode 100644 index 0a31b60bee7b..000000000000 --- a/drivers/char/mbcs.c +++ /dev/null @@ -1,831 +0,0 @@ -/* - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - * - * Copyright (c) 2005 Silicon Graphics, Inc. All rights reserved. - */ - -/* - * MOATB Core Services driver. - */ - -#include <linux/interrupt.h> -#include <linux/module.h> -#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> -#include <linux/fs.h> -#include <linux/delay.h> -#include <linux/device.h> -#include <linux/mm.h> -#include <linux/uio.h> -#include <linux/mutex.h> -#include <linux/slab.h> -#include <linux/pagemap.h> -#include <asm/io.h> -#include <linux/uaccess.h> -#include <asm/pgtable.h> -#include <asm/sn/addrs.h> -#include <asm/sn/intr.h> -#include <asm/sn/tiocx.h> -#include "mbcs.h" - -#define MBCS_DEBUG 0 -#if MBCS_DEBUG -#define DBG(fmt...) printk(KERN_ALERT fmt) -#else -#define DBG(fmt...) -#endif -static DEFINE_MUTEX(mbcs_mutex); -static int mbcs_major; - -static LIST_HEAD(soft_list); - -/* - * file operations - */ -static const struct file_operations mbcs_ops = { - .owner = THIS_MODULE, - .open = mbcs_open, - .llseek = mbcs_sram_llseek, - .read = mbcs_sram_read, - .write = mbcs_sram_write, - .mmap = mbcs_gscr_mmap, -}; - -struct mbcs_callback_arg { - int minor; - struct cx_dev *cx_dev; -}; - -static inline void mbcs_getdma_init(struct getdma *gdma) -{ - memset(gdma, 0, sizeof(struct getdma)); - gdma->DoneIntEnable = 1; -} - -static inline void mbcs_putdma_init(struct putdma *pdma) -{ - memset(pdma, 0, sizeof(struct putdma)); - pdma->DoneIntEnable = 1; -} - -static inline void mbcs_algo_init(struct algoblock *algo_soft) -{ - memset(algo_soft, 0, sizeof(struct algoblock)); -} - -static inline void mbcs_getdma_set(void *mmr, - uint64_t hostAddr, - uint64_t localAddr, - uint64_t localRamSel, - uint64_t numPkts, - uint64_t amoEnable, - uint64_t intrEnable, - uint64_t peerIO, - uint64_t amoHostDest, - uint64_t amoModType, uint64_t intrHostDest, - uint64_t intrVector) -{ - union dma_control rdma_control; - union dma_amo_dest amo_dest; - union intr_dest intr_dest; - union dma_localaddr local_addr; - union dma_hostaddr host_addr; - - rdma_control.dma_control_reg = 0; - amo_dest.dma_amo_dest_reg = 0; - intr_dest.intr_dest_reg = 0; - local_addr.dma_localaddr_reg = 0; - host_addr.dma_hostaddr_reg = 0; - - host_addr.dma_sys_addr = hostAddr; - MBCS_MMR_SET(mmr, MBCS_RD_DMA_SYS_ADDR, host_addr.dma_hostaddr_reg); - - local_addr.dma_ram_addr = localAddr; - local_addr.dma_ram_sel = localRamSel; - MBCS_MMR_SET(mmr, MBCS_RD_DMA_LOC_ADDR, local_addr.dma_localaddr_reg); - - rdma_control.dma_op_length = numPkts; - rdma_control.done_amo_en = amoEnable; - rdma_control.done_int_en = intrEnable; - rdma_control.pio_mem_n = peerIO; - MBCS_MMR_SET(mmr, MBCS_RD_DMA_CTRL, rdma_control.dma_control_reg); - - amo_dest.dma_amo_sys_addr = amoHostDest; - amo_dest.dma_amo_mod_type = amoModType; - MBCS_MMR_SET(mmr, MBCS_RD_DMA_AMO_DEST, amo_dest.dma_amo_dest_reg); - - intr_dest.address = intrHostDest; - intr_dest.int_vector = intrVector; - MBCS_MMR_SET(mmr, MBCS_RD_DMA_INT_DEST, intr_dest.intr_dest_reg); - -} - -static inline void mbcs_putdma_set(void *mmr, - uint64_t hostAddr, - uint64_t localAddr, - uint64_t localRamSel, - uint64_t numPkts, - uint64_t amoEnable, - uint64_t intrEnable, - uint64_t peerIO, - uint64_t amoHostDest, - uint64_t amoModType, - uint64_t intrHostDest, uint64_t intrVector) -{ - union dma_control wdma_control; - union dma_amo_dest amo_dest; - union intr_dest intr_dest; - union dma_localaddr local_addr; - union dma_hostaddr host_addr; - - wdma_control.dma_control_reg = 0; - amo_dest.dma_amo_dest_reg = 0; - intr_dest.intr_dest_reg = 0; - local_addr.dma_localaddr_reg = 0; - host_addr.dma_hostaddr_reg = 0; - - host_addr.dma_sys_addr = hostAddr; - MBCS_MMR_SET(mmr, MBCS_WR_DMA_SYS_ADDR, host_addr.dma_hostaddr_reg); - - local_addr.dma_ram_addr = localAddr; - local_addr.dma_ram_sel = localRamSel; - MBCS_MMR_SET(mmr, MBCS_WR_DMA_LOC_ADDR, local_addr.dma_localaddr_reg); - - wdma_control.dma_op_length = numPkts; - wdma_control.done_amo_en = amoEnable; - wdma_control.done_int_en = intrEnable; - wdma_control.pio_mem_n = peerIO; - MBCS_MMR_SET(mmr, MBCS_WR_DMA_CTRL, wdma_control.dma_control_reg); - - amo_dest.dma_amo_sys_addr = amoHostDest; - amo_dest.dma_amo_mod_type = amoModType; - MBCS_MMR_SET(mmr, MBCS_WR_DMA_AMO_DEST, amo_dest.dma_amo_dest_reg); - - intr_dest.address = intrHostDest; - intr_dest.int_vector = intrVector; - MBCS_MMR_SET(mmr, MBCS_WR_DMA_INT_DEST, intr_dest.intr_dest_reg); - -} - -static inline void mbcs_algo_set(void *mmr, - uint64_t amoHostDest, - uint64_t amoModType, - uint64_t intrHostDest, - uint64_t intrVector, uint64_t algoStepCount) -{ - union dma_amo_dest amo_dest; - union intr_dest intr_dest; - union algo_step step; - - step.algo_step_reg = 0; - intr_dest.intr_dest_reg = 0; - amo_dest.dma_amo_dest_reg = 0; - - amo_dest.dma_amo_sys_addr = amoHostDest; - amo_dest.dma_amo_mod_type = amoModType; - MBCS_MMR_SET(mmr, MBCS_ALG_AMO_DEST, amo_dest.dma_amo_dest_reg); - - intr_dest.address = intrHostDest; - intr_dest.int_vector = intrVector; - MBCS_MMR_SET(mmr, MBCS_ALG_INT_DEST, intr_dest.intr_dest_reg); - - step.alg_step_cnt = algoStepCount; - MBCS_MMR_SET(mmr, MBCS_ALG_STEP, step.algo_step_reg); -} - -static inline int mbcs_getdma_start(struct mbcs_soft *soft) -{ - void *mmr_base; - struct getdma *gdma; - uint64_t numPkts; - union cm_control cm_control; - - mmr_base = soft->mmr_base; - gdma = &soft->getdma; - - /* check that host address got setup */ - if (!gdma->hostAddr) - return -1; - - numPkts = - (gdma->bytes + (MBCS_CACHELINE_SIZE - 1)) / MBCS_CACHELINE_SIZE; - - /* program engine */ - mbcs_getdma_set(mmr_base, tiocx_dma_addr(gdma->hostAddr), - gdma->localAddr, - (gdma->localAddr < MB2) ? 0 : - (gdma->localAddr < MB4) ? 1 : - (gdma->localAddr < MB6) ? 2 : 3, - numPkts, - gdma->DoneAmoEnable, - gdma->DoneIntEnable, - gdma->peerIO, - gdma->amoHostDest, - gdma->amoModType, - gdma->intrHostDest, gdma->intrVector); - - /* start engine */ - cm_control.cm_control_reg = MBCS_MMR_GET(mmr_base, MBCS_CM_CONTROL); - cm_control.rd_dma_go = 1; - MBCS_MMR_SET(mmr_base, MBCS_CM_CONTROL, cm_control.cm_control_reg); - - return 0; - -} - -static inline int mbcs_putdma_start(struct mbcs_soft *soft) -{ - void *mmr_base; - struct putdma *pdma; - uint64_t numPkts; - union cm_control cm_control; - - mmr_base = soft->mmr_base; - pdma = &soft->putdma; - - /* check that host address got setup */ - if (!pdma->hostAddr) - return -1; - - numPkts = - (pdma->bytes + (MBCS_CACHELINE_SIZE - 1)) / MBCS_CACHELINE_SIZE; - - /* program engine */ - mbcs_putdma_set(mmr_base, tiocx_dma_addr(pdma->hostAddr), - pdma->localAddr, - (pdma->localAddr < MB2) ? 0 : - (pdma->localAddr < MB4) ? 1 : - (pdma->localAddr < MB6) ? 2 : 3, - numPkts, - pdma->DoneAmoEnable, - pdma->DoneIntEnable, - pdma->peerIO, - pdma->amoHostDest, - pdma->amoModType, - pdma->intrHostDest, pdma->intrVector); - - /* start engine */ - cm_control.cm_control_reg = MBCS_MMR_GET(mmr_base, MBCS_CM_CONTROL); - cm_control.wr_dma_go = 1; - MBCS_MMR_SET(mmr_base, MBCS_CM_CONTROL, cm_control.cm_control_reg); - - return 0; - -} - -static inline int mbcs_algo_start(struct mbcs_soft *soft) -{ - struct algoblock *algo_soft = &soft->algo; - void *mmr_base = soft->mmr_base; - union cm_control cm_control; - - if (mutex_lock_interruptible(&soft->algolock)) - return -ERESTARTSYS; - - atomic_set(&soft->algo_done, 0); - - mbcs_algo_set(mmr_base, - algo_soft->amoHostDest, - algo_soft->amoModType, - algo_soft->intrHostDest, - algo_soft->intrVector, algo_soft->algoStepCount); - - /* start algorithm */ - cm_control.cm_control_reg = MBCS_MMR_GET(mmr_base, MBCS_CM_CONTROL); - cm_control.alg_done_int_en = 1; - cm_control.alg_go = 1; - MBCS_MMR_SET(mmr_base, MBCS_CM_CONTROL, cm_control.cm_control_reg); - - mutex_unlock(&soft->algolock); - - return 0; -} - -static inline ssize_t -do_mbcs_sram_dmawrite(struct mbcs_soft *soft, uint64_t hostAddr, - size_t len, loff_t * off) -{ - int rv = 0; - - if (mutex_lock_interruptible(&soft->dmawritelock)) - return -ERESTARTSYS; - - atomic_set(&soft->dmawrite_done, 0); - - soft->putdma.hostAddr = hostAddr; - soft->putdma.localAddr = *off; - soft->putdma.bytes = len; - - if (mbcs_putdma_start(soft) < 0) { - DBG(KERN_ALERT "do_mbcs_sram_dmawrite: " - "mbcs_putdma_start failed\n"); - rv = -EAGAIN; - goto dmawrite_exit; - } - - if (wait_event_interruptible(soft->dmawrite_queue, - atomic_read(&soft->dmawrite_done))) { - rv = -ERESTARTSYS; - goto dmawrite_exit; - } - - rv = len; - *off += len; - -dmawrite_exit: - mutex_unlock(&soft->dmawritelock); - - return rv; -} - -static inline ssize_t -do_mbcs_sram_dmaread(struct mbcs_soft *soft, uint64_t hostAddr, - size_t len, loff_t * off) -{ - int rv = 0; - - if (mutex_lock_interruptible(&soft->dmareadlock)) - return -ERESTARTSYS; - - atomic_set(&soft->dmawrite_done, 0); - - soft->getdma.hostAddr = hostAddr; - soft->getdma.localAddr = *off; - soft->getdma.bytes = len; - - if (mbcs_getdma_start(soft) < 0) { - DBG(KERN_ALERT "mbcs_strategy: mbcs_getdma_start failed\n"); - rv = -EAGAIN; - goto dmaread_exit; - } - - if (wait_event_interruptible(soft->dmaread_queue, - atomic_read(&soft->dmaread_done))) { - rv = -ERESTARTSYS; - goto dmaread_exit; - } - - rv = len; - *off += len; - -dmaread_exit: - mutex_unlock(&soft->dmareadlock); - - return rv; -} - -static int mbcs_open(struct inode *ip, struct file *fp) -{ - struct mbcs_soft *soft; - int minor; - - mutex_lock(&mbcs_mutex); - minor = iminor(ip); - - /* Nothing protects access to this list... */ - list_for_each_entry(soft, &soft_list, list) { - if (soft->nasid == minor) { - fp->private_data = soft->cxdev; - mutex_unlock(&mbcs_mutex); - return 0; - } - } - - mutex_unlock(&mbcs_mutex); - return -ENODEV; -} - -static ssize_t mbcs_sram_read(struct file * fp, char __user *buf, size_t len, loff_t * off) -{ - struct cx_dev *cx_dev = fp->private_data; - struct mbcs_soft *soft = cx_dev->soft; - uint64_t hostAddr; - int rv = 0; - - hostAddr = __get_dma_pages(GFP_KERNEL, get_order(len)); - if (hostAddr == 0) - return -ENOMEM; - - rv = do_mbcs_sram_dmawrite(soft, hostAddr, len, off); - if (rv < 0) - goto exit; - - if (copy_to_user(buf, (void *)hostAddr, len)) - rv = -EFAULT; - - exit: - free_pages(hostAddr, get_order(len)); - - return rv; -} - -static ssize_t -mbcs_sram_write(struct file * fp, const char __user *buf, size_t len, loff_t * off) -{ - struct cx_dev *cx_dev = fp->private_data; - struct mbcs_soft *soft = cx_dev->soft; - uint64_t hostAddr; - int rv = 0; - - hostAddr = __get_dma_pages(GFP_KERNEL, get_order(len)); - if (hostAddr == 0) - return -ENOMEM; - - if (copy_from_user((void *)hostAddr, buf, len)) { - rv = -EFAULT; - goto exit; - } - - rv = do_mbcs_sram_dmaread(soft, hostAddr, len, off); - - exit: - free_pages(hostAddr, get_order(len)); - - return rv; -} - -static loff_t mbcs_sram_llseek(struct file * filp, loff_t off, int whence) -{ - return generic_file_llseek_size(filp, off, whence, MAX_LFS_FILESIZE, - MBCS_SRAM_SIZE); -} - -static uint64_t mbcs_pioaddr(struct mbcs_soft *soft, uint64_t offset) -{ - uint64_t mmr_base; - - mmr_base = (uint64_t) (soft->mmr_base + offset); - - return mmr_base; -} - -static void mbcs_debug_pioaddr_set(struct mbcs_soft *soft) -{ - soft->debug_addr = mbcs_pioaddr(soft, MBCS_DEBUG_START); -} - -static void mbcs_gscr_pioaddr_set(struct mbcs_soft *soft) -{ - soft->gscr_addr = mbcs_pioaddr(soft, MBCS_GSCR_START); -} - -static int mbcs_gscr_mmap(struct file *fp, struct vm_area_struct *vma) -{ - struct cx_dev *cx_dev = fp->private_data; - struct mbcs_soft *soft = cx_dev->soft; - - if (vma->vm_pgoff != 0) - return -EINVAL; - - vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); - - /* Remap-pfn-range will mark the range VM_IO */ - if (remap_pfn_range(vma, - vma->vm_start, - __pa(soft->gscr_addr) >> PAGE_SHIFT, - PAGE_SIZE, - vma->vm_page_prot)) - return -EAGAIN; - - return 0; -} - -/** - * mbcs_completion_intr_handler - Primary completion handler. - * @irq: irq - * @arg: soft struct for device - * - */ -static irqreturn_t -mbcs_completion_intr_handler(int irq, void *arg) -{ - struct mbcs_soft *soft = (struct mbcs_soft *)arg; - void *mmr_base; - union cm_status cm_status; - union cm_control cm_control; - - mmr_base = soft->mmr_base; - cm_status.cm_status_reg = MBCS_MMR_GET(mmr_base, MBCS_CM_STATUS); - - if (cm_status.rd_dma_done) { - /* stop dma-read engine, clear status */ - cm_control.cm_control_reg = - MBCS_MMR_GET(mmr_base, MBCS_CM_CONTROL); - cm_control.rd_dma_clr = 1; - MBCS_MMR_SET(mmr_base, MBCS_CM_CONTROL, - cm_control.cm_control_reg); - atomic_set(&soft->dmaread_done, 1); - wake_up(&soft->dmaread_queue); - } - if (cm_status.wr_dma_done) { - /* stop dma-write engine, clear status */ - cm_control.cm_control_reg = - MBCS_MMR_GET(mmr_base, MBCS_CM_CONTROL); - cm_control.wr_dma_clr = 1; - MBCS_MMR_SET(mmr_base, MBCS_CM_CONTROL, - cm_control.cm_control_reg); - atomic_set(&soft->dmawrite_done, 1); - wake_up(&soft->dmawrite_queue); - } - if (cm_status.alg_done) { - /* clear status */ - cm_control.cm_control_reg = - MBCS_MMR_GET(mmr_base, MBCS_CM_CONTROL); - cm_control.alg_done_clr = 1; - MBCS_MMR_SET(mmr_base, MBCS_CM_CONTROL, - cm_control.cm_control_reg); - atomic_set(&soft->algo_done, 1); - wake_up(&soft->algo_queue); - } - - return IRQ_HANDLED; -} - -/** - * mbcs_intr_alloc - Allocate interrupts. - * @dev: device pointer - * - */ -static int mbcs_intr_alloc(struct cx_dev *dev) -{ - struct sn_irq_info *sn_irq; - struct mbcs_soft *soft; - struct getdma *getdma; - struct putdma *putdma; - struct algoblock *algo; - - soft = dev->soft; - getdma = &soft->getdma; - putdma = &soft->putdma; - algo = &soft->algo; - - soft->get_sn_irq = NULL; - soft->put_sn_irq = NULL; - soft->algo_sn_irq = NULL; - - sn_irq = tiocx_irq_alloc(dev->cx_id.nasid, TIOCX_CORELET, -1, -1, -1); - if (sn_irq == NULL) - return -EAGAIN; - soft->get_sn_irq = sn_irq; - getdma->intrHostDest = sn_irq->irq_xtalkaddr; - getdma->intrVector = sn_irq->irq_irq; - if (request_irq(sn_irq->irq_irq, - (void *)mbcs_completion_intr_handler, IRQF_SHARED, - "MBCS get intr", (void *)soft)) { - tiocx_irq_free(soft->get_sn_irq); - return -EAGAIN; - } - - sn_irq = tiocx_irq_alloc(dev->cx_id.nasid, TIOCX_CORELET, -1, -1, -1); - if (sn_irq == NULL) { - free_irq(soft->get_sn_irq->irq_irq, soft); - tiocx_irq_free(soft->get_sn_irq); - return -EAGAIN; - } - soft->put_sn_irq = sn_irq; - putdma->intrHostDest = sn_irq->irq_xtalkaddr; - putdma->intrVector = sn_irq->irq_irq; - if (request_irq(sn_irq->irq_irq, - (void *)mbcs_completion_intr_handler, IRQF_SHARED, - "MBCS put intr", (void *)soft)) { - tiocx_irq_free(soft->put_sn_irq); - free_irq(soft->get_sn_irq->irq_irq, soft); - tiocx_irq_free(soft->get_sn_irq); - return -EAGAIN; - } - - sn_irq = tiocx_irq_alloc(dev->cx_id.nasid, TIOCX_CORELET, -1, -1, -1); - if (sn_irq == NULL) { - free_irq(soft->put_sn_irq->irq_irq, soft); - tiocx_irq_free(soft->put_sn_irq); - free_irq(soft->get_sn_irq->irq_irq, soft); - tiocx_irq_free(soft->get_sn_irq); - return -EAGAIN; - } - soft->algo_sn_irq = sn_irq; - algo->intrHostDest = sn_irq->irq_xtalkaddr; - algo->intrVector = sn_irq->irq_irq; - if (request_irq(sn_irq->irq_irq, - (void *)mbcs_completion_intr_handler, IRQF_SHARED, - "MBCS algo intr", (void *)soft)) { - tiocx_irq_free(soft->algo_sn_irq); - free_irq(soft->put_sn_irq->irq_irq, soft); - tiocx_irq_free(soft->put_sn_irq); - free_irq(soft->get_sn_irq->irq_irq, soft); - tiocx_irq_free(soft->get_sn_irq); - return -EAGAIN; - } - - return 0; -} - -/** - * mbcs_intr_dealloc - Remove interrupts. - * @dev: device pointer - * - */ -static void mbcs_intr_dealloc(struct cx_dev *dev) -{ - struct mbcs_soft *soft; - - soft = dev->soft; - - free_irq(soft->get_sn_irq->irq_irq, soft); - tiocx_irq_free(soft->get_sn_irq); - free_irq(soft->put_sn_irq->irq_irq, soft); - tiocx_irq_free(soft->put_sn_irq); - free_irq(soft->algo_sn_irq->irq_irq, soft); - tiocx_irq_free(soft->algo_sn_irq); -} - -static inline int mbcs_hw_init(struct mbcs_soft *soft) -{ - void *mmr_base = soft->mmr_base; - union cm_control cm_control; - union cm_req_timeout cm_req_timeout; - uint64_t err_stat; - - cm_req_timeout.cm_req_timeout_reg = - MBCS_MMR_GET(mmr_base, MBCS_CM_REQ_TOUT); - - cm_req_timeout.time_out = MBCS_CM_CONTROL_REQ_TOUT_MASK; - MBCS_MMR_SET(mmr_base, MBCS_CM_REQ_TOUT, - cm_req_timeout.cm_req_timeout_reg); - - mbcs_gscr_pioaddr_set(soft); - mbcs_debug_pioaddr_set(soft); - - /* clear errors */ - err_stat = MBCS_MMR_GET(mmr_base, MBCS_CM_ERR_STAT); - MBCS_MMR_SET(mmr_base, MBCS_CM_CLR_ERR_STAT, err_stat); - MBCS_MMR_ZERO(mmr_base, MBCS_CM_ERROR_DETAIL1); - - /* enable interrupts */ - /* turn off 2^23 (INT_EN_PIO_REQ_ADDR_INV) */ - MBCS_MMR_SET(mmr_base, MBCS_CM_ERR_INT_EN, 0x3ffffff7e00ffUL); - - /* arm status regs and clear engines */ - cm_control.cm_control_reg = MBCS_MMR_GET(mmr_base, MBCS_CM_CONTROL); - cm_control.rearm_stat_regs = 1; - cm_control.alg_clr = 1; - cm_control.wr_dma_clr = 1; - cm_control.rd_dma_clr = 1; - - MBCS_MMR_SET(mmr_base, MBCS_CM_CONTROL, cm_control.cm_control_reg); - - return 0; -} - -static ssize_t show_algo(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct cx_dev *cx_dev = to_cx_dev(dev); - struct mbcs_soft *soft = cx_dev->soft; - uint64_t debug0; - - /* - * By convention, the first debug register contains the - * algorithm number and revision. - */ - debug0 = *(uint64_t *) soft->debug_addr; - - 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) -{ - int n; - struct cx_dev *cx_dev = to_cx_dev(dev); - struct mbcs_soft *soft = cx_dev->soft; - - if (count <= 0) - return 0; - - n = simple_strtoul(buf, NULL, 0); - - if (n == 1) { - mbcs_algo_start(soft); - if (wait_event_interruptible(soft->algo_queue, - atomic_read(&soft->algo_done))) - return -ERESTARTSYS; - } - - return count; -} - -DEVICE_ATTR(algo, 0644, show_algo, store_algo); - -/** - * mbcs_probe - Initialize for device - * @dev: device pointer - * @device_id: id table pointer - * - */ -static int mbcs_probe(struct cx_dev *dev, const struct cx_device_id *id) -{ - struct mbcs_soft *soft; - - dev->soft = NULL; - - soft = kzalloc(sizeof(struct mbcs_soft), GFP_KERNEL); - if (soft == NULL) - return -ENOMEM; - - soft->nasid = dev->cx_id.nasid; - list_add(&soft->list, &soft_list); - soft->mmr_base = (void *)tiocx_swin_base(dev->cx_id.nasid); - dev->soft = soft; - soft->cxdev = dev; - - init_waitqueue_head(&soft->dmawrite_queue); - init_waitqueue_head(&soft->dmaread_queue); - init_waitqueue_head(&soft->algo_queue); - - mutex_init(&soft->dmawritelock); - mutex_init(&soft->dmareadlock); - mutex_init(&soft->algolock); - - mbcs_getdma_init(&soft->getdma); - mbcs_putdma_init(&soft->putdma); - mbcs_algo_init(&soft->algo); - - mbcs_hw_init(soft); - - /* Allocate interrupts */ - mbcs_intr_alloc(dev); - - device_create_file(&dev->dev, &dev_attr_algo); - - return 0; -} - -static int mbcs_remove(struct cx_dev *dev) -{ - if (dev->soft) { - mbcs_intr_dealloc(dev); - kfree(dev->soft); - } - - device_remove_file(&dev->dev, &dev_attr_algo); - - return 0; -} - -static const struct cx_device_id mbcs_id_table[] = { - { - .part_num = MBCS_PART_NUM, - .mfg_num = MBCS_MFG_NUM, - }, - { - .part_num = MBCS_PART_NUM_ALG0, - .mfg_num = MBCS_MFG_NUM, - }, - {0, 0} -}; - -MODULE_DEVICE_TABLE(cx, mbcs_id_table); - -static struct cx_drv mbcs_driver = { - .name = DEVICE_NAME, - .id_table = mbcs_id_table, - .probe = mbcs_probe, - .remove = mbcs_remove, -}; - -static void __exit mbcs_exit(void) -{ - unregister_chrdev(mbcs_major, DEVICE_NAME); - cx_driver_unregister(&mbcs_driver); -} - -static int __init mbcs_init(void) -{ - int rv; - - if (!ia64_platform_is("sn2")) - return -ENODEV; - - // Put driver into chrdevs[]. Get major number. - rv = register_chrdev(mbcs_major, DEVICE_NAME, &mbcs_ops); - if (rv < 0) { - DBG(KERN_ALERT "mbcs_init: can't get major number. %d\n", rv); - return rv; - } - mbcs_major = rv; - - return cx_driver_register(&mbcs_driver); -} - -module_init(mbcs_init); -module_exit(mbcs_exit); - -MODULE_AUTHOR("Bruce Losure <blosure@sgi.com>"); -MODULE_DESCRIPTION("Driver for MOATB Core Services"); -MODULE_LICENSE("GPL"); diff --git a/drivers/char/mbcs.h b/drivers/char/mbcs.h deleted file mode 100644 index 1a36884c48b5..000000000000 --- a/drivers/char/mbcs.h +++ /dev/null @@ -1,553 +0,0 @@ -/* - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - * - * Copyright (c) 2005 Silicon Graphics, Inc. All rights reserved. - */ - -#ifndef __MBCS_H__ -#define __MBCS_H__ - -/* - * General macros - */ -#define MB (1024*1024) -#define MB2 (2*MB) -#define MB4 (4*MB) -#define MB6 (6*MB) - -/* - * Offsets and masks - */ -#define MBCS_CM_ID 0x0000 /* Identification */ -#define MBCS_CM_STATUS 0x0008 /* Status */ -#define MBCS_CM_ERROR_DETAIL1 0x0010 /* Error Detail1 */ -#define MBCS_CM_ERROR_DETAIL2 0x0018 /* Error Detail2 */ -#define MBCS_CM_CONTROL 0x0020 /* Control */ -#define MBCS_CM_REQ_TOUT 0x0028 /* Request Time-out */ -#define MBCS_CM_ERR_INT_DEST 0x0038 /* Error Interrupt Destination */ -#define MBCS_CM_TARG_FL 0x0050 /* Target Flush */ -#define MBCS_CM_ERR_STAT 0x0060 /* Error Status */ -#define MBCS_CM_CLR_ERR_STAT 0x0068 /* Clear Error Status */ -#define MBCS_CM_ERR_INT_EN 0x0070 /* Error Interrupt Enable */ -#define MBCS_RD_DMA_SYS_ADDR 0x0100 /* Read DMA System Address */ -#define MBCS_RD_DMA_LOC_ADDR 0x0108 /* Read DMA Local Address */ -#define MBCS_RD_DMA_CTRL 0x0110 /* Read DMA Control */ -#define MBCS_RD_DMA_AMO_DEST 0x0118 /* Read DMA AMO Destination */ -#define MBCS_RD_DMA_INT_DEST 0x0120 /* Read DMA Interrupt Destination */ -#define MBCS_RD_DMA_AUX_STAT 0x0130 /* Read DMA Auxiliary Status */ -#define MBCS_WR_DMA_SYS_ADDR 0x0200 /* Write DMA System Address */ -#define MBCS_WR_DMA_LOC_ADDR 0x0208 /* Write DMA Local Address */ -#define MBCS_WR_DMA_CTRL 0x0210 /* Write DMA Control */ -#define MBCS_WR_DMA_AMO_DEST 0x0218 /* Write DMA AMO Destination */ -#define MBCS_WR_DMA_INT_DEST 0x0220 /* Write DMA Interrupt Destination */ -#define MBCS_WR_DMA_AUX_STAT 0x0230 /* Write DMA Auxiliary Status */ -#define MBCS_ALG_AMO_DEST 0x0300 /* Algorithm AMO Destination */ -#define MBCS_ALG_INT_DEST 0x0308 /* Algorithm Interrupt Destination */ -#define MBCS_ALG_OFFSETS 0x0310 -#define MBCS_ALG_STEP 0x0318 /* Algorithm Step */ - -#define MBCS_GSCR_START 0x0000000 -#define MBCS_DEBUG_START 0x0100000 -#define MBCS_RAM0_START 0x0200000 -#define MBCS_RAM1_START 0x0400000 -#define MBCS_RAM2_START 0x0600000 - -#define MBCS_CM_CONTROL_REQ_TOUT_MASK 0x0000000000ffffffUL -//#define PIO_BASE_ADDR_BASE_OFFSET_MASK 0x00fffffffff00000UL - -#define MBCS_SRAM_SIZE (1024*1024) -#define MBCS_CACHELINE_SIZE 128 - -/* - * MMR get's and put's - */ -#define MBCS_MMR_ADDR(mmr_base, offset)((uint64_t *)(mmr_base + offset)) -#define MBCS_MMR_SET(mmr_base, offset, value) { \ - uint64_t *mbcs_mmr_set_u64p, readback; \ - mbcs_mmr_set_u64p = (uint64_t *)(mmr_base + offset); \ - *mbcs_mmr_set_u64p = value; \ - readback = *mbcs_mmr_set_u64p; \ -} -#define MBCS_MMR_GET(mmr_base, offset) *(uint64_t *)(mmr_base + offset) -#define MBCS_MMR_ZERO(mmr_base, offset) MBCS_MMR_SET(mmr_base, offset, 0) - -/* - * MBCS mmr structures - */ -union cm_id { - uint64_t cm_id_reg; - struct { - uint64_t always_one:1, // 0 - mfg_id:11, // 11:1 - part_num:16, // 27:12 - bitstream_rev:8, // 35:28 - :28; // 63:36 - }; -}; - -union cm_status { - uint64_t cm_status_reg; - struct { - uint64_t pending_reads:8, // 7:0 - pending_writes:8, // 15:8 - ice_rsp_credits:8, // 23:16 - ice_req_credits:8, // 31:24 - cm_req_credits:8, // 39:32 - :1, // 40 - rd_dma_in_progress:1, // 41 - rd_dma_done:1, // 42 - :1, // 43 - wr_dma_in_progress:1, // 44 - wr_dma_done:1, // 45 - alg_waiting:1, // 46 - alg_pipe_running:1, // 47 - alg_done:1, // 48 - :3, // 51:49 - pending_int_reqs:8, // 59:52 - :3, // 62:60 - alg_half_speed_sel:1; // 63 - }; -}; - -union cm_error_detail1 { - uint64_t cm_error_detail1_reg; - struct { - uint64_t packet_type:4, // 3:0 - source_id:2, // 5:4 - data_size:2, // 7:6 - tnum:8, // 15:8 - byte_enable:8, // 23:16 - gfx_cred:8, // 31:24 - read_type:2, // 33:32 - pio_or_memory:1, // 34 - head_cw_error:1, // 35 - :12, // 47:36 - head_error_bit:1, // 48 - data_error_bit:1, // 49 - :13, // 62:50 - valid:1; // 63 - }; -}; - -union cm_error_detail2 { - uint64_t cm_error_detail2_reg; - struct { - uint64_t address:56, // 55:0 - :8; // 63:56 - }; -}; - -union cm_control { - uint64_t cm_control_reg; - struct { - uint64_t cm_id:2, // 1:0 - :2, // 3:2 - max_trans:5, // 8:4 - :3, // 11:9 - address_mode:1, // 12 - :7, // 19:13 - credit_limit:8, // 27:20 - :5, // 32:28 - rearm_stat_regs:1, // 33 - prescalar_byp:1, // 34 - force_gap_war:1, // 35 - rd_dma_go:1, // 36 - wr_dma_go:1, // 37 - alg_go:1, // 38 - rd_dma_clr:1, // 39 - wr_dma_clr:1, // 40 - alg_clr:1, // 41 - :2, // 43:42 - alg_wait_step:1, // 44 - alg_done_amo_en:1, // 45 - alg_done_int_en:1, // 46 - :1, // 47 - alg_sram0_locked:1, // 48 - alg_sram1_locked:1, // 49 - alg_sram2_locked:1, // 50 - alg_done_clr:1, // 51 - :12; // 63:52 - }; -}; - -union cm_req_timeout { - uint64_t cm_req_timeout_reg; - struct { - uint64_t time_out:24, // 23:0 - :40; // 63:24 - }; -}; - -union intr_dest { - uint64_t intr_dest_reg; - struct { - uint64_t address:56, // 55:0 - int_vector:8; // 63:56 - }; -}; - -union cm_error_status { - uint64_t cm_error_status_reg; - struct { - uint64_t ecc_sbe:1, // 0 - ecc_mbe:1, // 1 - unsupported_req:1, // 2 - unexpected_rsp:1, // 3 - bad_length:1, // 4 - bad_datavalid:1, // 5 - buffer_overflow:1, // 6 - request_timeout:1, // 7 - :8, // 15:8 - head_inv_data_size:1, // 16 - rsp_pactype_inv:1, // 17 - head_sb_err:1, // 18 - missing_head:1, // 19 - head_inv_rd_type:1, // 20 - head_cmd_err_bit:1, // 21 - req_addr_align_inv:1, // 22 - pio_req_addr_inv:1, // 23 - req_range_dsize_inv:1, // 24 - early_term:1, // 25 - early_tail:1, // 26 - missing_tail:1, // 27 - data_flit_sb_err:1, // 28 - cm2hcm_req_cred_of:1, // 29 - cm2hcm_rsp_cred_of:1, // 30 - rx_bad_didn:1, // 31 - rd_dma_err_rsp:1, // 32 - rd_dma_tnum_tout:1, // 33 - rd_dma_multi_tnum_tou:1, // 34 - wr_dma_err_rsp:1, // 35 - wr_dma_tnum_tout:1, // 36 - wr_dma_multi_tnum_tou:1, // 37 - alg_data_overflow:1, // 38 - alg_data_underflow:1, // 39 - ram0_access_conflict:1, // 40 - ram1_access_conflict:1, // 41 - ram2_access_conflict:1, // 42 - ram0_perr:1, // 43 - ram1_perr:1, // 44 - ram2_perr:1, // 45 - int_gen_rsp_err:1, // 46 - int_gen_tnum_tout:1, // 47 - rd_dma_prog_err:1, // 48 - wr_dma_prog_err:1, // 49 - :14; // 63:50 - }; -}; - -union cm_clr_error_status { - uint64_t cm_clr_error_status_reg; - struct { - uint64_t clr_ecc_sbe:1, // 0 - clr_ecc_mbe:1, // 1 - clr_unsupported_req:1, // 2 - clr_unexpected_rsp:1, // 3 - clr_bad_length:1, // 4 - clr_bad_datavalid:1, // 5 - clr_buffer_overflow:1, // 6 - clr_request_timeout:1, // 7 - :8, // 15:8 - clr_head_inv_data_siz:1, // 16 - clr_rsp_pactype_inv:1, // 17 - clr_head_sb_err:1, // 18 - clr_missing_head:1, // 19 - clr_head_inv_rd_type:1, // 20 - clr_head_cmd_err_bit:1, // 21 - clr_req_addr_align_in:1, // 22 - clr_pio_req_addr_inv:1, // 23 - clr_req_range_dsize_i:1, // 24 - clr_early_term:1, // 25 - clr_early_tail:1, // 26 - clr_missing_tail:1, // 27 - clr_data_flit_sb_err:1, // 28 - clr_cm2hcm_req_cred_o:1, // 29 - clr_cm2hcm_rsp_cred_o:1, // 30 - clr_rx_bad_didn:1, // 31 - clr_rd_dma_err_rsp:1, // 32 - clr_rd_dma_tnum_tout:1, // 33 - clr_rd_dma_multi_tnum:1, // 34 - clr_wr_dma_err_rsp:1, // 35 - clr_wr_dma_tnum_tout:1, // 36 - clr_wr_dma_multi_tnum:1, // 37 - clr_alg_data_overflow:1, // 38 - clr_alg_data_underflo:1, // 39 - clr_ram0_access_confl:1, // 40 - clr_ram1_access_confl:1, // 41 - clr_ram2_access_confl:1, // 42 - clr_ram0_perr:1, // 43 - clr_ram1_perr:1, // 44 - clr_ram2_perr:1, // 45 - clr_int_gen_rsp_err:1, // 46 - clr_int_gen_tnum_tout:1, // 47 - clr_rd_dma_prog_err:1, // 48 - clr_wr_dma_prog_err:1, // 49 - :14; // 63:50 - }; -}; - -union cm_error_intr_enable { - uint64_t cm_error_intr_enable_reg; - struct { - uint64_t int_en_ecc_sbe:1, // 0 - int_en_ecc_mbe:1, // 1 - int_en_unsupported_re:1, // 2 - int_en_unexpected_rsp:1, // 3 - int_en_bad_length:1, // 4 - int_en_bad_datavalid:1, // 5 - int_en_buffer_overflo:1, // 6 - int_en_request_timeou:1, // 7 - :8, // 15:8 - int_en_head_inv_data_:1, // 16 - int_en_rsp_pactype_in:1, // 17 - int_en_head_sb_err:1, // 18 - int_en_missing_head:1, // 19 - int_en_head_inv_rd_ty:1, // 20 - int_en_head_cmd_err_b:1, // 21 - int_en_req_addr_align:1, // 22 - int_en_pio_req_addr_i:1, // 23 - int_en_req_range_dsiz:1, // 24 - int_en_early_term:1, // 25 - int_en_early_tail:1, // 26 - int_en_missing_tail:1, // 27 - int_en_data_flit_sb_e:1, // 28 - int_en_cm2hcm_req_cre:1, // 29 - int_en_cm2hcm_rsp_cre:1, // 30 - int_en_rx_bad_didn:1, // 31 - int_en_rd_dma_err_rsp:1, // 32 - int_en_rd_dma_tnum_to:1, // 33 - int_en_rd_dma_multi_t:1, // 34 - int_en_wr_dma_err_rsp:1, // 35 - int_en_wr_dma_tnum_to:1, // 36 - int_en_wr_dma_multi_t:1, // 37 - int_en_alg_data_overf:1, // 38 - int_en_alg_data_under:1, // 39 - int_en_ram0_access_co:1, // 40 - int_en_ram1_access_co:1, // 41 - int_en_ram2_access_co:1, // 42 - int_en_ram0_perr:1, // 43 - int_en_ram1_perr:1, // 44 - int_en_ram2_perr:1, // 45 - int_en_int_gen_rsp_er:1, // 46 - int_en_int_gen_tnum_t:1, // 47 - int_en_rd_dma_prog_er:1, // 48 - int_en_wr_dma_prog_er:1, // 49 - :14; // 63:50 - }; -}; - -struct cm_mmr { - union cm_id id; - union cm_status status; - union cm_error_detail1 err_detail1; - union cm_error_detail2 err_detail2; - union cm_control control; - union cm_req_timeout req_timeout; - uint64_t reserved1[1]; - union intr_dest int_dest; - uint64_t reserved2[2]; - uint64_t targ_flush; - uint64_t reserved3[1]; - union cm_error_status err_status; - union cm_clr_error_status clr_err_status; - union cm_error_intr_enable int_enable; -}; - -union dma_hostaddr { - uint64_t dma_hostaddr_reg; - struct { - uint64_t dma_sys_addr:56, // 55:0 - :8; // 63:56 - }; -}; - -union dma_localaddr { - uint64_t dma_localaddr_reg; - struct { - uint64_t dma_ram_addr:21, // 20:0 - dma_ram_sel:2, // 22:21 - :41; // 63:23 - }; -}; - -union dma_control { - uint64_t dma_control_reg; - struct { - uint64_t dma_op_length:16, // 15:0 - :18, // 33:16 - done_amo_en:1, // 34 - done_int_en:1, // 35 - :1, // 36 - pio_mem_n:1, // 37 - :26; // 63:38 - }; -}; - -union dma_amo_dest { - uint64_t dma_amo_dest_reg; - struct { - uint64_t dma_amo_sys_addr:56, // 55:0 - dma_amo_mod_type:3, // 58:56 - :5; // 63:59 - }; -}; - -union rdma_aux_status { - uint64_t rdma_aux_status_reg; - struct { - uint64_t op_num_pacs_left:17, // 16:0 - :5, // 21:17 - lrsp_buff_empty:1, // 22 - :17, // 39:23 - pending_reqs_left:6, // 45:40 - :18; // 63:46 - }; -}; - -struct rdma_mmr { - union dma_hostaddr host_addr; - union dma_localaddr local_addr; - union dma_control control; - union dma_amo_dest amo_dest; - union intr_dest intr_dest; - union rdma_aux_status aux_status; -}; - -union wdma_aux_status { - uint64_t wdma_aux_status_reg; - struct { - uint64_t op_num_pacs_left:17, // 16:0 - :4, // 20:17 - lreq_buff_empty:1, // 21 - :18, // 39:22 - pending_reqs_left:6, // 45:40 - :18; // 63:46 - }; -}; - -struct wdma_mmr { - union dma_hostaddr host_addr; - union dma_localaddr local_addr; - union dma_control control; - union dma_amo_dest amo_dest; - union intr_dest intr_dest; - union wdma_aux_status aux_status; -}; - -union algo_step { - uint64_t algo_step_reg; - struct { - uint64_t alg_step_cnt:16, // 15:0 - :48; // 63:16 - }; -}; - -struct algo_mmr { - union dma_amo_dest amo_dest; - union intr_dest intr_dest; - union { - uint64_t algo_offset_reg; - struct { - uint64_t sram0_offset:7, // 6:0 - reserved0:1, // 7 - sram1_offset:7, // 14:8 - reserved1:1, // 15 - sram2_offset:7, // 22:16 - reserved2:14; // 63:23 - }; - } sram_offset; - union algo_step step; -}; - -struct mbcs_mmr { - struct cm_mmr cm; - uint64_t reserved1[17]; - struct rdma_mmr rdDma; - uint64_t reserved2[25]; - struct wdma_mmr wrDma; - uint64_t reserved3[25]; - struct algo_mmr algo; - uint64_t reserved4[156]; -}; - -/* - * defines - */ -#define DEVICE_NAME "mbcs" -#define MBCS_PART_NUM 0xfff0 -#define MBCS_PART_NUM_ALG0 0xf001 -#define MBCS_MFG_NUM 0x1 - -struct algoblock { - uint64_t amoHostDest; - uint64_t amoModType; - uint64_t intrHostDest; - uint64_t intrVector; - uint64_t algoStepCount; -}; - -struct getdma { - uint64_t hostAddr; - uint64_t localAddr; - uint64_t bytes; - uint64_t DoneAmoEnable; - uint64_t DoneIntEnable; - uint64_t peerIO; - uint64_t amoHostDest; - uint64_t amoModType; - uint64_t intrHostDest; - uint64_t intrVector; -}; - -struct putdma { - uint64_t hostAddr; - uint64_t localAddr; - uint64_t bytes; - uint64_t DoneAmoEnable; - uint64_t DoneIntEnable; - uint64_t peerIO; - uint64_t amoHostDest; - uint64_t amoModType; - uint64_t intrHostDest; - uint64_t intrVector; -}; - -struct mbcs_soft { - struct list_head list; - struct cx_dev *cxdev; - int major; - int nasid; - void *mmr_base; - wait_queue_head_t dmawrite_queue; - wait_queue_head_t dmaread_queue; - wait_queue_head_t algo_queue; - struct sn_irq_info *get_sn_irq; - struct sn_irq_info *put_sn_irq; - struct sn_irq_info *algo_sn_irq; - struct getdma getdma; - struct putdma putdma; - struct algoblock algo; - uint64_t gscr_addr; // pio addr - uint64_t ram0_addr; // pio addr - uint64_t ram1_addr; // pio addr - uint64_t ram2_addr; // pio addr - uint64_t debug_addr; // pio addr - atomic_t dmawrite_done; - atomic_t dmaread_done; - atomic_t algo_done; - struct mutex dmawritelock; - struct mutex dmareadlock; - struct mutex algolock; -}; - -static int mbcs_open(struct inode *ip, struct file *fp); -static ssize_t mbcs_sram_read(struct file *fp, char __user *buf, size_t len, - loff_t * off); -static ssize_t mbcs_sram_write(struct file *fp, const char __user *buf, size_t len, - loff_t * off); -static loff_t mbcs_sram_llseek(struct file *filp, loff_t off, int whence); -static int mbcs_gscr_mmap(struct file *fp, struct vm_area_struct *vma); - -#endif // __MBCS_H__ diff --git a/drivers/char/mspec.c b/drivers/char/mspec.c index e75c9df7c2d8..a9d9f074fbd6 100644 --- a/drivers/char/mspec.c +++ b/drivers/char/mspec.c @@ -9,11 +9,8 @@ * * This driver exports the SN special memory (mspec) facility to user * processes. - * There are three types of memory made available thru this driver: - * fetchops, uncached and cached. - * - * Fetchops are atomic memory operations that are implemented in the - * memory controller on SGI SN hardware. + * There are two types of memory made available thru this driver: + * uncached and cached. * * Uncached are used for memory write combining feature of the ia64 * cpu. @@ -46,16 +43,8 @@ #include <linux/atomic.h> #include <asm/tlbflush.h> #include <asm/uncached.h> -#include <asm/sn/addrs.h> -#include <asm/sn/arch.h> -#include <asm/sn/mspec.h> -#include <asm/sn/sn_cpuid.h> -#include <asm/sn/io.h> -#include <asm/sn/bte.h> -#include <asm/sn/shubio.h> -#define FETCHOP_ID "SGI Fetchop," #define CACHED_ID "Cached," #define UNCACHED_ID "Uncached" #define REVISION "4.0" @@ -65,17 +54,10 @@ * Page types allocated by the device. */ enum mspec_page_type { - MSPEC_FETCHOP = 1, - MSPEC_CACHED, + MSPEC_CACHED = 2, MSPEC_UNCACHED }; -#ifdef CONFIG_SGI_SN -static int is_sn2; -#else -#define is_sn2 0 -#endif - /* * One of these structures is allocated when an mspec region is mmaped. The * structure is pointed to by the vma->vm_private_data field in the vma struct. @@ -96,39 +78,6 @@ struct vma_data { unsigned long maddr[0]; /* Array of MSPEC addresses. */ }; -/* used on shub2 to clear FOP cache in the HUB */ -static unsigned long scratch_page[MAX_NUMNODES]; -#define SH2_AMO_CACHE_ENTRIES 4 - -static inline int -mspec_zero_block(unsigned long addr, int len) -{ - int status; - - if (is_sn2) { - if (is_shub2()) { - int nid; - void *p; - int i; - - nid = nasid_to_cnodeid(get_node_number(__pa(addr))); - p = (void *)TO_AMO(scratch_page[nid]); - - for (i=0; i < SH2_AMO_CACHE_ENTRIES; i++) { - FETCHOP_LOAD_OP(p, FETCHOP_LOAD); - p += FETCHOP_VAR_SIZE; - } - } - - status = bte_copy(0, addr & ~__IA64_UNCACHED_OFFSET, len, - BTE_WACQUIRE | BTE_ZERO_FILL, NULL); - } else { - memset((char *) addr, 0, len); - status = 0; - } - return status; -} - /* * mspec_open * @@ -173,11 +122,8 @@ mspec_close(struct vm_area_struct *vma) */ my_page = vdata->maddr[index]; vdata->maddr[index] = 0; - if (!mspec_zero_block(my_page, PAGE_SIZE)) - uncached_free_page(my_page, 1); - else - printk(KERN_WARNING "mspec_close(): " - "failed to zero page %ld\n", my_page); + memset((char *)my_page, 0, PAGE_SIZE); + uncached_free_page(my_page, 1); } kvfree(vdata); @@ -213,11 +159,7 @@ mspec_fault(struct vm_fault *vmf) spin_unlock(&vdata->lock); } - if (vdata->type == MSPEC_FETCHOP) - paddr = TO_AMO(maddr); - else - paddr = maddr & ~__IA64_UNCACHED_OFFSET; - + paddr = maddr & ~__IA64_UNCACHED_OFFSET; pfn = paddr >> PAGE_SHIFT; return vmf_insert_pfn(vmf->vma, vmf->address, pfn); @@ -269,7 +211,7 @@ mspec_mmap(struct file *file, struct vm_area_struct *vma, vma->vm_private_data = vdata; vma->vm_flags |= VM_IO | VM_PFNMAP | VM_DONTEXPAND | VM_DONTDUMP; - if (vdata->type == MSPEC_FETCHOP || vdata->type == MSPEC_UNCACHED) + if (vdata->type == MSPEC_UNCACHED) vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); vma->vm_ops = &mspec_vm_ops; @@ -277,12 +219,6 @@ mspec_mmap(struct file *file, struct vm_area_struct *vma, } static int -fetchop_mmap(struct file *file, struct vm_area_struct *vma) -{ - return mspec_mmap(file, vma, MSPEC_FETCHOP); -} - -static int cached_mmap(struct file *file, struct vm_area_struct *vma) { return mspec_mmap(file, vma, MSPEC_CACHED); @@ -294,18 +230,6 @@ uncached_mmap(struct file *file, struct vm_area_struct *vma) return mspec_mmap(file, vma, MSPEC_UNCACHED); } -static const struct file_operations fetchop_fops = { - .owner = THIS_MODULE, - .mmap = fetchop_mmap, - .llseek = noop_llseek, -}; - -static struct miscdevice fetchop_miscdev = { - .minor = MISC_DYNAMIC_MINOR, - .name = "sgi_fetchop", - .fops = &fetchop_fops -}; - static const struct file_operations cached_fops = { .owner = THIS_MODULE, .mmap = cached_mmap, @@ -339,89 +263,32 @@ static int __init mspec_init(void) { int ret; - int nid; - - /* - * The fetchop device only works on SN2 hardware, uncached and cached - * memory drivers should both be valid on all ia64 hardware - */ -#ifdef CONFIG_SGI_SN - if (ia64_platform_is("sn2")) { - is_sn2 = 1; - if (is_shub2()) { - ret = -ENOMEM; - for_each_node_state(nid, N_ONLINE) { - int actual_nid; - int nasid; - unsigned long phys; - - scratch_page[nid] = uncached_alloc_page(nid, 1); - if (scratch_page[nid] == 0) - goto free_scratch_pages; - phys = __pa(scratch_page[nid]); - nasid = get_node_number(phys); - actual_nid = nasid_to_cnodeid(nasid); - if (actual_nid != nid) - goto free_scratch_pages; - } - } - ret = misc_register(&fetchop_miscdev); - if (ret) { - printk(KERN_ERR - "%s: failed to register device %i\n", - FETCHOP_ID, ret); - goto free_scratch_pages; - } - } -#endif ret = misc_register(&cached_miscdev); if (ret) { printk(KERN_ERR "%s: failed to register device %i\n", CACHED_ID, ret); - if (is_sn2) - misc_deregister(&fetchop_miscdev); - goto free_scratch_pages; + return ret; } ret = misc_register(&uncached_miscdev); if (ret) { printk(KERN_ERR "%s: failed to register device %i\n", UNCACHED_ID, ret); misc_deregister(&cached_miscdev); - if (is_sn2) - misc_deregister(&fetchop_miscdev); - goto free_scratch_pages; + return ret; } - printk(KERN_INFO "%s %s initialized devices: %s %s %s\n", - MSPEC_BASENAME, REVISION, is_sn2 ? FETCHOP_ID : "", - CACHED_ID, UNCACHED_ID); + printk(KERN_INFO "%s %s initialized devices: %s %s\n", + MSPEC_BASENAME, REVISION, CACHED_ID, UNCACHED_ID); return 0; - - free_scratch_pages: - for_each_node(nid) { - if (scratch_page[nid] != 0) - uncached_free_page(scratch_page[nid], 1); - } - return ret; } static void __exit mspec_exit(void) { - int nid; - misc_deregister(&uncached_miscdev); misc_deregister(&cached_miscdev); - if (is_sn2) { - misc_deregister(&fetchop_miscdev); - - for_each_node(nid) { - if (scratch_page[nid] != 0) - uncached_free_page(scratch_page[nid], 1); - } - } } module_init(mspec_init); diff --git a/drivers/char/snsc.c b/drivers/char/snsc.c deleted file mode 100644 index 5918ea7499bb..000000000000 --- a/drivers/char/snsc.c +++ /dev/null @@ -1,469 +0,0 @@ -/* - * SN Platform system controller communication support - * - * 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) 2004, 2006 Silicon Graphics, Inc. All rights reserved. - */ - -/* - * System controller communication driver - * - * This driver allows a user process to communicate with the system - * controller (a.k.a. "IRouter") network in an SGI SN system. - */ - -#include <linux/interrupt.h> -#include <linux/sched/signal.h> -#include <linux/device.h> -#include <linux/poll.h> -#include <linux/init.h> -#include <linux/slab.h> -#include <linux/mutex.h> -#include <asm/sn/io.h> -#include <asm/sn/sn_sal.h> -#include <asm/sn/module.h> -#include <asm/sn/geo.h> -#include <asm/sn/nodepda.h> -#include "snsc.h" - -#define SYSCTL_BASENAME "snsc" - -#define SCDRV_BUFSZ 2048 -#define SCDRV_TIMEOUT 1000 - -static DEFINE_MUTEX(scdrv_mutex); -static irqreturn_t -scdrv_interrupt(int irq, void *subch_data) -{ - struct subch_data_s *sd = subch_data; - unsigned long flags; - int status; - - spin_lock_irqsave(&sd->sd_rlock, flags); - spin_lock(&sd->sd_wlock); - status = ia64_sn_irtr_intr(sd->sd_nasid, sd->sd_subch); - - if (status > 0) { - if (status & SAL_IROUTER_INTR_RECV) { - wake_up(&sd->sd_rq); - } - if (status & SAL_IROUTER_INTR_XMIT) { - ia64_sn_irtr_intr_disable - (sd->sd_nasid, sd->sd_subch, - SAL_IROUTER_INTR_XMIT); - wake_up(&sd->sd_wq); - } - } - spin_unlock(&sd->sd_wlock); - spin_unlock_irqrestore(&sd->sd_rlock, flags); - return IRQ_HANDLED; -} - -/* - * scdrv_open - * - * Reserve a subchannel for system controller communication. - */ - -static int -scdrv_open(struct inode *inode, struct file *file) -{ - struct sysctl_data_s *scd; - struct subch_data_s *sd; - int rv; - - /* look up device info for this device file */ - scd = container_of(inode->i_cdev, struct sysctl_data_s, scd_cdev); - - /* allocate memory for subchannel data */ - sd = kzalloc(sizeof (struct subch_data_s), GFP_KERNEL); - if (sd == NULL) { - printk("%s: couldn't allocate subchannel data\n", - __func__); - return -ENOMEM; - } - - /* initialize subch_data_s fields */ - sd->sd_nasid = scd->scd_nasid; - sd->sd_subch = ia64_sn_irtr_open(scd->scd_nasid); - - if (sd->sd_subch < 0) { - kfree(sd); - printk("%s: couldn't allocate subchannel\n", __func__); - return -EBUSY; - } - - spin_lock_init(&sd->sd_rlock); - spin_lock_init(&sd->sd_wlock); - init_waitqueue_head(&sd->sd_rq); - init_waitqueue_head(&sd->sd_wq); - sema_init(&sd->sd_rbs, 1); - sema_init(&sd->sd_wbs, 1); - - file->private_data = sd; - - /* hook this subchannel up to the system controller interrupt */ - mutex_lock(&scdrv_mutex); - rv = request_irq(SGI_UART_VECTOR, scdrv_interrupt, - IRQF_SHARED, SYSCTL_BASENAME, sd); - if (rv) { - ia64_sn_irtr_close(sd->sd_nasid, sd->sd_subch); - kfree(sd); - printk("%s: irq request failed (%d)\n", __func__, rv); - mutex_unlock(&scdrv_mutex); - return -EBUSY; - } - mutex_unlock(&scdrv_mutex); - return 0; -} - -/* - * scdrv_release - * - * Release a previously-reserved subchannel. - */ - -static int -scdrv_release(struct inode *inode, struct file *file) -{ - struct subch_data_s *sd = (struct subch_data_s *) file->private_data; - int rv; - - /* free the interrupt */ - free_irq(SGI_UART_VECTOR, sd); - - /* ask SAL to close the subchannel */ - rv = ia64_sn_irtr_close(sd->sd_nasid, sd->sd_subch); - - kfree(sd); - return rv; -} - -/* - * scdrv_read - * - * Called to read bytes from the open IRouter pipe. - * - */ - -static inline int -read_status_check(struct subch_data_s *sd, int *len) -{ - return ia64_sn_irtr_recv(sd->sd_nasid, sd->sd_subch, sd->sd_rb, len); -} - -static ssize_t -scdrv_read(struct file *file, char __user *buf, size_t count, loff_t *f_pos) -{ - int status; - int len; - unsigned long flags; - struct subch_data_s *sd = (struct subch_data_s *) file->private_data; - - /* try to get control of the read buffer */ - if (down_trylock(&sd->sd_rbs)) { - /* somebody else has it now; - * if we're non-blocking, then exit... - */ - if (file->f_flags & O_NONBLOCK) { - return -EAGAIN; - } - /* ...or if we want to block, then do so here */ - if (down_interruptible(&sd->sd_rbs)) { - /* something went wrong with wait */ - return -ERESTARTSYS; - } - } - - /* anything to read? */ - len = CHUNKSIZE; - spin_lock_irqsave(&sd->sd_rlock, flags); - status = read_status_check(sd, &len); - - /* if not, and we're blocking I/O, loop */ - while (status < 0) { - DECLARE_WAITQUEUE(wait, current); - - if (file->f_flags & O_NONBLOCK) { - spin_unlock_irqrestore(&sd->sd_rlock, flags); - up(&sd->sd_rbs); - return -EAGAIN; - } - - len = CHUNKSIZE; - set_current_state(TASK_INTERRUPTIBLE); - add_wait_queue(&sd->sd_rq, &wait); - spin_unlock_irqrestore(&sd->sd_rlock, flags); - - schedule_timeout(msecs_to_jiffies(SCDRV_TIMEOUT)); - - remove_wait_queue(&sd->sd_rq, &wait); - if (signal_pending(current)) { - /* wait was interrupted */ - up(&sd->sd_rbs); - return -ERESTARTSYS; - } - - spin_lock_irqsave(&sd->sd_rlock, flags); - status = read_status_check(sd, &len); - } - spin_unlock_irqrestore(&sd->sd_rlock, flags); - - if (len > 0) { - /* we read something in the last read_status_check(); copy - * it out to user space - */ - if (count < len) { - pr_debug("%s: only accepting %d of %d bytes\n", - __func__, (int) count, len); - } - len = min((int) count, len); - if (copy_to_user(buf, sd->sd_rb, len)) - len = -EFAULT; - } - - /* release the read buffer and wake anyone who might be - * waiting for it - */ - up(&sd->sd_rbs); - - /* return the number of characters read in */ - return len; -} - -/* - * scdrv_write - * - * Writes a chunk of an IRouter packet (or other system controller data) - * to the system controller. - * - */ -static inline int -write_status_check(struct subch_data_s *sd, int count) -{ - return ia64_sn_irtr_send(sd->sd_nasid, sd->sd_subch, sd->sd_wb, count); -} - -static ssize_t -scdrv_write(struct file *file, const char __user *buf, - size_t count, loff_t *f_pos) -{ - unsigned long flags; - int status; - struct subch_data_s *sd = (struct subch_data_s *) file->private_data; - - /* try to get control of the write buffer */ - if (down_trylock(&sd->sd_wbs)) { - /* somebody else has it now; - * if we're non-blocking, then exit... - */ - if (file->f_flags & O_NONBLOCK) { - return -EAGAIN; - } - /* ...or if we want to block, then do so here */ - if (down_interruptible(&sd->sd_wbs)) { - /* something went wrong with wait */ - return -ERESTARTSYS; - } - } - - count = min((int) count, CHUNKSIZE); - if (copy_from_user(sd->sd_wb, buf, count)) { - up(&sd->sd_wbs); - return -EFAULT; - } - - /* try to send the buffer */ - spin_lock_irqsave(&sd->sd_wlock, flags); - status = write_status_check(sd, count); - - /* if we failed, and we want to block, then loop */ - while (status <= 0) { - DECLARE_WAITQUEUE(wait, current); - - if (file->f_flags & O_NONBLOCK) { - spin_unlock_irqrestore(&sd->sd_wlock, flags); - up(&sd->sd_wbs); - return -EAGAIN; - } - - set_current_state(TASK_INTERRUPTIBLE); - add_wait_queue(&sd->sd_wq, &wait); - spin_unlock_irqrestore(&sd->sd_wlock, flags); - - schedule_timeout(msecs_to_jiffies(SCDRV_TIMEOUT)); - - remove_wait_queue(&sd->sd_wq, &wait); - if (signal_pending(current)) { - /* wait was interrupted */ - up(&sd->sd_wbs); - return -ERESTARTSYS; - } - - spin_lock_irqsave(&sd->sd_wlock, flags); - status = write_status_check(sd, count); - } - spin_unlock_irqrestore(&sd->sd_wlock, flags); - - /* release the write buffer and wake anyone who's waiting for it */ - up(&sd->sd_wbs); - - /* return the number of characters accepted (should be the complete - * "chunk" as requested) - */ - if ((status >= 0) && (status < count)) { - pr_debug("Didn't accept the full chunk; %d of %d\n", - status, (int) count); - } - return status; -} - -static __poll_t -scdrv_poll(struct file *file, struct poll_table_struct *wait) -{ - __poll_t mask = 0; - int status = 0; - struct subch_data_s *sd = (struct subch_data_s *) file->private_data; - unsigned long flags; - - poll_wait(file, &sd->sd_rq, wait); - poll_wait(file, &sd->sd_wq, wait); - - spin_lock_irqsave(&sd->sd_rlock, flags); - spin_lock(&sd->sd_wlock); - status = ia64_sn_irtr_intr(sd->sd_nasid, sd->sd_subch); - spin_unlock(&sd->sd_wlock); - spin_unlock_irqrestore(&sd->sd_rlock, flags); - - if (status > 0) { - if (status & SAL_IROUTER_INTR_RECV) { - mask |= EPOLLIN | EPOLLRDNORM; - } - if (status & SAL_IROUTER_INTR_XMIT) { - mask |= EPOLLOUT | EPOLLWRNORM; - } - } - - return mask; -} - -static const struct file_operations scdrv_fops = { - .owner = THIS_MODULE, - .read = scdrv_read, - .write = scdrv_write, - .poll = scdrv_poll, - .open = scdrv_open, - .release = scdrv_release, - .llseek = noop_llseek, -}; - -static struct class *snsc_class; - -/* - * scdrv_init - * - * Called at boot time to initialize the system controller communication - * facility. - */ -int __init -scdrv_init(void) -{ - geoid_t geoid; - cnodeid_t cnode; - char devname[32]; - char *devnamep; - struct sysctl_data_s *scd; - void *salbuf; - dev_t first_dev, dev; - nasid_t event_nasid; - - if (!ia64_platform_is("sn2")) - return -ENODEV; - - event_nasid = ia64_sn_get_console_nasid(); - - snsc_class = class_create(THIS_MODULE, SYSCTL_BASENAME); - if (IS_ERR(snsc_class)) { - printk("%s: failed to allocate class\n", __func__); - return PTR_ERR(snsc_class); - } - - if (alloc_chrdev_region(&first_dev, 0, num_cnodes, - SYSCTL_BASENAME) < 0) { - printk("%s: failed to register SN system controller device\n", - __func__); - return -ENODEV; - } - - for (cnode = 0; cnode < num_cnodes; cnode++) { - geoid = cnodeid_get_geoid(cnode); - devnamep = devname; - format_module_id(devnamep, geo_module(geoid), - MODULE_FORMAT_BRIEF); - devnamep = devname + strlen(devname); - sprintf(devnamep, "^%d#%d", geo_slot(geoid), - geo_slab(geoid)); - - /* allocate sysctl device data */ - scd = kzalloc(sizeof (struct sysctl_data_s), - GFP_KERNEL); - if (!scd) { - printk("%s: failed to allocate device info" - "for %s/%s\n", __func__, - SYSCTL_BASENAME, devname); - continue; - } - - /* initialize sysctl device data fields */ - scd->scd_nasid = cnodeid_to_nasid(cnode); - if (!(salbuf = kmalloc(SCDRV_BUFSZ, GFP_KERNEL))) { - printk("%s: failed to allocate driver buffer" - "(%s%s)\n", __func__, - SYSCTL_BASENAME, devname); - kfree(scd); - continue; - } - - if (ia64_sn_irtr_init(scd->scd_nasid, salbuf, - SCDRV_BUFSZ) < 0) { - printk - ("%s: failed to initialize SAL for" - " system controller communication" - " (%s/%s): outdated PROM?\n", - __func__, SYSCTL_BASENAME, devname); - kfree(scd); - kfree(salbuf); - continue; - } - - dev = first_dev + cnode; - cdev_init(&scd->scd_cdev, &scdrv_fops); - if (cdev_add(&scd->scd_cdev, dev, 1)) { - printk("%s: failed to register system" - " controller device (%s%s)\n", - __func__, SYSCTL_BASENAME, devname); - kfree(scd); - kfree(salbuf); - continue; - } - - device_create(snsc_class, NULL, dev, NULL, - "%s", devname); - - ia64_sn_irtr_intr_enable(scd->scd_nasid, - 0 /*ignored */ , - SAL_IROUTER_INTR_RECV); - - /* on the console nasid, prepare to receive - * system controller environmental events - */ - if(scd->scd_nasid == event_nasid) { - scdrv_event_init(scd); - } - } - return 0; -} -device_initcall(scdrv_init); diff --git a/drivers/char/snsc.h b/drivers/char/snsc.h deleted file mode 100644 index e8c52c882b21..000000000000 --- a/drivers/char/snsc.h +++ /dev/null @@ -1,92 +0,0 @@ -/* - * SN Platform system controller communication support - * - * 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) 2004-2006 Silicon Graphics, Inc. All rights reserved. - */ - -/* - * This file contains macros and data types for communication with the - * system controllers in SGI SN systems. - */ - -#ifndef _SN_SYSCTL_H_ -#define _SN_SYSCTL_H_ - -#include <linux/types.h> -#include <linux/spinlock.h> -#include <linux/wait.h> -#include <linux/fs.h> -#include <linux/cdev.h> -#include <linux/semaphore.h> -#include <asm/sn/types.h> - -#define CHUNKSIZE 127 - -/* This structure is used to track an open subchannel. */ -struct subch_data_s { - nasid_t sd_nasid; /* node on which the subchannel was opened */ - int sd_subch; /* subchannel number */ - spinlock_t sd_rlock; /* monitor lock for rsv */ - spinlock_t sd_wlock; /* monitor lock for wsv */ - wait_queue_head_t sd_rq; /* wait queue for readers */ - wait_queue_head_t sd_wq; /* wait queue for writers */ - struct semaphore sd_rbs; /* semaphore for read buffer */ - struct semaphore sd_wbs; /* semaphore for write buffer */ - - char sd_rb[CHUNKSIZE]; /* read buffer */ - char sd_wb[CHUNKSIZE]; /* write buffer */ -}; - -struct sysctl_data_s { - struct cdev scd_cdev; /* Character device info */ - nasid_t scd_nasid; /* Node on which subchannels are opened. */ -}; - - -/* argument types */ -#define IR_ARG_INT 0x00 /* 4-byte integer (big-endian) */ -#define IR_ARG_ASCII 0x01 /* null-terminated ASCII string */ -#define IR_ARG_UNKNOWN 0x80 /* unknown data type. The low - * 7 bits will contain the data - * length. */ -#define IR_ARG_UNKNOWN_LENGTH_MASK 0x7f - - -/* system controller event codes */ -#define EV_CLASS_MASK 0xf000ul -#define EV_SEVERITY_MASK 0x0f00ul -#define EV_COMPONENT_MASK 0x00fful - -#define EV_CLASS_POWER 0x1000ul -#define EV_CLASS_FAN 0x2000ul -#define EV_CLASS_TEMP 0x3000ul -#define EV_CLASS_ENV 0x4000ul -#define EV_CLASS_TEST_FAULT 0x5000ul -#define EV_CLASS_TEST_WARNING 0x6000ul -#define EV_CLASS_PWRD_NOTIFY 0x8000ul - -/* ENV class codes */ -#define ENV_PWRDN_PEND 0x4101ul - -#define EV_SEVERITY_POWER_STABLE 0x0000ul -#define EV_SEVERITY_POWER_LOW_WARNING 0x0100ul -#define EV_SEVERITY_POWER_HIGH_WARNING 0x0200ul -#define EV_SEVERITY_POWER_HIGH_FAULT 0x0300ul -#define EV_SEVERITY_POWER_LOW_FAULT 0x0400ul - -#define EV_SEVERITY_FAN_STABLE 0x0000ul -#define EV_SEVERITY_FAN_WARNING 0x0100ul -#define EV_SEVERITY_FAN_FAULT 0x0200ul - -#define EV_SEVERITY_TEMP_STABLE 0x0000ul -#define EV_SEVERITY_TEMP_ADVISORY 0x0100ul -#define EV_SEVERITY_TEMP_CRITICAL 0x0200ul -#define EV_SEVERITY_TEMP_FAULT 0x0300ul - -void scdrv_event_init(struct sysctl_data_s *); - -#endif /* _SN_SYSCTL_H_ */ diff --git a/drivers/char/snsc_event.c b/drivers/char/snsc_event.c deleted file mode 100644 index e452673dff66..000000000000 --- a/drivers/char/snsc_event.c +++ /dev/null @@ -1,303 +0,0 @@ -/* - * SN Platform system controller communication support - * - * 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) 2004-2006 Silicon Graphics, Inc. All rights reserved. - */ - -/* - * System controller event handler - * - * These routines deal with environmental events arriving from the - * system controllers. - */ - -#include <linux/interrupt.h> -#include <linux/sched/signal.h> -#include <linux/slab.h> -#include <asm/byteorder.h> -#include <asm/sn/sn_sal.h> -#include <asm/unaligned.h> -#include "snsc.h" - -static struct subch_data_s *event_sd; - -void scdrv_event(unsigned long); -DECLARE_TASKLET(sn_sysctl_event, scdrv_event, 0); - -/* - * scdrv_event_interrupt - * - * Pull incoming environmental events off the physical link to the - * system controller and put them in a temporary holding area in SAL. - * Schedule scdrv_event() to move them along to their ultimate - * destination. - */ -static irqreturn_t -scdrv_event_interrupt(int irq, void *subch_data) -{ - struct subch_data_s *sd = subch_data; - unsigned long flags; - int status; - - spin_lock_irqsave(&sd->sd_rlock, flags); - status = ia64_sn_irtr_intr(sd->sd_nasid, sd->sd_subch); - - if ((status > 0) && (status & SAL_IROUTER_INTR_RECV)) { - tasklet_schedule(&sn_sysctl_event); - } - spin_unlock_irqrestore(&sd->sd_rlock, flags); - return IRQ_HANDLED; -} - - -/* - * scdrv_parse_event - * - * Break an event (as read from SAL) into useful pieces so we can decide - * what to do with it. - */ -static int -scdrv_parse_event(char *event, int *src, int *code, int *esp_code, char *desc) -{ - char *desc_end; - - /* record event source address */ - *src = get_unaligned_be32(event); - event += 4; /* move on to event code */ - - /* record the system controller's event code */ - *code = get_unaligned_be32(event); - event += 4; /* move on to event arguments */ - - /* how many arguments are in the packet? */ - if (*event++ != 2) { - /* if not 2, give up */ - return -1; - } - - /* parse out the ESP code */ - if (*event++ != IR_ARG_INT) { - /* not an integer argument, so give up */ - return -1; - } - *esp_code = get_unaligned_be32(event); - event += 4; - - /* parse out the event description */ - if (*event++ != IR_ARG_ASCII) { - /* not an ASCII string, so give up */ - return -1; - } - event[CHUNKSIZE-1] = '\0'; /* ensure this string ends! */ - event += 2; /* skip leading CR/LF */ - desc_end = desc + sprintf(desc, "%s", event); - - /* strip trailing CR/LF (if any) */ - for (desc_end--; - (desc_end != desc) && ((*desc_end == 0xd) || (*desc_end == 0xa)); - desc_end--) { - *desc_end = '\0'; - } - - return 0; -} - - -/* - * scdrv_event_severity - * - * Figure out how urgent a message we should write to the console/syslog - * via printk. - */ -static char * -scdrv_event_severity(int code) -{ - int ev_class = (code & EV_CLASS_MASK); - int ev_severity = (code & EV_SEVERITY_MASK); - char *pk_severity = KERN_NOTICE; - - switch (ev_class) { - case EV_CLASS_POWER: - switch (ev_severity) { - case EV_SEVERITY_POWER_LOW_WARNING: - case EV_SEVERITY_POWER_HIGH_WARNING: - pk_severity = KERN_WARNING; - break; - case EV_SEVERITY_POWER_HIGH_FAULT: - case EV_SEVERITY_POWER_LOW_FAULT: - pk_severity = KERN_ALERT; - break; - } - break; - case EV_CLASS_FAN: - switch (ev_severity) { - case EV_SEVERITY_FAN_WARNING: - pk_severity = KERN_WARNING; - break; - case EV_SEVERITY_FAN_FAULT: - pk_severity = KERN_CRIT; - break; - } - break; - case EV_CLASS_TEMP: - switch (ev_severity) { - case EV_SEVERITY_TEMP_ADVISORY: - pk_severity = KERN_WARNING; - break; - case EV_SEVERITY_TEMP_CRITICAL: - pk_severity = KERN_CRIT; - break; - case EV_SEVERITY_TEMP_FAULT: - pk_severity = KERN_ALERT; - break; - } - break; - case EV_CLASS_ENV: - pk_severity = KERN_ALERT; - break; - case EV_CLASS_TEST_FAULT: - pk_severity = KERN_ALERT; - break; - case EV_CLASS_TEST_WARNING: - pk_severity = KERN_WARNING; - break; - case EV_CLASS_PWRD_NOTIFY: - pk_severity = KERN_ALERT; - break; - } - - return pk_severity; -} - - -/* - * scdrv_dispatch_event - * - * Do the right thing with an incoming event. That's often nothing - * more than printing it to the system log. For power-down notifications - * we start a graceful shutdown. - */ -static void -scdrv_dispatch_event(char *event, int len) -{ - static int snsc_shutting_down = 0; - int code, esp_code, src, class; - char desc[CHUNKSIZE]; - char *severity; - - if (scdrv_parse_event(event, &src, &code, &esp_code, desc) < 0) { - /* ignore uninterpretible event */ - return; - } - - /* how urgent is the message? */ - severity = scdrv_event_severity(code); - - class = (code & EV_CLASS_MASK); - - if (class == EV_CLASS_PWRD_NOTIFY || code == ENV_PWRDN_PEND) { - if (snsc_shutting_down) - return; - - snsc_shutting_down = 1; - - /* give a message for each type of event */ - if (class == EV_CLASS_PWRD_NOTIFY) - printk(KERN_NOTICE "Power off indication received." - " Sending SIGPWR to init...\n"); - else if (code == ENV_PWRDN_PEND) - printk(KERN_CRIT "WARNING: Shutting down the system" - " due to a critical environmental condition." - " Sending SIGPWR to init...\n"); - - /* give a SIGPWR signal to init proc */ - kill_cad_pid(SIGPWR, 0); - } else { - /* print to system log */ - printk("%s|$(0x%x)%s\n", severity, esp_code, desc); - } -} - - -/* - * scdrv_event - * - * Called as a tasklet when an event arrives from the L1. Read the event - * from where it's temporarily stored in SAL and call scdrv_dispatch_event() - * to send it on its way. Keep trying to read events until SAL indicates - * that there are no more immediately available. - */ -void -scdrv_event(unsigned long dummy) -{ - int status; - int len; - unsigned long flags; - struct subch_data_s *sd = event_sd; - - /* anything to read? */ - len = CHUNKSIZE; - spin_lock_irqsave(&sd->sd_rlock, flags); - status = ia64_sn_irtr_recv(sd->sd_nasid, sd->sd_subch, - sd->sd_rb, &len); - - while (!(status < 0)) { - spin_unlock_irqrestore(&sd->sd_rlock, flags); - scdrv_dispatch_event(sd->sd_rb, len); - len = CHUNKSIZE; - spin_lock_irqsave(&sd->sd_rlock, flags); - status = ia64_sn_irtr_recv(sd->sd_nasid, sd->sd_subch, - sd->sd_rb, &len); - } - spin_unlock_irqrestore(&sd->sd_rlock, flags); -} - - -/* - * scdrv_event_init - * - * Sets up a system controller subchannel to begin receiving event - * messages. This is sort of a specialized version of scdrv_open() - * in drivers/char/sn_sysctl.c. - */ -void -scdrv_event_init(struct sysctl_data_s *scd) -{ - int rv; - - event_sd = kzalloc(sizeof (struct subch_data_s), GFP_KERNEL); - if (event_sd == NULL) { - printk(KERN_WARNING "%s: couldn't allocate subchannel info" - " for event monitoring\n", __func__); - return; - } - - /* initialize subch_data_s fields */ - event_sd->sd_nasid = scd->scd_nasid; - spin_lock_init(&event_sd->sd_rlock); - - /* ask the system controllers to send events to this node */ - event_sd->sd_subch = ia64_sn_sysctl_event_init(scd->scd_nasid); - - if (event_sd->sd_subch < 0) { - kfree(event_sd); - printk(KERN_WARNING "%s: couldn't open event subchannel\n", - __func__); - return; - } - - /* hook event subchannel up to the system controller interrupt */ - rv = request_irq(SGI_UART_VECTOR, scdrv_event_interrupt, - IRQF_SHARED, "system controller events", event_sd); - if (rv) { - printk(KERN_WARNING "%s: irq request failed (%d)\n", - __func__, rv); - ia64_sn_irtr_close(event_sd->sd_nasid, event_sd->sd_subch); - kfree(event_sd); - return; - } -} diff --git a/drivers/ide/Kconfig b/drivers/ide/Kconfig index 9eada392df15..1c227ea8ecd3 100644 --- a/drivers/ide/Kconfig +++ b/drivers/ide/Kconfig @@ -567,15 +567,6 @@ config BLK_DEV_SVWKS This driver adds PIO/(U)DMA support for the ServerWorks OSB4/CSB5 chipsets. -config BLK_DEV_SGIIOC4 - tristate "Silicon Graphics IOC4 chipset ATA/ATAPI support" - depends on (IA64_SGI_SN2 || IA64_GENERIC) && SGI_IOC4 - select BLK_DEV_IDEDMA_PCI - help - This driver adds PIO & MultiMode DMA-2 support for the SGI IOC4 - chipset, which has one channel and can support two devices. - Please say Y here if you have an Altix System from SGI. - config BLK_DEV_SIIMAGE tristate "Silicon Image chipset support" select BLK_DEV_IDEDMA_PCI diff --git a/drivers/ide/Makefile b/drivers/ide/Makefile index 9f617a77970f..cac02db4098d 100644 --- a/drivers/ide/Makefile +++ b/drivers/ide/Makefile @@ -59,7 +59,6 @@ obj-$(CONFIG_BLK_DEV_PDC202XX_NEW) += pdc202xx_new.o obj-$(CONFIG_BLK_DEV_PIIX) += piix.o obj-$(CONFIG_BLK_DEV_RZ1000) += rz1000.o obj-$(CONFIG_BLK_DEV_SVWKS) += serverworks.o -obj-$(CONFIG_BLK_DEV_SGIIOC4) += sgiioc4.o obj-$(CONFIG_BLK_DEV_SIIMAGE) += siimage.o obj-$(CONFIG_BLK_DEV_SIS5513) += sis5513.o obj-$(CONFIG_BLK_DEV_SL82C105) += sl82c105.o diff --git a/drivers/ide/sgiioc4.c b/drivers/ide/sgiioc4.c deleted file mode 100644 index 2d35e9f7516f..000000000000 --- a/drivers/ide/sgiioc4.c +++ /dev/null @@ -1,630 +0,0 @@ -/* - * Copyright (c) 2003-2006 Silicon Graphics, Inc. All Rights Reserved. - * Copyright (C) 2008-2009 MontaVista Software, Inc. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of version 2 of the GNU General Public License - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it would be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * - * You should have received a copy of the GNU General Public - * License along with this program; if not, write the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. - * - * For further information regarding this notice, see: - * - * http://oss.sgi.com/projects/GenInfo/NoticeExplan - */ - -#include <linux/module.h> -#include <linux/types.h> -#include <linux/pci.h> -#include <linux/delay.h> -#include <linux/init.h> -#include <linux/kernel.h> -#include <linux/ioport.h> -#include <linux/blkdev.h> -#include <linux/scatterlist.h> -#include <linux/ioc4.h> -#include <linux/io.h> -#include <linux/ide.h> - -#define DRV_NAME "SGIIOC4" - -/* IOC4 Specific Definitions */ -#define IOC4_CMD_OFFSET 0x100 -#define IOC4_CTRL_OFFSET 0x120 -#define IOC4_DMA_OFFSET 0x140 -#define IOC4_INTR_OFFSET 0x0 - -#define IOC4_TIMING 0x00 -#define IOC4_DMA_PTR_L 0x01 -#define IOC4_DMA_PTR_H 0x02 -#define IOC4_DMA_ADDR_L 0x03 -#define IOC4_DMA_ADDR_H 0x04 -#define IOC4_BC_DEV 0x05 -#define IOC4_BC_MEM 0x06 -#define IOC4_DMA_CTRL 0x07 -#define IOC4_DMA_END_ADDR 0x08 - -/* Bits in the IOC4 Control/Status Register */ -#define IOC4_S_DMA_START 0x01 -#define IOC4_S_DMA_STOP 0x02 -#define IOC4_S_DMA_DIR 0x04 -#define IOC4_S_DMA_ACTIVE 0x08 -#define IOC4_S_DMA_ERROR 0x10 -#define IOC4_ATA_MEMERR 0x02 - -/* Read/Write Directions */ -#define IOC4_DMA_WRITE 0x04 -#define IOC4_DMA_READ 0x00 - -/* Interrupt Register Offsets */ -#define IOC4_INTR_REG 0x03 -#define IOC4_INTR_SET 0x05 -#define IOC4_INTR_CLEAR 0x07 - -#define IOC4_IDE_CACHELINE_SIZE 128 -#define IOC4_CMD_CTL_BLK_SIZE 0x20 -#define IOC4_SUPPORTED_FIRMWARE_REV 46 - -struct ioc4_dma_regs { - u32 timing_reg0; - u32 timing_reg1; - u32 low_mem_ptr; - u32 high_mem_ptr; - u32 low_mem_addr; - u32 high_mem_addr; - u32 dev_byte_count; - u32 mem_byte_count; - u32 status; -}; - -/* Each Physical Region Descriptor Entry size is 16 bytes (2 * 64 bits) */ -/* IOC4 has only 1 IDE channel */ -#define IOC4_PRD_BYTES 16 -#define IOC4_PRD_ENTRIES (PAGE_SIZE / (4 * IOC4_PRD_BYTES)) - - -static void sgiioc4_init_hwif_ports(struct ide_hw *hw, - unsigned long data_port, - unsigned long ctrl_port, - unsigned long irq_port) -{ - unsigned long reg = data_port; - int i; - - /* Registers are word (32 bit) aligned */ - for (i = 0; i <= 7; i++) - hw->io_ports_array[i] = reg + i * 4; - - hw->io_ports.ctl_addr = ctrl_port; - hw->io_ports.irq_addr = irq_port; -} - -static int sgiioc4_checkirq(ide_hwif_t *hwif) -{ - unsigned long intr_addr = hwif->io_ports.irq_addr + IOC4_INTR_REG * 4; - - if (readl((void __iomem *)intr_addr) & 0x03) - return 1; - - return 0; -} - -static u8 sgiioc4_read_status(ide_hwif_t *); - -static int sgiioc4_clearirq(ide_drive_t *drive) -{ - u32 intr_reg; - ide_hwif_t *hwif = drive->hwif; - struct ide_io_ports *io_ports = &hwif->io_ports; - unsigned long other_ir = io_ports->irq_addr + (IOC4_INTR_REG << 2); - - /* Code to check for PCI error conditions */ - intr_reg = readl((void __iomem *)other_ir); - if (intr_reg & 0x03) { /* Valid IOC4-IDE interrupt */ - /* - * Using sgiioc4_read_status to read the Status register has a - * side effect of clearing the interrupt. The first read should - * clear it if it is set. The second read should return - * a "clear" status if it got cleared. If not, then spin - * for a bit trying to clear it. - */ - u8 stat = sgiioc4_read_status(hwif); - int count = 0; - - stat = sgiioc4_read_status(hwif); - while ((stat & ATA_BUSY) && (count++ < 100)) { - udelay(1); - stat = sgiioc4_read_status(hwif); - } - - if (intr_reg & 0x02) { - struct pci_dev *dev = to_pci_dev(hwif->dev); - /* Error when transferring DMA data on PCI bus */ - u32 pci_err_addr_low, pci_err_addr_high, - pci_stat_cmd_reg; - - pci_err_addr_low = - readl((void __iomem *)io_ports->irq_addr); - pci_err_addr_high = - readl((void __iomem *)(io_ports->irq_addr + 4)); - pci_read_config_dword(dev, PCI_COMMAND, - &pci_stat_cmd_reg); - printk(KERN_ERR "%s(%s): PCI Bus Error when doing DMA: " - "status-cmd reg is 0x%x\n", - __func__, drive->name, pci_stat_cmd_reg); - printk(KERN_ERR "%s(%s): PCI Error Address is 0x%x%x\n", - __func__, drive->name, - pci_err_addr_high, pci_err_addr_low); - /* Clear the PCI Error indicator */ - pci_write_config_dword(dev, PCI_COMMAND, 0x00000146); - } - - /* Clear the Interrupt, Error bits on the IOC4 */ - writel(0x03, (void __iomem *)other_ir); - - intr_reg = readl((void __iomem *)other_ir); - } - - return intr_reg & 3; -} - -static void sgiioc4_dma_start(ide_drive_t *drive) -{ - ide_hwif_t *hwif = drive->hwif; - unsigned long ioc4_dma_addr = hwif->dma_base + IOC4_DMA_CTRL * 4; - unsigned int reg = readl((void __iomem *)ioc4_dma_addr); - unsigned int temp_reg = reg | IOC4_S_DMA_START; - - writel(temp_reg, (void __iomem *)ioc4_dma_addr); -} - -static u32 sgiioc4_ide_dma_stop(ide_hwif_t *hwif, u64 dma_base) -{ - unsigned long ioc4_dma_addr = dma_base + IOC4_DMA_CTRL * 4; - u32 ioc4_dma; - int count; - - count = 0; - ioc4_dma = readl((void __iomem *)ioc4_dma_addr); - while ((ioc4_dma & IOC4_S_DMA_STOP) && (count++ < 200)) { - udelay(1); - ioc4_dma = readl((void __iomem *)ioc4_dma_addr); - } - return ioc4_dma; -} - -/* Stops the IOC4 DMA Engine */ -static int sgiioc4_dma_end(ide_drive_t *drive) -{ - u32 ioc4_dma, bc_dev, bc_mem, num, valid = 0, cnt = 0; - ide_hwif_t *hwif = drive->hwif; - unsigned long dma_base = hwif->dma_base; - int dma_stat = 0; - unsigned long *ending_dma = ide_get_hwifdata(hwif); - - writel(IOC4_S_DMA_STOP, (void __iomem *)(dma_base + IOC4_DMA_CTRL * 4)); - - ioc4_dma = sgiioc4_ide_dma_stop(hwif, dma_base); - - if (ioc4_dma & IOC4_S_DMA_STOP) { - printk(KERN_ERR - "%s(%s): IOC4 DMA STOP bit is still 1 :" - "ioc4_dma_reg 0x%x\n", - __func__, drive->name, ioc4_dma); - dma_stat = 1; - } - - /* - * The IOC4 will DMA 1's to the ending DMA area to indicate that - * previous data DMA is complete. This is necessary because of relaxed - * ordering between register reads and DMA writes on the Altix. - */ - while ((cnt++ < 200) && (!valid)) { - for (num = 0; num < 16; num++) { - if (ending_dma[num]) { - valid = 1; - break; - } - } - udelay(1); - } - if (!valid) { - printk(KERN_ERR "%s(%s) : DMA incomplete\n", __func__, - drive->name); - dma_stat = 1; - } - - bc_dev = readl((void __iomem *)(dma_base + IOC4_BC_DEV * 4)); - bc_mem = readl((void __iomem *)(dma_base + IOC4_BC_MEM * 4)); - - if ((bc_dev & 0x01FF) || (bc_mem & 0x1FF)) { - if (bc_dev > bc_mem + 8) { - printk(KERN_ERR - "%s(%s): WARNING!! byte_count_dev %d " - "!= byte_count_mem %d\n", - __func__, drive->name, bc_dev, bc_mem); - } - } - - return dma_stat; -} - -static void sgiioc4_set_dma_mode(ide_hwif_t *hwif, ide_drive_t *drive) -{ -} - -/* Returns 1 if DMA IRQ issued, 0 otherwise */ -static int sgiioc4_dma_test_irq(ide_drive_t *drive) -{ - return sgiioc4_checkirq(drive->hwif); -} - -static void sgiioc4_dma_host_set(ide_drive_t *drive, int on) -{ - if (!on) - sgiioc4_clearirq(drive); -} - -static void sgiioc4_resetproc(ide_drive_t *drive) -{ - struct ide_cmd *cmd = &drive->hwif->cmd; - - sgiioc4_dma_end(drive); - ide_dma_unmap_sg(drive, cmd); - sgiioc4_clearirq(drive); -} - -static void sgiioc4_dma_lost_irq(ide_drive_t *drive) -{ - sgiioc4_resetproc(drive); - - ide_dma_lost_irq(drive); -} - -static u8 sgiioc4_read_status(ide_hwif_t *hwif) -{ - unsigned long port = hwif->io_ports.status_addr; - u8 reg = (u8) readb((void __iomem *) port); - - if (!(reg & ATA_BUSY)) { /* Not busy... check for interrupt */ - unsigned long other_ir = port - 0x110; - unsigned int intr_reg = (u32) readl((void __iomem *) other_ir); - - /* Clear the Interrupt, Error bits on the IOC4 */ - if (intr_reg & 0x03) { - writel(0x03, (void __iomem *) other_ir); - intr_reg = (u32) readl((void __iomem *) other_ir); - } - } - - return reg; -} - -/* Creates a DMA map for the scatter-gather list entries */ -static int ide_dma_sgiioc4(ide_hwif_t *hwif, const struct ide_port_info *d) -{ - struct pci_dev *dev = to_pci_dev(hwif->dev); - unsigned long dma_base = pci_resource_start(dev, 0) + IOC4_DMA_OFFSET; - int num_ports = sizeof(struct ioc4_dma_regs); - void *pad; - - printk(KERN_INFO " %s: MMIO-DMA\n", hwif->name); - - if (request_mem_region(dma_base, num_ports, hwif->name) == NULL) { - printk(KERN_ERR "%s(%s) -- ERROR: addresses 0x%08lx to 0x%08lx " - "already in use\n", __func__, hwif->name, - dma_base, dma_base + num_ports - 1); - return -1; - } - - hwif->dma_base = (unsigned long)hwif->io_ports.irq_addr + - IOC4_DMA_OFFSET; - - hwif->sg_max_nents = IOC4_PRD_ENTRIES; - - hwif->prd_max_nents = IOC4_PRD_ENTRIES; - hwif->prd_ent_size = IOC4_PRD_BYTES; - - if (ide_allocate_dma_engine(hwif)) - goto dma_pci_alloc_failure; - - pad = dma_alloc_coherent(&dev->dev, IOC4_IDE_CACHELINE_SIZE, - (dma_addr_t *)&hwif->extra_base, GFP_KERNEL); - if (pad) { - ide_set_hwifdata(hwif, pad); - return 0; - } - - ide_release_dma_engine(hwif); - - printk(KERN_ERR "%s(%s) -- ERROR: Unable to allocate DMA maps\n", - __func__, hwif->name); - printk(KERN_INFO "%s: changing from DMA to PIO mode", hwif->name); - -dma_pci_alloc_failure: - release_mem_region(dma_base, num_ports); - - return -1; -} - -/* Initializes the IOC4 DMA Engine */ -static void sgiioc4_configure_for_dma(int dma_direction, ide_drive_t *drive) -{ - u32 ioc4_dma; - ide_hwif_t *hwif = drive->hwif; - unsigned long dma_base = hwif->dma_base; - unsigned long ioc4_dma_addr = dma_base + IOC4_DMA_CTRL * 4; - u32 dma_addr, ending_dma_addr; - - ioc4_dma = readl((void __iomem *)ioc4_dma_addr); - - if (ioc4_dma & IOC4_S_DMA_ACTIVE) { - printk(KERN_WARNING "%s(%s): Warning!! DMA from previous " - "transfer was still active\n", __func__, drive->name); - writel(IOC4_S_DMA_STOP, (void __iomem *)ioc4_dma_addr); - ioc4_dma = sgiioc4_ide_dma_stop(hwif, dma_base); - - if (ioc4_dma & IOC4_S_DMA_STOP) - printk(KERN_ERR "%s(%s): IOC4 DMA STOP bit is " - "still 1\n", __func__, drive->name); - } - - ioc4_dma = readl((void __iomem *)ioc4_dma_addr); - if (ioc4_dma & IOC4_S_DMA_ERROR) { - printk(KERN_WARNING "%s(%s): Warning!! DMA Error during " - "previous transfer, status 0x%x\n", - __func__, drive->name, ioc4_dma); - writel(IOC4_S_DMA_STOP, (void __iomem *)ioc4_dma_addr); - ioc4_dma = sgiioc4_ide_dma_stop(hwif, dma_base); - - if (ioc4_dma & IOC4_S_DMA_STOP) - printk(KERN_ERR "%s(%s): IOC4 DMA STOP bit is " - "still 1\n", __func__, drive->name); - } - - /* Address of the Scatter Gather List */ - dma_addr = cpu_to_le32(hwif->dmatable_dma); - writel(dma_addr, (void __iomem *)(dma_base + IOC4_DMA_PTR_L * 4)); - - /* Address of the Ending DMA */ - memset(ide_get_hwifdata(hwif), 0, IOC4_IDE_CACHELINE_SIZE); - ending_dma_addr = cpu_to_le32(hwif->extra_base); - writel(ending_dma_addr, (void __iomem *)(dma_base + - IOC4_DMA_END_ADDR * 4)); - - writel(dma_direction, (void __iomem *)ioc4_dma_addr); -} - -/* IOC4 Scatter Gather list Format */ -/* 128 Bit entries to support 64 bit addresses in the future */ -/* The Scatter Gather list Entry should be in the BIG-ENDIAN Format */ -/* --------------------------------------------------------------------- */ -/* | Upper 32 bits - Zero | Lower 32 bits- address | */ -/* --------------------------------------------------------------------- */ -/* | Upper 32 bits - Zero |EOL| 15 unused | 16 Bit Length| */ -/* --------------------------------------------------------------------- */ -/* Creates the scatter gather list, DMA Table */ - -static int sgiioc4_build_dmatable(ide_drive_t *drive, struct ide_cmd *cmd) -{ - ide_hwif_t *hwif = drive->hwif; - unsigned int *table = hwif->dmatable_cpu; - unsigned int count = 0, i = cmd->sg_nents; - struct scatterlist *sg = hwif->sg_table; - - while (i && sg_dma_len(sg)) { - dma_addr_t cur_addr; - int cur_len; - cur_addr = sg_dma_address(sg); - cur_len = sg_dma_len(sg); - - while (cur_len) { - if (count++ >= IOC4_PRD_ENTRIES) { - printk(KERN_WARNING - "%s: DMA table too small\n", - drive->name); - return 0; - } else { - u32 bcount = - 0x10000 - (cur_addr & 0xffff); - - if (bcount > cur_len) - bcount = cur_len; - - /* - * Put the address, length in - * the IOC4 dma-table format - */ - *table = 0x0; - table++; - *table = cpu_to_be32(cur_addr); - table++; - *table = 0x0; - table++; - - *table = cpu_to_be32(bcount); - table++; - - cur_addr += bcount; - cur_len -= bcount; - } - } - - sg = sg_next(sg); - i--; - } - - if (count) { - table--; - *table |= cpu_to_be32(0x80000000); - return count; - } - - return 0; /* revert to PIO for this request */ -} - -static int sgiioc4_dma_setup(ide_drive_t *drive, struct ide_cmd *cmd) -{ - int ddir; - u8 write = !!(cmd->tf_flags & IDE_TFLAG_WRITE); - - if (sgiioc4_build_dmatable(drive, cmd) == 0) - /* try PIO instead of DMA */ - return 1; - - if (write) - /* Writes TO the IOC4 FROM Main Memory */ - ddir = IOC4_DMA_READ; - else - /* Writes FROM the IOC4 TO Main Memory */ - ddir = IOC4_DMA_WRITE; - - sgiioc4_configure_for_dma(ddir, drive); - - return 0; -} - -static const struct ide_tp_ops sgiioc4_tp_ops = { - .exec_command = ide_exec_command, - .read_status = sgiioc4_read_status, - .read_altstatus = ide_read_altstatus, - .write_devctl = ide_write_devctl, - - .dev_select = ide_dev_select, - .tf_load = ide_tf_load, - .tf_read = ide_tf_read, - - .input_data = ide_input_data, - .output_data = ide_output_data, -}; - -static const struct ide_port_ops sgiioc4_port_ops = { - .set_dma_mode = sgiioc4_set_dma_mode, - /* reset DMA engine, clear IRQs */ - .resetproc = sgiioc4_resetproc, -}; - -static const struct ide_dma_ops sgiioc4_dma_ops = { - .dma_host_set = sgiioc4_dma_host_set, - .dma_setup = sgiioc4_dma_setup, - .dma_start = sgiioc4_dma_start, - .dma_end = sgiioc4_dma_end, - .dma_test_irq = sgiioc4_dma_test_irq, - .dma_lost_irq = sgiioc4_dma_lost_irq, -}; - -static const struct ide_port_info sgiioc4_port_info = { - .name = DRV_NAME, - .chipset = ide_pci, - .init_dma = ide_dma_sgiioc4, - .tp_ops = &sgiioc4_tp_ops, - .port_ops = &sgiioc4_port_ops, - .dma_ops = &sgiioc4_dma_ops, - .host_flags = IDE_HFLAG_MMIO, - .irq_flags = IRQF_SHARED, - .mwdma_mask = ATA_MWDMA2_ONLY, -}; - -static int sgiioc4_ide_setup_pci_device(struct pci_dev *dev) -{ - unsigned long cmd_base, irqport; - unsigned long bar0, cmd_phys_base, ctl; - void __iomem *virt_base; - struct ide_hw hw, *hws[] = { &hw }; - int rc; - - /* Get the CmdBlk and CtrlBlk base registers */ - bar0 = pci_resource_start(dev, 0); - virt_base = pci_ioremap_bar(dev, 0); - if (virt_base == NULL) { - printk(KERN_ERR "%s: Unable to remap BAR 0 address: 0x%lx\n", - DRV_NAME, bar0); - return -ENOMEM; - } - cmd_base = (unsigned long)virt_base + IOC4_CMD_OFFSET; - ctl = (unsigned long)virt_base + IOC4_CTRL_OFFSET; - irqport = (unsigned long)virt_base + IOC4_INTR_OFFSET; - - cmd_phys_base = bar0 + IOC4_CMD_OFFSET; - if (request_mem_region(cmd_phys_base, IOC4_CMD_CTL_BLK_SIZE, - DRV_NAME) == NULL) { - printk(KERN_ERR "%s %s -- ERROR: addresses 0x%08lx to 0x%08lx " - "already in use\n", DRV_NAME, pci_name(dev), - cmd_phys_base, cmd_phys_base + IOC4_CMD_CTL_BLK_SIZE); - rc = -EBUSY; - goto req_mem_rgn_err; - } - - /* Initialize the IO registers */ - memset(&hw, 0, sizeof(hw)); - sgiioc4_init_hwif_ports(&hw, cmd_base, ctl, irqport); - hw.irq = dev->irq; - hw.dev = &dev->dev; - - /* Initialize chipset IRQ registers */ - writel(0x03, (void __iomem *)(irqport + IOC4_INTR_SET * 4)); - - rc = ide_host_add(&sgiioc4_port_info, hws, 1, NULL); - if (!rc) - return 0; - - release_mem_region(cmd_phys_base, IOC4_CMD_CTL_BLK_SIZE); -req_mem_rgn_err: - iounmap(virt_base); - return rc; -} - -static unsigned int pci_init_sgiioc4(struct pci_dev *dev) -{ - int ret; - - printk(KERN_INFO "%s: IDE controller at PCI slot %s, revision %d\n", - DRV_NAME, pci_name(dev), dev->revision); - - if (dev->revision < IOC4_SUPPORTED_FIRMWARE_REV) { - printk(KERN_ERR "Skipping %s IDE controller in slot %s: " - "firmware is obsolete - please upgrade to " - "revision46 or higher\n", - DRV_NAME, pci_name(dev)); - ret = -EAGAIN; - goto out; - } - ret = sgiioc4_ide_setup_pci_device(dev); -out: - return ret; -} - -static int ioc4_ide_attach_one(struct ioc4_driver_data *idd) -{ - /* - * PCI-RT does not bring out IDE connection. - * Do not attach to this particular IOC4. - */ - if (idd->idd_variant == IOC4_VARIANT_PCI_RT) - return 0; - - return pci_init_sgiioc4(idd->idd_pdev); -} - -static struct ioc4_submodule ioc4_ide_submodule = { - .is_name = "IOC4_ide", - .is_owner = THIS_MODULE, - .is_probe = ioc4_ide_attach_one, -}; - -static int __init ioc4_ide_init(void) -{ - return ioc4_register_submodule(&ioc4_ide_submodule); -} - -late_initcall(ioc4_ide_init); /* Call only after IDE init is done */ - -MODULE_AUTHOR("Aniket Malatpure/Jeremy Higdon"); -MODULE_DESCRIPTION("IDE PCI driver module for SGI IOC4 Base-IO Card"); -MODULE_LICENSE("GPL"); diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig index a4ddeade8ac4..e3842eabcfdd 100644 --- a/drivers/iommu/Kconfig +++ b/drivers/iommu/Kconfig @@ -177,7 +177,7 @@ config DMAR_TABLE config INTEL_IOMMU bool "Support for Intel IOMMU using DMA Remapping Devices" - depends on PCI_MSI && ACPI && (X86 || IA64_GENERIC) + depends on PCI_MSI && ACPI && (X86 || IA64) select IOMMU_API select IOMMU_IOVA select NEED_DMA_MAP_STATE diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 16900357afc2..45b61b8d8442 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -126,18 +126,6 @@ config INTEL_MID_PTI an Intel Atom (non-netbook) mobile device containing a MIPI P1149.7 standard implementation. -config SGI_IOC4 - tristate "SGI IOC4 Base IO support" - depends on PCI - ---help--- - This option enables basic support for the IOC4 chip on certain - SGI IO controller cards (IO9, IO10, and PCI-RT). This option - does not enable any specific functions on such a card, but provides - necessary infrastructure for other drivers to utilize. - - If you have an SGI Altix with an IOC4-based card say Y. - Otherwise say N. - config TIFM_CORE tristate "TI Flash Media interface support" depends on PCI @@ -200,9 +188,8 @@ config ENCLOSURE_SERVICES config SGI_XP tristate "Support communication between SGI SSIs" depends on NET - depends on (IA64_GENERIC || IA64_SGI_SN2 || IA64_SGI_UV || X86_UV) && SMP - select IA64_UNCACHED_ALLOCATOR if IA64_GENERIC || IA64_SGI_SN2 - select GENERIC_ALLOCATOR if IA64_GENERIC || IA64_SGI_SN2 + depends on (IA64_SGI_UV || X86_UV) && SMP + depends on X86_64 || BROKEN select SGI_GRU if X86_64 && SMP ---help--- An SGI machine can be divided into multiple Single System diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index abd8ae249746..8dae0a976200 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -21,7 +21,6 @@ obj-$(CONFIG_QCOM_COINCELL) += qcom-coincell.o obj-$(CONFIG_QCOM_FASTRPC) += fastrpc.o obj-$(CONFIG_SENSORS_BH1770) += bh1770glc.o obj-$(CONFIG_SENSORS_APDS990X) += apds990x.o -obj-$(CONFIG_SGI_IOC4) += ioc4.o obj-$(CONFIG_ENCLOSURE_SERVICES) += enclosure.o obj-$(CONFIG_KGDB_TESTS) += kgdbts.o obj-$(CONFIG_SGI_XP) += sgi-xp/ diff --git a/drivers/misc/ioc4.c b/drivers/misc/ioc4.c deleted file mode 100644 index 9d0445a567db..000000000000 --- a/drivers/misc/ioc4.c +++ /dev/null @@ -1,498 +0,0 @@ -/* - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - * - * Copyright (C) 2005-2006 Silicon Graphics, Inc. All Rights Reserved. - */ - -/* This file contains the master driver module for use by SGI IOC4 subdrivers. - * - * It allocates any resources shared between multiple subdevices, and - * provides accessor functions (where needed) and the like for those - * resources. It also provides a mechanism for the subdevice modules - * to support loading and unloading. - * - * Non-shared resources (e.g. external interrupt A_INT_OUT register page - * alias, serial port and UART registers) are handled by the subdevice - * modules themselves. - * - * This is all necessary because IOC4 is not implemented as a multi-function - * PCI device, but an amalgamation of disparate registers for several - * types of device (ATA, serial, external interrupts). The normal - * resource management in the kernel doesn't have quite the right interfaces - * to handle this situation (e.g. multiple modules can't claim the same - * PCI ID), thus this IOC4 master module. - */ - -#include <linux/errno.h> -#include <linux/module.h> -#include <linux/pci.h> -#include <linux/ioc4.h> -#include <linux/ktime.h> -#include <linux/slab.h> -#include <linux/mutex.h> -#include <linux/time.h> -#include <asm/io.h> - -/*************** - * Definitions * - ***************/ - -/* Tweakable values */ - -/* PCI bus speed detection/calibration */ -#define IOC4_CALIBRATE_COUNT 63 /* Calibration cycle period */ -#define IOC4_CALIBRATE_CYCLES 256 /* Average over this many cycles */ -#define IOC4_CALIBRATE_DISCARD 2 /* Discard first few cycles */ -#define IOC4_CALIBRATE_LOW_MHZ 25 /* Lower bound on bus speed sanity */ -#define IOC4_CALIBRATE_HIGH_MHZ 75 /* Upper bound on bus speed sanity */ -#define IOC4_CALIBRATE_DEFAULT_MHZ 66 /* Assumed if sanity check fails */ - -/************************ - * Submodule management * - ************************/ - -static DEFINE_MUTEX(ioc4_mutex); - -static LIST_HEAD(ioc4_devices); -static LIST_HEAD(ioc4_submodules); - -/* Register an IOC4 submodule */ -int -ioc4_register_submodule(struct ioc4_submodule *is) -{ - struct ioc4_driver_data *idd; - - mutex_lock(&ioc4_mutex); - list_add(&is->is_list, &ioc4_submodules); - - /* Initialize submodule for each IOC4 */ - if (!is->is_probe) - goto out; - - list_for_each_entry(idd, &ioc4_devices, idd_list) { - if (is->is_probe(idd)) { - printk(KERN_WARNING - "%s: IOC4 submodule %s probe failed " - "for pci_dev %s", - __func__, module_name(is->is_owner), - pci_name(idd->idd_pdev)); - } - } - out: - mutex_unlock(&ioc4_mutex); - return 0; -} - -/* Unregister an IOC4 submodule */ -void -ioc4_unregister_submodule(struct ioc4_submodule *is) -{ - struct ioc4_driver_data *idd; - - mutex_lock(&ioc4_mutex); - list_del(&is->is_list); - - /* Remove submodule for each IOC4 */ - if (!is->is_remove) - goto out; - - list_for_each_entry(idd, &ioc4_devices, idd_list) { - if (is->is_remove(idd)) { - printk(KERN_WARNING - "%s: IOC4 submodule %s remove failed " - "for pci_dev %s.\n", - __func__, module_name(is->is_owner), - pci_name(idd->idd_pdev)); - } - } - out: - mutex_unlock(&ioc4_mutex); -} - -/********************* - * Device management * - *********************/ - -#define IOC4_CALIBRATE_LOW_LIMIT \ - (1000*IOC4_EXTINT_COUNT_DIVISOR/IOC4_CALIBRATE_LOW_MHZ) -#define IOC4_CALIBRATE_HIGH_LIMIT \ - (1000*IOC4_EXTINT_COUNT_DIVISOR/IOC4_CALIBRATE_HIGH_MHZ) -#define IOC4_CALIBRATE_DEFAULT \ - (1000*IOC4_EXTINT_COUNT_DIVISOR/IOC4_CALIBRATE_DEFAULT_MHZ) - -#define IOC4_CALIBRATE_END \ - (IOC4_CALIBRATE_CYCLES + IOC4_CALIBRATE_DISCARD) - -#define IOC4_INT_OUT_MODE_TOGGLE 0x7 /* Toggle INT_OUT every COUNT+1 ticks */ - -/* Determines external interrupt output clock period of the PCI bus an - * IOC4 is attached to. This value can be used to determine the PCI - * bus speed. - * - * IOC4 has a design feature that various internal timers are derived from - * the PCI bus clock. This causes IOC4 device drivers to need to take the - * bus speed into account when setting various register values (e.g. INT_OUT - * register COUNT field, UART divisors, etc). Since this information is - * needed by several subdrivers, it is determined by the main IOC4 driver, - * even though the following code utilizes external interrupt registers - * to perform the speed calculation. - */ -static void -ioc4_clock_calibrate(struct ioc4_driver_data *idd) -{ - union ioc4_int_out int_out; - union ioc4_gpcr gpcr; - unsigned int state, last_state; - uint64_t start, end, period; - unsigned int count; - - /* Enable output */ - gpcr.raw = 0; - gpcr.fields.dir = IOC4_GPCR_DIR_0; - gpcr.fields.int_out_en = 1; - writel(gpcr.raw, &idd->idd_misc_regs->gpcr_s.raw); - - /* Reset to power-on state */ - writel(0, &idd->idd_misc_regs->int_out.raw); - - /* Set up square wave */ - int_out.raw = 0; - int_out.fields.count = IOC4_CALIBRATE_COUNT; - int_out.fields.mode = IOC4_INT_OUT_MODE_TOGGLE; - int_out.fields.diag = 0; - writel(int_out.raw, &idd->idd_misc_regs->int_out.raw); - - /* Check square wave period averaged over some number of cycles */ - start = ktime_get_ns(); - state = 1; /* make sure the first read isn't a rising edge */ - for (count = 0; count <= IOC4_CALIBRATE_END; count++) { - do { /* wait for a rising edge */ - last_state = state; - int_out.raw = readl(&idd->idd_misc_regs->int_out.raw); - state = int_out.fields.int_out; - } while (last_state || !state); - - /* discard the first few cycles */ - if (count == IOC4_CALIBRATE_DISCARD) - start = ktime_get_ns(); - } - end = ktime_get_ns(); - - /* Calculation rearranged to preserve intermediate precision. - * Logically: - * 1. "end - start" gives us the measurement period over all - * the square wave cycles. - * 2. Divide by number of square wave cycles to get the period - * of a square wave cycle. - * 3. Divide by 2*(int_out.fields.count+1), which is the formula - * by which the IOC4 generates the square wave, to get the - * period of an IOC4 INT_OUT count. - */ - period = (end - start) / - (IOC4_CALIBRATE_CYCLES * 2 * (IOC4_CALIBRATE_COUNT + 1)); - - /* Bounds check the result. */ - if (period > IOC4_CALIBRATE_LOW_LIMIT || - period < IOC4_CALIBRATE_HIGH_LIMIT) { - printk(KERN_INFO - "IOC4 %s: Clock calibration failed. Assuming" - "PCI clock is %d ns.\n", - pci_name(idd->idd_pdev), - IOC4_CALIBRATE_DEFAULT / IOC4_EXTINT_COUNT_DIVISOR); - period = IOC4_CALIBRATE_DEFAULT; - } else { - u64 ns = period; - - do_div(ns, IOC4_EXTINT_COUNT_DIVISOR); - printk(KERN_DEBUG - "IOC4 %s: PCI clock is %llu ns.\n", - pci_name(idd->idd_pdev), (unsigned long long)ns); - } - - /* Remember results. We store the extint clock period rather - * than the PCI clock period so that greater precision is - * retained. Divide by IOC4_EXTINT_COUNT_DIVISOR to get - * PCI clock period. - */ - idd->count_period = period; -} - -/* There are three variants of IOC4 cards: IO9, IO10, and PCI-RT. - * Each brings out different combinations of IOC4 signals, thus. - * the IOC4 subdrivers need to know to which we're attached. - * - * We look for the presence of a SCSI (IO9) or SATA (IO10) controller - * on the same PCI bus at slot number 3 to differentiate IO9 from IO10. - * If neither is present, it's a PCI-RT. - */ -static unsigned int -ioc4_variant(struct ioc4_driver_data *idd) -{ - struct pci_dev *pdev = NULL; - int found = 0; - - /* IO9: Look for a QLogic ISP 12160 at the same bus and slot 3. */ - do { - pdev = pci_get_device(PCI_VENDOR_ID_QLOGIC, - PCI_DEVICE_ID_QLOGIC_ISP12160, pdev); - if (pdev && - idd->idd_pdev->bus->number == pdev->bus->number && - 3 == PCI_SLOT(pdev->devfn)) - found = 1; - } while (pdev && !found); - if (NULL != pdev) { - pci_dev_put(pdev); - return IOC4_VARIANT_IO9; - } - - /* IO10: Look for a Vitesse VSC 7174 at the same bus and slot 3. */ - pdev = NULL; - do { - pdev = pci_get_device(PCI_VENDOR_ID_VITESSE, - PCI_DEVICE_ID_VITESSE_VSC7174, pdev); - if (pdev && - idd->idd_pdev->bus->number == pdev->bus->number && - 3 == PCI_SLOT(pdev->devfn)) - found = 1; - } while (pdev && !found); - if (NULL != pdev) { - pci_dev_put(pdev); - return IOC4_VARIANT_IO10; - } - - /* PCI-RT: No SCSI/SATA controller will be present */ - return IOC4_VARIANT_PCI_RT; -} - -static void -ioc4_load_modules(struct work_struct *work) -{ - request_module("sgiioc4"); -} - -static DECLARE_WORK(ioc4_load_modules_work, ioc4_load_modules); - -/* Adds a new instance of an IOC4 card */ -static int -ioc4_probe(struct pci_dev *pdev, const struct pci_device_id *pci_id) -{ - struct ioc4_driver_data *idd; - struct ioc4_submodule *is; - uint32_t pcmd; - int ret; - - /* Enable IOC4 and take ownership of it */ - if ((ret = pci_enable_device(pdev))) { - printk(KERN_WARNING - "%s: Failed to enable IOC4 device for pci_dev %s.\n", - __func__, pci_name(pdev)); - goto out; - } - pci_set_master(pdev); - - /* Set up per-IOC4 data */ - idd = kmalloc(sizeof(struct ioc4_driver_data), GFP_KERNEL); - if (!idd) { - printk(KERN_WARNING - "%s: Failed to allocate IOC4 data for pci_dev %s.\n", - __func__, pci_name(pdev)); - ret = -ENODEV; - goto out_idd; - } - idd->idd_pdev = pdev; - idd->idd_pci_id = pci_id; - - /* Map IOC4 misc registers. These are shared between subdevices - * so the main IOC4 module manages them. - */ - idd->idd_bar0 = pci_resource_start(idd->idd_pdev, 0); - if (!idd->idd_bar0) { - printk(KERN_WARNING - "%s: Unable to find IOC4 misc resource " - "for pci_dev %s.\n", - __func__, pci_name(idd->idd_pdev)); - ret = -ENODEV; - goto out_pci; - } - if (!request_mem_region(idd->idd_bar0, sizeof(struct ioc4_misc_regs), - "ioc4_misc")) { - printk(KERN_WARNING - "%s: Unable to request IOC4 misc region " - "for pci_dev %s.\n", - __func__, pci_name(idd->idd_pdev)); - ret = -ENODEV; - goto out_pci; - } - idd->idd_misc_regs = ioremap(idd->idd_bar0, - sizeof(struct ioc4_misc_regs)); - if (!idd->idd_misc_regs) { - printk(KERN_WARNING - "%s: Unable to remap IOC4 misc region " - "for pci_dev %s.\n", - __func__, pci_name(idd->idd_pdev)); - ret = -ENODEV; - goto out_misc_region; - } - - /* Failsafe portion of per-IOC4 initialization */ - - /* Detect card variant */ - idd->idd_variant = ioc4_variant(idd); - printk(KERN_INFO "IOC4 %s: %s card detected.\n", pci_name(pdev), - idd->idd_variant == IOC4_VARIANT_IO9 ? "IO9" : - idd->idd_variant == IOC4_VARIANT_PCI_RT ? "PCI-RT" : - idd->idd_variant == IOC4_VARIANT_IO10 ? "IO10" : "unknown"); - - /* Initialize IOC4 */ - pci_read_config_dword(idd->idd_pdev, PCI_COMMAND, &pcmd); - pci_write_config_dword(idd->idd_pdev, PCI_COMMAND, - pcmd | PCI_COMMAND_PARITY | PCI_COMMAND_SERR); - - /* Determine PCI clock */ - ioc4_clock_calibrate(idd); - - /* Disable/clear all interrupts. Need to do this here lest - * one submodule request the shared IOC4 IRQ, but interrupt - * is generated by a different subdevice. - */ - /* Disable */ - writel(~0, &idd->idd_misc_regs->other_iec.raw); - writel(~0, &idd->idd_misc_regs->sio_iec); - /* Clear (i.e. acknowledge) */ - writel(~0, &idd->idd_misc_regs->other_ir.raw); - writel(~0, &idd->idd_misc_regs->sio_ir); - - /* Track PCI-device specific data */ - idd->idd_serial_data = NULL; - pci_set_drvdata(idd->idd_pdev, idd); - - mutex_lock(&ioc4_mutex); - list_add_tail(&idd->idd_list, &ioc4_devices); - - /* Add this IOC4 to all submodules */ - list_for_each_entry(is, &ioc4_submodules, is_list) { - if (is->is_probe && is->is_probe(idd)) { - printk(KERN_WARNING - "%s: IOC4 submodule 0x%s probe failed " - "for pci_dev %s.\n", - __func__, module_name(is->is_owner), - pci_name(idd->idd_pdev)); - } - } - mutex_unlock(&ioc4_mutex); - - /* Request sgiioc4 IDE driver on boards that bring that functionality - * off of IOC4. The root filesystem may be hosted on a drive connected - * to IOC4, so we need to make sure the sgiioc4 driver is loaded as it - * won't be picked up by modprobes due to the ioc4 module owning the - * PCI device. - */ - if (idd->idd_variant != IOC4_VARIANT_PCI_RT) { - /* Request the module from a work procedure as the modprobe - * goes out to a userland helper and that will hang if done - * directly from ioc4_probe(). - */ - printk(KERN_INFO "IOC4 loading sgiioc4 submodule\n"); - schedule_work(&ioc4_load_modules_work); - } - - return 0; - -out_misc_region: - release_mem_region(idd->idd_bar0, sizeof(struct ioc4_misc_regs)); -out_pci: - kfree(idd); -out_idd: - pci_disable_device(pdev); -out: - return ret; -} - -/* Removes a particular instance of an IOC4 card. */ -static void -ioc4_remove(struct pci_dev *pdev) -{ - struct ioc4_submodule *is; - struct ioc4_driver_data *idd; - - idd = pci_get_drvdata(pdev); - - /* Remove this IOC4 from all submodules */ - mutex_lock(&ioc4_mutex); - list_for_each_entry(is, &ioc4_submodules, is_list) { - if (is->is_remove && is->is_remove(idd)) { - printk(KERN_WARNING - "%s: IOC4 submodule 0x%s remove failed " - "for pci_dev %s.\n", - __func__, module_name(is->is_owner), - pci_name(idd->idd_pdev)); - } - } - mutex_unlock(&ioc4_mutex); - - /* Release resources */ - iounmap(idd->idd_misc_regs); - if (!idd->idd_bar0) { - printk(KERN_WARNING - "%s: Unable to get IOC4 misc mapping for pci_dev %s. " - "Device removal may be incomplete.\n", - __func__, pci_name(idd->idd_pdev)); - } - release_mem_region(idd->idd_bar0, sizeof(struct ioc4_misc_regs)); - - /* Disable IOC4 and relinquish */ - pci_disable_device(pdev); - - /* Remove and free driver data */ - mutex_lock(&ioc4_mutex); - list_del(&idd->idd_list); - mutex_unlock(&ioc4_mutex); - kfree(idd); -} - -static const struct pci_device_id ioc4_id_table[] = { - {PCI_VENDOR_ID_SGI, PCI_DEVICE_ID_SGI_IOC4, PCI_ANY_ID, - PCI_ANY_ID, 0x0b4000, 0xFFFFFF}, - {0} -}; - -static struct pci_driver ioc4_driver = { - .name = "IOC4", - .id_table = ioc4_id_table, - .probe = ioc4_probe, - .remove = ioc4_remove, -}; - -MODULE_DEVICE_TABLE(pci, ioc4_id_table); - -/********************* - * Module management * - *********************/ - -/* Module load */ -static int __init -ioc4_init(void) -{ - return pci_register_driver(&ioc4_driver); -} - -/* Module unload */ -static void __exit -ioc4_exit(void) -{ - /* Ensure ioc4_load_modules() has completed before exiting */ - flush_work(&ioc4_load_modules_work); - pci_unregister_driver(&ioc4_driver); -} - -module_init(ioc4_init); -module_exit(ioc4_exit); - -MODULE_AUTHOR("Brent Casavant - Silicon Graphics, Inc. <bcasavan@sgi.com>"); -MODULE_DESCRIPTION("PCI driver master module for SGI IOC4 Base-IO Card"); -MODULE_LICENSE("GPL"); - -EXPORT_SYMBOL(ioc4_register_submodule); -EXPORT_SYMBOL(ioc4_unregister_submodule); diff --git a/drivers/misc/sgi-xp/Makefile b/drivers/misc/sgi-xp/Makefile index bbb622c19c06..34c55a4045af 100644 --- a/drivers/misc/sgi-xp/Makefile +++ b/drivers/misc/sgi-xp/Makefile @@ -4,17 +4,10 @@ # obj-$(CONFIG_SGI_XP) += xp.o -xp-y := xp_main.o -xp-$(CONFIG_IA64_SGI_SN2) += xp_sn2.o xp_nofault.o -xp-$(CONFIG_IA64_GENERIC) += xp_sn2.o xp_nofault.o -xp-$(CONFIG_IA64_SGI_UV) += xp_uv.o -xp-$(CONFIG_X86_64) += xp_uv.o +xp-y := xp_main.o xp_uv.o obj-$(CONFIG_SGI_XP) += xpc.o -xpc-y := xpc_main.o xpc_channel.o xpc_partition.o -xpc-$(CONFIG_IA64_SGI_SN2) += xpc_sn2.o -xpc-$(CONFIG_IA64_GENERIC) += xpc_sn2.o -xpc-$(CONFIG_IA64_SGI_UV) += xpc_uv.o -xpc-$(CONFIG_X86_64) += xpc_uv.o +xpc-y := xpc_main.o xpc_channel.o xpc_partition.o \ + xpc_uv.o obj-$(CONFIG_SGI_XP) += xpnet.o diff --git a/drivers/misc/sgi-xp/xp.h b/drivers/misc/sgi-xp/xp.h index b8069eec18cb..06469b12aced 100644 --- a/drivers/misc/sgi-xp/xp.h +++ b/drivers/misc/sgi-xp/xp.h @@ -24,23 +24,6 @@ #define is_uv() 0 #endif -#if defined CONFIG_IA64 -#include <asm/sn/arch.h> /* defines is_shub1() and is_shub2() */ -#define is_shub() ia64_platform_is("sn2") -#endif - -#ifndef is_shub1 -#define is_shub1() 0 -#endif - -#ifndef is_shub2 -#define is_shub2() 0 -#endif - -#ifndef is_shub -#define is_shub() 0 -#endif - #ifdef USE_DBUG_ON #define DBUG_ON(condition) BUG_ON(condition) #else @@ -360,9 +343,7 @@ extern int xp_nofault_PIOR(void *); extern int xp_error_PIOR(void); extern struct device *xp; -extern enum xp_retval xp_init_sn2(void); extern enum xp_retval xp_init_uv(void); -extern void xp_exit_sn2(void); extern void xp_exit_uv(void); #endif /* _DRIVERS_MISC_SGIXP_XP_H */ diff --git a/drivers/misc/sgi-xp/xp_main.c b/drivers/misc/sgi-xp/xp_main.c index 6d7f557fd1c1..5fd94d836070 100644 --- a/drivers/misc/sgi-xp/xp_main.c +++ b/drivers/misc/sgi-xp/xp_main.c @@ -233,9 +233,7 @@ xp_init(void) for (ch_number = 0; ch_number < XPC_MAX_NCHANNELS; ch_number++) mutex_init(&xpc_registrations[ch_number].mutex); - if (is_shub()) - ret = xp_init_sn2(); - else if (is_uv()) + if (is_uv()) ret = xp_init_uv(); else ret = 0; @@ -251,9 +249,7 @@ module_init(xp_init); void __exit xp_exit(void) { - if (is_shub()) - xp_exit_sn2(); - else if (is_uv()) + if (is_uv()) xp_exit_uv(); } diff --git a/drivers/misc/sgi-xp/xp_nofault.S b/drivers/misc/sgi-xp/xp_nofault.S deleted file mode 100644 index e38d43319429..000000000000 --- a/drivers/misc/sgi-xp/xp_nofault.S +++ /dev/null @@ -1,35 +0,0 @@ -/* - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - * - * Copyright (c) 2004-2008 Silicon Graphics, Inc. All Rights Reserved. - */ - -/* - * The xp_nofault_PIOR function takes a pointer to a remote PIO register - * and attempts to load and consume a value from it. This function - * will be registered as a nofault code block. In the event that the - * PIO read fails, the MCA handler will force the error to look - * corrected and vector to the xp_error_PIOR which will return an error. - * - * The definition of "consumption" and the time it takes for an MCA - * to surface is processor implementation specific. This code - * is sufficient on Itanium through the Montvale processor family. - * It may need to be adjusted for future processor implementations. - * - * extern int xp_nofault_PIOR(void *remote_register); - */ - - .global xp_nofault_PIOR -xp_nofault_PIOR: - mov r8=r0 // Stage a success return value - ld8.acq r9=[r32];; // PIO Read the specified register - adds r9=1,r9;; // Add to force consumption - srlz.i;; // Allow time for MCA to surface - br.ret.sptk.many b0;; // Return success - - .global xp_error_PIOR -xp_error_PIOR: - mov r8=1 // Return value of 1 - br.ret.sptk.many b0;; // Return failure diff --git a/drivers/misc/sgi-xp/xp_sn2.c b/drivers/misc/sgi-xp/xp_sn2.c deleted file mode 100644 index d8e463f87241..000000000000 --- a/drivers/misc/sgi-xp/xp_sn2.c +++ /dev/null @@ -1,190 +0,0 @@ -/* - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - * - * Copyright (c) 2008 Silicon Graphics, Inc. All Rights Reserved. - */ - -/* - * Cross Partition (XP) sn2-based functions. - * - * Architecture specific implementation of common functions. - */ - -#include <linux/module.h> -#include <linux/device.h> -#include <asm/sn/bte.h> -#include <asm/sn/sn_sal.h> -#include "xp.h" - -/* - * The export of xp_nofault_PIOR needs to happen here since it is defined - * in drivers/misc/sgi-xp/xp_nofault.S. The target of the nofault read is - * defined here. - */ -EXPORT_SYMBOL_GPL(xp_nofault_PIOR); - -u64 xp_nofault_PIOR_target; -EXPORT_SYMBOL_GPL(xp_nofault_PIOR_target); - -/* - * Register a nofault code region which performs a cross-partition PIO read. - * If the PIO read times out, the MCA handler will consume the error and - * return to a kernel-provided instruction to indicate an error. This PIO read - * exists because it is guaranteed to timeout if the destination is down - * (amo operations do not timeout on at least some CPUs on Shubs <= v1.2, - * which unfortunately we have to work around). - */ -static enum xp_retval -xp_register_nofault_code_sn2(void) -{ - int ret; - u64 func_addr; - u64 err_func_addr; - - func_addr = *(u64 *)xp_nofault_PIOR; - err_func_addr = *(u64 *)xp_error_PIOR; - ret = sn_register_nofault_code(func_addr, err_func_addr, err_func_addr, - 1, 1); - if (ret != 0) { - dev_err(xp, "can't register nofault code, error=%d\n", ret); - return xpSalError; - } - /* - * Setup the nofault PIO read target. (There is no special reason why - * SH_IPI_ACCESS was selected.) - */ - if (is_shub1()) - xp_nofault_PIOR_target = SH1_IPI_ACCESS; - else if (is_shub2()) - xp_nofault_PIOR_target = SH2_IPI_ACCESS0; - - return xpSuccess; -} - -static void -xp_unregister_nofault_code_sn2(void) -{ - u64 func_addr = *(u64 *)xp_nofault_PIOR; - u64 err_func_addr = *(u64 *)xp_error_PIOR; - - /* unregister the PIO read nofault code region */ - (void)sn_register_nofault_code(func_addr, err_func_addr, - err_func_addr, 1, 0); -} - -/* - * Convert a virtual memory address to a physical memory address. - */ -static unsigned long -xp_pa_sn2(void *addr) -{ - return __pa(addr); -} - -/* - * Convert a global physical to a socket physical address. - */ -static unsigned long -xp_socket_pa_sn2(unsigned long gpa) -{ - return gpa; -} - -/* - * Wrapper for bte_copy(). - * - * dst_pa - physical address of the destination of the transfer. - * src_pa - physical address of the source of the transfer. - * len - number of bytes to transfer from source to destination. - * - * Note: xp_remote_memcpy_sn2() should never be called while holding a spinlock. - */ -static enum xp_retval -xp_remote_memcpy_sn2(unsigned long dst_pa, const unsigned long src_pa, - size_t len) -{ - bte_result_t ret; - - ret = bte_copy(src_pa, dst_pa, len, (BTE_NOTIFY | BTE_WACQUIRE), NULL); - if (ret == BTE_SUCCESS) - return xpSuccess; - - if (is_shub2()) { - dev_err(xp, "bte_copy() on shub2 failed, error=0x%x dst_pa=" - "0x%016lx src_pa=0x%016lx len=%ld\\n", ret, dst_pa, - src_pa, len); - } else { - dev_err(xp, "bte_copy() failed, error=%d dst_pa=0x%016lx " - "src_pa=0x%016lx len=%ld\\n", ret, dst_pa, src_pa, len); - } - - return xpBteCopyError; -} - -static int -xp_cpu_to_nasid_sn2(int cpuid) -{ - return cpuid_to_nasid(cpuid); -} - -static enum xp_retval -xp_expand_memprotect_sn2(unsigned long phys_addr, unsigned long size) -{ - u64 nasid_array = 0; - int ret; - - ret = sn_change_memprotect(phys_addr, size, SN_MEMPROT_ACCESS_CLASS_1, - &nasid_array); - if (ret != 0) { - dev_err(xp, "sn_change_memprotect(,, " - "SN_MEMPROT_ACCESS_CLASS_1,) failed ret=%d\n", ret); - return xpSalError; - } - return xpSuccess; -} - -static enum xp_retval -xp_restrict_memprotect_sn2(unsigned long phys_addr, unsigned long size) -{ - u64 nasid_array = 0; - int ret; - - ret = sn_change_memprotect(phys_addr, size, SN_MEMPROT_ACCESS_CLASS_0, - &nasid_array); - if (ret != 0) { - dev_err(xp, "sn_change_memprotect(,, " - "SN_MEMPROT_ACCESS_CLASS_0,) failed ret=%d\n", ret); - return xpSalError; - } - return xpSuccess; -} - -enum xp_retval -xp_init_sn2(void) -{ - BUG_ON(!is_shub()); - - xp_max_npartitions = XP_MAX_NPARTITIONS_SN2; - xp_partition_id = sn_partition_id; - xp_region_size = sn_region_size; - - xp_pa = xp_pa_sn2; - xp_socket_pa = xp_socket_pa_sn2; - xp_remote_memcpy = xp_remote_memcpy_sn2; - xp_cpu_to_nasid = xp_cpu_to_nasid_sn2; - xp_expand_memprotect = xp_expand_memprotect_sn2; - xp_restrict_memprotect = xp_restrict_memprotect_sn2; - - return xp_register_nofault_code_sn2(); -} - -void -xp_exit_sn2(void) -{ - BUG_ON(!is_shub()); - - xp_unregister_nofault_code_sn2(); -} - diff --git a/drivers/misc/sgi-xp/xp_uv.c b/drivers/misc/sgi-xp/xp_uv.c index a0d093274dc0..f15a9f2ac1dd 100644 --- a/drivers/misc/sgi-xp/xp_uv.c +++ b/drivers/misc/sgi-xp/xp_uv.c @@ -17,7 +17,7 @@ #include <asm/uv/uv_hub.h> #if defined CONFIG_X86_64 #include <asm/uv/bios.h> -#elif defined CONFIG_IA64_GENERIC || defined CONFIG_IA64_SGI_UV +#elif defined CONFIG_IA64_SGI_UV #include <asm/sn/sn_sal.h> #endif #include "../sgi-gru/grukservices.h" @@ -99,7 +99,7 @@ xp_expand_memprotect_uv(unsigned long phys_addr, unsigned long size) return xpBiosError; } -#elif defined CONFIG_IA64_GENERIC || defined CONFIG_IA64_SGI_UV +#elif defined CONFIG_IA64_SGI_UV u64 nasid_array; ret = sn_change_memprotect(phys_addr, size, SN_MEMPROT_ACCESS_CLASS_1, @@ -129,7 +129,7 @@ xp_restrict_memprotect_uv(unsigned long phys_addr, unsigned long size) return xpBiosError; } -#elif defined CONFIG_IA64_GENERIC || defined CONFIG_IA64_SGI_UV +#elif defined CONFIG_IA64_SGI_UV u64 nasid_array; ret = sn_change_memprotect(phys_addr, size, SN_MEMPROT_ACCESS_CLASS_0, @@ -151,9 +151,10 @@ xp_init_uv(void) BUG_ON(!is_uv()); xp_max_npartitions = XP_MAX_NPARTITIONS_UV; +#ifdef CONFIG_X86 xp_partition_id = sn_partition_id; xp_region_size = sn_region_size; - +#endif xp_pa = xp_pa_uv; xp_socket_pa = xp_socket_pa_uv; xp_remote_memcpy = xp_remote_memcpy_uv; diff --git a/drivers/misc/sgi-xp/xpc.h b/drivers/misc/sgi-xp/xpc.h index b94d5f767703..71db60edff65 100644 --- a/drivers/misc/sgi-xp/xpc.h +++ b/drivers/misc/sgi-xp/xpc.h @@ -71,14 +71,10 @@ * 'SAL_nasids_size'. (Local partition's mask pointers are xpc_part_nasids * and xpc_mach_nasids.) * - * vars (ia64-sn2 only) - * vars part (ia64-sn2 only) - * * Immediately following the mach_nasids mask are the XPC variables * required by other partitions. First are those that are generic to all * partitions (vars), followed on the next available cacheline by those * which are partition specific (vars part). These are setup by XPC. - * (Local partition's vars pointers are xpc_vars and xpc_vars_part.) * * Note: Until 'ts_jiffies' is set non-zero, the partition XPC code has not been * initialized. @@ -93,9 +89,6 @@ struct xpc_rsvd_page { unsigned long ts_jiffies; /* timestamp when rsvd pg was setup by XPC */ union { struct { - unsigned long vars_pa; /* phys addr */ - } sn2; - struct { unsigned long heartbeat_gpa; /* phys addr */ unsigned long activate_gru_mq_desc_gpa; /* phys addr */ } uv; @@ -106,84 +99,14 @@ struct xpc_rsvd_page { #define XPC_RP_VERSION _XPC_VERSION(3, 0) /* version 3.0 of the reserved page */ -/* - * Define the structures by which XPC variables can be exported to other - * partitions. (There are two: struct xpc_vars and struct xpc_vars_part) - */ - -/* - * The following structure describes the partition generic variables - * needed by other partitions in order to properly initialize. - * - * struct xpc_vars version number also applies to struct xpc_vars_part. - * Changes to either structure and/or related functionality should be - * reflected by incrementing either the major or minor version numbers - * of struct xpc_vars. - */ -struct xpc_vars_sn2 { - u8 version; - u64 heartbeat; - DECLARE_BITMAP(heartbeating_to_mask, XP_MAX_NPARTITIONS_SN2); - u64 heartbeat_offline; /* if 0, heartbeat should be changing */ - int activate_IRQ_nasid; - int activate_IRQ_phys_cpuid; - unsigned long vars_part_pa; - unsigned long amos_page_pa;/* paddr of page of amos from MSPEC driver */ - struct amo *amos_page; /* vaddr of page of amos from MSPEC driver */ -}; - -#define XPC_V_VERSION _XPC_VERSION(3, 1) /* version 3.1 of the cross vars */ - -/* - * The following structure describes the per partition specific variables. - * - * An array of these structures, one per partition, will be defined. As a - * partition becomes active XPC will copy the array entry corresponding to - * itself from that partition. It is desirable that the size of this structure - * evenly divides into a 128-byte cacheline, such that none of the entries in - * this array crosses a 128-byte cacheline boundary. As it is now, each entry - * occupies 64-bytes. - */ -struct xpc_vars_part_sn2 { - u64 magic; - - unsigned long openclose_args_pa; /* phys addr of open and close args */ - unsigned long GPs_pa; /* physical address of Get/Put values */ - - unsigned long chctl_amo_pa; /* physical address of chctl flags' amo */ - - int notify_IRQ_nasid; /* nasid of where to send notify IRQs */ - int notify_IRQ_phys_cpuid; /* CPUID of where to send notify IRQs */ - - u8 nchannels; /* #of defined channels supported */ - - u8 reserved[23]; /* pad to a full 64 bytes */ -}; - -/* - * The vars_part MAGIC numbers play a part in the first contact protocol. - * - * MAGIC1 indicates that the per partition specific variables for a remote - * partition have been initialized by this partition. - * - * MAGIC2 indicates that this partition has pulled the remote partititions - * per partition variables that pertain to this partition. - */ -#define XPC_VP_MAGIC1_SN2 0x0053524156435058L /* 'XPCVARS\0'L (little endian) */ -#define XPC_VP_MAGIC2_SN2 0x0073726176435058L /* 'XPCvars\0'L (little endian) */ - /* the reserved page sizes and offsets */ #define XPC_RP_HEADER_SIZE L1_CACHE_ALIGN(sizeof(struct xpc_rsvd_page)) -#define XPC_RP_VARS_SIZE L1_CACHE_ALIGN(sizeof(struct xpc_vars_sn2)) #define XPC_RP_PART_NASIDS(_rp) ((unsigned long *)((u8 *)(_rp) + \ XPC_RP_HEADER_SIZE)) #define XPC_RP_MACH_NASIDS(_rp) (XPC_RP_PART_NASIDS(_rp) + \ xpc_nasid_mask_nlongs) -#define XPC_RP_VARS(_rp) ((struct xpc_vars_sn2 *) \ - (XPC_RP_MACH_NASIDS(_rp) + \ - xpc_nasid_mask_nlongs)) /* @@ -298,17 +221,6 @@ struct xpc_activate_mq_msg_chctl_opencomplete_uv { #define XPC_UNPACK_ARG2(_args) ((((u64)_args) >> 32) & 0xffffffff) /* - * Define a Get/Put value pair (pointers) used with a message queue. - */ -struct xpc_gp_sn2 { - s64 get; /* Get value */ - s64 put; /* Put value */ -}; - -#define XPC_GP_SIZE \ - L1_CACHE_ALIGN(sizeof(struct xpc_gp_sn2) * XPC_MAX_NCHANNELS) - -/* * Define a structure that contains arguments associated with opening and * closing a channel. */ @@ -341,30 +253,6 @@ struct xpc_fifo_head_uv { }; /* - * Define a sn2 styled message. - * - * A user-defined message resides in the payload area. The max size of the - * payload is defined by the user via xpc_connect(). - * - * The size of a message entry (within a message queue) must be a 128-byte - * cacheline sized multiple in order to facilitate the BTE transfer of messages - * from one message queue to another. - */ -struct xpc_msg_sn2 { - u8 flags; /* FOR XPC INTERNAL USE ONLY */ - u8 reserved[7]; /* FOR XPC INTERNAL USE ONLY */ - s64 number; /* FOR XPC INTERNAL USE ONLY */ - - u64 payload; /* user defined portion of message */ -}; - -/* struct xpc_msg_sn2 flags */ - -#define XPC_M_SN2_DONE 0x01 /* msg has been received/consumed */ -#define XPC_M_SN2_READY 0x02 /* msg is ready to be sent */ -#define XPC_M_SN2_INTERRUPT 0x04 /* send interrupt when msg consumed */ - -/* * The format of a uv XPC notify_mq GRU message is as follows: * * A user-defined message resides in the payload area. The max size of the @@ -390,20 +278,6 @@ struct xpc_notify_mq_msg_uv { unsigned long payload; }; -/* - * Define sn2's notify entry. - * - * This is used to notify a message's sender that their message was received - * and consumed by the intended recipient. - */ -struct xpc_notify_sn2 { - u8 type; /* type of notification */ - - /* the following two fields are only used if type == XPC_N_CALL */ - xpc_notify_func func; /* user's notify function */ - void *key; /* pointer to user's key */ -}; - /* struct xpc_notify_sn2 type of notification */ #define XPC_N_CALL 0x01 /* notify function provided by user */ @@ -431,102 +305,6 @@ struct xpc_send_msg_slot_uv { * of these structures for each potential channel connection to that partition. */ -/* - * The following is sn2 only. - * - * Each channel structure manages two message queues (circular buffers). - * They are allocated at the time a channel connection is made. One of - * these message queues (local_msgqueue) holds the locally created messages - * that are destined for the remote partition. The other of these message - * queues (remote_msgqueue) is a locally cached copy of the remote partition's - * own local_msgqueue. - * - * The following is a description of the Get/Put pointers used to manage these - * two message queues. Consider the local_msgqueue to be on one partition - * and the remote_msgqueue to be its cached copy on another partition. A - * description of what each of the lettered areas contains is included. - * - * - * local_msgqueue remote_msgqueue - * - * |/////////| |/////////| - * w_remote_GP.get --> +---------+ |/////////| - * | F | |/////////| - * remote_GP.get --> +---------+ +---------+ <-- local_GP->get - * | | | | - * | | | E | - * | | | | - * | | +---------+ <-- w_local_GP.get - * | B | |/////////| - * | | |////D////| - * | | |/////////| - * | | +---------+ <-- w_remote_GP.put - * | | |////C////| - * local_GP->put --> +---------+ +---------+ <-- remote_GP.put - * | | |/////////| - * | A | |/////////| - * | | |/////////| - * w_local_GP.put --> +---------+ |/////////| - * |/////////| |/////////| - * - * - * ( remote_GP.[get|put] are cached copies of the remote - * partition's local_GP->[get|put], and thus their values can - * lag behind their counterparts on the remote partition. ) - * - * - * A - Messages that have been allocated, but have not yet been sent to the - * remote partition. - * - * B - Messages that have been sent, but have not yet been acknowledged by the - * remote partition as having been received. - * - * C - Area that needs to be prepared for the copying of sent messages, by - * the clearing of the message flags of any previously received messages. - * - * D - Area into which sent messages are to be copied from the remote - * partition's local_msgqueue and then delivered to their intended - * recipients. [ To allow for a multi-message copy, another pointer - * (next_msg_to_pull) has been added to keep track of the next message - * number needing to be copied (pulled). It chases after w_remote_GP.put. - * Any messages lying between w_local_GP.get and next_msg_to_pull have - * been copied and are ready to be delivered. ] - * - * E - Messages that have been copied and delivered, but have not yet been - * acknowledged by the recipient as having been received. - * - * F - Messages that have been acknowledged, but XPC has not yet notified the - * sender that the message was received by its intended recipient. - * This is also an area that needs to be prepared for the allocating of - * new messages, by the clearing of the message flags of the acknowledged - * messages. - */ - -struct xpc_channel_sn2 { - struct xpc_openclose_args *local_openclose_args; /* args passed on */ - /* opening or closing of channel */ - - void *local_msgqueue_base; /* base address of kmalloc'd space */ - struct xpc_msg_sn2 *local_msgqueue; /* local message queue */ - void *remote_msgqueue_base; /* base address of kmalloc'd space */ - struct xpc_msg_sn2 *remote_msgqueue; /* cached copy of remote */ - /* partition's local message queue */ - unsigned long remote_msgqueue_pa; /* phys addr of remote partition's */ - /* local message queue */ - - struct xpc_notify_sn2 *notify_queue;/* notify queue for messages sent */ - - /* various flavors of local and remote Get/Put values */ - - struct xpc_gp_sn2 *local_GP; /* local Get/Put values */ - struct xpc_gp_sn2 remote_GP; /* remote Get/Put values */ - struct xpc_gp_sn2 w_local_GP; /* working local Get/Put values */ - struct xpc_gp_sn2 w_remote_GP; /* working remote Get/Put values */ - s64 next_msg_to_pull; /* Put value of next msg to pull */ - - struct mutex msg_to_pull_mutex; /* next msg to pull serialization */ -}; - struct xpc_channel_uv { void *cached_notify_gru_mq_desc; /* remote partition's notify mq's */ /* gru mq descriptor */ @@ -579,7 +357,6 @@ struct xpc_channel { wait_queue_head_t idle_wq; /* idle kthread wait queue */ union { - struct xpc_channel_sn2 sn2; struct xpc_channel_uv uv; } sn; @@ -666,43 +443,6 @@ xpc_any_msg_chctl_flags_set(union xpc_channel_ctl_flags *chctl) return 0; } -/* - * Manage channels on a partition basis. There is one of these structures - * for each partition (a partition will never utilize the structure that - * represents itself). - */ - -struct xpc_partition_sn2 { - unsigned long remote_amos_page_pa; /* paddr of partition's amos page */ - int activate_IRQ_nasid; /* active partition's act/deact nasid */ - int activate_IRQ_phys_cpuid; /* active part's act/deact phys cpuid */ - - unsigned long remote_vars_pa; /* phys addr of partition's vars */ - unsigned long remote_vars_part_pa; /* paddr of partition's vars part */ - u8 remote_vars_version; /* version# of partition's vars */ - - void *local_GPs_base; /* base address of kmalloc'd space */ - struct xpc_gp_sn2 *local_GPs; /* local Get/Put values */ - void *remote_GPs_base; /* base address of kmalloc'd space */ - struct xpc_gp_sn2 *remote_GPs; /* copy of remote partition's local */ - /* Get/Put values */ - unsigned long remote_GPs_pa; /* phys addr of remote partition's local */ - /* Get/Put values */ - - void *local_openclose_args_base; /* base address of kmalloc'd space */ - struct xpc_openclose_args *local_openclose_args; /* local's args */ - unsigned long remote_openclose_args_pa; /* phys addr of remote's args */ - - int notify_IRQ_nasid; /* nasid of where to send notify IRQs */ - int notify_IRQ_phys_cpuid; /* CPUID of where to send notify IRQs */ - char notify_IRQ_owner[8]; /* notify IRQ's owner's name */ - - struct amo *remote_chctl_amo_va; /* addr of remote chctl flags' amo */ - struct amo *local_chctl_amo_va; /* address of chctl flags' amo */ - - struct timer_list dropped_notify_IRQ_timer; /* dropped IRQ timer */ -}; - struct xpc_partition_uv { unsigned long heartbeat_gpa; /* phys addr of partition's heartbeat */ struct xpc_heartbeat_uv cached_heartbeat; /* cached copy of */ @@ -774,7 +514,6 @@ struct xpc_partition { wait_queue_head_t channel_mgr_wq; /* channel mgr's wait queue */ union { - struct xpc_partition_sn2 sn2; struct xpc_partition_uv uv; } sn; @@ -854,14 +593,6 @@ struct xpc_arch_operations { #define XPC_P_SS_WTEARDOWN 0x02 /* waiting to teardown infrastructure */ #define XPC_P_SS_TORNDOWN 0x03 /* infrastructure is torndown */ -/* - * struct xpc_partition_sn2's dropped notify IRQ timer is set to wait the - * following interval #of seconds before checking for dropped notify IRQs. - * These can occur whenever an IRQ's associated amo write doesn't complete - * until after the IRQ was received. - */ -#define XPC_DROPPED_NOTIFY_IRQ_WAIT_INTERVAL (0.25 * HZ) - /* number of seconds to wait for other partitions to disengage */ #define XPC_DISENGAGE_DEFAULT_TIMELIMIT 90 @@ -888,10 +619,6 @@ extern void xpc_activate_kthreads(struct xpc_channel *, int); extern void xpc_create_kthreads(struct xpc_channel *, int, int); extern void xpc_disconnect_wait(int); -/* found in xpc_sn2.c */ -extern int xpc_init_sn2(void); -extern void xpc_exit_sn2(void); - /* found in xpc_uv.c */ extern int xpc_init_uv(void); extern void xpc_exit_uv(void); diff --git a/drivers/misc/sgi-xp/xpc_main.c b/drivers/misc/sgi-xp/xpc_main.c index 83fc748a91a7..79a963105983 100644 --- a/drivers/misc/sgi-xp/xpc_main.c +++ b/drivers/misc/sgi-xp/xpc_main.c @@ -279,13 +279,6 @@ xpc_hb_checker(void *ignore) dev_dbg(xpc_part, "checking remote heartbeats\n"); xpc_check_remote_hb(); - - /* - * On sn2 we need to periodically recheck to ensure no - * IRQ/amo pairs have been missed. - */ - if (is_shub()) - force_IRQ = 1; } /* check for outstanding IRQs */ @@ -1050,9 +1043,7 @@ xpc_do_exit(enum xp_retval reason) xpc_teardown_partitions(); - if (is_shub()) - xpc_exit_sn2(); - else if (is_uv()) + if (is_uv()) xpc_exit_uv(); } @@ -1235,21 +1226,7 @@ xpc_init(void) dev_set_name(xpc_part, "part"); dev_set_name(xpc_chan, "chan"); - if (is_shub()) { - /* - * The ia64-sn2 architecture supports at most 64 partitions. - * And the inability to unregister remote amos restricts us - * further to only support exactly 64 partitions on this - * architecture, no less. - */ - if (xp_max_npartitions != 64) { - dev_err(xpc_part, "max #of partitions not set to 64\n"); - ret = -EINVAL; - } else { - ret = xpc_init_sn2(); - } - - } else if (is_uv()) { + if (is_uv()) { ret = xpc_init_uv(); } else { @@ -1335,9 +1312,7 @@ out_2: xpc_teardown_partitions(); out_1: - if (is_shub()) - xpc_exit_sn2(); - else if (is_uv()) + if (is_uv()) xpc_exit_uv(); return ret; } diff --git a/drivers/misc/sgi-xp/xpc_partition.c b/drivers/misc/sgi-xp/xpc_partition.c index 782ce95d3f17..21a04bc97d40 100644 --- a/drivers/misc/sgi-xp/xpc_partition.c +++ b/drivers/misc/sgi-xp/xpc_partition.c @@ -93,10 +93,6 @@ xpc_get_rsvd_page_pa(int nasid) if (ret != xpNeedMoreInfo) break; - /* !!! L1_CACHE_ALIGN() is only a sn2-bte_copy requirement */ - if (is_shub()) - len = L1_CACHE_ALIGN(len); - if (len > buf_len) { kfree(buf_base); buf_len = L1_CACHE_ALIGN(len); @@ -452,7 +448,6 @@ xpc_discovery(void) case 32: max_regions *= 2; region_size = 16; - DBUG_ON(!is_shub2()); } } diff --git a/drivers/misc/sgi-xp/xpc_sn2.c b/drivers/misc/sgi-xp/xpc_sn2.c deleted file mode 100644 index 0ae69b9390ce..000000000000 --- a/drivers/misc/sgi-xp/xpc_sn2.c +++ /dev/null @@ -1,2459 +0,0 @@ -/* - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - * - * Copyright (c) 2008-2009 Silicon Graphics, Inc. All Rights Reserved. - */ - -/* - * Cross Partition Communication (XPC) sn2-based functions. - * - * Architecture specific implementation of common functions. - * - */ - -#include <linux/delay.h> -#include <linux/slab.h> -#include <asm/uncached.h> -#include <asm/sn/mspec.h> -#include <asm/sn/sn_sal.h> -#include "xpc.h" - -/* - * Define the number of u64s required to represent all the C-brick nasids - * as a bitmap. The cross-partition kernel modules deal only with - * C-brick nasids, thus the need for bitmaps which don't account for - * odd-numbered (non C-brick) nasids. - */ -#define XPC_MAX_PHYSNODES_SN2 (MAX_NUMALINK_NODES / 2) -#define XP_NASID_MASK_BYTES_SN2 ((XPC_MAX_PHYSNODES_SN2 + 7) / 8) -#define XP_NASID_MASK_WORDS_SN2 ((XPC_MAX_PHYSNODES_SN2 + 63) / 64) - -/* - * Memory for XPC's amo variables is allocated by the MSPEC driver. These - * pages are located in the lowest granule. The lowest granule uses 4k pages - * for cached references and an alternate TLB handler to never provide a - * cacheable mapping for the entire region. This will prevent speculative - * reading of cached copies of our lines from being issued which will cause - * a PI FSB Protocol error to be generated by the SHUB. For XPC, we need 64 - * amo variables (based on XP_MAX_NPARTITIONS_SN2) to identify the senders of - * NOTIFY IRQs, 128 amo variables (based on XP_NASID_MASK_WORDS_SN2) to identify - * the senders of ACTIVATE IRQs, 1 amo variable to identify which remote - * partitions (i.e., XPCs) consider themselves currently engaged with the - * local XPC and 1 amo variable to request partition deactivation. - */ -#define XPC_NOTIFY_IRQ_AMOS_SN2 0 -#define XPC_ACTIVATE_IRQ_AMOS_SN2 (XPC_NOTIFY_IRQ_AMOS_SN2 + \ - XP_MAX_NPARTITIONS_SN2) -#define XPC_ENGAGED_PARTITIONS_AMO_SN2 (XPC_ACTIVATE_IRQ_AMOS_SN2 + \ - XP_NASID_MASK_WORDS_SN2) -#define XPC_DEACTIVATE_REQUEST_AMO_SN2 (XPC_ENGAGED_PARTITIONS_AMO_SN2 + 1) - -/* - * Buffer used to store a local copy of portions of a remote partition's - * reserved page (either its header and part_nasids mask, or its vars). - */ -static void *xpc_remote_copy_buffer_base_sn2; -static char *xpc_remote_copy_buffer_sn2; - -static struct xpc_vars_sn2 *xpc_vars_sn2; -static struct xpc_vars_part_sn2 *xpc_vars_part_sn2; - -static int -xpc_setup_partitions_sn2(void) -{ - /* nothing needs to be done */ - return 0; -} - -static void -xpc_teardown_partitions_sn2(void) -{ - /* nothing needs to be done */ -} - -/* SH_IPI_ACCESS shub register value on startup */ -static u64 xpc_sh1_IPI_access_sn2; -static u64 xpc_sh2_IPI_access0_sn2; -static u64 xpc_sh2_IPI_access1_sn2; -static u64 xpc_sh2_IPI_access2_sn2; -static u64 xpc_sh2_IPI_access3_sn2; - -/* - * Change protections to allow IPI operations. - */ -static void -xpc_allow_IPI_ops_sn2(void) -{ - int node; - int nasid; - - /* !!! The following should get moved into SAL. */ - if (is_shub2()) { - xpc_sh2_IPI_access0_sn2 = - (u64)HUB_L((u64 *)LOCAL_MMR_ADDR(SH2_IPI_ACCESS0)); - xpc_sh2_IPI_access1_sn2 = - (u64)HUB_L((u64 *)LOCAL_MMR_ADDR(SH2_IPI_ACCESS1)); - xpc_sh2_IPI_access2_sn2 = - (u64)HUB_L((u64 *)LOCAL_MMR_ADDR(SH2_IPI_ACCESS2)); - xpc_sh2_IPI_access3_sn2 = - (u64)HUB_L((u64 *)LOCAL_MMR_ADDR(SH2_IPI_ACCESS3)); - - for_each_online_node(node) { - nasid = cnodeid_to_nasid(node); - HUB_S((u64 *)GLOBAL_MMR_ADDR(nasid, SH2_IPI_ACCESS0), - -1UL); - HUB_S((u64 *)GLOBAL_MMR_ADDR(nasid, SH2_IPI_ACCESS1), - -1UL); - HUB_S((u64 *)GLOBAL_MMR_ADDR(nasid, SH2_IPI_ACCESS2), - -1UL); - HUB_S((u64 *)GLOBAL_MMR_ADDR(nasid, SH2_IPI_ACCESS3), - -1UL); - } - } else { - xpc_sh1_IPI_access_sn2 = - (u64)HUB_L((u64 *)LOCAL_MMR_ADDR(SH1_IPI_ACCESS)); - - for_each_online_node(node) { - nasid = cnodeid_to_nasid(node); - HUB_S((u64 *)GLOBAL_MMR_ADDR(nasid, SH1_IPI_ACCESS), - -1UL); - } - } -} - -/* - * Restrict protections to disallow IPI operations. - */ -static void -xpc_disallow_IPI_ops_sn2(void) -{ - int node; - int nasid; - - /* !!! The following should get moved into SAL. */ - if (is_shub2()) { - for_each_online_node(node) { - nasid = cnodeid_to_nasid(node); - HUB_S((u64 *)GLOBAL_MMR_ADDR(nasid, SH2_IPI_ACCESS0), - xpc_sh2_IPI_access0_sn2); - HUB_S((u64 *)GLOBAL_MMR_ADDR(nasid, SH2_IPI_ACCESS1), - xpc_sh2_IPI_access1_sn2); - HUB_S((u64 *)GLOBAL_MMR_ADDR(nasid, SH2_IPI_ACCESS2), - xpc_sh2_IPI_access2_sn2); - HUB_S((u64 *)GLOBAL_MMR_ADDR(nasid, SH2_IPI_ACCESS3), - xpc_sh2_IPI_access3_sn2); - } - } else { - for_each_online_node(node) { - nasid = cnodeid_to_nasid(node); - HUB_S((u64 *)GLOBAL_MMR_ADDR(nasid, SH1_IPI_ACCESS), - xpc_sh1_IPI_access_sn2); - } - } -} - -/* - * The following set of functions are used for the sending and receiving of - * IRQs (also known as IPIs). There are two flavors of IRQs, one that is - * associated with partition activity (SGI_XPC_ACTIVATE) and the other that - * is associated with channel activity (SGI_XPC_NOTIFY). - */ - -static u64 -xpc_receive_IRQ_amo_sn2(struct amo *amo) -{ - return FETCHOP_LOAD_OP(TO_AMO((u64)&amo->variable), FETCHOP_CLEAR); -} - -static enum xp_retval -xpc_send_IRQ_sn2(struct amo *amo, u64 flag, int nasid, int phys_cpuid, - int vector) -{ - int ret = 0; - unsigned long irq_flags; - - local_irq_save(irq_flags); - - FETCHOP_STORE_OP(TO_AMO((u64)&amo->variable), FETCHOP_OR, flag); - sn_send_IPI_phys(nasid, phys_cpuid, vector, 0); - - /* - * We must always use the nofault function regardless of whether we - * are on a Shub 1.1 system or a Shub 1.2 slice 0xc processor. If we - * didn't, we'd never know that the other partition is down and would - * keep sending IRQs and amos to it until the heartbeat times out. - */ - ret = xp_nofault_PIOR((u64 *)GLOBAL_MMR_ADDR(NASID_GET(&amo->variable), - xp_nofault_PIOR_target)); - - local_irq_restore(irq_flags); - - return (ret == 0) ? xpSuccess : xpPioReadError; -} - -static struct amo * -xpc_init_IRQ_amo_sn2(int index) -{ - struct amo *amo = xpc_vars_sn2->amos_page + index; - - (void)xpc_receive_IRQ_amo_sn2(amo); /* clear amo variable */ - return amo; -} - -/* - * Functions associated with SGI_XPC_ACTIVATE IRQ. - */ - -/* - * Notify the heartbeat check thread that an activate IRQ has been received. - */ -static irqreturn_t -xpc_handle_activate_IRQ_sn2(int irq, void *dev_id) -{ - unsigned long irq_flags; - - spin_lock_irqsave(&xpc_activate_IRQ_rcvd_lock, irq_flags); - xpc_activate_IRQ_rcvd++; - spin_unlock_irqrestore(&xpc_activate_IRQ_rcvd_lock, irq_flags); - - wake_up_interruptible(&xpc_activate_IRQ_wq); - return IRQ_HANDLED; -} - -/* - * Flag the appropriate amo variable and send an IRQ to the specified node. - */ -static void -xpc_send_activate_IRQ_sn2(unsigned long amos_page_pa, int from_nasid, - int to_nasid, int to_phys_cpuid) -{ - struct amo *amos = (struct amo *)__va(amos_page_pa + - (XPC_ACTIVATE_IRQ_AMOS_SN2 * - sizeof(struct amo))); - - (void)xpc_send_IRQ_sn2(&amos[BIT_WORD(from_nasid / 2)], - BIT_MASK(from_nasid / 2), to_nasid, - to_phys_cpuid, SGI_XPC_ACTIVATE); -} - -static void -xpc_send_local_activate_IRQ_sn2(int from_nasid) -{ - unsigned long irq_flags; - struct amo *amos = (struct amo *)__va(xpc_vars_sn2->amos_page_pa + - (XPC_ACTIVATE_IRQ_AMOS_SN2 * - sizeof(struct amo))); - - /* fake the sending and receipt of an activate IRQ from remote nasid */ - FETCHOP_STORE_OP(TO_AMO((u64)&amos[BIT_WORD(from_nasid / 2)].variable), - FETCHOP_OR, BIT_MASK(from_nasid / 2)); - - spin_lock_irqsave(&xpc_activate_IRQ_rcvd_lock, irq_flags); - xpc_activate_IRQ_rcvd++; - spin_unlock_irqrestore(&xpc_activate_IRQ_rcvd_lock, irq_flags); - - wake_up_interruptible(&xpc_activate_IRQ_wq); -} - -/* - * Functions associated with SGI_XPC_NOTIFY IRQ. - */ - -/* - * Check to see if any chctl flags were sent from the specified partition. - */ -static void -xpc_check_for_sent_chctl_flags_sn2(struct xpc_partition *part) -{ - union xpc_channel_ctl_flags chctl; - unsigned long irq_flags; - - chctl.all_flags = xpc_receive_IRQ_amo_sn2(part->sn.sn2. - local_chctl_amo_va); - if (chctl.all_flags == 0) - return; - - spin_lock_irqsave(&part->chctl_lock, irq_flags); - part->chctl.all_flags |= chctl.all_flags; - spin_unlock_irqrestore(&part->chctl_lock, irq_flags); - - dev_dbg(xpc_chan, "received notify IRQ from partid=%d, chctl.all_flags=" - "0x%llx\n", XPC_PARTID(part), chctl.all_flags); - - xpc_wakeup_channel_mgr(part); -} - -/* - * Handle the receipt of a SGI_XPC_NOTIFY IRQ by seeing whether the specified - * partition actually sent it. Since SGI_XPC_NOTIFY IRQs may be shared by more - * than one partition, we use an amo structure per partition to indicate - * whether a partition has sent an IRQ or not. If it has, then wake up the - * associated kthread to handle it. - * - * All SGI_XPC_NOTIFY IRQs received by XPC are the result of IRQs sent by XPC - * running on other partitions. - * - * Noteworthy Arguments: - * - * irq - Interrupt ReQuest number. NOT USED. - * - * dev_id - partid of IRQ's potential sender. - */ -static irqreturn_t -xpc_handle_notify_IRQ_sn2(int irq, void *dev_id) -{ - short partid = (short)(u64)dev_id; - struct xpc_partition *part = &xpc_partitions[partid]; - - DBUG_ON(partid < 0 || partid >= XP_MAX_NPARTITIONS_SN2); - - if (xpc_part_ref(part)) { - xpc_check_for_sent_chctl_flags_sn2(part); - - xpc_part_deref(part); - } - return IRQ_HANDLED; -} - -/* - * Check to see if xpc_handle_notify_IRQ_sn2() dropped any IRQs on the floor - * because the write to their associated amo variable completed after the IRQ - * was received. - */ -static void -xpc_check_for_dropped_notify_IRQ_sn2(struct timer_list *t) -{ - struct xpc_partition *part = - from_timer(part, t, sn.sn2.dropped_notify_IRQ_timer); - - if (xpc_part_ref(part)) { - xpc_check_for_sent_chctl_flags_sn2(part); - - t->expires = jiffies + XPC_DROPPED_NOTIFY_IRQ_WAIT_INTERVAL; - add_timer(t); - xpc_part_deref(part); - } -} - -/* - * Send a notify IRQ to the remote partition that is associated with the - * specified channel. - */ -static void -xpc_send_notify_IRQ_sn2(struct xpc_channel *ch, u8 chctl_flag, - char *chctl_flag_string, unsigned long *irq_flags) -{ - struct xpc_partition *part = &xpc_partitions[ch->partid]; - struct xpc_partition_sn2 *part_sn2 = &part->sn.sn2; - union xpc_channel_ctl_flags chctl = { 0 }; - enum xp_retval ret; - - if (likely(part->act_state != XPC_P_AS_DEACTIVATING)) { - chctl.flags[ch->number] = chctl_flag; - ret = xpc_send_IRQ_sn2(part_sn2->remote_chctl_amo_va, - chctl.all_flags, - part_sn2->notify_IRQ_nasid, - part_sn2->notify_IRQ_phys_cpuid, - SGI_XPC_NOTIFY); - dev_dbg(xpc_chan, "%s sent to partid=%d, channel=%d, ret=%d\n", - chctl_flag_string, ch->partid, ch->number, ret); - if (unlikely(ret != xpSuccess)) { - if (irq_flags != NULL) - spin_unlock_irqrestore(&ch->lock, *irq_flags); - XPC_DEACTIVATE_PARTITION(part, ret); - if (irq_flags != NULL) - spin_lock_irqsave(&ch->lock, *irq_flags); - } - } -} - -#define XPC_SEND_NOTIFY_IRQ_SN2(_ch, _ipi_f, _irq_f) \ - xpc_send_notify_IRQ_sn2(_ch, _ipi_f, #_ipi_f, _irq_f) - -/* - * Make it look like the remote partition, which is associated with the - * specified channel, sent us a notify IRQ. This faked IRQ will be handled - * by xpc_check_for_dropped_notify_IRQ_sn2(). - */ -static void -xpc_send_local_notify_IRQ_sn2(struct xpc_channel *ch, u8 chctl_flag, - char *chctl_flag_string) -{ - struct xpc_partition *part = &xpc_partitions[ch->partid]; - union xpc_channel_ctl_flags chctl = { 0 }; - - chctl.flags[ch->number] = chctl_flag; - FETCHOP_STORE_OP(TO_AMO((u64)&part->sn.sn2.local_chctl_amo_va-> - variable), FETCHOP_OR, chctl.all_flags); - dev_dbg(xpc_chan, "%s sent local from partid=%d, channel=%d\n", - chctl_flag_string, ch->partid, ch->number); -} - -#define XPC_SEND_LOCAL_NOTIFY_IRQ_SN2(_ch, _ipi_f) \ - xpc_send_local_notify_IRQ_sn2(_ch, _ipi_f, #_ipi_f) - -static void -xpc_send_chctl_closerequest_sn2(struct xpc_channel *ch, - unsigned long *irq_flags) -{ - struct xpc_openclose_args *args = ch->sn.sn2.local_openclose_args; - - args->reason = ch->reason; - XPC_SEND_NOTIFY_IRQ_SN2(ch, XPC_CHCTL_CLOSEREQUEST, irq_flags); -} - -static void -xpc_send_chctl_closereply_sn2(struct xpc_channel *ch, unsigned long *irq_flags) -{ - XPC_SEND_NOTIFY_IRQ_SN2(ch, XPC_CHCTL_CLOSEREPLY, irq_flags); -} - -static void -xpc_send_chctl_openrequest_sn2(struct xpc_channel *ch, unsigned long *irq_flags) -{ - struct xpc_openclose_args *args = ch->sn.sn2.local_openclose_args; - - args->entry_size = ch->entry_size; - args->local_nentries = ch->local_nentries; - XPC_SEND_NOTIFY_IRQ_SN2(ch, XPC_CHCTL_OPENREQUEST, irq_flags); -} - -static void -xpc_send_chctl_openreply_sn2(struct xpc_channel *ch, unsigned long *irq_flags) -{ - struct xpc_openclose_args *args = ch->sn.sn2.local_openclose_args; - - args->remote_nentries = ch->remote_nentries; - args->local_nentries = ch->local_nentries; - args->local_msgqueue_pa = xp_pa(ch->sn.sn2.local_msgqueue); - XPC_SEND_NOTIFY_IRQ_SN2(ch, XPC_CHCTL_OPENREPLY, irq_flags); -} - -static void -xpc_send_chctl_opencomplete_sn2(struct xpc_channel *ch, - unsigned long *irq_flags) -{ - XPC_SEND_NOTIFY_IRQ_SN2(ch, XPC_CHCTL_OPENCOMPLETE, irq_flags); -} - -static void -xpc_send_chctl_msgrequest_sn2(struct xpc_channel *ch) -{ - XPC_SEND_NOTIFY_IRQ_SN2(ch, XPC_CHCTL_MSGREQUEST, NULL); -} - -static void -xpc_send_chctl_local_msgrequest_sn2(struct xpc_channel *ch) -{ - XPC_SEND_LOCAL_NOTIFY_IRQ_SN2(ch, XPC_CHCTL_MSGREQUEST); -} - -static enum xp_retval -xpc_save_remote_msgqueue_pa_sn2(struct xpc_channel *ch, - unsigned long msgqueue_pa) -{ - ch->sn.sn2.remote_msgqueue_pa = msgqueue_pa; - return xpSuccess; -} - -/* - * This next set of functions are used to keep track of when a partition is - * potentially engaged in accessing memory belonging to another partition. - */ - -static void -xpc_indicate_partition_engaged_sn2(struct xpc_partition *part) -{ - unsigned long irq_flags; - struct amo *amo = (struct amo *)__va(part->sn.sn2.remote_amos_page_pa + - (XPC_ENGAGED_PARTITIONS_AMO_SN2 * - sizeof(struct amo))); - - local_irq_save(irq_flags); - - /* set bit corresponding to our partid in remote partition's amo */ - FETCHOP_STORE_OP(TO_AMO((u64)&amo->variable), FETCHOP_OR, - BIT(sn_partition_id)); - - /* - * We must always use the nofault function regardless of whether we - * are on a Shub 1.1 system or a Shub 1.2 slice 0xc processor. If we - * didn't, we'd never know that the other partition is down and would - * keep sending IRQs and amos to it until the heartbeat times out. - */ - (void)xp_nofault_PIOR((u64 *)GLOBAL_MMR_ADDR(NASID_GET(&amo-> - variable), - xp_nofault_PIOR_target)); - - local_irq_restore(irq_flags); -} - -static void -xpc_indicate_partition_disengaged_sn2(struct xpc_partition *part) -{ - struct xpc_partition_sn2 *part_sn2 = &part->sn.sn2; - unsigned long irq_flags; - struct amo *amo = (struct amo *)__va(part_sn2->remote_amos_page_pa + - (XPC_ENGAGED_PARTITIONS_AMO_SN2 * - sizeof(struct amo))); - - local_irq_save(irq_flags); - - /* clear bit corresponding to our partid in remote partition's amo */ - FETCHOP_STORE_OP(TO_AMO((u64)&amo->variable), FETCHOP_AND, - ~BIT(sn_partition_id)); - - /* - * We must always use the nofault function regardless of whether we - * are on a Shub 1.1 system or a Shub 1.2 slice 0xc processor. If we - * didn't, we'd never know that the other partition is down and would - * keep sending IRQs and amos to it until the heartbeat times out. - */ - (void)xp_nofault_PIOR((u64 *)GLOBAL_MMR_ADDR(NASID_GET(&amo-> - variable), - xp_nofault_PIOR_target)); - - local_irq_restore(irq_flags); - - /* - * Send activate IRQ to get other side to see that we've cleared our - * bit in their engaged partitions amo. - */ - xpc_send_activate_IRQ_sn2(part_sn2->remote_amos_page_pa, - cnodeid_to_nasid(0), - part_sn2->activate_IRQ_nasid, - part_sn2->activate_IRQ_phys_cpuid); -} - -static void -xpc_assume_partition_disengaged_sn2(short partid) -{ - struct amo *amo = xpc_vars_sn2->amos_page + - XPC_ENGAGED_PARTITIONS_AMO_SN2; - - /* clear bit(s) based on partid mask in our partition's amo */ - FETCHOP_STORE_OP(TO_AMO((u64)&amo->variable), FETCHOP_AND, - ~BIT(partid)); -} - -static int -xpc_partition_engaged_sn2(short partid) -{ - struct amo *amo = xpc_vars_sn2->amos_page + - XPC_ENGAGED_PARTITIONS_AMO_SN2; - - /* our partition's amo variable ANDed with partid mask */ - return (FETCHOP_LOAD_OP(TO_AMO((u64)&amo->variable), FETCHOP_LOAD) & - BIT(partid)) != 0; -} - -static int -xpc_any_partition_engaged_sn2(void) -{ - struct amo *amo = xpc_vars_sn2->amos_page + - XPC_ENGAGED_PARTITIONS_AMO_SN2; - - /* our partition's amo variable */ - return FETCHOP_LOAD_OP(TO_AMO((u64)&amo->variable), FETCHOP_LOAD) != 0; -} - -/* original protection values for each node */ -static u64 xpc_prot_vec_sn2[MAX_NUMNODES]; - -/* - * Change protections to allow amo operations on non-Shub 1.1 systems. - */ -static enum xp_retval -xpc_allow_amo_ops_sn2(struct amo *amos_page) -{ - enum xp_retval ret = xpSuccess; - - /* - * On SHUB 1.1, we cannot call sn_change_memprotect() since the BIST - * collides with memory operations. On those systems we call - * xpc_allow_amo_ops_shub_wars_1_1_sn2() instead. - */ - if (!enable_shub_wars_1_1()) - ret = xp_expand_memprotect(ia64_tpa((u64)amos_page), PAGE_SIZE); - - return ret; -} - -/* - * Change protections to allow amo operations on Shub 1.1 systems. - */ -static void -xpc_allow_amo_ops_shub_wars_1_1_sn2(void) -{ - int node; - int nasid; - - if (!enable_shub_wars_1_1()) - return; - - for_each_online_node(node) { - nasid = cnodeid_to_nasid(node); - /* save current protection values */ - xpc_prot_vec_sn2[node] = - (u64)HUB_L((u64 *)GLOBAL_MMR_ADDR(nasid, - SH1_MD_DQLP_MMR_DIR_PRIVEC0)); - /* open up everything */ - HUB_S((u64 *)GLOBAL_MMR_ADDR(nasid, - SH1_MD_DQLP_MMR_DIR_PRIVEC0), - -1UL); - HUB_S((u64 *)GLOBAL_MMR_ADDR(nasid, - SH1_MD_DQRP_MMR_DIR_PRIVEC0), - -1UL); - } -} - -static enum xp_retval -xpc_get_partition_rsvd_page_pa_sn2(void *buf, u64 *cookie, unsigned long *rp_pa, - size_t *len) -{ - s64 status; - enum xp_retval ret; - - status = sn_partition_reserved_page_pa((u64)buf, cookie, - (u64 *)rp_pa, (u64 *)len); - if (status == SALRET_OK) - ret = xpSuccess; - else if (status == SALRET_MORE_PASSES) - ret = xpNeedMoreInfo; - else - ret = xpSalError; - - return ret; -} - - -static int -xpc_setup_rsvd_page_sn2(struct xpc_rsvd_page *rp) -{ - struct amo *amos_page; - int i; - int ret; - - xpc_vars_sn2 = XPC_RP_VARS(rp); - - rp->sn.sn2.vars_pa = xp_pa(xpc_vars_sn2); - - /* vars_part array follows immediately after vars */ - xpc_vars_part_sn2 = (struct xpc_vars_part_sn2 *)((u8 *)XPC_RP_VARS(rp) + - XPC_RP_VARS_SIZE); - - /* - * Before clearing xpc_vars_sn2, see if a page of amos had been - * previously allocated. If not we'll need to allocate one and set - * permissions so that cross-partition amos are allowed. - * - * The allocated amo page needs MCA reporting to remain disabled after - * XPC has unloaded. To make this work, we keep a copy of the pointer - * to this page (i.e., amos_page) in the struct xpc_vars_sn2 structure, - * which is pointed to by the reserved page, and re-use that saved copy - * on subsequent loads of XPC. This amo page is never freed, and its - * memory protections are never restricted. - */ - amos_page = xpc_vars_sn2->amos_page; - if (amos_page == NULL) { - amos_page = (struct amo *)TO_AMO(uncached_alloc_page(0, 1)); - if (amos_page == NULL) { - dev_err(xpc_part, "can't allocate page of amos\n"); - return -ENOMEM; - } - - /* - * Open up amo-R/W to cpu. This is done on Shub 1.1 systems - * when xpc_allow_amo_ops_shub_wars_1_1_sn2() is called. - */ - ret = xpc_allow_amo_ops_sn2(amos_page); - if (ret != xpSuccess) { - dev_err(xpc_part, "can't allow amo operations\n"); - uncached_free_page(__IA64_UNCACHED_OFFSET | - TO_PHYS((u64)amos_page), 1); - return -EPERM; - } - } - - /* clear xpc_vars_sn2 */ - memset(xpc_vars_sn2, 0, sizeof(struct xpc_vars_sn2)); - - xpc_vars_sn2->version = XPC_V_VERSION; - xpc_vars_sn2->activate_IRQ_nasid = cpuid_to_nasid(0); - xpc_vars_sn2->activate_IRQ_phys_cpuid = cpu_physical_id(0); - xpc_vars_sn2->vars_part_pa = xp_pa(xpc_vars_part_sn2); - xpc_vars_sn2->amos_page_pa = ia64_tpa((u64)amos_page); - xpc_vars_sn2->amos_page = amos_page; /* save for next load of XPC */ - - /* clear xpc_vars_part_sn2 */ - memset((u64 *)xpc_vars_part_sn2, 0, sizeof(struct xpc_vars_part_sn2) * - XP_MAX_NPARTITIONS_SN2); - - /* initialize the activate IRQ related amo variables */ - for (i = 0; i < xpc_nasid_mask_nlongs; i++) - (void)xpc_init_IRQ_amo_sn2(XPC_ACTIVATE_IRQ_AMOS_SN2 + i); - - /* initialize the engaged remote partitions related amo variables */ - (void)xpc_init_IRQ_amo_sn2(XPC_ENGAGED_PARTITIONS_AMO_SN2); - (void)xpc_init_IRQ_amo_sn2(XPC_DEACTIVATE_REQUEST_AMO_SN2); - - return 0; -} - -static int -xpc_hb_allowed_sn2(short partid, void *heartbeating_to_mask) -{ - return test_bit(partid, heartbeating_to_mask); -} - -static void -xpc_allow_hb_sn2(short partid) -{ - DBUG_ON(xpc_vars_sn2 == NULL); - set_bit(partid, xpc_vars_sn2->heartbeating_to_mask); -} - -static void -xpc_disallow_hb_sn2(short partid) -{ - DBUG_ON(xpc_vars_sn2 == NULL); - clear_bit(partid, xpc_vars_sn2->heartbeating_to_mask); -} - -static void -xpc_disallow_all_hbs_sn2(void) -{ - DBUG_ON(xpc_vars_sn2 == NULL); - bitmap_zero(xpc_vars_sn2->heartbeating_to_mask, xp_max_npartitions); -} - -static void -xpc_increment_heartbeat_sn2(void) -{ - xpc_vars_sn2->heartbeat++; -} - -static void -xpc_offline_heartbeat_sn2(void) -{ - xpc_increment_heartbeat_sn2(); - xpc_vars_sn2->heartbeat_offline = 1; -} - -static void -xpc_online_heartbeat_sn2(void) -{ - xpc_increment_heartbeat_sn2(); - xpc_vars_sn2->heartbeat_offline = 0; -} - -static void -xpc_heartbeat_init_sn2(void) -{ - DBUG_ON(xpc_vars_sn2 == NULL); - - bitmap_zero(xpc_vars_sn2->heartbeating_to_mask, XP_MAX_NPARTITIONS_SN2); - xpc_online_heartbeat_sn2(); -} - -static void -xpc_heartbeat_exit_sn2(void) -{ - xpc_offline_heartbeat_sn2(); -} - -static enum xp_retval -xpc_get_remote_heartbeat_sn2(struct xpc_partition *part) -{ - struct xpc_vars_sn2 *remote_vars; - enum xp_retval ret; - - remote_vars = (struct xpc_vars_sn2 *)xpc_remote_copy_buffer_sn2; - - /* pull the remote vars structure that contains the heartbeat */ - ret = xp_remote_memcpy(xp_pa(remote_vars), - part->sn.sn2.remote_vars_pa, - XPC_RP_VARS_SIZE); - if (ret != xpSuccess) - return ret; - - dev_dbg(xpc_part, "partid=%d, heartbeat=%lld, last_heartbeat=%lld, " - "heartbeat_offline=%lld, HB_mask[0]=0x%lx\n", XPC_PARTID(part), - remote_vars->heartbeat, part->last_heartbeat, - remote_vars->heartbeat_offline, - remote_vars->heartbeating_to_mask[0]); - - if ((remote_vars->heartbeat == part->last_heartbeat && - !remote_vars->heartbeat_offline) || - !xpc_hb_allowed_sn2(sn_partition_id, - remote_vars->heartbeating_to_mask)) { - ret = xpNoHeartbeat; - } else { - part->last_heartbeat = remote_vars->heartbeat; - } - - return ret; -} - -/* - * Get a copy of the remote partition's XPC variables from the reserved page. - * - * remote_vars points to a buffer that is cacheline aligned for BTE copies and - * assumed to be of size XPC_RP_VARS_SIZE. - */ -static enum xp_retval -xpc_get_remote_vars_sn2(unsigned long remote_vars_pa, - struct xpc_vars_sn2 *remote_vars) -{ - enum xp_retval ret; - - if (remote_vars_pa == 0) - return xpVarsNotSet; - - /* pull over the cross partition variables */ - ret = xp_remote_memcpy(xp_pa(remote_vars), remote_vars_pa, - XPC_RP_VARS_SIZE); - if (ret != xpSuccess) - return ret; - - if (XPC_VERSION_MAJOR(remote_vars->version) != - XPC_VERSION_MAJOR(XPC_V_VERSION)) { - return xpBadVersion; - } - - return xpSuccess; -} - -static void -xpc_request_partition_activation_sn2(struct xpc_rsvd_page *remote_rp, - unsigned long remote_rp_pa, int nasid) -{ - xpc_send_local_activate_IRQ_sn2(nasid); -} - -static void -xpc_request_partition_reactivation_sn2(struct xpc_partition *part) -{ - xpc_send_local_activate_IRQ_sn2(part->sn.sn2.activate_IRQ_nasid); -} - -static void -xpc_request_partition_deactivation_sn2(struct xpc_partition *part) -{ - struct xpc_partition_sn2 *part_sn2 = &part->sn.sn2; - unsigned long irq_flags; - struct amo *amo = (struct amo *)__va(part_sn2->remote_amos_page_pa + - (XPC_DEACTIVATE_REQUEST_AMO_SN2 * - sizeof(struct amo))); - - local_irq_save(irq_flags); - - /* set bit corresponding to our partid in remote partition's amo */ - FETCHOP_STORE_OP(TO_AMO((u64)&amo->variable), FETCHOP_OR, - BIT(sn_partition_id)); - - /* - * We must always use the nofault function regardless of whether we - * are on a Shub 1.1 system or a Shub 1.2 slice 0xc processor. If we - * didn't, we'd never know that the other partition is down and would - * keep sending IRQs and amos to it until the heartbeat times out. - */ - (void)xp_nofault_PIOR((u64 *)GLOBAL_MMR_ADDR(NASID_GET(&amo-> - variable), - xp_nofault_PIOR_target)); - - local_irq_restore(irq_flags); - - /* - * Send activate IRQ to get other side to see that we've set our - * bit in their deactivate request amo. - */ - xpc_send_activate_IRQ_sn2(part_sn2->remote_amos_page_pa, - cnodeid_to_nasid(0), - part_sn2->activate_IRQ_nasid, - part_sn2->activate_IRQ_phys_cpuid); -} - -static void -xpc_cancel_partition_deactivation_request_sn2(struct xpc_partition *part) -{ - unsigned long irq_flags; - struct amo *amo = (struct amo *)__va(part->sn.sn2.remote_amos_page_pa + - (XPC_DEACTIVATE_REQUEST_AMO_SN2 * - sizeof(struct amo))); - - local_irq_save(irq_flags); - - /* clear bit corresponding to our partid in remote partition's amo */ - FETCHOP_STORE_OP(TO_AMO((u64)&amo->variable), FETCHOP_AND, - ~BIT(sn_partition_id)); - - /* - * We must always use the nofault function regardless of whether we - * are on a Shub 1.1 system or a Shub 1.2 slice 0xc processor. If we - * didn't, we'd never know that the other partition is down and would - * keep sending IRQs and amos to it until the heartbeat times out. - */ - (void)xp_nofault_PIOR((u64 *)GLOBAL_MMR_ADDR(NASID_GET(&amo-> - variable), - xp_nofault_PIOR_target)); - - local_irq_restore(irq_flags); -} - -static int -xpc_partition_deactivation_requested_sn2(short partid) -{ - struct amo *amo = xpc_vars_sn2->amos_page + - XPC_DEACTIVATE_REQUEST_AMO_SN2; - - /* our partition's amo variable ANDed with partid mask */ - return (FETCHOP_LOAD_OP(TO_AMO((u64)&amo->variable), FETCHOP_LOAD) & - BIT(partid)) != 0; -} - -/* - * Update the remote partition's info. - */ -static void -xpc_update_partition_info_sn2(struct xpc_partition *part, u8 remote_rp_version, - unsigned long *remote_rp_ts_jiffies, - unsigned long remote_rp_pa, - unsigned long remote_vars_pa, - struct xpc_vars_sn2 *remote_vars) -{ - struct xpc_partition_sn2 *part_sn2 = &part->sn.sn2; - - part->remote_rp_version = remote_rp_version; - dev_dbg(xpc_part, " remote_rp_version = 0x%016x\n", - part->remote_rp_version); - - part->remote_rp_ts_jiffies = *remote_rp_ts_jiffies; - dev_dbg(xpc_part, " remote_rp_ts_jiffies = 0x%016lx\n", - part->remote_rp_ts_jiffies); - - part->remote_rp_pa = remote_rp_pa; - dev_dbg(xpc_part, " remote_rp_pa = 0x%016lx\n", part->remote_rp_pa); - - part_sn2->remote_vars_pa = remote_vars_pa; - dev_dbg(xpc_part, " remote_vars_pa = 0x%016lx\n", - part_sn2->remote_vars_pa); - - part->last_heartbeat = remote_vars->heartbeat - 1; - dev_dbg(xpc_part, " last_heartbeat = 0x%016llx\n", - part->last_heartbeat); - - part_sn2->remote_vars_part_pa = remote_vars->vars_part_pa; - dev_dbg(xpc_part, " remote_vars_part_pa = 0x%016lx\n", - part_sn2->remote_vars_part_pa); - - part_sn2->activate_IRQ_nasid = remote_vars->activate_IRQ_nasid; - dev_dbg(xpc_part, " activate_IRQ_nasid = 0x%x\n", - part_sn2->activate_IRQ_nasid); - - part_sn2->activate_IRQ_phys_cpuid = - remote_vars->activate_IRQ_phys_cpuid; - dev_dbg(xpc_part, " activate_IRQ_phys_cpuid = 0x%x\n", - part_sn2->activate_IRQ_phys_cpuid); - - part_sn2->remote_amos_page_pa = remote_vars->amos_page_pa; - dev_dbg(xpc_part, " remote_amos_page_pa = 0x%lx\n", - part_sn2->remote_amos_page_pa); - - part_sn2->remote_vars_version = remote_vars->version; - dev_dbg(xpc_part, " remote_vars_version = 0x%x\n", - part_sn2->remote_vars_version); -} - -/* - * Prior code has determined the nasid which generated a activate IRQ. - * Inspect that nasid to determine if its partition needs to be activated - * or deactivated. - * - * A partition is considered "awaiting activation" if our partition - * flags indicate it is not active and it has a heartbeat. A - * partition is considered "awaiting deactivation" if our partition - * flags indicate it is active but it has no heartbeat or it is not - * sending its heartbeat to us. - * - * To determine the heartbeat, the remote nasid must have a properly - * initialized reserved page. - */ -static void -xpc_identify_activate_IRQ_req_sn2(int nasid) -{ - struct xpc_rsvd_page *remote_rp; - struct xpc_vars_sn2 *remote_vars; - unsigned long remote_rp_pa; - unsigned long remote_vars_pa; - int remote_rp_version; - int reactivate = 0; - unsigned long remote_rp_ts_jiffies = 0; - short partid; - struct xpc_partition *part; - struct xpc_partition_sn2 *part_sn2; - enum xp_retval ret; - - /* pull over the reserved page structure */ - - remote_rp = (struct xpc_rsvd_page *)xpc_remote_copy_buffer_sn2; - - ret = xpc_get_remote_rp(nasid, NULL, remote_rp, &remote_rp_pa); - if (ret != xpSuccess) { - dev_warn(xpc_part, "unable to get reserved page from nasid %d, " - "which sent interrupt, reason=%d\n", nasid, ret); - return; - } - - remote_vars_pa = remote_rp->sn.sn2.vars_pa; - remote_rp_version = remote_rp->version; - remote_rp_ts_jiffies = remote_rp->ts_jiffies; - - partid = remote_rp->SAL_partid; - part = &xpc_partitions[partid]; - part_sn2 = &part->sn.sn2; - - /* pull over the cross partition variables */ - - remote_vars = (struct xpc_vars_sn2 *)xpc_remote_copy_buffer_sn2; - - ret = xpc_get_remote_vars_sn2(remote_vars_pa, remote_vars); - if (ret != xpSuccess) { - dev_warn(xpc_part, "unable to get XPC variables from nasid %d, " - "which sent interrupt, reason=%d\n", nasid, ret); - - XPC_DEACTIVATE_PARTITION(part, ret); - return; - } - - part->activate_IRQ_rcvd++; - - dev_dbg(xpc_part, "partid for nasid %d is %d; IRQs = %d; HB = " - "%lld:0x%lx\n", (int)nasid, (int)partid, - part->activate_IRQ_rcvd, - remote_vars->heartbeat, remote_vars->heartbeating_to_mask[0]); - - if (xpc_partition_disengaged(part) && - part->act_state == XPC_P_AS_INACTIVE) { - - xpc_update_partition_info_sn2(part, remote_rp_version, - &remote_rp_ts_jiffies, - remote_rp_pa, remote_vars_pa, - remote_vars); - - if (xpc_partition_deactivation_requested_sn2(partid)) { - /* - * Other side is waiting on us to deactivate even though - * we already have. - */ - return; - } - - xpc_activate_partition(part); - return; - } - - DBUG_ON(part->remote_rp_version == 0); - DBUG_ON(part_sn2->remote_vars_version == 0); - - if (remote_rp_ts_jiffies != part->remote_rp_ts_jiffies) { - - /* the other side rebooted */ - - DBUG_ON(xpc_partition_engaged_sn2(partid)); - DBUG_ON(xpc_partition_deactivation_requested_sn2(partid)); - - xpc_update_partition_info_sn2(part, remote_rp_version, - &remote_rp_ts_jiffies, - remote_rp_pa, remote_vars_pa, - remote_vars); - reactivate = 1; - } - - if (part->disengage_timeout > 0 && !xpc_partition_disengaged(part)) { - /* still waiting on other side to disengage from us */ - return; - } - - if (reactivate) - XPC_DEACTIVATE_PARTITION(part, xpReactivating); - else if (xpc_partition_deactivation_requested_sn2(partid)) - XPC_DEACTIVATE_PARTITION(part, xpOtherGoingDown); -} - -/* - * Loop through the activation amo variables and process any bits - * which are set. Each bit indicates a nasid sending a partition - * activation or deactivation request. - * - * Return #of IRQs detected. - */ -int -xpc_identify_activate_IRQ_sender_sn2(void) -{ - int l; - int b; - unsigned long nasid_mask_long; - u64 nasid; /* remote nasid */ - int n_IRQs_detected = 0; - struct amo *act_amos; - - act_amos = xpc_vars_sn2->amos_page + XPC_ACTIVATE_IRQ_AMOS_SN2; - - /* scan through activate amo variables looking for non-zero entries */ - for (l = 0; l < xpc_nasid_mask_nlongs; l++) { - - if (xpc_exiting) - break; - - nasid_mask_long = xpc_receive_IRQ_amo_sn2(&act_amos[l]); - - b = find_first_bit(&nasid_mask_long, BITS_PER_LONG); - if (b >= BITS_PER_LONG) { - /* no IRQs from nasids in this amo variable */ - continue; - } - - dev_dbg(xpc_part, "amo[%d] gave back 0x%lx\n", l, - nasid_mask_long); - - /* - * If this nasid has been added to the machine since - * our partition was reset, this will retain the - * remote nasid in our reserved pages machine mask. - * This is used in the event of module reload. - */ - xpc_mach_nasids[l] |= nasid_mask_long; - - /* locate the nasid(s) which sent interrupts */ - - do { - n_IRQs_detected++; - nasid = (l * BITS_PER_LONG + b) * 2; - dev_dbg(xpc_part, "interrupt from nasid %lld\n", nasid); - xpc_identify_activate_IRQ_req_sn2(nasid); - - b = find_next_bit(&nasid_mask_long, BITS_PER_LONG, - b + 1); - } while (b < BITS_PER_LONG); - } - return n_IRQs_detected; -} - -static void -xpc_process_activate_IRQ_rcvd_sn2(void) -{ - unsigned long irq_flags; - int n_IRQs_expected; - int n_IRQs_detected; - - spin_lock_irqsave(&xpc_activate_IRQ_rcvd_lock, irq_flags); - n_IRQs_expected = xpc_activate_IRQ_rcvd; - xpc_activate_IRQ_rcvd = 0; - spin_unlock_irqrestore(&xpc_activate_IRQ_rcvd_lock, irq_flags); - - n_IRQs_detected = xpc_identify_activate_IRQ_sender_sn2(); - if (n_IRQs_detected < n_IRQs_expected) { - /* retry once to help avoid missing amo */ - (void)xpc_identify_activate_IRQ_sender_sn2(); - } -} - -/* - * Setup the channel structures that are sn2 specific. - */ -static enum xp_retval -xpc_setup_ch_structures_sn2(struct xpc_partition *part) -{ - struct xpc_partition_sn2 *part_sn2 = &part->sn.sn2; - struct xpc_channel_sn2 *ch_sn2; - enum xp_retval retval; - int ret; - int cpuid; - int ch_number; - struct timer_list *timer; - short partid = XPC_PARTID(part); - - /* allocate all the required GET/PUT values */ - - part_sn2->local_GPs = - xpc_kzalloc_cacheline_aligned(XPC_GP_SIZE, GFP_KERNEL, - &part_sn2->local_GPs_base); - if (part_sn2->local_GPs == NULL) { - dev_err(xpc_chan, "can't get memory for local get/put " - "values\n"); - return xpNoMemory; - } - - part_sn2->remote_GPs = - xpc_kzalloc_cacheline_aligned(XPC_GP_SIZE, GFP_KERNEL, - &part_sn2->remote_GPs_base); - if (part_sn2->remote_GPs == NULL) { - dev_err(xpc_chan, "can't get memory for remote get/put " - "values\n"); - retval = xpNoMemory; - goto out_1; - } - - part_sn2->remote_GPs_pa = 0; - - /* allocate all the required open and close args */ - - part_sn2->local_openclose_args = - xpc_kzalloc_cacheline_aligned(XPC_OPENCLOSE_ARGS_SIZE, - GFP_KERNEL, &part_sn2-> - local_openclose_args_base); - if (part_sn2->local_openclose_args == NULL) { - dev_err(xpc_chan, "can't get memory for local connect args\n"); - retval = xpNoMemory; - goto out_2; - } - - part_sn2->remote_openclose_args_pa = 0; - - part_sn2->local_chctl_amo_va = xpc_init_IRQ_amo_sn2(partid); - - part_sn2->notify_IRQ_nasid = 0; - part_sn2->notify_IRQ_phys_cpuid = 0; - part_sn2->remote_chctl_amo_va = NULL; - - sprintf(part_sn2->notify_IRQ_owner, "xpc%02d", partid); - ret = request_irq(SGI_XPC_NOTIFY, xpc_handle_notify_IRQ_sn2, - IRQF_SHARED, part_sn2->notify_IRQ_owner, - (void *)(u64)partid); - if (ret != 0) { - dev_err(xpc_chan, "can't register NOTIFY IRQ handler, " - "errno=%d\n", -ret); - retval = xpLackOfResources; - goto out_3; - } - - /* Setup a timer to check for dropped notify IRQs */ - timer = &part_sn2->dropped_notify_IRQ_timer; - timer_setup(timer, xpc_check_for_dropped_notify_IRQ_sn2, 0); - timer->expires = jiffies + XPC_DROPPED_NOTIFY_IRQ_WAIT_INTERVAL; - add_timer(timer); - - for (ch_number = 0; ch_number < part->nchannels; ch_number++) { - ch_sn2 = &part->channels[ch_number].sn.sn2; - - ch_sn2->local_GP = &part_sn2->local_GPs[ch_number]; - ch_sn2->local_openclose_args = - &part_sn2->local_openclose_args[ch_number]; - - mutex_init(&ch_sn2->msg_to_pull_mutex); - } - - /* - * Setup the per partition specific variables required by the - * remote partition to establish channel connections with us. - * - * The setting of the magic # indicates that these per partition - * specific variables are ready to be used. - */ - xpc_vars_part_sn2[partid].GPs_pa = xp_pa(part_sn2->local_GPs); - xpc_vars_part_sn2[partid].openclose_args_pa = - xp_pa(part_sn2->local_openclose_args); - xpc_vars_part_sn2[partid].chctl_amo_pa = - xp_pa(part_sn2->local_chctl_amo_va); - cpuid = raw_smp_processor_id(); /* any CPU in this partition will do */ - xpc_vars_part_sn2[partid].notify_IRQ_nasid = cpuid_to_nasid(cpuid); - xpc_vars_part_sn2[partid].notify_IRQ_phys_cpuid = - cpu_physical_id(cpuid); - xpc_vars_part_sn2[partid].nchannels = part->nchannels; - xpc_vars_part_sn2[partid].magic = XPC_VP_MAGIC1_SN2; - - return xpSuccess; - - /* setup of ch structures failed */ -out_3: - kfree(part_sn2->local_openclose_args_base); - part_sn2->local_openclose_args = NULL; -out_2: - kfree(part_sn2->remote_GPs_base); - part_sn2->remote_GPs = NULL; -out_1: - kfree(part_sn2->local_GPs_base); - part_sn2->local_GPs = NULL; - return retval; -} - -/* - * Teardown the channel structures that are sn2 specific. - */ -static void -xpc_teardown_ch_structures_sn2(struct xpc_partition *part) -{ - struct xpc_partition_sn2 *part_sn2 = &part->sn.sn2; - short partid = XPC_PARTID(part); - - /* - * Indicate that the variables specific to the remote partition are no - * longer available for its use. - */ - xpc_vars_part_sn2[partid].magic = 0; - - /* in case we've still got outstanding timers registered... */ - del_timer_sync(&part_sn2->dropped_notify_IRQ_timer); - free_irq(SGI_XPC_NOTIFY, (void *)(u64)partid); - - kfree(part_sn2->local_openclose_args_base); - part_sn2->local_openclose_args = NULL; - kfree(part_sn2->remote_GPs_base); - part_sn2->remote_GPs = NULL; - kfree(part_sn2->local_GPs_base); - part_sn2->local_GPs = NULL; - part_sn2->local_chctl_amo_va = NULL; -} - -/* - * Create a wrapper that hides the underlying mechanism for pulling a cacheline - * (or multiple cachelines) from a remote partition. - * - * src_pa must be a cacheline aligned physical address on the remote partition. - * dst must be a cacheline aligned virtual address on this partition. - * cnt must be cacheline sized - */ -/* ??? Replace this function by call to xp_remote_memcpy() or bte_copy()? */ -static enum xp_retval -xpc_pull_remote_cachelines_sn2(struct xpc_partition *part, void *dst, - const unsigned long src_pa, size_t cnt) -{ - enum xp_retval ret; - - DBUG_ON(src_pa != L1_CACHE_ALIGN(src_pa)); - DBUG_ON((unsigned long)dst != L1_CACHE_ALIGN((unsigned long)dst)); - DBUG_ON(cnt != L1_CACHE_ALIGN(cnt)); - - if (part->act_state == XPC_P_AS_DEACTIVATING) - return part->reason; - - ret = xp_remote_memcpy(xp_pa(dst), src_pa, cnt); - if (ret != xpSuccess) { - dev_dbg(xpc_chan, "xp_remote_memcpy() from partition %d failed," - " ret=%d\n", XPC_PARTID(part), ret); - } - return ret; -} - -/* - * Pull the remote per partition specific variables from the specified - * partition. - */ -static enum xp_retval -xpc_pull_remote_vars_part_sn2(struct xpc_partition *part) -{ - struct xpc_partition_sn2 *part_sn2 = &part->sn.sn2; - u8 buffer[L1_CACHE_BYTES * 2]; - struct xpc_vars_part_sn2 *pulled_entry_cacheline = - (struct xpc_vars_part_sn2 *)L1_CACHE_ALIGN((u64)buffer); - struct xpc_vars_part_sn2 *pulled_entry; - unsigned long remote_entry_cacheline_pa; - unsigned long remote_entry_pa; - short partid = XPC_PARTID(part); - enum xp_retval ret; - - /* pull the cacheline that contains the variables we're interested in */ - - DBUG_ON(part_sn2->remote_vars_part_pa != - L1_CACHE_ALIGN(part_sn2->remote_vars_part_pa)); - DBUG_ON(sizeof(struct xpc_vars_part_sn2) != L1_CACHE_BYTES / 2); - - remote_entry_pa = part_sn2->remote_vars_part_pa + - sn_partition_id * sizeof(struct xpc_vars_part_sn2); - - remote_entry_cacheline_pa = (remote_entry_pa & ~(L1_CACHE_BYTES - 1)); - - pulled_entry = (struct xpc_vars_part_sn2 *)((u64)pulled_entry_cacheline - + (remote_entry_pa & - (L1_CACHE_BYTES - 1))); - - ret = xpc_pull_remote_cachelines_sn2(part, pulled_entry_cacheline, - remote_entry_cacheline_pa, - L1_CACHE_BYTES); - if (ret != xpSuccess) { - dev_dbg(xpc_chan, "failed to pull XPC vars_part from " - "partition %d, ret=%d\n", partid, ret); - return ret; - } - - /* see if they've been set up yet */ - - if (pulled_entry->magic != XPC_VP_MAGIC1_SN2 && - pulled_entry->magic != XPC_VP_MAGIC2_SN2) { - - if (pulled_entry->magic != 0) { - dev_dbg(xpc_chan, "partition %d's XPC vars_part for " - "partition %d has bad magic value (=0x%llx)\n", - partid, sn_partition_id, pulled_entry->magic); - return xpBadMagic; - } - - /* they've not been initialized yet */ - return xpRetry; - } - - if (xpc_vars_part_sn2[partid].magic == XPC_VP_MAGIC1_SN2) { - - /* validate the variables */ - - if (pulled_entry->GPs_pa == 0 || - pulled_entry->openclose_args_pa == 0 || - pulled_entry->chctl_amo_pa == 0) { - - dev_err(xpc_chan, "partition %d's XPC vars_part for " - "partition %d are not valid\n", partid, - sn_partition_id); - return xpInvalidAddress; - } - - /* the variables we imported look to be valid */ - - part_sn2->remote_GPs_pa = pulled_entry->GPs_pa; - part_sn2->remote_openclose_args_pa = - pulled_entry->openclose_args_pa; - part_sn2->remote_chctl_amo_va = - (struct amo *)__va(pulled_entry->chctl_amo_pa); - part_sn2->notify_IRQ_nasid = pulled_entry->notify_IRQ_nasid; - part_sn2->notify_IRQ_phys_cpuid = - pulled_entry->notify_IRQ_phys_cpuid; - - if (part->nchannels > pulled_entry->nchannels) - part->nchannels = pulled_entry->nchannels; - - /* let the other side know that we've pulled their variables */ - - xpc_vars_part_sn2[partid].magic = XPC_VP_MAGIC2_SN2; - } - - if (pulled_entry->magic == XPC_VP_MAGIC1_SN2) - return xpRetry; - - return xpSuccess; -} - -/* - * Establish first contact with the remote partititon. This involves pulling - * the XPC per partition variables from the remote partition and waiting for - * the remote partition to pull ours. - */ -static enum xp_retval -xpc_make_first_contact_sn2(struct xpc_partition *part) -{ - struct xpc_partition_sn2 *part_sn2 = &part->sn.sn2; - enum xp_retval ret; - - /* - * Register the remote partition's amos with SAL so it can handle - * and cleanup errors within that address range should the remote - * partition go down. We don't unregister this range because it is - * difficult to tell when outstanding writes to the remote partition - * are finished and thus when it is safe to unregister. This should - * not result in wasted space in the SAL xp_addr_region table because - * we should get the same page for remote_amos_page_pa after module - * reloads and system reboots. - */ - if (sn_register_xp_addr_region(part_sn2->remote_amos_page_pa, - PAGE_SIZE, 1) < 0) { - dev_warn(xpc_part, "xpc_activating(%d) failed to register " - "xp_addr region\n", XPC_PARTID(part)); - - ret = xpPhysAddrRegFailed; - XPC_DEACTIVATE_PARTITION(part, ret); - return ret; - } - - /* - * Send activate IRQ to get other side to activate if they've not - * already begun to do so. - */ - xpc_send_activate_IRQ_sn2(part_sn2->remote_amos_page_pa, - cnodeid_to_nasid(0), - part_sn2->activate_IRQ_nasid, - part_sn2->activate_IRQ_phys_cpuid); - - while ((ret = xpc_pull_remote_vars_part_sn2(part)) != xpSuccess) { - if (ret != xpRetry) { - XPC_DEACTIVATE_PARTITION(part, ret); - return ret; - } - - dev_dbg(xpc_part, "waiting to make first contact with " - "partition %d\n", XPC_PARTID(part)); - - /* wait a 1/4 of a second or so */ - (void)msleep_interruptible(250); - - if (part->act_state == XPC_P_AS_DEACTIVATING) - return part->reason; - } - - return xpSuccess; -} - -/* - * Get the chctl flags and pull the openclose args and/or remote GPs as needed. - */ -static u64 -xpc_get_chctl_all_flags_sn2(struct xpc_partition *part) -{ - struct xpc_partition_sn2 *part_sn2 = &part->sn.sn2; - unsigned long irq_flags; - union xpc_channel_ctl_flags chctl; - enum xp_retval ret; - - /* - * See if there are any chctl flags to be handled. - */ - - spin_lock_irqsave(&part->chctl_lock, irq_flags); - chctl = part->chctl; - if (chctl.all_flags != 0) - part->chctl.all_flags = 0; - - spin_unlock_irqrestore(&part->chctl_lock, irq_flags); - - if (xpc_any_openclose_chctl_flags_set(&chctl)) { - ret = xpc_pull_remote_cachelines_sn2(part, part-> - remote_openclose_args, - part_sn2-> - remote_openclose_args_pa, - XPC_OPENCLOSE_ARGS_SIZE); - if (ret != xpSuccess) { - XPC_DEACTIVATE_PARTITION(part, ret); - - dev_dbg(xpc_chan, "failed to pull openclose args from " - "partition %d, ret=%d\n", XPC_PARTID(part), - ret); - - /* don't bother processing chctl flags anymore */ - chctl.all_flags = 0; - } - } - - if (xpc_any_msg_chctl_flags_set(&chctl)) { - ret = xpc_pull_remote_cachelines_sn2(part, part_sn2->remote_GPs, - part_sn2->remote_GPs_pa, - XPC_GP_SIZE); - if (ret != xpSuccess) { - XPC_DEACTIVATE_PARTITION(part, ret); - - dev_dbg(xpc_chan, "failed to pull GPs from partition " - "%d, ret=%d\n", XPC_PARTID(part), ret); - - /* don't bother processing chctl flags anymore */ - chctl.all_flags = 0; - } - } - - return chctl.all_flags; -} - -/* - * Allocate the local message queue and the notify queue. - */ -static enum xp_retval -xpc_allocate_local_msgqueue_sn2(struct xpc_channel *ch) -{ - struct xpc_channel_sn2 *ch_sn2 = &ch->sn.sn2; - unsigned long irq_flags; - int nentries; - size_t nbytes; - - for (nentries = ch->local_nentries; nentries > 0; nentries--) { - - nbytes = nentries * ch->entry_size; - ch_sn2->local_msgqueue = - xpc_kzalloc_cacheline_aligned(nbytes, GFP_KERNEL, - &ch_sn2->local_msgqueue_base); - if (ch_sn2->local_msgqueue == NULL) - continue; - - nbytes = nentries * sizeof(struct xpc_notify_sn2); - ch_sn2->notify_queue = kzalloc(nbytes, GFP_KERNEL); - if (ch_sn2->notify_queue == NULL) { - kfree(ch_sn2->local_msgqueue_base); - ch_sn2->local_msgqueue = NULL; - continue; - } - - spin_lock_irqsave(&ch->lock, irq_flags); - if (nentries < ch->local_nentries) { - dev_dbg(xpc_chan, "nentries=%d local_nentries=%d, " - "partid=%d, channel=%d\n", nentries, - ch->local_nentries, ch->partid, ch->number); - - ch->local_nentries = nentries; - } - spin_unlock_irqrestore(&ch->lock, irq_flags); - return xpSuccess; - } - - dev_dbg(xpc_chan, "can't get memory for local message queue and notify " - "queue, partid=%d, channel=%d\n", ch->partid, ch->number); - return xpNoMemory; -} - -/* - * Allocate the cached remote message queue. - */ -static enum xp_retval -xpc_allocate_remote_msgqueue_sn2(struct xpc_channel *ch) -{ - struct xpc_channel_sn2 *ch_sn2 = &ch->sn.sn2; - unsigned long irq_flags; - int nentries; - size_t nbytes; - - DBUG_ON(ch->remote_nentries <= 0); - - for (nentries = ch->remote_nentries; nentries > 0; nentries--) { - - nbytes = nentries * ch->entry_size; - ch_sn2->remote_msgqueue = - xpc_kzalloc_cacheline_aligned(nbytes, GFP_KERNEL, &ch_sn2-> - remote_msgqueue_base); - if (ch_sn2->remote_msgqueue == NULL) - continue; - - spin_lock_irqsave(&ch->lock, irq_flags); - if (nentries < ch->remote_nentries) { - dev_dbg(xpc_chan, "nentries=%d remote_nentries=%d, " - "partid=%d, channel=%d\n", nentries, - ch->remote_nentries, ch->partid, ch->number); - - ch->remote_nentries = nentries; - } - spin_unlock_irqrestore(&ch->lock, irq_flags); - return xpSuccess; - } - - dev_dbg(xpc_chan, "can't get memory for cached remote message queue, " - "partid=%d, channel=%d\n", ch->partid, ch->number); - return xpNoMemory; -} - -/* - * Allocate message queues and other stuff associated with a channel. - * - * Note: Assumes all of the channel sizes are filled in. - */ -static enum xp_retval -xpc_setup_msg_structures_sn2(struct xpc_channel *ch) -{ - struct xpc_channel_sn2 *ch_sn2 = &ch->sn.sn2; - enum xp_retval ret; - - DBUG_ON(ch->flags & XPC_C_SETUP); - - ret = xpc_allocate_local_msgqueue_sn2(ch); - if (ret == xpSuccess) { - - ret = xpc_allocate_remote_msgqueue_sn2(ch); - if (ret != xpSuccess) { - kfree(ch_sn2->local_msgqueue_base); - ch_sn2->local_msgqueue = NULL; - kfree(ch_sn2->notify_queue); - ch_sn2->notify_queue = NULL; - } - } - return ret; -} - -/* - * Free up message queues and other stuff that were allocated for the specified - * channel. - */ -static void -xpc_teardown_msg_structures_sn2(struct xpc_channel *ch) -{ - struct xpc_channel_sn2 *ch_sn2 = &ch->sn.sn2; - - lockdep_assert_held(&ch->lock); - - ch_sn2->remote_msgqueue_pa = 0; - - ch_sn2->local_GP->get = 0; - ch_sn2->local_GP->put = 0; - ch_sn2->remote_GP.get = 0; - ch_sn2->remote_GP.put = 0; - ch_sn2->w_local_GP.get = 0; - ch_sn2->w_local_GP.put = 0; - ch_sn2->w_remote_GP.get = 0; - ch_sn2->w_remote_GP.put = 0; - ch_sn2->next_msg_to_pull = 0; - - if (ch->flags & XPC_C_SETUP) { - dev_dbg(xpc_chan, "ch->flags=0x%x, partid=%d, channel=%d\n", - ch->flags, ch->partid, ch->number); - - kfree(ch_sn2->local_msgqueue_base); - ch_sn2->local_msgqueue = NULL; - kfree(ch_sn2->remote_msgqueue_base); - ch_sn2->remote_msgqueue = NULL; - kfree(ch_sn2->notify_queue); - ch_sn2->notify_queue = NULL; - } -} - -/* - * Notify those who wanted to be notified upon delivery of their message. - */ -static void -xpc_notify_senders_sn2(struct xpc_channel *ch, enum xp_retval reason, s64 put) -{ - struct xpc_notify_sn2 *notify; - u8 notify_type; - s64 get = ch->sn.sn2.w_remote_GP.get - 1; - - while (++get < put && atomic_read(&ch->n_to_notify) > 0) { - - notify = &ch->sn.sn2.notify_queue[get % ch->local_nentries]; - - /* - * See if the notify entry indicates it was associated with - * a message who's sender wants to be notified. It is possible - * that it is, but someone else is doing or has done the - * notification. - */ - notify_type = notify->type; - if (notify_type == 0 || - cmpxchg(¬ify->type, notify_type, 0) != notify_type) { - continue; - } - - DBUG_ON(notify_type != XPC_N_CALL); - - atomic_dec(&ch->n_to_notify); - - if (notify->func != NULL) { - dev_dbg(xpc_chan, "notify->func() called, notify=0x%p " - "msg_number=%lld partid=%d channel=%d\n", - (void *)notify, get, ch->partid, ch->number); - - notify->func(reason, ch->partid, ch->number, - notify->key); - - dev_dbg(xpc_chan, "notify->func() returned, notify=0x%p" - " msg_number=%lld partid=%d channel=%d\n", - (void *)notify, get, ch->partid, ch->number); - } - } -} - -static void -xpc_notify_senders_of_disconnect_sn2(struct xpc_channel *ch) -{ - xpc_notify_senders_sn2(ch, ch->reason, ch->sn.sn2.w_local_GP.put); -} - -/* - * Clear some of the msg flags in the local message queue. - */ -static inline void -xpc_clear_local_msgqueue_flags_sn2(struct xpc_channel *ch) -{ - struct xpc_channel_sn2 *ch_sn2 = &ch->sn.sn2; - struct xpc_msg_sn2 *msg; - s64 get; - - get = ch_sn2->w_remote_GP.get; - do { - msg = (struct xpc_msg_sn2 *)((u64)ch_sn2->local_msgqueue + - (get % ch->local_nentries) * - ch->entry_size); - DBUG_ON(!(msg->flags & XPC_M_SN2_READY)); - msg->flags = 0; - } while (++get < ch_sn2->remote_GP.get); -} - -/* - * Clear some of the msg flags in the remote message queue. - */ -static inline void -xpc_clear_remote_msgqueue_flags_sn2(struct xpc_channel *ch) -{ - struct xpc_channel_sn2 *ch_sn2 = &ch->sn.sn2; - struct xpc_msg_sn2 *msg; - s64 put, remote_nentries = ch->remote_nentries; - - /* flags are zeroed when the buffer is allocated */ - if (ch_sn2->remote_GP.put < remote_nentries) - return; - - put = max(ch_sn2->w_remote_GP.put, remote_nentries); - do { - msg = (struct xpc_msg_sn2 *)((u64)ch_sn2->remote_msgqueue + - (put % remote_nentries) * - ch->entry_size); - DBUG_ON(!(msg->flags & XPC_M_SN2_READY)); - DBUG_ON(!(msg->flags & XPC_M_SN2_DONE)); - DBUG_ON(msg->number != put - remote_nentries); - msg->flags = 0; - } while (++put < ch_sn2->remote_GP.put); -} - -static int -xpc_n_of_deliverable_payloads_sn2(struct xpc_channel *ch) -{ - return ch->sn.sn2.w_remote_GP.put - ch->sn.sn2.w_local_GP.get; -} - -static void -xpc_process_msg_chctl_flags_sn2(struct xpc_partition *part, int ch_number) -{ - struct xpc_channel *ch = &part->channels[ch_number]; - struct xpc_channel_sn2 *ch_sn2 = &ch->sn.sn2; - int npayloads_sent; - - ch_sn2->remote_GP = part->sn.sn2.remote_GPs[ch_number]; - - /* See what, if anything, has changed for each connected channel */ - - xpc_msgqueue_ref(ch); - - if (ch_sn2->w_remote_GP.get == ch_sn2->remote_GP.get && - ch_sn2->w_remote_GP.put == ch_sn2->remote_GP.put) { - /* nothing changed since GPs were last pulled */ - xpc_msgqueue_deref(ch); - return; - } - - if (!(ch->flags & XPC_C_CONNECTED)) { - xpc_msgqueue_deref(ch); - return; - } - - /* - * First check to see if messages recently sent by us have been - * received by the other side. (The remote GET value will have - * changed since we last looked at it.) - */ - - if (ch_sn2->w_remote_GP.get != ch_sn2->remote_GP.get) { - - /* - * We need to notify any senders that want to be notified - * that their sent messages have been received by their - * intended recipients. We need to do this before updating - * w_remote_GP.get so that we don't allocate the same message - * queue entries prematurely (see xpc_allocate_msg()). - */ - if (atomic_read(&ch->n_to_notify) > 0) { - /* - * Notify senders that messages sent have been - * received and delivered by the other side. - */ - xpc_notify_senders_sn2(ch, xpMsgDelivered, - ch_sn2->remote_GP.get); - } - - /* - * Clear msg->flags in previously sent messages, so that - * they're ready for xpc_allocate_msg(). - */ - xpc_clear_local_msgqueue_flags_sn2(ch); - - ch_sn2->w_remote_GP.get = ch_sn2->remote_GP.get; - - dev_dbg(xpc_chan, "w_remote_GP.get changed to %lld, partid=%d, " - "channel=%d\n", ch_sn2->w_remote_GP.get, ch->partid, - ch->number); - - /* - * If anyone was waiting for message queue entries to become - * available, wake them up. - */ - if (atomic_read(&ch->n_on_msg_allocate_wq) > 0) - wake_up(&ch->msg_allocate_wq); - } - - /* - * Now check for newly sent messages by the other side. (The remote - * PUT value will have changed since we last looked at it.) - */ - - if (ch_sn2->w_remote_GP.put != ch_sn2->remote_GP.put) { - /* - * Clear msg->flags in previously received messages, so that - * they're ready for xpc_get_deliverable_payload_sn2(). - */ - xpc_clear_remote_msgqueue_flags_sn2(ch); - - smp_wmb(); /* ensure flags have been cleared before bte_copy */ - ch_sn2->w_remote_GP.put = ch_sn2->remote_GP.put; - - dev_dbg(xpc_chan, "w_remote_GP.put changed to %lld, partid=%d, " - "channel=%d\n", ch_sn2->w_remote_GP.put, ch->partid, - ch->number); - - npayloads_sent = xpc_n_of_deliverable_payloads_sn2(ch); - if (npayloads_sent > 0) { - dev_dbg(xpc_chan, "msgs waiting to be copied and " - "delivered=%d, partid=%d, channel=%d\n", - npayloads_sent, ch->partid, ch->number); - - if (ch->flags & XPC_C_CONNECTEDCALLOUT_MADE) - xpc_activate_kthreads(ch, npayloads_sent); - } - } - - xpc_msgqueue_deref(ch); -} - -static struct xpc_msg_sn2 * -xpc_pull_remote_msg_sn2(struct xpc_channel *ch, s64 get) -{ - struct xpc_partition *part = &xpc_partitions[ch->partid]; - struct xpc_channel_sn2 *ch_sn2 = &ch->sn.sn2; - unsigned long remote_msg_pa; - struct xpc_msg_sn2 *msg; - u32 msg_index; - u32 nmsgs; - u64 msg_offset; - enum xp_retval ret; - - if (mutex_lock_interruptible(&ch_sn2->msg_to_pull_mutex) != 0) { - /* we were interrupted by a signal */ - return NULL; - } - - while (get >= ch_sn2->next_msg_to_pull) { - - /* pull as many messages as are ready and able to be pulled */ - - msg_index = ch_sn2->next_msg_to_pull % ch->remote_nentries; - - DBUG_ON(ch_sn2->next_msg_to_pull >= ch_sn2->w_remote_GP.put); - nmsgs = ch_sn2->w_remote_GP.put - ch_sn2->next_msg_to_pull; - if (msg_index + nmsgs > ch->remote_nentries) { - /* ignore the ones that wrap the msg queue for now */ - nmsgs = ch->remote_nentries - msg_index; - } - - msg_offset = msg_index * ch->entry_size; - msg = (struct xpc_msg_sn2 *)((u64)ch_sn2->remote_msgqueue + - msg_offset); - remote_msg_pa = ch_sn2->remote_msgqueue_pa + msg_offset; - - ret = xpc_pull_remote_cachelines_sn2(part, msg, remote_msg_pa, - nmsgs * ch->entry_size); - if (ret != xpSuccess) { - - dev_dbg(xpc_chan, "failed to pull %d msgs starting with" - " msg %lld from partition %d, channel=%d, " - "ret=%d\n", nmsgs, ch_sn2->next_msg_to_pull, - ch->partid, ch->number, ret); - - XPC_DEACTIVATE_PARTITION(part, ret); - - mutex_unlock(&ch_sn2->msg_to_pull_mutex); - return NULL; - } - - ch_sn2->next_msg_to_pull += nmsgs; - } - - mutex_unlock(&ch_sn2->msg_to_pull_mutex); - - /* return the message we were looking for */ - msg_offset = (get % ch->remote_nentries) * ch->entry_size; - msg = (struct xpc_msg_sn2 *)((u64)ch_sn2->remote_msgqueue + msg_offset); - - return msg; -} - -/* - * Get the next deliverable message's payload. - */ -static void * -xpc_get_deliverable_payload_sn2(struct xpc_channel *ch) -{ - struct xpc_channel_sn2 *ch_sn2 = &ch->sn.sn2; - struct xpc_msg_sn2 *msg; - void *payload = NULL; - s64 get; - - do { - if (ch->flags & XPC_C_DISCONNECTING) - break; - - get = ch_sn2->w_local_GP.get; - smp_rmb(); /* guarantee that .get loads before .put */ - if (get == ch_sn2->w_remote_GP.put) - break; - - /* There are messages waiting to be pulled and delivered. - * We need to try to secure one for ourselves. We'll do this - * by trying to increment w_local_GP.get and hope that no one - * else beats us to it. If they do, we'll we'll simply have - * to try again for the next one. - */ - - if (cmpxchg(&ch_sn2->w_local_GP.get, get, get + 1) == get) { - /* we got the entry referenced by get */ - - dev_dbg(xpc_chan, "w_local_GP.get changed to %lld, " - "partid=%d, channel=%d\n", get + 1, - ch->partid, ch->number); - - /* pull the message from the remote partition */ - - msg = xpc_pull_remote_msg_sn2(ch, get); - - if (msg != NULL) { - DBUG_ON(msg->number != get); - DBUG_ON(msg->flags & XPC_M_SN2_DONE); - DBUG_ON(!(msg->flags & XPC_M_SN2_READY)); - - payload = &msg->payload; - } - break; - } - - } while (1); - - return payload; -} - -/* - * Now we actually send the messages that are ready to be sent by advancing - * the local message queue's Put value and then send a chctl msgrequest to the - * recipient partition. - */ -static void -xpc_send_msgs_sn2(struct xpc_channel *ch, s64 initial_put) -{ - struct xpc_channel_sn2 *ch_sn2 = &ch->sn.sn2; - struct xpc_msg_sn2 *msg; - s64 put = initial_put + 1; - int send_msgrequest = 0; - - while (1) { - - while (1) { - if (put == ch_sn2->w_local_GP.put) - break; - - msg = (struct xpc_msg_sn2 *)((u64)ch_sn2-> - local_msgqueue + (put % - ch->local_nentries) * - ch->entry_size); - - if (!(msg->flags & XPC_M_SN2_READY)) - break; - - put++; - } - - if (put == initial_put) { - /* nothing's changed */ - break; - } - - if (cmpxchg_rel(&ch_sn2->local_GP->put, initial_put, put) != - initial_put) { - /* someone else beat us to it */ - DBUG_ON(ch_sn2->local_GP->put < initial_put); - break; - } - - /* we just set the new value of local_GP->put */ - - dev_dbg(xpc_chan, "local_GP->put changed to %lld, partid=%d, " - "channel=%d\n", put, ch->partid, ch->number); - - send_msgrequest = 1; - - /* - * We need to ensure that the message referenced by - * local_GP->put is not XPC_M_SN2_READY or that local_GP->put - * equals w_local_GP.put, so we'll go have a look. - */ - initial_put = put; - } - - if (send_msgrequest) - xpc_send_chctl_msgrequest_sn2(ch); -} - -/* - * Allocate an entry for a message from the message queue associated with the - * specified channel. - */ -static enum xp_retval -xpc_allocate_msg_sn2(struct xpc_channel *ch, u32 flags, - struct xpc_msg_sn2 **address_of_msg) -{ - struct xpc_channel_sn2 *ch_sn2 = &ch->sn.sn2; - struct xpc_msg_sn2 *msg; - enum xp_retval ret; - s64 put; - - /* - * Get the next available message entry from the local message queue. - * If none are available, we'll make sure that we grab the latest - * GP values. - */ - ret = xpTimeout; - - while (1) { - - put = ch_sn2->w_local_GP.put; - smp_rmb(); /* guarantee that .put loads before .get */ - if (put - ch_sn2->w_remote_GP.get < ch->local_nentries) { - - /* There are available message entries. We need to try - * to secure one for ourselves. We'll do this by trying - * to increment w_local_GP.put as long as someone else - * doesn't beat us to it. If they do, we'll have to - * try again. - */ - if (cmpxchg(&ch_sn2->w_local_GP.put, put, put + 1) == - put) { - /* we got the entry referenced by put */ - break; - } - continue; /* try again */ - } - - /* - * There aren't any available msg entries at this time. - * - * In waiting for a message entry to become available, - * we set a timeout in case the other side is not sending - * completion interrupts. This lets us fake a notify IRQ - * that will cause the notify IRQ handler to fetch the latest - * GP values as if an interrupt was sent by the other side. - */ - if (ret == xpTimeout) - xpc_send_chctl_local_msgrequest_sn2(ch); - - if (flags & XPC_NOWAIT) - return xpNoWait; - - ret = xpc_allocate_msg_wait(ch); - if (ret != xpInterrupted && ret != xpTimeout) - return ret; - } - - /* get the message's address and initialize it */ - msg = (struct xpc_msg_sn2 *)((u64)ch_sn2->local_msgqueue + - (put % ch->local_nentries) * - ch->entry_size); - - DBUG_ON(msg->flags != 0); - msg->number = put; - - dev_dbg(xpc_chan, "w_local_GP.put changed to %lld; msg=0x%p, " - "msg_number=%lld, partid=%d, channel=%d\n", put + 1, - (void *)msg, msg->number, ch->partid, ch->number); - - *address_of_msg = msg; - return xpSuccess; -} - -/* - * Common code that does the actual sending of the message by advancing the - * local message queue's Put value and sends a chctl msgrequest to the - * partition the message is being sent to. - */ -static enum xp_retval -xpc_send_payload_sn2(struct xpc_channel *ch, u32 flags, void *payload, - u16 payload_size, u8 notify_type, xpc_notify_func func, - void *key) -{ - enum xp_retval ret = xpSuccess; - struct xpc_channel_sn2 *ch_sn2 = &ch->sn.sn2; - struct xpc_msg_sn2 *msg = msg; - struct xpc_notify_sn2 *notify = notify; - s64 msg_number; - s64 put; - - DBUG_ON(notify_type == XPC_N_CALL && func == NULL); - - if (XPC_MSG_SIZE(payload_size) > ch->entry_size) - return xpPayloadTooBig; - - xpc_msgqueue_ref(ch); - - if (ch->flags & XPC_C_DISCONNECTING) { - ret = ch->reason; - goto out_1; - } - if (!(ch->flags & XPC_C_CONNECTED)) { - ret = xpNotConnected; - goto out_1; - } - - ret = xpc_allocate_msg_sn2(ch, flags, &msg); - if (ret != xpSuccess) - goto out_1; - - msg_number = msg->number; - - if (notify_type != 0) { - /* - * Tell the remote side to send an ACK interrupt when the - * message has been delivered. - */ - msg->flags |= XPC_M_SN2_INTERRUPT; - - atomic_inc(&ch->n_to_notify); - - notify = &ch_sn2->notify_queue[msg_number % ch->local_nentries]; - notify->func = func; - notify->key = key; - notify->type = notify_type; - - /* ??? Is a mb() needed here? */ - - if (ch->flags & XPC_C_DISCONNECTING) { - /* - * An error occurred between our last error check and - * this one. We will try to clear the type field from - * the notify entry. If we succeed then - * xpc_disconnect_channel() didn't already process - * the notify entry. - */ - if (cmpxchg(¬ify->type, notify_type, 0) == - notify_type) { - atomic_dec(&ch->n_to_notify); - ret = ch->reason; - } - goto out_1; - } - } - - memcpy(&msg->payload, payload, payload_size); - - msg->flags |= XPC_M_SN2_READY; - - /* - * The preceding store of msg->flags must occur before the following - * load of local_GP->put. - */ - smp_mb(); - - /* see if the message is next in line to be sent, if so send it */ - - put = ch_sn2->local_GP->put; - if (put == msg_number) - xpc_send_msgs_sn2(ch, put); - -out_1: - xpc_msgqueue_deref(ch); - return ret; -} - -/* - * Now we actually acknowledge the messages that have been delivered and ack'd - * by advancing the cached remote message queue's Get value and if requested - * send a chctl msgrequest to the message sender's partition. - * - * If a message has XPC_M_SN2_INTERRUPT set, send an interrupt to the partition - * that sent the message. - */ -static void -xpc_acknowledge_msgs_sn2(struct xpc_channel *ch, s64 initial_get, u8 msg_flags) -{ - struct xpc_channel_sn2 *ch_sn2 = &ch->sn.sn2; - struct xpc_msg_sn2 *msg; - s64 get = initial_get + 1; - int send_msgrequest = 0; - - while (1) { - - while (1) { - if (get == ch_sn2->w_local_GP.get) - break; - - msg = (struct xpc_msg_sn2 *)((u64)ch_sn2-> - remote_msgqueue + (get % - ch->remote_nentries) * - ch->entry_size); - - if (!(msg->flags & XPC_M_SN2_DONE)) - break; - - msg_flags |= msg->flags; - get++; - } - - if (get == initial_get) { - /* nothing's changed */ - break; - } - - if (cmpxchg_rel(&ch_sn2->local_GP->get, initial_get, get) != - initial_get) { - /* someone else beat us to it */ - DBUG_ON(ch_sn2->local_GP->get <= initial_get); - break; - } - - /* we just set the new value of local_GP->get */ - - dev_dbg(xpc_chan, "local_GP->get changed to %lld, partid=%d, " - "channel=%d\n", get, ch->partid, ch->number); - - send_msgrequest = (msg_flags & XPC_M_SN2_INTERRUPT); - - /* - * We need to ensure that the message referenced by - * local_GP->get is not XPC_M_SN2_DONE or that local_GP->get - * equals w_local_GP.get, so we'll go have a look. - */ - initial_get = get; - } - - if (send_msgrequest) - xpc_send_chctl_msgrequest_sn2(ch); -} - -static void -xpc_received_payload_sn2(struct xpc_channel *ch, void *payload) -{ - struct xpc_msg_sn2 *msg; - s64 msg_number; - s64 get; - - msg = container_of(payload, struct xpc_msg_sn2, payload); - msg_number = msg->number; - - dev_dbg(xpc_chan, "msg=0x%p, msg_number=%lld, partid=%d, channel=%d\n", - (void *)msg, msg_number, ch->partid, ch->number); - - DBUG_ON((((u64)msg - (u64)ch->sn.sn2.remote_msgqueue) / ch->entry_size) != - msg_number % ch->remote_nentries); - DBUG_ON(!(msg->flags & XPC_M_SN2_READY)); - DBUG_ON(msg->flags & XPC_M_SN2_DONE); - - msg->flags |= XPC_M_SN2_DONE; - - /* - * The preceding store of msg->flags must occur before the following - * load of local_GP->get. - */ - smp_mb(); - - /* - * See if this message is next in line to be acknowledged as having - * been delivered. - */ - get = ch->sn.sn2.local_GP->get; - if (get == msg_number) - xpc_acknowledge_msgs_sn2(ch, get, msg->flags); -} - -static struct xpc_arch_operations xpc_arch_ops_sn2 = { - .setup_partitions = xpc_setup_partitions_sn2, - .teardown_partitions = xpc_teardown_partitions_sn2, - .process_activate_IRQ_rcvd = xpc_process_activate_IRQ_rcvd_sn2, - .get_partition_rsvd_page_pa = xpc_get_partition_rsvd_page_pa_sn2, - .setup_rsvd_page = xpc_setup_rsvd_page_sn2, - - .allow_hb = xpc_allow_hb_sn2, - .disallow_hb = xpc_disallow_hb_sn2, - .disallow_all_hbs = xpc_disallow_all_hbs_sn2, - .increment_heartbeat = xpc_increment_heartbeat_sn2, - .offline_heartbeat = xpc_offline_heartbeat_sn2, - .online_heartbeat = xpc_online_heartbeat_sn2, - .heartbeat_init = xpc_heartbeat_init_sn2, - .heartbeat_exit = xpc_heartbeat_exit_sn2, - .get_remote_heartbeat = xpc_get_remote_heartbeat_sn2, - - .request_partition_activation = - xpc_request_partition_activation_sn2, - .request_partition_reactivation = - xpc_request_partition_reactivation_sn2, - .request_partition_deactivation = - xpc_request_partition_deactivation_sn2, - .cancel_partition_deactivation_request = - xpc_cancel_partition_deactivation_request_sn2, - - .setup_ch_structures = xpc_setup_ch_structures_sn2, - .teardown_ch_structures = xpc_teardown_ch_structures_sn2, - - .make_first_contact = xpc_make_first_contact_sn2, - - .get_chctl_all_flags = xpc_get_chctl_all_flags_sn2, - .send_chctl_closerequest = xpc_send_chctl_closerequest_sn2, - .send_chctl_closereply = xpc_send_chctl_closereply_sn2, - .send_chctl_openrequest = xpc_send_chctl_openrequest_sn2, - .send_chctl_openreply = xpc_send_chctl_openreply_sn2, - .send_chctl_opencomplete = xpc_send_chctl_opencomplete_sn2, - .process_msg_chctl_flags = xpc_process_msg_chctl_flags_sn2, - - .save_remote_msgqueue_pa = xpc_save_remote_msgqueue_pa_sn2, - - .setup_msg_structures = xpc_setup_msg_structures_sn2, - .teardown_msg_structures = xpc_teardown_msg_structures_sn2, - - .indicate_partition_engaged = xpc_indicate_partition_engaged_sn2, - .indicate_partition_disengaged = xpc_indicate_partition_disengaged_sn2, - .partition_engaged = xpc_partition_engaged_sn2, - .any_partition_engaged = xpc_any_partition_engaged_sn2, - .assume_partition_disengaged = xpc_assume_partition_disengaged_sn2, - - .n_of_deliverable_payloads = xpc_n_of_deliverable_payloads_sn2, - .send_payload = xpc_send_payload_sn2, - .get_deliverable_payload = xpc_get_deliverable_payload_sn2, - .received_payload = xpc_received_payload_sn2, - .notify_senders_of_disconnect = xpc_notify_senders_of_disconnect_sn2, -}; - -int -xpc_init_sn2(void) -{ - int ret; - size_t buf_size; - - xpc_arch_ops = xpc_arch_ops_sn2; - - if (offsetof(struct xpc_msg_sn2, payload) > XPC_MSG_HDR_MAX_SIZE) { - dev_err(xpc_part, "header portion of struct xpc_msg_sn2 is " - "larger than %d\n", XPC_MSG_HDR_MAX_SIZE); - return -E2BIG; - } - - buf_size = max(XPC_RP_VARS_SIZE, - XPC_RP_HEADER_SIZE + XP_NASID_MASK_BYTES_SN2); - xpc_remote_copy_buffer_sn2 = xpc_kmalloc_cacheline_aligned(buf_size, - GFP_KERNEL, - &xpc_remote_copy_buffer_base_sn2); - if (xpc_remote_copy_buffer_sn2 == NULL) { - dev_err(xpc_part, "can't get memory for remote copy buffer\n"); - return -ENOMEM; - } - - /* open up protections for IPI and [potentially] amo operations */ - xpc_allow_IPI_ops_sn2(); - xpc_allow_amo_ops_shub_wars_1_1_sn2(); - - /* - * This is safe to do before the xpc_hb_checker thread has started - * because the handler releases a wait queue. If an interrupt is - * received before the thread is waiting, it will not go to sleep, - * but rather immediately process the interrupt. - */ - ret = request_irq(SGI_XPC_ACTIVATE, xpc_handle_activate_IRQ_sn2, 0, - "xpc hb", NULL); - if (ret != 0) { - dev_err(xpc_part, "can't register ACTIVATE IRQ handler, " - "errno=%d\n", -ret); - xpc_disallow_IPI_ops_sn2(); - kfree(xpc_remote_copy_buffer_base_sn2); - } - return ret; -} - -void -xpc_exit_sn2(void) -{ - free_irq(SGI_XPC_ACTIVATE, NULL); - xpc_disallow_IPI_ops_sn2(); - kfree(xpc_remote_copy_buffer_base_sn2); -} diff --git a/drivers/misc/sgi-xp/xpc_uv.c b/drivers/misc/sgi-xp/xpc_uv.c index 0c6de97dd347..09e24659ef3d 100644 --- a/drivers/misc/sgi-xp/xpc_uv.c +++ b/drivers/misc/sgi-xp/xpc_uv.c @@ -27,7 +27,7 @@ #if defined CONFIG_X86_64 #include <asm/uv/bios.h> #include <asm/uv/uv_irq.h> -#elif defined CONFIG_IA64_GENERIC || defined CONFIG_IA64_SGI_UV +#elif defined CONFIG_IA64_SGI_UV #include <asm/sn/intr.h> #include <asm/sn/sn_sal.h> #endif @@ -35,7 +35,7 @@ #include "../sgi-gru/grukservices.h" #include "xpc.h" -#if defined CONFIG_IA64_GENERIC || defined CONFIG_IA64_SGI_UV +#if defined CONFIG_IA64_SGI_UV struct uv_IO_APIC_route_entry { __u64 vector : 8, delivery_mode : 3, @@ -48,6 +48,8 @@ struct uv_IO_APIC_route_entry { __reserved_2 : 15, dest : 32; }; + +#define sn_partition_id 0 #endif static struct xpc_heartbeat_uv *xpc_heartbeat_uv; @@ -119,7 +121,7 @@ xpc_get_gru_mq_irq_uv(struct xpc_gru_mq_uv *mq, int cpu, char *irq_name) mq->mmr_value = uv_read_global_mmr64(mmr_pnode, mq->mmr_offset); -#elif defined CONFIG_IA64_GENERIC || defined CONFIG_IA64_SGI_UV +#elif defined CONFIG_IA64_SGI_UV if (strcmp(irq_name, XPC_ACTIVATE_IRQ_NAME) == 0) mq->irq = SGI_XPC_ACTIVATE; else if (strcmp(irq_name, XPC_NOTIFY_IRQ_NAME) == 0) @@ -142,7 +144,7 @@ xpc_release_gru_mq_irq_uv(struct xpc_gru_mq_uv *mq) #if defined CONFIG_X86_64 uv_teardown_irq(mq->irq); -#elif defined CONFIG_IA64_GENERIC || defined CONFIG_IA64_SGI_UV +#elif defined CONFIG_IA64_SGI_UV int mmr_pnode; unsigned long mmr_value; @@ -160,7 +162,7 @@ xpc_gru_mq_watchlist_alloc_uv(struct xpc_gru_mq_uv *mq) { int ret; -#if defined CONFIG_IA64_GENERIC || defined CONFIG_IA64_SGI_UV +#if defined CONFIG_IA64_SGI_UV int mmr_pnode = uv_blade_to_pnode(mq->mmr_blade); ret = sn_mq_watchlist_alloc(mmr_pnode, (void *)uv_gpa(mq->address), @@ -195,7 +197,7 @@ xpc_gru_mq_watchlist_free_uv(struct xpc_gru_mq_uv *mq) #if defined CONFIG_X86_64 ret = uv_bios_mq_watchlist_free(mmr_pnode, mq->watchlist_num); BUG_ON(ret != BIOS_STATUS_SUCCESS); -#elif defined CONFIG_IA64_GENERIC || defined CONFIG_IA64_SGI_UV +#elif defined CONFIG_IA64_SGI_UV ret = sn_mq_watchlist_free(mmr_pnode, mq->watchlist_num); BUG_ON(ret != SALRET_OK); #else @@ -794,7 +796,7 @@ xpc_get_partition_rsvd_page_pa_uv(void *buf, u64 *cookie, unsigned long *rp_pa, else ret = xpBiosError; -#elif defined CONFIG_IA64_GENERIC || defined CONFIG_IA64_SGI_UV +#elif defined CONFIG_IA64_SGI_UV status = sn_partition_reserved_page_pa((u64)buf, cookie, rp_pa, len); if (status == SALRET_OK) ret = xpSuccess; diff --git a/drivers/misc/sgi-xp/xpnet.c b/drivers/misc/sgi-xp/xpnet.c index 44d750d98bc8..f7d610a22347 100644 --- a/drivers/misc/sgi-xp/xpnet.c +++ b/drivers/misc/sgi-xp/xpnet.c @@ -515,7 +515,7 @@ xpnet_init(void) { int result; - if (!is_shub() && !is_uv()) + if (!is_uv()) return -ENODEV; dev_info(xpnet, "registering network device %s\n", XPNET_DEVICE_NAME); diff --git a/drivers/pci/hotplug/Kconfig b/drivers/pci/hotplug/Kconfig index e9f78eb390d2..e7b493c22bf3 100644 --- a/drivers/pci/hotplug/Kconfig +++ b/drivers/pci/hotplug/Kconfig @@ -147,15 +147,6 @@ config HOTPLUG_PCI_RPA_DLPAR When in doubt, say N. -config HOTPLUG_PCI_SGI - tristate "SGI PCI Hotplug Support" - depends on IA64_SGI_SN2 || IA64_GENERIC - help - Say Y here if you want to use the SGI Altix Hotplug - Driver for PCI devices. - - When in doubt, say N. - config HOTPLUG_PCI_S390 bool "System z PCI Hotplug Support" depends on S390 && 64BIT diff --git a/drivers/pci/hotplug/Makefile b/drivers/pci/hotplug/Makefile index 7e3331603714..5196983220df 100644 --- a/drivers/pci/hotplug/Makefile +++ b/drivers/pci/hotplug/Makefile @@ -18,7 +18,6 @@ obj-$(CONFIG_HOTPLUG_PCI_SHPC) += shpchp.o obj-$(CONFIG_HOTPLUG_PCI_POWERNV) += pnv-php.o obj-$(CONFIG_HOTPLUG_PCI_RPA) += rpaphp.o obj-$(CONFIG_HOTPLUG_PCI_RPA_DLPAR) += rpadlpar_io.o -obj-$(CONFIG_HOTPLUG_PCI_SGI) += sgi_hotplug.o obj-$(CONFIG_HOTPLUG_PCI_ACPI) += acpiphp.o obj-$(CONFIG_HOTPLUG_PCI_S390) += s390_pci_hpc.o diff --git a/drivers/pci/hotplug/sgi_hotplug.c b/drivers/pci/hotplug/sgi_hotplug.c deleted file mode 100644 index 231f5bdd3d2d..000000000000 --- a/drivers/pci/hotplug/sgi_hotplug.c +++ /dev/null @@ -1,700 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright (C) 2005-2006 Silicon Graphics, Inc. All rights reserved. - * - * This work was based on the 2.4/2.6 kernel development by Dick Reigner. - * Work to add BIOS PROM support was completed by Mike Habeck. - */ - -#include <linux/acpi.h> -#include <linux/init.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/pci.h> -#include <linux/pci_hotplug.h> -#include <linux/proc_fs.h> -#include <linux/slab.h> -#include <linux/types.h> -#include <linux/mutex.h> - -#include <asm/sn/addrs.h> -#include <asm/sn/geo.h> -#include <asm/sn/l1.h> -#include <asm/sn/module.h> -#include <asm/sn/pcibr_provider.h> -#include <asm/sn/pcibus_provider_defs.h> -#include <asm/sn/pcidev.h> -#include <asm/sn/sn_feature_sets.h> -#include <asm/sn/sn_sal.h> -#include <asm/sn/types.h> -#include <asm/sn/acpi.h> - -#include "../pci.h" - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("SGI (prarit@sgi.com, dickie@sgi.com, habeck@sgi.com)"); -MODULE_DESCRIPTION("SGI Altix Hot Plug PCI Controller Driver"); - - -/* SAL call error codes. Keep in sync with prom header io/include/pcibr.h */ -#define PCI_SLOT_ALREADY_UP 2 /* slot already up */ -#define PCI_SLOT_ALREADY_DOWN 3 /* slot already down */ -#define PCI_L1_ERR 7 /* L1 console command error */ -#define PCI_EMPTY_33MHZ 15 /* empty 33 MHz bus */ - - -#define PCIIO_ASIC_TYPE_TIOCA 4 -#define PCI_L1_QSIZE 128 /* our L1 message buffer size */ -#define SN_MAX_HP_SLOTS 32 /* max hotplug slots */ -#define SN_SLOT_NAME_SIZE 33 /* size of name string */ - -/* internal list head */ -static struct list_head sn_hp_list; - -/* hotplug_slot struct's private pointer */ -struct slot { - int device_num; - struct pci_bus *pci_bus; - /* this struct for glue internal only */ - struct hotplug_slot hotplug_slot; - struct list_head hp_list; - char physical_path[SN_SLOT_NAME_SIZE]; -}; - -struct pcibr_slot_enable_resp { - int resp_sub_errno; - char resp_l1_msg[PCI_L1_QSIZE + 1]; -}; - -struct pcibr_slot_disable_resp { - int resp_sub_errno; - char resp_l1_msg[PCI_L1_QSIZE + 1]; -}; - -enum sn_pci_req_e { - PCI_REQ_SLOT_ELIGIBLE, - PCI_REQ_SLOT_DISABLE -}; - -static int enable_slot(struct hotplug_slot *slot); -static int disable_slot(struct hotplug_slot *slot); -static inline int get_power_status(struct hotplug_slot *slot, u8 *value); - -static const struct hotplug_slot_ops sn_hotplug_slot_ops = { - .enable_slot = enable_slot, - .disable_slot = disable_slot, - .get_power_status = get_power_status, -}; - -static DEFINE_MUTEX(sn_hotplug_mutex); - -static struct slot *to_slot(struct hotplug_slot *bss_hotplug_slot) -{ - return container_of(bss_hotplug_slot, struct slot, hotplug_slot); -} - -static ssize_t path_show(struct pci_slot *pci_slot, char *buf) -{ - int retval = -ENOENT; - struct slot *slot = to_slot(pci_slot->hotplug); - - if (!slot) - return retval; - - retval = sprintf(buf, "%s\n", slot->physical_path); - return retval; -} - -static struct pci_slot_attribute sn_slot_path_attr = __ATTR_RO(path); - -static int sn_pci_slot_valid(struct pci_bus *pci_bus, int device) -{ - struct pcibus_info *pcibus_info; - u16 busnum, segment, ioboard_type; - - pcibus_info = SN_PCIBUS_BUSSOFT_INFO(pci_bus); - - /* Check to see if this is a valid slot on 'pci_bus' */ - if (!(pcibus_info->pbi_valid_devices & (1 << device))) - return -EPERM; - - ioboard_type = sn_ioboard_to_pci_bus(pci_bus); - busnum = pcibus_info->pbi_buscommon.bs_persist_busnum; - segment = pci_domain_nr(pci_bus) & 0xf; - - /* Do not allow hotplug operations on base I/O cards */ - if ((ioboard_type == L1_BRICKTYPE_IX || - ioboard_type == L1_BRICKTYPE_IA) && - (segment == 1 && busnum == 0 && device != 1)) - return -EPERM; - - return 1; -} - -static int sn_pci_bus_valid(struct pci_bus *pci_bus) -{ - struct pcibus_info *pcibus_info; - u32 asic_type; - u16 ioboard_type; - - /* Don't register slots hanging off the TIOCA bus */ - pcibus_info = SN_PCIBUS_BUSSOFT_INFO(pci_bus); - asic_type = pcibus_info->pbi_buscommon.bs_asic_type; - if (asic_type == PCIIO_ASIC_TYPE_TIOCA) - return -EPERM; - - /* Only register slots in I/O Bricks that support hotplug */ - ioboard_type = sn_ioboard_to_pci_bus(pci_bus); - switch (ioboard_type) { - case L1_BRICKTYPE_IX: - case L1_BRICKTYPE_PX: - case L1_BRICKTYPE_IA: - case L1_BRICKTYPE_PA: - case L1_BOARDTYPE_PCIX3SLOT: - return 1; - break; - default: - return -EPERM; - break; - } - - return -EIO; -} - -static int sn_hp_slot_private_alloc(struct hotplug_slot **bss_hotplug_slot, - struct pci_bus *pci_bus, int device, - char *name) -{ - struct pcibus_info *pcibus_info; - struct slot *slot; - - pcibus_info = SN_PCIBUS_BUSSOFT_INFO(pci_bus); - - slot = kzalloc(sizeof(*slot), GFP_KERNEL); - if (!slot) - return -ENOMEM; - - slot->device_num = device; - slot->pci_bus = pci_bus; - sprintf(name, "%04x:%02x:%02x", - pci_domain_nr(pci_bus), - ((u16)pcibus_info->pbi_buscommon.bs_persist_busnum), - device + 1); - - sn_generate_path(pci_bus, slot->physical_path); - - list_add(&slot->hp_list, &sn_hp_list); - *bss_hotplug_slot = &slot->hotplug_slot; - - return 0; -} - -static struct hotplug_slot *sn_hp_destroy(void) -{ - struct slot *slot; - struct pci_slot *pci_slot; - struct hotplug_slot *bss_hotplug_slot = NULL; - - list_for_each_entry(slot, &sn_hp_list, hp_list) { - bss_hotplug_slot = &slot->hotplug_slot; - pci_slot = bss_hotplug_slot->pci_slot; - list_del(&slot->hp_list); - sysfs_remove_file(&pci_slot->kobj, - &sn_slot_path_attr.attr); - break; - } - return bss_hotplug_slot; -} - -static void sn_bus_free_data(struct pci_dev *dev) -{ - struct pci_bus *subordinate_bus; - struct pci_dev *child; - - /* Recursively clean up sn_irq_info structs */ - if (dev->subordinate) { - subordinate_bus = dev->subordinate; - list_for_each_entry(child, &subordinate_bus->devices, bus_list) - sn_bus_free_data(child); - } - /* - * Some drivers may use dma accesses during the - * driver remove function. We release the sysdata - * areas after the driver remove functions have - * been called. - */ - sn_bus_store_sysdata(dev); - sn_pci_unfixup_slot(dev); -} - -static int sn_slot_enable(struct hotplug_slot *bss_hotplug_slot, - int device_num, char **ssdt) -{ - struct slot *slot = to_slot(bss_hotplug_slot); - struct pcibus_info *pcibus_info; - struct pcibr_slot_enable_resp resp; - int rc; - - pcibus_info = SN_PCIBUS_BUSSOFT_INFO(slot->pci_bus); - - /* - * Power-on and initialize the slot in the SN - * PCI infrastructure. - */ - rc = sal_pcibr_slot_enable(pcibus_info, device_num, &resp, ssdt); - - - if (rc == PCI_SLOT_ALREADY_UP) { - pci_dbg(slot->pci_bus->self, "is already active\n"); - return 1; /* return 1 to user */ - } - - if (rc == PCI_L1_ERR) { - pci_dbg(slot->pci_bus->self, "L1 failure %d with message: %s", - resp.resp_sub_errno, resp.resp_l1_msg); - return -EPERM; - } - - if (rc) { - pci_dbg(slot->pci_bus->self, "insert failed with error %d sub-error %d\n", - rc, resp.resp_sub_errno); - return -EIO; - } - - pcibus_info = SN_PCIBUS_BUSSOFT_INFO(slot->pci_bus); - pcibus_info->pbi_enabled_devices |= (1 << device_num); - - return 0; -} - -static int sn_slot_disable(struct hotplug_slot *bss_hotplug_slot, - int device_num, int action) -{ - struct slot *slot = to_slot(bss_hotplug_slot); - struct pcibus_info *pcibus_info; - struct pcibr_slot_disable_resp resp; - int rc; - - pcibus_info = SN_PCIBUS_BUSSOFT_INFO(slot->pci_bus); - - rc = sal_pcibr_slot_disable(pcibus_info, device_num, action, &resp); - - if ((action == PCI_REQ_SLOT_ELIGIBLE) && - (rc == PCI_SLOT_ALREADY_DOWN)) { - pci_dbg(slot->pci_bus->self, "Slot %s already inactive\n", slot->physical_path); - return 1; /* return 1 to user */ - } - - if ((action == PCI_REQ_SLOT_ELIGIBLE) && (rc == PCI_EMPTY_33MHZ)) { - pci_dbg(slot->pci_bus->self, "Cannot remove last 33MHz card\n"); - return -EPERM; - } - - if ((action == PCI_REQ_SLOT_ELIGIBLE) && (rc == PCI_L1_ERR)) { - pci_dbg(slot->pci_bus->self, "L1 failure %d with message \n%s\n", - resp.resp_sub_errno, resp.resp_l1_msg); - return -EPERM; - } - - if ((action == PCI_REQ_SLOT_ELIGIBLE) && rc) { - pci_dbg(slot->pci_bus->self, "remove failed with error %d sub-error %d\n", - rc, resp.resp_sub_errno); - return -EIO; - } - - if ((action == PCI_REQ_SLOT_ELIGIBLE) && !rc) - return 0; - - if ((action == PCI_REQ_SLOT_DISABLE) && !rc) { - pcibus_info = SN_PCIBUS_BUSSOFT_INFO(slot->pci_bus); - pcibus_info->pbi_enabled_devices &= ~(1 << device_num); - pci_dbg(slot->pci_bus->self, "remove successful\n"); - return 0; - } - - if ((action == PCI_REQ_SLOT_DISABLE) && rc) { - pci_dbg(slot->pci_bus->self, "remove failed rc = %d\n", rc); - } - - return rc; -} - -/* - * Power up and configure the slot via a SAL call to PROM. - * Scan slot (and any children), do any platform specific fixup, - * and find device driver. - */ -static int enable_slot(struct hotplug_slot *bss_hotplug_slot) -{ - struct slot *slot = to_slot(bss_hotplug_slot); - struct pci_bus *new_bus = NULL; - struct pci_dev *dev; - int num_funcs; - int new_ppb = 0; - int rc; - char *ssdt = NULL; - void pcibios_fixup_device_resources(struct pci_dev *); - - /* Serialize the Linux PCI infrastructure */ - mutex_lock(&sn_hotplug_mutex); - - /* - * Power-on and initialize the slot in the SN - * PCI infrastructure. Also, retrieve the ACPI SSDT - * table for the slot (if ACPI capable PROM). - */ - rc = sn_slot_enable(bss_hotplug_slot, slot->device_num, &ssdt); - if (rc) { - mutex_unlock(&sn_hotplug_mutex); - return rc; - } - - if (ssdt) - ssdt = __va(ssdt); - /* Add the new SSDT for the slot to the ACPI namespace */ - if (SN_ACPI_BASE_SUPPORT() && ssdt) { - acpi_status ret; - - ret = acpi_load_table((struct acpi_table_header *)ssdt); - if (ACPI_FAILURE(ret)) { - printk(KERN_ERR "%s: acpi_load_table failed (0x%x)\n", - __func__, ret); - /* try to continue on */ - } - } - - num_funcs = pci_scan_slot(slot->pci_bus, - PCI_DEVFN(slot->device_num + 1, 0)); - if (!num_funcs) { - pci_dbg(slot->pci_bus->self, "no device in slot\n"); - mutex_unlock(&sn_hotplug_mutex); - return -ENODEV; - } - - /* - * Map SN resources for all functions on the card - * to the Linux PCI interface and tell the drivers - * about them. - */ - list_for_each_entry(dev, &slot->pci_bus->devices, bus_list) { - if (PCI_SLOT(dev->devfn) != slot->device_num + 1) - continue; - - /* Need to do slot fixup on PPB before fixup of children - * (PPB's pcidev_info needs to be in pcidev_info list - * before child's SN_PCIDEV_INFO() call to setup - * pdi_host_pcidev_info). - */ - pcibios_fixup_device_resources(dev); - if (SN_ACPI_BASE_SUPPORT()) - sn_acpi_slot_fixup(dev); - else - sn_io_slot_fixup(dev); - if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) { - pci_hp_add_bridge(dev); - if (dev->subordinate) { - new_bus = dev->subordinate; - new_ppb = 1; - } - } - } - - /* - * Add the slot's devices to the ACPI infrastructure */ - if (SN_ACPI_BASE_SUPPORT() && ssdt) { - unsigned long long adr; - struct acpi_device *pdevice; - acpi_handle phandle; - acpi_handle chandle = NULL; - acpi_handle rethandle; - acpi_status ret; - - phandle = acpi_device_handle(PCI_CONTROLLER(slot->pci_bus)->companion); - - if (acpi_bus_get_device(phandle, &pdevice)) { - pci_dbg(slot->pci_bus->self, "no parent device, assuming NULL\n"); - pdevice = NULL; - } - - acpi_scan_lock_acquire(); - /* - * Walk the rootbus node's immediate children looking for - * the slot's device node(s). There can be more than - * one for multifunction devices. - */ - for (;;) { - rethandle = NULL; - ret = acpi_get_next_object(ACPI_TYPE_DEVICE, - phandle, chandle, - &rethandle); - - if (ret == AE_NOT_FOUND || rethandle == NULL) - break; - - chandle = rethandle; - - ret = acpi_evaluate_integer(chandle, METHOD_NAME__ADR, - NULL, &adr); - - if (ACPI_SUCCESS(ret) && - (adr>>16) == (slot->device_num + 1)) { - - ret = acpi_bus_scan(chandle); - if (ACPI_FAILURE(ret)) { - printk(KERN_ERR "%s: acpi_bus_scan failed (0x%x) for slot %d func %d\n", - __func__, ret, (int)(adr>>16), - (int)(adr&0xffff)); - /* try to continue on */ - } - } - } - acpi_scan_lock_release(); - } - - pci_lock_rescan_remove(); - - /* Call the driver for the new device */ - pci_bus_add_devices(slot->pci_bus); - /* Call the drivers for the new devices subordinate to PPB */ - if (new_ppb) - pci_bus_add_devices(new_bus); - - pci_unlock_rescan_remove(); - mutex_unlock(&sn_hotplug_mutex); - - if (rc == 0) - pci_dbg(slot->pci_bus->self, "insert operation successful\n"); - else - pci_dbg(slot->pci_bus->self, "insert operation failed rc = %d\n", rc); - - return rc; -} - -static int disable_slot(struct hotplug_slot *bss_hotplug_slot) -{ - struct slot *slot = to_slot(bss_hotplug_slot); - struct pci_dev *dev, *temp; - int rc; - acpi_handle ssdt_hdl = NULL; - - /* Acquire update access to the bus */ - mutex_lock(&sn_hotplug_mutex); - - /* is it okay to bring this slot down? */ - rc = sn_slot_disable(bss_hotplug_slot, slot->device_num, - PCI_REQ_SLOT_ELIGIBLE); - if (rc) - goto leaving; - - /* free the ACPI resources for the slot */ - if (SN_ACPI_BASE_SUPPORT() && - PCI_CONTROLLER(slot->pci_bus)->companion) { - unsigned long long adr; - struct acpi_device *device; - acpi_handle phandle; - acpi_handle chandle = NULL; - acpi_handle rethandle; - acpi_status ret; - - /* Get the rootbus node pointer */ - phandle = acpi_device_handle(PCI_CONTROLLER(slot->pci_bus)->companion); - - acpi_scan_lock_acquire(); - /* - * Walk the rootbus node's immediate children looking for - * the slot's device node(s). There can be more than - * one for multifunction devices. - */ - for (;;) { - rethandle = NULL; - ret = acpi_get_next_object(ACPI_TYPE_DEVICE, - phandle, chandle, - &rethandle); - - if (ret == AE_NOT_FOUND || rethandle == NULL) - break; - - chandle = rethandle; - - ret = acpi_evaluate_integer(chandle, - METHOD_NAME__ADR, - NULL, &adr); - if (ACPI_SUCCESS(ret) && - (adr>>16) == (slot->device_num + 1)) { - /* retain the owner id */ - ssdt_hdl = chandle; - - ret = acpi_bus_get_device(chandle, - &device); - if (ACPI_SUCCESS(ret)) - acpi_bus_trim(device); - } - } - acpi_scan_lock_release(); - } - - pci_lock_rescan_remove(); - /* Free the SN resources assigned to the Linux device.*/ - list_for_each_entry_safe(dev, temp, &slot->pci_bus->devices, bus_list) { - if (PCI_SLOT(dev->devfn) != slot->device_num + 1) - continue; - - pci_dev_get(dev); - sn_bus_free_data(dev); - pci_stop_and_remove_bus_device(dev); - pci_dev_put(dev); - } - pci_unlock_rescan_remove(); - - /* Remove the SSDT for the slot from the ACPI namespace */ - if (SN_ACPI_BASE_SUPPORT() && ssdt_hdl) { - acpi_status ret; - ret = acpi_unload_parent_table(ssdt_hdl); - if (ACPI_FAILURE(ret)) { - acpi_handle_err(ssdt_hdl, - "%s: acpi_unload_parent_table failed (0x%x)\n", - __func__, ret); - /* try to continue on */ - } - } - - /* free the collected sysdata pointers */ - sn_bus_free_sysdata(); - - /* Deactivate slot */ - rc = sn_slot_disable(bss_hotplug_slot, slot->device_num, - PCI_REQ_SLOT_DISABLE); - leaving: - /* Release the bus lock */ - mutex_unlock(&sn_hotplug_mutex); - - return rc; -} - -static inline int get_power_status(struct hotplug_slot *bss_hotplug_slot, - u8 *value) -{ - struct slot *slot = to_slot(bss_hotplug_slot); - struct pcibus_info *pcibus_info; - u32 power; - - pcibus_info = SN_PCIBUS_BUSSOFT_INFO(slot->pci_bus); - mutex_lock(&sn_hotplug_mutex); - power = pcibus_info->pbi_enabled_devices & (1 << slot->device_num); - *value = power ? 1 : 0; - mutex_unlock(&sn_hotplug_mutex); - return 0; -} - -static void sn_release_slot(struct hotplug_slot *bss_hotplug_slot) -{ - kfree(to_slot(bss_hotplug_slot)); -} - -static int sn_hotplug_slot_register(struct pci_bus *pci_bus) -{ - int device; - struct pci_slot *pci_slot; - struct hotplug_slot *bss_hotplug_slot; - char name[SN_SLOT_NAME_SIZE]; - int rc = 0; - - /* - * Currently only four devices are supported, - * in the future there maybe more -- up to 32. - */ - - for (device = 0; device < SN_MAX_HP_SLOTS ; device++) { - if (sn_pci_slot_valid(pci_bus, device) != 1) - continue; - - if (sn_hp_slot_private_alloc(&bss_hotplug_slot, - pci_bus, device, name)) { - rc = -ENOMEM; - goto alloc_err; - } - bss_hotplug_slot->ops = &sn_hotplug_slot_ops; - - rc = pci_hp_register(bss_hotplug_slot, pci_bus, device, name); - if (rc) - goto register_err; - - pci_slot = bss_hotplug_slot->pci_slot; - rc = sysfs_create_file(&pci_slot->kobj, - &sn_slot_path_attr.attr); - if (rc) - goto alloc_err; - } - pci_dbg(pci_bus->self, "Registered bus with hotplug\n"); - return rc; - -register_err: - pci_dbg(pci_bus->self, "bus failed to register with err = %d\n", - rc); - - /* destroy THIS element */ - sn_hp_destroy(); - sn_release_slot(bss_hotplug_slot); - -alloc_err: - /* destroy anything else on the list */ - while ((bss_hotplug_slot = sn_hp_destroy())) { - pci_hp_deregister(bss_hotplug_slot); - sn_release_slot(bss_hotplug_slot); - } - - return rc; -} - -static int __init sn_pci_hotplug_init(void) -{ - struct pci_bus *pci_bus = NULL; - int rc; - int registered = 0; - - if (!sn_prom_feature_available(PRF_HOTPLUG_SUPPORT)) { - printk(KERN_ERR "%s: PROM version does not support hotplug.\n", - __func__); - return -EPERM; - } - - INIT_LIST_HEAD(&sn_hp_list); - - while ((pci_bus = pci_find_next_bus(pci_bus))) { - if (!pci_bus->sysdata) - continue; - - rc = sn_pci_bus_valid(pci_bus); - if (rc != 1) { - pci_dbg(pci_bus->self, "not a valid hotplug bus\n"); - continue; - } - pci_dbg(pci_bus->self, "valid hotplug bus\n"); - - rc = sn_hotplug_slot_register(pci_bus); - if (!rc) { - registered = 1; - } else { - registered = 0; - break; - } - } - - return registered == 1 ? 0 : -ENODEV; -} - -static void __exit sn_pci_hotplug_exit(void) -{ - struct hotplug_slot *bss_hotplug_slot; - - while ((bss_hotplug_slot = sn_hp_destroy())) { - pci_hp_deregister(bss_hotplug_slot); - sn_release_slot(bss_hotplug_slot); - } - - if (!list_empty(&sn_hp_list)) - printk(KERN_ERR "%s: internal list is not empty\n", __FILE__); -} - -module_init(sn_pci_hotplug_init); -module_exit(sn_pci_hotplug_exit); diff --git a/drivers/scsi/qla1280.c b/drivers/scsi/qla1280.c index e5760c4a27f0..832af4213046 100644 --- a/drivers/scsi/qla1280.c +++ b/drivers/scsi/qla1280.c @@ -357,10 +357,6 @@ #include <scsi/scsi_host.h> #include <scsi/scsi_tcq.h> -#if defined(CONFIG_IA64_GENERIC) || defined(CONFIG_IA64_SGI_SN2) -#include <asm/sn/io.h> -#endif - /* * Compile time Options: @@ -380,11 +376,6 @@ #define NVRAM_DELAY() udelay(500) /* 2 microseconds */ -#if defined(__ia64__) && !defined(ia64_platform_is) -#define ia64_platform_is(foo) (!strcmp(x, platform_name)) -#endif - - #define IS_ISP1040(ha) (ha->pdev->device == PCI_DEVICE_ID_QLOGIC_ISP1020) #define IS_ISP1x40(ha) (ha->pdev->device == PCI_DEVICE_ID_QLOGIC_ISP1020 || \ ha->pdev->device == PCI_DEVICE_ID_QLOGIC_ISP1240) @@ -1427,15 +1418,6 @@ qla1280_initialize_adapter(struct scsi_qla_host *ha) ha->flags.reset_active = 0; ha->flags.abort_isp_active = 0; -#if defined(CONFIG_IA64_GENERIC) || defined(CONFIG_IA64_SGI_SN2) - if (ia64_platform_is("sn2")) { - printk(KERN_INFO "scsi(%li): Enabling SN2 PCI DMA " - "dual channel lockup workaround\n", ha->host_no); - ha->flags.use_pci_vchannel = 1; - driver_setup.no_nvram = 1; - } -#endif - /* TODO: implement support for the 1040 nvram format */ if (IS_ISP1040(ha)) driver_setup.no_nvram = 1; @@ -2251,13 +2233,6 @@ qla1280_nvram_config(struct scsi_qla_host *ha) mb[1] = nv->firmware_feature.f.enable_fast_posting; mb[1] |= nv->firmware_feature.f.report_lvd_bus_transition << 1; mb[1] |= nv->firmware_feature.f.disable_synchronous_backoff << 5; -#if defined(CONFIG_IA64_GENERIC) || defined (CONFIG_IA64_SGI_SN2) - if (ia64_platform_is("sn2")) { - printk(KERN_INFO "scsi(%li): Enabling SN2 PCI DMA " - "workaround\n", ha->host_no); - mb[1] |= nv->firmware_feature.f.unused_9 << 9; /* XXX */ - } -#endif status |= qla1280_mailbox_command(ha, BIT_1 | BIT_0, mb); /* Retry count and delay. */ @@ -2888,12 +2863,6 @@ qla1280_64bit_start_scsi(struct scsi_qla_host *ha, struct srb * sp) break; dma_handle = sg_dma_address(s); -#if defined(CONFIG_IA64_GENERIC) || defined(CONFIG_IA64_SGI_SN2) - if (ha->flags.use_pci_vchannel) - sn_pci_set_vchan(ha->pdev, - (unsigned long *)&dma_handle, - SCSI_BUS_32(cmd)); -#endif *dword_ptr++ = cpu_to_le32(lower_32_bits(dma_handle)); *dword_ptr++ = @@ -2950,12 +2919,6 @@ qla1280_64bit_start_scsi(struct scsi_qla_host *ha, struct srb * sp) if (cnt == 5) break; dma_handle = sg_dma_address(s); -#if defined(CONFIG_IA64_GENERIC) || defined(CONFIG_IA64_SGI_SN2) - if (ha->flags.use_pci_vchannel) - sn_pci_set_vchan(ha->pdev, - (unsigned long *)&dma_handle, - SCSI_BUS_32(cmd)); -#endif *dword_ptr++ = cpu_to_le32(lower_32_bits(dma_handle)); *dword_ptr++ = diff --git a/drivers/scsi/qla1280.h b/drivers/scsi/qla1280.h index b496206362a9..a1a8aefc7cc3 100644 --- a/drivers/scsi/qla1280.h +++ b/drivers/scsi/qla1280.h @@ -1055,9 +1055,6 @@ struct scsi_qla_host { uint32_t reset_active:1; /* 3 */ uint32_t abort_isp_active:1; /* 4 */ uint32_t disable_risc_code_load:1; /* 5 */ -#ifdef __ia64__ - uint32_t use_pci_vchannel:1; -#endif } flags; struct nvram nvram; diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c index da83034d4759..d4c3baec9172 100644 --- a/drivers/scsi/qla2xxx/qla_init.c +++ b/drivers/scsi/qla2xxx/qla_init.c @@ -4575,20 +4575,6 @@ qla2x00_nvram_config(scsi_qla_host_t *vha) rval = 1; } -#if defined(CONFIG_IA64_GENERIC) || defined(CONFIG_IA64_SGI_SN2) - /* - * The SN2 does not provide BIOS emulation which means you can't change - * potentially bogus BIOS settings. Force the use of default settings - * for link rate and frame size. Hope that the rest of the settings - * are valid. - */ - if (ia64_platform_is("sn2")) { - nv->frame_payload_size = 2048; - if (IS_QLA23XX(ha)) - nv->special_options[1] = BIT_7; - } -#endif - /* Reset Initialization control block */ memset(icb, 0, ha->init_cb_size); diff --git a/drivers/sn/Kconfig b/drivers/sn/Kconfig deleted file mode 100644 index a6c443d31a3c..000000000000 --- a/drivers/sn/Kconfig +++ /dev/null @@ -1,22 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -# -# Miscellaneous SN-specific devices -# - -menu "SN Devices" - depends on SGI_SN - -config SGI_IOC3 - tristate "SGI IOC3 Base IO support" - default m - ---help--- - This option enables basic support for the SGI IOC3-based Base IO - controller card. This option does not enable any specific - functions on such a card, but provides necessary infrastructure - for other drivers to utilize. - - If you have an SGI Altix with an IOC3-based - I/O controller or a PCI IOC3 serial card say Y. - Otherwise say N. - -endmenu diff --git a/drivers/sn/Makefile b/drivers/sn/Makefile deleted file mode 100644 index f0e809a38b2d..000000000000 --- a/drivers/sn/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -# -# Makefile for the Altix device drivers. -# -# - -obj-$(CONFIG_SGI_IOC3) += ioc3.o diff --git a/drivers/sn/ioc3.c b/drivers/sn/ioc3.c deleted file mode 100644 index 358025af4918..000000000000 --- a/drivers/sn/ioc3.c +++ /dev/null @@ -1,844 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * SGI IOC3 master driver and IRQ demuxer - * - * Copyright (c) 2005 Stanislaw Skowronek <skylark@linux-mips.org> - * Heavily based on similar work by: - * Brent Casavant <bcasavan@sgi.com> - IOC4 master driver - * Pat Gefre <pfg@sgi.com> - IOC3 serial port IRQ demuxer - */ - -#include <linux/errno.h> -#include <linux/module.h> -#include <linux/pci.h> -#include <linux/dma-mapping.h> -#include <linux/interrupt.h> -#include <linux/spinlock.h> -#include <linux/delay.h> -#include <linux/ioc3.h> -#include <linux/rwsem.h> -#include <linux/slab.h> - -#define IOC3_PCI_SIZE 0x100000 - -static LIST_HEAD(ioc3_devices); -static int ioc3_counter; -static DECLARE_RWSEM(ioc3_devices_rwsem); - -static struct ioc3_submodule *ioc3_submodules[IOC3_MAX_SUBMODULES]; -static struct ioc3_submodule *ioc3_ethernet; -static DEFINE_RWLOCK(ioc3_submodules_lock); - -/* NIC probing code */ - -#define GPCR_MLAN_EN 0x00200000 /* enable MCR to pin 8 */ - -static inline unsigned mcr_pack(unsigned pulse, unsigned sample) -{ - return (pulse << 10) | (sample << 2); -} - -static int nic_wait(struct ioc3_driver_data *idd) -{ - unsigned mcr; - - do { - mcr = readl(&idd->vma->mcr); - } while (!(mcr & 2)); - - return mcr & 1; -} - -static int nic_reset(struct ioc3_driver_data *idd) -{ - int presence; - unsigned long flags; - - local_irq_save(flags); - writel(mcr_pack(500, 65), &idd->vma->mcr); - presence = nic_wait(idd); - local_irq_restore(flags); - - udelay(500); - - return presence; -} - -static int nic_read_bit(struct ioc3_driver_data *idd) -{ - int result; - unsigned long flags; - - local_irq_save(flags); - writel(mcr_pack(6, 13), &idd->vma->mcr); - result = nic_wait(idd); - local_irq_restore(flags); - - udelay(500); - - return result; -} - -static void nic_write_bit(struct ioc3_driver_data *idd, int bit) -{ - if (bit) - writel(mcr_pack(6, 110), &idd->vma->mcr); - else - writel(mcr_pack(80, 30), &idd->vma->mcr); - - nic_wait(idd); -} - -static unsigned nic_read_byte(struct ioc3_driver_data *idd) -{ - unsigned result = 0; - int i; - - for (i = 0; i < 8; i++) - result = (result >> 1) | (nic_read_bit(idd) << 7); - - return result; -} - -static void nic_write_byte(struct ioc3_driver_data *idd, int byte) -{ - int i, bit; - - for (i = 8; i; i--) { - bit = byte & 1; - byte >>= 1; - - nic_write_bit(idd, bit); - } -} - -static unsigned long -nic_find(struct ioc3_driver_data *idd, int *last, unsigned long addr) -{ - int a, b, index, disc; - - nic_reset(idd); - - /* Search ROM. */ - nic_write_byte(idd, 0xF0); - - /* Algorithm from ``Book of iButton Standards''. */ - for (index = 0, disc = 0; index < 64; index++) { - a = nic_read_bit(idd); - b = nic_read_bit(idd); - - if (a && b) { - printk(KERN_WARNING "IOC3 NIC search failed.\n"); - *last = 0; - return 0; - } - - if (!a && !b) { - if (index == *last) { - addr |= 1UL << index; - } else if (index > *last) { - addr &= ~(1UL << index); - disc = index; - } else if ((addr & (1UL << index)) == 0) - disc = index; - nic_write_bit(idd, (addr>>index)&1); - continue; - } else { - if (a) - addr |= 1UL << index; - else - addr &= ~(1UL << index); - nic_write_bit(idd, a); - continue; - } - } - *last = disc; - return addr; -} - -static void nic_addr(struct ioc3_driver_data *idd, unsigned long addr) -{ - int index; - - nic_reset(idd); - nic_write_byte(idd, 0xF0); - for (index = 0; index < 64; index++) { - nic_read_bit(idd); - nic_read_bit(idd); - nic_write_bit(idd, (addr>>index)&1); - } -} - -static void crc16_byte(unsigned int *crc, unsigned char db) -{ - int i; - - for(i=0;i<8;i++) { - *crc <<= 1; - if((db^(*crc>>16)) & 1) - *crc ^= 0x8005; - db >>= 1; - } - *crc &= 0xFFFF; -} - -static unsigned int crc16_area(unsigned char *dbs, int size, unsigned int crc) -{ - while(size--) - crc16_byte(&crc, *(dbs++)); - return crc; -} - -static void crc8_byte(unsigned int *crc, unsigned char db) -{ - int i,f; - - for(i=0;i<8;i++) { - f = (*crc ^ db) & 1; - *crc >>= 1; - db >>= 1; - if(f) - *crc ^= 0x8c; - } - *crc &= 0xff; -} - -static unsigned int crc8_addr(unsigned long addr) -{ - int i; - unsigned int crc = 0x00; - - for(i=0;i<8;i++) - crc8_byte(&crc, addr>>(i<<3)); - return crc; -} - -static void -read_redir_page(struct ioc3_driver_data *idd, unsigned long addr, int page, - unsigned char *redir, unsigned char *data) -{ - int loops = 16, i; - - while(redir[page] != 0xFF) { - page = redir[page]^0xFF; - loops--; - if(loops<0) { - printk(KERN_ERR "IOC3: NIC circular redirection\n"); - return; - } - } - loops = 3; - while(loops>0) { - nic_addr(idd, addr); - nic_write_byte(idd, 0xF0); - nic_write_byte(idd, (page << 5) & 0xE0); - nic_write_byte(idd, (page >> 3) & 0x1F); - for(i=0;i<0x20;i++) - data[i] = nic_read_byte(idd); - if(crc16_area(data, 0x20, 0x0000) == 0x800d) - return; - loops--; - } - printk(KERN_ERR "IOC3: CRC error in data page\n"); - for(i=0;i<0x20;i++) - data[i] = 0x00; -} - -static void -read_redir_map(struct ioc3_driver_data *idd, unsigned long addr, - unsigned char *redir) -{ - int i,j,loops = 3,crc_ok; - unsigned int crc; - - while(loops>0) { - crc_ok = 1; - nic_addr(idd, addr); - nic_write_byte(idd, 0xAA); - nic_write_byte(idd, 0x00); - nic_write_byte(idd, 0x01); - for(i=0;i<64;i+=8) { - for(j=0;j<8;j++) - redir[i+j] = nic_read_byte(idd); - crc = crc16_area(redir+i, 8, (i==0)?0x8707:0x0000); - crc16_byte(&crc, nic_read_byte(idd)); - crc16_byte(&crc, nic_read_byte(idd)); - if(crc != 0x800d) - crc_ok = 0; - } - if(crc_ok) - return; - loops--; - } - printk(KERN_ERR "IOC3: CRC error in redirection page\n"); - for(i=0;i<64;i++) - redir[i] = 0xFF; -} - -static void read_nic(struct ioc3_driver_data *idd, unsigned long addr) -{ - unsigned char redir[64]; - unsigned char data[64],part[32]; - int i,j; - - /* read redirections */ - read_redir_map(idd, addr, redir); - /* read data pages */ - read_redir_page(idd, addr, 0, redir, data); - read_redir_page(idd, addr, 1, redir, data+32); - /* assemble the part # */ - j=0; - for(i=0;i<19;i++) - if(data[i+11] != ' ') - part[j++] = data[i+11]; - for(i=0;i<6;i++) - if(data[i+32] != ' ') - part[j++] = data[i+32]; - part[j] = 0; - /* skip Octane power supplies */ - if(!strncmp(part, "060-0035-", 9)) - return; - if(!strncmp(part, "060-0038-", 9)) - return; - strcpy(idd->nic_part, part); - /* assemble the serial # */ - j=0; - for(i=0;i<10;i++) - if(data[i+1] != ' ') - idd->nic_serial[j++] = data[i+1]; - idd->nic_serial[j] = 0; -} - -static void read_mac(struct ioc3_driver_data *idd, unsigned long addr) -{ - int i, loops = 3; - unsigned char data[13]; - - while(loops>0) { - nic_addr(idd, addr); - nic_write_byte(idd, 0xF0); - nic_write_byte(idd, 0x00); - nic_write_byte(idd, 0x00); - nic_read_byte(idd); - for(i=0;i<13;i++) - data[i] = nic_read_byte(idd); - if(crc16_area(data, 13, 0x0000) == 0x800d) { - for(i=10;i>4;i--) - idd->nic_mac[10-i] = data[i]; - return; - } - loops--; - } - printk(KERN_ERR "IOC3: CRC error in MAC address\n"); - for(i=0;i<6;i++) - idd->nic_mac[i] = 0x00; -} - -static void probe_nic(struct ioc3_driver_data *idd) -{ - int save = 0, loops = 3; - unsigned long first, addr; - - writel(GPCR_MLAN_EN, &idd->vma->gpcr_s); - - while(loops>0) { - idd->nic_part[0] = 0; - idd->nic_serial[0] = 0; - addr = first = nic_find(idd, &save, 0); - if(!first) - return; - while(1) { - if(crc8_addr(addr)) - break; - else { - switch(addr & 0xFF) { - case 0x0B: - read_nic(idd, addr); - break; - case 0x09: - case 0x89: - case 0x91: - read_mac(idd, addr); - break; - } - } - addr = nic_find(idd, &save, addr); - if(addr == first) - return; - } - loops--; - } - printk(KERN_ERR "IOC3: CRC error in NIC address\n"); -} - -/* Interrupts */ - -static void write_ireg(struct ioc3_driver_data *idd, uint32_t val, int which) -{ - unsigned long flags; - - spin_lock_irqsave(&idd->ir_lock, flags); - switch (which) { - case IOC3_W_IES: - writel(val, &idd->vma->sio_ies); - break; - case IOC3_W_IEC: - writel(val, &idd->vma->sio_iec); - break; - } - spin_unlock_irqrestore(&idd->ir_lock, flags); -} -static inline uint32_t get_pending_intrs(struct ioc3_driver_data *idd) -{ - unsigned long flag; - uint32_t intrs = 0; - - spin_lock_irqsave(&idd->ir_lock, flag); - intrs = readl(&idd->vma->sio_ir); - intrs &= readl(&idd->vma->sio_ies); - spin_unlock_irqrestore(&idd->ir_lock, flag); - return intrs; -} - -static irqreturn_t ioc3_intr_io(int irq, void *arg) -{ - unsigned long flags; - struct ioc3_driver_data *idd = arg; - int handled = 1, id; - unsigned int pending; - - read_lock_irqsave(&ioc3_submodules_lock, flags); - - if(idd->dual_irq && readb(&idd->vma->eisr)) { - /* send Ethernet IRQ to the driver */ - if(ioc3_ethernet && idd->active[ioc3_ethernet->id] && - ioc3_ethernet->intr) { - handled = handled && !ioc3_ethernet->intr(ioc3_ethernet, - idd, 0); - } - } - pending = get_pending_intrs(idd); /* look at the IO IRQs */ - - for(id=0;id<IOC3_MAX_SUBMODULES;id++) { - if(idd->active[id] && ioc3_submodules[id] - && (pending & ioc3_submodules[id]->irq_mask) - && ioc3_submodules[id]->intr) { - write_ireg(idd, ioc3_submodules[id]->irq_mask, - IOC3_W_IEC); - if(!ioc3_submodules[id]->intr(ioc3_submodules[id], - idd, pending & ioc3_submodules[id]->irq_mask)) - pending &= ~ioc3_submodules[id]->irq_mask; - if (ioc3_submodules[id]->reset_mask) - write_ireg(idd, ioc3_submodules[id]->irq_mask, - IOC3_W_IES); - } - } - read_unlock_irqrestore(&ioc3_submodules_lock, flags); - if(pending) { - printk(KERN_WARNING - "IOC3: Pending IRQs 0x%08x discarded and disabled\n",pending); - write_ireg(idd, pending, IOC3_W_IEC); - handled = 1; - } - return handled?IRQ_HANDLED:IRQ_NONE; -} - -static irqreturn_t ioc3_intr_eth(int irq, void *arg) -{ - unsigned long flags; - struct ioc3_driver_data *idd = (struct ioc3_driver_data *)arg; - int handled = 1; - - if(!idd->dual_irq) - return IRQ_NONE; - read_lock_irqsave(&ioc3_submodules_lock, flags); - if(ioc3_ethernet && idd->active[ioc3_ethernet->id] - && ioc3_ethernet->intr) - handled = handled && !ioc3_ethernet->intr(ioc3_ethernet, idd, 0); - read_unlock_irqrestore(&ioc3_submodules_lock, flags); - return handled?IRQ_HANDLED:IRQ_NONE; -} - -void ioc3_enable(struct ioc3_submodule *is, - struct ioc3_driver_data *idd, unsigned int irqs) -{ - write_ireg(idd, irqs & is->irq_mask, IOC3_W_IES); -} - -void ioc3_ack(struct ioc3_submodule *is, struct ioc3_driver_data *idd, - unsigned int irqs) -{ - writel(irqs & is->irq_mask, &idd->vma->sio_ir); -} - -void ioc3_disable(struct ioc3_submodule *is, - struct ioc3_driver_data *idd, unsigned int irqs) -{ - write_ireg(idd, irqs & is->irq_mask, IOC3_W_IEC); -} - -void ioc3_gpcr_set(struct ioc3_driver_data *idd, unsigned int val) -{ - unsigned long flags; - spin_lock_irqsave(&idd->gpio_lock, flags); - writel(val, &idd->vma->gpcr_s); - spin_unlock_irqrestore(&idd->gpio_lock, flags); -} - -/* Keep it simple, stupid! */ -static int find_slot(void **tab, int max) -{ - int i; - for(i=0;i<max;i++) - if(!(tab[i])) - return i; - return -1; -} - -/* Register an IOC3 submodule */ -int ioc3_register_submodule(struct ioc3_submodule *is) -{ - struct ioc3_driver_data *idd; - int alloc_id; - unsigned long flags; - - write_lock_irqsave(&ioc3_submodules_lock, flags); - alloc_id = find_slot((void **)ioc3_submodules, IOC3_MAX_SUBMODULES); - if(alloc_id != -1) { - ioc3_submodules[alloc_id] = is; - if(is->ethernet) { - if(ioc3_ethernet==NULL) - ioc3_ethernet=is; - else - printk(KERN_WARNING - "IOC3 Ethernet module already registered!\n"); - } - } - write_unlock_irqrestore(&ioc3_submodules_lock, flags); - - if(alloc_id == -1) { - printk(KERN_WARNING "Increase IOC3_MAX_SUBMODULES!\n"); - return -ENOMEM; - } - - is->id=alloc_id; - - /* Initialize submodule for each IOC3 */ - if (!is->probe) - return 0; - - down_read(&ioc3_devices_rwsem); - list_for_each_entry(idd, &ioc3_devices, list) { - /* set to 1 for IRQs in probe */ - idd->active[alloc_id] = 1; - idd->active[alloc_id] = !is->probe(is, idd); - } - up_read(&ioc3_devices_rwsem); - - return 0; -} - -/* Unregister an IOC3 submodule */ -void ioc3_unregister_submodule(struct ioc3_submodule *is) -{ - struct ioc3_driver_data *idd; - unsigned long flags; - - write_lock_irqsave(&ioc3_submodules_lock, flags); - if(ioc3_submodules[is->id]==is) - ioc3_submodules[is->id]=NULL; - else - printk(KERN_WARNING - "IOC3 submodule %s has wrong ID.\n",is->name); - if(ioc3_ethernet==is) - ioc3_ethernet = NULL; - write_unlock_irqrestore(&ioc3_submodules_lock, flags); - - /* Remove submodule for each IOC3 */ - down_read(&ioc3_devices_rwsem); - list_for_each_entry(idd, &ioc3_devices, list) - if(idd->active[is->id]) { - if(is->remove) - if(is->remove(is, idd)) - printk(KERN_WARNING - "%s: IOC3 submodule %s remove failed " - "for pci_dev %s.\n", - __func__, module_name(is->owner), - pci_name(idd->pdev)); - idd->active[is->id] = 0; - if(is->irq_mask) - write_ireg(idd, is->irq_mask, IOC3_W_IEC); - } - up_read(&ioc3_devices_rwsem); -} - -/********************* - * Device management * - *********************/ - -static char *ioc3_class_names[] = { "unknown", "IP27 BaseIO", "IP30 system", - "MENET 1/2/3", "MENET 4", "CADduo", "Altix Serial" }; - -static int ioc3_class(struct ioc3_driver_data *idd) -{ - int res = IOC3_CLASS_NONE; - /* NIC-based logic */ - if(!strncmp(idd->nic_part, "030-0891-", 9)) - res = IOC3_CLASS_BASE_IP30; - if(!strncmp(idd->nic_part, "030-1155-", 9)) - res = IOC3_CLASS_CADDUO; - if(!strncmp(idd->nic_part, "030-1657-", 9)) - res = IOC3_CLASS_SERIAL; - if(!strncmp(idd->nic_part, "030-1664-", 9)) - res = IOC3_CLASS_SERIAL; - /* total random heuristics */ -#ifdef CONFIG_SGI_IP27 - if(!idd->nic_part[0]) - res = IOC3_CLASS_BASE_IP27; -#endif - /* print educational message */ - printk(KERN_INFO "IOC3 part: [%s], serial: [%s] => class %s\n", - idd->nic_part, idd->nic_serial, ioc3_class_names[res]); - return res; -} -/* Adds a new instance of an IOC3 card */ -static int ioc3_probe(struct pci_dev *pdev, const struct pci_device_id *pci_id) -{ - struct ioc3_driver_data *idd; - uint32_t pcmd; - int ret, id; - - /* Enable IOC3 and take ownership of it */ - if ((ret = pci_enable_device(pdev))) { - printk(KERN_WARNING - "%s: Failed to enable IOC3 device for pci_dev %s.\n", - __func__, pci_name(pdev)); - goto out; - } - pci_set_master(pdev); - -#ifdef USE_64BIT_DMA - ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(64)); - if (!ret) { - ret = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64)); - if (ret < 0) { - printk(KERN_WARNING "%s: Unable to obtain 64 bit DMA " - "for consistent allocations\n", - __func__); - } - } -#endif - - /* Set up per-IOC3 data */ - idd = kzalloc(sizeof(struct ioc3_driver_data), GFP_KERNEL); - if (!idd) { - printk(KERN_WARNING - "%s: Failed to allocate IOC3 data for pci_dev %s.\n", - __func__, pci_name(pdev)); - ret = -ENODEV; - goto out_idd; - } - spin_lock_init(&idd->ir_lock); - spin_lock_init(&idd->gpio_lock); - idd->pdev = pdev; - - /* Map all IOC3 registers. These are shared between subdevices - * so the main IOC3 module manages them. - */ - idd->pma = pci_resource_start(pdev, 0); - if (!idd->pma) { - printk(KERN_WARNING - "%s: Unable to find IOC3 resource " - "for pci_dev %s.\n", - __func__, pci_name(pdev)); - ret = -ENODEV; - goto out_pci; - } - if (!request_mem_region(idd->pma, IOC3_PCI_SIZE, "ioc3")) { - printk(KERN_WARNING - "%s: Unable to request IOC3 region " - "for pci_dev %s.\n", - __func__, pci_name(pdev)); - ret = -ENODEV; - goto out_pci; - } - idd->vma = ioremap(idd->pma, IOC3_PCI_SIZE); - if (!idd->vma) { - printk(KERN_WARNING - "%s: Unable to remap IOC3 region " - "for pci_dev %s.\n", - __func__, pci_name(pdev)); - ret = -ENODEV; - goto out_misc_region; - } - - /* Track PCI-device specific data */ - pci_set_drvdata(pdev, idd); - down_write(&ioc3_devices_rwsem); - list_add_tail(&idd->list, &ioc3_devices); - idd->id = ioc3_counter++; - up_write(&ioc3_devices_rwsem); - - idd->gpdr_shadow = readl(&idd->vma->gpdr); - - /* Read IOC3 NIC contents */ - probe_nic(idd); - - /* Detect IOC3 class */ - idd->class = ioc3_class(idd); - - /* Initialize IOC3 */ - pci_read_config_dword(pdev, PCI_COMMAND, &pcmd); - pci_write_config_dword(pdev, PCI_COMMAND, - pcmd | PCI_COMMAND_MEMORY | - PCI_COMMAND_PARITY | PCI_COMMAND_SERR | - PCI_SCR_DROP_MODE_EN); - - write_ireg(idd, ~0, IOC3_W_IEC); - writel(~0, &idd->vma->sio_ir); - - /* Set up IRQs */ - if(idd->class == IOC3_CLASS_BASE_IP30 - || idd->class == IOC3_CLASS_BASE_IP27) { - writel(0, &idd->vma->eier); - writel(~0, &idd->vma->eisr); - - idd->dual_irq = 1; - if (!request_irq(pdev->irq, ioc3_intr_eth, IRQF_SHARED, - "ioc3-eth", (void *)idd)) { - idd->irq_eth = pdev->irq; - } else { - printk(KERN_WARNING - "%s : request_irq fails for IRQ 0x%x\n ", - __func__, pdev->irq); - } - if (!request_irq(pdev->irq+2, ioc3_intr_io, IRQF_SHARED, - "ioc3-io", (void *)idd)) { - idd->irq_io = pdev->irq+2; - } else { - printk(KERN_WARNING - "%s : request_irq fails for IRQ 0x%x\n ", - __func__, pdev->irq+2); - } - } else { - if (!request_irq(pdev->irq, ioc3_intr_io, IRQF_SHARED, - "ioc3", (void *)idd)) { - idd->irq_io = pdev->irq; - } else { - printk(KERN_WARNING - "%s : request_irq fails for IRQ 0x%x\n ", - __func__, pdev->irq); - } - } - - /* Add this IOC3 to all submodules */ - for(id=0;id<IOC3_MAX_SUBMODULES;id++) - if(ioc3_submodules[id] && ioc3_submodules[id]->probe) { - idd->active[id] = 1; - idd->active[id] = !ioc3_submodules[id]->probe - (ioc3_submodules[id], idd); - } - - printk(KERN_INFO "IOC3 Master Driver loaded for %s\n", pci_name(pdev)); - - return 0; - -out_misc_region: - release_mem_region(idd->pma, IOC3_PCI_SIZE); -out_pci: - kfree(idd); -out_idd: - pci_disable_device(pdev); -out: - return ret; -} - -/* Removes a particular instance of an IOC3 card. */ -static void ioc3_remove(struct pci_dev *pdev) -{ - int id; - struct ioc3_driver_data *idd; - - idd = pci_get_drvdata(pdev); - - /* Remove this IOC3 from all submodules */ - for(id=0;id<IOC3_MAX_SUBMODULES;id++) - if(idd->active[id]) { - if(ioc3_submodules[id] && ioc3_submodules[id]->remove) - if(ioc3_submodules[id]->remove(ioc3_submodules[id], - idd)) - printk(KERN_WARNING - "%s: IOC3 submodule 0x%s remove failed " - "for pci_dev %s.\n", - __func__, - module_name(ioc3_submodules[id]->owner), - pci_name(pdev)); - idd->active[id] = 0; - } - - /* Clear and disable all IRQs */ - write_ireg(idd, ~0, IOC3_W_IEC); - writel(~0, &idd->vma->sio_ir); - - /* Release resources */ - free_irq(idd->irq_io, (void *)idd); - if(idd->dual_irq) - free_irq(idd->irq_eth, (void *)idd); - iounmap(idd->vma); - release_mem_region(idd->pma, IOC3_PCI_SIZE); - - /* Disable IOC3 and relinquish */ - pci_disable_device(pdev); - - /* Remove and free driver data */ - down_write(&ioc3_devices_rwsem); - list_del(&idd->list); - up_write(&ioc3_devices_rwsem); - kfree(idd); -} - -static struct pci_device_id ioc3_id_table[] = { - {PCI_VENDOR_ID_SGI, PCI_DEVICE_ID_SGI_IOC3, PCI_ANY_ID, PCI_ANY_ID}, - {0} -}; - -static struct pci_driver ioc3_driver = { - .name = "IOC3", - .id_table = ioc3_id_table, - .probe = ioc3_probe, - .remove = ioc3_remove, -}; - -MODULE_DEVICE_TABLE(pci, ioc3_id_table); - -/********************* - * Module management * - *********************/ - -/* Module load */ -static int __init ioc3_init(void) -{ - if (ia64_platform_is("sn2")) - return pci_register_driver(&ioc3_driver); - return -ENODEV; -} - -/* Module unload */ -static void __exit ioc3_exit(void) -{ - pci_unregister_driver(&ioc3_driver); -} - -module_init(ioc3_init); -module_exit(ioc3_exit); - -MODULE_AUTHOR("Stanislaw Skowronek <skylark@linux-mips.org>"); -MODULE_DESCRIPTION("PCI driver for SGI IOC3"); -MODULE_LICENSE("GPL"); - -EXPORT_SYMBOL_GPL(ioc3_register_submodule); -EXPORT_SYMBOL_GPL(ioc3_unregister_submodule); -EXPORT_SYMBOL_GPL(ioc3_ack); -EXPORT_SYMBOL_GPL(ioc3_gpcr_set); -EXPORT_SYMBOL_GPL(ioc3_disable); -EXPORT_SYMBOL_GPL(ioc3_enable); diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 3083dbae35f7..2f631501c75f 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -855,16 +855,6 @@ config SERIAL_CPM_CONSOLE your boot loader (lilo or loadlin) about how to pass options to the kernel at boot time.) -config SERIAL_SGI_L1_CONSOLE - bool "SGI Altix L1 serial console support" - depends on IA64_GENERIC || IA64_SGI_SN2 - select SERIAL_CORE - select SERIAL_CORE_CONSOLE - help - If you have an SGI Altix and you would like to use the system - controller serial port as your console (you want this!), - say Y. Otherwise, say N. - config SERIAL_PIC32 tristate "Microchip PIC32 serial support" depends on MACH_PIC32 @@ -982,23 +972,6 @@ config SERIAL_JSM To compile this driver as a module, choose M here: the module will be called jsm. -config SERIAL_SGI_IOC4 - tristate "SGI IOC4 controller serial support" - depends on (IA64_GENERIC || IA64_SGI_SN2) && SGI_IOC4 - select SERIAL_CORE - help - If you have an SGI Altix with an IOC4 based Base IO card - and wish to use the serial ports on this card, say Y. - Otherwise, say N. - -config SERIAL_SGI_IOC3 - tristate "SGI Altix IOC3 serial support" - depends on (IA64_GENERIC || IA64_SGI_SN2) && SGI_IOC3 - select SERIAL_CORE - help - If you have an SGI Altix with an IOC3 serial card, - say Y or M. Otherwise, say N. - config SERIAL_MSM tristate "MSM on-chip serial port support" depends on ARCH_QCOM diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile index 15a0fccadf7e..d2f44879384e 100644 --- a/drivers/tty/serial/Makefile +++ b/drivers/tty/serial/Makefile @@ -41,7 +41,6 @@ obj-$(CONFIG_SERIAL_HS_LPC32XX) += lpc32xx_hs.o obj-$(CONFIG_SERIAL_DZ) += dz.o obj-$(CONFIG_SERIAL_ZS) += zs.o obj-$(CONFIG_SERIAL_SH_SCI) += sh-sci.o -obj-$(CONFIG_SERIAL_SGI_L1_CONSOLE) += sn_console.o obj-$(CONFIG_SERIAL_CPM) += cpm_uart/ obj-$(CONFIG_SERIAL_IMX) += imx.o obj-$(CONFIG_SERIAL_MPC52xx) += mpc52xx_uart.o @@ -53,8 +52,6 @@ obj-$(CONFIG_SERIAL_SC16IS7XX_CORE) += sc16is7xx.o obj-$(CONFIG_SERIAL_JSM) += jsm/ obj-$(CONFIG_SERIAL_TXX9) += serial_txx9.o obj-$(CONFIG_SERIAL_VR41XX) += vr41xx_siu.o -obj-$(CONFIG_SERIAL_SGI_IOC4) += ioc4_serial.o -obj-$(CONFIG_SERIAL_SGI_IOC3) += ioc3_serial.o obj-$(CONFIG_SERIAL_ATMEL) += atmel_serial.o obj-$(CONFIG_SERIAL_UARTLITE) += uartlite.o obj-$(CONFIG_SERIAL_MSM) += msm_serial.o diff --git a/drivers/tty/serial/ioc3_serial.c b/drivers/tty/serial/ioc3_serial.c deleted file mode 100644 index d8a1cdd6a53d..000000000000 --- a/drivers/tty/serial/ioc3_serial.c +++ /dev/null @@ -1,2195 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright (C) 2005 Silicon Graphics, Inc. All Rights Reserved. - */ - -/* - * This file contains a module version of the ioc3 serial driver. This - * includes all the support functions needed (support functions, etc.) - * and the serial driver itself. - */ -#include <linux/errno.h> -#include <linux/tty.h> -#include <linux/tty_flip.h> -#include <linux/serial.h> -#include <linux/circ_buf.h> -#include <linux/serial_reg.h> -#include <linux/module.h> -#include <linux/pci.h> -#include <linux/serial_core.h> -#include <linux/ioc3.h> -#include <linux/slab.h> - -/* - * Interesting things about the ioc3 - */ - -#define LOGICAL_PORTS 2 /* rs232(0) and rs422(1) */ -#define PORTS_PER_CARD 2 -#define LOGICAL_PORTS_PER_CARD (PORTS_PER_CARD * LOGICAL_PORTS) -#define MAX_CARDS 8 -#define MAX_LOGICAL_PORTS (LOGICAL_PORTS_PER_CARD * MAX_CARDS) - -/* determine given the sio_ir what port it applies to */ -#define GET_PORT_FROM_SIO_IR(_x) (_x & SIO_IR_SA) ? 0 : 1 - - -/* - * we have 2 logical ports (rs232, rs422) for each physical port - * evens are rs232, odds are rs422 - */ -#define GET_PHYSICAL_PORT(_x) ((_x) >> 1) -#define GET_LOGICAL_PORT(_x) ((_x) & 1) -#define IS_PHYSICAL_PORT(_x) !((_x) & 1) -#define IS_RS232(_x) !((_x) & 1) - -static unsigned int Num_of_ioc3_cards; -static unsigned int Submodule_slot; - -/* defining this will get you LOTS of great debug info */ -//#define DEBUG_INTERRUPTS -#define DPRINT_CONFIG(_x...) ; -//#define DPRINT_CONFIG(_x...) printk _x -#define NOT_PROGRESS() ; -//#define NOT_PROGRESS() printk("%s : fails %d\n", __func__, __LINE__) - -/* number of characters we want to transmit to the lower level at a time */ -#define MAX_CHARS 256 -#define FIFO_SIZE (MAX_CHARS-1) /* it's a uchar */ - -/* Device name we're using */ -#define DEVICE_NAME "ttySIOC" -#define DEVICE_MAJOR 204 -#define DEVICE_MINOR 116 - -/* flags for next_char_state */ -#define NCS_BREAK 0x1 -#define NCS_PARITY 0x2 -#define NCS_FRAMING 0x4 -#define NCS_OVERRUN 0x8 - -/* cause we need SOME parameters ... */ -#define MIN_BAUD_SUPPORTED 1200 -#define MAX_BAUD_SUPPORTED 115200 - -/* protocol types supported */ -#define PROTO_RS232 0 -#define PROTO_RS422 1 - -/* Notification types */ -#define N_DATA_READY 0x01 -#define N_OUTPUT_LOWAT 0x02 -#define N_BREAK 0x04 -#define N_PARITY_ERROR 0x08 -#define N_FRAMING_ERROR 0x10 -#define N_OVERRUN_ERROR 0x20 -#define N_DDCD 0x40 -#define N_DCTS 0x80 - -#define N_ALL_INPUT (N_DATA_READY | N_BREAK \ - | N_PARITY_ERROR | N_FRAMING_ERROR \ - | N_OVERRUN_ERROR | N_DDCD | N_DCTS) - -#define N_ALL_OUTPUT N_OUTPUT_LOWAT - -#define N_ALL_ERRORS (N_PARITY_ERROR | N_FRAMING_ERROR \ - | N_OVERRUN_ERROR) - -#define N_ALL (N_DATA_READY | N_OUTPUT_LOWAT | N_BREAK \ - | N_PARITY_ERROR | N_FRAMING_ERROR \ - | N_OVERRUN_ERROR | N_DDCD | N_DCTS) - -#define SER_CLK_SPEED(prediv) ((22000000 << 1) / prediv) -#define SER_DIVISOR(x, clk) (((clk) + (x) * 8) / ((x) * 16)) -#define DIVISOR_TO_BAUD(div, clk) ((clk) / 16 / (div)) - -/* Some masks */ -#define LCR_MASK_BITS_CHAR (UART_LCR_WLEN5 | UART_LCR_WLEN6 \ - | UART_LCR_WLEN7 | UART_LCR_WLEN8) -#define LCR_MASK_STOP_BITS (UART_LCR_STOP) - -#define PENDING(_a, _p) (readl(&(_p)->vma->sio_ir) & (_a)->ic_enable) - -#define RING_BUF_SIZE 4096 -#define BUF_SIZE_BIT SBBR_L_SIZE -#define PROD_CONS_MASK PROD_CONS_PTR_4K - -#define TOTAL_RING_BUF_SIZE (RING_BUF_SIZE * 4) - -/* driver specific - one per card */ -struct ioc3_card { - struct { - /* uart ports are allocated here */ - struct uart_port icp_uart_port[LOGICAL_PORTS]; - /* the ioc3_port used for this port */ - struct ioc3_port *icp_port; - } ic_port[PORTS_PER_CARD]; - /* currently enabled interrupts */ - uint32_t ic_enable; -}; - -/* Local port info for each IOC3 serial port */ -struct ioc3_port { - /* handy reference material */ - struct uart_port *ip_port; - struct ioc3_card *ip_card; - struct ioc3_driver_data *ip_idd; - struct ioc3_submodule *ip_is; - - /* pci mem addresses for this port */ - struct ioc3_serialregs __iomem *ip_serial_regs; - struct ioc3_uartregs __iomem *ip_uart_regs; - - /* Ring buffer page for this port */ - dma_addr_t ip_dma_ringbuf; - /* vaddr of ring buffer */ - struct ring_buffer *ip_cpu_ringbuf; - - /* Rings for this port */ - struct ring *ip_inring; - struct ring *ip_outring; - - /* Hook to port specific values */ - struct port_hooks *ip_hooks; - - spinlock_t ip_lock; - - /* Various rx/tx parameters */ - int ip_baud; - int ip_tx_lowat; - int ip_rx_timeout; - - /* Copy of notification bits */ - int ip_notify; - - /* Shadow copies of various registers so we don't need to PIO - * read them constantly - */ - uint32_t ip_sscr; - uint32_t ip_tx_prod; - uint32_t ip_rx_cons; - unsigned char ip_flags; -}; - -/* tx low water mark. We need to notify the driver whenever tx is getting - * close to empty so it can refill the tx buffer and keep things going. - * Let's assume that if we interrupt 1 ms before the tx goes idle, we'll - * have no trouble getting in more chars in time (I certainly hope so). - */ -#define TX_LOWAT_LATENCY 1000 -#define TX_LOWAT_HZ (1000000 / TX_LOWAT_LATENCY) -#define TX_LOWAT_CHARS(baud) (baud / 10 / TX_LOWAT_HZ) - -/* Flags per port */ -#define INPUT_HIGH 0x01 - /* used to signify that we have turned off the rx_high - * temporarily - we need to drain the fifo and don't - * want to get blasted with interrupts. - */ -#define DCD_ON 0x02 - /* DCD state is on */ -#define LOWAT_WRITTEN 0x04 -#define READ_ABORTED 0x08 - /* the read was aborted - used to avaoid infinate looping - * in the interrupt handler - */ -#define INPUT_ENABLE 0x10 - -/* Since each port has different register offsets and bitmasks - * for everything, we'll store those that we need in tables so we - * don't have to be constantly checking the port we are dealing with. - */ -struct port_hooks { - uint32_t intr_delta_dcd; - uint32_t intr_delta_cts; - uint32_t intr_tx_mt; - uint32_t intr_rx_timer; - uint32_t intr_rx_high; - uint32_t intr_tx_explicit; - uint32_t intr_clear; - uint32_t intr_all; - char rs422_select_pin; -}; - -static struct port_hooks hooks_array[PORTS_PER_CARD] = { - /* values for port A */ - { - .intr_delta_dcd = SIO_IR_SA_DELTA_DCD, - .intr_delta_cts = SIO_IR_SA_DELTA_CTS, - .intr_tx_mt = SIO_IR_SA_TX_MT, - .intr_rx_timer = SIO_IR_SA_RX_TIMER, - .intr_rx_high = SIO_IR_SA_RX_HIGH, - .intr_tx_explicit = SIO_IR_SA_TX_EXPLICIT, - .intr_clear = (SIO_IR_SA_TX_MT | SIO_IR_SA_RX_FULL - | SIO_IR_SA_RX_HIGH - | SIO_IR_SA_RX_TIMER - | SIO_IR_SA_DELTA_DCD - | SIO_IR_SA_DELTA_CTS - | SIO_IR_SA_INT - | SIO_IR_SA_TX_EXPLICIT - | SIO_IR_SA_MEMERR), - .intr_all = SIO_IR_SA, - .rs422_select_pin = GPPR_UARTA_MODESEL_PIN, - }, - - /* values for port B */ - { - .intr_delta_dcd = SIO_IR_SB_DELTA_DCD, - .intr_delta_cts = SIO_IR_SB_DELTA_CTS, - .intr_tx_mt = SIO_IR_SB_TX_MT, - .intr_rx_timer = SIO_IR_SB_RX_TIMER, - .intr_rx_high = SIO_IR_SB_RX_HIGH, - .intr_tx_explicit = SIO_IR_SB_TX_EXPLICIT, - .intr_clear = (SIO_IR_SB_TX_MT | SIO_IR_SB_RX_FULL - | SIO_IR_SB_RX_HIGH - | SIO_IR_SB_RX_TIMER - | SIO_IR_SB_DELTA_DCD - | SIO_IR_SB_DELTA_CTS - | SIO_IR_SB_INT - | SIO_IR_SB_TX_EXPLICIT - | SIO_IR_SB_MEMERR), - .intr_all = SIO_IR_SB, - .rs422_select_pin = GPPR_UARTB_MODESEL_PIN, - } -}; - -struct ring_entry { - union { - struct { - uint32_t alldata; - uint32_t allsc; - } all; - struct { - char data[4]; /* data bytes */ - char sc[4]; /* status/control */ - } s; - } u; -}; - -/* Test the valid bits in any of the 4 sc chars using "allsc" member */ -#define RING_ANY_VALID \ - ((uint32_t)(RXSB_MODEM_VALID | RXSB_DATA_VALID) * 0x01010101) - -#define ring_sc u.s.sc -#define ring_data u.s.data -#define ring_allsc u.all.allsc - -/* Number of entries per ring buffer. */ -#define ENTRIES_PER_RING (RING_BUF_SIZE / (int) sizeof(struct ring_entry)) - -/* An individual ring */ -struct ring { - struct ring_entry entries[ENTRIES_PER_RING]; -}; - -/* The whole enchilada */ -struct ring_buffer { - struct ring TX_A; - struct ring RX_A; - struct ring TX_B; - struct ring RX_B; -}; - -/* Get a ring from a port struct */ -#define RING(_p, _wh) &(((struct ring_buffer *)((_p)->ip_cpu_ringbuf))->_wh) - -/* for Infinite loop detection */ -#define MAXITER 10000000 - - -/** - * set_baud - Baud rate setting code - * @port: port to set - * @baud: baud rate to use - */ -static int set_baud(struct ioc3_port *port, int baud) -{ - int divisor; - int actual_baud; - int diff; - int lcr, prediv; - struct ioc3_uartregs __iomem *uart; - - for (prediv = 6; prediv < 64; prediv++) { - divisor = SER_DIVISOR(baud, SER_CLK_SPEED(prediv)); - if (!divisor) - continue; /* invalid divisor */ - actual_baud = DIVISOR_TO_BAUD(divisor, SER_CLK_SPEED(prediv)); - - diff = actual_baud - baud; - if (diff < 0) - diff = -diff; - - /* if we're within 1% we've found a match */ - if (diff * 100 <= actual_baud) - break; - } - - /* if the above loop completed, we didn't match - * the baud rate. give up. - */ - if (prediv == 64) { - NOT_PROGRESS(); - return 1; - } - - uart = port->ip_uart_regs; - lcr = readb(&uart->iu_lcr); - - writeb(lcr | UART_LCR_DLAB, &uart->iu_lcr); - writeb((unsigned char)divisor, &uart->iu_dll); - writeb((unsigned char)(divisor >> 8), &uart->iu_dlm); - writeb((unsigned char)prediv, &uart->iu_scr); - writeb((unsigned char)lcr, &uart->iu_lcr); - - return 0; -} - -/** - * get_ioc3_port - given a uart port, return the control structure - * @the_port: uart port to find - */ -static struct ioc3_port *get_ioc3_port(struct uart_port *the_port) -{ - struct ioc3_driver_data *idd = dev_get_drvdata(the_port->dev); - struct ioc3_card *card_ptr = idd->data[Submodule_slot]; - int ii, jj; - - if (!card_ptr) { - NOT_PROGRESS(); - return NULL; - } - for (ii = 0; ii < PORTS_PER_CARD; ii++) { - for (jj = 0; jj < LOGICAL_PORTS; jj++) { - if (the_port == &card_ptr->ic_port[ii].icp_uart_port[jj]) - return card_ptr->ic_port[ii].icp_port; - } - } - NOT_PROGRESS(); - return NULL; -} - -/** - * port_init - Initialize the sio and ioc3 hardware for a given port - * called per port from attach... - * @port: port to initialize - */ -static inline int port_init(struct ioc3_port *port) -{ - uint32_t sio_cr; - struct port_hooks *hooks = port->ip_hooks; - struct ioc3_uartregs __iomem *uart; - int reset_loop_counter = 0xfffff; - struct ioc3_driver_data *idd = port->ip_idd; - - /* Idle the IOC3 serial interface */ - writel(SSCR_RESET, &port->ip_serial_regs->sscr); - - /* Wait until any pending bus activity for this port has ceased */ - do { - sio_cr = readl(&idd->vma->sio_cr); - if (reset_loop_counter-- <= 0) { - printk(KERN_WARNING - "IOC3 unable to come out of reset" - " scr 0x%x\n", sio_cr); - return -1; - } - } while (!(sio_cr & SIO_CR_ARB_DIAG_IDLE) && - (((sio_cr &= SIO_CR_ARB_DIAG) == SIO_CR_ARB_DIAG_TXA) - || sio_cr == SIO_CR_ARB_DIAG_TXB - || sio_cr == SIO_CR_ARB_DIAG_RXA - || sio_cr == SIO_CR_ARB_DIAG_RXB)); - - /* Finish reset sequence */ - writel(0, &port->ip_serial_regs->sscr); - - /* Once RESET is done, reload cached tx_prod and rx_cons values - * and set rings to empty by making prod == cons - */ - port->ip_tx_prod = readl(&port->ip_serial_regs->stcir) & PROD_CONS_MASK; - writel(port->ip_tx_prod, &port->ip_serial_regs->stpir); - port->ip_rx_cons = readl(&port->ip_serial_regs->srpir) & PROD_CONS_MASK; - writel(port->ip_rx_cons | SRCIR_ARM, &port->ip_serial_regs->srcir); - - /* Disable interrupts for this 16550 */ - uart = port->ip_uart_regs; - writeb(0, &uart->iu_lcr); - writeb(0, &uart->iu_ier); - - /* Set the default baud */ - set_baud(port, port->ip_baud); - - /* Set line control to 8 bits no parity */ - writeb(UART_LCR_WLEN8 | 0, &uart->iu_lcr); - /* UART_LCR_STOP == 1 stop */ - - /* Enable the FIFOs */ - writeb(UART_FCR_ENABLE_FIFO, &uart->iu_fcr); - /* then reset 16550 FIFOs */ - writeb(UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT, - &uart->iu_fcr); - - /* Clear modem control register */ - writeb(0, &uart->iu_mcr); - - /* Clear deltas in modem status register */ - writel(0, &port->ip_serial_regs->shadow); - - /* Only do this once per port pair */ - if (port->ip_hooks == &hooks_array[0]) { - unsigned long ring_pci_addr; - uint32_t __iomem *sbbr_l, *sbbr_h; - - sbbr_l = &idd->vma->sbbr_l; - sbbr_h = &idd->vma->sbbr_h; - ring_pci_addr = (unsigned long __iomem)port->ip_dma_ringbuf; - DPRINT_CONFIG(("%s: ring_pci_addr 0x%p\n", - __func__, (void *)ring_pci_addr)); - - writel((unsigned int)((uint64_t) ring_pci_addr >> 32), sbbr_h); - writel((unsigned int)ring_pci_addr | BUF_SIZE_BIT, sbbr_l); - } - - /* Set the receive timeout value to 10 msec */ - writel(SRTR_HZ / 100, &port->ip_serial_regs->srtr); - - /* Set rx threshold, enable DMA */ - /* Set high water mark at 3/4 of full ring */ - port->ip_sscr = (ENTRIES_PER_RING * 3 / 4); - - /* uart experiences pauses at high baud rate reducing actual - * throughput by 10% or so unless we enable high speed polling - * XXX when this hardware bug is resolved we should revert to - * normal polling speed - */ - port->ip_sscr |= SSCR_HIGH_SPD; - - writel(port->ip_sscr, &port->ip_serial_regs->sscr); - - /* Disable and clear all serial related interrupt bits */ - port->ip_card->ic_enable &= ~hooks->intr_clear; - ioc3_disable(port->ip_is, idd, hooks->intr_clear); - ioc3_ack(port->ip_is, idd, hooks->intr_clear); - return 0; -} - -/** - * enable_intrs - enable interrupts - * @port: port to enable - * @mask: mask to use - */ -static void enable_intrs(struct ioc3_port *port, uint32_t mask) -{ - if ((port->ip_card->ic_enable & mask) != mask) { - port->ip_card->ic_enable |= mask; - ioc3_enable(port->ip_is, port->ip_idd, mask); - } -} - -/** - * local_open - local open a port - * @port: port to open - */ -static inline int local_open(struct ioc3_port *port) -{ - int spiniter = 0; - - port->ip_flags = INPUT_ENABLE; - - /* Pause the DMA interface if necessary */ - if (port->ip_sscr & SSCR_DMA_EN) { - writel(port->ip_sscr | SSCR_DMA_PAUSE, - &port->ip_serial_regs->sscr); - while ((readl(&port->ip_serial_regs->sscr) - & SSCR_PAUSE_STATE) == 0) { - spiniter++; - if (spiniter > MAXITER) { - NOT_PROGRESS(); - return -1; - } - } - } - - /* Reset the input fifo. If the uart received chars while the port - * was closed and DMA is not enabled, the uart may have a bunch of - * chars hanging around in its rx fifo which will not be discarded - * by rclr in the upper layer. We must get rid of them here. - */ - writeb(UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR, - &port->ip_uart_regs->iu_fcr); - - writeb(UART_LCR_WLEN8, &port->ip_uart_regs->iu_lcr); - /* UART_LCR_STOP == 1 stop */ - - /* Re-enable DMA, set default threshold to intr whenever there is - * data available. - */ - port->ip_sscr &= ~SSCR_RX_THRESHOLD; - port->ip_sscr |= 1; /* default threshold */ - - /* Plug in the new sscr. This implicitly clears the DMA_PAUSE - * flag if it was set above - */ - writel(port->ip_sscr, &port->ip_serial_regs->sscr); - port->ip_tx_lowat = 1; - return 0; -} - -/** - * set_rx_timeout - Set rx timeout and threshold values. - * @port: port to use - * @timeout: timeout value in ticks - */ -static inline int set_rx_timeout(struct ioc3_port *port, int timeout) -{ - int threshold; - - port->ip_rx_timeout = timeout; - - /* Timeout is in ticks. Let's figure out how many chars we - * can receive at the current baud rate in that interval - * and set the rx threshold to that amount. There are 4 chars - * per ring entry, so we'll divide the number of chars that will - * arrive in timeout by 4. - * So .... timeout * baud / 10 / HZ / 4, with HZ = 100. - */ - threshold = timeout * port->ip_baud / 4000; - if (threshold == 0) - threshold = 1; /* otherwise we'll intr all the time! */ - - if ((unsigned)threshold > (unsigned)SSCR_RX_THRESHOLD) - return 1; - - port->ip_sscr &= ~SSCR_RX_THRESHOLD; - port->ip_sscr |= threshold; - writel(port->ip_sscr, &port->ip_serial_regs->sscr); - - /* Now set the rx timeout to the given value - * again timeout * SRTR_HZ / HZ - */ - timeout = timeout * SRTR_HZ / 100; - if (timeout > SRTR_CNT) - timeout = SRTR_CNT; - writel(timeout, &port->ip_serial_regs->srtr); - return 0; -} - -/** - * config_port - config the hardware - * @port: port to config - * @baud: baud rate for the port - * @byte_size: data size - * @stop_bits: number of stop bits - * @parenb: parity enable ? - * @parodd: odd parity ? - */ -static inline int -config_port(struct ioc3_port *port, - int baud, int byte_size, int stop_bits, int parenb, int parodd) -{ - char lcr, sizebits; - int spiniter = 0; - - DPRINT_CONFIG(("%s: line %d baud %d byte_size %d stop %d parenb %d " - "parodd %d\n", - __func__, ((struct uart_port *)port->ip_port)->line, - baud, byte_size, stop_bits, parenb, parodd)); - - if (set_baud(port, baud)) - return 1; - - switch (byte_size) { - case 5: - sizebits = UART_LCR_WLEN5; - break; - case 6: - sizebits = UART_LCR_WLEN6; - break; - case 7: - sizebits = UART_LCR_WLEN7; - break; - case 8: - sizebits = UART_LCR_WLEN8; - break; - default: - return 1; - } - - /* Pause the DMA interface if necessary */ - if (port->ip_sscr & SSCR_DMA_EN) { - writel(port->ip_sscr | SSCR_DMA_PAUSE, - &port->ip_serial_regs->sscr); - while ((readl(&port->ip_serial_regs->sscr) - & SSCR_PAUSE_STATE) == 0) { - spiniter++; - if (spiniter > MAXITER) - return -1; - } - } - - /* Clear relevant fields in lcr */ - lcr = readb(&port->ip_uart_regs->iu_lcr); - lcr &= ~(LCR_MASK_BITS_CHAR | UART_LCR_EPAR | - UART_LCR_PARITY | LCR_MASK_STOP_BITS); - - /* Set byte size in lcr */ - lcr |= sizebits; - - /* Set parity */ - if (parenb) { - lcr |= UART_LCR_PARITY; - if (!parodd) - lcr |= UART_LCR_EPAR; - } - - /* Set stop bits */ - if (stop_bits) - lcr |= UART_LCR_STOP /* 2 stop bits */ ; - - writeb(lcr, &port->ip_uart_regs->iu_lcr); - - /* Re-enable the DMA interface if necessary */ - if (port->ip_sscr & SSCR_DMA_EN) { - writel(port->ip_sscr, &port->ip_serial_regs->sscr); - } - port->ip_baud = baud; - - /* When we get within this number of ring entries of filling the - * entire ring on tx, place an EXPLICIT intr to generate a lowat - * notification when output has drained. - */ - port->ip_tx_lowat = (TX_LOWAT_CHARS(baud) + 3) / 4; - if (port->ip_tx_lowat == 0) - port->ip_tx_lowat = 1; - - set_rx_timeout(port, 2); - return 0; -} - -/** - * do_write - Write bytes to the port. Returns the number of bytes - * actually written. Called from transmit_chars - * @port: port to use - * @buf: the stuff to write - * @len: how many bytes in 'buf' - */ -static inline int do_write(struct ioc3_port *port, char *buf, int len) -{ - int prod_ptr, cons_ptr, total = 0; - struct ring *outring; - struct ring_entry *entry; - struct port_hooks *hooks = port->ip_hooks; - - BUG_ON(!(len >= 0)); - - prod_ptr = port->ip_tx_prod; - cons_ptr = readl(&port->ip_serial_regs->stcir) & PROD_CONS_MASK; - outring = port->ip_outring; - - /* Maintain a 1-entry red-zone. The ring buffer is full when - * (cons - prod) % ring_size is 1. Rather than do this subtraction - * in the body of the loop, I'll do it now. - */ - cons_ptr = (cons_ptr - (int)sizeof(struct ring_entry)) & PROD_CONS_MASK; - - /* Stuff the bytes into the output */ - while ((prod_ptr != cons_ptr) && (len > 0)) { - int xx; - - /* Get 4 bytes (one ring entry) at a time */ - entry = (struct ring_entry *)((caddr_t) outring + prod_ptr); - - /* Invalidate all entries */ - entry->ring_allsc = 0; - - /* Copy in some bytes */ - for (xx = 0; (xx < 4) && (len > 0); xx++) { - entry->ring_data[xx] = *buf++; - entry->ring_sc[xx] = TXCB_VALID; - len--; - total++; - } - - /* If we are within some small threshold of filling up the - * entire ring buffer, we must place an EXPLICIT intr here - * to generate a lowat interrupt in case we subsequently - * really do fill up the ring and the caller goes to sleep. - * No need to place more than one though. - */ - if (!(port->ip_flags & LOWAT_WRITTEN) && - ((cons_ptr - prod_ptr) & PROD_CONS_MASK) - <= port->ip_tx_lowat * (int)sizeof(struct ring_entry)) { - port->ip_flags |= LOWAT_WRITTEN; - entry->ring_sc[0] |= TXCB_INT_WHEN_DONE; - } - - /* Go on to next entry */ - prod_ptr += sizeof(struct ring_entry); - prod_ptr &= PROD_CONS_MASK; - } - - /* If we sent something, start DMA if necessary */ - if (total > 0 && !(port->ip_sscr & SSCR_DMA_EN)) { - port->ip_sscr |= SSCR_DMA_EN; - writel(port->ip_sscr, &port->ip_serial_regs->sscr); - } - - /* Store the new producer pointer. If tx is disabled, we stuff the - * data into the ring buffer, but we don't actually start tx. - */ - if (!uart_tx_stopped(port->ip_port)) { - writel(prod_ptr, &port->ip_serial_regs->stpir); - - /* If we are now transmitting, enable tx_mt interrupt so we - * can disable DMA if necessary when the tx finishes. - */ - if (total > 0) - enable_intrs(port, hooks->intr_tx_mt); - } - port->ip_tx_prod = prod_ptr; - - return total; -} - -/** - * disable_intrs - disable interrupts - * @port: port to enable - * @mask: mask to use - */ -static inline void disable_intrs(struct ioc3_port *port, uint32_t mask) -{ - if (port->ip_card->ic_enable & mask) { - ioc3_disable(port->ip_is, port->ip_idd, mask); - port->ip_card->ic_enable &= ~mask; - } -} - -/** - * set_notification - Modify event notification - * @port: port to use - * @mask: events mask - * @set_on: set ? - */ -static int set_notification(struct ioc3_port *port, int mask, int set_on) -{ - struct port_hooks *hooks = port->ip_hooks; - uint32_t intrbits, sscrbits; - - BUG_ON(!mask); - - intrbits = sscrbits = 0; - - if (mask & N_DATA_READY) - intrbits |= (hooks->intr_rx_timer | hooks->intr_rx_high); - if (mask & N_OUTPUT_LOWAT) - intrbits |= hooks->intr_tx_explicit; - if (mask & N_DDCD) { - intrbits |= hooks->intr_delta_dcd; - sscrbits |= SSCR_RX_RING_DCD; - } - if (mask & N_DCTS) - intrbits |= hooks->intr_delta_cts; - - if (set_on) { - enable_intrs(port, intrbits); - port->ip_notify |= mask; - port->ip_sscr |= sscrbits; - } else { - disable_intrs(port, intrbits); - port->ip_notify &= ~mask; - port->ip_sscr &= ~sscrbits; - } - - /* We require DMA if either DATA_READY or DDCD notification is - * currently requested. If neither of these is requested and - * there is currently no tx in progress, DMA may be disabled. - */ - if (port->ip_notify & (N_DATA_READY | N_DDCD)) - port->ip_sscr |= SSCR_DMA_EN; - else if (!(port->ip_card->ic_enable & hooks->intr_tx_mt)) - port->ip_sscr &= ~SSCR_DMA_EN; - - writel(port->ip_sscr, &port->ip_serial_regs->sscr); - return 0; -} - -/** - * set_mcr - set the master control reg - * @the_port: port to use - * @mask1: mcr mask - * @mask2: shadow mask - */ -static inline int set_mcr(struct uart_port *the_port, - int mask1, int mask2) -{ - struct ioc3_port *port = get_ioc3_port(the_port); - uint32_t shadow; - int spiniter = 0; - char mcr; - - if (!port) - return -1; - - /* Pause the DMA interface if necessary */ - if (port->ip_sscr & SSCR_DMA_EN) { - writel(port->ip_sscr | SSCR_DMA_PAUSE, - &port->ip_serial_regs->sscr); - while ((readl(&port->ip_serial_regs->sscr) - & SSCR_PAUSE_STATE) == 0) { - spiniter++; - if (spiniter > MAXITER) - return -1; - } - } - shadow = readl(&port->ip_serial_regs->shadow); - mcr = (shadow & 0xff000000) >> 24; - - /* Set new value */ - mcr |= mask1; - shadow |= mask2; - writeb(mcr, &port->ip_uart_regs->iu_mcr); - writel(shadow, &port->ip_serial_regs->shadow); - - /* Re-enable the DMA interface if necessary */ - if (port->ip_sscr & SSCR_DMA_EN) { - writel(port->ip_sscr, &port->ip_serial_regs->sscr); - } - return 0; -} - -/** - * ioc3_set_proto - set the protocol for the port - * @port: port to use - * @proto: protocol to use - */ -static int ioc3_set_proto(struct ioc3_port *port, int proto) -{ - struct port_hooks *hooks = port->ip_hooks; - - switch (proto) { - default: - case PROTO_RS232: - /* Clear the appropriate GIO pin */ - DPRINT_CONFIG(("%s: rs232\n", __func__)); - writel(0, (&port->ip_idd->vma->gppr[0] - + hooks->rs422_select_pin)); - break; - - case PROTO_RS422: - /* Set the appropriate GIO pin */ - DPRINT_CONFIG(("%s: rs422\n", __func__)); - writel(1, (&port->ip_idd->vma->gppr[0] - + hooks->rs422_select_pin)); - break; - } - return 0; -} - -/** - * transmit_chars - upper level write, called with the_port->lock - * @the_port: port to write - */ -static void transmit_chars(struct uart_port *the_port) -{ - int xmit_count, tail, head; - int result; - char *start; - struct tty_struct *tty; - struct ioc3_port *port = get_ioc3_port(the_port); - struct uart_state *state; - - if (!the_port) - return; - if (!port) - return; - - state = the_port->state; - tty = state->port.tty; - - if (uart_circ_empty(&state->xmit) || uart_tx_stopped(the_port)) { - /* Nothing to do or hw stopped */ - set_notification(port, N_ALL_OUTPUT, 0); - return; - } - - head = state->xmit.head; - tail = state->xmit.tail; - start = (char *)&state->xmit.buf[tail]; - - /* write out all the data or until the end of the buffer */ - xmit_count = (head < tail) ? (UART_XMIT_SIZE - tail) : (head - tail); - if (xmit_count > 0) { - result = do_write(port, start, xmit_count); - if (result > 0) { - /* booking */ - xmit_count -= result; - the_port->icount.tx += result; - /* advance the pointers */ - tail += result; - tail &= UART_XMIT_SIZE - 1; - state->xmit.tail = tail; - start = (char *)&state->xmit.buf[tail]; - } - } - if (uart_circ_chars_pending(&state->xmit) < WAKEUP_CHARS) - uart_write_wakeup(the_port); - - if (uart_circ_empty(&state->xmit)) { - set_notification(port, N_OUTPUT_LOWAT, 0); - } else { - set_notification(port, N_OUTPUT_LOWAT, 1); - } -} - -/** - * ioc3_change_speed - change the speed of the port - * @the_port: port to change - * @new_termios: new termios settings - * @old_termios: old termios settings - */ -static void -ioc3_change_speed(struct uart_port *the_port, - struct ktermios *new_termios, struct ktermios *old_termios) -{ - struct ioc3_port *port = get_ioc3_port(the_port); - unsigned int cflag, iflag; - int baud; - int new_parity = 0, new_parity_enable = 0, new_stop = 0, new_data = 8; - struct uart_state *state = the_port->state; - - cflag = new_termios->c_cflag; - iflag = new_termios->c_iflag; - - switch (cflag & CSIZE) { - case CS5: - new_data = 5; - break; - case CS6: - new_data = 6; - break; - case CS7: - new_data = 7; - break; - case CS8: - new_data = 8; - break; - default: - /* cuz we always need a default ... */ - new_data = 5; - break; - } - if (cflag & CSTOPB) { - new_stop = 1; - } - if (cflag & PARENB) { - new_parity_enable = 1; - if (cflag & PARODD) - new_parity = 1; - } - baud = uart_get_baud_rate(the_port, new_termios, old_termios, - MIN_BAUD_SUPPORTED, MAX_BAUD_SUPPORTED); - DPRINT_CONFIG(("%s: returned baud %d for line %d\n", __func__, baud, - the_port->line)); - - if (!the_port->fifosize) - the_port->fifosize = FIFO_SIZE; - uart_update_timeout(the_port, cflag, baud); - - the_port->ignore_status_mask = N_ALL_INPUT; - - state->port.low_latency = 1; - - if (iflag & IGNPAR) - the_port->ignore_status_mask &= ~(N_PARITY_ERROR - | N_FRAMING_ERROR); - if (iflag & IGNBRK) { - the_port->ignore_status_mask &= ~N_BREAK; - if (iflag & IGNPAR) - the_port->ignore_status_mask &= ~N_OVERRUN_ERROR; - } - if (!(cflag & CREAD)) { - /* ignore everything */ - the_port->ignore_status_mask &= ~N_DATA_READY; - } - - if (cflag & CRTSCTS) { - /* enable hardware flow control */ - port->ip_sscr |= SSCR_HFC_EN; - } - else { - /* disable hardware flow control */ - port->ip_sscr &= ~SSCR_HFC_EN; - } - writel(port->ip_sscr, &port->ip_serial_regs->sscr); - - /* Set the configuration and proper notification call */ - DPRINT_CONFIG(("%s : port 0x%p line %d cflag 0%o " - "config_port(baud %d data %d stop %d penable %d " - " parity %d), notification 0x%x\n", - __func__, (void *)port, the_port->line, cflag, baud, - new_data, new_stop, new_parity_enable, new_parity, - the_port->ignore_status_mask)); - - if ((config_port(port, baud, /* baud */ - new_data, /* byte size */ - new_stop, /* stop bits */ - new_parity_enable, /* set parity */ - new_parity)) >= 0) { /* parity 1==odd */ - set_notification(port, the_port->ignore_status_mask, 1); - } -} - -/** - * ic3_startup_local - Start up the serial port - returns >= 0 if no errors - * @the_port: Port to operate on - */ -static inline int ic3_startup_local(struct uart_port *the_port) -{ - struct ioc3_port *port; - - if (!the_port) { - NOT_PROGRESS(); - return -1; - } - - port = get_ioc3_port(the_port); - if (!port) { - NOT_PROGRESS(); - return -1; - } - - local_open(port); - - /* set the protocol */ - ioc3_set_proto(port, IS_RS232(the_port->line) ? PROTO_RS232 : - PROTO_RS422); - return 0; -} - -/* - * ioc3_cb_output_lowat - called when the output low water mark is hit - * @port: port to output - */ -static void ioc3_cb_output_lowat(struct ioc3_port *port) -{ - unsigned long pflags; - - /* the_port->lock is set on the call here */ - if (port->ip_port) { - spin_lock_irqsave(&port->ip_port->lock, pflags); - transmit_chars(port->ip_port); - spin_unlock_irqrestore(&port->ip_port->lock, pflags); - } -} - -/* - * ioc3_cb_post_ncs - called for some basic errors - * @port: port to use - * @ncs: event - */ -static void ioc3_cb_post_ncs(struct uart_port *the_port, int ncs) -{ - struct uart_icount *icount; - - icount = &the_port->icount; - - if (ncs & NCS_BREAK) - icount->brk++; - if (ncs & NCS_FRAMING) - icount->frame++; - if (ncs & NCS_OVERRUN) - icount->overrun++; - if (ncs & NCS_PARITY) - icount->parity++; -} - -/** - * do_read - Read in bytes from the port. Return the number of bytes - * actually read. - * @the_port: port to use - * @buf: place to put the stuff we read - * @len: how big 'buf' is - */ - -static inline int do_read(struct uart_port *the_port, char *buf, int len) -{ - int prod_ptr, cons_ptr, total; - struct ioc3_port *port = get_ioc3_port(the_port); - struct ring *inring; - struct ring_entry *entry; - struct port_hooks *hooks; - int byte_num; - char *sc; - int loop_counter; - - BUG_ON(!(len >= 0)); - BUG_ON(!port); - hooks = port->ip_hooks; - - /* There is a nasty timing issue in the IOC3. When the rx_timer - * expires or the rx_high condition arises, we take an interrupt. - * At some point while servicing the interrupt, we read bytes from - * the ring buffer and re-arm the rx_timer. However the rx_timer is - * not started until the first byte is received *after* it is armed, - * and any bytes pending in the rx construction buffers are not drained - * to memory until either there are 4 bytes available or the rx_timer - * expires. This leads to a potential situation where data is left - * in the construction buffers forever - 1 to 3 bytes were received - * after the interrupt was generated but before the rx_timer was - * re-armed. At that point as long as no subsequent bytes are received - * the timer will never be started and the bytes will remain in the - * construction buffer forever. The solution is to execute a DRAIN - * command after rearming the timer. This way any bytes received before - * the DRAIN will be drained to memory, and any bytes received after - * the DRAIN will start the TIMER and be drained when it expires. - * Luckily, this only needs to be done when the DMA buffer is empty - * since there is no requirement that this function return all - * available data as long as it returns some. - */ - /* Re-arm the timer */ - - writel(port->ip_rx_cons | SRCIR_ARM, &port->ip_serial_regs->srcir); - - prod_ptr = readl(&port->ip_serial_regs->srpir) & PROD_CONS_MASK; - cons_ptr = port->ip_rx_cons; - - if (prod_ptr == cons_ptr) { - int reset_dma = 0; - - /* Input buffer appears empty, do a flush. */ - - /* DMA must be enabled for this to work. */ - if (!(port->ip_sscr & SSCR_DMA_EN)) { - port->ip_sscr |= SSCR_DMA_EN; - reset_dma = 1; - } - - /* Potential race condition: we must reload the srpir after - * issuing the drain command, otherwise we could think the rx - * buffer is empty, then take a very long interrupt, and when - * we come back it's full and we wait forever for the drain to - * complete. - */ - writel(port->ip_sscr | SSCR_RX_DRAIN, - &port->ip_serial_regs->sscr); - prod_ptr = readl(&port->ip_serial_regs->srpir) & PROD_CONS_MASK; - - /* We must not wait for the DRAIN to complete unless there are - * at least 8 bytes (2 ring entries) available to receive the - * data otherwise the DRAIN will never complete and we'll - * deadlock here. - * In fact, to make things easier, I'll just ignore the flush if - * there is any data at all now available. - */ - if (prod_ptr == cons_ptr) { - loop_counter = 0; - while (readl(&port->ip_serial_regs->sscr) & - SSCR_RX_DRAIN) { - loop_counter++; - if (loop_counter > MAXITER) - return -1; - } - - /* SIGH. We have to reload the prod_ptr *again* since - * the drain may have caused it to change - */ - prod_ptr = readl(&port->ip_serial_regs->srpir) - & PROD_CONS_MASK; - } - if (reset_dma) { - port->ip_sscr &= ~SSCR_DMA_EN; - writel(port->ip_sscr, &port->ip_serial_regs->sscr); - } - } - inring = port->ip_inring; - port->ip_flags &= ~READ_ABORTED; - - total = 0; - loop_counter = 0xfffff; /* to avoid hangs */ - - /* Grab bytes from the hardware */ - while ((prod_ptr != cons_ptr) && (len > 0)) { - entry = (struct ring_entry *)((caddr_t) inring + cons_ptr); - - if (loop_counter-- <= 0) { - printk(KERN_WARNING "IOC3 serial: " - "possible hang condition/" - "port stuck on read (line %d).\n", - the_port->line); - break; - } - - /* According to the producer pointer, this ring entry - * must contain some data. But if the PIO happened faster - * than the DMA, the data may not be available yet, so let's - * wait until it arrives. - */ - if ((entry->ring_allsc & RING_ANY_VALID) == 0) { - /* Indicate the read is aborted so we don't disable - * the interrupt thinking that the consumer is - * congested. - */ - port->ip_flags |= READ_ABORTED; - len = 0; - break; - } - - /* Load the bytes/status out of the ring entry */ - for (byte_num = 0; byte_num < 4 && len > 0; byte_num++) { - sc = &(entry->ring_sc[byte_num]); - - /* Check for change in modem state or overrun */ - if ((*sc & RXSB_MODEM_VALID) - && (port->ip_notify & N_DDCD)) { - /* Notify upper layer if DCD dropped */ - if ((port->ip_flags & DCD_ON) - && !(*sc & RXSB_DCD)) { - /* If we have already copied some data, - * return it. We'll pick up the carrier - * drop on the next pass. That way we - * don't throw away the data that has - * already been copied back to - * the caller's buffer. - */ - if (total > 0) { - len = 0; - break; - } - port->ip_flags &= ~DCD_ON; - - /* Turn off this notification so the - * carrier drop protocol won't see it - * again when it does a read. - */ - *sc &= ~RXSB_MODEM_VALID; - - /* To keep things consistent, we need - * to update the consumer pointer so - * the next reader won't come in and - * try to read the same ring entries - * again. This must be done here before - * the dcd change. - */ - - if ((entry->ring_allsc & RING_ANY_VALID) - == 0) { - cons_ptr += (int)sizeof - (struct ring_entry); - cons_ptr &= PROD_CONS_MASK; - } - writel(cons_ptr, - &port->ip_serial_regs->srcir); - port->ip_rx_cons = cons_ptr; - - /* Notify upper layer of carrier drop */ - if ((port->ip_notify & N_DDCD) - && port->ip_port) { - uart_handle_dcd_change - (port->ip_port, 0); - wake_up_interruptible - (&the_port->state-> - port.delta_msr_wait); - } - - /* If we had any data to return, we - * would have returned it above. - */ - return 0; - } - } - if (*sc & RXSB_MODEM_VALID) { - /* Notify that an input overrun occurred */ - if ((*sc & RXSB_OVERRUN) - && (port->ip_notify & N_OVERRUN_ERROR)) { - ioc3_cb_post_ncs(the_port, NCS_OVERRUN); - } - /* Don't look at this byte again */ - *sc &= ~RXSB_MODEM_VALID; - } - - /* Check for valid data or RX errors */ - if ((*sc & RXSB_DATA_VALID) && - ((*sc & (RXSB_PAR_ERR - | RXSB_FRAME_ERR | RXSB_BREAK)) - && (port->ip_notify & (N_PARITY_ERROR - | N_FRAMING_ERROR - | N_BREAK)))) { - /* There is an error condition on the next byte. - * If we have already transferred some bytes, - * we'll stop here. Otherwise if this is the - * first byte to be read, we'll just transfer - * it alone after notifying the - * upper layer of its status. - */ - if (total > 0) { - len = 0; - break; - } else { - if ((*sc & RXSB_PAR_ERR) && - (port-> - ip_notify & N_PARITY_ERROR)) { - ioc3_cb_post_ncs(the_port, - NCS_PARITY); - } - if ((*sc & RXSB_FRAME_ERR) && - (port-> - ip_notify & N_FRAMING_ERROR)) { - ioc3_cb_post_ncs(the_port, - NCS_FRAMING); - } - if ((*sc & RXSB_BREAK) - && (port->ip_notify & N_BREAK)) { - ioc3_cb_post_ncs - (the_port, NCS_BREAK); - } - len = 1; - } - } - if (*sc & RXSB_DATA_VALID) { - *sc &= ~RXSB_DATA_VALID; - *buf = entry->ring_data[byte_num]; - buf++; - len--; - total++; - } - } - - /* If we used up this entry entirely, go on to the next one, - * otherwise we must have run out of buffer space, so - * leave the consumer pointer here for the next read in case - * there are still unread bytes in this entry. - */ - if ((entry->ring_allsc & RING_ANY_VALID) == 0) { - cons_ptr += (int)sizeof(struct ring_entry); - cons_ptr &= PROD_CONS_MASK; - } - } - - /* Update consumer pointer and re-arm rx timer interrupt */ - writel(cons_ptr, &port->ip_serial_regs->srcir); - port->ip_rx_cons = cons_ptr; - - /* If we have now dipped below the rx high water mark and we have - * rx_high interrupt turned off, we can now turn it back on again. - */ - if ((port->ip_flags & INPUT_HIGH) && (((prod_ptr - cons_ptr) - & PROD_CONS_MASK) < - ((port-> - ip_sscr & - SSCR_RX_THRESHOLD) - << PROD_CONS_PTR_OFF))) { - port->ip_flags &= ~INPUT_HIGH; - enable_intrs(port, hooks->intr_rx_high); - } - return total; -} - -/** - * receive_chars - upper level read. - * @the_port: port to read from - */ -static int receive_chars(struct uart_port *the_port) -{ - unsigned char ch[MAX_CHARS]; - int read_count = 0, read_room, flip = 0; - struct uart_state *state = the_port->state; - struct ioc3_port *port = get_ioc3_port(the_port); - unsigned long pflags; - - /* Make sure all the pointers are "good" ones */ - if (!state) - return 0; - - if (!(port->ip_flags & INPUT_ENABLE)) - return 0; - - spin_lock_irqsave(&the_port->lock, pflags); - - read_count = do_read(the_port, ch, MAX_CHARS); - if (read_count > 0) { - flip = 1; - read_room = tty_insert_flip_string(&state->port, ch, - read_count); - the_port->icount.rx += read_count; - } - spin_unlock_irqrestore(&the_port->lock, pflags); - - if (flip) - tty_flip_buffer_push(&state->port); - - return read_count; -} - -/** - * ioc3uart_intr_one - lowest level (per port) interrupt handler. - * @is : submodule - * @idd: driver data - * @pending: interrupts to handle - */ - -static inline int -ioc3uart_intr_one(struct ioc3_submodule *is, - struct ioc3_driver_data *idd, - unsigned int pending) -{ - int port_num = GET_PORT_FROM_SIO_IR(pending); - struct port_hooks *hooks; - unsigned int rx_high_rd_aborted = 0; - unsigned long flags; - struct uart_port *the_port; - struct ioc3_port *port; - int loop_counter; - struct ioc3_card *card_ptr; - unsigned int sio_ir; - - card_ptr = idd->data[is->id]; - port = card_ptr->ic_port[port_num].icp_port; - hooks = port->ip_hooks; - - /* Possible race condition here: The tx_mt interrupt bit may be - * cleared without the intervention of the interrupt handler, - * e.g. by a write. If the top level interrupt handler reads a - * tx_mt, then some other processor does a write, starting up - * output, then we come in here, see the tx_mt and stop DMA, the - * output started by the other processor will hang. Thus we can - * only rely on tx_mt being legitimate if it is read while the - * port lock is held. Therefore this bit must be ignored in the - * passed in interrupt mask which was read by the top level - * interrupt handler since the port lock was not held at the time - * it was read. We can only rely on this bit being accurate if it - * is read while the port lock is held. So we'll clear it for now, - * and reload it later once we have the port lock. - */ - - sio_ir = pending & ~(hooks->intr_tx_mt); - spin_lock_irqsave(&port->ip_lock, flags); - - loop_counter = MAXITER; /* to avoid hangs */ - - do { - uint32_t shadow; - - if (loop_counter-- <= 0) { - printk(KERN_WARNING "IOC3 serial: " - "possible hang condition/" - "port stuck on interrupt (line %d).\n", - ((struct uart_port *)port->ip_port)->line); - break; - } - /* Handle a DCD change */ - if (sio_ir & hooks->intr_delta_dcd) { - ioc3_ack(is, idd, hooks->intr_delta_dcd); - shadow = readl(&port->ip_serial_regs->shadow); - - if ((port->ip_notify & N_DDCD) - && (shadow & SHADOW_DCD) - && (port->ip_port)) { - the_port = port->ip_port; - uart_handle_dcd_change(the_port, - shadow & SHADOW_DCD); - wake_up_interruptible - (&the_port->state->port.delta_msr_wait); - } else if ((port->ip_notify & N_DDCD) - && !(shadow & SHADOW_DCD)) { - /* Flag delta DCD/no DCD */ - uart_handle_dcd_change(port->ip_port, - shadow & SHADOW_DCD); - port->ip_flags |= DCD_ON; - } - } - - /* Handle a CTS change */ - if (sio_ir & hooks->intr_delta_cts) { - ioc3_ack(is, idd, hooks->intr_delta_cts); - shadow = readl(&port->ip_serial_regs->shadow); - - if ((port->ip_notify & N_DCTS) && (port->ip_port)) { - the_port = port->ip_port; - uart_handle_cts_change(the_port, shadow - & SHADOW_CTS); - wake_up_interruptible - (&the_port->state->port.delta_msr_wait); - } - } - - /* rx timeout interrupt. Must be some data available. Put this - * before the check for rx_high since servicing this condition - * may cause that condition to clear. - */ - if (sio_ir & hooks->intr_rx_timer) { - ioc3_ack(is, idd, hooks->intr_rx_timer); - if ((port->ip_notify & N_DATA_READY) - && (port->ip_port)) { - receive_chars(port->ip_port); - } - } - - /* rx high interrupt. Must be after rx_timer. */ - else if (sio_ir & hooks->intr_rx_high) { - /* Data available, notify upper layer */ - if ((port->ip_notify & N_DATA_READY) && port->ip_port) { - receive_chars(port->ip_port); - } - - /* We can't ACK this interrupt. If receive_chars didn't - * cause the condition to clear, we'll have to disable - * the interrupt until the data is drained. - * If the read was aborted, don't disable the interrupt - * as this may cause us to hang indefinitely. An - * aborted read generally means that this interrupt - * hasn't been delivered to the cpu yet anyway, even - * though we see it as asserted when we read the sio_ir. - */ - if ((sio_ir = PENDING(card_ptr, idd)) - & hooks->intr_rx_high) { - if (port->ip_flags & READ_ABORTED) { - rx_high_rd_aborted++; - } - else { - card_ptr->ic_enable &= ~hooks->intr_rx_high; - port->ip_flags |= INPUT_HIGH; - } - } - } - - /* We got a low water interrupt: notify upper layer to - * send more data. Must come before tx_mt since servicing - * this condition may cause that condition to clear. - */ - if (sio_ir & hooks->intr_tx_explicit) { - port->ip_flags &= ~LOWAT_WRITTEN; - ioc3_ack(is, idd, hooks->intr_tx_explicit); - if (port->ip_notify & N_OUTPUT_LOWAT) - ioc3_cb_output_lowat(port); - } - - /* Handle tx_mt. Must come after tx_explicit. */ - else if (sio_ir & hooks->intr_tx_mt) { - /* If we are expecting a lowat notification - * and we get to this point it probably means that for - * some reason the tx_explicit didn't work as expected - * (that can legitimately happen if the output buffer is - * filled up in just the right way). - * So send the notification now. - */ - if (port->ip_notify & N_OUTPUT_LOWAT) { - ioc3_cb_output_lowat(port); - - /* We need to reload the sio_ir since the lowat - * call may have caused another write to occur, - * clearing the tx_mt condition. - */ - sio_ir = PENDING(card_ptr, idd); - } - - /* If the tx_mt condition still persists even after the - * lowat call, we've got some work to do. - */ - if (sio_ir & hooks->intr_tx_mt) { - /* If we are not currently expecting DMA input, - * and the transmitter has just gone idle, - * there is no longer any reason for DMA, so - * disable it. - */ - if (!(port->ip_notify - & (N_DATA_READY | N_DDCD))) { - BUG_ON(!(port->ip_sscr - & SSCR_DMA_EN)); - port->ip_sscr &= ~SSCR_DMA_EN; - writel(port->ip_sscr, - &port->ip_serial_regs->sscr); - } - /* Prevent infinite tx_mt interrupt */ - card_ptr->ic_enable &= ~hooks->intr_tx_mt; - } - } - sio_ir = PENDING(card_ptr, idd); - - /* if the read was aborted and only hooks->intr_rx_high, - * clear hooks->intr_rx_high, so we do not loop forever. - */ - - if (rx_high_rd_aborted && (sio_ir == hooks->intr_rx_high)) { - sio_ir &= ~hooks->intr_rx_high; - } - } while (sio_ir & hooks->intr_all); - - spin_unlock_irqrestore(&port->ip_lock, flags); - ioc3_enable(is, idd, card_ptr->ic_enable); - return 0; -} - -/** - * ioc3uart_intr - field all serial interrupts - * @is : submodule - * @idd: driver data - * @pending: interrupts to handle - * - */ - -static int ioc3uart_intr(struct ioc3_submodule *is, - struct ioc3_driver_data *idd, - unsigned int pending) -{ - int ret = 0; - - /* - * The upper level interrupt handler sends interrupts for both ports - * here. So we need to call for each port with its interrupts. - */ - - if (pending & SIO_IR_SA) - ret |= ioc3uart_intr_one(is, idd, pending & SIO_IR_SA); - if (pending & SIO_IR_SB) - ret |= ioc3uart_intr_one(is, idd, pending & SIO_IR_SB); - - return ret; -} - -/** - * ic3_type - * @port: Port to operate with (we ignore since we only have one port) - * - */ -static const char *ic3_type(struct uart_port *the_port) -{ - if (IS_RS232(the_port->line)) - return "SGI IOC3 Serial [rs232]"; - else - return "SGI IOC3 Serial [rs422]"; -} - -/** - * ic3_tx_empty - Is the transmitter empty? - * @port: Port to operate on - * - */ -static unsigned int ic3_tx_empty(struct uart_port *the_port) -{ - unsigned int ret = 0; - struct ioc3_port *port = get_ioc3_port(the_port); - - if (readl(&port->ip_serial_regs->shadow) & SHADOW_TEMT) - ret = TIOCSER_TEMT; - return ret; -} - -/** - * ic3_stop_tx - stop the transmitter - * @port: Port to operate on - * - */ -static void ic3_stop_tx(struct uart_port *the_port) -{ - struct ioc3_port *port = get_ioc3_port(the_port); - - if (port) - set_notification(port, N_OUTPUT_LOWAT, 0); -} - -/** - * ic3_stop_rx - stop the receiver - * @port: Port to operate on - * - */ -static void ic3_stop_rx(struct uart_port *the_port) -{ - struct ioc3_port *port = get_ioc3_port(the_port); - - if (port) - port->ip_flags &= ~INPUT_ENABLE; -} - -/** - * null_void_function - * @port: Port to operate on - * - */ -static void null_void_function(struct uart_port *the_port) -{ -} - -/** - * ic3_shutdown - shut down the port - free irq and disable - * @port: port to shut down - * - */ -static void ic3_shutdown(struct uart_port *the_port) -{ - unsigned long port_flags; - struct ioc3_port *port; - struct uart_state *state; - - port = get_ioc3_port(the_port); - if (!port) - return; - - state = the_port->state; - wake_up_interruptible(&state->port.delta_msr_wait); - - spin_lock_irqsave(&the_port->lock, port_flags); - set_notification(port, N_ALL, 0); - spin_unlock_irqrestore(&the_port->lock, port_flags); -} - -/** - * ic3_set_mctrl - set control lines (dtr, rts, etc) - * @port: Port to operate on - * @mctrl: Lines to set/unset - * - */ -static void ic3_set_mctrl(struct uart_port *the_port, unsigned int mctrl) -{ - unsigned char mcr = 0; - - if (mctrl & TIOCM_RTS) - mcr |= UART_MCR_RTS; - if (mctrl & TIOCM_DTR) - mcr |= UART_MCR_DTR; - if (mctrl & TIOCM_OUT1) - mcr |= UART_MCR_OUT1; - if (mctrl & TIOCM_OUT2) - mcr |= UART_MCR_OUT2; - if (mctrl & TIOCM_LOOP) - mcr |= UART_MCR_LOOP; - - set_mcr(the_port, mcr, SHADOW_DTR); -} - -/** - * ic3_get_mctrl - get control line info - * @port: port to operate on - * - */ -static unsigned int ic3_get_mctrl(struct uart_port *the_port) -{ - struct ioc3_port *port = get_ioc3_port(the_port); - uint32_t shadow; - unsigned int ret = 0; - - if (!port) - return 0; - - shadow = readl(&port->ip_serial_regs->shadow); - if (shadow & SHADOW_DCD) - ret |= TIOCM_CD; - if (shadow & SHADOW_DR) - ret |= TIOCM_DSR; - if (shadow & SHADOW_CTS) - ret |= TIOCM_CTS; - return ret; -} - -/** - * ic3_start_tx - Start transmitter. Called with the_port->lock - * @port: Port to operate on - * - */ -static void ic3_start_tx(struct uart_port *the_port) -{ - struct ioc3_port *port = get_ioc3_port(the_port); - - if (port) { - set_notification(port, N_OUTPUT_LOWAT, 1); - enable_intrs(port, port->ip_hooks->intr_tx_mt); - } -} - -/** - * ic3_break_ctl - handle breaks - * @port: Port to operate on - * @break_state: Break state - * - */ -static void ic3_break_ctl(struct uart_port *the_port, int break_state) -{ -} - -/** - * ic3_startup - Start up the serial port - always return 0 (We're always on) - * @port: Port to operate on - * - */ -static int ic3_startup(struct uart_port *the_port) -{ - int retval; - struct ioc3_port *port; - struct ioc3_card *card_ptr; - unsigned long port_flags; - - if (!the_port) { - NOT_PROGRESS(); - return -ENODEV; - } - port = get_ioc3_port(the_port); - if (!port) { - NOT_PROGRESS(); - return -ENODEV; - } - card_ptr = port->ip_card; - port->ip_port = the_port; - - if (!card_ptr) { - NOT_PROGRESS(); - return -ENODEV; - } - - /* Start up the serial port */ - spin_lock_irqsave(&the_port->lock, port_flags); - retval = ic3_startup_local(the_port); - spin_unlock_irqrestore(&the_port->lock, port_flags); - return retval; -} - -/** - * ic3_set_termios - set termios stuff - * @port: port to operate on - * @termios: New settings - * @termios: Old - * - */ -static void -ic3_set_termios(struct uart_port *the_port, - struct ktermios *termios, struct ktermios *old_termios) -{ - unsigned long port_flags; - - spin_lock_irqsave(&the_port->lock, port_flags); - ioc3_change_speed(the_port, termios, old_termios); - spin_unlock_irqrestore(&the_port->lock, port_flags); -} - -/** - * ic3_request_port - allocate resources for port - no op.... - * @port: port to operate on - * - */ -static int ic3_request_port(struct uart_port *port) -{ - return 0; -} - -/* Associate the uart functions above - given to serial core */ -static const struct uart_ops ioc3_ops = { - .tx_empty = ic3_tx_empty, - .set_mctrl = ic3_set_mctrl, - .get_mctrl = ic3_get_mctrl, - .stop_tx = ic3_stop_tx, - .start_tx = ic3_start_tx, - .stop_rx = ic3_stop_rx, - .break_ctl = ic3_break_ctl, - .startup = ic3_startup, - .shutdown = ic3_shutdown, - .set_termios = ic3_set_termios, - .type = ic3_type, - .release_port = null_void_function, - .request_port = ic3_request_port, -}; - -/* - * Boot-time initialization code - */ - -static struct uart_driver ioc3_uart = { - .owner = THIS_MODULE, - .driver_name = "ioc3_serial", - .dev_name = DEVICE_NAME, - .major = DEVICE_MAJOR, - .minor = DEVICE_MINOR, - .nr = MAX_LOGICAL_PORTS -}; - -/** - * ioc3_serial_core_attach - register with serial core - * This is done during pci probing - * @is: submodule struct for this - * @idd: handle for this card - */ -static inline int ioc3_serial_core_attach( struct ioc3_submodule *is, - struct ioc3_driver_data *idd) -{ - struct ioc3_port *port; - struct uart_port *the_port; - struct ioc3_card *card_ptr = idd->data[is->id]; - int ii, phys_port; - struct pci_dev *pdev = idd->pdev; - - DPRINT_CONFIG(("%s: attach pdev 0x%p - card_ptr 0x%p\n", - __func__, pdev, (void *)card_ptr)); - - if (!card_ptr) - return -ENODEV; - - /* once around for each logical port on this card */ - for (ii = 0; ii < LOGICAL_PORTS_PER_CARD; ii++) { - phys_port = GET_PHYSICAL_PORT(ii); - the_port = &card_ptr->ic_port[phys_port]. - icp_uart_port[GET_LOGICAL_PORT(ii)]; - port = card_ptr->ic_port[phys_port].icp_port; - port->ip_port = the_port; - - DPRINT_CONFIG(("%s: attach the_port 0x%p / port 0x%p [%d/%d]\n", - __func__, (void *)the_port, (void *)port, - phys_port, ii)); - - /* membase, iobase and mapbase just need to be non-0 */ - the_port->membase = (unsigned char __iomem *)1; - the_port->iobase = (pdev->bus->number << 16) | ii; - the_port->line = (Num_of_ioc3_cards << 2) | ii; - the_port->mapbase = 1; - the_port->type = PORT_16550A; - the_port->fifosize = FIFO_SIZE; - the_port->ops = &ioc3_ops; - the_port->irq = idd->irq_io; - the_port->dev = &pdev->dev; - - if (uart_add_one_port(&ioc3_uart, the_port) < 0) { - printk(KERN_WARNING - "%s: unable to add port %d bus %d\n", - __func__, the_port->line, pdev->bus->number); - } else { - DPRINT_CONFIG(("IOC3 serial port %d irq %d bus %d\n", - the_port->line, the_port->irq, pdev->bus->number)); - } - - /* all ports are rs232 for now */ - if (IS_PHYSICAL_PORT(ii)) - ioc3_set_proto(port, PROTO_RS232); - } - return 0; -} - -/** - * ioc3uart_remove - register detach function - * @is: submodule struct for this submodule - * @idd: ioc3 driver data for this submodule - */ - -static int ioc3uart_remove(struct ioc3_submodule *is, - struct ioc3_driver_data *idd) -{ - struct ioc3_card *card_ptr = idd->data[is->id]; - struct uart_port *the_port; - struct ioc3_port *port; - int ii; - - if (card_ptr) { - for (ii = 0; ii < LOGICAL_PORTS_PER_CARD; ii++) { - the_port = &card_ptr->ic_port[GET_PHYSICAL_PORT(ii)]. - icp_uart_port[GET_LOGICAL_PORT(ii)]; - if (the_port) - uart_remove_one_port(&ioc3_uart, the_port); - port = card_ptr->ic_port[GET_PHYSICAL_PORT(ii)].icp_port; - if (port && IS_PHYSICAL_PORT(ii) - && (GET_PHYSICAL_PORT(ii) == 0)) { - pci_free_consistent(port->ip_idd->pdev, - TOTAL_RING_BUF_SIZE, - (void *)port->ip_cpu_ringbuf, - port->ip_dma_ringbuf); - kfree(port); - card_ptr->ic_port[GET_PHYSICAL_PORT(ii)]. - icp_port = NULL; - } - } - kfree(card_ptr); - idd->data[is->id] = NULL; - } - return 0; -} - -/** - * ioc3uart_probe - card probe function called from shim driver - * @is: submodule struct for this submodule - * @idd: ioc3 driver data for this card - */ - -static int -ioc3uart_probe(struct ioc3_submodule *is, struct ioc3_driver_data *idd) -{ - struct pci_dev *pdev = idd->pdev; - struct ioc3_card *card_ptr; - int ret = 0; - struct ioc3_port *port; - struct ioc3_port *ports[PORTS_PER_CARD]; - int phys_port; - int cnt; - - DPRINT_CONFIG(("%s (0x%p, 0x%p)\n", __func__, is, idd)); - - card_ptr = kzalloc(sizeof(struct ioc3_card), GFP_KERNEL); - if (!card_ptr) { - printk(KERN_WARNING "ioc3_attach_one" - ": unable to get memory for the IOC3\n"); - return -ENOMEM; - } - idd->data[is->id] = card_ptr; - Submodule_slot = is->id; - - writel(((UARTA_BASE >> 3) << SIO_CR_SER_A_BASE_SHIFT) | - ((UARTB_BASE >> 3) << SIO_CR_SER_B_BASE_SHIFT) | - (0xf << SIO_CR_CMD_PULSE_SHIFT), &idd->vma->sio_cr); - - pci_write_config_dword(pdev, PCI_LAT, 0xff00); - - /* Enable serial port mode select generic PIO pins as outputs */ - ioc3_gpcr_set(idd, GPCR_UARTA_MODESEL | GPCR_UARTB_MODESEL); - - /* Create port structures for each port */ - for (phys_port = 0; phys_port < PORTS_PER_CARD; phys_port++) { - port = kzalloc(sizeof(struct ioc3_port), GFP_KERNEL); - if (!port) { - printk(KERN_WARNING - "IOC3 serial memory not available for port\n"); - ret = -ENOMEM; - goto out4; - } - spin_lock_init(&port->ip_lock); - - /* we need to remember the previous ones, to point back to - * them farther down - setting up the ring buffers. - */ - ports[phys_port] = port; - - /* init to something useful */ - card_ptr->ic_port[phys_port].icp_port = port; - port->ip_is = is; - port->ip_idd = idd; - port->ip_baud = 9600; - port->ip_card = card_ptr; - port->ip_hooks = &hooks_array[phys_port]; - - /* Setup each port */ - if (phys_port == 0) { - port->ip_serial_regs = &idd->vma->port_a; - port->ip_uart_regs = &idd->vma->sregs.uarta; - - DPRINT_CONFIG(("%s : Port A ip_serial_regs 0x%p " - "ip_uart_regs 0x%p\n", - __func__, - (void *)port->ip_serial_regs, - (void *)port->ip_uart_regs)); - - /* setup ring buffers */ - port->ip_cpu_ringbuf = pci_alloc_consistent(pdev, - TOTAL_RING_BUF_SIZE, &port->ip_dma_ringbuf); - - BUG_ON(!((((int64_t) port->ip_dma_ringbuf) & - (TOTAL_RING_BUF_SIZE - 1)) == 0)); - port->ip_inring = RING(port, RX_A); - port->ip_outring = RING(port, TX_A); - DPRINT_CONFIG(("%s : Port A ip_cpu_ringbuf 0x%p " - "ip_dma_ringbuf 0x%p, ip_inring 0x%p " - "ip_outring 0x%p\n", - __func__, - (void *)port->ip_cpu_ringbuf, - (void *)port->ip_dma_ringbuf, - (void *)port->ip_inring, - (void *)port->ip_outring)); - } - else { - port->ip_serial_regs = &idd->vma->port_b; - port->ip_uart_regs = &idd->vma->sregs.uartb; - - DPRINT_CONFIG(("%s : Port B ip_serial_regs 0x%p " - "ip_uart_regs 0x%p\n", - __func__, - (void *)port->ip_serial_regs, - (void *)port->ip_uart_regs)); - - /* share the ring buffers */ - port->ip_dma_ringbuf = - ports[phys_port - 1]->ip_dma_ringbuf; - port->ip_cpu_ringbuf = - ports[phys_port - 1]->ip_cpu_ringbuf; - port->ip_inring = RING(port, RX_B); - port->ip_outring = RING(port, TX_B); - DPRINT_CONFIG(("%s : Port B ip_cpu_ringbuf 0x%p " - "ip_dma_ringbuf 0x%p, ip_inring 0x%p " - "ip_outring 0x%p\n", - __func__, - (void *)port->ip_cpu_ringbuf, - (void *)port->ip_dma_ringbuf, - (void *)port->ip_inring, - (void *)port->ip_outring)); - } - - DPRINT_CONFIG(("%s : port %d [addr 0x%p] card_ptr 0x%p", - __func__, - phys_port, (void *)port, (void *)card_ptr)); - DPRINT_CONFIG((" ip_serial_regs 0x%p ip_uart_regs 0x%p\n", - (void *)port->ip_serial_regs, - (void *)port->ip_uart_regs)); - - /* Initialize the hardware for IOC3 */ - port_init(port); - - DPRINT_CONFIG(("%s: phys_port %d port 0x%p inring 0x%p " - "outring 0x%p\n", - __func__, - phys_port, (void *)port, - (void *)port->ip_inring, - (void *)port->ip_outring)); - - } - - /* register port with the serial core */ - - ret = ioc3_serial_core_attach(is, idd); - if (ret) - goto out4; - - Num_of_ioc3_cards++; - - return ret; - - /* error exits that give back resources */ -out4: - for (cnt = 0; cnt < phys_port; cnt++) - kfree(ports[cnt]); - - kfree(card_ptr); - return ret; -} - -static struct ioc3_submodule ioc3uart_ops = { - .name = "IOC3uart", - .probe = ioc3uart_probe, - .remove = ioc3uart_remove, - /* call .intr for both ports initially */ - .irq_mask = SIO_IR_SA | SIO_IR_SB, - .intr = ioc3uart_intr, - .owner = THIS_MODULE, -}; - -/** - * ioc3_detect - module init called, - */ -static int __init ioc3uart_init(void) -{ - int ret; - - /* register with serial core */ - if ((ret = uart_register_driver(&ioc3_uart)) < 0) { - printk(KERN_WARNING - "%s: Couldn't register IOC3 uart serial driver\n", - __func__); - return ret; - } - ret = ioc3_register_submodule(&ioc3uart_ops); - if (ret) - uart_unregister_driver(&ioc3_uart); - return ret; -} - -static void __exit ioc3uart_exit(void) -{ - ioc3_unregister_submodule(&ioc3uart_ops); - uart_unregister_driver(&ioc3_uart); -} - -module_init(ioc3uart_init); -module_exit(ioc3uart_exit); - -MODULE_AUTHOR("Pat Gefre - Silicon Graphics Inc. (SGI) <pfg@sgi.com>"); -MODULE_DESCRIPTION("Serial PCI driver module for SGI IOC3 card"); -MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/ioc4_serial.c b/drivers/tty/serial/ioc4_serial.c deleted file mode 100644 index db5b979e5a0c..000000000000 --- a/drivers/tty/serial/ioc4_serial.c +++ /dev/null @@ -1,2955 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright (C) 2003-2006 Silicon Graphics, Inc. All Rights Reserved. - */ - - -/* - * This file contains a module version of the ioc4 serial driver. This - * includes all the support functions needed (support functions, etc.) - * and the serial driver itself. - */ -#include <linux/errno.h> -#include <linux/tty.h> -#include <linux/tty_flip.h> -#include <linux/serial.h> -#include <linux/circ_buf.h> -#include <linux/serial_reg.h> -#include <linux/module.h> -#include <linux/pci.h> -#include <linux/ioc4.h> -#include <linux/serial_core.h> -#include <linux/slab.h> - -/* - * interesting things about the ioc4 - */ - -#define IOC4_NUM_SERIAL_PORTS 4 /* max ports per card */ -#define IOC4_NUM_CARDS 8 /* max cards per partition */ - -#define GET_SIO_IR(_n) (_n == 0) ? (IOC4_SIO_IR_S0) : \ - (_n == 1) ? (IOC4_SIO_IR_S1) : \ - (_n == 2) ? (IOC4_SIO_IR_S2) : \ - (IOC4_SIO_IR_S3) - -#define GET_OTHER_IR(_n) (_n == 0) ? (IOC4_OTHER_IR_S0_MEMERR) : \ - (_n == 1) ? (IOC4_OTHER_IR_S1_MEMERR) : \ - (_n == 2) ? (IOC4_OTHER_IR_S2_MEMERR) : \ - (IOC4_OTHER_IR_S3_MEMERR) - - -/* - * All IOC4 registers are 32 bits wide. - */ - -/* - * PCI Memory Space Map - */ -#define IOC4_PCI_ERR_ADDR_L 0x000 /* Low Error Address */ -#define IOC4_PCI_ERR_ADDR_VLD (0x1 << 0) -#define IOC4_PCI_ERR_ADDR_MST_ID_MSK (0xf << 1) -#define IOC4_PCI_ERR_ADDR_MST_NUM_MSK (0xe << 1) -#define IOC4_PCI_ERR_ADDR_MST_TYP_MSK (0x1 << 1) -#define IOC4_PCI_ERR_ADDR_MUL_ERR (0x1 << 5) -#define IOC4_PCI_ERR_ADDR_ADDR_MSK (0x3ffffff << 6) - -/* Interrupt types */ -#define IOC4_SIO_INTR_TYPE 0 -#define IOC4_OTHER_INTR_TYPE 1 -#define IOC4_NUM_INTR_TYPES 2 - -/* Bitmasks for IOC4_SIO_IR, IOC4_SIO_IEC, and IOC4_SIO_IES */ -#define IOC4_SIO_IR_S0_TX_MT 0x00000001 /* Serial port 0 TX empty */ -#define IOC4_SIO_IR_S0_RX_FULL 0x00000002 /* Port 0 RX buf full */ -#define IOC4_SIO_IR_S0_RX_HIGH 0x00000004 /* Port 0 RX hiwat */ -#define IOC4_SIO_IR_S0_RX_TIMER 0x00000008 /* Port 0 RX timeout */ -#define IOC4_SIO_IR_S0_DELTA_DCD 0x00000010 /* Port 0 delta DCD */ -#define IOC4_SIO_IR_S0_DELTA_CTS 0x00000020 /* Port 0 delta CTS */ -#define IOC4_SIO_IR_S0_INT 0x00000040 /* Port 0 pass-thru intr */ -#define IOC4_SIO_IR_S0_TX_EXPLICIT 0x00000080 /* Port 0 explicit TX thru */ -#define IOC4_SIO_IR_S1_TX_MT 0x00000100 /* Serial port 1 */ -#define IOC4_SIO_IR_S1_RX_FULL 0x00000200 /* */ -#define IOC4_SIO_IR_S1_RX_HIGH 0x00000400 /* */ -#define IOC4_SIO_IR_S1_RX_TIMER 0x00000800 /* */ -#define IOC4_SIO_IR_S1_DELTA_DCD 0x00001000 /* */ -#define IOC4_SIO_IR_S1_DELTA_CTS 0x00002000 /* */ -#define IOC4_SIO_IR_S1_INT 0x00004000 /* */ -#define IOC4_SIO_IR_S1_TX_EXPLICIT 0x00008000 /* */ -#define IOC4_SIO_IR_S2_TX_MT 0x00010000 /* Serial port 2 */ -#define IOC4_SIO_IR_S2_RX_FULL 0x00020000 /* */ -#define IOC4_SIO_IR_S2_RX_HIGH 0x00040000 /* */ -#define IOC4_SIO_IR_S2_RX_TIMER 0x00080000 /* */ -#define IOC4_SIO_IR_S2_DELTA_DCD 0x00100000 /* */ -#define IOC4_SIO_IR_S2_DELTA_CTS 0x00200000 /* */ -#define IOC4_SIO_IR_S2_INT 0x00400000 /* */ -#define IOC4_SIO_IR_S2_TX_EXPLICIT 0x00800000 /* */ -#define IOC4_SIO_IR_S3_TX_MT 0x01000000 /* Serial port 3 */ -#define IOC4_SIO_IR_S3_RX_FULL 0x02000000 /* */ -#define IOC4_SIO_IR_S3_RX_HIGH 0x04000000 /* */ -#define IOC4_SIO_IR_S3_RX_TIMER 0x08000000 /* */ -#define IOC4_SIO_IR_S3_DELTA_DCD 0x10000000 /* */ -#define IOC4_SIO_IR_S3_DELTA_CTS 0x20000000 /* */ -#define IOC4_SIO_IR_S3_INT 0x40000000 /* */ -#define IOC4_SIO_IR_S3_TX_EXPLICIT 0x80000000 /* */ - -/* Per device interrupt masks */ -#define IOC4_SIO_IR_S0 (IOC4_SIO_IR_S0_TX_MT | \ - IOC4_SIO_IR_S0_RX_FULL | \ - IOC4_SIO_IR_S0_RX_HIGH | \ - IOC4_SIO_IR_S0_RX_TIMER | \ - IOC4_SIO_IR_S0_DELTA_DCD | \ - IOC4_SIO_IR_S0_DELTA_CTS | \ - IOC4_SIO_IR_S0_INT | \ - IOC4_SIO_IR_S0_TX_EXPLICIT) -#define IOC4_SIO_IR_S1 (IOC4_SIO_IR_S1_TX_MT | \ - IOC4_SIO_IR_S1_RX_FULL | \ - IOC4_SIO_IR_S1_RX_HIGH | \ - IOC4_SIO_IR_S1_RX_TIMER | \ - IOC4_SIO_IR_S1_DELTA_DCD | \ - IOC4_SIO_IR_S1_DELTA_CTS | \ - IOC4_SIO_IR_S1_INT | \ - IOC4_SIO_IR_S1_TX_EXPLICIT) -#define IOC4_SIO_IR_S2 (IOC4_SIO_IR_S2_TX_MT | \ - IOC4_SIO_IR_S2_RX_FULL | \ - IOC4_SIO_IR_S2_RX_HIGH | \ - IOC4_SIO_IR_S2_RX_TIMER | \ - IOC4_SIO_IR_S2_DELTA_DCD | \ - IOC4_SIO_IR_S2_DELTA_CTS | \ - IOC4_SIO_IR_S2_INT | \ - IOC4_SIO_IR_S2_TX_EXPLICIT) -#define IOC4_SIO_IR_S3 (IOC4_SIO_IR_S3_TX_MT | \ - IOC4_SIO_IR_S3_RX_FULL | \ - IOC4_SIO_IR_S3_RX_HIGH | \ - IOC4_SIO_IR_S3_RX_TIMER | \ - IOC4_SIO_IR_S3_DELTA_DCD | \ - IOC4_SIO_IR_S3_DELTA_CTS | \ - IOC4_SIO_IR_S3_INT | \ - IOC4_SIO_IR_S3_TX_EXPLICIT) - -/* Bitmasks for IOC4_OTHER_IR, IOC4_OTHER_IEC, and IOC4_OTHER_IES */ -#define IOC4_OTHER_IR_ATA_INT 0x00000001 /* ATAPI intr pass-thru */ -#define IOC4_OTHER_IR_ATA_MEMERR 0x00000002 /* ATAPI DMA PCI error */ -#define IOC4_OTHER_IR_S0_MEMERR 0x00000004 /* Port 0 PCI error */ -#define IOC4_OTHER_IR_S1_MEMERR 0x00000008 /* Port 1 PCI error */ -#define IOC4_OTHER_IR_S2_MEMERR 0x00000010 /* Port 2 PCI error */ -#define IOC4_OTHER_IR_S3_MEMERR 0x00000020 /* Port 3 PCI error */ -#define IOC4_OTHER_IR_KBD_INT 0x00000040 /* Keyboard/mouse */ -#define IOC4_OTHER_IR_RESERVED 0x007fff80 /* Reserved */ -#define IOC4_OTHER_IR_RT_INT 0x00800000 /* INT_OUT section output */ -#define IOC4_OTHER_IR_GEN_INT 0xff000000 /* Generic pins */ - -#define IOC4_OTHER_IR_SER_MEMERR (IOC4_OTHER_IR_S0_MEMERR | IOC4_OTHER_IR_S1_MEMERR | \ - IOC4_OTHER_IR_S2_MEMERR | IOC4_OTHER_IR_S3_MEMERR) - -/* Bitmasks for IOC4_SIO_CR */ -#define IOC4_SIO_CR_CMD_PULSE_SHIFT 0 /* byte bus strobe shift */ -#define IOC4_SIO_CR_ARB_DIAG_TX0 0x00000000 -#define IOC4_SIO_CR_ARB_DIAG_RX0 0x00000010 -#define IOC4_SIO_CR_ARB_DIAG_TX1 0x00000020 -#define IOC4_SIO_CR_ARB_DIAG_RX1 0x00000030 -#define IOC4_SIO_CR_ARB_DIAG_TX2 0x00000040 -#define IOC4_SIO_CR_ARB_DIAG_RX2 0x00000050 -#define IOC4_SIO_CR_ARB_DIAG_TX3 0x00000060 -#define IOC4_SIO_CR_ARB_DIAG_RX3 0x00000070 -#define IOC4_SIO_CR_SIO_DIAG_IDLE 0x00000080 /* 0 -> active request among - serial ports (ro) */ -/* Defs for some of the generic I/O pins */ -#define IOC4_GPCR_UART0_MODESEL 0x10 /* Pin is output to port 0 - mode sel */ -#define IOC4_GPCR_UART1_MODESEL 0x20 /* Pin is output to port 1 - mode sel */ -#define IOC4_GPCR_UART2_MODESEL 0x40 /* Pin is output to port 2 - mode sel */ -#define IOC4_GPCR_UART3_MODESEL 0x80 /* Pin is output to port 3 - mode sel */ - -#define IOC4_GPPR_UART0_MODESEL_PIN 4 /* GIO pin controlling - uart 0 mode select */ -#define IOC4_GPPR_UART1_MODESEL_PIN 5 /* GIO pin controlling - uart 1 mode select */ -#define IOC4_GPPR_UART2_MODESEL_PIN 6 /* GIO pin controlling - uart 2 mode select */ -#define IOC4_GPPR_UART3_MODESEL_PIN 7 /* GIO pin controlling - uart 3 mode select */ - -/* Bitmasks for serial RX status byte */ -#define IOC4_RXSB_OVERRUN 0x01 /* Char(s) lost */ -#define IOC4_RXSB_PAR_ERR 0x02 /* Parity error */ -#define IOC4_RXSB_FRAME_ERR 0x04 /* Framing error */ -#define IOC4_RXSB_BREAK 0x08 /* Break character */ -#define IOC4_RXSB_CTS 0x10 /* State of CTS */ -#define IOC4_RXSB_DCD 0x20 /* State of DCD */ -#define IOC4_RXSB_MODEM_VALID 0x40 /* DCD, CTS, and OVERRUN are valid */ -#define IOC4_RXSB_DATA_VALID 0x80 /* Data byte, FRAME_ERR PAR_ERR - * & BREAK valid */ - -/* Bitmasks for serial TX control byte */ -#define IOC4_TXCB_INT_WHEN_DONE 0x20 /* Interrupt after this byte is sent */ -#define IOC4_TXCB_INVALID 0x00 /* Byte is invalid */ -#define IOC4_TXCB_VALID 0x40 /* Byte is valid */ -#define IOC4_TXCB_MCR 0x80 /* Data<7:0> to modem control reg */ -#define IOC4_TXCB_DELAY 0xc0 /* Delay data<7:0> mSec */ - -/* Bitmasks for IOC4_SBBR_L */ -#define IOC4_SBBR_L_SIZE 0x00000001 /* 0 == 1KB rings, 1 == 4KB rings */ - -/* Bitmasks for IOC4_SSCR_<3:0> */ -#define IOC4_SSCR_RX_THRESHOLD 0x000001ff /* Hiwater mark */ -#define IOC4_SSCR_TX_TIMER_BUSY 0x00010000 /* TX timer in progress */ -#define IOC4_SSCR_HFC_EN 0x00020000 /* Hardware flow control enabled */ -#define IOC4_SSCR_RX_RING_DCD 0x00040000 /* Post RX record on delta-DCD */ -#define IOC4_SSCR_RX_RING_CTS 0x00080000 /* Post RX record on delta-CTS */ -#define IOC4_SSCR_DIAG 0x00200000 /* Bypass clock divider for sim */ -#define IOC4_SSCR_RX_DRAIN 0x08000000 /* Drain RX buffer to memory */ -#define IOC4_SSCR_DMA_EN 0x10000000 /* Enable ring buffer DMA */ -#define IOC4_SSCR_DMA_PAUSE 0x20000000 /* Pause DMA */ -#define IOC4_SSCR_PAUSE_STATE 0x40000000 /* Sets when PAUSE takes effect */ -#define IOC4_SSCR_RESET 0x80000000 /* Reset DMA channels */ - -/* All producer/consumer pointers are the same bitfield */ -#define IOC4_PROD_CONS_PTR_4K 0x00000ff8 /* For 4K buffers */ -#define IOC4_PROD_CONS_PTR_1K 0x000003f8 /* For 1K buffers */ -#define IOC4_PROD_CONS_PTR_OFF 3 - -/* Bitmasks for IOC4_SRCIR_<3:0> */ -#define IOC4_SRCIR_ARM 0x80000000 /* Arm RX timer */ - -/* Bitmasks for IOC4_SHADOW_<3:0> */ -#define IOC4_SHADOW_DR 0x00000001 /* Data ready */ -#define IOC4_SHADOW_OE 0x00000002 /* Overrun error */ -#define IOC4_SHADOW_PE 0x00000004 /* Parity error */ -#define IOC4_SHADOW_FE 0x00000008 /* Framing error */ -#define IOC4_SHADOW_BI 0x00000010 /* Break interrupt */ -#define IOC4_SHADOW_THRE 0x00000020 /* Xmit holding register empty */ -#define IOC4_SHADOW_TEMT 0x00000040 /* Xmit shift register empty */ -#define IOC4_SHADOW_RFCE 0x00000080 /* Char in RX fifo has an error */ -#define IOC4_SHADOW_DCTS 0x00010000 /* Delta clear to send */ -#define IOC4_SHADOW_DDCD 0x00080000 /* Delta data carrier detect */ -#define IOC4_SHADOW_CTS 0x00100000 /* Clear to send */ -#define IOC4_SHADOW_DCD 0x00800000 /* Data carrier detect */ -#define IOC4_SHADOW_DTR 0x01000000 /* Data terminal ready */ -#define IOC4_SHADOW_RTS 0x02000000 /* Request to send */ -#define IOC4_SHADOW_OUT1 0x04000000 /* 16550 OUT1 bit */ -#define IOC4_SHADOW_OUT2 0x08000000 /* 16550 OUT2 bit */ -#define IOC4_SHADOW_LOOP 0x10000000 /* Loopback enabled */ - -/* Bitmasks for IOC4_SRTR_<3:0> */ -#define IOC4_SRTR_CNT 0x00000fff /* Reload value for RX timer */ -#define IOC4_SRTR_CNT_VAL 0x0fff0000 /* Current value of RX timer */ -#define IOC4_SRTR_CNT_VAL_SHIFT 16 -#define IOC4_SRTR_HZ 16000 /* SRTR clock frequency */ - -/* Serial port register map used for DMA and PIO serial I/O */ -struct ioc4_serialregs { - uint32_t sscr; - uint32_t stpir; - uint32_t stcir; - uint32_t srpir; - uint32_t srcir; - uint32_t srtr; - uint32_t shadow; -}; - -/* IOC4 UART register map */ -struct ioc4_uartregs { - char i4u_lcr; - union { - char iir; /* read only */ - char fcr; /* write only */ - } u3; - union { - char ier; /* DLAB == 0 */ - char dlm; /* DLAB == 1 */ - } u2; - union { - char rbr; /* read only, DLAB == 0 */ - char thr; /* write only, DLAB == 0 */ - char dll; /* DLAB == 1 */ - } u1; - char i4u_scr; - char i4u_msr; - char i4u_lsr; - char i4u_mcr; -}; - -/* short names */ -#define i4u_dll u1.dll -#define i4u_ier u2.ier -#define i4u_dlm u2.dlm -#define i4u_fcr u3.fcr - -/* Serial port registers used for DMA serial I/O */ -struct ioc4_serial { - uint32_t sbbr01_l; - uint32_t sbbr01_h; - uint32_t sbbr23_l; - uint32_t sbbr23_h; - - struct ioc4_serialregs port_0; - struct ioc4_serialregs port_1; - struct ioc4_serialregs port_2; - struct ioc4_serialregs port_3; - struct ioc4_uartregs uart_0; - struct ioc4_uartregs uart_1; - struct ioc4_uartregs uart_2; - struct ioc4_uartregs uart_3; -}; - -/* UART clock speed */ -#define IOC4_SER_XIN_CLK_66 66666667 -#define IOC4_SER_XIN_CLK_33 33333333 - -#define IOC4_W_IES 0 -#define IOC4_W_IEC 1 - -typedef void ioc4_intr_func_f(void *, uint32_t); -typedef ioc4_intr_func_f *ioc4_intr_func_t; - -static unsigned int Num_of_ioc4_cards; - -/* defining this will get you LOTS of great debug info */ -//#define DEBUG_INTERRUPTS -#define DPRINT_CONFIG(_x...) ; -//#define DPRINT_CONFIG(_x...) printk _x - -/* number of characters left in xmit buffer before we ask for more */ -#define WAKEUP_CHARS 256 - -/* number of characters we want to transmit to the lower level at a time */ -#define IOC4_MAX_CHARS 256 -#define IOC4_FIFO_CHARS 255 - -/* Device name we're using */ -#define DEVICE_NAME_RS232 "ttyIOC" -#define DEVICE_NAME_RS422 "ttyAIOC" -#define DEVICE_MAJOR 204 -#define DEVICE_MINOR_RS232 50 -#define DEVICE_MINOR_RS422 84 - - -/* register offsets */ -#define IOC4_SERIAL_OFFSET 0x300 - -/* flags for next_char_state */ -#define NCS_BREAK 0x1 -#define NCS_PARITY 0x2 -#define NCS_FRAMING 0x4 -#define NCS_OVERRUN 0x8 - -/* cause we need SOME parameters ... */ -#define MIN_BAUD_SUPPORTED 1200 -#define MAX_BAUD_SUPPORTED 115200 - -/* protocol types supported */ -#define PROTO_RS232 3 -#define PROTO_RS422 7 - -/* Notification types */ -#define N_DATA_READY 0x01 -#define N_OUTPUT_LOWAT 0x02 -#define N_BREAK 0x04 -#define N_PARITY_ERROR 0x08 -#define N_FRAMING_ERROR 0x10 -#define N_OVERRUN_ERROR 0x20 -#define N_DDCD 0x40 -#define N_DCTS 0x80 - -#define N_ALL_INPUT (N_DATA_READY | N_BREAK | \ - N_PARITY_ERROR | N_FRAMING_ERROR | \ - N_OVERRUN_ERROR | N_DDCD | N_DCTS) - -#define N_ALL_OUTPUT N_OUTPUT_LOWAT - -#define N_ALL_ERRORS (N_PARITY_ERROR | N_FRAMING_ERROR | N_OVERRUN_ERROR) - -#define N_ALL (N_DATA_READY | N_OUTPUT_LOWAT | N_BREAK | \ - N_PARITY_ERROR | N_FRAMING_ERROR | \ - N_OVERRUN_ERROR | N_DDCD | N_DCTS) - -#define SER_DIVISOR(_x, clk) (((clk) + (_x) * 8) / ((_x) * 16)) -#define DIVISOR_TO_BAUD(div, clk) ((clk) / 16 / (div)) - -/* Some masks */ -#define LCR_MASK_BITS_CHAR (UART_LCR_WLEN5 | UART_LCR_WLEN6 \ - | UART_LCR_WLEN7 | UART_LCR_WLEN8) -#define LCR_MASK_STOP_BITS (UART_LCR_STOP) - -#define PENDING(_p) (readl(&(_p)->ip_mem->sio_ir.raw) & _p->ip_ienb) -#define READ_SIO_IR(_p) readl(&(_p)->ip_mem->sio_ir.raw) - -/* Default to 4k buffers */ -#ifdef IOC4_1K_BUFFERS -#define RING_BUF_SIZE 1024 -#define IOC4_BUF_SIZE_BIT 0 -#define PROD_CONS_MASK IOC4_PROD_CONS_PTR_1K -#else -#define RING_BUF_SIZE 4096 -#define IOC4_BUF_SIZE_BIT IOC4_SBBR_L_SIZE -#define PROD_CONS_MASK IOC4_PROD_CONS_PTR_4K -#endif - -#define TOTAL_RING_BUF_SIZE (RING_BUF_SIZE * 4) - -/* - * This is the entry saved by the driver - one per card - */ - -#define UART_PORT_MIN 0 -#define UART_PORT_RS232 UART_PORT_MIN -#define UART_PORT_RS422 1 -#define UART_PORT_COUNT 2 /* one for each mode */ - -struct ioc4_control { - int ic_irq; - struct { - /* uart ports are allocated here - 1 for rs232, 1 for rs422 */ - struct uart_port icp_uart_port[UART_PORT_COUNT]; - /* Handy reference material */ - struct ioc4_port *icp_port; - } ic_port[IOC4_NUM_SERIAL_PORTS]; - struct ioc4_soft *ic_soft; -}; - -/* - * per-IOC4 data structure - */ -#define MAX_IOC4_INTR_ENTS (8 * sizeof(uint32_t)) -struct ioc4_soft { - struct ioc4_misc_regs __iomem *is_ioc4_misc_addr; - struct ioc4_serial __iomem *is_ioc4_serial_addr; - - /* Each interrupt type has an entry in the array */ - struct ioc4_intr_type { - - /* - * Each in-use entry in this array contains at least - * one nonzero bit in sd_bits; no two entries in this - * array have overlapping sd_bits values. - */ - struct ioc4_intr_info { - uint32_t sd_bits; - ioc4_intr_func_f *sd_intr; - void *sd_info; - } is_intr_info[MAX_IOC4_INTR_ENTS]; - - /* Number of entries active in the above array */ - atomic_t is_num_intrs; - } is_intr_type[IOC4_NUM_INTR_TYPES]; - - /* is_ir_lock must be held while - * modifying sio_ie values, so - * we can be sure that sio_ie is - * not changing when we read it - * along with sio_ir. - */ - spinlock_t is_ir_lock; /* SIO_IE[SC] mod lock */ -}; - -/* Local port info for each IOC4 serial ports */ -struct ioc4_port { - struct uart_port *ip_port; /* current active port ptr */ - /* Ptrs for all ports */ - struct uart_port *ip_all_ports[UART_PORT_COUNT]; - /* Back ptrs for this port */ - struct ioc4_control *ip_control; - struct pci_dev *ip_pdev; - struct ioc4_soft *ip_ioc4_soft; - - /* pci mem addresses */ - struct ioc4_misc_regs __iomem *ip_mem; - struct ioc4_serial __iomem *ip_serial; - struct ioc4_serialregs __iomem *ip_serial_regs; - struct ioc4_uartregs __iomem *ip_uart_regs; - - /* Ring buffer page for this port */ - dma_addr_t ip_dma_ringbuf; - /* vaddr of ring buffer */ - struct ring_buffer *ip_cpu_ringbuf; - - /* Rings for this port */ - struct ring *ip_inring; - struct ring *ip_outring; - - /* Hook to port specific values */ - struct hooks *ip_hooks; - - spinlock_t ip_lock; - - /* Various rx/tx parameters */ - int ip_baud; - int ip_tx_lowat; - int ip_rx_timeout; - - /* Copy of notification bits */ - int ip_notify; - - /* Shadow copies of various registers so we don't need to PIO - * read them constantly - */ - uint32_t ip_ienb; /* Enabled interrupts */ - uint32_t ip_sscr; - uint32_t ip_tx_prod; - uint32_t ip_rx_cons; - int ip_pci_bus_speed; - unsigned char ip_flags; -}; - -/* tx low water mark. We need to notify the driver whenever tx is getting - * close to empty so it can refill the tx buffer and keep things going. - * Let's assume that if we interrupt 1 ms before the tx goes idle, we'll - * have no trouble getting in more chars in time (I certainly hope so). - */ -#define TX_LOWAT_LATENCY 1000 -#define TX_LOWAT_HZ (1000000 / TX_LOWAT_LATENCY) -#define TX_LOWAT_CHARS(baud) (baud / 10 / TX_LOWAT_HZ) - -/* Flags per port */ -#define INPUT_HIGH 0x01 -#define DCD_ON 0x02 -#define LOWAT_WRITTEN 0x04 -#define READ_ABORTED 0x08 -#define PORT_ACTIVE 0x10 -#define PORT_INACTIVE 0 /* This is the value when "off" */ - - -/* Since each port has different register offsets and bitmasks - * for everything, we'll store those that we need in tables so we - * don't have to be constantly checking the port we are dealing with. - */ -struct hooks { - uint32_t intr_delta_dcd; - uint32_t intr_delta_cts; - uint32_t intr_tx_mt; - uint32_t intr_rx_timer; - uint32_t intr_rx_high; - uint32_t intr_tx_explicit; - uint32_t intr_dma_error; - uint32_t intr_clear; - uint32_t intr_all; - int rs422_select_pin; -}; - -static struct hooks hooks_array[IOC4_NUM_SERIAL_PORTS] = { - /* Values for port 0 */ - { - IOC4_SIO_IR_S0_DELTA_DCD, IOC4_SIO_IR_S0_DELTA_CTS, - IOC4_SIO_IR_S0_TX_MT, IOC4_SIO_IR_S0_RX_TIMER, - IOC4_SIO_IR_S0_RX_HIGH, IOC4_SIO_IR_S0_TX_EXPLICIT, - IOC4_OTHER_IR_S0_MEMERR, - (IOC4_SIO_IR_S0_TX_MT | IOC4_SIO_IR_S0_RX_FULL | - IOC4_SIO_IR_S0_RX_HIGH | IOC4_SIO_IR_S0_RX_TIMER | - IOC4_SIO_IR_S0_DELTA_DCD | IOC4_SIO_IR_S0_DELTA_CTS | - IOC4_SIO_IR_S0_INT | IOC4_SIO_IR_S0_TX_EXPLICIT), - IOC4_SIO_IR_S0, IOC4_GPPR_UART0_MODESEL_PIN, - }, - - /* Values for port 1 */ - { - IOC4_SIO_IR_S1_DELTA_DCD, IOC4_SIO_IR_S1_DELTA_CTS, - IOC4_SIO_IR_S1_TX_MT, IOC4_SIO_IR_S1_RX_TIMER, - IOC4_SIO_IR_S1_RX_HIGH, IOC4_SIO_IR_S1_TX_EXPLICIT, - IOC4_OTHER_IR_S1_MEMERR, - (IOC4_SIO_IR_S1_TX_MT | IOC4_SIO_IR_S1_RX_FULL | - IOC4_SIO_IR_S1_RX_HIGH | IOC4_SIO_IR_S1_RX_TIMER | - IOC4_SIO_IR_S1_DELTA_DCD | IOC4_SIO_IR_S1_DELTA_CTS | - IOC4_SIO_IR_S1_INT | IOC4_SIO_IR_S1_TX_EXPLICIT), - IOC4_SIO_IR_S1, IOC4_GPPR_UART1_MODESEL_PIN, - }, - - /* Values for port 2 */ - { - IOC4_SIO_IR_S2_DELTA_DCD, IOC4_SIO_IR_S2_DELTA_CTS, - IOC4_SIO_IR_S2_TX_MT, IOC4_SIO_IR_S2_RX_TIMER, - IOC4_SIO_IR_S2_RX_HIGH, IOC4_SIO_IR_S2_TX_EXPLICIT, - IOC4_OTHER_IR_S2_MEMERR, - (IOC4_SIO_IR_S2_TX_MT | IOC4_SIO_IR_S2_RX_FULL | - IOC4_SIO_IR_S2_RX_HIGH | IOC4_SIO_IR_S2_RX_TIMER | - IOC4_SIO_IR_S2_DELTA_DCD | IOC4_SIO_IR_S2_DELTA_CTS | - IOC4_SIO_IR_S2_INT | IOC4_SIO_IR_S2_TX_EXPLICIT), - IOC4_SIO_IR_S2, IOC4_GPPR_UART2_MODESEL_PIN, - }, - - /* Values for port 3 */ - { - IOC4_SIO_IR_S3_DELTA_DCD, IOC4_SIO_IR_S3_DELTA_CTS, - IOC4_SIO_IR_S3_TX_MT, IOC4_SIO_IR_S3_RX_TIMER, - IOC4_SIO_IR_S3_RX_HIGH, IOC4_SIO_IR_S3_TX_EXPLICIT, - IOC4_OTHER_IR_S3_MEMERR, - (IOC4_SIO_IR_S3_TX_MT | IOC4_SIO_IR_S3_RX_FULL | - IOC4_SIO_IR_S3_RX_HIGH | IOC4_SIO_IR_S3_RX_TIMER | - IOC4_SIO_IR_S3_DELTA_DCD | IOC4_SIO_IR_S3_DELTA_CTS | - IOC4_SIO_IR_S3_INT | IOC4_SIO_IR_S3_TX_EXPLICIT), - IOC4_SIO_IR_S3, IOC4_GPPR_UART3_MODESEL_PIN, - } -}; - -/* A ring buffer entry */ -struct ring_entry { - union { - struct { - uint32_t alldata; - uint32_t allsc; - } all; - struct { - char data[4]; /* data bytes */ - char sc[4]; /* status/control */ - } s; - } u; -}; - -/* Test the valid bits in any of the 4 sc chars using "allsc" member */ -#define RING_ANY_VALID \ - ((uint32_t)(IOC4_RXSB_MODEM_VALID | IOC4_RXSB_DATA_VALID) * 0x01010101) - -#define ring_sc u.s.sc -#define ring_data u.s.data -#define ring_allsc u.all.allsc - -/* Number of entries per ring buffer. */ -#define ENTRIES_PER_RING (RING_BUF_SIZE / (int) sizeof(struct ring_entry)) - -/* An individual ring */ -struct ring { - struct ring_entry entries[ENTRIES_PER_RING]; -}; - -/* The whole enchilada */ -struct ring_buffer { - struct ring TX_0_OR_2; - struct ring RX_0_OR_2; - struct ring TX_1_OR_3; - struct ring RX_1_OR_3; -}; - -/* Get a ring from a port struct */ -#define RING(_p, _wh) &(((struct ring_buffer *)((_p)->ip_cpu_ringbuf))->_wh) - -/* Infinite loop detection. - */ -#define MAXITER 10000000 - -/* Prototypes */ -static void receive_chars(struct uart_port *); -static void handle_intr(void *arg, uint32_t sio_ir); - -/* - * port_is_active - determines if this port is currently active - * @port: ptr to soft struct for this port - * @uart_port: uart port to test for - */ -static inline int port_is_active(struct ioc4_port *port, - struct uart_port *uart_port) -{ - if (port) { - if ((port->ip_flags & PORT_ACTIVE) - && (port->ip_port == uart_port)) - return 1; - } - return 0; -} - - -/** - * write_ireg - write the interrupt regs - * @ioc4_soft: ptr to soft struct for this port - * @val: value to write - * @which: which register - * @type: which ireg set - */ -static inline void -write_ireg(struct ioc4_soft *ioc4_soft, uint32_t val, int which, int type) -{ - struct ioc4_misc_regs __iomem *mem = ioc4_soft->is_ioc4_misc_addr; - unsigned long flags; - - spin_lock_irqsave(&ioc4_soft->is_ir_lock, flags); - - switch (type) { - case IOC4_SIO_INTR_TYPE: - switch (which) { - case IOC4_W_IES: - writel(val, &mem->sio_ies.raw); - break; - - case IOC4_W_IEC: - writel(val, &mem->sio_iec.raw); - break; - } - break; - - case IOC4_OTHER_INTR_TYPE: - switch (which) { - case IOC4_W_IES: - writel(val, &mem->other_ies.raw); - break; - - case IOC4_W_IEC: - writel(val, &mem->other_iec.raw); - break; - } - break; - - default: - break; - } - spin_unlock_irqrestore(&ioc4_soft->is_ir_lock, flags); -} - -/** - * set_baud - Baud rate setting code - * @port: port to set - * @baud: baud rate to use - */ -static int set_baud(struct ioc4_port *port, int baud) -{ - int actual_baud; - int diff; - int lcr; - unsigned short divisor; - struct ioc4_uartregs __iomem *uart; - - divisor = SER_DIVISOR(baud, port->ip_pci_bus_speed); - if (!divisor) - return 1; - actual_baud = DIVISOR_TO_BAUD(divisor, port->ip_pci_bus_speed); - - diff = actual_baud - baud; - if (diff < 0) - diff = -diff; - - /* If we're within 1%, we've found a match */ - if (diff * 100 > actual_baud) - return 1; - - uart = port->ip_uart_regs; - lcr = readb(&uart->i4u_lcr); - writeb(lcr | UART_LCR_DLAB, &uart->i4u_lcr); - writeb((unsigned char)divisor, &uart->i4u_dll); - writeb((unsigned char)(divisor >> 8), &uart->i4u_dlm); - writeb(lcr, &uart->i4u_lcr); - return 0; -} - - -/** - * get_ioc4_port - given a uart port, return the control structure - * @port: uart port - * @set: set this port as current - */ -static struct ioc4_port *get_ioc4_port(struct uart_port *the_port, int set) -{ - struct ioc4_driver_data *idd = dev_get_drvdata(the_port->dev); - struct ioc4_control *control = idd->idd_serial_data; - struct ioc4_port *port; - int port_num, port_type; - - if (control) { - for ( port_num = 0; port_num < IOC4_NUM_SERIAL_PORTS; - port_num++ ) { - port = control->ic_port[port_num].icp_port; - if (!port) - continue; - for (port_type = UART_PORT_MIN; - port_type < UART_PORT_COUNT; - port_type++) { - if (the_port == port->ip_all_ports - [port_type]) { - /* set local copy */ - if (set) { - port->ip_port = the_port; - } - return port; - } - } - } - } - return NULL; -} - -/* The IOC4 hardware provides no atomic way to determine if interrupts - * are pending since two reads are required to do so. The handler must - * read the SIO_IR and the SIO_IES, and take the logical and of the - * two. When this value is zero, all interrupts have been serviced and - * the handler may return. - * - * This has the unfortunate "hole" that, if some other CPU or - * some other thread or some higher level interrupt manages to - * modify SIO_IE between our reads of SIO_IR and SIO_IE, we may - * think we have observed SIO_IR&SIO_IE==0 when in fact this - * condition never really occurred. - * - * To solve this, we use a simple spinlock that must be held - * whenever modifying SIO_IE; holding this lock while observing - * both SIO_IR and SIO_IE guarantees that we do not falsely - * conclude that no enabled interrupts are pending. - */ - -static inline uint32_t -pending_intrs(struct ioc4_soft *soft, int type) -{ - struct ioc4_misc_regs __iomem *mem = soft->is_ioc4_misc_addr; - unsigned long flag; - uint32_t intrs = 0; - - BUG_ON(!((type == IOC4_SIO_INTR_TYPE) - || (type == IOC4_OTHER_INTR_TYPE))); - - spin_lock_irqsave(&soft->is_ir_lock, flag); - - switch (type) { - case IOC4_SIO_INTR_TYPE: - intrs = readl(&mem->sio_ir.raw) & readl(&mem->sio_ies.raw); - break; - - case IOC4_OTHER_INTR_TYPE: - intrs = readl(&mem->other_ir.raw) & readl(&mem->other_ies.raw); - - /* Don't process any ATA interrupte */ - intrs &= ~(IOC4_OTHER_IR_ATA_INT | IOC4_OTHER_IR_ATA_MEMERR); - break; - - default: - break; - } - spin_unlock_irqrestore(&soft->is_ir_lock, flag); - return intrs; -} - -/** - * port_init - Initialize the sio and ioc4 hardware for a given port - * called per port from attach... - * @port: port to initialize - */ -static inline int port_init(struct ioc4_port *port) -{ - uint32_t sio_cr; - struct hooks *hooks = port->ip_hooks; - struct ioc4_uartregs __iomem *uart; - - /* Idle the IOC4 serial interface */ - writel(IOC4_SSCR_RESET, &port->ip_serial_regs->sscr); - - /* Wait until any pending bus activity for this port has ceased */ - do - sio_cr = readl(&port->ip_mem->sio_cr.raw); - while (!(sio_cr & IOC4_SIO_CR_SIO_DIAG_IDLE)); - - /* Finish reset sequence */ - writel(0, &port->ip_serial_regs->sscr); - - /* Once RESET is done, reload cached tx_prod and rx_cons values - * and set rings to empty by making prod == cons - */ - port->ip_tx_prod = readl(&port->ip_serial_regs->stcir) & PROD_CONS_MASK; - writel(port->ip_tx_prod, &port->ip_serial_regs->stpir); - port->ip_rx_cons = readl(&port->ip_serial_regs->srpir) & PROD_CONS_MASK; - writel(port->ip_rx_cons | IOC4_SRCIR_ARM, &port->ip_serial_regs->srcir); - - /* Disable interrupts for this 16550 */ - uart = port->ip_uart_regs; - writeb(0, &uart->i4u_lcr); - writeb(0, &uart->i4u_ier); - - /* Set the default baud */ - set_baud(port, port->ip_baud); - - /* Set line control to 8 bits no parity */ - writeb(UART_LCR_WLEN8 | 0, &uart->i4u_lcr); - /* UART_LCR_STOP == 1 stop */ - - /* Enable the FIFOs */ - writeb(UART_FCR_ENABLE_FIFO, &uart->i4u_fcr); - /* then reset 16550 FIFOs */ - writeb(UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT, - &uart->i4u_fcr); - - /* Clear modem control register */ - writeb(0, &uart->i4u_mcr); - - /* Clear deltas in modem status register */ - readb(&uart->i4u_msr); - - /* Only do this once per port pair */ - if (port->ip_hooks == &hooks_array[0] - || port->ip_hooks == &hooks_array[2]) { - unsigned long ring_pci_addr; - uint32_t __iomem *sbbr_l; - uint32_t __iomem *sbbr_h; - - if (port->ip_hooks == &hooks_array[0]) { - sbbr_l = &port->ip_serial->sbbr01_l; - sbbr_h = &port->ip_serial->sbbr01_h; - } else { - sbbr_l = &port->ip_serial->sbbr23_l; - sbbr_h = &port->ip_serial->sbbr23_h; - } - - ring_pci_addr = (unsigned long __iomem)port->ip_dma_ringbuf; - DPRINT_CONFIG(("%s: ring_pci_addr 0x%lx\n", - __func__, ring_pci_addr)); - - writel((unsigned int)((uint64_t)ring_pci_addr >> 32), sbbr_h); - writel((unsigned int)ring_pci_addr | IOC4_BUF_SIZE_BIT, sbbr_l); - } - - /* Set the receive timeout value to 10 msec */ - writel(IOC4_SRTR_HZ / 100, &port->ip_serial_regs->srtr); - - /* Set rx threshold, enable DMA */ - /* Set high water mark at 3/4 of full ring */ - port->ip_sscr = (ENTRIES_PER_RING * 3 / 4); - writel(port->ip_sscr, &port->ip_serial_regs->sscr); - - /* Disable and clear all serial related interrupt bits */ - write_ireg(port->ip_ioc4_soft, hooks->intr_clear, - IOC4_W_IEC, IOC4_SIO_INTR_TYPE); - port->ip_ienb &= ~hooks->intr_clear; - writel(hooks->intr_clear, &port->ip_mem->sio_ir.raw); - return 0; -} - -/** - * handle_dma_error_intr - service any pending DMA error interrupts for the - * given port - 2nd level called via sd_intr - * @arg: handler arg - * @other_ir: ioc4regs - */ -static void handle_dma_error_intr(void *arg, uint32_t other_ir) -{ - struct ioc4_port *port = (struct ioc4_port *)arg; - struct hooks *hooks = port->ip_hooks; - unsigned long flags; - - spin_lock_irqsave(&port->ip_lock, flags); - - /* ACK the interrupt */ - writel(hooks->intr_dma_error, &port->ip_mem->other_ir.raw); - - if (readl(&port->ip_mem->pci_err_addr_l.raw) & IOC4_PCI_ERR_ADDR_VLD) { - printk(KERN_ERR - "PCI error address is 0x%llx, " - "master is serial port %c %s\n", - (((uint64_t)readl(&port->ip_mem->pci_err_addr_h) - << 32) - | readl(&port->ip_mem->pci_err_addr_l.raw)) - & IOC4_PCI_ERR_ADDR_ADDR_MSK, '1' + - ((char)(readl(&port->ip_mem->pci_err_addr_l.raw) & - IOC4_PCI_ERR_ADDR_MST_NUM_MSK) >> 1), - (readl(&port->ip_mem->pci_err_addr_l.raw) - & IOC4_PCI_ERR_ADDR_MST_TYP_MSK) - ? "RX" : "TX"); - - if (readl(&port->ip_mem->pci_err_addr_l.raw) - & IOC4_PCI_ERR_ADDR_MUL_ERR) { - printk(KERN_ERR - "Multiple errors occurred\n"); - } - } - spin_unlock_irqrestore(&port->ip_lock, flags); - - /* Re-enable DMA error interrupts */ - write_ireg(port->ip_ioc4_soft, hooks->intr_dma_error, IOC4_W_IES, - IOC4_OTHER_INTR_TYPE); -} - -/** - * intr_connect - interrupt connect function - * @soft: soft struct for this card - * @type: interrupt type - * @intrbits: bit pattern to set - * @intr: handler function - * @info: handler arg - */ -static void -intr_connect(struct ioc4_soft *soft, int type, - uint32_t intrbits, ioc4_intr_func_f * intr, void *info) -{ - int i; - struct ioc4_intr_info *intr_ptr; - - BUG_ON(!((type == IOC4_SIO_INTR_TYPE) - || (type == IOC4_OTHER_INTR_TYPE))); - - i = atomic_inc_return(&soft-> is_intr_type[type].is_num_intrs) - 1; - BUG_ON(!(i < MAX_IOC4_INTR_ENTS || (printk("i %d\n", i), 0))); - - /* Save off the lower level interrupt handler */ - intr_ptr = &soft->is_intr_type[type].is_intr_info[i]; - intr_ptr->sd_bits = intrbits; - intr_ptr->sd_intr = intr; - intr_ptr->sd_info = info; -} - -/** - * ioc4_intr - Top level IOC4 interrupt handler. - * @irq: irq value - * @arg: handler arg - */ - -static irqreturn_t ioc4_intr(int irq, void *arg) -{ - struct ioc4_soft *soft; - uint32_t this_ir, this_mir; - int xx, num_intrs = 0; - int intr_type; - int handled = 0; - struct ioc4_intr_info *intr_info; - - soft = arg; - for (intr_type = 0; intr_type < IOC4_NUM_INTR_TYPES; intr_type++) { - num_intrs = (int)atomic_read( - &soft->is_intr_type[intr_type].is_num_intrs); - - this_mir = this_ir = pending_intrs(soft, intr_type); - - /* Farm out the interrupt to the various drivers depending on - * which interrupt bits are set. - */ - for (xx = 0; xx < num_intrs; xx++) { - intr_info = &soft->is_intr_type[intr_type].is_intr_info[xx]; - this_mir = this_ir & intr_info->sd_bits; - if (this_mir) { - /* Disable owned interrupts, call handler */ - handled++; - write_ireg(soft, intr_info->sd_bits, IOC4_W_IEC, - intr_type); - intr_info->sd_intr(intr_info->sd_info, this_mir); - this_ir &= ~this_mir; - } - } - } -#ifdef DEBUG_INTERRUPTS - { - struct ioc4_misc_regs __iomem *mem = soft->is_ioc4_misc_addr; - unsigned long flag; - - spin_lock_irqsave(&soft->is_ir_lock, flag); - printk ("%s : %d : mem 0x%p sio_ir 0x%x sio_ies 0x%x " - "other_ir 0x%x other_ies 0x%x mask 0x%x\n", - __func__, __LINE__, - (void *)mem, readl(&mem->sio_ir.raw), - readl(&mem->sio_ies.raw), - readl(&mem->other_ir.raw), - readl(&mem->other_ies.raw), - IOC4_OTHER_IR_ATA_INT | IOC4_OTHER_IR_ATA_MEMERR); - spin_unlock_irqrestore(&soft->is_ir_lock, flag); - } -#endif - return handled ? IRQ_HANDLED : IRQ_NONE; -} - -/** - * ioc4_attach_local - Device initialization. - * Called at *_attach() time for each - * IOC4 with serial ports in the system. - * @idd: Master module data for this IOC4 - */ -static inline int ioc4_attach_local(struct ioc4_driver_data *idd) -{ - struct ioc4_port *port; - struct ioc4_port *ports[IOC4_NUM_SERIAL_PORTS]; - int port_number; - uint16_t ioc4_revid_min = 62; - uint16_t ioc4_revid; - struct pci_dev *pdev = idd->idd_pdev; - struct ioc4_control* control = idd->idd_serial_data; - struct ioc4_soft *soft = control->ic_soft; - void __iomem *ioc4_misc = idd->idd_misc_regs; - void __iomem *ioc4_serial = soft->is_ioc4_serial_addr; - - /* IOC4 firmware must be at least rev 62 */ - pci_read_config_word(pdev, PCI_COMMAND_SPECIAL, &ioc4_revid); - - printk(KERN_INFO "IOC4 firmware revision %d\n", ioc4_revid); - if (ioc4_revid < ioc4_revid_min) { - printk(KERN_WARNING - "IOC4 serial not supported on firmware rev %d, " - "please upgrade to rev %d or higher\n", - ioc4_revid, ioc4_revid_min); - return -EPERM; - } - BUG_ON(ioc4_misc == NULL); - BUG_ON(ioc4_serial == NULL); - - /* Create port structures for each port */ - for (port_number = 0; port_number < IOC4_NUM_SERIAL_PORTS; - port_number++) { - port = kzalloc(sizeof(struct ioc4_port), GFP_KERNEL); - if (!port) { - printk(KERN_WARNING - "IOC4 serial memory not available for port\n"); - goto free; - } - spin_lock_init(&port->ip_lock); - - /* we need to remember the previous ones, to point back to - * them farther down - setting up the ring buffers. - */ - ports[port_number] = port; - - /* Allocate buffers and jumpstart the hardware. */ - control->ic_port[port_number].icp_port = port; - port->ip_ioc4_soft = soft; - port->ip_pdev = pdev; - port->ip_ienb = 0; - /* Use baud rate calculations based on detected PCI - * bus speed. Simply test whether the PCI clock is - * running closer to 66MHz or 33MHz. - */ - if (idd->count_period/IOC4_EXTINT_COUNT_DIVISOR < 20) { - port->ip_pci_bus_speed = IOC4_SER_XIN_CLK_66; - } else { - port->ip_pci_bus_speed = IOC4_SER_XIN_CLK_33; - } - port->ip_baud = 9600; - port->ip_control = control; - port->ip_mem = ioc4_misc; - port->ip_serial = ioc4_serial; - - /* point to the right hook */ - port->ip_hooks = &hooks_array[port_number]; - - /* Get direct hooks to the serial regs and uart regs - * for this port - */ - switch (port_number) { - case 0: - port->ip_serial_regs = &(port->ip_serial->port_0); - port->ip_uart_regs = &(port->ip_serial->uart_0); - break; - case 1: - port->ip_serial_regs = &(port->ip_serial->port_1); - port->ip_uart_regs = &(port->ip_serial->uart_1); - break; - case 2: - port->ip_serial_regs = &(port->ip_serial->port_2); - port->ip_uart_regs = &(port->ip_serial->uart_2); - break; - default: - case 3: - port->ip_serial_regs = &(port->ip_serial->port_3); - port->ip_uart_regs = &(port->ip_serial->uart_3); - break; - } - - /* ring buffers are 1 to a pair of ports */ - if (port_number && (port_number & 1)) { - /* odd use the evens buffer */ - port->ip_dma_ringbuf = - ports[port_number - 1]->ip_dma_ringbuf; - port->ip_cpu_ringbuf = - ports[port_number - 1]->ip_cpu_ringbuf; - port->ip_inring = RING(port, RX_1_OR_3); - port->ip_outring = RING(port, TX_1_OR_3); - - } else { - if (port->ip_dma_ringbuf == 0) { - port->ip_cpu_ringbuf = pci_alloc_consistent - (pdev, TOTAL_RING_BUF_SIZE, - &port->ip_dma_ringbuf); - - } - BUG_ON(!((((int64_t)port->ip_dma_ringbuf) & - (TOTAL_RING_BUF_SIZE - 1)) == 0)); - DPRINT_CONFIG(("%s : ip_cpu_ringbuf 0x%p " - "ip_dma_ringbuf 0x%p\n", - __func__, - (void *)port->ip_cpu_ringbuf, - (void *)port->ip_dma_ringbuf)); - port->ip_inring = RING(port, RX_0_OR_2); - port->ip_outring = RING(port, TX_0_OR_2); - } - DPRINT_CONFIG(("%s : port %d [addr 0x%p] control 0x%p", - __func__, - port_number, (void *)port, (void *)control)); - DPRINT_CONFIG((" ip_serial_regs 0x%p ip_uart_regs 0x%p\n", - (void *)port->ip_serial_regs, - (void *)port->ip_uart_regs)); - - /* Initialize the hardware for IOC4 */ - port_init(port); - - DPRINT_CONFIG(("%s: port_number %d port 0x%p inring 0x%p " - "outring 0x%p\n", - __func__, - port_number, (void *)port, - (void *)port->ip_inring, - (void *)port->ip_outring)); - - /* Attach interrupt handlers */ - intr_connect(soft, IOC4_SIO_INTR_TYPE, - GET_SIO_IR(port_number), - handle_intr, port); - - intr_connect(soft, IOC4_OTHER_INTR_TYPE, - GET_OTHER_IR(port_number), - handle_dma_error_intr, port); - } - return 0; - -free: - while (port_number) - kfree(ports[--port_number]); - return -ENOMEM; -} - -/** - * enable_intrs - enable interrupts - * @port: port to enable - * @mask: mask to use - */ -static void enable_intrs(struct ioc4_port *port, uint32_t mask) -{ - struct hooks *hooks = port->ip_hooks; - - if ((port->ip_ienb & mask) != mask) { - write_ireg(port->ip_ioc4_soft, mask, IOC4_W_IES, - IOC4_SIO_INTR_TYPE); - port->ip_ienb |= mask; - } - - if (port->ip_ienb) - write_ireg(port->ip_ioc4_soft, hooks->intr_dma_error, - IOC4_W_IES, IOC4_OTHER_INTR_TYPE); -} - -/** - * local_open - local open a port - * @port: port to open - */ -static inline int local_open(struct ioc4_port *port) -{ - int spiniter = 0; - - port->ip_flags = PORT_ACTIVE; - - /* Pause the DMA interface if necessary */ - if (port->ip_sscr & IOC4_SSCR_DMA_EN) { - writel(port->ip_sscr | IOC4_SSCR_DMA_PAUSE, - &port->ip_serial_regs->sscr); - while((readl(&port->ip_serial_regs-> sscr) - & IOC4_SSCR_PAUSE_STATE) == 0) { - spiniter++; - if (spiniter > MAXITER) { - port->ip_flags = PORT_INACTIVE; - return -1; - } - } - } - - /* Reset the input fifo. If the uart received chars while the port - * was closed and DMA is not enabled, the uart may have a bunch of - * chars hanging around in its rx fifo which will not be discarded - * by rclr in the upper layer. We must get rid of them here. - */ - writeb(UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR, - &port->ip_uart_regs->i4u_fcr); - - writeb(UART_LCR_WLEN8, &port->ip_uart_regs->i4u_lcr); - /* UART_LCR_STOP == 1 stop */ - - /* Re-enable DMA, set default threshold to intr whenever there is - * data available. - */ - port->ip_sscr &= ~IOC4_SSCR_RX_THRESHOLD; - port->ip_sscr |= 1; /* default threshold */ - - /* Plug in the new sscr. This implicitly clears the DMA_PAUSE - * flag if it was set above - */ - writel(port->ip_sscr, &port->ip_serial_regs->sscr); - port->ip_tx_lowat = 1; - return 0; -} - -/** - * set_rx_timeout - Set rx timeout and threshold values. - * @port: port to use - * @timeout: timeout value in ticks - */ -static inline int set_rx_timeout(struct ioc4_port *port, int timeout) -{ - int threshold; - - port->ip_rx_timeout = timeout; - - /* Timeout is in ticks. Let's figure out how many chars we - * can receive at the current baud rate in that interval - * and set the rx threshold to that amount. There are 4 chars - * per ring entry, so we'll divide the number of chars that will - * arrive in timeout by 4. - * So .... timeout * baud / 10 / HZ / 4, with HZ = 100. - */ - threshold = timeout * port->ip_baud / 4000; - if (threshold == 0) - threshold = 1; /* otherwise we'll intr all the time! */ - - if ((unsigned)threshold > (unsigned)IOC4_SSCR_RX_THRESHOLD) - return 1; - - port->ip_sscr &= ~IOC4_SSCR_RX_THRESHOLD; - port->ip_sscr |= threshold; - - writel(port->ip_sscr, &port->ip_serial_regs->sscr); - - /* Now set the rx timeout to the given value - * again timeout * IOC4_SRTR_HZ / HZ - */ - timeout = timeout * IOC4_SRTR_HZ / 100; - if (timeout > IOC4_SRTR_CNT) - timeout = IOC4_SRTR_CNT; - - writel(timeout, &port->ip_serial_regs->srtr); - return 0; -} - -/** - * config_port - config the hardware - * @port: port to config - * @baud: baud rate for the port - * @byte_size: data size - * @stop_bits: number of stop bits - * @parenb: parity enable ? - * @parodd: odd parity ? - */ -static inline int -config_port(struct ioc4_port *port, - int baud, int byte_size, int stop_bits, int parenb, int parodd) -{ - char lcr, sizebits; - int spiniter = 0; - - DPRINT_CONFIG(("%s: baud %d byte_size %d stop %d parenb %d parodd %d\n", - __func__, baud, byte_size, stop_bits, parenb, parodd)); - - if (set_baud(port, baud)) - return 1; - - switch (byte_size) { - case 5: - sizebits = UART_LCR_WLEN5; - break; - case 6: - sizebits = UART_LCR_WLEN6; - break; - case 7: - sizebits = UART_LCR_WLEN7; - break; - case 8: - sizebits = UART_LCR_WLEN8; - break; - default: - return 1; - } - - /* Pause the DMA interface if necessary */ - if (port->ip_sscr & IOC4_SSCR_DMA_EN) { - writel(port->ip_sscr | IOC4_SSCR_DMA_PAUSE, - &port->ip_serial_regs->sscr); - while((readl(&port->ip_serial_regs->sscr) - & IOC4_SSCR_PAUSE_STATE) == 0) { - spiniter++; - if (spiniter > MAXITER) - return -1; - } - } - - /* Clear relevant fields in lcr */ - lcr = readb(&port->ip_uart_regs->i4u_lcr); - lcr &= ~(LCR_MASK_BITS_CHAR | UART_LCR_EPAR | - UART_LCR_PARITY | LCR_MASK_STOP_BITS); - - /* Set byte size in lcr */ - lcr |= sizebits; - - /* Set parity */ - if (parenb) { - lcr |= UART_LCR_PARITY; - if (!parodd) - lcr |= UART_LCR_EPAR; - } - - /* Set stop bits */ - if (stop_bits) - lcr |= UART_LCR_STOP /* 2 stop bits */ ; - - writeb(lcr, &port->ip_uart_regs->i4u_lcr); - - /* Re-enable the DMA interface if necessary */ - if (port->ip_sscr & IOC4_SSCR_DMA_EN) { - writel(port->ip_sscr, &port->ip_serial_regs->sscr); - } - port->ip_baud = baud; - - /* When we get within this number of ring entries of filling the - * entire ring on tx, place an EXPLICIT intr to generate a lowat - * notification when output has drained. - */ - port->ip_tx_lowat = (TX_LOWAT_CHARS(baud) + 3) / 4; - if (port->ip_tx_lowat == 0) - port->ip_tx_lowat = 1; - - set_rx_timeout(port, 2); - - return 0; -} - -/** - * do_write - Write bytes to the port. Returns the number of bytes - * actually written. Called from transmit_chars - * @port: port to use - * @buf: the stuff to write - * @len: how many bytes in 'buf' - */ -static inline int do_write(struct ioc4_port *port, char *buf, int len) -{ - int prod_ptr, cons_ptr, total = 0; - struct ring *outring; - struct ring_entry *entry; - struct hooks *hooks = port->ip_hooks; - - BUG_ON(!(len >= 0)); - - prod_ptr = port->ip_tx_prod; - cons_ptr = readl(&port->ip_serial_regs->stcir) & PROD_CONS_MASK; - outring = port->ip_outring; - - /* Maintain a 1-entry red-zone. The ring buffer is full when - * (cons - prod) % ring_size is 1. Rather than do this subtraction - * in the body of the loop, I'll do it now. - */ - cons_ptr = (cons_ptr - (int)sizeof(struct ring_entry)) & PROD_CONS_MASK; - - /* Stuff the bytes into the output */ - while ((prod_ptr != cons_ptr) && (len > 0)) { - int xx; - - /* Get 4 bytes (one ring entry) at a time */ - entry = (struct ring_entry *)((caddr_t) outring + prod_ptr); - - /* Invalidate all entries */ - entry->ring_allsc = 0; - - /* Copy in some bytes */ - for (xx = 0; (xx < 4) && (len > 0); xx++) { - entry->ring_data[xx] = *buf++; - entry->ring_sc[xx] = IOC4_TXCB_VALID; - len--; - total++; - } - - /* If we are within some small threshold of filling up the - * entire ring buffer, we must place an EXPLICIT intr here - * to generate a lowat interrupt in case we subsequently - * really do fill up the ring and the caller goes to sleep. - * No need to place more than one though. - */ - if (!(port->ip_flags & LOWAT_WRITTEN) && - ((cons_ptr - prod_ptr) & PROD_CONS_MASK) - <= port->ip_tx_lowat - * (int)sizeof(struct ring_entry)) { - port->ip_flags |= LOWAT_WRITTEN; - entry->ring_sc[0] |= IOC4_TXCB_INT_WHEN_DONE; - } - - /* Go on to next entry */ - prod_ptr += sizeof(struct ring_entry); - prod_ptr &= PROD_CONS_MASK; - } - - /* If we sent something, start DMA if necessary */ - if (total > 0 && !(port->ip_sscr & IOC4_SSCR_DMA_EN)) { - port->ip_sscr |= IOC4_SSCR_DMA_EN; - writel(port->ip_sscr, &port->ip_serial_regs->sscr); - } - - /* Store the new producer pointer. If tx is disabled, we stuff the - * data into the ring buffer, but we don't actually start tx. - */ - if (!uart_tx_stopped(port->ip_port)) { - writel(prod_ptr, &port->ip_serial_regs->stpir); - - /* If we are now transmitting, enable tx_mt interrupt so we - * can disable DMA if necessary when the tx finishes. - */ - if (total > 0) - enable_intrs(port, hooks->intr_tx_mt); - } - port->ip_tx_prod = prod_ptr; - return total; -} - -/** - * disable_intrs - disable interrupts - * @port: port to enable - * @mask: mask to use - */ -static void disable_intrs(struct ioc4_port *port, uint32_t mask) -{ - struct hooks *hooks = port->ip_hooks; - - if (port->ip_ienb & mask) { - write_ireg(port->ip_ioc4_soft, mask, IOC4_W_IEC, - IOC4_SIO_INTR_TYPE); - port->ip_ienb &= ~mask; - } - - if (!port->ip_ienb) - write_ireg(port->ip_ioc4_soft, hooks->intr_dma_error, - IOC4_W_IEC, IOC4_OTHER_INTR_TYPE); -} - -/** - * set_notification - Modify event notification - * @port: port to use - * @mask: events mask - * @set_on: set ? - */ -static int set_notification(struct ioc4_port *port, int mask, int set_on) -{ - struct hooks *hooks = port->ip_hooks; - uint32_t intrbits, sscrbits; - - BUG_ON(!mask); - - intrbits = sscrbits = 0; - - if (mask & N_DATA_READY) - intrbits |= (hooks->intr_rx_timer | hooks->intr_rx_high); - if (mask & N_OUTPUT_LOWAT) - intrbits |= hooks->intr_tx_explicit; - if (mask & N_DDCD) { - intrbits |= hooks->intr_delta_dcd; - sscrbits |= IOC4_SSCR_RX_RING_DCD; - } - if (mask & N_DCTS) - intrbits |= hooks->intr_delta_cts; - - if (set_on) { - enable_intrs(port, intrbits); - port->ip_notify |= mask; - port->ip_sscr |= sscrbits; - } else { - disable_intrs(port, intrbits); - port->ip_notify &= ~mask; - port->ip_sscr &= ~sscrbits; - } - - /* We require DMA if either DATA_READY or DDCD notification is - * currently requested. If neither of these is requested and - * there is currently no tx in progress, DMA may be disabled. - */ - if (port->ip_notify & (N_DATA_READY | N_DDCD)) - port->ip_sscr |= IOC4_SSCR_DMA_EN; - else if (!(port->ip_ienb & hooks->intr_tx_mt)) - port->ip_sscr &= ~IOC4_SSCR_DMA_EN; - - writel(port->ip_sscr, &port->ip_serial_regs->sscr); - return 0; -} - -/** - * set_mcr - set the master control reg - * @the_port: port to use - * @mask1: mcr mask - * @mask2: shadow mask - */ -static inline int set_mcr(struct uart_port *the_port, - int mask1, int mask2) -{ - struct ioc4_port *port = get_ioc4_port(the_port, 0); - uint32_t shadow; - int spiniter = 0; - char mcr; - - if (!port) - return -1; - - /* Pause the DMA interface if necessary */ - if (port->ip_sscr & IOC4_SSCR_DMA_EN) { - writel(port->ip_sscr | IOC4_SSCR_DMA_PAUSE, - &port->ip_serial_regs->sscr); - while ((readl(&port->ip_serial_regs->sscr) - & IOC4_SSCR_PAUSE_STATE) == 0) { - spiniter++; - if (spiniter > MAXITER) - return -1; - } - } - shadow = readl(&port->ip_serial_regs->shadow); - mcr = (shadow & 0xff000000) >> 24; - - /* Set new value */ - mcr |= mask1; - shadow |= mask2; - - writeb(mcr, &port->ip_uart_regs->i4u_mcr); - writel(shadow, &port->ip_serial_regs->shadow); - - /* Re-enable the DMA interface if necessary */ - if (port->ip_sscr & IOC4_SSCR_DMA_EN) { - writel(port->ip_sscr, &port->ip_serial_regs->sscr); - } - return 0; -} - -/** - * ioc4_set_proto - set the protocol for the port - * @port: port to use - * @proto: protocol to use - */ -static int ioc4_set_proto(struct ioc4_port *port, int proto) -{ - struct hooks *hooks = port->ip_hooks; - - switch (proto) { - case PROTO_RS232: - /* Clear the appropriate GIO pin */ - writel(0, (&port->ip_mem->gppr[hooks->rs422_select_pin].raw)); - break; - - case PROTO_RS422: - /* Set the appropriate GIO pin */ - writel(1, (&port->ip_mem->gppr[hooks->rs422_select_pin].raw)); - break; - - default: - return 1; - } - return 0; -} - -/** - * transmit_chars - upper level write, called with ip_lock - * @the_port: port to write - */ -static void transmit_chars(struct uart_port *the_port) -{ - int xmit_count, tail, head; - int result; - char *start; - struct tty_struct *tty; - struct ioc4_port *port = get_ioc4_port(the_port, 0); - struct uart_state *state; - - if (!the_port) - return; - if (!port) - return; - - state = the_port->state; - tty = state->port.tty; - - if (uart_circ_empty(&state->xmit) || uart_tx_stopped(the_port)) { - /* Nothing to do or hw stopped */ - set_notification(port, N_ALL_OUTPUT, 0); - return; - } - - head = state->xmit.head; - tail = state->xmit.tail; - start = (char *)&state->xmit.buf[tail]; - - /* write out all the data or until the end of the buffer */ - xmit_count = (head < tail) ? (UART_XMIT_SIZE - tail) : (head - tail); - if (xmit_count > 0) { - result = do_write(port, start, xmit_count); - if (result > 0) { - /* booking */ - xmit_count -= result; - the_port->icount.tx += result; - /* advance the pointers */ - tail += result; - tail &= UART_XMIT_SIZE - 1; - state->xmit.tail = tail; - start = (char *)&state->xmit.buf[tail]; - } - } - if (uart_circ_chars_pending(&state->xmit) < WAKEUP_CHARS) - uart_write_wakeup(the_port); - - if (uart_circ_empty(&state->xmit)) { - set_notification(port, N_OUTPUT_LOWAT, 0); - } else { - set_notification(port, N_OUTPUT_LOWAT, 1); - } -} - -/** - * ioc4_change_speed - change the speed of the port - * @the_port: port to change - * @new_termios: new termios settings - * @old_termios: old termios settings - */ -static void -ioc4_change_speed(struct uart_port *the_port, - struct ktermios *new_termios, struct ktermios *old_termios) -{ - struct ioc4_port *port = get_ioc4_port(the_port, 0); - int baud, bits; - unsigned cflag, iflag; - int new_parity = 0, new_parity_enable = 0, new_stop = 0, new_data = 8; - struct uart_state *state = the_port->state; - - cflag = new_termios->c_cflag; - iflag = new_termios->c_iflag; - - switch (cflag & CSIZE) { - case CS5: - new_data = 5; - bits = 7; - break; - case CS6: - new_data = 6; - bits = 8; - break; - case CS7: - new_data = 7; - bits = 9; - break; - case CS8: - new_data = 8; - bits = 10; - break; - default: - /* cuz we always need a default ... */ - new_data = 5; - bits = 7; - break; - } - if (cflag & CSTOPB) { - bits++; - new_stop = 1; - } - if (cflag & PARENB) { - bits++; - new_parity_enable = 1; - if (cflag & PARODD) - new_parity = 1; - } - baud = uart_get_baud_rate(the_port, new_termios, old_termios, - MIN_BAUD_SUPPORTED, MAX_BAUD_SUPPORTED); - DPRINT_CONFIG(("%s: returned baud %d\n", __func__, baud)); - - /* default is 9600 */ - if (!baud) - baud = 9600; - - if (!the_port->fifosize) - the_port->fifosize = IOC4_FIFO_CHARS; - the_port->timeout = ((the_port->fifosize * HZ * bits) / (baud / 10)); - the_port->timeout += HZ / 50; /* Add .02 seconds of slop */ - - the_port->ignore_status_mask = N_ALL_INPUT; - - state->port.low_latency = 1; - - if (iflag & IGNPAR) - the_port->ignore_status_mask &= ~(N_PARITY_ERROR - | N_FRAMING_ERROR); - if (iflag & IGNBRK) { - the_port->ignore_status_mask &= ~N_BREAK; - if (iflag & IGNPAR) - the_port->ignore_status_mask &= ~N_OVERRUN_ERROR; - } - if (!(cflag & CREAD)) { - /* ignore everything */ - the_port->ignore_status_mask &= ~N_DATA_READY; - } - - if (cflag & CRTSCTS) { - port->ip_sscr |= IOC4_SSCR_HFC_EN; - } - else { - port->ip_sscr &= ~IOC4_SSCR_HFC_EN; - } - writel(port->ip_sscr, &port->ip_serial_regs->sscr); - - /* Set the configuration and proper notification call */ - DPRINT_CONFIG(("%s : port 0x%p cflag 0%o " - "config_port(baud %d data %d stop %d p enable %d parity %d)," - " notification 0x%x\n", - __func__, (void *)port, cflag, baud, new_data, new_stop, - new_parity_enable, new_parity, the_port->ignore_status_mask)); - - if ((config_port(port, baud, /* baud */ - new_data, /* byte size */ - new_stop, /* stop bits */ - new_parity_enable, /* set parity */ - new_parity)) >= 0) { /* parity 1==odd */ - set_notification(port, the_port->ignore_status_mask, 1); - } -} - -/** - * ic4_startup_local - Start up the serial port - returns >= 0 if no errors - * @the_port: Port to operate on - */ -static inline int ic4_startup_local(struct uart_port *the_port) -{ - struct ioc4_port *port; - struct uart_state *state; - - if (!the_port) - return -1; - - port = get_ioc4_port(the_port, 0); - if (!port) - return -1; - - state = the_port->state; - - local_open(port); - - /* set the protocol - mapbase has the port type */ - ioc4_set_proto(port, the_port->mapbase); - - /* set the speed of the serial port */ - ioc4_change_speed(the_port, &state->port.tty->termios, - (struct ktermios *)0); - - return 0; -} - -/* - * ioc4_cb_output_lowat - called when the output low water mark is hit - * @the_port: port to output - */ -static void ioc4_cb_output_lowat(struct uart_port *the_port) -{ - unsigned long pflags; - - /* ip_lock is set on the call here */ - if (the_port) { - spin_lock_irqsave(&the_port->lock, pflags); - transmit_chars(the_port); - spin_unlock_irqrestore(&the_port->lock, pflags); - } -} - -/** - * handle_intr - service any interrupts for the given port - 2nd level - * called via sd_intr - * @arg: handler arg - * @sio_ir: ioc4regs - */ -static void handle_intr(void *arg, uint32_t sio_ir) -{ - struct ioc4_port *port = (struct ioc4_port *)arg; - struct hooks *hooks = port->ip_hooks; - unsigned int rx_high_rd_aborted = 0; - unsigned long flags; - struct uart_port *the_port; - int loop_counter; - - /* Possible race condition here: The tx_mt interrupt bit may be - * cleared without the intervention of the interrupt handler, - * e.g. by a write. If the top level interrupt handler reads a - * tx_mt, then some other processor does a write, starting up - * output, then we come in here, see the tx_mt and stop DMA, the - * output started by the other processor will hang. Thus we can - * only rely on tx_mt being legitimate if it is read while the - * port lock is held. Therefore this bit must be ignored in the - * passed in interrupt mask which was read by the top level - * interrupt handler since the port lock was not held at the time - * it was read. We can only rely on this bit being accurate if it - * is read while the port lock is held. So we'll clear it for now, - * and reload it later once we have the port lock. - */ - sio_ir &= ~(hooks->intr_tx_mt); - - spin_lock_irqsave(&port->ip_lock, flags); - - loop_counter = MAXITER; /* to avoid hangs */ - - do { - uint32_t shadow; - - if ( loop_counter-- <= 0 ) { - printk(KERN_WARNING "IOC4 serial: " - "possible hang condition/" - "port stuck on interrupt.\n"); - break; - } - - /* Handle a DCD change */ - if (sio_ir & hooks->intr_delta_dcd) { - /* ACK the interrupt */ - writel(hooks->intr_delta_dcd, - &port->ip_mem->sio_ir.raw); - - shadow = readl(&port->ip_serial_regs->shadow); - - if ((port->ip_notify & N_DDCD) - && (shadow & IOC4_SHADOW_DCD) - && (port->ip_port)) { - the_port = port->ip_port; - the_port->icount.dcd = 1; - wake_up_interruptible - (&the_port->state->port.delta_msr_wait); - } else if ((port->ip_notify & N_DDCD) - && !(shadow & IOC4_SHADOW_DCD)) { - /* Flag delta DCD/no DCD */ - port->ip_flags |= DCD_ON; - } - } - - /* Handle a CTS change */ - if (sio_ir & hooks->intr_delta_cts) { - /* ACK the interrupt */ - writel(hooks->intr_delta_cts, - &port->ip_mem->sio_ir.raw); - - shadow = readl(&port->ip_serial_regs->shadow); - - if ((port->ip_notify & N_DCTS) - && (port->ip_port)) { - the_port = port->ip_port; - the_port->icount.cts = - (shadow & IOC4_SHADOW_CTS) ? 1 : 0; - wake_up_interruptible - (&the_port->state->port.delta_msr_wait); - } - } - - /* rx timeout interrupt. Must be some data available. Put this - * before the check for rx_high since servicing this condition - * may cause that condition to clear. - */ - if (sio_ir & hooks->intr_rx_timer) { - /* ACK the interrupt */ - writel(hooks->intr_rx_timer, - &port->ip_mem->sio_ir.raw); - - if ((port->ip_notify & N_DATA_READY) - && (port->ip_port)) { - /* ip_lock is set on call here */ - receive_chars(port->ip_port); - } - } - - /* rx high interrupt. Must be after rx_timer. */ - else if (sio_ir & hooks->intr_rx_high) { - /* Data available, notify upper layer */ - if ((port->ip_notify & N_DATA_READY) - && port->ip_port) { - /* ip_lock is set on call here */ - receive_chars(port->ip_port); - } - - /* We can't ACK this interrupt. If receive_chars didn't - * cause the condition to clear, we'll have to disable - * the interrupt until the data is drained. - * If the read was aborted, don't disable the interrupt - * as this may cause us to hang indefinitely. An - * aborted read generally means that this interrupt - * hasn't been delivered to the cpu yet anyway, even - * though we see it as asserted when we read the sio_ir. - */ - if ((sio_ir = PENDING(port)) & hooks->intr_rx_high) { - if ((port->ip_flags & READ_ABORTED) == 0) { - port->ip_ienb &= ~hooks->intr_rx_high; - port->ip_flags |= INPUT_HIGH; - } else { - rx_high_rd_aborted++; - } - } - } - - /* We got a low water interrupt: notify upper layer to - * send more data. Must come before tx_mt since servicing - * this condition may cause that condition to clear. - */ - if (sio_ir & hooks->intr_tx_explicit) { - port->ip_flags &= ~LOWAT_WRITTEN; - - /* ACK the interrupt */ - writel(hooks->intr_tx_explicit, - &port->ip_mem->sio_ir.raw); - - if (port->ip_notify & N_OUTPUT_LOWAT) - ioc4_cb_output_lowat(port->ip_port); - } - - /* Handle tx_mt. Must come after tx_explicit. */ - else if (sio_ir & hooks->intr_tx_mt) { - /* If we are expecting a lowat notification - * and we get to this point it probably means that for - * some reason the tx_explicit didn't work as expected - * (that can legitimately happen if the output buffer is - * filled up in just the right way). - * So send the notification now. - */ - if (port->ip_notify & N_OUTPUT_LOWAT) { - ioc4_cb_output_lowat(port->ip_port); - - /* We need to reload the sio_ir since the lowat - * call may have caused another write to occur, - * clearing the tx_mt condition. - */ - sio_ir = PENDING(port); - } - - /* If the tx_mt condition still persists even after the - * lowat call, we've got some work to do. - */ - if (sio_ir & hooks->intr_tx_mt) { - - /* If we are not currently expecting DMA input, - * and the transmitter has just gone idle, - * there is no longer any reason for DMA, so - * disable it. - */ - if (!(port->ip_notify - & (N_DATA_READY | N_DDCD))) { - BUG_ON(!(port->ip_sscr - & IOC4_SSCR_DMA_EN)); - port->ip_sscr &= ~IOC4_SSCR_DMA_EN; - writel(port->ip_sscr, - &port->ip_serial_regs->sscr); - } - - /* Prevent infinite tx_mt interrupt */ - port->ip_ienb &= ~hooks->intr_tx_mt; - } - } - sio_ir = PENDING(port); - - /* if the read was aborted and only hooks->intr_rx_high, - * clear hooks->intr_rx_high, so we do not loop forever. - */ - - if (rx_high_rd_aborted && (sio_ir == hooks->intr_rx_high)) { - sio_ir &= ~hooks->intr_rx_high; - } - } while (sio_ir & hooks->intr_all); - - spin_unlock_irqrestore(&port->ip_lock, flags); - - /* Re-enable interrupts before returning from interrupt handler. - * Getting interrupted here is okay. It'll just v() our semaphore, and - * we'll come through the loop again. - */ - - write_ireg(port->ip_ioc4_soft, port->ip_ienb, IOC4_W_IES, - IOC4_SIO_INTR_TYPE); -} - -/* - * ioc4_cb_post_ncs - called for some basic errors - * @port: port to use - * @ncs: event - */ -static void ioc4_cb_post_ncs(struct uart_port *the_port, int ncs) -{ - struct uart_icount *icount; - - icount = &the_port->icount; - - if (ncs & NCS_BREAK) - icount->brk++; - if (ncs & NCS_FRAMING) - icount->frame++; - if (ncs & NCS_OVERRUN) - icount->overrun++; - if (ncs & NCS_PARITY) - icount->parity++; -} - -/** - * do_read - Read in bytes from the port. Return the number of bytes - * actually read. - * @the_port: port to use - * @buf: place to put the stuff we read - * @len: how big 'buf' is - */ - -static inline int do_read(struct uart_port *the_port, unsigned char *buf, - int len) -{ - int prod_ptr, cons_ptr, total; - struct ioc4_port *port = get_ioc4_port(the_port, 0); - struct ring *inring; - struct ring_entry *entry; - struct hooks *hooks; - int byte_num; - char *sc; - int loop_counter; - - BUG_ON(!(len >= 0)); - BUG_ON(!port); - hooks = port->ip_hooks; - - /* There is a nasty timing issue in the IOC4. When the rx_timer - * expires or the rx_high condition arises, we take an interrupt. - * At some point while servicing the interrupt, we read bytes from - * the ring buffer and re-arm the rx_timer. However the rx_timer is - * not started until the first byte is received *after* it is armed, - * and any bytes pending in the rx construction buffers are not drained - * to memory until either there are 4 bytes available or the rx_timer - * expires. This leads to a potential situation where data is left - * in the construction buffers forever - 1 to 3 bytes were received - * after the interrupt was generated but before the rx_timer was - * re-armed. At that point as long as no subsequent bytes are received - * the timer will never be started and the bytes will remain in the - * construction buffer forever. The solution is to execute a DRAIN - * command after rearming the timer. This way any bytes received before - * the DRAIN will be drained to memory, and any bytes received after - * the DRAIN will start the TIMER and be drained when it expires. - * Luckily, this only needs to be done when the DMA buffer is empty - * since there is no requirement that this function return all - * available data as long as it returns some. - */ - /* Re-arm the timer */ - writel(port->ip_rx_cons | IOC4_SRCIR_ARM, &port->ip_serial_regs->srcir); - - prod_ptr = readl(&port->ip_serial_regs->srpir) & PROD_CONS_MASK; - cons_ptr = port->ip_rx_cons; - - if (prod_ptr == cons_ptr) { - int reset_dma = 0; - - /* Input buffer appears empty, do a flush. */ - - /* DMA must be enabled for this to work. */ - if (!(port->ip_sscr & IOC4_SSCR_DMA_EN)) { - port->ip_sscr |= IOC4_SSCR_DMA_EN; - reset_dma = 1; - } - - /* Potential race condition: we must reload the srpir after - * issuing the drain command, otherwise we could think the rx - * buffer is empty, then take a very long interrupt, and when - * we come back it's full and we wait forever for the drain to - * complete. - */ - writel(port->ip_sscr | IOC4_SSCR_RX_DRAIN, - &port->ip_serial_regs->sscr); - prod_ptr = readl(&port->ip_serial_regs->srpir) - & PROD_CONS_MASK; - - /* We must not wait for the DRAIN to complete unless there are - * at least 8 bytes (2 ring entries) available to receive the - * data otherwise the DRAIN will never complete and we'll - * deadlock here. - * In fact, to make things easier, I'll just ignore the flush if - * there is any data at all now available. - */ - if (prod_ptr == cons_ptr) { - loop_counter = 0; - while (readl(&port->ip_serial_regs->sscr) & - IOC4_SSCR_RX_DRAIN) { - loop_counter++; - if (loop_counter > MAXITER) - return -1; - } - - /* SIGH. We have to reload the prod_ptr *again* since - * the drain may have caused it to change - */ - prod_ptr = readl(&port->ip_serial_regs->srpir) - & PROD_CONS_MASK; - } - if (reset_dma) { - port->ip_sscr &= ~IOC4_SSCR_DMA_EN; - writel(port->ip_sscr, &port->ip_serial_regs->sscr); - } - } - inring = port->ip_inring; - port->ip_flags &= ~READ_ABORTED; - - total = 0; - loop_counter = 0xfffff; /* to avoid hangs */ - - /* Grab bytes from the hardware */ - while ((prod_ptr != cons_ptr) && (len > 0)) { - entry = (struct ring_entry *)((caddr_t)inring + cons_ptr); - - if ( loop_counter-- <= 0 ) { - printk(KERN_WARNING "IOC4 serial: " - "possible hang condition/" - "port stuck on read.\n"); - break; - } - - /* According to the producer pointer, this ring entry - * must contain some data. But if the PIO happened faster - * than the DMA, the data may not be available yet, so let's - * wait until it arrives. - */ - if ((entry->ring_allsc & RING_ANY_VALID) == 0) { - /* Indicate the read is aborted so we don't disable - * the interrupt thinking that the consumer is - * congested. - */ - port->ip_flags |= READ_ABORTED; - len = 0; - break; - } - - /* Load the bytes/status out of the ring entry */ - for (byte_num = 0; byte_num < 4 && len > 0; byte_num++) { - sc = &(entry->ring_sc[byte_num]); - - /* Check for change in modem state or overrun */ - if ((*sc & IOC4_RXSB_MODEM_VALID) - && (port->ip_notify & N_DDCD)) { - /* Notify upper layer if DCD dropped */ - - if ((port->ip_flags & DCD_ON) - && !(*sc & IOC4_RXSB_DCD)) { - - /* If we have already copied some data, - * return it. We'll pick up the carrier - * drop on the next pass. That way we - * don't throw away the data that has - * already been copied back to - * the caller's buffer. - */ - if (total > 0) { - len = 0; - break; - } - port->ip_flags &= ~DCD_ON; - - /* Turn off this notification so the - * carrier drop protocol won't see it - * again when it does a read. - */ - *sc &= ~IOC4_RXSB_MODEM_VALID; - - /* To keep things consistent, we need - * to update the consumer pointer so - * the next reader won't come in and - * try to read the same ring entries - * again. This must be done here before - * the dcd change. - */ - - if ((entry->ring_allsc & RING_ANY_VALID) - == 0) { - cons_ptr += (int)sizeof - (struct ring_entry); - cons_ptr &= PROD_CONS_MASK; - } - writel(cons_ptr, - &port->ip_serial_regs->srcir); - port->ip_rx_cons = cons_ptr; - - /* Notify upper layer of carrier drop */ - if ((port->ip_notify & N_DDCD) - && port->ip_port) { - the_port->icount.dcd = 0; - wake_up_interruptible - (&the_port->state-> - port.delta_msr_wait); - } - - /* If we had any data to return, we - * would have returned it above. - */ - return 0; - } - } - if (*sc & IOC4_RXSB_MODEM_VALID) { - /* Notify that an input overrun occurred */ - if ((*sc & IOC4_RXSB_OVERRUN) - && (port->ip_notify & N_OVERRUN_ERROR)) { - ioc4_cb_post_ncs(the_port, NCS_OVERRUN); - } - /* Don't look at this byte again */ - *sc &= ~IOC4_RXSB_MODEM_VALID; - } - - /* Check for valid data or RX errors */ - if ((*sc & IOC4_RXSB_DATA_VALID) && - ((*sc & (IOC4_RXSB_PAR_ERR - | IOC4_RXSB_FRAME_ERR - | IOC4_RXSB_BREAK)) - && (port->ip_notify & (N_PARITY_ERROR - | N_FRAMING_ERROR - | N_BREAK)))) { - /* There is an error condition on the next byte. - * If we have already transferred some bytes, - * we'll stop here. Otherwise if this is the - * first byte to be read, we'll just transfer - * it alone after notifying the - * upper layer of its status. - */ - if (total > 0) { - len = 0; - break; - } else { - if ((*sc & IOC4_RXSB_PAR_ERR) && - (port->ip_notify & N_PARITY_ERROR)) { - ioc4_cb_post_ncs(the_port, - NCS_PARITY); - } - if ((*sc & IOC4_RXSB_FRAME_ERR) && - (port->ip_notify & N_FRAMING_ERROR)){ - ioc4_cb_post_ncs(the_port, - NCS_FRAMING); - } - if ((*sc & IOC4_RXSB_BREAK) - && (port->ip_notify & N_BREAK)) { - ioc4_cb_post_ncs - (the_port, - NCS_BREAK); - } - len = 1; - } - } - if (*sc & IOC4_RXSB_DATA_VALID) { - *sc &= ~IOC4_RXSB_DATA_VALID; - *buf = entry->ring_data[byte_num]; - buf++; - len--; - total++; - } - } - - /* If we used up this entry entirely, go on to the next one, - * otherwise we must have run out of buffer space, so - * leave the consumer pointer here for the next read in case - * there are still unread bytes in this entry. - */ - if ((entry->ring_allsc & RING_ANY_VALID) == 0) { - cons_ptr += (int)sizeof(struct ring_entry); - cons_ptr &= PROD_CONS_MASK; - } - } - - /* Update consumer pointer and re-arm rx timer interrupt */ - writel(cons_ptr, &port->ip_serial_regs->srcir); - port->ip_rx_cons = cons_ptr; - - /* If we have now dipped below the rx high water mark and we have - * rx_high interrupt turned off, we can now turn it back on again. - */ - if ((port->ip_flags & INPUT_HIGH) && (((prod_ptr - cons_ptr) - & PROD_CONS_MASK) < ((port->ip_sscr & - IOC4_SSCR_RX_THRESHOLD) - << IOC4_PROD_CONS_PTR_OFF))) { - port->ip_flags &= ~INPUT_HIGH; - enable_intrs(port, hooks->intr_rx_high); - } - return total; -} - -/** - * receive_chars - upper level read. Called with ip_lock. - * @the_port: port to read from - */ -static void receive_chars(struct uart_port *the_port) -{ - unsigned char ch[IOC4_MAX_CHARS]; - int read_count, request_count = IOC4_MAX_CHARS; - struct uart_icount *icount; - struct uart_state *state = the_port->state; - unsigned long pflags; - - /* Make sure all the pointers are "good" ones */ - if (!state) - return; - - spin_lock_irqsave(&the_port->lock, pflags); - - request_count = tty_buffer_request_room(&state->port, IOC4_MAX_CHARS); - - if (request_count > 0) { - icount = &the_port->icount; - read_count = do_read(the_port, ch, request_count); - if (read_count > 0) { - tty_insert_flip_string(&state->port, ch, read_count); - icount->rx += read_count; - } - } - - spin_unlock_irqrestore(&the_port->lock, pflags); - - tty_flip_buffer_push(&state->port); -} - -/** - * ic4_type - What type of console are we? - * @port: Port to operate with (we ignore since we only have one port) - * - */ -static const char *ic4_type(struct uart_port *the_port) -{ - if (the_port->mapbase == PROTO_RS232) - return "SGI IOC4 Serial [rs232]"; - else - return "SGI IOC4 Serial [rs422]"; -} - -/** - * ic4_tx_empty - Is the transmitter empty? - * @port: Port to operate on - * - */ -static unsigned int ic4_tx_empty(struct uart_port *the_port) -{ - struct ioc4_port *port = get_ioc4_port(the_port, 0); - unsigned int ret = 0; - - if (port_is_active(port, the_port)) { - if (readl(&port->ip_serial_regs->shadow) & IOC4_SHADOW_TEMT) - ret = TIOCSER_TEMT; - } - return ret; -} - -/** - * ic4_stop_tx - stop the transmitter - * @port: Port to operate on - * - */ -static void ic4_stop_tx(struct uart_port *the_port) -{ - struct ioc4_port *port = get_ioc4_port(the_port, 0); - - if (port_is_active(port, the_port)) - set_notification(port, N_OUTPUT_LOWAT, 0); -} - -/** - * null_void_function - - * @port: Port to operate on - * - */ -static void null_void_function(struct uart_port *the_port) -{ -} - -/** - * ic4_shutdown - shut down the port - free irq and disable - * @port: Port to shut down - * - */ -static void ic4_shutdown(struct uart_port *the_port) -{ - unsigned long port_flags; - struct ioc4_port *port; - struct uart_state *state; - - port = get_ioc4_port(the_port, 0); - if (!port) - return; - - state = the_port->state; - port->ip_port = NULL; - - wake_up_interruptible(&state->port.delta_msr_wait); - - if (state->port.tty) - set_bit(TTY_IO_ERROR, &state->port.tty->flags); - - spin_lock_irqsave(&the_port->lock, port_flags); - set_notification(port, N_ALL, 0); - port->ip_flags = PORT_INACTIVE; - spin_unlock_irqrestore(&the_port->lock, port_flags); -} - -/** - * ic4_set_mctrl - set control lines (dtr, rts, etc) - * @port: Port to operate on - * @mctrl: Lines to set/unset - * - */ -static void ic4_set_mctrl(struct uart_port *the_port, unsigned int mctrl) -{ - unsigned char mcr = 0; - struct ioc4_port *port; - - port = get_ioc4_port(the_port, 0); - if (!port_is_active(port, the_port)) - return; - - if (mctrl & TIOCM_RTS) - mcr |= UART_MCR_RTS; - if (mctrl & TIOCM_DTR) - mcr |= UART_MCR_DTR; - if (mctrl & TIOCM_OUT1) - mcr |= UART_MCR_OUT1; - if (mctrl & TIOCM_OUT2) - mcr |= UART_MCR_OUT2; - if (mctrl & TIOCM_LOOP) - mcr |= UART_MCR_LOOP; - - set_mcr(the_port, mcr, IOC4_SHADOW_DTR); -} - -/** - * ic4_get_mctrl - get control line info - * @port: port to operate on - * - */ -static unsigned int ic4_get_mctrl(struct uart_port *the_port) -{ - struct ioc4_port *port = get_ioc4_port(the_port, 0); - uint32_t shadow; - unsigned int ret = 0; - - if (!port_is_active(port, the_port)) - return 0; - - shadow = readl(&port->ip_serial_regs->shadow); - if (shadow & IOC4_SHADOW_DCD) - ret |= TIOCM_CAR; - if (shadow & IOC4_SHADOW_DR) - ret |= TIOCM_DSR; - if (shadow & IOC4_SHADOW_CTS) - ret |= TIOCM_CTS; - return ret; -} - -/** - * ic4_start_tx - Start transmitter, flush any output - * @port: Port to operate on - * - */ -static void ic4_start_tx(struct uart_port *the_port) -{ - struct ioc4_port *port = get_ioc4_port(the_port, 0); - - if (port_is_active(port, the_port)) { - set_notification(port, N_OUTPUT_LOWAT, 1); - enable_intrs(port, port->ip_hooks->intr_tx_mt); - } -} - -/** - * ic4_break_ctl - handle breaks - * @port: Port to operate on - * @break_state: Break state - * - */ -static void ic4_break_ctl(struct uart_port *the_port, int break_state) -{ -} - -/** - * ic4_startup - Start up the serial port - * @port: Port to operate on - * - */ -static int ic4_startup(struct uart_port *the_port) -{ - int retval; - struct ioc4_port *port; - struct ioc4_control *control; - struct uart_state *state; - unsigned long port_flags; - - if (!the_port) - return -ENODEV; - port = get_ioc4_port(the_port, 1); - if (!port) - return -ENODEV; - state = the_port->state; - - control = port->ip_control; - if (!control) { - port->ip_port = NULL; - return -ENODEV; - } - - /* Start up the serial port */ - spin_lock_irqsave(&the_port->lock, port_flags); - retval = ic4_startup_local(the_port); - spin_unlock_irqrestore(&the_port->lock, port_flags); - return retval; -} - -/** - * ic4_set_termios - set termios stuff - * @port: port to operate on - * @termios: New settings - * @termios: Old - * - */ -static void -ic4_set_termios(struct uart_port *the_port, - struct ktermios *termios, struct ktermios *old_termios) -{ - unsigned long port_flags; - - spin_lock_irqsave(&the_port->lock, port_flags); - ioc4_change_speed(the_port, termios, old_termios); - spin_unlock_irqrestore(&the_port->lock, port_flags); -} - -/** - * ic4_request_port - allocate resources for port - no op.... - * @port: port to operate on - * - */ -static int ic4_request_port(struct uart_port *port) -{ - return 0; -} - -/* Associate the uart functions above - given to serial core */ - -static const struct uart_ops ioc4_ops = { - .tx_empty = ic4_tx_empty, - .set_mctrl = ic4_set_mctrl, - .get_mctrl = ic4_get_mctrl, - .stop_tx = ic4_stop_tx, - .start_tx = ic4_start_tx, - .stop_rx = null_void_function, - .break_ctl = ic4_break_ctl, - .startup = ic4_startup, - .shutdown = ic4_shutdown, - .set_termios = ic4_set_termios, - .type = ic4_type, - .release_port = null_void_function, - .request_port = ic4_request_port, -}; - -/* - * Boot-time initialization code - */ - -static struct uart_driver ioc4_uart_rs232 = { - .owner = THIS_MODULE, - .driver_name = "ioc4_serial_rs232", - .dev_name = DEVICE_NAME_RS232, - .major = DEVICE_MAJOR, - .minor = DEVICE_MINOR_RS232, - .nr = IOC4_NUM_CARDS * IOC4_NUM_SERIAL_PORTS, -}; - -static struct uart_driver ioc4_uart_rs422 = { - .owner = THIS_MODULE, - .driver_name = "ioc4_serial_rs422", - .dev_name = DEVICE_NAME_RS422, - .major = DEVICE_MAJOR, - .minor = DEVICE_MINOR_RS422, - .nr = IOC4_NUM_CARDS * IOC4_NUM_SERIAL_PORTS, -}; - - -/** - * ioc4_serial_remove_one - detach function - * - * @idd: IOC4 master module data for this IOC4 - */ - -static int ioc4_serial_remove_one(struct ioc4_driver_data *idd) -{ - int port_num, port_type; - struct ioc4_control *control; - struct uart_port *the_port; - struct ioc4_port *port; - struct ioc4_soft *soft; - - /* If serial driver did not attach, don't try to detach */ - control = idd->idd_serial_data; - if (!control) - return 0; - - for (port_num = 0; port_num < IOC4_NUM_SERIAL_PORTS; port_num++) { - for (port_type = UART_PORT_MIN; - port_type < UART_PORT_COUNT; - port_type++) { - the_port = &control->ic_port[port_num].icp_uart_port - [port_type]; - if (the_port) { - switch (port_type) { - case UART_PORT_RS422: - uart_remove_one_port(&ioc4_uart_rs422, - the_port); - break; - default: - case UART_PORT_RS232: - uart_remove_one_port(&ioc4_uart_rs232, - the_port); - break; - } - } - } - port = control->ic_port[port_num].icp_port; - /* we allocate in pairs */ - if (!(port_num & 1) && port) { - pci_free_consistent(port->ip_pdev, - TOTAL_RING_BUF_SIZE, - port->ip_cpu_ringbuf, - port->ip_dma_ringbuf); - kfree(port); - } - } - soft = control->ic_soft; - if (soft) { - free_irq(control->ic_irq, soft); - if (soft->is_ioc4_serial_addr) { - iounmap(soft->is_ioc4_serial_addr); - release_mem_region((unsigned long) - soft->is_ioc4_serial_addr, - sizeof(struct ioc4_serial)); - } - kfree(soft); - } - kfree(control); - idd->idd_serial_data = NULL; - - return 0; -} - - -/** - * ioc4_serial_core_attach_rs232 - register with serial core - * This is done during pci probing - * @pdev: handle for this card - */ -static inline int -ioc4_serial_core_attach(struct pci_dev *pdev, int port_type) -{ - struct ioc4_port *port; - struct uart_port *the_port; - struct ioc4_driver_data *idd = pci_get_drvdata(pdev); - struct ioc4_control *control = idd->idd_serial_data; - int port_num; - int port_type_idx; - struct uart_driver *u_driver; - - - DPRINT_CONFIG(("%s: attach pdev 0x%p - control 0x%p\n", - __func__, pdev, (void *)control)); - - if (!control) - return -ENODEV; - - port_type_idx = (port_type == PROTO_RS232) ? UART_PORT_RS232 - : UART_PORT_RS422; - - u_driver = (port_type == PROTO_RS232) ? &ioc4_uart_rs232 - : &ioc4_uart_rs422; - - /* once around for each port on this card */ - for (port_num = 0; port_num < IOC4_NUM_SERIAL_PORTS; port_num++) { - the_port = &control->ic_port[port_num].icp_uart_port - [port_type_idx]; - port = control->ic_port[port_num].icp_port; - port->ip_all_ports[port_type_idx] = the_port; - - DPRINT_CONFIG(("%s: attach the_port 0x%p / port 0x%p : type %s\n", - __func__, (void *)the_port, - (void *)port, - port_type == PROTO_RS232 ? "rs232" : "rs422")); - - /* membase, iobase and mapbase just need to be non-0 */ - the_port->membase = (unsigned char __iomem *)1; - the_port->iobase = (pdev->bus->number << 16) | port_num; - the_port->line = (Num_of_ioc4_cards << 2) | port_num; - the_port->mapbase = port_type; - the_port->type = PORT_16550A; - the_port->fifosize = IOC4_FIFO_CHARS; - the_port->ops = &ioc4_ops; - the_port->irq = control->ic_irq; - the_port->dev = &pdev->dev; - spin_lock_init(&the_port->lock); - if (uart_add_one_port(u_driver, the_port) < 0) { - printk(KERN_WARNING - "%s: unable to add port %d bus %d\n", - __func__, the_port->line, pdev->bus->number); - } else { - DPRINT_CONFIG( - ("IOC4 serial port %d irq = %d, bus %d\n", - the_port->line, the_port->irq, pdev->bus->number)); - } - } - return 0; -} - -/** - * ioc4_serial_attach_one - register attach function - * called per card found from IOC4 master module. - * @idd: Master module data for this IOC4 - */ -static int -ioc4_serial_attach_one(struct ioc4_driver_data *idd) -{ - unsigned long tmp_addr1; - struct ioc4_serial __iomem *serial; - struct ioc4_soft *soft; - struct ioc4_control *control; - int ret = 0; - - - DPRINT_CONFIG(("%s (0x%p, 0x%p)\n", __func__, idd->idd_pdev, - idd->idd_pci_id)); - - /* PCI-RT does not bring out serial connections. - * Do not attach to this particular IOC4. - */ - if (idd->idd_variant == IOC4_VARIANT_PCI_RT) - return 0; - - /* request serial registers */ - tmp_addr1 = idd->idd_bar0 + IOC4_SERIAL_OFFSET; - - if (!request_mem_region(tmp_addr1, sizeof(struct ioc4_serial), - "sioc4_uart")) { - printk(KERN_WARNING - "ioc4 (%p): unable to get request region for " - "uart space\n", (void *)idd->idd_pdev); - ret = -ENODEV; - goto out1; - } - serial = ioremap(tmp_addr1, sizeof(struct ioc4_serial)); - if (!serial) { - printk(KERN_WARNING - "ioc4 (%p) : unable to remap ioc4 serial register\n", - (void *)idd->idd_pdev); - ret = -ENODEV; - goto out2; - } - DPRINT_CONFIG(("%s : mem 0x%p, serial 0x%p\n", - __func__, (void *)idd->idd_misc_regs, - (void *)serial)); - - /* Get memory for the new card */ - control = kzalloc(sizeof(struct ioc4_control), GFP_KERNEL); - - if (!control) { - printk(KERN_WARNING "ioc4_attach_one" - ": unable to get memory for the IOC4\n"); - ret = -ENOMEM; - goto out2; - } - idd->idd_serial_data = control; - - /* Allocate the soft structure */ - soft = kzalloc(sizeof(struct ioc4_soft), GFP_KERNEL); - if (!soft) { - printk(KERN_WARNING - "ioc4 (%p): unable to get memory for the soft struct\n", - (void *)idd->idd_pdev); - ret = -ENOMEM; - goto out3; - } - - spin_lock_init(&soft->is_ir_lock); - soft->is_ioc4_misc_addr = idd->idd_misc_regs; - soft->is_ioc4_serial_addr = serial; - - /* Init the IOC4 */ - writel(0xf << IOC4_SIO_CR_CMD_PULSE_SHIFT, - &idd->idd_misc_regs->sio_cr.raw); - - /* Enable serial port mode select generic PIO pins as outputs */ - writel(IOC4_GPCR_UART0_MODESEL | IOC4_GPCR_UART1_MODESEL - | IOC4_GPCR_UART2_MODESEL | IOC4_GPCR_UART3_MODESEL, - &idd->idd_misc_regs->gpcr_s.raw); - - /* Clear and disable all serial interrupts */ - write_ireg(soft, ~0, IOC4_W_IEC, IOC4_SIO_INTR_TYPE); - writel(~0, &idd->idd_misc_regs->sio_ir.raw); - write_ireg(soft, IOC4_OTHER_IR_SER_MEMERR, IOC4_W_IEC, - IOC4_OTHER_INTR_TYPE); - writel(IOC4_OTHER_IR_SER_MEMERR, &idd->idd_misc_regs->other_ir.raw); - control->ic_soft = soft; - - /* Hook up interrupt handler */ - if (!request_irq(idd->idd_pdev->irq, ioc4_intr, IRQF_SHARED, - "sgi-ioc4serial", soft)) { - control->ic_irq = idd->idd_pdev->irq; - } else { - printk(KERN_WARNING - "%s : request_irq fails for IRQ 0x%x\n ", - __func__, idd->idd_pdev->irq); - } - ret = ioc4_attach_local(idd); - if (ret) - goto out4; - - /* register port with the serial core - 1 rs232, 1 rs422 */ - - ret = ioc4_serial_core_attach(idd->idd_pdev, PROTO_RS232); - if (ret) - goto out4; - - ret = ioc4_serial_core_attach(idd->idd_pdev, PROTO_RS422); - if (ret) - goto out5; - - Num_of_ioc4_cards++; - - return ret; - - /* error exits that give back resources */ -out5: - ioc4_serial_remove_one(idd); - return ret; -out4: - kfree(soft); -out3: - kfree(control); -out2: - if (serial) - iounmap(serial); - release_mem_region(tmp_addr1, sizeof(struct ioc4_serial)); -out1: - - return ret; -} - - -static struct ioc4_submodule ioc4_serial_submodule = { - .is_name = "IOC4_serial", - .is_owner = THIS_MODULE, - .is_probe = ioc4_serial_attach_one, - .is_remove = ioc4_serial_remove_one, -}; - -/** - * ioc4_serial_init - module init - */ -static int __init ioc4_serial_init(void) -{ - int ret; - - /* register with serial core */ - if ((ret = uart_register_driver(&ioc4_uart_rs232)) < 0) { - printk(KERN_WARNING - "%s: Couldn't register rs232 IOC4 serial driver\n", - __func__); - goto out; - } - if ((ret = uart_register_driver(&ioc4_uart_rs422)) < 0) { - printk(KERN_WARNING - "%s: Couldn't register rs422 IOC4 serial driver\n", - __func__); - goto out_uart_rs232; - } - - /* register with IOC4 main module */ - ret = ioc4_register_submodule(&ioc4_serial_submodule); - if (ret) - goto out_uart_rs422; - return 0; - -out_uart_rs422: - uart_unregister_driver(&ioc4_uart_rs422); -out_uart_rs232: - uart_unregister_driver(&ioc4_uart_rs232); -out: - return ret; -} - -static void __exit ioc4_serial_exit(void) -{ - ioc4_unregister_submodule(&ioc4_serial_submodule); - uart_unregister_driver(&ioc4_uart_rs232); - uart_unregister_driver(&ioc4_uart_rs422); -} - -late_initcall(ioc4_serial_init); /* Call only after tty init is done */ -module_exit(ioc4_serial_exit); - -MODULE_AUTHOR("Pat Gefre - Silicon Graphics Inc. (SGI) <pfg@sgi.com>"); -MODULE_DESCRIPTION("Serial PCI driver module for SGI IOC4 Base-IO Card"); -MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/sn_console.c b/drivers/tty/serial/sn_console.c deleted file mode 100644 index 283493358a62..000000000000 --- a/drivers/tty/serial/sn_console.c +++ /dev/null @@ -1,1036 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * C-Brick Serial Port (and console) driver for SGI Altix machines. - * - * This driver is NOT suitable for talking to the l1-controller for - * anything other than 'console activities' --- please use the l1 - * driver for that. - * - * - * Copyright (c) 2004-2006 Silicon Graphics, Inc. All Rights Reserved. - * - * Contact information: Silicon Graphics, Inc., 1500 Crittenden Lane, - * Mountain View, CA 94043, or: - * - * http://www.sgi.com - * - * For further information regarding this notice, see: - * - * http://oss.sgi.com/projects/GenInfo/NoticeExplan - */ - -#include <linux/interrupt.h> -#include <linux/tty.h> -#include <linux/tty_flip.h> -#include <linux/serial.h> -#include <linux/console.h> -#include <linux/init.h> -#include <linux/sysrq.h> -#include <linux/circ_buf.h> -#include <linux/serial_reg.h> -#include <linux/delay.h> /* for mdelay */ -#include <linux/miscdevice.h> -#include <linux/serial_core.h> - -#include <asm/io.h> -#include <asm/sn/simulator.h> -#include <asm/sn/sn_sal.h> - -/* number of characters we can transmit to the SAL console at a time */ -#define SN_SAL_MAX_CHARS 120 - -/* 64K, when we're asynch, it must be at least printk's LOG_BUF_LEN to - * avoid losing chars, (always has to be a power of 2) */ -#define SN_SAL_BUFFER_SIZE (64 * (1 << 10)) - -#define SN_SAL_UART_FIFO_DEPTH 16 -#define SN_SAL_UART_FIFO_SPEED_CPS (9600/10) - -/* sn_transmit_chars() calling args */ -#define TRANSMIT_BUFFERED 0 -#define TRANSMIT_RAW 1 - -/* To use dynamic numbers only and not use the assigned major and minor, - * define the following.. */ - /* #define USE_DYNAMIC_MINOR 1 *//* use dynamic minor number */ -#define USE_DYNAMIC_MINOR 0 /* Don't rely on misc_register dynamic minor */ - -/* Device name we're using */ -#define DEVICE_NAME "ttySG" -#define DEVICE_NAME_DYNAMIC "ttySG0" /* need full name for misc_register */ -/* The major/minor we are using, ignored for USE_DYNAMIC_MINOR */ -#define DEVICE_MAJOR 204 -#define DEVICE_MINOR 40 - -#ifdef CONFIG_MAGIC_SYSRQ -static char sysrq_serial_str[] = "\eSYS"; -static char *sysrq_serial_ptr = sysrq_serial_str; -static unsigned long sysrq_requested; -#endif /* CONFIG_MAGIC_SYSRQ */ - -/* - * Port definition - this kinda drives it all - */ -struct sn_cons_port { - struct timer_list sc_timer; - struct uart_port sc_port; - struct sn_sal_ops { - int (*sal_puts_raw) (const char *s, int len); - int (*sal_puts) (const char *s, int len); - int (*sal_getc) (void); - int (*sal_input_pending) (void); - void (*sal_wakeup_transmit) (struct sn_cons_port *, int); - } *sc_ops; - unsigned long sc_interrupt_timeout; - int sc_is_asynch; -}; - -static struct sn_cons_port sal_console_port; -static int sn_process_input; - -/* Only used if USE_DYNAMIC_MINOR is set to 1 */ -static struct miscdevice misc; /* used with misc_register for dynamic */ - -extern void early_sn_setup(void); - -#undef DEBUG -#ifdef DEBUG -static int sn_debug_printf(const char *fmt, ...); -#define DPRINTF(x...) sn_debug_printf(x) -#else -#define DPRINTF(x...) do { } while (0) -#endif - -/* Prototypes */ -static int snt_hw_puts_raw(const char *, int); -static int snt_hw_puts_buffered(const char *, int); -static int snt_poll_getc(void); -static int snt_poll_input_pending(void); -static int snt_intr_getc(void); -static int snt_intr_input_pending(void); -static void sn_transmit_chars(struct sn_cons_port *, int); - -/* A table for polling: - */ -static struct sn_sal_ops poll_ops = { - .sal_puts_raw = snt_hw_puts_raw, - .sal_puts = snt_hw_puts_raw, - .sal_getc = snt_poll_getc, - .sal_input_pending = snt_poll_input_pending -}; - -/* A table for interrupts enabled */ -static struct sn_sal_ops intr_ops = { - .sal_puts_raw = snt_hw_puts_raw, - .sal_puts = snt_hw_puts_buffered, - .sal_getc = snt_intr_getc, - .sal_input_pending = snt_intr_input_pending, - .sal_wakeup_transmit = sn_transmit_chars -}; - -/* the console does output in two distinctly different ways: - * synchronous (raw) and asynchronous (buffered). initially, early_printk - * does synchronous output. any data written goes directly to the SAL - * to be output (incidentally, it is internally buffered by the SAL) - * after interrupts and timers are initialized and available for use, - * the console init code switches to asynchronous output. this is - * also the earliest opportunity to begin polling for console input. - * after console initialization, console output and tty (serial port) - * output is buffered and sent to the SAL asynchronously (either by - * timer callback or by UART interrupt) */ - -/* routines for running the console in polling mode */ - -/** - * snt_poll_getc - Get a character from the console in polling mode - * - */ -static int snt_poll_getc(void) -{ - int ch; - - ia64_sn_console_getc(&ch); - return ch; -} - -/** - * snt_poll_input_pending - Check if any input is waiting - polling mode. - * - */ -static int snt_poll_input_pending(void) -{ - int status, input; - - status = ia64_sn_console_check(&input); - return !status && input; -} - -/* routines for an interrupt driven console (normal) */ - -/** - * snt_intr_getc - Get a character from the console, interrupt mode - * - */ -static int snt_intr_getc(void) -{ - return ia64_sn_console_readc(); -} - -/** - * snt_intr_input_pending - Check if input is pending, interrupt mode - * - */ -static int snt_intr_input_pending(void) -{ - return ia64_sn_console_intr_status() & SAL_CONSOLE_INTR_RECV; -} - -/* these functions are polled and interrupt */ - -/** - * snt_hw_puts_raw - Send raw string to the console, polled or interrupt mode - * @s: String - * @len: Length - * - */ -static int snt_hw_puts_raw(const char *s, int len) -{ - /* this will call the PROM and not return until this is done */ - return ia64_sn_console_putb(s, len); -} - -/** - * snt_hw_puts_buffered - Send string to console, polled or interrupt mode - * @s: String - * @len: Length - * - */ -static int snt_hw_puts_buffered(const char *s, int len) -{ - /* queue data to the PROM */ - return ia64_sn_console_xmit_chars((char *)s, len); -} - -/* uart interface structs - * These functions are associated with the uart_port that the serial core - * infrastructure calls. - * - * Note: Due to how the console works, many routines are no-ops. - */ - -/** - * snp_type - What type of console are we? - * @port: Port to operate with (we ignore since we only have one port) - * - */ -static const char *snp_type(struct uart_port *port) -{ - return ("SGI SN L1"); -} - -/** - * snp_tx_empty - Is the transmitter empty? We pretend we're always empty - * @port: Port to operate on (we ignore since we only have one port) - * - */ -static unsigned int snp_tx_empty(struct uart_port *port) -{ - return 1; -} - -/** - * snp_stop_tx - stop the transmitter - no-op for us - * @port: Port to operat eon - we ignore - no-op function - * - */ -static void snp_stop_tx(struct uart_port *port) -{ -} - -/** - * snp_release_port - Free i/o and resources for port - no-op for us - * @port: Port to operate on - we ignore - no-op function - * - */ -static void snp_release_port(struct uart_port *port) -{ -} - -/** - * snp_shutdown - shut down the port - free irq and disable - no-op for us - * @port: Port to shut down - we ignore - * - */ -static void snp_shutdown(struct uart_port *port) -{ -} - -/** - * snp_set_mctrl - set control lines (dtr, rts, etc) - no-op for our console - * @port: Port to operate on - we ignore - * @mctrl: Lines to set/unset - we ignore - * - */ -static void snp_set_mctrl(struct uart_port *port, unsigned int mctrl) -{ -} - -/** - * snp_get_mctrl - get contorl line info, we just return a static value - * @port: port to operate on - we only have one port so we ignore this - * - */ -static unsigned int snp_get_mctrl(struct uart_port *port) -{ - return TIOCM_CAR | TIOCM_RNG | TIOCM_DSR | TIOCM_CTS; -} - -/** - * snp_stop_rx - Stop the receiver - we ignor ethis - * @port: Port to operate on - we ignore - * - */ -static void snp_stop_rx(struct uart_port *port) -{ -} - -/** - * snp_start_tx - Start transmitter - * @port: Port to operate on - * - */ -static void snp_start_tx(struct uart_port *port) -{ - if (sal_console_port.sc_ops->sal_wakeup_transmit) - sal_console_port.sc_ops->sal_wakeup_transmit(&sal_console_port, - TRANSMIT_BUFFERED); - -} - -/** - * snp_break_ctl - handle breaks - ignored by us - * @port: Port to operate on - * @break_state: Break state - * - */ -static void snp_break_ctl(struct uart_port *port, int break_state) -{ -} - -/** - * snp_startup - Start up the serial port - always return 0 (We're always on) - * @port: Port to operate on - * - */ -static int snp_startup(struct uart_port *port) -{ - return 0; -} - -/** - * snp_set_termios - set termios stuff - we ignore these - * @port: port to operate on - * @termios: New settings - * @termios: Old - * - */ -static void -snp_set_termios(struct uart_port *port, struct ktermios *termios, - struct ktermios *old) -{ -} - -/** - * snp_request_port - allocate resources for port - ignored by us - * @port: port to operate on - * - */ -static int snp_request_port(struct uart_port *port) -{ - return 0; -} - -/** - * snp_config_port - allocate resources, set up - we ignore, we're always on - * @port: Port to operate on - * @flags: flags used for port setup - * - */ -static void snp_config_port(struct uart_port *port, int flags) -{ -} - -/* Associate the uart functions above - given to serial core */ - -static const struct uart_ops sn_console_ops = { - .tx_empty = snp_tx_empty, - .set_mctrl = snp_set_mctrl, - .get_mctrl = snp_get_mctrl, - .stop_tx = snp_stop_tx, - .start_tx = snp_start_tx, - .stop_rx = snp_stop_rx, - .break_ctl = snp_break_ctl, - .startup = snp_startup, - .shutdown = snp_shutdown, - .set_termios = snp_set_termios, - .pm = NULL, - .type = snp_type, - .release_port = snp_release_port, - .request_port = snp_request_port, - .config_port = snp_config_port, - .verify_port = NULL, -}; - -/* End of uart struct functions and defines */ - -#ifdef DEBUG - -/** - * sn_debug_printf - close to hardware debugging printf - * @fmt: printf format - * - * This is as "close to the metal" as we can get, used when the driver - * itself may be broken. - * - */ -static int sn_debug_printf(const char *fmt, ...) -{ - static char printk_buf[1024]; - int printed_len; - va_list args; - - va_start(args, fmt); - printed_len = vsnprintf(printk_buf, sizeof(printk_buf), fmt, args); - - if (!sal_console_port.sc_ops) { - sal_console_port.sc_ops = &poll_ops; - early_sn_setup(); - } - sal_console_port.sc_ops->sal_puts_raw(printk_buf, printed_len); - - va_end(args); - return printed_len; -} -#endif /* DEBUG */ - -/* - * Interrupt handling routines. - */ - -/** - * sn_receive_chars - Grab characters, pass them to tty layer - * @port: Port to operate on - * @flags: irq flags - * - * Note: If we're not registered with the serial core infrastructure yet, - * we don't try to send characters to it... - * - */ -static void -sn_receive_chars(struct sn_cons_port *port, unsigned long flags) -{ - struct tty_port *tport = NULL; - int ch; - - if (!port) { - printk(KERN_ERR "sn_receive_chars - port NULL so can't receive\n"); - return; - } - - if (!port->sc_ops) { - printk(KERN_ERR "sn_receive_chars - port->sc_ops NULL so can't receive\n"); - return; - } - - if (port->sc_port.state) { - /* The serial_core stuffs are initialized, use them */ - tport = &port->sc_port.state->port; - } - - while (port->sc_ops->sal_input_pending()) { - ch = port->sc_ops->sal_getc(); - if (ch < 0) { - printk(KERN_ERR "sn_console: An error occurred while " - "obtaining data from the console (0x%0x)\n", ch); - break; - } -#ifdef CONFIG_MAGIC_SYSRQ - if (sysrq_requested) { - unsigned long sysrq_timeout = sysrq_requested + HZ*5; - - sysrq_requested = 0; - if (ch && time_before(jiffies, sysrq_timeout)) { - spin_unlock_irqrestore(&port->sc_port.lock, flags); - handle_sysrq(ch); - spin_lock_irqsave(&port->sc_port.lock, flags); - /* ignore actual sysrq command char */ - continue; - } - } - if (ch == *sysrq_serial_ptr) { - if (!(*++sysrq_serial_ptr)) { - sysrq_requested = jiffies; - sysrq_serial_ptr = sysrq_serial_str; - } - /* - * ignore the whole sysrq string except for the - * leading escape - */ - if (ch != '\e') - continue; - } - else - sysrq_serial_ptr = sysrq_serial_str; -#endif /* CONFIG_MAGIC_SYSRQ */ - - /* record the character to pass up to the tty layer */ - if (tport) { - if (tty_insert_flip_char(tport, ch, TTY_NORMAL) == 0) - break; - } - port->sc_port.icount.rx++; - } - - if (tport) - tty_flip_buffer_push(tport); -} - -/** - * sn_transmit_chars - grab characters from serial core, send off - * @port: Port to operate on - * @raw: Transmit raw or buffered - * - * Note: If we're early, before we're registered with serial core, the - * writes are going through sn_sal_console_write because that's how - * register_console has been set up. We currently could have asynch - * polls calling this function due to sn_sal_switch_to_asynch but we can - * ignore them until we register with the serial core stuffs. - * - */ -static void sn_transmit_chars(struct sn_cons_port *port, int raw) -{ - int xmit_count, tail, head, loops, ii; - int result; - char *start; - struct circ_buf *xmit; - - if (!port) - return; - - BUG_ON(!port->sc_is_asynch); - - if (port->sc_port.state) { - /* We're initialized, using serial core infrastructure */ - xmit = &port->sc_port.state->xmit; - } else { - /* Probably sn_sal_switch_to_asynch has been run but serial core isn't - * initialized yet. Just return. Writes are going through - * sn_sal_console_write (due to register_console) at this time. - */ - return; - } - - if (uart_circ_empty(xmit) || uart_tx_stopped(&port->sc_port)) { - /* Nothing to do. */ - ia64_sn_console_intr_disable(SAL_CONSOLE_INTR_XMIT); - return; - } - - head = xmit->head; - tail = xmit->tail; - start = &xmit->buf[tail]; - - /* twice around gets the tail to the end of the buffer and - * then to the head, if needed */ - loops = (head < tail) ? 2 : 1; - - for (ii = 0; ii < loops; ii++) { - xmit_count = (head < tail) ? - (UART_XMIT_SIZE - tail) : (head - tail); - - if (xmit_count > 0) { - if (raw == TRANSMIT_RAW) - result = - port->sc_ops->sal_puts_raw(start, - xmit_count); - else - result = - port->sc_ops->sal_puts(start, xmit_count); -#ifdef DEBUG - if (!result) - DPRINTF("`"); -#endif - if (result > 0) { - xmit_count -= result; - port->sc_port.icount.tx += result; - tail += result; - tail &= UART_XMIT_SIZE - 1; - xmit->tail = tail; - start = &xmit->buf[tail]; - } - } - } - - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) - uart_write_wakeup(&port->sc_port); - - if (uart_circ_empty(xmit)) - snp_stop_tx(&port->sc_port); /* no-op for us */ -} - -/** - * sn_sal_interrupt - Handle console interrupts - * @irq: irq #, useful for debug statements - * @dev_id: our pointer to our port (sn_cons_port which contains the uart port) - * - */ -static irqreturn_t sn_sal_interrupt(int irq, void *dev_id) -{ - struct sn_cons_port *port = (struct sn_cons_port *)dev_id; - unsigned long flags; - int status = ia64_sn_console_intr_status(); - - if (!port) - return IRQ_NONE; - - spin_lock_irqsave(&port->sc_port.lock, flags); - if (status & SAL_CONSOLE_INTR_RECV) { - sn_receive_chars(port, flags); - } - if (status & SAL_CONSOLE_INTR_XMIT) { - sn_transmit_chars(port, TRANSMIT_BUFFERED); - } - spin_unlock_irqrestore(&port->sc_port.lock, flags); - return IRQ_HANDLED; -} - -/** - * sn_sal_timer_poll - this function handles polled console mode - * @data: A pointer to our sn_cons_port (which contains the uart port) - * - * data is the pointer that init_timer will store for us. This function is - * associated with init_timer to see if there is any console traffic. - * Obviously not used in interrupt mode - * - */ -static void sn_sal_timer_poll(struct timer_list *t) -{ - struct sn_cons_port *port = from_timer(port, t, sc_timer); - unsigned long flags; - - if (!port) - return; - - if (!port->sc_port.irq) { - spin_lock_irqsave(&port->sc_port.lock, flags); - if (sn_process_input) - sn_receive_chars(port, flags); - sn_transmit_chars(port, TRANSMIT_RAW); - spin_unlock_irqrestore(&port->sc_port.lock, flags); - mod_timer(&port->sc_timer, - jiffies + port->sc_interrupt_timeout); - } -} - -/* - * Boot-time initialization code - */ - -/** - * sn_sal_switch_to_asynch - Switch to async mode (as opposed to synch) - * @port: Our sn_cons_port (which contains the uart port) - * - * So this is used by sn_sal_serial_console_init (early on, before we're - * registered with serial core). It's also used by sn_sal_init - * right after we've registered with serial core. The later only happens - * if we didn't already come through here via sn_sal_serial_console_init. - * - */ -static void __init sn_sal_switch_to_asynch(struct sn_cons_port *port) -{ - unsigned long flags; - - if (!port) - return; - - DPRINTF("sn_console: about to switch to asynchronous console\n"); - - /* without early_printk, we may be invoked late enough to race - * with other cpus doing console IO at this point, however - * console interrupts will never be enabled */ - spin_lock_irqsave(&port->sc_port.lock, flags); - - /* early_printk invocation may have done this for us */ - if (!port->sc_ops) - port->sc_ops = &poll_ops; - - /* we can't turn on the console interrupt (as request_irq - * calls kmalloc, which isn't set up yet), so we rely on a - * timer to poll for input and push data from the console - * buffer. - */ - timer_setup(&port->sc_timer, sn_sal_timer_poll, 0); - - if (IS_RUNNING_ON_SIMULATOR()) - port->sc_interrupt_timeout = 6; - else { - /* 960cps / 16 char FIFO = 60HZ - * HZ / (SN_SAL_FIFO_SPEED_CPS / SN_SAL_FIFO_DEPTH) */ - port->sc_interrupt_timeout = - HZ * SN_SAL_UART_FIFO_DEPTH / SN_SAL_UART_FIFO_SPEED_CPS; - } - mod_timer(&port->sc_timer, jiffies + port->sc_interrupt_timeout); - - port->sc_is_asynch = 1; - spin_unlock_irqrestore(&port->sc_port.lock, flags); -} - -/** - * sn_sal_switch_to_interrupts - Switch to interrupt driven mode - * @port: Our sn_cons_port (which contains the uart port) - * - * In sn_sal_init, after we're registered with serial core and - * the port is added, this function is called to switch us to interrupt - * mode. We were previously in asynch/polling mode (using init_timer). - * - * We attempt to switch to interrupt mode here by calling - * request_irq. If that works out, we enable receive interrupts. - */ -static void __init sn_sal_switch_to_interrupts(struct sn_cons_port *port) -{ - unsigned long flags; - - if (port) { - DPRINTF("sn_console: switching to interrupt driven console\n"); - - if (request_irq(SGI_UART_VECTOR, sn_sal_interrupt, - IRQF_SHARED, - "SAL console driver", port) >= 0) { - spin_lock_irqsave(&port->sc_port.lock, flags); - port->sc_port.irq = SGI_UART_VECTOR; - port->sc_ops = &intr_ops; - irq_set_handler(port->sc_port.irq, handle_level_irq); - - /* turn on receive interrupts */ - ia64_sn_console_intr_enable(SAL_CONSOLE_INTR_RECV); - spin_unlock_irqrestore(&port->sc_port.lock, flags); - } - else { - printk(KERN_INFO - "sn_console: console proceeding in polled mode\n"); - } - } -} - -/* - * Kernel console definitions - */ - -static void sn_sal_console_write(struct console *, const char *, unsigned); -static int sn_sal_console_setup(struct console *, char *); -static struct uart_driver sal_console_uart; -extern struct tty_driver *uart_console_device(struct console *, int *); - -static struct console sal_console = { - .name = DEVICE_NAME, - .write = sn_sal_console_write, - .device = uart_console_device, - .setup = sn_sal_console_setup, - .index = -1, /* unspecified */ - .data = &sal_console_uart, -}; - -#define SAL_CONSOLE &sal_console - -static struct uart_driver sal_console_uart = { - .owner = THIS_MODULE, - .driver_name = "sn_console", - .dev_name = DEVICE_NAME, - .major = 0, /* major/minor set at registration time per USE_DYNAMIC_MINOR */ - .minor = 0, - .nr = 1, /* one port */ - .cons = SAL_CONSOLE, -}; - -/** - * sn_sal_init - When the kernel loads us, get us rolling w/ serial core - * - * Before this is called, we've been printing kernel messages in a special - * early mode not making use of the serial core infrastructure. When our - * driver is loaded for real, we register the driver and port with serial - * core and try to enable interrupt driven mode. - * - */ -static int __init sn_sal_init(void) -{ - int retval; - - if (!ia64_platform_is("sn2")) - return 0; - - printk(KERN_INFO "sn_console: Console driver init\n"); - - if (USE_DYNAMIC_MINOR == 1) { - misc.minor = MISC_DYNAMIC_MINOR; - misc.name = DEVICE_NAME_DYNAMIC; - retval = misc_register(&misc); - if (retval != 0) { - printk(KERN_WARNING "Failed to register console " - "device using misc_register.\n"); - return -ENODEV; - } - sal_console_uart.major = MISC_MAJOR; - sal_console_uart.minor = misc.minor; - } else { - sal_console_uart.major = DEVICE_MAJOR; - sal_console_uart.minor = DEVICE_MINOR; - } - - /* We register the driver and the port before switching to interrupts - * or async above so the proper uart structures are populated */ - - if (uart_register_driver(&sal_console_uart) < 0) { - printk - ("ERROR sn_sal_init failed uart_register_driver, line %d\n", - __LINE__); - return -ENODEV; - } - - spin_lock_init(&sal_console_port.sc_port.lock); - - /* Setup the port struct with the minimum needed */ - sal_console_port.sc_port.membase = (char *)1; /* just needs to be non-zero */ - sal_console_port.sc_port.type = PORT_16550A; - sal_console_port.sc_port.fifosize = SN_SAL_MAX_CHARS; - sal_console_port.sc_port.ops = &sn_console_ops; - sal_console_port.sc_port.line = 0; - - if (uart_add_one_port(&sal_console_uart, &sal_console_port.sc_port) < 0) { - /* error - not sure what I'd do - so I'll do nothing */ - printk(KERN_ERR "%s: unable to add port\n", __func__); - } - - /* when this driver is compiled in, the console initialization - * will have already switched us into asynchronous operation - * before we get here through the initcalls */ - if (!sal_console_port.sc_is_asynch) { - sn_sal_switch_to_asynch(&sal_console_port); - } - - /* at this point (device_init) we can try to turn on interrupts */ - if (!IS_RUNNING_ON_SIMULATOR()) { - sn_sal_switch_to_interrupts(&sal_console_port); - } - sn_process_input = 1; - return 0; -} -device_initcall(sn_sal_init); - -/** - * puts_raw_fixed - sn_sal_console_write helper for adding \r's as required - * @puts_raw : puts function to do the writing - * @s: input string - * @count: length - * - * We need a \r ahead of every \n for direct writes through - * ia64_sn_console_putb (what sal_puts_raw below actually does). - * - */ - -static void puts_raw_fixed(int (*puts_raw) (const char *s, int len), - const char *s, int count) -{ - const char *s1; - - /* Output '\r' before each '\n' */ - while ((s1 = memchr(s, '\n', count)) != NULL) { - puts_raw(s, s1 - s); - puts_raw("\r\n", 2); - count -= s1 + 1 - s; - s = s1 + 1; - } - puts_raw(s, count); -} - -/** - * sn_sal_console_write - Print statements before serial core available - * @console: Console to operate on - we ignore since we have just one - * @s: String to send - * @count: length - * - * This is referenced in the console struct. It is used for early - * console printing before we register with serial core and for things - * such as kdb. The console_lock must be held when we get here. - * - * This function has some code for trying to print output even if the lock - * is held. We try to cover the case where a lock holder could have died. - * We don't use this special case code if we're not registered with serial - * core yet. After we're registered with serial core, the only time this - * function would be used is for high level kernel output like magic sys req, - * kdb, and printk's. - */ -static void -sn_sal_console_write(struct console *co, const char *s, unsigned count) -{ - unsigned long flags = 0; - struct sn_cons_port *port = &sal_console_port; - static int stole_lock = 0; - - BUG_ON(!port->sc_is_asynch); - - /* We can't look at the xmit buffer if we're not registered with serial core - * yet. So only do the fancy recovery after registering - */ - if (!port->sc_port.state) { - /* Not yet registered with serial core - simple case */ - puts_raw_fixed(port->sc_ops->sal_puts_raw, s, count); - return; - } - - /* somebody really wants this output, might be an - * oops, kdb, panic, etc. make sure they get it. */ - if (!spin_trylock_irqsave(&port->sc_port.lock, flags)) { - int lhead = port->sc_port.state->xmit.head; - int ltail = port->sc_port.state->xmit.tail; - int counter, got_lock = 0; - - /* - * We attempt to determine if someone has died with the - * lock. We wait ~20 secs after the head and tail ptrs - * stop moving and assume the lock holder is not functional - * and plow ahead. If the lock is freed within the time out - * period we re-get the lock and go ahead normally. We also - * remember if we have plowed ahead so that we don't have - * to wait out the time out period again - the asumption - * is that we will time out again. - */ - - for (counter = 0; counter < 150; mdelay(125), counter++) { - if (stole_lock) - break; - - if (spin_trylock_irqsave(&port->sc_port.lock, flags)) { - got_lock = 1; - break; - } else { - /* still locked */ - if ((lhead != port->sc_port.state->xmit.head) - || (ltail != - port->sc_port.state->xmit.tail)) { - lhead = - port->sc_port.state->xmit.head; - ltail = - port->sc_port.state->xmit.tail; - counter = 0; - } - } - } - /* flush anything in the serial core xmit buffer, raw */ - sn_transmit_chars(port, 1); - if (got_lock) { - spin_unlock_irqrestore(&port->sc_port.lock, flags); - stole_lock = 0; - } else { - /* fell thru */ - stole_lock = 1; - } - puts_raw_fixed(port->sc_ops->sal_puts_raw, s, count); - } else { - stole_lock = 0; - sn_transmit_chars(port, 1); - spin_unlock_irqrestore(&port->sc_port.lock, flags); - - puts_raw_fixed(port->sc_ops->sal_puts_raw, s, count); - } -} - - -/** - * sn_sal_console_setup - Set up console for early printing - * @co: Console to work with - * @options: Options to set - * - * Altix console doesn't do anything with baud rates, etc, anyway. - * - * This isn't required since not providing the setup function in the - * console struct is ok. However, other patches like KDB plop something - * here so providing it is easier. - * - */ -static int sn_sal_console_setup(struct console *co, char *options) -{ - return 0; -} - -/** - * sn_sal_console_write_early - simple early output routine - * @co - console struct - * @s - string to print - * @count - count - * - * Simple function to provide early output, before even - * sn_sal_serial_console_init is called. Referenced in the - * console struct registerd in sn_serial_console_early_setup. - * - */ -static void __init -sn_sal_console_write_early(struct console *co, const char *s, unsigned count) -{ - puts_raw_fixed(sal_console_port.sc_ops->sal_puts_raw, s, count); -} - -/* Used for very early console printing - again, before - * sn_sal_serial_console_init is run */ -static struct console sal_console_early __initdata = { - .name = "sn_sal", - .write = sn_sal_console_write_early, - .flags = CON_PRINTBUFFER, - .index = -1, -}; - -/** - * sn_serial_console_early_setup - Sets up early console output support - * - * Register a console early on... This is for output before even - * sn_sal_serial_cosnole_init is called. This function is called from - * setup.c. This allows us to do really early polled writes. When - * sn_sal_serial_console_init is called, this console is unregistered - * and a new one registered. - */ -int __init sn_serial_console_early_setup(void) -{ - if (!ia64_platform_is("sn2")) - return -1; - - sal_console_port.sc_ops = &poll_ops; - spin_lock_init(&sal_console_port.sc_port.lock); - early_sn_setup(); /* Find SAL entry points */ - register_console(&sal_console_early); - - return 0; -} - -/** - * sn_sal_serial_console_init - Early console output - set up for register - * - * This function is called when regular console init happens. Because we - * support even earlier console output with sn_serial_console_early_setup - * (called from setup.c directly), this function unregisters the really - * early console. - * - * Note: Even if setup.c doesn't register sal_console_early, unregistering - * it here doesn't hurt anything. - * - */ -static int __init sn_sal_serial_console_init(void) -{ - if (ia64_platform_is("sn2")) { - sn_sal_switch_to_asynch(&sal_console_port); - DPRINTF("sn_sal_serial_console_init : register console\n"); - register_console(&sal_console); - unregister_console(&sal_console_early); - } - return 0; -} - -console_initcall(sn_sal_serial_console_init); |