| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* | 
 | 2 |  *  sata_sx4.c - Promise SATA | 
 | 3 |  * | 
 | 4 |  *  Maintained by:  Jeff Garzik <jgarzik@pobox.com> | 
 | 5 |  *  		    Please ALWAYS copy linux-ide@vger.kernel.org | 
 | 6 |  *		    on emails. | 
 | 7 |  * | 
 | 8 |  *  Copyright 2003-2004 Red Hat, Inc. | 
 | 9 |  * | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 10 |  * | 
| Jeff Garzik | af36d7f | 2005-08-28 20:18:39 -0400 | [diff] [blame] | 11 |  *  This program is free software; you can redistribute it and/or modify | 
 | 12 |  *  it under the terms of the GNU General Public License as published by | 
 | 13 |  *  the Free Software Foundation; either version 2, or (at your option) | 
 | 14 |  *  any later version. | 
 | 15 |  * | 
 | 16 |  *  This program is distributed in the hope that it will be useful, | 
 | 17 |  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | 
 | 18 |  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
 | 19 |  *  GNU General Public License for more details. | 
 | 20 |  * | 
 | 21 |  *  You should have received a copy of the GNU General Public License | 
 | 22 |  *  along with this program; see the file COPYING.  If not, write to | 
 | 23 |  *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. | 
 | 24 |  * | 
 | 25 |  * | 
 | 26 |  *  libata documentation is available via 'make {ps|pdf}docs', | 
 | 27 |  *  as Documentation/DocBook/libata.* | 
 | 28 |  * | 
 | 29 |  *  Hardware documentation available under NDA. | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 30 |  * | 
 | 31 |  */ | 
 | 32 |  | 
| Jeff Garzik | a09060f | 2007-05-28 08:17:06 -0400 | [diff] [blame] | 33 | /* | 
 | 34 | 	Theory of operation | 
 | 35 | 	------------------- | 
 | 36 |  | 
 | 37 | 	The SX4 (PDC20621) chip features a single Host DMA (HDMA) copy | 
 | 38 | 	engine, DIMM memory, and four ATA engines (one per SATA port). | 
 | 39 | 	Data is copied to/from DIMM memory by the HDMA engine, before | 
 | 40 | 	handing off to one (or more) of the ATA engines.  The ATA | 
 | 41 | 	engines operate solely on DIMM memory. | 
 | 42 |  | 
 | 43 | 	The SX4 behaves like a PATA chip, with no SATA controls or | 
 | 44 | 	knowledge whatsoever, leading to the presumption that | 
 | 45 | 	PATA<->SATA bridges exist on SX4 boards, external to the | 
 | 46 | 	PDC20621 chip itself. | 
 | 47 |  | 
 | 48 | 	The chip is quite capable, supporting an XOR engine and linked | 
 | 49 | 	hardware commands (permits a string to transactions to be | 
 | 50 | 	submitted and waited-on as a single unit), and an optional | 
 | 51 | 	microprocessor. | 
 | 52 |  | 
 | 53 | 	The limiting factor is largely software.  This Linux driver was | 
 | 54 | 	written to multiplex the single HDMA engine to copy disk | 
 | 55 | 	transactions into a fixed DIMM memory space, from where an ATA | 
 | 56 | 	engine takes over.  As a result, each WRITE looks like this: | 
 | 57 |  | 
 | 58 | 		submit HDMA packet to hardware | 
 | 59 | 		hardware copies data from system memory to DIMM | 
 | 60 | 		hardware raises interrupt | 
 | 61 |  | 
 | 62 | 		submit ATA packet to hardware | 
 | 63 | 		hardware executes ATA WRITE command, w/ data in DIMM | 
 | 64 | 		hardware raises interrupt | 
| Jeff Garzik | 2dcb407 | 2007-10-19 06:42:56 -0400 | [diff] [blame] | 65 |  | 
| Jeff Garzik | a09060f | 2007-05-28 08:17:06 -0400 | [diff] [blame] | 66 | 	and each READ looks like this: | 
 | 67 |  | 
 | 68 | 		submit ATA packet to hardware | 
 | 69 | 		hardware executes ATA READ command, w/ data in DIMM | 
 | 70 | 		hardware raises interrupt | 
| Jeff Garzik | 2dcb407 | 2007-10-19 06:42:56 -0400 | [diff] [blame] | 71 |  | 
| Jeff Garzik | a09060f | 2007-05-28 08:17:06 -0400 | [diff] [blame] | 72 | 		submit HDMA packet to hardware | 
 | 73 | 		hardware copies data from DIMM to system memory | 
 | 74 | 		hardware raises interrupt | 
 | 75 |  | 
 | 76 | 	This is a very slow, lock-step way of doing things that can | 
 | 77 | 	certainly be improved by motivated kernel hackers. | 
 | 78 |  | 
 | 79 |  */ | 
 | 80 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 81 | #include <linux/kernel.h> | 
 | 82 | #include <linux/module.h> | 
 | 83 | #include <linux/pci.h> | 
 | 84 | #include <linux/init.h> | 
 | 85 | #include <linux/blkdev.h> | 
 | 86 | #include <linux/delay.h> | 
 | 87 | #include <linux/interrupt.h> | 
| Jeff Garzik | a9524a7 | 2005-10-30 14:39:11 -0500 | [diff] [blame] | 88 | #include <linux/device.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 89 | #include <scsi/scsi_host.h> | 
| Jeff Garzik | 193515d | 2005-11-07 00:59:37 -0500 | [diff] [blame] | 90 | #include <scsi/scsi_cmnd.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 91 | #include <linux/libata.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 92 | #include "sata_promise.h" | 
 | 93 |  | 
 | 94 | #define DRV_NAME	"sata_sx4" | 
| Jeff Garzik | 2a3103c | 2007-08-31 04:54:06 -0400 | [diff] [blame] | 95 | #define DRV_VERSION	"0.12" | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 96 |  | 
 | 97 |  | 
 | 98 | enum { | 
| Tejun Heo | 0d5ff56 | 2007-02-01 15:06:36 +0900 | [diff] [blame] | 99 | 	PDC_MMIO_BAR		= 3, | 
 | 100 | 	PDC_DIMM_BAR		= 4, | 
 | 101 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 102 | 	PDC_PRD_TBL		= 0x44,	/* Direct command DMA table addr */ | 
 | 103 |  | 
 | 104 | 	PDC_PKT_SUBMIT		= 0x40, /* Command packet pointer addr */ | 
 | 105 | 	PDC_HDMA_PKT_SUBMIT	= 0x100, /* Host DMA packet pointer addr */ | 
 | 106 | 	PDC_INT_SEQMASK		= 0x40,	/* Mask of asserted SEQ INTs */ | 
 | 107 | 	PDC_HDMA_CTLSTAT	= 0x12C, /* Host DMA control / status */ | 
 | 108 |  | 
| Jeff Garzik | a09060f | 2007-05-28 08:17:06 -0400 | [diff] [blame] | 109 | 	PDC_CTLSTAT		= 0x60,	/* IDEn control / status */ | 
 | 110 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 111 | 	PDC_20621_SEQCTL	= 0x400, | 
 | 112 | 	PDC_20621_SEQMASK	= 0x480, | 
 | 113 | 	PDC_20621_GENERAL_CTL	= 0x484, | 
 | 114 | 	PDC_20621_PAGE_SIZE	= (32 * 1024), | 
 | 115 |  | 
 | 116 | 	/* chosen, not constant, values; we design our own DIMM mem map */ | 
 | 117 | 	PDC_20621_DIMM_WINDOW	= 0x0C,	/* page# for 32K DIMM window */ | 
 | 118 | 	PDC_20621_DIMM_BASE	= 0x00200000, | 
 | 119 | 	PDC_20621_DIMM_DATA	= (64 * 1024), | 
 | 120 | 	PDC_DIMM_DATA_STEP	= (256 * 1024), | 
 | 121 | 	PDC_DIMM_WINDOW_STEP	= (8 * 1024), | 
 | 122 | 	PDC_DIMM_HOST_PRD	= (6 * 1024), | 
 | 123 | 	PDC_DIMM_HOST_PKT	= (128 * 0), | 
 | 124 | 	PDC_DIMM_HPKT_PRD	= (128 * 1), | 
 | 125 | 	PDC_DIMM_ATA_PKT	= (128 * 2), | 
 | 126 | 	PDC_DIMM_APKT_PRD	= (128 * 3), | 
 | 127 | 	PDC_DIMM_HEADER_SZ	= PDC_DIMM_APKT_PRD + 128, | 
 | 128 | 	PDC_PAGE_WINDOW		= 0x40, | 
 | 129 | 	PDC_PAGE_DATA		= PDC_PAGE_WINDOW + | 
 | 130 | 				  (PDC_20621_DIMM_DATA / PDC_20621_PAGE_SIZE), | 
 | 131 | 	PDC_PAGE_SET		= PDC_DIMM_DATA_STEP / PDC_20621_PAGE_SIZE, | 
 | 132 |  | 
 | 133 | 	PDC_CHIP0_OFS		= 0xC0000, /* offset of chip #0 */ | 
 | 134 |  | 
 | 135 | 	PDC_20621_ERR_MASK	= (1<<19) | (1<<20) | (1<<21) | (1<<22) | | 
 | 136 | 				  (1<<23), | 
 | 137 |  | 
 | 138 | 	board_20621		= 0,	/* FastTrak S150 SX4 */ | 
 | 139 |  | 
| Jeff Garzik | b2d46b6 | 2007-05-27 22:58:54 -0400 | [diff] [blame] | 140 | 	PDC_MASK_INT		= (1 << 10), /* HDMA/ATA mask int */ | 
 | 141 | 	PDC_RESET		= (1 << 11), /* HDMA/ATA reset */ | 
| Jeff Garzik | a09060f | 2007-05-28 08:17:06 -0400 | [diff] [blame] | 142 | 	PDC_DMA_ENABLE		= (1 << 7),  /* DMA start/stop */ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 143 |  | 
 | 144 | 	PDC_MAX_HDMA		= 32, | 
 | 145 | 	PDC_HDMA_Q_MASK		= (PDC_MAX_HDMA - 1), | 
 | 146 |  | 
| Jeff Garzik | b2d46b6 | 2007-05-27 22:58:54 -0400 | [diff] [blame] | 147 | 	PDC_DIMM0_SPD_DEV_ADDRESS	= 0x50, | 
 | 148 | 	PDC_DIMM1_SPD_DEV_ADDRESS	= 0x51, | 
 | 149 | 	PDC_I2C_CONTROL			= 0x48, | 
 | 150 | 	PDC_I2C_ADDR_DATA		= 0x4C, | 
 | 151 | 	PDC_DIMM0_CONTROL		= 0x80, | 
 | 152 | 	PDC_DIMM1_CONTROL		= 0x84, | 
 | 153 | 	PDC_SDRAM_CONTROL		= 0x88, | 
 | 154 | 	PDC_I2C_WRITE			= 0,		/* master -> slave */ | 
 | 155 | 	PDC_I2C_READ			= (1 << 6),	/* master <- slave */ | 
 | 156 | 	PDC_I2C_START			= (1 << 7),	/* start I2C proto */ | 
 | 157 | 	PDC_I2C_MASK_INT		= (1 << 5),	/* mask I2C interrupt */ | 
 | 158 | 	PDC_I2C_COMPLETE		= (1 << 16),	/* I2C normal compl. */ | 
 | 159 | 	PDC_I2C_NO_ACK			= (1 << 20),	/* slave no-ack addr */ | 
 | 160 | 	PDC_DIMM_SPD_SUBADDRESS_START	= 0x00, | 
 | 161 | 	PDC_DIMM_SPD_SUBADDRESS_END	= 0x7F, | 
 | 162 | 	PDC_DIMM_SPD_ROW_NUM		= 3, | 
 | 163 | 	PDC_DIMM_SPD_COLUMN_NUM		= 4, | 
 | 164 | 	PDC_DIMM_SPD_MODULE_ROW		= 5, | 
 | 165 | 	PDC_DIMM_SPD_TYPE		= 11, | 
 | 166 | 	PDC_DIMM_SPD_FRESH_RATE		= 12, | 
 | 167 | 	PDC_DIMM_SPD_BANK_NUM		= 17, | 
 | 168 | 	PDC_DIMM_SPD_CAS_LATENCY	= 18, | 
 | 169 | 	PDC_DIMM_SPD_ATTRIBUTE		= 21, | 
 | 170 | 	PDC_DIMM_SPD_ROW_PRE_CHARGE	= 27, | 
 | 171 | 	PDC_DIMM_SPD_ROW_ACTIVE_DELAY	= 28, | 
 | 172 | 	PDC_DIMM_SPD_RAS_CAS_DELAY	= 29, | 
 | 173 | 	PDC_DIMM_SPD_ACTIVE_PRECHARGE	= 30, | 
 | 174 | 	PDC_DIMM_SPD_SYSTEM_FREQ	= 126, | 
 | 175 | 	PDC_CTL_STATUS			= 0x08, | 
 | 176 | 	PDC_DIMM_WINDOW_CTLR		= 0x0C, | 
 | 177 | 	PDC_TIME_CONTROL		= 0x3C, | 
 | 178 | 	PDC_TIME_PERIOD			= 0x40, | 
 | 179 | 	PDC_TIME_COUNTER		= 0x44, | 
 | 180 | 	PDC_GENERAL_CTLR		= 0x484, | 
 | 181 | 	PCI_PLL_INIT			= 0x8A531824, | 
 | 182 | 	PCI_X_TCOUNT			= 0xEE1E5CFF, | 
 | 183 |  | 
 | 184 | 	/* PDC_TIME_CONTROL bits */ | 
 | 185 | 	PDC_TIMER_BUZZER		= (1 << 10), | 
 | 186 | 	PDC_TIMER_MODE_PERIODIC		= 0,		/* bits 9:8 == 00 */ | 
 | 187 | 	PDC_TIMER_MODE_ONCE		= (1 << 8),	/* bits 9:8 == 01 */ | 
 | 188 | 	PDC_TIMER_ENABLE		= (1 << 7), | 
 | 189 | 	PDC_TIMER_MASK_INT		= (1 << 5), | 
 | 190 | 	PDC_TIMER_SEQ_MASK		= 0x1f,		/* SEQ ID for timer */ | 
 | 191 | 	PDC_TIMER_DEFAULT		= PDC_TIMER_MODE_ONCE | | 
 | 192 | 					  PDC_TIMER_ENABLE | | 
 | 193 | 					  PDC_TIMER_MASK_INT, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 194 | }; | 
 | 195 |  | 
 | 196 |  | 
 | 197 | struct pdc_port_priv { | 
 | 198 | 	u8			dimm_buf[(ATA_PRD_SZ * ATA_MAX_PRD) + 512]; | 
 | 199 | 	u8			*pkt; | 
 | 200 | 	dma_addr_t		pkt_dma; | 
 | 201 | }; | 
 | 202 |  | 
 | 203 | struct pdc_host_priv { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 204 | 	unsigned int		doing_hdma; | 
 | 205 | 	unsigned int		hdma_prod; | 
 | 206 | 	unsigned int		hdma_cons; | 
 | 207 | 	struct { | 
 | 208 | 		struct ata_queued_cmd *qc; | 
 | 209 | 		unsigned int	seq; | 
 | 210 | 		unsigned long	pkt_ofs; | 
 | 211 | 	} hdma[32]; | 
 | 212 | }; | 
 | 213 |  | 
 | 214 |  | 
