diff mbox

[OpenWrt-Devel,v6,6/6] ar71xx: add support for Cisco's MR18

Message ID 6af485ea05c3589d784d955c0f476ec024501dfa.1449350080.git.chunkeey@googlemail.com
State Changes Requested
Headers show

Commit Message

Christian Lamparter Dec. 5, 2015, 10:48 p.m. UTC
From: Chris R Blake <chrisrblake93@gmail.com>

This patch adds support for Cisco's MR18.
Detailed instructions for the flashing the device can
be found in the OpenWrt forum thread:
<https://forum.openwrt.org/viewtopic.php?id=59248>

Signed-off-by: Chris R Blake <chrisrblake93@gmail.com>
---
 target/linux/ar71xx/base-files/etc/board.d/01_leds |   4 +
 .../linux/ar71xx/base-files/etc/board.d/02_network |   1 +
 target/linux/ar71xx/base-files/etc/diag.sh         |   3 +
 .../etc/hotplug.d/firmware/10-ath9k-eeprom         |  32 +++
 target/linux/ar71xx/base-files/lib/ar71xx.sh       |   3 +
 .../base-files/lib/preinit/05_set_iface_mac_ar71xx |   4 +
 .../ar71xx/base-files/lib/upgrade/merakinand.sh    | 137 ++++++++++
 .../ar71xx/base-files/lib/upgrade/platform.sh      |   7 +
 target/linux/ar71xx/config-4.1                     |   1 +
 .../linux/ar71xx/files/arch/mips/ath79/mach-mr18.c | 297 +++++++++++++++++++++
 target/linux/ar71xx/image/Makefile                 |  34 +++
 target/linux/ar71xx/nand/config-default            |   1 +
 target/linux/ar71xx/nand/profiles/meraki.mk        |  17 ++
 .../817-MIPS-ath79-add-meraki-mr18-support.patch   |  41 +++
 14 files changed, 582 insertions(+)
 create mode 100644 target/linux/ar71xx/base-files/lib/upgrade/merakinand.sh
 create mode 100644 target/linux/ar71xx/files/arch/mips/ath79/mach-mr18.c
 create mode 100644 target/linux/ar71xx/nand/profiles/meraki.mk
 create mode 100644 target/linux/ar71xx/patches-4.1/817-MIPS-ath79-add-meraki-mr18-support.patch

Comments

Alexander 'lynxis' Couzens Dec. 6, 2015, 6:56 a.m. UTC | #1
Hi Christian,

please use the new image/Makefile style. Like the most tplink or
ubiquity devices use it.

If you need any help, just ask.

Best,
lynxis

On Sat,  5 Dec 2015 23:48:08 +0100
Christian Lamparter <chunkeey@googlemail.com> wrote:

> +MIPS_MACHINE(ATH79_MACH_MR18, "MR18", "Meraki MR18", mr18_setup);
> diff --git a/target/linux/ar71xx/image/Makefile
> b/target/linux/ar71xx/image/Makefile index d12ad8b..bac4a18 100644
> --- a/target/linux/ar71xx/image/Makefile
> +++ b/target/linux/ar71xx/image/Makefile
> @@ -1494,6 +1494,7 @@
> dragino2_mtdlayout=mtdparts=spi0.0:256k(u-boot)ro,16000k(firmware),64k(config)ro
> hiwifi_hc6361_mtdlayout=mtdparts=spi0.0:64k(u-boot)ro,64k(bdinfo)ro,1280k(kernel),14848k(rootfs),64k(backup)ro,64k(art)ro,16128k@0x20000(firmware)
> mr12_mtdlayout=mtdparts=spi0.0:256k(u-boot)ro,256k(u-boot-env)ro,13440k(rootfs),2304k(kernel),128k(art)ro,15744k@0x80000(firmware)
> mr16_mtdlayout=mtdparts=spi0.0:256k(u-boot)ro,256k(u-boot-env)ro,13440k(rootfs),2304k(kernel),128k(art)ro,15744k@0x80000(firmware)
> +mr18_mtdlayout=mtdparts=ar934x-nfc:512k(nandloader)ro,8M(kernel),8M(recovery),113664k(ubi),128k@130944k(odm-caldata)ro
> pb92_mtdlayout=mtdparts=spi0.0:256k(u-boot)ro,64k(u-boot-env)ro,2752k(rootfs),896k(kernel),64k(nvram),64k(art)ro,3648k@0x50000(firmware)
> planex_mtdlayout=mtdparts=spi0.0:256k(u-boot)ro,64k(u-boot-env)ro,7744k(firmware),128k(art)ro
> ubntxm_mtdlayout=mtdparts=spi0.0:256k(u-boot)ro,64k(u-boot-env)ro,7552k(firmware),256k(cfg)ro,64k(EEPROM)ro
> @@ -1975,6 +1976,37 @@ Image/Build/CyberTANLZMA/buildkernel=$(call
> MkuImageLzma,$(2),$(3) $(4)) Image/Build/CyberTANLZMA=$(call
> Image/Build/CyberTAN,$(1),$(2),$(3),$(4),$(5)) 
> +define Image/Build/MerakiNAND/initramfs
> +	$(eval kernelsize=$(call mtdpartsize,kernel,$(4)))
> +	$(call PatchKernel,$(2),$(3) $(4),"-initramfs")
> +	-$(STAGING_DIR_HOST)/bin/mkmerakifw \
> +		-B $(2) -s \
> +		-i $(KDIR_TMP)/vmlinux-initramfs-$(2) \
> +		-o $(KDIR_TMP)/vmlinux-initramfs-$(2)-patched
> +	$(CP) $(KDIR_TMP)/vmlinux-initramfs-$(2)-patched
> $(BIN_DIR)/$(IMG_PREFIX)-$(2)-initramfs.bin +endef
> +
Chris Dec. 7, 2015, 2:19 p.m. UTC | #2
Hey lynxis,

