linux-ide.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Andrei Martynov <andrei.martynov@web.de>
To: Alan Cox <alan@lxorguk.ukuu.org.uk>
Cc: linux-ide@vger.kernel.org
Subject: Re[2]: porting IDE patches to PATA driver
Date: Fri, 26 Jun 2009 15:24:40 +0200	[thread overview]
Message-ID: <96179821.20090626152440@web.de> (raw)
In-Reply-To: <20090625122958.510c28ad@lxorguk.ukuu.org.uk>

[-- Attachment #1: Type: text/plain, Size: 998 bytes --]

Hi Alan,

Thursday, June 25, 2009, 1:29:58 PM, you wrote:

> It would be useful I think if you posted the current code, and also to
> know if you see the same problem (lost IRQ etc) with ATAPI as well as ATA

I've attached the current driver and reduced set of patches I'm trying
to port. Truly speaking I have very little knowledge about IDE / ATAPI
interfaces and simply try to reproduce the original logic using modern
libata functions. As you can see the new pata driver redefines set_piomode /
set_dmamode to tune chipset and bmdma_start to flush CPU cache. It
works fine in UDMA mode on a newer chipset but I cannot get MW-DMA
working on an older chipset.

The complete patch for the EM85XX architecture can be found at
http://mg35tools.svn.sourceforge.net/viewvc/mg35tools/trunk/sources/linux-2.6/

original 2.4.17 kernel for Sigma EM8500 is published at
http://www.uclinux.org/pub/uClinux/ports/arm/EM8500/

-- 
Best regards,
 Andrei                            mailto:andrei.martynov@web.de

[-- Attachment #2: em85xx-ide.patch --]
[-- Type: application/octet-stream, Size: 26160 bytes --]

diff -r 1e6ff995c4ef include/asm-armnommu/arch-jasper/ide.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/include/asm-armnommu/arch-jasper/ide.h	Fri Jun 26 10:47:45 2009 +0200
@@ -0,0 +1,73 @@
+/*
+ * linux/include/asm-armnommu/arch-jasper/ide.h
+ *
+ */
+ 
+#include <linux/config.h>
+#include <asm/irq.h>
+#include <asm/hardware.h>
+
+#define HAVE_ARCH_IN_BYTE
+#define HAVE_ARCH_OUT_BYTE
+
+#define OUT_BYTE(b,p)                \
+do {                                 \
+   int _tmp_;                        \
+   outb((b),(p));                    \
+   for(_tmp_=0; _tmp_<1000; _tmp_++);  \
+} while(0) 
+
+static inline byte jasper_ide_in_byte(int p)
+{
+	byte b; int _tmp;
+
+	b = (byte)inb(p);
+	for(_tmp=0; _tmp<1000; _tmp++);
+	return b;
+}
+
+#define IN_BYTE(p) jasper_ide_in_byte(p)
+
+
+/*
+ * Set up a hw structure for a specified data port, control port and IRQ.
+ * This should follow whatever the default interface uses.
+ */
+static __inline__ void
+ide_init_hwif_ports(hw_regs_t *hw, int data_port, int ctrl_port, int *irq)
+{
+	ide_ioreg_t reg;
+	int i;
+	int regincr = 1;
+	
+	memset(hw, 0, sizeof(*hw));
+
+	reg = (ide_ioreg_t)data_port;
+
+	for (i = IDE_DATA_OFFSET; i <= IDE_STATUS_OFFSET; i++) {
+		hw->io_ports[i] = reg;
+		reg += regincr;
+	}
+	
+	hw->io_ports[IDE_CONTROL_OFFSET] = (ide_ioreg_t) ctrl_port;
+	
+	if (irq)
+		*irq = 0;
+}
+
+
+/*
+ * This registers the standard ports for this architecture with the IDE
+ * driver.
+ */
+static __inline__ void
+ide_init_default_hwifs(void)
+{
+    hw_regs_t hw;
+
+    ide_init_hwif_ports(&hw, JASPER_IDE_BASE + IDE_PRI_DATA, JASPER_IDE_BASE + IDE_PRI_DEVICE_CONTROL, NULL);
+    hw.irq = IDE_IRQ;
+    ide_register_hw(&hw, NULL);
+}
+
+
diff -r 1e6ff995c4ef drivers/ide/ide-cd.c
--- a/drivers/ide/ide-cd.c	Fri Jun 26 10:02:05 2009 +0200
+++ b/drivers/ide/ide-cd.c	Fri Jun 26 10:47:45 2009 +0200
@@ -1312,7 +1358,25 @@ static ide_startstop_t cdrom_pc_intr (id
 	int ireason, len, stat, thislen;
 	struct request *rq = HWGROUP(drive)->rq;
 	struct packet_command *pc = (struct packet_command *)rq->buffer;
+	struct cdrom_info *info = drive->driver_data;
+	int dma = info->dma;
+	int dma_error;
 	ide_startstop_t startstop;
+
+	/* Check for errors. */
+	if (dma) {
+		info->dma = 0;
+		if ((dma_error = HWIF(drive)->dmaproc(ide_dma_end, drive))) {
+			/*
+			 * We don't disable drive DMA for packet DMA errors.
+			 * It's handled in cdda_read_audio()
+			 */
+			/* HWIF(drive)->dmaproc(ide_dma_off, drive); */
+			printk ("cdrom_pc_intr: dma error\n");
+			pc->stat = 2;	/* 2 -> DMA error */
+			printk(KERN_ERR "CDROM packet DMA error (0x%02x)\n", dma_error);
+		}
+	}
 
 	/* Check for errors. */
 	if (cdrom_decode_status (&startstop, drive, 0, &stat))
@@ -1321,6 +1385,14 @@ static ide_startstop_t cdrom_pc_intr (id
 	/* Read the interrupt reason and the transfer length. */
 	ireason = IN_BYTE (IDE_NSECTOR_REG);
 	len = IN_BYTE (IDE_LCYL_REG) + 256 * IN_BYTE (IDE_HCYL_REG);
+
+	if (dma) {
+		/*
+		 * If DMA succeeded, we have all the data
+		 */
+		pc->buffer += pc->buflen;
+		pc->buflen = 0;
+	}
 
 	/* If DRQ is clear, the command has completed.
 	   Complain if we still have data left to transfer. */
@@ -1346,7 +1418,10 @@ static ide_startstop_t cdrom_pc_intr (id
 			printk ("%s: cdrom_pc_intr: data underrun %d\n",
 				drive->name, pc->buflen);
 			*/
-			pc->stat = 1;
+			// XXX sigma - let's make it ok to send more memory than requested if
+			// we are just reading the TOC
+			if (pc->c[0] != 0x43)
+				pc->stat = 1;
 			cdrom_end_request (1, drive);
 		}
 		return ide_stopped;
@@ -1396,6 +1471,8 @@ static ide_startstop_t cdrom_pc_intr (id
 			"appears confused (ireason = 0x%2x)\n",
 			drive->name, ireason);
 		pc->stat = 1;
+		cdrom_end_request (1, drive);
+		return ide_stopped;
 	}
 
 	/* Now we wait for another interrupt. */
@@ -1424,7 +1501,12 @@ static ide_startstop_t cdrom_do_packet_c
 	struct packet_command *pc = (struct packet_command *)rq->buffer;
 	struct cdrom_info *info = drive->driver_data;
 
-	info->dma = 0;
+	if (rq->bh) {
+		info->dma = 1;
+	} else {
+		info->dma = 0;
+	}
+
 	info->cmd = 0;
 	pc->stat = 0;
 	len = pc->buflen;
@@ -1447,6 +1529,13 @@ void cdrom_sleep (int time)
 	} while (sleep);
 }
 
+/*
+ * end_buffer_io_sync() is not exported
+ */
+static void cdrom_end_buffer_io_sync(struct buffer_head *bh, int uptodate)
+{
+}
+
 static
 int cdrom_queue_packet_command(ide_drive_t *drive, struct packet_command *pc)
 {
@@ -1459,7 +1548,27 @@ int cdrom_queue_packet_command(ide_drive
 
 	/* Start of retry loop. */
 	do {
+		struct buffer_head bh;
+
 		ide_init_drive_cmd (&req);
+
+		if (pc->do_dma) {
+			/* Hack up a buffer_head for IDE DMA's use */
+			memset(&bh, 0, sizeof(bh));
+			bh.b_size = pc->buflen;
+			bh.b_data = pc->buffer;
+			bh.b_state = (1 << BH_Lock) | (1 << BH_Mapped) |
+					(1 << BH_Req);
+			bh.b_end_io = cdrom_end_buffer_io_sync;
+#if 0		/* Needed by end_buffer_io_sync, but not cdrom_end_buffer_io_sync */
+			atomic_set(&bh.b_count, 1);
+			init_waitqueue_head(&bh.b_wait);
+#endif
+			req.bh = &bh;
+		}
+		else
+			req.bh = 0;
+
 		req.cmd = PACKET_COMMAND;
 		req.buffer = (char *)pc;
 		if (ide_do_drive_cmd (drive, &req, ide_wait)) {
@@ -2202,7 +2311,12 @@ static int ide_cdrom_packet(struct cdrom
 	pc.quiet = cgc->quiet;
 	pc.timeout = cgc->timeout;
 	pc.sense = cgc->sense;
-	return cgc->stat = cdrom_queue_packet_command(drive, &pc);
+	if (cgc->do_dma && drive->using_dma)
+		pc.do_dma = 1;
+	cgc->stat = cdrom_queue_packet_command(drive, &pc);
+	if (pc.stat == 2)	/* DMA error: fall back to lower mode */
+		cgc->dma_error = 1;
+	return cgc->stat;
 }
 
 static
diff -r 1e6ff995c4ef drivers/ide/ide-cd.h
--- a/drivers/ide/ide-cd.h	Fri Jun 26 10:02:05 2009 +0200
+++ b/drivers/ide/ide-cd.h	Fri Jun 26 10:47:45 2009 +0200
@@ -109,6 +109,7 @@ struct packet_command {
 	int quiet;
 	int timeout;
 	struct request_sense *sense;
+	int do_dma;
 	unsigned char c[12];
 };
 
diff -r 1e6ff995c4ef drivers/ide/ide-dma.c
--- a/drivers/ide/ide-dma.c	Fri Jun 26 10:02:05 2009 +0200
+++ b/drivers/ide/ide-dma.c	Fri Jun 26 10:47:45 2009 +0200
@@ -90,12 +90,56 @@
 #include <asm/io.h>
 #include <asm/irq.h>
 
+#include <asm/arch/quasar.h>
+
+// XXX sigma
+#define ASSERT(cond,msg) {if(!(cond)) printk(msg);}
+struct packet_command {
+	char *buffer;
+	int buflen;
+	int stat;
+	int quiet;
+	int timeout;
+	struct request_sense *sense;
+	int do_dma;
+	unsigned char c[12];
+};
+unsigned int CPU_RD_32BIT_REG (int BASE_ADDR, int REG) 
+{
+	return(*(volatile int *)(BASE_ADDR + REG));
+}
+void CPU_WRRD_32BIT_REG (int BASE_ADDR , int REG, int DATA) 
+{
+	*(volatile int *)(BASE_ADDR + REG) = DATA;
+}
+void EM8500_SET_PLL_SPEED (int MULT, int DIV, int DIV_SEL)
+{
+	int temp;
+
+	// read and write pll ctrl value
+	temp = CPU_RD_32BIT_REG (0x00600000, ((0x1C04) << 2));
+	temp = temp & 0xF000;
+	if (DIV_SEL)
+	{	
+		temp = temp | 0x02;
+	}
+	temp = temp | (DIV  <<8 );
+	temp = temp | (MULT <<2 );
+
+	CPU_WRRD_32BIT_REG (0x00600000, ((0x1C04) << 2), (temp & 0x7FFF));
+
+} //END: void EM8500_SET_PLL_SPEED (int MULT, int DIV, int DIV_SEL)
+
 #ifdef CONFIG_PCI
 #define PCIDEV(hwif)	((hwif)->pci_dev)
 #else
 #define PCIDEV(hwif)	NULL
 #endif
 
+#define BMIC_OFFSET 0
+#define BMIS_OFFSET 4 
+#define BMIDTP_OFFSET 8
+#define DMA_IOREGION_SIZE 16
 #undef CONFIG_BLK_DEV_IDEDMA_TIMEOUT
 /*
  * Long lost data from 2.0.34 that is now in 2.0.39
@@ -256,7 +301,7 @@ ide_startstop_t ide_dma_intr (ide_drive_
 
 static int ide_build_sglist (ide_hwif_t *hwif, struct request *rq)
 {
-	struct buffer_head *bh;
+	struct buffer_head *bh, sbh;
 	struct scatterlist *sg = hwif->sg_table;
 	int nents = 0;
 
@@ -268,6 +313,24 @@ static int ide_build_sglist (ide_hwif_t 
 	else
 		hwif->sg_dma_direction = PCI_DMA_TODEVICE;
 	bh = rq->bh;
+
+	ASSERT (bh, "kernel bug: bh==NULL!\n");
+// XXX sigma
+	if (bh == 0)
+	{
+		struct packet_command *pc = (struct packet_command *)rq->buffer;
+		bh = &sbh;
+		sbh.b_data = pc->buffer;
+		sbh.b_size = pc->buflen;
+		sbh.b_reqnext = NULL;
+	}
+//	printk ("***************\n");
+//	printk ("bh = 0x%08lx\n", (unsigned long)bh);
+//	printk ("bh->b_data = 0x%08lx\n", (unsigned long)bh->b_data);
+//	printk ("bh->b_size = 0x%08lx\n", (unsigned long)bh->b_size);
+//	printk ("bh->b_reqnext = 0x%08lx\n", (unsigned long)bh->b_reqnext);
+//	printk ("***************\n");
+
 	do {
 		unsigned char *virt_addr = bh->b_data;
 		unsigned int size = bh->b_size;
@@ -283,10 +346,18 @@ static int ide_build_sglist (ide_hwif_t 
 		memset(&sg[nents], 0, sizeof(*sg));
 		sg[nents].address = virt_addr;
 		sg[nents].length = size;
-		nents++;
+
+		nents++;		
 	} while (bh != NULL);
-
 	return pci_map_sg(PCIDEV(hwif), sg, nents, hwif->sg_dma_direction);
+/*	{
+		int i;
+		for (i = 0; i < nents; i++, sg++) {
+			consistent_sync(sg->address, sg->length, hwif->sg_dma_direction);
+			sg->dma_address = virt_to_bus(sg->address);
+		}
+	}
+	return nents; */
 }
 
 /*
@@ -336,6 +405,10 @@ int ide_build_dmatable (ide_drive_t *dri
 					bcount = cur_len;
 				*table++ = cpu_to_le32(cur_addr);
 				xcount = bcount & 0xffff;
+#if 1
+				// added by Ho Lee 
+				quasar_flush_cache_data_region(cur_addr, cur_addr + (xcount == 0 ? 0x10000 : xcount));
+#endif
 				if (is_trm290_chipset)
 					xcount = ((xcount >> 2) - 1) << 16;
 				if (xcount == 0x0000) {
@@ -467,6 +540,7 @@ static int config_drive_for_dma (ide_dri
 	ide_hwif_t *hwif = HWIF(drive);
 	int autodma = hwif->autodma;
 
+#ifndef CONFIG_ARCH_JASPER // we dont have no BIOS
 	if (hwif->chipset != ide_trm290 && hwif->chipset != ide_acorn) {
 		/* take note of what the bios did when setting up
 		 * the interface.  If the BIOS didn't configure
@@ -474,7 +548,7 @@ static int config_drive_for_dma (ide_dri
 		 * it unless we are prepared to configure the
 		 * drive as well.  -- rmk
 		 */
-		byte dma_stat = inb(hwif->dma_base+2);
+		byte dma_stat = inb(hwif->dma_base+BMIS_OFFSET);
 
 		if (drive->select.b.unit) {
 			if (!(dma_stat & 0x40))
@@ -484,7 +558,14 @@ static int config_drive_for_dma (ide_dri
 				autodma = 0;
 		}
 	}
+#endif
 
+	printk ("config_drive_for_dma:\n");
+	printk (" capability    = 0x%04x\n", id->capability);
+	printk (" field_valid   = 0x%04x\n", id->field_valid);
+	printk (" dma_lword (s) = 0x%04x\n", id->dma_1word);
+	printk (" dma_mword (m) = 0x%04x\n", id->dma_mword);
+	printk (" autodma       = 0x%04x\n", autodma);
 	if (id && (id->capability & 1) && autodma) {
 		/* Consult the list of known "bad" drives */
 		if (ide_dmaproc(ide_dma_bad_drive, drive))
@@ -500,12 +581,21 @@ static int config_drive_for_dma (ide_dri
 				return hwif->dmaproc(ide_dma_on, drive);
 		/* Enable DMA on any drive that has mode2 DMA (multi or single) enabled */
 		if (id->field_valid & 2)	/* regular DMA */
+		{
+			// XXX sigma
 			if ((id->dma_mword & 0x404) == 0x404 || (id->dma_1word & 0x404) == 0x404)
 				return hwif->dmaproc(ide_dma_on, drive);
+			// allow any mode
+			if (id->dma_mword & 0xff00)	// single word dma
+				return hwif->dmaproc(ide_dma_on, drive);
+			if (id->dma_1word & 0xff00)	// multiword dma
+				return hwif->dmaproc(ide_dma_on, drive);
+		}
 		/* Consult the list of known "good" drives */
 		if (ide_dmaproc(ide_dma_good_drive, drive))
 			return hwif->dmaproc(ide_dma_on, drive);
 	}
+	printk ("DMA disabled quitely\n");
 	return hwif->dmaproc(ide_dma_off_quietly, drive);
 }
 
@@ -582,31 +672,39 @@ int ide_dmaproc (ide_dma_action_t func, 
 	byte unit			= (drive->select.b.unit & 0x01);
 	unsigned int count, reading	= 0;
 	byte dma_stat;
+	unsigned long ide_int_status;
 
 	switch (func) {
 		case ide_dma_off:
 			printk("%s: DMA disabled\n", drive->name);
 		case ide_dma_off_quietly:
-			outb(inb(dma_base+2) & ~(1<<(5+unit)), dma_base+2);
+			outb(inb(dma_base+BMIS_OFFSET) & ~(1<<(5+unit)), dma_base+BMIS_OFFSET);
+			// XXX sigma: never disable DMA
+			printk("%s: ignore DMA off message.\n", drive->name);
+			return 0;
 		case ide_dma_on:
 			drive->using_dma = (func == ide_dma_on);
 			if (drive->using_dma)
-				outb(inb(dma_base+2)|(1<<(5+unit)), dma_base+2);
+				outb(inb(dma_base+BMIS_OFFSET)|(1<<(5+unit)), dma_base+BMIS_OFFSET);
 			return 0;
 		case ide_dma_check:
 			return config_drive_for_dma (drive);
 		case ide_dma_read:
 			reading = 1 << 3;
-		case ide_dma_write:
+		case ide_dma_write:			
 			SELECT_READ_WRITE(hwif,drive,func);
 			if (!(count = ide_build_dmatable(drive, func)))
+			{
 				return 1;	/* try PIO instead of DMA */
-			outl(hwif->dmatable_dma, dma_base + 4); /* PRD table */
+			}
+			outl(hwif->dmatable_dma, dma_base + BMIDTP_OFFSET); /* PRD table */
 			outb(reading, dma_base);			/* specify r/w */
-			outb(inb(dma_base+2)|6, dma_base+2);		/* clear INTR & ERROR flags */
+			outb(inb(dma_base+BMIS_OFFSET)|6, dma_base+BMIS_OFFSET);		/* clear INTR & ERROR flags */
 			drive->waiting_for_dma = 1;
 			if (drive->media != ide_disk)
+			{
 				return 0;
+			}
 #ifdef CONFIG_BLK_DEV_IDEDMA_TIMEOUT
 			ide_set_handler(drive, &ide_dma_intr, WAIT_CMD, NULL);	/* issue cmd to drive */
 #else /* !CONFIG_BLK_DEV_IDEDMA_TIMEOUT */
@@ -624,19 +722,21 @@ int ide_dmaproc (ide_dma_action_t func, 
 		case ide_dma_end: /* returns 1 on error, 0 otherwise */
 			drive->waiting_for_dma = 0;
 			outb(inb(dma_base)&~1, dma_base);	/* stop DMA */
-			dma_stat = inb(dma_base+2);		/* get DMA status */
-			outb(dma_stat|6, dma_base+2);	/* clear the INTR & ERROR bits */
+			dma_stat = inb(dma_base+BMIS_OFFSET);		/* get DMA status */
+			outb(dma_stat|6, dma_base+BMIS_OFFSET);	/* clear the INTR & ERROR bits */
 			ide_destroy_dmatable(drive);	/* purge DMA mappings */
 			return (dma_stat & 7) != 4 ? (0x10 | dma_stat) : 0;	/* verify good DMA status */
 		case ide_dma_test_irq: /* returns 1 if dma irq issued, 0 otherwise */
-			dma_stat = inb(dma_base+2);
+			dma_stat = inb(dma_base+BMIS_OFFSET);
 #if 0	/* do not set unless you know what you are doing */
 			if (dma_stat & 4) {
 				byte stat = GET_STAT();
-				outb(dma_base+2, dma_stat & 0xE4);
+				outb(dma_base+BMIS_OFFSET, dma_stat & 0xE4);
 			}
 #endif
-			return (dma_stat & 4) == 4;	/* return 1 if INTR asserted */
+			// XXX sigma - check the interrupt status from jasper int controller
+			ide_int_status = *((volatile unsigned long *)0x500220);
+			return (((dma_stat & 4) == 4) || (ide_int_status & 0x800));	/* return 1 if INTR asserted */
 		case ide_dma_bad_drive:
 		case ide_dma_good_drive:
 			return check_drive_lists(drive, (func == ide_dma_good_drive));
diff -r 1e6ff995c4ef drivers/ide/ide-features.c
--- a/drivers/ide/ide-features.c	Fri Jun 26 10:02:05 2009 +0200
+++ b/drivers/ide/ide-features.c	Fri Jun 26 10:47:45 2009 +0200
@@ -285,10 +285,11 @@ int ide_config_drive_speed (ide_drive_t 
 	int	i, error = 1;
 	byte stat;
 
-#if defined(CONFIG_BLK_DEV_IDEDMA) && !defined(CONFIG_DMA_NONPCI)
+#if 0//defined(CONFIG_BLK_DEV_IDEDMA) && !defined(CONFIG_DMA_NONPCI)
 	byte unit = (drive->select.b.unit & 0x01);
-	outb(inb(hwif->dma_base+2) & ~(1<<(5+unit)), hwif->dma_base+2);
+	outb(inb(hwif->dma_base+4) & ~(1<<(5+unit)), hwif->dma_base+4);
 #endif /* (CONFIG_BLK_DEV_IDEDMA) && !(CONFIG_DMA_NONPCI) */
+
 
 	/*
 	 * Don't use ide_wait_cmd here - it will
@@ -355,13 +356,15 @@ int ide_config_drive_speed (ide_drive_t 
 	drive->id->dma_mword &= ~0x0F00;
 	drive->id->dma_1word &= ~0x0F00;
 
-#if defined(CONFIG_BLK_DEV_IDEDMA) && !defined(CONFIG_DMA_NONPCI)
+
+#if 0 //defined(CONFIG_BLK_DEV_IDEDMA) && !defined(CONFIG_DMA_NONPCI)
 	if (speed > XFER_PIO_4) {
 		outb(inb(hwif->dma_base+2)|(1<<(5+unit)), hwif->dma_base+2);
 	} else {
 		outb(inb(hwif->dma_base+2) & ~(1<<(5+unit)), hwif->dma_base+2);
 	}
 #endif /* (CONFIG_BLK_DEV_IDEDMA) && !(CONFIG_DMA_NONPCI) */
+
 
 	switch(speed) {
 		case XFER_UDMA_7:   drive->id->dma_ultra |= 0x8080; break;
diff -r 1e6ff995c4ef drivers/ide/ide-jasper.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/drivers/ide/ide-jasper.c	Fri Jun 26 10:47:45 2009 +0200
@@ -0,0 +1,197 @@
+/* ide-jasper.c */
+
+/* jasper specific initialization stuff for ide */
+
+#include <linux/init.h>
+#include <asm/io.h>
+#include <asm/hardware.h>
+#include <linux/ide.h>
+#include "ide_modes.h"
+
+extern int printk(const char *fmt,...);
+extern void ide_setup_dma (ide_hwif_t *hwif, unsigned long dma_base, unsigned int num_ports);
+
+//#define ENABLE_BMDMA 
+#undef ENABLE_BMDMA
+#define ENABLE_UDMA 
+//#undef ENABLE_UDMA
+
+int ide_config_drive_speed (ide_drive_t *drive, byte speed);
+
+//Setup register 0x54 and 0x58a for UDMA timing
+//Values assume 100Mhz system bus
+static int jasper_tune_chipset(ide_drive_t *drive, byte speed)
+{
+	ide_hwif_t *hwif = HWIF(drive);
+	char channel     = hwif->channel;
+	unsigned short reg54, reg58;
+	int slave = (&HWIF(drive)->drives[1] == drive);
+
+	printk("jasper_tune_chipset: speed = %02x\n", speed);
+	
+	reg54=inw(JASPER_IDE_BASE + 0x54);
+	reg58=inw(JASPER_IDE_BASE + 0x58);
+
+#ifdef ENABLE_UDMA 
+	if(speed>=XFER_UDMA_0 && speed<=XFER_UDMA_5){
+		// ---- This assume 100Mhz system bus - for UDMA
+		if(slave) {
+			reg54 |= 0x2;
+			reg58 |= (speed-XFER_UDMA_0)<<4;
+		}else {
+			reg54 |= 0x1;
+			reg58 |= (speed-XFER_UDMA_0);
+		}
+		// ---- 
+	} else
+#endif // ENABLE_UDMA			
+	{
+		if(slave) {
+			reg54 &= ~0x2;
+		} else {
+			reg54 &= ~0x1;
+		}
+	}
+	
+	outw(reg54, JASPER_IDE_BASE + 0x54);
+	outw(reg58, JASPER_IDE_BASE + 0x58);
+	return ide_config_drive_speed(drive, speed);
+}
+
+static int jasper_config_drive_for_dma (ide_drive_t *drive)
+{
+	struct hd_driveid *id = drive->id;
+	ide_hwif_t *hwif = HWIF(drive);
+	int autodma = hwif->autodma;
+	ide_dma_action_t dma_func = ide_dma_on;
+	byte speed;
+	byte udma_66 = eighty_ninty_three(drive); // 80 c ribbon ?
+
+	printk ("model            = %.40s\n", id->model);
+	printk ("firmware version = %.8s\n", id->fw_rev);
+	printk ("jasper_config_drive_for_dma:\n");
+	printk (" capability    = 0x%04x\n", id->capability);
+	printk (" field_valid   = 0x%04x\n", id->field_valid);
+	printk (" dma_ultra     = 0x%04x\n", id->dma_ultra);
+	printk (" dma_1word (s) = 0x%04x\n", id->dma_1word);
+	printk (" dma_mword (m) = 0x%04x\n", id->dma_mword);
+	printk (" autodma       = 0x%04x\n", autodma);
+	if (id && (id->capability & 1) && autodma) {
+		if (ide_dmaproc(ide_dma_bad_drive, drive)) {
+			// consult list of known "bad" drives
+			dma_func = ide_dma_off;
+// Ultra DMA support			
+#ifdef ENABLE_UDMA
+		} else if ((id->field_valid & 4) && (id->dma_ultra & 0x3F)) {
+			// Enable DMA on any drive that is UltraDMA (mode 0-5) capable
+			// mode3/4/5 needs 80c ribbon
+			if((id->dma_ultra & 0x20) && udma_66)
+				speed = XFER_UDMA_5; 
+			else if((id->dma_ultra & 0x10) && udma_66)
+				speed = XFER_UDMA_4;
+			else if((id->dma_ultra & 0x08) && udma_66)
+				speed = XFER_UDMA_3;
+			else if(id->dma_ultra & 0x04)
+				speed = XFER_UDMA_2;
+			else if(id->dma_ultra & 0x02)
+				speed = XFER_UDMA_1;
+			else if(id->dma_ultra & 0x01)
+				speed = XFER_UDMA_0;
+#endif			
+//Busmaster DMA support
+#ifdef ENABLE_BMDMA
+		} else if((id->field_valid & 2) &&	(id->dma_mword & 0x7)) {
+			// Multiword DMA
+			if(id->dma_mword & 0x4)
+				speed = XFER_MW_DMA_2;
+			else if (id->dma_mword & 0x2)
+				speed = XFER_MW_DMA_1;
+			else
+				speed = XFER_MW_DMA_0;
+		} else if((id->field_valid & 2) && (id->dma_1word & 0x7)) {
+			// Single word DMA
+			if(id->dma_1word & 0x4)
+				speed = XFER_SW_DMA_2;
+			else if (id->dma_1word & 0x2)
+				speed = XFER_SW_DMA_1;
+			else 
+				speed = XFER_SW_DMA_0;
+#endif			
+		} else {
+			speed = XFER_PIO_0 + ide_get_best_pio_mode(drive, 255, 5, NULL);
+			dma_func = ide_dma_off_quietly;
+		}
+
+		if(jasper_tune_chipset(drive, speed)) {
+			printk("Error in jasper_tune_chipset, disbale DMA\n");
+			dma_func = ide_dma_off;
+		}
+		
+		return hwif->dmaproc(dma_func, drive);
+	}
+	
+	printk ("DMA disabled quietly\n");
+	return hwif->dmaproc(ide_dma_off_quietly, drive);
+}
+
+static int jasper_dmaproc(ide_dma_action_t func, ide_drive_t *drive)
+{
+	unsigned char dma_stat;
+	unsigned long dma_base = HWIF(drive)->dma_base;
+	unsigned long dma_count, dma_address;
+	int i;
+	
+	switch (func) {
+		case ide_dma_check:
+			return jasper_config_drive_for_dma(drive);
+		case ide_dma_end:
+			drive->waiting_for_dma=0;
+			for(i=0; i<100; i++) {
+				dma_stat = inb(dma_base + 0x4);  /* get DMA status */
+				if((dma_stat & 0x4) ==4)
+					break;
+				dma_count = inl(dma_base + 0xe4);
+				dma_address = inl(dma_base + 0xe0);
+				//printk("J %1x %08x %04x\n", dma_stat & 0xF, dma_address, dma_count);
+			}
+			outb(inb(dma_base)&~1, dma_base); /* stop DMA */
+			outb(dma_stat|6, dma_base + 0x4); /* clear the INTR & ERROR bits */
+			ide_destroy_dmatable(drive); /* purge DMA mappings */
+			return (dma_stat & 4) != 4 ? (0x10 | dma_stat) : 0;
+		default :
+			break;
+	}
+	/* Other cases are done by generic IDE-DMA code. */
+	return ide_dmaproc(func, drive);
+}
+
+void __init jasper_ide_init(void)
+{
+	ide_hwif_t *hwif;
+	unsigned long dma_base = JASPER_IDE_DMA_BASE;
+	// enable ide interface 
+	
+	//set pio4 to 1
+	
+	outl(0x0010FFFF, JASPER_PIO0_BASE + PIO_DIR);
+	outl(0x0010FFFF, JASPER_PIO0_BASE + PIO_DATA);
+
+	//	choose ide over dvd loader (DVD_AV_CONTROL)
+	//	write 0x8000 to IDETIM
+	outl(0x00, JASPER_DVD_BASE + DVD_AV_CTRL);			//0x00 select IDE over DVD-loader
+	outl(0x8000, JASPER_IDE_BASE + IDE_TIM);
+//	outl(0xb301, JASPER_IDE_BASE + IDE_TIM);		//0x40 IDE decode enable
+
+	printk("JASPER ide controller activated\n");
+	
+	// grab hwif,
+	hwif = &ide_hwifs[0];
+	// setup busmaster dma
+	ide_setup_dma(hwif, dma_base, 16);
+	// override dmaproc
+ 	hwif->dmaproc = &jasper_dmaproc;
+	// autodma on
+	hwif->autodma = 1;
+}
+
+
diff -r 1e6ff995c4ef drivers/ide/ide-probe.c
--- a/drivers/ide/ide-probe.c	Fri Jun 26 10:02:05 2009 +0200
+++ b/drivers/ide/ide-probe.c	Fri Jun 26 10:47:45 2009 +0200
@@ -193,6 +193,7 @@ static int actual_try_to_identify (ide_d
 	ide_ioreg_t hd_status;
 	unsigned long timeout;
 	byte s, a;
+//	int i;
 
 	if (IDE_CONTROL_REG) {
 		/* take a deep breath */
@@ -209,11 +210,14 @@ static int actual_try_to_identify (ide_d
 		ide_delay_50ms();
 		hd_status = IDE_STATUS_REG;
 	}
+/*	for(i=0; i<10; i++)*/
+		ide_delay_50ms();
 
 	/* set features register for atapi identify command to be sure of reply */
 	if ((cmd == WIN_PIDENTIFY))
 		OUT_BYTE(0,IDE_FEATURE_REG);	/* disable dma & overlap */
-
+/*	for(i=0; i<10; i++)*/
+		ide_delay_50ms();
 #if CONFIG_BLK_DEV_PDC4030
 	if (HWIF(drive)->chipset == ide_pdc4030) {
 		/* DC4030 hosted drives need their own identify... */
@@ -224,6 +228,8 @@ static int actual_try_to_identify (ide_d
 	} else
 #endif /* CONFIG_BLK_DEV_PDC4030 */
 		OUT_BYTE(cmd,IDE_COMMAND_REG);		/* ask drive for ID */
+//	for(i=0; i<10; i++)
+		ide_delay_50ms();
 	timeout = ((cmd == WIN_IDENTIFY) ? WAIT_WORSTCASE : WAIT_PIDENTIFY) / 2;
 	timeout += jiffies;
 	do {
@@ -319,6 +325,25 @@ static int do_probe (ide_drive_t *drive,
 	ide_delay_50ms();	/* needed for some systems (e.g. crw9624 as drive0 with disk as slave) */
 	SELECT_DRIVE(hwif,drive);
 	ide_delay_50ms();
+
+#ifdef CONFIG_COLDFIRE
+	    /*
+		 *  ColdFire platforms boot up so quick that most hard drives
+		 *  have not completed there own self tests. Pause here for
+		 *  a couple of seconds if it looks like there is a drive
+		 *  present...
+		 */
+	    if (IN_BYTE(IDE_SELECT_REG) != drive->select.all) {
+			printk("IDE: waiting for drives to settle...\n");
+			for (rc = 0; (rc < 400); rc++) {
+				SELECT_DRIVE(hwif,drive);
+				ide_delay_50ms();
+				if (IN_BYTE(IDE_SELECT_REG) == drive->select.all)
+					break;
+			}
+		}
+#endif
+
 	if (IN_BYTE(IDE_SELECT_REG) != drive->select.all && !drive->present) {
 		if (drive->select.b.unit != 0) {
 			SELECT_DRIVE(hwif,&hwif->drives[0]);	/* exit with drive0 selected */
diff -r 1e6ff995c4ef drivers/ide/ide-tape.c
--- a/drivers/ide/ide-tape.c	Fri Jun 26 10:02:05 2009 +0200
+++ b/drivers/ide/ide-tape.c	Fri Jun 26 10:47:45 2009 +0200
@@ -3,7 +3,7 @@
  *
  * Copyright (C) 1995 - 1999 Gadi Oxman <gadio@netvision.net.il>
  *
- * $Header$
+ * $Header: /EM85xx/uClinux-2.4/drivers/ide/ide-tape.c 1     6/07/02 12:43p Fabrice $
  *
  * This driver was constructed as a student project in the software laboratory
  * of the faculty of electrical engineering in the Technion - Israel's
diff -r 1e6ff995c4ef drivers/ide/ide.c
--- a/drivers/ide/ide.c	Fri Jun 26 10:02:05 2009 +0200
+++ b/drivers/ide/ide.c	Fri Jun 26 10:47:45 2009 +0200
@@ -430,11 +431,18 @@ void ide_input_data (ide_drive_t *drive,
 			insl(IDE_DATA_REG, buffer, wcount);
 	} else {
 #if SUPPORT_SLOW_DATA_PORTS
-		if (drive->slow) {
-			unsigned short *ptr = (unsigned short *) buffer;
+		if (1) { // drive->slow) { // we cant use insw with any alignement also
+			unsigned char *ptr = (unsigned char *) buffer;
+			unsigned short w;
 			while (wcount--) {
-				*ptr++ = inw_p(IDE_DATA_REG);
-				*ptr++ = inw_p(IDE_DATA_REG);
+//				tmp = 100; while(tmp--);
+				w = inw_p(IDE_DATA_REG);
+				*ptr++ = (unsigned char)(w & 0xFF);
+				*ptr++ = (unsigned char)(w >> 8);
+//				tmp = 100; while(tmp--);
+				w = inw_p(IDE_DATA_REG);
+				*ptr++ = w&0xFF;
+				*ptr++ = w>>8;
 			}
 		} else
 #endif /* SUPPORT_SLOW_DATA_PORTS */
@@ -448,6 +456,7 @@ void ide_output_data (ide_drive_t *drive
 void ide_output_data (ide_drive_t *drive, void *buffer, unsigned int wcount)
 {
 	byte io_32bit;
+	int tmp;
 
 	if(HWIF(drive)->ideproc) {
 		HWIF(drive)->ideproc(ideproc_ide_output_data,
@@ -471,10 +480,12 @@ void ide_output_data (ide_drive_t *drive
 			outsl(IDE_DATA_REG, buffer, wcount);
 	} else {
 #if SUPPORT_SLOW_DATA_PORTS
-		if (drive->slow) {
+		if (1) { // *hack* drive->slow) { 
 			unsigned short *ptr = (unsigned short *) buffer;
 			while (wcount--) {
+				tmp = 10; while(tmp--);
 				outw_p(*ptr++, IDE_DATA_REG);
+				tmp = 10; while(tmp--);
 				outw_p(*ptr++, IDE_DATA_REG);
 			}
 		} else
@@ -1600,7 +1611,8 @@ void ide_timer_expiry (unsigned long dat
 			} else {
 				if (drive->waiting_for_dma) {
 					startstop = ide_stopped;
-					ide_dma_timeout_retry(drive);
+					// XXX sigma do not retry
+					//ide_dma_timeout_retry(drive);
 				} else
 					startstop = ide_error(drive, "irq timeout", GET_STAT());
 			}

[-- Attachment #3: pata_jasper.c --]
[-- Type: text/plain, Size: 6392 bytes --]

/* ide-jasper.c */

/* jasper specific initialization stuff for ide */

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/init.h>
#include <linux/blkdev.h>
#include <linux/delay.h>
#include <scsi/scsi_host.h>
#include <linux/libata.h>
#include <linux/platform_device.h>

#define DRV_NAME	"pata_jasper"
#define DRV_VERSION	"0.1"

#include <asm/io.h>
#include <asm/hardware.h>
#include <asm/arch/quasar.h>

// updated to match stock ide driver firmware 1.5.2
static int jasper_tune_chipset(int slave, u8 speed)
{
	unsigned short reg54, reg58;

	speed = speed & 0xFF;

	printk("jasper_tune_chipset: speed = %02x\n", speed);

	/*unsigned int */reg54 = inw(JASPER_IDE_BASE + IDE_UDMACTL);
	/*unsigned int */reg58 = inw(JASPER_IDE_BASE + IDE_UDMATIM);

	if (speed>=XFER_UDMA_0 && speed<=XFER_UDMA_5) {
		if(slave != 0) {
			reg54 |= 0x2;
			reg58 |= (speed-XFER_UDMA_0)<<4;
		}else {
			reg54 |= 0x1;
			reg58 |= (speed-XFER_UDMA_0);
		}

		if (speed == XFER_UDMA_0) {
			outl(0x11100009 + 0x10C00, JASPER_IDE_BASE + IDE_UDMATIM);
			outl(5, JASPER_IDE_BASE + 0x5C);
		} 
			else if (speed == XFER_UDMA_1)
		{
			outl(0x11110A06, JASPER_IDE_BASE + IDE_UDMATIM);
			outl(3, JASPER_IDE_BASE + 0x5C);
		}
			else 
		{
			outl(0x11100004 + 0x10800, JASPER_IDE_BASE + IDE_UDMATIM);
			outl(2, JASPER_IDE_BASE + 0x5C);
		}
	}
		else
	{
		if (!(speed >= XFER_MW_DMA_0 && speed <= XFER_MW_DMA_2)) {
			if (slave != 0) {
				reg54 &= ~0x2;
			} else {
				reg54 &= ~0x1;
			}
		}
	}
	outl(0x89000110, JASPER_IDE_BASE + 0x44);
	outw(reg54, JASPER_IDE_BASE + IDE_UDMACTL);
	outw(reg58, JASPER_IDE_BASE + IDE_UDMATIM);
	return 0; //ide_config_drive_speed(drive, speed);
}

static void jasper_set_dmamode (struct ata_port *ap, struct ata_device *adev)
{
//	int slave = adev->devno;
	jasper_tune_chipset(0/*slave*/, adev->dma_mode);
}

static void jasper_set_piomode(struct ata_port *ap, struct ata_device *adev)
{
//	int slave = adev->devno;
	jasper_tune_chipset(0/*slave*/, adev->pio_mode);
}

// standard is 2 (!)
#define JASPER_ATA_DMA_STATUS		4
// standard is 4
#define JASPER_ATA_DMA_TABLE_OFS	8

static void jasper_bmdma_start(struct ata_queued_cmd *qc)
{
	__asm__(
		"mcr	p15, 0, %0, c7, c10, 0\n"		// write-back data cache
		: : "r" (0)
	);
	ata_bmdma_start(qc);
}

static struct scsi_host_template jasper_sht = {
	.module			= THIS_MODULE,
	.name			= DRV_NAME,
	.ioctl			= ata_scsi_ioctl,
	.queuecommand		= ata_scsi_queuecmd,
	.can_queue		= ATA_DEF_QUEUE,
	.this_id		= ATA_SHT_THIS_ID,
	.sg_tablesize		= LIBATA_DUMB_MAX_PRD,
	.cmd_per_lun		= ATA_SHT_CMD_PER_LUN,
	.emulated		= ATA_SHT_EMULATED,
	.use_clustering		= ATA_SHT_USE_CLUSTERING,
	.proc_name		= DRV_NAME,
	.dma_boundary		= ATA_DMA_BOUNDARY,
	.slave_configure	= ata_scsi_slave_config,
	.slave_destroy		= ata_scsi_slave_destroy,
	.bios_param		= ata_std_bios_param,
};

static struct ata_port_operations jasper_port_ops = {
	.set_piomode		= jasper_set_piomode,
	.set_dmamode		= jasper_set_dmamode,

	.tf_load		= ata_tf_load,
	.tf_read		= ata_tf_read,
	.check_status		= ata_check_status,
	.exec_command		= ata_exec_command,
	.dev_select		= ata_std_dev_select,

	.freeze			= ata_bmdma_freeze,
	.thaw			= ata_bmdma_thaw,
	.error_handler		= ata_bmdma_error_handler,
	.post_internal_cmd	= ata_bmdma_post_internal_cmd,
	.cable_detect		= ata_cable_40wire,

	.bmdma_setup		= ata_bmdma_setup,

	.bmdma_start		= jasper_bmdma_start,
	.bmdma_stop		= ata_bmdma_stop,
	.bmdma_status		= ata_bmdma_status,

	.qc_prep		= ata_dumb_qc_prep,
	.qc_issue		= ata_qc_issue_prot,
	.data_xfer		= ata_data_xfer,

	.irq_clear		= ata_bmdma_irq_clear,
	.irq_on			= ata_irq_on,

	.port_start		= ata_port_start/*ata_sff_port_start*/,
};

static int __devinit jasper_atapi_probe(struct platform_device *pdev)
{
	struct ata_host *host;
	struct ata_ioports *ioaddr;

	struct ata_port_info pi = {
		.sht		= &jasper_sht,
		.flags		= ATA_FLAG_SLAVE_POSS,
		.pio_mask	= 0x1f,
		.port_ops	= &jasper_port_ops,
#ifdef CONFIG_JASPER_IDE_UDMA
		.mwdma_mask = 0x07,
		.udma_mask = 0x07,
#endif
	};
	const struct ata_port_info *ppi[] = { &pi, NULL };

	int can_udma;

	int chipsetID = __raw_readl(JASPER_SYSCTRL_BASE + SYSCTRL_CHIP_ID);
	can_udma = chipsetID > 0x8500;
	printk("JASPER PATA (IDE) chipset %x: UDMA is %ssupported\n", chipsetID, can_udma ? "" : "not ");
	//	choose ide over dvd loader (DVD_AV_CONTROL)
	//	write 0x8000 to IDETIM

	outl(/*R1*/ 0xffffff - 0xef0000, /*R0*/ JASPER_PIO0_BASE + PIO_DIR);
	outl(/*R1*/ 0xffffff - 0xef0000, /* R3 */ JASPER_PIO0_BASE + PIO_DATA);
	//	choose ide over dvd loader (DVD_AV_CONTROL)
	outl(0x00, JASPER_DVD_BASE + DVD_AV_CTRL);	//0x00 select IDE over DVD-loader
	outw(0x00, JASPER_IDE_BASE + 0x34);
	outl(0x8400+0xF, JASPER_IDE_BASE + IDE_TIM);

//	ppi[0] = ppi[1] = &pi;
	host = ata_host_alloc_pinfo(&pdev->dev, ppi, 1);
	if (!host)
		return -ENOMEM;
	ioaddr = &host->ports[0]->ioaddr;

	ioaddr->cmd_addr = __io(JASPER_IDE_BASE + IDE_PRI_DATA);
	ioaddr->ctl_addr = __io(JASPER_IDE_BASE + IDE_PRI_DEVICE_CONTROL);
	ioaddr->altstatus_addr = __io(JASPER_IDE_BASE + IDE_PRI_DEVICE_CONTROL);
	ioaddr->bmdma_addr = __io(JASPER_IDE_DMA_BASE);

	ata_std_ports(ioaddr); // sets other regs based on cmd_addr

	return ata_host_activate(host, IDE_IRQ, ata_interrupt, IRQF_SHARED,
				 &jasper_sht);
}

/**
 *	jasper_atapi_remove	-	unplug a jasper atapi interface
 *	@pdev: platform device
 *
 *	A jasper atapi device has been unplugged. Perform the needed
 *	cleanup. Also called on module unload for any active devices.
 */
static int __devexit jasper_atapi_remove(struct platform_device *pdev)
{
	struct device *dev = &pdev->dev;
	struct ata_host *host = dev_get_drvdata(dev);

	ata_host_detach(host);

//	peripheral_free_list(atapi_io_port);

	return 0;
}

static struct platform_driver jasper_atapi_driver = {
	.probe			= jasper_atapi_probe,
	.remove			= __devexit_p(jasper_atapi_remove),
	.driver = {
		.name		= DRV_NAME,
		.owner		= THIS_MODULE,
#ifdef CONFIG_PM
		.suspend	= jasper_atapi_suspend,
		.resume		= jasper_atapi_resume,
#endif
	},
};

static int __init jasper_atapi_init(void)
{
	return platform_driver_register(&jasper_atapi_driver);
}

static void __exit jasper_atapi_exit(void)
{
	platform_driver_unregister(&jasper_atapi_driver);
}

module_init(jasper_atapi_init);
module_exit(jasper_atapi_exit);

MODULE_AUTHOR("Anonymous");
MODULE_DESCRIPTION("JASPER IDE driver");
MODULE_LICENSE("GPL");

  reply	other threads:[~2009-06-26 13:24 UTC|newest]

Thread overview: 16+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2009-06-25  8:09 porting IDE patches to PATA driver Andrei Martynov
2009-06-25  8:23 ` Alan Cox
2009-06-25  8:38   ` Re[2]: " Andrei Martynov
2009-06-25  9:05     ` Alan Cox
2009-06-25 10:30       ` Re[2]: " Andrei Martynov
2009-06-25 11:29         ` Alan Cox
2009-06-26 13:24           ` Andrei Martynov [this message]
2009-06-26 13:46             ` Alan Cox
2009-06-26 14:40               ` Re[2]: " Andrei Martynov
2009-06-26 16:21                 ` Alan Cox
2009-06-28 20:33                   ` Sergei Shtylyov
2009-06-29 12:31                     ` Alan Cox
2009-06-29 12:01                   ` Re[2]: " Andrei Martynov
2009-06-29 14:53                     ` Alan Cox
2009-07-03  9:50                       ` Re[2]: " Andrei Martynov
2009-07-04  1:17                         ` Robert Hancock

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=96179821.20090626152440@web.de \
    --to=andrei.martynov@web.de \
    --cc=alan@lxorguk.ukuu.org.uk \
    --cc=linux-ide@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).