| Jeff Garzik | 5796d1c | 2007-10-26 00:03:37 -0400 | [diff] [blame] | 215 | static int pdc_sata_init_one(struct pci_dev *pdev, const struct pci_device_id *ent); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 216 | static void pdc_eng_timeout(struct ata_port *ap); | 
| Jeff Garzik | 5796d1c | 2007-10-26 00:03:37 -0400 | [diff] [blame] | 217 | static void pdc_20621_phy_reset(struct ata_port *ap); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 218 | static int pdc_port_start(struct ata_port *ap); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 219 | static void pdc20621_qc_prep(struct ata_queued_cmd *qc); | 
| Jeff Garzik | 057ace5 | 2005-10-22 14:27:05 -0400 | [diff] [blame] | 220 | static void pdc_tf_load_mmio(struct ata_port *ap, const struct ata_taskfile *tf); | 
 | 221 | static void pdc_exec_command_mmio(struct ata_port *ap, const struct ata_taskfile *tf); | 
| Tejun Heo | 4447d35 | 2007-04-17 23:44:08 +0900 | [diff] [blame] | 222 | static unsigned int pdc20621_dimm_init(struct ata_host *host); | 
 | 223 | static int pdc20621_detect_dimm(struct ata_host *host); | 
 | 224 | static unsigned int pdc20621_i2c_read(struct ata_host *host, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 225 | 				      u32 device, u32 subaddr, u32 *pdata); | 
| Tejun Heo | 4447d35 | 2007-04-17 23:44:08 +0900 | [diff] [blame] | 226 | static int pdc20621_prog_dimm0(struct ata_host *host); | 
 | 227 | static unsigned int pdc20621_prog_dimm_global(struct ata_host *host); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 228 | #ifdef ATA_VERBOSE_DEBUG | 
| Tejun Heo | 4447d35 | 2007-04-17 23:44:08 +0900 | [diff] [blame] | 229 | static void pdc20621_get_from_dimm(struct ata_host *host, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 230 | 				   void *psource, u32 offset, u32 size); | 
 | 231 | #endif | 
| Tejun Heo | 4447d35 | 2007-04-17 23:44:08 +0900 | [diff] [blame] | 232 | static void pdc20621_put_to_dimm(struct ata_host *host, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 233 | 				 void *psource, u32 offset, u32 size); | 
 | 234 | static void pdc20621_irq_clear(struct ata_port *ap); | 
| Tejun Heo | 9363c38 | 2008-04-07 22:47:16 +0900 | [diff] [blame] | 235 | static unsigned int pdc20621_qc_issue(struct ata_queued_cmd *qc); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 236 |  | 
 | 237 |  | 
| Jeff Garzik | 193515d | 2005-11-07 00:59:37 -0500 | [diff] [blame] | 238 | static struct scsi_host_template pdc_sata_sht = { | 
| Tejun Heo | 68d1d07 | 2008-03-25 12:22:49 +0900 | [diff] [blame] | 239 | 	ATA_BASE_SHT(DRV_NAME), | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 240 | 	.sg_tablesize		= LIBATA_MAX_PRD, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 241 | 	.dma_boundary		= ATA_DMA_BOUNDARY, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 242 | }; | 
 | 243 |  | 
| Tejun Heo | 029cfd6 | 2008-03-25 12:22:49 +0900 | [diff] [blame] | 244 | /* TODO: inherit from base port_ops after converting to new EH */ | 
 | 245 | static struct ata_port_operations pdc_20621_ops = { | 
| Tejun Heo | 5682ed3 | 2008-04-07 22:47:16 +0900 | [diff] [blame] | 246 | 	.sff_tf_load		= pdc_tf_load_mmio, | 
 | 247 | 	.sff_tf_read		= ata_sff_tf_read, | 
 | 248 | 	.sff_check_status	= ata_sff_check_status, | 
 | 249 | 	.sff_exec_command	= pdc_exec_command_mmio, | 
 | 250 | 	.sff_dev_select		= ata_sff_dev_select, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 251 | 	.phy_reset		= pdc_20621_phy_reset, | 
 | 252 | 	.qc_prep		= pdc20621_qc_prep, | 
| Tejun Heo | 9363c38 | 2008-04-07 22:47:16 +0900 | [diff] [blame] | 253 | 	.qc_issue		= pdc20621_qc_issue, | 
| Tejun Heo | 22183bf | 2008-04-07 22:47:20 +0900 | [diff] [blame] | 254 | 	.qc_fill_rtf		= ata_sff_qc_fill_rtf, | 
| Tejun Heo | 5682ed3 | 2008-04-07 22:47:16 +0900 | [diff] [blame] | 255 | 	.sff_data_xfer		= ata_sff_data_xfer, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 256 | 	.eng_timeout		= pdc_eng_timeout, | 
| Tejun Heo | 5682ed3 | 2008-04-07 22:47:16 +0900 | [diff] [blame] | 257 | 	.sff_irq_clear		= pdc20621_irq_clear, | 
 | 258 | 	.sff_irq_on		= ata_sff_irq_on, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 259 | 	.port_start		= pdc_port_start, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 260 | }; | 
 | 261 |  | 
| Arjan van de Ven | 98ac62d | 2005-11-28 10:06:23 +0100 | [diff] [blame] | 262 | static const struct ata_port_info pdc_port_info[] = { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 263 | 	/* board_20621 */ | 
 | 264 | 	{ | 
| Jeff Garzik | cca3974 | 2006-08-24 03:19:22 -0400 | [diff] [blame] | 265 | 		.flags		= ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | | 
| Jeff Garzik | 5063019 | 2005-12-13 02:29:45 -0500 | [diff] [blame] | 266 | 				  ATA_FLAG_SRST | ATA_FLAG_MMIO | | 
| Albert Lee | 1f3461a | 2006-05-23 18:12:30 +0800 | [diff] [blame] | 267 | 				  ATA_FLAG_NO_ATAPI | ATA_FLAG_PIO_POLLING, | 
| Erik Inge Bolsø | 14bdef9 | 2009-03-14 21:38:24 +0100 | [diff] [blame] | 268 | 		.pio_mask	= ATA_PIO4, | 
 | 269 | 		.mwdma_mask	= ATA_MWDMA2, | 
| Jeff Garzik | 469248a | 2007-07-08 01:13:16 -0400 | [diff] [blame] | 270 | 		.udma_mask	= ATA_UDMA6, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 271 | 		.port_ops	= &pdc_20621_ops, | 
 | 272 | 	}, | 
 | 273 |  | 
 | 274 | }; | 
 | 275 |  | 
| Jeff Garzik | 3b7d697 | 2005-11-10 11:04:11 -0500 | [diff] [blame] | 276 | static const struct pci_device_id pdc_sata_pci_tbl[] = { | 
| Jeff Garzik | 54bb3a9 | 2006-09-27 22:20:11 -0400 | [diff] [blame] | 277 | 	{ PCI_VDEVICE(PROMISE, 0x6622), board_20621 }, | 
 | 278 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 279 | 	{ }	/* terminate list */ | 
 | 280 | }; | 
 | 281 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 282 | static struct pci_driver pdc_sata_pci_driver = { | 
 | 283 | 	.name			= DRV_NAME, | 
 | 284 | 	.id_table		= pdc_sata_pci_tbl, | 
 | 285 | 	.probe			= pdc_sata_init_one, | 
 | 286 | 	.remove			= ata_pci_remove_one, | 
 | 287 | }; | 
 | 288 |  | 
 | 289 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 290 | static int pdc_port_start(struct ata_port *ap) | 
 | 291 | { | 
| Jeff Garzik | cca3974 | 2006-08-24 03:19:22 -0400 | [diff] [blame] | 292 | 	struct device *dev = ap->host->dev; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 293 | 	struct pdc_port_priv *pp; | 
 | 294 | 	int rc; | 
 | 295 |  | 
 | 296 | 	rc = ata_port_start(ap); | 
 | 297 | 	if (rc) | 
 | 298 | 		return rc; | 
 | 299 |  | 
| Tejun Heo | 24dc5f3 | 2007-01-20 16:00:28 +0900 | [diff] [blame] | 300 | 	pp = devm_kzalloc(dev, sizeof(*pp), GFP_KERNEL); | 
 | 301 | 	if (!pp) | 
 | 302 | 		return -ENOMEM; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 303 |  | 
| Tejun Heo | 24dc5f3 | 2007-01-20 16:00:28 +0900 | [diff] [blame] | 304 | 	pp->pkt = dmam_alloc_coherent(dev, 128, &pp->pkt_dma, GFP_KERNEL); | 
 | 305 | 	if (!pp->pkt) | 
 | 306 | 		return -ENOMEM; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 307 |  | 
 | 308 | 	ap->private_data = pp; | 
 | 309 |  | 
 | 310 | 	return 0; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 311 | } | 
 | 312 |  | 