Looking over the new Makefile style, I was unable to find any examples
of this for devices that rely on NAND and .tar sysupgrade images, such
as the NetgearNAND boards. (maybe I am blind?)

It's also worth noting this device is a bit special, as it doesn't use
a UBI generated image for the system.img, but instead a squashfs file.
If you have any tips on how to move this code over to the new style,
please let me know or feel free to reach out to me directly.

With that said, would it be possible to get this merged as is in the
meantime to help get this device mainline?

Best Regards,
Chris Blake

On Sun, Dec 6, 2015 at 12:56 AM, Alexander Couzens <lynxis@fe80.eu> wrote:
> Hi Christian,
>
> please use the new image/Makefile style. Like the most tplink or
> ubiquity devices use it.
>
> If you need any help, just ask.
>
> Best,
> lynxis
>
> On Sat,  5 Dec 2015 23:48:08 +0100
> Christian Lamparter <chunkeey@googlemail.com> wrote:
>
>> +MIPS_MACHINE(ATH79_MACH_MR18, "MR18", "Meraki MR18", mr18_setup);
>> diff --git a/target/linux/ar71xx/image/Makefile
>> b/target/linux/ar71xx/image/Makefile index d12ad8b..bac4a18 100644
>> --- a/target/linux/ar71xx/image/Makefile
>> +++ b/target/linux/ar71xx/image/Makefile
>> @@ -1494,6 +1494,7 @@
>> dragino2_mtdlayout=mtdparts=spi0.0:256k(u-boot)ro,16000k(firmware),64k(config)ro
>> hiwifi_hc6361_mtdlayout=mtdparts=spi0.0:64k(u-boot)ro,64k(bdinfo)ro,1280k(kernel),14848k(rootfs),64k(backup)ro,64k(art)ro,16128k@0x20000(firmware)
>> mr12_mtdlayout=mtdparts=spi0.0:256k(u-boot)ro,256k(u-boot-env)ro,13440k(rootfs),2304k(kernel),128k(art)ro,15744k@0x80000(firmware)
>> mr16_mtdlayout=mtdparts=spi0.0:256k(u-boot)ro,256k(u-boot-env)ro,13440k(rootfs),2304k(kernel),128k(art)ro,15744k@0x80000(firmware)
>> +mr18_mtdlayout=mtdparts=ar934x-nfc:512k(nandloader)ro,8M(kernel),8M(recovery),113664k(ubi),128k@130944k(odm-caldata)ro
>> pb92_mtdlayout=mtdparts=spi0.0:256k(u-boot)ro,64k(u-boot-env)ro,2752k(rootfs),896k(kernel),64k(nvram),64k(art)ro,3648k@0x50000(firmware)
>> planex_mtdlayout=mtdparts=spi0.0:256k(u-boot)ro,64k(u-boot-env)ro,7744k(firmware),128k(art)ro
>> ubntxm_mtdlayout=mtdparts=spi0.0:256k(u-boot)ro,64k(u-boot-env)ro,7552k(firmware),256k(cfg)ro,64k(EEPROM)ro
>> @@ -1975,6 +1976,37 @@ Image/Build/CyberTANLZMA/buildkernel=$(call
>> MkuImageLzma,$(2),$(3) $(4)) Image/Build/CyberTANLZMA=$(call
>> Image/Build/CyberTAN,$(1),$(2),$(3),$(4),$(5))
>> +define Image/Build/MerakiNAND/initramfs
>> +     $(eval kernelsize=$(call mtdpartsize,kernel,$(4)))
>> +     $(call PatchKernel,$(2),$(3) $(4),"-initramfs")
>> +     -$(STAGING_DIR_HOST)/bin/mkmerakifw \
>> +             -B $(2) -s \
>> +             -i $(KDIR_TMP)/vmlinux-initramfs-$(2) \
>> +             -o $(KDIR_TMP)/vmlinux-initramfs-$(2)-patched
>> +     $(CP) $(KDIR_TMP)/vmlinux-initramfs-$(2)-patched
>> $(BIN_DIR)/$(IMG_PREFIX)-$(2)-initramfs.bin +endef
>> +
>
>
> --
> Alexander Couzens
>
> mail: lynxis@fe80.eu
> jabber: lynxis@fe80.eu
> mobile: +4915123277221
> gpg: 390D CF78 8BF9 AA50 4F8F  F1E2 C29E 9DA6 A0DF 8604
>
> _______________________________________________
> openwrt-devel mailing list
> openwrt-devel@lists.openwrt.org
> https://lists.openwrt.org/cgi-bin/mailman/listinfo/openwrt-devel
>
diff mbox

Patch

diff --git a/target/linux/ar71xx/base-files/etc/board.d/01_leds b/target/linux/ar71xx/base-files/etc/board.d/01_leds
index 892b567..d868f7b 100755
--- a/target/linux/ar71xx/base-files/etc/board.d/01_leds
+++ b/target/linux/ar71xx/base-files/etc/board.d/01_leds
@@ -283,6 +283,10 @@  mr16)
 	ucidef_set_led_wlan "wlan4" "WLAN4" "mr16:green:wifi4" "phy0tpt"
 	;;
 
+mr18)
+	ucidef_set_led_netdev "wlan0" "WLAN0" "mr18:blue:tricolor0" "wlan0"
+	;;
+
 mr600)
 	ucidef_set_led_wlan "wlan58" "WLAN58" "mr600:green:wlan58" "phy0tpt"
 	;;
diff --git a/target/linux/ar71xx/base-files/etc/board.d/02_network b/target/linux/ar71xx/base-files/etc/board.d/02_network
index 296626b..41f07de 100755
--- a/target/linux/ar71xx/base-files/etc/board.d/02_network
+++ b/target/linux/ar71xx/base-files/etc/board.d/02_network
@@ -312,6 +312,7 @@  eap7660d |\
 el-mini |\
 loco-m-xw |\
 mr1750 |\
