All of lore.kernel.org
 help / color / mirror / Atom feed
From: Roman Kolesnikov <romank@randrlabs.com>
To: u-boot@lists.denx.de
Subject: [U-Boot-Users] Using at91 with a different parallel flash, request for help
Date: Mon, 12 Jun 2006 10:33:19 -0700	[thread overview]
Message-ID: <448DA55F.3070600@randrlabs.com> (raw)
In-Reply-To: <448D095C.5010209@koansoftware.com>

Hello Marco,

I am including the memory driver for the newer chip as well as the 
config file. The config file has been changed more than once, since I am 
trying to port the uboot 1.1.4 right. This is a current config file as 
of now.


/**
 * OUR FLASHY FLASH DRIVER
 **/

#include <common.h>


#define		MEM_FLASH_BASE		(*(volatile u16 *)(CFG_FLASH_BASE))


#define 	PRODUCT_ID_IN_CODE		0x90
#define		COMMAND_READ			0xFF
#define		COMMAND_SECTOR_UNLOCK_I		0x60
#define		COMMAND_SECTOR_UNLOCK_II	0xD0
#define		COMMAND_SECTOR_ERASE_SETUP	0x20
#define		COMMAND_GENERAL_ERASE_CONFIRM 	0xD0
#define		COMMAND_READ_STATUS_REGISTER	0x70
#define		COMMAND_WORD_PROGRAM_40		0x40

#define		ATM_ID_BV6416C			0xC5


#define READY 1
#define ERR   2
#define TMO   4


/**
 *	CFG_FLASH_BASE comes from include/configs/at91rm9200dk.h
 *	CFG_MAX_FLASH_BANKS comes from include/configs/at91rm9200dk.h
 *	PHYS_FLASH_1 comes from include/configs/at91rm9200dk.h
 **/

flash_info_t    flash_info[CFG_MAX_FLASH_BANKS];


/* Flash Organization Structure */
typedef struct OrgDef
{
	unsigned int sector_number;
	unsigned int sector_size;
} OrgDef;

OrgDef OrgAT49BV6416C[] =
{
	{   8,  8*1024 },	/*   8 *  8 kBytes sectors */
	{ 127, 64*1024 },	/* 127 * 64 kBytes sectors */
};


void flash_identification (flash_info_t * info)
{
	volatile u16 manuf_code, device_code, add_device_code;

	MEM_FLASH_BASE = PRODUCT_ID_IN_CODE;

	manuf_code 	= *(volatile u16 *) CFG_FLASH_BASE;
	device_code 	= *(volatile u16 *) (CFG_FLASH_BASE + 2);
	add_device_code = *(volatile u16 *) (CFG_FLASH_BASE + (3 << 1));

	/* Vendor type */
	info->flash_id = ATM_MANUFACT & FLASH_VENDMASK;
	printf ("Atmel: ");

 	if ((device_code & FLASH_TYPEMASK) == (ATM_ID_BV6416C & FLASH_TYPEMASK)) 
	{
 		info->flash_id |= ATM_ID_BV6416C & FLASH_TYPEMASK;
 		printf ("AT49BV6416C (64Mbit)\n");
 	}
}

ushort flash_number_sector(OrgDef *pOrgDef, unsigned int nb_blocks)
{
	int i, nb_sectors = 0;

	for (i=0; i<nb_blocks; i++){
		nb_sectors += pOrgDef[i].sector_number;
	}

	return nb_sectors;
}

void flash_unlock_sector(flash_info_t * info, unsigned int sector)
{
	volatile u16 *addr = (volatile u16 *) (info->start[sector]);
	
	*addr = COMMAND_SECTOR_UNLOCK_I;
	*addr = COMMAND_SECTOR_UNLOCK_II;
}