| Jeff Garzik | 5796d1c | 2007-10-26 00:03:37 -0400 | [diff] [blame] | 313 | static void pdc_20621_phy_reset(struct ata_port *ap) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 314 | { | 
 | 315 | 	VPRINTK("ENTER\n"); | 
| Jeff Garzik | 5796d1c | 2007-10-26 00:03:37 -0400 | [diff] [blame] | 316 | 	ap->cbl = ATA_CBL_SATA; | 
 | 317 | 	ata_port_probe(ap); | 
 | 318 | 	ata_bus_reset(ap); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 319 | } | 
 | 320 |  | 
 | 321 | static inline void pdc20621_ata_sg(struct ata_taskfile *tf, u8 *buf, | 
| Jeff Garzik | 5796d1c | 2007-10-26 00:03:37 -0400 | [diff] [blame] | 322 | 				   unsigned int portno, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 323 | 					   unsigned int total_len) | 
 | 324 | { | 
 | 325 | 	u32 addr; | 
 | 326 | 	unsigned int dw = PDC_DIMM_APKT_PRD >> 2; | 
| Al Viro | 4ca4e43 | 2007-12-30 09:32:22 +0000 | [diff] [blame] | 327 | 	__le32 *buf32 = (__le32 *) buf; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 328 |  | 
 | 329 | 	/* output ATA packet S/G table */ | 
 | 330 | 	addr = PDC_20621_DIMM_BASE + PDC_20621_DIMM_DATA + | 
 | 331 | 	       (PDC_DIMM_DATA_STEP * portno); | 
 | 332 | 	VPRINTK("ATA sg addr 0x%x, %d\n", addr, addr); | 
 | 333 | 	buf32[dw] = cpu_to_le32(addr); | 
 | 334 | 	buf32[dw + 1] = cpu_to_le32(total_len | ATA_PRD_EOT); | 
 | 335 |  | 
 | 336 | 	VPRINTK("ATA PSG @ %x == (0x%x, 0x%x)\n", | 
 | 337 | 		PDC_20621_DIMM_BASE + | 
 | 338 | 		       (PDC_DIMM_WINDOW_STEP * portno) + | 
 | 339 | 		       PDC_DIMM_APKT_PRD, | 
 | 340 | 		buf32[dw], buf32[dw + 1]); | 
 | 341 | } | 
 | 342 |  | 
 | 343 | static inline void pdc20621_host_sg(struct ata_taskfile *tf, u8 *buf, | 
| Jeff Garzik | 5796d1c | 2007-10-26 00:03:37 -0400 | [diff] [blame] | 344 | 				    unsigned int portno, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 345 | 					    unsigned int total_len) | 
 | 346 | { | 
 | 347 | 	u32 addr; | 
 | 348 | 	unsigned int dw = PDC_DIMM_HPKT_PRD >> 2; | 
| Al Viro | 4ca4e43 | 2007-12-30 09:32:22 +0000 | [diff] [blame] | 349 | 	__le32 *buf32 = (__le32 *) buf; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 350 |  | 
 | 351 | 	/* output Host DMA packet S/G table */ | 
 | 352 | 	addr = PDC_20621_DIMM_BASE + PDC_20621_DIMM_DATA + | 
 | 353 | 	       (PDC_DIMM_DATA_STEP * portno); | 
 | 354 |  | 
 | 355 | 	buf32[dw] = cpu_to_le32(addr); | 
 | 356 | 	buf32[dw + 1] = cpu_to_le32(total_len | ATA_PRD_EOT); | 
 | 357 |  | 
 | 358 | 	VPRINTK("HOST PSG @ %x == (0x%x, 0x%x)\n", | 
 | 359 | 		PDC_20621_DIMM_BASE + | 
 | 360 | 		       (PDC_DIMM_WINDOW_STEP * portno) + | 
 | 361 | 		       PDC_DIMM_HPKT_PRD, | 
 | 362 | 		buf32[dw], buf32[dw + 1]); | 
 | 363 | } | 
 | 364 |  | 
 | 365 | static inline unsigned int pdc20621_ata_pkt(struct ata_taskfile *tf, | 
 | 366 | 					    unsigned int devno, u8 *buf, | 
 | 367 | 					    unsigned int portno) | 
 | 368 | { | 
 | 369 | 	unsigned int i, dw; | 
| Al Viro | 4ca4e43 | 2007-12-30 09:32:22 +0000 | [diff] [blame] | 370 | 	__le32 *buf32 = (__le32 *) buf; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 371 | 	u8 dev_reg; | 
 | 372 |  | 
 | 373 | 	unsigned int dimm_sg = PDC_20621_DIMM_BASE + | 
 | 374 | 			       (PDC_DIMM_WINDOW_STEP * portno) + | 
 | 375 | 			       PDC_DIMM_APKT_PRD; | 
 | 376 | 	VPRINTK("ENTER, dimm_sg == 0x%x, %d\n", dimm_sg, dimm_sg); | 
 | 377 |  | 
 | 378 | 	i = PDC_DIMM_ATA_PKT; | 
 | 379 |  | 
 | 380 | 	/* | 
 | 381 | 	 * Set up ATA packet | 
 | 382 | 	 */ | 
 | 383 | 	if ((tf->protocol == ATA_PROT_DMA) && (!(tf->flags & ATA_TFLAG_WRITE))) | 
 | 384 | 		buf[i++] = PDC_PKT_READ; | 
 | 385 | 	else if (tf->protocol == ATA_PROT_NODATA) | 
 | 386 | 		buf[i++] = PDC_PKT_NODATA; | 
 | 387 | 	else | 
 | 388 | 		buf[i++] = 0; | 
 | 389 | 	buf[i++] = 0;			/* reserved */ | 
 | 390 | 	buf[i++] = portno + 1;		/* seq. id */ | 
 | 391 | 	buf[i++] = 0xff;		/* delay seq. id */ | 
 | 392 |  | 
 | 393 | 	/* dimm dma S/G, and next-pkt */ | 
 | 394 | 	dw = i >> 2; | 
 | 395 | 	if (tf->protocol == ATA_PROT_NODATA) | 
 | 396 | 		buf32[dw] = 0; | 
 | 397 | 	else | 
 | 398 | 		buf32[dw] = cpu_to_le32(dimm_sg); | 
 | 399 | 	buf32[dw + 1] = 0; | 
 | 400 | 	i += 8; | 
 | 401 |  | 
 | 402 | 	if (devno == 0) | 
 | 403 | 		dev_reg = ATA_DEVICE_OBS; | 
 | 404 | 	else | 
 | 405 | 		dev_reg = ATA_DEVICE_OBS | ATA_DEV1; | 
 | 406 |  | 
 | 407 | 	/* select device */ | 
 | 408 | 	buf[i++] = (1 << 5) | PDC_PKT_CLEAR_BSY | ATA_REG_DEVICE; | 
 | 409 | 	buf[i++] = dev_reg; | 
 | 410 |  | 
 | 411 | 	/* device control register */ | 
 | 412 | 	buf[i++] = (1 << 5) | PDC_REG_DEVCTL; | 
 | 413 | 	buf[i++] = tf->ctl; | 
 | 414 |  | 
 | 415 | 	return i; | 
 | 416 | } | 
 | 417 |  | 
 | 418 | static inline void pdc20621_host_pkt(struct ata_taskfile *tf, u8 *buf, | 
 | 419 | 				     unsigned int portno) | 
 | 420 | { | 
 | 421 | 	unsigned int dw; | 
| Al Viro | 4ca4e43 | 2007-12-30 09:32:22 +0000 | [diff] [blame] | 422 | 	u32 tmp; | 
 | 423 | 	__le32 *buf32 = (__le32 *) buf; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 424 |  | 
 | 425 | 	unsigned int host_sg = PDC_20621_DIMM_BASE + | 
 | 426 | 			       (PDC_DIMM_WINDOW_STEP * portno) + | 
 | 427 | 			       PDC_DIMM_HOST_PRD; | 
 | 428 | 	unsigned int dimm_sg = PDC_20621_DIMM_BASE + | 
 | 429 | 			       (PDC_DIMM_WINDOW_STEP * portno) + | 
 | 430 | 			       PDC_DIMM_HPKT_PRD; | 
 | 431 | 	VPRINTK("ENTER, dimm_sg == 0x%x, %d\n", dimm_sg, dimm_sg); | 
 | 432 | 	VPRINTK("host_sg == 0x%x, %d\n", host_sg, host_sg); | 
 | 433 |  | 
 | 434 | 	dw = PDC_DIMM_HOST_PKT >> 2; | 
 | 435 |  | 
 | 436 | 	/* | 
 | 437 | 	 * Set up Host DMA packet | 
 | 438 | 	 */ | 
 | 439 | 	if ((tf->protocol == ATA_PROT_DMA) && (!(tf->flags & ATA_TFLAG_WRITE))) | 
 | 440 | 		tmp = PDC_PKT_READ; | 
 | 441 | 	else | 
 | 442 | 		tmp = 0; | 
 | 443 | 	tmp |= ((portno + 1 + 4) << 16);	/* seq. id */ | 
 | 444 | 	tmp |= (0xff << 24);			/* delay seq. id */ | 
 | 445 | 	buf32[dw + 0] = cpu_to_le32(tmp); | 
 | 446 | 	buf32[dw + 1] = cpu_to_le32(host_sg); | 
 | 447 | 	buf32[dw + 2] = cpu_to_le32(dimm_sg); | 
 | 448 | 	buf32[dw + 3] = 0; | 
 | 449 |  | 
 | 450 | 	VPRINTK("HOST PKT @ %x == (0x%x 0x%x 0x%x 0x%x)\n", | 
 | 451 | 		PDC_20621_DIMM_BASE + (PDC_DIMM_WINDOW_STEP * portno) + | 
 | 452 | 			PDC_DIMM_HOST_PKT, | 
 | 453 | 		buf32[dw + 0], | 
 | 454 | 		buf32[dw + 1], | 
 | 455 | 		buf32[dw + 2], | 
 | 456 | 		buf32[dw + 3]); | 
 | 457 | } | 
 | 458 |  | 
 | 459 | static void pdc20621_dma_prep(struct ata_queued_cmd *qc) | 
 | 460 | { | 
| Jeff Garzik | cedc9a4 | 2005-10-05 07:13:30 -0400 | [diff] [blame] | 461 | 	struct scatterlist *sg; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 462 | 	struct ata_port *ap = qc->ap; | 
 | 463 | 	struct pdc_port_priv *pp = ap->private_data; | 
| Tejun Heo | 0d5ff56 | 2007-02-01 15:06:36 +0900 | [diff] [blame] | 464 | 	void __iomem *mmio = ap->host->iomap[PDC_MMIO_BAR]; | 
 | 465 | 	void __iomem *dimm_mmio = ap->host->iomap[PDC_DIMM_BAR]; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 466 | 	unsigned int portno = ap->port_no; | 
| Tejun Heo | ff2aeb1 | 2007-12-05 16:43:11 +0900 | [diff] [blame] | 467 | 	unsigned int i, si, idx, total_len = 0, sgt_len; | 
| Al Viro | 826cd15 | 2008-03-25 05:18:11 +0000 | [diff] [blame] | 468 | 	__le32 *buf = (__le32 *) &pp->dimm_buf[PDC_DIMM_HEADER_SZ]; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 469 |  | 
| Tejun Heo | beec7db | 2006-02-11 19:11:13 +0900 | [diff] [blame] | 470 | 	WARN_ON(!(qc->flags & ATA_QCFLAG_DMAMAP)); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 471 |  | 
| Tejun Heo | 44877b4 | 2007-02-21 01:06:51 +0900 | [diff] [blame] | 472 | 	VPRINTK("ata%u: ENTER\n", ap->print_id); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 473 |  | 
 | 474 | 	/* hard-code chip #0 */ | 
 | 475 | 	mmio += PDC_CHIP0_OFS; | 
 | 476 |  | 
 | 477 | 	/* | 
 | 478 | 	 * Build S/G table | 
 | 479 | 	 */ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 480 | 	idx = 0; | 
| Tejun Heo | ff2aeb1 | 2007-12-05 16:43:11 +0900 | [diff] [blame] | 481 | 	for_each_sg(qc->sg, sg, qc->n_elem, si) { | 
| Jeff Garzik | cedc9a4 | 2005-10-05 07:13:30 -0400 | [diff] [blame] | 482 | 		buf[idx++] = cpu_to_le32(sg_dma_address(sg)); | 
 | 483 | 		buf[idx++] = cpu_to_le32(sg_dma_len(sg)); | 
 | 484 | 		total_len += sg_dma_len(sg); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 485 | 	} | 
 | 486 | 	buf[idx - 1] |= cpu_to_le32(ATA_PRD_EOT); | 
 | 487 | 	sgt_len = idx * 4; | 
 | 488 |  | 
 | 489 | 	/* | 
 | 490 | 	 * Build ATA, host DMA packets | 
 | 491 | 	 */ | 
 | 492 | 	pdc20621_host_sg(&qc->tf, &pp->dimm_buf[0], portno, total_len); | 
 | 493 | 	pdc20621_host_pkt(&qc->tf, &pp->dimm_buf[0], portno); | 
 | 494 |  | 
 | 495 | 	pdc20621_ata_sg(&qc->tf, &pp->dimm_buf[0], portno, total_len); | 
 | 496 | 	i = pdc20621_ata_pkt(&qc->tf, qc->dev->devno, &pp->dimm_buf[0], portno); | 
 | 497 |  | 
 | 498 | 	if (qc->tf.flags & ATA_TFLAG_LBA48) | 
 | 499 | 		i = pdc_prep_lba48(&qc->tf, &pp->dimm_buf[0], i); | 
 | 500 | 	else | 
 | 501 | 		i = pdc_prep_lba28(&qc->tf, &pp->dimm_buf[0], i); | 
 | 502 |  | 
 | 503 | 	pdc_pkt_footer(&qc->tf, &pp->dimm_buf[0], i); | 
 | 504 |  | 
 | 505 | 	/* copy three S/G tables and two packets to DIMM MMIO window */ | 
 | 506 | 	memcpy_toio(dimm_mmio + (portno * PDC_DIMM_WINDOW_STEP), | 
 | 507 | 		    &pp->dimm_buf, PDC_DIMM_HEADER_SZ); | 
 | 508 | 	memcpy_toio(dimm_mmio + (portno * PDC_DIMM_WINDOW_STEP) + | 
 | 509 | 		    PDC_DIMM_HOST_PRD, | 
 | 510 | 		    &pp->dimm_buf[PDC_DIMM_HEADER_SZ], sgt_len); | 
 | 511 |  | 
 | 512 | 	/* force host FIFO dump */ | 
 | 513 | 	writel(0x00000001, mmio + PDC_20621_GENERAL_CTL); | 
 | 514 |  | 
 | 515 | 	readl(dimm_mmio);	/* MMIO PCI posting flush */ | 
 | 516 |  | 
 | 517 | 	VPRINTK("ata pkt buf ofs %u, prd size %u, mmio copied\n", i, sgt_len); | 
 | 518 | } | 
 | 519 |  | 
 | 520 | static void pdc20621_nodata_prep(struct ata_queued_cmd *qc) | 
 | 521 | { | 
 | 522 | 	struct ata_port *ap = qc->ap; | 
 | 523 | 	struct pdc_port_priv *pp = ap->private_data; | 
| Tejun Heo | 0d5ff56 | 2007-02-01 15:06:36 +0900 | [diff] [blame] | 524 | 	void __iomem *mmio = ap->host->iomap[PDC_MMIO_BAR]; | 
 | 525 | 	void __iomem *dimm_mmio = ap->host->iomap[PDC_DIMM_BAR]; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 526 | 	unsigned int portno = ap->port_no; | 
 | 527 | 	unsigned int i; | 
 | 528 |  | 
| Tejun Heo | 44877b4 | 2007-02-21 01:06:51 +0900 | [diff] [blame] | 529 | 	VPRINTK("ata%u: ENTER\n", ap->print_id); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 530 |  | 
 | 531 | 	/* hard-code chip #0 */ | 
 | 532 | 	mmio += PDC_CHIP0_OFS; | 
 | 533 |  | 
 | 534 | 	i = pdc20621_ata_pkt(&qc->tf, qc->dev->devno, &pp->dimm_buf[0], portno); | 
 | 535 |  | 
 | 536 | 	if (qc->tf.flags & ATA_TFLAG_LBA48) | 
 | 537 | 		i = pdc_prep_lba48(&qc->tf, &pp->dimm_buf[0], i); | 
 | 538 | 	else | 
 | 539 | 		i = pdc_prep_lba28(&qc->tf, &pp->dimm_buf[0], i); | 
 | 540 |  | 
 | 541 | 	pdc_pkt_footer(&qc->tf, &pp->dimm_buf[0], i); | 
 | 542 |  | 
 | 543 | 	/* copy three S/G tables and two packets to DIMM MMIO window */ | 
 | 544 | 	memcpy_toio(dimm_mmio + (portno * PDC_DIMM_WINDOW_STEP), | 
 | 545 | 		    &pp->dimm_buf, PDC_DIMM_HEADER_SZ); | 
 | 546 |  | 
 | 547 | 	/* force host FIFO dump */ | 
 | 548 | 	writel(0x00000001, mmio + PDC_20621_GENERAL_CTL); | 
 | 549 |  | 
 | 550 | 	readl(dimm_mmio);	/* MMIO PCI posting flush */ | 
 | 551 |  | 
 | 552 | 	VPRINTK("ata pkt buf ofs %u, mmio copied\n", i); | 
 | 553 | } | 
 | 554 |  | 
 | 555 | static void pdc20621_qc_prep(struct ata_queued_cmd *qc) | 
 | 556 | { | 
 | 557 | 	switch (qc->tf.protocol) { | 
 | 558 | 	case ATA_PROT_DMA: | 
 | 559 | 		pdc20621_dma_prep(qc); | 
 | 560 | 		break; | 
 | 561 | 	case ATA_PROT_NODATA: | 
 | 562 | 		pdc20621_nodata_prep(qc); | 
 | 563 | 		break; | 
 | 564 | 	default: | 
 | 565 | 		break; | 
 | 566 | 	} | 
 | 567 | } | 
 | 568 |  | 
 | 569 | static void __pdc20621_push_hdma(struct ata_queued_cmd *qc, | 
 | 570 | 				 unsigned int seq, | 
 | 571 | 				 u32 pkt_ofs) | 
 | 572 | { | 
 | 573 | 	struct ata_port *ap = qc->ap; | 
| Jeff Garzik | cca3974 | 2006-08-24 03:19:22 -0400 | [diff] [blame] | 574 | 	struct ata_host *host = ap->host; | 
| Tejun Heo | 0d5ff56 | 2007-02-01 15:06:36 +0900 | [diff] [blame] | 575 | 	void __iomem *mmio = host->iomap[PDC_MMIO_BAR]; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 576 |  | 
 | 577 | 	/* hard-code chip #0 */ | 
 | 578 | 	mmio += PDC_CHIP0_OFS; | 
 | 579 |  | 
 | 580 | 	writel(0x00000001, mmio + PDC_20621_SEQCTL + (seq * 4)); | 
 | 581 | 	readl(mmio + PDC_20621_SEQCTL + (seq * 4));	/* flush */ | 
 | 582 |  | 
 | 583 | 	writel(pkt_ofs, mmio + PDC_HDMA_PKT_SUBMIT); | 
 | 584 | 	readl(mmio + PDC_HDMA_PKT_SUBMIT);	/* flush */ | 
 | 585 | } | 
 | 586 |  | 
 | 587 | static void pdc20621_push_hdma(struct ata_queued_cmd *qc, | 
 | 588 | 				unsigned int seq, | 
 | 589 | 				u32 pkt_ofs) | 
 | 590 | { | 
 | 591 | 	struct ata_port *ap = qc->ap; | 
| Jeff Garzik | cca3974 | 2006-08-24 03:19:22 -0400 | [diff] [blame] | 592 | 	struct pdc_host_priv *pp = ap->host->private_data; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 593 | 	unsigned int idx = pp->hdma_prod & PDC_HDMA_Q_MASK; | 
 | 594 |  | 
 | 595 | 	if (!pp->doing_hdma) { | 
 | 596 | 		__pdc20621_push_hdma(qc, seq, pkt_ofs); | 
 | 597 | 		pp->doing_hdma = 1; | 
 | 598 | 		return; | 
 | 599 | 	} | 
 | 600 |  | 
 | 601 | 	pp->hdma[idx].qc = qc; | 
 | 602 | 	pp->hdma[idx].seq = seq; | 
 | 603 | 	pp->hdma[idx].pkt_ofs = pkt_ofs; | 
 | 604 | 	pp->hdma_prod++; | 
 | 605 | } | 
 | 606 |  | 
 | 607 | static void pdc20621_pop_hdma(struct ata_queued_cmd *qc) | 
 | 608 | { | 
 | 609 | 	struct ata_port *ap = qc->ap; | 
| Jeff Garzik | cca3974 | 2006-08-24 03:19:22 -0400 | [diff] [blame] | 610 | 	struct pdc_host_priv *pp = ap->host->private_data; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 611 | 	unsigned int idx = pp->hdma_cons & PDC_HDMA_Q_MASK; | 
 | 612 |  | 
 | 613 | 	/* if nothing on queue, we're done */ | 
 | 614 | 	if (pp->hdma_prod == pp->hdma_cons) { | 
 | 615 | 		pp->doing_hdma = 0; | 
 | 616 | 		return; | 
 | 617 | 	} | 
 | 618 |  | 
 | 619 | 	__pdc20621_push_hdma(pp->hdma[idx].qc, pp->hdma[idx].seq, | 
 | 620 | 			     pp->hdma[idx].pkt_ofs); | 
 | 621 | 	pp->hdma_cons++; | 
 | 622 | } | 
 | 623 |  | 
 | 624 | #ifdef ATA_VERBOSE_DEBUG | 
 | 625 | static void pdc20621_dump_hdma(struct ata_queued_cmd *qc) | 
 | 626 | { | 
 | 627 | 	struct ata_port *ap = qc->ap; | 
 | 628 | 	unsigned int port_no = ap->port_no; | 
| Tejun Heo | 0d5ff56 | 2007-02-01 15:06:36 +0900 | [diff] [blame] | 629 | 	void __iomem *dimm_mmio = ap->host->iomap[PDC_DIMM_BAR]; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 630 |  | 
 | 631 | 	dimm_mmio += (port_no * PDC_DIMM_WINDOW_STEP); | 
 | 632 | 	dimm_mmio += PDC_DIMM_HOST_PKT; | 
 | 633 |  | 
 | 634 | 	printk(KERN_ERR "HDMA[0] == 0x%08X\n", readl(dimm_mmio)); | 
 | 635 | 	printk(KERN_ERR "HDMA[1] == 0x%08X\n", readl(dimm_mmio + 4)); | 
 | 636 | 	printk(KERN_ERR "HDMA[2] == 0x%08X\n", readl(dimm_mmio + 8)); | 
 | 637 | 	printk(KERN_ERR "HDMA[3] == 0x%08X\n", readl(dimm_mmio + 12)); | 
 | 638 | } | 
 | 639 | #else | 
 | 640 | static inline void pdc20621_dump_hdma(struct ata_queued_cmd *qc) { } | 
 | 641 | #endif /* ATA_VERBOSE_DEBUG */ | 
 | 642 |  | 
 | 643 | static void pdc20621_packet_start(struct ata_queued_cmd *qc) | 
 | 644 | { | 
 | 645 | 	struct ata_port *ap = qc->ap; | 
| Jeff Garzik | cca3974 | 2006-08-24 03:19:22 -0400 | [diff] [blame] | 646 | 	struct ata_host *host = ap->host; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 647 | 	unsigned int port_no = ap->port_no; | 
| Tejun Heo | 0d5ff56 | 2007-02-01 15:06:36 +0900 | [diff] [blame] | 648 | 	void __iomem *mmio = host->iomap[PDC_MMIO_BAR]; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 649 | 	unsigned int rw = (qc->tf.flags & ATA_TFLAG_WRITE); | 
 | 650 | 	u8 seq = (u8) (port_no + 1); | 
 | 651 | 	unsigned int port_ofs; | 
 | 652 |  | 
 | 653 | 	/* hard-code chip #0 */ | 
 | 654 | 	mmio += PDC_CHIP0_OFS; | 
 | 655 |  | 
| Tejun Heo | 44877b4 | 2007-02-21 01:06:51 +0900 | [diff] [blame] | 656 | 	VPRINTK("ata%u: ENTER\n", ap->print_id); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 657 |  | 
 | 658 | 	wmb();			/* flush PRD, pkt writes */ | 
 | 659 |  | 
 | 660 | 	port_ofs = PDC_20621_DIMM_BASE + (PDC_DIMM_WINDOW_STEP * port_no); | 
 | 661 |  | 
 | 662 | 	/* if writing, we (1) DMA to DIMM, then (2) do ATA command */ | 
 | 663 | 	if (rw && qc->tf.protocol == ATA_PROT_DMA) { | 
 | 664 | 		seq += 4; | 
 | 665 |  | 
 | 666 | 		pdc20621_dump_hdma(qc); | 
 | 667 | 		pdc20621_push_hdma(qc, seq, port_ofs + PDC_DIMM_HOST_PKT); | 
 | 668 | 		VPRINTK("queued ofs 0x%x (%u), seq %u\n", | 
 | 669 | 			port_ofs + PDC_DIMM_HOST_PKT, | 
 | 670 | 			port_ofs + PDC_DIMM_HOST_PKT, | 
 | 671 | 			seq); | 
 | 672 | 	} else { | 
 | 673 | 		writel(0x00000001, mmio + PDC_20621_SEQCTL + (seq * 4)); | 
 | 674 | 		readl(mmio + PDC_20621_SEQCTL + (seq * 4));	/* flush */ | 
 | 675 |  | 
 | 676 | 		writel(port_ofs + PDC_DIMM_ATA_PKT, | 
| Tejun Heo | 0d5ff56 | 2007-02-01 15:06:36 +0900 | [diff] [blame] | 677 | 		       ap->ioaddr.cmd_addr + PDC_PKT_SUBMIT); | 
 | 678 | 		readl(ap->ioaddr.cmd_addr + PDC_PKT_SUBMIT); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 679 | 		VPRINTK("submitted ofs 0x%x (%u), seq %u\n", | 
 | 680 | 			port_ofs + PDC_DIMM_ATA_PKT, | 
 | 681 | 			port_ofs + PDC_DIMM_ATA_PKT, | 
 | 682 | 			seq); | 
 | 683 | 	} | 
 | 684 | } | 
 | 685 |  | 