+mr18 |\
 mr600 |\
 mr600v2 |\
 mr900 |\
diff --git a/target/linux/ar71xx/base-files/etc/diag.sh b/target/linux/ar71xx/base-files/etc/diag.sh
index a8e1721..8447cf5 100644
--- a/target/linux/ar71xx/base-files/etc/diag.sh
+++ b/target/linux/ar71xx/base-files/etc/diag.sh
@@ -149,6 +149,9 @@  get_status_led() {
 	mr16)
 		status_led="mr16:green:power"
 		;;
+	mr18)
+		status_led="mr18:green:tricolor0"
+		;;
 	mr600)
 		status_led="mr600:orange:power"
 		;;
diff --git a/target/linux/ar71xx/base-files/etc/hotplug.d/firmware/10-ath9k-eeprom b/target/linux/ar71xx/base-files/etc/hotplug.d/firmware/10-ath9k-eeprom
index 7287809..bfab8bb 100644
--- a/target/linux/ar71xx/base-files/etc/hotplug.d/firmware/10-ath9k-eeprom
+++ b/target/linux/ar71xx/base-files/etc/hotplug.d/firmware/10-ath9k-eeprom
@@ -42,6 +42,14 @@  board=$(ar71xx_board_name)
 case "$FIRMWARE" in
 "soc_wmac.eeprom")
 	case $board in
+	mr18)
+		if [ -n "$(nand_find_volume ubi0 caldata)" ]; then
+			ath9k_eeprom_extract "caldata" 4096 2048
+		else
+			ath9k_eeprom_extract "odm-caldata" 4096 2048
+		fi
+		ath9k_patch_firmware_mac $(macaddr_add $(mtd_get_mac_binary_ubi board-config 102) +1)
+		;;
 	r6100 | \
 	wndr3700v4 | \
 	wndr4300)
@@ -56,6 +64,14 @@  case "$FIRMWARE" in
 
 "pci_wmac0.eeprom")
 	case $board in
+	mr18)
+		if [ -n "$(nand_find_volume ubi0 caldata)" ]; then
+			ath9k_eeprom_extract "caldata" 20480 2048
+		else
+			ath9k_eeprom_extract "odm-caldata" 20480 2048
+		fi
+		ath9k_patch_firmware_mac $(macaddr_add $(mtd_get_mac_binary_ubi board-config 102) +2)
+		;;
 	wndr3700v4 | \
 	wndr4300)
 		ath9k_eeprom_extract "caldata" 20480 2048
@@ -66,4 +82,20 @@  case "$FIRMWARE" in
 		;;
 	esac
 	;;
+
+"pci_wmac1.eeprom")
+	case $board in
+	mr18)
+		if [ -n "$(nand_find_volume ubi0 caldata)" ]; then
+			ath9k_eeprom_extract "caldata" 36864 2048
+		else
+			ath9k_eeprom_extract "odm-caldata" 36864 2048
+		fi
+		ath9k_patch_firmware_mac $(macaddr_add $(mtd_get_mac_binary_ubi board-config 102) +3)
+		;;
+	*)
+		ath9k_eeprom_die "board $board is not supported yet"
+		;;
+	esac
+	;;
 esac
diff --git a/target/linux/ar71xx/base-files/lib/ar71xx.sh b/target/linux/ar71xx/base-files/lib/ar71xx.sh
index df77040..ecb0b73 100755
--- a/target/linux/ar71xx/base-files/lib/ar71xx.sh
+++ b/target/linux/ar71xx/base-files/lib/ar71xx.sh
@@ -548,6 +548,9 @@  ar71xx_board_detect() {
 	*MR16)
 		name="mr16"
 		;;
+	*MR18)
+		name="mr18"
+		;;
 	*MR600v2)
 		name="mr600v2"
 		;;
diff --git a/target/linux/ar71xx/base-files/lib/preinit/05_set_iface_mac_ar71xx b/target/linux/ar71xx/base-files/lib/preinit/05_set_iface_mac_ar71xx
index 92b3765..a9f4bf5 100644
--- a/target/linux/ar71xx/base-files/lib/preinit/05_set_iface_mac_ar71xx
+++ b/target/linux/ar71xx/base-files/lib/preinit/05_set_iface_mac_ar71xx
@@ -29,6 +29,10 @@  preinit_set_mac_address() {
 		dir-615-i1)
 			fetch_mac_from_mtd nvram sys_lan_mac sys_wan_mac
 			;;
+		mr18)
+			mac_lan=$(mtd_get_mac_binary_ubi board-config 102)
+			[ -n "$mac_lan" ] && ifconfig eth0 hw ether "$mac_lan"
+			;;
 		r6100)
 			mac_lan=$(mtd_get_mac_binary caldata 0)
 			[ -n "$mac_lan" ] && ifconfig eth1 hw ether "$mac_lan"