ulong flash_init (void)
{
	int i, j, k;
	unsigned int flash_nb_blocks, sector;
	unsigned int start_address;
	OrgDef *pOrgDef;

	ulong size = 0;

	for (i = 0; i < CFG_MAX_FLASH_BANKS; i++) 
	{
		ulong flashbase = 0;

		flash_identification (&flash_info[i]);

		if ((flash_info[i].flash_id & FLASH_TYPEMASK) == (ATM_ID_BV6416C & FLASH_TYPEMASK))
		{	/* AT49BV6416C Flash */

			pOrgDef = OrgAT49BV6416C;
			flash_nb_blocks = sizeof (OrgAT49BV6416C) / sizeof (OrgDef);
		} else 
		{
			flash_nb_blocks = 0;
		}

		flash_info[i].sector_count = flash_number_sector(pOrgDef, flash_nb_blocks);
		memset (flash_info[i].protect, 0, flash_info[i].sector_count);

		if (i == 0)
			flashbase = PHYS_FLASH_1;
		else
			panic ("error: configured too many flash banks!\n");

		sector = 0;
		start_address = flashbase;
		flash_info[i].size = 0;

		for (j = 0; j < flash_nb_blocks; j++) {
			for (k = 0; k < pOrgDef[j].sector_number; k++) {
				flash_info[i].start[sector++] = start_address;
				start_address += pOrgDef[j].sector_size;
				flash_info[i].size += pOrgDef[j].sector_size;
			}
		}

		size += flash_info[i].size;


		/* Unlock all sectors@reset */
		for (j=0; j<flash_info[i].sector_count; j++){
			flash_unlock_sector(&flash_info[i], j);
		}
	}

	/* Protect binary boot image */
	flash_protect (FLAG_PROTECT_SET,
		       CFG_FLASH_BASE,
		       CFG_FLASH_BASE + CFG_BOOT_SIZE - 1, &flash_info[0]);

	/* Protect environment variables */
	flash_protect (FLAG_PROTECT_SET,
		       CFG_ENV_ADDR,
		       CFG_ENV_ADDR + CFG_ENV_SIZE - 1, &flash_info[0]);

	/* Protect U-Boot gzipped image */
	flash_protect (FLAG_PROTECT_SET,
		       CFG_U_BOOT_BASE,
		       CFG_U_BOOT_BASE + CFG_U_BOOT_SIZE - 1, &flash_info[0]);

	return size;
}

void flash_print_info (flash_info_t * info)
{
	int i;

	switch (info->flash_id & FLASH_VENDMASK) 
	{
		case (ATM_MANUFACT & FLASH_VENDMASK):
			printf ("Atmel: ");
		break;

		default:
			printf ("Unknown Vendor ");
		break;
	}

	switch (info->flash_id & FLASH_TYPEMASK) 
	{
		case (ATM_ID_BV6416C & FLASH_TYPEMASK):
			printf ("AT49BV6416 (64Mbit)\n");
		break;
		default:
			printf ("Unknown Chip Type\n");
		return;
	}

	printf ("  Size: %ld MB in %d Sectors\n", info->size >> 20, info->sector_count);

	printf ("  Sector Start Addresses:");

	for (i = 0; i < info->sector_count; i++) 
	{
		if ((i % 5) == 0) 
		{
			printf ("\n   ");
		}
		printf (" %08lX%s", info->start[i],
			info->protect[i] ? " (RO)" : "     ");
	}
	printf ("\n");
}