| Tejun Heo | 9363c38 | 2008-04-07 22:47:16 +0900 | [diff] [blame] | 686 | static unsigned int pdc20621_qc_issue(struct ata_queued_cmd *qc) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 687 | { | 
 | 688 | 	switch (qc->tf.protocol) { | 
 | 689 | 	case ATA_PROT_DMA: | 
 | 690 | 	case ATA_PROT_NODATA: | 
 | 691 | 		pdc20621_packet_start(qc); | 
 | 692 | 		return 0; | 
 | 693 |  | 
| Tejun Heo | 0dc3688 | 2007-12-18 16:34:43 -0500 | [diff] [blame] | 694 | 	case ATAPI_PROT_DMA: | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 695 | 		BUG(); | 
 | 696 | 		break; | 
 | 697 |  | 
 | 698 | 	default: | 
 | 699 | 		break; | 
 | 700 | 	} | 
 | 701 |  | 
| Tejun Heo | 9363c38 | 2008-04-07 22:47:16 +0900 | [diff] [blame] | 702 | 	return ata_sff_qc_issue(qc); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 703 | } | 
 | 704 |  | 
| Jeff Garzik | 5796d1c | 2007-10-26 00:03:37 -0400 | [diff] [blame] | 705 | static inline unsigned int pdc20621_host_intr(struct ata_port *ap, | 
 | 706 | 					  struct ata_queued_cmd *qc, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 707 | 					  unsigned int doing_hdma, | 
| Jeff Garzik | ea6ba10 | 2005-08-30 05:18:18 -0400 | [diff] [blame] | 708 | 					  void __iomem *mmio) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 709 | { | 
 | 710 | 	unsigned int port_no = ap->port_no; | 
 | 711 | 	unsigned int port_ofs = | 
 | 712 | 		PDC_20621_DIMM_BASE + (PDC_DIMM_WINDOW_STEP * port_no); | 
 | 713 | 	u8 status; | 
 | 714 | 	unsigned int handled = 0; | 
 | 715 |  | 
 | 716 | 	VPRINTK("ENTER\n"); | 
 | 717 |  | 
 | 718 | 	if ((qc->tf.protocol == ATA_PROT_DMA) &&	/* read */ | 
 | 719 | 	    (!(qc->tf.flags & ATA_TFLAG_WRITE))) { | 
 | 720 |  | 
 | 721 | 		/* step two - DMA from DIMM to host */ | 
 | 722 | 		if (doing_hdma) { | 
| Tejun Heo | 44877b4 | 2007-02-21 01:06:51 +0900 | [diff] [blame] | 723 | 			VPRINTK("ata%u: read hdma, 0x%x 0x%x\n", ap->print_id, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 724 | 				readl(mmio + 0x104), readl(mmio + PDC_HDMA_CTLSTAT)); | 
 | 725 | 			/* get drive status; clear intr; complete txn */ | 
| Albert Lee | a22e2eb | 2005-12-05 15:38:02 +0800 | [diff] [blame] | 726 | 			qc->err_mask |= ac_err_mask(ata_wait_idle(ap)); | 
 | 727 | 			ata_qc_complete(qc); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 728 | 			pdc20621_pop_hdma(qc); | 
 | 729 | 		} | 
 | 730 |  | 
 | 731 | 		/* step one - exec ATA command */ | 
 | 732 | 		else { | 
 | 733 | 			u8 seq = (u8) (port_no + 1 + 4); | 
| Tejun Heo | 44877b4 | 2007-02-21 01:06:51 +0900 | [diff] [blame] | 734 | 			VPRINTK("ata%u: read ata, 0x%x 0x%x\n", ap->print_id, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 735 | 				readl(mmio + 0x104), readl(mmio + PDC_HDMA_CTLSTAT)); | 
 | 736 |  | 
 | 737 | 			/* submit hdma pkt */ | 
 | 738 | 			pdc20621_dump_hdma(qc); | 
 | 739 | 			pdc20621_push_hdma(qc, seq, | 
 | 740 | 					   port_ofs + PDC_DIMM_HOST_PKT); | 
 | 741 | 		} | 
 | 742 | 		handled = 1; | 
 | 743 |  | 
 | 744 | 	} else if (qc->tf.protocol == ATA_PROT_DMA) {	/* write */ | 
 | 745 |  | 
 | 746 | 		/* step one - DMA from host to DIMM */ | 
 | 747 | 		if (doing_hdma) { | 
 | 748 | 			u8 seq = (u8) (port_no + 1); | 
| Tejun Heo | 44877b4 | 2007-02-21 01:06:51 +0900 | [diff] [blame] | 749 | 			VPRINTK("ata%u: write hdma, 0x%x 0x%x\n", ap->print_id, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 750 | 				readl(mmio + 0x104), readl(mmio + PDC_HDMA_CTLSTAT)); | 
 | 751 |  | 
 | 752 | 			/* submit ata pkt */ | 
 | 753 | 			writel(0x00000001, mmio + PDC_20621_SEQCTL + (seq * 4)); | 
 | 754 | 			readl(mmio + PDC_20621_SEQCTL + (seq * 4)); | 
 | 755 | 			writel(port_ofs + PDC_DIMM_ATA_PKT, | 
| Tejun Heo | 0d5ff56 | 2007-02-01 15:06:36 +0900 | [diff] [blame] | 756 | 			       ap->ioaddr.cmd_addr + PDC_PKT_SUBMIT); | 
 | 757 | 			readl(ap->ioaddr.cmd_addr + PDC_PKT_SUBMIT); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 758 | 		} | 
 | 759 |  | 
 | 760 | 		/* step two - execute ATA command */ | 
 | 761 | 		else { | 
| Tejun Heo | 44877b4 | 2007-02-21 01:06:51 +0900 | [diff] [blame] | 762 | 			VPRINTK("ata%u: write ata, 0x%x 0x%x\n", ap->print_id, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 763 | 				readl(mmio + 0x104), readl(mmio + PDC_HDMA_CTLSTAT)); | 
 | 764 | 			/* get drive status; clear intr; complete txn */ | 
| Albert Lee | a22e2eb | 2005-12-05 15:38:02 +0800 | [diff] [blame] | 765 | 			qc->err_mask |= ac_err_mask(ata_wait_idle(ap)); | 
 | 766 | 			ata_qc_complete(qc); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 767 | 			pdc20621_pop_hdma(qc); | 
 | 768 | 		} | 
 | 769 | 		handled = 1; | 
 | 770 |  | 
 | 771 | 	/* command completion, but no data xfer */ | 
 | 772 | 	} else if (qc->tf.protocol == ATA_PROT_NODATA) { | 
 | 773 |  | 
| Tejun Heo | 9363c38 | 2008-04-07 22:47:16 +0900 | [diff] [blame] | 774 | 		status = ata_sff_busy_wait(ap, ATA_BUSY | ATA_DRQ, 1000); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 775 | 		DPRINTK("BUS_NODATA (drv_stat 0x%X)\n", status); | 
| Albert Lee | a22e2eb | 2005-12-05 15:38:02 +0800 | [diff] [blame] | 776 | 		qc->err_mask |= ac_err_mask(status); | 
 | 777 | 		ata_qc_complete(qc); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 778 | 		handled = 1; | 
 | 779 |  | 
 | 780 | 	} else { | 
 | 781 | 		ap->stats.idle_irq++; | 
 | 782 | 	} | 
 | 783 |  | 
 | 784 | 	return handled; | 
 | 785 | } | 
 | 786 |  | 
 | 787 | static void pdc20621_irq_clear(struct ata_port *ap) | 
 | 788 | { | 
| Jeff Garzik | cca3974 | 2006-08-24 03:19:22 -0400 | [diff] [blame] | 789 | 	struct ata_host *host = ap->host; | 
| Tejun Heo | 0d5ff56 | 2007-02-01 15:06:36 +0900 | [diff] [blame] | 790 | 	void __iomem *mmio = host->iomap[PDC_MMIO_BAR]; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 791 |  | 
 | 792 | 	mmio += PDC_CHIP0_OFS; | 
 | 793 |  | 
 | 794 | 	readl(mmio + PDC_20621_SEQMASK); | 
 | 795 | } | 
 | 796 |  | 