diff --git a/target/linux/ar71xx/base-files/lib/upgrade/merakinand.sh b/target/linux/ar71xx/base-files/lib/upgrade/merakinand.sh
new file mode 100644
index 0000000..fe78e9f
--- /dev/null
+++ b/target/linux/ar71xx/base-files/lib/upgrade/merakinand.sh
@@ -0,0 +1,137 @@ 
+#!/bin/sh
+#
+# Copyright (C) 2015 Chris Blake <chrisrblake93@gmail.com>
+#
+# Custom upgrade script for Meraki NAND devices (ex. MR18)
+# Based on dir825.sh and stock nand functions
+#
+. /lib/ar71xx.sh
+. /lib/functions.sh
+. /lib/upgrade/nand.sh
+
+get_magic_at() {
+	local mtddev=$1
+	local pos=$2
+	dd bs=1 count=2 skip=$pos if=$mtddev 2>/dev/null | hexdump -v -n 4 -e '1/1 "%02x"'
+}
+
+mr18_is_caldata_valid() {
+	local mtddev=$1
+	local magic
+
+	magic=$(get_magic_at $mtddev 4096)
+	[ "$magic" != "0202" ] && return 0
+
+	magic=$(get_magic_at $mtddev 20480)
+	[ "$magic" != "0202" ] && return 0
+
+	magic=$(get_magic_at $mtddev 36864)
+	[ "$magic" != "0202" ] && return 0
+
+	return 1
+}
+
+merakinand_copy_caldata() {
+	local cal_src=$1
+	local cal_dst=$2
+	local ubidev=$( nand_find_ubi $CI_UBIPART )
+	local board_name="$(cat /tmp/sysinfo/board_name)"
+	local rootfs_size="$(ubinfo /dev/ubi0 -N rootfs_data | grep "Size" | awk '{ print $6 }')"
+
+	# Setup partitions using board name, in case of future platforms
+	case "$board_name" in
+	"mr18")
+		# Src is MTD
+		mtd_src=$(find_mtd_chardev $cal_src)
+		[ -n "$mtd_src" ] || {
+			echo "no mtd device found for partition $cal_src"
+			exit 1
+		}
+
+		# Dest is UBI
+		# TODO: possibly add create (hard to do when rootfs_data is expanded & mounted)
+		# Would need to be done from ramdisk
+		mtd_dst="$(nand_find_volume $ubidev $cal_dst)"
+		[ -n "$mtd_dst" ] || {
+			echo "no ubi device found for partition $cal_dst"
+			exit 1
+		}
+
+		mr18_is_caldata_valid "$mtd_src" && {
+			echo "no valid calibration data found in $cal_src"
+			exit 1
+		}
+
+		mr18_is_caldata_valid "/dev/$mtd_dst" && {
+			echo "Copying calibration data from $cal_src to $cal_dst..."
+			dd if="$mtd_src" of=/tmp/caldata.tmp 2>/dev/null
+			ubiupdatevol "/dev/$mtd_dst" /tmp/caldata.tmp
+			rm /tmp/caldata.tmp
+			sync
+		}
+		return 0
+		;;
+	*)
+		echo "Unsupported device $board_name";
+		return 1
+		;;
+	esac
+}
+
+merakinand_do_kernel_check() {
+	local board_name="$1"
+	local tar_file="$2"
+	local image_magic_word=`(tar xf $tar_file sysupgrade-$board_name/kernel -O 2>/dev/null | dd bs=1 count=4 skip=0 2>/dev/null | hexdump -v -n 4 -e '1/1 "%02x"')`
+
+	# What is our kernel magic string?
+	case "$board_name" in
+	"mr18")
+		[ "$image_magic_word" == "8e73ed8a" ] && {
+			echo "pass" && return 0
+		}
+		;;
+	esac
+
+	exit 1
+}
+
+merakinand_do_platform_check() {
+	local board_name="$1"
+	local tar_file="$2"
+	local control_length=`(tar xf $tar_file sysupgrade-$board_name/CONTROL -O | wc -c) 2> /dev/null`
+	local file_type="$(identify_tar $2 sysupgrade-$board_name/root)"
+	local kernel_magic="$(merakinand_do_kernel_check $1 $2)"
+
+	case "$board_name" in
+	"mr18")
+		[ "$control_length" = 0 -o "$file_type" != "squashfs" -o "$kernel_magic" != "pass" ] && {
+			echo "Invalid sysupgrade file for $board_name"
+			return 1
+		}
+		;;
+	*)
+		echo "Unsupported device $board_name";
+		return 1
+		;;
+	esac
+
+	return 0
+}
+
+merakinand_do_upgrade() {
+	local tar_file="$1"
+	local board_name="$(cat /tmp/sysinfo/board_name)"
+
+	# Do we need to do any platform tweaks?
+	case "$board_name" in
+	"mr18")
+		# Check and create UBI caldata if it's invalid
+		merakinand_copy_caldata "odm-caldata" "caldata"
+		nand_do_upgrade $1
+		;;
+	*)
+		echo "Unsupported device $board_name";
+		exit 1
+		;;
+	esac
+}
diff --git a/target/linux/ar71xx/base-files/lib/upgrade/platform.sh b/target/linux/ar71xx/base-files/lib/upgrade/platform.sh
index 41886e3..1f1089e 100755
--- a/target/linux/ar71xx/base-files/lib/upgrade/platform.sh
+++ b/target/linux/ar71xx/base-files/lib/upgrade/platform.sh
@@ -433,6 +433,10 @@  platform_check_image() {
 		}
 		return 0
 		;;
+	mr18)
+		merakinand_do_platform_check $board $1
+		return $?;
+		;;
 	nbg6716 | \
 	r6100 | \
 	wndr3700v4 | \
@@ -493,6 +497,9 @@  platform_pre_upgrade() {
 	wndr4300 )
 		nand_do_upgrade "$1"
 		;;
+	mr18)
+		merakinand_do_upgrade "$1"
+		;;
 	esac
 }
 
diff --git a/target/linux/ar71xx/config-4.1 b/target/linux/ar71xx/config-4.1
index eff197a..bc1323a 100644
--- a/target/linux/ar71xx/config-4.1
+++ b/target/linux/ar71xx/config-4.1
@@ -90,6 +90,7 @@  CONFIG_ATH79_MACH_MC_MAC1200R=y
 CONFIG_ATH79_MACH_MR12=y
 CONFIG_ATH79_MACH_MR16=y
 CONFIG_ATH79_MACH_MR1750=y
+CONFIG_ATH79_MACH_MR18=y
 CONFIG_ATH79_MACH_MR600=y
 CONFIG_ATH79_MACH_MR900=y
 CONFIG_ATH79_MACH_MYNET_N600=y