int flash_erase (flash_info_t * info, int s_first, int s_last)
{
	u16 result;
	int iflag, cflag, prot, sect;
	int rc = ERR_OK;
	int chip1;

	/* first look for protection bits */

	if (info->flash_id == FLASH_UNKNOWN)
		return ERR_UNKNOWN_FLASH_TYPE;

	if ((s_first < 0) || (s_first > s_last)) {
		return ERR_INVAL;
	}

	if ((info->flash_id & FLASH_VENDMASK) !=
		(ATM_MANUFACT & FLASH_VENDMASK)) {
		return ERR_UNKNOWN_FLASH_VENDOR;
	}

	prot = 0;

	for (sect = s_first; sect <= s_last; ++sect) 
	{
		if (info->protect[sect]) 
		{
			prot++;
			sect = s_last;
			/* BREAK */
		}
	}

	if (prot)
		return ERR_PROTECTED;

	/*
	 * Disable interrupts which might cause a timeout
	 * here. Remember that our exception vectors are
	 * at address 0 in the flash, and we don't want a
	 * (ticker) exception to happen while the flash
	 * chip is in programming mode.
	 */

	cflag = icache_status ();
	icache_disable ();
	iflag = disable_interrupts ();

	/* Start erase on unprotected sectors */
	for (sect = s_first; sect <= s_last && !ctrlc (); sect++) 
	{
		printf ("Erasing sector %2d ... ", sect);

		/* arm simple, non interrupt dependent timer */
		reset_timer_masked ();

		if (info->protect[sect] == 0) 
		{
			/* not protected */
			volatile u16 *addr = (volatile u16 *) (info->start[sect]);

			*addr = COMMAND_SECTOR_ERASE_SETUP;
			*addr = COMMAND_GENERAL_ERASE_CONFIRM;

			/* wait until flash is ready */
			chip1 = 0;

			do
			{
				*addr  = COMMAND_READ_STATUS_REGISTER;
				result = *addr;

				/* check timeout */
				if (get_timer_masked () > CFG_FLASH_ERASE_TOUT) 
				{
					chip1 = TMO;
					break;
				}

				if( result&0x0080 )
				{
					chip1 = READY;
				}

			}
			while (!chip1);

			*addr = COMMAND_READ;

			if (chip1 == ERR) 
			{
				rc = ERR_PROG_ERROR;
			}	
			else if (chip1 == TMO)
			{
				rc = ERR_TIMOUT;
			}
			else
			{
				printf ("ok.\n");
			}
		}
		else
		{
			/* it was protected */
			printf ("protected!\n");
		}
	}

	if (ctrlc ()) printf ("User Interrupt!\n");

	/* allow flash to settle - wait 10 ms */
	udelay_masked (10000);

	if (iflag)
		enable_interrupts ();

	if (cflag)
		icache_enable ();

	return rc;
}

/*-----------------------------------------------------------------------
 * Copy memory to flash
 */

volatile static int write_word (flash_info_t * info, ulong dest, ulong data)
{
	volatile u16 *addr = (volatile u16 *) dest;
	ulong result;
	int rc = ERR_OK;
	int cflag, iflag;
	int chip1;

	/*
	 * Check if Flash is (sufficiently) erased
	 */
	result = *addr;

	if (result != 0xFF)
		return ERR_NOT_ERASED;

	/*
	 * Disable interrupts which might cause a timeout
	 * here. Remember that our exception vectors are
	 * at address 0 in the flash, and we don't want a
	 * (ticker) exception to happen while the flash
	 * chip is in programming mode.
	 */
	cflag = icache_status ();
	icache_disable ();
	iflag = disable_interrupts ();

	*addr = COMMAND_WORD_PROGRAM_40;
	*addr = data;

	/* arm simple, non interrupt dependent timer */
	reset_timer_masked ();

	/* wait until flash is ready */
	chip1 = 0;
	do 
	{
		*addr  = COMMAND_READ_STATUS_REGISTER;
		result = *addr;

		/* check timeout */
		if (get_timer_masked () > CFG_FLASH_ERASE_TOUT) 
		{
			chip1 = TMO;
			break;
		}

		if( result&0x0080 )
		{
			chip1 = READY;
		}
	} while (!chip);

	*addr = COMMAND_READ;

	if (chip1 == TMO || *addr != data)
		rc = ERR_PROG_ERROR;

	if (iflag)
		enable_interrupts ();

	if (cflag)
		icache_enable ();

	return rc;
}


/*-----------------------------------------------------------------------
 * Copy memory to flash.
 */

int write_buff (flash_info_t * info, uchar * src, ulong addr, ulong cnt)
{
	ulong wp, data;
	int rc;

	if (addr & 1) {
		printf ("unaligned destination not supported\n");
		return ERR_ALIGN;
	};

	if ((int) src & 1) {
		printf ("unaligned source not supported\n");
		return ERR_ALIGN;
	};

	wp = addr;

	while (cnt >= 2) {
		data = *((volatile u16 *) src);
		if ((rc = write_word (info, wp, data)) != 0) {
			return (rc);
		}
		src += 2;
		wp += 2;
		cnt -= 2;
	}

	if (cnt == 1) {
		data = (*((volatile u8 *) src)) | (*((volatile u8 *) (wp + 1)) <<
										   8);
		if ((rc = write_word (info, wp, data)) != 0) {
			return (rc);
		}
		src += 1;
		wp += 1;
		cnt -= 1;
	};

	return ERR_OK;
}