| Jeff Garzik | 5796d1c | 2007-10-26 00:03:37 -0400 | [diff] [blame] | 797 | static irqreturn_t pdc20621_interrupt(int irq, void *dev_instance) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 798 | { | 
| Jeff Garzik | cca3974 | 2006-08-24 03:19:22 -0400 | [diff] [blame] | 799 | 	struct ata_host *host = dev_instance; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 800 | 	struct ata_port *ap; | 
 | 801 | 	u32 mask = 0; | 
 | 802 | 	unsigned int i, tmp, port_no; | 
 | 803 | 	unsigned int handled = 0; | 
| Jeff Garzik | ea6ba10 | 2005-08-30 05:18:18 -0400 | [diff] [blame] | 804 | 	void __iomem *mmio_base; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 805 |  | 
 | 806 | 	VPRINTK("ENTER\n"); | 
 | 807 |  | 
| Tejun Heo | 0d5ff56 | 2007-02-01 15:06:36 +0900 | [diff] [blame] | 808 | 	if (!host || !host->iomap[PDC_MMIO_BAR]) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 809 | 		VPRINTK("QUICK EXIT\n"); | 
 | 810 | 		return IRQ_NONE; | 
 | 811 | 	} | 
 | 812 |  | 
| Tejun Heo | 0d5ff56 | 2007-02-01 15:06:36 +0900 | [diff] [blame] | 813 | 	mmio_base = host->iomap[PDC_MMIO_BAR]; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 814 |  | 
 | 815 | 	/* reading should also clear interrupts */ | 
 | 816 | 	mmio_base += PDC_CHIP0_OFS; | 
 | 817 | 	mask = readl(mmio_base + PDC_20621_SEQMASK); | 
 | 818 | 	VPRINTK("mask == 0x%x\n", mask); | 
 | 819 |  | 
 | 820 | 	if (mask == 0xffffffff) { | 
 | 821 | 		VPRINTK("QUICK EXIT 2\n"); | 
 | 822 | 		return IRQ_NONE; | 
 | 823 | 	} | 
 | 824 | 	mask &= 0xffff;		/* only 16 tags possible */ | 
 | 825 | 	if (!mask) { | 
 | 826 | 		VPRINTK("QUICK EXIT 3\n"); | 
 | 827 | 		return IRQ_NONE; | 
 | 828 | 	} | 
 | 829 |  | 
| Jeff Garzik | 5796d1c | 2007-10-26 00:03:37 -0400 | [diff] [blame] | 830 | 	spin_lock(&host->lock); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 831 |  | 
| Jeff Garzik | 5796d1c | 2007-10-26 00:03:37 -0400 | [diff] [blame] | 832 | 	for (i = 1; i < 9; i++) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 833 | 		port_no = i - 1; | 
 | 834 | 		if (port_no > 3) | 
 | 835 | 			port_no -= 4; | 
| Jeff Garzik | cca3974 | 2006-08-24 03:19:22 -0400 | [diff] [blame] | 836 | 		if (port_no >= host->n_ports) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 837 | 			ap = NULL; | 
 | 838 | 		else | 
| Jeff Garzik | cca3974 | 2006-08-24 03:19:22 -0400 | [diff] [blame] | 839 | 			ap = host->ports[port_no]; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 840 | 		tmp = mask & (1 << i); | 
 | 841 | 		VPRINTK("seq %u, port_no %u, ap %p, tmp %x\n", i, port_no, ap, tmp); | 
| Tejun Heo | c138950 | 2005-08-22 14:59:24 +0900 | [diff] [blame] | 842 | 		if (tmp && ap && | 
| Jeff Garzik | 029f546 | 2006-04-02 10:30:40 -0400 | [diff] [blame] | 843 | 		    !(ap->flags & ATA_FLAG_DISABLED)) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 844 | 			struct ata_queued_cmd *qc; | 
 | 845 |  | 
| Tejun Heo | 9af5c9c | 2007-08-06 18:36:22 +0900 | [diff] [blame] | 846 | 			qc = ata_qc_from_tag(ap, ap->link.active_tag); | 
| Albert Lee | e50362e | 2005-09-27 17:39:50 +0800 | [diff] [blame] | 847 | 			if (qc && (!(qc->tf.flags & ATA_TFLAG_POLLING))) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 848 | 				handled += pdc20621_host_intr(ap, qc, (i > 4), | 
 | 849 | 							      mmio_base); | 
 | 850 | 		} | 
 | 851 | 	} | 
 | 852 |  | 
| Jeff Garzik | 5796d1c | 2007-10-26 00:03:37 -0400 | [diff] [blame] | 853 | 	spin_unlock(&host->lock); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 854 |  | 
 | 855 | 	VPRINTK("mask == 0x%x\n", mask); | 
 | 856 |  | 
 | 857 | 	VPRINTK("EXIT\n"); | 
 | 858 |  | 
 | 859 | 	return IRQ_RETVAL(handled); | 
 | 860 | } | 
 | 861 |  | 
 | 862 | static void pdc_eng_timeout(struct ata_port *ap) | 
 | 863 | { | 
 | 864 | 	u8 drv_stat; | 
| Jeff Garzik | cca3974 | 2006-08-24 03:19:22 -0400 | [diff] [blame] | 865 | 	struct ata_host *host = ap->host; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 866 | 	struct ata_queued_cmd *qc; | 
| Jeff Garzik | b8f6153 | 2005-08-25 22:01:20 -0400 | [diff] [blame] | 867 | 	unsigned long flags; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 868 |  | 
 | 869 | 	DPRINTK("ENTER\n"); | 
 | 870 |  | 
| Jeff Garzik | cca3974 | 2006-08-24 03:19:22 -0400 | [diff] [blame] | 871 | 	spin_lock_irqsave(&host->lock, flags); | 
| Jeff Garzik | b8f6153 | 2005-08-25 22:01:20 -0400 | [diff] [blame] | 872 |  | 
| Tejun Heo | 9af5c9c | 2007-08-06 18:36:22 +0900 | [diff] [blame] | 873 | 	qc = ata_qc_from_tag(ap, ap->link.active_tag); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 874 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 875 | 	switch (qc->tf.protocol) { | 
 | 876 | 	case ATA_PROT_DMA: | 
 | 877 | 	case ATA_PROT_NODATA: | 
| Tejun Heo | f15a1da | 2006-05-15 20:57:56 +0900 | [diff] [blame] | 878 | 		ata_port_printk(ap, KERN_ERR, "command timeout\n"); | 
| Albert Lee | a22e2eb | 2005-12-05 15:38:02 +0800 | [diff] [blame] | 879 | 		qc->err_mask |= __ac_err_mask(ata_wait_idle(ap)); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 880 | 		break; | 
 | 881 |  | 
 | 882 | 	default: | 
| Tejun Heo | 9363c38 | 2008-04-07 22:47:16 +0900 | [diff] [blame] | 883 | 		drv_stat = ata_sff_busy_wait(ap, ATA_BUSY | ATA_DRQ, 1000); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 884 |  | 
| Tejun Heo | f15a1da | 2006-05-15 20:57:56 +0900 | [diff] [blame] | 885 | 		ata_port_printk(ap, KERN_ERR, | 
 | 886 | 				"unknown timeout, cmd 0x%x stat 0x%x\n", | 
 | 887 | 				qc->tf.command, drv_stat); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 888 |  | 
| Albert Lee | a22e2eb | 2005-12-05 15:38:02 +0800 | [diff] [blame] | 889 | 		qc->err_mask |= ac_err_mask(drv_stat); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 890 | 		break; | 
 | 891 | 	} | 
 | 892 |  | 
| Jeff Garzik | cca3974 | 2006-08-24 03:19:22 -0400 | [diff] [blame] | 893 | 	spin_unlock_irqrestore(&host->lock, flags); | 
| Tejun Heo | f637902 | 2006-02-10 15:10:48 +0900 | [diff] [blame] | 894 | 	ata_eh_qc_complete(qc); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 895 | 	DPRINTK("EXIT\n"); | 
 | 896 | } | 
 | 897 |  | 
| Jeff Garzik | 057ace5 | 2005-10-22 14:27:05 -0400 | [diff] [blame] | 898 | static void pdc_tf_load_mmio(struct ata_port *ap, const struct ata_taskfile *tf) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 899 | { | 
| Jeff Garzik | 5796d1c | 2007-10-26 00:03:37 -0400 | [diff] [blame] | 900 | 	WARN_ON(tf->protocol == ATA_PROT_DMA || | 
 | 901 | 		tf->protocol == ATA_PROT_NODATA); | 
| Tejun Heo | 9363c38 | 2008-04-07 22:47:16 +0900 | [diff] [blame] | 902 | 	ata_sff_tf_load(ap, tf); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 903 | } | 
 | 904 |  | 
 | 905 |  | 
| Jeff Garzik | 057ace5 | 2005-10-22 14:27:05 -0400 | [diff] [blame] | 906 | static void pdc_exec_command_mmio(struct ata_port *ap, const struct ata_taskfile *tf) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 907 | { | 
| Jeff Garzik | 5796d1c | 2007-10-26 00:03:37 -0400 | [diff] [blame] | 908 | 	WARN_ON(tf->protocol == ATA_PROT_DMA || | 
 | 909 | 		tf->protocol == ATA_PROT_NODATA); | 
| Tejun Heo | 9363c38 | 2008-04-07 22:47:16 +0900 | [diff] [blame] | 910 | 	ata_sff_exec_command(ap, tf); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 911 | } | 
 | 912 |  | 
 | 913 |  | 
| Tejun Heo | 0d5ff56 | 2007-02-01 15:06:36 +0900 | [diff] [blame] | 914 | static void pdc_sata_setup_port(struct ata_ioports *port, void __iomem *base) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 915 | { | 
 | 916 | 	port->cmd_addr		= base; | 
 | 917 | 	port->data_addr		= base; | 
 | 918 | 	port->feature_addr	= | 
 | 919 | 	port->error_addr	= base + 0x4; | 
 | 920 | 	port->nsect_addr	= base + 0x8; | 
 | 921 | 	port->lbal_addr		= base + 0xc; | 
 | 922 | 	port->lbam_addr		= base + 0x10; | 
 | 923 | 	port->lbah_addr		= base + 0x14; | 
 | 924 | 	port->device_addr	= base + 0x18; | 
 | 925 | 	port->command_addr	= | 
 | 926 | 	port->status_addr	= base + 0x1c; | 
 | 927 | 	port->altstatus_addr	= | 
 | 928 | 	port->ctl_addr		= base + 0x38; | 
 | 929 | } | 
 | 930 |  | 
 | 931 |  | 
 | 932 | #ifdef ATA_VERBOSE_DEBUG | 
| Tejun Heo | 4447d35 | 2007-04-17 23:44:08 +0900 | [diff] [blame] | 933 | static void pdc20621_get_from_dimm(struct ata_host *host, void *psource, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 934 | 				   u32 offset, u32 size) | 
 | 935 | { | 
 | 936 | 	u32 window_size; | 
 | 937 | 	u16 idx; | 
 | 938 | 	u8 page_mask; | 
 | 939 | 	long dist; | 
| Tejun Heo | 4447d35 | 2007-04-17 23:44:08 +0900 | [diff] [blame] | 940 | 	void __iomem *mmio = host->iomap[PDC_MMIO_BAR]; | 
 | 941 | 	void __iomem *dimm_mmio = host->iomap[PDC_DIMM_BAR]; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 942 |  | 
 | 943 | 	/* hard-code chip #0 */ | 
 | 944 | 	mmio += PDC_CHIP0_OFS; | 
 | 945 |  | 
| Jeff Garzik | 8a60a07 | 2005-07-31 13:13:24 -0400 | [diff] [blame] | 946 | 	page_mask = 0x00; | 
| Jeff Garzik | 5796d1c | 2007-10-26 00:03:37 -0400 | [diff] [blame] | 947 | 	window_size = 0x2000 * 4; /* 32K byte uchar size */ | 
| Jeff Garzik | 8a60a07 | 2005-07-31 13:13:24 -0400 | [diff] [blame] | 948 | 	idx = (u16) (offset / window_size); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 949 |  | 
 | 950 | 	writel(0x01, mmio + PDC_GENERAL_CTLR); | 
 | 951 | 	readl(mmio + PDC_GENERAL_CTLR); | 
 | 952 | 	writel(((idx) << page_mask), mmio + PDC_DIMM_WINDOW_CTLR); | 
 | 953 | 	readl(mmio + PDC_DIMM_WINDOW_CTLR); | 
 | 954 |  | 
 | 955 | 	offset -= (idx * window_size); | 
 | 956 | 	idx++; | 
| Jeff Garzik | 8a60a07 | 2005-07-31 13:13:24 -0400 | [diff] [blame] | 957 | 	dist = ((long) (window_size - (offset + size))) >= 0 ? size : | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 958 | 		(long) (window_size - offset); | 
| Jeff Garzik | 8a60a07 | 2005-07-31 13:13:24 -0400 | [diff] [blame] | 959 | 	memcpy_fromio((char *) psource, (char *) (dimm_mmio + offset / 4), | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 960 | 		      dist); | 
 | 961 |  | 
| Jeff Garzik | 8a60a07 | 2005-07-31 13:13:24 -0400 | [diff] [blame] | 962 | 	psource += dist; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 963 | 	size -= dist; | 
 | 964 | 	for (; (long) size >= (long) window_size ;) { | 
 | 965 | 		writel(0x01, mmio + PDC_GENERAL_CTLR); | 
 | 966 | 		readl(mmio + PDC_GENERAL_CTLR); | 
 | 967 | 		writel(((idx) << page_mask), mmio + PDC_DIMM_WINDOW_CTLR); | 
 | 968 | 		readl(mmio + PDC_DIMM_WINDOW_CTLR); | 
| Jeff Garzik | 8a60a07 | 2005-07-31 13:13:24 -0400 | [diff] [blame] | 969 | 		memcpy_fromio((char *) psource, (char *) (dimm_mmio), | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 970 | 			      window_size / 4); | 
 | 971 | 		psource += window_size; | 
 | 972 | 		size -= window_size; | 
| Jeff Garzik | 5796d1c | 2007-10-26 00:03:37 -0400 | [diff] [blame] | 973 | 		idx++; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 974 | 	} | 
 | 975 |  | 
 | 976 | 	if (size) { | 
 | 977 | 		writel(0x01, mmio + PDC_GENERAL_CTLR); | 
 | 978 | 		readl(mmio + PDC_GENERAL_CTLR); | 
 | 979 | 		writel(((idx) << page_mask), mmio + PDC_DIMM_WINDOW_CTLR); | 
 | 980 | 		readl(mmio + PDC_DIMM_WINDOW_CTLR); | 
| Jeff Garzik | 8a60a07 | 2005-07-31 13:13:24 -0400 | [diff] [blame] | 981 | 		memcpy_fromio((char *) psource, (char *) (dimm_mmio), | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 982 | 			      size / 4); | 
 | 983 | 	} | 
 | 984 | } | 
 | 985 | #endif | 
 | 986 |  | 
 | 987 |  | 