diff --git a/target/linux/ar71xx/files/arch/mips/ath79/mach-mr18.c b/target/linux/ar71xx/files/arch/mips/ath79/mach-mr18.c
new file mode 100644
index 0000000..a24cb3f
--- /dev/null
+++ b/target/linux/ar71xx/files/arch/mips/ath79/mach-mr18.c
@@ -0,0 +1,297 @@ 
+/*
+ *  Cisco Meraki MR18 board support
+ *
+ *  Copyright (C) 2015 Chris Blake <chrisrblake93@gmail.com>
+ *  Copyright (C) 2015 Christian Lamparter <chunkeey@googlemail.com>
+ *  Copyright (C) 2015 Thomas Hebb <tommyhebb@gmail.com>
+ *
+ *  Based on Cisco Meraki GPL Release r23-20150601 MR18 Device Config
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under the terms of the GNU General Public License version 2 as published
+ *  by the Free Software Foundation.
+ */
+#include <linux/platform_device.h>
+#include <linux/ath9k_platform.h>
+#include <linux/platform/ar934x_nfc.h>
+#include <linux/platform_data/phy-at803x.h>
+
+#include <asm/mach-ath79/ath79.h>
+#include <asm/mach-ath79/ar71xx_regs.h>
+
+#include <linux/leds-nu801.h>
+#include <linux/pci.h>
+
+#include "common.h"
+#include "dev-eth.h"
+#include "pci.h"
+#include "dev-gpio-buttons.h"
+#include "dev-leds-gpio.h"
+#include "dev-nfc.h"
+#include "dev-wmac.h"
+#include "machtypes.h"
+
+#define MR18_GPIO_LED_POWER_WHITE    18
+#define MR18_GPIO_LED_POWER_ORANGE    21
+
+#define MR18_GPIO_BTN_RESET    17
+#define MR18_KEYS_POLL_INTERVAL    20  /* msecs */
+#define MR18_KEYS_DEBOUNCE_INTERVAL  (3 * MR18_KEYS_POLL_INTERVAL)
+
+#define MR18_WAN_PHYADDR    3
+
+/* used for eth calibration */
+#define MR18_OTP_BASE			(AR71XX_APB_BASE + 0x130000)
+#define MR18_OTP_SIZE			(0x2000) /* just a guess */
+#define MR18_OTP_MEM_0_REG		(0x0000)
+#define MR18_OTP_INTF2_REG		(0x1008)
+#define MR18_OTP_STATUS0_REG		(0x1018)
+#define MR18_OTP_STATUS0_EFUSE_VALID	BIT(2)
+
+#define MR18_OTP_STATUS1_REG		(0x101c)
+#define MR18_OTP_LDO_CTRL_REG		(0x1024)
+#define MR18_OTP_LDO_STATUS_REG		(0x102c)
+#define MR18_OTP_LDO_STATUS_POWER_ON	BIT(0)
+
+static struct gpio_led MR18_leds_gpio[] __initdata = {
+	{
+		.name = "mr18:white:power",
+		.gpio = MR18_GPIO_LED_POWER_WHITE,
+		.active_low  = 1,
+	}, {
+		.name = "mr18:orange:power",
+		.gpio = MR18_GPIO_LED_POWER_ORANGE,
+		.active_low  = 0,
+	},
+};
+
+static struct gpio_keys_button MR18_gpio_keys[] __initdata = {
+	{
+		.desc = "reset",
+		.type = EV_KEY,
+		.code = KEY_RESTART,
+		.debounce_interval = MR18_KEYS_DEBOUNCE_INTERVAL,
+		.gpio    = MR18_GPIO_BTN_RESET,
+		.active_low  = 1,
+	},
+};
+
+static struct led_nu801_template tricolor_led_template = {
+	.device_name = "mr18",
+	.name = "tricolor",
+	.num_leds = 1,
+	.cki = 11,
+	.sdi = 12,
+	.lei = -1,
+	.ndelay = 500,
+	.init_brightness = {
+		LED_OFF,
+		LED_OFF,
+		LED_OFF,
+	},
+	.default_trigger = "none",
+	.led_colors = { "red", "green", "blue" },
+};
+
+static struct led_nu801_platform_data tricolor_led_data = {
+	.num_controllers = 1,
+	.template = &tricolor_led_template,
+};
+
+static struct platform_device tricolor_leds = {
+	.name = "leds-nu801",
+	.id = -1,
+	.dev.platform_data = &tricolor_led_data,
+};
+
+static int mr18_extract_sgmii_res_cal(void)
+{
+	void __iomem *base;
+	unsigned int reversed_sgmii_value;
+
+	unsigned int otp_value, otp_per_val, rbias_per, read_data;
+	unsigned int rbias_pos_or_neg;
+	unsigned int sgmii_res_cal_value;
+	int res_cal_val;
+
+	base = ioremap_nocache(MR18_OTP_BASE, MR18_OTP_SIZE);
+	if (!base)
+		return -EIO;
+
+	__raw_writel(0x7d, base + MR18_OTP_INTF2_REG);
+	__raw_writel(0x00, base + MR18_OTP_LDO_CTRL_REG);
+
+	while (__raw_readl(base + MR18_OTP_LDO_STATUS_REG) &
+		MR18_OTP_LDO_STATUS_POWER_ON);
+
+	__raw_readl(base + MR18_OTP_MEM_0_REG + 4);
+
+	while (!(__raw_readl(base + MR18_OTP_STATUS0_REG) &
+		MR18_OTP_STATUS0_EFUSE_VALID));
+
+	read_data = __raw_readl(base + MR18_OTP_STATUS1_REG);
+
+	iounmap(base);
+
+	if (!(read_data & 0x1fff))
+		return -ENODEV;
+
+	if (read_data & 0x00001000)
+		otp_value = (read_data & 0xfc0) >> 6;
+	else
+		otp_value = read_data & 0x3f;
+
+	if (otp_value > 31) {
+		otp_per_val = 63 - otp_value;
+		rbias_pos_or_neg = 1;
+	} else {
+		otp_per_val = otp_value;
+		rbias_pos_or_neg = 0;
+	}
+
+	rbias_per = otp_per_val * 15;
+
+	if (rbias_pos_or_neg == 1)
+		res_cal_val = (rbias_per + 34) / 21;
+	else if (rbias_per > 34)
+		res_cal_val = -((rbias_per - 34) / 21);
+	else
+		res_cal_val = (34 - rbias_per) / 21;
+
+	sgmii_res_cal_value = (8 + res_cal_val) & 0xf;
+
+	reversed_sgmii_value  = (sgmii_res_cal_value & 8) >> 3;
+	reversed_sgmii_value |= (sgmii_res_cal_value & 4) >> 1;
+	reversed_sgmii_value |= (sgmii_res_cal_value & 2) << 1;
+	reversed_sgmii_value |= (sgmii_res_cal_value & 1) << 3;
+	printk(KERN_INFO "SGMII cal value = 0x%x\n", reversed_sgmii_value);
+	return reversed_sgmii_value;
+}
+
+#define QCA955X_PLL_ETH_SGMII_SERDES_REG		0x004c
+#define QCA955X_PLL_ETH_SGMII_SERDES_LOCK_DETECT	BIT(2)
+#define QCA955X_PLL_ETH_SGMII_SERDES_PLL_REFCLK		BIT(1)
+#define QCA955X_PLL_ETH_SGMII_SERDES_EN_PLL		BIT(0)
+
+#define QCA955X_GMAC_REG_SGMII_SERDES			0x0018
+#define QCA955X_SGMII_SERDES_RES_CALIBRATION		BIT(23)
+#define QCA955X_SGMII_SERDES_RES_CALIBRATION_MASK	0xf
+#define QCA955X_SGMII_SERDES_RES_CALIBRATION_SHIFT	23
+#define QCA955X_SGMII_SERDES_LOCK_DETECT_STATUS		BIT(15)
+
+static void mr18_setup_qca955x_eth_serdes_cal(unsigned int sgmii_value)
+{
+	void __iomem *ethbase, *pllbase;
+	u32 t;
+
+	ethbase = ioremap_nocache(QCA955X_GMAC_BASE, QCA955X_GMAC_SIZE);
+	pllbase = ioremap_nocache(AR71XX_PLL_BASE, AR71XX_PLL_SIZE);
+
+	/* To Check the locking of the SGMII PLL */
+	t = __raw_readl(ethbase + QCA955X_GMAC_REG_SGMII_SERDES);
+	t &= ~(QCA955X_SGMII_SERDES_RES_CALIBRATION_MASK <<
+	       QCA955X_SGMII_SERDES_RES_CALIBRATION_SHIFT);
+	t |= (sgmii_value & QCA955X_SGMII_SERDES_RES_CALIBRATION_MASK) <<
+	     QCA955X_SGMII_SERDES_RES_CALIBRATION_SHIFT;
+	__raw_writel(t, ethbase + QCA955X_GMAC_REG_SGMII_SERDES);
+
+	__raw_writel(QCA955X_PLL_ETH_SGMII_SERDES_LOCK_DETECT |
+		     QCA955X_PLL_ETH_SGMII_SERDES_PLL_REFCLK |
+		     QCA955X_PLL_ETH_SGMII_SERDES_EN_PLL,
+		     pllbase + QCA955X_PLL_ETH_SGMII_SERDES_REG);
+
+	ath79_device_reset_clear(QCA955X_RESET_SGMII_ANALOG);
+	ath79_device_reset_clear(QCA955X_RESET_SGMII);
+
+	while (!(__raw_readl(ethbase + QCA955X_GMAC_REG_SGMII_SERDES) &
+		QCA955X_SGMII_SERDES_LOCK_DETECT_STATUS));
+
+	iounmap(ethbase);
+	iounmap(pllbase);
+}
+
+static struct ath9k_platform_data pci_main_wifi_data = {
+	.led_pin = -1,
+};
+static struct ath9k_platform_data pci_scan_wifi_data = {
+	.led_pin = -1,
+};
+
+static int mr18_dual_pci_plat_dev_init(struct pci_dev *dev)
+{
+	/* The PCIE devices are attached to different busses but they
+	 * both share the same slot number. Checking the PCI_SLOT vals
+	 * does not work.
+	 */
+	switch (dev->bus->number) {
+	case 0:
+		dev->dev.platform_data = &pci_main_wifi_data;
+		break;
+	case 1:
+		dev->dev.platform_data = &pci_scan_wifi_data;
+		break;
+	}
+
+	return 0;
+}
+
+static void __init mr18_setup(void)
+{
+	int res;
+
+	/* NAND */
+	ath79_nfc_set_ecc_mode(AR934X_NFC_ECC_SOFT_BCH);
+	ath79_register_nfc();
+
+	/* even though, the PHY is connected via RGMII,
+	 * the SGMII/SERDES PLLs need to be calibrated and locked.
+	 * Or else, the PHY won't be working for this platfrom.
+	 *
+	 * Figuring this out took such a long time, that we want to
+	 * point this quirk out, before someone wants to remove it.
+	 */
+	res = mr18_extract_sgmii_res_cal();
+	if (res >= 0) {
+		/* Setup SoC Eth Config */
+		ath79_setup_qca955x_eth_cfg(QCA955X_ETH_CFG_RGMII_EN |
+			(3 << QCA955X_ETH_CFG_RXD_DELAY_SHIFT) |
+			(3 << QCA955X_ETH_CFG_RDV_DELAY_SHIFT));
+
+		/* MDIO Interface */
+		ath79_register_mdio(0, 0x0);
+
+		mr18_setup_qca955x_eth_serdes_cal(res);
+
+		/* GMAC0 is connected to an Atheros AR8035-A */
+		ath79_init_mac(ath79_eth0_data.mac_addr, NULL, 0);
+		ath79_eth0_data.mii_bus_dev = &ath79_mdio0_device.dev;
+		ath79_eth0_data.phy_if_mode = PHY_INTERFACE_MODE_RGMII;
+		ath79_eth0_data.phy_mask = BIT(MR18_WAN_PHYADDR);
+		ath79_eth0_pll_data.pll_1000 = 0xa6000000;
+		ath79_eth0_pll_data.pll_100 = 0xa0000101;
+		ath79_eth0_pll_data.pll_10 = 0x80001313;
+		ath79_register_eth(0);
+	} else {
+		printk(KERN_ERR "failed to read EFUSE for ethernet cal\n");
+	}
+
+	/* LEDs and Buttons */
+	platform_device_register(&tricolor_leds);
+	ath79_register_leds_gpio(-1, ARRAY_SIZE(MR18_leds_gpio),
+				 MR18_leds_gpio);
+	ath79_register_gpio_keys_polled(-1, MR18_KEYS_POLL_INTERVAL,
+					ARRAY_SIZE(MR18_gpio_keys),
+					MR18_gpio_keys);
+
+	/* Clear RTC reset (Needed by SoC WiFi) */
+	ath79_device_reset_clear(QCA955X_RESET_RTC);
+
+	/* WiFi */
+	ath79_register_wmac_simple();
+
+	pci_main_wifi_data.eeprom_name = "pci_wmac0.eeprom";
+	pci_scan_wifi_data.eeprom_name = "pci_wmac1.eeprom";
+	ath79_pci_set_plat_dev_init(mr18_dual_pci_plat_dev_init);
+	ath79_register_pci();
+}
+MIPS_MACHINE(ATH79_MACH_MR18, "MR18", "Meraki MR18", mr18_setup);
diff --git a/target/linux/ar71xx/image/Makefile b/target/linux/ar71xx/image/Makefile
index d12ad8b..bac4a18 100644
--- a/target/linux/ar71xx/image/Makefile
+++ b/target/linux/ar71xx/image/Makefile
@@ -1494,6 +1494,7 @@  dragino2_mtdlayout=mtdparts=spi0.0:256k(u-boot)ro,16000k(firmware),64k(config)ro
 hiwifi_hc6361_mtdlayout=mtdparts=spi0.0:64k(u-boot)ro,64k(bdinfo)ro,1280k(kernel),14848k(rootfs),64k(backup)ro,64k(art)ro,16128k@0x20000(firmware)
 mr12_mtdlayout=mtdparts=spi0.0:256k(u-boot)ro,256k(u-boot-env)ro,13440k(rootfs),2304k(kernel),128k(art)ro,15744k@0x80000(firmware)
 mr16_mtdlayout=mtdparts=spi0.0:256k(u-boot)ro,256k(u-boot-env)ro,13440k(rootfs),2304k(kernel),128k(art)ro,15744k@0x80000(firmware)