------------------------------------------------------------------------

/*
 * Rick Bronson <rick@efn.org>
 *
 * Configuation settings for the AT91RM9200DK board.
 *
 * See file CREDITS for list of people who contributed to this
 * project.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 * MA 02111-1307 USA
 */

#ifndef __CONFIG_H
#define __CONFIG_H

/*
 * If we are developing, we might want to start armboot from ram
 * so we MUST NOT initialize critical regs like mem-timing ...
 */
#define CONFIG_INIT_CRITICAL		/* undef for developing */

/* ARM asynchronous clock */
#define AT91C_MAIN_CLOCK	179712000	/* from 18.432 MHz crystal (18432000 / 4 * 39) */
#define AT91C_MASTER_CLOCK	59904000	/* peripheral clock (AT91C_MASTER_CLOCK / 3) */
/* #define AT91C_MASTER_CLOCK	44928000 */	/* peripheral clock (AT91C_MASTER_CLOCK / 4) */

#define AT91_SLOW_CLOCK		32768	/* slow clock */

#define CONFIG_AT91RM9200DK	1	/* on an AT91RM9200DK Board	 */
#undef CONFIG_USE_IRQ			/* we don't need IRQ/FIQ stuff */
#define CONFIG_CMDLINE_TAG	1	/* enable passing of ATAGs	*/
#define CONFIG_SETUP_MEMORY_TAGS 1
#define CONFIG_INITRD_TAG	1

/* define this to include the functionality of boot.bin in u-boot */
#define CONFIG_BOOTBINFUNC	1

/*
 * Size of malloc() pool
 */
#define CFG_MALLOC_LEN	(CFG_ENV_SIZE + 128*1024)
#define CFG_GBL_DATA_SIZE	128	/* size in bytes reserved for initial data */

#define CONFIG_BAUDRATE 115200

#define CFG_AT91C_BRGR_DIVISOR	33	/* hardcode so no __divsi3 : AT91C_MASTER_CLOCK / baudrate / 16 */

/*
 * Hardware drivers
 */

/* define one of these to choose the DBGU, USART0  or USART1 as console */
#define CONFIG_DBGU
#undef CONFIG_USART0
#undef CONFIG_USART1

#undef	CONFIG_HWFLOW			/* don't include RTS/CTS flow control support	*/

#undef	CONFIG_MODEM_SUPPORT		/* disable modem initialization stuff */

#define CONFIG_BOOTDELAY      3
/* #define CONFIG_ENV_OVERWRITE	1 */

#define CONFIG_COMMANDS		\
		       ((CONFIG_CMD_DFL ) & \
		      ~(CFG_CMD_BDI | \
			CFG_CMD_IMI | \
			CFG_CMD_AUTOSCRIPT | \
			CFG_CMD_FPGA | \
			CFG_CMD_MISC | \
			CFG_CMD_LOADS | \
			CFG_CMD_NET))

/* this must be included AFTER the definition of CONFIG_COMMANDS (if any) */
#include <cmd_confdefs.h>
#if	0
#define CFG_MAX_NAND_DEVICE	1	/* Max number of NAND devices		*/
#define SECTORSIZE 512

#define ADDR_COLUMN 1
#define ADDR_PAGE 2
#define ADDR_COLUMN_PAGE 3

#define NAND_ChipID_UNKNOWN	0x00
#define NAND_MAX_FLOORS 1
#define NAND_MAX_CHIPS 1

#define AT91_SMART_MEDIA_ALE (1 << 22)	/* our ALE is AD22 */
#define AT91_SMART_MEDIA_CLE (1 << 21)	/* our CLE is AD21 */

