diff mbox

[OpenWrt-Devel] ar71xx: add support for Cisco's MR18

Message ID 1449879301-11979-1-git-send-email-chunkeey@googlemail.com
State Accepted
Headers show

Commit Message

Christian Lamparter Dec. 12, 2015, 12:15 a.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>
---
in before complains about "not using the latest method". ;-)
(yes, this is because of r47874 - I'm not spamming the ML
just because of this.)

Regards,
Christian
---
 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 +
 .../ar71xx/files/arch/mips/ath79/Kconfig.openwrt   |  11 +
 target/linux/ar71xx/files/arch/mips/ath79/Makefile |   1 +
 .../linux/ar71xx/files/arch/mips/ath79/mach-mr18.c | 297 +++++++++++++++++++++
 .../linux/ar71xx/files/arch/mips/ath79/machtypes.h |   1 +
 target/linux/ar71xx/image/Makefile                 |  21 ++
 target/linux/ar71xx/nand/config-default            |   1 +
 target/linux/ar71xx/nand/profiles/meraki.mk        |  17 ++
 16 files changed, 541 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

Comments

John Crispin Dec. 12, 2015, 6:28 a.m. UTC | #1
On 12/12/2015 01:15, Christian Lamparter wrote:
> 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>
> ---
> in before complains about "not using the latest method". ;-)
> (yes, this is because of r47874 - I'm not spamming the ML
> just because of this.)
> 
> Regards,
> Christian

looks like you had to rebase quite often without actually being the
cause ;) anyhow its now merged and i assume this patch obsoletes all
previous still open ones ?