+mr18_mtdlayout=mtdparts=ar934x-nfc:512k(nandloader)ro,8M(kernel),8M(recovery),113664k(ubi),128k@130944k(odm-caldata)ro
 pb92_mtdlayout=mtdparts=spi0.0:256k(u-boot)ro,64k(u-boot-env)ro,2752k(rootfs),896k(kernel),64k(nvram),64k(art)ro,3648k@0x50000(firmware)
 planex_mtdlayout=mtdparts=spi0.0:256k(u-boot)ro,64k(u-boot-env)ro,7744k(firmware),128k(art)ro
 ubntxm_mtdlayout=mtdparts=spi0.0:256k(u-boot)ro,64k(u-boot-env)ro,7552k(firmware),256k(cfg)ro,64k(EEPROM)ro
@@ -1975,6 +1976,37 @@  Image/Build/CyberTANLZMA/buildkernel=$(call MkuImageLzma,$(2),$(3) $(4))
 Image/Build/CyberTANLZMA=$(call Image/Build/CyberTAN,$(1),$(2),$(3),$(4),$(5))
 
 
+define Image/Build/MerakiNAND/initramfs
+	$(eval kernelsize=$(call mtdpartsize,kernel,$(4)))
+	$(call PatchKernel,$(2),$(3) $(4),"-initramfs")
+	-$(STAGING_DIR_HOST)/bin/mkmerakifw \
+		-B $(2) -s \
+		-i $(KDIR_TMP)/vmlinux-initramfs-$(2) \
+		-o $(KDIR_TMP)/vmlinux-initramfs-$(2)-patched
+	$(CP) $(KDIR_TMP)/vmlinux-initramfs-$(2)-patched $(BIN_DIR)/$(IMG_PREFIX)-$(2)-initramfs.bin
+endef
+
+define Image/Build/MerakiNAND/buildkernel
+	$(eval kernelsize=$(call mtdpartsize,kernel,$(4)))
+	$(call PatchKernel,$(2),$(3) $(4),"")
+	-$(STAGING_DIR_HOST)/bin/mkmerakifw \
+		-B $(2) -s \
+		-i $(KDIR_TMP)/vmlinux-$(2) \
+		-o $(KDIR_TMP)/vmlinux-$(2)-patched
+	$(CP) $(KDIR_TMP)/vmlinux-$(2)-patched $(BIN_DIR)/$(IMG_PREFIX)-$(2)-kernel.bin
+
+endef
+
+define Image/Build/MerakiNAND
+	$(eval fwsize=$(call mtdpartsize,firmware,$(4)))
+	$(eval rootsize=$(call mtdpartsize,rootfs,$(4)))
+	$(eval kernsize=$(call mtdpartsize,kernel,$(4)))
+	dd if=$(KDIR)/root.$(1) \
+			of=$(call imgname,$(1),$(2)-rootfs).bin bs=128k conv=sync; \
+	$(call Image/Build/SysupgradeNAND,$(2),squashfs,$(KDIR_TMP)/vmlinux-$(2)-patched)
+endef
+
+
 Image/Build/Netgear/initramfs=$(call MkuImageLzma/initramfs,$(2),$(3) $(4),,-M $(5))
 
 define Image/Build/Netgear/buildkernel