#define NAND_DISABLE_CE(nand) do { *AT91C_PIOC_SODR = AT91C_PIO_PC0;} while(0)
#define NAND_ENABLE_CE(nand) do { *AT91C_PIOC_CODR = AT91C_PIO_PC0;} while(0)

#define NAND_WAIT_READY(nand) while (!(*AT91C_PIOC_PDSR & AT91C_PIO_PC2))

#define WRITE_NAND_COMMAND(d, adr) do{ *(volatile __u8 *)((unsigned long)adr | AT91_SMART_MEDIA_CLE) = (__u8)(d); } while(0)
#define WRITE_NAND_ADDRESS(d, adr) do{ *(volatile __u8 *)((unsigned long)adr | AT91_SMART_MEDIA_ALE) = (__u8)(d); } while(0)
#define WRITE_NAND(d, adr) do{ *(volatile __u8 *)((unsigned long)adr) = (__u8)d; } while(0)
#define READ_NAND(adr) ((volatile unsigned char)(*(volatile __u8 *)(unsigned long)adr))
/* the following are NOP's in our implementation */
#define NAND_CTL_CLRALE(nandptr)
#define NAND_CTL_SETALE(nandptr)
#define NAND_CTL_CLRCLE(nandptr)
#define NAND_CTL_SETCLE(nandptr)
#endif
#define CONFIG_NR_DRAM_BANKS 1
#define PHYS_SDRAM 0x20000000
#define PHYS_SDRAM_SIZE 0x2000000  /* 32 megs */

#define CFG_MEMTEST_START		PHYS_SDRAM
#define CFG_MEMTEST_END			CFG_MEMTEST_START + PHYS_SDRAM_SIZE - 262144
#if	0
#define CONFIG_DRIVER_ETHER
#define CONFIG_NET_RETRY_COUNT		20
#define CONFIG_AT91C_USE_RMII

#define CONFIG_HAS_DATAFLASH		1
#define CFG_SPI_WRITE_TOUT		(5*CFG_HZ)
#define CFG_MAX_DATAFLASH_BANKS 	2
#define CFG_MAX_DATAFLASH_PAGES 	16384
#define CFG_DATAFLASH_LOGIC_ADDR_CS0	0xC0000000	/* Logical adress for CS0 */
#define CFG_DATAFLASH_LOGIC_ADDR_CS3	0xD0000000	/* Logical adress for CS3 */
#endif
#define PHYS_FLASH_1			0x10000000
#define PHYS_FLASH_SIZE			0x800000  /* 8 megs main flash */
#define CFG_FLASH_BASE			PHYS_FLASH_1
#define CFG_MAX_FLASH_BANKS		1
#define CFG_MAX_FLASH_SECT		256
#define CFG_FLASH_ERASE_TOUT		(2*CFG_HZ) /* Timeout for Flash Erase */
#define CFG_FLASH_WRITE_TOUT		(2*CFG_HZ) /* Timeout for Flash Write */

#undef	CFG_ENV_IS_IN_DATAFLASH

#ifdef CFG_ENV_IS_IN_DATAFLASH
#define CFG_ENV_OFFSET			0x20000
#define CFG_ENV_ADDR			(CFG_DATAFLASH_LOGIC_ADDR_CS0 + CFG_ENV_OFFSET)
#define CFG_ENV_SIZE			0x2000  /* 0x8000 */
#else
#define CFG_ENV_IS_IN_FLASH		1
#ifndef CONFIG_BOOTBINFUNC
#define CFG_ENV_ADDR			(PHYS_FLASH_1 + 0x20000)  /* after u-boot.bin */
#define CFG_ENV_SIZE			0x10000 /* sectors are 64K here */
#else
#define CFG_ENV_ADDR			(PHYS_FLASH_1 + 0xe000)  /* between boot.bin and u-boot.bin.gz */
#define CFG_ENV_SIZE			0x2000  /* 0x8000 */
#endif
#endif


#define CFG_LOAD_ADDR		0x21000000  /* default load address */