> ---
>  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 +
>  .../ar71xx/files/arch/mips/ath79/Kconfig.openwrt   |  11 +
>  target/linux/ar71xx/files/arch/mips/ath79/Makefile |   1 +
>  .../linux/ar71xx/files/arch/mips/ath79/mach-mr18.c | 297 +++++++++++++++++++++
>  .../linux/ar71xx/files/arch/mips/ath79/machtypes.h |   1 +
>  target/linux/ar71xx/image/Makefile                 |  21 ++
>  target/linux/ar71xx/nand/config-default            |   1 +
>  target/linux/ar71xx/nand/profiles/meraki.mk        |  17 ++
>  16 files changed, 541 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
> 
> 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 33f8377..d4f226e 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 0e77e93..11c4341 100755
> --- a/target/linux/ar71xx/base-files/etc/board.d/02_network
> +++ b/target/linux/ar71xx/base-files/etc/board.d/02_network
> @@ -313,6 +313,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 5b997e0..0e1df10 100644
> --- a/target/linux/ar71xx/base-files/etc/diag.sh
> +++ b/target/linux/ar71xx/base-files/etc/diag.sh
> @@ -152,6 +152,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 72f7858..f41c3de 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
> @@ -54,6 +54,14 @@ board=$(ar71xx_board_name)
>  case "$FIRMWARE" in
>  "soc_wmac.eeprom")
>  	case $board in
> +	mr18)
> +		if [ -n "$(nand_find_volume ubi0 caldata)" ]; then
> +			ath9k_ubi_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)
> @@ -68,6 +76,14 @@ case "$FIRMWARE" in
>  
>  "pci_wmac0.eeprom")
>  	case $board in
> +	mr18)
> +		if [ -n "$(nand_find_volume ubi0 caldata)" ]; then
> +			ath9k_ubi_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
> @@ -78,4 +94,20 @@ case "$FIRMWARE" in
>  		;;
>  	esac
>  	;;
> +
> +"pci_wmac1.eeprom")
> +	case $board in
> +	mr18)
> +		if [ -n "$(nand_find_volume ubi0 caldata)" ]; then
> +			ath9k_ubi_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 a112523..54e6166 100755
> --- a/target/linux/ar71xx/base-files/lib/ar71xx.sh
> +++ b/target/linux/ar71xx/base-files/lib/ar71xx.sh
> @@ -551,6 +551,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 5ec4499..bb64ef8 100755
> --- a/target/linux/ar71xx/base-files/lib/upgrade/platform.sh
> +++ b/target/linux/ar71xx/base-files/lib/upgrade/platform.sh
> @@ -434,6 +434,10 @@ platform_check_image() {
>  		}
>  		return 0
>  		;;
> +	mr18)
> +		merakinand_do_platform_check $board $1
> +		return $?;
> +		;;
>  	nbg6716 | \
>  	r6100 | \
>  	wndr3700v4 | \
> @@ -494,6 +498,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 e21581d..0e8b4a4 100644
> --- a/target/linux/ar71xx/config-4.1
> +++ b/target/linux/ar71xx/config-4.1
> @@ -91,6 +91,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/Kconfig.openwrt b/target/linux/ar71xx/files/arch/mips/ath79/Kconfig.openwrt
> index 43ffa70..2b62d89 100644
> --- a/target/linux/ar71xx/files/arch/mips/ath79/Kconfig.openwrt
> +++ b/target/linux/ar71xx/files/arch/mips/ath79/Kconfig.openwrt
> @@ -853,6 +853,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
> diff --git a/target/linux/ar71xx/files/arch/mips/ath79/Makefile b/target/linux/ar71xx/files/arch/mips/ath79/Makefile
> index b001876..f9c5b8f 100644
> --- a/target/linux/ar71xx/files/arch/mips/ath79/Makefile
> +++ b/target/linux/ar71xx/files/arch/mips/ath79/Makefile
> @@ -98,6 +98,7 @@ obj-$(CONFIG_ATH79_MACH_HORNET_UB)	+= mach-hornet-ub.o
>  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
> 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/files/arch/mips/ath79/machtypes.h b/target/linux/ar71xx/files/arch/mips/ath79/machtypes.h
> index 550d927..2ea772d 100644
> --- a/target/linux/ar71xx/files/arch/mips/ath79/machtypes.h
> +++ b/target/linux/ar71xx/files/arch/mips/ath79/machtypes.h
> @@ -87,6 +87,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 */
> diff --git a/target/linux/ar71xx/image/Makefile b/target/linux/ar71xx/image/Makefile
> index 78be345..e197ed3 100644
> --- a/target/linux/ar71xx/image/Makefile
> +++ b/target/linux/ar71xx/image/Makefile
> @@ -1986,6 +1986,14 @@ Image/Build/CyberTANLZMA/buildkernel=$(call MkuImageLzma,$(2),$(3) $(4))
>  Image/Build/CyberTANLZMA=$(call Image/Build/CyberTAN,$(1),$(2),$(3),$(4),$(5))
>  
>  
> +define Build/MerakiNAND
> +	-$(STAGING_DIR_HOST)/bin/mkmerakifw \
> +		-B $(BOARDNAME) -s \
> +		-i $@ \
> +		-o $@.new
> +	@mv $@.new $@
> +endef
> +
>  Image/Build/Netgear/initramfs=$(call MkuImageLzma/initramfs,$(2),$(3) $(4),,-M $(5))
>  
>  define Image/Build/Netgear/buildkernel
> @@ -2426,6 +2434,19 @@ $(eval $(call MultiProfile,Madwifi,EAP7660D WP543))
>  endif # ifeq ($(SUBTARGET),generic)
>  
>  ifeq ($(SUBTARGET),nand)
> +
> +define Device/mr18
> +  BOARDNAME = MR18
> +  BLOCKSIZE := 64k
> +  CONSOLE = ttyS0,115200
> +  MTDPARTS = ar934x-nfc:512k(nandloader)ro,8M(kernel),8M(recovery),113664k(ubi),128k@130944k(odm-caldata)ro
> +  IMAGES := sysupgrade.tar
> +  KERNEL := kernel-bin | patch-cmdline | MerakiNAND
> +  KERNEL_INITRAMFS := kernel-bin | patch-cmdline | MerakiNAND
> +  IMAGE/sysupgrade.tar := sysupgrade-nand
> +endef
> +TARGET_DEVICES += mr18
> +
>  $(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))
>
Christian Lamparter Dec. 12, 2015, 11:32 a.m. UTC | #2
On Saturday, December 12, 2015 07:28:35 AM John Crispin wrote:
> 
> On 12/12/2015 01:15, Christian Lamparter wrote:
> > 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>
> > ---
> > in before complains about "not using the latest method". ;-)
> > (yes, this is because of r47874 - I'm not spamming the ML
> > just because of this.)
> > 
> > Regards,
> > Christian
> 
> looks like you had to rebase quite often without actually being the
> cause ;) anyhow its now merged and i assume this patch obsoletes all
> previous still open ones ?
> 
Oh no, sorry. I didn't include the other parts in that. I just updated
this patch (my bad for dropping the subject [PATCH v7.1 4/5]). nbd just
fixed the break. but in order to get the MR18 to work, we will need the
other patches as well (please).