@@ -2413,6 +2445,8 @@  $(eval $(call MultiProfile,Madwifi,EAP7660D WP543))
 endif # ifeq ($(SUBTARGET),generic)
 
 ifeq ($(SUBTARGET),nand)
+$(eval $(call SingleProfile,MerakiNAND,64k,MR18,mr18,MR18,ttyS0,115200,$$(mr18_mtdlayout)))
+
 $(eval $(call SingleProfile,NetgearNAND,64k,WNDR3700V4,wndr3700v4,WNDR3700_V4,ttyS0,115200,$$(wndr4300_mtdlayout),0x33373033,WNDR3700v4,"",-H 29763948+128+128,wndr4300))
 $(eval $(call SingleProfile,NetgearNAND,64k,WNDR4300V1,wndr4300,WNDR4300,ttyS0,115200,$$(wndr4300_mtdlayout),0x33373033,WNDR4300,"",-H 29763948+0+128+128+2x2+3x3,wndr4300))
 $(eval $(call SingleProfile,NetgearNAND,64k,R6100,r6100,R6100,ttyS0,115200,$$(r6100_mtdlayout),0x36303030,R6100,"",-H 29764434+0+128+128+2x2+2x2,wndr4300))
diff --git a/target/linux/ar71xx/nand/config-default b/target/linux/ar71xx/nand/config-default
index 50b6dbe..05f52af 100644
--- a/target/linux/ar71xx/nand/config-default
+++ b/target/linux/ar71xx/nand/config-default
@@ -91,6 +91,7 @@  CONFIG_MTD_NAND=y
 CONFIG_MTD_NAND_AR934X=y
 CONFIG_MTD_NAND_AR934X_HW_ECC=y
 CONFIG_MTD_NAND_ECC=y