#ifndef CONFIG_BOOTBINFUNC
#define CFG_BOOT_SIZE		0x00 /* 0 KBytes */
#define CFG_U_BOOT_BASE		PHYS_FLASH_1
#define CFG_U_BOOT_SIZE		0x20000 /* 128 KBytes */
#else
#define CFG_BOOT_SIZE		0x6000 /* 24 KBytes */
#define CFG_U_BOOT_BASE		(PHYS_FLASH_1 + 0x10000)
#define CFG_U_BOOT_SIZE		0x10000 /* 64 KBytes */
#endif

#define CFG_BAUDRATE_TABLE	{115200 , 19200, 38400, 57600, 9600 }

#define CFG_PROMPT		"U-Boot 1.1.2 w/boot.bin> "	/* Monitor Command Prompt */
#define CFG_CBSIZE		256		/* Console I/O Buffer Size */
#define CFG_MAXARGS		16		/* max number of command args */
#define CFG_PBSIZE		(CFG_CBSIZE+sizeof(CFG_PROMPT)+16) /* Print Buffer Size */

#ifndef __ASSEMBLY__
/*-----------------------------------------------------------------------
 * Board specific extension for bd_info
 *
 * This structure is embedded in the global bd_info (bd_t) structure
 * and can be used by the board specific code (eg board/...)
 */

struct bd_info_ext {
	/* helper variable for board environment handling
	 *
	 * env_crc_valid == 0    =>   uninitialised
	 * env_crc_valid  > 0    =>   environment crc in flash is valid
	 * env_crc_valid  < 0    =>   environment crc in flash is invalid
	 */
	int env_crc_valid;
};
#endif

#define CFG_HZ 1000
#define CFG_HZ_CLOCK AT91C_MASTER_CLOCK/2	/* AT91C_TC0_CMR is implicitly set to */
					/* AT91C_TC_TIMER_DIV1_CLOCK */

#define CONFIG_STACKSIZE	(32*1024)	/* regular stack */

#ifdef CONFIG_USE_IRQ
#error CONFIG_USE_IRQ not supported
#endif

#endif




Marco Cavallini wrote:

> Roman Kolesnikov ha scritto:
>
>> Hello everyone,
>>
>> I found the difference (with the help of Koan) in the command list 
>> and cycles for the newer Atmel chip. I have a new driver now to 
>> access the flash, and it works ok. However, I still have problem with 
>> UBoot1.1.4. When I load UBoot originally into flash and run, 
>> everything works well. However, when I restart the uboot from flash 
>> again, I think uboot overwrites something in the environment 
>> variables. My environment variables become corrupt. For instance the 
>> very first sign is invalid input, output, and debug settigns on the 
>> second start up.
>>
>> When I print the environment variables, I get about 8K of garbage 
>> data. I wonder what could be causing my corruption. It only occurs in 
>> the environment variables.
>>
>
> Hi Roman,
> Please show us your include/configs/at91rm9200dk.h
>
> Ciao

  reply	other threads:[~2006-06-12 17:33 UTC|newest]

Thread overview: 11+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
     [not found] <mailman.873.1149966631.14549.u-boot-users@lists.sourceforge.net>
2006-06-11  1:33 ` [U-Boot-Users] Moving uboot to parallel flash trouble Roman Kolesnikov
2006-06-11 18:46 ` [U-Boot-Users] Using at91 with a different parallel flash, request for help Roman Kolesnikov
2006-06-12  6:27   ` Marco Cavallini
2006-06-12 17:33     ` Roman Kolesnikov [this message]
2006-06-12 19:07     ` Roman Kolesnikov
2006-06-13  5:04     ` Roman Kolesnikov
2006-06-13  7:05       ` Wolfgang Denk
2006-06-13 13:49         ` Roman Kolesnikov
2006-06-13 16:49           ` Jerry Van Baren
     [not found] <mailman.19631.1149909549.684.u-boot-users@lists.sourceforge.net>
2006-06-10  3:46 ` Roman Kolesnikov
2006-06-10  3:18 [U-Boot-Users] Using at91 with a different parallel flash " Roman Kolesnikov

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=448DA55F.3070600@randrlabs.com \
    --to=romank@randrlabs.com \
    --cc=u-boot@lists.denx.de \
    /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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.