| Tejun Heo | 4447d35 | 2007-04-17 23:44:08 +0900 | [diff] [blame] | 988 | static void pdc20621_put_to_dimm(struct ata_host *host, void *psource, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 989 | 				 u32 offset, u32 size) | 
 | 990 | { | 
 | 991 | 	u32 window_size; | 
 | 992 | 	u16 idx; | 
 | 993 | 	u8 page_mask; | 
 | 994 | 	long dist; | 
| Tejun Heo | 4447d35 | 2007-04-17 23:44:08 +0900 | [diff] [blame] | 995 | 	void __iomem *mmio = host->iomap[PDC_MMIO_BAR]; | 
 | 996 | 	void __iomem *dimm_mmio = host->iomap[PDC_DIMM_BAR]; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 997 |  | 
| Jeff Garzik | 8a60a07 | 2005-07-31 13:13:24 -0400 | [diff] [blame] | 998 | 	/* hard-code chip #0 */ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 999 | 	mmio += PDC_CHIP0_OFS; | 
 | 1000 |  | 
| Jeff Garzik | 8a60a07 | 2005-07-31 13:13:24 -0400 | [diff] [blame] | 1001 | 	page_mask = 0x00; | 
| Jeff Garzik | 5796d1c | 2007-10-26 00:03:37 -0400 | [diff] [blame] | 1002 | 	window_size = 0x2000 * 4;       /* 32K byte uchar size */ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1003 | 	idx = (u16) (offset / window_size); | 
 | 1004 |  | 
 | 1005 | 	writel(((idx) << page_mask), mmio + PDC_DIMM_WINDOW_CTLR); | 
 | 1006 | 	readl(mmio + PDC_DIMM_WINDOW_CTLR); | 
| Jeff Garzik | 8a60a07 | 2005-07-31 13:13:24 -0400 | [diff] [blame] | 1007 | 	offset -= (idx * window_size); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1008 | 	idx++; | 
 | 1009 | 	dist = ((long)(s32)(window_size - (offset + size))) >= 0 ? size : | 
 | 1010 | 		(long) (window_size - offset); | 
| Al Viro | a9afd7cd | 2005-10-21 06:46:02 +0100 | [diff] [blame] | 1011 | 	memcpy_toio(dimm_mmio + offset / 4, psource, dist); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1012 | 	writel(0x01, mmio + PDC_GENERAL_CTLR); | 
 | 1013 | 	readl(mmio + PDC_GENERAL_CTLR); | 
 | 1014 |  | 
| Jeff Garzik | 8a60a07 | 2005-07-31 13:13:24 -0400 | [diff] [blame] | 1015 | 	psource += dist; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1016 | 	size -= dist; | 
 | 1017 | 	for (; (long) size >= (long) window_size ;) { | 
 | 1018 | 		writel(((idx) << page_mask), mmio + PDC_DIMM_WINDOW_CTLR); | 
 | 1019 | 		readl(mmio + PDC_DIMM_WINDOW_CTLR); | 
| Al Viro | a9afd7cd | 2005-10-21 06:46:02 +0100 | [diff] [blame] | 1020 | 		memcpy_toio(dimm_mmio, psource, window_size / 4); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1021 | 		writel(0x01, mmio + PDC_GENERAL_CTLR); | 
 | 1022 | 		readl(mmio + PDC_GENERAL_CTLR); | 
 | 1023 | 		psource += window_size; | 
 | 1024 | 		size -= window_size; | 
| Jeff Garzik | 5796d1c | 2007-10-26 00:03:37 -0400 | [diff] [blame] | 1025 | 		idx++; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1026 | 	} | 
| Jeff Garzik | 8a60a07 | 2005-07-31 13:13:24 -0400 | [diff] [blame] | 1027 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1028 | 	if (size) { | 
 | 1029 | 		writel(((idx) << page_mask), mmio + PDC_DIMM_WINDOW_CTLR); | 
 | 1030 | 		readl(mmio + PDC_DIMM_WINDOW_CTLR); | 
| Al Viro | a9afd7cd | 2005-10-21 06:46:02 +0100 | [diff] [blame] | 1031 | 		memcpy_toio(dimm_mmio, psource, size / 4); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1032 | 		writel(0x01, mmio + PDC_GENERAL_CTLR); | 
 | 1033 | 		readl(mmio + PDC_GENERAL_CTLR); | 
 | 1034 | 	} | 
 | 1035 | } | 
 | 1036 |  | 
 | 1037 |  | 
| Tejun Heo | 4447d35 | 2007-04-17 23:44:08 +0900 | [diff] [blame] | 1038 | static unsigned int pdc20621_i2c_read(struct ata_host *host, u32 device, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1039 | 				      u32 subaddr, u32 *pdata) | 
 | 1040 | { | 
| Tejun Heo | 4447d35 | 2007-04-17 23:44:08 +0900 | [diff] [blame] | 1041 | 	void __iomem *mmio = host->iomap[PDC_MMIO_BAR]; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1042 | 	u32 i2creg  = 0; | 
| Jeff Garzik | 8a60a07 | 2005-07-31 13:13:24 -0400 | [diff] [blame] | 1043 | 	u32 status; | 
| Jeff Garzik | 5796d1c | 2007-10-26 00:03:37 -0400 | [diff] [blame] | 1044 | 	u32 count = 0; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1045 |  | 
 | 1046 | 	/* hard-code chip #0 */ | 
 | 1047 | 	mmio += PDC_CHIP0_OFS; | 
 | 1048 |  | 
 | 1049 | 	i2creg |= device << 24; | 
 | 1050 | 	i2creg |= subaddr << 16; | 
 | 1051 |  | 
 | 1052 | 	/* Set the device and subaddress */ | 
| Jeff Garzik | b2d46b6 | 2007-05-27 22:58:54 -0400 | [diff] [blame] | 1053 | 	writel(i2creg, mmio + PDC_I2C_ADDR_DATA); | 
 | 1054 | 	readl(mmio + PDC_I2C_ADDR_DATA); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1055 |  | 
 | 1056 | 	/* Write Control to perform read operation, mask int */ | 
| Jeff Garzik | 8a60a07 | 2005-07-31 13:13:24 -0400 | [diff] [blame] | 1057 | 	writel(PDC_I2C_READ | PDC_I2C_START | PDC_I2C_MASK_INT, | 
| Jeff Garzik | b2d46b6 | 2007-05-27 22:58:54 -0400 | [diff] [blame] | 1058 | 	       mmio + PDC_I2C_CONTROL); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1059 |  | 
 | 1060 | 	for (count = 0; count <= 1000; count ++) { | 
| Jeff Garzik | b2d46b6 | 2007-05-27 22:58:54 -0400 | [diff] [blame] | 1061 | 		status = readl(mmio + PDC_I2C_CONTROL); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1062 | 		if (status & PDC_I2C_COMPLETE) { | 
| Jeff Garzik | b2d46b6 | 2007-05-27 22:58:54 -0400 | [diff] [blame] | 1063 | 			status = readl(mmio + PDC_I2C_ADDR_DATA); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1064 | 			break; | 
 | 1065 | 		} else if (count == 1000) | 
 | 1066 | 			return 0; | 
 | 1067 | 	} | 
 | 1068 |  | 
 | 1069 | 	*pdata = (status >> 8) & 0x000000ff; | 
| Jeff Garzik | 8a60a07 | 2005-07-31 13:13:24 -0400 | [diff] [blame] | 1070 | 	return 1; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1071 | } | 
 | 1072 |  | 
 | 1073 |  | 
| Tejun Heo | 4447d35 | 2007-04-17 23:44:08 +0900 | [diff] [blame] | 1074 | static int pdc20621_detect_dimm(struct ata_host *host) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1075 | { | 
| Jeff Garzik | 5796d1c | 2007-10-26 00:03:37 -0400 | [diff] [blame] | 1076 | 	u32 data = 0; | 
| Tejun Heo | 4447d35 | 2007-04-17 23:44:08 +0900 | [diff] [blame] | 1077 | 	if (pdc20621_i2c_read(host, PDC_DIMM0_SPD_DEV_ADDRESS, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1078 | 			     PDC_DIMM_SPD_SYSTEM_FREQ, &data)) { | 
| Jeff Garzik | 5796d1c | 2007-10-26 00:03:37 -0400 | [diff] [blame] | 1079 | 		if (data == 100) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1080 | 			return 100; | 
| Jeff Garzik | 5796d1c | 2007-10-26 00:03:37 -0400 | [diff] [blame] | 1081 | 	} else | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1082 | 		return 0; | 
| Jeff Garzik | 8a60a07 | 2005-07-31 13:13:24 -0400 | [diff] [blame] | 1083 |  | 
| Tejun Heo | 4447d35 | 2007-04-17 23:44:08 +0900 | [diff] [blame] | 1084 | 	if (pdc20621_i2c_read(host, PDC_DIMM0_SPD_DEV_ADDRESS, 9, &data)) { | 
| Jeff Garzik | b447916 | 2007-10-25 20:47:30 -0400 | [diff] [blame] | 1085 | 		if (data <= 0x75) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1086 | 			return 133; | 
| Jeff Garzik | 5796d1c | 2007-10-26 00:03:37 -0400 | [diff] [blame] | 1087 | 	} else | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1088 | 		return 0; | 
| Jeff Garzik | 8a60a07 | 2005-07-31 13:13:24 -0400 | [diff] [blame] | 1089 |  | 
| Jeff Garzik | 5796d1c | 2007-10-26 00:03:37 -0400 | [diff] [blame] | 1090 | 	return 0; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1091 | } | 
 | 1092 |  | 
 | 1093 |  | 