Regards,
Christian
John Crispin Dec. 12, 2015, 2:03 p.m. UTC | #3
On 12/12/2015 12:32, Christian Lamparter wrote:
> On Saturday, December 12, 2015 07:28:35 AM John Crispin wrote:
>>
>> On 12/12/2015 01:15, Christian Lamparter wrote:
>>> 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>
>>> ---
>>> in before complains about "not using the latest method". ;-)
>>> (yes, this is because of r47874 - I'm not spamming the ML
>>> just because of this.)
>>>
>>> Regards,
>>> Christian
>>
>> looks like you had to rebase quite often without actually being the
>> cause ;) anyhow its now merged and i assume this patch obsoletes all
>> previous still open ones ?
>>
> Oh no, sorry. I didn't include the other parts in that. I just updated
> this patch (my bad for dropping the subject [PATCH v7.1 4/5]). nbd just
> fixed the break. but in order to get the MR18 to work, we will need the
> other patches as well (please).
> 
> Regards,
> Christian
> 

which ones are missing ? i merged a total of 5 now.
Christian Lamparter Dec. 12, 2015, 3:06 p.m. UTC | #4
On Saturday, December 12, 2015 03:03:55 PM John Crispin wrote:
> 
> On 12/12/2015 12:32, Christian Lamparter wrote:
> > On Saturday, December 12, 2015 07:28:35 AM John Crispin wrote:
> >>
> >> On 12/12/2015 01:15, Christian Lamparter wrote:
> >>> 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>
> >>> ---
> >>> in before complains about "not using the latest method". ;-)
> >>> (yes, this is because of r47874 - I'm not spamming the ML
> >>> just because of this.)
> >>>
> >>> Regards,
> >>> Christian
> >>
> >> looks like you had to rebase quite often without actually being the
> >> cause ;) anyhow its now merged and i assume this patch obsoletes all
> >> previous still open ones ?
> >>
> > Oh no, sorry. I didn't include the other parts in that. I just updated
> > this patch (my bad for dropping the subject [PATCH v7.1 4/5]). nbd just
> > fixed the break. but in order to get the MR18 to work, we will need the
> > other patches as well (please).
> > 
> > Regards,
> > Christian
> > 
> 
> which ones are missing ? i merged a total of 5 now.
here they are (the missing ones):

"ar71xx: refactor 10-ath9k-eeprom"
<https://patchwork.ozlabs.org/patch/555847/>

"ar71xx: scan nand ubi partition for ath9k eeprom files"
<https://patchwork.ozlabs.org/patch/555848/>

"base-files: cleanup mtd_get_mac_binary hexdump"
<https://patchwork.ozlabs.org/patch/555850/>

Note:
I removed the LED and firmware-tool patches from the
series as they have been merged. So we have a total of
9 patches. In case you wonder why not 8: Felix has 
merged one too (the QCA955x register definitions - r47882). 

Regards,
Christian
Christian Lamparter Dec. 17, 2015, 4:47 p.m. UTC | #5
Hello,

On Saturday, December 12, 2015 04:06:51 PM Christian Lamparter wrote:
> On Saturday, December 12, 2015 03:03:55 PM John Crispin wrote:

> > which ones are missing ? i merged a total of 5 now.

> here they are (the missing ones):
> 
> "ar71xx: refactor 10-ath9k-eeprom"
> <https://patchwork.ozlabs.org/patch/555847/>
> 
> "ar71xx: scan nand ubi partition for ath9k eeprom files"
> <https://patchwork.ozlabs.org/patch/555848/>
> 
> "base-files: cleanup mtd_get_mac_binary hexdump"
> <https://patchwork.ozlabs.org/patch/555850/>
> 

We found another issue in one of the three patches!
I'll make updates and post "v8" patches shortly. 
(I won't spam the ML with patches that have been 
accepted already)

Regards,
Christian
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 33f8377..d4f226e 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 0e77e93..11c4341 100755
--- a/target/linux/ar71xx/base-files/etc/board.d/02_network
+++ b/target/linux/ar71xx/base-files/etc/board.d/02_network
@@ -313,6 +313,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 5b997e0..0e1df10 100644
--- a/target/linux/ar71xx/base-files/etc/diag.sh
+++ b/target/linux/ar71xx/base-files/etc/diag.sh
@@ -152,6 +152,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 72f7858..f41c3de 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
@@ -54,6 +54,14 @@  board=$(ar71xx_board_name)
 case "$FIRMWARE" in
 "soc_wmac.eeprom")
 	case $board in
+	mr18)
+		if [ -n "$(nand_find_volume ubi0 caldata)" ]; then
+			ath9k_ubi_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)
@@ -68,6 +76,14 @@  case "$FIRMWARE" in
 
 "pci_wmac0.eeprom")
 	case $board in
