[adm5120] update to 2.6.22-rc6
[openwrt.git] / target / linux / adm5120-2.6 / files / arch / mips / adm5120 / adm5120_info.c
index 67b3e42..6d512f1 100644 (file)
@@ -18,6 +18,7 @@
 
 #include <asm/bootinfo.h>
 #include <asm/addrspace.h>
+#include <asm/byteorder.h>
 
 #include <asm/mach-adm5120/adm5120_defs.h>
 #include <asm/mach-adm5120/adm5120_switch.h>
@@ -26,6 +27,7 @@
 #include <asm/mach-adm5120/routerboot.h>
 #include <asm/mach-adm5120/zynos.h>
 
+extern char *prom_getenv(char *envname);
 /*
  * Globals
  */
@@ -37,6 +39,7 @@ unsigned int adm5120_revision;
 unsigned int adm5120_package;
 unsigned int adm5120_nand_boot;
 unsigned long adm5120_speed;
+unsigned long adm5120_memsize;
 
 /*
  * Locals
@@ -51,6 +54,63 @@ static char *boot_loader_names[BOOT_LOADER_LAST+1] = {
 };
 
 static struct adm5120_board __initdata adm5120_boards[] = {
+       /* FIXME: some boards have invalid fields */
+       {
+               .name           = "Cellvision CAS-630/630W",
+               .mach_type      = MACH_ADM5120_CAS630,
+               .has_usb        = 0,
+               .iface_num      = 1,
+               .flash0_size    = 4*1024*1024,
+       },
+       {
+               .name           = "Cellvision CAS-670/670W",
+               .mach_type      = MACH_ADM5120_CAS670,
+               .has_usb        = 0,
+               .iface_num      = 1,
+               .flash0_size    = 4*1024*1024,
+       },
+       {
+               .name           = "Cellvision CAS-700/700W",
+               .mach_type      = MACH_ADM5120_CAS700,
+               .has_usb        = 0,
+               .iface_num      = 1,
+               .flash0_size    = 4*1024*1024,
+       },
+       {
+               .name           = "Cellvision CAS-771/771W",
+               .mach_type      = MACH_ADM5120_CAS771,
+               .has_usb        = 0,
+               .iface_num      = 5,
+               .flash0_size    = 4*1024*1024,
+       },
+       {
+               .name           = "Cellvision CAS-790",
+               .mach_type      = MACH_ADM5120_CAS790,
+               .has_usb        = 0,
+               .iface_num      = 1,
+               .flash0_size    = 4*1024*1024,
+       },
+       {
+               .name           = "Cellvision CAS-861/861W",
+               .mach_type      = MACH_ADM5120_CAS861,
+               .has_usb        = 0,
+               .iface_num      = 1,
+               .flash0_size    = 4*1024*1024,
+       },
+       {
+               .name           = "Cellvision NFS-101U/101WU",
+               .mach_type      = MACH_ADM5120_NFS101U,
+               .has_usb        = 0,
+               .iface_num      = 1,
+               .flash0_size    = 4*1024*1024,
+       },
+       {
+               .name           = "Cellvision NFS-202U/202WU",
+               .mach_type      = MACH_ADM5120_NFS202U,
+               .has_usb        = 0,
+               .iface_num      = 1,
+               .flash0_size    = 4*1024*1024,
+       },
        {
                .name           = "Compex NetPassage 27G",
                .mach_type      = MACH_ADM5120_NP27G,
@@ -115,47 +175,131 @@ static struct adm5120_board __initdata adm5120_boards[] = {
                .flash0_size    = 4*1024*1024,
        },
        {
-               .name           = "RouterBOARD RB-111",
+               .name           = "Edimax BR-6104K",
+               .mach_type      = MACH_ADM5120_BR6104K,
+               .has_usb        = 0,
+               .iface_num      = 5,
+               .flash0_size    = 2*1024*1024,
+       },
+       {
+               .name           = "Infineon EASY 5120",
+               .mach_type      = MACH_ADM5120_EASY5120,
+               .has_usb        = 0,
+               .iface_num      = 0,
+               .flash0_size    = 2*1024*1024,
+       },
+       {
+               .name           = "Infineon EASY 5120-RT",
+               .mach_type      = MACH_ADM5120_EASY5120RT,
+               .has_usb        = 0,
+               .iface_num      = 0,
+               .flash0_size    = 2*1024*1024,
+       },
+       {
+               .name           = "Infineon EASY 5120P-ATA",
+               .mach_type      = MACH_ADM5120_EASY5120PATA,
+               .has_usb        = 0,
+               .iface_num      = 0,
+               .flash0_size    = 2*1024*1024,
+       },
+       {
+               .name           = "Infineon EASY 83000",
+               .mach_type      = MACH_ADM5120_EASY83000,
+               .has_usb        = 0,
+               .iface_num      = 6,
+               .flash0_size    = 4*1024*1024,
+       },
+       {
+               .name           = "RouterBOARD 111",
                .mach_type      = MACH_ADM5120_RB_111,
                .has_usb        = 0,
                .iface_num      = 1,
                .flash0_size    = 128*1024,
        },
        {
-               .name           = "RouterBOARD RB-112",
+               .name           = "RouterBOARD 112",
                .mach_type      = MACH_ADM5120_RB_112,
                .has_usb        = 0,
                .iface_num      = 1,
                .flash0_size    = 128*1024,
        },
        {
-               .name           = "RouterBOARD RB-133",
+               .name           = "RouterBOARD 133",
                .mach_type      = MACH_ADM5120_RB_133,
                .has_usb        = 0,
                .iface_num      = 3,
                .flash0_size    = 128*1024,
        },
        {
-               .name           = "RouterBOARD RB-133C",
+               .name           = "RouterBOARD 133C",
                .mach_type      = MACH_ADM5120_RB_133C,
                .has_usb        = 0,
                .iface_num      = 1,
                .flash0_size    = 128*1024,
        },
        {
-               .name           = "RouterBOARD RB-150",
+               .name           = "RouterBOARD 150",
                .mach_type      = MACH_ADM5120_RB_150,
                .has_usb        = 0,
                .iface_num      = 5,
                .flash0_size    = 128*1024,
        },
        {
-               .name           = "RouterBOARD RB-153",
+               .name           = "RouterBOARD 153",
                .mach_type      = MACH_ADM5120_RB_153,
                .has_usb        = 0,
                .iface_num      = 5,
                .flash0_size    = 128*1024,
        },
+       {
+               .name           = "ZyXEL ES-2024A",
+               .mach_type      = MACH_ADM5120_ES2024A,
+               .has_usb        = 0,
+               .iface_num      = 0,
+               .flash0_size    = 4*1024*1024,
+       },
+       {
+               .name           = "ZyXEL ES-2024PWR",
+               .mach_type      = MACH_ADM5120_ES2024PWR,
+               .has_usb        = 0,
+               .iface_num      = 0,
+               .flash0_size    = 4*1024*1024,
+       },
+       {
+               .name           = "ZyXEL ES-2108",
+               .mach_type      = MACH_ADM5120_ES2108,
+               .has_usb        = 0,
+               .iface_num      = 0,
+               .flash0_size    = 4*1024*1024,
+       },
+       {
+               .name           = "ZyXEL ES-2108-F",
+               .mach_type      = MACH_ADM5120_ES2108F,
+               .has_usb        = 0,
+               .iface_num      = 0,
+               .flash0_size    = 4*1024*1024,
+       },
+       {
+               .name           = "ZyXEL ES-2108-G",
+               .mach_type      = MACH_ADM5120_ES2108G,
+               .has_usb        = 0,
+               .iface_num      = 0,
+               .flash0_size    = 4*1024*1024,
+       },
+       {
+               .name           = "ZyXEL ES-2108-LC",
+               .mach_type      = MACH_ADM5120_ES2108LC,
+               .has_usb        = 0,
+               .iface_num      = 0,
+               .flash0_size    = 4*1024*1024,
+       },
+       {
+               .name           = "ZyXEL ES-2108-PWR",
+               .mach_type      = MACH_ADM5120_ES2108PWR,
+               .has_usb        = 0,
+               .iface_num      = 0,
+               .flash0_size    = 4*1024*1024,
+       },
        {
                .name           = "ZyXEL HomeSafe 100/100W",
                .mach_type      = MACH_ADM5120_HS100,
@@ -231,7 +375,7 @@ static struct adm5120_board __initdata adm5120_boards[] = {
                .mach_type      = MACH_ADM5120_UNKNOWN,
                .has_usb        = 1,
                .iface_num      = 5,
-               .flash0_size    = 0,
+               .flash0_size    = 4*1024*1024,
        }
 };
 
@@ -251,7 +395,7 @@ struct mylo_board {
 
 #define COMPEX_BOARD(d,mt) MYLO_BOARD(VENID_COMPEX,(d),VENID_COMPEX,(d),(mt))
 
-static struct mylo_board  __initdata mylo_boards[] = {
+static struct mylo_board mylo_boards[] __initdata = {
        COMPEX_BOARD(DEVID_COMPEX_NP27G, MACH_ADM5120_NP27G),
        COMPEX_BOARD(DEVID_COMPEX_NP28G, MACH_ADM5120_NP28G),
        COMPEX_BOARD(DEVID_COMPEX_NP28GHS, MACH_ADM5120_NP28GHS),
@@ -271,7 +415,7 @@ struct routerboard {
 };
 
 #define ROUTERBOARD(n, mt) { .name = (n), .mach_type = (mt) }
-static struct routerboard  __initdata routerboards[] = {
+static struct routerboard routerboards[] __initdata = {
        ROUTERBOARD("111", MACH_ADM5120_RB_111),
        ROUTERBOARD("112", MACH_ADM5120_RB_112),
        ROUTERBOARD("133", MACH_ADM5120_RB_133),
@@ -294,7 +438,7 @@ struct zynos_board {
 #define DLINK_BOARD(bi, mt) ZYNOS_BOARD(ZYNOS_VENDOR_ID_DLINK, bi, mt)
 #define LUCENT_BOARD(bi, mt) ZYNOS_BOARD(ZYNOS_VENDOR_ID_LUCENT, bi, mt)
 
-static struct zynos_board __initdata zynos_boards[] = {
+static struct zynos_board zynos_boards[] __initdata = {
        ZYXEL_BOARD(ZYNOS_BOARD_HS100, MACH_ADM5120_HS100),
        ZYXEL_BOARD(ZYNOS_BOARD_P334, MACH_ADM5120_P334),
        ZYXEL_BOARD(ZYNOS_BOARD_P334U, MACH_ADM5120_P334U),
@@ -308,6 +452,31 @@ static struct zynos_board __initdata zynos_boards[] = {
        DUMMY_BOARD()
 };
 
+struct common_board {
+       char *name;
+       unsigned long   mach_type;
+};
+
+#define DEFBOARD(n, mt) { .name = (n), .mach_type = (mt) }
+static struct common_board common_boards[] __initdata = {
+       DEFBOARD("CAS-630",             MACH_ADM5120_CAS630),
+       DEFBOARD("CAS-670",             MACH_ADM5120_CAS670),
+       DEFBOARD("CAS-700",             MACH_ADM5120_CAS700),
+       DEFBOARD("CAS-771",             MACH_ADM5120_CAS771),
+       DEFBOARD("CAS-790",             MACH_ADM5120_CAS790),
+       DEFBOARD("CAS-861",             MACH_ADM5120_CAS861),
+       DEFBOARD("NFS-101U",            MACH_ADM5120_NFS101U),
+       DEFBOARD("NFS-202U",            MACH_ADM5120_NFS202U),
+       DEFBOARD("EASY 5120",           MACH_ADM5120_EASY5120),
+       DEFBOARD("EASY 5120-RT",        MACH_ADM5120_EASY5120RT),
+       DEFBOARD("EASY 5120P-ATA",      MACH_ADM5120_EASY5120PATA),
+       DEFBOARD("EASY 83000",          MACH_ADM5120_EASY83000),
+       DEFBOARD("BR-6104K",            MACH_ADM5120_BR6104K),
+       DEFBOARD("WP54G-WRT",           MACH_ADM5120_WP54G_WRT),
+       DEFBOARD("P-334WT",             MACH_ADM5120_P334WT),
+       DEFBOARD("P-335",               MACH_ADM5120_P335),
+};
+
 /*
  * Helper routines
  */
@@ -367,8 +536,7 @@ static int __init cfe_present(void)
                return 0;
        }
 
-       /* cfe_a1_val must be 0, because only one CPU present in the ADM5120 SoC
-*/
+       /* cfe_a1_val must be 0, because only one CPU present in the ADM5120 */
        if (cfe_a1_val != 0) {
                return 0;
        }
@@ -434,75 +602,111 @@ out:
 /*
  * RouterBOOT based boards
  */
-static int __init routerboot_find_tag(u8 *buf, u16 tagid, void **tagval,
-       u16 *taglen)
+static int __init routerboot_load_hs(u8 *buf, u16 buflen,
+       struct rb_hard_settings *hs)
 {
        u16 id,len;
-       int ret;
+       u8 *mac;
+       int i,j;
+
+       if (buflen < 4)
+               return -1;
+
+       if (read_le32(buf) != RB_MAGIC_HARD)
+               return -1;
+
+       /* skip magic value */
+       buf += 4;
+       buflen -= 4;
 
-       ret = -1;
-       /* skip header */
-       buf += 8;
-       for (;;) {
+       while (buflen > 2) {
                id = read_le16(buf);
                buf += 2;
-               if (id == RB_ID_TERMINATOR)
+               buflen -= 2;
+               if (id == RB_ID_TERMINATOR || buflen < 2)
                        break;
 
                len = read_le16(buf);
                buf += 2;
-               if (id == tagid) {
-                       *tagval = buf;
-                       *taglen = len;
-                       ret = 0;
+               buflen -= 2;
+
+               if (buflen < len)
+                       break;
+
+               switch (id) {
+               case RB_ID_BIOS_VERSION:
+                       hs->bios_ver = (char *)buf;
+                       break;
+               case RB_ID_BOARD_NAME:
+                       hs->name = (char *)buf;
+                       break;
+               case RB_ID_MEMORY_SIZE:
+                       hs->mem_size = read_le32(buf);
+                       break;
+               case RB_ID_MAC_ADDRESS_COUNT:
+                       hs->mac_count = read_le32(buf);
+                       break;
+               case RB_ID_MAC_ADDRESS_PACK:
+                       hs->mac_count = len/RB_MAC_SIZE;
+                       if (hs->mac_count > RB_MAX_MAC_COUNT)
+                               hs->mac_count = RB_MAX_MAC_COUNT;
+                       mac = buf;
+                       for (i=0; i < hs->mac_count; i++) {
+                               for (j=0; j < RB_MAC_SIZE; j++)
+                                       hs->macs[i][j] = mac[j];
+                               mac += RB_MAC_SIZE;
+                       }
                        break;
                }
 
                buf += len;
+               buflen -= len;
+
        }
 
-       return ret;
+       return 0;
 }
 
-#define RB_HS_ADDR     KSEG1ADDR(ADM5120_SRAM0_BASE+0x1000)
-#define RB_SS_ADDR     KSEG1ADDR(ADM5120_SRAM0_BASE+0x2000)
-#define RB_FW_ADDR     KSEG1ADDR(ADM5120_SRAM0_BASE+0x10000)
+#define RB_BS_OFFS     0x14
+#define RB_OFFS_MAX    (128*1024)
 
 static unsigned long __init routerboot_detect_board(void)
 {
        struct routerboard *board;
-       u32 magic;
-       char *name;
-       u16 namelen;
+       struct rb_hard_settings hs;
+       struct rb_bios_settings *bs;
+       u8 *base;
+       u32 off,len;
        unsigned long ret;
 
        ret = MACH_ADM5120_UNKNOWN;
 
-       magic = le32_to_cpu(*(u32 *)RB_HS_ADDR);
-       if (magic != RB_MAGIC_HARD)
-               goto out;
+       base = (u8 *)KSEG1ADDR(ADM5120_SRAM0_BASE);
+       bs = (struct rb_bios_settings *)(base + RB_BS_OFFS);
 
-       magic = le32_to_cpu(*(u32 *)RB_SS_ADDR);
-       if ((magic != RB_MAGIC_SOFT) && (magic != RB_MAGIC_DAWN))
-               goto out;
+       off = read_le32(&bs->hs_offs);
+       len = read_le32(&bs->hs_size);
+       if (off > RB_OFFS_MAX)
+               return ret;
 
-       if (routerboot_find_tag((u8 *)RB_HS_ADDR, RB_ID_BOARD_NAME,
-               (void *)&name, &namelen))
-               goto out;
+       memset(&hs, 0, sizeof(hs));
+       if (routerboot_load_hs(base+off, len, &hs) != 0)
+               return ret;
+
+       /* assume RouterBOOT as the boot-loader */
+       adm5120_boot_loader = BOOT_LOADER_ROUTERBOOT;
+
+       if (hs.name == NULL)
+               return ret;
 
        for (board = routerboards; board->mach_type != MACH_ADM5120_UNKNOWN;
                board++) {
-               if (strncmp(board->name, name, strlen(board->name)) == 0) {
+               if (strncmp(board->name, hs.name, strlen(board->name)) == 0) {
                        ret = board->mach_type;
                        break;
                }
-
        }
 
-       /* assume RouterBOOT as the boot-loader */
-       adm5120_boot_loader = BOOT_LOADER_ROUTERBOOT;
-
-out:
        return ret;
 }
 
@@ -622,6 +826,34 @@ static unsigned long __init uboot_detect_board(void)
        return MACH_ADM5120_UNKNOWN;
 }
 
+/*
+ * Misc boards
+ */
+static unsigned long __init prom_detect_board(void)
+{
+       char *name;
+       unsigned long ret;
+       int i;
+
+       ret = MACH_ADM5120_UNKNOWN;
+       name = prom_getenv("board_name");
+       if (name == NULL)
+               goto out;
+
+       if (*name == '\0')
+               goto out;
+
+       for (i=0; i<ARRAY_SIZE(common_boards); i++) {
+               if (strcmp(common_boards[i].name, name) == 0) {
+                       ret = common_boards[i].mach_type;
+                       break;
+               }
+       }
+
+out:
+       return ret;
+}
+
 static void __init adm5120_detect_board(void)
 {
        struct adm5120_board *board;
@@ -647,6 +879,9 @@ static void __init adm5120_detect_board(void)
                adm5120_boot_loader = BOOT_LOADER_UBOOT;
                if (t == MACH_ADM5120_UNKNOWN)
                        t = uboot_detect_board();
+       } else {
+               if (t == MACH_ADM5120_UNKNOWN)
+                       t = prom_detect_board();
        }
 
        for (board = adm5120_boards; board->mach_type != MACH_ADM5120_UNKNOWN;
@@ -658,6 +893,9 @@ static void __init adm5120_detect_board(void)
        memcpy(&adm5120_board, board, sizeof(adm5120_board));
 }
 
+#define SWITCH_READ(r) *(u32 *)(KSEG1ADDR(ADM5120_SWITCH_BASE)+(r))
+#define SWITCH_WRITE(r,v) *(u32 *)(KSEG1ADDR(ADM5120_SWITCH_BASE)+(r))=(v)
+
 /*
  * CPU settings detection
  */
@@ -672,7 +910,7 @@ static void __init adm5120_detect_cpuinfo(void)
        u32 code;
        u32 clks;
 
-       code = *(u32 *)KSEG1ADDR(ADM5120_SWITCH_BASE+SWITCH_REG_CODE);
+       code = SWITCH_READ(SWITCH_REG_CODE);
 
        adm5120_product_code = CODE_GET_PC(code);
        adm5120_revision = CODE_GET_REV(code);
@@ -688,22 +926,146 @@ static void __init adm5120_detect_cpuinfo(void)
                adm5120_speed += 50000000;
 }
 
+#if 1
+#  define mem_dbg(f, ...)      prom_printf("mem_detect: " f, ## __VA_ARGS__)
+extern void prom_printf(char *, ...);
+#else
+#  define mem_dbg(f, ...)
+#endif
+
+static void __init adm5120_detect_memsize(void)
+{
+       u32     memctrl;
+       u32     size, maxsize;
+       volatile u8     *p,*r;
+       u8      t;
+
+       memctrl = SWITCH_READ(SWITCH_REG_MEMCTRL);
+       switch (memctrl & MEMCTRL_SDRS_MASK) {
+       case MEMCTRL_SDRS_4M:
+               maxsize = 4 << 20;
+               break;
+       case MEMCTRL_SDRS_8M:
+               maxsize = 8 << 20;
+               break;
+       case MEMCTRL_SDRS_16M:
+               maxsize = 16 << 20;
+               break;
+       default:
+               maxsize = 64 << 20;
+               break;
+       }
+
+       /* FIXME: need to disable buffers for both SDRAM banks? */
+
+       mem_dbg("checking for %ldMB chip\n",maxsize >> 20);
+
+       /* detect size of the 1st SDRAM bank */
+       p = (volatile u8 *)KSEG1ADDR(0);
+       t = *p;
+       for (size = 2<<20; size <= (maxsize >> 1); size <<= 1) {
+#if 1
+               r = (p+size);
+               *p = 0x55;
+               mem_dbg("1st pattern at 0x%lx is 0x%02x\n", size, *r);
+               if (*r == 0x55) {
+                       *p = 0xAA;
+                       mem_dbg("2nd pattern at 0x%lx is 0x%02x\n", size, *r);
+                       if (*r == 0xAA) {
+                               /* mirrored address */
+                               mem_dbg("mirrored data found at 0x%lx\n", size);
+                               break;
+                       }
+               }
+#else
+               p[0] = 0x55;
+               mem_dbg("1st pattern at 0x%lx is 0x%02x\n", size, p[size]);
+               if (p[size] != 0x55)
+                       continue;
+
+               p[0] = 0xAA;
+               mem_dbg("2nd pattern at 0x%lx is 0x%02x\n", size, p[size]);
+               if (p[size] != 0xAA)
+                       continue;
+
+               /* mirrored address */
+               mem_dbg("mirrored data found at 0x%lx\n", size);
+               break;
+#endif
+       }
+       *p = t;
+
+       mem_dbg("%ldMB chip found\n", size >> 20);
+
+       if (size == (32 << 20))
+               /* if bank size is 32MB, 2nd bank is not supported */
+               goto out;
+
+       if ((memctrl & MEMCTRL_SDR1_ENABLE) == 0)
+               /* if 2nd bank is not enabled, we are done */
+               goto out;
+
+       /*
+        * some bootloaders enable 2nd bank, even if the 2nd SDRAM chip
+        * are missing.
+        */
+       mem_dbg("checking second bank\n");
+       p += (maxsize+size)-1;
+       t = *p;
+       *p = 0x55;
+       if (*p != 0x55)
+               goto out;
+
+       *p = 0xAA;
+       if (*p != 0xAA)
+               goto out;
+
+       *p = t;
+       if (maxsize != size) {
+               /* adjusting MECTRL register */
+               memctrl &= ~(MEMCTRL_SDRS_MASK);
+               switch (size>>20) {
+               case 4:
+                       memctrl |= MEMCTRL_SDRS_4M;
+                       break;
+               case 8:
+                       memctrl |= MEMCTRL_SDRS_8M;
+                       break;
+               case 16:
+                       memctrl |= MEMCTRL_SDRS_16M;
+                       break;
+               default:
+                       memctrl |= MEMCTRL_SDRS_64M;
+                       break;
+               }
+               SWITCH_WRITE(SWITCH_REG_MEMCTRL, memctrl);
+       }
+       size <<= 1;
+
+out:
+       adm5120_memsize = size;
+       mem_dbg("%ldMB memory found\n",size>>20);
+}
+
 void __init adm5120_info_show(void)
 {
        /* FIXME: move this somewhere else */
-       printk("ADM%04X%s revision %d, running at %ldMHz\n",
+       printk(KERN_INFO "ADM%04X%s revision %d, running at %ldMHz\n",
                adm5120_product_code,
                (adm5120_package == ADM5120_PACKAGE_BGA) ? "" : "P",
                adm5120_revision, (adm5120_speed / 1000000)
                );
-       printk("Boot loader is: %s\n", boot_loader_names[adm5120_boot_loader]);
-       printk("Booted from   : %s flash\n", adm5120_nand_boot ? "NAND":"NOR");
-       printk("Board is      : %s\n", adm5120_board_name());
+       printk(KERN_INFO "Boot loader is: %s\n", boot_loader_names[adm5120_boot_loader]);
+       printk(KERN_INFO "Booted from   : %s flash\n", adm5120_nand_boot ? "NAND":"NOR");
+       printk(KERN_INFO "Board is      : %s\n", adm5120_board_name());
+       printk(KERN_INFO "Memory size   : %ldMB\n", adm5120_memsize >> 20);
 }
 
 void __init adm5120_info_init(void)
 {
+
        adm5120_detect_cpuinfo();
+       adm5120_detect_memsize();
        adm5120_detect_board();
 
        adm5120_info_show();
This page took 0.039173 seconds and 4 git commands to generate.