| Tejun Heo | 4447d35 | 2007-04-17 23:44:08 +0900 | [diff] [blame] | 1094 | static int pdc20621_prog_dimm0(struct ata_host *host) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1095 | { | 
 | 1096 | 	u32 spd0[50]; | 
 | 1097 | 	u32 data = 0; | 
| Jeff Garzik | 5796d1c | 2007-10-26 00:03:37 -0400 | [diff] [blame] | 1098 | 	int size, i; | 
 | 1099 | 	u8 bdimmsize; | 
| Tejun Heo | 4447d35 | 2007-04-17 23:44:08 +0900 | [diff] [blame] | 1100 | 	void __iomem *mmio = host->iomap[PDC_MMIO_BAR]; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1101 | 	static const struct { | 
 | 1102 | 		unsigned int reg; | 
 | 1103 | 		unsigned int ofs; | 
 | 1104 | 	} pdc_i2c_read_data [] = { | 
| Jeff Garzik | 8a60a07 | 2005-07-31 13:13:24 -0400 | [diff] [blame] | 1105 | 		{ PDC_DIMM_SPD_TYPE, 11 }, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1106 | 		{ PDC_DIMM_SPD_FRESH_RATE, 12 }, | 
| Jeff Garzik | 8a60a07 | 2005-07-31 13:13:24 -0400 | [diff] [blame] | 1107 | 		{ PDC_DIMM_SPD_COLUMN_NUM, 4 }, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1108 | 		{ PDC_DIMM_SPD_ATTRIBUTE, 21 }, | 
 | 1109 | 		{ PDC_DIMM_SPD_ROW_NUM, 3 }, | 
 | 1110 | 		{ PDC_DIMM_SPD_BANK_NUM, 17 }, | 
 | 1111 | 		{ PDC_DIMM_SPD_MODULE_ROW, 5 }, | 
 | 1112 | 		{ PDC_DIMM_SPD_ROW_PRE_CHARGE, 27 }, | 
 | 1113 | 		{ PDC_DIMM_SPD_ROW_ACTIVE_DELAY, 28 }, | 
 | 1114 | 		{ PDC_DIMM_SPD_RAS_CAS_DELAY, 29 }, | 
 | 1115 | 		{ PDC_DIMM_SPD_ACTIVE_PRECHARGE, 30 }, | 
| Jeff Garzik | 8a60a07 | 2005-07-31 13:13:24 -0400 | [diff] [blame] | 1116 | 		{ PDC_DIMM_SPD_CAS_LATENCY, 18 }, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1117 | 	}; | 
 | 1118 |  | 
 | 1119 | 	/* hard-code chip #0 */ | 
 | 1120 | 	mmio += PDC_CHIP0_OFS; | 
 | 1121 |  | 
| Jeff Garzik | 5796d1c | 2007-10-26 00:03:37 -0400 | [diff] [blame] | 1122 | 	for (i = 0; i < ARRAY_SIZE(pdc_i2c_read_data); i++) | 
| Tejun Heo | 4447d35 | 2007-04-17 23:44:08 +0900 | [diff] [blame] | 1123 | 		pdc20621_i2c_read(host, PDC_DIMM0_SPD_DEV_ADDRESS, | 
| Jeff Garzik | 8a60a07 | 2005-07-31 13:13:24 -0400 | [diff] [blame] | 1124 | 				  pdc_i2c_read_data[i].reg, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1125 | 				  &spd0[pdc_i2c_read_data[i].ofs]); | 
| Jeff Garzik | 8a60a07 | 2005-07-31 13:13:24 -0400 | [diff] [blame] | 1126 |  | 
| Jeff Garzik | 5796d1c | 2007-10-26 00:03:37 -0400 | [diff] [blame] | 1127 | 	data |= (spd0[4] - 8) | ((spd0[21] != 0) << 3) | ((spd0[3]-11) << 4); | 
 | 1128 | 	data |= ((spd0[17] / 4) << 6) | ((spd0[5] / 2) << 7) | | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1129 | 		((((spd0[27] + 9) / 10) - 1) << 8) ; | 
| Jeff Garzik | 5796d1c | 2007-10-26 00:03:37 -0400 | [diff] [blame] | 1130 | 	data |= (((((spd0[29] > spd0[28]) | 
| Jeff Garzik | 8a60a07 | 2005-07-31 13:13:24 -0400 | [diff] [blame] | 1131 | 		    ? spd0[29] : spd0[28]) + 9) / 10) - 1) << 10; | 
| Jeff Garzik | 5796d1c | 2007-10-26 00:03:37 -0400 | [diff] [blame] | 1132 | 	data |= ((spd0[30] - spd0[29] + 9) / 10 - 2) << 12; | 
| Jeff Garzik | 8a60a07 | 2005-07-31 13:13:24 -0400 | [diff] [blame] | 1133 |  | 
| Jeff Garzik | 5796d1c | 2007-10-26 00:03:37 -0400 | [diff] [blame] | 1134 | 	if (spd0[18] & 0x08) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1135 | 		data |= ((0x03) << 14); | 
| Jeff Garzik | 5796d1c | 2007-10-26 00:03:37 -0400 | [diff] [blame] | 1136 | 	else if (spd0[18] & 0x04) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1137 | 		data |= ((0x02) << 14); | 
| Jeff Garzik | 5796d1c | 2007-10-26 00:03:37 -0400 | [diff] [blame] | 1138 | 	else if (spd0[18] & 0x01) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1139 | 		data |= ((0x01) << 14); | 
| Jeff Garzik | 5796d1c | 2007-10-26 00:03:37 -0400 | [diff] [blame] | 1140 | 	else | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1141 | 		data |= (0 << 14); | 
 | 1142 |  | 
| Jeff Garzik | 5796d1c | 2007-10-26 00:03:37 -0400 | [diff] [blame] | 1143 | 	/* | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1144 | 	   Calculate the size of bDIMMSize (power of 2) and | 
 | 1145 | 	   merge the DIMM size by program start/end address. | 
 | 1146 | 	*/ | 
 | 1147 |  | 
| Jeff Garzik | 5796d1c | 2007-10-26 00:03:37 -0400 | [diff] [blame] | 1148 | 	bdimmsize = spd0[4] + (spd0[5] / 2) + spd0[3] + (spd0[17] / 2) + 3; | 
 | 1149 | 	size = (1 << bdimmsize) >> 20;	/* size = xxx(MB) */ | 
 | 1150 | 	data |= (((size / 16) - 1) << 16); | 
 | 1151 | 	data |= (0 << 23); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1152 | 	data |= 8; | 
| Jeff Garzik | 5796d1c | 2007-10-26 00:03:37 -0400 | [diff] [blame] | 1153 | 	writel(data, mmio + PDC_DIMM0_CONTROL); | 
| Jeff Garzik | b2d46b6 | 2007-05-27 22:58:54 -0400 | [diff] [blame] | 1154 | 	readl(mmio + PDC_DIMM0_CONTROL); | 
| Jeff Garzik | 5796d1c | 2007-10-26 00:03:37 -0400 | [diff] [blame] | 1155 | 	return size; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1156 | } | 
 | 1157 |  | 
 | 1158 |  | 
| Tejun Heo | 4447d35 | 2007-04-17 23:44:08 +0900 | [diff] [blame] | 1159 | static unsigned int pdc20621_prog_dimm_global(struct ata_host *host) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1160 | { | 
 | 1161 | 	u32 data, spd0; | 
| Tejun Heo | 0d5ff56 | 2007-02-01 15:06:36 +0900 | [diff] [blame] | 1162 | 	int error, i; | 
| Tejun Heo | 4447d35 | 2007-04-17 23:44:08 +0900 | [diff] [blame] | 1163 | 	void __iomem *mmio = host->iomap[PDC_MMIO_BAR]; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1164 |  | 
 | 1165 | 	/* hard-code chip #0 */ | 
| Jeff Garzik | 5796d1c | 2007-10-26 00:03:37 -0400 | [diff] [blame] | 1166 | 	mmio += PDC_CHIP0_OFS; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1167 |  | 
| Jeff Garzik | 5796d1c | 2007-10-26 00:03:37 -0400 | [diff] [blame] | 1168 | 	/* | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1169 | 	  Set To Default : DIMM Module Global Control Register (0x022259F1) | 
 | 1170 | 	  DIMM Arbitration Disable (bit 20) | 
 | 1171 | 	  DIMM Data/Control Output Driving Selection (bit12 - bit15) | 
 | 1172 | 	  Refresh Enable (bit 17) | 
 | 1173 | 	*/ | 
 | 1174 |  | 
| Jeff Garzik | 8a60a07 | 2005-07-31 13:13:24 -0400 | [diff] [blame] | 1175 | 	data = 0x022259F1; | 
| Jeff Garzik | b2d46b6 | 2007-05-27 22:58:54 -0400 | [diff] [blame] | 1176 | 	writel(data, mmio + PDC_SDRAM_CONTROL); | 
 | 1177 | 	readl(mmio + PDC_SDRAM_CONTROL); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1178 |  | 
 | 1179 | 	/* Turn on for ECC */ | 
| Tejun Heo | 4447d35 | 2007-04-17 23:44:08 +0900 | [diff] [blame] | 1180 | 	pdc20621_i2c_read(host, PDC_DIMM0_SPD_DEV_ADDRESS, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1181 | 			  PDC_DIMM_SPD_TYPE, &spd0); | 
 | 1182 | 	if (spd0 == 0x02) { | 
 | 1183 | 		data |= (0x01 << 16); | 
| Jeff Garzik | b2d46b6 | 2007-05-27 22:58:54 -0400 | [diff] [blame] | 1184 | 		writel(data, mmio + PDC_SDRAM_CONTROL); | 
 | 1185 | 		readl(mmio + PDC_SDRAM_CONTROL); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1186 | 		printk(KERN_ERR "Local DIMM ECC Enabled\n"); | 
| Jeff Garzik | 5796d1c | 2007-10-26 00:03:37 -0400 | [diff] [blame] | 1187 | 	} | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1188 |  | 
| Jeff Garzik | 5796d1c | 2007-10-26 00:03:37 -0400 | [diff] [blame] | 1189 | 	/* DIMM Initialization Select/Enable (bit 18/19) */ | 
 | 1190 | 	data &= (~(1<<18)); | 
 | 1191 | 	data |= (1<<19); | 
 | 1192 | 	writel(data, mmio + PDC_SDRAM_CONTROL); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1193 |  | 
| Jeff Garzik | 5796d1c | 2007-10-26 00:03:37 -0400 | [diff] [blame] | 1194 | 	error = 1; | 
 | 1195 | 	for (i = 1; i <= 10; i++) {   /* polling ~5 secs */ | 
| Jeff Garzik | b2d46b6 | 2007-05-27 22:58:54 -0400 | [diff] [blame] | 1196 | 		data = readl(mmio + PDC_SDRAM_CONTROL); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1197 | 		if (!(data & (1<<19))) { | 
| Jeff Garzik | 5796d1c | 2007-10-26 00:03:37 -0400 | [diff] [blame] | 1198 | 			error = 0; | 
 | 1199 | 			break; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1200 | 		} | 
 | 1201 | 		msleep(i*100); | 
| Jeff Garzik | 5796d1c | 2007-10-26 00:03:37 -0400 | [diff] [blame] | 1202 | 	} | 
 | 1203 | 	return error; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1204 | } | 
| Jeff Garzik | 8a60a07 | 2005-07-31 13:13:24 -0400 | [diff] [blame] | 1205 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1206 |  | 
| Tejun Heo | 4447d35 | 2007-04-17 23:44:08 +0900 | [diff] [blame] | 1207 | static unsigned int pdc20621_dimm_init(struct ata_host *host) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1208 | { | 
| Jeff Garzik | 8a60a07 | 2005-07-31 13:13:24 -0400 | [diff] [blame] | 1209 | 	int speed, size, length; | 
| Jeff Garzik | 5796d1c | 2007-10-26 00:03:37 -0400 | [diff] [blame] | 1210 | 	u32 addr, spd0, pci_status; | 
 | 1211 | 	u32 tmp = 0; | 
 | 1212 | 	u32 time_period = 0; | 
 | 1213 | 	u32 tcount = 0; | 
 | 1214 | 	u32 ticks = 0; | 
 | 1215 | 	u32 clock = 0; | 
 | 1216 | 	u32 fparam = 0; | 
| Tejun Heo | 4447d35 | 2007-04-17 23:44:08 +0900 | [diff] [blame] | 1217 | 	void __iomem *mmio = host->iomap[PDC_MMIO_BAR]; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1218 |  | 
 | 1219 | 	/* hard-code chip #0 */ | 
| Jeff Garzik | 5796d1c | 2007-10-26 00:03:37 -0400 | [diff] [blame] | 1220 | 	mmio += PDC_CHIP0_OFS; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1221 |  | 
 | 1222 | 	/* Initialize PLL based upon PCI Bus Frequency */ | 
 | 1223 |  | 
 | 1224 | 	/* Initialize Time Period Register */ | 
 | 1225 | 	writel(0xffffffff, mmio + PDC_TIME_PERIOD); | 
 | 1226 | 	time_period = readl(mmio + PDC_TIME_PERIOD); | 
 | 1227 | 	VPRINTK("Time Period Register (0x40): 0x%x\n", time_period); | 
 | 1228 |  | 
 | 1229 | 	/* Enable timer */ | 
| Jeff Garzik | b2d46b6 | 2007-05-27 22:58:54 -0400 | [diff] [blame] | 1230 | 	writel(PDC_TIMER_DEFAULT, mmio + PDC_TIME_CONTROL); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1231 | 	readl(mmio + PDC_TIME_CONTROL); | 
 | 1232 |  | 
 | 1233 | 	/* Wait 3 seconds */ | 
 | 1234 | 	msleep(3000); | 
 | 1235 |  | 
| Jeff Garzik | 8a60a07 | 2005-07-31 13:13:24 -0400 | [diff] [blame] | 1236 | 	/* | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1237 | 	   When timer is enabled, counter is decreased every internal | 
 | 1238 | 	   clock cycle. | 
 | 1239 | 	*/ | 
 | 1240 |  | 
 | 1241 | 	tcount = readl(mmio + PDC_TIME_COUNTER); | 
 | 1242 | 	VPRINTK("Time Counter Register (0x44): 0x%x\n", tcount); | 
 | 1243 |  | 
| Jeff Garzik | 8a60a07 | 2005-07-31 13:13:24 -0400 | [diff] [blame] | 1244 | 	/* | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1245 | 	   If SX4 is on PCI-X bus, after 3 seconds, the timer counter | 
 | 1246 | 	   register should be >= (0xffffffff - 3x10^8). | 
 | 1247 | 	*/ | 
| Jeff Garzik | b447916 | 2007-10-25 20:47:30 -0400 | [diff] [blame] | 1248 | 	if (tcount >= PCI_X_TCOUNT) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1249 | 		ticks = (time_period - tcount); | 
 | 1250 | 		VPRINTK("Num counters 0x%x (%d)\n", ticks, ticks); | 
| Jeff Garzik | 8a60a07 | 2005-07-31 13:13:24 -0400 | [diff] [blame] | 1251 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1252 | 		clock = (ticks / 300000); | 
 | 1253 | 		VPRINTK("10 * Internal clk = 0x%x (%d)\n", clock, clock); | 
| Jeff Garzik | 8a60a07 | 2005-07-31 13:13:24 -0400 | [diff] [blame] | 1254 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1255 | 		clock = (clock * 33); | 
 | 1256 | 		VPRINTK("10 * Internal clk * 33 = 0x%x (%d)\n", clock, clock); | 
 | 1257 |  | 
 | 1258 | 		/* PLL F Param (bit 22:16) */ | 
 | 1259 | 		fparam = (1400000 / clock) - 2; | 
 | 1260 | 		VPRINTK("PLL F Param: 0x%x (%d)\n", fparam, fparam); | 
| Jeff Garzik | 8a60a07 | 2005-07-31 13:13:24 -0400 | [diff] [blame] | 1261 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1262 | 		/* OD param = 0x2 (bit 31:30), R param = 0x5 (bit 29:25) */ | 
 | 1263 | 		pci_status = (0x8a001824 | (fparam << 16)); | 
 | 1264 | 	} else | 
 | 1265 | 		pci_status = PCI_PLL_INIT; | 
 | 1266 |  | 
 | 1267 | 	/* Initialize PLL. */ | 
 | 1268 | 	VPRINTK("pci_status: 0x%x\n", pci_status); | 
 | 1269 | 	writel(pci_status, mmio + PDC_CTL_STATUS); | 
 | 1270 | 	readl(mmio + PDC_CTL_STATUS); | 
 | 1271 |  | 
| Jeff Garzik | 8a60a07 | 2005-07-31 13:13:24 -0400 | [diff] [blame] | 1272 | 	/* | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1273 | 	   Read SPD of DIMM by I2C interface, | 
 | 1274 | 	   and program the DIMM Module Controller. | 
 | 1275 | 	*/ | 
| Tejun Heo | 4447d35 | 2007-04-17 23:44:08 +0900 | [diff] [blame] | 1276 | 	if (!(speed = pdc20621_detect_dimm(host))) { | 
| Jeff Garzik | 8a60a07 | 2005-07-31 13:13:24 -0400 | [diff] [blame] | 1277 | 		printk(KERN_ERR "Detect Local DIMM Fail\n"); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1278 | 		return 1;	/* DIMM error */ | 
| Jeff Garzik | 5796d1c | 2007-10-26 00:03:37 -0400 | [diff] [blame] | 1279 | 	} | 
 | 1280 | 	VPRINTK("Local DIMM Speed = %d\n", speed); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1281 |  | 
