18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * AMD am53c974 driver.
48c2ecf20Sopenharmony_ci * Copyright (c) 2014 Hannes Reinecke, SUSE Linux GmbH
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include <linux/kernel.h>
88c2ecf20Sopenharmony_ci#include <linux/module.h>
98c2ecf20Sopenharmony_ci#include <linux/init.h>
108c2ecf20Sopenharmony_ci#include <linux/delay.h>
118c2ecf20Sopenharmony_ci#include <linux/pci.h>
128c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#include <scsi/scsi_host.h>
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#include "esp_scsi.h"
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#define DRV_MODULE_NAME "am53c974"
198c2ecf20Sopenharmony_ci#define DRV_MODULE_VERSION "1.00"
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_cistatic bool am53c974_debug;
228c2ecf20Sopenharmony_cistatic bool am53c974_fenab = true;
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci#define esp_dma_log(f, a...)						\
258c2ecf20Sopenharmony_ci	do {								\
268c2ecf20Sopenharmony_ci		if (am53c974_debug)					\
278c2ecf20Sopenharmony_ci			shost_printk(KERN_DEBUG, esp->host, f, ##a);	\
288c2ecf20Sopenharmony_ci	} while (0)
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci#define ESP_DMA_CMD 0x10
318c2ecf20Sopenharmony_ci#define ESP_DMA_STC 0x11
328c2ecf20Sopenharmony_ci#define ESP_DMA_SPA 0x12
338c2ecf20Sopenharmony_ci#define ESP_DMA_WBC 0x13
348c2ecf20Sopenharmony_ci#define ESP_DMA_WAC 0x14
358c2ecf20Sopenharmony_ci#define ESP_DMA_STATUS 0x15
368c2ecf20Sopenharmony_ci#define ESP_DMA_SMDLA 0x16
378c2ecf20Sopenharmony_ci#define ESP_DMA_WMAC 0x17
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci#define ESP_DMA_CMD_IDLE 0x00
408c2ecf20Sopenharmony_ci#define ESP_DMA_CMD_BLAST 0x01
418c2ecf20Sopenharmony_ci#define ESP_DMA_CMD_ABORT 0x02
428c2ecf20Sopenharmony_ci#define ESP_DMA_CMD_START 0x03
438c2ecf20Sopenharmony_ci#define ESP_DMA_CMD_MASK  0x03
448c2ecf20Sopenharmony_ci#define ESP_DMA_CMD_DIAG 0x04
458c2ecf20Sopenharmony_ci#define ESP_DMA_CMD_MDL 0x10
468c2ecf20Sopenharmony_ci#define ESP_DMA_CMD_INTE_P 0x20
478c2ecf20Sopenharmony_ci#define ESP_DMA_CMD_INTE_D 0x40
488c2ecf20Sopenharmony_ci#define ESP_DMA_CMD_DIR 0x80
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci#define ESP_DMA_STAT_PWDN 0x01
518c2ecf20Sopenharmony_ci#define ESP_DMA_STAT_ERROR 0x02
528c2ecf20Sopenharmony_ci#define ESP_DMA_STAT_ABORT 0x04
538c2ecf20Sopenharmony_ci#define ESP_DMA_STAT_DONE 0x08
548c2ecf20Sopenharmony_ci#define ESP_DMA_STAT_SCSIINT 0x10
558c2ecf20Sopenharmony_ci#define ESP_DMA_STAT_BCMPLT 0x20
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci/* EEPROM is accessed with 16-bit values */
588c2ecf20Sopenharmony_ci#define DC390_EEPROM_READ 0x80
598c2ecf20Sopenharmony_ci#define DC390_EEPROM_LEN 0x40
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci/*
628c2ecf20Sopenharmony_ci * DC390 EEPROM
638c2ecf20Sopenharmony_ci *
648c2ecf20Sopenharmony_ci * 8 * 4 bytes of per-device options
658c2ecf20Sopenharmony_ci * followed by HBA specific options
668c2ecf20Sopenharmony_ci */
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci/* Per-device options */
698c2ecf20Sopenharmony_ci#define DC390_EE_MODE1 0x00
708c2ecf20Sopenharmony_ci#define DC390_EE_SPEED 0x01
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci/* HBA-specific options */
738c2ecf20Sopenharmony_ci#define DC390_EE_ADAPT_SCSI_ID 0x40
748c2ecf20Sopenharmony_ci#define DC390_EE_MODE2 0x41
758c2ecf20Sopenharmony_ci#define DC390_EE_DELAY 0x42
768c2ecf20Sopenharmony_ci#define DC390_EE_TAG_CMD_NUM 0x43
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci#define DC390_EE_MODE1_PARITY_CHK   0x01
798c2ecf20Sopenharmony_ci#define DC390_EE_MODE1_SYNC_NEGO    0x02
808c2ecf20Sopenharmony_ci#define DC390_EE_MODE1_EN_DISC      0x04
818c2ecf20Sopenharmony_ci#define DC390_EE_MODE1_SEND_START   0x08
828c2ecf20Sopenharmony_ci#define DC390_EE_MODE1_TCQ          0x10
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci#define DC390_EE_MODE2_MORE_2DRV    0x01
858c2ecf20Sopenharmony_ci#define DC390_EE_MODE2_GREATER_1G   0x02
868c2ecf20Sopenharmony_ci#define DC390_EE_MODE2_RST_SCSI_BUS 0x04
878c2ecf20Sopenharmony_ci#define DC390_EE_MODE2_ACTIVE_NEGATION 0x08
888c2ecf20Sopenharmony_ci#define DC390_EE_MODE2_NO_SEEK      0x10
898c2ecf20Sopenharmony_ci#define DC390_EE_MODE2_LUN_CHECK    0x20
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_cistruct pci_esp_priv {
928c2ecf20Sopenharmony_ci	struct esp *esp;
938c2ecf20Sopenharmony_ci	u8 dma_status;
948c2ecf20Sopenharmony_ci};
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_cistatic void pci_esp_dma_drain(struct esp *esp);
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_cistatic inline struct pci_esp_priv *pci_esp_get_priv(struct esp *esp)
998c2ecf20Sopenharmony_ci{
1008c2ecf20Sopenharmony_ci	return dev_get_drvdata(esp->dev);
1018c2ecf20Sopenharmony_ci}
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_cistatic void pci_esp_write8(struct esp *esp, u8 val, unsigned long reg)
1048c2ecf20Sopenharmony_ci{
1058c2ecf20Sopenharmony_ci	iowrite8(val, esp->regs + (reg * 4UL));
1068c2ecf20Sopenharmony_ci}
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_cistatic u8 pci_esp_read8(struct esp *esp, unsigned long reg)
1098c2ecf20Sopenharmony_ci{
1108c2ecf20Sopenharmony_ci	return ioread8(esp->regs + (reg * 4UL));
1118c2ecf20Sopenharmony_ci}
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_cistatic void pci_esp_write32(struct esp *esp, u32 val, unsigned long reg)
1148c2ecf20Sopenharmony_ci{
1158c2ecf20Sopenharmony_ci	return iowrite32(val, esp->regs + (reg * 4UL));
1168c2ecf20Sopenharmony_ci}
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_cistatic int pci_esp_irq_pending(struct esp *esp)
1198c2ecf20Sopenharmony_ci{
1208c2ecf20Sopenharmony_ci	struct pci_esp_priv *pep = pci_esp_get_priv(esp);
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	pep->dma_status = pci_esp_read8(esp, ESP_DMA_STATUS);
1238c2ecf20Sopenharmony_ci	esp_dma_log("dma intr dreg[%02x]\n", pep->dma_status);
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	if (pep->dma_status & (ESP_DMA_STAT_ERROR |
1268c2ecf20Sopenharmony_ci			       ESP_DMA_STAT_ABORT |
1278c2ecf20Sopenharmony_ci			       ESP_DMA_STAT_DONE |
1288c2ecf20Sopenharmony_ci			       ESP_DMA_STAT_SCSIINT))
1298c2ecf20Sopenharmony_ci		return 1;
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	return 0;
1328c2ecf20Sopenharmony_ci}
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_cistatic void pci_esp_reset_dma(struct esp *esp)
1358c2ecf20Sopenharmony_ci{
1368c2ecf20Sopenharmony_ci	/* Nothing to do ? */
1378c2ecf20Sopenharmony_ci}
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_cistatic void pci_esp_dma_drain(struct esp *esp)
1408c2ecf20Sopenharmony_ci{
1418c2ecf20Sopenharmony_ci	u8 resid;
1428c2ecf20Sopenharmony_ci	int lim = 1000;
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	if ((esp->sreg & ESP_STAT_PMASK) == ESP_DOP ||
1468c2ecf20Sopenharmony_ci	    (esp->sreg & ESP_STAT_PMASK) == ESP_DIP)
1478c2ecf20Sopenharmony_ci		/* Data-In or Data-Out, nothing to be done */
1488c2ecf20Sopenharmony_ci		return;
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	while (--lim > 0) {
1518c2ecf20Sopenharmony_ci		resid = pci_esp_read8(esp, ESP_FFLAGS) & ESP_FF_FBYTES;
1528c2ecf20Sopenharmony_ci		if (resid <= 1)
1538c2ecf20Sopenharmony_ci			break;
1548c2ecf20Sopenharmony_ci		cpu_relax();
1558c2ecf20Sopenharmony_ci	}
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	/*
1588c2ecf20Sopenharmony_ci	 * When there is a residual BCMPLT will never be set
1598c2ecf20Sopenharmony_ci	 * (obviously). But we still have to issue the BLAST
1608c2ecf20Sopenharmony_ci	 * command, otherwise the data will not being transferred.
1618c2ecf20Sopenharmony_ci	 * But we'll never know when the BLAST operation is
1628c2ecf20Sopenharmony_ci	 * finished. So check for some time and give up eventually.
1638c2ecf20Sopenharmony_ci	 */
1648c2ecf20Sopenharmony_ci	lim = 1000;
1658c2ecf20Sopenharmony_ci	pci_esp_write8(esp, ESP_DMA_CMD_DIR | ESP_DMA_CMD_BLAST, ESP_DMA_CMD);
1668c2ecf20Sopenharmony_ci	while (pci_esp_read8(esp, ESP_DMA_STATUS) & ESP_DMA_STAT_BCMPLT) {
1678c2ecf20Sopenharmony_ci		if (--lim == 0)
1688c2ecf20Sopenharmony_ci			break;
1698c2ecf20Sopenharmony_ci		cpu_relax();
1708c2ecf20Sopenharmony_ci	}
1718c2ecf20Sopenharmony_ci	pci_esp_write8(esp, ESP_DMA_CMD_DIR | ESP_DMA_CMD_IDLE, ESP_DMA_CMD);
1728c2ecf20Sopenharmony_ci	esp_dma_log("DMA blast done (%d tries, %d bytes left)\n", lim, resid);
1738c2ecf20Sopenharmony_ci	/* BLAST residual handling is currently untested */
1748c2ecf20Sopenharmony_ci	if (WARN_ON_ONCE(resid == 1)) {
1758c2ecf20Sopenharmony_ci		struct esp_cmd_entry *ent = esp->active_cmd;
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci		ent->flags |= ESP_CMD_FLAG_RESIDUAL;
1788c2ecf20Sopenharmony_ci	}
1798c2ecf20Sopenharmony_ci}
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_cistatic void pci_esp_dma_invalidate(struct esp *esp)
1828c2ecf20Sopenharmony_ci{
1838c2ecf20Sopenharmony_ci	struct pci_esp_priv *pep = pci_esp_get_priv(esp);
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	esp_dma_log("invalidate DMA\n");
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	pci_esp_write8(esp, ESP_DMA_CMD_IDLE, ESP_DMA_CMD);
1888c2ecf20Sopenharmony_ci	pep->dma_status = 0;
1898c2ecf20Sopenharmony_ci}
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_cistatic int pci_esp_dma_error(struct esp *esp)
1928c2ecf20Sopenharmony_ci{
1938c2ecf20Sopenharmony_ci	struct pci_esp_priv *pep = pci_esp_get_priv(esp);
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	if (pep->dma_status & ESP_DMA_STAT_ERROR) {
1968c2ecf20Sopenharmony_ci		u8 dma_cmd = pci_esp_read8(esp, ESP_DMA_CMD);
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci		if ((dma_cmd & ESP_DMA_CMD_MASK) == ESP_DMA_CMD_START)
1998c2ecf20Sopenharmony_ci			pci_esp_write8(esp, ESP_DMA_CMD_ABORT, ESP_DMA_CMD);
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci		return 1;
2028c2ecf20Sopenharmony_ci	}
2038c2ecf20Sopenharmony_ci	if (pep->dma_status & ESP_DMA_STAT_ABORT) {
2048c2ecf20Sopenharmony_ci		pci_esp_write8(esp, ESP_DMA_CMD_IDLE, ESP_DMA_CMD);
2058c2ecf20Sopenharmony_ci		pep->dma_status = pci_esp_read8(esp, ESP_DMA_CMD);
2068c2ecf20Sopenharmony_ci		return 1;
2078c2ecf20Sopenharmony_ci	}
2088c2ecf20Sopenharmony_ci	return 0;
2098c2ecf20Sopenharmony_ci}
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_cistatic void pci_esp_send_dma_cmd(struct esp *esp, u32 addr, u32 esp_count,
2128c2ecf20Sopenharmony_ci				 u32 dma_count, int write, u8 cmd)
2138c2ecf20Sopenharmony_ci{
2148c2ecf20Sopenharmony_ci	struct pci_esp_priv *pep = pci_esp_get_priv(esp);
2158c2ecf20Sopenharmony_ci	u32 val = 0;
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	BUG_ON(!(cmd & ESP_CMD_DMA));
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	pep->dma_status = 0;
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	/* Set DMA engine to IDLE */
2228c2ecf20Sopenharmony_ci	if (write)
2238c2ecf20Sopenharmony_ci		/* DMA write direction logic is inverted */
2248c2ecf20Sopenharmony_ci		val |= ESP_DMA_CMD_DIR;
2258c2ecf20Sopenharmony_ci	pci_esp_write8(esp, ESP_DMA_CMD_IDLE | val, ESP_DMA_CMD);
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	pci_esp_write8(esp, (esp_count >> 0) & 0xff, ESP_TCLOW);
2288c2ecf20Sopenharmony_ci	pci_esp_write8(esp, (esp_count >> 8) & 0xff, ESP_TCMED);
2298c2ecf20Sopenharmony_ci	if (esp->config2 & ESP_CONFIG2_FENAB)
2308c2ecf20Sopenharmony_ci		pci_esp_write8(esp, (esp_count >> 16) & 0xff, ESP_TCHI);
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	pci_esp_write32(esp, esp_count, ESP_DMA_STC);
2338c2ecf20Sopenharmony_ci	pci_esp_write32(esp, addr, ESP_DMA_SPA);
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	esp_dma_log("start dma addr[%x] count[%d:%d]\n",
2368c2ecf20Sopenharmony_ci		    addr, esp_count, dma_count);
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	scsi_esp_cmd(esp, cmd);
2398c2ecf20Sopenharmony_ci	/* Send DMA Start command */
2408c2ecf20Sopenharmony_ci	pci_esp_write8(esp, ESP_DMA_CMD_START | val, ESP_DMA_CMD);
2418c2ecf20Sopenharmony_ci}
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_cistatic u32 pci_esp_dma_length_limit(struct esp *esp, u32 dma_addr, u32 dma_len)
2448c2ecf20Sopenharmony_ci{
2458c2ecf20Sopenharmony_ci	int dma_limit = 16;
2468c2ecf20Sopenharmony_ci	u32 base, end;
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	/*
2498c2ecf20Sopenharmony_ci	 * If CONFIG2_FENAB is set we can
2508c2ecf20Sopenharmony_ci	 * handle up to 24 bit addresses
2518c2ecf20Sopenharmony_ci	 */
2528c2ecf20Sopenharmony_ci	if (esp->config2 & ESP_CONFIG2_FENAB)
2538c2ecf20Sopenharmony_ci		dma_limit = 24;
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	if (dma_len > (1U << dma_limit))
2568c2ecf20Sopenharmony_ci		dma_len = (1U << dma_limit);
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci	/*
2598c2ecf20Sopenharmony_ci	 * Prevent crossing a 24-bit address boundary.
2608c2ecf20Sopenharmony_ci	 */
2618c2ecf20Sopenharmony_ci	base = dma_addr & ((1U << 24) - 1U);
2628c2ecf20Sopenharmony_ci	end = base + dma_len;
2638c2ecf20Sopenharmony_ci	if (end > (1U << 24))
2648c2ecf20Sopenharmony_ci		end = (1U <<24);
2658c2ecf20Sopenharmony_ci	dma_len = end - base;
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	return dma_len;
2688c2ecf20Sopenharmony_ci}
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_cistatic const struct esp_driver_ops pci_esp_ops = {
2718c2ecf20Sopenharmony_ci	.esp_write8	=	pci_esp_write8,
2728c2ecf20Sopenharmony_ci	.esp_read8	=	pci_esp_read8,
2738c2ecf20Sopenharmony_ci	.irq_pending	=	pci_esp_irq_pending,
2748c2ecf20Sopenharmony_ci	.reset_dma	=	pci_esp_reset_dma,
2758c2ecf20Sopenharmony_ci	.dma_drain	=	pci_esp_dma_drain,
2768c2ecf20Sopenharmony_ci	.dma_invalidate	=	pci_esp_dma_invalidate,
2778c2ecf20Sopenharmony_ci	.send_dma_cmd	=	pci_esp_send_dma_cmd,
2788c2ecf20Sopenharmony_ci	.dma_error	=	pci_esp_dma_error,
2798c2ecf20Sopenharmony_ci	.dma_length_limit =	pci_esp_dma_length_limit,
2808c2ecf20Sopenharmony_ci};
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci/*
2838c2ecf20Sopenharmony_ci * Read DC-390 eeprom
2848c2ecf20Sopenharmony_ci */
2858c2ecf20Sopenharmony_cistatic void dc390_eeprom_prepare_read(struct pci_dev *pdev, u8 cmd)
2868c2ecf20Sopenharmony_ci{
2878c2ecf20Sopenharmony_ci	u8 carry_flag = 1, j = 0x80, bval;
2888c2ecf20Sopenharmony_ci	int i;
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	for (i = 0; i < 9; i++) {
2918c2ecf20Sopenharmony_ci		if (carry_flag) {
2928c2ecf20Sopenharmony_ci			pci_write_config_byte(pdev, 0x80, 0x40);
2938c2ecf20Sopenharmony_ci			bval = 0xc0;
2948c2ecf20Sopenharmony_ci		} else
2958c2ecf20Sopenharmony_ci			bval = 0x80;
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci		udelay(160);
2988c2ecf20Sopenharmony_ci		pci_write_config_byte(pdev, 0x80, bval);
2998c2ecf20Sopenharmony_ci		udelay(160);
3008c2ecf20Sopenharmony_ci		pci_write_config_byte(pdev, 0x80, 0);
3018c2ecf20Sopenharmony_ci		udelay(160);
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci		carry_flag = (cmd & j) ? 1 : 0;
3048c2ecf20Sopenharmony_ci		j >>= 1;
3058c2ecf20Sopenharmony_ci	}
3068c2ecf20Sopenharmony_ci}
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_cistatic u16 dc390_eeprom_get_data(struct pci_dev *pdev)
3098c2ecf20Sopenharmony_ci{
3108c2ecf20Sopenharmony_ci	int i;
3118c2ecf20Sopenharmony_ci	u16 wval = 0;
3128c2ecf20Sopenharmony_ci	u8 bval;
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci	for (i = 0; i < 16; i++) {
3158c2ecf20Sopenharmony_ci		wval <<= 1;
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci		pci_write_config_byte(pdev, 0x80, 0x80);
3188c2ecf20Sopenharmony_ci		udelay(160);
3198c2ecf20Sopenharmony_ci		pci_write_config_byte(pdev, 0x80, 0x40);
3208c2ecf20Sopenharmony_ci		udelay(160);
3218c2ecf20Sopenharmony_ci		pci_read_config_byte(pdev, 0x00, &bval);
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci		if (bval == 0x22)
3248c2ecf20Sopenharmony_ci			wval |= 1;
3258c2ecf20Sopenharmony_ci	}
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	return wval;
3288c2ecf20Sopenharmony_ci}
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_cistatic void dc390_read_eeprom(struct pci_dev *pdev, u16 *ptr)
3318c2ecf20Sopenharmony_ci{
3328c2ecf20Sopenharmony_ci	u8 cmd = DC390_EEPROM_READ, i;
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci	for (i = 0; i < DC390_EEPROM_LEN; i++) {
3358c2ecf20Sopenharmony_ci		pci_write_config_byte(pdev, 0xc0, 0);
3368c2ecf20Sopenharmony_ci		udelay(160);
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci		dc390_eeprom_prepare_read(pdev, cmd++);
3398c2ecf20Sopenharmony_ci		*ptr++ = dc390_eeprom_get_data(pdev);
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci		pci_write_config_byte(pdev, 0x80, 0);
3428c2ecf20Sopenharmony_ci		pci_write_config_byte(pdev, 0x80, 0);
3438c2ecf20Sopenharmony_ci		udelay(160);
3448c2ecf20Sopenharmony_ci	}
3458c2ecf20Sopenharmony_ci}
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_cistatic void dc390_check_eeprom(struct esp *esp)
3488c2ecf20Sopenharmony_ci{
3498c2ecf20Sopenharmony_ci	struct pci_dev *pdev = to_pci_dev(esp->dev);
3508c2ecf20Sopenharmony_ci	u8 EEbuf[128];
3518c2ecf20Sopenharmony_ci	u16 *ptr = (u16 *)EEbuf, wval = 0;
3528c2ecf20Sopenharmony_ci	int i;
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci	dc390_read_eeprom(pdev, ptr);
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci	for (i = 0; i < DC390_EEPROM_LEN; i++, ptr++)
3578c2ecf20Sopenharmony_ci		wval += *ptr;
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci	/* no Tekram EEprom found */
3608c2ecf20Sopenharmony_ci	if (wval != 0x1234) {
3618c2ecf20Sopenharmony_ci		dev_printk(KERN_INFO, &pdev->dev,
3628c2ecf20Sopenharmony_ci			   "No valid Tekram EEprom found\n");
3638c2ecf20Sopenharmony_ci		return;
3648c2ecf20Sopenharmony_ci	}
3658c2ecf20Sopenharmony_ci	esp->scsi_id = EEbuf[DC390_EE_ADAPT_SCSI_ID];
3668c2ecf20Sopenharmony_ci	esp->num_tags = 2 << EEbuf[DC390_EE_TAG_CMD_NUM];
3678c2ecf20Sopenharmony_ci	if (EEbuf[DC390_EE_MODE2] & DC390_EE_MODE2_ACTIVE_NEGATION)
3688c2ecf20Sopenharmony_ci		esp->config4 |= ESP_CONFIG4_RADE | ESP_CONFIG4_RAE;
3698c2ecf20Sopenharmony_ci}
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_cistatic int pci_esp_probe_one(struct pci_dev *pdev,
3728c2ecf20Sopenharmony_ci			      const struct pci_device_id *id)
3738c2ecf20Sopenharmony_ci{
3748c2ecf20Sopenharmony_ci	struct scsi_host_template *hostt = &scsi_esp_template;
3758c2ecf20Sopenharmony_ci	int err = -ENODEV;
3768c2ecf20Sopenharmony_ci	struct Scsi_Host *shost;
3778c2ecf20Sopenharmony_ci	struct esp *esp;
3788c2ecf20Sopenharmony_ci	struct pci_esp_priv *pep;
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	if (pci_enable_device(pdev)) {
3818c2ecf20Sopenharmony_ci		dev_printk(KERN_INFO, &pdev->dev, "cannot enable device\n");
3828c2ecf20Sopenharmony_ci		return -ENODEV;
3838c2ecf20Sopenharmony_ci	}
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_ci	if (dma_set_mask(&pdev->dev, DMA_BIT_MASK(32))) {
3868c2ecf20Sopenharmony_ci		dev_printk(KERN_INFO, &pdev->dev,
3878c2ecf20Sopenharmony_ci			   "failed to set 32bit DMA mask\n");
3888c2ecf20Sopenharmony_ci		goto fail_disable_device;
3898c2ecf20Sopenharmony_ci	}
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci	shost = scsi_host_alloc(hostt, sizeof(struct esp));
3928c2ecf20Sopenharmony_ci	if (!shost) {
3938c2ecf20Sopenharmony_ci		dev_printk(KERN_INFO, &pdev->dev,
3948c2ecf20Sopenharmony_ci			   "failed to allocate scsi host\n");
3958c2ecf20Sopenharmony_ci		err = -ENOMEM;
3968c2ecf20Sopenharmony_ci		goto fail_disable_device;
3978c2ecf20Sopenharmony_ci	}
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci	pep = kzalloc(sizeof(struct pci_esp_priv), GFP_KERNEL);
4008c2ecf20Sopenharmony_ci	if (!pep) {
4018c2ecf20Sopenharmony_ci		dev_printk(KERN_INFO, &pdev->dev,
4028c2ecf20Sopenharmony_ci			   "failed to allocate esp_priv\n");
4038c2ecf20Sopenharmony_ci		err = -ENOMEM;
4048c2ecf20Sopenharmony_ci		goto fail_host_alloc;
4058c2ecf20Sopenharmony_ci	}
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ci	esp = shost_priv(shost);
4088c2ecf20Sopenharmony_ci	esp->host = shost;
4098c2ecf20Sopenharmony_ci	esp->dev = &pdev->dev;
4108c2ecf20Sopenharmony_ci	esp->ops = &pci_esp_ops;
4118c2ecf20Sopenharmony_ci	/*
4128c2ecf20Sopenharmony_ci	 * The am53c974 HBA has a design flaw of generating
4138c2ecf20Sopenharmony_ci	 * spurious DMA completion interrupts when using
4148c2ecf20Sopenharmony_ci	 * DMA for command submission.
4158c2ecf20Sopenharmony_ci	 */
4168c2ecf20Sopenharmony_ci	esp->flags |= ESP_FLAG_USE_FIFO;
4178c2ecf20Sopenharmony_ci	/*
4188c2ecf20Sopenharmony_ci	 * Enable CONFIG2_FENAB to allow for large DMA transfers
4198c2ecf20Sopenharmony_ci	 */
4208c2ecf20Sopenharmony_ci	if (am53c974_fenab)
4218c2ecf20Sopenharmony_ci		esp->config2 |= ESP_CONFIG2_FENAB;
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci	pep->esp = esp;
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci	if (pci_request_regions(pdev, DRV_MODULE_NAME)) {
4268c2ecf20Sopenharmony_ci		dev_printk(KERN_ERR, &pdev->dev,
4278c2ecf20Sopenharmony_ci			   "pci memory selection failed\n");
4288c2ecf20Sopenharmony_ci		goto fail_priv_alloc;
4298c2ecf20Sopenharmony_ci	}
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci	esp->regs = pci_iomap(pdev, 0, pci_resource_len(pdev, 0));
4328c2ecf20Sopenharmony_ci	if (!esp->regs) {
4338c2ecf20Sopenharmony_ci		dev_printk(KERN_ERR, &pdev->dev, "pci I/O map failed\n");
4348c2ecf20Sopenharmony_ci		err = -EINVAL;
4358c2ecf20Sopenharmony_ci		goto fail_release_regions;
4368c2ecf20Sopenharmony_ci	}
4378c2ecf20Sopenharmony_ci	esp->dma_regs = esp->regs;
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci	pci_set_master(pdev);
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci	esp->command_block = dma_alloc_coherent(&pdev->dev, 16,
4428c2ecf20Sopenharmony_ci			&esp->command_block_dma, GFP_KERNEL);
4438c2ecf20Sopenharmony_ci	if (!esp->command_block) {
4448c2ecf20Sopenharmony_ci		dev_printk(KERN_ERR, &pdev->dev,
4458c2ecf20Sopenharmony_ci			   "failed to allocate command block\n");
4468c2ecf20Sopenharmony_ci		err = -ENOMEM;
4478c2ecf20Sopenharmony_ci		goto fail_unmap_regs;
4488c2ecf20Sopenharmony_ci	}
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci	pci_set_drvdata(pdev, pep);
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_ci	err = request_irq(pdev->irq, scsi_esp_intr, IRQF_SHARED,
4538c2ecf20Sopenharmony_ci			  DRV_MODULE_NAME, esp);
4548c2ecf20Sopenharmony_ci	if (err < 0) {
4558c2ecf20Sopenharmony_ci		dev_printk(KERN_ERR, &pdev->dev, "failed to register IRQ\n");
4568c2ecf20Sopenharmony_ci		goto fail_unmap_command_block;
4578c2ecf20Sopenharmony_ci	}
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci	esp->scsi_id = 7;
4608c2ecf20Sopenharmony_ci	dc390_check_eeprom(esp);
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ci	shost->this_id = esp->scsi_id;
4638c2ecf20Sopenharmony_ci	shost->max_id = 8;
4648c2ecf20Sopenharmony_ci	shost->irq = pdev->irq;
4658c2ecf20Sopenharmony_ci	shost->io_port = pci_resource_start(pdev, 0);
4668c2ecf20Sopenharmony_ci	shost->n_io_port = pci_resource_len(pdev, 0);
4678c2ecf20Sopenharmony_ci	shost->unique_id = shost->io_port;
4688c2ecf20Sopenharmony_ci	esp->scsi_id_mask = (1 << esp->scsi_id);
4698c2ecf20Sopenharmony_ci	/* Assume 40MHz clock */
4708c2ecf20Sopenharmony_ci	esp->cfreq = 40000000;
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_ci	err = scsi_esp_register(esp);
4738c2ecf20Sopenharmony_ci	if (err)
4748c2ecf20Sopenharmony_ci		goto fail_free_irq;
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ci	return 0;
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_cifail_free_irq:
4798c2ecf20Sopenharmony_ci	free_irq(pdev->irq, esp);
4808c2ecf20Sopenharmony_cifail_unmap_command_block:
4818c2ecf20Sopenharmony_ci	pci_set_drvdata(pdev, NULL);
4828c2ecf20Sopenharmony_ci	dma_free_coherent(&pdev->dev, 16, esp->command_block,
4838c2ecf20Sopenharmony_ci			  esp->command_block_dma);
4848c2ecf20Sopenharmony_cifail_unmap_regs:
4858c2ecf20Sopenharmony_ci	pci_iounmap(pdev, esp->regs);
4868c2ecf20Sopenharmony_cifail_release_regions:
4878c2ecf20Sopenharmony_ci	pci_release_regions(pdev);
4888c2ecf20Sopenharmony_cifail_priv_alloc:
4898c2ecf20Sopenharmony_ci	kfree(pep);
4908c2ecf20Sopenharmony_cifail_host_alloc:
4918c2ecf20Sopenharmony_ci	scsi_host_put(shost);
4928c2ecf20Sopenharmony_cifail_disable_device:
4938c2ecf20Sopenharmony_ci	pci_disable_device(pdev);
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ci	return err;
4968c2ecf20Sopenharmony_ci}
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_cistatic void pci_esp_remove_one(struct pci_dev *pdev)
4998c2ecf20Sopenharmony_ci{
5008c2ecf20Sopenharmony_ci	struct pci_esp_priv *pep = pci_get_drvdata(pdev);
5018c2ecf20Sopenharmony_ci	struct esp *esp = pep->esp;
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_ci	scsi_esp_unregister(esp);
5048c2ecf20Sopenharmony_ci	free_irq(pdev->irq, esp);
5058c2ecf20Sopenharmony_ci	pci_set_drvdata(pdev, NULL);
5068c2ecf20Sopenharmony_ci	dma_free_coherent(&pdev->dev, 16, esp->command_block,
5078c2ecf20Sopenharmony_ci			  esp->command_block_dma);
5088c2ecf20Sopenharmony_ci	pci_iounmap(pdev, esp->regs);
5098c2ecf20Sopenharmony_ci	pci_release_regions(pdev);
5108c2ecf20Sopenharmony_ci	pci_disable_device(pdev);
5118c2ecf20Sopenharmony_ci	kfree(pep);
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_ci	scsi_host_put(esp->host);
5148c2ecf20Sopenharmony_ci}
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_cistatic struct pci_device_id am53c974_pci_tbl[] = {
5178c2ecf20Sopenharmony_ci	{ PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_SCSI,
5188c2ecf20Sopenharmony_ci		PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
5198c2ecf20Sopenharmony_ci	{ }
5208c2ecf20Sopenharmony_ci};
5218c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, am53c974_pci_tbl);
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_cistatic struct pci_driver am53c974_driver = {
5248c2ecf20Sopenharmony_ci	.name           = DRV_MODULE_NAME,
5258c2ecf20Sopenharmony_ci	.id_table       = am53c974_pci_tbl,
5268c2ecf20Sopenharmony_ci	.probe          = pci_esp_probe_one,
5278c2ecf20Sopenharmony_ci	.remove         = pci_esp_remove_one,
5288c2ecf20Sopenharmony_ci};
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_cimodule_pci_driver(am53c974_driver);
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("AM53C974 SCSI driver");
5338c2ecf20Sopenharmony_ciMODULE_AUTHOR("Hannes Reinecke <hare@suse.de>");
5348c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
5358c2ecf20Sopenharmony_ciMODULE_VERSION(DRV_MODULE_VERSION);
5368c2ecf20Sopenharmony_ciMODULE_ALIAS("tmscsim");
5378c2ecf20Sopenharmony_ci
5388c2ecf20Sopenharmony_cimodule_param(am53c974_debug, bool, 0644);
5398c2ecf20Sopenharmony_ciMODULE_PARM_DESC(am53c974_debug, "Enable debugging");
5408c2ecf20Sopenharmony_ci
5418c2ecf20Sopenharmony_cimodule_param(am53c974_fenab, bool, 0444);
5428c2ecf20Sopenharmony_ciMODULE_PARM_DESC(am53c974_fenab, "Enable 24-bit DMA transfer sizes");
543