From nobody Sat Jun 13 04:46:34 2026 Received: from mail-pl1-f173.google.com (mail-pl1-f173.google.com [209.85.214.173]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 66A73369990 for ; Sun, 10 May 2026 09:05:05 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.173 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778403915; cv=none; b=KJSpu3q/WN/Rnp79lLD2C3z8bsK9JB8wWBPl3el34yBXpdUHfahlq5PZSOSz1QR6tAHE8dbFmBBIuQBKyK55iTK7VxnI/wTgFbxCx+1EYN6z9b8h/zERZ/R3j/RdXRc3JudDmAebAQlKnZ8DeONuYh1td73R7iAfVuUHKZxmgZ4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778403915; c=relaxed/simple; bh=oj0OOGFe23tVHCQq2nZVWWAI2z5GhFDhLD0KYS+MnsE=; h=From:To:Cc:Subject:Date:Message-ID:MIME-Version; b=t39vHpiEJgzCM9ZxBqygvz3/yQVqNqffocT3fER6169ngLHBHEDzcoX/5ewahUKnY3kdgjmPS6IQpCxz/nVLHOIqoqU+Za5W7WGzJlwRbD6p0kQldARscIMFzO70bLo0hJvke/uBUBF1OaA3KE+EXer1UqHvWNuJ26lRwV9feos= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=pXmJ+KHz; arc=none smtp.client-ip=209.85.214.173 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="pXmJ+KHz" Received: by mail-pl1-f173.google.com with SMTP id d9443c01a7336-2ba856db1c0so23125125ad.3 for ; Sun, 10 May 2026 02:05:05 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1778403905; x=1779008705; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to; bh=awZROnsy5A/kDk8PHJGdoJpA5NfduQ8sxFkjgqfx1g4=; b=pXmJ+KHzNziUL7X361BgCJQYh53jPndg/Kbxdp6ENMJ5t6f11x9vkLA/nCAIkkjl7c +VUXXRnxLwPw7i/18Bk8ivCjYNZy+Yvmqz/0oPBHSx/azS27XDoiAqrMYH/uQ7j9xObX Ct8qrnNUCp/mQBnBK+xVaSxs4nx5+oR9pWh3pCsTZoYNiahOJVDlk//1C5lAGzETuiTw IqvmKtlrS97EVucQyddD8fJDfF8KdHoQCBu8FPxuInFh5I5AEFj8yQNKvHeahGisiDBy A4RlPff3DPP6XtKYMuALQb+325MN3npC8FCh0RAUaHLwIq2qMb50RDeuP4cCPjYOD2Vu YNFQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1778403905; x=1779008705; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=awZROnsy5A/kDk8PHJGdoJpA5NfduQ8sxFkjgqfx1g4=; b=bq6x8j7kwD1yfOZ5UD3ADcCR1EFSbyUGne6P13EblrUEbdLo1pbZlY7YrTsmWp+7HO W2uqU/BjcxI3gfMzTq3polTbnEh5yJSTyPK7PvB648DbnP+v6yh2cW+ATf4fXc/UK1Oj aH1SXv0h63gP/VwoUQVnPzZA7fajWjoE8wFeE4bwS2lJS5tItTikvPmSJuEdV34m0s4y 45JPh8FxHBHtSQO0qL80YtCEMkjmCkN0zp3khsO2eCFpBVGoaj9HHRD+eeULvO1fren9 wDzV5WMWlBIpgV5oVOu2X88sjsQivrZp4OlX0XMq31LjZ2FaYBkdXQwlrLeQjv48Rniu SMUw== X-Gm-Message-State: AOJu0YwMlYMeByTtWpOr0BkbdJ3q1+fijzzc6oCUPjr3EXRIcMpWXof8 BA1xTA2XqPROpJ5J+fMW+PzOnGfnsaEM70u/GYiFUmVh/VGl8YxTHfNMnNShBXMs X-Gm-Gg: Acq92OF0zgiVc2iFvzzu9/A0MgEGzOOENR8YPrBhkMIKaYrhlO7Zzf5oC4+mNg8hLHs EbUYg4ewyGtFLY9IjWGgmqqPMalEgMyV7CjjqeaLaXsXF4OrIyXRC7rU2Sy6Vp7Ow8qCss0Fzdb RffhYWY+Zq/dSy9/vh9xmDwjsxQEVb+lfa79Ue6RQiuv5rEZVgOpYceKohrT1AuSIeSqKxN1++n OxQ1SvOvG/7XLDnnjJYaQJrYur5QXwtu78rFU8V7v7XKFh991tecQPXMTXHA2D6V7gKFNa84RZJ 2DY/8Hl7gDtmCJ9ggkikxBk7bgYEpAbVYHyM9KWIN9XPVEiVuRJuS1OGcWyAsFKF4ub83HErl/+ za+oTOxR2yIEQ3OKA8IpuUpC73TlDN8JHdhDBR8AhE4ZWUZCtqjN0M/gJyRfCget0xDb2OPV/6c JoCBeiGm5c+TV9aHE1DupCdDyOjnKCIUpY7xFPft2+Iw+f X-Received: by 2002:a17:902:f791:b0:2b4:5d51:ce96 with SMTP id d9443c01a7336-2bc7aa11ba6mr47293255ad.24.1778403903850; Sun, 10 May 2026 02:05:03 -0700 (PDT) Received: from MAHADEV.localdomain ([103.175.63.225]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-2baf1d27254sm69756605ad.4.2026.05.10.02.05.00 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 10 May 2026 02:05:03 -0700 (PDT) From: Hanu-man12shaw X-Google-Original-From: Hanu-man12shaw To: gregkh@linuxfoundation.org Cc: linux-kernel@vger.kernel.org, linux-staging@lists.linux.dev, Hanu-man12shaw Subject: [PATCH] staging: vme_user: fix lines exceeding 80 characters in vme_tsi148.c Date: Sun, 10 May 2026 09:04:54 +0000 Message-ID: <20260510090454.3474-1-harshit116@gmail.com> X-Mailer: git-send-email 2.53.0 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Signed-off-by: Hanu-man12shaw --- drivers/staging/vme_user/vme_tsi148.c | 5270 +++++++++++++------------ 1 file changed, 2636 insertions(+), 2634 deletions(-) diff --git a/drivers/staging/vme_user/vme_tsi148.c b/drivers/staging/vme_us= er/vme_tsi148.c index 4cf3486646ce..e0ce118280d5 100644 --- a/drivers/staging/vme_user/vme_tsi148.c +++ b/drivers/staging/vme_user/vme_tsi148.c @@ -1,2634 +1,2636 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Support for the Tundra TSI148 VME-PCI Bridge Chip - * - * Author: Martyn Welch - * Copyright 2008 GE Intelligent Platforms Embedded Systems, Inc. - * - * Based on work by Tom Armistead and Ajit Prem - * Copyright 2004 Motorola Inc. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "vme.h" -#include "vme_bridge.h" -#include "vme_tsi148.h" - -static int tsi148_probe(struct pci_dev *, const struct pci_device_id *); -static void tsi148_remove(struct pci_dev *); - -/* Module parameter */ -static bool err_chk; -static u32 geoid; - -static const char driver_name[] =3D "vme_tsi148"; - -static const struct pci_device_id tsi148_ids[] =3D { - { PCI_DEVICE(PCI_VENDOR_ID_TUNDRA, PCI_DEVICE_ID_TUNDRA_TSI148) }, - { }, -}; - -MODULE_DEVICE_TABLE(pci, tsi148_ids); - -static struct pci_driver tsi148_driver =3D { - .name =3D driver_name, - .id_table =3D tsi148_ids, - .probe =3D tsi148_probe, - .remove =3D tsi148_remove, -}; - -static void reg_join(unsigned int high, unsigned int low, - unsigned long long *variable) -{ - *variable =3D (unsigned long long)high << 32; - *variable |=3D (unsigned long long)low; -} - -static void reg_split(unsigned long long variable, unsigned int *high, - unsigned int *low) -{ - *low =3D (unsigned int)variable & 0xFFFFFFFF; - *high =3D (unsigned int)(variable >> 32); -} - -/* - * Wakes up DMA queue. - */ -static u32 tsi148_DMA_irqhandler(struct tsi148_driver *bridge, - int channel_mask) -{ - u32 serviced =3D 0; - - if (channel_mask & TSI148_LCSR_INTS_DMA0S) { - wake_up(&bridge->dma_queue[0]); - serviced |=3D TSI148_LCSR_INTC_DMA0C; - } - if (channel_mask & TSI148_LCSR_INTS_DMA1S) { - wake_up(&bridge->dma_queue[1]); - serviced |=3D TSI148_LCSR_INTC_DMA1C; - } - - return serviced; -} - -/* - * Wake up location monitor queue - */ -static u32 tsi148_LM_irqhandler(struct tsi148_driver *bridge, u32 stat) -{ - int i; - u32 serviced =3D 0; - - for (i =3D 0; i < 4; i++) { - if (stat & TSI148_LCSR_INTS_LMS[i]) { - /* We only enable interrupts if the callback is set */ - bridge->lm_callback[i](bridge->lm_data[i]); - serviced |=3D TSI148_LCSR_INTC_LMC[i]; - } - } - - return serviced; -} - -/* - * Wake up mail box queue. - * - * XXX This functionality is not exposed up though API. - */ -static u32 tsi148_MB_irqhandler(struct vme_bridge *tsi148_bridge, u32 stat) -{ - int i; - u32 val; - u32 serviced =3D 0; - struct tsi148_driver *bridge; - - bridge =3D tsi148_bridge->driver_priv; - - for (i =3D 0; i < 4; i++) { - if (stat & TSI148_LCSR_INTS_MBS[i]) { - val =3D ioread32be(bridge->base + TSI148_GCSR_MBOX[i]); - dev_err(tsi148_bridge->parent, "VME Mailbox %d received: 0x%x\n", - i, val); - serviced |=3D TSI148_LCSR_INTC_MBC[i]; - } - } - - return serviced; -} - -/* - * Display error & status message when PERR (PCI) exception interrupt occu= rs. - */ -static u32 tsi148_PERR_irqhandler(struct vme_bridge *tsi148_bridge) -{ - struct tsi148_driver *bridge; - - bridge =3D tsi148_bridge->driver_priv; - - dev_err(tsi148_bridge->parent, "PCI Exception at address: 0x%08x:%08x, at= tributes: %08x\n", - ioread32be(bridge->base + TSI148_LCSR_EDPAU), - ioread32be(bridge->base + TSI148_LCSR_EDPAL), - ioread32be(bridge->base + TSI148_LCSR_EDPAT)); - - dev_err(tsi148_bridge->parent, "PCI-X attribute reg: %08x, PCI-X split co= mpletion reg: %08x\n", - ioread32be(bridge->base + TSI148_LCSR_EDPXA), - ioread32be(bridge->base + TSI148_LCSR_EDPXS)); - - iowrite32be(TSI148_LCSR_EDPAT_EDPCL, bridge->base + TSI148_LCSR_EDPAT); - - return TSI148_LCSR_INTC_PERRC; -} - -/* - * Save address and status when VME error interrupt occurs. - */ -static u32 tsi148_VERR_irqhandler(struct vme_bridge *tsi148_bridge) -{ - unsigned int error_addr_high, error_addr_low; - unsigned long long error_addr; - u32 error_attrib; - int error_am; - struct tsi148_driver *bridge; - - bridge =3D tsi148_bridge->driver_priv; - - error_addr_high =3D ioread32be(bridge->base + TSI148_LCSR_VEAU); - error_addr_low =3D ioread32be(bridge->base + TSI148_LCSR_VEAL); - error_attrib =3D ioread32be(bridge->base + TSI148_LCSR_VEAT); - error_am =3D (error_attrib & TSI148_LCSR_VEAT_AM_M) >> 8; - - reg_join(error_addr_high, error_addr_low, &error_addr); - - /* Check for exception register overflow (we have lost error data) */ - if (error_attrib & TSI148_LCSR_VEAT_VEOF) - dev_err(tsi148_bridge->parent, "VME Bus Exception Overflow Occurred\n"); - - if (err_chk) - vme_bus_error_handler(tsi148_bridge, error_addr, error_am); - else - dev_err(tsi148_bridge->parent, - "VME Bus Error at address: 0x%llx, attributes: %08x\n", - error_addr, error_attrib); - - /* Clear Status */ - iowrite32be(TSI148_LCSR_VEAT_VESCL, bridge->base + TSI148_LCSR_VEAT); - - return TSI148_LCSR_INTC_VERRC; -} - -/* - * Wake up IACK queue. - */ -static u32 tsi148_IACK_irqhandler(struct tsi148_driver *bridge) -{ - wake_up(&bridge->iack_queue); - - return TSI148_LCSR_INTC_IACKC; -} - -/* - * Calling VME bus interrupt callback if provided. - */ -static u32 tsi148_VIRQ_irqhandler(struct vme_bridge *tsi148_bridge, - u32 stat) -{ - int vec, i, serviced =3D 0; - struct tsi148_driver *bridge; - - bridge =3D tsi148_bridge->driver_priv; - - for (i =3D 7; i > 0; i--) { - if (stat & (1 << i)) { - /* - * Note: Even though the registers are defined as - * 32-bits in the spec, we only want to issue 8-bit - * IACK cycles on the bus, read from offset 3. - */ - vec =3D ioread8(bridge->base + TSI148_LCSR_VIACK[i] + 3); - - vme_irq_handler(tsi148_bridge, i, vec); - - serviced |=3D (1 << i); - } - } - - return serviced; -} - -/* - * Top level interrupt handler. Clears appropriate interrupt status bits = and - * then calls appropriate sub handler(s). - */ -static irqreturn_t tsi148_irqhandler(int irq, void *ptr) -{ - u32 stat, enable, serviced =3D 0; - struct vme_bridge *tsi148_bridge; - struct tsi148_driver *bridge; - - tsi148_bridge =3D ptr; - - bridge =3D tsi148_bridge->driver_priv; - - /* Determine which interrupts are unmasked and set */ - enable =3D ioread32be(bridge->base + TSI148_LCSR_INTEO); - stat =3D ioread32be(bridge->base + TSI148_LCSR_INTS); - - /* Only look at unmasked interrupts */ - stat &=3D enable; - - if (unlikely(!stat)) - return IRQ_NONE; - - /* Call subhandlers as appropriate */ - /* DMA irqs */ - if (stat & (TSI148_LCSR_INTS_DMA1S | TSI148_LCSR_INTS_DMA0S)) - serviced |=3D tsi148_DMA_irqhandler(bridge, stat); - - /* Location monitor irqs */ - if (stat & (TSI148_LCSR_INTS_LM3S | TSI148_LCSR_INTS_LM2S | - TSI148_LCSR_INTS_LM1S | TSI148_LCSR_INTS_LM0S)) - serviced |=3D tsi148_LM_irqhandler(bridge, stat); - - /* Mail box irqs */ - if (stat & (TSI148_LCSR_INTS_MB3S | TSI148_LCSR_INTS_MB2S | - TSI148_LCSR_INTS_MB1S | TSI148_LCSR_INTS_MB0S)) - serviced |=3D tsi148_MB_irqhandler(tsi148_bridge, stat); - - /* PCI bus error */ - if (stat & TSI148_LCSR_INTS_PERRS) - serviced |=3D tsi148_PERR_irqhandler(tsi148_bridge); - - /* VME bus error */ - if (stat & TSI148_LCSR_INTS_VERRS) - serviced |=3D tsi148_VERR_irqhandler(tsi148_bridge); - - /* IACK irq */ - if (stat & TSI148_LCSR_INTS_IACKS) - serviced |=3D tsi148_IACK_irqhandler(bridge); - - /* VME bus irqs */ - if (stat & (TSI148_LCSR_INTS_IRQ7S | TSI148_LCSR_INTS_IRQ6S | - TSI148_LCSR_INTS_IRQ5S | TSI148_LCSR_INTS_IRQ4S | - TSI148_LCSR_INTS_IRQ3S | TSI148_LCSR_INTS_IRQ2S | - TSI148_LCSR_INTS_IRQ1S)) - serviced |=3D tsi148_VIRQ_irqhandler(tsi148_bridge, stat); - - /* Clear serviced interrupts */ - iowrite32be(serviced, bridge->base + TSI148_LCSR_INTC); - - return IRQ_HANDLED; -} - -static int tsi148_irq_init(struct vme_bridge *tsi148_bridge) -{ - int result; - unsigned int tmp; - struct pci_dev *pdev; - struct tsi148_driver *bridge; - - pdev =3D to_pci_dev(tsi148_bridge->parent); - - bridge =3D tsi148_bridge->driver_priv; - - result =3D request_irq(pdev->irq, - tsi148_irqhandler, - IRQF_SHARED, - driver_name, tsi148_bridge); - if (result) { - dev_err(tsi148_bridge->parent, "Can't get assigned pci irq vector %02X\n= ", - pdev->irq); - return result; - } - - /* Enable and unmask interrupts */ - tmp =3D TSI148_LCSR_INTEO_DMA1EO | TSI148_LCSR_INTEO_DMA0EO | - TSI148_LCSR_INTEO_MB3EO | TSI148_LCSR_INTEO_MB2EO | - TSI148_LCSR_INTEO_MB1EO | TSI148_LCSR_INTEO_MB0EO | - TSI148_LCSR_INTEO_PERREO | TSI148_LCSR_INTEO_VERREO | - TSI148_LCSR_INTEO_IACKEO; - - /* This leaves the following interrupts masked. - * TSI148_LCSR_INTEO_VIEEO - * TSI148_LCSR_INTEO_SYSFLEO - * TSI148_LCSR_INTEO_ACFLEO - */ - - /* Don't enable Location Monitor interrupts here - they will be - * enabled when the location monitors are properly configured and - * a callback has been attached. - * TSI148_LCSR_INTEO_LM0EO - * TSI148_LCSR_INTEO_LM1EO - * TSI148_LCSR_INTEO_LM2EO - * TSI148_LCSR_INTEO_LM3EO - */ - - /* Don't enable VME interrupts until we add a handler, else the board - * will respond to it and we don't want that unless it knows how to - * properly deal with it. - * TSI148_LCSR_INTEO_IRQ7EO - * TSI148_LCSR_INTEO_IRQ6EO - * TSI148_LCSR_INTEO_IRQ5EO - * TSI148_LCSR_INTEO_IRQ4EO - * TSI148_LCSR_INTEO_IRQ3EO - * TSI148_LCSR_INTEO_IRQ2EO - * TSI148_LCSR_INTEO_IRQ1EO - */ - - iowrite32be(tmp, bridge->base + TSI148_LCSR_INTEO); - iowrite32be(tmp, bridge->base + TSI148_LCSR_INTEN); - - return 0; -} - -static void tsi148_irq_exit(struct vme_bridge *tsi148_bridge, - struct pci_dev *pdev) -{ - struct tsi148_driver *bridge =3D tsi148_bridge->driver_priv; - - /* Turn off interrupts */ - iowrite32be(0x0, bridge->base + TSI148_LCSR_INTEO); - iowrite32be(0x0, bridge->base + TSI148_LCSR_INTEN); - - /* Clear all interrupts */ - iowrite32be(0xFFFFFFFF, bridge->base + TSI148_LCSR_INTC); - - /* Detach interrupt handler */ - free_irq(pdev->irq, tsi148_bridge); -} - -/* - * Check to see if an IACk has been received, return true (1) or false (0). - */ -static int tsi148_iack_received(struct tsi148_driver *bridge) -{ - u32 tmp; - - tmp =3D ioread32be(bridge->base + TSI148_LCSR_VICR); - - if (tmp & TSI148_LCSR_VICR_IRQS) - return 0; - else - return 1; -} - -/* - * Configure VME interrupt - */ -static void tsi148_irq_set(struct vme_bridge *tsi148_bridge, int level, - int state, int sync) -{ - struct pci_dev *pdev; - u32 tmp; - struct tsi148_driver *bridge; - - bridge =3D tsi148_bridge->driver_priv; - - /* We need to do the ordering differently for enabling and disabling */ - if (state =3D=3D 0) { - tmp =3D ioread32be(bridge->base + TSI148_LCSR_INTEN); - tmp &=3D ~TSI148_LCSR_INTEN_IRQEN[level - 1]; - iowrite32be(tmp, bridge->base + TSI148_LCSR_INTEN); - - tmp =3D ioread32be(bridge->base + TSI148_LCSR_INTEO); - tmp &=3D ~TSI148_LCSR_INTEO_IRQEO[level - 1]; - iowrite32be(tmp, bridge->base + TSI148_LCSR_INTEO); - - if (sync !=3D 0) { - pdev =3D to_pci_dev(tsi148_bridge->parent); - synchronize_irq(pdev->irq); - } - } else { - tmp =3D ioread32be(bridge->base + TSI148_LCSR_INTEO); - tmp |=3D TSI148_LCSR_INTEO_IRQEO[level - 1]; - iowrite32be(tmp, bridge->base + TSI148_LCSR_INTEO); - - tmp =3D ioread32be(bridge->base + TSI148_LCSR_INTEN); - tmp |=3D TSI148_LCSR_INTEN_IRQEN[level - 1]; - iowrite32be(tmp, bridge->base + TSI148_LCSR_INTEN); - } -} - -/* - * Generate a VME bus interrupt at the requested level & vector. Wait for - * interrupt to be acked. - */ -static int tsi148_irq_generate(struct vme_bridge *tsi148_bridge, int level, - int statid) -{ - u32 tmp; - struct tsi148_driver *bridge; - - bridge =3D tsi148_bridge->driver_priv; - - mutex_lock(&bridge->vme_int); - - /* Read VICR register */ - tmp =3D ioread32be(bridge->base + TSI148_LCSR_VICR); - - /* Set Status/ID */ - tmp =3D (tmp & ~TSI148_LCSR_VICR_STID_M) | - (statid & TSI148_LCSR_VICR_STID_M); - iowrite32be(tmp, bridge->base + TSI148_LCSR_VICR); - - /* Assert VMEbus IRQ */ - tmp =3D tmp | TSI148_LCSR_VICR_IRQL[level]; - iowrite32be(tmp, bridge->base + TSI148_LCSR_VICR); - - /* XXX Consider implementing a timeout? */ - wait_event_interruptible(bridge->iack_queue, - tsi148_iack_received(bridge)); - - mutex_unlock(&bridge->vme_int); - - return 0; -} - -/* - * Initialize a slave window with the requested attributes. - */ -static int tsi148_slave_set(struct vme_slave_resource *image, int enabled, - unsigned long long vme_base, unsigned long long size, - dma_addr_t pci_base, u32 aspace, u32 cycle) -{ - unsigned int i, addr =3D 0, granularity =3D 0; - unsigned int temp_ctl =3D 0; - unsigned int vme_base_low, vme_base_high; - unsigned int vme_bound_low, vme_bound_high; - unsigned int pci_offset_low, pci_offset_high; - unsigned long long vme_bound, pci_offset; - struct vme_bridge *tsi148_bridge; - struct tsi148_driver *bridge; - - tsi148_bridge =3D image->parent; - bridge =3D tsi148_bridge->driver_priv; - - i =3D image->number; - - switch (aspace) { - case VME_A16: - granularity =3D 0x10; - addr |=3D TSI148_LCSR_ITAT_AS_A16; - break; - case VME_A24: - granularity =3D 0x1000; - addr |=3D TSI148_LCSR_ITAT_AS_A24; - break; - case VME_A32: - granularity =3D 0x10000; - addr |=3D TSI148_LCSR_ITAT_AS_A32; - break; - case VME_A64: - granularity =3D 0x10000; - addr |=3D TSI148_LCSR_ITAT_AS_A64; - break; - default: - dev_err(tsi148_bridge->parent, "Invalid address space\n"); - return -EINVAL; - } - - /* Convert 64-bit variables to 2x 32-bit variables */ - reg_split(vme_base, &vme_base_high, &vme_base_low); - - /* - * Bound address is a valid address for the window, adjust - * accordingly - */ - vme_bound =3D vme_base + size - granularity; - reg_split(vme_bound, &vme_bound_high, &vme_bound_low); - pci_offset =3D (unsigned long long)pci_base - vme_base; - reg_split(pci_offset, &pci_offset_high, &pci_offset_low); - - if (vme_base_low & (granularity - 1)) { - dev_err(tsi148_bridge->parent, "Invalid VME base alignment\n"); - return -EINVAL; - } - if (vme_bound_low & (granularity - 1)) { - dev_err(tsi148_bridge->parent, "Invalid VME bound alignment\n"); - return -EINVAL; - } - if (pci_offset_low & (granularity - 1)) { - dev_err(tsi148_bridge->parent, "Invalid PCI Offset alignment\n"); - return -EINVAL; - } - - /* Disable while we are mucking around */ - temp_ctl =3D ioread32be(bridge->base + TSI148_LCSR_IT[i] + - TSI148_LCSR_OFFSET_ITAT); - temp_ctl &=3D ~TSI148_LCSR_ITAT_EN; - iowrite32be(temp_ctl, bridge->base + TSI148_LCSR_IT[i] + - TSI148_LCSR_OFFSET_ITAT); - - /* Setup mapping */ - iowrite32be(vme_base_high, bridge->base + TSI148_LCSR_IT[i] + - TSI148_LCSR_OFFSET_ITSAU); - iowrite32be(vme_base_low, bridge->base + TSI148_LCSR_IT[i] + - TSI148_LCSR_OFFSET_ITSAL); - iowrite32be(vme_bound_high, bridge->base + TSI148_LCSR_IT[i] + - TSI148_LCSR_OFFSET_ITEAU); - iowrite32be(vme_bound_low, bridge->base + TSI148_LCSR_IT[i] + - TSI148_LCSR_OFFSET_ITEAL); - iowrite32be(pci_offset_high, bridge->base + TSI148_LCSR_IT[i] + - TSI148_LCSR_OFFSET_ITOFU); - iowrite32be(pci_offset_low, bridge->base + TSI148_LCSR_IT[i] + - TSI148_LCSR_OFFSET_ITOFL); - - /* Setup 2eSST speeds */ - temp_ctl &=3D ~TSI148_LCSR_ITAT_2eSSTM_M; - switch (cycle & (VME_2eSST160 | VME_2eSST267 | VME_2eSST320)) { - case VME_2eSST160: - temp_ctl |=3D TSI148_LCSR_ITAT_2eSSTM_160; - break; - case VME_2eSST267: - temp_ctl |=3D TSI148_LCSR_ITAT_2eSSTM_267; - break; - case VME_2eSST320: - temp_ctl |=3D TSI148_LCSR_ITAT_2eSSTM_320; - break; - } - - /* Setup cycle types */ - temp_ctl &=3D ~(0x1F << 7); - if (cycle & VME_BLT) - temp_ctl |=3D TSI148_LCSR_ITAT_BLT; - if (cycle & VME_MBLT) - temp_ctl |=3D TSI148_LCSR_ITAT_MBLT; - if (cycle & VME_2eVME) - temp_ctl |=3D TSI148_LCSR_ITAT_2eVME; - if (cycle & VME_2eSST) - temp_ctl |=3D TSI148_LCSR_ITAT_2eSST; - if (cycle & VME_2eSSTB) - temp_ctl |=3D TSI148_LCSR_ITAT_2eSSTB; - - /* Setup address space */ - temp_ctl &=3D ~TSI148_LCSR_ITAT_AS_M; - temp_ctl |=3D addr; - - temp_ctl &=3D ~0xF; - if (cycle & VME_SUPER) - temp_ctl |=3D TSI148_LCSR_ITAT_SUPR; - if (cycle & VME_USER) - temp_ctl |=3D TSI148_LCSR_ITAT_NPRIV; - if (cycle & VME_PROG) - temp_ctl |=3D TSI148_LCSR_ITAT_PGM; - if (cycle & VME_DATA) - temp_ctl |=3D TSI148_LCSR_ITAT_DATA; - - /* Write ctl reg without enable */ - iowrite32be(temp_ctl, bridge->base + TSI148_LCSR_IT[i] + - TSI148_LCSR_OFFSET_ITAT); - - if (enabled) - temp_ctl |=3D TSI148_LCSR_ITAT_EN; - - iowrite32be(temp_ctl, bridge->base + TSI148_LCSR_IT[i] + - TSI148_LCSR_OFFSET_ITAT); - - return 0; -} - -/* - * Get slave window configuration. - */ -static int tsi148_slave_get(struct vme_slave_resource *image, int *enabled, - unsigned long long *vme_base, unsigned long long *size, - dma_addr_t *pci_base, u32 *aspace, u32 *cycle) -{ - unsigned int i, granularity =3D 0, ctl =3D 0; - unsigned int vme_base_low, vme_base_high; - unsigned int vme_bound_low, vme_bound_high; - unsigned int pci_offset_low, pci_offset_high; - unsigned long long vme_bound, pci_offset; - struct tsi148_driver *bridge; - - bridge =3D image->parent->driver_priv; - - i =3D image->number; - - /* Read registers */ - ctl =3D ioread32be(bridge->base + TSI148_LCSR_IT[i] + - TSI148_LCSR_OFFSET_ITAT); - - vme_base_high =3D ioread32be(bridge->base + TSI148_LCSR_IT[i] + - TSI148_LCSR_OFFSET_ITSAU); - vme_base_low =3D ioread32be(bridge->base + TSI148_LCSR_IT[i] + - TSI148_LCSR_OFFSET_ITSAL); - vme_bound_high =3D ioread32be(bridge->base + TSI148_LCSR_IT[i] + - TSI148_LCSR_OFFSET_ITEAU); - vme_bound_low =3D ioread32be(bridge->base + TSI148_LCSR_IT[i] + - TSI148_LCSR_OFFSET_ITEAL); - pci_offset_high =3D ioread32be(bridge->base + TSI148_LCSR_IT[i] + - TSI148_LCSR_OFFSET_ITOFU); - pci_offset_low =3D ioread32be(bridge->base + TSI148_LCSR_IT[i] + - TSI148_LCSR_OFFSET_ITOFL); - - /* Convert 64-bit variables to 2x 32-bit variables */ - reg_join(vme_base_high, vme_base_low, vme_base); - reg_join(vme_bound_high, vme_bound_low, &vme_bound); - reg_join(pci_offset_high, pci_offset_low, &pci_offset); - - *pci_base =3D (dma_addr_t)(*vme_base + pci_offset); - - *enabled =3D 0; - *aspace =3D 0; - *cycle =3D 0; - - if (ctl & TSI148_LCSR_ITAT_EN) - *enabled =3D 1; - - if ((ctl & TSI148_LCSR_ITAT_AS_M) =3D=3D TSI148_LCSR_ITAT_AS_A16) { - granularity =3D 0x10; - *aspace |=3D VME_A16; - } - if ((ctl & TSI148_LCSR_ITAT_AS_M) =3D=3D TSI148_LCSR_ITAT_AS_A24) { - granularity =3D 0x1000; - *aspace |=3D VME_A24; - } - if ((ctl & TSI148_LCSR_ITAT_AS_M) =3D=3D TSI148_LCSR_ITAT_AS_A32) { - granularity =3D 0x10000; - *aspace |=3D VME_A32; - } - if ((ctl & TSI148_LCSR_ITAT_AS_M) =3D=3D TSI148_LCSR_ITAT_AS_A64) { - granularity =3D 0x10000; - *aspace |=3D VME_A64; - } - - /* Need granularity before we set the size */ - *size =3D (unsigned long long)((vme_bound - *vme_base) + granularity); - - if ((ctl & TSI148_LCSR_ITAT_2eSSTM_M) =3D=3D TSI148_LCSR_ITAT_2eSSTM_160) - *cycle |=3D VME_2eSST160; - if ((ctl & TSI148_LCSR_ITAT_2eSSTM_M) =3D=3D TSI148_LCSR_ITAT_2eSSTM_267) - *cycle |=3D VME_2eSST267; - if ((ctl & TSI148_LCSR_ITAT_2eSSTM_M) =3D=3D TSI148_LCSR_ITAT_2eSSTM_320) - *cycle |=3D VME_2eSST320; - - if (ctl & TSI148_LCSR_ITAT_BLT) - *cycle |=3D VME_BLT; - if (ctl & TSI148_LCSR_ITAT_MBLT) - *cycle |=3D VME_MBLT; - if (ctl & TSI148_LCSR_ITAT_2eVME) - *cycle |=3D VME_2eVME; - if (ctl & TSI148_LCSR_ITAT_2eSST) - *cycle |=3D VME_2eSST; - if (ctl & TSI148_LCSR_ITAT_2eSSTB) - *cycle |=3D VME_2eSSTB; - - if (ctl & TSI148_LCSR_ITAT_SUPR) - *cycle |=3D VME_SUPER; - if (ctl & TSI148_LCSR_ITAT_NPRIV) - *cycle |=3D VME_USER; - if (ctl & TSI148_LCSR_ITAT_PGM) - *cycle |=3D VME_PROG; - if (ctl & TSI148_LCSR_ITAT_DATA) - *cycle |=3D VME_DATA; - - return 0; -} - -/* - * Allocate and map PCI Resource - */ -static int tsi148_alloc_resource(struct vme_master_resource *image, - unsigned long long size) -{ - unsigned long long existing_size; - int retval =3D 0; - struct pci_dev *pdev; - struct vme_bridge *tsi148_bridge; - - tsi148_bridge =3D image->parent; - - pdev =3D to_pci_dev(tsi148_bridge->parent); - - existing_size =3D (unsigned long long)(image->bus_resource.end - - image->bus_resource.start); - - /* If the existing size is OK, return */ - if ((size !=3D 0) && (existing_size =3D=3D (size - 1))) - return 0; - - if (existing_size !=3D 0) { - iounmap(image->kern_base); - image->kern_base =3D NULL; - kfree(image->bus_resource.name); - release_resource(&image->bus_resource); - memset(&image->bus_resource, 0, sizeof(image->bus_resource)); - } - - /* Exit here if size is zero */ - if (size =3D=3D 0) - return 0; - - if (!image->bus_resource.name) { - image->bus_resource.name =3D kmalloc(VMENAMSIZ + 3, GFP_ATOMIC); - if (!image->bus_resource.name) { - retval =3D -ENOMEM; - goto err_name; - } - } - - sprintf((char *)image->bus_resource.name, "%s.%d", tsi148_bridge->name, - image->number); - - image->bus_resource.start =3D 0; - image->bus_resource.end =3D (unsigned long)size; - image->bus_resource.flags =3D IORESOURCE_MEM; - - retval =3D pci_bus_alloc_resource(pdev->bus, &image->bus_resource, - size, 0x10000, PCIBIOS_MIN_MEM, - 0, NULL, NULL); - if (retval) { - dev_err(tsi148_bridge->parent, "Failed to allocate mem resource for wind= ow %d size 0x%lx start 0x%lx\n", - image->number, (unsigned long)size, - (unsigned long)image->bus_resource.start); - goto err_resource; - } - - image->kern_base =3D ioremap(image->bus_resource.start, size); - if (!image->kern_base) { - dev_err(tsi148_bridge->parent, "Failed to remap resource\n"); - retval =3D -ENOMEM; - goto err_remap; - } - - return 0; - -err_remap: - release_resource(&image->bus_resource); -err_resource: - kfree(image->bus_resource.name); - memset(&image->bus_resource, 0, sizeof(image->bus_resource)); -err_name: - return retval; -} - -/* - * Free and unmap PCI Resource - */ -static void tsi148_free_resource(struct vme_master_resource *image) -{ - iounmap(image->kern_base); - image->kern_base =3D NULL; - release_resource(&image->bus_resource); - kfree(image->bus_resource.name); - memset(&image->bus_resource, 0, sizeof(image->bus_resource)); -} - -/* - * Set the attributes of an outbound window. - */ -static int tsi148_master_set(struct vme_master_resource *image, int enable= d, - unsigned long long vme_base, unsigned long long size, - u32 aspace, u32 cycle, u32 dwidth) -{ - int retval =3D 0; - unsigned int i; - unsigned int temp_ctl =3D 0; - unsigned int pci_base_low, pci_base_high; - unsigned int pci_bound_low, pci_bound_high; - unsigned int vme_offset_low, vme_offset_high; - unsigned long long pci_bound, vme_offset, pci_base; - struct vme_bridge *tsi148_bridge; - struct tsi148_driver *bridge; - struct pci_bus_region region; - struct pci_dev *pdev; - - tsi148_bridge =3D image->parent; - - bridge =3D tsi148_bridge->driver_priv; - - pdev =3D to_pci_dev(tsi148_bridge->parent); - - /* Verify input data */ - if (vme_base & 0xFFFF) { - dev_err(tsi148_bridge->parent, "Invalid VME Window alignment\n"); - retval =3D -EINVAL; - goto err_window; - } - - if ((size =3D=3D 0) && (enabled !=3D 0)) { - dev_err(tsi148_bridge->parent, "Size must be non-zero for enabled window= s\n"); - retval =3D -EINVAL; - goto err_window; - } - - spin_lock(&image->lock); - - /* Let's allocate the resource here rather than further up the stack as - * it avoids pushing loads of bus dependent stuff up the stack. If size - * is zero, any existing resource will be freed. - */ - retval =3D tsi148_alloc_resource(image, size); - if (retval) { - spin_unlock(&image->lock); - dev_err(tsi148_bridge->parent, "Unable to allocate memory for resource\n= "); - goto err_res; - } - - if (size =3D=3D 0) { - pci_base =3D 0; - pci_bound =3D 0; - vme_offset =3D 0; - } else { - pcibios_resource_to_bus(pdev->bus, ®ion, - &image->bus_resource); - pci_base =3D region.start; - - /* - * Bound address is a valid address for the window, adjust - * according to window granularity. - */ - pci_bound =3D pci_base + (size - 0x10000); - vme_offset =3D vme_base - pci_base; - } - - /* Convert 64-bit variables to 2x 32-bit variables */ - reg_split(pci_base, &pci_base_high, &pci_base_low); - reg_split(pci_bound, &pci_bound_high, &pci_bound_low); - reg_split(vme_offset, &vme_offset_high, &vme_offset_low); - - if (pci_base_low & 0xFFFF) { - spin_unlock(&image->lock); - dev_err(tsi148_bridge->parent, "Invalid PCI base alignment\n"); - retval =3D -EINVAL; - goto err_gran; - } - if (pci_bound_low & 0xFFFF) { - spin_unlock(&image->lock); - dev_err(tsi148_bridge->parent, "Invalid PCI bound alignment\n"); - retval =3D -EINVAL; - goto err_gran; - } - if (vme_offset_low & 0xFFFF) { - spin_unlock(&image->lock); - dev_err(tsi148_bridge->parent, "Invalid VME Offset alignment\n"); - retval =3D -EINVAL; - goto err_gran; - } - - i =3D image->number; - - /* Disable while we are mucking around */ - temp_ctl =3D ioread32be(bridge->base + TSI148_LCSR_OT[i] + - TSI148_LCSR_OFFSET_OTAT); - temp_ctl &=3D ~TSI148_LCSR_OTAT_EN; - iowrite32be(temp_ctl, bridge->base + TSI148_LCSR_OT[i] + - TSI148_LCSR_OFFSET_OTAT); - - /* Setup 2eSST speeds */ - temp_ctl &=3D ~TSI148_LCSR_OTAT_2eSSTM_M; - switch (cycle & (VME_2eSST160 | VME_2eSST267 | VME_2eSST320)) { - case VME_2eSST160: - temp_ctl |=3D TSI148_LCSR_OTAT_2eSSTM_160; - break; - case VME_2eSST267: - temp_ctl |=3D TSI148_LCSR_OTAT_2eSSTM_267; - break; - case VME_2eSST320: - temp_ctl |=3D TSI148_LCSR_OTAT_2eSSTM_320; - break; - } - - /* Setup cycle types */ - if (cycle & VME_BLT) { - temp_ctl &=3D ~TSI148_LCSR_OTAT_TM_M; - temp_ctl |=3D TSI148_LCSR_OTAT_TM_BLT; - } - if (cycle & VME_MBLT) { - temp_ctl &=3D ~TSI148_LCSR_OTAT_TM_M; - temp_ctl |=3D TSI148_LCSR_OTAT_TM_MBLT; - } - if (cycle & VME_2eVME) { - temp_ctl &=3D ~TSI148_LCSR_OTAT_TM_M; - temp_ctl |=3D TSI148_LCSR_OTAT_TM_2eVME; - } - if (cycle & VME_2eSST) { - temp_ctl &=3D ~TSI148_LCSR_OTAT_TM_M; - temp_ctl |=3D TSI148_LCSR_OTAT_TM_2eSST; - } - if (cycle & VME_2eSSTB) { - dev_warn(tsi148_bridge->parent, "Currently not setting Broadcast Select = Registers\n"); - temp_ctl &=3D ~TSI148_LCSR_OTAT_TM_M; - temp_ctl |=3D TSI148_LCSR_OTAT_TM_2eSSTB; - } - - /* Setup data width */ - temp_ctl &=3D ~TSI148_LCSR_OTAT_DBW_M; - switch (dwidth) { - case VME_D16: - temp_ctl |=3D TSI148_LCSR_OTAT_DBW_16; - break; - case VME_D32: - temp_ctl |=3D TSI148_LCSR_OTAT_DBW_32; - break; - default: - spin_unlock(&image->lock); - dev_err(tsi148_bridge->parent, "Invalid data width\n"); - retval =3D -EINVAL; - goto err_dwidth; - } - - /* Setup address space */ - temp_ctl &=3D ~TSI148_LCSR_OTAT_AMODE_M; - switch (aspace) { - case VME_A16: - temp_ctl |=3D TSI148_LCSR_OTAT_AMODE_A16; - break; - case VME_A24: - temp_ctl |=3D TSI148_LCSR_OTAT_AMODE_A24; - break; - case VME_A32: - temp_ctl |=3D TSI148_LCSR_OTAT_AMODE_A32; - break; - case VME_A64: - temp_ctl |=3D TSI148_LCSR_OTAT_AMODE_A64; - break; - case VME_CRCSR: - temp_ctl |=3D TSI148_LCSR_OTAT_AMODE_CRCSR; - break; - case VME_USER1: - temp_ctl |=3D TSI148_LCSR_OTAT_AMODE_USER1; - break; - case VME_USER2: - temp_ctl |=3D TSI148_LCSR_OTAT_AMODE_USER2; - break; - case VME_USER3: - temp_ctl |=3D TSI148_LCSR_OTAT_AMODE_USER3; - break; - case VME_USER4: - temp_ctl |=3D TSI148_LCSR_OTAT_AMODE_USER4; - break; - default: - spin_unlock(&image->lock); - dev_err(tsi148_bridge->parent, "Invalid address space\n"); - retval =3D -EINVAL; - goto err_aspace; - } - - temp_ctl &=3D ~(3 << 4); - if (cycle & VME_SUPER) - temp_ctl |=3D TSI148_LCSR_OTAT_SUP; - if (cycle & VME_PROG) - temp_ctl |=3D TSI148_LCSR_OTAT_PGM; - - /* Setup mapping */ - iowrite32be(pci_base_high, bridge->base + TSI148_LCSR_OT[i] + - TSI148_LCSR_OFFSET_OTSAU); - iowrite32be(pci_base_low, bridge->base + TSI148_LCSR_OT[i] + - TSI148_LCSR_OFFSET_OTSAL); - iowrite32be(pci_bound_high, bridge->base + TSI148_LCSR_OT[i] + - TSI148_LCSR_OFFSET_OTEAU); - iowrite32be(pci_bound_low, bridge->base + TSI148_LCSR_OT[i] + - TSI148_LCSR_OFFSET_OTEAL); - iowrite32be(vme_offset_high, bridge->base + TSI148_LCSR_OT[i] + - TSI148_LCSR_OFFSET_OTOFU); - iowrite32be(vme_offset_low, bridge->base + TSI148_LCSR_OT[i] + - TSI148_LCSR_OFFSET_OTOFL); - - /* Write ctl reg without enable */ - iowrite32be(temp_ctl, bridge->base + TSI148_LCSR_OT[i] + - TSI148_LCSR_OFFSET_OTAT); - - if (enabled) - temp_ctl |=3D TSI148_LCSR_OTAT_EN; - - iowrite32be(temp_ctl, bridge->base + TSI148_LCSR_OT[i] + - TSI148_LCSR_OFFSET_OTAT); - - spin_unlock(&image->lock); - return 0; - -err_aspace: -err_dwidth: -err_gran: - tsi148_free_resource(image); -err_res: -err_window: - return retval; -} - -/* - * Set the attributes of an outbound window. - * - * XXX Not parsing prefetch information. - */ -static int __tsi148_master_get(struct vme_master_resource *image, int *ena= bled, - unsigned long long *vme_base, unsigned long long *size, - u32 *aspace, u32 *cycle, u32 *dwidth) -{ - unsigned int i, ctl; - unsigned int pci_base_low, pci_base_high; - unsigned int pci_bound_low, pci_bound_high; - unsigned int vme_offset_low, vme_offset_high; - - unsigned long long pci_base, pci_bound, vme_offset; - struct tsi148_driver *bridge; - - bridge =3D image->parent->driver_priv; - - i =3D image->number; - - ctl =3D ioread32be(bridge->base + TSI148_LCSR_OT[i] + - TSI148_LCSR_OFFSET_OTAT); - - pci_base_high =3D ioread32be(bridge->base + TSI148_LCSR_OT[i] + - TSI148_LCSR_OFFSET_OTSAU); - pci_base_low =3D ioread32be(bridge->base + TSI148_LCSR_OT[i] + - TSI148_LCSR_OFFSET_OTSAL); - pci_bound_high =3D ioread32be(bridge->base + TSI148_LCSR_OT[i] + - TSI148_LCSR_OFFSET_OTEAU); - pci_bound_low =3D ioread32be(bridge->base + TSI148_LCSR_OT[i] + - TSI148_LCSR_OFFSET_OTEAL); - vme_offset_high =3D ioread32be(bridge->base + TSI148_LCSR_OT[i] + - TSI148_LCSR_OFFSET_OTOFU); - vme_offset_low =3D ioread32be(bridge->base + TSI148_LCSR_OT[i] + - TSI148_LCSR_OFFSET_OTOFL); - - /* Convert 64-bit variables to 2x 32-bit variables */ - reg_join(pci_base_high, pci_base_low, &pci_base); - reg_join(pci_bound_high, pci_bound_low, &pci_bound); - reg_join(vme_offset_high, vme_offset_low, &vme_offset); - - *vme_base =3D pci_base + vme_offset; - *size =3D (unsigned long long)(pci_bound - pci_base) + 0x10000; - - *enabled =3D 0; - *aspace =3D 0; - *cycle =3D 0; - *dwidth =3D 0; - - if (ctl & TSI148_LCSR_OTAT_EN) - *enabled =3D 1; - - /* Setup address space */ - if ((ctl & TSI148_LCSR_OTAT_AMODE_M) =3D=3D TSI148_LCSR_OTAT_AMODE_A16) - *aspace |=3D VME_A16; - if ((ctl & TSI148_LCSR_OTAT_AMODE_M) =3D=3D TSI148_LCSR_OTAT_AMODE_A24) - *aspace |=3D VME_A24; - if ((ctl & TSI148_LCSR_OTAT_AMODE_M) =3D=3D TSI148_LCSR_OTAT_AMODE_A32) - *aspace |=3D VME_A32; - if ((ctl & TSI148_LCSR_OTAT_AMODE_M) =3D=3D TSI148_LCSR_OTAT_AMODE_A64) - *aspace |=3D VME_A64; - if ((ctl & TSI148_LCSR_OTAT_AMODE_M) =3D=3D TSI148_LCSR_OTAT_AMODE_CRCSR) - *aspace |=3D VME_CRCSR; - if ((ctl & TSI148_LCSR_OTAT_AMODE_M) =3D=3D TSI148_LCSR_OTAT_AMODE_USER1) - *aspace |=3D VME_USER1; - if ((ctl & TSI148_LCSR_OTAT_AMODE_M) =3D=3D TSI148_LCSR_OTAT_AMODE_USER2) - *aspace |=3D VME_USER2; - if ((ctl & TSI148_LCSR_OTAT_AMODE_M) =3D=3D TSI148_LCSR_OTAT_AMODE_USER3) - *aspace |=3D VME_USER3; - if ((ctl & TSI148_LCSR_OTAT_AMODE_M) =3D=3D TSI148_LCSR_OTAT_AMODE_USER4) - *aspace |=3D VME_USER4; - - /* Setup 2eSST speeds */ - if ((ctl & TSI148_LCSR_OTAT_2eSSTM_M) =3D=3D TSI148_LCSR_OTAT_2eSSTM_160) - *cycle |=3D VME_2eSST160; - if ((ctl & TSI148_LCSR_OTAT_2eSSTM_M) =3D=3D TSI148_LCSR_OTAT_2eSSTM_267) - *cycle |=3D VME_2eSST267; - if ((ctl & TSI148_LCSR_OTAT_2eSSTM_M) =3D=3D TSI148_LCSR_OTAT_2eSSTM_320) - *cycle |=3D VME_2eSST320; - - /* Setup cycle types */ - if ((ctl & TSI148_LCSR_OTAT_TM_M) =3D=3D TSI148_LCSR_OTAT_TM_SCT) - *cycle |=3D VME_SCT; - if ((ctl & TSI148_LCSR_OTAT_TM_M) =3D=3D TSI148_LCSR_OTAT_TM_BLT) - *cycle |=3D VME_BLT; - if ((ctl & TSI148_LCSR_OTAT_TM_M) =3D=3D TSI148_LCSR_OTAT_TM_MBLT) - *cycle |=3D VME_MBLT; - if ((ctl & TSI148_LCSR_OTAT_TM_M) =3D=3D TSI148_LCSR_OTAT_TM_2eVME) - *cycle |=3D VME_2eVME; - if ((ctl & TSI148_LCSR_OTAT_TM_M) =3D=3D TSI148_LCSR_OTAT_TM_2eSST) - *cycle |=3D VME_2eSST; - if ((ctl & TSI148_LCSR_OTAT_TM_M) =3D=3D TSI148_LCSR_OTAT_TM_2eSSTB) - *cycle |=3D VME_2eSSTB; - - if (ctl & TSI148_LCSR_OTAT_SUP) - *cycle |=3D VME_SUPER; - else - *cycle |=3D VME_USER; - - if (ctl & TSI148_LCSR_OTAT_PGM) - *cycle |=3D VME_PROG; - else - *cycle |=3D VME_DATA; - - /* Setup data width */ - if ((ctl & TSI148_LCSR_OTAT_DBW_M) =3D=3D TSI148_LCSR_OTAT_DBW_16) - *dwidth =3D VME_D16; - if ((ctl & TSI148_LCSR_OTAT_DBW_M) =3D=3D TSI148_LCSR_OTAT_DBW_32) - *dwidth =3D VME_D32; - - return 0; -} - -static int tsi148_master_get(struct vme_master_resource *image, int *enabl= ed, - unsigned long long *vme_base, unsigned long long *size, - u32 *aspace, u32 *cycle, u32 *dwidth) -{ - int retval; - - spin_lock(&image->lock); - - retval =3D __tsi148_master_get(image, enabled, vme_base, size, aspace, - cycle, dwidth); - - spin_unlock(&image->lock); - - return retval; -} - -static ssize_t tsi148_master_read(struct vme_master_resource *image, void = *buf, - size_t count, loff_t offset) -{ - int retval, enabled; - unsigned long long vme_base, size; - u32 aspace, cycle, dwidth; - struct vme_error_handler *handler =3D NULL; - struct vme_bridge *tsi148_bridge; - void __iomem *addr =3D image->kern_base + offset; - unsigned int done =3D 0; - unsigned int count32; - - tsi148_bridge =3D image->parent; - - spin_lock(&image->lock); - - if (err_chk) { - __tsi148_master_get(image, &enabled, &vme_base, &size, &aspace, - &cycle, &dwidth); - handler =3D vme_register_error_handler(tsi148_bridge, aspace, - vme_base + offset, count); - if (!handler) { - spin_unlock(&image->lock); - return -ENOMEM; - } - } - - /* The following code handles VME address alignment. We cannot use - * memcpy_xxx here because it may cut data transfers in to 8-bit - * cycles when D16 or D32 cycles are required on the VME bus. - * On the other hand, the bridge itself assures that the maximum data - * cycle configured for the transfer is used and splits it - * automatically for non-aligned addresses, so we don't want the - * overhead of needlessly forcing small transfers for the entire cycle. - */ - if ((uintptr_t)addr & 0x1) { - *(u8 *)buf =3D ioread8(addr); - done +=3D 1; - if (done =3D=3D count) - goto out; - } - if ((uintptr_t)(addr + done) & 0x2) { - if ((count - done) < 2) { - *(u8 *)(buf + done) =3D ioread8(addr + done); - done +=3D 1; - goto out; - } else { - *(u16 *)(buf + done) =3D ioread16(addr + done); - done +=3D 2; - } - } - - count32 =3D (count - done) & ~0x3; - while (done < count32) { - *(u32 *)(buf + done) =3D ioread32(addr + done); - done +=3D 4; - } - - if ((count - done) & 0x2) { - *(u16 *)(buf + done) =3D ioread16(addr + done); - done +=3D 2; - } - if ((count - done) & 0x1) { - *(u8 *)(buf + done) =3D ioread8(addr + done); - done +=3D 1; - } - -out: - retval =3D count; - - if (err_chk) { - if (handler->num_errors) { - dev_err(image->parent->parent, - "First VME read error detected an at address 0x%llx\n", - handler->first_error); - retval =3D handler->first_error - (vme_base + offset); - } - vme_unregister_error_handler(handler); - } - - spin_unlock(&image->lock); - - return retval; -} - -static ssize_t tsi148_master_write(struct vme_master_resource *image, void= *buf, - size_t count, loff_t offset) -{ - int retval =3D 0, enabled; - unsigned long long vme_base, size; - u32 aspace, cycle, dwidth; - void __iomem *addr =3D image->kern_base + offset; - unsigned int done =3D 0; - unsigned int count32; - - struct vme_error_handler *handler =3D NULL; - struct vme_bridge *tsi148_bridge; - struct tsi148_driver *bridge; - - tsi148_bridge =3D image->parent; - - bridge =3D tsi148_bridge->driver_priv; - - spin_lock(&image->lock); - - if (err_chk) { - __tsi148_master_get(image, &enabled, &vme_base, &size, &aspace, - &cycle, &dwidth); - handler =3D vme_register_error_handler(tsi148_bridge, aspace, - vme_base + offset, count); - if (!handler) { - spin_unlock(&image->lock); - return -ENOMEM; - } - } - - /* Here we apply for the same strategy we do in master_read - * function in order to assure the correct cycles. - */ - if ((uintptr_t)addr & 0x1) { - iowrite8(*(u8 *)buf, addr); - done +=3D 1; - if (done =3D=3D count) - goto out; - } - if ((uintptr_t)(addr + done) & 0x2) { - if ((count - done) < 2) { - iowrite8(*(u8 *)(buf + done), addr + done); - done +=3D 1; - goto out; - } else { - iowrite16(*(u16 *)(buf + done), addr + done); - done +=3D 2; - } - } - - count32 =3D (count - done) & ~0x3; - while (done < count32) { - iowrite32(*(u32 *)(buf + done), addr + done); - done +=3D 4; - } - - if ((count - done) & 0x2) { - iowrite16(*(u16 *)(buf + done), addr + done); - done +=3D 2; - } - if ((count - done) & 0x1) { - iowrite8(*(u8 *)(buf + done), addr + done); - done +=3D 1; - } - -out: - retval =3D count; - - /* - * Writes are posted. We need to do a read on the VME bus to flush out - * all of the writes before we check for errors. We can't guarantee - * that reading the data we have just written is safe. It is believed - * that there isn't any read, write re-ordering, so we can read any - * location in VME space, so lets read the Device ID from the tsi148's - * own registers as mapped into CR/CSR space. - * - * We check for saved errors in the written address range/space. - */ - - if (err_chk) { - ioread16(bridge->flush_image->kern_base + 0x7F000); - - if (handler->num_errors) { - dev_warn(tsi148_bridge->parent, - "First VME write error detected an at address 0x%llx\n", - handler->first_error); - retval =3D handler->first_error - (vme_base + offset); - } - vme_unregister_error_handler(handler); - } - - spin_unlock(&image->lock); - - return retval; -} - -/* - * Perform an RMW cycle on the VME bus. - * - * Requires a previously configured master window, returns final value. - */ -static unsigned int tsi148_master_rmw(struct vme_master_resource *image, u= nsigned int mask, - unsigned int compare, unsigned int swap, loff_t offset) -{ - unsigned long long pci_addr; - unsigned int pci_addr_high, pci_addr_low; - u32 tmp, result; - int i; - struct tsi148_driver *bridge; - - bridge =3D image->parent->driver_priv; - - /* Find the PCI address that maps to the desired VME address */ - i =3D image->number; - - /* Locking as we can only do one of these at a time */ - mutex_lock(&bridge->vme_rmw); - - /* Lock image */ - spin_lock(&image->lock); - - pci_addr_high =3D ioread32be(bridge->base + TSI148_LCSR_OT[i] + - TSI148_LCSR_OFFSET_OTSAU); - pci_addr_low =3D ioread32be(bridge->base + TSI148_LCSR_OT[i] + - TSI148_LCSR_OFFSET_OTSAL); - - reg_join(pci_addr_high, pci_addr_low, &pci_addr); - reg_split(pci_addr + offset, &pci_addr_high, &pci_addr_low); - - /* Configure registers */ - iowrite32be(mask, bridge->base + TSI148_LCSR_RMWEN); - iowrite32be(compare, bridge->base + TSI148_LCSR_RMWC); - iowrite32be(swap, bridge->base + TSI148_LCSR_RMWS); - iowrite32be(pci_addr_high, bridge->base + TSI148_LCSR_RMWAU); - iowrite32be(pci_addr_low, bridge->base + TSI148_LCSR_RMWAL); - - /* Enable RMW */ - tmp =3D ioread32be(bridge->base + TSI148_LCSR_VMCTRL); - tmp |=3D TSI148_LCSR_VMCTRL_RMWEN; - iowrite32be(tmp, bridge->base + TSI148_LCSR_VMCTRL); - - /* Kick process off with a read to the required address. */ - result =3D ioread32be(image->kern_base + offset); - - /* Disable RMW */ - tmp =3D ioread32be(bridge->base + TSI148_LCSR_VMCTRL); - tmp &=3D ~TSI148_LCSR_VMCTRL_RMWEN; - iowrite32be(tmp, bridge->base + TSI148_LCSR_VMCTRL); - - spin_unlock(&image->lock); - - mutex_unlock(&bridge->vme_rmw); - - return result; -} - -static int tsi148_dma_set_vme_src_attributes(struct device *dev, __be32 *a= ttr, - u32 aspace, u32 cycle, u32 dwidth) -{ - u32 val; - - val =3D be32_to_cpu(*attr); - - /* Setup 2eSST speeds */ - switch (cycle & (VME_2eSST160 | VME_2eSST267 | VME_2eSST320)) { - case VME_2eSST160: - val |=3D TSI148_LCSR_DSAT_2eSSTM_160; - break; - case VME_2eSST267: - val |=3D TSI148_LCSR_DSAT_2eSSTM_267; - break; - case VME_2eSST320: - val |=3D TSI148_LCSR_DSAT_2eSSTM_320; - break; - } - - /* Setup cycle types */ - if (cycle & VME_SCT) - val |=3D TSI148_LCSR_DSAT_TM_SCT; - - if (cycle & VME_BLT) - val |=3D TSI148_LCSR_DSAT_TM_BLT; - - if (cycle & VME_MBLT) - val |=3D TSI148_LCSR_DSAT_TM_MBLT; - - if (cycle & VME_2eVME) - val |=3D TSI148_LCSR_DSAT_TM_2eVME; - - if (cycle & VME_2eSST) - val |=3D TSI148_LCSR_DSAT_TM_2eSST; - - if (cycle & VME_2eSSTB) { - dev_err(dev, "Currently not setting Broadcast Select Registers\n"); - val |=3D TSI148_LCSR_DSAT_TM_2eSSTB; - } - - /* Setup data width */ - switch (dwidth) { - case VME_D16: - val |=3D TSI148_LCSR_DSAT_DBW_16; - break; - case VME_D32: - val |=3D TSI148_LCSR_DSAT_DBW_32; - break; - default: - dev_err(dev, "Invalid data width\n"); - return -EINVAL; - } - - /* Setup address space */ - switch (aspace) { - case VME_A16: - val |=3D TSI148_LCSR_DSAT_AMODE_A16; - break; - case VME_A24: - val |=3D TSI148_LCSR_DSAT_AMODE_A24; - break; - case VME_A32: - val |=3D TSI148_LCSR_DSAT_AMODE_A32; - break; - case VME_A64: - val |=3D TSI148_LCSR_DSAT_AMODE_A64; - break; - case VME_CRCSR: - val |=3D TSI148_LCSR_DSAT_AMODE_CRCSR; - break; - case VME_USER1: - val |=3D TSI148_LCSR_DSAT_AMODE_USER1; - break; - case VME_USER2: - val |=3D TSI148_LCSR_DSAT_AMODE_USER2; - break; - case VME_USER3: - val |=3D TSI148_LCSR_DSAT_AMODE_USER3; - break; - case VME_USER4: - val |=3D TSI148_LCSR_DSAT_AMODE_USER4; - break; - default: - dev_err(dev, "Invalid address space\n"); - return -EINVAL; - } - - if (cycle & VME_SUPER) - val |=3D TSI148_LCSR_DSAT_SUP; - if (cycle & VME_PROG) - val |=3D TSI148_LCSR_DSAT_PGM; - - *attr =3D cpu_to_be32(val); - - return 0; -} - -static int tsi148_dma_set_vme_dest_attributes(struct device *dev, __be32 *= attr, - u32 aspace, u32 cycle, u32 dwidth) -{ - u32 val; - - val =3D be32_to_cpu(*attr); - - /* Setup 2eSST speeds */ - switch (cycle & (VME_2eSST160 | VME_2eSST267 | VME_2eSST320)) { - case VME_2eSST160: - val |=3D TSI148_LCSR_DDAT_2eSSTM_160; - break; - case VME_2eSST267: - val |=3D TSI148_LCSR_DDAT_2eSSTM_267; - break; - case VME_2eSST320: - val |=3D TSI148_LCSR_DDAT_2eSSTM_320; - break; - } - - /* Setup cycle types */ - if (cycle & VME_SCT) - val |=3D TSI148_LCSR_DDAT_TM_SCT; - - if (cycle & VME_BLT) - val |=3D TSI148_LCSR_DDAT_TM_BLT; - - if (cycle & VME_MBLT) - val |=3D TSI148_LCSR_DDAT_TM_MBLT; - - if (cycle & VME_2eVME) - val |=3D TSI148_LCSR_DDAT_TM_2eVME; - - if (cycle & VME_2eSST) - val |=3D TSI148_LCSR_DDAT_TM_2eSST; - - if (cycle & VME_2eSSTB) { - dev_err(dev, "Currently not setting Broadcast Select Registers\n"); - val |=3D TSI148_LCSR_DDAT_TM_2eSSTB; - } - - /* Setup data width */ - switch (dwidth) { - case VME_D16: - val |=3D TSI148_LCSR_DDAT_DBW_16; - break; - case VME_D32: - val |=3D TSI148_LCSR_DDAT_DBW_32; - break; - default: - dev_err(dev, "Invalid data width\n"); - return -EINVAL; - } - - /* Setup address space */ - switch (aspace) { - case VME_A16: - val |=3D TSI148_LCSR_DDAT_AMODE_A16; - break; - case VME_A24: - val |=3D TSI148_LCSR_DDAT_AMODE_A24; - break; - case VME_A32: - val |=3D TSI148_LCSR_DDAT_AMODE_A32; - break; - case VME_A64: - val |=3D TSI148_LCSR_DDAT_AMODE_A64; - break; - case VME_CRCSR: - val |=3D TSI148_LCSR_DDAT_AMODE_CRCSR; - break; - case VME_USER1: - val |=3D TSI148_LCSR_DDAT_AMODE_USER1; - break; - case VME_USER2: - val |=3D TSI148_LCSR_DDAT_AMODE_USER2; - break; - case VME_USER3: - val |=3D TSI148_LCSR_DDAT_AMODE_USER3; - break; - case VME_USER4: - val |=3D TSI148_LCSR_DDAT_AMODE_USER4; - break; - default: - dev_err(dev, "Invalid address space\n"); - return -EINVAL; - } - - if (cycle & VME_SUPER) - val |=3D TSI148_LCSR_DDAT_SUP; - if (cycle & VME_PROG) - val |=3D TSI148_LCSR_DDAT_PGM; - - *attr =3D cpu_to_be32(val); - - return 0; -} - -/* - * Add a link list descriptor to the list - * - * Note: DMA engine expects the DMA descriptor to be big endian. - */ -static int tsi148_dma_list_add(struct vme_dma_list *list, struct vme_dma_a= ttr *src, - struct vme_dma_attr *dest, size_t count) -{ - struct tsi148_dma_entry *entry, *prev; - u32 address_high, address_low, val; - struct vme_dma_pattern *pattern_attr; - struct vme_dma_pci *pci_attr; - struct vme_dma_vme *vme_attr; - int retval =3D 0; - struct vme_bridge *tsi148_bridge; - - tsi148_bridge =3D list->parent->parent; - - /* Descriptor must be aligned on 64-bit boundaries */ - entry =3D kmalloc_obj(*entry); - if (!entry) { - retval =3D -ENOMEM; - goto err_mem; - } - - /* Test descriptor alignment */ - if ((unsigned long)&entry->descriptor & 0x7) { - dev_err(tsi148_bridge->parent, "Descriptor not aligned to 8 byte boundar= y as required: %p\n", - &entry->descriptor); - retval =3D -EINVAL; - goto err_align; - } - - /* Given we are going to fill out the structure, we probably don't - * need to zero it, but better safe than sorry for now. - */ - memset(&entry->descriptor, 0, sizeof(entry->descriptor)); - - /* Fill out source part */ - switch (src->type) { - case VME_DMA_PATTERN: - pattern_attr =3D src->private; - - entry->descriptor.dsal =3D cpu_to_be32(pattern_attr->pattern); - - val =3D TSI148_LCSR_DSAT_TYP_PAT; - - /* Default behaviour is 32 bit pattern */ - if (pattern_attr->type & VME_DMA_PATTERN_BYTE) - val |=3D TSI148_LCSR_DSAT_PSZ; - - /* It seems that the default behaviour is to increment */ - if ((pattern_attr->type & VME_DMA_PATTERN_INCREMENT) =3D=3D 0) - val |=3D TSI148_LCSR_DSAT_NIN; - entry->descriptor.dsat =3D cpu_to_be32(val); - break; - case VME_DMA_PCI: - pci_attr =3D src->private; - - reg_split((unsigned long long)pci_attr->address, &address_high, &address= _low); - entry->descriptor.dsau =3D cpu_to_be32(address_high); - entry->descriptor.dsal =3D cpu_to_be32(address_low); - entry->descriptor.dsat =3D cpu_to_be32(TSI148_LCSR_DSAT_TYP_PCI); - break; - case VME_DMA_VME: - vme_attr =3D src->private; - - reg_split((unsigned long long)vme_attr->address, &address_high, &address= _low); - entry->descriptor.dsau =3D cpu_to_be32(address_high); - entry->descriptor.dsal =3D cpu_to_be32(address_low); - entry->descriptor.dsat =3D cpu_to_be32(TSI148_LCSR_DSAT_TYP_VME); - - retval =3D tsi148_dma_set_vme_src_attributes(tsi148_bridge->parent, - &entry->descriptor.dsat, - vme_attr->aspace, - vme_attr->cycle, - vme_attr->dwidth); - if (retval < 0) - goto err_source; - break; - default: - dev_err(tsi148_bridge->parent, "Invalid source type\n"); - retval =3D -EINVAL; - goto err_source; - } - - /* Assume last link - this will be over-written by adding another */ - entry->descriptor.dnlau =3D cpu_to_be32(0); - entry->descriptor.dnlal =3D cpu_to_be32(TSI148_LCSR_DNLAL_LLA); - - /* Fill out destination part */ - switch (dest->type) { - case VME_DMA_PCI: - pci_attr =3D dest->private; - - reg_split((unsigned long long)pci_attr->address, &address_high, - &address_low); - entry->descriptor.ddau =3D cpu_to_be32(address_high); - entry->descriptor.ddal =3D cpu_to_be32(address_low); - entry->descriptor.ddat =3D cpu_to_be32(TSI148_LCSR_DDAT_TYP_PCI); - break; - case VME_DMA_VME: - vme_attr =3D dest->private; - - reg_split((unsigned long long)vme_attr->address, &address_high, - &address_low); - entry->descriptor.ddau =3D cpu_to_be32(address_high); - entry->descriptor.ddal =3D cpu_to_be32(address_low); - entry->descriptor.ddat =3D cpu_to_be32(TSI148_LCSR_DDAT_TYP_VME); - - retval =3D tsi148_dma_set_vme_dest_attributes(tsi148_bridge->parent, - &entry->descriptor.ddat, - vme_attr->aspace, - vme_attr->cycle, - vme_attr->dwidth); - if (retval < 0) - goto err_dest; - break; - default: - dev_err(tsi148_bridge->parent, "Invalid destination type\n"); - retval =3D -EINVAL; - goto err_dest; - } - - /* Fill out count */ - entry->descriptor.dcnt =3D cpu_to_be32((u32)count); - - /* Add to list */ - list_add_tail(&entry->list, &list->entries); - - entry->dma_handle =3D dma_map_single(tsi148_bridge->parent, - &entry->descriptor, - sizeof(entry->descriptor), - DMA_TO_DEVICE); - if (dma_mapping_error(tsi148_bridge->parent, entry->dma_handle)) { - dev_err(tsi148_bridge->parent, "DMA mapping error\n"); - retval =3D -EINVAL; - goto err_dma; - } - - /* Fill out previous descriptors "Next Address" */ - if (entry->list.prev !=3D &list->entries) { - reg_split((unsigned long long)entry->dma_handle, &address_high, - &address_low); - prev =3D list_entry(entry->list.prev, struct tsi148_dma_entry, - list); - prev->descriptor.dnlau =3D cpu_to_be32(address_high); - prev->descriptor.dnlal =3D cpu_to_be32(address_low); - } - - return 0; - -err_dma: - list_del(&entry->list); -err_dest: -err_source: -err_align: - kfree(entry); -err_mem: - return retval; -} - -/* - * Check to see if the provided DMA channel is busy. - */ -static int tsi148_dma_busy(struct vme_bridge *tsi148_bridge, int channel) -{ - u32 tmp; - struct tsi148_driver *bridge; - - bridge =3D tsi148_bridge->driver_priv; - - tmp =3D ioread32be(bridge->base + TSI148_LCSR_DMA[channel] + - TSI148_LCSR_OFFSET_DSTA); - - if (tmp & TSI148_LCSR_DSTA_BSY) - return 0; - else - return 1; -} - -/* - * Execute a previously generated link list - * - * XXX Need to provide control register configuration. - */ -static int tsi148_dma_list_exec(struct vme_dma_list *list) -{ - struct vme_dma_resource *ctrlr; - int channel, retval; - struct tsi148_dma_entry *entry; - u32 bus_addr_high, bus_addr_low; - u32 val, dctlreg =3D 0; - struct vme_bridge *tsi148_bridge; - struct tsi148_driver *bridge; - - ctrlr =3D list->parent; - - tsi148_bridge =3D ctrlr->parent; - - bridge =3D tsi148_bridge->driver_priv; - - mutex_lock(&ctrlr->mtx); - - channel =3D ctrlr->number; - - if (!list_empty(&ctrlr->running)) { - /* - * XXX We have an active DMA transfer and currently haven't - * sorted out the mechanism for "pending" DMA transfers. - * Return busy. - */ - /* Need to add to pending here */ - mutex_unlock(&ctrlr->mtx); - return -EBUSY; - } - - list_add(&list->list, &ctrlr->running); - - /* Get first bus address and write into registers */ - entry =3D list_first_entry(&list->entries, struct tsi148_dma_entry, - list); - - mutex_unlock(&ctrlr->mtx); - - reg_split(entry->dma_handle, &bus_addr_high, &bus_addr_low); - - iowrite32be(bus_addr_high, bridge->base + - TSI148_LCSR_DMA[channel] + TSI148_LCSR_OFFSET_DNLAU); - iowrite32be(bus_addr_low, bridge->base + - TSI148_LCSR_DMA[channel] + TSI148_LCSR_OFFSET_DNLAL); - - dctlreg =3D ioread32be(bridge->base + TSI148_LCSR_DMA[channel] + - TSI148_LCSR_OFFSET_DCTL); - - /* Start the operation */ - iowrite32be(dctlreg | TSI148_LCSR_DCTL_DGO, bridge->base + - TSI148_LCSR_DMA[channel] + TSI148_LCSR_OFFSET_DCTL); - - retval =3D wait_event_interruptible(bridge->dma_queue[channel], - tsi148_dma_busy(ctrlr->parent, channel)); - - if (retval) { - iowrite32be(dctlreg | TSI148_LCSR_DCTL_ABT, bridge->base + - TSI148_LCSR_DMA[channel] + TSI148_LCSR_OFFSET_DCTL); - /* Wait for the operation to abort */ - wait_event(bridge->dma_queue[channel], - tsi148_dma_busy(ctrlr->parent, channel)); - retval =3D -EINTR; - goto exit; - } - - /* - * Read status register, this register is valid until we kick off a - * new transfer. - */ - val =3D ioread32be(bridge->base + TSI148_LCSR_DMA[channel] + - TSI148_LCSR_OFFSET_DSTA); - - if (val & TSI148_LCSR_DSTA_VBE) { - dev_err(tsi148_bridge->parent, "DMA Error. DSTA=3D%08X\n", val); - retval =3D -EIO; - } - -exit: - /* Remove list from running list */ - mutex_lock(&ctrlr->mtx); - list_del(&list->list); - mutex_unlock(&ctrlr->mtx); - - return retval; -} - -/* - * Clean up a previously generated link list - * - * We have a separate function, don't assume that the chain can't be reuse= d. - */ -static int tsi148_dma_list_empty(struct vme_dma_list *list) -{ - struct list_head *pos, *temp; - struct tsi148_dma_entry *entry; - - struct vme_bridge *tsi148_bridge =3D list->parent->parent; - - /* detach and free each entry */ - list_for_each_safe(pos, temp, &list->entries) { - list_del(pos); - entry =3D list_entry(pos, struct tsi148_dma_entry, list); - - dma_unmap_single(tsi148_bridge->parent, entry->dma_handle, - sizeof(struct tsi148_dma_descriptor), DMA_TO_DEVICE); - kfree(entry); - } - - return 0; -} - -/* - * All 4 location monitors reside at the same base - this is therefore a - * system wide configuration. - * - * This does not enable the LM monitor - that should be done when the first - * callback is attached and disabled when the last callback is removed. - */ -static int tsi148_lm_set(struct vme_lm_resource *lm, unsigned long long lm= _base, - u32 aspace, u32 cycle) -{ - u32 lm_base_high, lm_base_low, lm_ctl =3D 0; - int i; - struct vme_bridge *tsi148_bridge; - struct tsi148_driver *bridge; - - tsi148_bridge =3D lm->parent; - - bridge =3D tsi148_bridge->driver_priv; - - mutex_lock(&lm->mtx); - - /* If we already have a callback attached, we can't move it! */ - for (i =3D 0; i < lm->monitors; i++) { - if (bridge->lm_callback[i]) { - mutex_unlock(&lm->mtx); - dev_err(tsi148_bridge->parent, "Location monitor callback attached, can= 't reset\n"); - return -EBUSY; - } - } - - switch (aspace) { - case VME_A16: - lm_ctl |=3D TSI148_LCSR_LMAT_AS_A16; - break; - case VME_A24: - lm_ctl |=3D TSI148_LCSR_LMAT_AS_A24; - break; - case VME_A32: - lm_ctl |=3D TSI148_LCSR_LMAT_AS_A32; - break; - case VME_A64: - lm_ctl |=3D TSI148_LCSR_LMAT_AS_A64; - break; - default: - mutex_unlock(&lm->mtx); - dev_err(tsi148_bridge->parent, "Invalid address space\n"); - return -EINVAL; - } - - if (cycle & VME_SUPER) - lm_ctl |=3D TSI148_LCSR_LMAT_SUPR; - if (cycle & VME_USER) - lm_ctl |=3D TSI148_LCSR_LMAT_NPRIV; - if (cycle & VME_PROG) - lm_ctl |=3D TSI148_LCSR_LMAT_PGM; - if (cycle & VME_DATA) - lm_ctl |=3D TSI148_LCSR_LMAT_DATA; - - reg_split(lm_base, &lm_base_high, &lm_base_low); - - iowrite32be(lm_base_high, bridge->base + TSI148_LCSR_LMBAU); - iowrite32be(lm_base_low, bridge->base + TSI148_LCSR_LMBAL); - iowrite32be(lm_ctl, bridge->base + TSI148_LCSR_LMAT); - - mutex_unlock(&lm->mtx); - - return 0; -} - -/* Get configuration of the callback monitor and return whether it is enab= led - * or disabled. - */ -static int tsi148_lm_get(struct vme_lm_resource *lm, - unsigned long long *lm_base, u32 *aspace, u32 *cycle) -{ - u32 lm_base_high, lm_base_low, lm_ctl, enabled =3D 0; - struct tsi148_driver *bridge; - - bridge =3D lm->parent->driver_priv; - - mutex_lock(&lm->mtx); - - lm_base_high =3D ioread32be(bridge->base + TSI148_LCSR_LMBAU); - lm_base_low =3D ioread32be(bridge->base + TSI148_LCSR_LMBAL); - lm_ctl =3D ioread32be(bridge->base + TSI148_LCSR_LMAT); - - reg_join(lm_base_high, lm_base_low, lm_base); - - if (lm_ctl & TSI148_LCSR_LMAT_EN) - enabled =3D 1; - - if ((lm_ctl & TSI148_LCSR_LMAT_AS_M) =3D=3D TSI148_LCSR_LMAT_AS_A16) - *aspace |=3D VME_A16; - - if ((lm_ctl & TSI148_LCSR_LMAT_AS_M) =3D=3D TSI148_LCSR_LMAT_AS_A24) - *aspace |=3D VME_A24; - - if ((lm_ctl & TSI148_LCSR_LMAT_AS_M) =3D=3D TSI148_LCSR_LMAT_AS_A32) - *aspace |=3D VME_A32; - - if ((lm_ctl & TSI148_LCSR_LMAT_AS_M) =3D=3D TSI148_LCSR_LMAT_AS_A64) - *aspace |=3D VME_A64; - - if (lm_ctl & TSI148_LCSR_LMAT_SUPR) - *cycle |=3D VME_SUPER; - if (lm_ctl & TSI148_LCSR_LMAT_NPRIV) - *cycle |=3D VME_USER; - if (lm_ctl & TSI148_LCSR_LMAT_PGM) - *cycle |=3D VME_PROG; - if (lm_ctl & TSI148_LCSR_LMAT_DATA) - *cycle |=3D VME_DATA; - - mutex_unlock(&lm->mtx); - - return enabled; -} - -/* - * Attach a callback to a specific location monitor. - * - * Callback will be passed the monitor triggered. - */ -static int tsi148_lm_attach(struct vme_lm_resource *lm, int monitor, - void (*callback)(void *), void *data) -{ - u32 lm_ctl, tmp; - struct vme_bridge *tsi148_bridge; - struct tsi148_driver *bridge; - - tsi148_bridge =3D lm->parent; - - bridge =3D tsi148_bridge->driver_priv; - - mutex_lock(&lm->mtx); - - /* Ensure that the location monitor is configured - need PGM or DATA */ - lm_ctl =3D ioread32be(bridge->base + TSI148_LCSR_LMAT); - if ((lm_ctl & (TSI148_LCSR_LMAT_PGM | TSI148_LCSR_LMAT_DATA)) =3D=3D 0) { - mutex_unlock(&lm->mtx); - dev_err(tsi148_bridge->parent, "Location monitor not properly configured= \n"); - return -EINVAL; - } - - /* Check that a callback isn't already attached */ - if (bridge->lm_callback[monitor]) { - mutex_unlock(&lm->mtx); - dev_err(tsi148_bridge->parent, "Existing callback attached\n"); - return -EBUSY; - } - - /* Attach callback */ - bridge->lm_callback[monitor] =3D callback; - bridge->lm_data[monitor] =3D data; - - /* Enable Location Monitor interrupt */ - tmp =3D ioread32be(bridge->base + TSI148_LCSR_INTEN); - tmp |=3D TSI148_LCSR_INTEN_LMEN[monitor]; - iowrite32be(tmp, bridge->base + TSI148_LCSR_INTEN); - - tmp =3D ioread32be(bridge->base + TSI148_LCSR_INTEO); - tmp |=3D TSI148_LCSR_INTEO_LMEO[monitor]; - iowrite32be(tmp, bridge->base + TSI148_LCSR_INTEO); - - /* Ensure that global Location Monitor Enable set */ - if ((lm_ctl & TSI148_LCSR_LMAT_EN) =3D=3D 0) { - lm_ctl |=3D TSI148_LCSR_LMAT_EN; - iowrite32be(lm_ctl, bridge->base + TSI148_LCSR_LMAT); - } - - mutex_unlock(&lm->mtx); - - return 0; -} - -/* - * Detach a callback function forn a specific location monitor. - */ -static int tsi148_lm_detach(struct vme_lm_resource *lm, int monitor) -{ - u32 lm_en, tmp; - struct tsi148_driver *bridge; - - bridge =3D lm->parent->driver_priv; - - mutex_lock(&lm->mtx); - - /* Disable Location Monitor and ensure previous interrupts are clear */ - lm_en =3D ioread32be(bridge->base + TSI148_LCSR_INTEN); - lm_en &=3D ~TSI148_LCSR_INTEN_LMEN[monitor]; - iowrite32be(lm_en, bridge->base + TSI148_LCSR_INTEN); - - tmp =3D ioread32be(bridge->base + TSI148_LCSR_INTEO); - tmp &=3D ~TSI148_LCSR_INTEO_LMEO[monitor]; - iowrite32be(tmp, bridge->base + TSI148_LCSR_INTEO); - - iowrite32be(TSI148_LCSR_INTC_LMC[monitor], - bridge->base + TSI148_LCSR_INTC); - - /* Detach callback */ - bridge->lm_callback[monitor] =3D NULL; - bridge->lm_data[monitor] =3D NULL; - - /* If all location monitors disabled, disable global Location Monitor */ - if ((lm_en & (TSI148_LCSR_INTS_LM0S | TSI148_LCSR_INTS_LM1S | - TSI148_LCSR_INTS_LM2S | TSI148_LCSR_INTS_LM3S)) =3D=3D 0) { - tmp =3D ioread32be(bridge->base + TSI148_LCSR_LMAT); - tmp &=3D ~TSI148_LCSR_LMAT_EN; - iowrite32be(tmp, bridge->base + TSI148_LCSR_LMAT); - } - - mutex_unlock(&lm->mtx); - - return 0; -} - -/* - * Determine Geographical Addressing - */ -static int tsi148_slot_get(struct vme_bridge *tsi148_bridge) -{ - u32 slot =3D 0; - struct tsi148_driver *bridge; - - bridge =3D tsi148_bridge->driver_priv; - - if (!geoid) { - slot =3D ioread32be(bridge->base + TSI148_LCSR_VSTAT); - slot =3D slot & TSI148_LCSR_VSTAT_GA_M; - } else { - slot =3D geoid; - } - - return (int)slot; -} - -static void *tsi148_alloc_consistent(struct device *parent, size_t size, - dma_addr_t *dma) -{ - struct pci_dev *pdev; - - /* Find pci_dev container of dev */ - pdev =3D to_pci_dev(parent); - - return dma_alloc_coherent(&pdev->dev, size, dma, GFP_KERNEL); -} - -static void tsi148_free_consistent(struct device *parent, size_t size, - void *vaddr, dma_addr_t dma) -{ - struct pci_dev *pdev; - - /* Find pci_dev container of dev */ - pdev =3D to_pci_dev(parent); - - dma_free_coherent(&pdev->dev, size, vaddr, dma); -} - -/* - * Configure CR/CSR space - * - * Access to the CR/CSR can be configured at power-up. The location of the - * CR/CSR registers in the CR/CSR address space is determined by the boards - * Auto-ID or Geographic address. This function ensures that the window is - * enabled at an offset consistent with the boards geopgraphic address. - * - * Each board has a 512kB window, with the highest 4kB being used for the - * boards registers, this means there is a fix length 508kB window which m= ust - * be mapped onto PCI memory. - */ -static int tsi148_crcsr_init(struct vme_bridge *tsi148_bridge, - struct pci_dev *pdev) -{ - u32 cbar, crat, vstat; - u32 crcsr_bus_high, crcsr_bus_low; - int retval; - struct tsi148_driver *bridge; - - bridge =3D tsi148_bridge->driver_priv; - - /* Allocate mem for CR/CSR image */ - bridge->crcsr_kernel =3D dma_alloc_coherent(&pdev->dev, - VME_CRCSR_BUF_SIZE, - &bridge->crcsr_bus, GFP_KERNEL); - if (!bridge->crcsr_kernel) { - dev_err(tsi148_bridge->parent, "Failed to allocate memory for CR/CSR ima= ge\n"); - return -ENOMEM; - } - - reg_split(bridge->crcsr_bus, &crcsr_bus_high, &crcsr_bus_low); - - iowrite32be(crcsr_bus_high, bridge->base + TSI148_LCSR_CROU); - iowrite32be(crcsr_bus_low, bridge->base + TSI148_LCSR_CROL); - - /* Ensure that the CR/CSR is configured at the correct offset */ - cbar =3D ioread32be(bridge->base + TSI148_CBAR); - cbar =3D (cbar & TSI148_CRCSR_CBAR_M) >> 3; - - vstat =3D tsi148_slot_get(tsi148_bridge); - - if (cbar !=3D vstat) { - cbar =3D vstat; - dev_info(tsi148_bridge->parent, "Setting CR/CSR offset\n"); - iowrite32be(cbar << 3, bridge->base + TSI148_CBAR); - } - dev_info(tsi148_bridge->parent, "CR/CSR Offset: %d\n", cbar); - - crat =3D ioread32be(bridge->base + TSI148_LCSR_CRAT); - if (crat & TSI148_LCSR_CRAT_EN) { - dev_info(tsi148_bridge->parent, "CR/CSR already enabled\n"); - } else { - dev_info(tsi148_bridge->parent, "Enabling CR/CSR space\n"); - iowrite32be(crat | TSI148_LCSR_CRAT_EN, bridge->base + TSI148_LCSR_CRAT); - } - - /* If we want flushed, error-checked writes, set up a window - * over the CR/CSR registers. We read from here to safely flush - * through VME writes. - */ - if (err_chk) { - retval =3D tsi148_master_set(bridge->flush_image, 1, (vstat * 0x80000), - 0x80000, VME_CRCSR, VME_SCT, VME_D16); - if (retval) - dev_err(tsi148_bridge->parent, "Configuring flush image failed\n"); - } - - return 0; -} - -static void tsi148_crcsr_exit(struct vme_bridge *tsi148_bridge, - struct pci_dev *pdev) -{ - u32 crat; - struct tsi148_driver *bridge; - - bridge =3D tsi148_bridge->driver_priv; - - /* Turn off CR/CSR space */ - crat =3D ioread32be(bridge->base + TSI148_LCSR_CRAT); - iowrite32be(crat & ~TSI148_LCSR_CRAT_EN, - bridge->base + TSI148_LCSR_CRAT); - - /* Free image */ - iowrite32be(0, bridge->base + TSI148_LCSR_CROU); - iowrite32be(0, bridge->base + TSI148_LCSR_CROL); - - dma_free_coherent(&pdev->dev, VME_CRCSR_BUF_SIZE, - bridge->crcsr_kernel, bridge->crcsr_bus); -} - -static int tsi148_probe(struct pci_dev *pdev, const struct pci_device_id *= id) -{ - int retval, i, master_num; - u32 data; - struct list_head *pos =3D NULL, *n; - struct vme_bridge *tsi148_bridge; - struct tsi148_driver *tsi148_device; - struct vme_master_resource *master_image; - struct vme_slave_resource *slave_image; - struct vme_dma_resource *dma_ctrlr; - struct vme_lm_resource *lm; - - if (geoid >=3D VME_MAX_SLOTS) { - dev_err(&pdev->dev, "VME geographical address must be between 0 and %d (= exclusive), but got %d\n", - VME_MAX_SLOTS, geoid); - return -EINVAL; - } - - /* If we want to support more than one of each bridge, we need to - * dynamically generate this so we get one per device - */ - tsi148_bridge =3D kzalloc_obj(*tsi148_bridge); - if (!tsi148_bridge) { - retval =3D -ENOMEM; - goto err_struct; - } - vme_init_bridge(tsi148_bridge); - - tsi148_device =3D kzalloc_obj(*tsi148_device); - if (!tsi148_device) { - retval =3D -ENOMEM; - goto err_driver; - } - - tsi148_bridge->driver_priv =3D tsi148_device; - - /* Enable the device */ - retval =3D pci_enable_device(pdev); - if (retval) { - dev_err(&pdev->dev, "Unable to enable device\n"); - goto err_enable; - } - - /* Map Registers */ - retval =3D pci_request_regions(pdev, driver_name); - if (retval) { - dev_err(&pdev->dev, "Unable to reserve resources\n"); - goto err_resource; - } - - /* map registers in BAR 0 */ - tsi148_device->base =3D ioremap(pci_resource_start(pdev, 0), - 4096); - if (!tsi148_device->base) { - dev_err(&pdev->dev, "Unable to remap CRG region\n"); - retval =3D -EIO; - goto err_remap; - } - - /* Check to see if the mapping worked out */ - data =3D ioread32(tsi148_device->base + TSI148_PCFS_ID) & 0x0000FFFF; - if (data !=3D PCI_VENDOR_ID_TUNDRA) { - dev_err(&pdev->dev, "CRG region check failed\n"); - retval =3D -EIO; - goto err_test; - } - - /* Initialize wait queues & mutual exclusion flags */ - init_waitqueue_head(&tsi148_device->dma_queue[0]); - init_waitqueue_head(&tsi148_device->dma_queue[1]); - init_waitqueue_head(&tsi148_device->iack_queue); - mutex_init(&tsi148_device->vme_int); - mutex_init(&tsi148_device->vme_rmw); - - tsi148_bridge->parent =3D &pdev->dev; - strscpy(tsi148_bridge->name, driver_name, VMENAMSIZ); - - /* Setup IRQ */ - retval =3D tsi148_irq_init(tsi148_bridge); - if (retval !=3D 0) { - dev_err(&pdev->dev, "Chip Initialization failed.\n"); - goto err_irq; - } - - /* If we are going to flush writes, we need to read from the VME bus. - * We need to do this safely, thus we read the devices own CR/CSR - * register. To do this we must set up a window in CR/CSR space and - * hence have one less master window resource available. - */ - master_num =3D TSI148_MAX_MASTER; - if (err_chk) { - master_num--; - - tsi148_device->flush_image =3D kmalloc_obj(*tsi148_device->flush_image); - if (!tsi148_device->flush_image) { - retval =3D -ENOMEM; - goto err_master; - } - tsi148_device->flush_image->parent =3D tsi148_bridge; - spin_lock_init(&tsi148_device->flush_image->lock); - tsi148_device->flush_image->locked =3D 1; - tsi148_device->flush_image->number =3D master_num; - memset(&tsi148_device->flush_image->bus_resource, 0, - sizeof(tsi148_device->flush_image->bus_resource)); - tsi148_device->flush_image->kern_base =3D NULL; - } - - /* Add master windows to list */ - for (i =3D 0; i < master_num; i++) { - master_image =3D kmalloc_obj(*master_image); - if (!master_image) { - retval =3D -ENOMEM; - goto err_master; - } - master_image->parent =3D tsi148_bridge; - spin_lock_init(&master_image->lock); - master_image->locked =3D 0; - master_image->number =3D i; - master_image->address_attr =3D VME_A16 | VME_A24 | VME_A32 | - VME_A64 | VME_CRCSR | VME_USER1 | VME_USER2 | - VME_USER3 | VME_USER4; - master_image->cycle_attr =3D VME_SCT | VME_BLT | VME_MBLT | - VME_2eVME | VME_2eSST | VME_2eSSTB | VME_2eSST160 | - VME_2eSST267 | VME_2eSST320 | VME_SUPER | VME_USER | - VME_PROG | VME_DATA; - master_image->width_attr =3D VME_D16 | VME_D32; - memset(&master_image->bus_resource, 0, - sizeof(master_image->bus_resource)); - master_image->kern_base =3D NULL; - list_add_tail(&master_image->list, - &tsi148_bridge->master_resources); - } - - /* Add slave windows to list */ - for (i =3D 0; i < TSI148_MAX_SLAVE; i++) { - slave_image =3D kmalloc_obj(*slave_image); - if (!slave_image) { - retval =3D -ENOMEM; - goto err_slave; - } - slave_image->parent =3D tsi148_bridge; - mutex_init(&slave_image->mtx); - slave_image->locked =3D 0; - slave_image->number =3D i; - slave_image->address_attr =3D VME_A16 | VME_A24 | VME_A32 | - VME_A64; - slave_image->cycle_attr =3D VME_SCT | VME_BLT | VME_MBLT | - VME_2eVME | VME_2eSST | VME_2eSSTB | VME_2eSST160 | - VME_2eSST267 | VME_2eSST320 | VME_SUPER | VME_USER | - VME_PROG | VME_DATA; - list_add_tail(&slave_image->list, - &tsi148_bridge->slave_resources); - } - - /* Add dma engines to list */ - for (i =3D 0; i < TSI148_MAX_DMA; i++) { - dma_ctrlr =3D kmalloc_obj(*dma_ctrlr); - if (!dma_ctrlr) { - retval =3D -ENOMEM; - goto err_dma; - } - dma_ctrlr->parent =3D tsi148_bridge; - mutex_init(&dma_ctrlr->mtx); - dma_ctrlr->locked =3D 0; - dma_ctrlr->number =3D i; - dma_ctrlr->route_attr =3D VME_DMA_VME_TO_MEM | - VME_DMA_MEM_TO_VME | VME_DMA_VME_TO_VME | - VME_DMA_MEM_TO_MEM | VME_DMA_PATTERN_TO_VME | - VME_DMA_PATTERN_TO_MEM; - INIT_LIST_HEAD(&dma_ctrlr->pending); - INIT_LIST_HEAD(&dma_ctrlr->running); - list_add_tail(&dma_ctrlr->list, - &tsi148_bridge->dma_resources); - } - - /* Add location monitor to list */ - lm =3D kmalloc_obj(*lm); - if (!lm) { - retval =3D -ENOMEM; - goto err_lm; - } - lm->parent =3D tsi148_bridge; - mutex_init(&lm->mtx); - lm->locked =3D 0; - lm->number =3D 1; - lm->monitors =3D 4; - list_add_tail(&lm->list, &tsi148_bridge->lm_resources); - - tsi148_bridge->slave_get =3D tsi148_slave_get; - tsi148_bridge->slave_set =3D tsi148_slave_set; - tsi148_bridge->master_get =3D tsi148_master_get; - tsi148_bridge->master_set =3D tsi148_master_set; - tsi148_bridge->master_read =3D tsi148_master_read; - tsi148_bridge->master_write =3D tsi148_master_write; - tsi148_bridge->master_rmw =3D tsi148_master_rmw; - tsi148_bridge->dma_list_add =3D tsi148_dma_list_add; - tsi148_bridge->dma_list_exec =3D tsi148_dma_list_exec; - tsi148_bridge->dma_list_empty =3D tsi148_dma_list_empty; - tsi148_bridge->irq_set =3D tsi148_irq_set; - tsi148_bridge->irq_generate =3D tsi148_irq_generate; - tsi148_bridge->lm_set =3D tsi148_lm_set; - tsi148_bridge->lm_get =3D tsi148_lm_get; - tsi148_bridge->lm_attach =3D tsi148_lm_attach; - tsi148_bridge->lm_detach =3D tsi148_lm_detach; - tsi148_bridge->slot_get =3D tsi148_slot_get; - tsi148_bridge->alloc_consistent =3D tsi148_alloc_consistent; - tsi148_bridge->free_consistent =3D tsi148_free_consistent; - - data =3D ioread32be(tsi148_device->base + TSI148_LCSR_VSTAT); - dev_info(&pdev->dev, "Board is%s the VME system controller\n", - (data & TSI148_LCSR_VSTAT_SCONS) ? "" : " not"); - if (!geoid) - dev_info(&pdev->dev, "VME geographical address is %d\n", - data & TSI148_LCSR_VSTAT_GA_M); - else - dev_info(&pdev->dev, "VME geographical address is set to %d\n", - geoid); - - dev_info(&pdev->dev, "VME Write and flush and error check is %s\n", - err_chk ? "enabled" : "disabled"); - - retval =3D tsi148_crcsr_init(tsi148_bridge, pdev); - if (retval) { - dev_err(&pdev->dev, "CR/CSR configuration failed.\n"); - goto err_crcsr; - } - - retval =3D vme_register_bridge(tsi148_bridge); - if (retval !=3D 0) { - dev_err(&pdev->dev, "Chip Registration failed.\n"); - goto err_reg; - } - - pci_set_drvdata(pdev, tsi148_bridge); - - /* Clear VME bus "board fail", and "power-up reset" lines */ - data =3D ioread32be(tsi148_device->base + TSI148_LCSR_VSTAT); - data &=3D ~TSI148_LCSR_VSTAT_BRDFL; - data |=3D TSI148_LCSR_VSTAT_CPURST; - iowrite32be(data, tsi148_device->base + TSI148_LCSR_VSTAT); - - return 0; - -err_reg: - tsi148_crcsr_exit(tsi148_bridge, pdev); -err_crcsr: -err_lm: - /* resources are stored in link list */ - list_for_each_safe(pos, n, &tsi148_bridge->lm_resources) { - lm =3D list_entry(pos, struct vme_lm_resource, list); - list_del(pos); - kfree(lm); - } -err_dma: - /* resources are stored in link list */ - list_for_each_safe(pos, n, &tsi148_bridge->dma_resources) { - dma_ctrlr =3D list_entry(pos, struct vme_dma_resource, list); - list_del(pos); - kfree(dma_ctrlr); - } -err_slave: - /* resources are stored in link list */ - list_for_each_safe(pos, n, &tsi148_bridge->slave_resources) { - slave_image =3D list_entry(pos, struct vme_slave_resource, list); - list_del(pos); - kfree(slave_image); - } -err_master: - /* resources are stored in link list */ - list_for_each_safe(pos, n, &tsi148_bridge->master_resources) { - master_image =3D list_entry(pos, struct vme_master_resource, list); - list_del(pos); - kfree(master_image); - } - - tsi148_irq_exit(tsi148_bridge, pdev); -err_irq: -err_test: - iounmap(tsi148_device->base); -err_remap: - pci_release_regions(pdev); -err_resource: - pci_disable_device(pdev); -err_enable: - kfree(tsi148_device); -err_driver: - kfree(tsi148_bridge); -err_struct: - return retval; -} - -static void tsi148_remove(struct pci_dev *pdev) -{ - struct list_head *pos =3D NULL; - struct list_head *tmplist; - struct vme_master_resource *master_image; - struct vme_slave_resource *slave_image; - struct vme_dma_resource *dma_ctrlr; - int i; - struct tsi148_driver *bridge; - struct vme_bridge *tsi148_bridge =3D pci_get_drvdata(pdev); - - bridge =3D tsi148_bridge->driver_priv; - - dev_dbg(&pdev->dev, "Driver is being unloaded.\n"); - - /* - * Shutdown all inbound and outbound windows. - */ - for (i =3D 0; i < 8; i++) { - iowrite32be(0, bridge->base + TSI148_LCSR_IT[i] + - TSI148_LCSR_OFFSET_ITAT); - iowrite32be(0, bridge->base + TSI148_LCSR_OT[i] + - TSI148_LCSR_OFFSET_OTAT); - } - - /* - * Shutdown Location monitor. - */ - iowrite32be(0, bridge->base + TSI148_LCSR_LMAT); - - /* - * Shutdown CRG map. - */ - iowrite32be(0, bridge->base + TSI148_LCSR_CSRAT); - - /* - * Clear error status. - */ - iowrite32be(0xFFFFFFFF, bridge->base + TSI148_LCSR_EDPAT); - iowrite32be(0xFFFFFFFF, bridge->base + TSI148_LCSR_VEAT); - iowrite32be(0x07000700, bridge->base + TSI148_LCSR_PSTAT); - - /* - * Remove VIRQ interrupt (if any) - */ - if (ioread32be(bridge->base + TSI148_LCSR_VICR) & 0x800) - iowrite32be(0x8000, bridge->base + TSI148_LCSR_VICR); - - /* - * Map all Interrupts to PCI INTA - */ - iowrite32be(0x0, bridge->base + TSI148_LCSR_INTM1); - iowrite32be(0x0, bridge->base + TSI148_LCSR_INTM2); - - tsi148_irq_exit(tsi148_bridge, pdev); - - vme_unregister_bridge(tsi148_bridge); - - tsi148_crcsr_exit(tsi148_bridge, pdev); - - /* resources are stored in link list */ - list_for_each_safe(pos, tmplist, &tsi148_bridge->dma_resources) { - dma_ctrlr =3D list_entry(pos, struct vme_dma_resource, list); - list_del(pos); - kfree(dma_ctrlr); - } - - /* resources are stored in link list */ - list_for_each_safe(pos, tmplist, &tsi148_bridge->slave_resources) { - slave_image =3D list_entry(pos, struct vme_slave_resource, list); - list_del(pos); - kfree(slave_image); - } - - /* resources are stored in link list */ - list_for_each_safe(pos, tmplist, &tsi148_bridge->master_resources) { - master_image =3D list_entry(pos, struct vme_master_resource, list); - list_del(pos); - kfree(master_image); - } - - iounmap(bridge->base); - - pci_release_regions(pdev); - - pci_disable_device(pdev); - - kfree(tsi148_bridge->driver_priv); - - kfree(tsi148_bridge); -} - -module_pci_driver(tsi148_driver); - -MODULE_PARM_DESC(err_chk, "Check for VME errors on reads and writes"); -module_param(err_chk, bool, 0); - -MODULE_PARM_DESC(geoid, "Override geographical addressing"); -module_param(geoid, uint, 0); - -MODULE_DESCRIPTION("VME driver for the Tundra Tempe VME bridge"); -MODULE_LICENSE("GPL"); +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Support for the Tundra TSI148 VME-PCI Bridge Chip + * + * Author: Martyn Welch + * Copyright 2008 GE Intelligent Platforms Embedded Systems, Inc. + * + * Based on work by Tom Armistead and Ajit Prem + * Copyright 2004 Motorola Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "vme.h" +#include "vme_bridge.h" +#include "vme_tsi148.h" + +static int tsi148_probe(struct pci_dev *, const struct pci_device_id *); +static void tsi148_remove(struct pci_dev *); + +/* Module parameter */ +static bool err_chk; +static u32 geoid; + +static const char driver_name[] =3D "vme_tsi148"; + +static const struct pci_device_id tsi148_ids[] =3D { + { PCI_DEVICE(PCI_VENDOR_ID_TUNDRA, PCI_DEVICE_ID_TUNDRA_TSI148) }, + { }, +}; + +MODULE_DEVICE_TABLE(pci, tsi148_ids); + +static struct pci_driver tsi148_driver =3D { + .name =3D driver_name, + .id_table =3D tsi148_ids, + .probe =3D tsi148_probe, + .remove =3D tsi148_remove, +}; + +static void reg_join(unsigned int high, unsigned int low, + unsigned long long *variable) +{ + *variable =3D (unsigned long long)high << 32; + *variable |=3D (unsigned long long)low; +} + +static void reg_split(unsigned long long variable, unsigned int *high, + unsigned int *low) +{ + *low =3D (unsigned int)variable & 0xFFFFFFFF; + *high =3D (unsigned int)(variable >> 32); +} + +/* + * Wakes up DMA queue. + */ +static u32 tsi148_DMA_irqhandler(struct tsi148_driver *bridge, + int channel_mask) +{ + u32 serviced =3D 0; + + if (channel_mask & TSI148_LCSR_INTS_DMA0S) { + wake_up(&bridge->dma_queue[0]); + serviced |=3D TSI148_LCSR_INTC_DMA0C; + } + if (channel_mask & TSI148_LCSR_INTS_DMA1S) { + wake_up(&bridge->dma_queue[1]); + serviced |=3D TSI148_LCSR_INTC_DMA1C; + } + + return serviced; +} + +/* + * Wake up location monitor queue + */ +static u32 tsi148_LM_irqhandler(struct tsi148_driver *bridge, u32 stat) +{ + int i; + u32 serviced =3D 0; + + for (i =3D 0; i < 4; i++) { + if (stat & TSI148_LCSR_INTS_LMS[i]) { + /* We only enable interrupts if the callback is set */ + bridge->lm_callback[i](bridge->lm_data[i]); + serviced |=3D TSI148_LCSR_INTC_LMC[i]; + } + } + + return serviced; +} + +/* + * Wake up mail box queue. + * + * XXX This functionality is not exposed up though API. + */ +static u32 tsi148_MB_irqhandler(struct vme_bridge *tsi148_bridge, u32 stat) +{ + int i; + u32 val; + u32 serviced =3D 0; + struct tsi148_driver *bridge; + + bridge =3D tsi148_bridge->driver_priv; + + for (i =3D 0; i < 4; i++) { + if (stat & TSI148_LCSR_INTS_MBS[i]) { + val =3D ioread32be(bridge->base + TSI148_GCSR_MBOX[i]); + dev_err(tsi148_bridge->parent, "VME Mailbox %d received: 0x%x\n", + i, val); + serviced |=3D TSI148_LCSR_INTC_MBC[i]; + } + } + + return serviced; +} + +/* + * Display error & status message when PERR (PCI) exception interrupt occu= rs. + */ +static u32 tsi148_PERR_irqhandler(struct vme_bridge *tsi148_bridge) +{ + struct tsi148_driver *bridge; + + bridge =3D tsi148_bridge->driver_priv; + + dev_err(tsi148_bridge->parent, "PCI Exception at address: 0x%08x:%08x, at= tributes: %08x\n", + ioread32be(bridge->base + TSI148_LCSR_EDPAU), + ioread32be(bridge->base + TSI148_LCSR_EDPAL), + ioread32be(bridge->base + TSI148_LCSR_EDPAT)); + + dev_err(tsi148_bridge->parent, "PCI-X attribute reg: %08x, PCI-X split co= mpletion reg: %08x\n", + ioread32be(bridge->base + TSI148_LCSR_EDPXA), + ioread32be(bridge->base + TSI148_LCSR_EDPXS)); + + iowrite32be(TSI148_LCSR_EDPAT_EDPCL, bridge->base + TSI148_LCSR_EDPAT); + + return TSI148_LCSR_INTC_PERRC; +} + +/* + * Save address and status when VME error interrupt occurs. + */ +static u32 tsi148_VERR_irqhandler(struct vme_bridge *tsi148_bridge) +{ + unsigned int error_addr_high, error_addr_low; + unsigned long long error_addr; + u32 error_attrib; + int error_am; + struct tsi148_driver *bridge; + + bridge =3D tsi148_bridge->driver_priv; + + error_addr_high =3D ioread32be(bridge->base + TSI148_LCSR_VEAU); + error_addr_low =3D ioread32be(bridge->base + TSI148_LCSR_VEAL); + error_attrib =3D ioread32be(bridge->base + TSI148_LCSR_VEAT); + error_am =3D (error_attrib & TSI148_LCSR_VEAT_AM_M) >> 8; + + reg_join(error_addr_high, error_addr_low, &error_addr); + + /* Check for exception register overflow (we have lost error data) */ + if (error_attrib & TSI148_LCSR_VEAT_VEOF) + dev_err(tsi148_bridge->parent, "VME Bus Exception Overflow Occurred\n"); + + if (err_chk) + vme_bus_error_handler(tsi148_bridge, error_addr, error_am); + else + dev_err(tsi148_bridge->parent, + "VME Bus Error at address: 0x%llx, attributes: %08x\n", + error_addr, error_attrib); + + /* Clear Status */ + iowrite32be(TSI148_LCSR_VEAT_VESCL, bridge->base + TSI148_LCSR_VEAT); + + return TSI148_LCSR_INTC_VERRC; +} + +/* + * Wake up IACK queue. + */ +static u32 tsi148_IACK_irqhandler(struct tsi148_driver *bridge) +{ + wake_up(&bridge->iack_queue); + + return TSI148_LCSR_INTC_IACKC; +} + +/* + * Calling VME bus interrupt callback if provided. + */ +static u32 tsi148_VIRQ_irqhandler(struct vme_bridge *tsi148_bridge, + u32 stat) +{ + int vec, i, serviced =3D 0; + struct tsi148_driver *bridge; + + bridge =3D tsi148_bridge->driver_priv; + + for (i =3D 7; i > 0; i--) { + if (stat & (1 << i)) { + /* + * Note: Even though the registers are defined as + * 32-bits in the spec, we only want to issue 8-bit + * IACK cycles on the bus, read from offset 3. + */ + vec =3D ioread8(bridge->base + TSI148_LCSR_VIACK[i] + 3); + + vme_irq_handler(tsi148_bridge, i, vec); + + serviced |=3D (1 << i); + } + } + + return serviced; +} + +/* + * Top level interrupt handler. Clears appropriate interrupt status bits = and + * then calls appropriate sub handler(s). + */ +static irqreturn_t tsi148_irqhandler(int irq, void *ptr) +{ + u32 stat, enable, serviced =3D 0; + struct vme_bridge *tsi148_bridge; + struct tsi148_driver *bridge; + + tsi148_bridge =3D ptr; + + bridge =3D tsi148_bridge->driver_priv; + + /* Determine which interrupts are unmasked and set */ + enable =3D ioread32be(bridge->base + TSI148_LCSR_INTEO); + stat =3D ioread32be(bridge->base + TSI148_LCSR_INTS); + + /* Only look at unmasked interrupts */ + stat &=3D enable; + + if (unlikely(!stat)) + return IRQ_NONE; + + /* Call subhandlers as appropriate */ + /* DMA irqs */ + if (stat & (TSI148_LCSR_INTS_DMA1S | TSI148_LCSR_INTS_DMA0S)) + serviced |=3D tsi148_DMA_irqhandler(bridge, stat); + + /* Location monitor irqs */ + if (stat & (TSI148_LCSR_INTS_LM3S | TSI148_LCSR_INTS_LM2S | + TSI148_LCSR_INTS_LM1S | TSI148_LCSR_INTS_LM0S)) + serviced |=3D tsi148_LM_irqhandler(bridge, stat); + + /* Mail box irqs */ + if (stat & (TSI148_LCSR_INTS_MB3S | TSI148_LCSR_INTS_MB2S | + TSI148_LCSR_INTS_MB1S | TSI148_LCSR_INTS_MB0S)) + serviced |=3D tsi148_MB_irqhandler(tsi148_bridge, stat); + + /* PCI bus error */ + if (stat & TSI148_LCSR_INTS_PERRS) + serviced |=3D tsi148_PERR_irqhandler(tsi148_bridge); + + /* VME bus error */ + if (stat & TSI148_LCSR_INTS_VERRS) + serviced |=3D tsi148_VERR_irqhandler(tsi148_bridge); + + /* IACK irq */ + if (stat & TSI148_LCSR_INTS_IACKS) + serviced |=3D tsi148_IACK_irqhandler(bridge); + + /* VME bus irqs */ + if (stat & (TSI148_LCSR_INTS_IRQ7S | TSI148_LCSR_INTS_IRQ6S | + TSI148_LCSR_INTS_IRQ5S | TSI148_LCSR_INTS_IRQ4S | + TSI148_LCSR_INTS_IRQ3S | TSI148_LCSR_INTS_IRQ2S | + TSI148_LCSR_INTS_IRQ1S)) + serviced |=3D tsi148_VIRQ_irqhandler(tsi148_bridge, stat); + + /* Clear serviced interrupts */ + iowrite32be(serviced, bridge->base + TSI148_LCSR_INTC); + + return IRQ_HANDLED; +} + +static int tsi148_irq_init(struct vme_bridge *tsi148_bridge) +{ + int result; + unsigned int tmp; + struct pci_dev *pdev; + struct tsi148_driver *bridge; + + pdev =3D to_pci_dev(tsi148_bridge->parent); + + bridge =3D tsi148_bridge->driver_priv; + + result =3D request_irq(pdev->irq, + tsi148_irqhandler, + IRQF_SHARED, + driver_name, tsi148_bridge); + if (result) { + dev_err(tsi148_bridge->parent, "Can't get assigned pci irq vector %02X\n= ", + pdev->irq); + return result; + } + + /* Enable and unmask interrupts */ + tmp =3D TSI148_LCSR_INTEO_DMA1EO | TSI148_LCSR_INTEO_DMA0EO | + TSI148_LCSR_INTEO_MB3EO | TSI148_LCSR_INTEO_MB2EO | + TSI148_LCSR_INTEO_MB1EO | TSI148_LCSR_INTEO_MB0EO | + TSI148_LCSR_INTEO_PERREO | TSI148_LCSR_INTEO_VERREO | + TSI148_LCSR_INTEO_IACKEO; + + /* This leaves the following interrupts masked. + * TSI148_LCSR_INTEO_VIEEO + * TSI148_LCSR_INTEO_SYSFLEO + * TSI148_LCSR_INTEO_ACFLEO + */ + + /* Don't enable Location Monitor interrupts here - they will be + * enabled when the location monitors are properly configured and + * a callback has been attached. + * TSI148_LCSR_INTEO_LM0EO + * TSI148_LCSR_INTEO_LM1EO + * TSI148_LCSR_INTEO_LM2EO + * TSI148_LCSR_INTEO_LM3EO + */ + + /* Don't enable VME interrupts until we add a handler, else the board + * will respond to it and we don't want that unless it knows how to + * properly deal with it. + * TSI148_LCSR_INTEO_IRQ7EO + * TSI148_LCSR_INTEO_IRQ6EO + * TSI148_LCSR_INTEO_IRQ5EO + * TSI148_LCSR_INTEO_IRQ4EO + * TSI148_LCSR_INTEO_IRQ3EO + * TSI148_LCSR_INTEO_IRQ2EO + * TSI148_LCSR_INTEO_IRQ1EO + */ + + iowrite32be(tmp, bridge->base + TSI148_LCSR_INTEO); + iowrite32be(tmp, bridge->base + TSI148_LCSR_INTEN); + + return 0; +} + +static void tsi148_irq_exit(struct vme_bridge *tsi148_bridge, + struct pci_dev *pdev) +{ + struct tsi148_driver *bridge =3D tsi148_bridge->driver_priv; + + /* Turn off interrupts */ + iowrite32be(0x0, bridge->base + TSI148_LCSR_INTEO); + iowrite32be(0x0, bridge->base + TSI148_LCSR_INTEN); + + /* Clear all interrupts */ + iowrite32be(0xFFFFFFFF, bridge->base + TSI148_LCSR_INTC); + + /* Detach interrupt handler */ + free_irq(pdev->irq, tsi148_bridge); +} + +/* + * Check to see if an IACk has been received, return true (1) or false (0). + */ +static int tsi148_iack_received(struct tsi148_driver *bridge) +{ + u32 tmp; + + tmp =3D ioread32be(bridge->base + TSI148_LCSR_VICR); + + if (tmp & TSI148_LCSR_VICR_IRQS) + return 0; + else + return 1; +} + +/* + * Configure VME interrupt + */ +static void tsi148_irq_set(struct vme_bridge *tsi148_bridge, int level, + int state, int sync) +{ + struct pci_dev *pdev; + u32 tmp; + struct tsi148_driver *bridge; + + bridge =3D tsi148_bridge->driver_priv; + + /* We need to do the ordering differently for enabling and disabling */ + if (state =3D=3D 0) { + tmp =3D ioread32be(bridge->base + TSI148_LCSR_INTEN); + tmp &=3D ~TSI148_LCSR_INTEN_IRQEN[level - 1]; + iowrite32be(tmp, bridge->base + TSI148_LCSR_INTEN); + + tmp =3D ioread32be(bridge->base + TSI148_LCSR_INTEO); + tmp &=3D ~TSI148_LCSR_INTEO_IRQEO[level - 1]; + iowrite32be(tmp, bridge->base + TSI148_LCSR_INTEO); + + if (sync !=3D 0) { + pdev =3D to_pci_dev(tsi148_bridge->parent); + synchronize_irq(pdev->irq); + } + } else { + tmp =3D ioread32be(bridge->base + TSI148_LCSR_INTEO); + tmp |=3D TSI148_LCSR_INTEO_IRQEO[level - 1]; + iowrite32be(tmp, bridge->base + TSI148_LCSR_INTEO); + + tmp =3D ioread32be(bridge->base + TSI148_LCSR_INTEN); + tmp |=3D TSI148_LCSR_INTEN_IRQEN[level - 1]; + iowrite32be(tmp, bridge->base + TSI148_LCSR_INTEN); + } +} + +/* + * Generate a VME bus interrupt at the requested level & vector. Wait for + * interrupt to be acked. + */ +static int tsi148_irq_generate(struct vme_bridge *tsi148_bridge, int level, + int statid) +{ + u32 tmp; + struct tsi148_driver *bridge; + + bridge =3D tsi148_bridge->driver_priv; + + mutex_lock(&bridge->vme_int); + + /* Read VICR register */ + tmp =3D ioread32be(bridge->base + TSI148_LCSR_VICR); + + /* Set Status/ID */ + tmp =3D (tmp & ~TSI148_LCSR_VICR_STID_M) | + (statid & TSI148_LCSR_VICR_STID_M); + iowrite32be(tmp, bridge->base + TSI148_LCSR_VICR); + + /* Assert VMEbus IRQ */ + tmp =3D tmp | TSI148_LCSR_VICR_IRQL[level]; + iowrite32be(tmp, bridge->base + TSI148_LCSR_VICR); + + /* XXX Consider implementing a timeout? */ + wait_event_interruptible(bridge->iack_queue, + tsi148_iack_received(bridge)); + + mutex_unlock(&bridge->vme_int); + + return 0; +} + +/* + * Initialize a slave window with the requested attributes. + */ +static int tsi148_slave_set(struct vme_slave_resource *image, int enabled, + unsigned long long vme_base, unsigned long long size, + dma_addr_t pci_base, u32 aspace, u32 cycle) +{ + unsigned int i, addr =3D 0, granularity =3D 0; + unsigned int temp_ctl =3D 0; + unsigned int vme_base_low, vme_base_high; + unsigned int vme_bound_low, vme_bound_high; + unsigned int pci_offset_low, pci_offset_high; + unsigned long long vme_bound, pci_offset; + struct vme_bridge *tsi148_bridge; + struct tsi148_driver *bridge; + + tsi148_bridge =3D image->parent; + bridge =3D tsi148_bridge->driver_priv; + + i =3D image->number; + + switch (aspace) { + case VME_A16: + granularity =3D 0x10; + addr |=3D TSI148_LCSR_ITAT_AS_A16; + break; + case VME_A24: + granularity =3D 0x1000; + addr |=3D TSI148_LCSR_ITAT_AS_A24; + break; + case VME_A32: + granularity =3D 0x10000; + addr |=3D TSI148_LCSR_ITAT_AS_A32; + break; + case VME_A64: + granularity =3D 0x10000; + addr |=3D TSI148_LCSR_ITAT_AS_A64; + break; + default: + dev_err(tsi148_bridge->parent, "Invalid address space\n"); + return -EINVAL; + } + + /* Convert 64-bit variables to 2x 32-bit variables */ + reg_split(vme_base, &vme_base_high, &vme_base_low); + + /* + * Bound address is a valid address for the window, adjust + * accordingly + */ + vme_bound =3D vme_base + size - granularity; + reg_split(vme_bound, &vme_bound_high, &vme_bound_low); + pci_offset =3D (unsigned long long)pci_base - vme_base; + reg_split(pci_offset, &pci_offset_high, &pci_offset_low); + + if (vme_base_low & (granularity - 1)) { + dev_err(tsi148_bridge->parent, "Invalid VME base alignment\n"); + return -EINVAL; + } + if (vme_bound_low & (granularity - 1)) { + dev_err(tsi148_bridge->parent, "Invalid VME bound alignment\n"); + return -EINVAL; + } + if (pci_offset_low & (granularity - 1)) { + dev_err(tsi148_bridge->parent, "Invalid PCI Offset alignment\n"); + return -EINVAL; + } + + /* Disable while we are mucking around */ + temp_ctl =3D ioread32be(bridge->base + TSI148_LCSR_IT[i] + + TSI148_LCSR_OFFSET_ITAT); + temp_ctl &=3D ~TSI148_LCSR_ITAT_EN; + iowrite32be(temp_ctl, bridge->base + TSI148_LCSR_IT[i] + + TSI148_LCSR_OFFSET_ITAT); + + /* Setup mapping */ + iowrite32be(vme_base_high, bridge->base + TSI148_LCSR_IT[i] + + TSI148_LCSR_OFFSET_ITSAU); + iowrite32be(vme_base_low, bridge->base + TSI148_LCSR_IT[i] + + TSI148_LCSR_OFFSET_ITSAL); + iowrite32be(vme_bound_high, bridge->base + TSI148_LCSR_IT[i] + + TSI148_LCSR_OFFSET_ITEAU); + iowrite32be(vme_bound_low, bridge->base + TSI148_LCSR_IT[i] + + TSI148_LCSR_OFFSET_ITEAL); + iowrite32be(pci_offset_high, bridge->base + TSI148_LCSR_IT[i] + + TSI148_LCSR_OFFSET_ITOFU); + iowrite32be(pci_offset_low, bridge->base + TSI148_LCSR_IT[i] + + TSI148_LCSR_OFFSET_ITOFL); + + /* Setup 2eSST speeds */ + temp_ctl &=3D ~TSI148_LCSR_ITAT_2eSSTM_M; + switch (cycle & (VME_2eSST160 | VME_2eSST267 | VME_2eSST320)) { + case VME_2eSST160: + temp_ctl |=3D TSI148_LCSR_ITAT_2eSSTM_160; + break; + case VME_2eSST267: + temp_ctl |=3D TSI148_LCSR_ITAT_2eSSTM_267; + break; + case VME_2eSST320: + temp_ctl |=3D TSI148_LCSR_ITAT_2eSSTM_320; + break; + } + + /* Setup cycle types */ + temp_ctl &=3D ~(0x1F << 7); + if (cycle & VME_BLT) + temp_ctl |=3D TSI148_LCSR_ITAT_BLT; + if (cycle & VME_MBLT) + temp_ctl |=3D TSI148_LCSR_ITAT_MBLT; + if (cycle & VME_2eVME) + temp_ctl |=3D TSI148_LCSR_ITAT_2eVME; + if (cycle & VME_2eSST) + temp_ctl |=3D TSI148_LCSR_ITAT_2eSST; + if (cycle & VME_2eSSTB) + temp_ctl |=3D TSI148_LCSR_ITAT_2eSSTB; + + /* Setup address space */ + temp_ctl &=3D ~TSI148_LCSR_ITAT_AS_M; + temp_ctl |=3D addr; + + temp_ctl &=3D ~0xF; + if (cycle & VME_SUPER) + temp_ctl |=3D TSI148_LCSR_ITAT_SUPR; + if (cycle & VME_USER) + temp_ctl |=3D TSI148_LCSR_ITAT_NPRIV; + if (cycle & VME_PROG) + temp_ctl |=3D TSI148_LCSR_ITAT_PGM; + if (cycle & VME_DATA) + temp_ctl |=3D TSI148_LCSR_ITAT_DATA; + + /* Write ctl reg without enable */ + iowrite32be(temp_ctl, bridge->base + TSI148_LCSR_IT[i] + + TSI148_LCSR_OFFSET_ITAT); + + if (enabled) + temp_ctl |=3D TSI148_LCSR_ITAT_EN; + + iowrite32be(temp_ctl, bridge->base + TSI148_LCSR_IT[i] + + TSI148_LCSR_OFFSET_ITAT); + + return 0; +} + +/* + * Get slave window configuration. + */ +static int tsi148_slave_get(struct vme_slave_resource *image, int *enabled, + unsigned long long *vme_base, unsigned long long *size, + dma_addr_t *pci_base, u32 *aspace, u32 *cycle) +{ + unsigned int i, granularity =3D 0, ctl =3D 0; + unsigned int vme_base_low, vme_base_high; + unsigned int vme_bound_low, vme_bound_high; + unsigned int pci_offset_low, pci_offset_high; + unsigned long long vme_bound, pci_offset; + struct tsi148_driver *bridge; + + bridge =3D image->parent->driver_priv; + + i =3D image->number; + + /* Read registers */ + ctl =3D ioread32be(bridge->base + TSI148_LCSR_IT[i] + + TSI148_LCSR_OFFSET_ITAT); + + vme_base_high =3D ioread32be(bridge->base + TSI148_LCSR_IT[i] + + TSI148_LCSR_OFFSET_ITSAU); + vme_base_low =3D ioread32be(bridge->base + TSI148_LCSR_IT[i] + + TSI148_LCSR_OFFSET_ITSAL); + vme_bound_high =3D ioread32be(bridge->base + TSI148_LCSR_IT[i] + + TSI148_LCSR_OFFSET_ITEAU); + vme_bound_low =3D ioread32be(bridge->base + TSI148_LCSR_IT[i] + + TSI148_LCSR_OFFSET_ITEAL); + pci_offset_high =3D ioread32be(bridge->base + TSI148_LCSR_IT[i] + + TSI148_LCSR_OFFSET_ITOFU); + pci_offset_low =3D ioread32be(bridge->base + TSI148_LCSR_IT[i] + + TSI148_LCSR_OFFSET_ITOFL); + + /* Convert 64-bit variables to 2x 32-bit variables */ + reg_join(vme_base_high, vme_base_low, vme_base); + reg_join(vme_bound_high, vme_bound_low, &vme_bound); + reg_join(pci_offset_high, pci_offset_low, &pci_offset); + + *pci_base =3D (dma_addr_t)(*vme_base + pci_offset); + + *enabled =3D 0; + *aspace =3D 0; + *cycle =3D 0; + + if (ctl & TSI148_LCSR_ITAT_EN) + *enabled =3D 1; + + if ((ctl & TSI148_LCSR_ITAT_AS_M) =3D=3D TSI148_LCSR_ITAT_AS_A16) { + granularity =3D 0x10; + *aspace |=3D VME_A16; + } + if ((ctl & TSI148_LCSR_ITAT_AS_M) =3D=3D TSI148_LCSR_ITAT_AS_A24) { + granularity =3D 0x1000; + *aspace |=3D VME_A24; + } + if ((ctl & TSI148_LCSR_ITAT_AS_M) =3D=3D TSI148_LCSR_ITAT_AS_A32) { + granularity =3D 0x10000; + *aspace |=3D VME_A32; + } + if ((ctl & TSI148_LCSR_ITAT_AS_M) =3D=3D TSI148_LCSR_ITAT_AS_A64) { + granularity =3D 0x10000; + *aspace |=3D VME_A64; + } + + /* Need granularity before we set the size */ + *size =3D (unsigned long long)((vme_bound - *vme_base) + granularity); + + if ((ctl & TSI148_LCSR_ITAT_2eSSTM_M) =3D=3D TSI148_LCSR_ITAT_2eSSTM_160) + *cycle |=3D VME_2eSST160; + if ((ctl & TSI148_LCSR_ITAT_2eSSTM_M) =3D=3D TSI148_LCSR_ITAT_2eSSTM_267) + *cycle |=3D VME_2eSST267; + if ((ctl & TSI148_LCSR_ITAT_2eSSTM_M) =3D=3D TSI148_LCSR_ITAT_2eSSTM_320) + *cycle |=3D VME_2eSST320; + + if (ctl & TSI148_LCSR_ITAT_BLT) + *cycle |=3D VME_BLT; + if (ctl & TSI148_LCSR_ITAT_MBLT) + *cycle |=3D VME_MBLT; + if (ctl & TSI148_LCSR_ITAT_2eVME) + *cycle |=3D VME_2eVME; + if (ctl & TSI148_LCSR_ITAT_2eSST) + *cycle |=3D VME_2eSST; + if (ctl & TSI148_LCSR_ITAT_2eSSTB) + *cycle |=3D VME_2eSSTB; + + if (ctl & TSI148_LCSR_ITAT_SUPR) + *cycle |=3D VME_SUPER; + if (ctl & TSI148_LCSR_ITAT_NPRIV) + *cycle |=3D VME_USER; + if (ctl & TSI148_LCSR_ITAT_PGM) + *cycle |=3D VME_PROG; + if (ctl & TSI148_LCSR_ITAT_DATA) + *cycle |=3D VME_DATA; + + return 0; +} + +/* + * Allocate and map PCI Resource + */ +static int tsi148_alloc_resource(struct vme_master_resource *image, + unsigned long long size) +{ + unsigned long long existing_size; + int retval =3D 0; + struct pci_dev *pdev; + struct vme_bridge *tsi148_bridge; + + tsi148_bridge =3D image->parent; + + pdev =3D to_pci_dev(tsi148_bridge->parent); + + existing_size =3D (unsigned long long)(image->bus_resource.end - + image->bus_resource.start); + + /* If the existing size is OK, return */ + if ((size !=3D 0) && (existing_size =3D=3D (size - 1))) + return 0; + + if (existing_size !=3D 0) { + iounmap(image->kern_base); + image->kern_base =3D NULL; + kfree(image->bus_resource.name); + release_resource(&image->bus_resource); + memset(&image->bus_resource, 0, sizeof(image->bus_resource)); + } + + /* Exit here if size is zero */ + if (size =3D=3D 0) + return 0; + + if (!image->bus_resource.name) { + image->bus_resource.name =3D kmalloc(VMENAMSIZ + 3, GFP_ATOMIC); + if (!image->bus_resource.name) { + retval =3D -ENOMEM; + goto err_name; + } + } + + sprintf((char *)image->bus_resource.name, "%s.%d", tsi148_bridge->name, + image->number); + + image->bus_resource.start =3D 0; + image->bus_resource.end =3D (unsigned long)size; + image->bus_resource.flags =3D IORESOURCE_MEM; + + retval =3D pci_bus_alloc_resource(pdev->bus, &image->bus_resource, + size, 0x10000, PCIBIOS_MIN_MEM, + 0, NULL, NULL); + if (retval) { + dev_err(tsi148_bridge->parent, + "Failed to allocate mem resource for window %d size 0x%lx start 0x%lx\n= ", + image->number, (unsigned long)size, + (unsigned long)image->bus_resource.start); + goto err_resource; + } + + image->kern_base =3D ioremap(image->bus_resource.start, size); + if (!image->kern_base) { + dev_err(tsi148_bridge->parent, "Failed to remap resource\n"); + retval =3D -ENOMEM; + goto err_remap; + } + + return 0; + +err_remap: + release_resource(&image->bus_resource); +err_resource: + kfree(image->bus_resource.name); + memset(&image->bus_resource, 0, sizeof(image->bus_resource)); +err_name: + return retval; +} + +/* + * Free and unmap PCI Resource + */ +static void tsi148_free_resource(struct vme_master_resource *image) +{ + iounmap(image->kern_base); + image->kern_base =3D NULL; + release_resource(&image->bus_resource); + kfree(image->bus_resource.name); + memset(&image->bus_resource, 0, sizeof(image->bus_resource)); +} + +/* + * Set the attributes of an outbound window. + */ +static int tsi148_master_set(struct vme_master_resource *image, int enable= d, + unsigned long long vme_base, unsigned long long size, + u32 aspace, u32 cycle, u32 dwidth) +{ + int retval =3D 0; + unsigned int i; + unsigned int temp_ctl =3D 0; + unsigned int pci_base_low, pci_base_high; + unsigned int pci_bound_low, pci_bound_high; + unsigned int vme_offset_low, vme_offset_high; + unsigned long long pci_bound, vme_offset, pci_base; + struct vme_bridge *tsi148_bridge; + struct tsi148_driver *bridge; + struct pci_bus_region region; + struct pci_dev *pdev; + + tsi148_bridge =3D image->parent; + + bridge =3D tsi148_bridge->driver_priv; + + pdev =3D to_pci_dev(tsi148_bridge->parent); + + /* Verify input data */ + if (vme_base & 0xFFFF) { + dev_err(tsi148_bridge->parent, "Invalid VME Window alignment\n"); + retval =3D -EINVAL; + goto err_window; + } + + if ((size =3D=3D 0) && (enabled !=3D 0)) { + dev_err(tsi148_bridge->parent, "Size must be non-zero for enabled window= s\n"); + retval =3D -EINVAL; + goto err_window; + } + + spin_lock(&image->lock); + + /* Let's allocate the resource here rather than further up the stack as + * it avoids pushing loads of bus dependent stuff up the stack. If size + * is zero, any existing resource will be freed. + */ + retval =3D tsi148_alloc_resource(image, size); + if (retval) { + spin_unlock(&image->lock); + dev_err(tsi148_bridge->parent, "Unable to allocate memory for resource\n= "); + goto err_res; + } + + if (size =3D=3D 0) { + pci_base =3D 0; + pci_bound =3D 0; + vme_offset =3D 0; + } else { + pcibios_resource_to_bus(pdev->bus, ®ion, + &image->bus_resource); + pci_base =3D region.start; + + /* + * Bound address is a valid address for the window, adjust + * according to window granularity. + */ + pci_bound =3D pci_base + (size - 0x10000); + vme_offset =3D vme_base - pci_base; + } + + /* Convert 64-bit variables to 2x 32-bit variables */ + reg_split(pci_base, &pci_base_high, &pci_base_low); + reg_split(pci_bound, &pci_bound_high, &pci_bound_low); + reg_split(vme_offset, &vme_offset_high, &vme_offset_low); + + if (pci_base_low & 0xFFFF) { + spin_unlock(&image->lock); + dev_err(tsi148_bridge->parent, "Invalid PCI base alignment\n"); + retval =3D -EINVAL; + goto err_gran; + } + if (pci_bound_low & 0xFFFF) { + spin_unlock(&image->lock); + dev_err(tsi148_bridge->parent, "Invalid PCI bound alignment\n"); + retval =3D -EINVAL; + goto err_gran; + } + if (vme_offset_low & 0xFFFF) { + spin_unlock(&image->lock); + dev_err(tsi148_bridge->parent, "Invalid VME Offset alignment\n"); + retval =3D -EINVAL; + goto err_gran; + } + + i =3D image->number; + + /* Disable while we are mucking around */ + temp_ctl =3D ioread32be(bridge->base + TSI148_LCSR_OT[i] + + TSI148_LCSR_OFFSET_OTAT); + temp_ctl &=3D ~TSI148_LCSR_OTAT_EN; + iowrite32be(temp_ctl, bridge->base + TSI148_LCSR_OT[i] + + TSI148_LCSR_OFFSET_OTAT); + + /* Setup 2eSST speeds */ + temp_ctl &=3D ~TSI148_LCSR_OTAT_2eSSTM_M; + switch (cycle & (VME_2eSST160 | VME_2eSST267 | VME_2eSST320)) { + case VME_2eSST160: + temp_ctl |=3D TSI148_LCSR_OTAT_2eSSTM_160; + break; + case VME_2eSST267: + temp_ctl |=3D TSI148_LCSR_OTAT_2eSSTM_267; + break; + case VME_2eSST320: + temp_ctl |=3D TSI148_LCSR_OTAT_2eSSTM_320; + break; + } + + /* Setup cycle types */ + if (cycle & VME_BLT) { + temp_ctl &=3D ~TSI148_LCSR_OTAT_TM_M; + temp_ctl |=3D TSI148_LCSR_OTAT_TM_BLT; + } + if (cycle & VME_MBLT) { + temp_ctl &=3D ~TSI148_LCSR_OTAT_TM_M; + temp_ctl |=3D TSI148_LCSR_OTAT_TM_MBLT; + } + if (cycle & VME_2eVME) { + temp_ctl &=3D ~TSI148_LCSR_OTAT_TM_M; + temp_ctl |=3D TSI148_LCSR_OTAT_TM_2eVME; + } + if (cycle & VME_2eSST) { + temp_ctl &=3D ~TSI148_LCSR_OTAT_TM_M; + temp_ctl |=3D TSI148_LCSR_OTAT_TM_2eSST; + } + if (cycle & VME_2eSSTB) { + dev_warn(tsi148_bridge->parent, "Currently not setting Broadcast Select = Registers\n"); + temp_ctl &=3D ~TSI148_LCSR_OTAT_TM_M; + temp_ctl |=3D TSI148_LCSR_OTAT_TM_2eSSTB; + } + + /* Setup data width */ + temp_ctl &=3D ~TSI148_LCSR_OTAT_DBW_M; + switch (dwidth) { + case VME_D16: + temp_ctl |=3D TSI148_LCSR_OTAT_DBW_16; + break; + case VME_D32: + temp_ctl |=3D TSI148_LCSR_OTAT_DBW_32; + break; + default: + spin_unlock(&image->lock); + dev_err(tsi148_bridge->parent, "Invalid data width\n"); + retval =3D -EINVAL; + goto err_dwidth; + } + + /* Setup address space */ + temp_ctl &=3D ~TSI148_LCSR_OTAT_AMODE_M; + switch (aspace) { + case VME_A16: + temp_ctl |=3D TSI148_LCSR_OTAT_AMODE_A16; + break; + case VME_A24: + temp_ctl |=3D TSI148_LCSR_OTAT_AMODE_A24; + break; + case VME_A32: + temp_ctl |=3D TSI148_LCSR_OTAT_AMODE_A32; + break; + case VME_A64: + temp_ctl |=3D TSI148_LCSR_OTAT_AMODE_A64; + break; + case VME_CRCSR: + temp_ctl |=3D TSI148_LCSR_OTAT_AMODE_CRCSR; + break; + case VME_USER1: + temp_ctl |=3D TSI148_LCSR_OTAT_AMODE_USER1; + break; + case VME_USER2: + temp_ctl |=3D TSI148_LCSR_OTAT_AMODE_USER2; + break; + case VME_USER3: + temp_ctl |=3D TSI148_LCSR_OTAT_AMODE_USER3; + break; + case VME_USER4: + temp_ctl |=3D TSI148_LCSR_OTAT_AMODE_USER4; + break; + default: + spin_unlock(&image->lock); + dev_err(tsi148_bridge->parent, "Invalid address space\n"); + retval =3D -EINVAL; + goto err_aspace; + } + + temp_ctl &=3D ~(3 << 4); + if (cycle & VME_SUPER) + temp_ctl |=3D TSI148_LCSR_OTAT_SUP; + if (cycle & VME_PROG) + temp_ctl |=3D TSI148_LCSR_OTAT_PGM; + + /* Setup mapping */ + iowrite32be(pci_base_high, bridge->base + TSI148_LCSR_OT[i] + + TSI148_LCSR_OFFSET_OTSAU); + iowrite32be(pci_base_low, bridge->base + TSI148_LCSR_OT[i] + + TSI148_LCSR_OFFSET_OTSAL); + iowrite32be(pci_bound_high, bridge->base + TSI148_LCSR_OT[i] + + TSI148_LCSR_OFFSET_OTEAU); + iowrite32be(pci_bound_low, bridge->base + TSI148_LCSR_OT[i] + + TSI148_LCSR_OFFSET_OTEAL); + iowrite32be(vme_offset_high, bridge->base + TSI148_LCSR_OT[i] + + TSI148_LCSR_OFFSET_OTOFU); + iowrite32be(vme_offset_low, bridge->base + TSI148_LCSR_OT[i] + + TSI148_LCSR_OFFSET_OTOFL); + + /* Write ctl reg without enable */ + iowrite32be(temp_ctl, bridge->base + TSI148_LCSR_OT[i] + + TSI148_LCSR_OFFSET_OTAT); + + if (enabled) + temp_ctl |=3D TSI148_LCSR_OTAT_EN; + + iowrite32be(temp_ctl, bridge->base + TSI148_LCSR_OT[i] + + TSI148_LCSR_OFFSET_OTAT); + + spin_unlock(&image->lock); + return 0; + +err_aspace: +err_dwidth: +err_gran: + tsi148_free_resource(image); +err_res: +err_window: + return retval; +} + +/* + * Set the attributes of an outbound window. + * + * XXX Not parsing prefetch information. + */ +static int __tsi148_master_get(struct vme_master_resource *image, int *ena= bled, + unsigned long long *vme_base, unsigned long long *size, + u32 *aspace, u32 *cycle, u32 *dwidth) +{ + unsigned int i, ctl; + unsigned int pci_base_low, pci_base_high; + unsigned int pci_bound_low, pci_bound_high; + unsigned int vme_offset_low, vme_offset_high; + + unsigned long long pci_base, pci_bound, vme_offset; + struct tsi148_driver *bridge; + + bridge =3D image->parent->driver_priv; + + i =3D image->number; + + ctl =3D ioread32be(bridge->base + TSI148_LCSR_OT[i] + + TSI148_LCSR_OFFSET_OTAT); + + pci_base_high =3D ioread32be(bridge->base + TSI148_LCSR_OT[i] + + TSI148_LCSR_OFFSET_OTSAU); + pci_base_low =3D ioread32be(bridge->base + TSI148_LCSR_OT[i] + + TSI148_LCSR_OFFSET_OTSAL); + pci_bound_high =3D ioread32be(bridge->base + TSI148_LCSR_OT[i] + + TSI148_LCSR_OFFSET_OTEAU); + pci_bound_low =3D ioread32be(bridge->base + TSI148_LCSR_OT[i] + + TSI148_LCSR_OFFSET_OTEAL); + vme_offset_high =3D ioread32be(bridge->base + TSI148_LCSR_OT[i] + + TSI148_LCSR_OFFSET_OTOFU); + vme_offset_low =3D ioread32be(bridge->base + TSI148_LCSR_OT[i] + + TSI148_LCSR_OFFSET_OTOFL); + + /* Convert 64-bit variables to 2x 32-bit variables */ + reg_join(pci_base_high, pci_base_low, &pci_base); + reg_join(pci_bound_high, pci_bound_low, &pci_bound); + reg_join(vme_offset_high, vme_offset_low, &vme_offset); + + *vme_base =3D pci_base + vme_offset; + *size =3D (unsigned long long)(pci_bound - pci_base) + 0x10000; + + *enabled =3D 0; + *aspace =3D 0; + *cycle =3D 0; + *dwidth =3D 0; + + if (ctl & TSI148_LCSR_OTAT_EN) + *enabled =3D 1; + + /* Setup address space */ + if ((ctl & TSI148_LCSR_OTAT_AMODE_M) =3D=3D TSI148_LCSR_OTAT_AMODE_A16) + *aspace |=3D VME_A16; + if ((ctl & TSI148_LCSR_OTAT_AMODE_M) =3D=3D TSI148_LCSR_OTAT_AMODE_A24) + *aspace |=3D VME_A24; + if ((ctl & TSI148_LCSR_OTAT_AMODE_M) =3D=3D TSI148_LCSR_OTAT_AMODE_A32) + *aspace |=3D VME_A32; + if ((ctl & TSI148_LCSR_OTAT_AMODE_M) =3D=3D TSI148_LCSR_OTAT_AMODE_A64) + *aspace |=3D VME_A64; + if ((ctl & TSI148_LCSR_OTAT_AMODE_M) =3D=3D TSI148_LCSR_OTAT_AMODE_CRCSR) + *aspace |=3D VME_CRCSR; + if ((ctl & TSI148_LCSR_OTAT_AMODE_M) =3D=3D TSI148_LCSR_OTAT_AMODE_USER1) + *aspace |=3D VME_USER1; + if ((ctl & TSI148_LCSR_OTAT_AMODE_M) =3D=3D TSI148_LCSR_OTAT_AMODE_USER2) + *aspace |=3D VME_USER2; + if ((ctl & TSI148_LCSR_OTAT_AMODE_M) =3D=3D TSI148_LCSR_OTAT_AMODE_USER3) + *aspace |=3D VME_USER3; + if ((ctl & TSI148_LCSR_OTAT_AMODE_M) =3D=3D TSI148_LCSR_OTAT_AMODE_USER4) + *aspace |=3D VME_USER4; + + /* Setup 2eSST speeds */ + if ((ctl & TSI148_LCSR_OTAT_2eSSTM_M) =3D=3D TSI148_LCSR_OTAT_2eSSTM_160) + *cycle |=3D VME_2eSST160; + if ((ctl & TSI148_LCSR_OTAT_2eSSTM_M) =3D=3D TSI148_LCSR_OTAT_2eSSTM_267) + *cycle |=3D VME_2eSST267; + if ((ctl & TSI148_LCSR_OTAT_2eSSTM_M) =3D=3D TSI148_LCSR_OTAT_2eSSTM_320) + *cycle |=3D VME_2eSST320; + + /* Setup cycle types */ + if ((ctl & TSI148_LCSR_OTAT_TM_M) =3D=3D TSI148_LCSR_OTAT_TM_SCT) + *cycle |=3D VME_SCT; + if ((ctl & TSI148_LCSR_OTAT_TM_M) =3D=3D TSI148_LCSR_OTAT_TM_BLT) + *cycle |=3D VME_BLT; + if ((ctl & TSI148_LCSR_OTAT_TM_M) =3D=3D TSI148_LCSR_OTAT_TM_MBLT) + *cycle |=3D VME_MBLT; + if ((ctl & TSI148_LCSR_OTAT_TM_M) =3D=3D TSI148_LCSR_OTAT_TM_2eVME) + *cycle |=3D VME_2eVME; + if ((ctl & TSI148_LCSR_OTAT_TM_M) =3D=3D TSI148_LCSR_OTAT_TM_2eSST) + *cycle |=3D VME_2eSST; + if ((ctl & TSI148_LCSR_OTAT_TM_M) =3D=3D TSI148_LCSR_OTAT_TM_2eSSTB) + *cycle |=3D VME_2eSSTB; + + if (ctl & TSI148_LCSR_OTAT_SUP) + *cycle |=3D VME_SUPER; + else + *cycle |=3D VME_USER; + + if (ctl & TSI148_LCSR_OTAT_PGM) + *cycle |=3D VME_PROG; + else + *cycle |=3D VME_DATA; + + /* Setup data width */ + if ((ctl & TSI148_LCSR_OTAT_DBW_M) =3D=3D TSI148_LCSR_OTAT_DBW_16) + *dwidth =3D VME_D16; + if ((ctl & TSI148_LCSR_OTAT_DBW_M) =3D=3D TSI148_LCSR_OTAT_DBW_32) + *dwidth =3D VME_D32; + + return 0; +} + +static int tsi148_master_get(struct vme_master_resource *image, int *enabl= ed, + unsigned long long *vme_base, unsigned long long *size, + u32 *aspace, u32 *cycle, u32 *dwidth) +{ + int retval; + + spin_lock(&image->lock); + + retval =3D __tsi148_master_get(image, enabled, vme_base, size, aspace, + cycle, dwidth); + + spin_unlock(&image->lock); + + return retval; +} + +static ssize_t tsi148_master_read(struct vme_master_resource *image, void = *buf, + size_t count, loff_t offset) +{ + int retval, enabled; + unsigned long long vme_base, size; + u32 aspace, cycle, dwidth; + struct vme_error_handler *handler =3D NULL; + struct vme_bridge *tsi148_bridge; + void __iomem *addr =3D image->kern_base + offset; + unsigned int done =3D 0; + unsigned int count32; + + tsi148_bridge =3D image->parent; + + spin_lock(&image->lock); + + if (err_chk) { + __tsi148_master_get(image, &enabled, &vme_base, &size, &aspace, + &cycle, &dwidth); + handler =3D vme_register_error_handler(tsi148_bridge, aspace, + vme_base + offset, count); + if (!handler) { + spin_unlock(&image->lock); + return -ENOMEM; + } + } + + /* The following code handles VME address alignment. We cannot use + * memcpy_xxx here because it may cut data transfers in to 8-bit + * cycles when D16 or D32 cycles are required on the VME bus. + * On the other hand, the bridge itself assures that the maximum data + * cycle configured for the transfer is used and splits it + * automatically for non-aligned addresses, so we don't want the + * overhead of needlessly forcing small transfers for the entire cycle. + */ + if ((uintptr_t)addr & 0x1) { + *(u8 *)buf =3D ioread8(addr); + done +=3D 1; + if (done =3D=3D count) + goto out; + } + if ((uintptr_t)(addr + done) & 0x2) { + if ((count - done) < 2) { + *(u8 *)(buf + done) =3D ioread8(addr + done); + done +=3D 1; + goto out; + } else { + *(u16 *)(buf + done) =3D ioread16(addr + done); + done +=3D 2; + } + } + + count32 =3D (count - done) & ~0x3; + while (done < count32) { + *(u32 *)(buf + done) =3D ioread32(addr + done); + done +=3D 4; + } + + if ((count - done) & 0x2) { + *(u16 *)(buf + done) =3D ioread16(addr + done); + done +=3D 2; + } + if ((count - done) & 0x1) { + *(u8 *)(buf + done) =3D ioread8(addr + done); + done +=3D 1; + } + +out: + retval =3D count; + + if (err_chk) { + if (handler->num_errors) { + dev_err(image->parent->parent, + "First VME read error detected an at address 0x%llx\n", + handler->first_error); + retval =3D handler->first_error - (vme_base + offset); + } + vme_unregister_error_handler(handler); + } + + spin_unlock(&image->lock); + + return retval; +} + +static ssize_t tsi148_master_write(struct vme_master_resource *image, void= *buf, + size_t count, loff_t offset) +{ + int retval =3D 0, enabled; + unsigned long long vme_base, size; + u32 aspace, cycle, dwidth; + void __iomem *addr =3D image->kern_base + offset; + unsigned int done =3D 0; + unsigned int count32; + + struct vme_error_handler *handler =3D NULL; + struct vme_bridge *tsi148_bridge; + struct tsi148_driver *bridge; + + tsi148_bridge =3D image->parent; + + bridge =3D tsi148_bridge->driver_priv; + + spin_lock(&image->lock); + + if (err_chk) { + __tsi148_master_get(image, &enabled, &vme_base, &size, &aspace, + &cycle, &dwidth); + handler =3D vme_register_error_handler(tsi148_bridge, aspace, + vme_base + offset, count); + if (!handler) { + spin_unlock(&image->lock); + return -ENOMEM; + } + } + + /* Here we apply for the same strategy we do in master_read + * function in order to assure the correct cycles. + */ + if ((uintptr_t)addr & 0x1) { + iowrite8(*(u8 *)buf, addr); + done +=3D 1; + if (done =3D=3D count) + goto out; + } + if ((uintptr_t)(addr + done) & 0x2) { + if ((count - done) < 2) { + iowrite8(*(u8 *)(buf + done), addr + done); + done +=3D 1; + goto out; + } else { + iowrite16(*(u16 *)(buf + done), addr + done); + done +=3D 2; + } + } + + count32 =3D (count - done) & ~0x3; + while (done < count32) { + iowrite32(*(u32 *)(buf + done), addr + done); + done +=3D 4; + } + + if ((count - done) & 0x2) { + iowrite16(*(u16 *)(buf + done), addr + done); + done +=3D 2; + } + if ((count - done) & 0x1) { + iowrite8(*(u8 *)(buf + done), addr + done); + done +=3D 1; + } + +out: + retval =3D count; + + /* + * Writes are posted. We need to do a read on the VME bus to flush out + * all of the writes before we check for errors. We can't guarantee + * that reading the data we have just written is safe. It is believed + * that there isn't any read, write re-ordering, so we can read any + * location in VME space, so lets read the Device ID from the tsi148's + * own registers as mapped into CR/CSR space. + * + * We check for saved errors in the written address range/space. + */ + + if (err_chk) { + ioread16(bridge->flush_image->kern_base + 0x7F000); + + if (handler->num_errors) { + dev_warn(tsi148_bridge->parent, + "First VME write error detected an at address 0x%llx\n", + handler->first_error); + retval =3D handler->first_error - (vme_base + offset); + } + vme_unregister_error_handler(handler); + } + + spin_unlock(&image->lock); + + return retval; +} + +/* + * Perform an RMW cycle on the VME bus. + * + * Requires a previously configured master window, returns final value. + */ +static unsigned int tsi148_master_rmw(struct vme_master_resource *image, u= nsigned int mask, + unsigned int compare, unsigned int swap, loff_t offset) +{ + unsigned long long pci_addr; + unsigned int pci_addr_high, pci_addr_low; + u32 tmp, result; + int i; + struct tsi148_driver *bridge; + + bridge =3D image->parent->driver_priv; + + /* Find the PCI address that maps to the desired VME address */ + i =3D image->number; + + /* Locking as we can only do one of these at a time */ + mutex_lock(&bridge->vme_rmw); + + /* Lock image */ + spin_lock(&image->lock); + + pci_addr_high =3D ioread32be(bridge->base + TSI148_LCSR_OT[i] + + TSI148_LCSR_OFFSET_OTSAU); + pci_addr_low =3D ioread32be(bridge->base + TSI148_LCSR_OT[i] + + TSI148_LCSR_OFFSET_OTSAL); + + reg_join(pci_addr_high, pci_addr_low, &pci_addr); + reg_split(pci_addr + offset, &pci_addr_high, &pci_addr_low); + + /* Configure registers */ + iowrite32be(mask, bridge->base + TSI148_LCSR_RMWEN); + iowrite32be(compare, bridge->base + TSI148_LCSR_RMWC); + iowrite32be(swap, bridge->base + TSI148_LCSR_RMWS); + iowrite32be(pci_addr_high, bridge->base + TSI148_LCSR_RMWAU); + iowrite32be(pci_addr_low, bridge->base + TSI148_LCSR_RMWAL); + + /* Enable RMW */ + tmp =3D ioread32be(bridge->base + TSI148_LCSR_VMCTRL); + tmp |=3D TSI148_LCSR_VMCTRL_RMWEN; + iowrite32be(tmp, bridge->base + TSI148_LCSR_VMCTRL); + + /* Kick process off with a read to the required address. */ + result =3D ioread32be(image->kern_base + offset); + + /* Disable RMW */ + tmp =3D ioread32be(bridge->base + TSI148_LCSR_VMCTRL); + tmp &=3D ~TSI148_LCSR_VMCTRL_RMWEN; + iowrite32be(tmp, bridge->base + TSI148_LCSR_VMCTRL); + + spin_unlock(&image->lock); + + mutex_unlock(&bridge->vme_rmw); + + return result; +} + +static int tsi148_dma_set_vme_src_attributes(struct device *dev, __be32 *a= ttr, + u32 aspace, u32 cycle, u32 dwidth) +{ + u32 val; + + val =3D be32_to_cpu(*attr); + + /* Setup 2eSST speeds */ + switch (cycle & (VME_2eSST160 | VME_2eSST267 | VME_2eSST320)) { + case VME_2eSST160: + val |=3D TSI148_LCSR_DSAT_2eSSTM_160; + break; + case VME_2eSST267: + val |=3D TSI148_LCSR_DSAT_2eSSTM_267; + break; + case VME_2eSST320: + val |=3D TSI148_LCSR_DSAT_2eSSTM_320; + break; + } + + /* Setup cycle types */ + if (cycle & VME_SCT) + val |=3D TSI148_LCSR_DSAT_TM_SCT; + + if (cycle & VME_BLT) + val |=3D TSI148_LCSR_DSAT_TM_BLT; + + if (cycle & VME_MBLT) + val |=3D TSI148_LCSR_DSAT_TM_MBLT; + + if (cycle & VME_2eVME) + val |=3D TSI148_LCSR_DSAT_TM_2eVME; + + if (cycle & VME_2eSST) + val |=3D TSI148_LCSR_DSAT_TM_2eSST; + + if (cycle & VME_2eSSTB) { + dev_err(dev, "Currently not setting Broadcast Select Registers\n"); + val |=3D TSI148_LCSR_DSAT_TM_2eSSTB; + } + + /* Setup data width */ + switch (dwidth) { + case VME_D16: + val |=3D TSI148_LCSR_DSAT_DBW_16; + break; + case VME_D32: + val |=3D TSI148_LCSR_DSAT_DBW_32; + break; + default: + dev_err(dev, "Invalid data width\n"); + return -EINVAL; + } + + /* Setup address space */ + switch (aspace) { + case VME_A16: + val |=3D TSI148_LCSR_DSAT_AMODE_A16; + break; + case VME_A24: + val |=3D TSI148_LCSR_DSAT_AMODE_A24; + break; + case VME_A32: + val |=3D TSI148_LCSR_DSAT_AMODE_A32; + break; + case VME_A64: + val |=3D TSI148_LCSR_DSAT_AMODE_A64; + break; + case VME_CRCSR: + val |=3D TSI148_LCSR_DSAT_AMODE_CRCSR; + break; + case VME_USER1: + val |=3D TSI148_LCSR_DSAT_AMODE_USER1; + break; + case VME_USER2: + val |=3D TSI148_LCSR_DSAT_AMODE_USER2; + break; + case VME_USER3: + val |=3D TSI148_LCSR_DSAT_AMODE_USER3; + break; + case VME_USER4: + val |=3D TSI148_LCSR_DSAT_AMODE_USER4; + break; + default: + dev_err(dev, "Invalid address space\n"); + return -EINVAL; + } + + if (cycle & VME_SUPER) + val |=3D TSI148_LCSR_DSAT_SUP; + if (cycle & VME_PROG) + val |=3D TSI148_LCSR_DSAT_PGM; + + *attr =3D cpu_to_be32(val); + + return 0; +} + +static int tsi148_dma_set_vme_dest_attributes(struct device *dev, __be32 *= attr, + u32 aspace, u32 cycle, u32 dwidth) +{ + u32 val; + + val =3D be32_to_cpu(*attr); + + /* Setup 2eSST speeds */ + switch (cycle & (VME_2eSST160 | VME_2eSST267 | VME_2eSST320)) { + case VME_2eSST160: + val |=3D TSI148_LCSR_DDAT_2eSSTM_160; + break; + case VME_2eSST267: + val |=3D TSI148_LCSR_DDAT_2eSSTM_267; + break; + case VME_2eSST320: + val |=3D TSI148_LCSR_DDAT_2eSSTM_320; + break; + } + + /* Setup cycle types */ + if (cycle & VME_SCT) + val |=3D TSI148_LCSR_DDAT_TM_SCT; + + if (cycle & VME_BLT) + val |=3D TSI148_LCSR_DDAT_TM_BLT; + + if (cycle & VME_MBLT) + val |=3D TSI148_LCSR_DDAT_TM_MBLT; + + if (cycle & VME_2eVME) + val |=3D TSI148_LCSR_DDAT_TM_2eVME; + + if (cycle & VME_2eSST) + val |=3D TSI148_LCSR_DDAT_TM_2eSST; + + if (cycle & VME_2eSSTB) { + dev_err(dev, "Currently not setting Broadcast Select Registers\n"); + val |=3D TSI148_LCSR_DDAT_TM_2eSSTB; + } + + /* Setup data width */ + switch (dwidth) { + case VME_D16: + val |=3D TSI148_LCSR_DDAT_DBW_16; + break; + case VME_D32: + val |=3D TSI148_LCSR_DDAT_DBW_32; + break; + default: + dev_err(dev, "Invalid data width\n"); + return -EINVAL; + } + + /* Setup address space */ + switch (aspace) { + case VME_A16: + val |=3D TSI148_LCSR_DDAT_AMODE_A16; + break; + case VME_A24: + val |=3D TSI148_LCSR_DDAT_AMODE_A24; + break; + case VME_A32: + val |=3D TSI148_LCSR_DDAT_AMODE_A32; + break; + case VME_A64: + val |=3D TSI148_LCSR_DDAT_AMODE_A64; + break; + case VME_CRCSR: + val |=3D TSI148_LCSR_DDAT_AMODE_CRCSR; + break; + case VME_USER1: + val |=3D TSI148_LCSR_DDAT_AMODE_USER1; + break; + case VME_USER2: + val |=3D TSI148_LCSR_DDAT_AMODE_USER2; + break; + case VME_USER3: + val |=3D TSI148_LCSR_DDAT_AMODE_USER3; + break; + case VME_USER4: + val |=3D TSI148_LCSR_DDAT_AMODE_USER4; + break; + default: + dev_err(dev, "Invalid address space\n"); + return -EINVAL; + } + + if (cycle & VME_SUPER) + val |=3D TSI148_LCSR_DDAT_SUP; + if (cycle & VME_PROG) + val |=3D TSI148_LCSR_DDAT_PGM; + + *attr =3D cpu_to_be32(val); + + return 0; +} + +/* + * Add a link list descriptor to the list + * + * Note: DMA engine expects the DMA descriptor to be big endian. + */ +static int tsi148_dma_list_add(struct vme_dma_list *list, struct vme_dma_a= ttr *src, + struct vme_dma_attr *dest, size_t count) +{ + struct tsi148_dma_entry *entry, *prev; + u32 address_high, address_low, val; + struct vme_dma_pattern *pattern_attr; + struct vme_dma_pci *pci_attr; + struct vme_dma_vme *vme_attr; + int retval =3D 0; + struct vme_bridge *tsi148_bridge; + + tsi148_bridge =3D list->parent->parent; + + /* Descriptor must be aligned on 64-bit boundaries */ + entry =3D kmalloc_obj(*entry); + if (!entry) { + retval =3D -ENOMEM; + goto err_mem; + } + + /* Test descriptor alignment */ + if ((unsigned long)&entry->descriptor & 0x7) { + dev_err(tsi148_bridge->parent, "Descriptor not aligned to 8 byte boundar= y as required: %p\n", + &entry->descriptor); + retval =3D -EINVAL; + goto err_align; + } + + /* Given we are going to fill out the structure, we probably don't + * need to zero it, but better safe than sorry for now. + */ + memset(&entry->descriptor, 0, sizeof(entry->descriptor)); + + /* Fill out source part */ + switch (src->type) { + case VME_DMA_PATTERN: + pattern_attr =3D src->private; + + entry->descriptor.dsal =3D cpu_to_be32(pattern_attr->pattern); + + val =3D TSI148_LCSR_DSAT_TYP_PAT; + + /* Default behaviour is 32 bit pattern */ + if (pattern_attr->type & VME_DMA_PATTERN_BYTE) + val |=3D TSI148_LCSR_DSAT_PSZ; + + /* It seems that the default behaviour is to increment */ + if ((pattern_attr->type & VME_DMA_PATTERN_INCREMENT) =3D=3D 0) + val |=3D TSI148_LCSR_DSAT_NIN; + entry->descriptor.dsat =3D cpu_to_be32(val); + break; + case VME_DMA_PCI: + pci_attr =3D src->private; + + reg_split((unsigned long long)pci_attr->address, &address_high, &address= _low); + entry->descriptor.dsau =3D cpu_to_be32(address_high); + entry->descriptor.dsal =3D cpu_to_be32(address_low); + entry->descriptor.dsat =3D cpu_to_be32(TSI148_LCSR_DSAT_TYP_PCI); + break; + case VME_DMA_VME: + vme_attr =3D src->private; + + reg_split((unsigned long long)vme_attr->address, &address_high, &address= _low); + entry->descriptor.dsau =3D cpu_to_be32(address_high); + entry->descriptor.dsal =3D cpu_to_be32(address_low); + entry->descriptor.dsat =3D cpu_to_be32(TSI148_LCSR_DSAT_TYP_VME); + + retval =3D tsi148_dma_set_vme_src_attributes(tsi148_bridge->parent, + &entry->descriptor.dsat, + vme_attr->aspace, + vme_attr->cycle, + vme_attr->dwidth); + if (retval < 0) + goto err_source; + break; + default: + dev_err(tsi148_bridge->parent, "Invalid source type\n"); + retval =3D -EINVAL; + goto err_source; + } + + /* Assume last link - this will be over-written by adding another */ + entry->descriptor.dnlau =3D cpu_to_be32(0); + entry->descriptor.dnlal =3D cpu_to_be32(TSI148_LCSR_DNLAL_LLA); + + /* Fill out destination part */ + switch (dest->type) { + case VME_DMA_PCI: + pci_attr =3D dest->private; + + reg_split((unsigned long long)pci_attr->address, &address_high, + &address_low); + entry->descriptor.ddau =3D cpu_to_be32(address_high); + entry->descriptor.ddal =3D cpu_to_be32(address_low); + entry->descriptor.ddat =3D cpu_to_be32(TSI148_LCSR_DDAT_TYP_PCI); + break; + case VME_DMA_VME: + vme_attr =3D dest->private; + + reg_split((unsigned long long)vme_attr->address, &address_high, + &address_low); + entry->descriptor.ddau =3D cpu_to_be32(address_high); + entry->descriptor.ddal =3D cpu_to_be32(address_low); + entry->descriptor.ddat =3D cpu_to_be32(TSI148_LCSR_DDAT_TYP_VME); + + retval =3D tsi148_dma_set_vme_dest_attributes(tsi148_bridge->parent, + &entry->descriptor.ddat, + vme_attr->aspace, + vme_attr->cycle, + vme_attr->dwidth); + if (retval < 0) + goto err_dest; + break; + default: + dev_err(tsi148_bridge->parent, "Invalid destination type\n"); + retval =3D -EINVAL; + goto err_dest; + } + + /* Fill out count */ + entry->descriptor.dcnt =3D cpu_to_be32((u32)count); + + /* Add to list */ + list_add_tail(&entry->list, &list->entries); + + entry->dma_handle =3D dma_map_single(tsi148_bridge->parent, + &entry->descriptor, + sizeof(entry->descriptor), + DMA_TO_DEVICE); + if (dma_mapping_error(tsi148_bridge->parent, entry->dma_handle)) { + dev_err(tsi148_bridge->parent, "DMA mapping error\n"); + retval =3D -EINVAL; + goto err_dma; + } + + /* Fill out previous descriptors "Next Address" */ + if (entry->list.prev !=3D &list->entries) { + reg_split((unsigned long long)entry->dma_handle, &address_high, + &address_low); + prev =3D list_entry(entry->list.prev, struct tsi148_dma_entry, + list); + prev->descriptor.dnlau =3D cpu_to_be32(address_high); + prev->descriptor.dnlal =3D cpu_to_be32(address_low); + } + + return 0; + +err_dma: + list_del(&entry->list); +err_dest: +err_source: +err_align: + kfree(entry); +err_mem: + return retval; +} + +/* + * Check to see if the provided DMA channel is busy. + */ +static int tsi148_dma_busy(struct vme_bridge *tsi148_bridge, int channel) +{ + u32 tmp; + struct tsi148_driver *bridge; + + bridge =3D tsi148_bridge->driver_priv; + + tmp =3D ioread32be(bridge->base + TSI148_LCSR_DMA[channel] + + TSI148_LCSR_OFFSET_DSTA); + + if (tmp & TSI148_LCSR_DSTA_BSY) + return 0; + else + return 1; +} + +/* + * Execute a previously generated link list + * + * XXX Need to provide control register configuration. + */ +static int tsi148_dma_list_exec(struct vme_dma_list *list) +{ + struct vme_dma_resource *ctrlr; + int channel, retval; + struct tsi148_dma_entry *entry; + u32 bus_addr_high, bus_addr_low; + u32 val, dctlreg =3D 0; + struct vme_bridge *tsi148_bridge; + struct tsi148_driver *bridge; + + ctrlr =3D list->parent; + + tsi148_bridge =3D ctrlr->parent; + + bridge =3D tsi148_bridge->driver_priv; + + mutex_lock(&ctrlr->mtx); + + channel =3D ctrlr->number; + + if (!list_empty(&ctrlr->running)) { + /* + * XXX We have an active DMA transfer and currently haven't + * sorted out the mechanism for "pending" DMA transfers. + * Return busy. + */ + /* Need to add to pending here */ + mutex_unlock(&ctrlr->mtx); + return -EBUSY; + } + + list_add(&list->list, &ctrlr->running); + + /* Get first bus address and write into registers */ + entry =3D list_first_entry(&list->entries, struct tsi148_dma_entry, + list); + + mutex_unlock(&ctrlr->mtx); + + reg_split(entry->dma_handle, &bus_addr_high, &bus_addr_low); + + iowrite32be(bus_addr_high, bridge->base + + TSI148_LCSR_DMA[channel] + TSI148_LCSR_OFFSET_DNLAU); + iowrite32be(bus_addr_low, bridge->base + + TSI148_LCSR_DMA[channel] + TSI148_LCSR_OFFSET_DNLAL); + + dctlreg =3D ioread32be(bridge->base + TSI148_LCSR_DMA[channel] + + TSI148_LCSR_OFFSET_DCTL); + + /* Start the operation */ + iowrite32be(dctlreg | TSI148_LCSR_DCTL_DGO, bridge->base + + TSI148_LCSR_DMA[channel] + TSI148_LCSR_OFFSET_DCTL); + + retval =3D wait_event_interruptible(bridge->dma_queue[channel], + tsi148_dma_busy(ctrlr->parent, channel)); + + if (retval) { + iowrite32be(dctlreg | TSI148_LCSR_DCTL_ABT, bridge->base + + TSI148_LCSR_DMA[channel] + TSI148_LCSR_OFFSET_DCTL); + /* Wait for the operation to abort */ + wait_event(bridge->dma_queue[channel], + tsi148_dma_busy(ctrlr->parent, channel)); + retval =3D -EINTR; + goto exit; + } + + /* + * Read status register, this register is valid until we kick off a + * new transfer. + */ + val =3D ioread32be(bridge->base + TSI148_LCSR_DMA[channel] + + TSI148_LCSR_OFFSET_DSTA); + + if (val & TSI148_LCSR_DSTA_VBE) { + dev_err(tsi148_bridge->parent, "DMA Error. DSTA=3D%08X\n", val); + retval =3D -EIO; + } + +exit: + /* Remove list from running list */ + mutex_lock(&ctrlr->mtx); + list_del(&list->list); + mutex_unlock(&ctrlr->mtx); + + return retval; +} + +/* + * Clean up a previously generated link list + * + * We have a separate function, don't assume that the chain can't be reuse= d. + */ +static int tsi148_dma_list_empty(struct vme_dma_list *list) +{ + struct list_head *pos, *temp; + struct tsi148_dma_entry *entry; + + struct vme_bridge *tsi148_bridge =3D list->parent->parent; + + /* detach and free each entry */ + list_for_each_safe(pos, temp, &list->entries) { + list_del(pos); + entry =3D list_entry(pos, struct tsi148_dma_entry, list); + + dma_unmap_single(tsi148_bridge->parent, entry->dma_handle, + sizeof(struct tsi148_dma_descriptor), DMA_TO_DEVICE); + kfree(entry); + } + + return 0; +} + +/* + * All 4 location monitors reside at the same base - this is therefore a + * system wide configuration. + * + * This does not enable the LM monitor - that should be done when the first + * callback is attached and disabled when the last callback is removed. + */ +static int tsi148_lm_set(struct vme_lm_resource *lm, unsigned long long lm= _base, + u32 aspace, u32 cycle) +{ + u32 lm_base_high, lm_base_low, lm_ctl =3D 0; + int i; + struct vme_bridge *tsi148_bridge; + struct tsi148_driver *bridge; + + tsi148_bridge =3D lm->parent; + + bridge =3D tsi148_bridge->driver_priv; + + mutex_lock(&lm->mtx); + + /* If we already have a callback attached, we can't move it! */ + for (i =3D 0; i < lm->monitors; i++) { + if (bridge->lm_callback[i]) { + mutex_unlock(&lm->mtx); + dev_err(tsi148_bridge->parent, "Location monitor callback attached, can= 't reset\n"); + return -EBUSY; + } + } + + switch (aspace) { + case VME_A16: + lm_ctl |=3D TSI148_LCSR_LMAT_AS_A16; + break; + case VME_A24: + lm_ctl |=3D TSI148_LCSR_LMAT_AS_A24; + break; + case VME_A32: + lm_ctl |=3D TSI148_LCSR_LMAT_AS_A32; + break; + case VME_A64: + lm_ctl |=3D TSI148_LCSR_LMAT_AS_A64; + break; + default: + mutex_unlock(&lm->mtx); + dev_err(tsi148_bridge->parent, "Invalid address space\n"); + return -EINVAL; + } + + if (cycle & VME_SUPER) + lm_ctl |=3D TSI148_LCSR_LMAT_SUPR; + if (cycle & VME_USER) + lm_ctl |=3D TSI148_LCSR_LMAT_NPRIV; + if (cycle & VME_PROG) + lm_ctl |=3D TSI148_LCSR_LMAT_PGM; + if (cycle & VME_DATA) + lm_ctl |=3D TSI148_LCSR_LMAT_DATA; + + reg_split(lm_base, &lm_base_high, &lm_base_low); + + iowrite32be(lm_base_high, bridge->base + TSI148_LCSR_LMBAU); + iowrite32be(lm_base_low, bridge->base + TSI148_LCSR_LMBAL); + iowrite32be(lm_ctl, bridge->base + TSI148_LCSR_LMAT); + + mutex_unlock(&lm->mtx); + + return 0; +} + +/* Get configuration of the callback monitor and return whether it is enab= led + * or disabled. + */ +static int tsi148_lm_get(struct vme_lm_resource *lm, + unsigned long long *lm_base, u32 *aspace, u32 *cycle) +{ + u32 lm_base_high, lm_base_low, lm_ctl, enabled =3D 0; + struct tsi148_driver *bridge; + + bridge =3D lm->parent->driver_priv; + + mutex_lock(&lm->mtx); + + lm_base_high =3D ioread32be(bridge->base + TSI148_LCSR_LMBAU); + lm_base_low =3D ioread32be(bridge->base + TSI148_LCSR_LMBAL); + lm_ctl =3D ioread32be(bridge->base + TSI148_LCSR_LMAT); + + reg_join(lm_base_high, lm_base_low, lm_base); + + if (lm_ctl & TSI148_LCSR_LMAT_EN) + enabled =3D 1; + + if ((lm_ctl & TSI148_LCSR_LMAT_AS_M) =3D=3D TSI148_LCSR_LMAT_AS_A16) + *aspace |=3D VME_A16; + + if ((lm_ctl & TSI148_LCSR_LMAT_AS_M) =3D=3D TSI148_LCSR_LMAT_AS_A24) + *aspace |=3D VME_A24; + + if ((lm_ctl & TSI148_LCSR_LMAT_AS_M) =3D=3D TSI148_LCSR_LMAT_AS_A32) + *aspace |=3D VME_A32; + + if ((lm_ctl & TSI148_LCSR_LMAT_AS_M) =3D=3D TSI148_LCSR_LMAT_AS_A64) + *aspace |=3D VME_A64; + + if (lm_ctl & TSI148_LCSR_LMAT_SUPR) + *cycle |=3D VME_SUPER; + if (lm_ctl & TSI148_LCSR_LMAT_NPRIV) + *cycle |=3D VME_USER; + if (lm_ctl & TSI148_LCSR_LMAT_PGM) + *cycle |=3D VME_PROG; + if (lm_ctl & TSI148_LCSR_LMAT_DATA) + *cycle |=3D VME_DATA; + + mutex_unlock(&lm->mtx); + + return enabled; +} + +/* + * Attach a callback to a specific location monitor. + * + * Callback will be passed the monitor triggered. + */ +static int tsi148_lm_attach(struct vme_lm_resource *lm, int monitor, + void (*callback)(void *), void *data) +{ + u32 lm_ctl, tmp; + struct vme_bridge *tsi148_bridge; + struct tsi148_driver *bridge; + + tsi148_bridge =3D lm->parent; + + bridge =3D tsi148_bridge->driver_priv; + + mutex_lock(&lm->mtx); + + /* Ensure that the location monitor is configured - need PGM or DATA */ + lm_ctl =3D ioread32be(bridge->base + TSI148_LCSR_LMAT); + if ((lm_ctl & (TSI148_LCSR_LMAT_PGM | TSI148_LCSR_LMAT_DATA)) =3D=3D 0) { + mutex_unlock(&lm->mtx); + dev_err(tsi148_bridge->parent, "Location monitor not properly configured= \n"); + return -EINVAL; + } + + /* Check that a callback isn't already attached */ + if (bridge->lm_callback[monitor]) { + mutex_unlock(&lm->mtx); + dev_err(tsi148_bridge->parent, "Existing callback attached\n"); + return -EBUSY; + } + + /* Attach callback */ + bridge->lm_callback[monitor] =3D callback; + bridge->lm_data[monitor] =3D data; + + /* Enable Location Monitor interrupt */ + tmp =3D ioread32be(bridge->base + TSI148_LCSR_INTEN); + tmp |=3D TSI148_LCSR_INTEN_LMEN[monitor]; + iowrite32be(tmp, bridge->base + TSI148_LCSR_INTEN); + + tmp =3D ioread32be(bridge->base + TSI148_LCSR_INTEO); + tmp |=3D TSI148_LCSR_INTEO_LMEO[monitor]; + iowrite32be(tmp, bridge->base + TSI148_LCSR_INTEO); + + /* Ensure that global Location Monitor Enable set */ + if ((lm_ctl & TSI148_LCSR_LMAT_EN) =3D=3D 0) { + lm_ctl |=3D TSI148_LCSR_LMAT_EN; + iowrite32be(lm_ctl, bridge->base + TSI148_LCSR_LMAT); + } + + mutex_unlock(&lm->mtx); + + return 0; +} + +/* + * Detach a callback function forn a specific location monitor. + */ +static int tsi148_lm_detach(struct vme_lm_resource *lm, int monitor) +{ + u32 lm_en, tmp; + struct tsi148_driver *bridge; + + bridge =3D lm->parent->driver_priv; + + mutex_lock(&lm->mtx); + + /* Disable Location Monitor and ensure previous interrupts are clear */ + lm_en =3D ioread32be(bridge->base + TSI148_LCSR_INTEN); + lm_en &=3D ~TSI148_LCSR_INTEN_LMEN[monitor]; + iowrite32be(lm_en, bridge->base + TSI148_LCSR_INTEN); + + tmp =3D ioread32be(bridge->base + TSI148_LCSR_INTEO); + tmp &=3D ~TSI148_LCSR_INTEO_LMEO[monitor]; + iowrite32be(tmp, bridge->base + TSI148_LCSR_INTEO); + + iowrite32be(TSI148_LCSR_INTC_LMC[monitor], + bridge->base + TSI148_LCSR_INTC); + + /* Detach callback */ + bridge->lm_callback[monitor] =3D NULL; + bridge->lm_data[monitor] =3D NULL; + + /* If all location monitors disabled, disable global Location Monitor */ + if ((lm_en & (TSI148_LCSR_INTS_LM0S | TSI148_LCSR_INTS_LM1S | + TSI148_LCSR_INTS_LM2S | TSI148_LCSR_INTS_LM3S)) =3D=3D 0) { + tmp =3D ioread32be(bridge->base + TSI148_LCSR_LMAT); + tmp &=3D ~TSI148_LCSR_LMAT_EN; + iowrite32be(tmp, bridge->base + TSI148_LCSR_LMAT); + } + + mutex_unlock(&lm->mtx); + + return 0; +} + +/* + * Determine Geographical Addressing + */ +static int tsi148_slot_get(struct vme_bridge *tsi148_bridge) +{ + u32 slot =3D 0; + struct tsi148_driver *bridge; + + bridge =3D tsi148_bridge->driver_priv; + + if (!geoid) { + slot =3D ioread32be(bridge->base + TSI148_LCSR_VSTAT); + slot =3D slot & TSI148_LCSR_VSTAT_GA_M; + } else { + slot =3D geoid; + } + + return (int)slot; +} + +static void *tsi148_alloc_consistent(struct device *parent, size_t size, + dma_addr_t *dma) +{ + struct pci_dev *pdev; + + /* Find pci_dev container of dev */ + pdev =3D to_pci_dev(parent); + + return dma_alloc_coherent(&pdev->dev, size, dma, GFP_KERNEL); +} + +static void tsi148_free_consistent(struct device *parent, size_t size, + void *vaddr, dma_addr_t dma) +{ + struct pci_dev *pdev; + + /* Find pci_dev container of dev */ + pdev =3D to_pci_dev(parent); + + dma_free_coherent(&pdev->dev, size, vaddr, dma); +} + +/* + * Configure CR/CSR space + * + * Access to the CR/CSR can be configured at power-up. The location of the + * CR/CSR registers in the CR/CSR address space is determined by the boards + * Auto-ID or Geographic address. This function ensures that the window is + * enabled at an offset consistent with the boards geopgraphic address. + * + * Each board has a 512kB window, with the highest 4kB being used for the + * boards registers, this means there is a fix length 508kB window which m= ust + * be mapped onto PCI memory. + */ +static int tsi148_crcsr_init(struct vme_bridge *tsi148_bridge, + struct pci_dev *pdev) +{ + u32 cbar, crat, vstat; + u32 crcsr_bus_high, crcsr_bus_low; + int retval; + struct tsi148_driver *bridge; + + bridge =3D tsi148_bridge->driver_priv; + + /* Allocate mem for CR/CSR image */ + bridge->crcsr_kernel =3D dma_alloc_coherent(&pdev->dev, + VME_CRCSR_BUF_SIZE, + &bridge->crcsr_bus, GFP_KERNEL); + if (!bridge->crcsr_kernel) { + dev_err(tsi148_bridge->parent, "Failed to allocate memory for CR/CSR ima= ge\n"); + return -ENOMEM; + } + + reg_split(bridge->crcsr_bus, &crcsr_bus_high, &crcsr_bus_low); + + iowrite32be(crcsr_bus_high, bridge->base + TSI148_LCSR_CROU); + iowrite32be(crcsr_bus_low, bridge->base + TSI148_LCSR_CROL); + + /* Ensure that the CR/CSR is configured at the correct offset */ + cbar =3D ioread32be(bridge->base + TSI148_CBAR); + cbar =3D (cbar & TSI148_CRCSR_CBAR_M) >> 3; + + vstat =3D tsi148_slot_get(tsi148_bridge); + + if (cbar !=3D vstat) { + cbar =3D vstat; + dev_info(tsi148_bridge->parent, "Setting CR/CSR offset\n"); + iowrite32be(cbar << 3, bridge->base + TSI148_CBAR); + } + dev_info(tsi148_bridge->parent, "CR/CSR Offset: %d\n", cbar); + + crat =3D ioread32be(bridge->base + TSI148_LCSR_CRAT); + if (crat & TSI148_LCSR_CRAT_EN) { + dev_info(tsi148_bridge->parent, "CR/CSR already enabled\n"); + } else { + dev_info(tsi148_bridge->parent, "Enabling CR/CSR space\n"); + iowrite32be(crat | TSI148_LCSR_CRAT_EN, bridge->base + TSI148_LCSR_CRAT); + } + + /* If we want flushed, error-checked writes, set up a window + * over the CR/CSR registers. We read from here to safely flush + * through VME writes. + */ + if (err_chk) { + retval =3D tsi148_master_set(bridge->flush_image, 1, (vstat * 0x80000), + 0x80000, VME_CRCSR, VME_SCT, VME_D16); + if (retval) + dev_err(tsi148_bridge->parent, "Configuring flush image failed\n"); + } + + return 0; +} + +static void tsi148_crcsr_exit(struct vme_bridge *tsi148_bridge, + struct pci_dev *pdev) +{ + u32 crat; + struct tsi148_driver *bridge; + + bridge =3D tsi148_bridge->driver_priv; + + /* Turn off CR/CSR space */ + crat =3D ioread32be(bridge->base + TSI148_LCSR_CRAT); + iowrite32be(crat & ~TSI148_LCSR_CRAT_EN, + bridge->base + TSI148_LCSR_CRAT); + + /* Free image */ + iowrite32be(0, bridge->base + TSI148_LCSR_CROU); + iowrite32be(0, bridge->base + TSI148_LCSR_CROL); + + dma_free_coherent(&pdev->dev, VME_CRCSR_BUF_SIZE, + bridge->crcsr_kernel, bridge->crcsr_bus); +} + +static int tsi148_probe(struct pci_dev *pdev, const struct pci_device_id *= id) +{ + int retval, i, master_num; + u32 data; + struct list_head *pos =3D NULL, *n; + struct vme_bridge *tsi148_bridge; + struct tsi148_driver *tsi148_device; + struct vme_master_resource *master_image; + struct vme_slave_resource *slave_image; + struct vme_dma_resource *dma_ctrlr; + struct vme_lm_resource *lm; + + if (geoid >=3D VME_MAX_SLOTS) { + dev_err(&pdev->dev, + "VME geographical address must be between 0 and %d (exclusive), but got= %d\n", + VME_MAX_SLOTS, geoid); + return -EINVAL; + } + + /* If we want to support more than one of each bridge, we need to + * dynamically generate this so we get one per device + */ + tsi148_bridge =3D kzalloc_obj(*tsi148_bridge); + if (!tsi148_bridge) { + retval =3D -ENOMEM; + goto err_struct; + } + vme_init_bridge(tsi148_bridge); + + tsi148_device =3D kzalloc_obj(*tsi148_device); + if (!tsi148_device) { + retval =3D -ENOMEM; + goto err_driver; + } + + tsi148_bridge->driver_priv =3D tsi148_device; + + /* Enable the device */ + retval =3D pci_enable_device(pdev); + if (retval) { + dev_err(&pdev->dev, "Unable to enable device\n"); + goto err_enable; + } + + /* Map Registers */ + retval =3D pci_request_regions(pdev, driver_name); + if (retval) { + dev_err(&pdev->dev, "Unable to reserve resources\n"); + goto err_resource; + } + + /* map registers in BAR 0 */ + tsi148_device->base =3D ioremap(pci_resource_start(pdev, 0), + 4096); + if (!tsi148_device->base) { + dev_err(&pdev->dev, "Unable to remap CRG region\n"); + retval =3D -EIO; + goto err_remap; + } + + /* Check to see if the mapping worked out */ + data =3D ioread32(tsi148_device->base + TSI148_PCFS_ID) & 0x0000FFFF; + if (data !=3D PCI_VENDOR_ID_TUNDRA) { + dev_err(&pdev->dev, "CRG region check failed\n"); + retval =3D -EIO; + goto err_test; + } + + /* Initialize wait queues & mutual exclusion flags */ + init_waitqueue_head(&tsi148_device->dma_queue[0]); + init_waitqueue_head(&tsi148_device->dma_queue[1]); + init_waitqueue_head(&tsi148_device->iack_queue); + mutex_init(&tsi148_device->vme_int); + mutex_init(&tsi148_device->vme_rmw); + + tsi148_bridge->parent =3D &pdev->dev; + strscpy(tsi148_bridge->name, driver_name, VMENAMSIZ); + + /* Setup IRQ */ + retval =3D tsi148_irq_init(tsi148_bridge); + if (retval !=3D 0) { + dev_err(&pdev->dev, "Chip Initialization failed.\n"); + goto err_irq; + } + + /* If we are going to flush writes, we need to read from the VME bus. + * We need to do this safely, thus we read the devices own CR/CSR + * register. To do this we must set up a window in CR/CSR space and + * hence have one less master window resource available. + */ + master_num =3D TSI148_MAX_MASTER; + if (err_chk) { + master_num--; + + tsi148_device->flush_image =3D kmalloc_obj(*tsi148_device->flush_image); + if (!tsi148_device->flush_image) { + retval =3D -ENOMEM; + goto err_master; + } + tsi148_device->flush_image->parent =3D tsi148_bridge; + spin_lock_init(&tsi148_device->flush_image->lock); + tsi148_device->flush_image->locked =3D 1; + tsi148_device->flush_image->number =3D master_num; + memset(&tsi148_device->flush_image->bus_resource, 0, + sizeof(tsi148_device->flush_image->bus_resource)); + tsi148_device->flush_image->kern_base =3D NULL; + } + + /* Add master windows to list */ + for (i =3D 0; i < master_num; i++) { + master_image =3D kmalloc_obj(*master_image); + if (!master_image) { + retval =3D -ENOMEM; + goto err_master; + } + master_image->parent =3D tsi148_bridge; + spin_lock_init(&master_image->lock); + master_image->locked =3D 0; + master_image->number =3D i; + master_image->address_attr =3D VME_A16 | VME_A24 | VME_A32 | + VME_A64 | VME_CRCSR | VME_USER1 | VME_USER2 | + VME_USER3 | VME_USER4; + master_image->cycle_attr =3D VME_SCT | VME_BLT | VME_MBLT | + VME_2eVME | VME_2eSST | VME_2eSSTB | VME_2eSST160 | + VME_2eSST267 | VME_2eSST320 | VME_SUPER | VME_USER | + VME_PROG | VME_DATA; + master_image->width_attr =3D VME_D16 | VME_D32; + memset(&master_image->bus_resource, 0, + sizeof(master_image->bus_resource)); + master_image->kern_base =3D NULL; + list_add_tail(&master_image->list, + &tsi148_bridge->master_resources); + } + + /* Add slave windows to list */ + for (i =3D 0; i < TSI148_MAX_SLAVE; i++) { + slave_image =3D kmalloc_obj(*slave_image); + if (!slave_image) { + retval =3D -ENOMEM; + goto err_slave; + } + slave_image->parent =3D tsi148_bridge; + mutex_init(&slave_image->mtx); + slave_image->locked =3D 0; + slave_image->number =3D i; + slave_image->address_attr =3D VME_A16 | VME_A24 | VME_A32 | + VME_A64; + slave_image->cycle_attr =3D VME_SCT | VME_BLT | VME_MBLT | + VME_2eVME | VME_2eSST | VME_2eSSTB | VME_2eSST160 | + VME_2eSST267 | VME_2eSST320 | VME_SUPER | VME_USER | + VME_PROG | VME_DATA; + list_add_tail(&slave_image->list, + &tsi148_bridge->slave_resources); + } + + /* Add dma engines to list */ + for (i =3D 0; i < TSI148_MAX_DMA; i++) { + dma_ctrlr =3D kmalloc_obj(*dma_ctrlr); + if (!dma_ctrlr) { + retval =3D -ENOMEM; + goto err_dma; + } + dma_ctrlr->parent =3D tsi148_bridge; + mutex_init(&dma_ctrlr->mtx); + dma_ctrlr->locked =3D 0; + dma_ctrlr->number =3D i; + dma_ctrlr->route_attr =3D VME_DMA_VME_TO_MEM | + VME_DMA_MEM_TO_VME | VME_DMA_VME_TO_VME | + VME_DMA_MEM_TO_MEM | VME_DMA_PATTERN_TO_VME | + VME_DMA_PATTERN_TO_MEM; + INIT_LIST_HEAD(&dma_ctrlr->pending); + INIT_LIST_HEAD(&dma_ctrlr->running); + list_add_tail(&dma_ctrlr->list, + &tsi148_bridge->dma_resources); + } + + /* Add location monitor to list */ + lm =3D kmalloc_obj(*lm); + if (!lm) { + retval =3D -ENOMEM; + goto err_lm; + } + lm->parent =3D tsi148_bridge; + mutex_init(&lm->mtx); + lm->locked =3D 0; + lm->number =3D 1; + lm->monitors =3D 4; + list_add_tail(&lm->list, &tsi148_bridge->lm_resources); + + tsi148_bridge->slave_get =3D tsi148_slave_get; + tsi148_bridge->slave_set =3D tsi148_slave_set; + tsi148_bridge->master_get =3D tsi148_master_get; + tsi148_bridge->master_set =3D tsi148_master_set; + tsi148_bridge->master_read =3D tsi148_master_read; + tsi148_bridge->master_write =3D tsi148_master_write; + tsi148_bridge->master_rmw =3D tsi148_master_rmw; + tsi148_bridge->dma_list_add =3D tsi148_dma_list_add; + tsi148_bridge->dma_list_exec =3D tsi148_dma_list_exec; + tsi148_bridge->dma_list_empty =3D tsi148_dma_list_empty; + tsi148_bridge->irq_set =3D tsi148_irq_set; + tsi148_bridge->irq_generate =3D tsi148_irq_generate; + tsi148_bridge->lm_set =3D tsi148_lm_set; + tsi148_bridge->lm_get =3D tsi148_lm_get; + tsi148_bridge->lm_attach =3D tsi148_lm_attach; + tsi148_bridge->lm_detach =3D tsi148_lm_detach; + tsi148_bridge->slot_get =3D tsi148_slot_get; + tsi148_bridge->alloc_consistent =3D tsi148_alloc_consistent; + tsi148_bridge->free_consistent =3D tsi148_free_consistent; + + data =3D ioread32be(tsi148_device->base + TSI148_LCSR_VSTAT); + dev_info(&pdev->dev, "Board is%s the VME system controller\n", + (data & TSI148_LCSR_VSTAT_SCONS) ? "" : " not"); + if (!geoid) + dev_info(&pdev->dev, "VME geographical address is %d\n", + data & TSI148_LCSR_VSTAT_GA_M); + else + dev_info(&pdev->dev, "VME geographical address is set to %d\n", + geoid); + + dev_info(&pdev->dev, "VME Write and flush and error check is %s\n", + err_chk ? "enabled" : "disabled"); + + retval =3D tsi148_crcsr_init(tsi148_bridge, pdev); + if (retval) { + dev_err(&pdev->dev, "CR/CSR configuration failed.\n"); + goto err_crcsr; + } + + retval =3D vme_register_bridge(tsi148_bridge); + if (retval !=3D 0) { + dev_err(&pdev->dev, "Chip Registration failed.\n"); + goto err_reg; + } + + pci_set_drvdata(pdev, tsi148_bridge); + + /* Clear VME bus "board fail", and "power-up reset" lines */ + data =3D ioread32be(tsi148_device->base + TSI148_LCSR_VSTAT); + data &=3D ~TSI148_LCSR_VSTAT_BRDFL; + data |=3D TSI148_LCSR_VSTAT_CPURST; + iowrite32be(data, tsi148_device->base + TSI148_LCSR_VSTAT); + + return 0; + +err_reg: + tsi148_crcsr_exit(tsi148_bridge, pdev); +err_crcsr: +err_lm: + /* resources are stored in link list */ + list_for_each_safe(pos, n, &tsi148_bridge->lm_resources) { + lm =3D list_entry(pos, struct vme_lm_resource, list); + list_del(pos); + kfree(lm); + } +err_dma: + /* resources are stored in link list */ + list_for_each_safe(pos, n, &tsi148_bridge->dma_resources) { + dma_ctrlr =3D list_entry(pos, struct vme_dma_resource, list); + list_del(pos); + kfree(dma_ctrlr); + } +err_slave: + /* resources are stored in link list */ + list_for_each_safe(pos, n, &tsi148_bridge->slave_resources) { + slave_image =3D list_entry(pos, struct vme_slave_resource, list); + list_del(pos); + kfree(slave_image); + } +err_master: + /* resources are stored in link list */ + list_for_each_safe(pos, n, &tsi148_bridge->master_resources) { + master_image =3D list_entry(pos, struct vme_master_resource, list); + list_del(pos); + kfree(master_image); + } + + tsi148_irq_exit(tsi148_bridge, pdev); +err_irq: +err_test: + iounmap(tsi148_device->base); +err_remap: + pci_release_regions(pdev); +err_resource: + pci_disable_device(pdev); +err_enable: + kfree(tsi148_device); +err_driver: + kfree(tsi148_bridge); +err_struct: + return retval; +} + +static void tsi148_remove(struct pci_dev *pdev) +{ + struct list_head *pos =3D NULL; + struct list_head *tmplist; + struct vme_master_resource *master_image; + struct vme_slave_resource *slave_image; + struct vme_dma_resource *dma_ctrlr; + int i; + struct tsi148_driver *bridge; + struct vme_bridge *tsi148_bridge =3D pci_get_drvdata(pdev); + + bridge =3D tsi148_bridge->driver_priv; + + dev_dbg(&pdev->dev, "Driver is being unloaded.\n"); + + /* + * Shutdown all inbound and outbound windows. + */ + for (i =3D 0; i < 8; i++) { + iowrite32be(0, bridge->base + TSI148_LCSR_IT[i] + + TSI148_LCSR_OFFSET_ITAT); + iowrite32be(0, bridge->base + TSI148_LCSR_OT[i] + + TSI148_LCSR_OFFSET_OTAT); + } + + /* + * Shutdown Location monitor. + */ + iowrite32be(0, bridge->base + TSI148_LCSR_LMAT); + + /* + * Shutdown CRG map. + */ + iowrite32be(0, bridge->base + TSI148_LCSR_CSRAT); + + /* + * Clear error status. + */ + iowrite32be(0xFFFFFFFF, bridge->base + TSI148_LCSR_EDPAT); + iowrite32be(0xFFFFFFFF, bridge->base + TSI148_LCSR_VEAT); + iowrite32be(0x07000700, bridge->base + TSI148_LCSR_PSTAT); + + /* + * Remove VIRQ interrupt (if any) + */ + if (ioread32be(bridge->base + TSI148_LCSR_VICR) & 0x800) + iowrite32be(0x8000, bridge->base + TSI148_LCSR_VICR); + + /* + * Map all Interrupts to PCI INTA + */ + iowrite32be(0x0, bridge->base + TSI148_LCSR_INTM1); + iowrite32be(0x0, bridge->base + TSI148_LCSR_INTM2); + + tsi148_irq_exit(tsi148_bridge, pdev); + + vme_unregister_bridge(tsi148_bridge); + + tsi148_crcsr_exit(tsi148_bridge, pdev); + + /* resources are stored in link list */ + list_for_each_safe(pos, tmplist, &tsi148_bridge->dma_resources) { + dma_ctrlr =3D list_entry(pos, struct vme_dma_resource, list); + list_del(pos); + kfree(dma_ctrlr); + } + + /* resources are stored in link list */ + list_for_each_safe(pos, tmplist, &tsi148_bridge->slave_resources) { + slave_image =3D list_entry(pos, struct vme_slave_resource, list); + list_del(pos); + kfree(slave_image); + } + + /* resources are stored in link list */ + list_for_each_safe(pos, tmplist, &tsi148_bridge->master_resources) { + master_image =3D list_entry(pos, struct vme_master_resource, list); + list_del(pos); + kfree(master_image); + } + + iounmap(bridge->base); + + pci_release_regions(pdev); + + pci_disable_device(pdev); + + kfree(tsi148_bridge->driver_priv); + + kfree(tsi148_bridge); +} + +module_pci_driver(tsi148_driver); + +MODULE_PARM_DESC(err_chk, "Check for VME errors on reads and writes"); +module_param(err_chk, bool, 0); + +MODULE_PARM_DESC(geoid, "Override geographical addressing"); +module_param(geoid, uint, 0); + +MODULE_DESCRIPTION("VME driver for the Tundra Tempe VME bridge"); +MODULE_LICENSE("GPL"); --=20 2.53.0