+CONFIG_MTD_NAND_ECC_BCH=y
 # CONFIG_MTD_REDBOOT_PARTS is not set
 # CONFIG_MTD_SM_COMMON is not set
 # CONFIG_MTD_SPLIT_SEAMA_FW is not set
diff --git a/target/linux/ar71xx/nand/profiles/meraki.mk b/target/linux/ar71xx/nand/profiles/meraki.mk
new file mode 100644
index 0000000..2c848c9
--- /dev/null
+++ b/target/linux/ar71xx/nand/profiles/meraki.mk
@@ -0,0 +1,17 @@ 
+#
+# Copyright (C) 2014-2015 Chris Blake (chrisrblake93@gmail.com)
+#
+# This is free software, licensed under the GNU General Public License v2.
+# See /LICENSE for more information.
+#
+
+define Profile/MR18
+	NAME:=Meraki MR18
+	PACKAGES:=kmod-spi-gpio kmod-ath9k
+endef
+
+define Profile/MR18/description
+	Package set optimized for the Cisco Meraki MR18 Access Point.
+endef
+
+$(eval $(call Profile,MR18))
diff --git a/target/linux/ar71xx/patches-4.1/817-MIPS-ath79-add-meraki-mr18-support.patch b/target/linux/ar71xx/patches-4.1/817-MIPS-ath79-add-meraki-mr18-support.patch
new file mode 100644
index 0000000..a36075a
--- /dev/null
+++ b/target/linux/ar71xx/patches-4.1/817-MIPS-ath79-add-meraki-mr18-support.patch
@@ -0,0 +1,41 @@ 
+--- a/arch/mips/ath79/Kconfig
++++ b/arch/mips/ath79/Kconfig
+@@ -824,6 +824,17 @@ config ATH79_MACH_MR16
+        select ATH79_DEV_M25P80
+        select ATH79_DEV_WMAC
+
++config ATH79_MACH_MR18
++	bool "Meraki MR18 board support"
++	select SOC_QCA955X
++	select ATH79_DEV_AP9X_PCI if PCI
++	select ATH79_DEV_ETH
++	select ATH79_DEV_GPIO_BUTTONS
++	select ATH79_DEV_LEDS_GPIO
++	select ATH79_DEV_NFC
++	select ATH79_DEV_WMAC
++	select LEDS_NU801
++
+ config ATH79_MACH_MR600
+        bool "OpenMesh MR600 board support"
+        select SOC_AR934X
+
+--- a/arch/mips/ath79/Makefile
++++ b/arch/mips/ath79/Makefile
+@@ -91,6 +91,7 @@ obj-$(CONFIG_ATH79_MACH_HORNET_UB)	+= ma
+ obj-$(CONFIG_ATH79_MACH_MC_MAC1200R)     += mach-mc-mac1200r.o
+ obj-$(CONFIG_ATH79_MACH_MR12)		+= mach-mr12.o
+ obj-$(CONFIG_ATH79_MACH_MR16)		+= mach-mr16.o
++obj-$(CONFIG_ATH79_MACH_MR18)		+= mach-mr18.o
+ obj-$(CONFIG_ATH79_MACH_MR1750)		+= mach-mr1750.o
+ obj-$(CONFIG_ATH79_MACH_MR600)		+= mach-mr600.o
+ obj-$(CONFIG_ATH79_MACH_MR900)		+= mach-mr900.o
+--- a/arch/mips/ath79/machtypes.h
++++ b/arch/mips/ath79/machtypes.h
+@@ -80,6 +80,7 @@ enum ath79_mach_type {
+ 	ATH79_MACH_HORNET_UB,		/* ALFA Networks Hornet-UB */
+ 	ATH79_MACH_MR12,		/* Cisco Meraki MR12 */
+ 	ATH79_MACH_MR16,		/* Cisco Meraki MR16 */
++	ATH79_MACH_MR18,		/* Cisco Meraki MR18 */
+ 	ATH79_MACH_MR1750,		/* OpenMesh MR1750 */
+ 	ATH79_MACH_MR600V2,		/* OpenMesh MR600v2 */
+ 	ATH79_MACH_MR600,		/* OpenMesh MR600 */