| Jeff Garzik | 5796d1c | 2007-10-26 00:03:37 -0400 | [diff] [blame] | 1282 | 	/* Programming DIMM0 Module Control Register (index_CID0:80h) */ | 
| Tejun Heo | 4447d35 | 2007-04-17 23:44:08 +0900 | [diff] [blame] | 1283 | 	size = pdc20621_prog_dimm0(host); | 
| Jeff Garzik | 5796d1c | 2007-10-26 00:03:37 -0400 | [diff] [blame] | 1284 | 	VPRINTK("Local DIMM Size = %dMB\n", size); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1285 |  | 
| Jeff Garzik | 5796d1c | 2007-10-26 00:03:37 -0400 | [diff] [blame] | 1286 | 	/* Programming DIMM Module Global Control Register (index_CID0:88h) */ | 
| Tejun Heo | 4447d35 | 2007-04-17 23:44:08 +0900 | [diff] [blame] | 1287 | 	if (pdc20621_prog_dimm_global(host)) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1288 | 		printk(KERN_ERR "Programming DIMM Module Global Control Register Fail\n"); | 
 | 1289 | 		return 1; | 
| Jeff Garzik | 5796d1c | 2007-10-26 00:03:37 -0400 | [diff] [blame] | 1290 | 	} | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1291 |  | 
 | 1292 | #ifdef ATA_VERBOSE_DEBUG | 
 | 1293 | 	{ | 
| Jeff Garzik | 5796d1c | 2007-10-26 00:03:37 -0400 | [diff] [blame] | 1294 | 		u8 test_parttern1[40] = | 
 | 1295 | 			{0x55,0xAA,'P','r','o','m','i','s','e',' ', | 
 | 1296 | 			'N','o','t',' ','Y','e','t',' ', | 
 | 1297 | 			'D','e','f','i','n','e','d',' ', | 
 | 1298 | 			'1','.','1','0', | 
 | 1299 | 			'9','8','0','3','1','6','1','2',0,0}; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1300 | 		u8 test_parttern2[40] = {0}; | 
 | 1301 |  | 
| Jeff Garzik | 5796d1c | 2007-10-26 00:03:37 -0400 | [diff] [blame] | 1302 | 		pdc20621_put_to_dimm(host, test_parttern2, 0x10040, 40); | 
 | 1303 | 		pdc20621_put_to_dimm(host, test_parttern2, 0x40, 40); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1304 |  | 
| Jeff Garzik | 5796d1c | 2007-10-26 00:03:37 -0400 | [diff] [blame] | 1305 | 		pdc20621_put_to_dimm(host, test_parttern1, 0x10040, 40); | 
 | 1306 | 		pdc20621_get_from_dimm(host, test_parttern2, 0x40, 40); | 
| Jeff Garzik | 8a60a07 | 2005-07-31 13:13:24 -0400 | [diff] [blame] | 1307 | 		printk(KERN_ERR "%x, %x, %s\n", test_parttern2[0], | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1308 | 		       test_parttern2[1], &(test_parttern2[2])); | 
| Jeff Garzik | 5796d1c | 2007-10-26 00:03:37 -0400 | [diff] [blame] | 1309 | 		pdc20621_get_from_dimm(host, test_parttern2, 0x10040, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1310 | 				       40); | 
| Jeff Garzik | 8a60a07 | 2005-07-31 13:13:24 -0400 | [diff] [blame] | 1311 | 		printk(KERN_ERR "%x, %x, %s\n", test_parttern2[0], | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1312 | 		       test_parttern2[1], &(test_parttern2[2])); | 
 | 1313 |  | 
| Jeff Garzik | 5796d1c | 2007-10-26 00:03:37 -0400 | [diff] [blame] | 1314 | 		pdc20621_put_to_dimm(host, test_parttern1, 0x40, 40); | 
 | 1315 | 		pdc20621_get_from_dimm(host, test_parttern2, 0x40, 40); | 
| Jeff Garzik | 8a60a07 | 2005-07-31 13:13:24 -0400 | [diff] [blame] | 1316 | 		printk(KERN_ERR "%x, %x, %s\n", test_parttern2[0], | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1317 | 		       test_parttern2[1], &(test_parttern2[2])); | 
 | 1318 | 	} | 
 | 1319 | #endif | 
 | 1320 |  | 
 | 1321 | 	/* ECC initiliazation. */ | 
 | 1322 |  | 
| Tejun Heo | 4447d35 | 2007-04-17 23:44:08 +0900 | [diff] [blame] | 1323 | 	pdc20621_i2c_read(host, PDC_DIMM0_SPD_DEV_ADDRESS, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1324 | 			  PDC_DIMM_SPD_TYPE, &spd0); | 
 | 1325 | 	if (spd0 == 0x02) { | 
 | 1326 | 		VPRINTK("Start ECC initialization\n"); | 
 | 1327 | 		addr = 0; | 
 | 1328 | 		length = size * 1024 * 1024; | 
 | 1329 | 		while (addr < length) { | 
| Tejun Heo | 4447d35 | 2007-04-17 23:44:08 +0900 | [diff] [blame] | 1330 | 			pdc20621_put_to_dimm(host, (void *) &tmp, addr, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1331 | 					     sizeof(u32)); | 
 | 1332 | 			addr += sizeof(u32); | 
 | 1333 | 		} | 
 | 1334 | 		VPRINTK("Finish ECC initialization\n"); | 
 | 1335 | 	} | 
 | 1336 | 	return 0; | 
 | 1337 | } | 
 | 1338 |  | 
 | 1339 |  | 
| Tejun Heo | 4447d35 | 2007-04-17 23:44:08 +0900 | [diff] [blame] | 1340 | static void pdc_20621_init(struct ata_host *host) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1341 | { | 
 | 1342 | 	u32 tmp; | 
| Tejun Heo | 4447d35 | 2007-04-17 23:44:08 +0900 | [diff] [blame] | 1343 | 	void __iomem *mmio = host->iomap[PDC_MMIO_BAR]; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1344 |  | 
 | 1345 | 	/* hard-code chip #0 */ | 
 | 1346 | 	mmio += PDC_CHIP0_OFS; | 
 | 1347 |  | 
 | 1348 | 	/* | 
 | 1349 | 	 * Select page 0x40 for our 32k DIMM window | 
 | 1350 | 	 */ | 
 | 1351 | 	tmp = readl(mmio + PDC_20621_DIMM_WINDOW) & 0xffff0000; | 
 | 1352 | 	tmp |= PDC_PAGE_WINDOW;	/* page 40h; arbitrarily selected */ | 
 | 1353 | 	writel(tmp, mmio + PDC_20621_DIMM_WINDOW); | 
 | 1354 |  | 
 | 1355 | 	/* | 
 | 1356 | 	 * Reset Host DMA | 
 | 1357 | 	 */ | 
 | 1358 | 	tmp = readl(mmio + PDC_HDMA_CTLSTAT); | 
 | 1359 | 	tmp |= PDC_RESET; | 
 | 1360 | 	writel(tmp, mmio + PDC_HDMA_CTLSTAT); | 
 | 1361 | 	readl(mmio + PDC_HDMA_CTLSTAT);		/* flush */ | 
 | 1362 |  | 
 | 1363 | 	udelay(10); | 
 | 1364 |  | 
 | 1365 | 	tmp = readl(mmio + PDC_HDMA_CTLSTAT); | 
 | 1366 | 	tmp &= ~PDC_RESET; | 
 | 1367 | 	writel(tmp, mmio + PDC_HDMA_CTLSTAT); | 
 | 1368 | 	readl(mmio + PDC_HDMA_CTLSTAT);		/* flush */ | 
 | 1369 | } | 
 | 1370 |  | 
| Jeff Garzik | 5796d1c | 2007-10-26 00:03:37 -0400 | [diff] [blame] | 1371 | static int pdc_sata_init_one(struct pci_dev *pdev, | 
 | 1372 | 			     const struct pci_device_id *ent) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1373 | { | 
 | 1374 | 	static int printed_version; | 
| Tejun Heo | 4447d35 | 2007-04-17 23:44:08 +0900 | [diff] [blame] | 1375 | 	const struct ata_port_info *ppi[] = | 
 | 1376 | 		{ &pdc_port_info[ent->driver_data], NULL }; | 
 | 1377 | 	struct ata_host *host; | 
| Tejun Heo | 24dc5f3 | 2007-01-20 16:00:28 +0900 | [diff] [blame] | 1378 | 	struct pdc_host_priv *hpriv; | 
| Tejun Heo | cbcdd87 | 2007-08-18 13:14:55 +0900 | [diff] [blame] | 1379 | 	int i, rc; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1380 |  | 
 | 1381 | 	if (!printed_version++) | 
| Jeff Garzik | a9524a7 | 2005-10-30 14:39:11 -0500 | [diff] [blame] | 1382 | 		dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n"); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1383 |  | 
| Tejun Heo | 4447d35 | 2007-04-17 23:44:08 +0900 | [diff] [blame] | 1384 | 	/* allocate host */ | 
 | 1385 | 	host = ata_host_alloc_pinfo(&pdev->dev, ppi, 4); | 
 | 1386 | 	hpriv = devm_kzalloc(&pdev->dev, sizeof(*hpriv), GFP_KERNEL); | 
 | 1387 | 	if (!host || !hpriv) | 
 | 1388 | 		return -ENOMEM; | 
 | 1389 |  | 
 | 1390 | 	host->private_data = hpriv; | 
 | 1391 |  | 
 | 1392 | 	/* acquire resources and fill host */ | 
| Tejun Heo | 24dc5f3 | 2007-01-20 16:00:28 +0900 | [diff] [blame] | 1393 | 	rc = pcim_enable_device(pdev); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1394 | 	if (rc) | 
 | 1395 | 		return rc; | 
 | 1396 |  | 
| Tejun Heo | 0d5ff56 | 2007-02-01 15:06:36 +0900 | [diff] [blame] | 1397 | 	rc = pcim_iomap_regions(pdev, (1 << PDC_MMIO_BAR) | (1 << PDC_DIMM_BAR), | 
 | 1398 | 				DRV_NAME); | 
 | 1399 | 	if (rc == -EBUSY) | 
| Tejun Heo | 24dc5f3 | 2007-01-20 16:00:28 +0900 | [diff] [blame] | 1400 | 		pcim_pin_device(pdev); | 
| Tejun Heo | 0d5ff56 | 2007-02-01 15:06:36 +0900 | [diff] [blame] | 1401 | 	if (rc) | 
| Tejun Heo | 24dc5f3 | 2007-01-20 16:00:28 +0900 | [diff] [blame] | 1402 | 		return rc; | 
| Tejun Heo | 4447d35 | 2007-04-17 23:44:08 +0900 | [diff] [blame] | 1403 | 	host->iomap = pcim_iomap_table(pdev); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1404 |  | 
| Tejun Heo | cbcdd87 | 2007-08-18 13:14:55 +0900 | [diff] [blame] | 1405 | 	for (i = 0; i < 4; i++) { | 
 | 1406 | 		struct ata_port *ap = host->ports[i]; | 
 | 1407 | 		void __iomem *base = host->iomap[PDC_MMIO_BAR] + PDC_CHIP0_OFS; | 
 | 1408 | 		unsigned int offset = 0x200 + i * 0x80; | 
 | 1409 |  | 
 | 1410 | 		pdc_sata_setup_port(&ap->ioaddr, base + offset); | 
 | 1411 |  | 
 | 1412 | 		ata_port_pbar_desc(ap, PDC_MMIO_BAR, -1, "mmio"); | 
 | 1413 | 		ata_port_pbar_desc(ap, PDC_DIMM_BAR, -1, "dimm"); | 
 | 1414 | 		ata_port_pbar_desc(ap, PDC_MMIO_BAR, offset, "port"); | 
 | 1415 | 	} | 
| Tejun Heo | 4447d35 | 2007-04-17 23:44:08 +0900 | [diff] [blame] | 1416 |  | 
 | 1417 | 	/* configure and activate */ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1418 | 	rc = pci_set_dma_mask(pdev, ATA_DMA_MASK); | 
 | 1419 | 	if (rc) | 
| Tejun Heo | 24dc5f3 | 2007-01-20 16:00:28 +0900 | [diff] [blame] | 1420 | 		return rc; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1421 | 	rc = pci_set_consistent_dma_mask(pdev, ATA_DMA_MASK); | 
 | 1422 | 	if (rc) | 
| Tejun Heo | 24dc5f3 | 2007-01-20 16:00:28 +0900 | [diff] [blame] | 1423 | 		return rc; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1424 |  | 
| Tejun Heo | 4447d35 | 2007-04-17 23:44:08 +0900 | [diff] [blame] | 1425 | 	if (pdc20621_dimm_init(host)) | 
| Tejun Heo | 24dc5f3 | 2007-01-20 16:00:28 +0900 | [diff] [blame] | 1426 | 		return -ENOMEM; | 
| Tejun Heo | 4447d35 | 2007-04-17 23:44:08 +0900 | [diff] [blame] | 1427 | 	pdc_20621_init(host); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1428 |  | 
 | 1429 | 	pci_set_master(pdev); | 
| Tejun Heo | 4447d35 | 2007-04-17 23:44:08 +0900 | [diff] [blame] | 1430 | 	return ata_host_activate(host, pdev->irq, pdc20621_interrupt, | 
 | 1431 | 				 IRQF_SHARED, &pdc_sata_sht); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1432 | } | 
 | 1433 |  | 
 | 1434 |  | 
 | 1435 | static int __init pdc_sata_init(void) | 
 | 1436 | { | 
| Pavel Roskin | b788719 | 2006-08-10 18:13:18 +0900 | [diff] [blame] | 1437 | 	return pci_register_driver(&pdc_sata_pci_driver); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1438 | } | 
 | 1439 |  | 
 | 1440 |  | 
 | 1441 | static void __exit pdc_sata_exit(void) | 
 | 1442 | { | 
 | 1443 | 	pci_unregister_driver(&pdc_sata_pci_driver); | 
 | 1444 | } | 
 | 1445 |  | 
 | 1446 |  | 
 | 1447 | MODULE_AUTHOR("Jeff Garzik"); | 
 | 1448 | MODULE_DESCRIPTION("Promise SATA low-level driver"); | 
 | 1449 | MODULE_LICENSE("GPL"); | 
 | 1450 | MODULE_DEVICE_TABLE(pci, pdc_sata_pci_tbl); | 
 | 1451 | MODULE_VERSION(DRV_VERSION); | 
 | 1452 |  | 
 | 1453 | module_init(pdc_sata_init); | 
 | 1454 | module_exit(pdc_sata_exit); |