+	mr18)
+		if [ -n "$(nand_find_volume ubi0 caldata)" ]; then
+			ath9k_ubi_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
@@ -78,4 +94,20 @@  case "$FIRMWARE" in
 		;;
 	esac
 	;;
+
+"pci_wmac1.eeprom")
+	case $board in
+	mr18)
+		if [ -n "$(nand_find_volume ubi0 caldata)" ]; then
+			ath9k_ubi_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 a112523..54e6166 100755
--- a/target/linux/ar71xx/base-files/lib/ar71xx.sh
+++ b/target/linux/ar71xx/base-files/lib/ar71xx.sh
@@ -551,6 +551,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 5ec4499..bb64ef8 100755
--- a/target/linux/ar71xx/base-files/lib/upgrade/platform.sh
+++ b/target/linux/ar71xx/base-files/lib/upgrade/platform.sh
@@ -434,6 +434,10 @@  platform_check_image() {
 		}
 		return 0
 		;;
+	mr18)
+		merakinand_do_platform_check $board $1
+		return $?;
+		;;
 	nbg6716 | \
 	r6100 | \
 	wndr3700v4 | \
@@ -494,6 +498,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 e21581d..0e8b4a4 100644
--- a/target/linux/ar71xx/config-4.1
+++ b/target/linux/ar71xx/config-4.1
@@ -91,6 +91,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/Kconfig.openwrt b/target/linux/ar71xx/files/arch/mips/ath79/Kconfig.openwrt
index 43ffa70..2b62d89 100644
--- a/target/linux/ar71xx/files/arch/mips/ath79/Kconfig.openwrt
+++ b/target/linux/ar71xx/files/arch/mips/ath79/Kconfig.openwrt
@@ -853,6 +853,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
diff --git a/target/linux/ar71xx/files/arch/mips/ath79/Makefile b/target/linux/ar71xx/files/arch/mips/ath79/Makefile
index b001876..f9c5b8f 100644
--- a/target/linux/ar71xx/files/arch/mips/ath79/Makefile
+++ b/target/linux/ar71xx/files/arch/mips/ath79/Makefile
@@ -98,6 +98,7 @@  obj-$(CONFIG_ATH79_MACH_HORNET_UB)	+= mach-hornet-ub.o
 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
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/files/arch/mips/ath79/machtypes.h b/target/linux/ar71xx/files/arch/mips/ath79/machtypes.h
index 550d927..2ea772d 100644
--- a/target/linux/ar71xx/files/arch/mips/ath79/machtypes.h
+++ b/target/linux/ar71xx/files/arch/mips/ath79/machtypes.h
@@ -87,6 +87,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 */
diff --git a/target/linux/ar71xx/image/Makefile b/target/linux/ar71xx/image/Makefile
index 78be345..e197ed3 100644
--- a/target/linux/ar71xx/image/Makefile
+++ b/target/linux/ar71xx/image/Makefile
@@ -1986,6 +1986,14 @@  Image/Build/CyberTANLZMA/buildkernel=$(call MkuImageLzma,$(2),$(3) $(4))
 Image/Build/CyberTANLZMA=$(call Image/Build/CyberTAN,$(1),$(2),$(3),$(4),$(5))
 
 
+define Build/MerakiNAND
+	-$(STAGING_DIR_HOST)/bin/mkmerakifw \
+		-B $(BOARDNAME) -s \
+		-i $@ \
+		-o $@.new
+	@mv $@.new $@
+endef
+
 Image/Build/Netgear/initramfs=$(call MkuImageLzma/initramfs,$(2),$(3) $(4),,-M $(5))
 
 define Image/Build/Netgear/buildkernel
@@ -2426,6 +2434,19 @@  $(eval $(call MultiProfile,Madwifi,EAP7660D WP543))
 endif # ifeq ($(SUBTARGET),generic)
 
 ifeq ($(SUBTARGET),nand)
+
+define Device/mr18
+  BOARDNAME = MR18
+  BLOCKSIZE := 64k
+  CONSOLE = ttyS0,115200
+  MTDPARTS = ar934x-nfc:512k(nandloader)ro,8M(kernel),8M(recovery),113664k(ubi),128k@130944k(odm-caldata)ro
+  IMAGES := sysupgrade.tar
+  KERNEL := kernel-bin | patch-cmdline | MerakiNAND
+  KERNEL_INITRAMFS := kernel-bin | patch-cmdline | MerakiNAND
+  IMAGE/sysupgrade.tar := sysupgrade-nand
+endef
+TARGET_DEVICES += mr18
+
 $(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))