[Feature]add MT2731_MP2_MR2_SVN388 baseline version

Change-Id: Ief04314834b31e27effab435d3ca8ba33b499059
diff --git a/src/devtools/datool/.gitignore b/src/devtools/datool/.gitignore
new file mode 100644
index 0000000..0d20b64
--- /dev/null
+++ b/src/devtools/datool/.gitignore
@@ -0,0 +1 @@
+*.pyc
diff --git a/src/devtools/datool/README b/src/devtools/datool/README
new file mode 100644
index 0000000..960be87
--- /dev/null
+++ b/src/devtools/datool/README
@@ -0,0 +1,78 @@
+== fbtool introduction ==
+	fbtool.py is a tool program running at host pc.
+	It will send preloader and lk to platform via usb in order to support fastboot command.
+
+
+1 environment preparation
+	1.1 Need to install Python 2.7 or Python 3 (or higher version) firstly
+	1.2 Local pySerial library is preferred.
+	  If there is no pySerial folder or fail to import local pySerial,
+	  fbtool.py will find pySerial library in the host PC.
+	  In this case, please ensure host PC has installed pySerial library (2.6 or higher version):
+	  - Download pySerial from https://pypi.python.org/pypi/pyserial . e.g. pyserial-3.2.1.tar.gz
+	    Exceptionally, please install *pySerial 3.0.1* (https://pypi.python.org/pypi/pyserial/3.0.1) on Windows XP.
+	  - Unzip and execute 'python setup.py install' to install it
+
+3 usage
+	fbtool.py [options]
+	Options:
+		-h, --help				show this help message and exit
+		-f FILE, --file=FILE	read config file, default config file: dl_addr.ini
+		-d, --debug				enable debug log
+		-m, --meid				get meid
+
+	auth_file:	${PROJECT}/fbtool-da/auth_sv5.auth
+	preloader:	${PROJECT}/fbtool-da/fbtool-da-pl.bin
+	lk:		${PROJECT}/fbtool-da/fbtool-da-lk.bin
+
+3.1 usage with normal chip
+	command : 	command: python fbtool.py (or python fbtool.py -f config.ini)
+
+	Add preloader file path and preloader load address to dl_addr.ini or config.ini "da1_path" key and "da1_addr" key
+	Add preloader load address to dl_addr.ini or config.ini 'da1_addr' key
+	Add lk file path and lk load address  to dl_addr.ini or config.ini "da2_path" key and "da2_addr" key
+
+	preloader support the following files:
+		${PROJECT}/fbtool-da/fbtool-da-pl.bin
+		${OUT}/.../bin/preloader_${PROJECT}_NO_GFH.bin
+		${OUT}/.../preloader_${PROJECT}.bin
+		${OUT}/.../preloader.img
+	We recommend to use pre-built binary fbtool-da-pl.bin in case customer disable the download support of preloader.
+
+	lk support only ${PROJECT}/fbtool-da/fbtool-da-lk.bin
+	fbtool-da-lk.bin is generated by ./tool/gen-dalk-from-lk.py
+	usage: python ./tool/gen-dalk-from-lk.py ${CHIP_ID} ${OUT}/.../lk-no-mtk-header.bin fbtool-da-lk.bin
+
+3.2 usage with secure chip (enable DAA)
+	command: python fbtool.py (or python fbtool.py -f config.ini)
+	Add authorization file path to dl_addr.ini or config.ini "auth_path" key
+
+	Generate an auth_file which contains the corresponding public key of private-key.pem (private-key.pem is used to sign preloader)
+	(just like SP Flash tool).
+
+	Sign preloader with ./tool/signfile-for-brom.sh
+	usage: signfile-for-brom.sh private-key.pem fbtool-da-pl.bin
+		private-key.pem is generated from openssl command.
+		run the script and it will output a file : fbtool-da-pl.bin.sign.
+		(fbtool-da-pl.bin is a pre-built preloader_${PROJECT}_NO_GFH.bin.
+		 DO NOT use preloader.bin or preloader.img to generate the sign file.)
+
+	With DAA enabled, the fbtool will automatically use *.sign as the signature of pl & lk.
+	Thus, we have to
+		put fbtool-da-pl.bin and fbtool-da-pl.bin.sign in the same folder.
+		put fbtool-da-lk.bin and fbtool-da-lk.bin.sign in the same folder.
+
+3.3 usage with secure preloader (enable preloader secure DA verify)
+	Sign lk with ./tool/signfile-for-pl.sh
+
+	usage: signfile-for-pl.sh private-key.pem fbtool-da-lk.bin
+		the private-key.pem is generated from openssl rsa-1024.
+		and the fbtool-da-lk.bin.sign is generated.
+
+3.4 usage with single lk
+	command: python fbtool.py (or python fbtool.py -f config.ini)
+	Just add single lk path and load address to dl_addr.ini or config.ini "da1_path" key and "da1_addr" key
+
+3.5 send certificate file
+	command: python fbtool.py (or python fbtool.py -f config.ini)
+	Just add certificate file path to dl_addr.ini or config.ini "cert_path" key
diff --git a/src/devtools/datool/fbtool.py b/src/devtools/datool/fbtool.py
new file mode 100644
index 0000000..60caa0d
--- /dev/null
+++ b/src/devtools/datool/fbtool.py
@@ -0,0 +1,531 @@
+#!/usr/bin/env python

+# -*- coding: utf-8 -*

+try:

+    import pyserial as serial

+    import pyserial.tools.list_ports as ser_tools

+except ImportError:

+    import serial

+    import serial.tools.list_ports as ser_tools

+import sys, time, struct, logging

+from optparse import OptionParser

+try:

+    # python2

+    import ConfigParser as configparser

+except ImportError:

+    # python3

+    import configparser

+

+class Fbtool:

+    def __init__(self, config_file, meid=False):

+        self.TGT_CFG_SBC_EN = 0x00000001

+        self.TGT_CFG_SLA_EN = 0x00000002

+        self.TGT_CFG_DAA_EN = 0x00000004

+        self.E_ERROR = 0x1000

+        self.__ser = None

+        self.__connect_type = 'UNKNOWN'

+        self.__meid = meid

+        cfg = configparser.ConfigParser()

+        cfg.read(config_file)

+        self.__da1_path = cfg.get('DA1', 'da1_path')

+        self.__da1_addr = int(cfg.get('DA1', 'da1_addr'), 16)

+        self.__da1_jump_64 = int(cfg.get('DA1', 'da1_jump_64'), 16)

+        self.__da2_path = cfg.get('DA2', 'da2_path')

+        self.__da2_addr = int(cfg.get('DA2', 'da2_addr'), 16)

+        self.__auth_path = cfg.get('Auth', 'auth_path')

+        self.__cert_path = cfg.get('Cert', 'cert_path')

+        self.__fall_thru_to_fb = int(cfg.get('FALL_THRU_TO_FB', 'fall_thru_to_fb'))

+        logging.debug('da1_path: %s' %(self.__da1_path))

+        logging.debug('da1_addr: 0x%x' %(self.__da1_addr))

+        logging.debug('da1_jump_64: 0x%x' %(self.__da1_jump_64))

+        logging.debug('da2_path: %s' %(self.__da2_path))

+        logging.debug('ad2_addr: 0x%x' %(self.__da2_addr))

+        logging.debug('auth_path: %s' %(self.__auth_path))

+        logging.debug('cert_path: %s' %(self.__cert_path))

+

+    def __del__(self):

+        # compatible with pySerial 2.6.

+        # isOpen() is deprecated since version 3.0, 3.0 uses is_open

+        if self.__ser and self.__ser.isOpen():

+            self.__ser.close()

+

+    def __match_usb_br(self, vid, pid):

+        if vid == 0x0e8d and pid == 0x0003:

+            self.__connect_type = 'BROM'

+            return True

+        return False

+

+    def __match_usb_pl(self, vid, pid):

+        if ((vid == 0x0e8d and pid == 0x2000) or (vid == 0x0e8d and pid == 0x3000)):

+            self.__connect_type = 'PRELOADER'

+            return True

+        return False

+

+    def __match_usb_auto(self, vid, pid):

+        if self.__match_usb_br(vid, pid):

+            return True

+        if self.__match_usb_pl(vid, pid):

+            return True

+        return False

+

+    def __open_usb_device(self, match_usb_func, comport, vid, pid):

+        if match_usb_func(vid, pid):

+            time.sleep(0.1)

+            try:

+                self.__ser = serial.Serial(comport, 115200)

+            except serial.SerialException as e:

+                logging.debug('%s, retry...' %(str(e)))

+            else:

+                logging.info('Got %s' %(comport))

+                return True

+        return False

+

+    def __find_usb_device(self, match_usb_func):

+        while True:

+            ports = ser_tools.comports()

+            if serial.VERSION < '3':

+                ports_list = list(ports)

+                for port in ports_list:

+                    if 'USB' in port[2]:

+                        if sys.platform.startswith('win'):

+                            idx = port[2].index('VID_')+4

+                            vid = int(port[2][idx : idx + 4], 16)

+                            idx = port[2].index('PID_')+4

+                            pid = int(port[2][idx : idx + 4], 16)

+                        elif sys.platform.startswith('linux') or sys.platform.startswith('cygwin'):

+                            idx = port[2].index('VID:PID=') + 8

+                            vid = int(port[2][idx : idx + 4], 16)

+                            pid = int(port[2][idx + 5 : idx + 13], 16)

+                        elif sys.platform.startswith('darwin'):

+                            raise EnvironmentError('Unsupport macOS')

+                        else:

+                            raise EnvironmentError('Unsupported platform')

+                        if self.__open_usb_device(match_usb_func, port[0], vid, pid):

+                            return

+            else:

+                for port in ports:

+                    if self.__open_usb_device(match_usb_func, port.device, port.vid, port.pid):

+                        return

+

+    def __read8(self):

+        return struct.unpack('!B', self.__ser.read())[0]

+

+    def __read16(self):

+        return struct.unpack('!H', self.__ser.read(2))[0]

+

+    def __read32(self):

+        return struct.unpack('!I', self.__ser.read(4))[0]

+

+    def __write8(self, data, echo):

+        self.__ser.write(struct.pack('!B', data))

+        if echo:

+            return struct.unpack('!B', self.__ser.read())[0] == data

+        return True

+

+    def __write16(self, data, echo):

+        self.__ser.write(struct.pack('!H', data))

+        if echo:

+            return struct.unpack('!H', self.__ser.read(2))[0] == data

+        return True

+

+    def __write32(self, data, echo):

+        self.__ser.write(struct.pack('!I', data))

+        if echo:

+            return struct.unpack('!I', self.__ser.read(4))[0] == data

+        return True

+

+    def __start_cmd(self):

+        cmd = (0xa0, 0x0a, 0x50, 0x05)

+        echo_cmd = (0x5f, 0xf5, 0xaf, 0xfa)

+

+        i = 0

+        while (i < len(cmd)):

+            self.__write8(cmd[i], False)

+            if self.__read8() != echo_cmd[i]:

+                i = 0

+                self.__ser.flushInput()

+            else:

+                i = i + 1

+        time.sleep(0.1)

+        # self.__ser.flush()

+        self.__ser.flushInput()

+        self.__ser.flushOutput()

+        if self.__connect_type == 'BROM':

+            logging.info('Connect brom')

+        elif self.__connect_type == 'PRELOADER':

+            logging.info('Connect preloader')

+

+    def __load_binary(self, path):

+        logging.info("Loading file: %s" %path)

+        with open(path, 'rb') as f:

+            return f.read()

+

+    def __checksum(self, data, length):

+        checksum = 0

+        for i in range(0, length, 2):

+            checksum ^= struct.unpack('<H', data[i:i+2])[0]

+        checksum &= 0xFFFF

+        return checksum

+

+    def __get_target_config(self):

+        if not self.__write8(0xd8, True):

+            return -1, None

+        cfg = self.__read32()

+        status = self.__read16()

+

+        if status >= self.E_ERROR:

+            return status, None

+        return 0, cfg

+

+    def __send_auth(self, cfg):

+        if self.TGT_CFG_DAA_EN & cfg == 0:

+            return 0

+        else:

+            if self.TGT_CFG_SBC_EN & cfg == 0:

+                logging.error('daa=1, sbc=0')

+                return -2

+            if self.__auth_path == '':

+                logging.error('no auth file')

+                return -3

+            auth = self.__load_binary(self.__auth_path)

+            auth_len = len(auth)

+            logging.debug("auth file size: 0x%x" %(auth_len))

+

+            if not self.__write8(0xe2, True):

+                return -4

+            if not self.__write32(len(auth), True):

+                return -5

+

+            status = self.__read16()

+            if status >= self.E_ERROR:

+                return status

+            self.__ser.write(auth)

+

+            # compare checksum

+            if self.__read16() != self.__checksum(auth, auth_len):

+                return -6

+

+            status = self.__read16()

+            if status >= self.E_ERROR:

+                return status

+            return 0

+

+    def __strip_pl_hdr(self, pl, pl_len):

+        # EMMC_HEADER_V1

+        identifier, ver, dev_rw_unit = struct.unpack('12s2I', pl[:20])

+        # GFH_FILE_INFO_V1

+        gfh = pl[:56]

+        gfh_offset = 0

+

+        if identifier.strip(b'\0') == b'EMMC_BOOT' and ver == 1:

+            logging.debug('emmc_hdr: identifier:%s, ver:0x%08x, dev_rw_unit:0x%08x' %(identifier, ver, dev_rw_unit))

+            # BR_Layout_v1 size: 40

+            if dev_rw_unit + 40 > pl_len:

+                logging.error('EMMC HDR error. dev_rw_unit=0x%x, brlyt_size=0x%x, pl_len=0x%x'

+                    %(dev_rw_unit, brlyt_size, pl_len))

+                return False, None, None

+

+            brlyt_identifier, brlyt_ver = struct.unpack('8sI', pl[dev_rw_unit:dev_rw_unit + 12])

+            logging.debug('brlyt_identifier: %s, brlyt_ver=0x%x' %(brlyt_identifier, brlyt_ver))

+            if brlyt_identifier.strip(b'\0') != b'BRLYT' or brlyt_ver != 1:

+                logging.error('BRLYT error. ver=0x%x, identifier=%s' %(brlyt_ver, brlyt_identifier))

+                return False, None, None

+            # BL_Descriptor

+            bl_begin_dev_addr = struct.unpack('I', pl[dev_rw_unit + 28 : dev_rw_unit + 32])[0]

+            if bl_begin_dev_addr + 56 > pl_len:

+                logging.error('BRLYT error. bl_begin_dev_addr=0x%x' %bl_begin_dev_addr)

+                return False, None, None

+            # GFH_FILE_INFO_v1

+            gfh = pl[bl_begin_dev_addr:bl_begin_dev_addr + 56]

+            gfh_offset = bl_begin_dev_addr

+

+        gfh_struct =struct.unpack('I2H12sIH2B7I', gfh)

+

+        gfh_magic_ver = gfh_struct[0]

+        gfh_type = gfh_struct[2]

+        gfh_identifier = gfh_struct[3]

+        gfh_file_len = gfh_struct[9]

+        gfh_jump_offset = gfh_struct[13]

+        gfh_sig_len = gfh_struct[12]

+        if (gfh_magic_ver & 0x00FFFFFF) == 0x004D4D4D and gfh_type == 0 and gfh_identifier.strip(b'\0') == b'FILE_INFO':

+            if gfh_file_len < gfh_jump_offset + gfh_sig_len:

+                logging.error('GFH error. pl_len=0x%x, file_len=0x%x, jump_offset=0x%x, sig_len=0x%x'

+                                %(pl_len, gfh_file_len, gfh_jump_offset, gfh_sig_len))

+                return False, None, None

+            logging.debug('gfh: magic_ver: 0x%08x' %gfh_struct[0])

+            logging.debug('gfh: size: 0x%04x' %gfh_struct[1])

+            logging.debug('gfh: type: 0x%04x' %gfh_struct[2])

+            logging.debug('gfh: identifier: %s' %gfh_struct[3])

+            logging.debug('gfh: file_ver: 0x%08x' %gfh_struct[4])

+            logging.debug('gfh: file_type: 0x%04x' %gfh_struct[5])

+            logging.debug('gfh: flash_dev: 0x%02x' %gfh_struct[6])

+            logging.debug('gfh: sig_type: 0x%02x' %gfh_struct[7])

+            logging.debug('gfh: load_addr: 0x%08x' %gfh_struct[8])

+            logging.debug('gfh: file_len: 0x%08x' %gfh_struct[9])

+            logging.debug('gfh: max_size: 0x%08x' %gfh_struct[10])

+            logging.debug('gfh: content_offset: 0x%08x' %gfh_struct[11])

+            logging.debug('gfh: sig_len: 0x%08x' %gfh_struct[12])

+            logging.debug('gfh: jump_offset: 0x%08x' %gfh_struct[13])

+            logging.debug('gfh: attr: 0x%08x' %gfh_struct[14])

+            strip_pl_len = gfh_file_len - gfh_jump_offset - gfh_sig_len

+            start = gfh_offset + gfh_jump_offset

+            strip_pl = pl[start:start + strip_pl_len]

+            return (True, strip_pl, strip_pl_len)

+        else:

+            return (True, pl, pl_len)

+

+    def __send_da(self, addr, da, da_len, sig, sig_len):

+        if not self.__write8(0xd7, True):

+            return -1

+        if not self.__write32(addr, True):

+            return -2

+        logging.debug('len: 0x%x' %(da_len + sig_len))

+        if not self.__write32(da_len + sig_len, True):

+            return -3

+        if not self.__write32(sig_len, True):

+            return -4

+        status = self.__read16()

+        if status >= self.E_ERROR:

+            return status

+

+        if da_len > 0:

+            self.__ser.write(da)

+        if sig_len > 0:

+            self.__ser.write(sig)

+

+        checksum = self.__checksum(da, da_len) ^ self.__checksum(sig, sig_len)

+        data = self.__read16()

+        logging.debug('checksum: 0x%x - 0x%x' %(checksum, data))

+        if data != checksum:

+            return -5

+        status = self.__read16()

+        if status >= self.E_ERROR:

+            return status

+

+        return 0

+

+    def __jump_da(self, addr):

+        if not self.__write8(0xd5, True):

+            return -1

+        if not self.__write32(addr, True):

+            return -2

+        status = self.__read16()

+        if status >= self.E_ERROR:

+            return status

+        return 0

+

+    def __jump_da_ex(self, addr):

+        if not self.__write8(0xde, True):

+            return -1

+        if not self.__write32(addr, True):

+            return -2

+        if not self.__write8(0x1, True):

+            return -3

+        status = self.__read16()

+        if status >= self.E_ERROR:

+            return status

+        if not self.__write8(0x64, True):

+            return -4

+        status = self.__read16()

+        if status >= self.E_ERROR:

+            return status

+        return 0

+

+    def __get_meid(self):

+        if not self.__write8(0xe1, True):

+            return -1

+        len = self.__read32()

+        logging.debug('meid len: 0x%x' %len)

+        data = struct.unpack('!'+str(len)+'B', self.__ser.read(len))

+        meid_str = lambda s: ''.join(map(lambda c: '%02x' %c, s))

+        status = self.__read16()

+        if status >= self.E_ERROR:

+            return status

+        logging.info(meid_str(data));

+        return 0

+

+    def __send_cert(self, data, len):

+        if not self.__write8(0xe0, True):

+            return -1

+        if not self.__write32(len, True):

+            return -2

+        status = self.__read16()

+        if status >= self.E_ERROR:

+            return status

+        self.__ser.write(data)

+        checksum = self.__checksum(data, len)

+        data = self.__read16()

+        if checksum != data:

+            logging.error("checksum: 0x%x - 0x%x" %(checksum, data))

+            return -3

+        status = self.__read16()

+        if status >= self.E_ERROR:

+            return status

+        return 0

+

+    def __reboot_platform(self):

+        if not self.__write8(0xd4, True):

+            return -1

+        if not self.__write32(0x10007000, True):

+            return -2

+        if not self.__write32(0x1, True):

+            return -3

+        status = self.__read16()

+        if status >= self.E_ERROR:

+            return status

+        if not self.__write32(0x22000004, True):

+            return -4

+        status = self.__read16()

+        if status >= self.E_ERROR:

+            return status

+        if not self.__write8(0xd4, True):

+            return -5

+        if not self.__write32(0x10007014, True):

+            return -6

+        if not self.__write32(0x1, True):

+            return -7

+        status = self.__read16()

+        if status >= self.E_ERROR:

+            return status

+        if not self.__write32(0x1209, True):

+            return -8

+        return 0

+

+    def __fall_through_to_fastboot(self):

+        if not self.__write8(0xd9, True):

+            return -1

+        status = self.__read16()

+        if status >= self.E_ERROR:

+            return status

+        return 0

+

+    def start(self):

+        self.__find_usb_device(self.__match_usb_auto)

+        self.__start_cmd()

+

+        # get meid

+        if self.__meid:

+            status = self.__get_meid()

+            if status != 0:

+                logging.error('get meid (%d)' %status)

+                return -1

+            return 0

+

+        # send cert

+        if self.__cert_path != '':

+            cert = self.__load_binary(self.__cert_path)

+            cert_len = len(cert)

+            logging.debug('cert_len: 0x%x' %cert_len)

+            status = self.__send_cert(cert, cert_len)

+            if status != 0:

+                logging.error('send cert (%d)' %status)

+                return -1

+            logging.info('Reboot...')

+            status = self.__reboot_platform()

+            if status != 0:

+                logging.error('reboot platform (%d)' %status)

+                return -1

+            return 0

+

+        if self.__connect_type == 'BROM':

+            status, cfg = self.__get_target_config()

+            if status != 0:

+                logging.error('get target config (%s)' %status)

+                return -1

+            logging.debug('cfg=0x%x' %cfg)

+

+            status = self.__send_auth(cfg)

+            if status != 0:

+                logging.error('send auth (%d)' %status)

+                return -1

+

+            da1 = self.__load_binary(self.__da1_path)

+            da1_len = len(da1)

+            logging.debug('da1 length: 0x%x' %da1_len)

+            status, strip_pl, strip_pl_len = self.__strip_pl_hdr(da1, da1_len)

+            if not status:

+                logging.error('strip pl hdr')

+                return -1

+

+            sig_da1 = None

+            sig_da1_len = 0

+            if self.TGT_CFG_DAA_EN & cfg:

+                sig_da1 = self.__load_binary(self.__da1_path + '.sign')

+                sig_da1_len = len(sig_da1)

+

+            logging.debug('strip_pl_len: 0x%x' %strip_pl_len)

+            logging.info('Send %s' %self.__da1_path)

+            status = self.__send_da(self.__da1_addr, strip_pl, strip_pl_len, sig_da1, sig_da1_len)

+            if status != 0:

+                logging.error('send da1 (%d)' %status)

+                return -1

+            logging.info('Jump da')

+            if self.__da1_jump_64 == 0:

+                status = self.__jump_da(self.__da1_addr)

+            else:

+                status = self.__jump_da_ex(self.__da1_addr)

+            if status != 0:

+                logging.error('jump da1 (%d)' %status)

+                return -1

+

+            self.__ser.close()

+            if self.__da2_path == '':

+                return 0

+

+            # handshake to preloader

+            self.__find_usb_device(self.__match_usb_pl)

+            self.__start_cmd()

+

+        if self.__fall_thru_to_fb == 1:

+            logging.info("fall through to fastboot")

+            status = self.__fall_through_to_fastboot()

+            self.__ser.close()

+            if status != 0:

+                logging.error("fall through to fastboot failed(%d)" %(status))

+                return -1

+            else:

+                return 0

+

+        # load da2 (lk)

+        da2 = self.__load_binary(self.__da2_path)

+        da2_len = len(da2)

+        sig_da2 = self.__load_binary(self.__da2_path + '.sign')

+        sig_da2_len = len(sig_da2)

+        logging.info('Send %s' %self.__da2_path)

+        status = self.__send_da(self.__da2_addr, da2, da2_len, sig_da2, sig_da2_len)

+        if status != 0:

+            logging.error('send da2 (%d)' %status)

+            return -1

+        logging.info('Jump da2')

+        status = self.__jump_da(self.__da2_addr)

+        if status != 0:

+            logging.error('jump da2 (%d)' %status)

+            return -1

+

+        self.__ser.close()

+        return 0

+

+

+if __name__ == '__main__':

+    parser = OptionParser()

+    parser.add_option('-f', '--file', dest='configfile', help='read config file',

+                    metavar='FILE', default='dl_addr.ini')

+    parser.add_option('-d', '--debug', action='store_true', dest='debuglog',

+                    default=False, help='enable debug log')

+    parser.add_option('-m', '--meid', action='store_true', dest='meid',

+                    default=False, help='get meid')

+    options, args = parser.parse_args()

+    config_file = options.configfile

+    meid = options.meid

+    debug = options.debuglog

+    if debug:

+        logging.basicConfig(level=logging.DEBUG, format='%(levelname)s: %(message)s')

+    else:

+        logging.basicConfig(level=logging.INFO, format='%(levelname)s: %(message)s')

+

+    logging.info('pySerial version: (%s)' %serial.VERSION)

+    if serial.VERSION < '2.6':

+        logging.error('pySerial version(%s) is lower than 2.6, please upgrade!' %serial.VERSION)

+    logging.info('Use config file: %s' %(config_file))

+    logging.info('Waiting to connect platform...')

+    fbtool = Fbtool(config_file, meid)

+    fbtool.start()

diff --git a/src/devtools/datool/pyserial/__init__.py b/src/devtools/datool/pyserial/__init__.py
new file mode 100644
index 0000000..a5d1233
--- /dev/null
+++ b/src/devtools/datool/pyserial/__init__.py
@@ -0,0 +1,86 @@
+#!/usr/bin/env python
+#
+# This is a wrapper module for different platform implementations
+#
+# This file is part of pySerial. https://github.com/pyserial/pyserial
+# (C) 2001-2016 Chris Liechti <cliechti@gmx.net>
+#
+# SPDX-License-Identifier:    BSD-3-Clause
+
+import importlib
+import sys
+
+from pyserial.serialutil import *
+#~ SerialBase, SerialException, to_bytes, iterbytes
+
+VERSION = '3.0.1'
+
+if sys.platform == 'cli':
+    from serial.serialcli import Serial
+else:
+    import os
+    # chose an implementation, depending on os
+    if os.name == 'nt':  # sys.platform == 'win32':
+        from pyserial.serialwin32 import Serial
+    elif os.name == 'posix':
+        from pyserial.serialposix import Serial, PosixPollSerial, VTIMESerial  # noqa
+    elif os.name == 'java':
+        from pyserial.serialjava import Serial
+    else:
+        raise ImportError("Sorry: no implementation for your platform ('%s') available" % (os.name,))
+
+
+protocol_handler_packages = [
+    'pyserial.urlhandler',
+]
+
+
+def serial_for_url(url, *args, **kwargs):
+    """\
+    Get an instance of the Serial class, depending on port/url. The port is not
+    opened when the keyword parameter 'do_not_open' is true, by default it
+    is. All other parameters are directly passed to the __init__ method when
+    the port is instantiated.
+
+    The list of package names that is searched for protocol handlers is kept in
+    ``protocol_handler_packages``.
+
+    e.g. we want to support a URL ``foobar://``. A module
+    ``my_handlers.protocol_foobar`` is provided by the user. Then
+    ``protocol_handler_packages.append("my_handlers")`` would extend the search
+    path so that ``serial_for_url("foobar://"))`` would work.
+    """
+    # check and remove extra parameter to not confuse the Serial class
+    do_open = not kwargs.pop('do_not_open', False)
+    # the default is to use the native implementation
+    klass = Serial
+    try:
+        url_lowercase = url.lower()
+    except AttributeError:
+        # it's not a string, use default
+        pass
+    else:
+        # if it is an URL, try to import the handler module from the list of possible packages
+        if '://' in url_lowercase:
+            protocol = url_lowercase.split('://', 1)[0]
+            module_name = '.protocol_%s' % (protocol,)
+            for package_name in protocol_handler_packages:
+                try:
+                    package = importlib.import_module(package_name)
+                    handler_module = importlib.import_module(module_name, package_name)
+                except ImportError:
+                    continue
+                else:
+                    if hasattr(handler_module, 'serial_class_for_url'):
+                        url, klass = handler_module.serial_class_for_url(url)
+                    else:
+                        klass = handler_module.Serial
+                    break
+            else:
+                raise ValueError('invalid URL, protocol %r not known' % (protocol,))
+    # instantiate and open when desired
+    instance = klass(None, *args, **kwargs)
+    instance.port = url
+    if do_open:
+        instance.open()
+    return instance
diff --git a/src/devtools/datool/pyserial/serialcli.py b/src/devtools/datool/pyserial/serialcli.py
new file mode 100644
index 0000000..c79cd7b
--- /dev/null
+++ b/src/devtools/datool/pyserial/serialcli.py
@@ -0,0 +1,270 @@
+#! python
+#
+# Backend for .NET/Mono (IronPython), .NET >= 2
+#
+# This file is part of pySerial. https://github.com/pyserial/pyserial
+# (C) 2008-2015 Chris Liechti <cliechti@gmx.net>
+#
+# SPDX-License-Identifier:    BSD-3-Clause
+
+import clr
+import System
+import System.IO.Ports
+from pyserial.serialutil import *
+
+
+#~ def device(portnum):
+    #~ """Turn a port number into a device name"""
+    #~ return System.IO.Ports.SerialPort.GetPortNames()[portnum]
+
+
+# must invoke function with byte array, make a helper to convert strings
+# to byte arrays
+sab = System.Array[System.Byte]
+def as_byte_array(string):
+    return sab([ord(x) for x in string])  # XXX will require adaption when run with a 3.x compatible IronPython
+
+class Serial(SerialBase):
+    """Serial port implementation for .NET/Mono."""
+
+    BAUDRATES = (50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
+                9600, 19200, 38400, 57600, 115200)
+
+    def open(self):
+        """\
+        Open port with current settings. This may throw a SerialException
+        if the port cannot be opened.
+        """
+        if self._port is None:
+            raise SerialException("Port must be configured before it can be used.")
+        if self.is_open:
+            raise SerialException("Port is already open.")
+        try:
+            self._port_handle = System.IO.Ports.SerialPort(self.portstr)
+        except Exception as msg:
+            self._port_handle = None
+            raise SerialException("could not open port %s: %s" % (self.portstr, msg))
+
+        self._reconfigurePort()
+        self._port_handle.Open()
+        self.is_open = True
+        if not self._dsrdtr:
+            self._update_dtr_state()
+        if not self._rtscts:
+            self._update_rts_state()
+        self.reset_input_buffer()
+
+    def _reconfigurePort(self):
+        """Set communication parameters on opened port."""
+        if not self._port_handle:
+            raise SerialException("Can only operate on a valid port handle")
+
+        #~ self._port_handle.ReceivedBytesThreshold = 1
+
+        if self._timeout is None:
+            self._port_handle.ReadTimeout = System.IO.Ports.SerialPort.InfiniteTimeout
+        else:
+            self._port_handle.ReadTimeout = int(self._timeout*1000)
+
+        # if self._timeout != 0 and self._interCharTimeout is not None:
+            # timeouts = (int(self._interCharTimeout * 1000),) + timeouts[1:]
+
+        if self._write_timeout is None:
+            self._port_handle.WriteTimeout = System.IO.Ports.SerialPort.InfiniteTimeout
+        else:
+            self._port_handle.WriteTimeout = int(self._write_timeout*1000)
+
+
+        # Setup the connection info.
+        try:
+            self._port_handle.BaudRate = self._baudrate
+        except IOError as e:
+            # catch errors from illegal baudrate settings
+            raise ValueError(str(e))
+
+        if self._bytesize == FIVEBITS:
+            self._port_handle.DataBits     = 5
+        elif self._bytesize == SIXBITS:
+            self._port_handle.DataBits     = 6
+        elif self._bytesize == SEVENBITS:
+            self._port_handle.DataBits     = 7
+        elif self._bytesize == EIGHTBITS:
+            self._port_handle.DataBits     = 8
+        else:
+            raise ValueError("Unsupported number of data bits: %r" % self._bytesize)
+
+        if self._parity == PARITY_NONE:
+            self._port_handle.Parity       = getattr(System.IO.Ports.Parity, 'None') # reserved keyword in Py3k
+        elif self._parity == PARITY_EVEN:
+            self._port_handle.Parity       = System.IO.Ports.Parity.Even
+        elif self._parity == PARITY_ODD:
+            self._port_handle.Parity       = System.IO.Ports.Parity.Odd
+        elif self._parity == PARITY_MARK:
+            self._port_handle.Parity       = System.IO.Ports.Parity.Mark
+        elif self._parity == PARITY_SPACE:
+            self._port_handle.Parity       = System.IO.Ports.Parity.Space
+        else:
+            raise ValueError("Unsupported parity mode: %r" % self._parity)
+
+        if self._stopbits == STOPBITS_ONE:
+            self._port_handle.StopBits     = System.IO.Ports.StopBits.One
+        elif self._stopbits == STOPBITS_ONE_POINT_FIVE:
+            self._port_handle.StopBits     = System.IO.Ports.StopBits.OnePointFive
+        elif self._stopbits == STOPBITS_TWO:
+            self._port_handle.StopBits     = System.IO.Ports.StopBits.Two
+        else:
+            raise ValueError("Unsupported number of stop bits: %r" % self._stopbits)
+
+        if self._rtscts and self._xonxoff:
+            self._port_handle.Handshake  = System.IO.Ports.Handshake.RequestToSendXOnXOff
+        elif self._rtscts:
+            self._port_handle.Handshake  = System.IO.Ports.Handshake.RequestToSend
+        elif self._xonxoff:
+            self._port_handle.Handshake  = System.IO.Ports.Handshake.XOnXOff
+        else:
+            self._port_handle.Handshake  = getattr(System.IO.Ports.Handshake, 'None')   # reserved keyword in Py3k
+
+    #~ def __del__(self):
+        #~ self.close()
+
+    def close(self):
+        """Close port"""
+        if self.is_open:
+            if self._port_handle:
+                try:
+                    self._port_handle.Close()
+                except System.IO.Ports.InvalidOperationException:
+                    # ignore errors. can happen for unplugged USB serial devices
+                    pass
+                self._port_handle = None
+            self.is_open = False
+
+    #  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -
+
+    @property
+    def in_waiting(self):
+        """Return the number of characters currently in the input buffer."""
+        if not self._port_handle:
+            raise portNotOpenError
+        return self._port_handle.BytesToRead
+
+    def read(self, size=1):
+        """\
+        Read size bytes from the serial port. If a timeout is set it may
+        return less characters as requested. With no timeout it will block
+        until the requested number of bytes is read.
+        """
+        if not self._port_handle:
+            raise portNotOpenError
+        # must use single byte reads as this is the only way to read
+        # without applying encodings
+        data = bytearray()
+        while size:
+            try:
+                data.append(self._port_handle.ReadByte())
+            except System.TimeoutException as e:
+                break
+            else:
+                size -= 1
+        return bytes(data)
+
+    def write(self, data):
+        """Output the given string over the serial port."""
+        if not self._port_handle:
+            raise portNotOpenError
+        #~ if not isinstance(data, (bytes, bytearray)):
+            #~ raise TypeError('expected %s or bytearray, got %s' % (bytes, type(data)))
+        try:
+            # must call overloaded method with byte array argument
+            # as this is the only one not applying encodings
+            self._port_handle.Write(as_byte_array(data), 0, len(data))
+        except System.TimeoutException as e:
+            raise writeTimeoutError
+        return len(data)
+
+    def reset_input_buffer(self):
+        """Clear input buffer, discarding all that is in the buffer."""
+        if not self._port_handle:
+            raise portNotOpenError
+        self._port_handle.DiscardInBuffer()
+
+    def reset_output_buffer(self):
+        """\
+        Clear output buffer, aborting the current output and
+        discarding all that is in the buffer.
+        """
+        if not self._port_handle:
+            raise portNotOpenError
+        self._port_handle.DiscardOutBuffer()
+
+    def _update_break_state(self):
+        """
+        Set break: Controls TXD. When active, to transmitting is possible.
+        """
+        if not self._port_handle:
+            raise portNotOpenError
+        self._port_handle.BreakState = bool(self._break_state)
+
+    def _update_rts_state(self):
+        """Set terminal status line: Request To Send"""
+        if not self._port_handle:
+            raise portNotOpenError
+        self._port_handle.RtsEnable = bool(self._rts_state)
+
+    def _update_dtr_state(self):
+        """Set terminal status line: Data Terminal Ready"""
+        if not self._port_handle:
+            raise portNotOpenError
+        self._port_handle.DtrEnable = bool(self._dtr_state)
+
+    @property
+    def cts(self):
+        """Read terminal status line: Clear To Send"""
+        if not self._port_handle:
+            raise portNotOpenError
+        return self._port_handle.CtsHolding
+
+    @property
+    def dsr(self):
+        """Read terminal status line: Data Set Ready"""
+        if not self._port_handle:
+            raise portNotOpenError
+        return self._port_handle.DsrHolding
+
+    @property
+    def ri(self):
+        """Read terminal status line: Ring Indicator"""
+        if not self._port_handle:
+            raise portNotOpenError
+        #~ return self._port_handle.XXX
+        return False #XXX an error would be better
+
+    @property
+    def cd(self):
+        """Read terminal status line: Carrier Detect"""
+        if not self._port_handle:
+            raise portNotOpenError
+        return self._port_handle.CDHolding
+
+    # - - platform specific - - - -
+    # none
+
+
+# Nur Testfunktion!!
+if __name__ == '__main__':
+    import sys
+
+    s = Serial(0)
+    sys.stdio.write('%s\n' % s)
+
+    s = Serial()
+    sys.stdio.write('%s\n' % s)
+
+
+    s.baudrate = 19200
+    s.databits = 7
+    s.close()
+    s.port = 0
+    s.open()
+    sys.stdio.write('%s\n' % s)
+
diff --git a/src/devtools/datool/pyserial/serialjava.py b/src/devtools/datool/pyserial/serialjava.py
new file mode 100644
index 0000000..dae8b5d
--- /dev/null
+++ b/src/devtools/datool/pyserial/serialjava.py
@@ -0,0 +1,270 @@
+#!jython
+#
+# Backend Jython with JavaComm
+#
+# This file is part of pySerial. https://github.com/pyserial/pyserial
+# (C) 2002-2015 Chris Liechti <cliechti@gmx.net>
+#
+# SPDX-License-Identifier:    BSD-3-Clause
+
+from pyserial.serialutil import *
+
+
+def my_import(name):
+    mod = __import__(name)
+    components = name.split('.')
+    for comp in components[1:]:
+        mod = getattr(mod, comp)
+    return mod
+
+
+def detect_java_comm(names):
+    """try given list of modules and return that imports"""
+    for name in names:
+        try:
+            mod = my_import(name)
+            mod.SerialPort
+            return mod
+        except (ImportError, AttributeError):
+            pass
+    raise ImportError("No Java Communications API implementation found")
+
+
+# Java Communications API implementations
+# http://mho.republika.pl/java/comm/
+
+comm = detect_java_comm([
+    'javax.comm',  # Sun/IBM
+    'gnu.io',      # RXTX
+])
+
+
+def device(portnumber):
+    """Turn a port number into a device name"""
+    enum = comm.CommPortIdentifier.getPortIdentifiers()
+    ports = []
+    while enum.hasMoreElements():
+        el = enum.nextElement()
+        if el.getPortType() == comm.CommPortIdentifier.PORT_SERIAL:
+            ports.append(el)
+    return ports[portnumber].getName()
+
+
+class Serial(SerialBase):
+    """\
+    Serial port class, implemented with Java Communications API and
+    thus usable with jython and the appropriate java extension.
+    """
+
+    def open(self):
+        """\
+        Open port with current settings. This may throw a SerialException
+        if the port cannot be opened.
+        """
+        if self._port is None:
+            raise SerialException("Port must be configured before it can be used.")
+        if self.is_open:
+            raise SerialException("Port is already open.")
+        if type(self._port) == type(''):      # strings are taken directly
+            portId = comm.CommPortIdentifier.getPortIdentifier(self._port)
+        else:
+            portId = comm.CommPortIdentifier.getPortIdentifier(device(self._port))     # numbers are transformed to a comport id obj
+        try:
+            self.sPort = portId.open("python serial module", 10)
+        except Exception as msg:
+            self.sPort = None
+            raise SerialException("Could not open port: %s" % msg)
+        self._reconfigurePort()
+        self._instream = self.sPort.getInputStream()
+        self._outstream = self.sPort.getOutputStream()
+        self.is_open = True
+
+    def _reconfigurePort(self):
+        """Set communication parameters on opened port."""
+        if not self.sPort:
+            raise SerialException("Can only operate on a valid port handle")
+
+        self.sPort.enableReceiveTimeout(30)
+        if self._bytesize == FIVEBITS:
+            jdatabits = comm.SerialPort.DATABITS_5
+        elif self._bytesize == SIXBITS:
+            jdatabits = comm.SerialPort.DATABITS_6
+        elif self._bytesize == SEVENBITS:
+            jdatabits = comm.SerialPort.DATABITS_7
+        elif self._bytesize == EIGHTBITS:
+            jdatabits = comm.SerialPort.DATABITS_8
+        else:
+            raise ValueError("unsupported bytesize: %r" % self._bytesize)
+
+        if self._stopbits == STOPBITS_ONE:
+            jstopbits = comm.SerialPort.STOPBITS_1
+        elif self._stopbits == STOPBITS_ONE_POINT_FIVE:
+            jstopbits = comm.SerialPort.STOPBITS_1_5
+        elif self._stopbits == STOPBITS_TWO:
+            jstopbits = comm.SerialPort.STOPBITS_2
+        else:
+            raise ValueError("unsupported number of stopbits: %r" % self._stopbits)
+
+        if self._parity == PARITY_NONE:
+            jparity = comm.SerialPort.PARITY_NONE
+        elif self._parity == PARITY_EVEN:
+            jparity = comm.SerialPort.PARITY_EVEN
+        elif self._parity == PARITY_ODD:
+            jparity = comm.SerialPort.PARITY_ODD
+        elif self._parity == PARITY_MARK:
+            jparity = comm.SerialPort.PARITY_MARK
+        elif self._parity == PARITY_SPACE:
+            jparity = comm.SerialPort.PARITY_SPACE
+        else:
+            raise ValueError("unsupported parity type: %r" % self._parity)
+
+        jflowin = jflowout = 0
+        if self._rtscts:
+            jflowin |= comm.SerialPort.FLOWCONTROL_RTSCTS_IN
+            jflowout |= comm.SerialPort.FLOWCONTROL_RTSCTS_OUT
+        if self._xonxoff:
+            jflowin |= comm.SerialPort.FLOWCONTROL_XONXOFF_IN
+            jflowout |= comm.SerialPort.FLOWCONTROL_XONXOFF_OUT
+
+        self.sPort.setSerialPortParams(self._baudrate, jdatabits, jstopbits, jparity)
+        self.sPort.setFlowControlMode(jflowin | jflowout)
+
+        if self._timeout >= 0:
+            self.sPort.enableReceiveTimeout(int(self._timeout*1000))
+        else:
+            self.sPort.disableReceiveTimeout()
+
+    def close(self):
+        """Close port"""
+        if self.is_open:
+            if self.sPort:
+                self._instream.close()
+                self._outstream.close()
+                self.sPort.close()
+                self.sPort = None
+            self.is_open = False
+
+    #  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -
+
+    @property
+    def in_waiting(self):
+        """Return the number of characters currently in the input buffer."""
+        if not self.sPort:
+            raise portNotOpenError
+        return self._instream.available()
+
+    def read(self, size=1):
+        """\
+        Read size bytes from the serial port. If a timeout is set it may
+        return less characters as requested. With no timeout it will block
+        until the requested number of bytes is read.
+        """
+        if not self.sPort:
+            raise portNotOpenError
+        read = bytearray()
+        if size > 0:
+            while len(read) < size:
+                x = self._instream.read()
+                if x == -1:
+                    if self.timeout >= 0:
+                        break
+                else:
+                    read.append(x)
+        return bytes(read)
+
+    def write(self, data):
+        """Output the given string over the serial port."""
+        if not self.sPort:
+            raise portNotOpenError
+        if not isinstance(data, (bytes, bytearray)):
+            raise TypeError('expected %s or bytearray, got %s' % (bytes, type(data)))
+        self._outstream.write(data)
+        return len(data)
+
+    def reset_input_buffer(self):
+        """Clear input buffer, discarding all that is in the buffer."""
+        if not self.sPort:
+            raise portNotOpenError
+        self._instream.skip(self._instream.available())
+
+    def reset_output_buffer(self):
+        """\
+        Clear output buffer, aborting the current output and
+        discarding all that is in the buffer.
+        """
+        if not self.sPort:
+            raise portNotOpenError
+        self._outstream.flush()
+
+    def send_break(self, duration=0.25):
+        """Send break condition. Timed, returns to idle state after given duration."""
+        if not self.sPort:
+            raise portNotOpenError
+        self.sPort.sendBreak(duration*1000.0)
+
+    def _update_break_state(self):
+        """Set break: Controls TXD. When active, to transmitting is possible."""
+        if self.fd is None:
+            raise portNotOpenError
+        raise SerialException("The _update_break_state function is not implemented in java.")
+
+    def _update_rts_state(self):
+        """Set terminal status line: Request To Send"""
+        if not self.sPort:
+            raise portNotOpenError
+        self.sPort.setRTS(self._rts_state)
+
+    def _update_dtr_state(self):
+        """Set terminal status line: Data Terminal Ready"""
+        if not self.sPort:
+            raise portNotOpenError
+        self.sPort.setDTR(self._dtr_state)
+
+    @property
+    def cts(self):
+        """Read terminal status line: Clear To Send"""
+        if not self.sPort:
+            raise portNotOpenError
+        self.sPort.isCTS()
+
+    @property
+    def dsr(self):
+        """Read terminal status line: Data Set Ready"""
+        if not self.sPort:
+            raise portNotOpenError
+        self.sPort.isDSR()
+
+    @property
+    def ri(self):
+        """Read terminal status line: Ring Indicator"""
+        if not self.sPort:
+            raise portNotOpenError
+        self.sPort.isRI()
+
+    @property
+    def cd(self):
+        """Read terminal status line: Carrier Detect"""
+        if not self.sPort:
+            raise portNotOpenError
+        self.sPort.isCD()
+
+
+if __name__ == '__main__':
+    s = Serial(0,
+         baudrate=19200,        # baudrate
+         bytesize=EIGHTBITS,    # number of databits
+         parity=PARITY_EVEN,    # enable parity checking
+         stopbits=STOPBITS_ONE, # number of stopbits
+         timeout=3,             # set a timeout value, None for waiting forever
+         xonxoff=0,             # enable software flow control
+         rtscts=0,              # enable RTS/CTS flow control
+    )
+    s.setRTS(1)
+    s.setDTR(1)
+    s.reset_input_buffer()
+    s.reset_output_buffer()
+    s.write('hello')
+    sys.stdio.write('%r\n' % s.read(5))
+    sys.stdio.write('%s\n' % s.in_waiting())
+    del s
+
diff --git a/src/devtools/datool/pyserial/serialposix.py b/src/devtools/datool/pyserial/serialposix.py
new file mode 100644
index 0000000..0e26e78
--- /dev/null
+++ b/src/devtools/datool/pyserial/serialposix.py
@@ -0,0 +1,798 @@
+#!/usr/bin/env python
+#
+# backend for serial IO for POSIX compatible systems, like Linux, OSX
+#
+# This file is part of pySerial. https://github.com/pyserial/pyserial
+# (C) 2001-2015 Chris Liechti <cliechti@gmx.net>
+#
+# SPDX-License-Identifier:    BSD-3-Clause
+#
+# parts based on code from Grant B. Edwards  <grante@visi.com>:
+#  ftp://ftp.visi.com/users/grante/python/PosixSerial.py
+#
+# references: http://www.easysw.com/~mike/serial/serial.html
+
+import errno
+import fcntl
+import os
+import select
+import struct
+import sys
+import termios
+import time
+
+import pyserial as serial
+from pyserial.serialutil import SerialBase, SerialException, to_bytes, portNotOpenError, writeTimeoutError
+
+
+class PlatformSpecificBase(object):
+    BAUDRATE_CONSTANTS = {}
+
+    def number_to_device(self, port_number):
+        sys.stderr.write("""\
+don't know how to number ttys on this system.
+! Use an explicit path (eg /dev/ttyS1) or send this information to
+! the author of this module:
+
+sys.platform = %r
+os.name = %r
+serialposix.py version = %s
+
+also add the device name of the serial port and where the
+counting starts for the first serial port.
+e.g. 'first serial port: /dev/ttyS0'
+and with a bit luck you can get this module running...
+""" % (sys.platform, os.name, serial.VERSION))
+        raise NotImplementedError('no number-to-device mapping defined on this platform')
+
+    def _set_special_baudrate(self, baudrate):
+        raise NotImplementedError('non-standard baudrates are not supported on this platform')
+
+    def _set_rs485_mode(self, rs485_settings):
+        raise NotImplementedError('RS485 not supported on this platform')
+
+# try to detect the OS so that a device can be selected...
+# this code block should supply a device() and set_special_baudrate() function
+# for the platform
+plat = sys.platform.lower()
+
+if plat[:5] == 'linux':    # Linux (confirmed)
+    import array
+
+    # baudrate ioctls
+    TCGETS2 = 0x802C542A
+    TCSETS2 = 0x402C542B
+    BOTHER = 0o010000
+
+    # RS485 ioctls
+    TIOCGRS485 = 0x542E
+    TIOCSRS485 = 0x542F
+    SER_RS485_ENABLED = 0b00000001
+    SER_RS485_RTS_ON_SEND = 0b00000010
+    SER_RS485_RTS_AFTER_SEND = 0b00000100
+    SER_RS485_RX_DURING_TX = 0b00010000
+
+    class PlatformSpecific(PlatformSpecificBase):
+        BAUDRATE_CONSTANTS = {
+            0:       0o000000,  # hang up
+            50:      0o000001,
+            75:      0o000002,
+            110:     0o000003,
+            134:     0o000004,
+            150:     0o000005,
+            200:     0o000006,
+            300:     0o000007,
+            600:     0o000010,
+            1200:    0o000011,
+            1800:    0o000012,
+            2400:    0o000013,
+            4800:    0o000014,
+            9600:    0o000015,
+            19200:   0o000016,
+            38400:   0o000017,
+            57600:   0o010001,
+            115200:  0o010002,
+            230400:  0o010003,
+            460800:  0o010004,
+            500000:  0o010005,
+            576000:  0o010006,
+            921600:  0o010007,
+            1000000: 0o010010,
+            1152000: 0o010011,
+            1500000: 0o010012,
+            2000000: 0o010013,
+            2500000: 0o010014,
+            3000000: 0o010015,
+            3500000: 0o010016,
+            4000000: 0o010017
+        }
+
+        def number_to_device(self, port_number):
+            return '/dev/ttyS%d' % (port_number,)
+
+        def _set_special_baudrate(self, baudrate):
+            # right size is 44 on x86_64, allow for some growth
+            buf = array.array('i', [0] * 64)
+            try:
+                # get serial_struct
+                fcntl.ioctl(self.fd, TCGETS2, buf)
+                # set custom speed
+                buf[2] &= ~termios.CBAUD
+                buf[2] |= BOTHER
+                buf[9] = buf[10] = baudrate
+
+                # set serial_struct
+                fcntl.ioctl(self.fd, TCSETS2, buf)
+            except IOError as e:
+                raise ValueError('Failed to set custom baud rate (%s): %s' % (baudrate, e))
+
+        def _set_rs485_mode(self, rs485_settings):
+            buf = array.array('i', [0] * 8)  # flags, delaytx, delayrx, padding
+            try:
+                fcntl.ioctl(self.fd, TIOCGRS485, buf)
+                if rs485_settings is not None:
+                    if rs485_settings.loopback:
+                        buf[0] |= SER_RS485_RX_DURING_TX
+                    else:
+                        buf[0] &= ~SER_RS485_RX_DURING_TX
+                    if rs485_settings.rts_level_for_tx:
+                        buf[0] |= SER_RS485_RTS_ON_SEND
+                    else:
+                        buf[0] &= ~SER_RS485_RTS_ON_SEND
+                    if rs485_settings.rts_level_for_rx:
+                        buf[0] |= SER_RS485_RTS_AFTER_SEND
+                    else:
+                        buf[0] &= ~SER_RS485_RTS_AFTER_SEND
+                    buf[1] = int(rs485_settings.delay_rts_before_send * 1000)
+                    buf[2] = int(rs485_settings.delay_rts_after_send * 1000)
+                else:
+                    buf[0] = 0  # clear SER_RS485_ENABLED
+                fcntl.ioctl(self.fd, TIOCSRS485, buf)
+            except IOError as e:
+                raise ValueError('Failed to set RS485 mode: %s' % (e,))
+
+
+elif plat == 'cygwin':       # cygwin/win32 (confirmed)
+
+    class PlatformSpecific(PlatformSpecificBase):
+        BAUDRATE_CONSTANTS = {
+            128000: 0x01003,
+            256000: 0x01005,
+            500000: 0x01007,
+            576000: 0x01008,
+            921600: 0x01009,
+            1000000: 0x0100a,
+            1152000: 0x0100b,
+            1500000: 0x0100c,
+            2000000: 0x0100d,
+            2500000: 0x0100e,
+            3000000: 0x0100f
+        }
+
+        def number_to_device(self, port_number):
+            return '/dev/com%d' % (port_number + 1,)
+
+
+elif plat[:7] == 'openbsd':    # OpenBSD
+    class PlatformSpecific(PlatformSpecificBase):
+        def number_to_device(self, port_number):
+            return '/dev/cua%02d' % (port_number,)
+
+elif plat[:3] == 'bsd' or plat[:7] == 'freebsd':
+    class PlatformSpecific(PlatformSpecificBase):
+        def number_to_device(self, port_number):
+            return '/dev/cuad%d' % (port_number,)
+
+elif plat[:6] == 'darwin':   # OS X
+    import array
+    IOSSIOSPEED = 0x80045402  # _IOW('T', 2, speed_t)
+
+    class PlatformSpecific(PlatformSpecificBase):
+        def number_to_device(self, port_number):
+            return '/dev/cuad%d' % (port_number,)
+
+        osx_version = os.uname()[2].split('.')
+        # Tiger or above can support arbitrary serial speeds
+        if int(osx_version[0]) >= 8:
+            def _set_special_baudrate(self, baudrate):
+                # use IOKit-specific call to set up high speeds
+                buf = array.array('i', [baudrate])
+                fcntl.ioctl(self.fd, IOSSIOSPEED, buf, 1)
+
+
+elif plat[:6] == 'netbsd':   # NetBSD 1.6 testing by Erk
+    class PlatformSpecific(PlatformSpecificBase):
+        def number_to_device(self, port_number):
+            return '/dev/dty%02d' % (port_number,)
+
+elif plat[:4] == 'irix':     # IRIX (partially tested)
+    class PlatformSpecific(PlatformSpecificBase):
+        def number_to_device(self, port_number):
+            return '/dev/ttyf%d' % (port_number + 1,)  # XXX different device names depending on flow control
+
+elif plat[:2] == 'hp':       # HP-UX (not tested)
+    class PlatformSpecific(PlatformSpecificBase):
+        def number_to_device(self, port_number):
+            return '/dev/tty%dp0' % (port_number + 1,)
+
+elif plat[:5] == 'sunos':    # Solaris/SunOS (confirmed)
+    class PlatformSpecific(PlatformSpecificBase):
+        def number_to_device(self, port_number):
+            return '/dev/tty%c' % (ord('a') + port_number,)
+
+elif plat[:3] == 'aix':      # AIX
+    class PlatformSpecific(PlatformSpecificBase):
+        def number_to_device(self, port_number):
+            return '/dev/tty%d' % (port_number,)
+
+else:
+    class PlatformSpecific(PlatformSpecificBase):
+        pass
+
+# whats up with "aix", "beos", ....
+# they should work, just need to know the device names.
+
+
+# load some constants for later use.
+# try to use values from termios, use defaults from linux otherwise
+TIOCMGET = getattr(termios, 'TIOCMGET', 0x5415)
+TIOCMBIS = getattr(termios, 'TIOCMBIS', 0x5416)
+TIOCMBIC = getattr(termios, 'TIOCMBIC', 0x5417)
+TIOCMSET = getattr(termios, 'TIOCMSET', 0x5418)
+
+# TIOCM_LE = getattr(termios, 'TIOCM_LE', 0x001)
+TIOCM_DTR = getattr(termios, 'TIOCM_DTR', 0x002)
+TIOCM_RTS = getattr(termios, 'TIOCM_RTS', 0x004)
+# TIOCM_ST = getattr(termios, 'TIOCM_ST', 0x008)
+# TIOCM_SR = getattr(termios, 'TIOCM_SR', 0x010)
+
+TIOCM_CTS = getattr(termios, 'TIOCM_CTS', 0x020)
+TIOCM_CAR = getattr(termios, 'TIOCM_CAR', 0x040)
+TIOCM_RNG = getattr(termios, 'TIOCM_RNG', 0x080)
+TIOCM_DSR = getattr(termios, 'TIOCM_DSR', 0x100)
+TIOCM_CD = getattr(termios, 'TIOCM_CD', TIOCM_CAR)
+TIOCM_RI = getattr(termios, 'TIOCM_RI', TIOCM_RNG)
+# TIOCM_OUT1 = getattr(termios, 'TIOCM_OUT1', 0x2000)
+# TIOCM_OUT2 = getattr(termios, 'TIOCM_OUT2', 0x4000)
+if hasattr(termios, 'TIOCINQ'):
+    TIOCINQ = termios.TIOCINQ
+else:
+    TIOCINQ = getattr(termios, 'FIONREAD', 0x541B)
+TIOCOUTQ = getattr(termios, 'TIOCOUTQ', 0x5411)
+
+TIOCM_zero_str = struct.pack('I', 0)
+TIOCM_RTS_str = struct.pack('I', TIOCM_RTS)
+TIOCM_DTR_str = struct.pack('I', TIOCM_DTR)
+
+TIOCSBRK = getattr(termios, 'TIOCSBRK', 0x5427)
+TIOCCBRK = getattr(termios, 'TIOCCBRK', 0x5428)
+
+CMSPAR = 0o10000000000  # Use "stick" (mark/space) parity
+
+
+class Serial(SerialBase, PlatformSpecific):
+    """\
+    Serial port class POSIX implementation. Serial port configuration is
+    done with termios and fcntl. Runs on Linux and many other Un*x like
+    systems.
+    """
+
+    def open(self):
+        """\
+        Open port with current settings. This may throw a SerialException
+        if the port cannot be opened."""
+        if self._port is None:
+            raise SerialException("Port must be configured before it can be used.")
+        if self.is_open:
+            raise SerialException("Port is already open.")
+        self.fd = None
+        # open
+        try:
+            self.fd = os.open(self.portstr, os.O_RDWR | os.O_NOCTTY | os.O_NONBLOCK)
+        except OSError as msg:
+            self.fd = None
+            raise SerialException(msg.errno, "could not open port %s: %s" % (self._port, msg))
+        #~ fcntl.fcntl(self.fd, fcntl.F_SETFL, 0)  # set blocking
+
+        try:
+            self._reconfigure_port(force_update=True)
+        except:
+            try:
+                os.close(self.fd)
+            except:
+                # ignore any exception when closing the port
+                # also to keep original exception that happened when setting up
+                pass
+            self.fd = None
+            raise
+        else:
+            self.is_open = True
+        if not self._dsrdtr:
+            self._update_dtr_state()
+        if not self._rtscts:
+            self._update_rts_state()
+        self.reset_input_buffer()
+
+    def _reconfigure_port(self, force_update=False):
+        """Set communication parameters on opened port."""
+        if self.fd is None:
+            raise SerialException("Can only operate on a valid file descriptor")
+        custom_baud = None
+
+        vmin = vtime = 0                # timeout is done via select
+        if self._inter_byte_timeout is not None:
+            vmin = 1
+            vtime = int(self._inter_byte_timeout * 10)
+        try:
+            orig_attr = termios.tcgetattr(self.fd)
+            iflag, oflag, cflag, lflag, ispeed, ospeed, cc = orig_attr
+        except termios.error as msg:      # if a port is nonexistent but has a /dev file, it'll fail here
+            raise SerialException("Could not configure port: %s" % msg)
+        # set up raw mode / no echo / binary
+        cflag |= (termios.CLOCAL | termios.CREAD)
+        lflag &= ~(termios.ICANON | termios.ECHO | termios.ECHOE |
+                   termios.ECHOK | termios.ECHONL |
+                   termios.ISIG | termios.IEXTEN)  # |termios.ECHOPRT
+        for flag in ('ECHOCTL', 'ECHOKE'):  # netbsd workaround for Erk
+            if hasattr(termios, flag):
+                lflag &= ~getattr(termios, flag)
+
+        oflag &= ~(termios.OPOST | termios.ONLCR | termios.OCRNL)
+        iflag &= ~(termios.INLCR | termios.IGNCR | termios.ICRNL | termios.IGNBRK)
+        if hasattr(termios, 'IUCLC'):
+            iflag &= ~termios.IUCLC
+        if hasattr(termios, 'PARMRK'):
+            iflag &= ~termios.PARMRK
+
+        # setup baud rate
+        try:
+            ispeed = ospeed = getattr(termios, 'B%s' % (self._baudrate))
+        except AttributeError:
+            try:
+                ispeed = ospeed = self.BAUDRATE_CONSTANTS[self._baudrate]
+            except KeyError:
+                #~ raise ValueError('Invalid baud rate: %r' % self._baudrate)
+                # may need custom baud rate, it isn't in our list.
+                ispeed = ospeed = getattr(termios, 'B38400')
+                try:
+                    custom_baud = int(self._baudrate)  # store for later
+                except ValueError:
+                    raise ValueError('Invalid baud rate: %r' % self._baudrate)
+                else:
+                    if custom_baud < 0:
+                        raise ValueError('Invalid baud rate: %r' % self._baudrate)
+
+        # setup char len
+        cflag &= ~termios.CSIZE
+        if self._bytesize == 8:
+            cflag |= termios.CS8
+        elif self._bytesize == 7:
+            cflag |= termios.CS7
+        elif self._bytesize == 6:
+            cflag |= termios.CS6
+        elif self._bytesize == 5:
+            cflag |= termios.CS5
+        else:
+            raise ValueError('Invalid char len: %r' % self._bytesize)
+        # setup stop bits
+        if self._stopbits == serial.STOPBITS_ONE:
+            cflag &= ~(termios.CSTOPB)
+        elif self._stopbits == serial.STOPBITS_ONE_POINT_FIVE:
+            cflag |= (termios.CSTOPB)  # XXX same as TWO.. there is no POSIX support for 1.5
+        elif self._stopbits == serial.STOPBITS_TWO:
+            cflag |= (termios.CSTOPB)
+        else:
+            raise ValueError('Invalid stop bit specification: %r' % self._stopbits)
+        # setup parity
+        iflag &= ~(termios.INPCK | termios.ISTRIP)
+        if self._parity == serial.PARITY_NONE:
+            cflag &= ~(termios.PARENB | termios.PARODD)
+        elif self._parity == serial.PARITY_EVEN:
+            cflag &= ~(termios.PARODD)
+            cflag |= (termios.PARENB)
+        elif self._parity == serial.PARITY_ODD:
+            cflag |= (termios.PARENB | termios.PARODD)
+        elif self._parity == serial.PARITY_MARK and plat[:5] == 'linux':
+            cflag |= (termios.PARENB | CMSPAR | termios.PARODD)
+        elif self._parity == serial.PARITY_SPACE and plat[:5] == 'linux':
+            cflag |= (termios.PARENB | CMSPAR)
+            cflag &= ~(termios.PARODD)
+        else:
+            raise ValueError('Invalid parity: %r' % self._parity)
+        # setup flow control
+        # xonxoff
+        if hasattr(termios, 'IXANY'):
+            if self._xonxoff:
+                iflag |= (termios.IXON | termios.IXOFF)  # |termios.IXANY)
+            else:
+                iflag &= ~(termios.IXON | termios.IXOFF | termios.IXANY)
+        else:
+            if self._xonxoff:
+                iflag |= (termios.IXON | termios.IXOFF)
+            else:
+                iflag &= ~(termios.IXON | termios.IXOFF)
+        # rtscts
+        if hasattr(termios, 'CRTSCTS'):
+            if self._rtscts:
+                cflag |= (termios.CRTSCTS)
+            else:
+                cflag &= ~(termios.CRTSCTS)
+        elif hasattr(termios, 'CNEW_RTSCTS'):   # try it with alternate constant name
+            if self._rtscts:
+                cflag |= (termios.CNEW_RTSCTS)
+            else:
+                cflag &= ~(termios.CNEW_RTSCTS)
+        # XXX should there be a warning if setting up rtscts (and xonxoff etc) fails??
+
+        # buffer
+        # vmin "minimal number of characters to be read. 0 for non blocking"
+        if vmin < 0 or vmin > 255:
+            raise ValueError('Invalid vmin: %r ' % vmin)
+        cc[termios.VMIN] = vmin
+        # vtime
+        if vtime < 0 or vtime > 255:
+            raise ValueError('Invalid vtime: %r' % vtime)
+        cc[termios.VTIME] = vtime
+        # activate settings
+        if force_update or [iflag, oflag, cflag, lflag, ispeed, ospeed, cc] != orig_attr:
+            termios.tcsetattr(
+                    self.fd,
+                    termios.TCSANOW,
+                    [iflag, oflag, cflag, lflag, ispeed, ospeed, cc])
+
+        # apply custom baud rate, if any
+        if custom_baud is not None:
+            self._set_special_baudrate(custom_baud)
+
+        if self._rs485_mode is not None:
+            self._set_rs485_mode(self._rs485_mode)
+
+    def close(self):
+        """Close port"""
+        if self.is_open:
+            if self.fd is not None:
+                os.close(self.fd)
+                self.fd = None
+            self.is_open = False
+
+    #  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -
+
+    @property
+    def in_waiting(self):
+        """Return the number of bytes currently in the input buffer."""
+        #~ s = fcntl.ioctl(self.fd, termios.FIONREAD, TIOCM_zero_str)
+        s = fcntl.ioctl(self.fd, TIOCINQ, TIOCM_zero_str)
+        return struct.unpack('I', s)[0]
+
+    # select based implementation, proved to work on many systems
+    def read(self, size=1):
+        """\
+        Read size bytes from the serial port. If a timeout is set it may
+        return less characters as requested. With no timeout it will block
+        until the requested number of bytes is read.
+        """
+        if not self.is_open:
+            raise portNotOpenError
+        read = bytearray()
+        timeout = self._timeout
+        while len(read) < size:
+            try:
+                start_time = time.time()
+                ready, _, _ = select.select([self.fd], [], [], timeout)
+                # If select was used with a timeout, and the timeout occurs, it
+                # returns with empty lists -> thus abort read operation.
+                # For timeout == 0 (non-blocking operation) also abort when
+                # there is nothing to read.
+                if not ready:
+                    break   # timeout
+                buf = os.read(self.fd, size - len(read))
+                # read should always return some data as select reported it was
+                # ready to read when we get to this point.
+                if not buf:
+                    # Disconnected devices, at least on Linux, show the
+                    # behavior that they are always ready to read immediately
+                    # but reading returns nothing.
+                    raise SerialException('device reports readiness to read but returned no data (device disconnected or multiple access on port?)')
+                read.extend(buf)
+                if timeout is not None:
+                    timeout -= time.time() - start_time
+                    if timeout <= 0:
+                        break
+            except OSError as e:
+                # this is for Python 3.x where select.error is a subclass of
+                # OSError ignore EAGAIN errors. all other errors are shown
+                if e.errno != errno.EAGAIN:
+                    raise SerialException('read failed: %s' % (e,))
+            except select.error as e:
+                # this is for Python 2.x
+                # ignore EAGAIN errors. all other errors are shown
+                # see also http://www.python.org/dev/peps/pep-3151/#select
+                if e[0] != errno.EAGAIN:
+                    raise SerialException('read failed: %s' % (e,))
+        return bytes(read)
+
+    def write(self, data):
+        """Output the given byte string over the serial port."""
+        if not self.is_open:
+            raise portNotOpenError
+        d = to_bytes(data)
+        tx_len = len(d)
+        if self._write_timeout is not None and self._write_timeout > 0:
+            timeout = time.time() + self._write_timeout
+        else:
+            timeout = None
+        while tx_len > 0:
+            try:
+                n = os.write(self.fd, d)
+                if timeout:
+                    # when timeout is set, use select to wait for being ready
+                    # with the time left as timeout
+                    timeleft = timeout - time.time()
+                    if timeleft < 0:
+                        raise writeTimeoutError
+                    _, ready, _ = select.select([], [self.fd], [], timeleft)
+                    if not ready:
+                        raise writeTimeoutError
+                else:
+                    # wait for write operation
+                    _, ready, _ = select.select([], [self.fd], [], None)
+                    if not ready:
+                        raise SerialException('write failed (select)')
+                d = d[n:]
+                tx_len -= n
+            except SerialException:
+                raise
+            except OSError as v:
+                if v.errno != errno.EAGAIN:
+                    raise SerialException('write failed: %s' % (v,))
+                # still calculate and check timeout
+                if timeout and timeout - time.time() < 0:
+                    raise writeTimeoutError
+        return len(data)
+
+    def flush(self):
+        """\
+        Flush of file like objects. In this case, wait until all data
+        is written.
+        """
+        if not self.is_open:
+            raise portNotOpenError
+        termios.tcdrain(self.fd)
+
+    def reset_input_buffer(self):
+        """Clear input buffer, discarding all that is in the buffer."""
+        if not self.is_open:
+            raise portNotOpenError
+        termios.tcflush(self.fd, termios.TCIFLUSH)
+
+    def reset_output_buffer(self):
+        """\
+        Clear output buffer, aborting the current output and discarding all
+        that is in the buffer.
+        """
+        if not self.is_open:
+            raise portNotOpenError
+        termios.tcflush(self.fd, termios.TCOFLUSH)
+
+    def send_break(self, duration=0.25):
+        """\
+        Send break condition. Timed, returns to idle state after given
+        duration.
+        """
+        if not self.is_open:
+            raise portNotOpenError
+        termios.tcsendbreak(self.fd, int(duration / 0.25))
+
+    def _update_break_state(self):
+        """\
+        Set break: Controls TXD. When active, no transmitting is possible.
+        """
+        if self._break_state:
+            fcntl.ioctl(self.fd, TIOCSBRK)
+        else:
+            fcntl.ioctl(self.fd, TIOCCBRK)
+
+    def _update_rts_state(self):
+        """Set terminal status line: Request To Send"""
+        if self._rts_state:
+            fcntl.ioctl(self.fd, TIOCMBIS, TIOCM_RTS_str)
+        else:
+            fcntl.ioctl(self.fd, TIOCMBIC, TIOCM_RTS_str)
+
+    def _update_dtr_state(self):
+        """Set terminal status line: Data Terminal Ready"""
+        if self._dtr_state:
+            fcntl.ioctl(self.fd, TIOCMBIS, TIOCM_DTR_str)
+        else:
+            fcntl.ioctl(self.fd, TIOCMBIC, TIOCM_DTR_str)
+
+    @property
+    def cts(self):
+        """Read terminal status line: Clear To Send"""
+        if not self.is_open:
+            raise portNotOpenError
+        s = fcntl.ioctl(self.fd, TIOCMGET, TIOCM_zero_str)
+        return struct.unpack('I', s)[0] & TIOCM_CTS != 0
+
+    @property
+    def dsr(self):
+        """Read terminal status line: Data Set Ready"""
+        if not self.is_open:
+            raise portNotOpenError
+        s = fcntl.ioctl(self.fd, TIOCMGET, TIOCM_zero_str)
+        return struct.unpack('I', s)[0] & TIOCM_DSR != 0
+
+    @property
+    def ri(self):
+        """Read terminal status line: Ring Indicator"""
+        if not self.is_open:
+            raise portNotOpenError
+        s = fcntl.ioctl(self.fd, TIOCMGET, TIOCM_zero_str)
+        return struct.unpack('I', s)[0] & TIOCM_RI != 0
+
+    @property
+    def cd(self):
+        """Read terminal status line: Carrier Detect"""
+        if not self.is_open:
+            raise portNotOpenError
+        s = fcntl.ioctl(self.fd, TIOCMGET, TIOCM_zero_str)
+        return struct.unpack('I', s)[0] & TIOCM_CD != 0
+
+    # - - platform specific - - - -
+
+    @property
+    def out_waiting(self):
+        """Return the number of bytes currently in the output buffer."""
+        #~ s = fcntl.ioctl(self.fd, termios.FIONREAD, TIOCM_zero_str)
+        s = fcntl.ioctl(self.fd, TIOCOUTQ, TIOCM_zero_str)
+        return struct.unpack('I', s)[0]
+
+    def nonblocking(self):
+        """internal - not portable!"""
+        if not self.is_open:
+            raise portNotOpenError
+        fcntl.fcntl(self.fd, fcntl.F_SETFL, os.O_NONBLOCK)
+
+    def fileno(self):
+        """\
+        For easier use of the serial port instance with select.
+        WARNING: this function is not portable to different platforms!
+        """
+        if not self.is_open:
+            raise portNotOpenError
+        return self.fd
+
+    def set_input_flow_control(self, enable=True):
+        """\
+        Manually control flow - when software flow control is enabled.
+        This will send XON (true) or XOFF (false) to the other device.
+        WARNING: this function is not portable to different platforms!
+        """
+        if not self.is_open:
+            raise portNotOpenError
+        if enable:
+            termios.tcflow(self.fd, termios.TCION)
+        else:
+            termios.tcflow(self.fd, termios.TCIOFF)
+
+    def set_output_flow_control(self, enable=True):
+        """\
+        Manually control flow of outgoing data - when hardware or software flow
+        control is enabled.
+        WARNING: this function is not portable to different platforms!
+        """
+        if not self.is_open:
+            raise portNotOpenError
+        if enable:
+            termios.tcflow(self.fd, termios.TCOON)
+        else:
+            termios.tcflow(self.fd, termios.TCOOFF)
+
+
+class PosixPollSerial(Serial):
+    """\
+    Poll based read implementation. Not all systems support poll properly.
+    However this one has better handling of errors, such as a device
+    disconnecting while it's in use (e.g. USB-serial unplugged).
+    """
+
+    def read(self, size=1):
+        """\
+        Read size bytes from the serial port. If a timeout is set it may
+        return less characters as requested. With no timeout it will block
+        until the requested number of bytes is read.
+        """
+        if self.fd is None:
+            raise portNotOpenError
+        read = bytearray()
+        poll = select.poll()
+        poll.register(self.fd, select.POLLIN | select.POLLERR | select.POLLHUP | select.POLLNVAL)
+        if size > 0:
+            while len(read) < size:
+                # print "\tread(): size",size, "have", len(read)    #debug
+                # wait until device becomes ready to read (or something fails)
+                for fd, event in poll.poll(self._timeout * 1000):
+                    if event & (select.POLLERR | select.POLLHUP | select.POLLNVAL):
+                        raise SerialException('device reports error (poll)')
+                    #  we don't care if it is select.POLLIN or timeout, that's
+                    #  handled below
+                buf = os.read(self.fd, size - len(read))
+                read.extend(buf)
+                if ((self._timeout is not None and self._timeout >= 0) or
+                        (self._inter_byte_timeout is not None and self._inter_byte_timeout > 0)) and not buf:
+                    break   # early abort on timeout
+        return bytes(read)
+
+
+class VTIMESerial(Serial):
+    """\
+    Implement timeout using vtime of tty device instead of using select.
+    This means that no inter character timeout can be specified and that
+    the error handling is degraded.
+
+    Overall timeout is disabled when inter-character timeout is used.
+    """
+
+    def _reconfigure_port(self, force_update=True):
+        """Set communication parameters on opened port."""
+        super(VTIMESerial, self)._reconfigure_port()
+        fcntl.fcntl(self.fd, fcntl.F_SETFL, 0) # clear O_NONBLOCK
+
+        if self._inter_byte_timeout is not None:
+            vmin = 1
+            vtime = int(self._inter_byte_timeout * 10)
+        else:
+            vmin = 0
+            vtime = int(self._timeout * 10)
+        try:
+            orig_attr = termios.tcgetattr(self.fd)
+            iflag, oflag, cflag, lflag, ispeed, ospeed, cc = orig_attr
+        except termios.error as msg:      # if a port is nonexistent but has a /dev file, it'll fail here
+            raise serial.SerialException("Could not configure port: %s" % msg)
+
+        if vtime < 0 or vtime > 255:
+            raise ValueError('Invalid vtime: %r' % vtime)
+        cc[termios.VTIME] = vtime
+        cc[termios.VMIN] = vmin
+
+        termios.tcsetattr(
+                self.fd,
+                termios.TCSANOW,
+                [iflag, oflag, cflag, lflag, ispeed, ospeed, cc])
+
+
+    def read(self, size=1):
+        """\
+        Read size bytes from the serial port. If a timeout is set it may
+        return less characters as requested. With no timeout it will block
+        until the requested number of bytes is read.
+        """
+        if not self.is_open:
+            raise portNotOpenError
+        read = bytearray()
+        while len(read) < size:
+            buf = os.read(self.fd, size - len(read))
+            if not buf:
+                break
+            read.extend(buf)
+        return bytes(read)
+
+
+if __name__ == '__main__':
+    s = Serial(0,
+               baudrate=19200,         # baud rate
+               bytesize=serial.EIGHTBITS,     # number of data bits
+               parity=serial.PARITY_EVEN,     # enable parity checking
+               stopbits=serial.STOPBITS_ONE,  # number of stop bits
+               timeout=3,              # set a timeout value, None for waiting forever
+               xonxoff=0,              # enable software flow control
+               rtscts=0,               # enable RTS/CTS flow control
+               )
+    s.rts = True
+    s.dtr = True
+    s.reset_input_buffer()
+    s.reset_output_buffer()
+    s.write('hello')
+    sys.stdout.write('%r\n' % s.read(5))
+    sys.stdout.write('%s\n' % s.inWaiting())
+    del s
diff --git a/src/devtools/datool/pyserial/serialutil.py b/src/devtools/datool/pyserial/serialutil.py
new file mode 100644
index 0000000..6f31efe
--- /dev/null
+++ b/src/devtools/datool/pyserial/serialutil.py
@@ -0,0 +1,635 @@
+#! python
+#
+# Base class and support functions used by various backends.
+#
+# This file is part of pySerial. https://github.com/pyserial/pyserial
+# (C) 2001-2015 Chris Liechti <cliechti@gmx.net>
+#
+# SPDX-License-Identifier:    BSD-3-Clause
+
+import io
+import time
+
+# ``memoryview`` was introduced in Python 2.7 and ``bytes(some_memoryview)``
+# isn't returning the contents (very unfortunate). Therefore we need special
+# cases and test for it. Ensure that there is a ``memoryview`` object for older
+# Python versions. This is easier than making every test dependent on its
+# existence.
+try:
+    memoryview
+except (NameError, AttributeError):
+    # implementation does not matter as we do not realy use it.
+    # it just must not inherit from something else we might care for.
+    class memoryview(object):
+        pass
+
+try:
+    unicode
+except (NameError, AttributeError):
+    unicode = str       # for Python 3
+
+
+# "for byte in data" fails for python3 as it returns ints instead of bytes
+def iterbytes(b):
+    """Iterate over bytes, returning bytes instead of ints (python3)"""
+    if isinstance(b, memoryview):
+        b = b.tobytes()
+    x = 0
+    while True:
+        a = b[x:x + 1]
+        x += 1
+        if a:
+            yield a
+        else:
+            break
+
+
+# all Python versions prior 3.x convert ``str([17])`` to '[17]' instead of '\x11'
+# so a simple ``bytes(sequence)`` doesn't work for all versions
+def to_bytes(seq):
+    """convert a sequence to a bytes type"""
+    if isinstance(seq, bytes):
+        return seq
+    elif isinstance(seq, bytearray):
+        return bytes(seq)
+    elif isinstance(seq, memoryview):
+        return seq.tobytes()
+    elif isinstance(seq, unicode):
+        raise TypeError('unicode strings are not supported, please encode to bytes: %r' % (seq,))
+    else:
+        b = bytearray()
+        for item in seq:
+            # this one handles int and bytes in Python 2.7
+            # add conversion in case of Python 3.x
+            if isinstance(item, bytes):
+                item = ord(item)
+            b.append(item)
+        return bytes(b)
+
+# create control bytes
+XON = to_bytes([17])
+XOFF = to_bytes([19])
+
+CR = to_bytes([13])
+LF = to_bytes([10])
+
+
+PARITY_NONE, PARITY_EVEN, PARITY_ODD, PARITY_MARK, PARITY_SPACE = 'N', 'E', 'O', 'M', 'S'
+STOPBITS_ONE, STOPBITS_ONE_POINT_FIVE, STOPBITS_TWO = (1, 1.5, 2)
+FIVEBITS, SIXBITS, SEVENBITS, EIGHTBITS = (5, 6, 7, 8)
+
+PARITY_NAMES = {
+    PARITY_NONE: 'None',
+    PARITY_EVEN: 'Even',
+    PARITY_ODD: 'Odd',
+    PARITY_MARK: 'Mark',
+    PARITY_SPACE: 'Space',
+}
+
+
+class SerialException(IOError):
+    """Base class for serial port related exceptions."""
+
+
+class SerialTimeoutException(SerialException):
+    """Write timeouts give an exception"""
+
+
+writeTimeoutError = SerialTimeoutException('Write timeout')
+portNotOpenError = SerialException('Attempting to use a port that is not open')
+
+
+class SerialBase(io.RawIOBase):
+    """\
+    Serial port base class. Provides __init__ function and properties to
+    get/set port settings.
+    """
+
+    # default values, may be overridden in subclasses that do not support all values
+    BAUDRATES = (50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
+                 9600, 19200, 38400, 57600, 115200, 230400, 460800, 500000,
+                 576000, 921600, 1000000, 1152000, 1500000, 2000000, 2500000,
+                 3000000, 3500000, 4000000)
+    BYTESIZES = (FIVEBITS, SIXBITS, SEVENBITS, EIGHTBITS)
+    PARITIES = (PARITY_NONE, PARITY_EVEN, PARITY_ODD, PARITY_MARK, PARITY_SPACE)
+    STOPBITS = (STOPBITS_ONE, STOPBITS_ONE_POINT_FIVE, STOPBITS_TWO)
+
+    def __init__(self,
+                 port=None,             # number of device, numbering starts at
+                                        # zero. if everything fails, the user
+                                        # can specify a device string, note
+                                        # that this isn't portable anymore
+                                        # port will be opened if one is specified
+                 baudrate=9600,         # baud rate
+                 bytesize=EIGHTBITS,    # number of data bits
+                 parity=PARITY_NONE,    # enable parity checking
+                 stopbits=STOPBITS_ONE, # number of stop bits
+                 timeout=None,          # set a timeout value, None to wait forever
+                 xonxoff=False,         # enable software flow control
+                 rtscts=False,          # enable RTS/CTS flow control
+                 write_timeout=None,    # set a timeout for writes
+                 dsrdtr=False,          # None: use rtscts setting, dsrdtr override if True or False
+                 inter_byte_timeout=None, # Inter-character timeout, None to disable
+                 **kwargs
+                 ):
+        """\
+        Initialize comm port object. If a port is given, then the port will be
+        opened immediately. Otherwise a Serial port object in closed state
+        is returned.
+        """
+
+        self.is_open = False
+        # correct values are assigned below through properties
+        self._port = None
+        self._baudrate = None
+        self._bytesize = None
+        self._parity = None
+        self._stopbits = None
+        self._timeout = None
+        self._write_timeout = None
+        self._xonxoff = None
+        self._rtscts = None
+        self._dsrdtr = None
+        self._inter_byte_timeout = None
+        self._rs485_mode = None  # disabled by default
+        self._rts_state = True
+        self._dtr_state = True
+        self._break_state = False
+
+        # assign values using get/set methods using the properties feature
+        self.port = port
+        self.baudrate = baudrate
+        self.bytesize = bytesize
+        self.parity = parity
+        self.stopbits = stopbits
+        self.timeout = timeout
+        self.write_timeout = write_timeout
+        self.xonxoff = xonxoff
+        self.rtscts = rtscts
+        self.dsrdtr = dsrdtr
+        self.inter_byte_timeout = inter_byte_timeout
+        # watch for backward compatible kwargs
+        if 'writeTimeout' in kwargs:
+            self.write_timeout = kwargs.pop('writeTimeout')
+        if 'interCharTimeout' in kwargs:
+            self.inter_byte_timeout = kwargs.pop('interCharTimeout')
+        if kwargs:
+            raise ValueError('unexpected keyword arguments: %r' % (kwargs,))
+
+        if port is not None:
+            self.open()
+
+    #  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -
+
+    # to be implemented by subclasses:
+    # def open(self):
+    # def close(self):
+
+    #  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -
+
+    @property
+    def port(self):
+        """\
+        Get the current port setting. The value that was passed on init or using
+        setPort() is passed back. See also the attribute portstr which contains
+        the name of the port as a string.
+        """
+        return self._port
+
+    @port.setter
+    def port(self, port):
+        """\
+        Change the port. The attribute portstr is set to a string that
+        contains the name of the port.
+        """
+
+        was_open = self.is_open
+        if was_open:
+            self.close()
+        self.portstr = port
+        self._port = port
+        self.name = self.portstr
+        if was_open:
+            self.open()
+
+
+    @property
+    def baudrate(self):
+        """Get the current baud rate setting."""
+        return self._baudrate
+
+    @baudrate.setter
+    def baudrate(self, baudrate):
+        """\
+        Change baud rate. It raises a ValueError if the port is open and the
+        baud rate is not possible. If the port is closed, then the value is
+        accepted and the exception is raised when the port is opened.
+        """
+        try:
+            b = int(baudrate)
+        except TypeError:
+            raise ValueError("Not a valid baudrate: %r" % (baudrate,))
+        else:
+            if b <= 0:
+                raise ValueError("Not a valid baudrate: %r" % (baudrate,))
+            self._baudrate = b
+            if self.is_open:
+                self._reconfigure_port()
+
+
+    @property
+    def bytesize(self):
+        """Get the current byte size setting."""
+        return self._bytesize
+
+    @bytesize.setter
+    def bytesize(self, bytesize):
+        """Change byte size."""
+        if bytesize not in self.BYTESIZES:
+            raise ValueError("Not a valid byte size: %r" % (bytesize,))
+        self._bytesize = bytesize
+        if self.is_open:
+            self._reconfigure_port()
+
+
+
+    @property
+    def parity(self):
+        """Get the current parity setting."""
+        return self._parity
+
+    @parity.setter
+    def parity(self, parity):
+        """Change parity setting."""
+        if parity not in self.PARITIES:
+            raise ValueError("Not a valid parity: %r" % (parity,))
+        self._parity = parity
+        if self.is_open:
+            self._reconfigure_port()
+
+
+
+    @property
+    def stopbits(self):
+        """Get the current stop bits setting."""
+        return self._stopbits
+
+    @stopbits.setter
+    def stopbits(self, stopbits):
+        """Change stop bits size."""
+        if stopbits not in self.STOPBITS:
+            raise ValueError("Not a valid stop bit size: %r" % (stopbits,))
+        self._stopbits = stopbits
+        if self.is_open:
+            self._reconfigure_port()
+
+
+    @property
+    def timeout(self):
+        """Get the current timeout setting."""
+        return self._timeout
+
+    @timeout.setter
+    def timeout(self, timeout):
+        """Change timeout setting."""
+        if timeout is not None:
+            try:
+                timeout + 1     # test if it's a number, will throw a TypeError if not...
+            except TypeError:
+                raise ValueError("Not a valid timeout: %r" % (timeout,))
+            if timeout < 0:
+                raise ValueError("Not a valid timeout: %r" % (timeout,))
+        self._timeout = timeout
+        if self.is_open:
+            self._reconfigure_port()
+
+
+    @property
+    def write_timeout(self):
+        """Get the current timeout setting."""
+        return self._write_timeout
+
+    @write_timeout.setter
+    def write_timeout(self, timeout):
+        """Change timeout setting."""
+        if timeout is not None:
+            if timeout < 0:
+                raise ValueError("Not a valid timeout: %r" % (timeout,))
+            try:
+                timeout + 1     # test if it's a number, will throw a TypeError if not...
+            except TypeError:
+                raise ValueError("Not a valid timeout: %r" % timeout)
+
+        self._write_timeout = timeout
+        if self.is_open:
+            self._reconfigure_port()
+
+
+    @property
+    def inter_byte_timeout(self):
+        """Get the current inter-character timeout setting."""
+        return self._inter_byte_timeout
+
+    @inter_byte_timeout.setter
+    def inter_byte_timeout(self, ic_timeout):
+        """Change inter-byte timeout setting."""
+        if ic_timeout is not None:
+            if ic_timeout < 0:
+                raise ValueError("Not a valid timeout: %r" % ic_timeout)
+            try:
+                ic_timeout + 1     # test if it's a number, will throw a TypeError if not...
+            except TypeError:
+                raise ValueError("Not a valid timeout: %r" % ic_timeout)
+
+        self._inter_byte_timeout = ic_timeout
+        if self.is_open:
+            self._reconfigure_port()
+
+
+    @property
+    def xonxoff(self):
+        """Get the current XON/XOFF setting."""
+        return self._xonxoff
+
+    @xonxoff.setter
+    def xonxoff(self, xonxoff):
+        """Change XON/XOFF setting."""
+        self._xonxoff = xonxoff
+        if self.is_open:
+            self._reconfigure_port()
+
+
+    @property
+    def rtscts(self):
+        """Get the current RTS/CTS flow control setting."""
+        return self._rtscts
+
+    @rtscts.setter
+    def rtscts(self, rtscts):
+        """Change RTS/CTS flow control setting."""
+        self._rtscts = rtscts
+        if self.is_open:
+            self._reconfigure_port()
+
+
+    @property
+    def dsrdtr(self):
+        """Get the current DSR/DTR flow control setting."""
+        return self._dsrdtr
+
+    @dsrdtr.setter
+    def dsrdtr(self, dsrdtr=None):
+        """Change DsrDtr flow control setting."""
+        if dsrdtr is None:
+            # if not set, keep backwards compatibility and follow rtscts setting
+            self._dsrdtr = self._rtscts
+        else:
+            # if defined independently, follow its value
+            self._dsrdtr = dsrdtr
+        if self.is_open:
+            self._reconfigure_port()
+
+
+    @property
+    def rts(self):
+        return self._rts_state
+
+    @rts.setter
+    def rts(self, value):
+        self._rts_state = value
+        if self.is_open:
+            self._update_rts_state()
+
+    @property
+    def dtr(self):
+        return self._dtr_state
+
+    @dtr.setter
+    def dtr(self, value):
+        self._dtr_state = value
+        if self.is_open:
+            self._update_dtr_state()
+
+    @property
+    def break_condition(self):
+        return self._break_state
+
+    @break_condition.setter
+    def break_condition(self, value):
+        self._break_state = value
+        if self.is_open:
+            self._update_break_state()
+
+    #  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -
+    # functions useful for RS-485 adapters
+
+    @property
+    def rs485_mode(self):
+        """\
+        Enable RS485 mode and apply new settings, set to None to disable.
+        See serial.rs485.RS485Settings for more info about the value.
+        """
+        return self._rs485_mode
+
+    @rs485_mode.setter
+    def rs485_mode(self, rs485_settings):
+        self._rs485_mode = rs485_settings
+        if self.is_open:
+            self._reconfigure_port()
+
+    #  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -
+
+    _SAVED_SETTINGS = ('baudrate', 'bytesize', 'parity', 'stopbits', 'xonxoff',
+                       'dsrdtr', 'rtscts', 'timeout', 'write_timeout',
+                       'inter_byte_timeout')
+
+    def get_settings(self):
+        """\
+        Get current port settings as a dictionary. For use with
+        apply_settings().
+        """
+        return dict([(key, getattr(self, '_' + key)) for key in self._SAVED_SETTINGS])
+
+    def apply_settings(self, d):
+        """\
+        Apply stored settings from a dictionary returned from
+        get_settings(). It's allowed to delete keys from the dictionary. These
+        values will simply left unchanged.
+        """
+        for key in self._SAVED_SETTINGS:
+            if key in d and d[key] != getattr(self, '_' + key):   # check against internal "_" value
+                setattr(self, key, d[key])          # set non "_" value to use properties write function
+
+    #  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -
+
+    def __repr__(self):
+        """String representation of the current port settings and its state."""
+        return "%s<id=0x%x, open=%s>(port=%r, baudrate=%r, bytesize=%r, parity=%r, stopbits=%r, timeout=%r, xonxoff=%r, rtscts=%r, dsrdtr=%r)" % (
+                self.__class__.__name__,
+                id(self),
+                self.is_open,
+                self.portstr,
+                self.baudrate,
+                self.bytesize,
+                self.parity,
+                self.stopbits,
+                self.timeout,
+                self.xonxoff,
+                self.rtscts,
+                self.dsrdtr,
+        )
+
+    #  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -
+    # compatibility with io library
+
+    def readable(self):
+        return True
+
+    def writable(self):
+        return True
+
+    def seekable(self):
+        return False
+
+    def readinto(self, b):
+        data = self.read(len(b))
+        n = len(data)
+        try:
+            b[:n] = data
+        except TypeError as err:
+            import array
+            if not isinstance(b, array.array):
+                raise err
+            b[:n] = array.array('b', data)
+        return n
+
+    #  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -
+    # context manager
+
+    def __enter__(self):
+        return self
+
+    def __exit__(self, *args, **kwargs):
+        self.close()
+
+    #  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -
+
+    def send_break(self, duration=0.25):
+        """\
+        Send break condition. Timed, returns to idle state after given
+        duration.
+        """
+        if not self.is_open:
+            raise portNotOpenError
+        self.break_condition = True
+        time.sleep(duration)
+        self.break_condition = False
+
+    #  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -
+    # backwards compatibility / deprecated functions
+
+    def flushInput(self):
+        self.reset_input_buffer()
+
+    def flushOutput(self):
+        self.reset_output_buffer()
+
+    def inWaiting(self):
+        return self.in_waiting
+
+    def sendBreak(self, duration=0.25):
+        self.send_break(duration)
+
+    def setRTS(self, value=1):
+        self.rts = value
+
+    def setDTR(self, value=1):
+        self.dtr = value
+
+    def getCTS(self):
+        return self.cts
+
+    def getDSR(self):
+        return self.dsr
+
+    def getRI(self):
+        return self.ri
+
+    def getCD(self):
+        return self.cd
+
+    @property
+    def writeTimeout(self):
+        return self.write_timeout
+
+    @writeTimeout.setter
+    def writeTimeout(self, timeout):
+        self.write_timeout = timeout
+
+    @property
+    def interCharTimeout(self):
+        return self.inter_byte_timeout
+
+    @interCharTimeout.setter
+    def interCharTimeout(self, interCharTimeout):
+        self.inter_byte_timeout = interCharTimeout
+
+    def getSettingsDict(self):
+        return self.get_settings()
+
+    def applySettingsDict(self, d):
+        self.apply_settings(d)
+
+    def isOpen(self):
+        return self.is_open
+
+    #  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -
+    # additional functionality
+
+    def read_all(self):
+        """\
+        Read all bytes currently available in the buffer of the OS.
+        """
+        return self.read(self.in_waiting)
+
+    def read_until(self, terminator=LF, size=None):
+        """\
+        Read until a termination sequence is found ('\n' by default), the size
+        is exceeded or until timeout occurs.
+        """
+        lenterm = len(terminator)
+        line = bytearray()
+        while True:
+            c = self.read(1)
+            if c:
+                line += c
+                if line[-lenterm:] == terminator:
+                    break
+                if size is not None and len(line) >= size:
+                    break
+            else:
+                break
+        return bytes(line)
+
+    def iread_until(self, *args, **kwargs):
+        """\
+        Read lines, implemented as generator. It will raise StopIteration on
+        timeout (empty read).
+        """
+        while True:
+            line = self.read_until(*args, **kwargs)
+            if not line:
+                break
+            yield line
+
+
+#  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -
+if __name__ == '__main__':
+    import sys
+    s = SerialBase()
+    sys.stdout.write('port name:  %s\n' % s.name)
+    sys.stdout.write('baud rates: %s\n' % s.BAUDRATES)
+    sys.stdout.write('byte sizes: %s\n' % s.BYTESIZES)
+    sys.stdout.write('parities:   %s\n' % s.PARITIES)
+    sys.stdout.write('stop bits:  %s\n' % s.STOPBITS)
+    sys.stdout.write('%s\n' % s)
diff --git a/src/devtools/datool/pyserial/serialwin32.py b/src/devtools/datool/pyserial/serialwin32.py
new file mode 100644
index 0000000..309264e
--- /dev/null
+++ b/src/devtools/datool/pyserial/serialwin32.py
@@ -0,0 +1,443 @@
+#! python
+#
+# backend for Windows ("win32" incl. 32/64 bit support)
+#
+# (C) 2001-2015 Chris Liechti <cliechti@gmx.net>
+#
+# This file is part of pySerial. https://github.com/pyserial/pyserial
+# SPDX-License-Identifier:    BSD-3-Clause
+#
+# Initial patch to use ctypes by Giovanni Bajo <rasky@develer.com>
+
+import ctypes
+import time
+from pyserial import win32
+
+import pyserial as serial
+from pyserial.serialutil import SerialBase, SerialException, to_bytes, portNotOpenError, writeTimeoutError
+
+
+class Serial(SerialBase):
+    """Serial port implementation for Win32 based on ctypes."""
+
+    BAUDRATES = (50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
+                 9600, 19200, 38400, 57600, 115200)
+
+    def __init__(self, *args, **kwargs):
+        super(SerialBase, self).__init__()
+        self._port_handle = None
+        self._overlapped_read = None
+        self._overlapped_write = None
+        SerialBase.__init__(self, *args, **kwargs)
+
+    def open(self):
+        """\
+        Open port with current settings. This may throw a SerialException
+        if the port cannot be opened.
+        """
+        if self._port is None:
+            raise SerialException("Port must be configured before it can be used.")
+        if self.is_open:
+            raise SerialException("Port is already open.")
+        # the "\\.\COMx" format is required for devices other than COM1-COM8
+        # not all versions of windows seem to support this properly
+        # so that the first few ports are used with the DOS device name
+        port = self.name
+        try:
+            if port.upper().startswith('COM') and int(port[3:]) > 8:
+                port = '\\\\.\\' + port
+        except ValueError:
+            # for like COMnotanumber
+            pass
+        self._port_handle = win32.CreateFile(
+                port,
+                win32.GENERIC_READ | win32.GENERIC_WRITE,
+                0,  # exclusive access
+                None,  # no security
+                win32.OPEN_EXISTING,
+                win32.FILE_ATTRIBUTE_NORMAL | win32.FILE_FLAG_OVERLAPPED,
+                0)
+        if self._port_handle == win32.INVALID_HANDLE_VALUE:
+            self._port_handle = None    # 'cause __del__ is called anyway
+            raise SerialException("could not open port %r: %r" % (self.portstr, ctypes.WinError()))
+
+        try:
+            self._overlapped_read = win32.OVERLAPPED()
+            self._overlapped_read.hEvent = win32.CreateEvent(None, 1, 0, None)
+            self._overlapped_write = win32.OVERLAPPED()
+            #~ self._overlapped_write.hEvent = win32.CreateEvent(None, 1, 0, None)
+            self._overlapped_write.hEvent = win32.CreateEvent(None, 0, 0, None)
+
+            # Setup a 4k buffer
+            win32.SetupComm(self._port_handle, 4096, 4096)
+
+            # Save original timeout values:
+            self._orgTimeouts = win32.COMMTIMEOUTS()
+            win32.GetCommTimeouts(self._port_handle, ctypes.byref(self._orgTimeouts))
+
+            self._reconfigure_port()
+
+            # Clear buffers:
+            # Remove anything that was there
+            win32.PurgeComm(
+                    self._port_handle,
+                    win32.PURGE_TXCLEAR | win32.PURGE_TXABORT |
+                    win32.PURGE_RXCLEAR | win32.PURGE_RXABORT)
+        except:
+            try:
+                self._close()
+            except:
+                # ignore any exception when closing the port
+                # also to keep original exception that happened when setting up
+                pass
+            self._port_handle = None
+            raise
+        else:
+            self.is_open = True
+
+    def _reconfigure_port(self):
+        """Set communication parameters on opened port."""
+        if not self._port_handle:
+            raise SerialException("Can only operate on a valid port handle")
+
+        # Set Windows timeout values
+        # timeouts is a tuple with the following items:
+        # (ReadIntervalTimeout,ReadTotalTimeoutMultiplier,
+        #  ReadTotalTimeoutConstant,WriteTotalTimeoutMultiplier,
+        #  WriteTotalTimeoutConstant)
+        timeouts = win32.COMMTIMEOUTS()
+        if self._timeout is None:
+            pass  # default of all zeros is OK
+        elif self._timeout == 0:
+            timeouts.ReadIntervalTimeout = win32.MAXDWORD
+        else:
+            timeouts.ReadTotalTimeoutConstant = max(int(self._timeout * 1000), 1)
+        if self._timeout != 0 and self._inter_byte_timeout is not None:
+            timeouts.ReadIntervalTimeout = max(int(self._inter_byte_timeout * 1000), 1)
+
+        if self._write_timeout is None:
+            pass
+        elif self._write_timeout == 0:
+            timeouts.WriteTotalTimeoutConstant = win32.MAXDWORD
+        else:
+            timeouts.WriteTotalTimeoutConstant = max(int(self._write_timeout * 1000), 1)
+        win32.SetCommTimeouts(self._port_handle, ctypes.byref(timeouts))
+
+        win32.SetCommMask(self._port_handle, win32.EV_ERR)
+
+        # Setup the connection info.
+        # Get state and modify it:
+        comDCB = win32.DCB()
+        win32.GetCommState(self._port_handle, ctypes.byref(comDCB))
+        comDCB.BaudRate = self._baudrate
+
+        if self._bytesize == serial.FIVEBITS:
+            comDCB.ByteSize = 5
+        elif self._bytesize == serial.SIXBITS:
+            comDCB.ByteSize = 6
+        elif self._bytesize == serial.SEVENBITS:
+            comDCB.ByteSize = 7
+        elif self._bytesize == serial.EIGHTBITS:
+            comDCB.ByteSize = 8
+        else:
+            raise ValueError("Unsupported number of data bits: %r" % self._bytesize)
+
+        if self._parity == serial.PARITY_NONE:
+            comDCB.Parity = win32.NOPARITY
+            comDCB.fParity = 0  # Disable Parity Check
+        elif self._parity == serial.PARITY_EVEN:
+            comDCB.Parity = win32.EVENPARITY
+            comDCB.fParity = 1  # Enable Parity Check
+        elif self._parity == serial.PARITY_ODD:
+            comDCB.Parity = win32.ODDPARITY
+            comDCB.fParity = 1  # Enable Parity Check
+        elif self._parity == serial.PARITY_MARK:
+            comDCB.Parity = win32.MARKPARITY
+            comDCB.fParity = 1  # Enable Parity Check
+        elif self._parity == serial.PARITY_SPACE:
+            comDCB.Parity = win32.SPACEPARITY
+            comDCB.fParity = 1  # Enable Parity Check
+        else:
+            raise ValueError("Unsupported parity mode: %r" % self._parity)
+
+        if self._stopbits == serial.STOPBITS_ONE:
+            comDCB.StopBits = win32.ONESTOPBIT
+        elif self._stopbits == serial.STOPBITS_ONE_POINT_FIVE:
+            comDCB.StopBits = win32.ONE5STOPBITS
+        elif self._stopbits == serial.STOPBITS_TWO:
+            comDCB.StopBits = win32.TWOSTOPBITS
+        else:
+            raise ValueError("Unsupported number of stop bits: %r" % self._stopbits)
+
+        comDCB.fBinary = 1  # Enable Binary Transmission
+        # Char. w/ Parity-Err are replaced with 0xff (if fErrorChar is set to TRUE)
+        if self._rs485_mode is None:
+            if self._rtscts:
+                comDCB.fRtsControl = win32.RTS_CONTROL_HANDSHAKE
+            else:
+                comDCB.fRtsControl = win32.RTS_CONTROL_ENABLE if self._rts_state else win32.RTS_CONTROL_DISABLE
+            comDCB.fOutxCtsFlow = self._rtscts
+        else:
+            # checks for unsupported settings
+            # XXX verify if platform really does not have a setting for those
+            if not self._rs485_mode.rts_level_for_tx:
+                raise ValueError(
+                        'Unsupported value for RS485Settings.rts_level_for_tx: %r' % (
+                            self._rs485_mode.rts_level_for_tx,))
+            if self._rs485_mode.rts_level_for_rx:
+                raise ValueError(
+                        'Unsupported value for RS485Settings.rts_level_for_rx: %r' % (
+                            self._rs485_mode.rts_level_for_rx,))
+            if self._rs485_mode.delay_before_tx is not None:
+                raise ValueError(
+                        'Unsupported value for RS485Settings.delay_before_tx: %r' % (
+                            self._rs485_mode.delay_before_tx,))
+            if self._rs485_mode.delay_before_rx is not None:
+                raise ValueError(
+                        'Unsupported value for RS485Settings.delay_before_rx: %r' % (
+                            self._rs485_mode.delay_before_rx,))
+            if self._rs485_mode.loopback:
+                raise ValueError(
+                        'Unsupported value for RS485Settings.loopback: %r' % (
+                            self._rs485_mode.loopback,))
+            comDCB.fRtsControl = win32.RTS_CONTROL_TOGGLE
+            comDCB.fOutxCtsFlow = 0
+
+        if self._dsrdtr:
+            comDCB.fDtrControl = win32.DTR_CONTROL_HANDSHAKE
+        else:
+            comDCB.fDtrControl = win32.DTR_CONTROL_ENABLE if self._dtr_state else win32.DTR_CONTROL_DISABLE
+        comDCB.fOutxDsrFlow = self._dsrdtr
+        comDCB.fOutX = self._xonxoff
+        comDCB.fInX = self._xonxoff
+        comDCB.fNull = 0
+        comDCB.fErrorChar = 0
+        comDCB.fAbortOnError = 0
+        comDCB.XonChar = serial.XON
+        comDCB.XoffChar = serial.XOFF
+
+        if not win32.SetCommState(self._port_handle, ctypes.byref(comDCB)):
+            raise SerialException("Cannot configure port, something went wrong. Original message: %r" % ctypes.WinError())
+
+    #~ def __del__(self):
+        #~ self.close()
+
+    def _close(self):
+        """internal close port helper"""
+        if self._port_handle:
+            # Restore original timeout values:
+            win32.SetCommTimeouts(self._port_handle, self._orgTimeouts)
+            # Close COM-Port:
+            win32.CloseHandle(self._port_handle)
+            if self._overlapped_read is not None:
+                win32.CloseHandle(self._overlapped_read.hEvent)
+                self._overlapped_read = None
+            if self._overlapped_write is not None:
+                win32.CloseHandle(self._overlapped_write.hEvent)
+                self._overlapped_write = None
+            self._port_handle = None
+
+    def close(self):
+        """Close port"""
+        if self.is_open:
+            self._close()
+            self.is_open = False
+
+    #  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -
+
+    @property
+    def in_waiting(self):
+        """Return the number of bytes currently in the input buffer."""
+        flags = win32.DWORD()
+        comstat = win32.COMSTAT()
+        if not win32.ClearCommError(self._port_handle, ctypes.byref(flags), ctypes.byref(comstat)):
+            raise SerialException('call to ClearCommError failed')
+        return comstat.cbInQue
+
+    def read(self, size=1):
+        """\
+        Read size bytes from the serial port. If a timeout is set it may
+        return less characters as requested. With no timeout it will block
+        until the requested number of bytes is read."""
+        if not self._port_handle:
+            raise portNotOpenError
+        if size > 0:
+            win32.ResetEvent(self._overlapped_read.hEvent)
+            flags = win32.DWORD()
+            comstat = win32.COMSTAT()
+            if not win32.ClearCommError(self._port_handle, ctypes.byref(flags), ctypes.byref(comstat)):
+                raise SerialException('call to ClearCommError failed')
+            if self.timeout == 0:
+                n = min(comstat.cbInQue, size)
+                if n > 0:
+                    buf = ctypes.create_string_buffer(n)
+                    rc = win32.DWORD()
+                    read_ok = win32.ReadFile(self._port_handle, buf, n, ctypes.byref(rc), ctypes.byref(self._overlapped_read))
+                    if not read_ok and win32.GetLastError() not in (win32.ERROR_SUCCESS, win32.ERROR_IO_PENDING):
+                        raise SerialException("ReadFile failed (%r)" % ctypes.WinError())
+                    win32.GetOverlappedResult(self._port_handle, ctypes.byref(self._overlapped_read), ctypes.byref(rc), True)
+                    read = buf.raw[:rc.value]
+                else:
+                    read = bytes()
+            else:
+                buf = ctypes.create_string_buffer(size)
+                rc = win32.DWORD()
+                read_ok = win32.ReadFile(self._port_handle, buf, size, ctypes.byref(rc), ctypes.byref(self._overlapped_read))
+                if not read_ok and win32.GetLastError() not in (win32.ERROR_SUCCESS, win32.ERROR_IO_PENDING):
+                    raise SerialException("ReadFile failed (%r)" % ctypes.WinError())
+                win32.GetOverlappedResult(self._port_handle, ctypes.byref(self._overlapped_read), ctypes.byref(rc), True)
+                read = buf.raw[:rc.value]
+        else:
+            read = bytes()
+        return bytes(read)
+
+    def write(self, data):
+        """Output the given byte string over the serial port."""
+        if not self._port_handle:
+            raise portNotOpenError
+        #~ if not isinstance(data, (bytes, bytearray)):
+            #~ raise TypeError('expected %s or bytearray, got %s' % (bytes, type(data)))
+        # convert data (needed in case of memoryview instance: Py 3.1 io lib), ctypes doesn't like memoryview
+        data = to_bytes(data)
+        if data:
+            #~ win32event.ResetEvent(self._overlapped_write.hEvent)
+            n = win32.DWORD()
+            err = win32.WriteFile(self._port_handle, data, len(data), ctypes.byref(n), self._overlapped_write)
+            if not err and win32.GetLastError() != win32.ERROR_IO_PENDING:
+                raise SerialException("WriteFile failed (%r)" % ctypes.WinError())
+            if self._write_timeout != 0:  # if blocking (None) or w/ write timeout (>0)
+                # Wait for the write to complete.
+                #~ win32.WaitForSingleObject(self._overlapped_write.hEvent, win32.INFINITE)
+                err = win32.GetOverlappedResult(self._port_handle, self._overlapped_write, ctypes.byref(n), True)
+                if n.value != len(data):
+                    raise writeTimeoutError
+            return n.value
+        else:
+            return 0
+
+    def flush(self):
+        """\
+        Flush of file like objects. In this case, wait until all data
+        is written.
+        """
+        while self.out_waiting:
+            time.sleep(0.05)
+        # XXX could also use WaitCommEvent with mask EV_TXEMPTY, but it would
+        # require overlapped IO and its also only possible to set a single mask
+        # on the port---
+
+    def reset_input_buffer(self):
+        """Clear input buffer, discarding all that is in the buffer."""
+        if not self._port_handle:
+            raise portNotOpenError
+        win32.PurgeComm(self._port_handle, win32.PURGE_RXCLEAR | win32.PURGE_RXABORT)
+
+    def reset_output_buffer(self):
+        """\
+        Clear output buffer, aborting the current output and discarding all
+        that is in the buffer.
+        """
+        if not self._port_handle:
+            raise portNotOpenError
+        win32.PurgeComm(self._port_handle, win32.PURGE_TXCLEAR | win32.PURGE_TXABORT)
+
+    def _update_break_state(self):
+        """Set break: Controls TXD. When active, to transmitting is possible."""
+        if not self._port_handle:
+            raise portNotOpenError
+        if self._break_state:
+            win32.SetCommBreak(self._port_handle)
+        else:
+            win32.ClearCommBreak(self._port_handle)
+
+    def _update_rts_state(self):
+        """Set terminal status line: Request To Send"""
+        if self._rts_state:
+            win32.EscapeCommFunction(self._port_handle, win32.SETRTS)
+        else:
+            win32.EscapeCommFunction(self._port_handle, win32.CLRRTS)
+
+    def _update_dtr_state(self):
+        """Set terminal status line: Data Terminal Ready"""
+        if self._dtr_state:
+            win32.EscapeCommFunction(self._port_handle, win32.SETDTR)
+        else:
+            win32.EscapeCommFunction(self._port_handle, win32.CLRDTR)
+
+    def _GetCommModemStatus(self):
+        if not self._port_handle:
+            raise portNotOpenError
+        stat = win32.DWORD()
+        win32.GetCommModemStatus(self._port_handle, ctypes.byref(stat))
+        return stat.value
+
+    @property
+    def cts(self):
+        """Read terminal status line: Clear To Send"""
+        return win32.MS_CTS_ON & self._GetCommModemStatus() != 0
+
+    @property
+    def dsr(self):
+        """Read terminal status line: Data Set Ready"""
+        return win32.MS_DSR_ON & self._GetCommModemStatus() != 0
+
+    @property
+    def ri(self):
+        """Read terminal status line: Ring Indicator"""
+        return win32.MS_RING_ON & self._GetCommModemStatus() != 0
+
+    @property
+    def cd(self):
+        """Read terminal status line: Carrier Detect"""
+        return win32.MS_RLSD_ON & self._GetCommModemStatus() != 0
+
+    # - - platform specific - - - -
+
+    def set_buffer_size(self, rx_size=4096, tx_size=None):
+        """\
+        Recommend a buffer size to the driver (device driver can ignore this
+        value). Must be called before the port is opended.
+        """
+        if tx_size is None:
+            tx_size = rx_size
+        win32.SetupComm(self._port_handle, rx_size, tx_size)
+
+    def set_output_flow_control(self, enable=True):
+        """\
+        Manually control flow - when software flow control is enabled.
+        This will do the same as if XON (true) or XOFF (false) are received
+        from the other device and control the transmission accordingly.
+        WARNING: this function is not portable to different platforms!
+        """
+        if not self._port_handle:
+            raise portNotOpenError
+        if enable:
+            win32.EscapeCommFunction(self._port_handle, win32.SETXON)
+        else:
+            win32.EscapeCommFunction(self._port_handle, win32.SETXOFF)
+
+    @property
+    def out_waiting(self):
+        """Return how many bytes the in the outgoing buffer"""
+        flags = win32.DWORD()
+        comstat = win32.COMSTAT()
+        if not win32.ClearCommError(self._port_handle, ctypes.byref(flags), ctypes.byref(comstat)):
+            raise SerialException('call to ClearCommError failed')
+        return comstat.cbOutQue
+
+
+# Nur Testfunktion!!
+if __name__ == '__main__':
+    import sys
+    s = Serial(0)
+    sys.stdout.write("%s\n" % s)
+
+    s = Serial()
+    sys.stdout.write("%s\n" % s)
+
+    s.baudrate = 19200
+    s.databits = 7
+    s.close()
+    s.port = 0
+    s.open()
+    sys.stdout.write("%s\n" % s)
diff --git a/src/devtools/datool/pyserial/tools/__init__.py b/src/devtools/datool/pyserial/tools/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/devtools/datool/pyserial/tools/__init__.py
diff --git a/src/devtools/datool/pyserial/tools/hexlify_codec.py b/src/devtools/datool/pyserial/tools/hexlify_codec.py
new file mode 100644
index 0000000..3200701
--- /dev/null
+++ b/src/devtools/datool/pyserial/tools/hexlify_codec.py
@@ -0,0 +1,98 @@
+#! python
+#
+# This is a codec to create and decode hexdumps with spaces between characters. used by miniterm.
+#
+# This file is part of pySerial. https://github.com/pyserial/pyserial
+# (C) 2011 Chris Liechti <cliechti@gmx.net>
+#
+# SPDX-License-Identifier:    BSD-3-Clause
+"""\
+Python 'hex' Codec - 2-digit hex with spaces content transfer encoding.
+"""
+
+import codecs
+import pyserial as serial
+
+HEXDIGITS = '0123456789ABCDEF'
+
+### Codec APIs
+
+
+def hex_encode(input, errors='strict'):
+    return (serial.to_bytes([int(h, 16) for h in input.split()]), len(input))
+
+
+def hex_decode(input, errors='strict'):
+    return (''.join('{:02X} '.format(b) for b in input), len(input))
+
+
+class Codec(codecs.Codec):
+    def encode(self, input, errors='strict'):
+        return serial.to_bytes([int(h, 16) for h in input.split()])
+
+    def decode(self, input, errors='strict'):
+        return ''.join('{:02X} '.format(b) for b in input)
+
+
+class IncrementalEncoder(codecs.IncrementalEncoder):
+
+    def __init__(self, errors='strict'):
+        self.errors = errors
+        self.state = 0
+
+    def reset(self):
+        self.state = 0
+
+    def getstate(self):
+        return self.state
+
+    def setstate(self, state):
+        self.state = state
+
+    def encode(self, input, final=False):
+        state = self.state
+        encoded = []
+        for c in input.upper():
+            if c in HEXDIGITS:
+                z = HEXDIGITS.index(c)
+                if state:
+                    encoded.append(z + (state & 0xf0))
+                    state = 0
+                else:
+                    state = 0x100 + (z << 4)
+            elif c == ' ':      # allow spaces to separate values
+                if state and self.errors == 'strict':
+                    raise UnicodeError('odd number of hex digits')
+                state = 0
+            else:
+                if self.errors == 'strict':
+                    raise UnicodeError('non-hex digit found: %r' % c)
+        self.state = state
+        return serial.to_bytes(encoded)
+
+
+class IncrementalDecoder(codecs.IncrementalDecoder):
+    def decode(self, input, final=False):
+        return ''.join('{:02X} '.format(b) for b in input)
+
+
+class StreamWriter(Codec, codecs.StreamWriter):
+    pass
+
+
+class StreamReader(Codec, codecs.StreamReader):
+    pass
+
+
+### encodings module API
+def getregentry():
+    return codecs.CodecInfo(
+        name='hexlify',
+        encode=hex_encode,
+        decode=hex_decode,
+        incrementalencoder=IncrementalEncoder,
+        incrementaldecoder=IncrementalDecoder,
+        streamwriter=StreamWriter,
+        streamreader=StreamReader,
+        _is_text_encoding=True,
+    )
diff --git a/src/devtools/datool/pyserial/tools/list_ports.py b/src/devtools/datool/pyserial/tools/list_ports.py
new file mode 100644
index 0000000..d174f2a
--- /dev/null
+++ b/src/devtools/datool/pyserial/tools/list_ports.py
@@ -0,0 +1,103 @@
+#!/usr/bin/env python
+#
+# Serial port enumeration. Console tool and backend selection.
+#
+# This file is part of pySerial. https://github.com/pyserial/pyserial
+# (C) 2011-2015 Chris Liechti <cliechti@gmx.net>
+#
+# SPDX-License-Identifier:    BSD-3-Clause
+
+"""\
+This module will provide a function called comports that returns an
+iterable (generator or list) that will enumerate available com ports. Note that
+on some systems non-existent ports may be listed.
+
+Additionally a grep function is supplied that can be used to search for ports
+based on their descriptions or hardware ID.
+"""
+
+import sys
+import os
+import re
+
+# chose an implementation, depending on os
+#~ if sys.platform == 'cli':
+#~ else:
+if os.name == 'nt':  # sys.platform == 'win32':
+    from pyserial.tools.list_ports_windows import comports
+elif os.name == 'posix':
+    from pyserial.tools.list_ports_posix import comports
+#~ elif os.name == 'java':
+else:
+    raise ImportError("Sorry: no implementation for your platform ('%s') available" % (os.name,))
+
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+
+def grep(regexp):
+    """\
+    Search for ports using a regular expression. Port name, description and
+    hardware ID are searched. The function returns an iterable that returns the
+    same tuples as comport() would do.
+    """
+    r = re.compile(regexp, re.I)
+    for info in comports():
+        port, desc, hwid = info
+        if r.search(port) or r.search(desc) or r.search(hwid):
+            yield info
+
+
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+def main():
+    import argparse
+
+    parser = argparse.ArgumentParser(description='Serial port enumeration')
+
+    parser.add_argument(
+            'regexp',
+            nargs='?',
+            help='only show ports that match this regex')
+
+    parser.add_argument(
+            '-v', '--verbose',
+            action='store_true',
+            help='show more messages')
+
+    parser.add_argument(
+            '-q', '--quiet',
+            action='store_true',
+            help='suppress all messages')
+
+    parser.add_argument(
+            '-n',
+            type=int,
+            help='only output the N-th entry')
+
+    args = parser.parse_args()
+
+    hits = 0
+    # get iteraror w/ or w/o filter
+    if args.regexp:
+        if not args.quiet:
+            sys.stderr.write("Filtered list with regexp: %r\n" % (args.regexp,))
+        iterator = sorted(grep(args.regexp))
+    else:
+        iterator = sorted(comports())
+    # list them
+    for n, (port, desc, hwid) in enumerate(iterator, 1):
+        if args.n is None or args.n == n:
+            sys.stdout.write("{:20}\n".format(port))
+            if args.verbose:
+                sys.stdout.write("    desc: {}\n".format(desc))
+                sys.stdout.write("    hwid: {}\n".format(hwid))
+        hits += 1
+    if not args.quiet:
+        if hits:
+            sys.stderr.write("{} ports found\n".format(hits))
+        else:
+            sys.stderr.write("no ports found\n")
+
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+# test
+if __name__ == '__main__':
+    main()
diff --git a/src/devtools/datool/pyserial/tools/list_ports_common.py b/src/devtools/datool/pyserial/tools/list_ports_common.py
new file mode 100644
index 0000000..640b2a1
--- /dev/null
+++ b/src/devtools/datool/pyserial/tools/list_ports_common.py
@@ -0,0 +1,90 @@
+#!/usr/bin/env python
+#
+# This is a helper module for the various platform dependent list_port
+# implementations.
+#
+# This file is part of pySerial. https://github.com/pyserial/pyserial
+# (C) 2015 Chris Liechti <cliechti@gmx.net>
+#
+# SPDX-License-Identifier:    BSD-3-Clause
+import re
+
+
+def numsplit(text):
+    """\
+    Convert string into a list of texts and numbers in order to support a
+    natural sorting.
+    """
+    result = []
+    for group in re.split(r'(\d+)', text):
+        if group:
+            try:
+                group = int(group)
+            except ValueError:
+                pass
+            result.append(group)
+    return result
+
+
+class ListPortInfo(object):
+    """Info collection base class for serial ports"""
+
+    def __init__(self, device=None):
+        self.device = device
+        self.name = None
+        self.description = 'n/a'
+        self.hwid = 'n/a'
+        # USB specific data
+        self.vid = None
+        self.pid = None
+        self.serial_number = None
+        self.location = None
+        self.manufacturer = None
+        self.product = None
+        self.interface = None
+
+    def usb_description(self):
+        if self.interface is not None:
+            return '{} - {}'.format(self.product, self.interface)
+        elif self.product is not None:
+            return self.product
+        else:
+            return self.name
+
+    def usb_info(self):
+        return 'USB VID:PID={:04X}:{:04X}{}{}'.format(
+                self.vid,
+                self.pid,
+                ' SER={}'.format(self.serial_number) if self.serial_number is not None else '',
+                ' LOCATION={}'.format(self.location) if self.location is not None else '',
+                )
+
+    def apply_usb_info(self):
+        """update description and hwid from USB data"""
+        self.description = self.usb_description()
+        self.hwid = self.usb_info()
+
+    def __eq__(self, other):
+        return self.device == other.device
+
+    def __lt__(self, other):
+        return numsplit(self.device) < numsplit(other.device)
+
+    def __str__(self):
+        return '{} - {}'.format(self.device, self.description)
+
+    def __getitem__(self, index):
+        """Item access: backwards compatible -> (port, desc, hwid)"""
+        if index == 0:
+            return self.device
+        elif index == 1:
+            return self.description
+        elif index == 2:
+            return self.hwid
+        else:
+            raise IndexError('{} > 2'.format(index))
+
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+# test
+if __name__ == '__main__':
+    print(ListPortInfo('dummy'))
diff --git a/src/devtools/datool/pyserial/tools/list_ports_linux.py b/src/devtools/datool/pyserial/tools/list_ports_linux.py
new file mode 100644
index 0000000..1969695
--- /dev/null
+++ b/src/devtools/datool/pyserial/tools/list_ports_linux.py
@@ -0,0 +1,84 @@
+#!/usr/bin/env python
+#
+# This is a module that gathers a list of serial ports including details on
+# GNU/Linux systems.
+#
+# This file is part of pySerial. https://github.com/pyserial/pyserial
+# (C) 2011-2015 Chris Liechti <cliechti@gmx.net>
+#
+# SPDX-License-Identifier:    BSD-3-Clause
+
+import glob
+import os
+from pyserial.tools import list_ports_common
+
+
+class SysFS(list_ports_common.ListPortInfo):
+    """Wrapper for easy sysfs access and device info"""
+
+    def __init__(self, device):
+        super(SysFS, self).__init__(device)
+        self.name = os.path.basename(device)
+        self.usb_device_path = None
+        if os.path.exists('/sys/class/tty/%s/device' % (self.name,)):
+            self.device_path = os.path.realpath('/sys/class/tty/%s/device' % (self.name,))
+            self.subsystem = os.path.basename(os.path.realpath(os.path.join(self.device_path, 'subsystem')))
+        else:
+            self.device_path = None
+            self.subsystem = None
+        # check device type
+        if self.subsystem == 'usb-serial':
+            self.usb_device_path = os.path.dirname(os.path.dirname(self.device_path))
+        elif self.subsystem == 'usb':
+            self.usb_device_path = os.path.dirname(self.device_path)
+        else:
+            self.usb_device_path = None
+        # fill-in info for USB devices
+        if self.usb_device_path is not None:
+            self.vid = int(self.read_line(self.usb_device_path, 'idVendor'), 16)
+            self.pid = int(self.read_line(self.usb_device_path, 'idProduct'), 16)
+            self.serial_number = self.read_line(self.usb_device_path, 'serial')
+            self.location = os.path.basename(self.usb_device_path)
+            self.manufacturer = self.read_line(self.usb_device_path, 'manufacturer')
+            self.product = self.read_line(self.usb_device_path, 'product')
+            self.interface = self.read_line(self.device_path, 'interface')
+
+        if self.subsystem in ('usb', 'usb-serial'):
+            self.apply_usb_info()
+        #~ elif self.subsystem in ('pnp', 'amba'):  # PCI based devices, raspi
+        elif self.subsystem == 'pnp':  # PCI based devices
+            self.description = self.name
+            self.hwid = self.read_line(self.device_path, 'id')
+        elif self.subsystem == 'amba':  # raspi
+            self.description = self.name
+            self.hwid = os.path.basename(self.device_path)
+
+    def read_line(self, *args):
+        """\
+        Helper function to read a single line from a file.
+        One or more parameters are allowed, they are joined with os.path.join.
+        Returns None on errors..
+        """
+        try:
+            with open(os.path.join(*args)) as f:
+                line = f.readline().strip()
+            return line
+        except IOError:
+            return None
+
+
+def comports():
+    devices = glob.glob('/dev/ttyS*')           # built-in serial ports
+    devices.extend(glob.glob('/dev/ttyUSB*'))   # usb-serial with own driver
+    devices.extend(glob.glob('/dev/ttyACM*'))   # usb-serial with CDC-ACM profile
+    devices.extend(glob.glob('/dev/ttyAMA*'))   # ARM internal port (raspi)
+    devices.extend(glob.glob('/dev/rfcomm*'))   # BT serial devices
+    return [info
+            for info in [SysFS(d) for d in devices]
+            if info.subsystem != "platform"]    # hide non-present internal serial ports
+
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+# test
+if __name__ == '__main__':
+    for port, desc, hwid in sorted(comports()):
+        print("%s: %s [%s]" % (port, desc, hwid))
diff --git a/src/devtools/datool/pyserial/tools/list_ports_osx.py b/src/devtools/datool/pyserial/tools/list_ports_osx.py
new file mode 100644
index 0000000..543b3c7
--- /dev/null
+++ b/src/devtools/datool/pyserial/tools/list_ports_osx.py
@@ -0,0 +1,259 @@
+#!/usr/bin/env python
+#
+# This is a module that gathers a list of serial ports including details on OSX
+#
+# code originally from https://github.com/makerbot/pyserial/tree/master/serial/tools
+# with contributions from cibomahto, dgs3, FarMcKon, tedbrandston
+# and modifications by cliechti, hoihu, hardkrash
+#
+# This file is part of pySerial. https://github.com/pyserial/pyserial
+# (C) 2013-2015
+#
+# SPDX-License-Identifier:    BSD-3-Clause
+
+
+# List all of the callout devices in OS/X by querying IOKit.
+
+# See the following for a reference of how to do this:
+# http://developer.apple.com/library/mac/#documentation/DeviceDrivers/Conceptual/WorkingWSerial/WWSerial_SerialDevs/SerialDevices.html#//apple_ref/doc/uid/TP30000384-CIHGEAFD
+
+# More help from darwin_hid.py
+
+# Also see the 'IORegistryExplorer' for an idea of what we are actually searching
+
+import ctypes
+from ctypes import util
+
+from pyserial.tools import list_ports_common
+
+iokit = ctypes.cdll.LoadLibrary(ctypes.util.find_library('IOKit'))
+cf = ctypes.cdll.LoadLibrary(ctypes.util.find_library('CoreFoundation'))
+
+kIOMasterPortDefault = ctypes.c_void_p.in_dll(iokit, "kIOMasterPortDefault")
+kCFAllocatorDefault = ctypes.c_void_p.in_dll(cf, "kCFAllocatorDefault")
+
+kCFStringEncodingMacRoman = 0
+
+iokit.IOServiceMatching.restype = ctypes.c_void_p
+
+iokit.IOServiceGetMatchingServices.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p]
+iokit.IOServiceGetMatchingServices.restype = ctypes.c_void_p
+
+iokit.IORegistryEntryGetParentEntry.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p]
+
+iokit.IORegistryEntryCreateCFProperty.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_uint32]
+iokit.IORegistryEntryCreateCFProperty.restype = ctypes.c_void_p
+
+iokit.IORegistryEntryGetPath.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p]
+iokit.IORegistryEntryGetPath.restype = ctypes.c_void_p
+
+iokit.IORegistryEntryGetName.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
+iokit.IORegistryEntryGetName.restype = ctypes.c_void_p
+
+iokit.IOObjectGetClass.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
+iokit.IOObjectGetClass.restype = ctypes.c_void_p
+
+iokit.IOObjectRelease.argtypes = [ctypes.c_void_p]
+
+
+cf.CFStringCreateWithCString.argtypes = [ctypes.c_void_p, ctypes.c_char_p, ctypes.c_int32]
+cf.CFStringCreateWithCString.restype = ctypes.c_void_p
+
+cf.CFStringGetCStringPtr.argtypes = [ctypes.c_void_p, ctypes.c_uint32]
+cf.CFStringGetCStringPtr.restype = ctypes.c_char_p
+
+cf.CFNumberGetValue.argtypes = [ctypes.c_void_p, ctypes.c_uint32, ctypes.c_void_p]
+cf.CFNumberGetValue.restype = ctypes.c_void_p
+
+# void CFRelease ( CFTypeRef cf );
+cf.CFRelease.argtypes = [ctypes.c_void_p]
+cf.CFRelease.restype = None
+
+# CFNumber type defines
+kCFNumberSInt8Type = 1
+kCFNumberSInt16Type = 2
+kCFNumberSInt32Type = 3
+kCFNumberSInt64Type = 4
+
+
+def get_string_property(device_type, property):
+    """
+    Search the given device for the specified string property
+
+    @param device_type Type of Device
+    @param property String to search for
+    @return Python string containing the value, or None if not found.
+    """
+    key = cf.CFStringCreateWithCString(
+            kCFAllocatorDefault,
+            property.encode("mac_roman"),
+            kCFStringEncodingMacRoman)
+
+    CFContainer = iokit.IORegistryEntryCreateCFProperty(
+            device_type,
+            key,
+            kCFAllocatorDefault,
+            0)
+    output = None
+
+    if CFContainer:
+        output = cf.CFStringGetCStringPtr(CFContainer, 0)
+        if output is not None:
+            output = output.decode('mac_roman')
+        cf.CFRelease(CFContainer)
+    return output
+
+
+def get_int_property(device_type, property, cf_number_type):
+    """
+    Search the given device for the specified string property
+
+    @param device_type Device to search
+    @param property String to search for
+    @param cf_number_type CFType number
+
+    @return Python string containing the value, or None if not found.
+    """
+    key = cf.CFStringCreateWithCString(
+            kCFAllocatorDefault,
+            property.encode("mac_roman"),
+            kCFStringEncodingMacRoman)
+
+    CFContainer = iokit.IORegistryEntryCreateCFProperty(
+            device_type,
+            key,
+            kCFAllocatorDefault,
+            0)
+
+    if CFContainer:
+        if (cf_number_type == kCFNumberSInt32Type):
+            number = ctypes.c_uint32()
+        elif (cf_number_type == kCFNumberSInt16Type):
+            number = ctypes.c_uint16()
+        cf.CFNumberGetValue(CFContainer, cf_number_type, ctypes.byref(number))
+        cf.CFRelease(CFContainer)
+        return number.value
+    return None
+
+
+def IORegistryEntryGetName(device):
+    pathname = ctypes.create_string_buffer(100)  # TODO: Is this ok?
+    iokit.IOObjectGetClass(device, ctypes.byref(pathname))
+    return pathname.value
+
+
+def GetParentDeviceByType(device, parent_type):
+    """ Find the first parent of a device that implements the parent_type
+        @param IOService Service to inspect
+        @return Pointer to the parent type, or None if it was not found.
+    """
+    # First, try to walk up the IOService tree to find a parent of this device that is a IOUSBDevice.
+    parent_type = parent_type.encode('mac_roman')
+    while IORegistryEntryGetName(device) != parent_type:
+        parent = ctypes.c_void_p()
+        response = iokit.IORegistryEntryGetParentEntry(
+                device,
+                "IOService".encode("mac_roman"),
+                ctypes.byref(parent))
+        # If we weren't able to find a parent for the device, we're done.
+        if response != 0:
+            return None
+        device = parent
+    return device
+
+
+def GetIOServicesByType(service_type):
+    """
+    returns iterator over specified service_type
+    """
+    serial_port_iterator = ctypes.c_void_p()
+
+    iokit.IOServiceGetMatchingServices(
+            kIOMasterPortDefault,
+            iokit.IOServiceMatching(service_type.encode('mac_roman')),
+            ctypes.byref(serial_port_iterator))
+
+    services = []
+    while iokit.IOIteratorIsValid(serial_port_iterator):
+        service = iokit.IOIteratorNext(serial_port_iterator)
+        if not service:
+            break
+        services.append(service)
+    iokit.IOObjectRelease(serial_port_iterator)
+    return services
+
+
+def location_to_string(locationID):
+    """
+    helper to calculate port and bus number from locationID
+    """
+    loc = ['{}-'.format(locationID >> 24)]
+    while locationID & 0xf00000:
+        if len(loc) > 1:
+            loc.append('.')
+        loc.append('{}'.format((locationID >> 20) & 0xf))
+        locationID <<= 4
+    return ''.join(loc)
+
+
+class SuitableSerialInterface(object):
+    pass
+
+
+def scan_interfaces():
+    """
+    helper function to scan USB interfaces
+    returns a list of SuitableSerialInterface objects with name and id attributes
+    """
+    interfaces = []
+    for service in GetIOServicesByType('IOSerialBSDClient'):
+        device = get_string_property(service, "IOCalloutDevice")
+        if device:
+            usb_device = GetParentDeviceByType(service, "IOUSBInterface")
+            if usb_device:
+                name = get_string_property(usb_device, "USB Interface Name") or None
+                locationID = get_int_property(usb_device, "locationID", kCFNumberSInt32Type) or ''
+                i = SuitableSerialInterface()
+                i.id = locationID
+                i.name = name
+                interfaces.append(i)
+    return interfaces
+
+
+def search_for_locationID_in_interfaces(serial_interfaces, locationID):
+    for interface in serial_interfaces:
+        if (interface.id == locationID):
+            return interface.name
+    return None
+
+
+def comports():
+    # Scan for all iokit serial ports
+    services = GetIOServicesByType('IOSerialBSDClient')
+    ports = []
+    serial_interfaces = scan_interfaces()
+    for service in services:
+        # First, add the callout device file.
+        device = get_string_property(service, "IOCalloutDevice")
+        if device:
+            info = list_ports_common.ListPortInfo(device)
+            # If the serial port is implemented by IOUSBDevice
+            usb_device = GetParentDeviceByType(service, "IOUSBDevice")
+            if usb_device:
+                # fetch some useful informations from properties
+                info.vid = get_int_property(usb_device, "idVendor", kCFNumberSInt16Type)
+                info.pid = get_int_property(usb_device, "idProduct", kCFNumberSInt16Type)
+                info.serial_number = get_string_property(usb_device, "USB Serial Number")
+                info.product = get_string_property(usb_device, "USB Product Name") or 'n/a'
+                info.manufacturer = get_string_property(usb_device, "USB Vendor Name")
+                locationID = get_int_property(usb_device, "locationID", kCFNumberSInt32Type)
+                info.location = location_to_string(locationID)
+                info.interface = search_for_locationID_in_interfaces(serial_interfaces, locationID)
+                info.apply_usb_info()
+            ports.append(info)
+    return ports
+
+# test
+if __name__ == '__main__':
+    for port, desc, hwid in sorted(comports()):
+        print("%s: %s [%s]" % (port, desc, hwid))
diff --git a/src/devtools/datool/pyserial/tools/list_ports_posix.py b/src/devtools/datool/pyserial/tools/list_ports_posix.py
new file mode 100644
index 0000000..ecb2334
--- /dev/null
+++ b/src/devtools/datool/pyserial/tools/list_ports_posix.py
@@ -0,0 +1,102 @@
+#!/usr/bin/env python
+#
+# This is a module that gathers a list of serial ports on POSIXy systems.
+# For some specific implementations, see also list_ports_linux, list_ports_osx
+#
+# This file is part of pySerial. https://github.com/pyserial/pyserial
+# (C) 2011-2015 Chris Liechti <cliechti@gmx.net>
+#
+# SPDX-License-Identifier:    BSD-3-Clause
+
+"""\
+The ``comports`` function is expected to return an iterable that yields tuples
+of 3 strings: port name, human readable description and a hardware ID.
+
+As currently no method is known to get the second two strings easily, they are
+currently just identical to the port name.
+"""
+
+import glob
+import sys
+import os
+from pyserial.tools import list_ports_common
+
+# try to detect the OS so that a device can be selected...
+plat = sys.platform.lower()
+
+if plat[:5] == 'linux':    # Linux (confirmed)  # noqa
+    from pyserial.tools.list_ports_linux import comports
+
+elif plat[:6] == 'darwin':   # OS X (confirmed)
+    from pyserial.tools.list_ports_osx import comports
+
+elif plat == 'cygwin':       # cygwin/win32
+    # cygwin accepts /dev/com* in many contexts
+    # (such as 'open' call, explicit 'ls'), but 'glob.glob'
+    # and bare 'ls' do not; so use /dev/ttyS* instead
+    def comports():
+        devices = glob.glob('/dev/ttyS*')
+        return [list_ports_common.ListPortInfo(d) for d in devices]
+
+elif plat[:7] == 'openbsd':    # OpenBSD
+    def comports():
+        devices = glob.glob('/dev/cua*')
+        return [list_ports_common.ListPortInfo(d) for d in devices]
+
+elif plat[:3] == 'bsd' or plat[:7] == 'freebsd':
+
+    def comports():
+        devices = glob.glob('/dev/cua*[!.init][!.lock]')
+        return [list_ports_common.ListPortInfo(d) for d in devices]
+
+elif plat[:6] == 'netbsd':   # NetBSD
+    def comports():
+        """scan for available ports. return a list of device names."""
+        devices = glob.glob('/dev/dty*')
+        return [list_ports_common.ListPortInfo(d) for d in devices]
+
+elif plat[:4] == 'irix':     # IRIX
+    def comports():
+        """scan for available ports. return a list of device names."""
+        devices = glob.glob('/dev/ttyf*')
+        return [list_ports_common.ListPortInfo(d) for d in devices]
+
+elif plat[:2] == 'hp':       # HP-UX (not tested)
+    def comports():
+        """scan for available ports. return a list of device names."""
+        devices = glob.glob('/dev/tty*p0')
+        return [list_ports_common.ListPortInfo(d) for d in devices]
+
+elif plat[:5] == 'sunos':    # Solaris/SunOS
+    def comports():
+        """scan for available ports. return a list of device names."""
+        devices = glob.glob('/dev/tty*c')
+        return [list_ports_common.ListPortInfo(d) for d in devices]
+
+elif plat[:3] == 'aix':      # AIX
+    def comports():
+        """scan for available ports. return a list of device names."""
+        devices = glob.glob('/dev/tty*')
+        return [list_ports_common.ListPortInfo(d) for d in devices]
+
+else:
+    # platform detection has failed...
+    import pyserial as serial
+    sys.stderr.write("""\
+don't know how to enumerate ttys on this system.
+! I you know how the serial ports are named send this information to
+! the author of this module:
+
+sys.platform = %r
+os.name = %r
+pySerial version = %s
+
+also add the naming scheme of the serial ports and with a bit luck you can get
+this module running...
+""" % (sys.platform, os.name, serial.VERSION))
+    raise ImportError("Sorry: no implementation for your platform ('%s') available" % (os.name,))
+
+# test
+if __name__ == '__main__':
+    for port, desc, hwid in sorted(comports()):
+        print("%s: %s [%s]" % (port, desc, hwid))
diff --git a/src/devtools/datool/pyserial/tools/list_ports_windows.py b/src/devtools/datool/pyserial/tools/list_ports_windows.py
new file mode 100644
index 0000000..e234fc2
--- /dev/null
+++ b/src/devtools/datool/pyserial/tools/list_ports_windows.py
@@ -0,0 +1,294 @@
+#! python
+#
+# Enumerate serial ports on Windows including a human readable description
+# and hardware information.
+#
+# This file is part of pySerial. https://github.com/pyserial/pyserial
+# (C) 2001-2016 Chris Liechti <cliechti@gmx.net>
+#
+# SPDX-License-Identifier:    BSD-3-Clause
+
+import re
+import ctypes
+from ctypes.wintypes import BOOL
+from ctypes.wintypes import HWND
+from ctypes.wintypes import DWORD
+from ctypes.wintypes import WORD
+from ctypes.wintypes import LONG
+from ctypes.wintypes import ULONG
+from ctypes.wintypes import LPCSTR
+from ctypes.wintypes import HKEY
+from ctypes.wintypes import BYTE
+import pyserial as serial
+from pyserial.win32 import ULONG_PTR
+from pyserial.tools import list_ports_common
+
+
+def ValidHandle(value, func, arguments):
+    if value == 0:
+        raise ctypes.WinError()
+    return value
+
+NULL = 0
+HDEVINFO = ctypes.c_void_p
+PCTSTR = ctypes.c_char_p
+PTSTR = ctypes.c_void_p
+CHAR = ctypes.c_char
+LPDWORD = PDWORD = ctypes.POINTER(DWORD)
+#~ LPBYTE = PBYTE = ctypes.POINTER(BYTE)
+LPBYTE = PBYTE = ctypes.c_void_p        # XXX avoids error about types
+
+ACCESS_MASK = DWORD
+REGSAM = ACCESS_MASK
+
+
+def byte_buffer(length):
+    """Get a buffer for a string"""
+    return (BYTE*length)()
+
+
+def string(buffer):
+    s = []
+    for c in buffer:
+        if c == 0:
+            break
+        s.append(chr(c & 0xff))  # "& 0xff": hack to convert signed to unsigned
+    return ''.join(s)
+
+
+class GUID(ctypes.Structure):
+    _fields_ = [
+        ('Data1', DWORD),
+        ('Data2', WORD),
+        ('Data3', WORD),
+        ('Data4', BYTE*8),
+    ]
+
+    def __str__(self):
+        return "{%08x-%04x-%04x-%s-%s}" % (
+            self.Data1,
+            self.Data2,
+            self.Data3,
+            ''.join(["%02x" % d for d in self.Data4[:2]]),
+            ''.join(["%02x" % d for d in self.Data4[2:]]),
+        )
+
+
+class SP_DEVINFO_DATA(ctypes.Structure):
+    _fields_ = [
+        ('cbSize', DWORD),
+        ('ClassGuid', GUID),
+        ('DevInst', DWORD),
+        ('Reserved', ULONG_PTR),
+    ]
+
+    def __str__(self):
+        return "ClassGuid:%s DevInst:%s" % (self.ClassGuid, self.DevInst)
+
+PSP_DEVINFO_DATA = ctypes.POINTER(SP_DEVINFO_DATA)
+
+PSP_DEVICE_INTERFACE_DETAIL_DATA = ctypes.c_void_p
+
+setupapi = ctypes.windll.LoadLibrary("setupapi")
+SetupDiDestroyDeviceInfoList = setupapi.SetupDiDestroyDeviceInfoList
+SetupDiDestroyDeviceInfoList.argtypes = [HDEVINFO]
+SetupDiDestroyDeviceInfoList.restype = BOOL
+
+SetupDiClassGuidsFromName = setupapi.SetupDiClassGuidsFromNameA
+SetupDiClassGuidsFromName.argtypes = [PCTSTR, ctypes.POINTER(GUID), DWORD, PDWORD]
+SetupDiClassGuidsFromName.restype = BOOL
+
+SetupDiEnumDeviceInfo = setupapi.SetupDiEnumDeviceInfo
+SetupDiEnumDeviceInfo.argtypes = [HDEVINFO, DWORD, PSP_DEVINFO_DATA]
+SetupDiEnumDeviceInfo.restype = BOOL
+
+SetupDiGetClassDevs = setupapi.SetupDiGetClassDevsA
+SetupDiGetClassDevs.argtypes = [ctypes.POINTER(GUID), PCTSTR, HWND, DWORD]
+SetupDiGetClassDevs.restype = HDEVINFO
+SetupDiGetClassDevs.errcheck = ValidHandle
+
+SetupDiGetDeviceRegistryProperty = setupapi.SetupDiGetDeviceRegistryPropertyA
+SetupDiGetDeviceRegistryProperty.argtypes = [HDEVINFO, PSP_DEVINFO_DATA, DWORD, PDWORD, PBYTE, DWORD, PDWORD]
+SetupDiGetDeviceRegistryProperty.restype = BOOL
+
+SetupDiGetDeviceInstanceId = setupapi.SetupDiGetDeviceInstanceIdA
+SetupDiGetDeviceInstanceId.argtypes = [HDEVINFO, PSP_DEVINFO_DATA, PTSTR, DWORD, PDWORD]
+SetupDiGetDeviceInstanceId.restype = BOOL
+
+SetupDiOpenDevRegKey = setupapi.SetupDiOpenDevRegKey
+SetupDiOpenDevRegKey.argtypes = [HDEVINFO, PSP_DEVINFO_DATA, DWORD, DWORD, DWORD, REGSAM]
+SetupDiOpenDevRegKey.restype = HKEY
+
+advapi32 = ctypes.windll.LoadLibrary("Advapi32")
+RegCloseKey = advapi32.RegCloseKey
+RegCloseKey.argtypes = [HKEY]
+RegCloseKey.restype = LONG
+
+RegQueryValueEx = advapi32.RegQueryValueExA
+RegQueryValueEx.argtypes = [HKEY, LPCSTR, LPDWORD, LPDWORD, LPBYTE, LPDWORD]
+RegQueryValueEx.restype = LONG
+
+
+DIGCF_PRESENT = 2
+DIGCF_DEVICEINTERFACE = 16
+INVALID_HANDLE_VALUE = 0
+ERROR_INSUFFICIENT_BUFFER = 122
+SPDRP_HARDWAREID = 1
+SPDRP_FRIENDLYNAME = 12
+SPDRP_LOCATION_PATHS = 35
+DICS_FLAG_GLOBAL = 1
+DIREG_DEV = 0x00000001
+KEY_READ = 0x20019
+
+# workaround for compatibility between Python 2.x and 3.x
+Ports = serial.to_bytes([80, 111, 114, 116, 115])  # "Ports"
+PortName = serial.to_bytes([80, 111, 114, 116, 78, 97, 109, 101])  # "PortName"
+
+
+def comports():
+    GUIDs = (GUID*8)()  # so far only seen one used, so hope 8 are enough...
+    guids_size = DWORD()
+    if not SetupDiClassGuidsFromName(
+            Ports,
+            GUIDs,
+            ctypes.sizeof(GUIDs),
+            ctypes.byref(guids_size)):
+        raise ctypes.WinError()
+
+    # repeat for all possible GUIDs
+    for index in range(guids_size.value):
+        g_hdi = SetupDiGetClassDevs(
+                ctypes.byref(GUIDs[index]),
+                None,
+                NULL,
+                DIGCF_PRESENT)  # was DIGCF_PRESENT|DIGCF_DEVICEINTERFACE which misses CDC ports
+
+        devinfo = SP_DEVINFO_DATA()
+        devinfo.cbSize = ctypes.sizeof(devinfo)
+        index = 0
+        while SetupDiEnumDeviceInfo(g_hdi, index, ctypes.byref(devinfo)):
+            index += 1
+
+            # get the real com port name
+            hkey = SetupDiOpenDevRegKey(
+                    g_hdi,
+                    ctypes.byref(devinfo),
+                    DICS_FLAG_GLOBAL,
+                    0,
+                    DIREG_DEV,  # DIREG_DRV for SW info
+                    KEY_READ)
+            port_name_buffer = byte_buffer(250)
+            port_name_length = ULONG(ctypes.sizeof(port_name_buffer))
+            RegQueryValueEx(
+                    hkey,
+                    PortName,
+                    None,
+                    None,
+                    ctypes.byref(port_name_buffer),
+                    ctypes.byref(port_name_length))
+            RegCloseKey(hkey)
+
+            # unfortunately does this method also include parallel ports.
+            # we could check for names starting with COM or just exclude LPT
+            # and hope that other "unknown" names are serial ports...
+            if string(port_name_buffer).startswith('LPT'):
+                continue
+
+            # hardware ID
+            szHardwareID = byte_buffer(250)
+            # try to get ID that includes serial number
+            if not SetupDiGetDeviceInstanceId(
+                    g_hdi,
+                    ctypes.byref(devinfo),
+                    ctypes.byref(szHardwareID),
+                    ctypes.sizeof(szHardwareID) - 1,
+                    None):
+                # fall back to more generic hardware ID if that would fail
+                if not SetupDiGetDeviceRegistryProperty(
+                        g_hdi,
+                        ctypes.byref(devinfo),
+                        SPDRP_HARDWAREID,
+                        None,
+                        ctypes.byref(szHardwareID),
+                        ctypes.sizeof(szHardwareID) - 1,
+                        None):
+                    # Ignore ERROR_INSUFFICIENT_BUFFER
+                    if ctypes.GetLastError() != ERROR_INSUFFICIENT_BUFFER:
+                        raise ctypes.WinError()
+            # stringify
+            szHardwareID_str = string(szHardwareID)
+
+            info = list_ports_common.ListPortInfo(string(port_name_buffer))
+
+            # in case of USB, make a more readable string, similar to that form
+            # that we also generate on other platforms
+            if szHardwareID_str.startswith('USB'):
+                m = re.search(r'VID_([0-9a-f]{4})&PID_([0-9a-f]{4})(\\(\w+))?', szHardwareID_str, re.I)
+                if m:
+                    info.vid = int(m.group(1), 16)
+                    info.pid = int(m.group(2), 16)
+                    if m.group(4):
+                        info.serial_number = m.group(4)
+                # calculate a location string
+                loc_path_str = byte_buffer(250)
+                if SetupDiGetDeviceRegistryProperty(
+                        g_hdi,
+                        ctypes.byref(devinfo),
+                        SPDRP_LOCATION_PATHS,
+                        None,
+                        ctypes.byref(loc_path_str),
+                        ctypes.sizeof(loc_path_str) - 1,
+                        None):
+                    #~ print (string(loc_path_str))
+                    m = re.finditer(r'USBROOT\((\w+)\)|#USB\((\w+)\)', string(loc_path_str))
+                    location = []
+                    for g in m:
+                        if g.group(1):
+                            location.append('%d' % (int(g.group(1)) + 1))
+                        else:
+                            if len(location) > 1:
+                                location.append('.')
+                            else:
+                                location.append('-')
+                            location.append(g.group(2))
+                    if location:
+                        info.location = ''.join(location)
+                info.hwid = info.usb_info()
+            elif szHardwareID_str.startswith('FTDIBUS'):
+                m = re.search(r'VID_([0-9a-f]{4})\+PID_([0-9a-f]{4})(\+(\w+))?', szHardwareID_str, re.I)
+                if m:
+                    info.vid = int(m.group(1), 16)
+                    info.pid = int(m.group(2), 16)
+                    if m.group(4):
+                        info.serial_number = m.group(4)
+                # USB location is hidden by FDTI driver :(
+                info.hwid = info.usb_info()
+            else:
+                info.hwid = szHardwareID_str
+
+            # friendly name
+            szFriendlyName = byte_buffer(250)
+            if SetupDiGetDeviceRegistryProperty(
+                    g_hdi,
+                    ctypes.byref(devinfo),
+                    SPDRP_FRIENDLYNAME,
+                    #~ SPDRP_DEVICEDESC,
+                    None,
+                    ctypes.byref(szFriendlyName),
+                    ctypes.sizeof(szFriendlyName) - 1,
+                    None):
+                info.description = string(szFriendlyName)
+            #~ else:
+                # Ignore ERROR_INSUFFICIENT_BUFFER
+                #~ if ctypes.GetLastError() != ERROR_INSUFFICIENT_BUFFER:
+                    #~ raise IOError("failed to get details for %s (%s)" % (devinfo, szHardwareID.value))
+                # ignore errors and still include the port in the list, friendly name will be same as port name
+            yield info
+        SetupDiDestroyDeviceInfoList(g_hdi)
+
+
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+# test
+if __name__ == '__main__':
+    for port, desc, hwid in sorted(comports()):
+        print("%s: %s [%s]" % (port, desc, hwid))
diff --git a/src/devtools/datool/pyserial/tools/miniterm.py b/src/devtools/datool/pyserial/tools/miniterm.py
new file mode 100644
index 0000000..186ac10
--- /dev/null
+++ b/src/devtools/datool/pyserial/tools/miniterm.py
@@ -0,0 +1,878 @@
+#!/usr/bin/env python
+#
+# Very simple serial terminal
+#
+# This file is part of pySerial. https://github.com/pyserial/pyserial
+# (C)2002-2015 Chris Liechti <cliechti@gmx.net>
+#
+# SPDX-License-Identifier:    BSD-3-Clause
+
+import codecs
+import os
+import sys
+import threading
+
+import pyserial as serial
+from pyserial.tools.list_ports import comports
+from pyserial.tools import hexlify_codec
+
+codecs.register(lambda c: hexlify_codec.getregentry() if c == 'hexlify' else None)
+
+try:
+    raw_input
+except NameError:
+    raw_input = input   # in python3 it's "raw"
+    unichr = chr
+
+
+def key_description(character):
+    """generate a readable description for a key"""
+    ascii_code = ord(character)
+    if ascii_code < 32:
+        return 'Ctrl+%c' % (ord('@') + ascii_code)
+    else:
+        return repr(character)
+
+
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+class ConsoleBase(object):
+    def __init__(self):
+        if sys.version_info >= (3, 0):
+            self.byte_output = sys.stdout.buffer
+        else:
+            self.byte_output = sys.stdout
+        self.output = sys.stdout
+
+    def setup(self):
+        pass
+
+    def cleanup(self):
+        pass
+
+    def getkey(self):
+        return None
+
+    def write_bytes(self, s):
+        self.byte_output.write(s)
+        self.byte_output.flush()
+
+    def write(self, s):
+        self.output.write(s)
+        self.output.flush()
+
+    #  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -
+    # context manager:
+    # switch terminal temporary to normal mode (e.g. to get user input)
+
+    def __enter__(self):
+        self.cleanup()
+        return self
+
+    def __exit__(self, *args, **kwargs):
+        self.setup()
+
+
+if os.name == 'nt':
+    import msvcrt
+    import ctypes
+
+    class Out(object):
+        def __init__(self, fd):
+            self.fd = fd
+
+        def flush(self):
+            pass
+
+        def write(self, s):
+            os.write(self.fd, s)
+
+    class Console(ConsoleBase):
+        def __init__(self):
+            super(Console, self).__init__()
+            self._saved_ocp = ctypes.windll.kernel32.GetConsoleOutputCP()
+            self._saved_icp = ctypes.windll.kernel32.GetConsoleCP()
+            ctypes.windll.kernel32.SetConsoleOutputCP(65001)
+            ctypes.windll.kernel32.SetConsoleCP(65001)
+            self.output = codecs.getwriter('UTF-8')(Out(sys.stdout.fileno()), 'replace')
+            # the change of the code page is not propagated to Python, manually fix it
+            sys.stderr = codecs.getwriter('UTF-8')(Out(sys.stderr.fileno()), 'replace')
+            sys.stdout = self.output
+            self.output.encoding = 'UTF-8'  # needed for input
+
+        def __del__(self):
+            ctypes.windll.kernel32.SetConsoleOutputCP(self._saved_ocp)
+            ctypes.windll.kernel32.SetConsoleCP(self._saved_icp)
+
+        def getkey(self):
+            while True:
+                z = msvcrt.getwch()
+                if z == unichr(13):
+                    return unichr(10)
+                elif z in (unichr(0), unichr(0x0e)):    # functions keys, ignore
+                    msvcrt.getwch()
+                else:
+                    return z
+
+elif os.name == 'posix':
+    import atexit
+    import termios
+
+    class Console(ConsoleBase):
+        def __init__(self):
+            super(Console, self).__init__()
+            self.fd = sys.stdin.fileno()
+            self.old = termios.tcgetattr(self.fd)
+            atexit.register(self.cleanup)
+            if sys.version_info < (3, 0):
+                self.enc_stdin = codecs.getreader(sys.stdin.encoding)(sys.stdin)
+            else:
+                self.enc_stdin = sys.stdin
+
+        def setup(self):
+            new = termios.tcgetattr(self.fd)
+            new[3] = new[3] & ~termios.ICANON & ~termios.ECHO & ~termios.ISIG
+            new[6][termios.VMIN] = 1
+            new[6][termios.VTIME] = 0
+            termios.tcsetattr(self.fd, termios.TCSANOW, new)
+
+        def getkey(self):
+            c = self.enc_stdin.read(1)
+            if c == unichr(0x7f):
+                c = unichr(8)    # map the BS key (which yields DEL) to backspace
+            return c
+
+        def cleanup(self):
+            termios.tcsetattr(self.fd, termios.TCSAFLUSH, self.old)
+
+else:
+    raise NotImplementedError("Sorry no implementation for your platform (%s) available." % sys.platform)
+
+
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+class Transform(object):
+    """do-nothing: forward all data unchanged"""
+    def rx(self, text):
+        """text received from serial port"""
+        return text
+
+    def tx(self, text):
+        """text to be sent to serial port"""
+        return text
+
+    def echo(self, text):
+        """text to be sent but displayed on console"""
+        return text
+
+
+class CRLF(Transform):
+    """ENTER sends CR+LF"""
+
+    def tx(self, text):
+        return text.replace('\n', '\r\n')
+
+
+class CR(Transform):
+    """ENTER sends CR"""
+
+    def rx(self, text):
+        return text.replace('\r', '\n')
+
+    def tx(self, text):
+        return text.replace('\n', '\r')
+
+
+class LF(Transform):
+    """ENTER sends LF"""
+
+
+class NoTerminal(Transform):
+    """remove typical terminal control codes from input"""
+
+    REPLACEMENT_MAP = dict((x, 0x2400 + x) for x in range(32) if unichr(x) not in '\r\n\b\t')
+    REPLACEMENT_MAP.update({
+            0x7F: 0x2421,  # DEL
+            0x9B: 0x2425,  # CSI
+            })
+
+    def rx(self, text):
+        return text.translate(self.REPLACEMENT_MAP)
+
+    echo = rx
+
+
+class NoControls(NoTerminal):
+    """Remove all control codes, incl. CR+LF"""
+
+    REPLACEMENT_MAP = dict((x, 0x2400 + x) for x in range(32))
+    REPLACEMENT_MAP.update({
+            32: 0x2423,    # visual space
+            0x7F: 0x2421,  # DEL
+            0x9B: 0x2425,  # CSI
+            })
+
+
+class Printable(Transform):
+    """Show decimal code for all non-ASCII characters and replace most control codes"""
+
+    def rx(self, text):
+        r = []
+        for t in text:
+            if ' ' <= t < '\x7f' or t in '\r\n\b\t':
+                r.append(t)
+            elif t < ' ':
+                r.append(unichr(0x2400 + ord(t)))
+            else:
+                r.extend(unichr(0x2080 + ord(d) - 48) for d in '{:d}'.format(ord(t)))
+                r.append(' ')
+        return ''.join(r)
+
+    echo = rx
+
+
+class Colorize(Transform):
+    """Apply different colors for received and echo"""
+
+    def __init__(self):
+        # XXX make it configurable, use colorama?
+        self.input_color = '\x1b[37m'
+        self.echo_color = '\x1b[31m'
+
+    def rx(self, text):
+        return self.input_color + text
+
+    def echo(self, text):
+        return self.echo_color + text
+
+
+class DebugIO(Transform):
+    """Print what is sent and received"""
+
+    def rx(self, text):
+        sys.stderr.write(' [RX:{}] '.format(repr(text)))
+        sys.stderr.flush()
+        return text
+
+    def tx(self, text):
+        sys.stderr.write(' [TX:{}] '.format(repr(text)))
+        sys.stderr.flush()
+        return text
+
+
+# other ideas:
+# - add date/time for each newline
+# - insert newline after: a) timeout b) packet end character
+
+EOL_TRANSFORMATIONS = {
+        'crlf': CRLF,
+        'cr': CR,
+        'lf': LF,
+        }
+
+TRANSFORMATIONS = {
+        'direct': Transform,    # no transformation
+        'default': NoTerminal,
+        'nocontrol': NoControls,
+        'printable': Printable,
+        'colorize': Colorize,
+        'debug': DebugIO,
+        }
+
+
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+def ask_for_port():
+    """\
+    Show a list of ports and ask the user for a choice. To make selection
+    easier on systems with long device names, also allow the input of an
+    index.
+    """
+    sys.stderr.write('\n--- Available ports:\n')
+    ports = []
+    for n, (port, desc, hwid) in enumerate(sorted(comports()), 1):
+        #~ sys.stderr.write('--- %-20s %s [%s]\n' % (port, desc, hwid))
+        sys.stderr.write('--- {:2}: {:20} {}\n'.format(n, port, desc))
+        ports.append(port)
+    while True:
+        port = raw_input('--- Enter port index or full name: ')
+        try:
+            index = int(port) - 1
+            if not 0 <= index < len(ports):
+                sys.stderr.write('--- Invalid index!\n')
+                continue
+        except ValueError:
+            pass
+        else:
+            port = ports[index]
+        return port
+
+
+class Miniterm(object):
+    """\
+    Terminal application. Copy data from serial port to console and vice versa.
+    Handle special keys from the console to show menu etc.
+    """
+
+    def __init__(self, serial_instance, echo=False, eol='crlf', filters=()):
+        self.console = Console()
+        self.serial = serial_instance
+        self.echo = echo
+        self.raw = False
+        self.input_encoding = 'UTF-8'
+        self.output_encoding = 'UTF-8'
+        self.eol = eol
+        self.filters = filters
+        self.update_transformations()
+        self.exit_character = 0x1d  # GS/CTRL+]
+        self.menu_character = 0x14  # Menu: CTRL+T
+
+    def _start_reader(self):
+        """Start reader thread"""
+        self._reader_alive = True
+        # start serial->console thread
+        self.receiver_thread = threading.Thread(target=self.reader, name='rx')
+        self.receiver_thread.daemon = True
+        self.receiver_thread.start()
+
+    def _stop_reader(self):
+        """Stop reader thread only, wait for clean exit of thread"""
+        self._reader_alive = False
+        self.receiver_thread.join()
+
+    def start(self):
+        self.alive = True
+        self._start_reader()
+        # enter console->serial loop
+        self.transmitter_thread = threading.Thread(target=self.writer, name='tx')
+        self.transmitter_thread.daemon = True
+        self.transmitter_thread.start()
+        self.console.setup()
+
+    def stop(self):
+        self.alive = False
+
+    def join(self, transmit_only=False):
+        self.transmitter_thread.join()
+        if not transmit_only:
+            self.receiver_thread.join()
+
+    def update_transformations(self):
+        transformations = [EOL_TRANSFORMATIONS[self.eol]] + [TRANSFORMATIONS[f] for f in self.filters]
+        self.tx_transformations = [t() for t in transformations]
+        self.rx_transformations = list(reversed(self.tx_transformations))
+
+    def set_rx_encoding(self, encoding, errors='replace'):
+        self.input_encoding = encoding
+        self.rx_decoder = codecs.getincrementaldecoder(encoding)(errors)
+
+    def set_tx_encoding(self, encoding, errors='replace'):
+        self.output_encoding = encoding
+        self.tx_encoder = codecs.getincrementalencoder(encoding)(errors)
+
+    def dump_port_settings(self):
+        sys.stderr.write("\n--- Settings: {p.name}  {p.baudrate},{p.bytesize},{p.parity},{p.stopbits}\n".format(
+                p=self.serial))
+        sys.stderr.write('--- RTS: {:8}  DTR: {:8}  BREAK: {:8}\n'.format(
+                ('active' if self.serial.rts else 'inactive'),
+                ('active' if self.serial.dtr else 'inactive'),
+                ('active' if self.serial.break_condition else 'inactive')))
+        try:
+            sys.stderr.write('--- CTS: {:8}  DSR: {:8}  RI: {:8}  CD: {:8}\n'.format(
+                    ('active' if self.serial.cts else 'inactive'),
+                    ('active' if self.serial.dsr else 'inactive'),
+                    ('active' if self.serial.ri else 'inactive'),
+                    ('active' if self.serial.cd else 'inactive')))
+        except serial.SerialException:
+            # on RFC 2217 ports, it can happen if no modem state notification was
+            # yet received. ignore this error.
+            pass
+        sys.stderr.write('--- software flow control: {}\n'.format('active' if self.serial.xonxoff else 'inactive'))
+        sys.stderr.write('--- hardware flow control: {}\n'.format('active' if self.serial.rtscts else 'inactive'))
+        #~ sys.stderr.write('--- data escaping: %s  linefeed: %s\n' % (
+                #~ REPR_MODES[self.repr_mode],
+                #~ LF_MODES[self.convert_outgoing]))
+        sys.stderr.write('--- serial input encoding: {}\n'.format(self.input_encoding))
+        sys.stderr.write('--- serial output encoding: {}\n'.format(self.output_encoding))
+        sys.stderr.write('--- EOL: {}\n'.format(self.eol.upper()))
+        sys.stderr.write('--- filters: {}\n'.format(' '.join(self.filters)))
+
+    def reader(self):
+        """loop and copy serial->console"""
+        try:
+            while self.alive and self._reader_alive:
+                # read all that is there or wait for one byte
+                data = self.serial.read(self.serial.in_waiting or 1)
+                if data:
+                    if self.raw:
+                        self.console.write_bytes(data)
+                    else:
+                        text = self.rx_decoder.decode(data)
+                        for transformation in self.rx_transformations:
+                            text = transformation.rx(text)
+                        self.console.write(text)
+        except serial.SerialException:
+            self.alive = False
+            # XXX would be nice if the writer could be interrupted at this
+            #     point... to exit completely
+            raise
+
+    def writer(self):
+        """\
+        Loop and copy console->serial until self.exit_character character is
+        found. When self.menu_character is found, interpret the next key
+        locally.
+        """
+        menu_active = False
+        try:
+            while self.alive:
+                try:
+                    c = self.console.getkey()
+                except KeyboardInterrupt:
+                    c = '\x03'
+                if menu_active:
+                    self.handle_menu_key(c)
+                    menu_active = False
+                elif c == self.menu_character:
+                    menu_active = True      # next char will be for menu
+                elif c == self.exit_character:
+                    self.stop()             # exit app
+                    break
+                else:
+                    #~ if self.raw:
+                    text = c
+                    for transformation in self.tx_transformations:
+                        text = transformation.tx(text)
+                    self.serial.write(self.tx_encoder.encode(text))
+                    if self.echo:
+                        echo_text = c
+                        for transformation in self.tx_transformations:
+                            echo_text = transformation.echo(echo_text)
+                        self.console.write(echo_text)
+        except:
+            self.alive = False
+            raise
+
+    def handle_menu_key(self, c):
+        """Implement a simple menu / settings"""
+        if c == self.menu_character or c == self.exit_character:
+            # Menu/exit character again -> send itself
+            self.serial.write(self.tx_encoder.encode(c))
+            if self.echo:
+                self.console.write(c)
+        elif c == '\x15':                       # CTRL+U -> upload file
+            sys.stderr.write('\n--- File to upload: ')
+            sys.stderr.flush()
+            with self.console:
+                filename = sys.stdin.readline().rstrip('\r\n')
+                if filename:
+                    try:
+                        with open(filename, 'rb') as f:
+                            sys.stderr.write('--- Sending file {} ---\n'.format(filename))
+                            while True:
+                                block = f.read(1024)
+                                if not block:
+                                    break
+                                self.serial.write(block)
+                                # Wait for output buffer to drain.
+                                self.serial.flush()
+                                sys.stderr.write('.')   # Progress indicator.
+                        sys.stderr.write('\n--- File {} sent ---\n'.format(filename))
+                    except IOError as e:
+                        sys.stderr.write('--- ERROR opening file {}: {} ---\n'.format(filename, e))
+        elif c in '\x08hH?':                    # CTRL+H, h, H, ? -> Show help
+            sys.stderr.write(self.get_help_text())
+        elif c == '\x12':                       # CTRL+R -> Toggle RTS
+            self.serial.rts = not self.serial.rts
+            sys.stderr.write('--- RTS {} ---\n'.format('active' if self.serial.rts else 'inactive'))
+        elif c == '\x04':                       # CTRL+D -> Toggle DTR
+            self.serial.dtr = not self.serial.dtr
+            sys.stderr.write('--- DTR {} ---\n'.format('active' if self.serial.dtr else 'inactive'))
+        elif c == '\x02':                       # CTRL+B -> toggle BREAK condition
+            self.serial.break_condition = not self.serial.break_condition
+            sys.stderr.write('--- BREAK {} ---\n'.format('active' if self.serial.break_condition else 'inactive'))
+        elif c == '\x05':                       # CTRL+E -> toggle local echo
+            self.echo = not self.echo
+            sys.stderr.write('--- local echo {} ---\n'.format('active' if self.echo else 'inactive'))
+        elif c == '\x06':                       # CTRL+F -> edit filters
+            sys.stderr.write('\n--- Available Filters:\n')
+            sys.stderr.write('\n'.join(
+                    '---   {:<10} = {.__doc__}'.format(k, v)
+                    for k, v in sorted(TRANSFORMATIONS.items())))
+            sys.stderr.write('\n--- Enter new filter name(s) [{}]: '.format(' '.join(self.filters)))
+            with self.console:
+                new_filters = sys.stdin.readline().lower().split()
+            if new_filters:
+                for f in new_filters:
+                    if f not in TRANSFORMATIONS:
+                        sys.stderr.write('--- unknown filter: {}'.format(repr(f)))
+                        break
+                else:
+                    self.filters = new_filters
+                    self.update_transformations()
+            sys.stderr.write('--- filters: {}\n'.format(' '.join(self.filters)))
+        elif c == '\x0c':                       # CTRL+L -> EOL mode
+            modes = list(EOL_TRANSFORMATIONS)  # keys
+            eol = modes.index(self.eol) + 1
+            if eol >= len(modes):
+                eol = 0
+            self.eol = modes[eol]
+            sys.stderr.write('--- EOL: {} ---\n'.format(self.eol.upper()))
+            self.update_transformations()
+        elif c == '\x01':                       # CTRL+A -> set encoding
+            sys.stderr.write('\n--- Enter new encoding name [{}]: '.format(self.input_encoding))
+            with self.console:
+                new_encoding = sys.stdin.readline().strip()
+            if new_encoding:
+                try:
+                    codecs.lookup(new_encoding)
+                except LookupError:
+                    sys.stderr.write('--- invalid encoding name: {}\n'.format(new_encoding))
+                else:
+                    self.set_rx_encoding(new_encoding)
+                    self.set_tx_encoding(new_encoding)
+            sys.stderr.write('--- serial input encoding: {}\n'.format(self.input_encoding))
+            sys.stderr.write('--- serial output encoding: {}\n'.format(self.output_encoding))
+        elif c == '\x09':                       # CTRL+I -> info
+            self.dump_port_settings()
+        #~ elif c == '\x01':                       # CTRL+A -> cycle escape mode
+        #~ elif c == '\x0c':                       # CTRL+L -> cycle linefeed mode
+        elif c in 'pP':                         # P -> change port
+            with self.console:
+                try:
+                    port = ask_for_port()
+                except KeyboardInterrupt:
+                    port = None
+            if port and port != self.serial.port:
+                # reader thread needs to be shut down
+                self._stop_reader()
+                # save settings
+                settings = self.serial.getSettingsDict()
+                try:
+                    new_serial = serial.serial_for_url(port, do_not_open=True)
+                    # restore settings and open
+                    new_serial.applySettingsDict(settings)
+                    new_serial.rts = self.serial.rts
+                    new_serial.dtr = self.serial.dtr
+                    new_serial.open()
+                    new_serial.break_condition = self.serial.break_condition
+                except Exception as e:
+                    sys.stderr.write('--- ERROR opening new port: {} ---\n'.format(e))
+                    new_serial.close()
+                else:
+                    self.serial.close()
+                    self.serial = new_serial
+                    sys.stderr.write('--- Port changed to: {} ---\n'.format(self.serial.port))
+                # and restart the reader thread
+                self._start_reader()
+        elif c in 'bB':                         # B -> change baudrate
+            sys.stderr.write('\n--- Baudrate: ')
+            sys.stderr.flush()
+            with self.console:
+                backup = self.serial.baudrate
+                try:
+                    self.serial.baudrate = int(sys.stdin.readline().strip())
+                except ValueError as e:
+                    sys.stderr.write('--- ERROR setting baudrate: %s ---\n'.format(e))
+                    self.serial.baudrate = backup
+                else:
+                    self.dump_port_settings()
+        elif c == '8':                          # 8 -> change to 8 bits
+            self.serial.bytesize = serial.EIGHTBITS
+            self.dump_port_settings()
+        elif c == '7':                          # 7 -> change to 8 bits
+            self.serial.bytesize = serial.SEVENBITS
+            self.dump_port_settings()
+        elif c in 'eE':                         # E -> change to even parity
+            self.serial.parity = serial.PARITY_EVEN
+            self.dump_port_settings()
+        elif c in 'oO':                         # O -> change to odd parity
+            self.serial.parity = serial.PARITY_ODD
+            self.dump_port_settings()
+        elif c in 'mM':                         # M -> change to mark parity
+            self.serial.parity = serial.PARITY_MARK
+            self.dump_port_settings()
+        elif c in 'sS':                         # S -> change to space parity
+            self.serial.parity = serial.PARITY_SPACE
+            self.dump_port_settings()
+        elif c in 'nN':                         # N -> change to no parity
+            self.serial.parity = serial.PARITY_NONE
+            self.dump_port_settings()
+        elif c == '1':                          # 1 -> change to 1 stop bits
+            self.serial.stopbits = serial.STOPBITS_ONE
+            self.dump_port_settings()
+        elif c == '2':                          # 2 -> change to 2 stop bits
+            self.serial.stopbits = serial.STOPBITS_TWO
+            self.dump_port_settings()
+        elif c == '3':                          # 3 -> change to 1.5 stop bits
+            self.serial.stopbits = serial.STOPBITS_ONE_POINT_FIVE
+            self.dump_port_settings()
+        elif c in 'xX':                         # X -> change software flow control
+            self.serial.xonxoff = (c == 'X')
+            self.dump_port_settings()
+        elif c in 'rR':                         # R -> change hardware flow control
+            self.serial.rtscts = (c == 'R')
+            self.dump_port_settings()
+        else:
+            sys.stderr.write('--- unknown menu character {} --\n'.format(key_description(c)))
+
+    def get_help_text(self):
+        # help text, starts with blank line!
+        return """
+--- pySerial ({version}) - miniterm - help
+---
+--- {exit:8} Exit program
+--- {menu:8} Menu escape key, followed by:
+--- Menu keys:
+---    {menu:7} Send the menu character itself to remote
+---    {exit:7} Send the exit character itself to remote
+---    {info:7} Show info
+---    {upload:7} Upload file (prompt will be shown)
+---    {repr:7} encoding
+---    {filter:7} edit filters
+--- Toggles:
+---    {rts:7} RTS   {dtr:7} DTR   {brk:7} BREAK
+---    {echo:7} echo  {eol:7} EOL
+---
+--- Port settings ({menu} followed by the following):
+---    p          change port
+---    7 8        set data bits
+---    N E O S M  change parity (None, Even, Odd, Space, Mark)
+---    1 2 3      set stop bits (1, 2, 1.5)
+---    b          change baud rate
+---    x X        disable/enable software flow control
+---    r R        disable/enable hardware flow control
+""".format(
+                version=getattr(serial, 'VERSION', 'unknown version'),
+                exit=key_description(self.exit_character),
+                menu=key_description(self.menu_character),
+                rts=key_description('\x12'),
+                dtr=key_description('\x04'),
+                brk=key_description('\x02'),
+                echo=key_description('\x05'),
+                info=key_description('\x09'),
+                upload=key_description('\x15'),
+                repr=key_description('\x01'),
+                filter=key_description('\x06'),
+                eol=key_description('\x0c'),
+                )
+
+
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+# default args can be used to override when calling main() from an other script
+# e.g to create a miniterm-my-device.py
+def main(default_port=None, default_baudrate=9600, default_rts=None, default_dtr=None):
+    import argparse
+
+    parser = argparse.ArgumentParser(
+            description="Miniterm - A simple terminal program for the serial port.")
+
+    parser.add_argument(
+            "port",
+            nargs='?',
+            help="serial port name ('-' to show port list)",
+            default=default_port)
+
+    parser.add_argument(
+            "baudrate",
+            nargs='?',
+            type=int,
+            help="set baud rate, default: %(default)s",
+            default=default_baudrate)
+
+    group = parser.add_argument_group("port settings")
+
+    group.add_argument(
+            "--parity",
+            choices=['N', 'E', 'O', 'S', 'M'],
+            type=lambda c: c.upper(),
+            help="set parity, one of {N E O S M}, default: N",
+            default='N')
+
+    group.add_argument(
+            "--rtscts",
+            action="store_true",
+            help="enable RTS/CTS flow control (default off)",
+            default=False)
+
+    group.add_argument(
+            "--xonxoff",
+            action="store_true",
+            help="enable software flow control (default off)",
+            default=False)
+
+    group.add_argument(
+            "--rts",
+            type=int,
+            help="set initial RTS line state (possible values: 0, 1)",
+            default=default_rts)
+
+    group.add_argument(
+            "--dtr",
+            type=int,
+            help="set initial DTR line state (possible values: 0, 1)",
+            default=default_dtr)
+
+    group.add_argument(
+            "--ask",
+            action="store_true",
+            help="ask again for port when open fails",
+            default=False)
+
+    group = parser.add_argument_group("data handling")
+
+    group.add_argument(
+            "-e", "--echo",
+            action="store_true",
+            help="enable local echo (default off)",
+            default=False)
+
+    group.add_argument(
+            "--encoding",
+            dest="serial_port_encoding",
+            metavar="CODEC",
+            help="set the encoding for the serial port (e.g. hexlify, Latin1, UTF-8), default: %(default)s",
+            default='UTF-8')
+
+    group.add_argument(
+            "-f", "--filter",
+            action="append",
+            metavar="NAME",
+            help="add text transformation",
+            default=[])
+
+    group.add_argument(
+            "--eol",
+            choices=['CR', 'LF', 'CRLF'],
+            type=lambda c: c.upper(),
+            help="end of line mode",
+            default='CRLF')
+
+    group.add_argument(
+            "--raw",
+            action="store_true",
+            help="Do no apply any encodings/transformations",
+            default=False)
+
+    group = parser.add_argument_group("hotkeys")
+
+    group.add_argument(
+            "--exit-char",
+            type=int,
+            metavar='NUM',
+            help="Unicode of special character that is used to exit the application, default: %(default)s",
+            default=0x1d  # GS/CTRL+]
+            )
+
+    group.add_argument(
+            "--menu-char",
+            type=int,
+            metavar='NUM',
+            help="Unicode code of special character that is used to control miniterm (menu), default: %(default)s",
+            default=0x14  # Menu: CTRL+T
+            )
+
+    group = parser.add_argument_group("diagnostics")
+
+    group.add_argument(
+            "-q", "--quiet",
+            action="store_true",
+            help="suppress non-error messages",
+            default=False)
+
+    group.add_argument(
+            "--develop",
+            action="store_true",
+            help="show Python traceback on error",
+            default=False)
+
+    args = parser.parse_args()
+
+    if args.menu_char == args.exit_char:
+        parser.error('--exit-char can not be the same as --menu-char')
+
+    if args.filter:
+        if 'help' in args.filter:
+            sys.stderr.write('Available filters:\n')
+            sys.stderr.write('\n'.join(
+                    '{:<10} = {.__doc__}'.format(k, v)
+                    for k, v in sorted(TRANSFORMATIONS.items())))
+            sys.stderr.write('\n')
+            sys.exit(1)
+        filters = args.filter
+    else:
+        filters = ['default']
+
+    while True:
+        # no port given on command line -> ask user now
+        if args.port is None or args.port == '-':
+            try:
+                args.port = ask_for_port()
+            except KeyboardInterrupt:
+                sys.stderr.write('\n')
+                parser.error('user aborted and port is not given')
+            else:
+                if not args.port:
+                    parser.error('port is not given')
+        try:
+            serial_instance = serial.serial_for_url(
+                    args.port,
+                    args.baudrate,
+                    parity=args.parity,
+                    rtscts=args.rtscts,
+                    xonxoff=args.xonxoff,
+                    timeout=1,
+                    do_not_open=True)
+
+            if args.dtr is not None:
+                if not args.quiet:
+                    sys.stderr.write('--- forcing DTR {}\n'.format('active' if args.dtr else 'inactive'))
+                serial_instance.dtr = args.dtr
+            if args.rts is not None:
+                if not args.quiet:
+                    sys.stderr.write('--- forcing RTS {}\n'.format('active' if args.rts else 'inactive'))
+                serial_instance.rts = args.rts
+
+            serial_instance.open()
+        except serial.SerialException as e:
+            sys.stderr.write('could not open port {}: {}\n'.format(repr(args.port), e))
+            if args.develop:
+                raise
+            if not args.ask:
+                sys.exit(1)
+            else:
+                args.port = '-'
+        else:
+            break
+
+    miniterm = Miniterm(
+            serial_instance,
+            echo=args.echo,
+            eol=args.eol.lower(),
+            filters=filters)
+    miniterm.exit_character = unichr(args.exit_char)
+    miniterm.menu_character = unichr(args.menu_char)
+    miniterm.raw = args.raw
+    miniterm.set_rx_encoding(args.serial_port_encoding)
+    miniterm.set_tx_encoding(args.serial_port_encoding)
+
+    if not args.quiet:
+        sys.stderr.write('--- Miniterm on {p.name}  {p.baudrate},{p.bytesize},{p.parity},{p.stopbits} ---\n'.format(
+                p=miniterm.serial))
+        sys.stderr.write('--- Quit: {} | Menu: {} | Help: {} followed by {} ---\n'.format(
+                key_description(miniterm.exit_character),
+                key_description(miniterm.menu_character),
+                key_description(miniterm.menu_character),
+                key_description('\x08'),
+                ))
+
+    miniterm.start()
+    try:
+        miniterm.join(True)
+    except KeyboardInterrupt:
+        pass
+    if not args.quiet:
+        sys.stderr.write("\n--- exit ---\n")
+    miniterm.join()
+
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+if __name__ == '__main__':
+    main()
diff --git a/src/devtools/datool/pyserial/win32.py b/src/devtools/datool/pyserial/win32.py
new file mode 100644
index 0000000..cca6195
--- /dev/null
+++ b/src/devtools/datool/pyserial/win32.py
@@ -0,0 +1,343 @@
+#! python
+#
+# Constants and types for use with Windows API, used by serialwin32.py
+#
+# This file is part of pySerial. https://github.com/pyserial/pyserial
+# (C) 2001-2015 Chris Liechti <cliechti@gmx.net>
+#
+# SPDX-License-Identifier:    BSD-3-Clause
+
+from ctypes import *
+from ctypes.wintypes import HANDLE
+from ctypes.wintypes import BOOL
+from ctypes.wintypes import LPCWSTR
+from ctypes.wintypes import DWORD
+from ctypes.wintypes import WORD
+from ctypes.wintypes import BYTE
+_stdcall_libraries = {}
+_stdcall_libraries['kernel32'] = WinDLL('kernel32')
+
+INVALID_HANDLE_VALUE = HANDLE(-1).value
+
+
+# some details of the windows API differ between 32 and 64 bit systems..
+def is_64bit():
+    """Returns true when running on a 64 bit system"""
+    return sizeof(c_ulong) != sizeof(c_void_p)
+
+# ULONG_PTR is a an ordinary number, not a pointer and contrary to the name it
+# is either 32 or 64 bits, depending on the type of windows...
+# so test if this a 32 bit windows...
+if is_64bit():
+    # assume 64 bits
+    ULONG_PTR = c_int64
+else:
+    # 32 bits
+    ULONG_PTR = c_ulong
+
+
+class _SECURITY_ATTRIBUTES(Structure):
+    pass
+LPSECURITY_ATTRIBUTES = POINTER(_SECURITY_ATTRIBUTES)
+
+
+try:
+    CreateEventW = _stdcall_libraries['kernel32'].CreateEventW
+except AttributeError:
+    # Fallback to non wide char version for old OS...
+    from ctypes.wintypes import LPCSTR
+    CreateEventA = _stdcall_libraries['kernel32'].CreateEventA
+    CreateEventA.restype = HANDLE
+    CreateEventA.argtypes = [LPSECURITY_ATTRIBUTES, BOOL, BOOL, LPCSTR]
+    CreateEvent = CreateEventA
+
+    CreateFileA = _stdcall_libraries['kernel32'].CreateFileA
+    CreateFileA.restype = HANDLE
+    CreateFileA.argtypes = [LPCSTR, DWORD, DWORD, LPSECURITY_ATTRIBUTES, DWORD, DWORD, HANDLE]
+    CreateFile = CreateFileA
+else:
+    CreateEventW.restype = HANDLE
+    CreateEventW.argtypes = [LPSECURITY_ATTRIBUTES, BOOL, BOOL, LPCWSTR]
+    CreateEvent = CreateEventW  # alias
+
+    CreateFileW = _stdcall_libraries['kernel32'].CreateFileW
+    CreateFileW.restype = HANDLE
+    CreateFileW.argtypes = [LPCWSTR, DWORD, DWORD, LPSECURITY_ATTRIBUTES, DWORD, DWORD, HANDLE]
+    CreateFile = CreateFileW  # alias
+
+
+class _OVERLAPPED(Structure):
+    pass
+
+OVERLAPPED = _OVERLAPPED
+
+
+class _COMSTAT(Structure):
+    pass
+
+COMSTAT = _COMSTAT
+
+
+class _DCB(Structure):
+    pass
+
+DCB = _DCB
+
+
+class _COMMTIMEOUTS(Structure):
+    pass
+
+COMMTIMEOUTS = _COMMTIMEOUTS
+
+GetLastError = _stdcall_libraries['kernel32'].GetLastError
+GetLastError.restype = DWORD
+GetLastError.argtypes = []
+
+LPOVERLAPPED = POINTER(_OVERLAPPED)
+LPDWORD = POINTER(DWORD)
+
+GetOverlappedResult = _stdcall_libraries['kernel32'].GetOverlappedResult
+GetOverlappedResult.restype = BOOL
+GetOverlappedResult.argtypes = [HANDLE, LPOVERLAPPED, LPDWORD, BOOL]
+
+ResetEvent = _stdcall_libraries['kernel32'].ResetEvent
+ResetEvent.restype = BOOL
+ResetEvent.argtypes = [HANDLE]
+
+LPCVOID = c_void_p
+
+WriteFile = _stdcall_libraries['kernel32'].WriteFile
+WriteFile.restype = BOOL
+WriteFile.argtypes = [HANDLE, LPCVOID, DWORD, LPDWORD, LPOVERLAPPED]
+
+LPVOID = c_void_p
+
+ReadFile = _stdcall_libraries['kernel32'].ReadFile
+ReadFile.restype = BOOL
+ReadFile.argtypes = [HANDLE, LPVOID, DWORD, LPDWORD, LPOVERLAPPED]
+
+CloseHandle = _stdcall_libraries['kernel32'].CloseHandle
+CloseHandle.restype = BOOL
+CloseHandle.argtypes = [HANDLE]
+
+ClearCommBreak = _stdcall_libraries['kernel32'].ClearCommBreak
+ClearCommBreak.restype = BOOL
+ClearCommBreak.argtypes = [HANDLE]
+
+LPCOMSTAT = POINTER(_COMSTAT)
+
+ClearCommError = _stdcall_libraries['kernel32'].ClearCommError
+ClearCommError.restype = BOOL
+ClearCommError.argtypes = [HANDLE, LPDWORD, LPCOMSTAT]
+
+SetupComm = _stdcall_libraries['kernel32'].SetupComm
+SetupComm.restype = BOOL
+SetupComm.argtypes = [HANDLE, DWORD, DWORD]
+
+EscapeCommFunction = _stdcall_libraries['kernel32'].EscapeCommFunction
+EscapeCommFunction.restype = BOOL
+EscapeCommFunction.argtypes = [HANDLE, DWORD]
+
+GetCommModemStatus = _stdcall_libraries['kernel32'].GetCommModemStatus
+GetCommModemStatus.restype = BOOL
+GetCommModemStatus.argtypes = [HANDLE, LPDWORD]
+
+LPDCB = POINTER(_DCB)
+
+GetCommState = _stdcall_libraries['kernel32'].GetCommState
+GetCommState.restype = BOOL
+GetCommState.argtypes = [HANDLE, LPDCB]
+
+LPCOMMTIMEOUTS = POINTER(_COMMTIMEOUTS)
+
+GetCommTimeouts = _stdcall_libraries['kernel32'].GetCommTimeouts
+GetCommTimeouts.restype = BOOL
+GetCommTimeouts.argtypes = [HANDLE, LPCOMMTIMEOUTS]
+
+PurgeComm = _stdcall_libraries['kernel32'].PurgeComm
+PurgeComm.restype = BOOL
+PurgeComm.argtypes = [HANDLE, DWORD]
+
+SetCommBreak = _stdcall_libraries['kernel32'].SetCommBreak
+SetCommBreak.restype = BOOL
+SetCommBreak.argtypes = [HANDLE]
+
+SetCommMask = _stdcall_libraries['kernel32'].SetCommMask
+SetCommMask.restype = BOOL
+SetCommMask.argtypes = [HANDLE, DWORD]
+
+SetCommState = _stdcall_libraries['kernel32'].SetCommState
+SetCommState.restype = BOOL
+SetCommState.argtypes = [HANDLE, LPDCB]
+
+SetCommTimeouts = _stdcall_libraries['kernel32'].SetCommTimeouts
+SetCommTimeouts.restype = BOOL
+SetCommTimeouts.argtypes = [HANDLE, LPCOMMTIMEOUTS]
+
+WaitForSingleObject = _stdcall_libraries['kernel32'].WaitForSingleObject
+WaitForSingleObject.restype = DWORD
+WaitForSingleObject.argtypes = [HANDLE, DWORD]
+
+ONESTOPBIT = 0  # Variable c_int
+TWOSTOPBITS = 2  # Variable c_int
+ONE5STOPBITS = 1
+
+NOPARITY = 0  # Variable c_int
+ODDPARITY = 1  # Variable c_int
+EVENPARITY = 2  # Variable c_int
+MARKPARITY = 3
+SPACEPARITY = 4
+
+RTS_CONTROL_HANDSHAKE = 2  # Variable c_int
+RTS_CONTROL_DISABLE = 0  # Variable c_int
+RTS_CONTROL_ENABLE = 1  # Variable c_int
+RTS_CONTROL_TOGGLE = 3  # Variable c_int
+SETRTS = 3
+CLRRTS = 4
+
+DTR_CONTROL_HANDSHAKE = 2  # Variable c_int
+DTR_CONTROL_DISABLE = 0  # Variable c_int
+DTR_CONTROL_ENABLE = 1  # Variable c_int
+SETDTR = 5
+CLRDTR = 6
+
+MS_DSR_ON = 32  # Variable c_ulong
+EV_RING = 256  # Variable c_int
+EV_PERR = 512  # Variable c_int
+EV_ERR = 128  # Variable c_int
+SETXOFF = 1  # Variable c_int
+EV_RXCHAR = 1  # Variable c_int
+GENERIC_WRITE = 1073741824  # Variable c_long
+PURGE_TXCLEAR = 4  # Variable c_int
+FILE_FLAG_OVERLAPPED = 1073741824  # Variable c_int
+EV_DSR = 16  # Variable c_int
+MAXDWORD = 4294967295  # Variable c_uint
+EV_RLSD = 32  # Variable c_int
+ERROR_SUCCESS = 0
+ERROR_IO_PENDING = 997  # Variable c_long
+MS_CTS_ON = 16  # Variable c_ulong
+EV_EVENT1 = 2048  # Variable c_int
+EV_RX80FULL = 1024  # Variable c_int
+PURGE_RXABORT = 2  # Variable c_int
+FILE_ATTRIBUTE_NORMAL = 128  # Variable c_int
+PURGE_TXABORT = 1  # Variable c_int
+SETXON = 2  # Variable c_int
+OPEN_EXISTING = 3  # Variable c_int
+MS_RING_ON = 64  # Variable c_ulong
+EV_TXEMPTY = 4  # Variable c_int
+EV_RXFLAG = 2  # Variable c_int
+MS_RLSD_ON = 128  # Variable c_ulong
+GENERIC_READ = 2147483648  # Variable c_ulong
+EV_EVENT2 = 4096  # Variable c_int
+EV_CTS = 8  # Variable c_int
+EV_BREAK = 64  # Variable c_int
+PURGE_RXCLEAR = 8  # Variable c_int
+INFINITE = 0xFFFFFFFF
+
+
+class N11_OVERLAPPED4DOLLAR_48E(Union):
+    pass
+
+
+class N11_OVERLAPPED4DOLLAR_484DOLLAR_49E(Structure):
+    pass
+
+
+N11_OVERLAPPED4DOLLAR_484DOLLAR_49E._fields_ = [
+    ('Offset', DWORD),
+    ('OffsetHigh', DWORD),
+]
+
+PVOID = c_void_p
+
+N11_OVERLAPPED4DOLLAR_48E._anonymous_ = ['_0']
+N11_OVERLAPPED4DOLLAR_48E._fields_ = [
+    ('_0', N11_OVERLAPPED4DOLLAR_484DOLLAR_49E),
+    ('Pointer', PVOID),
+]
+_OVERLAPPED._anonymous_ = ['_0']
+_OVERLAPPED._fields_ = [
+    ('Internal', ULONG_PTR),
+    ('InternalHigh', ULONG_PTR),
+    ('_0', N11_OVERLAPPED4DOLLAR_48E),
+    ('hEvent', HANDLE),
+]
+_SECURITY_ATTRIBUTES._fields_ = [
+    ('nLength', DWORD),
+    ('lpSecurityDescriptor', LPVOID),
+    ('bInheritHandle', BOOL),
+]
+_COMSTAT._fields_ = [
+    ('fCtsHold', DWORD, 1),
+    ('fDsrHold', DWORD, 1),
+    ('fRlsdHold', DWORD, 1),
+    ('fXoffHold', DWORD, 1),
+    ('fXoffSent', DWORD, 1),
+    ('fEof', DWORD, 1),
+    ('fTxim', DWORD, 1),
+    ('fReserved', DWORD, 25),
+    ('cbInQue', DWORD),
+    ('cbOutQue', DWORD),
+]
+_DCB._fields_ = [
+    ('DCBlength', DWORD),
+    ('BaudRate', DWORD),
+    ('fBinary', DWORD, 1),
+    ('fParity', DWORD, 1),
+    ('fOutxCtsFlow', DWORD, 1),
+    ('fOutxDsrFlow', DWORD, 1),
+    ('fDtrControl', DWORD, 2),
+    ('fDsrSensitivity', DWORD, 1),
+    ('fTXContinueOnXoff', DWORD, 1),
+    ('fOutX', DWORD, 1),
+    ('fInX', DWORD, 1),
+    ('fErrorChar', DWORD, 1),
+    ('fNull', DWORD, 1),
+    ('fRtsControl', DWORD, 2),
+    ('fAbortOnError', DWORD, 1),
+    ('fDummy2', DWORD, 17),
+    ('wReserved', WORD),
+    ('XonLim', WORD),
+    ('XoffLim', WORD),
+    ('ByteSize', BYTE),
+    ('Parity', BYTE),
+    ('StopBits', BYTE),
+    ('XonChar', c_char),
+    ('XoffChar', c_char),
+    ('ErrorChar', c_char),
+    ('EofChar', c_char),
+    ('EvtChar', c_char),
+    ('wReserved1', WORD),
+]
+_COMMTIMEOUTS._fields_ = [
+    ('ReadIntervalTimeout', DWORD),
+    ('ReadTotalTimeoutMultiplier', DWORD),
+    ('ReadTotalTimeoutConstant', DWORD),
+    ('WriteTotalTimeoutMultiplier', DWORD),
+    ('WriteTotalTimeoutConstant', DWORD),
+]
+__all__ = ['GetLastError', 'MS_CTS_ON', 'FILE_ATTRIBUTE_NORMAL',
+           'DTR_CONTROL_ENABLE', '_COMSTAT', 'MS_RLSD_ON',
+           'GetOverlappedResult', 'SETXON', 'PURGE_TXABORT',
+           'PurgeComm', 'N11_OVERLAPPED4DOLLAR_48E', 'EV_RING',
+           'ONESTOPBIT', 'SETXOFF', 'PURGE_RXABORT', 'GetCommState',
+           'RTS_CONTROL_ENABLE', '_DCB', 'CreateEvent',
+           '_COMMTIMEOUTS', '_SECURITY_ATTRIBUTES', 'EV_DSR',
+           'EV_PERR', 'EV_RXFLAG', 'OPEN_EXISTING', 'DCB',
+           'FILE_FLAG_OVERLAPPED', 'EV_CTS', 'SetupComm',
+           'LPOVERLAPPED', 'EV_TXEMPTY', 'ClearCommBreak',
+           'LPSECURITY_ATTRIBUTES', 'SetCommBreak', 'SetCommTimeouts',
+           'COMMTIMEOUTS', 'ODDPARITY', 'EV_RLSD',
+           'GetCommModemStatus', 'EV_EVENT2', 'PURGE_TXCLEAR',
+           'EV_BREAK', 'EVENPARITY', 'LPCVOID', 'COMSTAT', 'ReadFile',
+           'PVOID', '_OVERLAPPED', 'WriteFile', 'GetCommTimeouts',
+           'ResetEvent', 'EV_RXCHAR', 'LPCOMSTAT', 'ClearCommError',
+           'ERROR_IO_PENDING', 'EscapeCommFunction', 'GENERIC_READ',
+           'RTS_CONTROL_HANDSHAKE', 'OVERLAPPED',
+           'DTR_CONTROL_HANDSHAKE', 'PURGE_RXCLEAR', 'GENERIC_WRITE',
+           'LPDCB', 'CreateEventW', 'SetCommMask', 'EV_EVENT1',
+           'SetCommState', 'LPVOID', 'CreateFileW', 'LPDWORD',
+           'EV_RX80FULL', 'TWOSTOPBITS', 'LPCOMMTIMEOUTS', 'MAXDWORD',
+           'MS_DSR_ON', 'MS_RING_ON',
+           'N11_OVERLAPPED4DOLLAR_484DOLLAR_49E', 'EV_ERR',
+           'ULONG_PTR', 'CreateFile', 'NOPARITY', 'CloseHandle']
diff --git a/src/devtools/datool/tool/gen-dalk-from-lk.py b/src/devtools/datool/tool/gen-dalk-from-lk.py
new file mode 100755
index 0000000..52dffa9
--- /dev/null
+++ b/src/devtools/datool/tool/gen-dalk-from-lk.py
@@ -0,0 +1,197 @@
+#!/usr/bin/env python
+
+#
+# Copyright (C) 2015 MediaTek Inc. All rights reserved.
+# Tristan Shieh <tristan.shieh@mediatek.com>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+#    notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+#    notice, this list of conditions and the following disclaimer in the
+#    documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+
+import os
+import struct
+import sys
+
+def read(path):
+        with open(path, "rb") as f:
+                return f.read()
+
+def write(path, data):
+        with open(path, "wb") as f:
+                f.write(data)
+
+def padding(data, size, pattern = '\0'):
+        return data + pattern * (size - len(data))
+
+LK_MEM_ADDRs = {'8135': 0x81e00000,
+                '8127': 0x81e00000,
+                '6595': 0x41e00000,
+                '8172': 0x41e00000,
+                '8173': 0x41e00000,
+                '7623': 0x81e00000,
+                '8163': 0x41e00000}
+
+BOOTARG_OFFSET = 0x80
+
+boot_args = {
+        '8135': struct.pack("26I",
+        0x504c504c, 0x00000063, 0x00000000, 0x11009000,
+        0x000e1000, 0x00500a01, 0x00000001, 0x34000000,
+        0x10240d40, 0x02101010, 0x000a8200, 0x00000000,
+        0x00000000, 0x00000000, 0x00000231, 0x00000000,
+        0x00000000, 0x00000000, 0x822041c1, 0x51200655,
+        0x92124805, 0x18420000, 0x3a00a284, 0xc0444890,
+        0x1980a991, 0x04000099),
+
+        '8127': struct.pack("27I",
+        0x504C504C, 0x00000063, 0x00000000, 0x11002000,
+        0x000E1000, 0x00000301, 0x00000001, 0x37C00000,
+        0x00000000, 0x00000000, 0x00000000, 0x00000003,
+        0x00000000, 0x00000000, 0x00002116, 0x00000000,
+        0x00000000, 0x00000000, 0x00000000, 0x00000000,
+        0x00000000, 0x00000000, 0xAFB50204, 0x00000000,
+        0x00000000, 0x00000000, 0x00008127),
+
+        '6595': struct.pack("26I",
+        0x504c504c, 0x00000063, 0x00000000, 0x11002000,
+        0x000e1000, 0xEBFE0101, 0x00000001, 0x80000000,
+        0x00000000, 0xE59304C0, 0xE3500000, 0x00000000,
+        0x00000000, 0x00000000, 0x00000231, 0x00000000,
+        0x00000000, 0x00000000, 0x822041c1, 0x51200655,
+        0x92124805, 0x18420000, 0x40079a84, 0xE1A09000,
+        0x00000000, 0x00000000),
+
+        '8172': struct.pack("102I",
+        0x504C504C, 0x00000063, 0x00000000, 0x11002000,
+        0x000E1000, 0x00000301, 0x00000001, 0x76C00000,
+        0x00000000, 0x00000000, 0x00000000, 0x00000000,
+        0x00000000, 0x00000000, 0x00000000, 0x00000000,
+        0x00000000, 0x00000000, 0x00000000, 0x00000000,
+        0x00000000, 0x00000000, 0x00000000, 0x00000000,
+        0x00000000, 0x00000000, 0x00000000, 0x00000000,
+        0x00000000, 0x00000000, 0x00000000, 0x00000000,
+        0x00000000, 0x00000000, 0x00000000, 0x00000000,
+        0x00000000, 0x00000000, 0x00000000, 0x00000000,
+        0x00000000, 0x00000000, 0x00000000, 0x00000000,
+        0x00000000, 0x00000000, 0x00000000, 0x00000000,
+        0x00000000, 0x00000000, 0x00000000, 0x00000000,
+        0x00000000, 0x00000000, 0x00000000, 0x00000000,
+        0x00000000, 0x00000000, 0x00000000, 0x00000000,
+        0xB6C00000, 0x00000000, 0x00000000, 0x00000000,
+        0x00000000, 0x00000000, 0x00000000, 0x00000A2B,
+        0x00000000, 0x00000000, 0x00000000, 0x00000000,
+        0x00000000, 0x00000000, 0x00000000, 0x42079A84,
+        0x00000000, 0x00000000, 0x00000000, 0x002198C0,
+        0x00000000, 0x00000000, 0x00000007, 0x00000005,
+        0x0012FB00, 0x00000500, 0x00000000, 0x00000000,
+        0x00000000, 0x00000000, 0x00000000, 0x00000000,
+        0x00000000, 0x00000000, 0x00000000, 0x00000000,
+        0x00000000, 0x00000000, 0xB6C00000, 0x00000000,
+        0x09400000, 0x00000000),
+
+        '8173': struct.pack("44I",
+        0x504C504C, 0x00000063, 0x00000000, 0x11002000,
+        0x000E1000, 0x00000301, 0x00000001, 0x7F800000,
+        0x00000000, 0x00000000, 0x00000000, 0x00000001,
+        0x00000000, 0x00000000, 0x000014E7, 0x00000000,
+        0x00000000, 0x00000000, 0x00000000, 0x00000000,
+        0x00000000, 0x00000000, 0x40079A84, 0x00000000,
+        0x00000000, 0x00000000, 0x00000000, 0x00000000,
+        0x00000000, 0x00000000, 0x00000000, 0x00000000,
+        0x00000000, 0x00000000, 0x001997C0, 0x00000000,
+        0x00000007, 0x00000005, 0x0012C000, 0x00004000,
+        0xBF800000, 0x00000000, 0x00800000, 0x00000000),
+
+        '7623': struct.pack("27I",
+        0x504C504C, 0x00000063, 0x00000000, 0x11004000,
+        0x000E1000, 0xFFFC4201, 0x00000001, 0x3FE00000,
+        0xDFF4FFBF, 0xFE7FFFFF, 0xDFEFFFFF, 0x00000000,
+        0x00000000, 0x00000000, 0x000011FD, 0xFFFDB7FE,
+        0xF1FF2FFF, 0xFFFFFFAF, 0xFFF29FFF, 0xBF7BDE7F,
+        0x3FFD997F, 0xFFEFFFBF, 0xAFB50204, 0xFECF3FFF,
+        0x00000000, 0x00000000, 0x00008590),
+
+        '8163': struct.pack("88I",
+        0x504C504C, 0x00000063, 0x00000000, 0x11002000,
+        0x000E1000, 0xFFFF0301, 0x00000001, 0x3FC00000,
+        0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+        0x00000001, 0x00000000, 0x40000000, 0x00000000,
+        0x3FC00000, 0x00000000, 0x00000000, 0x00000000,
+        0x00000000, 0x00000000, 0x00000000, 0x00000000,
+        0x00000000, 0x00000000, 0x00000000, 0x00000000,
+        0x00000000, 0x00000000, 0x00000000, 0x00000000,
+        0x00000000, 0x00000000, 0x00000000, 0x00000000,
+        0x00000000, 0x00000000, 0x00000001, 0xFFFFFFFF,
+        0x40000000, 0x00000000, 0x40000000, 0x00000000,
+        0x00000000, 0x00000000, 0x00000000, 0x00000000,
+        0x00000000, 0x00000000, 0x00000000, 0x00000000,
+        0x00000000, 0x00000000, 0x00000000, 0x00000000,
+        0x00000000, 0x00000000, 0x00000000, 0x00000000,
+        0x7FC00000, 0x00000000, 0x00400000, 0x00000000,
+        0x00000000, 0x00000000, 0x00000000, 0x0000E4DC,
+        0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+        0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x42058A04,
+        0xFFFFFFFF, 0x00000000, 0x00000000, 0x00178980,
+        0x11003000, 0x00000000, 0x00000006, 0x00000004,
+        0x0012C000, 0x00004000, 0x2E312E30, 0x25003030)}
+
+def main(argv):
+        lk = read(argv[2])
+
+        print("CHIP_ID: %s" % argv[1])
+        boot_arg = boot_args[argv[1]]
+        LK_MEM_ADDR = LK_MEM_ADDRs[argv[1]]
+
+        lk_wrapper = struct.pack("21I",
+					#LK_WRAPPER:
+         0xe1a0000f, 			#   0:	mov     r0, pc
+         0xe2400008, 			#   4:	sub     r0, r0, #8
+         0xe59f1030, 			#   8:	ldr     r1, [pc, #48]   ; 40 <COPY+0x1c>
+         0xe0800001, 			#   c:	add     r0, r0, r1
+         0xe59f102c, 			#  10:	ldr     r1, [pc, #44]   ; 44 <COPY+0x20>
+         0xe59f202c, 			#  14:	ldr     r2, [pc, #44]   ; 48 <COPY+0x24>
+         0xe0812002, 			#  18:	add     r2, r1, r2
+         0xe59f3028, 			#  1c:	ldr     r3, [pc, #40]   ; 4c <COPY+0x28>
+         0xe0822003, 			#  20:	add     r2, r2, r3
+					#COPY:
+         0xe1510002, 			#  24:	cmp     r1, r2
+         0x34903004, 			#  28:	ldrcc   r3, [r0], #4
+         0x34813004, 			#  2c:	strcc   r3, [r1], #4
+         0x3afffffb, 			#  30:	bcc     24 <COPY>
+         0xe59f4008, 			#  34:	ldr     r4, [pc, #8]    ; 44 <COPY+0x20>
+         0xe59f5008, 			#  38:	ldr     r5, [pc, #8]    ; 48 <COPY+0x24>
+         0xe59ff00c, 			#  3c:	ldr     pc, [pc, #12]   ; 50 <COPY+0x2c>
+         BOOTARG_OFFSET,		#  40:	BOOTARG_OFFSET  .word   0x11111111
+         LK_MEM_ADDR - len(boot_arg),	#  44:	BOOTARG_ADDR    .word   0x22222222
+         len(boot_arg),			#  48:	BOOTARG_SIZE    .word   0x33333333
+         len(lk),			#  4c:	LK_SIZE         .word   0x44444444
+         LK_MEM_ADDR			#  50:	LK_ADDR         .word   0x55555555
+        )
+
+        o = padding(lk_wrapper, BOOTARG_OFFSET, '\0') + boot_arg + lk
+        # padding to even-sized output
+        write(argv[3], padding(o, len(o)+len(o)%2, '\0'))
+
+
+if __name__ == "__main__":
+        main(sys.argv)
+
+
diff --git a/src/devtools/datool/tool/signfile-for-brom.sh b/src/devtools/datool/tool/signfile-for-brom.sh
new file mode 100755
index 0000000..335f291
--- /dev/null
+++ b/src/devtools/datool/tool/signfile-for-brom.sh
@@ -0,0 +1,88 @@
+#!/bin/bash
+
+#
+# Copyright (C) 2015 MediaTek Inc. All rights reserved.
+# Tristan Shieh <tristan.shieh@mediatek.com>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+#    notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+#    notice, this list of conditions and the following disclaimer in the
+#    documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+
+if [ $# -lt 2 -o $# -gt 3 ]
+then
+       echo "Usage: $0 private-key.pem filename [pss]"
+       exit -1
+fi
+
+TMPFILE1=$(mktemp)
+TMPFILE2=$(mktemp)
+
+python -c "
+import hashlib
+
+f = open('$2', 'rb')
+b = f.read()
+f.close()
+
+d = hashlib.sha256(b).digest()
+if '$3' != 'pss':
+	b = '\0\0'
+	for i in range(0, len(d), 2):
+	       b += d[i + 1] + d[i]
+	b += '\0' * 222
+else:
+	b = d
+
+f = open('${TMPFILE1}', 'wb')
+f.write(b)
+f.close()
+"
+
+if [ -z "$3" -o "$3" != "pss" ]; then
+openssl rsautl -sign -inkey $1 -raw -in ${TMPFILE1} -out ${TMPFILE2}
+else
+openssl pkeyutl -sign -inkey $1 -in ${TMPFILE1} -out ${TMPFILE2} -pkeyopt digest:sha256 -pkeyopt rsa_padding_mode:pss -pkeyopt rsa_pss_saltlen:32
+fi
+RET=$?
+
+python -c "
+f = open('${TMPFILE2}', 'rb')
+d = f.read()
+f.close()
+b = ''
+if '$3' != 'pss':
+	for i in range(0, len(d), 2):
+		b += d[i + 1] + d[i]
+else:
+	b = d;
+f = open('$2.sign', 'wb')
+f.write(b)
+f.close()
+"
+
+if [ "$3" = "pss" ]; then
+        echo "Signature file $2.sign with pss padding is generated"
+else
+        echo "Signature file $2.sign with legacy padding is generated"
+fi
+
+rm -f ${TMPFILE1} ${TMPFILE2}
+exit ${RET}
diff --git a/src/devtools/datool/tool/signfile-for-pl.sh b/src/devtools/datool/tool/signfile-for-pl.sh
new file mode 100755
index 0000000..2f7070c
--- /dev/null
+++ b/src/devtools/datool/tool/signfile-for-pl.sh
@@ -0,0 +1,91 @@
+#!/bin/bash
+
+#
+# Copyright (C) 2015 MediaTek Inc. All rights reserved.
+# Tristan Shieh <tristan.shieh@mediatek.com>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+#    notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+#    notice, this list of conditions and the following disclaimer in the
+#    documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+
+if [ $# -ne 2 ]
+then
+       echo "Usage: $0 private-key.pem filename"
+       exit -1
+fi
+
+TMPFILE1=$(mktemp)
+TMPFILE2=$(mktemp)
+
+python -c "
+import hashlib
+import struct
+import os
+
+file_len = os.path.getsize('$2')
+b = struct.pack('16I', \
+         0x53535353, 0x54535543, 0x00000000, 0x00000000, \
+         0x00000000, 0x00000000, 0x00000000, 0x00000000, \
+         0x00000000, 0x00000001, file_len, 0x00000040, \
+         0x00000000, file_len, file_len+0x40, 0x00000094, )
+
+f = open('$2', 'rb')
+b += f.read()
+f.close()
+
+d = hashlib.sha1(b).digest()
+
+f = open('${TMPFILE1}', 'wb')
+f.write(d)
+f.close()
+"
+
+openssl rsautl -sign -inkey $1 -in ${TMPFILE1} -out ${TMPFILE2}
+RET=$?
+
+python -c "
+import struct
+import os
+
+file_len = os.path.getsize('$2')
+b = struct.pack('16I', \
+         0x53535353, 0x54535543, 0x00000000, 0x00000000, \
+         0x00000000, 0x00000000, 0x00000000, 0x00000000, \
+         0x00000000, 0x00000001, file_len, 0x00000040, \
+         0x00000000, file_len, file_len+0x40, 0x00000094, )
+
+f = open('${TMPFILE2}', 'rb')
+b += f.read()
+f.close()
+
+f = open('${TMPFILE1}', 'rb')
+b += f.read(20)
+f.close()
+
+b += '\0' * 44
+
+f = open('$2.sign', 'wb')
+f.write(b)
+f.close()
+"
+
+rm -f ${TMPFILE1} ${TMPFILE2}
+exit ${RET}
diff --git a/src/devtools/met-driver/4.14/Android.mk.backup b/src/devtools/met-driver/4.14/Android.mk.backup
new file mode 100644
index 0000000..42d6942
--- /dev/null
+++ b/src/devtools/met-driver/4.14/Android.mk.backup
@@ -0,0 +1,11 @@
+LOCAL_PATH := $(call my-dir)
+
+ifneq (,$(filter $(word 2,$(subst -, ,$(LINUX_KERNEL_VERSION))),$(subst /, ,$(LOCAL_PATH))))
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := met.ko
+LOCAL_STRIP_MODULE := true
+MET_DRIVER_DIR := $(LOCAL_PATH)
+include $(MTK_KERNEL_MODULE)
+
+endif # Kernel version matches current path
diff --git a/src/devtools/met-driver/4.14/Makefile b/src/devtools/met-driver/4.14/Makefile
new file mode 100644
index 0000000..56fc419
--- /dev/null
+++ b/src/devtools/met-driver/4.14/Makefile
@@ -0,0 +1,47 @@
+ifneq ($(MET_DRIVER_DIR),)
+	MTK_PLATFORM := $(subst ",,$(CONFIG_MTK_PLATFORM))
+	MET_COMMON_DIR := $(MET_DRIVER_DIR)/common
+	MET_PLF_DIR := $(MET_DRIVER_DIR)/$(MTK_PLATFORM)
+	MET_BUILD_DEFAULT := n
+
+	ifneq ($(KERNEL_OUT),)
+		include $(KERNEL_OUT)/.config
+		ccflags-y += -imacros $(KERNEL_OUT)/include/generated/autoconf.h
+	endif
+
+	ifeq ($(CONFIG_MODULES),y)
+		ifeq ($(CONFIG_FTRACE),y)
+			ifeq ($(CONFIG_TRACING),y)
+				FTRACE_READY := y
+			endif
+		endif
+
+        $(info ******** Start to build met_drv for $(MTK_PLATFORM) ********)
+		ifneq ($(MET_PLF_DIR),)
+			ifeq ($(FTRACE_READY),y)
+				ifeq ($(CONFIG_BUILD_YOCTO),y)
+					include $(MET_COMMON_DIR)/Kbuild.yocto
+				else
+					$(warning No Kbuild.yocto, build met default)
+					MET_BUILD_DEFAULT = y
+				endif
+			else
+				$(warning Not building met.ko due to CONFIG_FTRACE/CONFIG_TRACING is not set, build met default)
+				MET_BUILD_DEFAULT = y
+			endif
+		else
+			$(warning not support $(MTK_PLATFORM), build met default)
+			MET_BUILD_DEFAULT = y
+		endif
+	else
+		$(warning Not building met.ko due to CONFIG_MODULES is not set, build met default)
+		MET_BUILD_DEFAULT := y
+	endif
+
+	ifeq ($(MET_BUILD_DEFAULT),y)
+		MET_DEF_DIR := $(MET_DRIVER_DIR)/default
+		include $(MET_DEF_DIR)/Kbuild
+	endif
+else
+$(info ******** MET_DRIVER_DIR is empty ********)
+endif
diff --git a/src/devtools/met-driver/4.14/Makefile.yocto b/src/devtools/met-driver/4.14/Makefile.yocto
new file mode 100644
index 0000000..12b3158
--- /dev/null
+++ b/src/devtools/met-driver/4.14/Makefile.yocto
@@ -0,0 +1,15 @@
+MODULE_NAME := met
+
+##############################################################
+# Compile settings
+##############################################################
+all: 
+	make -C $(KERNEL_OUT) M=$(MET_DRIVER_DIR) modules
+
+clean: 
+	make -C $(KERNEL_OUT) M=$(MET_DRIVER_DIR) clean
+	if [ -e $(MET_DRIVER_DIR)/$(MODULE_NAME).ko ]; then rm $(MET_DRIVER_DIR)/$(MODULE_NAME).ko; fi;
+
+.PHONY: all clean
+
+
diff --git a/src/devtools/met-driver/4.14/common/Kbuild.yocto b/src/devtools/met-driver/4.14/common/Kbuild.yocto
new file mode 100644
index 0000000..a04e158
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/Kbuild.yocto
@@ -0,0 +1,359 @@
+################################################################################
+#	MET Kernel module mode
+################################################################################
+$(info ======== Build met.ko ... ========)
+MET_CORE := common
+obj-m := met.o
+
+ifneq ($(wildcard $(MET_PLF_DIR)/Kbuild.platform.include),)
+    include $(MET_PLF_DIR)/Kbuild.platform.include
+else
+    $(info ======= Missing $(MET_PLF_DIR)/Kbuild.platform.include ========)
+endif
+
+ccflags-y += -DCONFIG_MET_MODULE
+ccflags-y += -DMET_PLF_USE
+ccflags-y += -I$(MET_COMMON_DIR)
+ccflags-y += -I$(MET_PLF_DIR)
+ccflags-y += -I$(KERNEL_SRC)/include/
+ccflags-y += -I$(KERNEL_SRC)/drivers/misc/mediatek/include/
+ccflags-y += -I$(KERNEL_SRC)/drivers/misc/mediatek/include/mt-plat/
+ccflags-y += -I$(KERNEL_SRC)/drivers/misc/mediatek/include/mt-plat/$(MTK_PLATFORM)/include/
+
+ccflags-y += $(EXTRA_ARGS) $(EXTRA_CFLAGS)
+ccflags-y += -DMTK_PLATFORM=$(MTK_PLATFORM)
+
+met-y := $(MET_CORE)/met_main.o \
+    $(MET_CORE)/met_backlight.o \
+    $(MET_CORE)/met_tag_ex.o \
+    $(MET_CORE)/interface.o \
+    $(MET_CORE)/sampler.o \
+    $(MET_CORE)/dummy_header.o \
+    $(MET_CORE)/util.o \
+    $(MET_CORE)/stat.o \
+    $(MET_CORE)/cookie.o \
+    $(MET_CORE)/mem_stat.o \
+    $(MET_CORE)/switch.o \
+    $(MET_CORE)/trace_event.o \
+    $(MET_CORE)/core_plf_init.o \
+    $(MET_CORE)/core_plf_trace.o \
+    $(MET_CORE)/ondiemet.o \
+    $(MET_CORE)/ondiemet_log.o \
+    $(MET_CORE)/sspm/ondiemet_sspm.o
+
+CFLAGS_interface.o += -DMET_USER_EVENT_SUPPORT
+CFLAGS_met_tag_ex.o += -DMET_USER_EVENT_SUPPORT
+
+$(info CPUPMU_VERSION = $(CPUPMU_VERSION))
+ifeq ("$(CPUPMU_VERSION)", "V8_2")
+    ccflags-y += -DCPUPMU_V8_2
+endif
+
+$(info ARCH = $(ARCH))
+ifeq ($(ARCH), mips)
+    met-y += $(MET_CORE)/mips_pmu_hw.o
+endif #ifeq ($(ARCH), mips)
+
+ifeq ($(ARCH), arm)
+    ccflags-y += -DCONFIG_MET_ARM_32BIT
+    met-y += $(MET_CORE)/cpu_pmu.o
+    met-y += $(MET_CORE)/v7_pmu_hw.o
+    met-y += $(MET_CORE)/v6_pmu_hw.o
+endif #ifeq ($(ARCH), arm)
+
+ifeq ($(ARCH), arm64)
+    met-y += $(MET_CORE)/cpu_pmu.o
+    met-y += $(MET_CORE)/v8_pmu_hw.o
+endif
+
+$(info CONFIG_CPU_FREQ = $(CONFIG_CPU_FREQ))
+ifeq ($(CONFIG_CPU_FREQ),y)
+    met-y += $(MET_CORE)/power.o
+endif
+
+################################################################################
+# MET_SPM_TWAM
+################################################################################
+FEATURE_SPMTWAM := $(if $(FEATURE_SPMTWAM),$(FEATURE_SPMTWAM),y)
+$(info FEATURE_SPMTWAM = $(FEATURE_SPMTWAM))
+
+ifneq ($(FEATURE_SPMTWAM), n)
+    MET_SPM_TWAM := y
+
+    # for mtk_spm.h
+    ifneq ("$(wildcard $(KERNEL_SRC)/drivers/misc/mediatek/base/power/include/mtk_spm.h)","")
+        ccflags-y += -I$(KERNEL_SRC)/drivers/misc/mediatek/base/power/include/
+        ccflags-y += -I$(MET_COMMON_DIR)/spmtwam/include/
+    else
+        MET_SPM_TWAM = n
+        $(info ========= Missing $(KERNEL_SRC)/drivers/misc/mediatek/base/power/include/mtk_spm.h ========)
+        $(info ======== disable MET_SPM_TWAM ========)
+    endif
+else
+    MET_SPM_TWAM := n
+endif
+
+$(info SPMTWAM_VERSION = $(SPMTWAM_VERSION))
+$(info SPMTWAM_IDLE_SIGNAL_SUPPORT = $(SPMTWAM_IDLE_SIGNAL_SUPPORT))
+
+ifeq ("$(SPMTWAM_IDLE_SIGNAL_SUPPORT)", "single")
+    ccflags-y += -DSPMTWAM_SINGLE_IDLE_SIGNAL
+endif
+
+ifeq ("$(SPMTWAM_IDLE_SIGNAL_SUPPORT)", "multiple")
+    ccflags-y += -DSPMTWAM_MULTIPLE_IDLE_SIGNAL
+endif
+
+ifeq ("$(SPMTWAM_VERSION)", "ap")
+    ccflags-y += -DSPMTWAM_AP
+    met-$(MET_SPM_TWAM) += $(MET_CORE)/spmtwam/ap/met_spmtwam.o
+endif
+
+ifeq ("$(SPMTWAM_VERSION)", "sspm")
+    ccflags-y += -DSPMTWAM_SSPM
+    met-$(MET_SPM_TWAM) += $(MET_CORE)/spmtwam/sspm/met_spmtwam.o
+endif
+
+################################################################################
+# MET_SSPM_EMI
+################################################################################
+FEATURE_SSPM_EMI := $(if $(FEATURE_SSPM_EMI),$(FEATURE_SSPM_EMI),y)
+$(info FEATURE_SSPM_EMI = $(FEATURE_SSPM_EMI))
+
+MET_SSPM_EMI := $(if $(filter n,$(FEATURE_SSPM_EMI)),n,y)
+
+met-$(MET_SSPM_EMI) += $(MET_CORE)/met_emi.o \
+        $(MET_CORE)/mtk_emi_bm.o
+
+################################################################################
+# MET_GPU
+################################################################################
+FEATURE_GPU := $(if $(FEATURE_GPU),$(FEATURE_GPU),y)
+$(info FEATURE_GPU = $(FEATURE_GPU))
+
+ifneq ($(FEATURE_GPU), n)
+    MET_GPU := y
+
+    # for mtk_gpufreq.h
+    ifneq ("$(wildcard $(KERNEL_SRC)/drivers/misc/mediatek/base/power/$(MTK_PLATFORM)/mtk_gpufreq.h)","")
+        ccflags-y += -I$(KERNEL_SRC)/drivers/misc/mediatek/base/power/$(MTK_PLATFORM)/
+    else
+        MET_GPU = n
+        $(info ======= Missing $(KERNEL_SRC)/drivers/misc/mediatek/base/power/$(MTK_PLATFORM)/mtk_gpufreq.h ========)
+        $(info ======== disable MET_GPU ========)
+    endif
+
+    # for mtk_gpu_utility.h
+    ifneq ("$(wildcard $(KERNEL_SRC)/drivers/misc/mediatek/include/mt-plat/mtk_gpu_utility.h)","")
+        ccflags-y += -I$(KERNEL_SRC)/drivers/misc/mediatek/include/mt-plat/
+    else
+        MET_GPU = n
+        $(info ======== Missing $(KERNEL_SRC)/drivers/misc/mediatek/include/mt-plat/mtk_gpu_utility.h ========)
+        $(info ======== disable MET_GPU ========)
+    endif
+
+    ifneq ($(CONFIG_MTK_GPU_SUPPORT), y)
+        MET_GPU = n
+        $(info ======== CONFIG_MTK_GPU_SUPPORT = n ========)
+        $(info ======== disable MET_GPU ========)
+    endif
+else
+    MET_GPU := n
+endif
+
+met-$(MET_GPU) += $(MET_CORE)/mtk_gpu_metmonitor.o
+
+
+################################################################################
+# MET_VCOREDVFS
+################################################################################
+FEATURE_VCOREDVFS := $(if $(FEATURE_VCOREDVFS),$(FEATURE_VCOREDVFS),y)
+$(info FEATURE_VCOREDVFS = $(FEATURE_VCOREDVFS))
+
+ifneq ($(FEATURE_VCOREDVFS), n)
+    MET_VCOREDVFS := y
+
+    # for mtk_vcorefs_manager.h
+    ifneq ("$(wildcard $(MET_VCOREDVFS_INC)/mtk_vcorefs_manager.h)","")
+        ccflags-y += -I$(MET_VCOREDVFS_INC)/
+    else
+        MET_VCOREDVFS = n
+        $(info ======== Missing $(MET_VCOREDVFS_INC)/mtk_vcorefs_manager.h ========)
+        $(info ======== disable MET_VCOREDVFS ========)
+    endif
+
+    # for mtk_vcorefs_governor.h
+    ifneq ("$(wildcard $(MET_VCOREDVFS_INC)/mtk_vcorefs_governor.h)","")
+        ccflags-y += -I$(MET_VCOREDVFS_INC)
+    else
+        MET_VCOREDVFS = n
+        $(info ======== Missing $(MET_VCOREDVFS_INC)/mtk_vcorefs_governor.h ========)
+        $(info ======== disable MET_VCOREDVFS ========)
+    endif
+
+    # for helio-dvfsrc.h
+    ifneq ("$(wildcard $(KERNEL_SRC)/drivers/devfreq/helio-dvfsrc.h)","")
+        ccflags-y += -I$(KERNEL_SRC)/drivers/devfreq/
+    else
+        MET_VCOREDVFS = n
+        $(info ======== Missing $(KERNEL_SRC)/drivers/devfreq/helio-dvfsrc.h ========)
+        $(info ======== disable MET_VCOREDVFS ========)
+    endif
+else
+    MET_VCOREDVFS := n
+endif
+
+met-$(MET_VCOREDVFS) += $(MET_CORE)/met_vcoredvfs.o
+
+
+################################################################################
+# MET_PTPOD
+################################################################################
+FEATURE_PTPOD := $(if $(FEATURE_PTPOD),$(FEATURE_PTPOD),y)
+$(info FEATURE_PTPOD = $(FEATURE_PTPOD))
+
+ifneq ($(FEATURE_PTPOD), n)
+    MET_PTPOD := y
+
+    # for mtk_gpufreq.h
+    ifneq ("$(wildcard $(KERNEL_SRC)/drivers/misc/mediatek/base/power/$(MTK_PLATFORM)/mtk_gpufreq.h)","")
+        ccflags-y += -I$(KERNEL_SRC)/drivers/misc/mediatek/base/power/$(MTK_PLATFORM)/
+    else
+        MET_PTPOD = n
+        $(info ======== Missing $(KERNEL_SRC)/drivers/misc/mediatek/base/power/$(MTK_PLATFORM)/mtk_gpufreq.h ========)
+        $(info ======== disable MET_PTPOD ========)
+    endif
+
+    # for mtk_cpufreq_api.h
+    ifneq ("$(wildcard $(KERNEL_SRC)/drivers/misc/mediatek/include/mt-plat/$(MTK_PLATFORM)/include/mach/mtk_cpufreq_api.h)","")
+        ccflags-y += -I$(KERNEL_SRC)/drivers/misc/mediatek/include/mt-plat/$(MTK_PLATFORM)/include/
+    else
+        MET_PTPOD = n
+        $(info ======== Missing $(KERNEL_SRC)/drivers/misc/mediatek/include/mt-plat/$(MTK_PLATFORM)/include/mach/mtk_cpufreq_api.h ========)
+        $(info ======== disable MET_PTPOD ========)
+    endif
+
+    # for mtk_cpufreq_config.h
+    ifneq ("$(wildcard $(MET_PTPOD_INC)/mtk_cpufreq_config.h)","")
+        ccflags-y += -I$(MET_PTPOD_INC)
+    else
+        MET_PTPOD = n
+        $(info ======== Missing $(MET_PTPOD_INC)/mtk_cpufreq_config.h ========)
+        $(info ======== disable MET_PTPOD ========)
+    endif
+else
+    MET_PTPOD := n
+endif
+
+met-$(MET_PTPOD) += $(MET_CORE)/met_ptpod.o
+
+
+################################################################################
+# MET_CPUDSU
+################################################################################
+FEATURE_CPUDSU := $(if $(FEATURE_CPUDSU),$(FEATURE_CPUDSU),y)
+$(info FEATURE_CPUDSU = $(FEATURE_CPUDSU))
+
+MET_CPUDSU := $(if $(filter n,$(FEATURE_CPUDSU)),n,y)
+
+met-$(MET_CPUDSU) += $(MET_CORE)/cpu_dsu.o \
+                     $(MET_CORE)/v8_dsu_hw.o
+
+################################################################################
+# MET_WALLTIME
+################################################################################
+FEATURE_WALLTIME := $(if $(FEATURE_WALLTIME),$(FEATURE_WALLTIME),y)
+$(info FEATURE_WALLTIME = $(FEATURE_WALLTIME))
+
+MET_WALLTIME := $(if $(filter n,$(FEATURE_WALLTIME)),n,y)
+
+met-$(MET_WALLTIME) += $(MET_CORE)/met_wall_time.o
+
+################################################################################
+# MET_SMI
+################################################################################
+FEATURE_SMI := $(if $(FEATURE_SMI),$(FEATURE_SMI),y)
+$(info FEATURE_SMI = $(FEATURE_SMI))
+
+MET_SMI := $(if $(filter n,$(FEATURE_SMI)),n,y)
+
+met-$(MET_SMI) += $(MET_CORE)/sspm/sspm_met_smi.o
+
+################################################################################
+# EVENT_POWER
+################################################################################
+FEATURE_EVENT_POWER := $(if $(FEATURE_EVENT_POWER),$(FEATURE_EVENT_POWER),y)
+$(info FEATURE_EVENT_POWER = $(FEATURE_EVENT_POWER))
+
+ifeq ($(FEATURE_EVENT_POWER), y)
+	ccflags-y += -DMET_EVENT_POWER_SUPPORT
+endif
+
+################################################################################
+# On-die-met SSPM only module
+################################################################################
+FEATURE_ONDIEMET := $(if $(FEATURE_ONDIEMET),$(FEATURE_ONDIEMET),y)
+ifeq ($(FEATURE_ONDIEMET), y)
+    FEATURE_ONDIEMET_WALLTIME := $(if $(FEATURE_ONDIEMET_WALLTIME),$(FEATURE_ONDIEMET_WALLTIME),y)
+else
+    FEATURE_ONDIEMET_WALLTIME := n
+endif
+
+$(info FEATURE_ONDIEMET = $(FEATURE_ONDIEMET))
+$(info FEATURE_ONDIEMET_WALLTIME = $(FEATURE_ONDIEMET_WALLTIME))
+
+ifneq ($(FEATURE_ONDIEMET), n)
+    subdir-ccflags-y += -DONDIEMET_SUPPORT
+
+    ifeq ($(CONFIG_MTK_TINYSYS_SSPM_SUPPORT),)
+        $(info CONFIG_MTK_TINYSYS_SSPM_SUPPORT = n)
+    else
+        $(info CONFIG_MTK_TINYSYS_SSPM_SUPPORT = $(CONFIG_MTK_TINYSYS_SSPM_SUPPORT))
+    endif
+
+    ifeq ($(CONFIG_MTK_TINYSYS_SSPM_SUPPORT),y)
+        # for sspm_ipi.h
+        subdir-ccflags-y += -I$(KERNEL_SRC)/drivers/misc/mediatek/sspm
+        subdir-ccflags-y += -I$(KERNEL_SRC)/drivers/misc/mediatek/sspm/$(MTK_PLATFORM)
+        met-y += $(MET_CORE)/sspm/sspm_ipi_handle.o
+        met-y += $(MET_CORE)/sspm/sspm_common.o
+        ccflags-y += -DMTK_TINYSYS_SSPM_SUPPORT
+
+        ifneq ("$(wildcard $(KERNEL_SRC)/drivers/misc/mediatek/sspm/$(MTK_PLATFORM)/sspm_ipi_define.h)","")
+            subdir-ccflags-y += -I$(KERNEL_SRC)/drivers/misc/mediatek/sspm \
+                    -I$(KERNEL_SRC)/drivers/misc/mediatek/sspm/$(MTK_PLATFORM)
+
+            SYS_SSPM_READY := y
+        else
+            $(info ======== Missing $(KERNEL_SRC)/drivers/misc/mediatek/sspm/$(MTK_PLATFORM)/sspm_ipi_define.h========)
+            $(info ======== disable ALL ondiemet feature ========)
+
+            SYS_SSPM_READY := n
+        endif
+    else
+        $(info ======== CONFIG_MTK_TINYSYS_SSPM_SUPPORT = n ========)
+        $(info ======== disable ALL ondiemet feature ========)
+
+        SYS_SSPM_READY := n
+    endif
+
+    ifeq ($(SYS_SSPM_READY), y)
+        MET_SSPM_WALLTIME := $(if $(filter n,$(FEATURE_ONDIEMET_WALLTIME)),n,y)
+
+        met-$(MET_SSPM_WALLTIME) += $(MET_CORE)/sspm/sspm_walltime.o
+    endif
+endif
+
+##############################################################################################
+# include $(MET_PLF_DIR)/Kbuild
+##############################################################################################
+ifneq ($(wildcard $(MET_PLF_DIR)/Kbuild.yocto),)
+	include $(MET_PLF_DIR)/Kbuild.yocto
+else
+	$(info ======= Missing $(MET_PLF_DIR)/Kbuild.yocto ========)
+endif
+
+#################################################################################
+# add met_device flags
+#################################################################################
+ccflags-y += $(foreach v, $(filter MET_%,$(.VARIABLES)), $(if $(filter $($(v)),y),-D$(v)))
diff --git a/src/devtools/met-driver/4.14/common/cookie.c b/src/devtools/met-driver/4.14/common/cookie.c
new file mode 100644
index 0000000..0e309fb
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/cookie.c
@@ -0,0 +1,281 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/cpu.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/cpu.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <asm/irq_regs.h>
+#include <asm/stacktrace.h>
+#include <linux/stacktrace.h>
+#include "interface.h"
+#include "met_drv.h"
+
+#define LINE_SIZE	256
+
+struct cookie_info {
+	int depth;
+	int strlen;
+	char strbuf[LINE_SIZE];
+};
+
+static unsigned int back_trace_depth;
+static DEFINE_PER_CPU(struct cookie_info, info);
+static DEFINE_PER_CPU(int, cpu_status);
+
+static int reset_driver_stat(void)
+{
+	back_trace_depth = 0;
+	met_cookie.mode = 0;
+	return 0;
+}
+
+
+noinline void cookie(char *strbuf)
+{
+	MET_TRACE("%s\n", strbuf);
+}
+
+noinline void cookie2(char *strbuf)
+{
+	MET_TRACE("%s\n", strbuf);
+}
+
+static void get_kernel_cookie(unsigned long pc, struct cookie_info *pinfo)
+{
+	int ret;
+#ifdef CONFIG_MODULES
+	off_t off;
+	struct module *mod = __module_address(pc);
+
+	if (mod) {
+		off = pc - (unsigned long)mod->core_layout.base;
+		ret = snprintf(pinfo->strbuf + pinfo->strlen, LINE_SIZE - pinfo->strlen,
+			       ",%s,%lx", mod->name, off);
+		pinfo->strlen += ret;
+		/* cookie(current->comm, pc, mod->name, off, 1); */
+	} else
+#endif
+	{
+		ret =
+		    snprintf(pinfo->strbuf + pinfo->strlen, LINE_SIZE - pinfo->strlen,
+			     ",vmlinux,%lx", pc);
+		pinfo->strlen += ret;
+		/* cookie(current->comm, pc, "vmlinux", pc, 0); */
+	}
+}
+
+#if defined(__arm__)
+static int report_trace(struct stackframe *frame, void *d)
+{
+	struct cookie_info *pinfo = d;
+	unsigned long pc = frame->pc;
+
+	if (pinfo->depth > 0) {
+		get_kernel_cookie(pc, pinfo);
+		pinfo->depth--;
+		return 0;
+	}
+	return 1;
+}
+#endif
+
+static void kernel_backtrace(struct pt_regs *const regs, struct cookie_info *pinfo)
+{
+#if defined(__arm__)
+	struct stackframe frame;
+
+	frame.fp = regs->ARM_fp;
+	frame.sp = regs->ARM_sp;
+	frame.lr = regs->ARM_lr;
+	frame.pc = regs->ARM_pc;
+	walk_stackframe(&frame, report_trace, pinfo);
+#else
+	return;
+#endif
+}
+
+
+void met_cookie_polling(unsigned long long stamp, int cpu)
+{
+	struct pt_regs *regs;
+	struct cookie_info *pinfo;
+	unsigned long pc;
+	int ret, outflag = 0;
+	off_t off;
+
+	if (per_cpu(cpu_status, cpu) != MET_CPU_ONLINE)
+		return;
+
+	regs = get_irq_regs();
+
+	if (regs == 0)
+		return;
+
+	pc = profile_pc(regs);
+
+	pinfo = &(per_cpu(info, cpu));
+	pinfo->strlen = snprintf(pinfo->strbuf, LINE_SIZE, "%s,%lx", current->comm, pc);
+
+	if (user_mode(regs)) {
+		struct mm_struct *mm;
+		struct vm_area_struct *vma;
+		struct path *ppath;
+
+		mm = current->mm;
+		for (vma = find_vma(mm, pc); vma; vma = vma->vm_next) {
+
+			if (pc < vma->vm_start || pc >= vma->vm_end)
+				continue;
+
+			if (vma->vm_file) {
+				ppath = &(vma->vm_file->f_path);
+
+				if (vma->vm_flags & VM_DENYWRITE)
+					off = pc;
+				else
+					off = (vma->vm_pgoff << PAGE_SHIFT) + pc - vma->vm_start;
+
+				ret =
+				    snprintf(pinfo->strbuf + pinfo->strlen,
+					     LINE_SIZE - pinfo->strlen, ",%s,%lx",
+					     (char *)(ppath->dentry->d_name.name), off);
+				pinfo->strlen += ret;
+				outflag = 1;
+			} else {
+				/* must be an anonymous map */
+				ret =
+				    snprintf(pinfo->strbuf + pinfo->strlen,
+					     LINE_SIZE - pinfo->strlen, ",nofile,%lx", pc);
+				pinfo->strlen += ret;
+				outflag = 1;
+			}
+			break;
+		}
+	} else {
+		/* kernel mode code */
+		if (back_trace_depth > 0) {
+			pinfo->depth = back_trace_depth + 1;
+			kernel_backtrace(regs, pinfo);
+		} else
+			get_kernel_cookie(pc, pinfo);
+		outflag = 1;
+	}
+
+	/* check task is resolvable */
+	if (outflag == 0)
+		return;
+
+	if (back_trace_depth == 0)
+		cookie(pinfo->strbuf);
+	else
+		cookie2(pinfo->strbuf);
+}
+
+
+static void met_cookie_start(void)
+{
+	int	cpu = raw_smp_processor_id();
+	per_cpu(cpu_status, cpu) = MET_CPU_ONLINE;
+	/* return; */
+}
+
+static void met_cookie_stop(void)
+{
+	/* return; */
+}
+
+
+static int met_cookie_process_argument(const char *arg, int len)
+{
+	unsigned int value;
+
+	if (met_parse_num(arg, &value, len) < 0) {
+		met_cookie.mode = 0;
+		return -EINVAL;
+	}
+
+	back_trace_depth = value;
+	met_cookie.mode = 1;
+
+	return 0;
+}
+
+static const char help[] =
+"  --cookie                              enable sampling task and PC\n"
+"  --cookie=N                            enable back trace (depth is N)\n";
+
+static int met_cookie_print_help(char *buf, int len)
+{
+	len = snprintf(buf, PAGE_SIZE, help);
+	return len;
+}
+
+
+static const char header[] =
+"# cookie: task_name,PC,cookie_name,offset\n"
+"met-info [000] 0.0: cookie_header: task_name,PC,cookie_name,offset\n";
+
+static const char header2_1[] = "# cookie2: task_name,PC,cookie,offset";
+static const char header2_2[] = "met-info [000] 0.0: cookie2_header: task_name,PC,cookie,offset";
+
+static int met_cookie_print_header(char *buf, int len)
+{
+	int i, ret;
+
+	if (back_trace_depth == 0) {
+		len = snprintf(buf, PAGE_SIZE, header);
+	} else {
+		len = snprintf(buf, PAGE_SIZE, header2_1);
+		for (i = 0; i < back_trace_depth; i++) {
+			ret = snprintf(buf + len, PAGE_SIZE, ",cookie%d,offset%d", i + 1, i + 1);
+			len += ret;
+		}
+		ret = snprintf(buf + len, PAGE_SIZE, "\n");
+		len += ret;
+
+		ret = snprintf(buf + len, PAGE_SIZE, header2_2);
+		len += ret;
+		for (i = 0; i < back_trace_depth; i++) {
+			ret = snprintf(buf + len, PAGE_SIZE, ",cookie%d,offset%d", i + 1, i + 1);
+			len += ret;
+		}
+		ret = snprintf(buf + len, PAGE_SIZE, "\n");
+		len += ret;
+	}
+
+	return len;
+}
+
+static void met_cookie_cpu_state_notify(long cpu, unsigned long action)
+{
+	per_cpu(cpu_status, cpu) = action;
+}
+
+struct metdevice met_cookie = {
+	.name = "cookie",
+	.type = MET_TYPE_PMU,
+	.cpu_related = 1,
+	.start = met_cookie_start,
+	.stop = met_cookie_stop,
+	.reset = reset_driver_stat,
+	.polling_interval = 1,
+	.timed_polling = met_cookie_polling,
+	.process_argument = met_cookie_process_argument,
+	.print_help = met_cookie_print_help,
+	.print_header = met_cookie_print_header,
+	.cpu_state_notify = met_cookie_cpu_state_notify,
+};
diff --git a/src/devtools/met-driver/4.14/common/core_plf_init.c b/src/devtools/met-driver/4.14/common/core_plf_init.c
new file mode 100644
index 0000000..bd778a1
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/core_plf_init.c
@@ -0,0 +1,421 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/kallsyms.h>
+#include "met_drv.h"
+#include "met_api_tbl.h"
+#include "interface.h"
+#include "core_plf_init.h"
+#include "plf_init.h"
+
+#undef	DEBUG
+
+#ifdef MET_GPU
+/*
+ *   GPU
+ */
+bool (*mtk_get_gpu_loading_symbol)(unsigned int *pLoading);
+bool (*mtk_get_gpu_block_symbol)(unsigned int *pBlock);
+bool (*mtk_get_gpu_idle_symbol)(unsigned int *pIdle);
+bool (*mtk_get_gpu_dvfs_from_symbol)(MTK_GPU_DVFS_TYPE *peType, unsigned long *pulFreq);
+bool (*mtk_get_gpu_sub_loading_symbol)(unsigned int *pLoading);
+bool (*mtk_get_3D_fences_count_symbol)(int *pi32Count);
+bool (*mtk_get_gpu_memory_usage_symbol)(unsigned int *pMemUsage);
+bool (*mtk_get_gpu_power_loading_symbol)(unsigned int *pLoading);
+bool (*mtk_get_custom_boost_gpu_freq_symbol)(unsigned long *pulFreq);
+bool (*mtk_get_custom_upbound_gpu_freq_symbol)(unsigned long *pulFreq);
+bool (*mtk_get_vsync_based_target_freq_symbol)(unsigned long *pulFreq);
+bool (*mtk_get_vsync_offset_event_status_symbol)(unsigned int *pui32EventStatus);
+bool (*mtk_get_vsync_offset_debug_status_symbol)(unsigned int *pui32EventStatus);
+bool (*mtk_enable_gpu_perf_monitor_symbol)(bool enable);
+bool (*mtk_get_gpu_pmu_init_symbol)(GPU_PMU *pmus, int pmu_size, int *ret_size);
+bool (*mtk_get_gpu_pmu_swapnreset_symbol)(GPU_PMU *pmus, int pmu_size);
+#if 1
+bool (*mtk_get_gpu_pmu_deinit_symbol)(void);
+bool (*mtk_get_gpu_pmu_swapnreset_stop_symbol)(void);
+#endif
+unsigned int (*mt_gpufreq_get_cur_freq_symbol)(void);
+unsigned int (*mt_gpufreq_get_thermal_limit_freq_symbol)(void);
+bool (*mtk_register_gpu_power_change_symbol)(const char *name, gpu_power_change_notify_fp callback);
+bool (*mtk_unregister_gpu_power_change_symbol)(const char *name);
+#endif /* MET_GPU */
+
+#ifdef MET_VCOREDVFS
+/*
+ *   VCORE DVFS
+ */
+#include <helio-dvfsrc.h>
+
+int (*vcorefs_get_num_opp_symbol)(void);
+int  (*vcorefs_get_opp_info_num_symbol)(void);
+char ** (*vcorefs_get_opp_info_name_symbol)(void);
+unsigned int * (*vcorefs_get_opp_info_symbol)(void);
+int  (*vcorefs_get_src_req_num_symbol)(void);
+char ** (*vcorefs_get_src_req_name_symbol)(void);
+unsigned int * (*vcorefs_get_src_req_symbol)(void);
+#endif /* MET_VCOREDVFS */
+
+#ifdef MET_SSPM_EMI
+void *(*mt_cen_emi_base_get_symbol)(void);
+unsigned int (*get_dram_data_rate_symbol)(void);
+unsigned int (*get_ddr_type_symbol)(void);
+unsigned int (*get_cur_ddr_ratio_symbol)(void);
+#endif /* MET_SSPM_EMI */
+
+#ifdef MET_AP_EMI
+/*
+ *   EMI Monitor
+ */
+void *(*mt_cen_emi_base_get_symbol)(void);
+void *(*mt_chn_emi_base_get_symbol)(int chn);
+unsigned int (*get_dram_data_rate_symbol)(void);
+unsigned int (*get_ddr_type_symbol)(void);
+void *(*mt_dramc_nao_chn_base_get_symbol)(int channel);
+void *(*mt_ddrphy_chn_base_get_symbol)(int channel);
+void *(*mt_dramc_chn_base_get_symbol)(int channel);
+unsigned int (*get_cur_ddr_ratio_symbol)(void);
+#endif
+
+#ifdef MET_PTPOD
+unsigned int (*mt_gpufreq_get_cur_volt_symbol)(void);
+unsigned int (*mt_cpufreq_get_cur_volt_symbol)(unsigned int cluster_id);
+#endif /* MET_PTPOD */
+
+#ifdef MET_SPM_TWAM
+/* ap side used */
+#ifdef SPMTWAM_AP
+void (*spm_twam_enable_monitor_symbol)(const struct twam_sig *twamsig, bool speed_mode);
+void (*spm_twam_register_handler_symbol)(twam_handler_t handler);
+void (*spm_twam_disable_monitor_symbol)(void);
+void (*spm_twam_set_idle_select_symbol)(unsigned int sel);
+void (*spm_twam_set_window_length_symbol)(unsigned int len);
+void (*spm_twam_set_mon_type_symbol)(struct twam_sig *mon);
+#endif
+
+/* sspm side used */
+#ifdef SPMTWAM_SSPM
+void (*spm_twam_enable_monitor_symbol)(bool en_monitor, bool debug_signal, twam_handler_t cb_handler);
+bool (*spm_twam_met_enable_symbol)(void);
+void (*spm_twam_config_channel_symbol)(struct twam_cfg *cfg, bool speed_mode, unsigned int window_len_hz);
+#endif
+#endif
+
+
+static int met_symbol_get(void)
+{
+#define _MET_SYMBOL_GET(_func_name_) \
+	do { \
+		_func_name_##_symbol = (void *)symbol_get(_func_name_); \
+		if (_func_name_##_symbol == NULL) { \
+			pr_debug("MET ext. symbol : %s is not found!\n", #_func_name_); \
+			PR_BOOTMSG_ONCE("MET ext. symbol : %s is not found!\n", #_func_name_); \
+		} \
+	} while (0)
+
+
+#ifdef MET_GPU
+	_MET_SYMBOL_GET(mtk_get_gpu_loading);
+	_MET_SYMBOL_GET(mtk_get_gpu_block);
+	_MET_SYMBOL_GET(mtk_get_gpu_idle);
+	_MET_SYMBOL_GET(mtk_get_gpu_dvfs_from);
+	_MET_SYMBOL_GET(mtk_get_gpu_sub_loading);
+	_MET_SYMBOL_GET(mtk_get_3D_fences_count);
+	_MET_SYMBOL_GET(mtk_get_gpu_memory_usage);
+	_MET_SYMBOL_GET(mtk_get_gpu_power_loading);
+	_MET_SYMBOL_GET(mtk_get_custom_boost_gpu_freq);
+	_MET_SYMBOL_GET(mtk_get_custom_upbound_gpu_freq);
+	_MET_SYMBOL_GET(mtk_get_vsync_based_target_freq);
+	_MET_SYMBOL_GET(mtk_get_vsync_offset_event_status);
+	_MET_SYMBOL_GET(mtk_get_vsync_offset_debug_status);
+	_MET_SYMBOL_GET(mtk_enable_gpu_perf_monitor);
+	_MET_SYMBOL_GET(mtk_get_gpu_pmu_init);
+	_MET_SYMBOL_GET(mtk_get_gpu_pmu_swapnreset);
+	_MET_SYMBOL_GET(mt_gpufreq_get_cur_freq);
+	_MET_SYMBOL_GET(mt_gpufreq_get_thermal_limit_freq);
+	_MET_SYMBOL_GET(mtk_register_gpu_power_change);
+	_MET_SYMBOL_GET(mtk_unregister_gpu_power_change);
+#if 1
+	_MET_SYMBOL_GET(mtk_get_gpu_pmu_swapnreset_stop);
+	_MET_SYMBOL_GET(mtk_get_gpu_pmu_deinit);
+#endif
+#endif /* MET_GPU */
+
+#ifdef MET_VCOREDVFS
+	_MET_SYMBOL_GET(vcorefs_get_num_opp);
+	_MET_SYMBOL_GET(vcorefs_get_opp_info_num);
+	_MET_SYMBOL_GET(vcorefs_get_opp_info_name);
+	_MET_SYMBOL_GET(vcorefs_get_opp_info);
+	_MET_SYMBOL_GET(vcorefs_get_src_req_num);
+	_MET_SYMBOL_GET(vcorefs_get_src_req_name);
+	_MET_SYMBOL_GET(vcorefs_get_src_req);
+#endif
+
+#ifdef MET_SSPM_EMI
+	_MET_SYMBOL_GET(mt_cen_emi_base_get);
+	_MET_SYMBOL_GET(get_dram_data_rate);
+	_MET_SYMBOL_GET(get_ddr_type);
+	_MET_SYMBOL_GET(get_cur_ddr_ratio);
+#endif
+
+#ifdef MET_AP_EMI
+	_MET_SYMBOL_GET(get_dram_data_rate);
+	_MET_SYMBOL_GET(get_ddr_type);
+	_MET_SYMBOL_GET(mt_cen_emi_base_get);
+	_MET_SYMBOL_GET(mt_chn_emi_base_get);
+	_MET_SYMBOL_GET(mt_dramc_nao_chn_base_get);
+	_MET_SYMBOL_GET(mt_ddrphy_chn_base_get);
+	_MET_SYMBOL_GET(mt_dramc_chn_base_get);
+/*	_MET_SYMBOL_GET(get_cur_ddr_ratio); */
+#endif
+
+#ifdef MET_PTPOD
+	_MET_SYMBOL_GET(mt_gpufreq_get_cur_volt);
+	_MET_SYMBOL_GET(mt_cpufreq_get_cur_volt);
+#endif
+
+#ifdef MET_SPM_TWAM
+		/* ap side used */
+#ifdef SPMTWAM_AP
+		_MET_SYMBOL_GET(spm_twam_enable_monitor);
+		_MET_SYMBOL_GET(spm_twam_register_handler);
+		_MET_SYMBOL_GET(spm_twam_disable_monitor);
+		_MET_SYMBOL_GET(spm_twam_set_idle_select);
+		_MET_SYMBOL_GET(spm_twam_set_window_length);
+		_MET_SYMBOL_GET(spm_twam_set_mon_type);
+#endif
+
+		/* sspm side used */
+#ifdef SPMTWAM_SSPM
+		_MET_SYMBOL_GET(spm_twam_enable_monitor);
+		_MET_SYMBOL_GET(spm_twam_met_enable);
+		_MET_SYMBOL_GET(spm_twam_config_channel);
+#endif
+#endif
+
+	return 0;
+}
+
+static int met_symbol_put(void)
+{
+#define _MET_SYMBOL_PUT(_func_name_) { \
+		if (_func_name_##_symbol) { \
+			symbol_put(_func_name_); \
+			_func_name_##_symbol = NULL; \
+		} \
+	}
+
+#ifdef MET_GPU
+	_MET_SYMBOL_PUT(mtk_get_gpu_loading);
+	_MET_SYMBOL_PUT(mtk_get_gpu_block);
+	_MET_SYMBOL_PUT(mtk_get_gpu_idle);
+	_MET_SYMBOL_PUT(mtk_get_gpu_dvfs_from);
+	_MET_SYMBOL_PUT(mtk_get_gpu_sub_loading);
+	_MET_SYMBOL_PUT(mtk_get_3D_fences_count);
+	_MET_SYMBOL_PUT(mtk_get_gpu_memory_usage);
+	_MET_SYMBOL_PUT(mtk_get_gpu_power_loading);
+	_MET_SYMBOL_PUT(mtk_get_custom_boost_gpu_freq);
+	_MET_SYMBOL_PUT(mtk_get_custom_upbound_gpu_freq);
+	_MET_SYMBOL_PUT(mtk_get_vsync_based_target_freq);
+	_MET_SYMBOL_PUT(mtk_get_vsync_offset_event_status);
+	_MET_SYMBOL_PUT(mtk_get_vsync_offset_debug_status);
+	_MET_SYMBOL_PUT(mtk_enable_gpu_perf_monitor);
+	_MET_SYMBOL_PUT(mtk_get_gpu_pmu_init);
+	_MET_SYMBOL_PUT(mtk_get_gpu_pmu_swapnreset);
+	_MET_SYMBOL_PUT(mt_gpufreq_get_cur_freq);
+	_MET_SYMBOL_PUT(mt_gpufreq_get_thermal_limit_freq);
+	_MET_SYMBOL_PUT(mtk_register_gpu_power_change);
+	_MET_SYMBOL_PUT(mtk_unregister_gpu_power_change);
+#if 1
+	_MET_SYMBOL_PUT(mtk_get_gpu_pmu_swapnreset_stop);
+	_MET_SYMBOL_PUT(mtk_get_gpu_pmu_deinit);
+#endif
+#endif /* MET_GPU */
+
+#ifdef MET_VCOREDVFS
+	_MET_SYMBOL_PUT(vcorefs_get_num_opp);
+	_MET_SYMBOL_PUT(vcorefs_get_opp_info_num);
+	_MET_SYMBOL_PUT(vcorefs_get_opp_info_name);
+	_MET_SYMBOL_PUT(vcorefs_get_opp_info);
+	_MET_SYMBOL_PUT(vcorefs_get_src_req_num);
+	_MET_SYMBOL_PUT(vcorefs_get_src_req_name);
+	_MET_SYMBOL_PUT(vcorefs_get_src_req);
+#endif
+
+#ifdef MET_SSPM_EMI
+	_MET_SYMBOL_PUT(mt_cen_emi_base_get);
+	_MET_SYMBOL_PUT(get_dram_data_rate);
+	_MET_SYMBOL_PUT(get_ddr_type);
+	_MET_SYMBOL_PUT(get_cur_ddr_ratio);
+#endif
+
+#ifdef MET_AP_EMI
+	_MET_SYMBOL_PUT(get_dram_data_rate);
+	_MET_SYMBOL_PUT(get_ddr_type);
+	_MET_SYMBOL_PUT(mt_cen_emi_base_get);
+	_MET_SYMBOL_PUT(mt_chn_emi_base_get);
+	_MET_SYMBOL_PUT(mt_dramc_nao_chn_base_get);
+	_MET_SYMBOL_PUT(mt_ddrphy_chn_base_get);
+	_MET_SYMBOL_PUT(mt_dramc_chn_base_get);
+/*	_MET_SYMBOL_PUT(get_cur_ddr_ratio); */
+#endif
+
+#ifdef MET_PTPOD
+	_MET_SYMBOL_PUT(mt_gpufreq_get_cur_volt);
+	_MET_SYMBOL_PUT(mt_cpufreq_get_cur_volt);
+#endif
+
+#ifdef MET_SPM_TWAM
+		/* ap side used */
+#ifdef SPMTWAM_AP
+		_MET_SYMBOL_PUT(spm_twam_enable_monitor);
+		_MET_SYMBOL_PUT(spm_twam_register_handler);
+		_MET_SYMBOL_PUT(spm_twam_disable_monitor);
+		_MET_SYMBOL_PUT(spm_twam_set_idle_select);
+		_MET_SYMBOL_PUT(spm_twam_set_window_length);
+		_MET_SYMBOL_PUT(spm_twam_set_mon_type);
+#endif
+
+		/* sspm side used */
+#ifdef SPMTWAM_SSPM
+		_MET_SYMBOL_PUT(spm_twam_enable_monitor);
+		_MET_SYMBOL_PUT(spm_twam_met_enable);
+		_MET_SYMBOL_PUT(spm_twam_config_channel);
+#endif
+#endif
+
+	return 0;
+}
+
+int core_plf_init(void)
+{
+	/*initial met external symbol*/
+	met_symbol_get();
+
+#ifdef MET_GPU
+	met_register(&met_gpu);
+	met_register(&met_gpudvfs);
+	met_register(&met_gpumem);
+	met_register(&met_gpupwr);
+	met_register(&met_gpu_pmu);
+#ifdef MET_GPU_STALL_MONITOR
+	met_register(&met_gpu_stall);
+#endif
+#endif
+
+#ifdef MET_VCOREDVFS
+	met_register(&met_vcoredvfs);
+#endif
+
+#ifdef MET_SSPM_EMI
+	met_register(&met_sspm_emi);
+#endif
+
+#ifdef MET_AP_EMI
+	met_register(&met_emi);
+#endif
+
+#ifdef MET_SMI
+	met_register(&met_sspm_smi);
+#endif
+
+#ifdef MET_PTPOD
+	met_register(&met_ptpod);
+#endif
+
+#ifdef MET_WALLTIME
+	met_register(&met_wall_time);
+#endif
+
+#ifdef MTK_TINYSYS_SSPM_SUPPORT
+	met_register(&met_sspm_common);
+#endif
+
+#if defined(CONFIG_MTK_TINYSYS_SSPM_SUPPORT) && defined(ONDIEMET_SUPPORT)
+#ifdef MET_SSPM_WALLTIME
+	met_register(&met_sspm_walltime);
+#endif
+#endif /* CONFIG_MTK_TINYSYS_SSPM_SUPPORT && ONDIEMET_SUPPORT */
+
+#ifdef MET_CPUDSU
+	met_register(&met_cpudsu);
+#endif
+
+#ifdef MET_SPM_TWAM
+	met_register(&met_spmtwam);
+#endif
+
+	return 0;
+}
+
+void core_plf_exit(void)
+{
+	/*release met external symbol*/
+	met_symbol_put();
+
+#ifdef MET_GPU
+	met_deregister(&met_gpu);
+	met_deregister(&met_gpudvfs);
+	met_deregister(&met_gpumem);
+	met_deregister(&met_gpupwr);
+	met_deregister(&met_gpu_pmu);
+#ifdef MET_GPU_STALL_MONITOR
+	met_deregister(&met_gpu_stall);
+#endif
+#endif
+
+#ifdef MET_VCOREDVFS
+	met_deregister(&met_vcoredvfs);
+#endif
+
+#ifdef MET_SSPM_EMI
+	met_deregister(&met_sspm_emi);
+#endif
+
+#ifdef MET_AP_EMI
+	met_deregister(&met_emi);
+#endif
+
+#ifdef MET_SMI
+	met_deregister(&met_sspm_smi);
+#endif
+
+#ifdef MET_PTPOD
+	met_deregister(&met_ptpod);
+#endif
+
+#ifdef MET_WALLTIME
+	met_deregister(&met_wall_time);
+#endif
+
+#ifdef MTK_TINYSYS_SSPM_SUPPORT
+	met_deregister(&met_sspm_common);
+#endif
+
+#if defined(CONFIG_MTK_TINYSYS_SSPM_SUPPORT) && defined(ONDIEMET_SUPPORT)
+#ifdef MET_SSPM_WALLTIME
+	met_deregister(&met_sspm_walltime);
+#endif
+#endif /* CONFIG_MTK_TINYSYS_SSPM_SUPPORT && ONDIEMET_SUPPORT */
+
+#ifdef MET_CPUDSU
+	met_deregister(&met_cpudsu);
+#endif
+
+#ifdef MET_SPM_TWAM
+	met_deregister(&met_spmtwam);
+#endif
+
+}
diff --git a/src/devtools/met-driver/4.14/common/core_plf_init.h b/src/devtools/met-driver/4.14/common/core_plf_init.h
new file mode 100644
index 0000000..ae53627
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/core_plf_init.h
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __CORE_PLF_INIT_H__
+#define __CORE_PLF_INIT_H__
+
+extern struct miscdevice met_device;
+
+/*
+ *   MET External Symbol
+ */
+
+#ifdef MET_GPU
+/*
+ *   GPU
+ */
+#include <mtk_gpu_utility.h>
+#include <mtk_gpufreq.h>
+#include "met_gpu_monitor.h"
+
+extern bool mtk_get_gpu_loading(unsigned int *pLoading);
+extern bool mtk_get_gpu_block(unsigned int *pBlock);
+extern bool mtk_get_gpu_idle(unsigned int *pIdle);
+extern bool mtk_get_gpu_dvfs_from(MTK_GPU_DVFS_TYPE *peType, unsigned long *pulFreq);
+extern bool mtk_get_gpu_sub_loading(unsigned int *pLoading);
+extern bool mtk_get_3D_fences_count(int *pi32Count);
+extern bool mtk_get_gpu_memory_usage(unsigned int *pMemUsage);
+extern bool mtk_get_gpu_power_loading(unsigned int *pLoading);
+extern bool mtk_get_custom_boost_gpu_freq(unsigned int *pui32FreqLevel);
+extern bool mtk_get_custom_upbound_gpu_freq(unsigned int *pui32FreqLevel);
+extern bool mtk_get_vsync_based_target_freq(unsigned long *pulFreq);
+extern bool mtk_get_vsync_offset_event_status(unsigned int *pui32EventStatus);
+extern bool mtk_get_vsync_offset_debug_status(unsigned int *pui32EventStatus);
+extern bool mtk_enable_gpu_perf_monitor(bool enable);
+extern bool mtk_get_gpu_pmu_init(GPU_PMU *pmus, int pmu_size, int *ret_size);
+extern bool mtk_get_gpu_pmu_swapnreset(GPU_PMU *pmus, int pmu_size);
+
+
+extern bool (*mtk_get_gpu_loading_symbol)(unsigned int *pLoading);
+extern bool (*mtk_get_gpu_block_symbol)(unsigned int *pBlock);
+extern bool (*mtk_get_gpu_idle_symbol)(unsigned int *pIdle);
+extern bool (*mtk_get_gpu_dvfs_from_symbol)(MTK_GPU_DVFS_TYPE *peType, unsigned long *pulFreq);
+extern bool (*mtk_get_gpu_sub_loading_symbol)(unsigned int *pLoading);
+extern bool (*mtk_get_3D_fences_count_symbol)(int *pi32Count);
+extern bool (*mtk_get_gpu_memory_usage_symbol)(unsigned int *pMemUsage);
+extern bool (*mtk_get_gpu_power_loading_symbol)(unsigned int *pLoading);
+extern bool (*mtk_get_custom_boost_gpu_freq_symbol)(unsigned long *pulFreq);
+extern bool (*mtk_get_custom_upbound_gpu_freq_symbol)(unsigned long *pulFreq);
+extern bool (*mtk_get_vsync_based_target_freq_symbol)(unsigned long *pulFreq);
+extern bool (*mtk_get_vsync_offset_event_status_symbol)(unsigned int *pui32EventStatus);
+extern bool (*mtk_get_vsync_offset_debug_status_symbol)(unsigned int *pui32EventStatus);
+extern bool (*mtk_enable_gpu_perf_monitor_symbol)(bool enable);
+extern bool (*mtk_get_gpu_pmu_init_symbol)(GPU_PMU *pmus, int pmu_size, int *ret_size);
+extern bool (*mtk_get_gpu_pmu_swapnreset_symbol)(GPU_PMU *pmus, int pmu_size);
+extern bool (*mtk_get_gpu_pmu_deinit_symbol)(void);
+extern bool (*mtk_get_gpu_pmu_swapnreset_stop_symbol)(void);
+
+extern bool mtk_register_gpu_power_change(const char *name, gpu_power_change_notify_fp callback);
+extern bool mtk_unregister_gpu_power_change(const char *name);
+extern bool (*mtk_register_gpu_power_change_symbol)(const char *name,
+					gpu_power_change_notify_fp callback);
+extern bool (*mtk_unregister_gpu_power_change_symbol)(const char *name);
+
+
+extern unsigned int (*mt_gpufreq_get_cur_freq_symbol)(void);
+extern unsigned int (*mt_gpufreq_get_thermal_limit_freq_symbol)(void);
+
+extern struct metdevice met_gpu;
+extern struct metdevice met_gpudvfs;
+extern struct metdevice met_gpumem;
+extern struct metdevice met_gpupwr;
+extern struct metdevice met_gpu_pmu;
+#ifdef MET_GPU_STALL_MONITOR
+extern struct metdevice met_gpu_stall;
+#endif
+#endif /* MET_GPU */
+
+
+#ifdef MET_VCOREDVFS
+/*
+ *   VCORE DVFS
+ */
+extern int vcorefs_get_num_opp(void);
+extern int  vcorefs_get_opp_info_num(void);
+extern char ** vcorefs_get_opp_info_name(void);
+extern unsigned int * vcorefs_get_opp_info(void);
+extern int  vcorefs_get_src_req_num(void);
+extern char ** vcorefs_get_src_req_name(void);
+extern unsigned int * vcorefs_get_src_req(void);
+
+extern int (*vcorefs_get_num_opp_symbol)(void);
+extern int  (*vcorefs_get_opp_info_num_symbol)(void);
+extern char ** (*vcorefs_get_opp_info_name_symbol)(void);
+extern unsigned int * (*vcorefs_get_opp_info_symbol)(void);
+extern int  (*vcorefs_get_src_req_num_symbol)(void);
+extern char ** (*vcorefs_get_src_req_name_symbol)(void);
+extern unsigned int * (*vcorefs_get_src_req_symbol)(void);
+
+extern struct metdevice met_vcoredvfs;
+#endif /* MET_VCOREDVFS */
+
+
+#ifdef MET_SSPM_EMI
+extern void *mt_cen_emi_base_get(void);
+extern unsigned int get_dram_data_rate(void);      /* in Mhz */
+extern int get_ddr_type(void);
+extern void *get_cur_ddr_ratio(void);
+
+extern void *(*mt_cen_emi_base_get_symbol)(void);
+extern unsigned int (*get_dram_data_rate_symbol)(void); /* in Mhz */
+extern unsigned int (*get_ddr_type_symbol)(void);
+extern unsigned int (*get_cur_ddr_ratio_symbol)(void);
+
+
+
+extern struct metdevice met_sspm_emi;
+#endif /* MET_SSPM_EMI */
+
+#ifdef MET_SMI
+extern struct metdevice met_sspm_smi;
+#endif
+
+#ifdef MET_PTPOD
+#include <mtk_gpufreq.h>
+#include <mach/mtk_cpufreq_api.h>
+#include <mtk_cpufreq_config.h>
+
+extern unsigned int (*mt_gpufreq_get_cur_volt_symbol)(void);
+extern unsigned int (*mt_cpufreq_get_cur_volt_symbol)(unsigned int cluster_id);
+
+extern struct metdevice met_ptpod;
+#endif /* MET_PTPOD */
+
+#ifdef MET_WALLTIME
+extern struct metdevice met_wall_time;
+#endif
+
+#ifdef MTK_TINYSYS_SSPM_SUPPORT
+extern struct metdevice met_sspm_common;
+#endif /* MTK_TINYSYS_SSPM_SUPPORT */
+
+#if defined(CONFIG_MTK_TINYSYS_SSPM_SUPPORT) && defined(ONDIEMET_SUPPORT)
+#ifdef MET_SSPM_WALLTIME
+extern struct metdevice met_sspm_walltime;
+#endif
+#endif /* CONFIG_MTK_TINYSYS_SSPM_SUPPORT */
+
+#ifdef MET_CPUDSU
+extern struct metdevice met_cpudsu;
+#endif
+
+#ifdef MET_SPM_TWAM
+#include "mtk_spm.h"
+
+/* ap side used */
+#ifdef SPMTWAM_AP
+extern void spm_twam_enable_monitor(const struct twam_sig *twamsig, bool speed_mode);
+extern void spm_twam_register_handler(twam_handler_t handler);
+extern void spm_twam_disable_monitor(void);
+extern void spm_twam_set_idle_select(unsigned int sel);
+extern void spm_twam_set_window_length(unsigned int len);
+extern void spm_twam_set_mon_type(struct twam_sig *mon);
+
+extern void (*spm_twam_enable_monitor_symbol)(const struct twam_sig *twamsig, bool speed_mode);
+extern void (*spm_twam_register_handler_symbol)(twam_handler_t handler);
+extern void (*spm_twam_disable_monitor_symbol)(void);
+extern void (*spm_twam_set_idle_select_symbol)(unsigned int sel);
+extern void (*spm_twam_set_window_length_symbol)(unsigned int len);
+extern void (*spm_twam_set_mon_type_symbol)(struct twam_sig *mon);
+#endif
+
+/* sspm side used */
+#ifdef SPMTWAM_SSPM
+extern void spm_twam_enable_monitor(bool en_monitor, bool debug_signal, twam_handler_t cb_handler);
+extern bool spm_twam_met_enable(void);
+extern void spm_twam_config_channel(struct twam_cfg *cfg, bool speed_mode, unsigned int window_len_hz);
+
+extern void (*spm_twam_enable_monitor_symbol)(bool en_monitor, bool debug_signal, twam_handler_t cb_handler);
+extern bool (*spm_twam_met_enable_symbol)(void);
+extern void (*spm_twam_config_channel_symbol)(struct twam_cfg *cfg, bool speed_mode, unsigned int window_len_hz);
+#endif
+
+extern struct metdevice met_spmtwam;
+#endif
+
+
+#endif /*__CORE_PLF_INIT_H__*/
diff --git a/src/devtools/met-driver/4.14/common/core_plf_trace.c b/src/devtools/met-driver/4.14/common/core_plf_trace.c
new file mode 100644
index 0000000..0dc7090
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/core_plf_trace.c
@@ -0,0 +1,322 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+#include "met_drv.h"
+#include "interface.h"
+#include "trace.h"
+
+char *ms_formatH(char *__restrict__ buf, unsigned char cnt, unsigned int *__restrict__ value)
+{
+	char *s = buf;
+	int len;
+
+	if (cnt == 0) {
+		buf[0] = '\0';
+		return buf;
+	}
+
+	switch (cnt % 4) {
+	case 1:
+		len = sprintf(s, "%x", value[0]);
+		s += len;
+		value += 1;
+		cnt -= 1;
+		break;
+	case 2:
+		len = sprintf(s, "%x,%x", value[0], value[1]);
+		s += len;
+		value += 2;
+		cnt -= 2;
+		break;
+	case 3:
+		len = sprintf(s, "%x,%x,%x", value[0], value[1], value[2]);
+		s += len;
+		value += 3;
+		cnt -= 3;
+		break;
+	case 0:
+		len = sprintf(s, "%x,%x,%x,%x", value[0], value[1], value[2], value[3]);
+		s += len;
+		value += 4;
+		cnt -= 4;
+		break;
+	}
+
+	while (cnt) {
+		len = sprintf(s, ",%x,%x,%x,%x", value[0], value[1], value[2], value[3]);
+		s += len;
+		value += 4;
+		cnt -= 4;
+	}
+
+	s[0] = '\0';
+
+	return s;
+}
+EXPORT_SYMBOL(ms_formatH);
+
+char *ms_formatD(char *__restrict__ buf, unsigned char cnt, unsigned int *__restrict__ value)
+{
+	char *s = buf;
+	int len;
+
+	if (cnt == 0) {
+		buf[0] = '\0';
+		return buf;
+	}
+
+	switch (cnt % 4) {
+	case 1:
+		len = sprintf(s, "%u", value[0]);
+		s += len;
+		value += 1;
+		cnt -= 1;
+		break;
+	case 2:
+		len = sprintf(s, "%u,%u", value[0], value[1]);
+		s += len;
+		value += 2;
+		cnt -= 2;
+		break;
+	case 3:
+		len = sprintf(s, "%u,%u,%u", value[0], value[1], value[2]);
+		s += len;
+		value += 3;
+		cnt -= 3;
+		break;
+	case 0:
+		len = sprintf(s, "%u,%u,%u,%u", value[0], value[1], value[2], value[3]);
+		s += len;
+		value += 4;
+		cnt -= 4;
+		break;
+	}
+
+	while (cnt) {
+		len = sprintf(s, ",%u,%u,%u,%u", value[0], value[1], value[2], value[3]);
+		s += len;
+		value += 4;
+		cnt -= 4;
+	}
+
+	s[0] = '\0';
+
+	return s;
+}
+EXPORT_SYMBOL(ms_formatD);
+
+char *ms_formatH_ulong(char *__restrict__ buf, unsigned char cnt, unsigned long *__restrict__ value)
+{
+	char *s = buf;
+	int len;
+
+	if (cnt == 0) {
+		buf[0] = '\0';
+		return buf;
+	}
+
+	switch (cnt % 4) {
+	case 1:
+		len = sprintf(s, "%lx", value[0]);
+		s += len;
+		value += 1;
+		cnt -= 1;
+		break;
+	case 2:
+		len = sprintf(s, "%lx,%lx", value[0], value[1]);
+		s += len;
+		value += 2;
+		cnt -= 2;
+		break;
+	case 3:
+		len = sprintf(s, "%lx,%lx,%lx", value[0], value[1], value[2]);
+		s += len;
+		value += 3;
+		cnt -= 3;
+		break;
+	case 0:
+		len = sprintf(s, "%lx,%lx,%lx,%lx", value[0], value[1], value[2], value[3]);
+		s += len;
+		value += 4;
+		cnt -= 4;
+		break;
+	}
+
+	while (cnt) {
+		len = sprintf(s, ",%lx,%lx,%lx,%lx", value[0], value[1], value[2], value[3]);
+		s += len;
+		value += 4;
+		cnt -= 4;
+	}
+
+	s[0] = '\0';
+
+	return buf;
+}
+EXPORT_SYMBOL(ms_formatH_ulong);
+
+char *ms_formatD_ulong(char *__restrict__ buf, unsigned char cnt, unsigned long *__restrict__ value)
+{
+	char *s = buf;
+	int len;
+
+	if (cnt == 0) {
+		buf[0] = '\0';
+		return buf;
+	}
+
+	switch (cnt % 4) {
+	case 1:
+		len = sprintf(s, "%lu", value[0]);
+		s += len;
+		value += 1;
+		cnt -= 1;
+		break;
+	case 2:
+		len = sprintf(s, "%lu,%lu", value[0], value[1]);
+		s += len;
+		value += 2;
+		cnt -= 2;
+		break;
+	case 3:
+		len = sprintf(s, "%lu,%lu,%lu", value[0], value[1], value[2]);
+		s += len;
+		value += 3;
+		cnt -= 3;
+		break;
+	case 0:
+		len = sprintf(s, "%lu,%lu,%lu,%lu", value[0], value[1], value[2], value[3]);
+		s += len;
+		value += 4;
+		cnt -= 4;
+		break;
+	}
+
+	while (cnt) {
+		len = sprintf(s, ",%lu,%lu,%lu,%lu", value[0], value[1], value[2], value[3]);
+		s += len;
+		value += 4;
+		cnt -= 4;
+	}
+
+	s[0] = '\0';
+
+	return buf;
+}
+EXPORT_SYMBOL(ms_formatD_ulong);
+
+char *ms_formatH_EOL(char *__restrict__ buf, unsigned char cnt, unsigned int *__restrict__ value)
+{
+	char *s = buf;
+	int len;
+
+	if (cnt == 0) {
+		buf[0] = '\0';
+		return buf;
+	}
+
+	switch (cnt % 4) {
+	case 1:
+		len = sprintf(s, "%x", value[0]);
+		s += len;
+		value += 1;
+		cnt -= 1;
+		break;
+	case 2:
+		len = sprintf(s, "%x,%x", value[0], value[1]);
+		s += len;
+		value += 2;
+		cnt -= 2;
+		break;
+	case 3:
+		len = sprintf(s, "%x,%x,%x", value[0], value[1], value[2]);
+		s += len;
+		value += 3;
+		cnt -= 3;
+		break;
+	case 0:
+		len = sprintf(s, "%x,%x,%x,%x", value[0], value[1], value[2], value[3]);
+		s += len;
+		value += 4;
+		cnt -= 4;
+		break;
+	}
+
+	while (cnt) {
+		len = sprintf(s, ",%x,%x,%x,%x", value[0], value[1], value[2], value[3]);
+		s += len;
+		value += 4;
+		cnt -= 4;
+	}
+
+	s[0] = '\n';
+	s[1] = '\0';
+
+	return s + 1;
+}
+EXPORT_SYMBOL(ms_formatH_EOL);
+
+char *ms_formatD_EOL(char *__restrict__ buf, unsigned char cnt, unsigned int *__restrict__ value)
+{
+	char *s = buf;
+	int len;
+
+	if (cnt == 0) {
+		buf[0] = '\0';
+		return buf;
+	}
+
+	switch (cnt % 4) {
+	case 1:
+		len = sprintf(s, "%u", value[0]);
+		s += len;
+		value += 1;
+		cnt -= 1;
+		break;
+	case 2:
+		len = sprintf(s, "%u,%u", value[0], value[1]);
+		s += len;
+		value += 2;
+		cnt -= 2;
+		break;
+	case 3:
+		len = sprintf(s, "%u,%u,%u", value[0], value[1], value[2]);
+		s += len;
+		value += 3;
+		cnt -= 3;
+		break;
+	case 0:
+		len = sprintf(s, "%u,%u,%u,%u", value[0], value[1], value[2], value[3]);
+		s += len;
+		value += 4;
+		cnt -= 4;
+		break;
+	}
+
+	while (cnt) {
+		len = sprintf(s, ",%u,%u,%u,%u", value[0], value[1], value[2], value[3]);
+		s += len;
+		value += 4;
+		cnt -= 4;
+	}
+
+	s[0] = '\n';
+	s[1] = '\0';
+
+	return s + 1;
+}
+EXPORT_SYMBOL(ms_formatD_EOL);
+
diff --git a/src/devtools/met-driver/4.14/common/core_plf_trace.h b/src/devtools/met-driver/4.14/common/core_plf_trace.h
new file mode 100644
index 0000000..5c89efd
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/core_plf_trace.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _CORE_PLF_TRACE_H_
+#define _CORE_PLF_TRACE_H_
+
+#define	HVALUE_SIZE	9	/* 8 chars (max value ffffffff) + 1 char (',' or NULL) */
+#define	DVALUE_SIZE	12	/* 10 chars (max value 4,294,967,295) + 1 char (',' or NULL) */
+
+char *ms_formatH(char *__restrict__ buf, unsigned char cnt, unsigned int *__restrict__ value);
+char *core_ms_formatD(char *__restrict__ buf, unsigned char cnt, unsigned int *__restrict__ value);
+char *ms_formatH_ulong(char *__restrict__ buf, unsigned char cnt,
+		       unsigned long *__restrict__ value);
+char *ms_formatD_ulong(char *__restrict__ buf, unsigned char cnt,
+		       unsigned long *__restrict__ value);
+char *ms_formatH_EOL(char *__restrict__ buf, unsigned char cnt, unsigned int *__restrict__ value);
+char *ms_formatD_EOL(char *__restrict__ buf, unsigned char cnt, unsigned int *__restrict__ value);
+
+#endif	/* _CORE_PLF_TRACE_H_ */
diff --git a/src/devtools/met-driver/4.14/common/cpu_dsu.c b/src/devtools/met-driver/4.14/common/cpu_dsu.c
new file mode 100644
index 0000000..092d49b
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/cpu_dsu.c
@@ -0,0 +1,362 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/perf_event.h>
+#include "met_drv.h"
+#include "met_kernel_symbol.h"
+#include "interface.h"
+#include "trace.h"
+#include "cpu_dsu.h"
+#include "core_plf_init.h"
+
+
+struct cpu_dsu_hw *cpu_dsu;
+static int counter_cnt;
+static struct kobject *kobj_dsu;
+static int nr_arg;
+static unsigned long long perfCurr[MXNR_DSU_EVENTS];
+static unsigned long long perfPrev[MXNR_DSU_EVENTS];
+static int perfCntFirst[MXNR_DSU_EVENTS];
+static struct perf_event * pevent[MXNR_DSU_EVENTS];
+static struct perf_event_attr pevent_attr[MXNR_DSU_EVENTS];
+static unsigned int perf_device_type = 7;
+static ssize_t perf_type_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%d\n", perf_device_type);
+}
+
+static ssize_t perf_type_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t n)
+{
+	if (kstrtouint(buf, 0, &perf_device_type) != 0)
+		return -EINVAL;
+
+	return n;
+}
+static struct kobj_attribute perf_type_attr = __ATTR(perf_type, 0664, perf_type_show, perf_type_store);
+
+noinline void mp_dsu(unsigned char cnt, unsigned int *value)
+{
+	MET_GENERAL_PRINT(MET_TRACE, cnt, value);
+}
+
+static void dummy_handler(struct perf_event *event, struct perf_sample_data *data,
+			  struct pt_regs *regs)
+{
+	/*
+	 * Required as perf_event_create_kernel_counter() requires an overflow handler,
+	 * even though all we do is poll.
+	 */
+}
+
+static void perf_cpudsu_polling(unsigned long long stamp, int cpu)
+{
+	int	event_count = cpu_dsu->event_count;
+	struct met_dsu	*pmu = cpu_dsu->pmu;
+	int	i, count;
+	unsigned long long	delta;
+	struct perf_event	*ev;
+	unsigned int pmu_value[MXNR_DSU_EVENTS];
+	u64 value;
+
+	count = 0;
+	for (i = 0; i < event_count; i++) {
+		if (pmu[i].mode == 0)
+			continue;
+
+		ev = pevent[i];
+		if ((ev != NULL) && (ev->state == PERF_EVENT_STATE_ACTIVE)) {
+			met_perf_event_read_local_symbol(ev, &value);
+			perfCurr[i] = value;
+			delta = (perfCurr[i] - perfPrev[i]);
+			perfPrev[i] = perfCurr[i];
+			if (perfCntFirst[i] == 1) {
+				/* we shall omit delta counter when we get first counter */
+				perfCntFirst[i] = 0;
+				continue;
+			}
+			pmu_value[count] = (unsigned int)delta;
+			count++;
+		}
+	}
+
+	if (count == counter_cnt)
+		mp_dsu(count, pmu_value);
+}
+
+static int perf_thread_set_perf_events(unsigned int cpu)
+{
+	int			i, size;
+	struct perf_event	*ev;
+	struct perf_event_attr	*ev_attr;
+	int event_count = cpu_dsu->event_count;
+	struct met_dsu *pmu = cpu_dsu->pmu;
+
+	size = sizeof(struct perf_event_attr);
+
+	for (i = 0; i < event_count; i++) {
+		pevent[i] = NULL;
+		if (!pmu[i].mode)
+			continue;	/* Skip disabled counters */
+		perfPrev[i] = 0;
+		perfCurr[i] = 0;
+		ev_attr = pevent_attr+i;
+		memset(ev_attr, 0, size);
+		ev_attr->config = pmu[i].event;
+		ev_attr->type = perf_device_type;
+		ev_attr->size = size;
+		ev_attr->sample_period = 0;
+		ev_attr->pinned = 1;
+
+		ev = perf_event_create_kernel_counter(ev_attr, cpu, NULL, dummy_handler, NULL);
+		if (IS_ERR(ev))
+			continue;
+		if (ev->state != PERF_EVENT_STATE_ACTIVE) {
+			perf_event_release_kernel(ev);
+			continue;
+		}
+		pevent[i] = ev;
+		if (ev != NULL)
+			perf_event_enable(ev);
+	}	/* for all PMU counter */
+	return 0;
+}
+
+void met_perf_cpudsu_down(void)
+{
+	int i;
+	struct perf_event *ev;
+	int event_count;
+	struct met_dsu *pmu;
+
+	if (met_cpudsu.mode == 0)
+		return;
+	event_count = cpu_dsu->event_count;
+	pmu = cpu_dsu->pmu;
+	for (i = 0; i < event_count; i++) {
+		if (!pmu[i].mode)
+			continue;
+		ev = pevent[i];
+		if ((ev != NULL) && (ev->state == PERF_EVENT_STATE_ACTIVE)) {
+			perf_event_disable(ev);
+			perf_event_release_kernel(ev);
+		}
+		pevent[i] = NULL;
+	}
+	//perf_delayed_work_setup = NULL;
+}
+
+inline static void met_perf_cpudsu_start(int cpu)
+{
+	if (met_cpudsu.mode == 0)
+		return;
+	if (cpu != 0)
+		return;
+	perf_thread_set_perf_events(cpu);
+}
+
+static int cpudsu_create_subfs(struct kobject *parent)
+{
+	int ret = 0;
+	cpu_dsu = cpu_dsu_hw_init();
+	if (cpu_dsu == NULL) {
+		PR_BOOTMSG("Failed to init CPU PMU HW!!\n");
+		return -ENODEV;
+	}
+	kobj_dsu = parent;
+	ret = sysfs_create_file(kobj_dsu, &perf_type_attr.attr);
+	if (ret != 0) {
+		PR_BOOTMSG("Failed to create perf_type in sysfs\n");
+		goto out;
+	}
+ out:
+	return ret;
+}
+
+static void cpudsu_delete_subfs(void)
+{
+}
+
+void met_perf_cpudsu_polling(unsigned long long stamp, int cpu)
+{
+	perf_cpudsu_polling(stamp, cpu);
+}
+
+static void cpudsu_start(void)
+{
+	int	cpu = raw_smp_processor_id();
+	for_each_online_cpu(cpu)
+		met_perf_cpudsu_start(cpu);
+}
+
+static void cpudsu_stop(void)
+{
+	met_perf_cpudsu_down();
+}
+
+static const char header[] =
+	"met-info [000] 0.0: met_dsu_pmu_header: DSU";
+
+static const char help[] =
+	"  --dsu=EVENT         select DSU-PMU events.\n"
+	"                      you can enable at most \"%d general purpose events\"\n";
+
+static int cpudsu_print_help(char *buf, int len)
+{
+	return snprintf(buf, PAGE_SIZE, help, cpu_dsu->event_count);
+}
+
+static int reset_driver_stat(void)
+{
+	int		i;
+	int		event_count;
+	struct met_dsu	*pmu;
+
+	met_cpudsu.mode = 0;
+	event_count = cpu_dsu->event_count;
+	pmu = cpu_dsu->pmu;
+	counter_cnt = 0;
+	nr_arg = 0;
+	for (i = 0; i < event_count; i++) {
+		pmu[i].mode = MODE_DISABLED;
+		pmu[i].event = 0;
+		pmu[i].freq = 0;
+	}
+	return 0;
+}
+
+static int cpudsu_print_header(char *buf, int len)
+{
+	int		first;
+	int		i, ret;
+	int		event_count;
+	struct met_dsu	*pmu;
+	ret = 0;
+
+	ret += snprintf(buf + ret, PAGE_SIZE - ret, "# mp_dsu: pmu_value1, ...\n");
+	event_count = cpu_dsu->event_count;
+	pmu = cpu_dsu->pmu;
+	first = 1;
+	for (i = 0; i < event_count; i++) {
+		if (pmu[i].mode == 0)
+			continue;
+		if (first) {
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, header);
+			first = 0;
+		}
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, ",0x%x", pmu[i].event);
+		pmu[i].mode = 0;
+	}
+	ret += snprintf(buf + ret, PAGE_SIZE - ret, "\n");
+	reset_driver_stat();
+	return ret;
+}
+
+static int met_parse_num_list(char *arg, int len, int *list, int list_cnt)
+{
+	int	nr_num = 0;
+	char	*num;
+	int	num_len;
+
+	/* search ',' as the splitter */
+	while (len) {
+		num = arg;
+		num_len = 0;
+		if (list_cnt <= 0)
+			return -1;
+		while (len) {
+			len--;
+			if (*arg == ',') {
+				*(arg++) = '\0';
+				break;
+			}
+			arg++;
+			num_len++;
+		}
+		if (met_parse_num(num, list, num_len) < 0)
+			return -1;
+		list++;
+		list_cnt--;
+		nr_num++;
+	}
+	return nr_num;
+}
+
+static int cpudsu_process_argument(const char *arg, int len)
+{
+	int		nr_events, event_list[MXNR_DSU_EVENTS];
+	int		i;
+	int		nr_counters;
+	struct met_dsu	*pmu;
+	int		arg_nr;
+	int		counters;
+	int		event_no;
+
+	/* get event_list */
+	if ((nr_events = met_parse_num_list((char*)arg, len, event_list, ARRAY_SIZE(event_list))) <= 0)
+		goto arg_out;
+
+	/* for each cpu in cpu_list, add all the events in event_list */
+	nr_counters = cpu_dsu->event_count;
+	pmu = cpu_dsu->pmu;
+	arg_nr = nr_arg;
+
+	/*
+	 * setup nr_counters for linux native perf mode.
+	 * because the selected events are stored in pmu,
+	 * so nr_counters can't large then event count in pmu.
+	 */
+	counters = perf_num_counters();
+	if (counters < nr_counters)
+		nr_counters = counters;
+
+	if (nr_counters == 0)
+		goto arg_out;
+
+	for (i = 0; i < nr_events; i++) {
+		event_no = event_list[i];
+		/*
+		 * check if event is duplicate,
+		 * but may not include 0xff when met_cpu_dsu_method == 0.
+		 */
+		if (cpu_dsu->check_event(pmu, arg_nr, event_no) < 0)
+			goto arg_out;
+		if (arg_nr >= nr_counters)
+			goto arg_out;
+		pmu[arg_nr].mode = MODE_POLLING;
+		pmu[arg_nr].event = event_no;
+		pmu[arg_nr].freq = 0;
+		arg_nr++;
+		counter_cnt++;
+	}
+	nr_arg = arg_nr;
+	met_cpudsu.mode = 1;
+	return 0;
+
+arg_out:
+	reset_driver_stat();
+	return -EINVAL;
+}
+
+struct metdevice met_cpudsu = {
+	.name = "dsu",
+	.type = MET_TYPE_PMU,
+	.cpu_related = 0,
+	.create_subfs = cpudsu_create_subfs,
+	.delete_subfs = cpudsu_delete_subfs,
+	.start = cpudsu_start,
+	.stop = cpudsu_stop,
+	.polling_interval = 1,
+	.timed_polling = met_perf_cpudsu_polling,
+	.print_help = cpudsu_print_help,
+	.print_header = cpudsu_print_header,
+	.process_argument = cpudsu_process_argument
+};
diff --git a/src/devtools/met-driver/4.14/common/cpu_dsu.h b/src/devtools/met-driver/4.14/common/cpu_dsu.h
new file mode 100644
index 0000000..084c81a
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/cpu_dsu.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _CPU_DSU_H_
+#define _CPU_DSU_H_
+
+#include <linux/device.h>
+
+#define MODE_DISABLED	0
+#define MODE_INTERRUPT	1
+#define MODE_POLLING	2
+
+#define MXNR_CPU	NR_CPUS
+
+#define	MXNR_DSU_EVENTS	8	/* max number of pmu counter for armv8 is 6+1 */
+struct met_dsu {
+	unsigned char mode;
+	unsigned short event;
+	unsigned long freq;
+	struct kobject *kobj_cpu_dsu;
+};
+
+struct cpu_dsu_hw {
+	const char *name;
+	int nr_cnt;
+	int (*check_event)(struct met_dsu *pmu, int idx, int event);
+	void (*start)(struct met_dsu *pmu, int count);
+	void (*stop)(int count);
+	unsigned int (*polling)(struct met_dsu *pmu, int count, unsigned int *pmu_value);
+	struct met_dsu *pmu;
+	int event_count;
+};
+
+
+struct cpu_dsu_hw *cpu_dsu_hw_init(void);
+
+
+
+#endif	/* _CPU_DSU_H_ */
diff --git a/src/devtools/met-driver/4.14/common/cpu_pmu.c b/src/devtools/met-driver/4.14/common/cpu_pmu.c
new file mode 100644
index 0000000..36a6402
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/cpu_pmu.c
@@ -0,0 +1,831 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/cpu.h>
+#include <linux/cpu_pm.h>
+#include <linux/perf_event.h>
+
+#if (defined(CONFIG_ARM64) || defined(CONFIG_ARM))
+#include <linux/platform_device.h>
+#include <linux/perf/arm_pmu.h>
+#endif
+
+#include "met_drv.h"
+#include "met_kernel_symbol.h"
+#include "interface.h"
+#include "trace.h"
+#include "cpu_pmu.h"
+#include "mtk_typedefs.h"
+
+struct cpu_pmu_hw *cpu_pmu;
+static int counter_cnt[MXNR_CPU];
+static int nr_arg[MXNR_CPU];
+
+int met_perf_cpupmu_status;
+
+static int mtk_pmu_event_enable = 0;
+static struct kobject *kobj_cpu;
+DECLARE_KOBJ_ATTR_INT(mtk_pmu_event_enable, mtk_pmu_event_enable);
+#define KOBJ_ATTR_LIST \
+	do { \
+		KOBJ_ATTR_ITEM(mtk_pmu_event_enable); \
+	} while (0)
+
+#ifdef CONFIG_CPU_PM
+static int use_cpu_pm_pmu_notifier = 0;
+
+/* helper notifier for maintaining pmu states before cpu state transition */
+static int cpu_pm_pmu_notify(struct notifier_block *b,
+			     unsigned long cmd,
+			     void *p)
+{
+	int ii;
+	int cpu, count;
+	unsigned int pmu_value[MXNR_PMU_EVENTS];
+
+	if (!met_perf_cpupmu_status)
+		return NOTIFY_OK;
+
+	cpu = raw_smp_processor_id();
+
+	switch (cmd) {
+	case CPU_PM_ENTER:
+		count = cpu_pmu->polling(cpu_pmu->pmu[cpu], cpu_pmu->event_count[cpu], pmu_value);
+		for (ii = 0; ii < count; ii ++)
+			cpu_pmu->cpu_pm_unpolled_loss[cpu][ii] += pmu_value[ii];
+
+		cpu_pmu->stop(cpu_pmu->event_count[cpu]);
+		break;
+	case CPU_PM_ENTER_FAILED:
+	case CPU_PM_EXIT:
+		cpu_pmu->start(cpu_pmu->pmu[cpu], cpu_pmu->event_count[cpu]);
+		break;
+	default:
+		return NOTIFY_DONE;
+	}
+	return NOTIFY_OK;
+}
+
+struct notifier_block cpu_pm_pmu_notifier = {
+	.notifier_call = cpu_pm_pmu_notify,
+};
+#endif
+
+static DEFINE_PER_CPU(unsigned long long[MXNR_PMU_EVENTS], perfCurr);
+static DEFINE_PER_CPU(unsigned long long[MXNR_PMU_EVENTS], perfPrev);
+static DEFINE_PER_CPU(int[MXNR_PMU_EVENTS], perfCntFirst);
+static DEFINE_PER_CPU(struct perf_event * [MXNR_PMU_EVENTS], pevent);
+static DEFINE_PER_CPU(struct perf_event_attr [MXNR_PMU_EVENTS], pevent_attr);
+static DEFINE_PER_CPU(int, perfSet);
+static DEFINE_PER_CPU(unsigned int, perf_task_init_done);
+static DEFINE_PER_CPU(int, perf_cpuid);
+static DEFINE_PER_CPU(struct delayed_work, cpu_pmu_dwork_setup);
+static DEFINE_PER_CPU(struct delayed_work*, perf_delayed_work_setup);
+static DEFINE_PER_CPU(struct delayed_work, cpu_pmu_dwork_down);
+static DEFINE_PER_CPU(int, cpu_status);
+
+#ifdef CPUPMU_V8_2
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <mt-plat/sync_write.h>
+#include <mt-plat/mtk_io.h>
+
+static char mcucfg_desc[] = "mediatek,mcucfg";
+static void __iomem *mcucfg_base = NULL;
+#define DBG_CONTROL_CPU6	((unsigned long)mcucfg_base + 0x3000 + 0x308)  /* DBG_CONTROL */
+#define DBG_CONTROL_CPU7	((unsigned long)mcucfg_base + 0x3800 + 0x308)  /* DBG_CONTROL */
+#define ENABLE_MTK_PMU_EVENTS_OFFSET 1
+static int restore_dbg_ctrl_cpu6;
+static int restore_dbg_ctrl_cpu7;
+
+int cpu_pmu_debug_init(void)
+{
+	struct device_node  *node = NULL;
+	unsigned int value6,value7;
+
+	 /*for A75 MTK internal event*/
+	 if (mcucfg_base == NULL) {
+		node = of_find_compatible_node(NULL, NULL, mcucfg_desc);
+		if (node == NULL) {
+			MET_TRACE("[MET_PMU_DB] of_find node == NULL\n");
+			pr_debug("[MET_PMU_DB] of_find node == NULL\n");
+			goto out;
+		}
+		mcucfg_base = of_iomap(node, 0);
+		of_node_put(node);
+		if (mcucfg_base == NULL) {
+			MET_TRACE("[MET_PMU_DB] mcucfg_base == NULL\n");
+			pr_debug("[MET_PMU_DB] mcucfg_base == NULL\n");
+			goto out;
+		}
+		MET_TRACE("[MET_PMU_DB] regbase %08lx\n", DBG_CONTROL_CPU7);
+		pr_debug("[MET_PMU_DB] regbase %08lx\n", DBG_CONTROL_CPU7);
+	}
+
+	value6 = readl(IOMEM(DBG_CONTROL_CPU6));
+	if (value6 & (1 << ENABLE_MTK_PMU_EVENTS_OFFSET)) {
+		restore_dbg_ctrl_cpu6 = 1;
+	} else {
+		restore_dbg_ctrl_cpu6 = 0;
+		mt_reg_sync_writel(value6 | (1 << ENABLE_MTK_PMU_EVENTS_OFFSET), DBG_CONTROL_CPU6);
+	}
+
+	value7 = readl(IOMEM(DBG_CONTROL_CPU7));
+	if (value7 & (1 << ENABLE_MTK_PMU_EVENTS_OFFSET)) {
+		restore_dbg_ctrl_cpu7 = 1;
+	} else {
+		restore_dbg_ctrl_cpu7 = 0;
+		mt_reg_sync_writel(value7 | (1 << ENABLE_MTK_PMU_EVENTS_OFFSET), DBG_CONTROL_CPU7);
+	}
+
+	value6 = readl(IOMEM(DBG_CONTROL_CPU6));
+	value7 = readl(IOMEM(DBG_CONTROL_CPU7));
+	MET_TRACE("[MET_PMU_DB]DBG_CONTROL_CPU6 = %08x,  DBG_CONTROL_CPU7 = %08x\n", value6, value7);
+	pr_debug("[MET_PMU_DB]DBG_CONTROL_CPU6 = %08x,  DBG_CONTROL_CPU7 = %08x\n", value6, value7);
+	return 1;
+
+out:
+	if (mcucfg_base != NULL) {
+		iounmap(mcucfg_base);
+		mcucfg_base = NULL;
+	}
+	MET_TRACE("[MET_PMU_DB]DBG_CONTROL init error");
+	pr_debug("[MET_PMU_DB]DBG_CONTROL init error");
+	return 0;
+}
+
+int cpu_pmu_debug_uninit(void)
+{
+	unsigned int value6,value7;
+
+	if (restore_dbg_ctrl_cpu6 == 0) {
+		value6 = readl(IOMEM(DBG_CONTROL_CPU6));
+		mt_reg_sync_writel(value6 & (~(1 << ENABLE_MTK_PMU_EVENTS_OFFSET)), DBG_CONTROL_CPU6);
+	}
+	if (restore_dbg_ctrl_cpu7 == 0) {
+		value7 = readl(IOMEM(DBG_CONTROL_CPU7));
+		mt_reg_sync_writel(value7 & (~(1 << ENABLE_MTK_PMU_EVENTS_OFFSET)), DBG_CONTROL_CPU7);
+	}
+
+	value6 = readl(IOMEM(DBG_CONTROL_CPU6));
+	value7 = readl(IOMEM(DBG_CONTROL_CPU7));
+	MET_TRACE("[MET_PMU_DB]DBG_CONTROL_CPU6 = %08x,  DBG_CONTROL_CPU7 = %08x\n", value6, value7);
+	pr_debug("[MET_PMU_DB]DBG_CONTROL_CPU6 = %08x,  DBG_CONTROL_CPU7 = %08x\n", value6, value7);
+
+	if (mcucfg_base != NULL) {
+		iounmap(mcucfg_base);
+		mcucfg_base = NULL;
+	}
+	restore_dbg_ctrl_cpu6 = 0;
+	restore_dbg_ctrl_cpu7 = 0;
+	return 1;
+}
+#endif
+
+
+
+
+noinline void mp_cpu(unsigned char cnt, unsigned int *value)
+{
+	MET_GENERAL_PRINT(MET_TRACE, cnt, value);
+}
+
+static void dummy_handler(struct perf_event *event, struct perf_sample_data *data,
+			  struct pt_regs *regs)
+{
+	/*
+	 * Required as perf_event_create_kernel_counter() requires an overflow handler,
+	 * even though all we do is poll.
+	 */
+}
+
+static void perf_cpupmu_polling(unsigned long long stamp, int cpu)
+{
+	int			event_count = cpu_pmu->event_count[cpu];
+	struct met_pmu		*pmu = cpu_pmu->pmu[cpu];
+	int			i, count;
+	unsigned long long	delta;
+	struct perf_event	*ev;
+	unsigned int		pmu_value[MXNR_PMU_EVENTS];
+	u64 value;
+
+	if (per_cpu(perfSet, cpu) == 0)
+		return;
+
+	count = 0;
+	for (i = 0; i < event_count; i++) {
+		if (pmu[i].mode == 0)
+			continue;
+
+		ev = per_cpu(pevent, cpu)[i];
+		if ((ev != NULL) && (ev->state == PERF_EVENT_STATE_ACTIVE)) {
+			met_perf_event_read_local_symbol(ev, &value);
+			per_cpu(perfCurr, cpu)[i] = value;
+			delta = (per_cpu(perfCurr, cpu)[i] - per_cpu(perfPrev, cpu)[i]);
+			per_cpu(perfPrev, cpu)[i] = per_cpu(perfCurr, cpu)[i];
+			if (per_cpu(perfCntFirst, cpu)[i] == 1) {
+				/* we shall omit delta counter when we get first counter */
+				per_cpu(perfCntFirst, cpu)[i] = 0;
+				continue;
+			}
+			pmu_value[count] = (unsigned int)delta;
+			count++;
+		}
+	}
+
+	if (count == counter_cnt[cpu])
+		mp_cpu(count, pmu_value);
+}
+
+static struct perf_event* perf_event_create(int cpu, unsigned short event, int count)
+{
+	struct perf_event_attr	*ev_attr;
+	struct perf_event	*ev;
+
+	ev_attr = per_cpu(pevent_attr, cpu)+count;
+	memset(ev_attr, 0, sizeof(*ev_attr));
+	if (event == 0xff) {
+		ev_attr->config = PERF_COUNT_HW_CPU_CYCLES;
+		ev_attr->type = PERF_TYPE_HARDWARE;
+	} else {
+		ev_attr->config = event;
+		ev_attr->type = PERF_TYPE_RAW;
+	}
+	ev_attr->size = sizeof(*ev_attr);
+	ev_attr->sample_period = 0;
+	ev_attr->pinned = 1;
+
+	ev = perf_event_create_kernel_counter(ev_attr, cpu, NULL, dummy_handler, NULL);
+	if (IS_ERR(ev))
+		return NULL;
+	do {
+		if (ev->state == PERF_EVENT_STATE_ACTIVE)
+			break;
+		if (ev->state == PERF_EVENT_STATE_ERROR) {
+			perf_event_enable(ev);
+			if (ev->state == PERF_EVENT_STATE_ACTIVE)
+				break;
+		}
+		perf_event_release_kernel(ev);
+		return NULL;
+	} while (0);
+
+	return ev;
+}
+
+static void perf_event_release(int cpu, struct perf_event *ev)
+{
+	if (ev->state == PERF_EVENT_STATE_ACTIVE)
+		perf_event_disable(ev);
+	perf_event_release_kernel(ev);
+}
+
+static int perf_thread_set_perf_events(int cpu)
+{
+	int			i, size;
+	struct perf_event	*ev;
+
+	size = sizeof(struct perf_event_attr);
+	if (per_cpu(perfSet, cpu) == 0) {
+		int event_count = cpu_pmu->event_count[cpu];
+		struct met_pmu *pmu = cpu_pmu->pmu[cpu];
+		for (i = 0; i < event_count; i++) {
+			if (!pmu[i].mode)
+				continue;	/* Skip disabled counters */
+			ev = perf_event_create(cpu, pmu[i].event, i);
+			if (ev == NULL) {
+				met_cpupmu.mode = 0;
+				met_perf_cpupmu_status = 0;
+
+				MET_TRACE("[MET_PMU] failed to register pmu event %4x\n", pmu[i].event);
+				pr_notice("[MET_PMU] failed to register pmu event %4x\n", pmu[i].event);
+				continue;
+			}
+
+			MET_TRACE("[MET_PMU] registered pmu slot: [%d] evt=%#04x\n", ev->hw.idx, pmu[i].event);
+			pr_debug("[MET_PMU] registered pmu slot: [%d] evt=%#04x\n", ev->hw.idx, pmu[i].event);
+
+			per_cpu(pevent, cpu)[i] = ev;
+			per_cpu(perfPrev, cpu)[i] = 0;
+			per_cpu(perfCurr, cpu)[i] = 0;
+			perf_event_enable(ev);
+			per_cpu(perfCntFirst, cpu)[i] = 1;
+		}	/* for all PMU counter */
+		per_cpu(perfSet, cpu) = 1;
+	}	/* for perfSet */
+
+	return 0;
+}
+
+static void perf_thread_setup(struct work_struct *work)
+{
+	int			cpu;
+	struct delayed_work	*dwork = to_delayed_work(work);
+
+	cpu = dwork->cpu;
+	if (per_cpu(perf_task_init_done, cpu) == 0) {
+		per_cpu(perf_task_init_done, cpu) = 1;
+		perf_thread_set_perf_events(cpu);
+	}
+}
+
+static void met_perf_cpupmu_start(int cpu)
+{
+	if (met_cpupmu.mode == 0)
+		return;
+
+	per_cpu(perf_cpuid, cpu) = cpu;
+	if (per_cpu(perf_delayed_work_setup, cpu) == NULL) {
+		struct delayed_work *dwork = &per_cpu(cpu_pmu_dwork_setup, cpu);
+		INIT_DELAYED_WORK(dwork, perf_thread_setup);
+		dwork->cpu = cpu;
+		schedule_delayed_work_on(cpu, dwork, 0);
+		per_cpu(perf_delayed_work_setup, cpu) = dwork;
+	}
+}
+
+static void perf_thread_down(struct work_struct *work)
+{
+	struct delayed_work	*dwork = to_delayed_work(work);
+	int			cpu, i;
+	struct perf_event	*ev;
+	int			event_count;
+	struct met_pmu		*pmu;
+
+	cpu = dwork->cpu;
+	if (per_cpu(perfSet, cpu) == 0)
+		return;
+
+	per_cpu(perfSet, cpu) = 0;
+	event_count = cpu_pmu->event_count[cpu];
+	pmu = cpu_pmu->pmu[cpu];
+	for (i = 0; i < event_count; i++) {
+		ev = per_cpu(pevent, cpu)[i];
+		if (ev != NULL) {
+			perf_event_release(cpu, ev);
+			per_cpu(pevent, cpu)[i] = NULL;
+		}
+	}
+	per_cpu(perf_task_init_done, cpu) = 0;
+	per_cpu(perf_delayed_work_setup, cpu) = NULL;
+}
+
+static void met_perf_cpupmu_stop(int cpu)
+{
+	struct delayed_work	*dwork;
+
+	per_cpu(perf_cpuid, cpu) = cpu;
+	dwork = &per_cpu(cpu_pmu_dwork_down, cpu);
+	INIT_DELAYED_WORK(dwork, perf_thread_down);
+	dwork->cpu = cpu;
+	schedule_delayed_work_on(cpu, dwork, 0);
+}
+
+static int cpupmu_create_subfs(struct kobject *parent)
+{
+	int ret = 0;
+
+	cpu_pmu = cpu_pmu_hw_init();
+	if (cpu_pmu == NULL) {
+		PR_BOOTMSG("Failed to init CPU PMU HW!!\n");
+		return -ENODEV;
+	}
+
+	kobj_cpu = parent;
+
+#define KOBJ_ATTR_ITEM(attr_name) \
+	do { \
+		ret = sysfs_create_file(kobj_cpu, &attr_name ## _attr.attr); \
+		if (ret != 0) { \
+			pr_notice("Failed to create " #attr_name " in sysfs\n"); \
+			return ret; \
+		} \
+	} while (0)
+	KOBJ_ATTR_LIST;
+#undef  KOBJ_ATTR_ITEM
+
+	return 0;
+}
+
+static void cpupmu_delete_subfs(void)
+{
+#define KOBJ_ATTR_ITEM(attr_name) \
+	sysfs_remove_file(kobj_cpu, &attr_name ## _attr.attr)
+
+	if (kobj_cpu != NULL) {
+		KOBJ_ATTR_LIST;
+		kobj_cpu = NULL;
+	}
+#undef  KOBJ_ATTR_ITEM
+}
+
+void met_perf_cpupmu_polling(unsigned long long stamp, int cpu)
+{
+	int count;
+	unsigned int pmu_value[MXNR_PMU_EVENTS];
+
+	if (per_cpu(cpu_status, cpu) != MET_CPU_ONLINE)
+		return;
+
+	if (met_cpu_pmu_method) {
+		perf_cpupmu_polling(stamp, cpu);
+	} else {
+		count = cpu_pmu->polling(cpu_pmu->pmu[cpu], cpu_pmu->event_count[cpu], pmu_value);
+
+#ifdef CONFIG_CPU_PM
+		if (met_cpu_pm_pmu_reconfig) {
+			int ii;
+			for (ii = 0; ii < count; ii ++)
+				pmu_value[ii] += cpu_pmu->cpu_pm_unpolled_loss[cpu][ii];
+		}
+#endif
+
+		mp_cpu(count, pmu_value);
+
+#ifdef CONFIG_CPU_PM
+		if (met_cpu_pm_pmu_reconfig) {
+			memset(cpu_pmu->cpu_pm_unpolled_loss[cpu], 0, sizeof (cpu_pmu->cpu_pm_unpolled_loss[0]));
+		}
+#endif
+	}
+}
+
+static void cpupmu_start(void)
+{
+	int	cpu = raw_smp_processor_id();
+
+	if (met_cpu_pmu_method)
+		met_perf_cpupmu_start(cpu);
+	else {
+		nr_arg[cpu] = 0;
+		cpu_pmu->start(cpu_pmu->pmu[cpu], cpu_pmu->event_count[cpu]);
+	}
+	met_perf_cpupmu_status = 1;
+	per_cpu(cpu_status, cpu) = MET_CPU_ONLINE;
+}
+
+
+static void cpupmu_unique_start(void)
+{
+#ifdef CPUPMU_V8_2
+	int ret = 0;
+	if (mtk_pmu_event_enable == 1){
+		ret = cpu_pmu_debug_init();
+		if (ret == 0)
+			PR_BOOTMSG("Failed to init CPU PMU debug!!\n");
+	}
+#endif
+
+#ifdef CONFIG_CPU_PM
+	use_cpu_pm_pmu_notifier = 0;
+	if (met_cpu_pm_pmu_reconfig) {
+		if (met_cpu_pmu_method) {
+			met_cpu_pm_pmu_reconfig = 0;
+			MET_TRACE("[MET_PMU] met_cpu_pmu_method=%d, met_cpu_pm_pmu_reconfig forced disabled\n", met_cpu_pmu_method);
+			pr_debug("[MET_PMU] met_cpu_pmu_method=%d, met_cpu_pm_pmu_reconfig forced disabled\n", met_cpu_pmu_method);
+		} else {
+			memset(cpu_pmu->cpu_pm_unpolled_loss, 0, sizeof (cpu_pmu->cpu_pm_unpolled_loss));
+			cpu_pm_register_notifier(&cpu_pm_pmu_notifier);
+			use_cpu_pm_pmu_notifier = 1;
+		}
+	}
+#else
+	if (met_cpu_pm_pmu_reconfig) {
+		met_cpu_pm_pmu_reconfig = 0;
+		MET_TRACE("[MET_PMU] CONFIG_CPU_PM=%d, met_cpu_pm_pmu_reconfig forced disabled\n", CONFIG_CPU_PM);
+		pr_debug("[MET_PMU] CONFIG_CPU_PM=%d, met_cpu_pm_pmu_reconfig forced disabled\n", CONFIG_CPU_PM);
+	}
+#endif
+	MET_TRACE("[MET_PMU] met_cpu_pm_pmu_reconfig=%u\n", met_cpu_pm_pmu_reconfig);
+	pr_debug("[MET_PMU] met_cpu_pm_pmu_reconfig=%u\n", met_cpu_pm_pmu_reconfig);
+
+	return;
+}
+
+static void cpupmu_stop(void)
+{
+	int	cpu = raw_smp_processor_id();
+
+	met_perf_cpupmu_status = 0;
+	if (met_cpu_pmu_method)
+		met_perf_cpupmu_stop(cpu);
+	else
+		cpu_pmu->stop(cpu_pmu->event_count[cpu]);
+}
+
+static void cpupmu_unique_stop(void)
+{
+#ifdef CPUPMU_V8_2
+	if (mtk_pmu_event_enable == 1)
+		cpu_pmu_debug_uninit();
+#endif
+
+#ifdef CONFIG_CPU_PM
+	if (use_cpu_pm_pmu_notifier) {
+		cpu_pm_unregister_notifier(&cpu_pm_pmu_notifier);
+	}
+#endif
+	return;
+}
+
+static const char cache_line_header[] =
+	"met-info [000] 0.0: met_cpu_cache_line_size: %d\n";
+static const char header[] =
+	"met-info [000] 0.0: met_cpu_header_v2: %d";
+
+static const char help[] =
+	"  --pmu-cpu-evt=[cpu_list:]event_list   select CPU-PMU events in %s\n"
+	"                                        cpu_list: specify the cpu_id list or apply to all the cores\n"
+	"                                            example: 0,1,2\n"
+	"                                        event_list: specify the event number\n"
+	"                                            example: 0x8,0xff\n";
+
+static int cpupmu_print_help(char *buf, int len)
+{
+	return snprintf(buf, PAGE_SIZE, help, cpu_pmu->cpu_name);
+}
+
+static int reset_driver_stat(void)
+{
+	int		cpu, i;
+	int		event_count;
+	struct met_pmu	*pmu;
+
+	met_cpupmu.mode = 0;
+	for_each_possible_cpu(cpu) {
+		event_count = cpu_pmu->event_count[cpu];
+		pmu = cpu_pmu->pmu[cpu];
+		counter_cnt[cpu] = 0;
+		nr_arg[cpu] = 0;
+		for (i = 0; i < event_count; i++) {
+			pmu[i].mode = MODE_DISABLED;
+			pmu[i].event = 0;
+			pmu[i].freq = 0;
+		}
+	}
+
+	return 0;
+}
+
+static int cpupmu_print_header(char *buf, int len)
+{
+	int		cpu, i, ret, first;
+	int		event_count;
+	struct met_pmu	*pmu;
+
+	ret = 0;
+
+	/*append CPU PMU access method*/
+	if (met_cpu_pmu_method)
+		ret += snprintf(buf + ret, PAGE_SIZE,
+			"met-info [000] 0.0: CPU_PMU_method: perf APIs\n");
+	else
+		ret += snprintf(buf + ret, PAGE_SIZE,
+			"met-info [000] 0.0: CPU_PMU_method: MET pmu driver\n");
+
+	/*append cache line size*/
+	ret += snprintf(buf + ret, PAGE_SIZE - ret, cache_line_header, cache_line_size());
+	ret += snprintf(buf + ret, PAGE_SIZE - ret, "# mp_cpu: pmu_value1, ...\n");
+
+	for_each_possible_cpu(cpu) {
+		event_count = cpu_pmu->event_count[cpu];
+		pmu = cpu_pmu->pmu[cpu];
+		first = 1;
+		for (i = 0; i < event_count; i++) {
+			if (pmu[i].mode == 0)
+				continue;
+			if (first) {
+				ret += snprintf(buf + ret, PAGE_SIZE - ret, header, cpu);
+				first = 0;
+			}
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, ",0x%x", pmu[i].event);
+			pmu[i].mode = 0;
+		}
+		if (!first)
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, "\n");
+	}
+
+	reset_driver_stat();
+
+	return ret;
+}
+
+static int met_parse_num_list(char *arg, int len, int *list, int list_cnt)
+{
+	int	nr_num = 0;
+	char	*num;
+	int	num_len;
+
+	/* search ',' as the splitter */
+	while (len) {
+		num = arg;
+		num_len = 0;
+		if (list_cnt <= 0)
+			return -1;
+		while (len) {
+			len--;
+			if (*arg == ',') {
+				*(arg++) = '\0';
+				break;
+			}
+			arg++;
+			num_len++;
+		}
+		if (met_parse_num(num, list, num_len) < 0)
+			return -1;
+		list++;
+		list_cnt--;
+		nr_num++;
+	}
+
+	return nr_num;
+}
+
+static int cpupmu_process_argument(const char *arg, int len)
+{
+	char		*arg1 = (char*)arg;
+	int		len1 = len;
+	int		cpu, cpu_list[MXNR_CPU];
+	int		nr_events, event_list[MXNR_PMU_EVENTS];
+	int		i;
+	int		nr_counters;
+	struct met_pmu	*pmu;
+	int		arg_nr;
+	int		event_no;
+
+	/*
+	 * split cpu_list and event_list by ':'
+	 *   arg, len: cpu_list when found (i < len)
+	 *   arg1, len1: event_list
+	 */
+	for (i = 0; i < len; i++) {
+		if (arg[i] == ':') {
+			arg1[i] = '\0';
+			arg1 += i+1;
+			len1 = len - i - 1;
+			len = i;
+			break;
+		}
+	}
+
+	/*
+	 * setup cpu_list array
+	 *   1: selected
+	 *   0: unselected
+	 */
+	if (arg1 != arg) {	/* is cpu_id list specified? */
+		int list[MXNR_CPU], cnt;
+		int cpu_id;
+		if ((cnt = met_parse_num_list((char*)arg, len, list, ARRAY_SIZE(list))) <= 0)
+			goto arg_out;
+		memset(cpu_list, 0, sizeof(cpu_list));
+		for (i = 0; i < cnt; i++) {
+			cpu_id = list[i];
+			if (cpu_id < 0 || cpu_id >= ARRAY_SIZE(cpu_list))
+				goto arg_out;
+			cpu_list[cpu_id] = 1;
+		}
+	}
+	else
+		memset(cpu_list, 1, sizeof(cpu_list));
+
+	/* get event_list */
+	if ((nr_events = met_parse_num_list(arg1, len1, event_list, ARRAY_SIZE(event_list))) <= 0)
+		goto arg_out;
+
+	/* for each cpu in cpu_list, add all the events in event_list */
+	for_each_possible_cpu(cpu) {
+		pmu = cpu_pmu->pmu[cpu];
+		arg_nr = nr_arg[cpu];
+
+		if (cpu_list[cpu] == 0)
+			continue;
+
+		if (met_cpu_pmu_method) {
+			nr_counters = perf_num_counters();
+		} else {
+			nr_counters = cpu_pmu->event_count[cpu];
+		}
+
+		pr_debug("[MET_PMU] pmu slot count=%d\n", nr_counters);
+
+		if (nr_counters == 0)
+			goto arg_out;
+
+		for (i = 0; i < nr_events; i++) {
+			event_no = event_list[i];
+			/*
+			 * check if event is duplicate,
+			 * but may not include 0xff when met_cpu_pmu_method == 0.
+			 */
+			if (cpu_pmu->check_event(pmu, arg_nr, event_no) < 0)
+				goto arg_out;
+
+			/*
+			 * test if this event is available when in perf_APIs mode
+			 */
+			if (met_cpu_pmu_method) {
+				struct perf_event *ev;
+				ev = perf_event_create(cpu, event_no, arg_nr);
+				if (ev == NULL) {
+					pr_debug("!!!!!!!! [MET_PMU] failed pmu alloction test (event_no=%#04x)\n", event_no);
+				} else {
+					perf_event_release(cpu, ev);
+				}
+			}
+
+			if (met_cpu_pmu_method) {
+				if (arg_nr >= nr_counters)
+					goto arg_out;
+				pmu[arg_nr].mode = MODE_POLLING;
+				pmu[arg_nr].event = event_no;
+				pmu[arg_nr].freq = 0;
+				arg_nr++;
+			} else {
+				if (event_no == 0xff) {
+					if (pmu[nr_counters-1].mode == MODE_POLLING)
+						goto arg_out;
+					pmu[nr_counters-1].mode = MODE_POLLING;
+					pmu[nr_counters-1].event = 0xff;
+					pmu[nr_counters-1].freq = 0;
+				} else {
+					if (arg_nr >= (nr_counters - 1))
+						goto arg_out;
+					pmu[arg_nr].mode = MODE_POLLING;
+					pmu[arg_nr].event = event_no;
+					pmu[arg_nr].freq = 0;
+					arg_nr++;
+				}
+			}
+			counter_cnt[cpu]++;
+		}
+		nr_arg[cpu] = arg_nr;
+	}
+
+	met_cpupmu.mode = 1;
+	return 0;
+
+arg_out:
+	reset_driver_stat();
+	return -EINVAL;
+}
+
+
+static void cpupmu_cpu_state_notify(long cpu, unsigned long action)
+{
+	per_cpu(cpu_status, cpu) = action;
+
+#if (defined(CONFIG_ARM64) || defined(CONFIG_ARM))
+	if (met_cpu_pmu_method && action == MET_CPU_OFFLINE) {
+		struct perf_event *event = NULL;
+		struct arm_pmu *armpmu = NULL;
+		struct platform_device *pmu_device = NULL;
+		int irq = 0;
+
+		event = per_cpu(pevent, cpu)[0];
+		if (event)
+			armpmu = to_arm_pmu(event->pmu);
+		pr_debug("!!!!!!!! %s_%ld, event=%p\n", __FUNCTION__, cpu, event);
+
+		if (armpmu)
+			pmu_device = armpmu->plat_device;
+		pr_debug("!!!!!!!! %s_%ld, armpmu=%p\n", __FUNCTION__, cpu, armpmu);
+
+		if (pmu_device)
+			irq = platform_get_irq(pmu_device, 0);
+		pr_debug("!!!!!!!! %s_%ld, pmu_device=%p\n", __FUNCTION__, cpu, pmu_device);
+
+		if (irq > 0)
+			disable_percpu_irq(irq);
+		pr_debug("!!!!!!!! %s_%ld, irq=%d\n", __FUNCTION__, cpu, irq);
+	}
+#endif
+}
+
+
+struct metdevice met_cpupmu = {
+	.name = "cpu",
+	.type = MET_TYPE_PMU,
+	.cpu_related = 1,
+	.create_subfs = cpupmu_create_subfs,
+	.delete_subfs = cpupmu_delete_subfs,
+	.start = cpupmu_start,
+	.uniq_start = cpupmu_unique_start,
+	.stop = cpupmu_stop,
+	.uniq_stop = cpupmu_unique_stop,
+	.polling_interval = 1,
+	.timed_polling = met_perf_cpupmu_polling,
+	.print_help = cpupmu_print_help,
+	.print_header = cpupmu_print_header,
+	.process_argument = cpupmu_process_argument,
+	.cpu_state_notify = cpupmu_cpu_state_notify
+};
diff --git a/src/devtools/met-driver/4.14/common/cpu_pmu.h b/src/devtools/met-driver/4.14/common/cpu_pmu.h
new file mode 100644
index 0000000..3d046d5
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/cpu_pmu.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _CPU_PMU_H_
+#define _CPU_PMU_H_
+
+#include <linux/device.h>
+
+#define MODE_DISABLED	0
+#define MODE_INTERRUPT	1
+#define MODE_POLLING	2
+
+#define MXSIZE_PMU_DESC 32
+#define MXNR_CPU	NR_CPUS
+
+#define	MXNR_PMU_EVENTS	8	/* max number of pmu counter for armv8 is 6+1 */
+struct met_pmu {
+	unsigned char mode;
+	unsigned short event;
+	unsigned long freq;
+	struct kobject *kobj_cpu_pmu;
+};
+
+struct cpu_pmu_hw {
+	const char *name;
+	const char *cpu_name;
+	int nr_cnt;
+	int (*get_event_desc)(int idx, int event, char *event_desc);
+	int (*check_event)(struct met_pmu *pmu, int idx, int event);
+	void (*start)(struct met_pmu *pmu, int count);
+	void (*stop)(int count);
+	unsigned int (*polling)(struct met_pmu *pmu, int count, unsigned int *pmu_value);
+	struct met_pmu *pmu[MXNR_CPU];
+	int event_count[MXNR_CPU];
+	/*
+	 * used for compensation of pmu counter loss
+	 * between end of polling and start of cpu pm
+	 */
+	unsigned int cpu_pm_unpolled_loss[MXNR_CPU][MXNR_PMU_EVENTS];
+};
+
+struct pmu_desc {
+	unsigned int event;
+	char name[MXSIZE_PMU_DESC];
+};
+
+struct cpu_pmu_hw *cpu_pmu_hw_init(void);
+
+extern struct cpu_pmu_hw *cpu_pmu;
+extern noinline void mp_cpu(unsigned char cnt, unsigned int *value);
+
+extern int met_perf_cpupmu_status;
+extern void met_perf_cpupmu_polling(unsigned long long stamp, int cpu);
+
+#endif	/* _CPU_PMU_H_ */
diff --git a/src/devtools/met-driver/4.14/common/dummy_header.c b/src/devtools/met-driver/4.14/common/dummy_header.c
new file mode 100644
index 0000000..f8d1187
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/dummy_header.c
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+
+#include "interface.h"
+#include "met_drv.h"
+
+static struct kobject *kobj_met_dummy;
+static char header_str[PAGE_SIZE];
+static int header_str_len;
+struct metdevice met_dummy_header;
+
+static ssize_t dummy_str_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf);
+static ssize_t dummy_str_store(struct kobject *kobj,
+			       struct kobj_attribute *attr, const char *buf, size_t n);
+static struct kobj_attribute dummy_attr = __ATTR(dummy_str, 0664, dummy_str_show, dummy_str_store);
+
+
+static ssize_t dummy_str_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	int ret;
+
+	ret = snprintf(buf, PAGE_SIZE, "%s", header_str);
+
+	return ret;
+}
+
+static ssize_t dummy_str_store(struct kobject *kobj,
+			       struct kobj_attribute *attr, const char *buf, size_t n)
+{
+	int ret = 0;
+	char *ptr = header_str;
+
+	if ((header_str_len + strlen(buf)) < PAGE_SIZE) {
+		ret = snprintf(ptr + header_str_len, PAGE_SIZE - header_str_len, "%s\n", buf);
+		header_str_len += ret;
+	}
+	met_dummy_header.mode = 1;
+
+	return n;
+}
+
+static int dummy_reset(void)
+{
+	met_dummy_header.mode = 0;
+	memset(header_str, 0x00, PAGE_SIZE);
+	header_str_len = 0;
+
+	return 0;
+}
+
+static int dummy_print_header(char *buf, int len)
+{
+	if (header_str_len > 0)
+		len = snprintf(buf, PAGE_SIZE, "%s", header_str);
+	else
+		len = snprintf(buf, PAGE_SIZE, "# dummy header is empty\n");
+
+	return len;
+}
+
+static int dummy_create(struct kobject *parent)
+{
+	int ret = 0;
+
+	kobj_met_dummy = parent;
+	ret = sysfs_create_file(kobj_met_dummy, &dummy_attr.attr);
+	if (ret != 0) {
+		pr_debug("Failed to create montype0 in sysfs\n");
+		return ret;
+	}
+
+	return ret;
+}
+
+static void dummy_delete(void)
+{
+	sysfs_remove_file(kobj_met_dummy, &dummy_attr.attr);
+	kobj_met_dummy = NULL;
+}
+
+struct metdevice met_dummy_header = {
+	.name = "dummy_header",
+	.type = MET_TYPE_MISC,
+	.cpu_related = 0,
+	.start = NULL,
+	.stop = NULL,
+	.reset = dummy_reset,
+	.polling_interval = 0,
+	.timed_polling = NULL,
+	.print_help = NULL,
+	.print_header = dummy_print_header,
+	.create_subfs = dummy_create,
+	.delete_subfs = dummy_delete,
+};
diff --git a/src/devtools/met-driver/4.14/common/interface.c b/src/devtools/met-driver/4.14/common/interface.c
new file mode 100644
index 0000000..f8bb644
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/interface.c
@@ -0,0 +1,1441 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/sched/clock.h>
+#include <linux/fs.h>
+#include <linux/sched.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/miscdevice.h>
+#include <linux/kallsyms.h>
+#include <linux/syscore_ops.h>
+#include <linux/dma-mapping.h>
+#include "interface.h"
+#include "sampler.h"
+#include "util.h"
+
+#include "ondiemet.h"
+
+#include "met_drv.h"
+#include "met_tag.h"
+#include "met_kernel_symbol.h"
+#include "met_power.h"
+#include "met_api_tbl.h"
+#include "version.h"
+
+extern int enable_met_backlight_tag(void);
+extern int output_met_backlight_tag(int level);
+
+static int run = -1;
+static int sample_rate = 1000;	/* Default: 1000 Hz */
+static int met_suspend_compensation_mode;
+static int met_suspend_compensation_flag;
+
+/*
+ * met_cpu_pmu_method:
+ *   0: MET pmu driver
+ *   1: perf APIs
+ */
+unsigned int met_cpu_pmu_method = 1;
+/*
+ * controls whether re-configuring pmu events after leaving cpu off state
+ */
+unsigned int met_cpu_pm_pmu_reconfig = 1;
+
+int met_hrtimer_expire;		/* in ns */
+int met_timer_expire;		/* in jiffies */
+unsigned int ctrl_flags;
+int met_mode;
+EXPORT_SYMBOL(met_mode);
+
+DEFINE_PER_CPU(char[MET_STRBUF_SIZE], met_strbuf_nmi);
+EXPORT_SYMBOL(met_strbuf_nmi);
+
+DEFINE_PER_CPU(char[MET_STRBUF_SIZE], met_strbuf_irq);
+EXPORT_SYMBOL(met_strbuf_irq);
+
+DEFINE_PER_CPU(char[MET_STRBUF_SIZE], met_strbuf_sirq);
+EXPORT_SYMBOL(met_strbuf_sirq);
+
+DEFINE_PER_CPU(char[MET_STRBUF_SIZE], met_strbuf);
+EXPORT_SYMBOL(met_strbuf);
+
+static void calc_timer_value(int rate)
+{
+	sample_rate = rate;
+
+	if (rate == 0) {
+		met_hrtimer_expire = 0;
+		met_timer_expire = 0;
+		return;
+	}
+
+	met_hrtimer_expire = 1000000000 / rate;
+
+	/* Case 1: hrtimer < 1 OS tick, met_timer_expire = 1 OS tick */
+	if (rate > HZ)
+		met_timer_expire = 1;
+	/* Case 2: hrtimer > 1 OS tick, met_timer_expire is hrtimer + 1 OS tick */
+	else
+		met_timer_expire = (HZ / rate) + 1;
+
+	/* pr_debug("JBK HZ=%d, met_hrtimer_expire=%d ns, met_timer_expire=%d ticks\n", */
+	/* HZ, met_hrtimer_expire, met_timer_expire); */
+}
+
+int met_parse_num(const char *str, unsigned int *value, int len)
+{
+	int ret;
+
+	if (len <= 0)
+		return -1;
+
+	if ((len > 2) &&
+		((str[0] == '0') &&
+		((str[1] == 'x') || (str[1] == 'X')))) {
+		ret = kstrtoint(str, 16, value);
+	} else {
+		ret = kstrtoint(str, 10, value);
+	}
+
+	if (ret != 0)
+		return -1;
+
+	return 0;
+}
+
+void met_set_suspend_notify(int flag)
+{
+	if (met_suspend_compensation_mode == 1)
+		met_suspend_compensation_flag = flag;
+	else
+		met_suspend_compensation_flag = 0;
+}
+
+LIST_HEAD(met_list);
+static struct kobject *kobj_misc;
+static struct kobject *kobj_pmu;
+static struct kobject *kobj_bus;
+
+static ssize_t ver_show(struct device *dev, struct device_attribute *attr, char *buf);
+static DEVICE_ATTR(ver, 0444, ver_show, NULL);
+
+static ssize_t plf_show(struct device *dev, struct device_attribute *attr, char *buf);
+static DEVICE_ATTR(plf, 0444, plf_show, NULL);
+
+static ssize_t core_topology_show(struct device *dev, struct device_attribute *attr, char *buf);
+static DEVICE_ATTR(core_topology, 0444, core_topology_show, NULL);
+
+static ssize_t devices_show(struct device *dev, struct device_attribute *attr, char *buf);
+static DEVICE_ATTR(devices, 0444, devices_show, NULL);
+
+static ssize_t ctrl_show(struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t ctrl_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			  size_t count);
+static DEVICE_ATTR(ctrl, 0664, ctrl_show, ctrl_store);
+
+static ssize_t spr_show(struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t spr_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			 size_t count);
+static DEVICE_ATTR(sample_rate, 0664, spr_show, spr_store);
+
+static ssize_t run_show(struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t run_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			 size_t count);
+static DEVICE_ATTR(run, 0664, run_show, run_store);
+
+static ssize_t ksym_show(struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t ksym_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			 size_t count);
+static DEVICE_ATTR(ksym, 0664, ksym_show, ksym_store);
+
+static ssize_t cpu_pmu_method_show(struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t cpu_pmu_method_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			  size_t count);
+static DEVICE_ATTR(cpu_pmu_method, 0664, cpu_pmu_method_show, cpu_pmu_method_store);
+
+static ssize_t cpu_pm_pmu_reconfig_show(struct device *dev,
+					struct device_attribute *attr,
+					char *buf);
+static ssize_t cpu_pm_pmu_reconfig_store(struct device *dev,
+					 struct device_attribute *attr,
+					 const char *buf,
+					 size_t count);
+static DEVICE_ATTR(cpu_pm_pmu_reconfig,
+		   0664,
+		   cpu_pm_pmu_reconfig_show,
+		   cpu_pm_pmu_reconfig_store);
+
+#ifdef PR_CPU_NOTIFY
+int met_cpu_notify;
+static ssize_t cpu_notify_show(struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t cpu_notify_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			  size_t count);
+static DEVICE_ATTR(cpu_notify, 0664, cpu_notify_show, cpu_notify_store);
+#endif
+
+#ifdef CONFIG_CPU_FREQ
+static ssize_t dvfs_show(struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t dvfs_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			  size_t count);
+static DEVICE_ATTR(dvfs, 0664, dvfs_show, dvfs_store);
+#endif
+
+static ssize_t suspend_compensation_enable_show(struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t suspend_compensation_enable_store(struct device *dev, struct device_attribute *attr,
+							const char *buf, size_t count);
+static DEVICE_ATTR(suspend_compensation_enable, 0664, suspend_compensation_enable_show,
+			suspend_compensation_enable_store);
+
+static ssize_t suspend_compensation_flag_show(struct device *dev, struct device_attribute *attr, char *buf);
+static DEVICE_ATTR(suspend_compensation_flag, 0444, suspend_compensation_flag_show, NULL);
+
+static ssize_t ipi_test_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			 size_t count);
+static DEVICE_ATTR(ipi_test, 0220, NULL, ipi_test_store);
+
+static const struct file_operations met_file_ops = {
+	.owner = THIS_MODULE
+};
+
+struct miscdevice met_device = {
+	.minor = MISC_DYNAMIC_MINOR,
+	.name = "met",
+	.mode = 0664,
+	.fops = &met_file_ops
+};
+EXPORT_SYMBOL(met_device);
+
+static int met_run(void)
+{
+	sampler_start();
+#ifdef MET_USER_EVENT_SUPPORT
+	bltab.flag &= (~MET_CLASS_ALL);
+#endif
+	ondiemet_start();
+	return 0;
+}
+
+static void met_stop(void)
+{
+#ifdef MET_USER_EVENT_SUPPORT
+	bltab.flag |= MET_CLASS_ALL;
+#endif
+	sampler_stop();
+	/* the met.ko will be use by script "cat ...", release it */
+	if ((ondiemet_module[ONDIEMET_SSPM] == 0) || (sspm_buffer_size == -1))
+		ondiemet_log_manager_stop();
+	ondiemet_stop();
+	ondiemet_extract();
+}
+
+static ssize_t ver_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int i;
+
+	mutex_lock(&dev->mutex);
+	i = snprintf(buf, PAGE_SIZE, "%s\n", MET_BACKEND_VERSION);
+	mutex_unlock(&dev->mutex);
+	return i;
+}
+
+static ssize_t devices_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int len, total_len = 0;
+	struct metdevice *c = NULL;
+
+	mutex_lock(&dev->mutex);
+	list_for_each_entry(c, &met_list, list) {
+		len = 0;
+		if (c->type == MET_TYPE_PMU)
+			len = snprintf(buf, PAGE_SIZE - total_len, "pmu/%s:0\n", c->name);
+		else if (c->type == MET_TYPE_BUS)
+			len = snprintf(buf, PAGE_SIZE - total_len, "bus/%s:0\n", c->name);
+		else if (c->type == MET_TYPE_MISC)
+			len = snprintf(buf, PAGE_SIZE - total_len, "misc/%s:0\n", c->name);
+
+		if (c->ondiemet_mode == 0) {
+			if (c->process_argument)
+				buf[len - 2]++;
+		} else if (c->ondiemet_mode == 1) {
+			if (c->ondiemet_process_argument)
+				buf[len - 2]++;
+		} else if (c->ondiemet_mode == 2) {
+			if (c->process_argument)
+				buf[len - 2]++;
+			if (c->ondiemet_process_argument)
+				buf[len - 2]++;
+		}
+
+		buf += len;
+		total_len += len;
+	}
+
+	mutex_unlock(&dev->mutex);
+	return total_len;
+}
+
+static char met_platform[16] = "none";
+static ssize_t plf_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int i;
+
+	mutex_lock(&dev->mutex);
+	i = snprintf(buf, PAGE_SIZE, "%s\n", met_platform);
+	mutex_unlock(&dev->mutex);
+	return i;
+}
+
+static char met_topology[64] = "none";
+static ssize_t core_topology_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int i;
+
+	mutex_lock(&dev->mutex);
+	i = snprintf(buf, PAGE_SIZE, "%s\n", met_topology);
+	mutex_unlock(&dev->mutex);
+	return i;
+}
+
+static ssize_t ctrl_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%d\n", ctrl_flags);
+}
+
+static ssize_t ctrl_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			  size_t count)
+{
+	unsigned int value = 0;
+
+	if (met_parse_num(buf, &value, count) < 0)
+		return -EINVAL;
+
+	ctrl_flags = value;
+	return count;
+}
+
+static ssize_t cpu_pmu_method_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%d\n", met_cpu_pmu_method);
+}
+
+static ssize_t cpu_pmu_method_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			  size_t count)
+{
+	unsigned int value;
+
+	if (met_parse_num(buf, &value, count) < 0)
+		return -EINVAL;
+
+	met_cpu_pmu_method = value;
+	return count;
+}
+
+static ssize_t cpu_pm_pmu_reconfig_show(struct device *dev,
+					struct device_attribute *attr,
+					char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%d\n", met_cpu_pm_pmu_reconfig);
+}
+
+static ssize_t cpu_pm_pmu_reconfig_store(struct device *dev,
+					 struct device_attribute *attr,
+					 const char *buf,
+					 size_t count)
+{
+	unsigned int value;
+
+	if (met_parse_num(buf, &value, count) < 0)
+		return -EINVAL;
+
+	met_cpu_pm_pmu_reconfig = value;
+	return count;
+}
+
+static void _test_trace_ipi_raise(void *info)
+{
+	unsigned int *cpu = (unsigned int *)info;
+	void (*arch_send_call_function_single_ipi_sym)(int cpu) = NULL;
+
+	arch_send_call_function_single_ipi_sym = (void *)symbol_get(met_arch_send_call_function_single_ipi);
+	if (arch_send_call_function_single_ipi_sym)
+		arch_send_call_function_single_ipi_sym(*cpu);
+}
+
+
+static ssize_t ipi_test_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			 size_t count)
+{
+	int this_cpu = smp_processor_id();
+	unsigned int cpu = 0;
+	unsigned int value;
+
+	if (met_parse_num(buf, &value, count) < 0)
+		return -EINVAL;
+
+	cpu = value;
+	if (cpu == this_cpu)
+		_test_trace_ipi_raise(&cpu);
+	else
+		met_smp_call_function_single_symbol(cpu, _test_trace_ipi_raise, &cpu, 1);
+
+	return count;
+}
+
+
+#if	defined(MET_BOOT_MSG)
+char met_boot_msg_tmp[256];
+char met_boot_msg[PAGE_SIZE];
+int met_boot_msg_idx;
+
+int pr_bootmsg(int str_len, char *str)
+{
+	if (met_boot_msg_idx+str_len+1 > PAGE_SIZE)
+		return -1;
+	memcpy(met_boot_msg+met_boot_msg_idx, str, str_len);
+	met_boot_msg_idx += str_len;
+	return 0;
+}
+
+static ssize_t bootmsg_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int	i;
+
+	mutex_lock(&dev->mutex);
+	i = snprintf(buf, PAGE_SIZE, "%s\n", met_boot_msg);
+	mutex_unlock(&dev->mutex);
+	return i;
+}
+
+static DEVICE_ATTR_RO(bootmsg);
+EXPORT_SYMBOL(met_boot_msg_tmp);
+EXPORT_SYMBOL(pr_bootmsg);
+#endif
+
+static ssize_t spr_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int i;
+
+	mutex_lock(&dev->mutex);
+	i = snprintf(buf, PAGE_SIZE, "%d\n", sample_rate);
+	mutex_unlock(&dev->mutex);
+	return i;
+}
+
+static ssize_t spr_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			 size_t count)
+{
+	int value;
+	struct metdevice *c = NULL;
+
+	mutex_lock(&dev->mutex);
+
+	if ((run == 1) || (count == 0) || (buf == NULL)) {
+		mutex_unlock(&dev->mutex);
+		return -EINVAL;
+	}
+	if (kstrtoint(buf, 0, &value) != 0) {
+		mutex_unlock(&dev->mutex);
+		return -EINVAL;
+	}
+
+	if ((value < 0) || (value > 10000)) {
+		mutex_unlock(&dev->mutex);
+		return -EINVAL;
+	}
+
+	calc_timer_value(value);
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->polling_interval > 0)
+			c->polling_count_reload = ((c->polling_interval * sample_rate) - 1) / 1000;
+		else
+			c->polling_count_reload = 0;
+	}
+
+	mutex_unlock(&dev->mutex);
+
+	return count;
+}
+
+static ssize_t run_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int i;
+
+	mutex_lock(&dev->mutex);
+	i = snprintf(buf, PAGE_SIZE, "%d\n", run);
+	mutex_unlock(&dev->mutex);
+	return i;
+}
+
+static ssize_t run_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			 size_t count)
+{
+	int value;
+
+	mutex_lock(&dev->mutex);
+
+	if ((count == 0) || (buf == NULL)) {
+		mutex_unlock(&dev->mutex);
+		return -EINVAL;
+	}
+	if (kstrtoint(buf, 0, &value) != 0) {
+		mutex_unlock(&dev->mutex);
+		return -EINVAL;
+	}
+
+	switch (value) {
+	case 1:
+		if (run != 1) {
+			run = 1;
+			met_run();
+		}
+		break;
+	case 0:
+		if (run != 0) {
+			if (run == 1) {
+				met_stop();
+#ifdef MET_USER_EVENT_SUPPORT
+#ifdef CONFIG_MET_MODULE
+				met_save_dump_buffer_real("/data/trace.dump");
+#else
+				met_save_dump_buffer("/data/trace.dump");
+#endif
+#endif
+				run = 0;
+			} else
+				/* run == -1 */
+				run = 0;
+		}
+		break;
+	case -1:
+		if (run != -1) {
+			if (run == 1)
+				met_stop();
+
+			run = -1;
+		}
+		break;
+	default:
+		mutex_unlock(&dev->mutex);
+		return -EINVAL;
+	}
+
+	mutex_unlock(&dev->mutex);
+
+	return count;
+}
+
+static unsigned int met_ksym_addr;
+static char met_func_name[512];
+static ssize_t ksym_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int i;
+	int len = 0;
+	int idx = 0;
+
+	mutex_lock(&dev->mutex);
+	if (met_ksym_addr != 0)
+		len = sprint_symbol_no_offset(met_func_name, met_ksym_addr);
+	if (len != 0) {
+		for (idx = 0; idx < 512; idx++)
+			if (met_func_name[idx] == ' ')
+				met_func_name[idx] = '\0';
+		i = snprintf(buf, PAGE_SIZE, "%s\n", met_func_name);
+	} else
+		i = snprintf(buf, PAGE_SIZE, "ksymlookup fail(%x)\n", met_ksym_addr);
+
+	mutex_unlock(&dev->mutex);
+	return i;
+}
+
+static ssize_t ksym_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			 size_t count)
+{
+	mutex_lock(&dev->mutex);
+
+	if ((count == 0) || (buf == NULL)) {
+		mutex_unlock(&dev->mutex);
+		return -EINVAL;
+	}
+	if (kstrtoint(buf, 16, &met_ksym_addr) != 0) {
+		mutex_unlock(&dev->mutex);
+		return -EINVAL;
+	}
+
+	mutex_unlock(&dev->mutex);
+
+	return count;
+}
+
+#if	defined(PR_CPU_NOTIFY)
+static ssize_t cpu_notify_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int i;
+
+	i = snprintf(buf, PAGE_SIZE, "%d\n", met_cpu_notify);
+	return i;
+}
+
+static ssize_t cpu_notify_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			  size_t count)
+{
+	if ((count == 0) || (buf == NULL))
+		return -EINVAL;
+
+	if (kstrtoint(buf, 0, &met_cpu_notify) != 0)
+		return -EINVAL;
+
+	return count;
+}
+#endif
+
+#ifdef CONFIG_CPU_FREQ
+static ssize_t dvfs_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int i;
+
+	i = snprintf(buf, PAGE_SIZE, "%d\n", 0);
+	return i;
+}
+
+static ssize_t dvfs_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			  size_t count)
+{
+	return count;
+}
+#endif
+
+static ssize_t suspend_compensation_enable_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int ret;
+
+	ret = snprintf(buf, PAGE_SIZE, "%d\n", met_suspend_compensation_mode);
+
+	return ret;
+}
+
+static ssize_t suspend_compensation_enable_store(struct device *dev, struct device_attribute *attr,
+			const char *buf, size_t count)
+{
+	int value;
+
+	if ((count == 0) || (buf == NULL))
+		return -EINVAL;
+
+	if (kstrtoint(buf, 0, &value) != 0)
+		return -EINVAL;
+
+	if (value < 0)
+		return -EINVAL;
+
+	met_suspend_compensation_mode = value;
+
+	return count;
+}
+
+static ssize_t suspend_compensation_flag_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int ret;
+
+	ret = snprintf(buf, PAGE_SIZE, "%d\n", met_suspend_compensation_flag);
+
+	return ret;
+}
+
+static ssize_t hash_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	return 0;
+}
+
+static ssize_t hash_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			 size_t count)
+{
+	return 0;
+}
+
+static DEVICE_ATTR(hash, 0664, hash_show, hash_store);
+
+static ssize_t mode_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	struct metdevice *c = NULL;
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->kobj == kobj)
+			break;
+	}
+	if (c == NULL)
+		return -ENOENT;
+
+	return snprintf(buf, PAGE_SIZE, "%d\n", c->mode);
+}
+
+static ssize_t mode_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf,
+			  size_t n)
+{
+	struct metdevice *c = NULL;
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->kobj == kobj)
+			break;
+	}
+	if (c == NULL)
+		return -ENOENT;
+
+	if (kstrtoint(buf, 0, &(c->mode)) != 0)
+		return -EINVAL;
+
+	return n;
+}
+
+static struct kobj_attribute mode_attr = __ATTR(mode, 0664, mode_show, mode_store);
+
+static ssize_t ondiemet_mode_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	struct metdevice *c = NULL;
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->kobj == kobj)
+			break;
+	}
+	if (c == NULL)
+		return -ENOENT;
+
+	return snprintf(buf, PAGE_SIZE, "%d\n", c->ondiemet_mode);
+}
+
+static ssize_t ondiemet_mode_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf,
+			  size_t n)
+{
+	struct metdevice *c = NULL;
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->kobj == kobj)
+			break;
+	}
+	if (c == NULL)
+		return -ENOENT;
+
+	if (kstrtoint(buf, 0, &(c->ondiemet_mode)) != 0)
+		return -EINVAL;
+
+	return n;
+}
+
+static struct kobj_attribute ondiemet_mode_attr = __ATTR(ondiemet_mode, 0664, ondiemet_mode_show, ondiemet_mode_store);
+
+static ssize_t polling_interval_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	int interval = 1;
+	struct metdevice *c = NULL;
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->kobj == kobj)
+			break;
+	}
+	if (c == NULL)
+		return -ENOENT;
+
+	if (c->polling_interval)
+		interval = c->polling_interval;
+
+	return snprintf(buf, PAGE_SIZE, "%d\n", interval);
+}
+
+static ssize_t polling_interval_store(struct kobject *kobj, struct kobj_attribute *attr,
+				      const char *buf, size_t n)
+{
+	struct metdevice *c = NULL;
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->kobj == kobj)
+			break;
+	}
+	if (c == NULL)
+		return -ENOENT;
+
+	if (kstrtoint(buf, 0, &(c->polling_interval)) != 0)
+		return -EINVAL;
+
+	if (c->polling_interval > 0)
+		c->polling_count_reload = ((c->polling_interval * sample_rate) - 1) / 1000;
+	else
+		c->polling_count_reload = 0;
+
+	return n;
+}
+
+static struct kobj_attribute polling_interval_attr =
+__ATTR(polling_ms, 0664, polling_interval_show, polling_interval_store);
+
+static ssize_t header_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	struct metdevice *c = NULL;
+	ssize_t count = 0;
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->kobj == kobj)
+			break;
+	}
+	if (c == NULL)
+		return -ENOENT;
+
+	if (c->ondiemet_mode == 0) {
+		if ((c->mode) && (c->print_header))
+			return c->print_header(buf, PAGE_SIZE);
+	} else if (c->ondiemet_mode == 1) {
+		if ((c->mode) && (c->ondiemet_print_header))
+			return c->ondiemet_print_header(buf, PAGE_SIZE);
+	} else if (c->ondiemet_mode == 2) {
+		if ((c->mode) && (c->print_header))
+			count = c->print_header(buf, PAGE_SIZE);
+		if (count < PAGE_SIZE) {
+			if ((c->mode) && (c->ondiemet_print_header))
+				count += c->ondiemet_print_header(buf+count, PAGE_SIZE - count);
+		}
+		return count;
+	}
+
+	return 0;
+}
+
+static struct kobj_attribute header_attr = __ATTR(header, 0444, header_show, NULL);
+
+static ssize_t help_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	struct metdevice *c = NULL;
+	ssize_t count = 0;
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->kobj == kobj)
+			break;
+	}
+	if (c == NULL)
+		return -ENOENT;
+
+	if (c->ondiemet_mode == 0) {
+		if (c->print_help)
+			return c->print_help(buf, PAGE_SIZE);
+	} else if (c->ondiemet_mode == 1) {
+		if (c->ondiemet_print_help)
+			return c->ondiemet_print_help(buf, PAGE_SIZE);
+	} else if (c->ondiemet_mode == 2) {
+		if (c->print_help)
+			count = c->print_help(buf, PAGE_SIZE);
+		if (count < PAGE_SIZE) {
+			if (c->ondiemet_print_help)
+				count += c->ondiemet_print_help(buf+count, PAGE_SIZE - count);
+		}
+		return count;
+	}
+
+	return 0;
+}
+
+static struct kobj_attribute help_attr = __ATTR(help, 0444, help_show, NULL);
+
+static int argu_status = -1;
+static ssize_t argu_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf,
+			  size_t n)
+{
+	int ret = 0;
+	struct metdevice *c = NULL;
+
+	argu_status = -1;
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->kobj == kobj)
+			break;
+	}
+	if (c == NULL)
+		return -ENOENT;
+
+	if (c->ondiemet_mode == 0) {
+		if (c->process_argument)
+			ret = c->process_argument(buf, (int)n);
+	} else if (c->ondiemet_mode == 1) {
+		if (c->ondiemet_process_argument)
+			ret = c->ondiemet_process_argument(buf, (int)n);
+	} else if (c->ondiemet_mode == 2) {
+		if (c->process_argument)
+			ret = c->process_argument(buf, (int)n);
+		if (c->ondiemet_process_argument)
+			ret = c->ondiemet_process_argument(buf, (int)n);
+	}
+
+	if (ret != 0)
+		return -EINVAL;
+
+	argu_status = 0;
+	return n;
+}
+
+static ssize_t argu_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%d\n", argu_status);
+}
+
+static struct kobj_attribute argu_attr = __ATTR(argu, 0664, argu_show, argu_store);
+
+static ssize_t reset_store(struct kobject *kobj,
+			struct kobj_attribute *attr,
+			const char *buf,
+			size_t n)
+{
+	int ret = 0;
+	struct metdevice *c = NULL;
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->kobj == kobj)
+			break;
+	}
+	if (c == NULL)
+		return -ENOENT;
+
+	if (c->ondiemet_mode == 0) {
+		if (c->reset)
+			ret = c->reset();
+		else
+			c->mode = 0;
+	} else if (c->ondiemet_mode == 1) {
+		if (c->ondiemet_reset)
+			ret = c->ondiemet_reset();
+	} else if (c->ondiemet_mode == 2) {
+		if (c->reset)
+			ret = c->reset();
+		else
+			c->mode = 0;
+		if (c->ondiemet_reset)
+			ret = c->ondiemet_reset();
+	}
+
+	if (ret != 0)
+		return -EINVAL;
+
+	return n;
+}
+
+static struct kobj_attribute reset_attr = __ATTR(reset, 0220, NULL, reset_store);
+
+static ssize_t header_read_again_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	struct metdevice *c = NULL;
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->kobj == kobj)
+			break;
+	}
+	if (c == NULL)
+		return -ENOENT;
+
+	return snprintf(buf, PAGE_SIZE, "%d\n", c->header_read_again);
+}
+
+static struct kobj_attribute header_read_again_attr = __ATTR(header_read_again, 0664, header_read_again_show, NULL);
+
+
+int met_register(struct metdevice *met)
+{
+	int ret, cpu;
+	struct metdevice *c;
+
+	list_for_each_entry(c, &met_list, list) {
+		if (!strcmp(c->name, met->name))
+			return -EEXIST;
+	}
+
+	PR_BOOTMSG("met_register %s ...\n", met->name);
+
+	INIT_LIST_HEAD(&met->list);
+
+	/* Allocate timer count for per CPU */
+	met->polling_count = alloc_percpu(int);
+	if (met->polling_count == NULL)
+		return -EINVAL;
+
+	for_each_possible_cpu(cpu)
+		*(per_cpu_ptr(met->polling_count, cpu)) = 0;
+
+	if (met->polling_interval > 0) {
+		ret = ((met->polling_interval * sample_rate) - 1) / 1000;
+		met->polling_count_reload = ret;
+	} else
+		met->polling_count_reload = 0;
+
+	met->kobj = NULL;
+
+	if (met->type == MET_TYPE_BUS)
+		met->kobj = kobject_create_and_add(met->name, kobj_bus);
+	else if (met->type == MET_TYPE_PMU)
+		met->kobj = kobject_create_and_add(met->name, kobj_pmu);
+	else if (met->type == MET_TYPE_MISC)
+		met->kobj = kobject_create_and_add(met->name, kobj_misc);
+	else {
+		ret = -EINVAL;
+		goto err_out;
+	}
+
+	if (met->kobj == NULL) {
+		ret = -EINVAL;
+		goto err_out;
+	}
+
+	if (met->create_subfs) {
+		ret = met->create_subfs(met->kobj);
+		if (ret)
+			goto err_out;
+	}
+
+	ret = sysfs_create_file(met->kobj, &mode_attr.attr);
+	if (ret)
+		goto err_out;
+
+
+	ret = sysfs_create_file(met->kobj, &ondiemet_mode_attr.attr);
+	if (ret)
+		goto err_out;
+
+	ret = sysfs_create_file(met->kobj, &polling_interval_attr.attr);
+	if (ret)
+		goto err_out;
+
+	ret = sysfs_create_file(met->kobj, &header_read_again_attr.attr);
+	if (ret)
+		goto err_out;
+
+	if (met->print_header || met->ondiemet_print_header) {
+		ret = sysfs_create_file(met->kobj, &header_attr.attr);
+		if (ret)
+			goto err_out;
+	}
+
+	if (met->print_help || met->ondiemet_print_help) {
+		ret = sysfs_create_file(met->kobj, &help_attr.attr);
+		if (ret)
+			goto err_out;
+	}
+
+	if (met->process_argument || met->ondiemet_process_argument) {
+		ret = sysfs_create_file(met->kobj, &argu_attr.attr);
+		if (ret)
+			goto err_out;
+	}
+
+	if (met->reset) {
+		ret = sysfs_create_file(met->kobj, &reset_attr.attr);
+		if (ret)
+			goto err_out;
+	}
+
+	spin_lock_init(&met->my_lock);
+
+	list_add(&met->list, &met_list);
+	return 0;
+
+ err_out:
+
+	if (met->polling_count)
+		free_percpu(met->polling_count);
+
+	if (met->kobj) {
+		kobject_del(met->kobj);
+		kobject_put(met->kobj);
+		met->kobj = NULL;
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL(met_register);
+
+int met_deregister(struct metdevice *met)
+{
+	struct metdevice *c = NULL;
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c == met)
+			break;
+	}
+	if (c != met)
+		return -ENOENT;
+
+	if (met->print_header || met->ondiemet_print_header)
+		sysfs_remove_file(met->kobj, &header_attr.attr);
+
+	if (met->print_help || met->ondiemet_print_help)
+		sysfs_remove_file(met->kobj, &help_attr.attr);
+
+	if (met->process_argument || met->ondiemet_process_argument)
+		sysfs_remove_file(met->kobj, &argu_attr.attr);
+
+	sysfs_remove_file(met->kobj, &reset_attr.attr);
+	sysfs_remove_file(met->kobj, &header_read_again_attr.attr);
+	sysfs_remove_file(met->kobj, &polling_interval_attr.attr);
+	sysfs_remove_file(met->kobj, &mode_attr.attr);
+	sysfs_remove_file(met->kobj, &ondiemet_mode_attr.attr);
+
+	if (met->delete_subfs)
+		met->delete_subfs();
+
+	kobject_del(met->kobj);
+	kobject_put(met->kobj);
+	met->kobj = NULL;
+
+	if (met->polling_count)
+		free_percpu(met->polling_count);
+
+	list_del(&met->list);
+	return 0;
+}
+EXPORT_SYMBOL(met_deregister);
+
+int met_set_platform(const char *plf_name, int flag)
+{
+	strncpy(met_platform, plf_name, sizeof(met_platform) - 1);
+#if 0
+	int ret;
+
+	if (flag) {
+		ret = device_create_file(met_device.this_device, &dev_attr_plf);
+		if (ret != 0) {
+			pr_debug("can not create device file: plf\n");
+			return ret;
+		}
+		strncpy(met_platform, plf_name, sizeof(met_platform) - 1);
+	} else
+		device_remove_file(met_device.this_device, &dev_attr_plf);
+
+#endif
+	return 0;
+}
+EXPORT_SYMBOL(met_set_platform);
+
+int met_set_topology(const char *topology_name, int flag)
+{
+	strncpy(met_topology, topology_name, sizeof(met_topology) - 1);
+#if 0
+	int ret;
+
+	if (flag) {
+		ret = device_create_file(met_device.this_device, &dev_attr_core_topology);
+		if (ret != 0) {
+			pr_debug("can not create device file: topology\n");
+			return ret;
+		}
+		strncpy(met_topology, topology_name, sizeof(met_topology) - 1);
+	} else {
+		device_remove_file(met_device.this_device, &dev_attr_core_topology);
+	}
+#endif
+	return 0;
+}
+EXPORT_SYMBOL(met_set_topology);
+
+#include "met_struct.h"
+
+void force_sample(void *unused)
+{
+	int cpu;
+	unsigned long long stamp;
+	struct metdevice *c;
+	struct met_cpu_struct *met_cpu_ptr;
+
+	if ((run != 1) || (sample_rate == 0))
+		return;
+
+	/* to avoid met tag is coming after __met_hrtimer_stop and before run=-1 */
+	met_cpu_ptr = this_cpu_ptr(&met_cpu);
+	if (met_cpu_ptr->work_enabled == 0)
+		return;
+
+	cpu = smp_processor_id();
+
+	stamp = cpu_clock(cpu);
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->ondiemet_mode == 0) {
+			if ((c->mode != 0) && (c->tagged_polling != NULL))
+				c->tagged_polling(stamp, 0);
+		} else if (c->ondiemet_mode == 1) {
+			if ((c->mode != 0) && (c->ondiemet_tagged_polling != NULL))
+				c->ondiemet_tagged_polling(stamp, 0);
+		} else if (c->ondiemet_mode == 2) {
+			if ((c->mode != 0) && (c->tagged_polling != NULL))
+				c->tagged_polling(stamp, 0);
+			if ((c->mode != 0) && (c->ondiemet_tagged_polling != NULL))
+				c->ondiemet_tagged_polling(stamp, 0);
+		}
+	}
+}
+
+#define MET_SUSPEND_HAND
+#ifdef MET_SUSPEND_HAND
+static struct syscore_ops met_hrtimer_ops = {
+	.suspend = met_hrtimer_suspend,
+	.resume = met_hrtimer_resume,
+};
+#endif
+
+int fs_reg(void)
+{
+	int ret = 0;
+
+	ctrl_flags = 0;
+	met_mode = 0;
+
+#ifdef MET_SUSPEND_HAND
+	/* suspend/resume function handle register */
+	register_syscore_ops(&met_hrtimer_ops);
+#endif
+
+	calc_timer_value(sample_rate);
+
+	ret = misc_register(&met_device);
+	if (ret != 0) {
+		pr_debug("misc register failed\n");
+		return ret;
+	}
+
+	/* dma map config */
+	/* arch_setup_dma_ops(met_device.this_device, 0, 0, NULL, false); */
+	met_arch_setup_dma_ops_symbol(met_device.this_device);
+
+	ret = device_create_file(met_device.this_device, &dev_attr_ksym);
+	if (ret != 0) {
+		pr_debug("can not create device file: ksym\n");
+		return ret;
+	}
+
+	ret = device_create_file(met_device.this_device, &dev_attr_run);
+	if (ret != 0) {
+		pr_debug("can not create device file: run\n");
+		return ret;
+	}
+
+#if	defined(PR_CPU_NOTIFY)
+	ret = device_create_file(met_device.this_device, &dev_attr_cpu_notify);
+	if (ret != 0) {
+		pr_debug("can not create device file: cpu_notify\n");
+		return ret;
+	}
+#endif
+
+#ifdef CONFIG_CPU_FREQ
+	ret = device_create_file(met_device.this_device, &dev_attr_dvfs);
+	if (ret != 0) {
+		pr_debug("can not create device file: dvfs\n");
+		return ret;
+	}
+#endif
+
+	ret = device_create_file(met_device.this_device, &dev_attr_suspend_compensation_enable);
+	if (ret != 0) {
+		pr_debug("can not create device file: suspend_compensation_enable\n");
+		return ret;
+	}
+
+	ret = device_create_file(met_device.this_device, &dev_attr_suspend_compensation_flag);
+	if (ret != 0) {
+		pr_debug("can not create device file: suspend_compensation_enable\n");
+		return ret;
+	}
+
+	ret = device_create_file(met_device.this_device, &dev_attr_ver);
+	if (ret != 0) {
+		pr_debug("can not create device file: ver\n");
+		return ret;
+	}
+
+	ret = device_create_file(met_device.this_device, &dev_attr_devices);
+	if (ret != 0) {
+		pr_debug("can not create device file: devices\n");
+		return ret;
+	}
+
+	ret = device_create_file(met_device.this_device, &dev_attr_ctrl);
+	if (ret != 0) {
+		pr_debug("can not create device file: ctrl\n");
+		return ret;
+	}
+
+	ret = device_create_file(met_device.this_device, &dev_attr_cpu_pmu_method);
+	if (ret != 0) {
+		pr_debug("can not create device file: cpu_pmu_method\n");
+		return ret;
+	}
+
+	ret = device_create_file(met_device.this_device, &dev_attr_cpu_pm_pmu_reconfig);
+	if (ret != 0) {
+		pr_debug("can not create device file: cpu_pm_pmu_reconfig\n");
+		return ret;
+	}
+
+#if	defined(MET_BOOT_MSG)
+	ret = device_create_file(met_device.this_device, &dev_attr_bootmsg);
+	if (ret != 0) {
+		pr_debug("can not create device file: bootmsg\n");
+		return ret;
+	}
+#endif
+
+	ret = device_create_file(met_device.this_device, &dev_attr_sample_rate);
+	if (ret != 0) {
+		pr_debug("can not create device file: sample_rate\n");
+		return ret;
+	}
+
+	ret = device_create_file(met_device.this_device, &dev_attr_core_topology);
+	if (ret != 0) {
+		pr_debug("can not create device file: topology\n");
+		return ret;
+	}
+
+	ret = device_create_file(met_device.this_device, &dev_attr_plf);
+	if (ret != 0) {
+		pr_debug("can not create device file: plf\n");
+		return ret;
+	}
+
+	ret = device_create_file(met_device.this_device, &dev_attr_hash);
+	if (ret != 0) {
+		pr_debug("can not create device file: hash\n");
+		return ret;
+	}
+
+	ret = device_create_file(met_device.this_device, &dev_attr_ipi_test);
+	if (ret != 0) {
+		pr_debug("can not create device file: ipi_test\n");
+		return ret;
+	}
+
+	kobj_misc = kobject_create_and_add("misc", &met_device.this_device->kobj);
+	if (kobj_misc == NULL) {
+		pr_debug("can not create kobject: kobj_misc\n");
+		return ret;
+	}
+
+	kobj_pmu = kobject_create_and_add("pmu", &met_device.this_device->kobj);
+	if (kobj_pmu == NULL) {
+		pr_debug("can not create kobject: kobj_pmu\n");
+		return ret;
+	}
+
+	kobj_bus = kobject_create_and_add("bus", &met_device.this_device->kobj);
+	if (kobj_bus == NULL) {
+		pr_debug("can not create kobject: kobj_bus\n");
+		return ret;
+	}
+
+	met_register(&met_cookie);
+	met_register(&met_cpupmu);
+	met_register(&met_memstat);
+	met_register(&met_switch);
+#ifdef MET_EVENT_POWER_SUPPORT
+	met_register(&met_trace_event);
+	met_ext_api.met_pm_qos_update_request = pm_qos_update_request;
+	met_ext_api.met_pm_qos_update_target = pm_qos_update_target;
+#endif
+
+	met_register(&met_dummy_header);
+
+	met_register(&met_backlight);
+	met_ext_api.enable_met_backlight_tag = enable_met_backlight_tag_real;
+	met_ext_api.output_met_backlight_tag = output_met_backlight_tag_real;
+
+#ifdef MET_USER_EVENT_SUPPORT
+	tag_reg((struct file_operations * const) met_device.fops, &met_device.this_device->kobj);
+#endif
+
+	met_register(&met_stat);
+
+	ondiemet_log_manager_init(met_device.this_device);
+	ondiemet_attr_init(met_device.this_device);
+
+	return ret;
+}
+
+void fs_unreg(void)
+{
+	if (run == 1)
+		met_stop();
+
+	run = -1;
+
+	met_deregister(&met_stat);
+
+#ifdef MET_USER_EVENT_SUPPORT
+	tag_unreg();
+#endif
+
+	met_deregister(&met_dummy_header);
+#ifdef MET_EVENT_POWER_SUPPORT
+	met_deregister(&met_trace_event);
+	met_ext_api.met_pm_qos_update_request = NULL;
+	met_ext_api.met_pm_qos_update_target = NULL;
+#endif
+	met_deregister(&met_switch);
+	met_deregister(&met_memstat);
+	met_deregister(&met_cpupmu);
+	met_deregister(&met_cookie);
+	met_deregister(&met_backlight);
+	met_ext_api.enable_met_backlight_tag = NULL;
+	met_ext_api.output_met_backlight_tag = NULL;
+
+	kobject_del(kobj_misc);
+	kobject_put(kobj_misc);
+	kobj_misc = NULL;
+	kobject_del(kobj_pmu);
+	kobject_put(kobj_pmu);
+	kobj_pmu = NULL;
+	kobject_del(kobj_bus);
+	kobject_put(kobj_bus);
+	kobj_bus = NULL;
+
+	device_remove_file(met_device.this_device, &dev_attr_ksym);
+
+	device_remove_file(met_device.this_device, &dev_attr_run);
+#ifdef PR_CPU_NOTIFY
+	device_remove_file(met_device.this_device, &dev_attr_cpu_notify);
+#endif
+#ifdef CONFIG_CPU_FREQ
+	device_remove_file(met_device.this_device, &dev_attr_dvfs);
+#endif
+	device_remove_file(met_device.this_device, &dev_attr_suspend_compensation_enable);
+	device_remove_file(met_device.this_device, &dev_attr_suspend_compensation_flag);
+
+	device_remove_file(met_device.this_device, &dev_attr_ver);
+	device_remove_file(met_device.this_device, &dev_attr_devices);
+	device_remove_file(met_device.this_device, &dev_attr_sample_rate);
+
+	device_remove_file(met_device.this_device, &dev_attr_ctrl);
+	device_remove_file(met_device.this_device, &dev_attr_cpu_pmu_method);
+	device_remove_file(met_device.this_device, &dev_attr_cpu_pm_pmu_reconfig);
+
+	device_remove_file(met_device.this_device, &dev_attr_core_topology);
+	device_remove_file(met_device.this_device, &dev_attr_plf);
+	device_remove_file(met_device.this_device, &dev_attr_hash);
+	device_remove_file(met_device.this_device, &dev_attr_ipi_test);
+
+	ondiemet_log_manager_uninit(met_device.this_device);
+	ondiemet_attr_uninit(met_device.this_device);
+
+	misc_deregister(&met_device);
+#ifdef MET_SUSPEND_HAND
+	/* suspend/resume function handle register */
+	unregister_syscore_ops(&met_hrtimer_ops);
+#endif
+}
+
+unsigned int get_ctrl_flags(void)
+{
+	return ctrl_flags;
+}
diff --git a/src/devtools/met-driver/4.14/common/interface.h b/src/devtools/met-driver/4.14/common/interface.h
new file mode 100644
index 0000000..6cbbe6d
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/interface.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __INTERFACE_H__
+#define __INTERFACE_H__
+
+#include <linux/fs.h>
+
+#ifdef MET_USER_EVENT_SUPPORT
+extern int tag_reg(struct file_operations *const fops, struct kobject *kobj);
+extern int tag_unreg(void);
+#include "met_drv.h"
+#include "met_tag.h"
+extern struct bltable_t bltab;
+#endif
+
+extern struct metdevice met_stat;
+extern struct metdevice met_cpupmu;
+extern struct metdevice met_cookie;
+extern struct metdevice met_memstat;
+extern struct metdevice met_switch;
+extern struct metdevice met_trace_event;
+extern struct metdevice met_dummy_header;
+extern struct metdevice met_backlight;
+
+/* This variable will decide which method to access the CPU PMU counter */
+/*     0: access registers directly */
+/*     others: via Linux perf driver */
+extern unsigned int met_cpu_pmu_method;
+
+/*
+ * controls whether re-configuring pmu events after leaving cpu off state
+ */
+extern unsigned int met_cpu_pm_pmu_reconfig;
+
+extern int met_parse_num(const char *str, unsigned int *value, int len);
+extern void met_set_suspend_notify(int flag);
+
+#define	PR_CPU_NOTIFY
+#if	defined(PR_CPU_NOTIFY)
+extern int met_cpu_notify;
+#endif
+
+//#undef	MET_BOOT_MSG
+#define	MET_BOOT_MSG
+#if	defined(MET_BOOT_MSG)
+extern char met_boot_msg_tmp[256];
+extern int pr_bootmsg(int str_len, char *str);
+#define	PR_BOOTMSG(fmt, args...) { \
+	int str_len = snprintf(met_boot_msg_tmp, sizeof(met_boot_msg_tmp), \
+			       fmt, ##args); \
+	pr_bootmsg(str_len, met_boot_msg_tmp); }
+#define	PR_BOOTMSG_ONCE(fmt, args...) { \
+	static int once; \
+	if (!once) { \
+		int str_len = snprintf(met_boot_msg_tmp, \
+				       sizeof(met_boot_msg_tmp), \
+				       fmt, ##args); \
+		pr_bootmsg(str_len, met_boot_msg_tmp); \
+		once = 1; \
+	} }
+#else
+#define	pr_bootmsg(str_len, str)
+#define PR_BOOTMSG(fmt, args...)
+#define	PR_BOOTMSG_ONCE(fmt, args...)
+#endif
+
+#endif	/* __INTERFACE_H__ */
diff --git a/src/devtools/met-driver/4.14/common/mem_stat.c b/src/devtools/met-driver/4.14/common/mem_stat.c
new file mode 100644
index 0000000..2409e70
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/mem_stat.c
@@ -0,0 +1,313 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/cpu.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+
+#include "met_drv.h"
+#include "mem_stat.h"
+#include "trace.h"
+
+
+/* define MEMSTAT_DEBUG */
+#ifdef MEMSTAT_DEBUG
+#define debug_memstat(fmt, arg...) pr_debug(fmt, ##arg)
+#else
+#define debug_memstat(fmt, arg...) do {} while (0)
+#endif
+
+struct metdevice met_memstat;
+
+unsigned int phy_memstat_mask;
+unsigned int vir_memstat_mask;
+
+#define MAX_PHY_MEMSTAT_EVENT_AMOUNT 6
+#define MAX_VIR_MEMSTAT_EVENT_AMOUNT 6
+
+struct mem_event phy_memstat_table[] = {
+	{FREE_MEM, "free_mem", "Free Memory"}
+};
+
+#define PHY_MEMSTAT_TABLE_SIZE (sizeof(phy_memstat_table) / sizeof(struct mem_event))
+
+struct mem_event vir_memstat_table[] = {
+	{FILE_PAGES, "file_pages", "File Pages"},
+	{FILE_DIRTY, "file_dirty", "FD APP->FS(KB)"},
+	{NUM_DIRTIED, "num_dirtied", "Num Dirtied"},
+	{WRITE_BACK, "write_back", "WB. FS->Block IO(KB)"},
+	{NUM_WRITTEN, "num_written", "Num Written"},
+	{PG_FAULT_CNT, "pg_fault_cnt", "Page Fault Count"}
+};
+
+#define VIR_MEMSTAT_TABLE_SIZE (sizeof(vir_memstat_table) / sizeof(struct mem_event))
+
+int vm_event_counters_enable;
+unsigned long *vm_status;
+static struct delayed_work dwork;
+
+noinline void memstat(unsigned int cnt, unsigned int *value)
+{
+	MET_GENERAL_PRINT(MET_TRACE, cnt, value);
+}
+
+static int get_phy_memstat(unsigned int *value)
+{
+	int i, cnt = 0;
+	struct sysinfo info;
+
+#define K(x) ((x) << (PAGE_SHIFT - 10))
+
+	si_meminfo(&info);
+
+	for (i = 0; i < MAX_PHY_MEMSTAT_EVENT_AMOUNT; i++) {
+		if (phy_memstat_mask & (1 << i)) {
+			switch (i) {
+			case FREE_MEM:
+				value[cnt] = K(info.freeram);
+				break;
+			}
+
+			cnt++;
+		}
+	}
+
+	return cnt;
+}
+
+static int get_vir_memstat(unsigned int *value)
+{
+	int i, cnt = 0;
+
+	for (i = 0; i < NR_VM_ZONE_STAT_ITEMS; i++)
+		vm_status[i] = global_zone_page_state(i);
+
+	all_vm_events(vm_status + NR_VM_ZONE_STAT_ITEMS);
+
+	for (i = 0; i < MAX_VIR_MEMSTAT_EVENT_AMOUNT; i++) {
+		if (vir_memstat_mask & (1 << i)) {
+			switch (i) {
+			case FILE_PAGES:
+				value[cnt] = vm_status[NR_FILE_PAGES] << (PAGE_SHIFT - 10);
+				break;
+			case FILE_DIRTY:
+				value[cnt] = vm_status[NR_FILE_DIRTY] << (PAGE_SHIFT - 10);
+				break;
+			case NUM_DIRTIED:
+				value[cnt] = vm_status[NR_DIRTIED] << (PAGE_SHIFT - 10);
+				break;
+			case WRITE_BACK:
+				value[cnt] = vm_status[NR_WRITEBACK] << (PAGE_SHIFT - 10);
+				break;
+			case NUM_WRITTEN:
+				value[cnt] = vm_status[NR_WRITTEN] << (PAGE_SHIFT - 10);
+				break;
+			case PG_FAULT_CNT:
+				value[cnt] = vm_status[NR_VM_ZONE_STAT_ITEMS + PGFAULT];
+				break;
+			}
+
+			cnt++;
+		}
+	}
+
+	return cnt;
+}
+
+static void wq_get_memstat(struct work_struct *work)
+{
+	int total_event_amount = 0, phy_event_amount = 0;
+	unsigned int stat_val[MAX_PHY_MEMSTAT_EVENT_AMOUNT + MAX_VIR_MEMSTAT_EVENT_AMOUNT];
+
+	memset(stat_val, 0, sizeof(unsigned int) * (MAX_PHY_MEMSTAT_EVENT_AMOUNT + MAX_VIR_MEMSTAT_EVENT_AMOUNT));
+	total_event_amount = phy_event_amount = get_phy_memstat(stat_val);
+
+	if (vm_event_counters_enable)
+		total_event_amount += get_vir_memstat(&(stat_val[phy_event_amount]));
+
+	if (total_event_amount <= (MAX_PHY_MEMSTAT_EVENT_AMOUNT + MAX_VIR_MEMSTAT_EVENT_AMOUNT))
+		memstat(total_event_amount-1, stat_val);
+}
+
+void met_memstat_polling(unsigned long long stamp, int cpu)
+{
+	schedule_delayed_work(&dwork, 0);
+}
+
+static void met_memstat_start(void)
+{
+	int stat_items_size = 0;
+
+	stat_items_size = NR_VM_ZONE_STAT_ITEMS * sizeof(unsigned long);
+
+#ifdef CONFIG_VM_EVENT_COUNTERS
+	stat_items_size += sizeof(struct vm_event_state);
+#endif
+
+	vm_status = kmalloc(stat_items_size, GFP_KERNEL);
+	if (vm_status == NULL)
+		return;
+	INIT_DELAYED_WORK(&dwork, wq_get_memstat);
+}
+
+static void met_memstat_stop(void)
+{
+	kfree(vm_status);
+	cancel_delayed_work_sync(&dwork);
+}
+
+static const char help[] =
+"  --memstat=[phy_mem_stat|vir_mem_stat]:event_name enable sampling physical & virtual memory status\n";
+
+static int met_memstat_print_help(char *buf, int len)
+{
+	int i, l;
+
+	l = snprintf(buf, PAGE_SIZE, help);
+
+	for (i = 0; i < PHY_MEMSTAT_TABLE_SIZE; i++)
+		l += snprintf(buf + l, PAGE_SIZE - l, "  --memstat=phy_mem_stat:%s\n",
+			      phy_memstat_table[i].name);
+
+#ifdef CONFIG_VM_EVENT_COUNTERS
+	for (i = 0; i < VIR_MEMSTAT_TABLE_SIZE; i++)
+		l += snprintf(buf + l, PAGE_SIZE - l, "  --memstat=vir_mem_stat:%s\n",
+			      vir_memstat_table[i].name);
+#endif
+
+	return l;
+}
+
+static const char header[] = "met-info [000] 0.0: ms_ud_sys_header: memstat,";
+
+
+static int met_memstat_print_header(char *buf, int len)
+{
+	int i, l;
+	int event_amount = 0;
+
+	l = snprintf(buf, PAGE_SIZE, header);
+
+	for (i = 0; i < MAX_PHY_MEMSTAT_EVENT_AMOUNT; i++) {
+		if ((phy_memstat_mask & (1 << i)) && (i < PHY_MEMSTAT_TABLE_SIZE)) {
+			l += snprintf(buf + l, PAGE_SIZE - l, phy_memstat_table[i].header_name);
+			l += snprintf(buf + l, PAGE_SIZE - l, ",");
+			event_amount++;
+		}
+	}
+
+#ifdef CONFIG_VM_EVENT_COUNTERS
+	for (i = 0; i < MAX_VIR_MEMSTAT_EVENT_AMOUNT; i++) {
+		if ((vir_memstat_mask & (1 << i)) && (i < VIR_MEMSTAT_TABLE_SIZE)) {
+			l += snprintf(buf + l, PAGE_SIZE - l, vir_memstat_table[i].header_name);
+			l += snprintf(buf + l, PAGE_SIZE - l, ",");
+			event_amount++;
+		}
+	}
+#endif
+
+	for (i = 0; i < event_amount; i++) {
+		l += snprintf(buf + l, PAGE_SIZE - l, "x");
+		l += snprintf(buf + l, PAGE_SIZE - l, ",");
+	}
+
+	phy_memstat_mask = 0;
+	vir_memstat_mask = 0;
+
+	l += snprintf(buf + l, PAGE_SIZE - l, "\n");
+
+	return l;
+}
+
+static int met_memstat_process_argument(const char *arg, int len)
+{
+	int i, found_event = 0;
+	char choice[16], event[32];
+	char *pch;
+	int str_len;
+
+
+#ifdef CONFIG_VM_EVENT_COUNTERS
+	vm_event_counters_enable = 1;
+#endif
+
+	pch = strchr(arg, ':');
+	if (pch == NULL)
+		goto error;
+
+	memset(choice, 0, sizeof(choice));
+	memset(event, 0, sizeof(event));
+
+	str_len = (int)(pch - arg);
+	memcpy(choice, arg, str_len);
+	memcpy(event, arg + str_len + 1, len - (str_len + 1));
+
+	if (strncmp(choice, "phy_mem_stat", 12) == 0) {
+		for (i = 0; i < PHY_MEMSTAT_TABLE_SIZE; i++) {
+			if (strncmp(event, phy_memstat_table[i].name, MAX_EVENT_NAME_LEN) == 0) {
+				phy_memstat_mask |= (1 << phy_memstat_table[i].id);
+				found_event = 1;
+
+				break;
+			}
+		}
+	} else if (strncmp(choice, "vir_mem_stat", 12) == 0) {
+		if (!vm_event_counters_enable) {
+			pr_debug("[%s] %d: CONFIG_VM_EVENT_COUNTERS is not configured\n", __func__,
+				 __LINE__);
+			goto error;
+		}
+
+		for (i = 0; i < VIR_MEMSTAT_TABLE_SIZE; i++) {
+			if (strncmp(event, vir_memstat_table[i].name, MAX_EVENT_NAME_LEN) == 0) {
+				vir_memstat_mask |= (1 << vir_memstat_table[i].id);
+				found_event = 1;
+
+				break;
+			}
+		}
+	} else {
+		pr_debug("[%s] %d: only support phy_mem_stat & vir_mem_stat keyword\n", __func__,
+			 __LINE__);
+		goto error;
+	}
+
+	if (!found_event) {
+		pr_debug("[%s] %d: input event name error\n", __func__, __LINE__);
+		goto error;
+	}
+
+	met_memstat.mode = 1;
+	return 0;
+
+error:
+	met_memstat.mode = 0;
+	return -EINVAL;
+}
+
+struct metdevice met_memstat = {
+	.name = "memstat",
+	.type = MET_TYPE_PMU,
+	.cpu_related = 0,
+	.start = met_memstat_start,
+	.stop = met_memstat_stop,
+	.polling_interval = 1,
+	.timed_polling = met_memstat_polling,
+	.tagged_polling = met_memstat_polling,
+	.print_help = met_memstat_print_help,
+	.print_header = met_memstat_print_header,
+	.process_argument = met_memstat_process_argument
+};
diff --git a/src/devtools/met-driver/4.14/common/mem_stat.h b/src/devtools/met-driver/4.14/common/mem_stat.h
new file mode 100644
index 0000000..d716075
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/mem_stat.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __MEM_STAT_H__
+#define __MEM_STAT_H__
+
+#define MAX_EVENT_NAME_LEN 32
+
+enum phy_mem_event_id {
+	FREE_MEM = 0
+};
+
+enum vir_mem_event_id {
+	FILE_PAGES = 0,
+	FILE_DIRTY,
+	NUM_DIRTIED,
+	WRITE_BACK,
+	NUM_WRITTEN,
+	PG_FAULT_CNT
+};
+
+struct mem_event {
+	int id;
+	char name[32];
+	char header_name[32];
+};
+
+#endif
diff --git a/src/devtools/met-driver/4.14/common/met_api_tbl.h b/src/devtools/met-driver/4.14/common/met_api_tbl.h
new file mode 100644
index 0000000..1119ac4
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/met_api_tbl.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+struct met_api_tbl {
+	int (*met_tag_start)(unsigned int class_id, const char *name);
+	int (*met_tag_end)(unsigned int class_id, const char *name);
+	int (*met_tag_async_start)(unsigned int class_id, const char *name, unsigned int cookie);
+	int (*met_tag_async_end)(unsigned int class_id, const char *name, unsigned int cookie);
+	int (*met_tag_oneshot)(unsigned int class_id, const char *name, unsigned int value);
+	int (*met_tag_userdata)(char *pData);
+	int (*met_tag_dump)(unsigned int class_id, const char *name, void *data, unsigned int length);
+	int (*met_tag_disable)(unsigned int class_id);
+	int (*met_tag_enable)(unsigned int class_id);
+	int (*met_set_dump_buffer)(int size);
+	int (*met_save_dump_buffer)(const char *pathname);
+	int (*met_save_log)(const char *pathname);
+	int (*met_show_bw_limiter)(void);
+	int (*met_reg_bw_limiter)(void *fp);
+	int (*met_show_clk_tree)(const char *name, unsigned int addr, unsigned int status);
+	int (*met_reg_clk_tree)(void *fp);
+	void (*met_sched_switch)(struct task_struct *prev, struct task_struct *next);
+	void (*met_pm_qos_update_request)(int pm_qos_class, s32 value, char *owner);
+	void (*met_pm_qos_update_target)(unsigned int action, int prev_value, int curr_value);
+	int (*enable_met_backlight_tag)(void);
+	int (*output_met_backlight_tag)(int level);
+};
+
+extern struct met_api_tbl met_ext_api;
diff --git a/src/devtools/met-driver/4.14/common/met_backlight.c b/src/devtools/met-driver/4.14/common/met_backlight.c
new file mode 100644
index 0000000..ffaa08d
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/met_backlight.c
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+#define MET_USER_EVENT_SUPPORT
+#include "met_drv.h"
+#include "trace.h"
+
+static int met_backlight_enable;
+static DEFINE_SPINLOCK(met_backlight_lock);
+static struct kobject *kobj_met_backlight;
+
+static ssize_t bl_tag_enable_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf);
+static ssize_t bl_tag_enable_store(struct kobject *kobj,
+				   struct kobj_attribute *attr, const char *buf, size_t n);
+static struct kobj_attribute bl_tag_enable_attr =
+__ATTR(backlight_tag_enable, 0664, bl_tag_enable_show, bl_tag_enable_store);
+
+int enable_met_backlight_tag_real(void)
+{
+	return met_backlight_enable;
+}
+
+int output_met_backlight_tag_real(int level)
+{
+	int ret;
+	unsigned long flags;
+
+	spin_lock_irqsave(&met_backlight_lock, flags);
+#ifdef CONFIG_MET_MODULE
+	ret = met_tag_oneshot_real(33880, "_MM_BL_", level);
+#else
+	ret = met_tag_oneshot(33880, "_MM_BL_", level);
+#endif
+	spin_unlock_irqrestore(&met_backlight_lock, flags);
+
+	return ret;
+}
+
+static ssize_t bl_tag_enable_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	int ret;
+
+	ret = snprintf(buf, PAGE_SIZE, "%d\n", met_backlight_enable);
+
+	return ret;
+}
+
+static ssize_t bl_tag_enable_store(struct kobject *kobj,
+				   struct kobj_attribute *attr, const char *buf, size_t n)
+{
+	int value;
+
+	if ((n == 0) || (buf == NULL))
+		return -EINVAL;
+
+	if (kstrtoint(buf, 0, &value) != 0)
+		return -EINVAL;
+
+	if (value < 0)
+		return -EINVAL;
+
+	met_backlight_enable = value;
+
+	return n;
+}
+
+static int met_backlight_create(struct kobject *parent)
+{
+	int ret = 0;
+
+	kobj_met_backlight = parent;
+
+	ret = sysfs_create_file(kobj_met_backlight, &bl_tag_enable_attr.attr);
+	if (ret != 0) {
+		pr_debug("Failed to create montype0 in sysfs\n");
+		return ret;
+	}
+
+	return ret;
+}
+
+static void met_backlight_delete(void)
+{
+	sysfs_remove_file(kobj_met_backlight, &bl_tag_enable_attr.attr);
+	kobj_met_backlight = NULL;
+}
+
+struct metdevice met_backlight = {
+	.name = "backlight",
+	.owner = THIS_MODULE,
+	.type = MET_TYPE_BUS,
+	.create_subfs = met_backlight_create,
+	.delete_subfs = met_backlight_delete,
+	.cpu_related = 0,
+};
+EXPORT_SYMBOL(met_backlight);
diff --git a/src/devtools/met-driver/4.14/common/met_drv.h b/src/devtools/met-driver/4.14/common/met_drv.h
new file mode 100644
index 0000000..bfe6490
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/met_drv.h
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+
+#ifndef MET_DRV
+#define MET_DRV
+
+#include <linux/version.h>
+#include <linux/preempt.h>
+#include <linux/device.h>
+#include <linux/percpu.h>
+#include <linux/hardirq.h>
+#include <linux/clk.h>
+
+extern int met_mode;
+extern int core_plf_init(void);
+extern void core_plf_exit(void);
+
+#define MET_MODE_TRACE_CMD_OFFSET	(1)
+#define MET_MODE_TRACE_CMD			(1<<MET_MODE_TRACE_CMD_OFFSET)
+
+#ifdef CONFIG_MET_MODULE
+#define my_preempt_enable() preempt_enable()
+#else
+#define my_preempt_enable() preempt_enable_no_resched()
+#endif
+
+#define MET_STRBUF_SIZE		1024
+DECLARE_PER_CPU(char[MET_STRBUF_SIZE], met_strbuf_nmi);
+DECLARE_PER_CPU(char[MET_STRBUF_SIZE], met_strbuf_irq);
+DECLARE_PER_CPU(char[MET_STRBUF_SIZE], met_strbuf_sirq);
+DECLARE_PER_CPU(char[MET_STRBUF_SIZE], met_strbuf);
+
+#ifdef CONFIG_TRACING
+#define TRACE_PUTS(p) \
+	do { \
+		trace_puts(p);; \
+	} while (0)
+#else
+#define TRACE_PUTS(p) do {} while (0)
+#endif
+
+#define GET_MET_TRACE_BUFFER_ENTER_CRITICAL() \
+	({ \
+		char *pmet_strbuf; \
+		preempt_disable(); \
+		if (in_nmi()) \
+			pmet_strbuf = per_cpu(met_strbuf_nmi, smp_processor_id()); \
+		else if (in_irq()) \
+			pmet_strbuf = per_cpu(met_strbuf_irq, smp_processor_id()); \
+		else if (in_softirq()) \
+			pmet_strbuf = per_cpu(met_strbuf_sirq, smp_processor_id()); \
+		else \
+			pmet_strbuf = per_cpu(met_strbuf, smp_processor_id()); \
+		pmet_strbuf;\
+	})
+
+#define PUT_MET_TRACE_BUFFER_EXIT_CRITICAL(pmet_strbuf) \
+	do {\
+		if (pmet_strbuf)\
+			TRACE_PUTS(pmet_strbuf); \
+		my_preempt_enable(); \
+	} while (0)
+
+#define MET_TRACE(FORMAT, args...) \
+	do { \
+		char *pmet_strbuf; \
+		preempt_disable(); \
+		if (in_nmi()) \
+			pmet_strbuf = per_cpu(met_strbuf_nmi, smp_processor_id()); \
+		else if (in_irq()) \
+			pmet_strbuf = per_cpu(met_strbuf_irq, smp_processor_id()); \
+		else if (in_softirq()) \
+			pmet_strbuf = per_cpu(met_strbuf_sirq, smp_processor_id()); \
+		else \
+			pmet_strbuf = per_cpu(met_strbuf, smp_processor_id()); \
+		if (met_mode & MET_MODE_TRACE_CMD) \
+			snprintf(pmet_strbuf, MET_STRBUF_SIZE, "%s: " FORMAT, __func__, ##args); \
+		else \
+			snprintf(pmet_strbuf, MET_STRBUF_SIZE, FORMAT, ##args); \
+		TRACE_PUTS(pmet_strbuf); \
+		my_preempt_enable(); \
+	} while (0)
+
+/*
+ * SOB: start of buf
+ * EOB: end of buf
+ */
+#define MET_TRACE_GETBUF(pSOB, pEOB) \
+	({ \
+		preempt_disable(); \
+		if (in_nmi()) \
+			*pSOB = per_cpu(met_strbuf_nmi, smp_processor_id()); \
+		else if (in_irq()) \
+			*pSOB = per_cpu(met_strbuf_irq, smp_processor_id()); \
+		else if (in_softirq()) \
+			*pSOB = per_cpu(met_strbuf_sirq, smp_processor_id()); \
+		else \
+			*pSOB = per_cpu(met_strbuf, smp_processor_id()); \
+		*pEOB = *pSOB; \
+		if (met_mode & MET_MODE_TRACE_CMD) \
+			*pEOB += snprintf(*pEOB, MET_STRBUF_SIZE, "%s: ", __func__); \
+	})
+
+#define MET_TRACE_PUTBUF(SOB, EOB) \
+	({ \
+		__trace_puts(_THIS_IP_, (SOB), (uintptr_t)((EOB)-(SOB))); \
+		my_preempt_enable(); \
+	})
+
+#define MET_FTRACE_DUMP(TRACE_NAME, args...)			\
+	do {							\
+		trace_##TRACE_NAME(args);;			\
+	} while (0)
+
+
+#define MET_TYPE_PMU	1
+#define MET_TYPE_BUS	2
+#define MET_TYPE_MISC	3
+
+enum met_action {
+	MET_CPU_ONLINE,
+	MET_CPU_OFFLINE,
+
+	NR_MET_ACTION,
+};
+
+struct metdevice {
+	struct list_head list;
+	int type;
+	const char *name;
+	struct module *owner;
+	struct kobject *kobj;
+
+	int (*create_subfs)(struct kobject *parent);
+	void (*delete_subfs)(void);
+	int mode;
+	int ondiemet_mode;	/* new for ondiemet; 1: call ondiemet functions */
+	int cpu_related;
+	int polling_interval;
+	int polling_count_reload;
+	int __percpu *polling_count;
+	int header_read_again;	/*for header size > 1 page */
+	void (*start)(void);
+	void (*uniq_start)(void);
+	void (*stop)(void);
+	void (*uniq_stop)(void);
+	int (*reset)(void);
+	void (*timed_polling)(unsigned long long stamp, int cpu);
+	void (*tagged_polling)(unsigned long long stamp, int cpu);
+	int (*print_help)(char *buf, int len);
+	int (*print_header)(char *buf, int len);
+	int (*process_argument)(const char *arg, int len);
+	void (*cpu_state_notify)(long cpu, unsigned long action);
+
+	void (*ondiemet_start)(void);
+	void (*ondiemet_stop)(void);
+	int (*ondiemet_reset)(void);
+	int (*ondiemet_print_help)(char *buf, int len);
+	int (*ondiemet_print_header)(char *buf, int len);
+	int (*ondiemet_process_argument)(const char *arg, int len);
+	void (*ondiemet_timed_polling)(unsigned long long stamp, int cpu);
+	void (*ondiemet_tagged_polling)(unsigned long long stamp, int cpu);
+
+	struct list_head exlist;	/* for linked list before register */
+	void (*suspend)(void);
+	void (*resume)(void);
+
+	unsigned long long prev_stamp;
+	spinlock_t my_lock;
+	void *reversed1;
+};
+
+int met_register(struct metdevice *met);
+int met_deregister(struct metdevice *met);
+int met_set_platform(const char *plf_name, int flag);
+int met_set_topology(const char *topology_name, int flag);
+int met_devlink_add(struct metdevice *met);
+int met_devlink_del(struct metdevice *met);
+int met_devlink_register_all(void);
+int met_devlink_deregister_all(void);
+
+int fs_reg(void);
+void fs_unreg(void);
+
+/******************************************************************************
+ * Tracepoints
+ ******************************************************************************/
+#define MET_DEFINE_PROBE(probe_name, proto) \
+		static void probe_##probe_name(void *data, PARAMS(proto))
+#define MET_REGISTER_TRACE(probe_name) \
+		register_trace_##probe_name(probe_##probe_name, NULL)
+#define MET_UNREGISTER_TRACE(probe_name) \
+		unregister_trace_##probe_name(probe_##probe_name, NULL)
+
+
+/* ====================== Tagging API ================================ */
+
+#define MAX_EVENT_CLASS	31
+#define MAX_TAGNAME_LEN	128
+#define MET_CLASS_ALL	0x80000000
+
+/* IOCTL commands of MET tagging */
+struct mtag_cmd_t {
+	unsigned int class_id;
+	unsigned int value;
+	unsigned int slen;
+	char tname[MAX_TAGNAME_LEN];
+	void *data;
+	unsigned int size;
+};
+
+#define TYPE_START		1
+#define TYPE_END		2
+#define TYPE_ONESHOT		3
+#define TYPE_ENABLE		4
+#define TYPE_DISABLE		5
+#define TYPE_REC_SET		6
+#define TYPE_DUMP		7
+#define TYPE_DUMP_SIZE		8
+#define TYPE_DUMP_SAVE		9
+#define TYPE_USRDATA		10
+#define TYPE_DUMP_AGAIN		11
+#define TYPE_ASYNC_START	12
+#define TYPE_ASYNC_END		13
+#define TYPE_MET_SUSPEND	15
+#define TYPE_MET_RESUME		16
+
+/* Use 'm' as magic number */
+#define MTAG_IOC_MAGIC  'm'
+/* Please use a different 8-bit number in your code */
+#define MTAG_CMD_START		_IOW(MTAG_IOC_MAGIC, TYPE_START, struct mtag_cmd_t)
+#define MTAG_CMD_END		_IOW(MTAG_IOC_MAGIC, TYPE_END, struct mtag_cmd_t)
+#define MTAG_CMD_ONESHOT	_IOW(MTAG_IOC_MAGIC, TYPE_ONESHOT, struct mtag_cmd_t)
+#define MTAG_CMD_ENABLE		_IOW(MTAG_IOC_MAGIC, TYPE_ENABLE, int)
+#define MTAG_CMD_DISABLE	_IOW(MTAG_IOC_MAGIC, TYPE_DISABLE, int)
+#define MTAG_CMD_REC_SET	_IOW(MTAG_IOC_MAGIC, TYPE_REC_SET, int)
+#define MTAG_CMD_DUMP		_IOW(MTAG_IOC_MAGIC, TYPE_DUMP, struct mtag_cmd_t)
+#define MTAG_CMD_DUMP_SIZE	_IOWR(MTAG_IOC_MAGIC, TYPE_DUMP_SIZE, int)
+#define MTAG_CMD_DUMP_SAVE	_IOW(MTAG_IOC_MAGIC, TYPE_DUMP_SAVE, struct mtag_cmd_t)
+#define MTAG_CMD_USRDATA	_IOW(MTAG_IOC_MAGIC, TYPE_USRDATA, struct mtag_cmd_t)
+#define MTAG_CMD_DUMP_AGAIN	_IOW(MTAG_IOC_MAGIC, TYPE_DUMP_AGAIN, void *)
+#define MTAG_CMD_ASYNC_START	_IOW(MTAG_IOC_MAGIC, TYPE_ASYNC_START, struct mtag_cmd_t)
+#define MTAG_CMD_ASYNC_END	_IOW(MTAG_IOC_MAGIC, TYPE_ASYNC_END, struct mtag_cmd_t)
+
+/* include file */
+extern int met_tag_start_real(unsigned int class_id, const char *name);
+extern int met_tag_end_real(unsigned int class_id, const char *name);
+extern int met_tag_async_start_real(unsigned int class_id, const char *name, unsigned int cookie);
+extern int met_tag_async_end_real(unsigned int class_id, const char *name, unsigned int cookie);
+extern int met_tag_oneshot_real(unsigned int class_id, const char *name, unsigned int value);
+extern int met_tag_userdata_real(char *pData);
+extern int met_tag_dump_real(unsigned int class_id, const char *name, void *data, unsigned int length);
+extern int met_tag_disable_real(unsigned int class_id);
+extern int met_tag_enable_real(unsigned int class_id);
+extern int met_set_dump_buffer_real(int size);
+extern int met_save_dump_buffer_real(const char *pathname);
+extern int met_save_log_real(const char *pathname);
+extern int met_show_bw_limiter_real(void);
+extern int met_reg_bw_limiter_real(void *fp);
+extern int met_show_clk_tree_real(const char *name, unsigned int addr, unsigned int status);
+extern int met_reg_clk_tree_real(void *fp);
+extern int enable_met_backlight_tag_real(void);
+extern int output_met_backlight_tag_real(int level);
+
+#endif	/* MET_DRV */
diff --git a/src/devtools/met-driver/4.14/common/met_emi.c b/src/devtools/met-driver/4.14/common/met_emi.c
new file mode 100644
index 0000000..9770cc9
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/met_emi.c
@@ -0,0 +1,950 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/device.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/string.h>
+
+#ifdef CONFIG_ARM
+//#include <asm/dma-mapping.h> /* arm_coherent_dma_ops */
+#endif /* CONFIG_ARM */
+
+#include <linux/dma-mapping.h>
+
+#define MET_USER_EVENT_SUPPORT
+#include "met_drv.h"
+#include "trace.h"
+
+#include "mtk_typedefs.h"
+#include "core_plf_init.h"
+/* #include "plf_trace.h" */
+#include "mtk_emi_bm.h"
+#include "interface.h"
+
+#if defined(CONFIG_MTK_TINYSYS_SSPM_SUPPORT) && defined(ONDIEMET_SUPPORT)
+#include "sspm/ondiemet_sspm.h"
+#endif
+
+/*======================================================================*/
+/*	Global variable definitions					*/
+/*======================================================================*/
+int emi_TP_busfiltr_enable;
+static int msel_enable;
+static unsigned int msel_group1 = _GP_1_Default;
+static unsigned int msel_group2 = _GP_2_Default;
+static unsigned int msel_group3 = _GP_3_Default;
+
+/* Global variables */
+static struct kobject *kobj_emi;
+
+static int ttype1_16_en = BM_TTYPE1_16_DISABLE;
+static int ttype17_21_en = BM_TTYPE17_21_DISABLE;
+
+
+
+static int times;
+static ssize_t test_store(struct kobject *kobj,
+				struct kobj_attribute *attr,
+				const char *buf,
+				size_t n)
+{
+	int i;
+	unsigned int    *src_addr_v = NULL;
+	dma_addr_t src_addr_p;
+#ifdef CONFIG_ARM
+	struct dma_map_ops *ops = (struct dma_map_ops *)symbol_get(arm_dma_ops);
+#endif /* CONFIG_ARM */
+
+	if ((n == 0) || (buf == NULL))
+		return -EINVAL;
+	if (kstrtoint(buf, 10, &times) != 0)
+		return -EINVAL;
+	if (times < 0)
+		return -EINVAL;
+
+	if (times > 5000)
+		return -EINVAL;
+
+#ifdef CONFIG_ARM
+	if (ops && ops->alloc) {
+		(met_device.this_device)->coherent_dma_mask = DMA_BIT_MASK(32);
+		src_addr_v = ops->alloc(met_device.this_device,
+						PAGE_SIZE,
+						&src_addr_p,
+						GFP_KERNEL,
+						0);
+	}
+#endif /* CONFIG_ARM */
+
+#ifdef CONFIG_ARM64
+	/* dma_alloc */
+	src_addr_v = dma_alloc_coherent(met_device.this_device,
+					PAGE_SIZE,
+					&src_addr_p,
+					GFP_KERNEL);
+#endif /* CONFIG_ARM64 */
+
+	if (src_addr_v == NULL) {
+#ifdef CONFIG_MET_MODULE
+		met_tag_oneshot_real(0, "test dma alloc fail", PAGE_SIZE);
+#else
+		met_tag_oneshot(0, "test dma alloc fail", PAGE_SIZE);
+#endif
+		return -ENOMEM;
+	}
+
+	preempt_disable();
+#ifdef CONFIG_MET_MODULE
+	met_tag_start_real(0, "TEST_EMI");
+#else
+	met_tag_start(0, "TEST_EMI");
+#endif
+	for (i = 0; i < times; i++) {
+		memset(src_addr_v, 2*i, PAGE_SIZE);
+#ifdef CONFIG_MET_MODULE
+		met_tag_oneshot_real(0, "TEST_EMI", PAGE_SIZE);
+#else
+		met_tag_oneshot(0, "TEST_EMI", PAGE_SIZE);
+#endif
+	}
+#ifdef CONFIG_MET_MODULE
+	met_tag_end_real(0, "TEST_EMI");
+#else
+	met_tag_end(0, "TEST_EMI");
+#endif
+
+	my_preempt_enable();
+
+#ifdef CONFIG_ARM
+	/* dma_free */
+	if (ops && ops->free) {
+		ops->free(met_device.this_device,
+				  PAGE_SIZE,
+				  src_addr_v,
+				  src_addr_p,
+			0);
+	}
+#endif /* CONFIG_ARM */
+
+#ifdef CONFIG_ARM64
+	/* dma_free */
+	if (src_addr_v != NULL)
+		dma_free_coherent(met_device.this_device,
+				  PAGE_SIZE,
+				  src_addr_v,
+				  src_addr_p);
+#endif /* CONFIG_ARM64 */
+
+	return n;
+}
+
+/*======================================================================*/
+/*	KOBJ Declarations						*/
+/*======================================================================*/
+DECLARE_KOBJ_ATTR_INT(emi_TP_busfiltr_enable, emi_TP_busfiltr_enable);
+DECLARE_KOBJ_ATTR_INT(msel_enable, msel_enable);
+DECLARE_KOBJ_ATTR_HEX_CHECK(msel_group1, msel_group1, msel_group1 > 0 && msel_group1 <= _ALL);
+DECLARE_KOBJ_ATTR_HEX_CHECK(msel_group2, msel_group2, msel_group2 > 0 && msel_group2 <= _ALL);
+DECLARE_KOBJ_ATTR_HEX_CHECK(msel_group3, msel_group3, msel_group3 > 0 && msel_group3 <= _ALL);
+
+
+DECLARE_KOBJ_ATTR_STR_LIST_ITEM(
+	ttype1_16_en,
+	KOBJ_ITEM_LIST(
+		{ BM_TTYPE1_16_ENABLE,   "ENABLE" },
+		{ BM_TTYPE1_16_DISABLE,  "DISABLE" }
+		)
+	);
+DECLARE_KOBJ_ATTR_STR_LIST(ttype1_16_en, ttype1_16_en, ttype1_16_en);
+
+
+DECLARE_KOBJ_ATTR_STR_LIST_ITEM(
+	ttype17_21_en,
+	KOBJ_ITEM_LIST(
+		{ BM_TTYPE17_21_ENABLE,  "ENABLE" },
+		{ BM_TTYPE17_21_DISABLE, "DISABLE" }
+		)
+	);
+DECLARE_KOBJ_ATTR_STR_LIST(ttype17_21_en, ttype17_21_en, ttype17_21_en);
+
+DECLARE_KOBJ_ATTR_STR_LIST_ITEM(
+	ttype_master,
+	KOBJ_ITEM_LIST(
+		{ _M0,  "M0" },
+		{ _M1,  "M1" },
+		{ _M2,  "M2" },
+		{ _M3,  "M3" },
+		{ _M4,  "M4" },
+		{ _M5,  "M5" },
+		{ _M6,  "M6" },
+		{ _M7,  "M7" }
+		)
+	);
+
+
+DECLARE_KOBJ_ATTR_INT_LIST_ITEM(
+	ttype_nbeat,
+	KOBJ_ITEM_LIST(
+		{ BM_TRANS_TYPE_1BEAT,   1 },
+		{ BM_TRANS_TYPE_2BEAT,   2 },
+		{ BM_TRANS_TYPE_3BEAT,   3 },
+		{ BM_TRANS_TYPE_4BEAT,   4 },
+		{ BM_TRANS_TYPE_5BEAT,   5 },
+		{ BM_TRANS_TYPE_6BEAT,   6 },
+		{ BM_TRANS_TYPE_7BEAT,   7 },
+		{ BM_TRANS_TYPE_8BEAT,   8 },
+		{ BM_TRANS_TYPE_9BEAT,   9 },
+		{ BM_TRANS_TYPE_10BEAT,  10 },
+		{ BM_TRANS_TYPE_11BEAT,  11 },
+		{ BM_TRANS_TYPE_12BEAT,  12 },
+		{ BM_TRANS_TYPE_13BEAT,  13 },
+		{ BM_TRANS_TYPE_14BEAT,  14 },
+		{ BM_TRANS_TYPE_15BEAT,  15 },
+		{ BM_TRANS_TYPE_16BEAT,  16 }
+		)
+	);
+DECLARE_KOBJ_ATTR_INT_LIST_ITEM(
+	ttype_nbyte,
+	KOBJ_ITEM_LIST(
+		{ BM_TRANS_TYPE_1Byte,   1 },
+		{ BM_TRANS_TYPE_2Byte,   2 },
+		{ BM_TRANS_TYPE_4Byte,   4 },
+		{ BM_TRANS_TYPE_8Byte,   8 },
+		{ BM_TRANS_TYPE_16Byte,  16 },
+		{ BM_TRANS_TYPE_32Byte,  32 }
+		)
+	);
+DECLARE_KOBJ_ATTR_STR_LIST_ITEM(
+	ttype_burst,
+	KOBJ_ITEM_LIST(
+		{ BM_TRANS_TYPE_BURST_INCR,      "INCR" },
+		{ BM_TRANS_TYPE_BURST_WRAP,      "WRAP" }
+		)
+	);
+
+DECLARE_KOBJ_ATTR_STR_LIST_ITEM(
+	ttype_rw,
+	KOBJ_ITEM_LIST(
+		{ BM_TRANS_RW_DEFAULT,   "DEFAULT" },
+		{ BM_TRANS_RW_READONLY,  "R" },
+		{ BM_TRANS_RW_WRITEONLY, "W" },
+		{ BM_TRANS_RW_RWBOTH,    "BOTH" }
+		)
+	);
+
+
+DECLARE_KOBJ_ATTR_SHOW_INT(test, times);
+
+DECLARE_KOBJ_ATTR(test);
+
+
+static int high_priority_filter;
+DECLARE_KOBJ_ATTR_HEX(high_priority_filter, high_priority_filter);
+
+
+static int ttype_master_val[21];
+static int ttype_busid_val[21];
+static int ttype_nbeat_val[21];
+static int ttype_nbyte_val[21];
+static int ttype_burst_val[21];
+static int ttype_rw_val[21];
+
+#define DECLARE_KOBJ_TTYPE_MASTER(nr) \
+	DECLARE_KOBJ_ATTR_STR_LIST(ttype ## nr ## _master, ttype_master_val[nr - 1], ttype_master)
+
+#define DECLARE_KOBJ_TTYPE_NBEAT(nr) \
+	DECLARE_KOBJ_ATTR_INT_LIST(ttype ## nr ## _nbeat, ttype_nbeat_val[nr - 1], ttype_nbeat)
+
+#define DECLARE_KOBJ_TTYPE_NBYTE(nr) \
+	DECLARE_KOBJ_ATTR_INT_LIST(ttype ## nr ## _nbyte, ttype_nbyte_val[nr - 1], ttype_nbyte)
+
+#define DECLARE_KOBJ_TTYPE_BURST(nr) \
+	DECLARE_KOBJ_ATTR_STR_LIST(ttype ## nr ## _burst, ttype_burst_val[nr - 1], ttype_burst)
+
+#define DECLARE_KOBJ_TTYPE_RW(nr) \
+	DECLARE_KOBJ_ATTR_STR_LIST(ttype ## nr ## _rw, ttype_rw_val[nr - 1], ttype_rw)
+
+#define DECLARE_KOBJ_TTYPE_BUSID_VAL(nr) \
+	DECLARE_KOBJ_ATTR_HEX(ttype ## nr ## _busid, ttype_busid_val[nr - 1])
+
+DECLARE_KOBJ_TTYPE_MASTER(1);
+DECLARE_KOBJ_TTYPE_NBEAT(1);
+DECLARE_KOBJ_TTYPE_NBYTE(1);
+DECLARE_KOBJ_TTYPE_BURST(1);
+DECLARE_KOBJ_TTYPE_RW(1);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(1);
+
+DECLARE_KOBJ_TTYPE_MASTER(2);
+DECLARE_KOBJ_TTYPE_NBEAT(2);
+DECLARE_KOBJ_TTYPE_NBYTE(2);
+DECLARE_KOBJ_TTYPE_BURST(2);
+DECLARE_KOBJ_TTYPE_RW(2);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(2);
+
+DECLARE_KOBJ_TTYPE_MASTER(3);
+DECLARE_KOBJ_TTYPE_NBEAT(3);
+DECLARE_KOBJ_TTYPE_NBYTE(3);
+DECLARE_KOBJ_TTYPE_BURST(3);
+DECLARE_KOBJ_TTYPE_RW(3);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(3);
+
+DECLARE_KOBJ_TTYPE_MASTER(4);
+DECLARE_KOBJ_TTYPE_NBEAT(4);
+DECLARE_KOBJ_TTYPE_NBYTE(4);
+DECLARE_KOBJ_TTYPE_BURST(4);
+DECLARE_KOBJ_TTYPE_RW(4);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(4);
+
+DECLARE_KOBJ_TTYPE_MASTER(5);
+DECLARE_KOBJ_TTYPE_NBEAT(5);
+DECLARE_KOBJ_TTYPE_NBYTE(5);
+DECLARE_KOBJ_TTYPE_BURST(5);
+DECLARE_KOBJ_TTYPE_RW(5);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(5);
+
+DECLARE_KOBJ_TTYPE_MASTER(6);
+DECLARE_KOBJ_TTYPE_NBEAT(6);
+DECLARE_KOBJ_TTYPE_NBYTE(6);
+DECLARE_KOBJ_TTYPE_BURST(6);
+DECLARE_KOBJ_TTYPE_RW(6);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(6);
+
+DECLARE_KOBJ_TTYPE_MASTER(7);
+DECLARE_KOBJ_TTYPE_NBEAT(7);
+DECLARE_KOBJ_TTYPE_NBYTE(7);
+DECLARE_KOBJ_TTYPE_BURST(7);
+DECLARE_KOBJ_TTYPE_RW(7);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(7);
+
+DECLARE_KOBJ_TTYPE_MASTER(8);
+DECLARE_KOBJ_TTYPE_NBEAT(8);
+DECLARE_KOBJ_TTYPE_NBYTE(8);
+DECLARE_KOBJ_TTYPE_BURST(8);
+DECLARE_KOBJ_TTYPE_RW(8);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(8);
+
+DECLARE_KOBJ_TTYPE_MASTER(9);
+DECLARE_KOBJ_TTYPE_NBEAT(9);
+DECLARE_KOBJ_TTYPE_NBYTE(9);
+DECLARE_KOBJ_TTYPE_BURST(9);
+DECLARE_KOBJ_TTYPE_RW(9);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(9);
+
+DECLARE_KOBJ_TTYPE_MASTER(10);
+DECLARE_KOBJ_TTYPE_NBEAT(10);
+DECLARE_KOBJ_TTYPE_NBYTE(10);
+DECLARE_KOBJ_TTYPE_BURST(10);
+DECLARE_KOBJ_TTYPE_RW(10);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(10);
+
+DECLARE_KOBJ_TTYPE_MASTER(11);
+DECLARE_KOBJ_TTYPE_NBEAT(11);
+DECLARE_KOBJ_TTYPE_NBYTE(11);
+DECLARE_KOBJ_TTYPE_BURST(11);
+DECLARE_KOBJ_TTYPE_RW(11);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(11);
+
+DECLARE_KOBJ_TTYPE_MASTER(12);
+DECLARE_KOBJ_TTYPE_NBEAT(12);
+DECLARE_KOBJ_TTYPE_NBYTE(12);
+DECLARE_KOBJ_TTYPE_BURST(12);
+DECLARE_KOBJ_TTYPE_RW(12);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(12);
+
+DECLARE_KOBJ_TTYPE_MASTER(13);
+DECLARE_KOBJ_TTYPE_NBEAT(13);
+DECLARE_KOBJ_TTYPE_NBYTE(13);
+DECLARE_KOBJ_TTYPE_BURST(13);
+DECLARE_KOBJ_TTYPE_RW(13);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(13);
+
+DECLARE_KOBJ_TTYPE_MASTER(14);
+DECLARE_KOBJ_TTYPE_NBEAT(14);
+DECLARE_KOBJ_TTYPE_NBYTE(14);
+DECLARE_KOBJ_TTYPE_BURST(14);
+DECLARE_KOBJ_TTYPE_RW(14);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(14);
+
+DECLARE_KOBJ_TTYPE_MASTER(15);
+DECLARE_KOBJ_TTYPE_NBEAT(15);
+DECLARE_KOBJ_TTYPE_NBYTE(15);
+DECLARE_KOBJ_TTYPE_BURST(15);
+DECLARE_KOBJ_TTYPE_RW(15);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(15);
+
+DECLARE_KOBJ_TTYPE_MASTER(16);
+DECLARE_KOBJ_TTYPE_NBEAT(16);
+DECLARE_KOBJ_TTYPE_NBYTE(16);
+DECLARE_KOBJ_TTYPE_BURST(16);
+DECLARE_KOBJ_TTYPE_RW(16);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(16);
+
+DECLARE_KOBJ_TTYPE_MASTER(17);
+DECLARE_KOBJ_TTYPE_NBEAT(17);
+DECLARE_KOBJ_TTYPE_NBYTE(17);
+DECLARE_KOBJ_TTYPE_BURST(17);
+DECLARE_KOBJ_TTYPE_RW(17);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(17);
+
+DECLARE_KOBJ_TTYPE_MASTER(18);
+DECLARE_KOBJ_TTYPE_NBEAT(18);
+DECLARE_KOBJ_TTYPE_NBYTE(18);
+DECLARE_KOBJ_TTYPE_BURST(18);
+DECLARE_KOBJ_TTYPE_RW(18);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(18);
+
+DECLARE_KOBJ_TTYPE_MASTER(19);
+DECLARE_KOBJ_TTYPE_NBEAT(19);
+DECLARE_KOBJ_TTYPE_NBYTE(19);
+DECLARE_KOBJ_TTYPE_BURST(19);
+DECLARE_KOBJ_TTYPE_RW(19);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(19);
+
+DECLARE_KOBJ_TTYPE_MASTER(20);
+DECLARE_KOBJ_TTYPE_NBEAT(20);
+DECLARE_KOBJ_TTYPE_NBYTE(20);
+DECLARE_KOBJ_TTYPE_BURST(20);
+DECLARE_KOBJ_TTYPE_RW(20);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(20);
+
+DECLARE_KOBJ_TTYPE_MASTER(21);
+DECLARE_KOBJ_TTYPE_NBEAT(21);
+DECLARE_KOBJ_TTYPE_NBYTE(21);
+DECLARE_KOBJ_TTYPE_BURST(21);
+DECLARE_KOBJ_TTYPE_RW(21);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(21);
+
+#define KOBJ_ATTR_ITEM_SERIAL_FNODE(nr) \
+	do { \
+		KOBJ_ATTR_ITEM(ttype ## nr ## _master); \
+		KOBJ_ATTR_ITEM(ttype ## nr ## _nbeat); \
+		KOBJ_ATTR_ITEM(ttype ## nr ## _nbyte); \
+		KOBJ_ATTR_ITEM(ttype ## nr ## _burst); \
+		KOBJ_ATTR_ITEM(ttype ## nr ## _busid); \
+		KOBJ_ATTR_ITEM(ttype ## nr ## _rw); \
+	} while (0)
+
+#define KOBJ_ATTR_LIST \
+	do { \
+		KOBJ_ATTR_ITEM(high_priority_filter); \
+		KOBJ_ATTR_ITEM(emi_TP_busfiltr_enable); \
+		KOBJ_ATTR_ITEM(msel_enable); \
+		KOBJ_ATTR_ITEM(msel_group1); \
+		KOBJ_ATTR_ITEM(msel_group2); \
+		KOBJ_ATTR_ITEM(msel_group3); \
+		KOBJ_ATTR_ITEM(ttype17_21_en); \
+		KOBJ_ATTR_ITEM(ttype1_16_en); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(1); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(2); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(3); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(4); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(5); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(6); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(7); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(8); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(9); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(10); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(11); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(12); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(13); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(14); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(15); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(16); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(17); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(18); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(19); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(20); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(21); \
+		KOBJ_ATTR_ITEM(test); \
+	} while (0)
+
+/*======================================================================*/
+/*	EMI Operations							*/
+/*======================================================================*/
+static void emi_init(void)
+{
+	unsigned int bmrw0_val = 0, bmrw1_val = 0, i, enable;
+	unsigned int msel_group_val[4];
+
+	MET_BM_SaveCfg();
+
+	if ((ttype1_16_en != BM_TTYPE1_16_ENABLE) && (emi_TP_busfiltr_enable != 1)) {
+		if (msel_enable) {
+			msel_group_val[0] = _ALL;
+			msel_group_val[1] = msel_group1;
+			msel_group_val[2] = msel_group2;
+			msel_group_val[3] = msel_group3;
+		} else {
+			msel_group_val[0] = _ALL;
+			msel_group_val[1] = _ALL;
+			msel_group_val[2] = _ALL;
+			msel_group_val[3] = _ALL;
+		}
+
+		MET_BM_SetLatencyCounter(1);
+
+		for (i = 1; i <= 4; i++) {
+			MET_BM_SetMonitorCounter(i,
+						 msel_group_val[i - 1] & _ALL,
+						 BM_TRANS_TYPE_4BEAT |
+						 BM_TRANS_TYPE_8Byte |
+						 BM_TRANS_TYPE_BURST_WRAP);
+			MET_BM_SetbusID(i, 0);
+			MET_BM_SetbusID_En(i, 0);
+		}
+		for (i = 0; i < 4; i++)
+			MET_BM_Set_WsctTsct_id_sel(i, 0);
+
+	} else if ((ttype1_16_en != BM_TTYPE1_16_ENABLE) && (emi_TP_busfiltr_enable == 1)) {
+		MET_BM_SetLatencyCounter(1);
+
+		for (i = 1; i <= 4; i++) {
+			MET_BM_SetMonitorCounter(i,
+						 ttype_master_val[i - 1],
+						 ttype_nbeat_val[i - 1] |
+						 ttype_nbyte_val[i - 1] |
+						 ttype_burst_val[i - 1]);
+			MET_BM_SetbusID(i, ttype_busid_val[i - 1]);
+			MET_BM_SetbusID_En(i, 0);
+		}
+		for (i = 0; i < 4; i++)
+			MET_BM_Set_WsctTsct_id_sel(i, 1);
+
+	} else if ((ttype1_16_en == BM_TTYPE1_16_ENABLE) && (emi_TP_busfiltr_enable != 1)) {
+		MET_BM_SetLatencyCounter(0);
+
+		for (i = 1; i <= 16; i++) {
+			MET_BM_SetMonitorCounter(i,
+						 ttype_master_val[i - 1],
+						 ttype_nbeat_val[i - 1] |
+						 ttype_nbyte_val[i - 1] |
+						 ttype_burst_val[i - 1]);
+
+			MET_BM_SetbusID(i, ttype_busid_val[i - 1]);
+
+			MET_BM_SetbusID_En(i, (ttype_busid_val[i - 1] > 0xffff) ? 0 : 1);
+		}
+		for (i = 0; i < 4; i++)
+			MET_BM_Set_WsctTsct_id_sel(i, 0);
+	} else {	/* (ttype1_16_en == BM_TTYPE1_16_ENABLE)  &&  (emi_TP_busfiltr_enable == 1) */
+
+		MET_BM_SetLatencyCounter(0);
+
+		for (i = 1; i <= 16; i++) {
+			MET_BM_SetMonitorCounter(i,
+						 ttype_master_val[i - 1],
+						 ttype_nbeat_val[i - 1] |
+						 ttype_nbyte_val[i - 1] |
+						 ttype_burst_val[i - 1]);
+
+			MET_BM_SetbusID(i, ttype_busid_val[i - 1]);
+
+			MET_BM_SetbusID_En(i, (ttype_busid_val[i - 1] > 0xffff) ? 0 : 1);
+		}
+		for (i = 0; i < 4; i++)
+			MET_BM_Set_WsctTsct_id_sel(i, 1);
+	}
+
+	if (ttype17_21_en == BM_TTYPE17_21_ENABLE) {
+		for (i = 17; i <= 21; i++) {
+			MET_BM_SetMonitorCounter(i,
+						 ttype_master_val[i - 1],
+						 ttype_nbeat_val[i - 1] |
+						 ttype_nbyte_val[i - 1] |
+						 ttype_burst_val[i - 1]);
+			MET_BM_SetbusID(i, ttype_busid_val[i - 1]);
+
+			MET_BM_SetbusID_En(i, (ttype_busid_val[i - 1] > 0xffff) ? 0 : 1);
+		}
+	}
+
+	bmrw0_val = (
+		(ttype_rw_val[0] << 0) | (ttype_rw_val[1] << 2) |
+		(ttype_rw_val[2] << 4) | (ttype_rw_val[3] << 6) |
+		(ttype_rw_val[4] << 8) | (ttype_rw_val[5] << 10) |
+		(ttype_rw_val[6] << 12) | (ttype_rw_val[7] << 14) |
+		(ttype_rw_val[8] << 16) | (ttype_rw_val[9] << 18) |
+		(ttype_rw_val[10] << 20) | (ttype_rw_val[11] << 22) |
+		(ttype_rw_val[12] << 24) | (ttype_rw_val[13] << 26) |
+		(ttype_rw_val[14] << 28) | (ttype_rw_val[15] << 30));
+
+	bmrw1_val = (
+		(ttype_rw_val[16] << 0) | (ttype_rw_val[17] << 2) |
+		(ttype_rw_val[18] << 4) | (ttype_rw_val[19] << 6) |
+		(ttype_rw_val[20] << 8));
+
+	MET_BM_SetTtypeCounterRW(bmrw0_val, bmrw1_val);
+
+	for (i = 0; i < BM_COUNTER_MAX; i++) {
+		if ((high_priority_filter & (1 << i)) == 0)
+			enable = 0;
+		else
+			enable = 1;
+
+		MET_BM_SetUltraHighFilter(i + 1, enable);
+	}
+
+}
+
+static inline int do_emi(void)
+{
+	return met_sspm_emi.mode;
+}
+
+/*======================================================================*/
+/*	MET Device Operations						*/
+/*======================================================================*/
+static int emi_inited;
+static int met_emi_create(struct kobject *parent)
+{
+	int ret = 0;
+	int i;
+
+	for (i = 0; i < 21; i++) {
+		ttype_master_val[i] = _M0;
+		ttype_nbeat_val[i] = BM_TRANS_TYPE_1BEAT;
+		ttype_nbyte_val[i] = BM_TRANS_TYPE_8Byte;
+		ttype_burst_val[i] = BM_TRANS_TYPE_BURST_INCR;
+		ttype_busid_val[i] = 0xfffff;
+		ttype_rw_val[i] =  BM_TRANS_RW_DEFAULT;
+	}
+	ret = MET_BM_Init();
+	if (ret != 0) {
+		pr_debug("MET_BM_Init failed!!!\n");
+		ret = 0;
+	} else
+		emi_inited = 1;
+
+	kobj_emi = parent;
+
+#define	KOBJ_ATTR_ITEM(attr_name) \
+do { \
+	ret = sysfs_create_file(kobj_emi, &attr_name##_attr.attr); \
+	if (ret != 0) { \
+		pr_debug("Failed to create " #attr_name " in sysfs\n"); \
+		return ret; \
+	} \
+} while (0)
+	KOBJ_ATTR_LIST;
+#undef	KOBJ_ATTR_ITEM
+
+	return ret;
+}
+
+static void met_emi_delete(void)
+{
+#define	KOBJ_ATTR_ITEM(attr_name) \
+	sysfs_remove_file(kobj_emi, &attr_name##_attr.attr)
+	if (kobj_emi != NULL) {
+		KOBJ_ATTR_LIST;
+		kobj_emi = NULL;
+	}
+#undef	KOBJ_ATTR_ITEM
+
+	if (emi_inited)
+		MET_BM_DeInit();
+}
+
+
+static void met_emi_resume(void)
+{
+	if (!do_emi())
+		return;
+
+	emi_init();
+}
+
+
+#if defined(CONFIG_MTK_TINYSYS_SSPM_SUPPORT) && defined(ONDIEMET_SUPPORT)
+static const char help[] = "  --emi                                 monitor EMI banwidth\n";
+
+#define TTYPE_NAME_STR_LEN  64
+/* static char ttype_name[21][TTYPE_NAME_STR_LEN]; */
+
+static int emi_print_help(char *buf, int len)
+{
+	return snprintf(buf, PAGE_SIZE, help);
+}
+
+static int emi_print_header(char *buf, int len)
+{
+	int ret = 0;
+/*	int ret_m[21]; */
+	int i = 0;
+
+#if 0
+	for (i = 0; i < 21; i++) {
+		int k;
+
+		if (ttype_busid_val[i] > 0xffff) {
+			int j;
+
+			for (j = 0; j < ARRAY_SIZE(ttype_master_list_item); j++) {
+				if (ttype_master_val[i] == ttype_master_list_item[j].key) {
+					ret_m[i] = snprintf(ttype_name[i], TTYPE_NAME_STR_LEN, "ttype%d_%s",
+							    i + 1, ttype_master_list_item[j].val);
+					break;
+				}
+			}
+			if (j == ARRAY_SIZE(ttype_master_list_item))
+				ret_m[i] = snprintf(ttype_name[i], TTYPE_NAME_STR_LEN, "ttype%d_%s",
+						    i + 1, "unknown");
+		} else {
+			ret_m[i] = snprintf(ttype_name[i], TTYPE_NAME_STR_LEN, "ttype%d_%x",
+					    i + 1, ttype_busid_val[i]);
+		}
+
+		for (k = 0; k < ARRAY_SIZE(ttype_nbeat_list_item); k++) {
+			if (ttype_nbeat_val[i] == ttype_nbeat_list_item[k].key)
+				ret_m[i] += snprintf(ttype_name[i] + ret_m[i], TTYPE_NAME_STR_LEN - ret_m[i], "_%d",
+						     ttype_nbeat_list_item[k].val);
+		}
+
+		for (k = 0; k < ARRAY_SIZE(ttype_nbyte_list_item); k++) {
+			if (ttype_nbyte_val[i] == ttype_nbyte_list_item[k].key)
+				ret_m[i] += snprintf(ttype_name[i] + ret_m[i], TTYPE_NAME_STR_LEN - ret_m[i], "x%d",
+						     ttype_nbyte_list_item[k].val);
+		}
+
+		for (k = 0; k < ARRAY_SIZE(ttype_burst_list_item); k++) {
+			if (ttype_burst_val[i] == ttype_burst_list_item[k].key)
+				ret_m[i] += snprintf(ttype_name[i] + ret_m[i], TTYPE_NAME_STR_LEN - ret_m[i], "_%s",
+						     ttype_burst_list_item[k].val);
+		}
+
+		for (k = 0; k < ARRAY_SIZE(ttype_rw_list_item); k++) {
+			if (ttype_rw_val[i] == ttype_rw_list_item[k].key)
+				ret_m[i] += snprintf(ttype_name[i] + ret_m[i], TTYPE_NAME_STR_LEN - ret_m[i], "_%s",
+						     ttype_rw_list_item[k].val);
+		}
+	}
+#endif
+
+	if ((ttype1_16_en != BM_TTYPE1_16_ENABLE) && (emi_TP_busfiltr_enable != 1)) {
+		if (msel_enable) {
+			ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"met-info [000] 0.0: met_emi_msel: %x,%x,%x\n",
+				msel_group1 & _ALL,
+				msel_group2 & _ALL,
+				msel_group3 & _ALL);
+		} else {
+			ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"met-info [000] 0.0: met_emi_msel: %x,%x,%x\n",
+				_ALL & _ALL,
+				_ALL & _ALL,
+				_ALL & _ALL);
+		}
+	} else {
+		ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"met-info [000] 0.0: met_emi_ttype_master: %x,%x,%x,%x\n",
+				ttype_master_val[0], ttype_master_val[1], ttype_master_val[2], ttype_master_val[3]);
+
+		if (emi_TP_busfiltr_enable == 1) {
+
+			ret += snprintf(buf + ret, PAGE_SIZE - ret,
+					"met-info [000] 0.0: met_emi_ttype_busid: %x,%x,%x,%x\n",
+					ttype_busid_val[0], ttype_busid_val[1], ttype_busid_val[2], ttype_busid_val[3]);
+		}
+	}
+
+	ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_emi_rw_cfg: ");
+	ret += snprintf(buf + ret, PAGE_SIZE - ret, "BOTH");
+
+	for (i = 0; i < 21; i++) {
+		if (ttype_rw_val[i] == BM_TRANS_RW_DEFAULT)
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, ",DEFAULT");
+		else if (ttype_rw_val[i] == BM_TRANS_RW_READONLY)
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, ",R");
+		else if (ttype_rw_val[i] == BM_TRANS_RW_WRITEONLY)
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, ",W");
+		else
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, ",BOTH");
+	}
+	ret += snprintf(buf + ret, PAGE_SIZE - ret, "\n");
+
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"met-info [000] 0.0: met_emi_ultra_filter: %x\n", high_priority_filter);
+#if 0
+	if ((ttype1_16_en == BM_TTYPE1_16_ENABLE) && (ttype17_21_en == BM_TTYPE17_21_ENABLE)) {
+		int i;
+
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: ms_ud_sys_header: ms_ttype,");
+		for (i = 0; i < 21; i++)
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, "%s,", ttype_name[i]);
+
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x\n");
+
+	} else if (ttype17_21_en == BM_TTYPE17_21_ENABLE) {
+		int i;
+
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: ms_ud_sys_header: ms_ttype,");
+
+		for (i = 16; i < 21; i++)
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, "%s,", ttype_name[i]);
+
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "x,x,x,x,x\n");
+	}
+#else
+	/* ttype header */
+	if (ttype17_21_en == BM_TTYPE17_21_ENABLE) {
+		int i = 0;
+		int j = 0;
+
+		/* ttype master list */
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_emi_ttype_master_list: ");
+		for (i = 0; i < 21; i++) {
+			for (j = 0; j < ARRAY_SIZE(ttype_master_list_item); j++) {
+				if (ttype_master_val[i] == ttype_master_list_item[j].key) {
+					ret += snprintf(buf + ret, PAGE_SIZE - ret, "%s,", ttype_master_list_item[j].val);
+				}
+			}
+		}
+		/* remove the last comma */
+		snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+		/* ttype busid list */
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_emi_ttype_busid_list: ");
+		for (i = 0; i < 21; i++)
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, "%x,", ttype_busid_val[i]);
+
+		snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+		/* ttype nbeat list */
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_emi_ttype_nbeat_list: ");
+		for (i = 0; i < 21; i++) {
+			for (j = 0; j < ARRAY_SIZE(ttype_nbeat_list_item); j++) {
+				if (ttype_nbeat_val[i] == ttype_nbeat_list_item[j].key) {
+					ret += snprintf(buf + ret, PAGE_SIZE - ret, "%d,", ttype_nbeat_list_item[j].val);
+				}
+			}
+		}
+		snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+		/* ttype nbyte list */
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_emi_ttype_nbyte_list: ");
+		for (i = 0; i < 21; i++) {
+			for (j = 0; j < ARRAY_SIZE(ttype_nbyte_list_item); j++) {
+				if (ttype_nbyte_val[i] == ttype_nbyte_list_item[j].key) {
+					ret += snprintf(buf + ret, PAGE_SIZE - ret, "%d,", ttype_nbyte_list_item[j].val);
+				}
+			}
+		}
+		snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+		/* ttype burst list */
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_emi_ttype_burst_list: ");
+		for (i = 0; i < 21; i++) {
+			for (j = 0; j < ARRAY_SIZE(ttype_burst_list_item); j++) {
+				if (ttype_burst_val[i] == ttype_burst_list_item[j].key) {
+					ret += snprintf(buf + ret, PAGE_SIZE - ret, "%s,", ttype_burst_list_item[j].val);
+				}
+			}
+		}
+		snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+		/* ttype enable */
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_emi_ttype_enable: %d,%d\n",ttype1_16_en, ttype17_21_en);
+	}
+#endif
+
+	return ret;
+}
+
+static int ondiemet_emi_print_header(char *buf, int len)
+{
+	return emi_print_header(buf, len);
+}
+
+static void MET_BM_IPI_REGISTER_CB(void)
+{
+	int ret, i;
+	unsigned int rdata;
+	unsigned int ipi_buf[4];
+
+	for (i = 0; i < 4; i++)
+		ipi_buf[i] = 0;
+
+	if (sspm_buf_available == 1) {
+		ipi_buf[0] = MET_MAIN_ID | (MID_EMI << MID_BIT_SHIFT) | MET_ARGU | SET_REGISTER_CB;
+		ret = sspm_ipi_send_sync(IPI_ID_MET, IPI_OPT_WAIT, (void *)ipi_buf, 0, &rdata, 1);
+	}
+}
+
+static void MET_BM_IPI_configs(void)
+{
+	int ret, i;
+	unsigned int rdata;
+	unsigned int ipi_buf[4];
+
+	for (i = 0; i < 4; i++)
+		ipi_buf[i] = 0;
+
+	if (sspm_buf_available == 1) {
+		ipi_buf[0] = MET_MAIN_ID | (MID_EMI << MID_BIT_SHIFT) | MET_ARGU | SET_EBM_CONFIGS1;
+		ret = sspm_ipi_send_sync(IPI_ID_MET, IPI_OPT_WAIT, (void *)ipi_buf, 0, &rdata, 1);
+	}
+}
+
+static void ondiemet_emi_start(void)
+{
+	MET_BM_IPI_REGISTER_CB();
+	if (!emi_inited) {
+		if (MET_BM_Init() != 0) {
+			met_sspm_emi.mode = 0;
+			pr_notice("MET_BM_Init failed!!!\n");
+			return;
+		}
+		emi_inited = 1;
+	}
+	MET_BM_IPI_configs();
+
+	if (do_emi())
+		emi_init();
+
+	ondiemet_module[ONDIEMET_SSPM] |= ID_EMI;
+}
+
+static void emi_uninit(void)
+{
+	MET_BM_RestoreCfg();
+}
+
+static void ondiemet_emi_stop(void)
+{
+	if (!emi_inited)
+		return;
+
+	if (do_emi())
+		emi_uninit();
+}
+#endif
+
+struct metdevice met_sspm_emi = {
+	.name			= "emi",
+	.owner			= THIS_MODULE,
+	.type			= MET_TYPE_BUS,
+	.create_subfs		= met_emi_create,
+	.delete_subfs		= met_emi_delete,
+	.resume			= met_emi_resume,
+#if defined(CONFIG_MTK_TINYSYS_SSPM_SUPPORT) && defined(ONDIEMET_SUPPORT)
+	.ondiemet_start		= ondiemet_emi_start,
+	.ondiemet_stop		= ondiemet_emi_stop,
+	.ondiemet_print_help	= emi_print_help,
+	.ondiemet_print_header	= ondiemet_emi_print_header,
+#endif
+	.ondiemet_mode		= 1,
+};
+EXPORT_SYMBOL(met_sspm_emi);
diff --git a/src/devtools/met-driver/4.14/common/met_emi_35.c b/src/devtools/met-driver/4.14/common/met_emi_35.c
new file mode 100644
index 0000000..411fc1a
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/met_emi_35.c
@@ -0,0 +1,2244 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/device.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/dma-mapping.h>
+#include <linux/string.h>
+
+#define MET_USER_EVENT_SUPPORT
+#include "met_drv.h"
+#include "trace.h"
+
+#include "mtk_typedefs.h"
+#include "core_plf_init.h"
+#include "mtk_emi_bm_35.h"
+#include "interface.h"
+#include "met_dramc.h"
+
+#if defined(CONFIG_MTK_TINYSYS_SSPM_SUPPORT) && defined(ONDIEMET_SUPPORT)
+#include "sspm/ondiemet_sspm.h"
+#endif
+
+#define FILE_NODE_DBG
+
+/*======================================================================*/
+/*	Global variable definitions					*/
+/*======================================================================*/
+/*ondiemet emi sampling interval in us */
+int emi_tsct_enable = 1;
+int emi_mdct_enable = 1;
+int emi_TP_busfiltr_enable;
+
+
+/* Dynamic MonitorCounter selection !!!EXPERIMENT!!! */
+static int msel_enable;
+static unsigned int msel_group1 = BM_MASTER_ALL;
+static unsigned int msel_group2 = BM_MASTER_ALL;
+static unsigned int msel_group3 = BM_MASTER_ALL;
+
+
+/* Global variables */
+static struct kobject *kobj_emi;
+static int rwtype = BM_BOTH_READ_WRITE;
+
+/* BW Limiter */
+/*#define CNT_COUNTDOWN	(1000-1)*/		/* 1000 * 1ms = 1sec */
+#define CNT_COUNTDOWN   (0)                     /* 1ms */
+static int countdown;
+static int bw_limiter_enable = BM_BW_LIMITER_ENABLE;
+
+/* TTYPE counter */
+static int ttype1_16_en = BM_TTYPE1_16_DISABLE;
+static int ttype17_21_en = BM_TTYPE17_21_DISABLE;
+
+static int dramc_pdir_enable;
+static int dram_chann_num = 1;
+
+enum SSPM_Mode {
+	CUSTOMER_MODE = 0x0,
+	UNDEFINE_MODE = 0x1,
+	INTERNAL_MODE = 0X2780
+};
+
+
+
+/*======================================================================*/
+/*	KOBJ Declarations						*/
+/*======================================================================*/
+
+
+DECLARE_KOBJ_ATTR_INT(emi_TP_busfiltr_enable, emi_TP_busfiltr_enable);
+
+
+DECLARE_KOBJ_ATTR_INT(msel_enable, msel_enable);
+DECLARE_KOBJ_ATTR_HEX_CHECK(msel_group1, msel_group1, msel_group1 > 0 && msel_group1 <= BM_MASTER_ALL);
+DECLARE_KOBJ_ATTR_HEX_CHECK(msel_group2, msel_group2, msel_group2 > 0 && msel_group2 <= BM_MASTER_ALL);
+DECLARE_KOBJ_ATTR_HEX_CHECK(msel_group3, msel_group3, msel_group3 > 0 && msel_group3 <= BM_MASTER_ALL);
+
+
+
+/* KOBJ: rwtype */
+DECLARE_KOBJ_ATTR_INT_CHECK(rwtype, rwtype, rwtype >= 0 && rwtype <= BM_WRITE_ONLY);
+
+static unsigned int get_emi_clock_rate(unsigned int dram_data_rate_MHz)
+{
+	unsigned int DRAM_TYPE;
+
+	if (get_ddr_type_symbol) {
+		DRAM_TYPE = get_ddr_type_symbol();
+
+		if ((DRAM_TYPE == 2) || (DRAM_TYPE == 3))
+			return dram_data_rate_MHz / DRAM_EMI_BASECLOCK_RATE_LP4 / DRAM_DATARATE;
+		else
+			return dram_data_rate_MHz / DRAM_EMI_BASECLOCK_RATE_LP3 / DRAM_DATARATE;
+	} else {
+		METERROR("[%s][%d]get_ddr_type_symbol = NULL , use the TYPE_LPDDR3 setting\n", __func__, __LINE__);
+		return dram_data_rate_MHz / DRAM_EMI_BASECLOCK_RATE_LP3 / DRAM_DATARATE;
+	}
+}
+
+
+/* KOBJ: ttype1_16_en */
+DECLARE_KOBJ_ATTR_STR_LIST_ITEM(
+	ttype1_16_en,
+	KOBJ_ITEM_LIST(
+		{ BM_TTYPE1_16_ENABLE,   "ENABLE" },
+		{ BM_TTYPE1_16_DISABLE,  "DISABLE" }
+		)
+	);
+DECLARE_KOBJ_ATTR_STR_LIST(ttype1_16_en, ttype1_16_en, ttype1_16_en);
+
+/* KOBJ: ttype17_21_en */
+DECLARE_KOBJ_ATTR_STR_LIST_ITEM(
+	ttype17_21_en,
+	KOBJ_ITEM_LIST(
+		{ BM_TTYPE17_21_ENABLE,  "ENABLE" },
+		{ BM_TTYPE17_21_DISABLE, "DISABLE" }
+		)
+	);
+DECLARE_KOBJ_ATTR_STR_LIST(ttype17_21_en, ttype17_21_en, ttype17_21_en);
+
+/* KOBJ: bw_limiter_enable */
+DECLARE_KOBJ_ATTR_STR_LIST_ITEM(
+	bw_limiter_enable,
+	KOBJ_ITEM_LIST(
+		{ BM_BW_LIMITER_ENABLE,  "ENABLE" },
+		{ BM_BW_LIMITER_DISABLE, "DISABLE" }
+		)
+	);
+
+DECLARE_KOBJ_ATTR_STR_LIST(bw_limiter_enable, bw_limiter_enable, bw_limiter_enable);
+
+/* KOBJ: ttype_master */
+DECLARE_KOBJ_ATTR_STR_LIST_ITEM(
+	ttype_master,
+	KOBJ_ITEM_LIST(
+		{ BM_MASTER_M0,  "M0" },
+		{ BM_MASTER_M1,  "M1" },
+		{ BM_MASTER_M2,  "M2" },
+		{ BM_MASTER_M3,  "M3" },
+		{ BM_MASTER_M4,  "M4" },
+		{ BM_MASTER_M5,  "M5" },
+		{ BM_MASTER_M6,  "M6" },
+		{ BM_MASTER_M7,  "M7" }
+		)
+	);
+
+
+/* KOBJ: ttypeX_nbeat, ttypeX_nbyte, ttypeX_burst */
+DECLARE_KOBJ_ATTR_INT_LIST_ITEM(
+	ttype_nbeat,
+	KOBJ_ITEM_LIST(
+		{ BM_TRANS_TYPE_1BEAT,   1 },
+		{ BM_TRANS_TYPE_2BEAT,   2 },
+		{ BM_TRANS_TYPE_3BEAT,   3 },
+		{ BM_TRANS_TYPE_4BEAT,   4 },
+		{ BM_TRANS_TYPE_5BEAT,   5 },
+		{ BM_TRANS_TYPE_6BEAT,   6 },
+		{ BM_TRANS_TYPE_7BEAT,   7 },
+		{ BM_TRANS_TYPE_8BEAT,   8 },
+		{ BM_TRANS_TYPE_9BEAT,   9 },
+		{ BM_TRANS_TYPE_10BEAT,  10 },
+		{ BM_TRANS_TYPE_11BEAT,  11 },
+		{ BM_TRANS_TYPE_12BEAT,  12 },
+		{ BM_TRANS_TYPE_13BEAT,  13 },
+		{ BM_TRANS_TYPE_14BEAT,  14 },
+		{ BM_TRANS_TYPE_15BEAT,  15 },
+		{ BM_TRANS_TYPE_16BEAT,  16 }
+		)
+	);
+DECLARE_KOBJ_ATTR_INT_LIST_ITEM(
+	ttype_nbyte,
+	KOBJ_ITEM_LIST(
+		{ BM_TRANS_TYPE_1Byte,   1 },
+		{ BM_TRANS_TYPE_2Byte,   2 },
+		{ BM_TRANS_TYPE_4Byte,   4 },
+		{ BM_TRANS_TYPE_8Byte,   8 },
+		{ BM_TRANS_TYPE_16Byte,  16 },
+		{ BM_TRANS_TYPE_32Byte,  32 }
+		)
+	);
+DECLARE_KOBJ_ATTR_STR_LIST_ITEM(
+	ttype_burst,
+	KOBJ_ITEM_LIST(
+		{ BM_TRANS_TYPE_BURST_INCR,      "INCR" },
+		{ BM_TRANS_TYPE_BURST_WRAP,      "WRAP" }
+		)
+	);
+
+DECLARE_KOBJ_ATTR_STR_LIST_ITEM(
+	ttype_rw,
+	KOBJ_ITEM_LIST(
+		{ BM_TRANS_RW_DEFAULT,   "DEFAULT" },
+		{ BM_TRANS_RW_READONLY,  "R" },
+		{ BM_TRANS_RW_WRITEONLY, "W" },
+		{ BM_TRANS_RW_RWBOTH,    "BOTH" }
+		)
+	);
+
+
+DECLARE_KOBJ_ATTR_INT(dramc_pdir_enable, dramc_pdir_enable);
+
+/*enable high priority filter*/
+static int high_priority_filter;
+DECLARE_KOBJ_ATTR_HEX(high_priority_filter, high_priority_filter);
+
+
+
+/**/
+static int ttype_master_val[21];
+static int ttype_busid_val[21];
+static int ttype_nbeat_val[21];
+static int ttype_nbyte_val[21];
+static int ttype_burst_val[21];
+static int ttype_rw_val[21];
+
+#define DECLARE_KOBJ_TTYPE_MASTER(nr) \
+	DECLARE_KOBJ_ATTR_STR_LIST(ttype ## nr ## _master, ttype_master_val[nr - 1], ttype_master)
+
+#define DECLARE_KOBJ_TTYPE_NBEAT(nr) \
+	DECLARE_KOBJ_ATTR_INT_LIST(ttype ## nr ## _nbeat, ttype_nbeat_val[nr - 1], ttype_nbeat)
+
+#define DECLARE_KOBJ_TTYPE_NBYTE(nr) \
+	DECLARE_KOBJ_ATTR_INT_LIST(ttype ## nr ## _nbyte, ttype_nbyte_val[nr - 1], ttype_nbyte)
+
+#define DECLARE_KOBJ_TTYPE_BURST(nr) \
+	DECLARE_KOBJ_ATTR_STR_LIST(ttype ## nr ## _burst, ttype_burst_val[nr - 1], ttype_burst)
+
+#define DECLARE_KOBJ_TTYPE_RW(nr) \
+	DECLARE_KOBJ_ATTR_STR_LIST(ttype ## nr ## _rw, ttype_rw_val[nr - 1], ttype_rw)
+
+#define DECLARE_KOBJ_TTYPE_BUSID_VAL(nr) \
+	DECLARE_KOBJ_ATTR_HEX(ttype ## nr ## _busid, ttype_busid_val[nr - 1])
+
+DECLARE_KOBJ_TTYPE_MASTER(1);
+DECLARE_KOBJ_TTYPE_NBEAT(1);
+DECLARE_KOBJ_TTYPE_NBYTE(1);
+DECLARE_KOBJ_TTYPE_BURST(1);
+DECLARE_KOBJ_TTYPE_RW(1);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(1);
+
+DECLARE_KOBJ_TTYPE_MASTER(2);
+DECLARE_KOBJ_TTYPE_NBEAT(2);
+DECLARE_KOBJ_TTYPE_NBYTE(2);
+DECLARE_KOBJ_TTYPE_BURST(2);
+DECLARE_KOBJ_TTYPE_RW(2);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(2);
+
+DECLARE_KOBJ_TTYPE_MASTER(3);
+DECLARE_KOBJ_TTYPE_NBEAT(3);
+DECLARE_KOBJ_TTYPE_NBYTE(3);
+DECLARE_KOBJ_TTYPE_BURST(3);
+DECLARE_KOBJ_TTYPE_RW(3);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(3);
+
+DECLARE_KOBJ_TTYPE_MASTER(4);
+DECLARE_KOBJ_TTYPE_NBEAT(4);
+DECLARE_KOBJ_TTYPE_NBYTE(4);
+DECLARE_KOBJ_TTYPE_BURST(4);
+DECLARE_KOBJ_TTYPE_RW(4);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(4);
+
+DECLARE_KOBJ_TTYPE_MASTER(5);
+DECLARE_KOBJ_TTYPE_NBEAT(5);
+DECLARE_KOBJ_TTYPE_NBYTE(5);
+DECLARE_KOBJ_TTYPE_BURST(5);
+DECLARE_KOBJ_TTYPE_RW(5);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(5);
+
+DECLARE_KOBJ_TTYPE_MASTER(6);
+DECLARE_KOBJ_TTYPE_NBEAT(6);
+DECLARE_KOBJ_TTYPE_NBYTE(6);
+DECLARE_KOBJ_TTYPE_BURST(6);
+DECLARE_KOBJ_TTYPE_RW(6);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(6);
+
+DECLARE_KOBJ_TTYPE_MASTER(7);
+DECLARE_KOBJ_TTYPE_NBEAT(7);
+DECLARE_KOBJ_TTYPE_NBYTE(7);
+DECLARE_KOBJ_TTYPE_BURST(7);
+DECLARE_KOBJ_TTYPE_RW(7);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(7);
+
+DECLARE_KOBJ_TTYPE_MASTER(8);
+DECLARE_KOBJ_TTYPE_NBEAT(8);
+DECLARE_KOBJ_TTYPE_NBYTE(8);
+DECLARE_KOBJ_TTYPE_BURST(8);
+DECLARE_KOBJ_TTYPE_RW(8);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(8);
+
+DECLARE_KOBJ_TTYPE_MASTER(9);
+DECLARE_KOBJ_TTYPE_NBEAT(9);
+DECLARE_KOBJ_TTYPE_NBYTE(9);
+DECLARE_KOBJ_TTYPE_BURST(9);
+DECLARE_KOBJ_TTYPE_RW(9);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(9);
+
+DECLARE_KOBJ_TTYPE_MASTER(10);
+DECLARE_KOBJ_TTYPE_NBEAT(10);
+DECLARE_KOBJ_TTYPE_NBYTE(10);
+DECLARE_KOBJ_TTYPE_BURST(10);
+DECLARE_KOBJ_TTYPE_RW(10);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(10);
+
+DECLARE_KOBJ_TTYPE_MASTER(11);
+DECLARE_KOBJ_TTYPE_NBEAT(11);
+DECLARE_KOBJ_TTYPE_NBYTE(11);
+DECLARE_KOBJ_TTYPE_BURST(11);
+DECLARE_KOBJ_TTYPE_RW(11);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(11);
+
+DECLARE_KOBJ_TTYPE_MASTER(12);
+DECLARE_KOBJ_TTYPE_NBEAT(12);
+DECLARE_KOBJ_TTYPE_NBYTE(12);
+DECLARE_KOBJ_TTYPE_BURST(12);
+DECLARE_KOBJ_TTYPE_RW(12);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(12);
+
+DECLARE_KOBJ_TTYPE_MASTER(13);
+DECLARE_KOBJ_TTYPE_NBEAT(13);
+DECLARE_KOBJ_TTYPE_NBYTE(13);
+DECLARE_KOBJ_TTYPE_BURST(13);
+DECLARE_KOBJ_TTYPE_RW(13);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(13);
+
+DECLARE_KOBJ_TTYPE_MASTER(14);
+DECLARE_KOBJ_TTYPE_NBEAT(14);
+DECLARE_KOBJ_TTYPE_NBYTE(14);
+DECLARE_KOBJ_TTYPE_BURST(14);
+DECLARE_KOBJ_TTYPE_RW(14);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(14);
+
+DECLARE_KOBJ_TTYPE_MASTER(15);
+DECLARE_KOBJ_TTYPE_NBEAT(15);
+DECLARE_KOBJ_TTYPE_NBYTE(15);
+DECLARE_KOBJ_TTYPE_BURST(15);
+DECLARE_KOBJ_TTYPE_RW(15);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(15);
+
+DECLARE_KOBJ_TTYPE_MASTER(16);
+DECLARE_KOBJ_TTYPE_NBEAT(16);
+DECLARE_KOBJ_TTYPE_NBYTE(16);
+DECLARE_KOBJ_TTYPE_BURST(16);
+DECLARE_KOBJ_TTYPE_RW(16);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(16);
+
+DECLARE_KOBJ_TTYPE_MASTER(17);
+DECLARE_KOBJ_TTYPE_NBEAT(17);
+DECLARE_KOBJ_TTYPE_NBYTE(17);
+DECLARE_KOBJ_TTYPE_BURST(17);
+DECLARE_KOBJ_TTYPE_RW(17);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(17);
+
+DECLARE_KOBJ_TTYPE_MASTER(18);
+DECLARE_KOBJ_TTYPE_NBEAT(18);
+DECLARE_KOBJ_TTYPE_NBYTE(18);
+DECLARE_KOBJ_TTYPE_BURST(18);
+DECLARE_KOBJ_TTYPE_RW(18);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(18);
+
+DECLARE_KOBJ_TTYPE_MASTER(19);
+DECLARE_KOBJ_TTYPE_NBEAT(19);
+DECLARE_KOBJ_TTYPE_NBYTE(19);
+DECLARE_KOBJ_TTYPE_BURST(19);
+DECLARE_KOBJ_TTYPE_RW(19);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(19);
+
+DECLARE_KOBJ_TTYPE_MASTER(20);
+DECLARE_KOBJ_TTYPE_NBEAT(20);
+DECLARE_KOBJ_TTYPE_NBYTE(20);
+DECLARE_KOBJ_TTYPE_BURST(20);
+DECLARE_KOBJ_TTYPE_RW(20);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(20);
+
+DECLARE_KOBJ_TTYPE_MASTER(21);
+DECLARE_KOBJ_TTYPE_NBEAT(21);
+DECLARE_KOBJ_TTYPE_NBYTE(21);
+DECLARE_KOBJ_TTYPE_BURST(21);
+DECLARE_KOBJ_TTYPE_RW(21);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(21);
+
+
+/* SEDA 3.5 ext */
+static unsigned int msel_group_ext_val[WSCT_AMOUNT];
+static unsigned int wsct_rw_val[WSCT_AMOUNT];
+
+char* const delim_comma = ",";
+char* const delim_coclon = ":";
+
+
+char msel_group_ext[FILE_NODE_DATA_LEN] = {'\0'};
+
+static void _clear_msel_group_ext(void) {
+	int i;
+
+	for (i=0;i<WSCT_AMOUNT;i++) {
+		msel_group_ext_val[i] = BM_MASTER_ALL;
+	}
+	
+	/*WSCT 4~5 default is ultra, pre-ultra total*/
+	msel_group_ext[0] = '\0';
+}
+
+static ssize_t msel_group_ext_store(struct kobject *kobj,
+		struct kobj_attribute *attr,
+		const char *buf,
+		size_t n)
+{
+	/*parse wsct_id:group,
+	1. split data  by ","
+	2. split subdata by ":"
+	3. check the value is OK
+
+	don't clear the setting, do this by echo 1 > clear_setting
+	*/
+
+	char *token, *cur= msel_group_ext;
+	char *_id = NULL, *_master_group = NULL;
+	int id_int = 0;
+
+	_clear_msel_group_ext();
+
+	snprintf(msel_group_ext, FILE_NODE_DATA_LEN, "%s", buf);
+	msel_group_ext[n-1]='\0';
+	
+
+	while (cur != NULL) {
+		token = strsep(&cur, delim_comma);
+		PR_BOOTMSG("token: %s\n",token);
+		/*token EX: 4:0xff , (ID,master_group)*/
+
+		_id = strsep(&token, delim_coclon); // ID
+		_master_group = strsep(&token, delim_coclon);
+
+		PR_BOOTMSG("_id[%s] _master_group[%s]\n",_id,_master_group);
+
+		if (_id == NULL || _master_group == NULL) {
+			PR_BOOTMSG("err: _id[%s] _master_group[%s], para can't be NULL\n",_id,_master_group);
+			_clear_msel_group_ext();
+			return -EINVAL;
+		}
+
+		if (kstrtouint(_id, 0, &id_int) != 0) {
+			PR_BOOTMSG("_id[%s] trans to hex err\n",_id);
+			_clear_msel_group_ext();
+			return -EINVAL;
+		}
+
+
+		if ( id_int >= 0 && id_int < WSCT_AMOUNT) {
+			if (kstrtouint(_master_group, 0, &msel_group_ext_val[id_int]) != 0) {
+				PR_BOOTMSG("master_group[%s] trans to hex err\n",_master_group);
+				_clear_msel_group_ext();
+				return -EINVAL;
+			}
+		} else {
+			PR_BOOTMSG("id[%d] exceed the range, it must be 0~%d\n",id_int, WSCT_AMOUNT-1);
+			_clear_msel_group_ext();
+			return -EINVAL;
+		}
+	}
+#ifdef FILE_NODE_DBG	
+	PR_BOOTMSG("input data [%s]\n",msel_group_ext);
+	/*PR_BOOTMSG("msel_group_ext_store size para n[%d]\n",n);*/
+	int i;
+	PR_BOOTMSG("save data\n");
+	for (i=0;i<WSCT_AMOUNT;i++) {
+		PR_BOOTMSG("id[%d]=%X\n",i,msel_group_ext_val[i]);
+	}
+#endif	
+	return n;
+}
+
+static ssize_t msel_group_ext_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%s\n", msel_group_ext);
+}
+
+
+char wsct_rw[FILE_NODE_DATA_LEN] = {'\0'};
+
+static void _clear_wsct_rw(void) {
+	int i;
+
+	for (i=0;i<WSCT_AMOUNT;i++) {
+		wsct_rw_val[i] = BM_WSCT_RW_RWBOTH;
+	}
+	wsct_rw[0] = '\0';
+}
+
+static ssize_t wsct_rw_store(struct kobject *kobj,
+		struct kobj_attribute *attr,
+		const char *buf,
+		size_t n)
+{
+	char *token, *cur= wsct_rw;
+	char *_id = NULL, *_rw_type = NULL;
+	int id_int = 0;
+
+	_clear_wsct_rw();
+
+	snprintf(wsct_rw, FILE_NODE_DATA_LEN, "%s", buf);
+	wsct_rw[n-1]='\0';
+
+	while (cur != NULL) {
+		token = strsep(&cur, delim_comma);
+		PR_BOOTMSG("token: %s\n",token);
+		/*token EX: 4:R , 5:W (ID,RW)*/
+
+		_id = strsep(&token, delim_coclon); // ID
+		_rw_type = strsep(&token, delim_coclon);
+
+		if (_id == NULL || _rw_type == NULL) {
+			PR_BOOTMSG("err: _id[%s] _rw_type[%s], para can't be NULL\n",_id, _rw_type);
+			_clear_wsct_rw();
+			return -EINVAL;
+		}
+
+		PR_BOOTMSG("_id[%s] _rw_type[%s]\n",_id, _rw_type);
+		if (kstrtouint(_id, 0, &id_int) != 0) {
+			PR_BOOTMSG("_id[%s] trans to hex err\n",_id);
+			_clear_wsct_rw();
+			return -EINVAL;
+		}
+
+
+		if ( id_int >= 0 && id_int < WSCT_AMOUNT) {
+			if ( 0 == strncmp("NONE",_rw_type,4))
+				wsct_rw_val[id_int] = BM_WSCT_RW_DISABLE;
+			else if (0 == strncmp("R",_rw_type,4))
+				wsct_rw_val[id_int] = BM_WSCT_RW_READONLY;
+			else if (0 == strncmp("W",_rw_type,4))
+				wsct_rw_val[id_int] = BM_WSCT_RW_WRITEONLY;
+			else if (0 == strncmp("RW",_rw_type,4))
+				wsct_rw_val[id_int] = BM_WSCT_RW_RWBOTH;
+			else {
+				PR_BOOTMSG("_id[%s] has err rwtype[%s]\n", _id, _rw_type);
+				_clear_wsct_rw();
+				return -EINVAL;
+			}
+
+		} else {
+			PR_BOOTMSG("id[%d] exceed the range, it must be 0~%d\n",id_int, WSCT_AMOUNT-1);
+			_clear_wsct_rw();
+			return -EINVAL;
+		}
+	}
+
+#ifdef FILE_NODE_DBG
+	PR_BOOTMSG("wsct_rw_store input data [%s]\n",wsct_rw);
+	int i;
+	PR_BOOTMSG("rwtype save data\n");
+	for (i=0;i<WSCT_AMOUNT;i++) {
+		PR_BOOTMSG("id[%d]=%d\n",i,wsct_rw_val[i]);
+	}
+#endif
+	return n;
+}
+
+static ssize_t wsct_rw_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%s\n", wsct_rw);
+}
+
+
+static unsigned int WSCT_HPRI_DIS[WSCT_AMOUNT];
+static unsigned int WSCT_HPRI_SEL[WSCT_AMOUNT];
+char wsct_high_priority_enable[FILE_NODE_DATA_LEN] = {'\0'};
+
+static void _clear_wsct_high_priority_enable(void) {
+	int i;
+
+	for (i=0;i<WSCT_AMOUNT;i++) {
+		WSCT_HPRI_DIS[i] = 1;
+		WSCT_HPRI_SEL[i] = 0xF;
+	}
+
+	WSCT_HPRI_DIS[4] = 0;
+	WSCT_HPRI_SEL[4] = 0x8;  /* ultra */
+
+	WSCT_HPRI_DIS[5] = 0;
+	WSCT_HPRI_SEL[5] = 0x4; /* pre_ultra */
+
+
+	wsct_high_priority_enable[0] = '\0';
+}
+
+static ssize_t wsct_high_priority_enable_store(struct kobject *kobj,
+		struct kobj_attribute *attr,
+		const char *buf,
+		size_t n)
+{
+	char *token, *cur= wsct_high_priority_enable;
+	char *_id = NULL, *_enable = NULL,  *_level = NULL;
+	int  id_int = 0, level_int = 0;
+
+	_clear_wsct_high_priority_enable();
+
+	snprintf(wsct_high_priority_enable, FILE_NODE_DATA_LEN, "%s", buf);
+	wsct_high_priority_enable[n-1]='\0';
+	
+	while (cur != NULL) {
+		token = strsep(&cur, delim_comma);
+		PR_BOOTMSG("token: %s\n",token);
+		/*token EX: 4:R , 5:W (ID,RW)*/
+
+		_id = strsep(&token, delim_coclon); // ID
+		_enable = strsep(&token, delim_coclon);
+		_level = strsep(&token, delim_coclon);
+
+		PR_BOOTMSG("_id[%s] _enable[%s] _level[%s]\n",_id, _enable, _level);
+
+		if (_id == NULL || _enable == NULL || _level == NULL ) {
+			PR_BOOTMSG("err : _id[%s] _enable[%s] _level[%s], para can't be NULL\n",_id, _enable, _level);
+			_clear_wsct_high_priority_enable();
+			return -EINVAL;
+		}
+
+		if (kstrtouint(_id, 0, &id_int) != 0) {
+			PR_BOOTMSG("_id[%s] trans to hex err\n",_id);
+			_clear_wsct_high_priority_enable();
+			return -EINVAL;
+		}
+
+
+		if ( id_int >= 0 && id_int < WSCT_AMOUNT) {
+			if ( 0 == strncmp("disable", _enable, 7)) {
+
+				WSCT_HPRI_DIS[id_int] = 1;
+				WSCT_HPRI_SEL[id_int] = 0xf;
+			} else if ( 0 == strncmp("enable", _enable, 6)) {
+
+				WSCT_HPRI_DIS[id_int] = 0;
+				if (kstrtouint(_level, 0, &level_int) != 0) {
+					PR_BOOTMSG("_id[%s] trans ultraLevel[%s] to hex err\n",_id, _level);
+					_clear_wsct_high_priority_enable();
+					return -EINVAL;
+				}
+				WSCT_HPRI_SEL[id_int] = level_int & 0xF;				
+			} else {
+				PR_BOOTMSG("_id[%s] has err enable[%s] (enable/disable)\n", _id, _enable);
+				_clear_wsct_high_priority_enable();
+				return -EINVAL;
+			}
+
+		} else {
+			PR_BOOTMSG("id[%d] exceed the range, it must be 0~%d\n",id_int, WSCT_AMOUNT-1);
+			_clear_wsct_high_priority_enable();
+			return -EINVAL;
+		}
+	}
+#ifdef FILE_NODE_DBG	
+	PR_BOOTMSG("input data [%s]\n",wsct_high_priority_enable);
+	int i;
+	PR_BOOTMSG("wsct_high_priority_enable save data\n");
+	for (i=0;i<WSCT_AMOUNT;i++) {
+		PR_BOOTMSG("id[%d]=(%X,%X)\n", i, WSCT_HPRI_DIS[i], WSCT_HPRI_SEL[i]);
+	}
+#endif
+	return n;
+}
+
+static ssize_t wsct_high_priority_enable_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%s\n", wsct_high_priority_enable);
+}
+
+
+static unsigned int wsct_busid_val[WSCT_AMOUNT];
+static unsigned int wsct_idMask_val[WSCT_AMOUNT];
+
+char wsct_busid[FILE_NODE_DATA_LEN] = {'\0'};
+
+static void _clear_wsct_busid(void) {
+	int i;
+
+	for (i=0;i<WSCT_AMOUNT;i++) {
+		wsct_busid_val[i] = 0xfffff;
+		wsct_idMask_val[i] = 0x1FFF;
+	}
+	wsct_busid[0] = '\0';
+}
+
+static ssize_t wsct_busid_store(struct kobject *kobj,
+		struct kobj_attribute *attr,
+		const char *buf,
+		size_t n)
+{
+	char *token, *cur= wsct_busid;
+
+	char *_id = NULL, *_busid = NULL, *_idMask = NULL;
+	int id_int = 0, busid_int = 0, idMask_int = 0;
+
+	_clear_wsct_busid();
+
+	snprintf(wsct_busid, FILE_NODE_DATA_LEN, "%s", buf);
+	wsct_busid[n-1]='\0';
+	
+	while (cur != NULL) {
+		token = strsep(&cur, delim_comma);
+		PR_BOOTMSG("token: %s\n",token);
+		/*token EX: 4:R , 5:W (ID,RW)*/
+
+		_id = strsep(&token, delim_coclon); // ID
+		_busid = strsep(&token, delim_coclon);
+		_idMask = strsep(&token, delim_coclon);
+
+		PR_BOOTMSG("_id[%s] _busid[%s] _idMask[%s]\n",_id, _busid, _idMask);
+
+		if (_id == NULL || _busid == NULL || _idMask == NULL) {
+			PR_BOOTMSG("err: _id[%s] _busid[%s] _idMask[%s] ,parameter can't be NULL\n",_id, _busid, _idMask);
+			_clear_wsct_busid();
+			return -EINVAL;
+		}
+
+		
+		if (kstrtouint(_id, 0, &id_int) != 0) {
+			PR_BOOTMSG("_id[%s] trans to hex err\n",_id);
+			_clear_wsct_busid();
+			return -EINVAL;
+		}
+		if (kstrtouint(_busid, 0, &busid_int) != 0) {
+			PR_BOOTMSG("_busid[%s] trans to hex err\n",_busid);
+			_clear_wsct_busid();
+			return -EINVAL;
+		}
+		if (kstrtouint(_idMask, 0, &idMask_int) != 0) {
+			PR_BOOTMSG("_idMask[%s] trans to hex err\n",_idMask);
+			_clear_wsct_busid();
+			return -EINVAL;
+		}
+
+
+		if ( id_int >= 0 && id_int < WSCT_AMOUNT) {
+			wsct_busid_val[id_int] = busid_int;
+			wsct_idMask_val[id_int] = idMask_int;
+
+		} else {
+			PR_BOOTMSG("id[%d] exceed the range, it must be 0~%d\n",id_int, WSCT_AMOUNT-1);
+			_clear_wsct_busid();
+			return -EINVAL;
+		}
+	}
+#ifdef FILE_NODE_DBG	
+	PR_BOOTMSG("input data [%s]\n",wsct_busid);
+	int i;
+	PR_BOOTMSG("wsct_busid save data\n");
+	for (i=0;i<WSCT_AMOUNT;i++) {
+		PR_BOOTMSG("id[%d](busid,idMask)=(%X,%X)\n", i, wsct_busid_val[i], wsct_idMask_val[i]);
+	}
+#endif
+	return n;
+}
+
+static ssize_t wsct_busid_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%s\n", wsct_busid);
+}
+
+
+static unsigned int  wsct_chn_rank_sel_val[WSCT_AMOUNT];
+char wsct_chn_rank_sel[FILE_NODE_DATA_LEN] = {'\0'};
+
+static void _clear_wsct_chn_rank_sel(void) {
+	int i;
+
+	for (i=0;i<WSCT_AMOUNT;i++) {
+		wsct_chn_rank_sel_val[i] = 0xF;
+	}
+	wsct_chn_rank_sel[0] = '\0';
+}
+
+static ssize_t wsct_chn_rank_sel_store(struct kobject *kobj,
+		struct kobj_attribute *attr,
+		const char *buf,
+		size_t n)
+{
+	char *token, *cur= wsct_chn_rank_sel;
+	char *_id = NULL, *_chn_rank = NULL;
+	int id_int = 0, chn_rank_int = 0;
+
+	_clear_wsct_chn_rank_sel();
+
+	snprintf(wsct_chn_rank_sel, FILE_NODE_DATA_LEN, "%s", buf);
+	wsct_chn_rank_sel[n-1]='\0';
+
+
+	while (cur != NULL) {
+		token = strsep(&cur, delim_comma);
+		PR_BOOTMSG("token: %s\n",token);
+		/*token EX: 4:f , 5:C (ID,chn_rnk_sel)*/
+
+		_id = strsep(&token, delim_coclon); // ID
+		_chn_rank = strsep(&token, delim_coclon);
+
+		PR_BOOTMSG("_id[%s] _chn_rank[%s]\n",_id, _chn_rank);
+
+		if (_id == NULL || _chn_rank == NULL) {
+			PR_BOOTMSG("err : _id[%s] _chn_rank[%s], para can't be NULL\n",_id, _chn_rank);
+			_clear_wsct_chn_rank_sel();
+			return -EINVAL;
+		}
+
+		
+		if (kstrtouint(_id, 0, &id_int) != 0) {
+			PR_BOOTMSG("_id[%s] trans to hex err\n",_id);
+			_clear_wsct_chn_rank_sel();
+			return -EINVAL;
+		}
+		if (kstrtouint(_chn_rank, 0, &chn_rank_int) != 0) {
+			PR_BOOTMSG("_chn_rank[%s] trans to hex err\n",_id);
+			_clear_wsct_chn_rank_sel();
+			return -EINVAL;
+		}
+
+		if ( id_int >= 0 && id_int < WSCT_AMOUNT) {
+			wsct_chn_rank_sel_val[id_int] = chn_rank_int;
+
+		} else {
+			PR_BOOTMSG("id[%d] exceed the range, it must be 0~%d\n",id_int, WSCT_AMOUNT-1);
+			_clear_wsct_chn_rank_sel();
+			return -EINVAL;
+		}
+	}
+
+#ifdef FILE_NODE_DBG	
+	PR_BOOTMSG("wsct_chn_rank_sel input data [%s]\n",wsct_chn_rank_sel);
+	int i;
+	PR_BOOTMSG("wsct_chn_rank_sel_val save data\n");
+	for (i=0;i<WSCT_AMOUNT;i++) {
+		PR_BOOTMSG("id[%d]=%X\n",i,wsct_chn_rank_sel_val[i]);
+	}
+#endif
+	return n;
+}
+
+static ssize_t wsct_chn_rank_sel_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%s\n", wsct_chn_rank_sel);
+}
+
+static unsigned int  wsct_byte_low_bnd_val[WSCT_AMOUNT];
+static unsigned int  wsct_byte_up_bnd_val[WSCT_AMOUNT];
+static unsigned int  wsct_byte_bnd_dis[WSCT_AMOUNT];
+char wsct_burst_range[FILE_NODE_DATA_LEN] = {'\0'};
+
+static void _clear_wsct_burst_range(void) {
+	int i;
+
+	for (i=0;i<WSCT_AMOUNT;i++) {
+		wsct_byte_low_bnd_val[i] = 0x0;
+		wsct_byte_up_bnd_val[i] = 0x1FF;
+		wsct_byte_bnd_dis[i] = 1;
+	}
+	wsct_burst_range[0] = '\0';
+}
+
+static ssize_t wsct_burst_range_store(struct kobject *kobj,
+		struct kobj_attribute *attr,
+		const char *buf,
+		size_t n)
+{
+	char *token, *cur= wsct_burst_range;
+	char *_id = NULL, *_low_bnd = NULL, *_up_bnd = NULL;
+	int id_int = 0, low_bnd_int = 0, up_bnd_int = 0;
+
+	_clear_wsct_burst_range();
+	
+	snprintf(wsct_burst_range, FILE_NODE_DATA_LEN, "%s", buf);
+	wsct_burst_range[n-1]='\0';
+
+
+	while (cur != NULL) {
+		token = strsep(&cur, delim_comma);
+		PR_BOOTMSG("token: %s\n",token);
+		/*token EX: 4:f , 5:C (ID,chn_rnk_sel)*/
+
+		_id = strsep(&token, delim_coclon); // ID
+		_low_bnd = strsep(&token, delim_coclon);
+		_up_bnd = strsep(&token, delim_coclon);
+
+		PR_BOOTMSG("_id[%s] _low_bnd[%s] _up_bnd[%s]\n",_id, _low_bnd, _up_bnd);
+
+		if (_id == NULL || _low_bnd == NULL || _up_bnd == NULL) {
+			PR_BOOTMSG("err : _id[%s] _low_bnd[%s] _up_bnd[%s], para can't be NULL\n",_id, _low_bnd, _up_bnd);
+			_clear_wsct_burst_range();
+			return -EINVAL;
+		}
+
+		if (kstrtouint(_id, 0, &id_int) != 0) {
+			PR_BOOTMSG("_id[%s] trans to hex err\n",_id);
+			_clear_wsct_burst_range();
+			return -EINVAL;
+		}
+		if (kstrtouint(_low_bnd, 0, &low_bnd_int) != 0) {
+			PR_BOOTMSG("_low_bnd[%s] trans to hex err\n",_id);
+			_clear_wsct_burst_range();
+			return -EINVAL;
+		}
+		if (kstrtouint(_up_bnd, 0, &up_bnd_int) != 0) {
+			PR_BOOTMSG("_up_bnd[%s] trans to hex err\n",_id);
+			_clear_wsct_burst_range();
+			return -EINVAL;
+		}
+
+		if ( id_int >= 0 && id_int < WSCT_AMOUNT) {
+			wsct_byte_low_bnd_val[id_int] = low_bnd_int;
+			wsct_byte_up_bnd_val[id_int] = up_bnd_int;
+			wsct_byte_bnd_dis[id_int] = 0;
+		} else {
+			PR_BOOTMSG("id[%d] exceed the range, it must be 0~%d\n",id_int, WSCT_AMOUNT-1);
+			_clear_wsct_burst_range();
+			return -EINVAL;
+		}
+	}
+
+#ifdef FILE_NODE_DBG
+	PR_BOOTMSG("wsct_burst_range_store input data [%s]\n",wsct_burst_range);
+	int i;
+	PR_BOOTMSG("wsct_burst_range save data\n");
+	for (i=0;i<WSCT_AMOUNT;i++) {
+		PR_BOOTMSG("id[%d](low_bnd,up_bnd)=(%X,%X)\n",i,wsct_byte_low_bnd_val[i],wsct_byte_up_bnd_val[i]);
+	}
+#endif
+	return n;
+}
+
+static ssize_t wsct_burst_range_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%s\n", wsct_burst_range);
+}
+
+
+
+static unsigned int tsct_busid_enable_val[TSCT_AMOUNT];
+char tsct_busid_enable[FILE_NODE_DATA_LEN] = {'\0'};
+
+static void _clear_tsct_busid_enable(void) {
+	int i;
+
+	for (i=0;i<TSCT_AMOUNT;i++) {
+		tsct_busid_enable_val[i] = 0;
+	}
+	tsct_busid_enable[0] = '\0';
+}
+
+static ssize_t tsct_busid_enable_store(struct kobject *kobj,
+		struct kobj_attribute *attr,
+		const char *buf,
+		size_t n)
+{
+	char *token, *cur= tsct_busid_enable;
+	char *_id = NULL, *_enable = NULL;
+	int  id_int = 0;
+
+	_clear_tsct_busid_enable();
+
+	snprintf(tsct_busid_enable, FILE_NODE_DATA_LEN, "%s", buf);
+	tsct_busid_enable[n-1]='\0';
+
+
+	while (cur != NULL) {
+		token = strsep(&cur, delim_comma);
+		PR_BOOTMSG("token: %s\n",token);
+		/*token EX: 4:R , 5:W (ID,RW)*/
+
+		_id = strsep(&token, delim_coclon); // ID
+		_enable = strsep(&token, delim_coclon);
+
+
+		PR_BOOTMSG("_id[%s] _enable[%s]\n",_id, _enable);
+
+		if (_id == NULL || _enable == NULL) {
+			PR_BOOTMSG("err : _id[%s] _enable[%s], para can't be NULL\n",_id, _enable);
+			_clear_tsct_busid_enable();
+			return -EINVAL;
+		}
+
+		if (kstrtouint(_id, 0, &id_int) != 0) {
+			PR_BOOTMSG("_id[%s] trans to hex err\n",_id);
+			_clear_tsct_busid_enable();
+			return -EINVAL;
+		}
+
+
+		if ( id_int >= 0 && id_int < TSCT_AMOUNT) {
+			if ( 0 == strncmp("disable", _enable, 7)) {
+				tsct_busid_enable_val[id_int] = 0;
+			} else if ( 0 == strncmp("enable", _enable, 6)) {
+				tsct_busid_enable_val[id_int] = 1;
+			} else {
+				PR_BOOTMSG("_id[%s] has err enable[%s] (enable/disable)\n", _id, _enable);
+				_clear_tsct_busid_enable();
+				return -EINVAL;
+			}
+
+		} else {
+			PR_BOOTMSG("id[%d] exceed the range, it must be 0~%d\n",id_int, TSCT_AMOUNT-1);
+			_clear_tsct_busid_enable();
+			return -EINVAL;
+		}
+	}
+#ifdef FILE_NODE_DBG	
+	PR_BOOTMSG("tsct_busid_enable input data [%s]\n",tsct_busid_enable);
+	int i;
+	PR_BOOTMSG("wsct_high_priority_enable save data\n");
+	for (i=0;i<TSCT_AMOUNT;i++) {
+		PR_BOOTMSG("id[%d]=(%d)\n", i, tsct_busid_enable_val[i]);
+	}
+#endif
+	return n;
+}
+
+static ssize_t tsct_busid_enable_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%s\n", tsct_busid_enable);
+}
+
+
+/* use the origin para high_priority_filter to save the en/dis setting */
+static unsigned int TTYPE_HPRI_SEL[BM_COUNTER_MAX];
+char ttype_high_priority_ext[FILE_NODE_DATA_LEN] = {'\0'};
+
+static void _clear_ttype_high_priority_ext(void) {
+	int i;
+
+	for (i=0;i<BM_COUNTER_MAX;i++) {
+		TTYPE_HPRI_SEL[i] = 0xf;
+	}
+
+	high_priority_filter = 0x0;
+	ttype_high_priority_ext[0] = '\0';
+}
+
+static ssize_t ttype_high_priority_ext_store(struct kobject *kobj,
+		struct kobj_attribute *attr,
+		const char *buf,
+		size_t n)
+{
+	char *token, *cur= ttype_high_priority_ext;
+	char *_id = NULL, *_enable = NULL,  *_level = NULL;
+	int  id_int = 0, level_int = 0;
+
+	_clear_ttype_high_priority_ext();
+
+	snprintf(ttype_high_priority_ext, FILE_NODE_DATA_LEN, "%s", buf);
+	ttype_high_priority_ext[n-1]='\0';
+
+	while (cur != NULL) {
+		token = strsep(&cur, delim_comma);
+		PR_BOOTMSG("token: %s\n",token);
+		/*token EX: 4:R , 5:W (ID,RW)*/
+
+		_id = strsep(&token, delim_coclon); // ID
+		_enable = strsep(&token, delim_coclon);
+		_level = strsep(&token, delim_coclon);
+
+		PR_BOOTMSG("_id[%s] _enable[%s] _level[%s]\n",_id, _enable, _level);
+
+		if (_id == NULL || _enable == NULL || _level == NULL ) {
+			PR_BOOTMSG("err : _id[%s] _enable[%s] _level[%s], para can't be NULL\n",_id, _enable, _level);
+			_clear_ttype_high_priority_ext();
+			return -EINVAL;
+		}
+
+		if (kstrtouint(_id, 0, &id_int) != 0) {
+			PR_BOOTMSG("_id[%s] trans to hex err\n",_id);
+			_clear_ttype_high_priority_ext();
+			return -EINVAL;
+		}
+
+		id_int = id_int - 1;
+		if ( id_int >= 0 && id_int < BM_COUNTER_MAX) {
+			if ( 0 == strncmp("disable", _enable, 7)) {
+
+				high_priority_filter = ( high_priority_filter & ~(1<<id_int) ) | ( 0<<id_int );
+				TTYPE_HPRI_SEL[id_int] = 0xf;
+			} else if ( 0 == strncmp("enable", _enable, 6)) {
+
+				high_priority_filter = ( high_priority_filter & ~(1<<id_int) ) | ( 1<<id_int );
+				if (kstrtouint(_level, 0, &level_int) != 0) {
+					PR_BOOTMSG("_id[%s] trans ultraLevel[%s] to hex err\n",_id, _level);
+					_clear_ttype_high_priority_ext();
+					return -EINVAL;
+				}
+				TTYPE_HPRI_SEL[id_int] = level_int & 0xF;				
+			} else {
+				PR_BOOTMSG("ttype_high_priority_ext: _id[%s] has err enable[%s] (enable/disable)\n", _id, _enable);
+				_clear_ttype_high_priority_ext();
+				return -EINVAL;
+			}
+
+		} else {
+			PR_BOOTMSG("id[%d] exceed the range, it must be 1~%d\n",id_int+1, BM_COUNTER_MAX);
+			_clear_ttype_high_priority_ext();
+			return -EINVAL;
+		}
+	}
+#ifdef FILE_NODE_DBG
+	PR_BOOTMSG("input data [%s]\n",ttype_high_priority_ext);
+
+	int i;
+	PR_BOOTMSG("wsct_high_priority_enable save data\n");
+	for (i=0;i<BM_COUNTER_MAX;i++) {
+		PR_BOOTMSG("id[%d]=(%X,%X)\n", i+1, high_priority_filter>>i & 0x1, TTYPE_HPRI_SEL[i]);
+	}
+#endif
+	return n;
+}
+
+static ssize_t ttype_high_priority_ext_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%s\n", ttype_high_priority_ext);
+}
+
+
+static unsigned int ttype_idMask_val[BM_COUNTER_MAX];
+char ttype_busid_ext[FILE_NODE_DATA_LEN] = {'\0'};
+
+static void _clear_ttype_busid_ext(void) {
+	int i;
+
+	for (i=0;i<BM_COUNTER_MAX;i++) {
+		ttype_busid_val[i] = 0xfffff;
+		ttype_idMask_val[i] = 0x1FFF;	
+	}
+	ttype_busid_ext[0] = '\0';
+}
+
+/*id: 1~21*/
+static ssize_t ttype_busid_ext_store(struct kobject *kobj,
+		struct kobj_attribute *attr,
+		const char *buf,
+		size_t n)
+{
+	char *token, *cur= ttype_busid_ext;
+
+	char *_id = NULL, *_busid = NULL, *_idMask = NULL;
+	int id_int = 0, busid_int = 0, idMask_int = 0;
+
+	_clear_ttype_busid_ext();
+
+	snprintf(ttype_busid_ext, FILE_NODE_DATA_LEN, "%s", buf);
+	ttype_busid_ext[n-1]='\0';
+
+	while (cur != NULL) {
+		token = strsep(&cur, delim_comma);
+		PR_BOOTMSG("token: %s\n",token);
+		/*token EX: 4:R , 5:W (ID,RW)*/
+
+		_id = strsep(&token, delim_coclon); // ID
+		_busid = strsep(&token, delim_coclon);
+		_idMask = strsep(&token, delim_coclon);
+
+		PR_BOOTMSG("_id[%s] _busid[%s] _idMask[%s]\n",_id, _busid, _idMask);
+
+		if (_id == NULL || _busid == NULL || _idMask == NULL) {
+			PR_BOOTMSG("err: ttype_busid_ext _id[%s] _busid[%s] _idMask[%s] ,parameter can't be NULL\n",_id, _busid, _idMask);
+			_clear_ttype_busid_ext();
+			return -EINVAL;
+		}
+	
+		if (kstrtouint(_id, 0, &id_int) != 0) {
+			PR_BOOTMSG("_id[%s] trans to hex err\n",_id);
+			_clear_ttype_busid_ext();
+			return -EINVAL;
+		}
+		if (kstrtouint(_busid, 0, &busid_int) != 0) {
+			PR_BOOTMSG("_busid[%s] trans to hex err\n",_busid);
+			_clear_ttype_busid_ext();
+			return -EINVAL;
+		}
+		if (kstrtouint(_idMask, 0, &idMask_int) != 0) {
+			PR_BOOTMSG("_idMask[%s] trans to hex err\n",_idMask);
+			_clear_ttype_busid_ext();
+			return -EINVAL;
+		}
+
+		id_int = id_int - 1;
+		if ( id_int >= 0 && id_int < BM_COUNTER_MAX) {
+			ttype_busid_val[id_int] = busid_int;
+			ttype_idMask_val[id_int] = idMask_int;
+
+		} else {
+			PR_BOOTMSG("ttype_busid_ext id[%d] exceed the range, it must be 1~%d\n",id_int+1, BM_COUNTER_MAX);
+			_clear_ttype_busid_ext();
+			return -EINVAL;
+		}
+	}
+#ifdef FILE_NODE_DBG
+	PR_BOOTMSG("ttype_busid_ext input data [%s]\n",ttype_busid_ext);
+
+	int i;
+	PR_BOOTMSG("ttype_busid_ext save data\n");
+	for (i=0;i<BM_COUNTER_MAX;i++) {
+		PR_BOOTMSG("id[%d](busid,idMask)=(%X,%X)\n", i+1, ttype_busid_val[i], ttype_idMask_val[i]);
+	}
+#endif
+	return n;
+}
+
+static ssize_t ttype_busid_ext_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%s\n", ttype_busid_ext);
+}
+
+
+static unsigned int  ttype_chn_rank_sel_val[BM_COUNTER_MAX];
+char ttype_chn_rank_sel[FILE_NODE_DATA_LEN] = {'\0'};
+
+static void _clear_ttype_chn_rank_sel(void) {
+	int i;
+
+	for (i=0;i<BM_COUNTER_MAX;i++) {
+		ttype_chn_rank_sel_val[i] = 0xF;
+	}
+	ttype_chn_rank_sel[0] = '\0';
+}
+
+static ssize_t ttype_chn_rank_sel_store(struct kobject *kobj,
+		struct kobj_attribute *attr,
+		const char *buf,
+		size_t n)
+{
+	char *token, *cur= ttype_chn_rank_sel;
+	char *_id = NULL, *_chn_rank = NULL;
+	int id_int = 0, chn_rank_int = 0;
+
+	_clear_ttype_chn_rank_sel();
+
+	snprintf(ttype_chn_rank_sel, FILE_NODE_DATA_LEN, "%s", buf);
+	ttype_chn_rank_sel[n-1]='\0';
+
+	while (cur != NULL) {
+		token = strsep(&cur, delim_comma);
+		PR_BOOTMSG("token: %s\n",token);
+		/*token EX: 4:f , 5:C (ID,chn_rnk_sel)*/
+
+		_id = strsep(&token, delim_coclon); // ID
+		_chn_rank = strsep(&token, delim_coclon);
+
+		PR_BOOTMSG("_id[%s] _chn_rank[%s]\n",_id, _chn_rank);
+
+		if (_id == NULL || _chn_rank == NULL) {
+			PR_BOOTMSG("err (ttype_chn_rank_sel): _id[%s] _chn_rank[%s], para can't be NULL\n",_id, _chn_rank);
+			_clear_ttype_chn_rank_sel();
+			return -EINVAL;
+		}
+
+		
+		if (kstrtouint(_id, 0, &id_int) != 0) {
+			PR_BOOTMSG("_id[%s] trans to hex err\n",_id);
+			_clear_ttype_chn_rank_sel();
+			return -EINVAL;
+		}
+		if (kstrtouint(_chn_rank, 0, &chn_rank_int) != 0) {
+			PR_BOOTMSG("_chn_rank[%s] trans to hex err\n",_id);
+			_clear_ttype_chn_rank_sel();
+			return -EINVAL;
+		}
+
+		id_int = id_int -1;
+
+		if ( id_int >= 0 && id_int < BM_COUNTER_MAX) {
+			ttype_chn_rank_sel[id_int] = chn_rank_int;
+
+		} else {
+			PR_BOOTMSG("id[%d] exceed the range, it must be 1~%d\n",id_int+1, BM_COUNTER_MAX);
+			_clear_ttype_chn_rank_sel();
+			return -EINVAL;
+		}
+	}
+
+#ifdef FILE_NODE_DBG
+	PR_BOOTMSG("ttype_chn_rank_sel input data [%s]\n",ttype_chn_rank_sel);
+
+	int i;
+	PR_BOOTMSG("wsct_chn_rank_sel_val save data\n");
+	for (i=0;i<BM_COUNTER_MAX;i++) {
+		PR_BOOTMSG("id[%d]=%X\n",i+1,ttype_chn_rank_sel[i]);
+	}
+#endif
+	return n;
+}
+
+static ssize_t ttype_chn_rank_sel_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%s\n", ttype_chn_rank_sel);
+}
+
+
+static unsigned int  ttype_byte_low_bnd_val[BM_COUNTER_MAX];
+static unsigned int  ttype_byte_up_bnd_val[BM_COUNTER_MAX];
+static unsigned int  ttype_byte_bnd_dis[BM_COUNTER_MAX];
+char ttype_burst_range[FILE_NODE_DATA_LEN] = {'\0'};
+
+static void _clear_ttype_burst_range(void) {
+	int i;
+
+	for (i=0;i<BM_COUNTER_MAX;i++) {
+		ttype_byte_low_bnd_val[i] = 0x0;
+		ttype_byte_up_bnd_val[i] = 0x1FF;
+		ttype_byte_bnd_dis[i] = 1;
+	}
+	ttype_burst_range[0] = '\0';
+}
+
+static ssize_t ttype_burst_range_store(struct kobject *kobj,
+		struct kobj_attribute *attr,
+		const char *buf,
+		size_t n)
+{
+	char *token, *cur= ttype_burst_range;
+	char *_id = NULL, *_low_bnd = NULL, *_up_bnd = NULL;
+	int id_int = 0, low_bnd_int = 0, up_bnd_int = 0;
+
+	_clear_ttype_burst_range();
+	
+	snprintf(ttype_burst_range, FILE_NODE_DATA_LEN, "%s", buf);
+	ttype_burst_range[n-1]='\0';
+
+
+	while (cur != NULL) {
+		token = strsep(&cur, delim_comma);
+		PR_BOOTMSG("token: %s\n",token);
+		/*token EX: 4:f , 5:C (ID,chn_rnk_sel)*/
+
+		_id = strsep(&token, delim_coclon); // ID
+		_low_bnd = strsep(&token, delim_coclon);
+		_up_bnd = strsep(&token, delim_coclon);
+
+		PR_BOOTMSG("_id[%s] _low_bnd[%s] _up_bnd[%s]\n",_id, _low_bnd, _up_bnd);
+
+		if (_id == NULL || _low_bnd == NULL || _up_bnd == NULL) {
+			PR_BOOTMSG("err (ttype_burst_range): _id[%s] _low_bnd[%s] _up_bnd[%s], para can't be NULL\n",
+				        _id, _low_bnd, _up_bnd);
+			_clear_ttype_burst_range();
+			return -EINVAL;
+		}
+
+		if (kstrtouint(_id, 0, &id_int) != 0) {
+			PR_BOOTMSG("_id[%s] trans to hex err\n",_id);
+			_clear_ttype_burst_range();
+			return -EINVAL;
+		}
+		if (kstrtouint(_low_bnd, 0, &low_bnd_int) != 0) {
+			PR_BOOTMSG("_low_bnd[%s] trans to hex err\n",_id);
+			_clear_ttype_burst_range();
+			return -EINVAL;
+		}
+		if (kstrtouint(_up_bnd, 0, &up_bnd_int) != 0) {
+			PR_BOOTMSG("_up_bnd[%s] trans to hex err\n",_id);
+			_clear_ttype_burst_range();
+			return -EINVAL;
+		}
+
+		id_int = id_int - 1;
+		if ( id_int >= 0 && id_int < BM_COUNTER_MAX) {
+			ttype_byte_low_bnd_val[id_int] = low_bnd_int;
+			ttype_byte_up_bnd_val[id_int] = up_bnd_int;
+			ttype_byte_bnd_dis[id_int] = 0;
+		} else {
+			PR_BOOTMSG("id[%d] exceed the range, it must be 1~%d\n",id_int, BM_COUNTER_MAX);
+			_clear_ttype_burst_range();
+			return -EINVAL;
+		}
+	}
+
+#ifdef FILE_NODE_DBG
+	PR_BOOTMSG("ttype_burst_range_store input data [%s]\n",ttype_burst_range);
+
+	int i;
+	PR_BOOTMSG("ttype_burst_range save data\n");
+	for (i=0;i<BM_COUNTER_MAX;i++) {
+		PR_BOOTMSG("id[%d](low_bnd,up_bnd)=(%X,%X)\n",i+1,ttype_byte_low_bnd_val[i],ttype_byte_up_bnd_val[i]);
+	}
+#endif
+	return n;
+}
+
+static ssize_t ttype_burst_range_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%s\n", ttype_burst_range);
+}
+
+static int reserve_wsct_setting;
+DECLARE_KOBJ_ATTR_INT(reserve_wsct_setting, reserve_wsct_setting);
+
+static void _clear_setting(void) {
+	/*clear all file node para here*/
+	
+	PR_BOOTMSG("clear EMI file node setting\n");
+
+	_clear_msel_group_ext();
+	_clear_wsct_rw();
+	_clear_wsct_high_priority_enable();
+	_clear_wsct_busid();
+	_clear_wsct_chn_rank_sel();
+	_clear_wsct_burst_range();
+
+	_clear_tsct_busid_enable();
+	_clear_ttype_high_priority_ext();
+	_clear_ttype_busid_ext();
+	_clear_ttype_chn_rank_sel();
+	_clear_ttype_burst_range();
+	reserve_wsct_setting = 0;
+
+
+
+	emi_TP_busfiltr_enable = 0;
+
+	high_priority_filter = 0x0;
+	rwtype = BM_BOTH_READ_WRITE;
+	dramc_pdir_enable = 1;
+
+
+	msel_enable = 0;
+	msel_group1 = BM_MASTER_ALL;
+	msel_group2 = BM_MASTER_ALL;
+	msel_group3 = BM_MASTER_ALL;
+
+
+	bw_limiter_enable = BM_BW_LIMITER_ENABLE;
+	ttype1_16_en = BM_TTYPE1_16_DISABLE;
+	ttype17_21_en = BM_TTYPE17_21_DISABLE;
+
+}
+
+static ssize_t clear_setting_store(struct kobject *kobj,
+			struct kobj_attribute *attr,
+			const char *buf,
+			size_t n)
+{
+	int value;
+
+	if ((n == 0) || (buf == NULL))
+		return -EINVAL;
+
+	if (kstrtoint(buf, 0, &value) != 0)
+		return -EINVAL;
+
+	if (value == 1)
+		_clear_setting();
+
+	return n;
+}
+
+static struct kobj_attribute clear_setting_attr = __ATTR_WO(clear_setting); // OK
+static struct kobj_attribute msel_group_ext_attr = __ATTR(msel_group_ext, 0664, msel_group_ext_show, msel_group_ext_store); //OK
+static struct kobj_attribute wsct_rw_attr = __ATTR(wsct_rw, 0664, wsct_rw_show, wsct_rw_store);
+static struct kobj_attribute wsct_high_priority_enable_attr = __ATTR(wsct_high_priority_enable, 0664, wsct_high_priority_enable_show, wsct_high_priority_enable_store);
+static struct kobj_attribute wsct_busid_attr = __ATTR(wsct_busid, 0664, wsct_busid_show, wsct_busid_store);
+static struct kobj_attribute wsct_chn_rank_sel_attr = __ATTR(wsct_chn_rank_sel, 0664, wsct_chn_rank_sel_show, wsct_chn_rank_sel_store);
+static struct kobj_attribute wsct_burst_range_attr = __ATTR(wsct_burst_range, 0664, wsct_burst_range_show, wsct_burst_range_store);
+static struct kobj_attribute tsct_busid_enable_attr = __ATTR(tsct_busid_enable, 0664, tsct_busid_enable_show, tsct_busid_enable_store);
+static struct kobj_attribute ttype_high_priority_ext_attr = __ATTR(ttype_high_priority_ext, 0664, ttype_high_priority_ext_show, ttype_high_priority_ext_store);
+static struct kobj_attribute ttype_busid_ext_attr = __ATTR(ttype_busid_ext, 0664, ttype_busid_ext_show, ttype_busid_ext_store);
+static struct kobj_attribute ttype_chn_rank_sel_attr = __ATTR(ttype_chn_rank_sel, 0664, ttype_chn_rank_sel_show, ttype_chn_rank_sel_store);
+static struct kobj_attribute ttype_burst_range_attr = __ATTR(ttype_burst_range, 0664, ttype_burst_range_show, ttype_burst_range_store);
+
+
+
+
+
+
+/**/
+#define KOBJ_ATTR_ITEM_SERIAL_FNODE(nr) \
+	do { \
+		KOBJ_ATTR_ITEM(ttype ## nr ## _master); \
+		KOBJ_ATTR_ITEM(ttype ## nr ## _nbeat); \
+		KOBJ_ATTR_ITEM(ttype ## nr ## _nbyte); \
+		KOBJ_ATTR_ITEM(ttype ## nr ## _burst); \
+		KOBJ_ATTR_ITEM(ttype ## nr ## _busid); \
+		KOBJ_ATTR_ITEM(ttype ## nr ## _rw); \
+	} while (0)
+
+#define KOBJ_ATTR_LIST \
+	do { \
+		KOBJ_ATTR_ITEM(high_priority_filter); \
+		KOBJ_ATTR_ITEM(emi_TP_busfiltr_enable); \
+		KOBJ_ATTR_ITEM(msel_enable); \
+		KOBJ_ATTR_ITEM(msel_group1); \
+		KOBJ_ATTR_ITEM(msel_group2); \
+		KOBJ_ATTR_ITEM(msel_group3); \
+		KOBJ_ATTR_ITEM(rwtype); \
+		KOBJ_ATTR_ITEM(ttype17_21_en); \
+		KOBJ_ATTR_ITEM(ttype1_16_en); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(1); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(2); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(3); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(4); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(5); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(6); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(7); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(8); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(9); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(10); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(11); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(12); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(13); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(14); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(15); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(16); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(17); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(18); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(19); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(20); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(21); \
+		KOBJ_ATTR_ITEM(bw_limiter_enable); \
+		KOBJ_ATTR_ITEM(dramc_pdir_enable); \
+		KOBJ_ATTR_ITEM(clear_setting);\
+		KOBJ_ATTR_ITEM(msel_group_ext);\
+		KOBJ_ATTR_ITEM(wsct_rw);\
+		KOBJ_ATTR_ITEM(wsct_high_priority_enable);\
+		KOBJ_ATTR_ITEM(wsct_busid);\
+		KOBJ_ATTR_ITEM(wsct_chn_rank_sel);\
+		KOBJ_ATTR_ITEM(wsct_burst_range);\
+		KOBJ_ATTR_ITEM(tsct_busid_enable);\
+		KOBJ_ATTR_ITEM(ttype_high_priority_ext);\
+		KOBJ_ATTR_ITEM(ttype_busid_ext);\
+		KOBJ_ATTR_ITEM(ttype_chn_rank_sel);\
+		KOBJ_ATTR_ITEM(ttype_burst_range);\
+		KOBJ_ATTR_ITEM(reserve_wsct_setting);\
+	} while (0)
+
+
+
+/*======================================================================*/
+/*	EMI Operations							*/
+/*======================================================================*/
+static void emi_init(void)
+{
+	unsigned int bmrw0_val, bmrw1_val, i, enable;
+	/*unsigned int msel_group_val[4];*/
+
+	/*save origianl EMI config*/
+	MET_BM_SaveCfg();
+
+	/* get dram channel number */
+	dram_chann_num = MET_EMI_GetDramChannNum();
+
+	/* Init. EMI bus monitor */
+	MET_BM_SetReadWriteType(rwtype);
+
+	/*handle the ori */
+
+	if (ttype1_16_en != BM_TTYPE1_16_ENABLE) {
+		MET_BM_SetLatencyCounter(1);    /*enable latency count*/
+	}
+	else {
+		MET_BM_SetLatencyCounter(0);    /*disable latency count*/
+
+		for (i = 1; i <= 16; i++) {
+			MET_BM_SetMonitorCounter(i,
+						 ttype_master_val[i - 1],
+						 ttype_nbeat_val[i - 1] |
+						 ttype_nbyte_val[i - 1] |
+						 ttype_burst_val[i - 1]);
+		}
+	}
+	
+	if (ttype17_21_en == BM_TTYPE17_21_ENABLE) {
+		for (i = 17; i <= 21; i++) {
+			MET_BM_SetMonitorCounter(i,
+						 ttype_master_val[i - 1],
+						 ttype_nbeat_val[i - 1] |
+						 ttype_nbyte_val[i - 1] |
+						 ttype_burst_val[i - 1]);
+		}
+	}
+
+	PR_BOOTMSG("[%s]reserve_wsct_setting=%d\n",__func__,reserve_wsct_setting);
+
+	if (reserve_wsct_setting == 0) {
+		/* wsct 0 : total-all*/
+		msel_group_ext_val[0] = BM_MASTER_ALL;
+		wsct_rw_val[0] = BM_WSCT_RW_RWBOTH;
+		WSCT_HPRI_DIS[0] = 1;
+		WSCT_HPRI_SEL[0] = 0xF;
+		wsct_busid_val[0] = 0xFFFFF;
+		wsct_idMask_val[0] = 0x1FFF;
+		wsct_chn_rank_sel_val[0] = 0xF;
+		wsct_byte_bnd_dis[0] = 1;
+
+		/* wsct 4 : total-ultra*/
+		msel_group_ext_val[4] = BM_MASTER_ALL;
+		wsct_rw_val[4] = BM_WSCT_RW_RWBOTH;		
+		WSCT_HPRI_DIS[4] = 0;
+		WSCT_HPRI_SEL[4] = 0x8;  /* ultra */
+		wsct_busid_val[4] = 0xFFFFF;
+		wsct_idMask_val[4] = 0x1FFF;
+		wsct_chn_rank_sel_val[4] = 0xF;
+		wsct_byte_bnd_dis[4] = 1;
+
+		/* wsct 5 : total-pre_ultra*/
+		msel_group_ext_val[5] = BM_MASTER_ALL;
+		wsct_rw_val[5] = BM_WSCT_RW_RWBOTH;
+		WSCT_HPRI_DIS[5] = 0;
+		WSCT_HPRI_SEL[5] = 0x4; /* pre_ultra */
+		wsct_busid_val[5] = 0xFFFFF;
+		wsct_idMask_val[5] = 0x1FFF;
+		wsct_chn_rank_sel_val[5] = 0xF;
+		wsct_byte_bnd_dis[5] = 1;
+	}
+
+	if (msel_enable) {
+		/* if ole file node set, use the value */
+		if ( msel_group1 != BM_MASTER_ALL )
+			msel_group_ext_val[1] = msel_group1;
+
+		if ( msel_group2 != BM_MASTER_ALL )
+			msel_group_ext_val[2] = msel_group2;
+
+		if ( msel_group3 != BM_MASTER_ALL )
+			msel_group_ext_val[3] = msel_group3;
+
+	} else {
+		for ( i=1; i<=3; i++) {
+			msel_group_ext_val[i] = BM_MASTER_ALL;
+		}
+	}
+
+	MET_BM_SetWSCT_master_rw(msel_group_ext_val, wsct_rw_val);
+	MET_BM_SetWSCT_high_priority(WSCT_HPRI_DIS, WSCT_HPRI_SEL);
+	MET_BM_SetWSCT_busid_idmask(wsct_busid_val, wsct_idMask_val);
+	MET_BM_SetWSCT_chn_rank_sel(wsct_chn_rank_sel_val);
+	MET_BM_SetWSCT_burst_range(wsct_byte_bnd_dis, wsct_byte_low_bnd_val, wsct_byte_up_bnd_val);
+	MET_BM_SetTSCT_busid_enable(tsct_busid_enable_val);
+
+	MET_BM_SetTtype_high_priority_sel(high_priority_filter, TTYPE_HPRI_SEL);
+	MET_BM_SetTtype_busid_idmask(ttype_busid_val, ttype_idMask_val, ttype1_16_en, ttype17_21_en);
+	MET_BM_SetTtype_chn_rank_sel(ttype_chn_rank_sel_val);
+	MET_BM_SetTtype_burst_range(ttype_byte_bnd_dis, ttype_byte_low_bnd_val, ttype_byte_up_bnd_val);
+
+
+	bmrw0_val = 0;
+	for (i = 0; i < 16; i++)
+		bmrw0_val |= (ttype_rw_val[i] << (i * 2));
+
+	bmrw1_val = 0;
+	for (i = 16; i < 21; i++)
+		bmrw1_val |= (ttype_rw_val[i] << ((i-16) * 2));
+
+	MET_BM_SetTtypeCounterRW(bmrw0_val, bmrw1_val);
+
+}
+
+
+static void emi_uninit(void)
+{
+	MET_BM_RestoreCfg();
+}
+
+
+static inline int do_emi(void)
+{
+	return met_sspm_emi.mode;
+}
+
+
+
+
+
+
+
+/*======================================================================*/
+/*	MET Device Operations						*/
+/*======================================================================*/
+static int emi_inited;
+
+static int met_emi_create(struct kobject *parent)
+{
+	int ret = 0;
+	int i;
+
+	for (i = 0; i < 21; i++) {
+		ttype_master_val[i] = BM_MASTER_M0;
+		ttype_nbeat_val[i] = BM_TRANS_TYPE_1BEAT;
+		ttype_nbyte_val[i] = BM_TRANS_TYPE_8Byte;
+		ttype_burst_val[i] = BM_TRANS_TYPE_BURST_INCR;
+		ttype_busid_val[i] = 0xfffff;   /*default disable ttype bus sel if busid > 0xff_ff */
+		ttype_rw_val[i] =  BM_TRANS_RW_DEFAULT;
+	}
+
+	_clear_msel_group_ext();
+	_clear_wsct_rw();
+	_clear_wsct_high_priority_enable();
+	_clear_wsct_busid();
+	_clear_wsct_chn_rank_sel();
+	_clear_wsct_burst_range();
+
+	_clear_tsct_busid_enable();
+	_clear_ttype_high_priority_ext();
+	_clear_ttype_high_priority_ext();
+	_clear_ttype_busid_ext();
+	_clear_ttype_chn_rank_sel();
+	_clear_ttype_burst_range();
+
+	reserve_wsct_setting = 0;
+
+
+	ret = MET_BM_Init();
+	if (ret != 0) {
+		pr_notice("MET_BM_Init failed!!!\n");
+		ret = 0;        /* will retry later */
+	} else {
+		emi_inited = 1;
+	}
+
+	kobj_emi = parent;
+
+#define KOBJ_ATTR_ITEM(attr_name) \
+	do { \
+		ret = sysfs_create_file(kobj_emi, &attr_name ## _attr.attr); \
+		if (ret != 0) { \
+			pr_notice("Failed to create " #attr_name " in sysfs\n"); \
+			return ret; \
+		} \
+	} while (0)
+	KOBJ_ATTR_LIST;
+#undef  KOBJ_ATTR_ITEM
+
+	return ret;
+}
+
+
+static void met_emi_delete(void)
+{
+#define	KOBJ_ATTR_ITEM(attr_name) \
+	sysfs_remove_file(kobj_emi, &attr_name##_attr.attr)
+	if (kobj_emi != NULL) {
+		KOBJ_ATTR_LIST;
+		kobj_emi = NULL;
+	}
+#undef	KOBJ_ATTR_ITEM
+
+	if (emi_inited)
+		MET_BM_DeInit();
+}
+
+
+
+static void met_emi_resume(void)
+{
+	if (!do_emi())
+		return;
+
+	emi_init();
+}
+
+
+static const char help[] = "  --emi                                 monitor EMI banwidth\n";
+static int emi_print_help(char *buf, int len)
+{
+	return snprintf(buf, PAGE_SIZE, help);
+}
+
+
+#define TTYPE_NAME_STR_LEN  64
+/* static char ttype_name[21][TTYPE_NAME_STR_LEN]; */
+static int emi_print_header(char *buf, int len)
+{
+	int ret = 0;
+/*	int ret_m[21]; */
+	int i = 0;
+
+#if 1 /* move to AP side print header */
+/*#ifndef CONFIG_MTK_TINYSYS_SSPM_SUPPORT*/
+	unsigned int dram_data_rate_MHz;
+	unsigned int DRAM_TYPE;
+	unsigned int base_clock_rate;
+#endif
+
+
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+		"met-info [000] 0.0: met_emi_wsct_amount: %d\n",WSCT_AMOUNT);
+
+	/* master selection header */
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+		"met-info [000] 0.0: met_emi_msel: %x,%x,%x\n",
+		msel_group_ext_val[1] & BM_MASTER_ALL,
+		msel_group_ext_val[2] & BM_MASTER_ALL,
+		msel_group_ext_val[3] & BM_MASTER_ALL);
+	
+	/*Ttype RW type header*/
+	PR_BOOTMSG("rwtype=%d\n",rwtype);
+	ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_emi_rw_cfg: ");
+	if (rwtype == BM_READ_ONLY)
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "R");
+	else if (rwtype == BM_WRITE_ONLY)
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "W");
+	else
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "BOTH");
+
+	for (i = 0; i < 21; i++) {
+		if (ttype_rw_val[i] == BM_TRANS_RW_DEFAULT)
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, ",DEFAULT");
+		else if (ttype_rw_val[i] == BM_TRANS_RW_READONLY)
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, ",R");
+		else if (ttype_rw_val[i] == BM_TRANS_RW_WRITEONLY)
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, ",W");
+		else    /*BM_TRANS_RW_RWBOTH*/
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, ",BOTH");
+	}
+	ret += snprintf(buf + ret, PAGE_SIZE - ret, "\n");
+
+	/*ultra header*/
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"met-info [000] 0.0: met_emi_ultra_filter: %x\n", high_priority_filter);
+
+	/* ttype header */
+	if (ttype17_21_en == BM_TTYPE17_21_ENABLE) {
+		int i = 0;
+		int j = 0;
+
+		/* ttype master list */
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_emi_ttype_master_list: ");
+		for (i = 0; i < 21; i++) {
+			for (j = 0; j < ARRAY_SIZE(ttype_master_list_item); j++) {
+				if (ttype_master_val[i] == ttype_master_list_item[j].key) {
+					ret += snprintf(buf + ret, PAGE_SIZE - ret, "%s,", ttype_master_list_item[j].val);
+				}
+			}
+		}
+		/* remove the last comma */
+		snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+		/* ttype busid list */
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_emi_ttype_busid_list: ");
+		for (i = 0; i < 21; i++)
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, "%x,", ttype_busid_val[i]);
+
+		snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+		/* ttype nbeat list */
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_emi_ttype_nbeat_list: ");
+		for (i = 0; i < 21; i++) {
+			for (j = 0; j < ARRAY_SIZE(ttype_nbeat_list_item); j++) {
+				if (ttype_nbeat_val[i] == ttype_nbeat_list_item[j].key) {
+					ret += snprintf(buf + ret, PAGE_SIZE - ret, "%d,", ttype_nbeat_list_item[j].val);
+				}
+			}
+		}
+		snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+		/* ttype nbyte list */
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_emi_ttype_nbyte_list: ");
+		for (i = 0; i < 21; i++) {
+			for (j = 0; j < ARRAY_SIZE(ttype_nbyte_list_item); j++) {
+				if (ttype_nbyte_val[i] == ttype_nbyte_list_item[j].key) {
+					ret += snprintf(buf + ret, PAGE_SIZE - ret, "%d,", ttype_nbyte_list_item[j].val);
+				}
+			}
+		}
+		snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+		/* ttype burst list */
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_emi_ttype_burst_list: ");
+		for (i = 0; i < 21; i++) {
+			for (j = 0; j < ARRAY_SIZE(ttype_burst_list_item); j++) {
+				if (ttype_burst_val[i] == ttype_burst_list_item[j].key) {
+					ret += snprintf(buf + ret, PAGE_SIZE - ret, "%s,", ttype_burst_list_item[j].val);
+				}
+			}
+		}
+		snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+	}
+	/* ttype enable */
+	ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_emi_ttype_enable: %d,%d\n",ttype1_16_en, ttype17_21_en);
+
+
+#if 1 /*SEDA 3.5*/
+
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+		"met-info [000] 0.0: met_emi_msel_ext: %x,%x,%x\n",
+		msel_group_ext_val[0] & BM_MASTER_ALL,
+		msel_group_ext_val[4] & BM_MASTER_ALL,
+		msel_group_ext_val[5] & BM_MASTER_ALL);
+
+	ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_emi_wsct_rw: ");
+
+	for (i=0;i<WSCT_AMOUNT;i++) {
+		if (wsct_rw_val[i] == BM_WSCT_RW_RWBOTH)
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, "RW,");
+		else if (wsct_rw_val[i] == BM_WSCT_RW_READONLY)
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, "R,");
+		else if (wsct_rw_val[i] == BM_WSCT_RW_WRITEONLY)
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, "W,");
+		else    /*disable*/
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, "NONE,");
+	}
+	snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+
+	ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_emi_wsct_HPRI_DIS: ");
+	for (i=0;i<WSCT_AMOUNT;i++) {
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "%d,",WSCT_HPRI_DIS[i]);
+	}
+	snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+
+	ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_emi_wsct_HPRI_SEL: ");
+	for (i=0;i<WSCT_AMOUNT;i++) {
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "%x,",WSCT_HPRI_SEL[i]);
+	}
+	snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+	ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_emi_wsct_busid: ");
+	for (i=0;i<WSCT_AMOUNT;i++) {
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "%x,",wsct_busid_val[i]);
+	}
+	snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+
+	ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_emi_wsct_idMask: ");
+	for (i=0;i<WSCT_AMOUNT;i++) {
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "%x,",wsct_idMask_val[i]);
+	}
+	snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+	ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: wsct_chn_rank_sel: ");
+	for (i=0;i<WSCT_AMOUNT;i++) {
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "%x,",wsct_chn_rank_sel_val[i]);
+	}
+	snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+	ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: wsct_byte_bnd_dis: ");
+	for (i=0;i<WSCT_AMOUNT;i++) {
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "%d,",wsct_byte_bnd_dis[i]);
+	}
+	snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+
+	ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: wsct_byte_low_bnd: ");
+	for (i=0;i<WSCT_AMOUNT;i++) {
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "%x,",wsct_byte_low_bnd_val[i]);
+	}
+	snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+	ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: wsct_byte_up_bnd: ");
+	for (i=0;i<WSCT_AMOUNT;i++) {
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "%x,",wsct_byte_up_bnd_val[i]);
+	}
+	snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+	ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: tsct_busid_enable: ");
+	for (i=0;i<TSCT_AMOUNT;i++) {
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "%d,",tsct_busid_enable_val[i]);
+	}
+	snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+	/***************************** ttype ****************************************/
+	if (ttype17_21_en == BM_TTYPE17_21_ENABLE) {
+
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: TTYPE_HPRI_SEL: ");
+		for (i=0;i<BM_COUNTER_MAX;i++) {
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, "%x,",TTYPE_HPRI_SEL[i]);
+		}
+		snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: ttype_idMask: ");
+		for (i=0;i<BM_COUNTER_MAX;i++) {
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, "%x,",ttype_idMask_val[i]);
+		}
+		snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: ttype_chn_rank_sel: ");
+		for (i=0;i<BM_COUNTER_MAX;i++) {
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, "%x,",ttype_chn_rank_sel_val[i]);
+		}
+		snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: ttype_byte_bnd_dis: ");
+		for (i=0;i<BM_COUNTER_MAX;i++) {
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, "%d,",ttype_byte_bnd_dis[i]);
+		}
+		snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: ttype_byte_low_bnd_val: ");
+		for (i=0;i<BM_COUNTER_MAX;i++) {
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, "%x,",ttype_byte_low_bnd_val[i]);
+		}
+		snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: ttype_byte_up_bnd_val: ");
+		for (i=0;i<BM_COUNTER_MAX;i++) {
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, "%x,",ttype_byte_up_bnd_val[i]);
+		}
+		snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+	}
+#endif
+
+	/*IP version*/
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"met-info [000] 0.0: DRAMC_VER: %d\n", DRAMC_VER);
+
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"met-info [000] 0.0: EMI_VER: %d.%d\n", EMI_VER_MAJOR, EMI_VER_MINOR);
+
+#if 1 /* SEDA3.5 header print move to AP side */
+
+	dram_chann_num = MET_EMI_GetDramChannNum();
+
+	if (!get_cur_ddr_ratio_symbol)
+		PR_BOOTMSG("[%s][%d]get_cur_ddr_ratio_symbol = NULL , use the TYPE_LPDDR4 get_cur_ddr_ratio_symbol\n", __func__, __LINE__);
+
+	if (get_ddr_type_symbol) {
+		DRAM_TYPE = get_ddr_type_symbol();
+
+		base_clock_rate = MET_EMI_Get_BaseClock_Rate();
+
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_dram_type: %d\n", DRAM_TYPE);
+
+		if ((DRAM_TYPE == 2) || (DRAM_TYPE == 3))
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_dram_chann_num_header: %d,%d,%d,%d\n",
+					dram_chann_num, base_clock_rate,
+					DRAM_IO_BUS_WIDTH_LP4, DRAM_DATARATE);
+		else
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_dram_chann_num_header: %d,%d,%d,%d\n",
+					dram_chann_num, base_clock_rate,
+					DRAM_IO_BUS_WIDTH_LP3, DRAM_DATARATE);
+	} else
+		METERROR("[%s][%d]get_ddr_type_symbol = NULL , use the TYPE_LPDDR3 setting\n", __func__, __LINE__);
+
+
+	/* met_emi_clockrate */
+	if (get_dram_data_rate_symbol) {
+		dram_data_rate_MHz = get_dram_data_rate_symbol();
+	} else {
+		METERROR("get_dram_data_rate_symbol = NULL\n");
+		dram_data_rate_MHz = 0;
+	}
+
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"met-info [000] 0.0: met_dram_clockrate: %d\n",
+			dram_data_rate_MHz);
+
+
+	/*dram bank num*/
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"met-info [000] 0.0: met_dram_rank_num_header: %u,%u\n", MET_EMI_GetDramRankNum(),
+				MET_EMI_GetDramRankNum());
+
+	/* ms_emi header */
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"# ms_emi: TS0,TS1,GP0_WSCT,GP1_WSCT,GP2_WSCT,GP3_WSCT,");
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"M0_LATENCY,M1_LATENCY,M2_LATENCY,M3_LATENCY,M4_LATENCY,M5_LATENCY,M6_LATENCY,M7_LATENCY,");
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"M0_TRANS,M1_TRANS,M2_TRANS,M3_TRANS,M4_TRANS,M5_TRANS,M6_TRANS,M7_TRANS,");
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"BACT,BSCT,BCNT,WACT,DCM_CTRL,TACT,");
+
+	for (i = 0; i < dram_chann_num; i++) {
+		if (i != 0)
+			ret += snprintf(buf + ret, PAGE_SIZE - ret,
+					",");
+		ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"PageHit_%d,PageMiss_%d,InterBank_%d,Idle_%d,", i, i, i, i);
+		ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"mr4_%d,refresh_pop_%d,freerun_26m_%d,", i, i, i);
+		ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"read_bytes_%d,write_bytes_%d", i, i);
+	}
+	ret += snprintf(buf + ret, PAGE_SIZE - ret, "\n");
+
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"met-info [000] 0.0: met_emi_header: TS0,TS1,GP0_WSCT,GP1_WSCT,GP2_WSCT,GP3_WSCT,");
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"M0_LATENCY,M1_LATENCY,M2_LATENCY,M3_LATENCY,M4_LATENCY,M5_LATENCY,M6_LATENCY,M7_LATENCY,");
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"M0_TRANS,M1_TRANS,M2_TRANS,M3_TRANS,M4_TRANS,M5_TRANS,M6_TRANS,M7_TRANS,");
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"BACT,BSCT,BCNT,WACT,DCM_CTRL,TACT,");
+
+	for (i = 0; i < dram_chann_num; i++) {
+		if (i != 0)
+			ret += snprintf(buf + ret, PAGE_SIZE - ret,
+					",");
+		ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"PageHit_%d,PageMiss_%d,InterBank_%d,Idle_%d,", i, i, i, i);
+		ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"mr4_%d,refresh_pop_%d,freerun_26m_%d,", i, i, i);
+		ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"read_bytes_%d,write_bytes_%d", i, i);
+	}
+	ret += snprintf(buf + ret, PAGE_SIZE - ret, "\n");
+
+	/*TSCT header*/
+	if (emi_tsct_enable == 1) {
+		ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"met-info [000] 0.0: ms_emi_tsct_header: ms_emi_tsct,");
+		ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"tsct1,tsct2,tsct3\n");
+	}
+
+	/*MDCT header*/
+	if (emi_mdct_enable == 1) {
+		ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"met-info [000] 0.0: ms_emi_mdct_header: ms_emi_mdct,");
+		ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"RD_ULTRA,RD_MDMCU\n");
+	}
+
+	/* met_bw_limiter_header */
+	if (bw_limiter_enable == BM_BW_LIMITER_ENABLE) {
+		ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"met-info [000] 0.0: met_bw_limiter_header: CLK,");
+		ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"ARBA,ARBB,ARBC,ARBD,ARBE,ARBF,ARBG,ARBH,BWCT0,BWCT1,BWCT2,BWCT3,BWCT4,BWST0,BWST1,BWCT0_2ND,BWCT1_2ND,BWST_2ND\n");
+	}
+
+	/* DRAM DVFS header */
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"met-info [000] 0.0: DRAM_DVFS_header: datarate(MHz)\n");
+
+	/*PDIR met_dramc_header*/
+	if (dramc_pdir_enable == 1 && DRAMC_VER >= 2 ) {
+		ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"met-info [000] 0.0: met_dramc_header: ");
+		for (i = 0; i < dram_chann_num; i++) {
+			if (i != 0)
+				ret += snprintf(buf + ret, PAGE_SIZE - ret,
+						",");
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, "freerun_26m_%d,", i);
+			ret += snprintf(buf + ret, PAGE_SIZE - ret,
+					"rk0_pre_sb_%d,rk0_pre_pd_%d,rk0_act_sb_%d,rk0_act_pd_%d,", i, i, i, i);
+			ret += snprintf(buf + ret, PAGE_SIZE - ret,
+					"rk1_pre_sb_%d,rk1_pre_pd_%d,rk1_act_sb_%d,rk1_act_pd_%d,", i, i, i, i);
+			ret += snprintf(buf + ret, PAGE_SIZE - ret,
+					"rk2_pre_sb_%d,rk2_pre_pd_%d,rk2_act_sb_%d,rk2_act_pd_%d", i, i, i, i);
+		}
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "\n");
+	}
+
+	/* DRS header */
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"met-info [000] 0.0: emi_drs_header: ch0_RANK1_GP(%%),ch0_RANK1_SF(%%),ch0_ALL_SF(%%),ch1_RANK1_GP(%%),ch1_RANK1_SF(%%),ch1_ALL_SF(%%)\n");
+#endif
+
+	return ret;
+}
+
+
+
+static int ondiemet_emi_print_header(char *buf, int len)
+{
+	return emi_print_header(buf, len);
+}
+
+
+
+static void MET_BM_IPI_REGISTER_CB(void)
+{
+	int ret, i;
+	unsigned int rdata;
+	unsigned int ipi_buf[4];
+
+	for (i = 0; i < 4; i++)
+		ipi_buf[i] = 0;
+
+	if (sspm_buf_available == 1) {
+		ipi_buf[0] = MET_MAIN_ID | (MID_EMI << MID_BIT_SHIFT) | MET_ARGU | SET_REGISTER_CB;
+		ret = sspm_ipi_send_sync(IPI_ID_MET, IPI_OPT_WAIT, (void *)ipi_buf, 0, &rdata, 1);
+	}
+}
+
+
+static void MET_BM_IPI_configs(void)
+{
+	int ret, i;
+	unsigned int rdata;
+	unsigned int ipi_buf[4];
+
+	for (i = 0; i < 4; i++)
+		ipi_buf[i] = 0;
+
+	if (sspm_buf_available == 1) {
+		ipi_buf[0] = MET_MAIN_ID | (MID_EMI << MID_BIT_SHIFT) | MET_ARGU | SET_EBM_CONFIGS1;
+		ipi_buf[2] = EMI_VER_MAJOR << 24 | EMI_VER_MINOR << 16 | DRAMC_VER << 8 | 0;
+		ret = sspm_ipi_send_sync(IPI_ID_MET, IPI_OPT_WAIT, (void *)ipi_buf, 0, &rdata, 1);
+	}
+}
+
+
+static void ondiemet_emi_start(void)
+{
+	MET_BM_IPI_REGISTER_CB();
+	if (!emi_inited) {
+		if (MET_BM_Init() != 0) {
+			met_sspm_emi.mode = 0;
+			pr_notice("MET_BM_Init failed!!!\n");
+			return;
+		}
+		emi_inited = 1;
+	}
+	MET_BM_IPI_configs();
+
+	if (do_emi())
+		emi_init();
+
+	ondiemet_module[ONDIEMET_SSPM] |= ID_EMI;
+}
+
+static void ondiemet_emi_stop(void)
+{
+	if (!emi_inited)
+		return;
+
+	if (do_emi())
+		emi_uninit();
+}
+
+
+
+struct metdevice met_sspm_emi = {
+	.name			= "emi",
+	.owner			= THIS_MODULE,
+	.type			= MET_TYPE_BUS,
+	.create_subfs		= met_emi_create,
+	.delete_subfs		= met_emi_delete,
+	.resume			= met_emi_resume,
+#if defined(CONFIG_MTK_TINYSYS_SSPM_SUPPORT) && defined(ONDIEMET_SUPPORT)
+	.ondiemet_start		= ondiemet_emi_start,
+	.ondiemet_stop		= ondiemet_emi_stop,
+	.ondiemet_print_help	= emi_print_help,
+	.ondiemet_print_header	= ondiemet_emi_print_header,
+#endif
+	.ondiemet_mode		= 1,
+};
+EXPORT_SYMBOL(met_sspm_emi);
\ No newline at end of file
diff --git a/src/devtools/met-driver/4.14/common/met_kernel_symbol.h b/src/devtools/met-driver/4.14/common/met_kernel_symbol.h
new file mode 100644
index 0000000..2eec3ce
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/met_kernel_symbol.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef MET_KERNEL_SYMBOL
+#define MET_KERNEL_SYMBOL
+
+/*lookup symbol*/
+#include <asm/cpu.h>
+#include <linux/kallsyms.h>
+#include <linux/perf_event.h>
+#include <linux/kthread.h>
+
+#if	defined(CONFIG_MET_ARM_32BIT)
+extern void met_get_cpuinfo(int cpu, struct cpuinfo_arm **cpuinfo);
+extern void (*met_get_cpuinfo_symbol)(int cpu, struct cpuinfo_arm **cpuinfo);
+#else
+extern void met_get_cpuinfo(int cpu, struct cpuinfo_arm64 **cpuinfo);
+extern void (*met_get_cpuinfo_symbol)(int cpu, struct cpuinfo_arm64 **cpuinfo);
+#endif
+
+extern void (*tracing_record_cmdline_symbol)(struct task_struct *tsk);
+extern void met_cpu_frequency(unsigned int frequency, unsigned int cpu_id);
+extern void (*met_cpu_frequency_symbol)(unsigned int frequency, unsigned int cpu_id);
+extern void (*met_arch_setup_dma_ops_symbol)(struct device *dev);
+extern int (*met_perf_event_read_local_symbol)(struct perf_event *ev, u64 *value);
+extern struct task_struct *(*met_kthread_create_on_cpu_symbol)(int (*threadfn)(void *data),
+				void *data, unsigned int cpu,
+				const char *namefmt);
+extern int (*met_smp_call_function_single_symbol)(int cpu, smp_call_func_t func, void *info, int wait);
+
+extern void met_tracing_record_cmdline(struct task_struct *tsk);
+extern int met_reg_switch(void);
+extern int (*met_reg_switch_symbol)(void);
+extern void met_unreg_switch(void);
+extern void (*met_unreg_switch_symbol)(void);
+#ifdef MET_EVENT_POWER_SUPPORT
+extern int met_reg_event_power(void);
+extern int (*met_reg_event_power_symbol)(void);
+extern void met_unreg_event_power(void);
+extern void (*met_unreg_event_power_symbol)(void);
+#endif
+extern void met_arch_setup_dma_ops(struct device *dev);
+extern int met_perf_event_read_local(struct perf_event *ev, u64 *value);
+extern struct task_struct *met_kthread_create_on_cpu(int (*threadfn)(void *data),
+				void *data, unsigned int cpu,
+				const char *namefmt);
+extern int met_smp_call_function_single(int cpu, smp_call_func_t func, void *info, int wait);
+extern void met_arch_send_call_function_single_ipi(int cpu);
+#endif	/* MET_KERNEL_SYMBOL */
diff --git a/src/devtools/met-driver/4.14/common/met_main.c b/src/devtools/met-driver/4.14/common/met_main.c
new file mode 100644
index 0000000..16fa86b
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/met_main.c
@@ -0,0 +1,400 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/hrtimer.h>
+#include <linux/cpu.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/profile.h>
+#include <linux/dcache.h>
+#include <linux/types.h>
+#include <linux/dcookies.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+
+#include <asm/irq_regs.h>
+
+#include "met_struct.h"
+#include "met_drv.h"
+#include "met_kernel_symbol.h"
+#include "interface.h"
+#include <linux/of.h>
+
+
+extern struct device_node *of_root;
+static const char *platform_name;
+
+struct cpu_type_name {
+	char full_name[32];
+	char abbr_name[8];
+};
+
+static struct cpu_type_name met_known_cpu_type[] = {
+	{"arm,cortex-a53", "CA53"},
+	{"arm,cortex-a55", "CA55"},
+	{"arm,cortex-a73", "CA73"},
+	{"arm,cortex-a75", "CA75"},
+};
+#define MET_KNOWN_CPU_TYPE_COUNT \
+	(sizeof(met_known_cpu_type)/sizeof(struct cpu_type_name))
+
+static char met_cpu_topology[64];
+
+#if	defined(CONFIG_MET_ARM_32BIT)
+void (*met_get_cpuinfo_symbol)(int cpu, struct cpuinfo_arm **cpuinfo);
+#else
+void (*met_get_cpuinfo_symbol)(int cpu, struct cpuinfo_arm64 **cpuinfo);
+#endif
+
+void (*tracing_record_cmdline_symbol)(struct task_struct *tsk);
+void (*met_cpu_frequency_symbol)(unsigned int frequency, unsigned int cpu_id);
+int (*met_reg_switch_symbol)(void);
+void (*met_unreg_switch_symbol)(void);
+
+#ifdef MET_EVENT_POWER_SUPPORT
+int (*met_reg_event_power_symbol)(void);
+void (*met_unreg_event_power_symbol)(void);
+#endif
+
+void (*met_arch_setup_dma_ops_symbol)(struct device *dev);
+int (*met_perf_event_read_local_symbol)(struct perf_event *ev, u64 *value);
+struct task_struct *(*met_kthread_create_on_cpu_symbol)(int (*threadfn)(void *data),
+				void *data, unsigned int cpu,
+				const char *namefmt);
+int (*met_smp_call_function_single_symbol)(int cpu, smp_call_func_t func, void *info, int wait);
+
+#ifdef MTK_PLATFORM
+#define _SHOW_MTK_PLATFORM(X) #X
+#define SHOW_MTK_PLATFORM(X) _SHOW_MTK_PLATFORM(X)
+#endif
+
+static int is_platform_name_valid(const char * buf)
+{
+	int len = strlen(buf);
+	int i;
+
+	for (i=0; i<len; i++) {
+		if ((buf[i] == 'm' && buf[i+1] == 't')
+			|| (buf[i] == 'M' && buf[i+1] == 'T')
+			|| (buf[i] == 'M' && buf[i+1] == 't')
+			|| (buf[i] == 'm' && buf[i+1] == 'T')) {
+			return 1;
+		}
+	}
+	return 0;
+}
+
+const char *met_get_platform_name(void)
+{
+	const char default_platform_name[7] = "mtxxxx";
+	int found = 0;
+
+	found = is_platform_name_valid(platform_name);
+
+	if(found == 0){
+#ifdef MTK_PLATFORM
+		const char buf[7] = SHOW_MTK_PLATFORM(MTK_PLATFORM);
+		found = is_platform_name_valid(buf);
+		if(found == 1)
+			platform_name = buf;
+		else
+#endif
+			platform_name = default_platform_name;
+	}
+	return platform_name;
+}
+EXPORT_SYMBOL(met_get_platform_name);
+
+static void get_cpu_type_name(const char *compatible, char *cpu_type)
+{
+	int i;
+
+	for (i = 0; i < MET_KNOWN_CPU_TYPE_COUNT; i++) {
+		if (!strncmp(compatible, met_known_cpu_type[i].full_name,strlen(met_known_cpu_type[i].full_name)))
+			strscpy(cpu_type, met_known_cpu_type[i].abbr_name,sizeof(cpu_type));
+	}
+}
+
+static void met_set_cpu_topology(int core_id, int cluster_core_num)
+{
+	int i, buf_len = strlen(met_cpu_topology);
+	struct device_node *node = NULL;
+	const char *prev_cptb = NULL;
+	const char *cptb;
+	char cpu_type[16];
+
+	for (i = 0; i < cluster_core_num; i++) {
+		node = of_get_cpu_node(core_id + i, NULL);
+		if (node) {
+			cptb = of_get_property(node, "compatible", NULL);
+			if (cptb) {
+				get_cpu_type_name(cptb, cpu_type);
+				if (prev_cptb == NULL)
+					/* first write:  write core_type & core_number */
+					buf_len += snprintf(met_cpu_topology + buf_len,
+								sizeof(met_cpu_topology) - buf_len,
+								"%s:%d", cpu_type, core_id + i);
+				else if (!strncmp(prev_cptb, cptb, strlen(prev_cptb)))
+					/* cpu type is the same with before */
+					/* write core_number */
+					buf_len += snprintf(met_cpu_topology + buf_len,
+								sizeof(met_cpu_topology) - buf_len,
+								",%d", core_id + i);
+				else
+					/* cpu type is different with before */
+					/* write core_type & core_number */
+					buf_len += snprintf(met_cpu_topology + buf_len,
+								sizeof(met_cpu_topology) - buf_len,
+								"|%s:%d", cpu_type, core_id + i);
+
+				prev_cptb = cptb;
+			}
+		}
+	}
+}
+
+static int met_create_cpu_topology(void)
+{
+	int i, j, len;
+	struct device_node *node = NULL, *core_node = NULL;
+	int start_core_id = 0;
+	int cluster_num = 0, cluster_core_num = 0;
+	char cluster_name[16], core_name[16];
+
+	node = of_find_node_by_name(NULL, "cpu-map");
+	if (!node)
+		node = of_find_node_by_name(NULL, "virtual-cpu-map");
+
+	if (node) {
+		cluster_num = of_get_child_count(node);
+		of_node_put(node);
+
+		for (i = 0; i < cluster_num; i++) {
+			snprintf(cluster_name, sizeof(cluster_name), "cluster%d", i);
+			node = of_find_node_by_name(NULL, cluster_name);
+			if (node) {
+
+				j = 0;
+				cluster_core_num = 0;
+				do {
+					snprintf(core_name, sizeof(core_name), "core%d", j);
+					core_node = of_get_child_by_name(node, core_name);
+					if (core_node) {
+						cluster_core_num++;
+						of_node_put(core_node);
+					}
+					j++;
+				} while (core_node);
+				of_node_put(node);
+
+				/* "|" use to separate different cluster */
+				if (i > 0) {
+					len = strlen(met_cpu_topology);
+					snprintf(met_cpu_topology + len, sizeof(met_cpu_topology) - len, "|");
+				}
+
+				met_set_cpu_topology(start_core_id, cluster_core_num);
+				start_core_id = cluster_core_num;
+			}
+		}
+	}
+
+	return strlen(met_cpu_topology);
+}
+
+static int met_kernel_symbol_get(void)
+{
+	int ret = 0;
+
+	if (met_get_cpuinfo_symbol == NULL)
+		met_get_cpuinfo_symbol = (void *)symbol_get(met_get_cpuinfo);
+	if (met_get_cpuinfo_symbol == NULL)
+		return -2;
+
+	if (tracing_record_cmdline_symbol == NULL)
+		tracing_record_cmdline_symbol = (void *)symbol_get(met_tracing_record_cmdline);
+	if (tracing_record_cmdline_symbol == NULL)
+		ret = -3;
+
+	if (met_cpu_frequency_symbol == NULL)
+		met_cpu_frequency_symbol = (void *)symbol_get(met_cpu_frequency);
+	if (met_cpu_frequency_symbol == NULL)
+		ret = -4;
+
+	if (met_reg_switch_symbol == NULL)
+		met_reg_switch_symbol = (void *)symbol_get(met_reg_switch);
+	if (met_reg_switch_symbol == NULL)
+		ret = -5;
+
+	if (met_unreg_switch_symbol == NULL)
+		met_unreg_switch_symbol = (void *)symbol_get(met_unreg_switch);
+	if (met_unreg_switch_symbol == NULL)
+		ret = -6;
+
+	if (met_arch_setup_dma_ops_symbol == NULL)
+		met_arch_setup_dma_ops_symbol = (void *)symbol_get(met_arch_setup_dma_ops);
+	if (met_arch_setup_dma_ops_symbol == NULL)
+		ret = -7;
+
+	if (met_perf_event_read_local_symbol == NULL)
+		met_perf_event_read_local_symbol = (void *)symbol_get(met_perf_event_read_local);
+	if (met_perf_event_read_local_symbol == NULL)
+		ret = -8;
+
+	if (met_kthread_create_on_cpu_symbol == NULL)
+		met_kthread_create_on_cpu_symbol = (void *)symbol_get(met_kthread_create_on_cpu);
+	if (met_kthread_create_on_cpu_symbol == NULL)
+		ret = -9;
+
+	if (met_smp_call_function_single_symbol == NULL)
+		met_smp_call_function_single_symbol = (void *)symbol_get(met_smp_call_function_single);
+	if (met_smp_call_function_single_symbol == NULL)
+		ret = -10;
+
+#ifdef MET_EVENT_POWER_SUPPORT
+	if (met_reg_event_power_symbol == NULL)
+		met_reg_event_power_symbol = (void *)symbol_get(met_reg_event_power);
+	if (met_reg_event_power_symbol == NULL)
+		ret = -11;
+
+	if (met_unreg_event_power_symbol == NULL)
+		met_unreg_event_power_symbol = (void *)symbol_get(met_unreg_event_power);
+	if (met_unreg_event_power_symbol == NULL)
+		ret = -12;
+#endif
+
+	return ret;
+}
+
+DEFINE_PER_CPU(struct met_cpu_struct, met_cpu);
+
+static int __init met_drv_init(void)
+{
+	int cpu;
+	int ret;
+	int cpu_topology_len;
+	struct met_cpu_struct *met_cpu_ptr;
+
+	for_each_possible_cpu(cpu) {
+		met_cpu_ptr = &per_cpu(met_cpu, cpu);
+		/* snprintf(&(met_cpu_ptr->name[0]), sizeof(met_cpu_ptr->name), "met%02d", cpu); */
+		met_cpu_ptr->cpu = cpu;
+	}
+
+	ret = met_kernel_symbol_get();
+	if (ret) {
+		pr_notice("[MET] met_kernel_symbol_get fail, ret = %d\n", ret);
+		return ret;
+	}
+	fs_reg();
+
+	if (of_root){
+		/*
+			mt6765.dts
+			model = "MT6765";
+			compatible = "mediatek,MT6765";
+			interrupt-parent = <&sysirq>;
+		*/
+		if (of_root->properties) {
+			of_property_read_string(of_root, "compatible", &platform_name);
+			PR_BOOTMSG("dts property compatible=%s\n", platform_name);
+		}
+	}
+	if (platform_name) {
+		char buf[7];
+		int len = strlen(platform_name);
+		int i;
+		int found = 0;
+
+		for (i=0; i<len; i++) {
+			if ((platform_name[i] == 'm' && platform_name[i+1] == 't')
+				|| (platform_name[i] == 'M' && platform_name[i+1] == 'T')
+				|| (platform_name[i] == 'M' && platform_name[i+1] == 't')
+				|| (platform_name[i] == 'm' && platform_name[i+1] == 'T')) {
+				/* be sure to have 4 chars (chip id) behind 'mt' for copy */
+				if (len - i - 2 >= 4)
+					found = 1;
+				break;
+			}
+		}
+
+		if (found == 1) {
+			memset(buf, 0x0, 7);
+			buf[0] = 'm';
+			buf[1] = 't';
+			strncpy(&buf[2], &platform_name[i+2], 4);
+			met_set_platform(buf, 1);
+			PR_BOOTMSG("Get platform info from dts, platform_name=%s\n", buf);
+		} else {
+#ifdef MTK_PLATFORM
+			memset(buf, 0x0, 7);
+			strcpy(buf, SHOW_MTK_PLATFORM(MTK_PLATFORM));
+			found = is_platform_name_valid((const char *)buf);
+			if(found == 1){
+				PR_BOOTMSG("Get platform info from met_drv Kbuild, platform_name=%s\n", buf);
+				met_set_platform(buf, 1);
+			}
+			else
+#endif
+			{
+				PR_BOOTMSG("Can not get platform info from dts nor met_drv Kbuild, set platform_name=mtxxxx\n");
+				met_set_platform("mtxxxx", 1);
+			}
+		}
+	}
+
+	cpu_topology_len = met_create_cpu_topology();
+	if (cpu_topology_len)
+		met_set_topology(met_cpu_topology, 1);
+
+#ifdef MET_PLF_USE
+	core_plf_init();
+#endif
+	return 0;
+}
+
+static void __exit met_drv_exit(void)
+{
+	if (met_cpu_frequency_symbol)
+		symbol_put(met_cpu_frequency);
+	if (met_reg_switch_symbol)
+		symbol_put(met_reg_switch);
+	if (met_unreg_switch_symbol)
+		symbol_put(met_unreg_switch);
+
+#ifdef MET_EVENT_POWER_SUPPORT
+	if (met_reg_event_power_symbol)
+		symbol_put(met_reg_event_power);
+	if (met_unreg_event_power_symbol)
+		symbol_put(met_unreg_event_power);
+#endif
+
+	if (tracing_record_cmdline_symbol)
+		symbol_put(met_tracing_record_cmdline);
+	if (met_get_cpuinfo_symbol)
+		symbol_put(met_get_cpuinfo);
+
+#ifdef MET_PLF_USE
+	core_plf_exit();
+#endif
+	fs_unreg();
+
+}
+module_init(met_drv_init);
+module_exit(met_drv_exit);
+
+MODULE_AUTHOR("DT_DM5");
+MODULE_DESCRIPTION("MET_CORE");
+MODULE_LICENSE("GPL");
diff --git a/src/devtools/met-driver/4.14/common/met_power.h b/src/devtools/met-driver/4.14/common/met_power.h
new file mode 100644
index 0000000..fc82995
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/met_power.h
@@ -0,0 +1,63 @@
+/*
+ * * Copyright (C) 2018 MediaTek Inc.
+ * *
+ * * 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.
+ * *
+ * * This program is distributed in the hope that it will be useful,
+ * * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * * GNU General Public License for more details.
+ * */
+
+
+#ifndef MET_POWER
+#define MET_POWER
+
+enum {
+	_PM_QOS_RESERVED = 0,
+	_PM_QOS_CPU_DMA_LATENCY,
+	_PM_QOS_NETWORK_LATENCY,
+	_PM_QOS_NETWORK_THROUGHPUT,
+	_PM_QOS_MEMORY_BANDWIDTH,
+
+	_PM_QOS_CPU_MEMORY_BANDWIDTH,
+	_PM_QOS_GPU_MEMORY_BANDWIDTH,
+	_PM_QOS_MM_MEMORY_BANDWIDTH,
+	_PM_QOS_OTHER_MEMORY_BANDWIDTH,
+	_PM_QOS_MM0_BANDWIDTH_LIMITER,
+	_PM_QOS_MM1_BANDWIDTH_LIMITER,
+
+	_PM_QOS_DDR_OPP,
+	_PM_QOS_VCORE_OPP,
+	_PM_QOS_SCP_VCORE_REQUEST,
+	_PM_QOS_POWER_MODEL_DDR_REQUEST,
+	_PM_QOS_POWER_MODEL_VCORE_REQUEST,
+	_PM_QOS_VCORE_DVFS_FORCE_OPP,
+
+	_PM_QOS_DISP_FREQ,
+	_PM_QOS_MDP_FREQ,
+	_PM_QOS_VDEC_FREQ,
+	_PM_QOS_VENC_FREQ,
+	_PM_QOS_IMG_FREQ,
+	_PM_QOS_CAM_FREQ,
+	_PM_QOS_VVPU_OPP,
+	_PM_QOS_VMDLA_OPP,
+	_PM_QOS_ISP_HRT_BANDWIDTH,
+	_PM_QOS_APU_MEMORY_BANDWIDTH,
+	/* insert new class ID */
+	_PM_QOS_NUM_CLASSES,
+};
+/* Action requested to pm_qos_update_target */
+enum _pm_qos_req_action {
+	_PM_QOS_ADD_REQ,		/* Add a new request */
+	_PM_QOS_UPDATE_REQ,	/* Update an existing request */
+	_PM_QOS_REMOVE_REQ	/* Remove an existing request */
+};
+
+extern void pm_qos_update_request(int pm_qos_class, s32 value, char *owner);
+extern void pm_qos_update_target(unsigned int action, int prev_value, int curr_value);
+
+#endif	/* MET_DRV */
+
diff --git a/src/devtools/met-driver/4.14/common/met_ptpod.c b/src/devtools/met-driver/4.14/common/met_ptpod.c
new file mode 100644
index 0000000..fce982b
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/met_ptpod.c
@@ -0,0 +1,377 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/random.h>
+#include <linux/fs.h>
+
+#include "met_drv.h"
+#include "trace.h"
+#include "core_plf_init.h"
+#include "core_plf_trace.h"
+
+static unsigned int MT_GPU_DVFS_IDX = NR_MT_CPU_DVFS;
+static unsigned int g_u4GPUVolt;
+static unsigned int g_u4Volt[NR_MT_CPU_DVFS + 1];
+
+/* g_ap_ptpod: cpu volt from ap or sspm setting
+ * if 1: cpu volt from ap
+ * if 0: cpu volt from sspm */
+static unsigned int g_ap_ptpod;
+
+/* gpu_volt_enable:
+ * if 1: enable gpu volt to output
+ * if 0: disable gpu volt to output */
+static unsigned int gpu_volt_enable = 1;
+
+/* get_volt_by_wq:
+ * if 1: get cpu/gpu volt by workqueue
+ * if 0: get cpu/gpu volt in irq */
+static unsigned int get_volt_by_wq;
+
+static int ptpod_started;
+static struct kobject *kobj_ptpod;
+static struct delayed_work get_volt_dwork;
+
+noinline void ms_ptpod(void)
+{
+	char *SOB, *EOB;
+
+	if (g_ap_ptpod) {
+		MET_TRACE_GETBUF(&SOB, &EOB);
+
+		if (gpu_volt_enable) {
+			g_u4Volt[MT_GPU_DVFS_IDX] = g_u4GPUVolt;
+			EOB = ms_formatD_EOL(EOB, ARRAY_SIZE(g_u4Volt), g_u4Volt);
+		} else
+			EOB = ms_formatD_EOL(EOB, ARRAY_SIZE(g_u4Volt) - 1, g_u4Volt);
+
+		MET_TRACE_PUTBUF(SOB, EOB);
+	} else {
+		if (gpu_volt_enable) {
+			MET_TRACE_GETBUF(&SOB, &EOB);
+			EOB = ms_formatD_EOL(EOB, 1, &g_u4GPUVolt);
+			MET_TRACE_PUTBUF(SOB, EOB);
+		}
+	}
+}
+
+#if 0
+static void ptpod_cpu_voltSampler(enum mt_cpu_dvfs_id id, unsigned int volt)
+{
+	switch (id) {
+	case MT_CPU_DVFS_LL:
+		g_u4CPUVolt_LL = volt;
+		break;
+	case MT_CPU_DVFS_L:
+		g_u4CPUVolt_L = volt;
+		break;
+	case MT_CPU_DVFS_CCI:
+		g_u4CPUVolt_CCI = volt;
+		break;
+	default:
+		return;
+	}
+	ptpod();
+}
+#endif
+
+#if 0
+static void ptpod_gpu_voltSampler(unsigned int a_u4Volt)
+{
+	g_u4GPUVolt = (a_u4Volt+50)/100;
+
+	if (ptpod_started)
+		ptpod();
+}
+#endif
+
+#define PTPOD_CONF_SHOW_IMPLEMENT(var) \
+	do { \
+		int i; \
+		i = snprintf(buf, PAGE_SIZE, "%d\n", var); \
+		return i; \
+	} while (0)
+
+#define PTPOD_CONF_STORE_IMPLEMENT(var) \
+	do { \
+		int value; \
+		if ((n == 0) || (buf == NULL)) \
+			return -EINVAL; \
+		if (kstrtoint(buf, 0, &value) != 0) \
+			return -EINVAL; \
+		if (value == 1) \
+			var = 1; \
+		else \
+			var = 0; \
+		return n; \
+	} while (0)
+
+
+static ssize_t ap_ptpod_enable_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	PTPOD_CONF_SHOW_IMPLEMENT(g_ap_ptpod);
+}
+
+static ssize_t ap_ptpod_enable_store(struct kobject *kobj,
+			struct kobj_attribute *attr,
+			const char *buf,
+			size_t n)
+{
+	PTPOD_CONF_STORE_IMPLEMENT(g_ap_ptpod);
+}
+
+static ssize_t gpu_volt_enable_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	PTPOD_CONF_SHOW_IMPLEMENT(gpu_volt_enable);
+}
+
+static ssize_t gpu_volt_enable_store(struct kobject *kobj,
+			struct kobj_attribute *attr,
+			const char *buf,
+			size_t n)
+{
+	PTPOD_CONF_STORE_IMPLEMENT(gpu_volt_enable);
+}
+
+static ssize_t get_volt_by_wq_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	PTPOD_CONF_SHOW_IMPLEMENT(get_volt_by_wq);
+}
+
+static ssize_t get_volt_by_wq_store(struct kobject *kobj,
+			struct kobj_attribute *attr,
+			const char *buf,
+			size_t n)
+{
+	PTPOD_CONF_STORE_IMPLEMENT(get_volt_by_wq);
+}
+
+static struct kobj_attribute ap_ptpod_enable_attr = __ATTR(ap_ptpod_enable, 0664, ap_ptpod_enable_show, ap_ptpod_enable_store);
+static struct kobj_attribute gpu_volt_enable_attr = __ATTR(gpu_volt_enable, 0664, gpu_volt_enable_show, gpu_volt_enable_store);
+static struct kobj_attribute get_volt_by_wq_attr = __ATTR(get_volt_by_wq, 0664, get_volt_by_wq_show, get_volt_by_wq_store);
+
+/* create ptpod related kobj node */
+#define KOBJ_ATTR_LIST \
+	do { \
+		KOBJ_ATTR_ITEM(ap_ptpod_enable); \
+		KOBJ_ATTR_ITEM(gpu_volt_enable); \
+		KOBJ_ATTR_ITEM(get_volt_by_wq); \
+	} while (0)
+
+static int ptpod_create(struct kobject *parent)
+{
+	int ret = 0;
+
+	kobj_ptpod = parent;
+
+#define KOBJ_ATTR_ITEM(attr_name) \
+	do { \
+		ret = sysfs_create_file(kobj_ptpod, &attr_name ## _attr.attr); \
+		if (ret != 0) { \
+			pr_notice("Failed to create " #attr_name " in sysfs\n"); \
+			return ret; \
+		} \
+	} while (0)
+	KOBJ_ATTR_LIST;
+#undef  KOBJ_ATTR_ITEM
+
+	return 0;
+}
+
+static void ptpod_delete(void)
+{
+#define KOBJ_ATTR_ITEM(attr_name) \
+	sysfs_remove_file(kobj_ptpod, &attr_name ## _attr.attr)
+
+	if (kobj_ptpod != NULL) {
+		KOBJ_ATTR_LIST;
+		kobj_ptpod = NULL;
+	}
+#undef  KOBJ_ATTR_ITEM
+}
+
+static void update_volt_value(void)
+{
+	int i;
+
+	if (g_ap_ptpod && mt_cpufreq_get_cur_volt_symbol) {
+		/*
+		g_u4CPUVolt_LL = mt_cpufreq_get_cur_volt_symbol(MT_CPU_DVFS_LL)/100;
+		g_u4CPUVolt_L = mt_cpufreq_get_cur_volt_symbol(MT_CPU_DVFS_L)/100;
+		g_u4CPUVolt_CCI = mt_cpufreq_get_cur_volt_symbol(MT_CPU_DVFS_CCI)/100;
+		*/
+		for (i = 0; i < NR_MT_CPU_DVFS; i++)
+			g_u4Volt[i] = mt_cpufreq_get_cur_volt_symbol(i) / 100;
+	}
+
+	if (gpu_volt_enable) {
+		if (mt_gpufreq_get_cur_volt_symbol)
+			g_u4GPUVolt = ((mt_gpufreq_get_cur_volt_symbol() + 50) / 100);
+	}
+}
+
+static void get_volt_notify(unsigned long long stamp, int cpu)
+{
+	schedule_delayed_work(&get_volt_dwork, 0);
+}
+
+static void met_ptpod_polling_by_wq(struct work_struct *work)
+{
+	update_volt_value();
+
+	ms_ptpod();
+}
+
+static void met_ptpod_polling(unsigned long long stamp, int cpu)
+{
+	update_volt_value();
+
+	ms_ptpod();
+}
+
+/*
+ * Called from "met-cmd --start"
+ */
+static void ptpod_start(void)
+{
+#if 0
+	met_gpufreq_setvolt_registerCB(ptpod_gpu_voltSampler, kFOR_MET_PTPOD_USE);
+#endif
+
+	if (get_volt_by_wq) {
+		met_ptpod.timed_polling = get_volt_notify;
+
+		INIT_DELAYED_WORK(&get_volt_dwork, met_ptpod_polling_by_wq);
+	} else
+		met_ptpod.timed_polling = met_ptpod_polling;
+
+	update_volt_value();
+
+	ms_ptpod();
+
+#if 0
+	/* register callback */
+	if (mt_cpufreq_setvolt_registerCB_symbol)
+		mt_cpufreq_setvolt_registerCB_symbol(ptpod_cpu_voltSampler);
+#endif
+
+	ptpod_started = 1;
+}
+
+/*
+ * Called from "met-cmd --stop"
+ */
+static void ptpod_stop(void)
+{
+	ptpod_started = 0;
+
+#if 0
+	/* unregister callback */
+	if (mt_cpufreq_setvolt_registerCB_symbol)
+		mt_cpufreq_setvolt_registerCB_symbol(NULL);
+#endif
+
+	if (get_volt_by_wq)
+		cancel_delayed_work_sync(&get_volt_dwork);
+
+	update_volt_value();
+
+	ms_ptpod();
+
+#if 0
+	met_gpufreq_setvolt_registerCB(NULL, kFOR_MET_PTPOD_USE);
+#endif
+}
+
+static char help[] =
+	"  --ptpod                               Measure CPU/GPU voltage\n";
+static int ptpod_print_help(char *buf, int len)
+{
+	return snprintf(buf, PAGE_SIZE, help);
+}
+
+/*
+ * It will be called back when run "met-cmd --extract" and mode is 1
+ */
+static int ptpod_print_header(char *buf, int len)
+{
+	int str_len = 0;
+
+	if (g_ap_ptpod) {
+		int i;
+
+		str_len += snprintf(buf + str_len, PAGE_SIZE - str_len,
+						"met-info [000] 0.0: met_ptpod_header: ");
+
+		for (i = 0; i < NR_MT_CPU_DVFS; i++)
+		{
+#ifdef MT_CPU_DVFS_CCI
+			if( i == MT_CPU_DVFS_CCI )
+			{
+				str_len += snprintf(buf + str_len, PAGE_SIZE - str_len, "CPUVolt_CCI,", i);
+			}
+			else
+			{
+				str_len += snprintf(buf + str_len, PAGE_SIZE - str_len, "CPUVolt_%d,", i);
+			}
+#else
+				str_len += snprintf(buf + str_len, PAGE_SIZE - str_len, "CPUVolt_%d,", i);
+#endif
+		}
+
+		if (gpu_volt_enable)
+			str_len += snprintf(buf + str_len, PAGE_SIZE - str_len, "GPUVolt,");
+
+		buf[str_len-1] = '\n';
+
+		str_len += snprintf(buf + str_len, PAGE_SIZE - str_len,
+							"met-info [000] 0.0: met_ptpod_version: ap\n");
+	} else {
+		if (gpu_volt_enable) {
+			str_len += snprintf(buf + str_len, PAGE_SIZE - str_len,
+							"met-info [000] 0.0: met_ptpod_header: ");
+
+			str_len += snprintf(buf + str_len, PAGE_SIZE - str_len, "GPUVolt\n");
+		}
+
+		str_len += snprintf(buf + str_len, PAGE_SIZE - str_len,
+							"met-info [000] 0.0: met_ptpod_version: sspm\n");
+	}
+
+	if (gpu_volt_enable)
+		str_len += snprintf(buf + str_len, PAGE_SIZE - str_len,
+						"met-info [000] 0.0: met_ptpod_gpu_volt_enable: YES\n");
+	else
+		str_len += snprintf(buf + str_len, PAGE_SIZE - str_len,
+						"met-info [000] 0.0: met_ptpod_gpu_volt_enable: NO\n");
+
+	return str_len;
+}
+
+struct metdevice met_ptpod = {
+	.name = "ptpod",
+	.owner = THIS_MODULE,
+	.type = MET_TYPE_PMU,
+	.cpu_related = 0,
+	.create_subfs = ptpod_create,
+	.delete_subfs = ptpod_delete,
+	.start = ptpod_start,
+	.stop = ptpod_stop,
+	.timed_polling = met_ptpod_polling,
+	.print_help = ptpod_print_help,
+	.print_header = ptpod_print_header,
+};
+EXPORT_SYMBOL(met_ptpod);
diff --git a/src/devtools/met-driver/4.14/common/met_struct.h b/src/devtools/met-driver/4.14/common/met_struct.h
new file mode 100644
index 0000000..a74ff78
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/met_struct.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MET_STRUCT_H_
+#define _MET_STRUCT_H_
+
+#include <linux/hrtimer.h>
+
+struct met_cpu_struct {
+	struct hrtimer hrtimer;
+	struct delayed_work dwork;
+/* struct kmem_cache *cachep; */
+/* struct list_head sample_head; */
+/* spinlock_t list_lock; */
+/* struct mutex list_sync_lock; */
+	int work_enabled;
+	int cpu;
+	int hrtimer_online_check;
+/* char name[16]; */
+};
+
+DECLARE_PER_CPU(struct met_cpu_struct, met_cpu);
+
+#endif				/* _MET_STRUCT_H_ */
diff --git a/src/devtools/met-driver/4.14/common/met_tag.h b/src/devtools/met-driver/4.14/common/met_tag.h
new file mode 100644
index 0000000..04b5e09
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/met_tag.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __MET_TAG_EX_H__
+#define __MET_TAG_EX_H__
+
+#ifdef BUILD_WITH_MET
+void force_sample(void *unused);
+#else
+#include <linux/string.h>
+#endif
+
+/* Black List Table */
+struct bltable_t {
+	struct mutex mlock;
+	/* flag - Bit31: Global ON/OFF; Bit0~30: ON/OF slot map of class_id */
+	unsigned int flag;
+	int class_id[MAX_EVENT_CLASS];
+};
+
+extern void met_sched_switch(struct task_struct *prev, struct task_struct *next);
+
+extern int tracing_mark_write(int type, unsigned int class_id,
+		const char *name, unsigned int value,
+		unsigned int value2, unsigned int value3);
+
+#endif				/* __MET_TAG_EX_H__ */
diff --git a/src/devtools/met-driver/4.14/common/met_tag_ex.c b/src/devtools/met-driver/4.14/common/met_tag_ex.c
new file mode 100644
index 0000000..4413b4e
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/met_tag_ex.c
@@ -0,0 +1,644 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define BUILD_WITH_MET
+
+#ifdef MET_USER_EVENT_SUPPORT
+
+#include <linux/kernel.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/fs.h>
+/* #include <asm/uaccess.h> */
+#include <linux/uaccess.h>
+
+#include "met_drv.h"
+#include "met_tag.h"
+#include "interface.h"
+#include "switch.h"
+#include "met_api_tbl.h"
+
+struct bltable_t bltab;
+
+static int dump_buffer_size;
+static int dump_data_size;
+static int dump_overrun;
+static int dump_overrun_size;
+static int dump_seq_no;
+static void *dump_buffer;
+
+#define OPFLAG_OVERWRITE	0x1
+static unsigned int options_flag;
+
+#define DEVICE_NAME		"met_tag"
+
+/* #define ERRF_ENABLE */
+/* #define DEBF_ENABLE */
+
+#ifdef ERRF_ENABLE
+#define MSG_ERR			"Error:["DEVICE_NAME"]"
+#define ERRF(args...)	pr_debug(MSG_ERR args)
+#else
+#define ERRF(args...)
+#endif
+
+#ifdef DEBF_ENABLE
+#define MSG_IFO			"Info :["DEVICE_NAME"]"
+#define DEBF(args...)	pr_debug(MSG_IFO args)
+#else
+#define DEBF(args...)
+#endif
+
+static int is_enabled(unsigned int class_id)
+{
+	int i;
+
+	if (bltab.flag == 0)
+		return 1;
+	if (bltab.flag & MET_CLASS_ALL)
+		return 0;
+
+	for (i = 0; i < MAX_EVENT_CLASS; i++) {
+		if ((bltab.flag & (1 << i)) && (bltab.class_id[i] == class_id))
+			return 0;
+	}
+
+	return 1;
+}
+
+noinline int tracing_mark_write(int type, unsigned int class_id,
+				       const char *name, unsigned int value,
+				       unsigned int value2, unsigned int value3)
+{
+	if (type == TYPE_MET_SUSPEND) {
+		MET_TRACE("C|0|MET_SUSPEND|1");
+		return 0;
+	}
+	if (type == TYPE_MET_RESUME) {
+		MET_TRACE("C|0|MET_SUSPEND|0");
+		return 0;
+	}
+	if (!is_enabled(class_id))
+		return 0;
+	switch (type) {
+	case TYPE_START:
+		MET_TRACE("B|%d|%s\n", class_id, name);
+		break;
+	case TYPE_END:
+		MET_TRACE("E|%s\n", name);
+		break;
+	case TYPE_ONESHOT:
+		MET_TRACE("C|%d|%s|%d\n", class_id, name, value);
+		break;
+	case TYPE_ASYNC_START:
+		MET_TRACE("S|%d|%s|%d\n", class_id, name, value);
+		break;
+	case TYPE_ASYNC_END:
+		MET_TRACE("F|%d|%s|%d\n", class_id, name, value);
+		break;
+	case TYPE_DUMP:
+		MET_TRACE("D|%d|%s|%d|%d|%d\n", class_id, name, value, value2, value3);
+		break;
+	default:
+		return -1;
+	}
+	return 0;
+}
+
+int met_tag_init(void)
+{
+	memset(&bltab, 0, sizeof(struct bltable_t));
+	bltab.flag = MET_CLASS_ALL;
+	mutex_init(&bltab.mlock);
+	return 0;
+}
+
+int met_tag_uninit(void)
+{
+	met_set_dump_buffer_real(0);
+	return 0;
+}
+
+int met_tag_start_real(unsigned int class_id, const char *name)
+{
+	int ret;
+
+	ret = tracing_mark_write(TYPE_START, class_id, name, 0, 0, 0);
+#if 0
+	if ((met_switch.mode & MT_SWITCH_TAGPOLLING)) {
+		/* tag polling only enable when MT_SWITCH_TAGPOLLING is config */
+		force_sample(NULL);
+	}
+#endif
+	return ret;
+}
+EXPORT_SYMBOL(met_tag_start_real);
+
+int met_tag_end_real(unsigned int class_id, const char *name)
+{
+	int ret;
+#if 0
+	if ((met_switch.mode & MT_SWITCH_TAGPOLLING)) {
+		/* tag polling only enable when MT_SWITCH_TAGPOLLING is config */
+		force_sample(NULL);
+	}
+#endif
+	ret = tracing_mark_write(TYPE_END, class_id, name, 0, 0, 0);
+
+	return ret;
+}
+EXPORT_SYMBOL(met_tag_end_real);
+
+int met_tag_async_start_real(unsigned int class_id, const char *name, unsigned int cookie)
+{
+	int ret;
+
+	ret = tracing_mark_write(TYPE_ASYNC_START, class_id, name, cookie, 0, 0);
+#if 0
+	if ((met_switch.mode & MT_SWITCH_TAGPOLLING)) {
+		/* tag polling only enable when MT_SWITCH_TAGPOLLING is config */
+		force_sample(NULL);
+	}
+#endif
+	return ret;
+}
+
+int met_tag_async_end_real(unsigned int class_id, const char *name, unsigned int cookie)
+{
+	int ret;
+
+#if 0
+	if ((met_switch.mode & MT_SWITCH_TAGPOLLING)) {
+		/* tag polling only enable when MT_SWITCH_TAGPOLLING is config */
+		force_sample(NULL);
+	}
+#endif
+	ret = tracing_mark_write(TYPE_ASYNC_END, class_id, name, cookie, 0, 0);
+	return ret;
+}
+
+int met_tag_oneshot_real(unsigned int class_id, const char *name, unsigned int value)
+{
+	int ret;
+
+	ret = tracing_mark_write(TYPE_ONESHOT, class_id, name, value, 0, 0);
+#if 0
+	if ((met_switch.mode & MT_SWITCH_TAGPOLLING)) {
+		/* tag polling only enable when MT_SWITCH_TAGPOLLING is config */
+		force_sample(NULL);
+	}
+#endif
+	return ret;
+}
+EXPORT_SYMBOL(met_tag_oneshot_real);
+
+int met_tag_userdata_real(char *pData)
+{
+	MET_TRACE("%s\n", pData);
+	return 0;
+}
+
+int met_tag_dump_real(unsigned int class_id, const char *name, void *data, unsigned int length)
+{
+	int ret;
+
+	if ((dump_data_size + length + sizeof(int)) > dump_buffer_size) {
+		if (options_flag & OPFLAG_OVERWRITE) {
+			dump_overrun_size = dump_data_size;
+			dump_overrun++;
+			memcpy(dump_buffer, &dump_seq_no, sizeof(int));
+			memcpy(dump_buffer + sizeof(int), data, length);
+			ret = tracing_mark_write(TYPE_DUMP, class_id, name,
+						 dump_seq_no++, 0, length + sizeof(int));
+			dump_data_size = length + sizeof(int);
+		} else {
+			ret = tracing_mark_write(TYPE_DUMP, class_id, name, dump_seq_no++, 0, 0);
+		}
+	} else {
+		memcpy(dump_buffer + dump_data_size, &dump_seq_no, sizeof(int));
+		memcpy(dump_buffer + dump_data_size + sizeof(int), data, length);
+		ret = tracing_mark_write(TYPE_DUMP, class_id, name,
+					 dump_seq_no++, dump_data_size, length + sizeof(int));
+		dump_data_size += length + sizeof(int);
+	}
+	return ret;
+}
+
+int met_tag_disable_real(unsigned int class_id)
+{
+	int i;
+
+	mutex_lock(&bltab.mlock);
+
+	if (class_id == MET_CLASS_ALL) {
+		bltab.flag |= MET_CLASS_ALL;
+		mutex_unlock(&bltab.mlock);
+		return 0;
+	}
+
+	for (i = 0; i < MAX_EVENT_CLASS; i++) {
+		if ((bltab.flag & (1 << i)) == 0) {
+			bltab.class_id[i] = class_id;
+			bltab.flag |= (1 << i);
+			mutex_unlock(&bltab.mlock);
+			return 0;
+		}
+	}
+
+	mutex_unlock(&bltab.mlock);
+	return -1;
+}
+
+int met_tag_enable_real(unsigned int class_id)
+{
+	int i;
+
+	mutex_lock(&bltab.mlock);
+
+	if (class_id == MET_CLASS_ALL) {
+		bltab.flag &= (~MET_CLASS_ALL);
+		mutex_unlock(&bltab.mlock);
+		return 0;
+	}
+
+	for (i = 0; i < MAX_EVENT_CLASS; i++) {
+		if ((bltab.flag & (1 << i)) && (bltab.class_id[i] == class_id)) {
+			bltab.flag &= (~(1 << i));
+			bltab.class_id[i] = 0;
+			mutex_unlock(&bltab.mlock);
+			return 0;
+		}
+	}
+
+	mutex_unlock(&bltab.mlock);
+	return -1;
+}
+
+int met_set_dump_buffer_real(int size)
+{
+	if (dump_buffer_size && dump_buffer) {
+		free_pages((unsigned long)dump_buffer, get_order(dump_buffer_size));
+		dump_data_size = 0;
+		dump_overrun = 0;
+		dump_overrun_size = 0;
+		dump_seq_no = 0;
+		dump_buffer_size = 0;
+	}
+	/* size is 0 means free dump buffer */
+	if (size == 0)
+		return 0;
+
+	if (size < 0)
+		return -1;
+
+	size = (size + (PAGE_SIZE - 1)) & (~(PAGE_SIZE - 1));
+	dump_buffer = (void *)__get_free_pages(GFP_KERNEL, get_order(size));
+	if (dump_buffer == NULL) {
+		ERRF("can not allocate buffer to copy\n");
+		return -ENOMEM;
+	}
+
+	dump_buffer_size = size;
+	return dump_buffer_size;
+}
+
+int met_save_dump_buffer_real(const char *pathname)
+{
+	int size, ret = 0;
+	struct file *outfp = NULL;
+	mm_segment_t oldfs;
+
+	if (dump_data_size == 0)
+		return 0;
+
+	if (dump_data_size < 0 || dump_overrun_size < 0)
+		return -1;
+
+	if (dump_buffer == NULL || dump_buffer_size <= 0)
+		return -1;
+
+	if (dump_overrun)
+		size = dump_overrun_size;
+	else
+		size = dump_data_size;
+
+	if (size >= dump_buffer_size)
+		return -1;
+
+	oldfs = get_fs();
+	set_fs(KERNEL_DS);
+
+	outfp = filp_open(pathname, O_WRONLY | O_TRUNC | O_CREAT, 0664);
+	if (unlikely(outfp == NULL)) {
+		ERRF("can not open saved file for write\n");
+		return -EIO;
+	}
+
+	ret = vfs_write(outfp, dump_buffer, size, &(outfp->f_pos));
+	if (ret < 0)
+		ERRF("can not write to dump file\n");
+	else {
+		dump_data_size = 0;
+		dump_overrun = 0;
+		dump_overrun_size = 0;
+		dump_seq_no = 0;
+	}
+
+	set_fs(oldfs);
+
+	if (outfp != NULL)
+		filp_close(outfp, NULL);
+
+	return 0;
+}
+
+int met_save_log_real(const char *pathname)
+{
+	int len, ret = 0;
+	struct file *infp = NULL;
+	struct file *outfp = NULL;
+	void *ptr = NULL;
+	mm_segment_t oldfs;
+
+	infp = filp_open("/sys/kernel/debug/tracing/trace", O_RDONLY, 0);
+	if (unlikely(infp == NULL)) {
+		ERRF("can not open trace file for read\n");
+		ret = -1;
+		goto save_out;
+	}
+
+	outfp = filp_open(pathname, O_WRONLY | O_TRUNC | O_CREAT, 0664);
+	if (unlikely(outfp == NULL)) {
+		ERRF("can not open saved file for write\n");
+		ret = -2;
+		goto save_out;
+	}
+
+	ptr = (void *)__get_free_pages(GFP_KERNEL, 2);
+	if (ptr == NULL) {
+		ERRF("can not allocate buffer to copy\n");
+		ret = -3;
+		goto save_out;
+	}
+
+	oldfs = get_fs();
+	set_fs(KERNEL_DS);
+
+	while (1) {
+		len = vfs_read(infp, ptr, PAGE_SIZE << 2, &(infp->f_pos));
+		if (len < 0) {
+			ERRF("can not read from trace file\n");
+			ret = -3;
+			break;
+		} else if (len == 0) {
+			break;
+		}
+
+		ret = vfs_write(outfp, ptr, len, &(outfp->f_pos));
+		if (ret < 0) {
+			ERRF("can not write to saved file\n");
+			break;
+		}
+	}
+
+	set_fs(oldfs);
+
+save_out:
+	if (ptr != NULL)
+		free_pages((unsigned long)ptr, 2);
+	if (infp != NULL)
+		filp_close(infp, NULL);
+	if (outfp != NULL)
+		filp_close(outfp, NULL);
+
+	return ret;
+}
+
+#ifdef BUILD_WITH_MET
+#include <linux/module.h>
+#include <linux/uaccess.h>
+/* =========================================================================== */
+/* misc file nodes */
+/* =========================================================================== */
+static ssize_t enable_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	int i;
+
+	i = snprintf(buf, PAGE_SIZE, "%d\n", (bltab.flag >> 31) ? 0 : 1);
+	return i;
+}
+
+static ssize_t enable_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf,
+			    size_t n)
+{
+	int value;
+
+	if ((n == 0) || (buf == NULL))
+		return -EINVAL;
+	if (kstrtoint(buf, 0, &value) != 0)
+		return -EINVAL;
+
+	mutex_lock(&bltab.mlock);
+
+	if (value == 1)
+		bltab.flag &= (~MET_CLASS_ALL);
+	else
+		bltab.flag |= MET_CLASS_ALL;
+
+	mutex_unlock(&bltab.mlock);
+
+	return n;
+}
+
+static struct kobject *kobj_tag;
+static struct kobj_attribute enable_attr = __ATTR(enable, 0664, enable_show, enable_store);
+
+static ssize_t dump_buffer_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	int i, size;
+
+	if (dump_overrun)
+		size = dump_overrun_size;
+	else
+		size = dump_data_size;
+
+	i = snprintf(buf, PAGE_SIZE, "Buffer Size (KB)=%d\nData Size (KB)=%d\nOverrun=%d\n",
+		     dump_buffer_size >> 10, size >> 10, dump_overrun);
+	return i;
+}
+
+static ssize_t dump_buffer_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf,
+				 size_t n)
+{
+	int ret, value;
+
+	if ((n == 0) || (buf == NULL))
+		return -EINVAL;
+	if (kstrtoint(buf, 0, &value) != 0)
+		return -EINVAL;
+
+	ret = met_set_dump_buffer_real(value << 10);
+
+	if (ret < 0)
+		return ret;
+
+	return n;
+}
+
+static struct kobj_attribute dump_buffer_attr =
+__ATTR(dump_buffer_kb, 0664, dump_buffer_show, dump_buffer_store);
+
+static ssize_t options_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	int i = 0;
+
+	buf[0] = 0;
+
+	if (options_flag == 0) {
+		strncat(buf, "none\n", PAGE_SIZE - 1 - i);
+		i += 5;
+	}
+
+	if (options_flag & OPFLAG_OVERWRITE) {
+		strncat(buf, "overwrite\n", PAGE_SIZE - 1 - i);
+		i += 10;
+	}
+
+	return i;
+}
+
+static ssize_t options_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf,
+			     size_t n)
+{
+	if ((n == 0) || (buf == NULL))
+		return -EINVAL;
+
+	if ((n == 1) && (buf[0] == 0xA)) {
+		options_flag = 0;
+		return n;
+	}
+
+	if (strncmp(buf, "overwrite", 9) == 0)
+		options_flag |= OPFLAG_OVERWRITE;
+	else
+		return -EINVAL;
+
+	return n;
+}
+
+static struct kobj_attribute options_attr = __ATTR(options, 0664, options_show, options_store);
+
+int tag_reg(struct file_operations *const fops, struct kobject *kobj)
+{
+	int ret;
+
+	kobj_tag = kobject_create_and_add("tag", kobj);
+	if (kobj_tag == NULL) {
+		ERRF("can not create kobject: kobj_bus\n");
+		return -1;
+	}
+
+	ret = sysfs_create_file(kobj_tag, &enable_attr.attr);
+	if (ret != 0) {
+		ERRF("Failed to create enable in sysfs\n");
+		kobject_del(kobj_tag);
+		kobject_put(kobj_tag);
+		kobj_tag = NULL;
+		return ret;
+	}
+
+	ret = sysfs_create_file(kobj_tag, &dump_buffer_attr.attr);
+	if (ret != 0) {
+		ERRF("Failed to create dump_buffer in sysfs\n");
+		sysfs_remove_file(kobj_tag, &enable_attr.attr);
+		kobject_del(kobj_tag);
+		kobject_put(kobj_tag);
+		kobj_tag = NULL;
+		return ret;
+	}
+
+	ret = sysfs_create_file(kobj_tag, &options_attr.attr);
+	if (ret != 0) {
+		ERRF("Failed to create options in sysfs\n");
+		sysfs_remove_file(kobj_tag, &enable_attr.attr);
+		sysfs_remove_file(kobj_tag, &dump_buffer_attr.attr);
+		kobject_del(kobj_tag);
+		kobject_put(kobj_tag);
+		kobj_tag = NULL;
+		return ret;
+	}
+
+	met_tag_init();
+	met_ext_api.met_tag_start = met_tag_start_real;
+	met_ext_api.met_tag_end = met_tag_end_real;
+	met_ext_api.met_tag_async_start = met_tag_async_start_real;
+	met_ext_api.met_tag_async_end = met_tag_async_end_real;
+	met_ext_api.met_tag_oneshot = met_tag_oneshot_real;
+	met_ext_api.met_tag_userdata = met_tag_userdata_real;
+	met_ext_api.met_tag_dump = met_tag_dump_real;
+	met_ext_api.met_tag_disable = met_tag_disable_real;
+	met_ext_api.met_tag_enable = met_tag_enable_real;
+	met_ext_api.met_set_dump_buffer = met_set_dump_buffer_real;
+	met_ext_api.met_save_dump_buffer = met_save_dump_buffer_real;
+	met_ext_api.met_save_log = met_save_log_real;
+	met_ext_api.met_sched_switch = met_sched_switch;
+	return 0;
+}
+
+int tag_unreg(void)
+{
+	met_ext_api.met_tag_start = NULL;
+	met_ext_api.met_tag_end = NULL;
+	met_ext_api.met_tag_async_start = NULL;
+	met_ext_api.met_tag_async_end = NULL;
+	met_ext_api.met_tag_oneshot = NULL;
+	met_ext_api.met_tag_userdata = NULL;
+	met_ext_api.met_tag_dump = NULL;
+	met_ext_api.met_tag_disable = NULL;
+	met_ext_api.met_tag_enable = NULL;
+	met_ext_api.met_set_dump_buffer = NULL;
+	met_ext_api.met_save_dump_buffer = NULL;
+	met_ext_api.met_save_log = NULL;
+	met_ext_api.met_show_bw_limiter = NULL;
+	met_ext_api.met_reg_bw_limiter = NULL;
+	met_ext_api.met_show_clk_tree = NULL;
+	met_ext_api.met_reg_clk_tree = NULL;
+	met_ext_api.met_sched_switch = NULL;
+	met_tag_uninit();
+	sysfs_remove_file(kobj_tag, &enable_attr.attr);
+	sysfs_remove_file(kobj_tag, &dump_buffer_attr.attr);
+	sysfs_remove_file(kobj_tag, &options_attr.attr);
+	kobject_del(kobj_tag);
+	kobject_put(kobj_tag);
+	kobj_tag = NULL;
+	return 0;
+}
+
+#endif				/* BUILD_WITH_MET */
+
+#else				/* not MET_USER_EVENT_SUPPORT */
+
+#ifdef BUILD_WITH_MET
+int tag_reg(void *p, void *q)
+{
+	return 0;
+}
+
+int tag_unreg(void)
+{
+	return 0;
+}
+#endif
+
+#endif				/* MET_USER_EVENT_SUPPORT */
diff --git a/src/devtools/met-driver/4.14/common/met_vcoredvfs.c b/src/devtools/met-driver/4.14/common/met_vcoredvfs.c
new file mode 100644
index 0000000..560ef67
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/met_vcoredvfs.c
@@ -0,0 +1,314 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/dma-mapping.h>
+
+#include <helio-dvfsrc.h>
+
+#define MET_USER_EVENT_SUPPORT
+#include "met_drv.h"
+#include "trace.h"
+
+#include "core_plf_init.h"
+#include "core_plf_trace.h"
+
+/*======================================================================*/
+/*	Global variable definitions					*/
+/*======================================================================*/
+
+/* Global variables */
+struct metdevice met_vcoredvfs;
+int polling_mode = 1;
+
+/* external symbols */
+#define DEFAULT_INFO_NUM 3
+#define DEFAULT_SRC_NUM 1
+
+char *default_info_name[DEFAULT_INFO_NUM] = {
+	"OPP",
+	"VOLT",
+	"FREQ"
+};
+
+char *default_src_name[DEFAULT_SRC_NUM] = {
+	"MD2SPM"
+};
+
+unsigned int opp_info[DEFAULT_INFO_NUM];
+unsigned int src_req[DEFAULT_SRC_NUM];
+
+static int met_vcorefs_get_num_opp(void)
+{
+	if (vcorefs_get_num_opp_symbol)
+		return vcorefs_get_num_opp_symbol();
+	else
+		return 0;
+}
+
+static int met_vcorefs_get_opp_info_num(void)
+{
+	if (vcorefs_get_opp_info_num_symbol)
+		return vcorefs_get_opp_info_num_symbol();
+	else
+		return DEFAULT_INFO_NUM;
+}
+
+
+static int met_vcorefs_get_src_req_num(void)
+{
+	if (vcorefs_get_src_req_num_symbol)
+		return vcorefs_get_src_req_num_symbol();
+	else
+		return DEFAULT_SRC_NUM;
+}
+
+
+static char **met_vcorefs_get_opp_info_name(void)
+{
+	if (vcorefs_get_opp_info_name_symbol)
+		return vcorefs_get_opp_info_name_symbol();
+	else
+		return default_info_name;
+}
+
+static char **met_vcorefs_get_src_req_name(void)
+{
+	if (vcorefs_get_src_req_name_symbol)
+		return vcorefs_get_src_req_name_symbol();
+	else
+		return default_src_name;
+}
+
+static unsigned int *met_vcorefs_get_opp_info(void)
+{
+	if (vcorefs_get_opp_info_symbol)
+		return vcorefs_get_opp_info_symbol();
+
+	return opp_info;
+}
+
+static unsigned int *met_vcorefs_get_src_req(void)
+{
+	if (vcorefs_get_src_req_symbol)
+		return vcorefs_get_src_req_symbol();
+
+	return src_req;
+}
+
+
+/*======================================================================*/
+/*	File Node definitions					*/
+/*======================================================================*/
+
+static struct kobject *kobj_met_vcoredvfs;
+
+static ssize_t vcorefs_polling_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf);
+static ssize_t vcorefs_polling_store(struct kobject *kobj,
+				   struct kobj_attribute *attr, const char *buf, size_t n);
+static struct kobj_attribute vcorefs_polling_attr =
+__ATTR(vcorefs_polling, 0664, vcorefs_polling_show, vcorefs_polling_store);
+
+/*======================================================================*/
+/*	Utilities					*/
+/*======================================================================*/
+
+static inline int do_vcoredvfs(void)
+{
+	return met_vcoredvfs.mode;
+}
+
+/*======================================================================*/
+/*	Data Output					*/
+/*======================================================================*/
+
+noinline void vcorefs(unsigned char cnt, unsigned int *value)
+{
+	char *SOB, *EOB;
+
+	MET_TRACE_GETBUF(&SOB, &EOB);
+	EOB = ms_formatH_EOL(EOB, cnt, value);
+	MET_TRACE_PUTBUF(SOB, EOB);
+}
+
+noinline void vcorefs_kicker(unsigned char cnt, int *value)
+{
+	char *SOB, *EOB;
+
+	MET_TRACE_GETBUF(&SOB, &EOB);
+	EOB = ms_formatH_EOL(EOB, cnt, value);
+	MET_TRACE_PUTBUF(SOB, EOB);
+}
+
+noinline void ms_vcorefs(unsigned char cnt, unsigned int *value)
+{
+	char *SOB, *EOB;
+
+	MET_TRACE_GETBUF(&SOB, &EOB);
+	EOB = ms_formatH_EOL(EOB, cnt, value);
+	MET_TRACE_PUTBUF(SOB, EOB);
+}
+
+/*======================================================================*/
+/*	Callback functions						*/
+/*======================================================================*/
+void vcoredvfs_irq(int opp)
+{
+	int num;
+	unsigned int *out;
+
+	num = met_vcorefs_get_opp_info_num();
+	out = met_vcorefs_get_opp_info();
+
+	vcorefs(num, out);
+}
+
+/*======================================================================*/
+/*	File Node Operations						*/
+/*======================================================================*/
+static ssize_t vcorefs_polling_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%d\n", polling_mode);
+}
+
+static ssize_t vcorefs_polling_store(struct kobject *kobj,
+				   struct kobj_attribute *attr, const char *buf, size_t n)
+{
+	int value;
+
+	if ((n == 0) || (buf == NULL))
+		return -EINVAL;
+
+	if (kstrtoint(buf, 0, &value) != 0)
+		return -EINVAL;
+
+	if (value < 0)
+		return -EINVAL;
+
+	polling_mode = value;
+
+	return n;
+}
+
+/*======================================================================*/
+/*	MET Device Operations						*/
+/*======================================================================*/
+static int met_vcoredvfs_create(struct kobject *parent)
+{
+	int ret = 0;
+
+	kobj_met_vcoredvfs = parent;
+
+	ret = sysfs_create_file(kobj_met_vcoredvfs, &vcorefs_polling_attr.attr);
+	if (ret != 0) {
+		pr_debug("Failed to create vcoredvfs in sysfs\n");
+		return ret;
+	}
+
+	return ret;
+}
+
+static void met_vcoredvfs_delete(void)
+{
+	sysfs_remove_file(kobj_met_vcoredvfs, &vcorefs_polling_attr.attr);
+}
+
+static void met_vcoredvfs_start(void)
+{
+	vcoredvfs_irq(-1);
+}
+
+static void met_vcoredvfs_stop(void)
+{
+	vcoredvfs_irq(-1);
+}
+
+static void met_vcoredvfs_polling(unsigned long long stamp, int cpu)
+{
+	int num;
+	unsigned int *out;
+
+	if (!do_vcoredvfs())
+		return;
+
+	/* vcorefs opp information */
+	if (polling_mode)
+		vcoredvfs_irq(-1);
+
+	/* vcorefs source request */
+	num = met_vcorefs_get_src_req_num();
+	out = met_vcorefs_get_src_req();
+
+	ms_vcorefs(num, out);
+}
+
+static const char help[] =
+	"  --vcoredvfs				monitor VCORE DVFS\n";
+static int vcoredvfs_print_help(char *buf, int len)
+{
+	return snprintf(buf, PAGE_SIZE, help);
+}
+
+static const char header_dvfs[] = "met-info [000] 0.0: met_vcorefs_header:";
+
+static const char header_ms_dvfs[] = "met-info [000] 0.0: ms_vcorefs_header:";
+
+static int vcoredvfs_print_header(char *buf, int len)
+{
+	int ret = 0;
+	int idx = 0;
+	int num = 0;
+	char **header;
+
+	ret = snprintf(buf, PAGE_SIZE, "met-info [000] 0.0: met_vcorefs_cfg: NUM_OPP:%d\n", met_vcorefs_get_num_opp());
+
+	/* opp information */
+	num = met_vcorefs_get_opp_info_num();
+	header = met_vcorefs_get_opp_info_name();
+
+	ret += snprintf(buf + ret, PAGE_SIZE, header_dvfs);
+	for (idx = 0; idx < num; idx++)
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "%s,", header[idx]);
+	buf[strlen(buf)-1] = '\n';
+
+	/* source requests */
+	num = met_vcorefs_get_src_req_num();
+	header = met_vcorefs_get_src_req_name();
+
+	ret += snprintf(buf + ret, PAGE_SIZE - ret, header_ms_dvfs);
+	for (idx = 0; idx < num; idx++)
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "%s,", header[idx]);
+	buf[strlen(buf)-1] = '\n';
+
+	met_vcoredvfs.mode = 0;
+
+	return ret;
+}
+
+struct metdevice met_vcoredvfs = {
+	.name = "vcoredvfs",
+	.owner = THIS_MODULE,
+	.type = MET_TYPE_BUS,
+	.create_subfs = met_vcoredvfs_create,
+	.delete_subfs = met_vcoredvfs_delete,
+	.cpu_related = 0,
+	.start = met_vcoredvfs_start,
+	.stop = met_vcoredvfs_stop,
+	.polling_interval = 1,	/* ms */
+	.timed_polling = met_vcoredvfs_polling,
+	.print_help = vcoredvfs_print_help,
+	.print_header = vcoredvfs_print_header,
+};
diff --git a/src/devtools/met-driver/4.14/common/met_wall_time.c b/src/devtools/met-driver/4.14/common/met_wall_time.c
new file mode 100644
index 0000000..ac7e18e
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/met_wall_time.c
@@ -0,0 +1,256 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/sched/clock.h>
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <asm/div64.h>
+#include <linux/types.h>
+#include <linux/kallsyms.h>
+
+#define MET_USER_EVENT_SUPPORT
+#include "met_drv.h"
+#include "trace.h"
+#include "interface.h"
+
+#define MODE_CUSOM_CLKSRC       2
+struct metdevice met_wall_time;
+
+/**                                                                        */
+/* How to add a new clocksource:                                           */
+/*                                                                         */
+/* 1. add constant for new clocksource in #define-macro                    */
+/* 2. declare new weakref function                                         */
+/* 3. implement handler functions:                                         */
+/*     (1) clksrc_attr_t::*ready:                                          */
+/*         check if ...                                                    */
+/*         (i) clocksource correctly working                               */
+/*         (ii) weakref function is not null                               */
+/*     (2) clksrc_attr_t::*get_cnt: read clocksource from weakref function */
+/* 4. place attrs of new clocksource into clksrc_attr_tb                   */
+/* 5. update DEFAULT_CLKSRC_STR                                            */
+/* 6. update help message                                                  */
+/**                                                                        */
+
+#define __SYS_TIMER             0x0
+#define __GPT1                  0x1
+#define __GPT2                  0x2
+#define __GPT3                  0x3
+#define __GPT4                  0x4
+#define __GPT5                  0x5
+#define __GPT6                  0x6
+
+#define DEFAULT_CLKSRC_STR      "SYS_TIMER"
+
+extern u64 met_arch_counter_get_cntvct(void);
+u64 (*met_arch_counter_get_cntvct_symbol)(void);
+
+int __sys_timer_get_cnt(u8 clksrc, u64 *cycles)
+{
+	if (met_arch_counter_get_cntvct_symbol)
+		*cycles = met_arch_counter_get_cntvct_symbol();
+	return 0;
+}
+
+
+struct clksrc_attr_t {
+	u8 clksrc;
+	const char *clksrc_str;
+	/* checks if clksrc/get_cnt function is working/available */
+	int (*clksrc_ready)(u8 clksrc);
+	int (*clksrc_get_cnt)(u8 clksrc, u64 *cycles);
+};
+
+struct clksrc_attr_t clksrc_attr_tb[] = {
+	{__SYS_TIMER, "SYS_TIMER", NULL, __sys_timer_get_cnt},
+	/* {__GPT1, "GPT1", __gpt_timer_ready, __gpt_timer_get_cnt}, */
+	/* {__GPT2, "GPT2", __gpt_timer_ready, __gpt_timer_get_cnt}, */
+	/* {__GPT3, "GPT3", __gpt_timer_ready, __gpt_timer_get_cnt}, */
+	/* {__GPT4, "GPT4", __gpt_timer_ready, __gpt_timer_get_cnt}, */
+	/* {__GPT5, "GPT5", __gpt_timer_ready, __gpt_timer_get_cnt}, */
+	/* {__GPT6, "GPT6", __gpt_timer_ready, __gpt_timer_get_cnt}, */
+};
+
+static const struct clksrc_attr_t *lookup_clksrc_attr_tb(const char *clksrc_str, int len);
+static const struct clksrc_attr_t *wall_time_attr;
+
+/* definitions for auto-sampling of working freq. of clocksource */
+/* maximum tolerable error percentage(%) of sampled clock freq */
+#define FREQ_ERR_PERCENT        3
+
+/* expected working freq. of clocksources */
+static const u32 freq_level[] = { 32768, 13000000, 26000000 };
+
+static u32 lookup_freq_level(u32 freq);
+
+/* flag indicating whether sampling is on-going */
+static u32 do_sample;
+static u32 freq;
+static u64 start_us_ts;
+static u64 start_wall_time;
+/* end definitions for sampling of freq. */
+
+static void wall_time_start(void)
+{
+	met_arch_counter_get_cntvct_symbol = (void *)symbol_get(met_arch_counter_get_cntvct);
+
+	if (met_wall_time.mode != MODE_CUSOM_CLKSRC) {
+		wall_time_attr = lookup_clksrc_attr_tb(DEFAULT_CLKSRC_STR,
+						       strlen(DEFAULT_CLKSRC_STR));
+	}
+
+	freq = 0;
+	do_sample = 1;
+
+	if (wall_time_attr) {
+
+		/* XXX: always use CPU 0 */
+		start_us_ts = cpu_clock(0);
+		wall_time_attr->clksrc_get_cnt(wall_time_attr->clksrc, &start_wall_time);
+
+		/* us_ts = ap_ts/1000; */
+		do_div(start_us_ts, 1000);
+	}
+}
+
+noinline void met_ap_wall_time(unsigned long long ts, int cpu)
+{
+	u64 ap_ts;
+	u64 us_ts;
+	u64 sec;
+	u64 usec;
+	u64 cycles;
+	u64 f;
+
+	if (wall_time_attr) {
+
+		wall_time_attr->clksrc_get_cnt(wall_time_attr->clksrc, &cycles);
+		ap_ts = cpu_clock(cpu);
+
+		us_ts = ap_ts;
+		do_div(us_ts, 1000);	/* us_ts = ap_ts/1000; */
+
+		sec = us_ts;
+		usec = do_div(sec, 1000000);	/* sec = us_ts/1000000; usec = us_ts%1000000; */
+		MET_TRACE("TS.APTS=%llu.%06llu WCLK=%llu\n", (unsigned long long)sec,
+			   (unsigned long long)usec, (unsigned long long)cycles);
+
+		if (do_sample) {
+
+			do_sample = 0;
+
+			f = (cycles - start_wall_time) * 1000000;
+			do_div(f, us_ts - start_us_ts);
+
+			/* don't worry about the u64 -> u32 assignment,           */
+			/* sampled wall-clock freq is expected to be below 2^32-1 */
+			freq = lookup_freq_level(f);
+
+			/* debug message */
+			/* MET_TRACE("wall_time debug: result: %u," */
+			/*         "start cycle: %llu, end cycle: %llu, cycle diff: %llu," */
+			/*         "start us: %llu, end us: %llu, us diff: %llu", */
+			/*         f, */
+			/*         start_wall_time, cycles, cycles - start_wall_time, */
+			/*         start_us_ts, us_ts, us_ts - start_us_ts); */
+
+			if (freq != 0)
+				met_tag_oneshot_real(33880, "_WCLK_FREQ_", freq);
+		}
+	}
+}
+
+static const char help[] =
+"  --wall_time                             output wall-clock syncing info in system timer\n";
+/* "  --wall_time=SYS_TIMER|GPT[1-6]  output wall-clock syncing info in custom clocksource\n"; */
+
+static int wall_time_print_help(char *buf, int len)
+{
+	return snprintf(buf, PAGE_SIZE, help);
+}
+
+static const char *header =
+"met-info [000] 0.0: WCLK: %d\n"
+"met-info [000] 0.0: clocksource: %s\n";
+
+static int wall_time_print_header(char *buf, int len)
+{
+	return snprintf(buf, len, header,
+			freq == 0 ? -1 : freq,
+			wall_time_attr ? wall_time_attr->clksrc_str : "NONE");
+}
+
+static int wall_time_process_argument(const char *arg, int len)
+{
+	/* reset wall-time clocksource */
+	wall_time_attr = lookup_clksrc_attr_tb(arg, len);
+
+	if (!wall_time_attr) {
+		met_wall_time.mode = 0;
+		return -1;
+	}
+
+	met_wall_time.mode = MODE_CUSOM_CLKSRC;
+	return 0;
+}
+
+static const struct clksrc_attr_t *lookup_clksrc_attr_tb(const char *clksrc_str, int len)
+{
+	int i;
+	const struct clksrc_attr_t *attr;
+	int tb_nmemb = sizeof(clksrc_attr_tb) / sizeof(*clksrc_attr_tb);
+
+	for (i = 0; i < tb_nmemb; i++) {
+
+		attr = clksrc_attr_tb + i;
+
+		if (strlen(attr->clksrc_str) == len &&
+		    strncmp(clksrc_str, attr->clksrc_str, len) == 0) {
+			return attr;
+		}
+	}
+
+	return NULL;
+}
+
+static u32 lookup_freq_level(u32 freq)
+{
+
+	int ii;
+	int freq_nmemb = sizeof(freq_level) / sizeof(*freq_level);
+	u32 fdiff;
+
+	for (ii = 0; ii < freq_nmemb; ii++) {
+		fdiff = freq_level[ii] > freq ? freq_level[ii] - freq : freq - freq_level[ii];
+		if (fdiff < freq_level[ii] * FREQ_ERR_PERCENT / 100)
+			return freq_level[ii];
+	}
+
+	return 0;
+}
+
+struct metdevice met_wall_time = {
+	.name = "wall_time",
+	.owner = THIS_MODULE,
+	.type = MET_TYPE_BUS,
+	.cpu_related = 0,
+	.start = wall_time_start,
+	.polling_interval = 1000,
+	.timed_polling = met_ap_wall_time,
+	.print_help = wall_time_print_help,
+	.print_header = wall_time_print_header,
+	.process_argument = wall_time_process_argument,
+};
+EXPORT_SYMBOL(met_wall_time);
diff --git a/src/devtools/met-driver/4.14/common/mips_pmu_hw.c b/src/devtools/met-driver/4.14/common/mips_pmu_hw.c
new file mode 100644
index 0000000..19e836b
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/mips_pmu_hw.c
@@ -0,0 +1,438 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <asm/system.h>
+#include <linux/smp.h>
+
+#include "cpu_pmu.h"
+#include "mips_pmu_name.h"
+
+struct chip_pmu {
+	enum cpu_type_enum type;
+	struct pmu_desc **desc;
+	void *refptr;
+	const char *cpu_name;
+	unsigned int pmu_desc_size;
+	unsigned int max_hw_events;
+	unsigned int max_reg_count;
+};
+
+struct pmu_desc *mips_pmu_desc[MIPS_MAX_HWEVENTS];
+
+static struct chip_pmu chips[] = {
+	{CPU_1004K, mips_pmu_desc, (void *)mips_1004k_pmu_desc, "MIPS_1004K",
+	 MIPS_1004K_PMU_DESC_SIZE, MIPS_1004K_PMU_DESC_COUNT, PMU_1004K_MAX_HW_REGS},
+};
+
+static struct chip_pmu chip_unknown = { CPU_UNKNOWN, NULL, NULL, "Unknown CPU", 0, 0, 0 };
+
+#define CHIP_PMU_COUNT (sizeof(chips) / sizeof(struct chip_pmu))
+static struct chip_pmu *chip;
+#define M_CONFIG1_PC    (1 << 4)
+
+#define M_PERFCTL_EXL           (1  <<  0)
+#define M_PERFCTL_KERNEL        (1  <<  1)
+#define M_PERFCTL_SUPERVISOR        (1  <<  2)
+#define M_PERFCTL_USER          (1  <<  3)
+#define M_PERFCTL_INTERRUPT_ENABLE  (1  <<  4)
+#define M_PERFCTL_EVENT(event)      (((event) & 0x3ff)  << 5)
+#define M_PERFCTL_VPEID(vpe)        ((vpe)    << 16)
+
+#ifdef CONFIG_CPU_BMIPS5000
+#define M_PERFCTL_MT_EN(filter)     0
+#else				/* !CONFIG_CPU_BMIPS5000 */
+#define M_PERFCTL_MT_EN(filter)     ((filter) << 20)
+#endif				/* CONFIG_CPU_BMIPS5000 */
+
+#define    M_TC_EN_ALL          M_PERFCTL_MT_EN(0)
+#define    M_TC_EN_VPE          M_PERFCTL_MT_EN(1)
+#define    M_TC_EN_TC           M_PERFCTL_MT_EN(2)
+#define M_PERFCTL_TCID(tcid)        ((tcid)   << 22)
+#define M_PERFCTL_WIDE          (1  << 30)
+#define M_PERFCTL_MORE          (1  << 31)
+#define M_PERFCTL_TC            (1  << 30)
+
+#define M_PERFCTL_COUNT_EVENT_WHENEVER  (M_PERFCTL_EXL |        \
+		M_PERFCTL_KERNEL |      \
+		M_PERFCTL_USER |        \
+		M_PERFCTL_SUPERVISOR |      \
+		M_PERFCTL_INTERRUPT_ENABLE)
+
+#ifdef CONFIG_MIPS_MT_SMP
+#define M_PERFCTL_CONFIG_MASK       0x3fff801f
+#else
+#define M_PERFCTL_CONFIG_MASK       0x1f
+#endif
+#define M_PERFCTL_EVENT_MASK        0xfe0
+
+#define vpe_id()    0
+
+/* To get current TCID*/
+#define read_c0_tcbind() __read_32bit_c0_register($2, 2)
+
+struct cpu_hw_events {
+	unsigned int config_base[MIPS_MAX_HWEVENTS];
+	unsigned int saved_ctrl[MIPS_MAX_HWEVENTS];
+};
+
+DEFINE_PER_CPU(struct cpu_hw_events, cpu_hw_events) = {
+	.config_base = {
+	0, 0, 0, 0}, .saved_ctrl = {
+0, 0, 0, 0},};
+
+static enum cpu_type_enum mips_get_ic(void)
+{
+	unsigned int value = current_cpu_type();
+
+	/* pr_debug("ic value: %X\n", value); */
+	return value;
+}
+
+static int __n_counters(void)
+{
+	if (!(read_c0_config1() & M_CONFIG1_PC))
+		return 0;
+	if (!(read_c0_perfctrl0() & M_PERFCTL_MORE))
+		return 1;
+	if (!(read_c0_perfctrl1() & M_PERFCTL_MORE))
+		return 2;
+	if (!(read_c0_perfctrl2() & M_PERFCTL_MORE))
+		return 3;
+
+	return 4;
+}
+
+static int n_counters(void)
+{
+	int counters;
+
+	switch (current_cpu_type()) {
+	case CPU_R10000:
+		counters = 2;
+		break;
+	case CPU_R12000:
+	case CPU_R14000:
+		counters = 4;
+		break;
+	default:
+		counters = __n_counters();
+		break;
+	}
+
+	return counters;
+}
+
+static int mips_pmu_hw_get_counters(void)
+{
+	int count = n_counters();
+
+	/* pr_debug("pmu hw event nr: %d\n", count); */
+	return count;
+}
+
+static unsigned int mipsxx_pmu_swizzle_perf_idx(unsigned int idx)
+{
+	if (vpe_id() == 1)
+		idx = (idx + 2) & 3;
+	return idx;
+}
+
+static void mipsxx_pmu_write_counter(unsigned int idx, u64 val)
+{
+	idx = mipsxx_pmu_swizzle_perf_idx(idx);
+
+	switch (idx) {
+	case 0:
+		write_c0_perfcntr0(val);
+		return;
+	case 1:
+		write_c0_perfcntr1(val);
+		return;
+	case 2:
+		write_c0_perfcntr2(val);
+		return;
+	case 3:
+		write_c0_perfcntr3(val);
+		return;
+	}
+}
+
+static u64 mipsxx_pmu_read_counter(unsigned int idx)
+{
+	idx = mipsxx_pmu_swizzle_perf_idx(idx);
+
+	switch (idx) {
+	case 0:
+		/*
+		 * The counters are unsigned, we must cast to truncate
+		 * off the high bits.
+		 */
+		return (u32) read_c0_perfcntr0();
+	case 1:
+		return (u32) read_c0_perfcntr1();
+	case 2:
+		return (u32) read_c0_perfcntr2();
+	case 3:
+		return (u32) read_c0_perfcntr3();
+	default:
+		WARN_ONCE(1, "Invalid performance counter number (%d)\n", idx);
+		return 0;
+	}
+}
+
+
+static unsigned int mipsxx_pmu_read_control(unsigned int idx)
+{
+	idx = mipsxx_pmu_swizzle_perf_idx(idx);
+
+	switch (idx) {
+	case 0:
+		return read_c0_perfctrl0();
+	case 1:
+		return read_c0_perfctrl1();
+	case 2:
+		return read_c0_perfctrl2();
+	case 3:
+		return read_c0_perfctrl3();
+	default:
+		WARN_ONCE(1, "Invalid performance counter number (%d)\n", idx);
+		return 0;
+	}
+}
+
+static void mipsxx_pmu_write_control(unsigned int idx, unsigned int val)
+{
+	idx = mipsxx_pmu_swizzle_perf_idx(idx);
+
+	switch (idx) {
+	case 0:
+		write_c0_perfctrl0(val);
+		return;
+	case 1:
+		write_c0_perfctrl1(val);
+		return;
+	case 2:
+		write_c0_perfctrl2(val);
+		return;
+	case 3:
+		write_c0_perfctrl3(val);
+		return;
+	}
+}
+
+static int mipsxx_pmu_get_vpeid(void)
+{
+	return read_c0_tcbind() & 0xF;
+}
+
+static void mipsxx_pmu_reset_counters(int idx)
+{
+	switch (idx) {
+	case 3:
+		mipsxx_pmu_write_control(3, 0);
+		mipsxx_pmu_write_counter(3, 0);
+		break;
+	case 2:
+		mipsxx_pmu_write_control(2, 0);
+		mipsxx_pmu_write_counter(2, 0);
+		break;
+	case 1:
+		mipsxx_pmu_write_control(1, 0);
+		mipsxx_pmu_write_counter(1, 0);
+		break;
+	case 0:
+		mipsxx_pmu_write_control(0, 0);
+		mipsxx_pmu_write_counter(0, 0);
+		break;
+	}
+}
+
+static void mipsxx_pmu_enable_event(int idx, int event)
+{
+	struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
+	unsigned long flags;
+
+	WARN_ON(idx < 0 || idx >= chip->max_hw_events);
+	cpuc->saved_ctrl[idx] = M_PERFCTL_EVENT(event & 0xff) |
+	    M_PERFCTL_VPEID(mipsxx_pmu_get_vpeid()) |
+	    (cpuc->config_base[idx] & M_PERFCTL_CONFIG_MASK);
+#ifdef CONFIG_CPU_BMIPS5000
+	/* if (IS_ENABLED(CONFIG_CPU_BMIPS5000)) */
+	/* enable the counter for the calling thread */
+	cpuc->saved_ctrl[idx] |= (1 << (12 + vpe_id())) | M_PERFCTL_TC;
+#endif
+	/*
+	 * To enable pmu count
+	 */
+	local_irq_save(flags);
+	mipsxx_pmu_write_control(idx, cpuc->saved_ctrl[idx]);
+	local_irq_restore(flags);
+}
+
+static void mipsxx_pmu_disable_event(int idx)
+{
+	struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
+	unsigned long flags;
+
+	/* WARN_ON(idx < 0 || idx >= mipspmu.num_counters); */
+	WARN_ON(idx < 0 || idx >= chip->max_hw_events);
+
+	local_irq_save(flags);
+	cpuc->saved_ctrl[idx] = mipsxx_pmu_read_control(idx) & ~M_PERFCTL_COUNT_EVENT_WHENEVER;
+	mipsxx_pmu_write_control(idx, cpuc->saved_ctrl[idx]);
+	local_irq_restore(flags);
+}
+
+static int mips_pmu_hw_get_event_desc(int idx, int event, char *event_desc)
+{
+	int i;
+
+	if (event_desc == NULL) {
+		pr_debug("event_desc is NULL\n");
+		return -1;
+	}
+
+	for (i = 0; i < chip->max_reg_count; i++) {
+		if (chip->desc[idx][i].event == event) {
+			strncpy(event_desc, chip->desc[idx][i].name, MXSIZE_PMU_DESC - 1);
+			break;
+		}
+	}
+	if (i == chip->max_reg_count)
+		return -1;
+
+	return 0;
+}
+
+
+static int mips_pmu_hw_check_event(struct met_pmu *pmu, int idx, int event)
+{
+	int i;
+
+	/* to check index over run */
+	if (!chip)
+		return -1;
+
+	if (idx >= chip->max_hw_events)
+		return -1;
+
+	for (i = 0; i < chip->max_reg_count; i++) {
+		if (chip->desc[idx][i].event == event)
+			break;
+	}
+	if (i == chip->max_reg_count)
+		return -1;
+
+	return 0;
+}
+
+static void mips_pmu_hw_start(struct met_pmu *pmu, int count)
+{
+	int i;
+	int generic = count - 1;
+	struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
+
+	/* pr_debug("hw_start generic: %d\n", generic); */
+	for (i = 0; i < generic; i++) {
+		/* init config */
+		cpuc->config_base[i] = 0;
+		cpuc->config_base[i] |= M_TC_EN_VPE;
+		cpuc->config_base[i] |= M_PERFCTL_USER;
+		cpuc->config_base[i] |= M_PERFCTL_KERNEL;
+		cpuc->config_base[i] |= M_PERFCTL_EXL;
+		cpuc->config_base[i] |= M_PERFCTL_SUPERVISOR;
+		cpuc->config_base[i] &= M_PERFCTL_CONFIG_MASK;
+		 /**/ mipsxx_pmu_reset_counters(i);
+		if (pmu[i].mode == MODE_POLLING)
+			mipsxx_pmu_enable_event(i, pmu[i].event);
+	}
+	if (pmu[count - 1].mode == MODE_POLLING)
+		pr_debug("%s %d BUG!!! index over run!!\n", __func__, __LINE__);
+}
+
+static void mips_pmu_hw_stop(int count)
+{
+	int idx = 0;
+	int generic = count - 1;
+	/* pr_debug("reset %d\n", generic); */
+	for (idx = 0; idx < generic; idx++) {
+		mipsxx_pmu_reset_counters(idx);
+		mipsxx_pmu_disable_event(idx);
+	}
+}
+
+
+static unsigned int mips_pmu_hw_polling(struct met_pmu *pmu, int count, unsigned int *pmu_value)
+{
+	int i, cnt = 0;
+	int generic = count - 1;
+
+	for (i = 0; i < generic; i++) {
+		if (pmu[i].mode == MODE_POLLING) {
+			pmu_value[cnt] = mipsxx_pmu_read_counter(i);
+			cnt++;
+			mipsxx_pmu_reset_counters(i);
+			mipsxx_pmu_enable_event(i, pmu[i].event);
+		}
+	}
+	if (pmu[count - 1].mode == MODE_POLLING) {
+		pr_debug("%s %d BUG!!! index over run!!\n", __func__, __LINE__);
+		pmu_value[cnt] = 0xFFFF;
+		cnt++;
+	}
+
+	return cnt;
+}
+
+
+
+struct cpu_pmu_hw mips_pmu = {
+	.name = "mips_pmu",
+	.get_event_desc = mips_pmu_hw_get_event_desc,
+	.check_event = mips_pmu_hw_check_event,
+	.start = mips_pmu_hw_start,
+	.stop = mips_pmu_hw_stop,
+	.polling = mips_pmu_hw_polling,
+};
+
+struct cpu_pmu_hw *cpu_pmu_hw_init(void)
+{
+	int i = 0;
+	enum cpu_type_enum type;
+	int pmu_hw_count = 0;
+
+	type = mips_get_ic();
+
+	if (CPU_UNKNOWN == type || CPU_LAST == type) {
+		chip = &chip_unknown;
+		return NULL;
+	}
+	for (i = 0; i < CHIP_PMU_COUNT; i++) {
+		if (chips[i].type == type) {
+			chip = &(chips[i]);
+			break;
+		}
+	}
+	if (i == CHIP_PMU_COUNT) {
+		chip = &chip_unknown;
+		return NULL;
+	}
+
+	pmu_hw_count = mips_pmu_hw_get_counters();
+	for (i = 0; i < pmu_hw_count; i++)
+		chip->desc[i] = chip->refptr + (chip->pmu_desc_size * i);
+
+	mips_pmu.nr_cnt = pmu_hw_count + 1;
+	mips_pmu.cpu_name = chip->cpu_name;
+	return &mips_pmu;
+}
diff --git a/src/devtools/met-driver/4.14/common/mips_pmu_name.h b/src/devtools/met-driver/4.14/common/mips_pmu_name.h
new file mode 100644
index 0000000..d7ea26a
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/mips_pmu_name.h
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+
+#ifndef _MIPS_PMU_NAME_H_
+#define _MIPS_PMU_NAME_H_
+
+/* MIPS 1004K */
+#define MIPS_MAX_HWEVENTS		(4)
+
+#define PMU_1004K_MAX_HW_REGS	(128)
+struct pmu_desc mips_1004k_pmu_desc[][PMU_1004K_MAX_HW_REGS] = {
+	/* COUNT 0 */
+	{
+	 {0, "CPU_CYCLES"},
+	 {1, "CPU_INST"},
+	 {2, "BRANCH_INSNS"},
+	 {3, "JR_31_INSNS"},
+	 {4, "JR_NON_31_INSNS"},
+	 {5, "ITLB_ACCESSES"},
+	 {6, "DTLB_ACCESSES"},
+	 {7, "JTLB_INSN_ACCESSES"},
+	 {8, "JTLB_DATA_ACCESSES"},
+	 {9, "ICACHE_ACCESSES"},
+	 {10, "DCACHE_ACCESSES"},
+	 {11, "DCACHE_MISSES"},
+	 {12, "RESERVED"},
+	 {13, "STORE_MISS_INSNS"},
+	 {14, "INTEGER_INSNS"},
+	 {15, "LOAD_INSNS"},
+	 {16, "J_JAL_INSNS"},
+	 {17, "NO_OPS_INSNS"},
+	 {18, "ALL_STALLS"},
+	 {19, "SC_INSNS"},
+	 {20, "PREFETCH_INSNS"},
+	 {21, "L2_CACHE_WRITEBACKS"},
+	 {22, "L2_CACHE_MISSES"},
+	 {23, "EXCEPTIONS_TAKEN"},
+	 {24, "CACHE_FIXUP_CYCLES"},
+	 {25, "IFU_STALLS"},
+	 {26, "DSP_INSNS"},
+	 {27, "RESERVED"},
+	 {28, "POLICY_EVENTS"},
+	 {29, "ISPRAM_EVENTS"},
+	 {30, "COREEXTEND_EVENTS"},
+	 {31, "YIELD_EVENTS"},
+	 {32, "ITC_LOADS"},
+	 {33, "UNCACHED_LOAD_INSNS"},
+	 {34, "FORK_INSNS"},
+	 {35, "CP2_ARITH_INSNS"},
+	 {36, "INTERVENTION_STALLS"},
+	 {37, "ICACHE_MISS_STALLS"},
+	 {38, "RESERVED"},
+	 {39, "DCACHE_MISS_CYCLES"},
+	 {40, "UNCACHED_STALLS"},
+	 {41, "MDU_STALLS"},
+	 {42, "CP2_STALLS"},
+	 {43, "ISPRAM_STALLS"},
+	 {44, "CACHE_INSN_STALLS"},
+	 {45, "LOAD_USE_STALLS"},
+	 {46, "INTERLOCK_STALLS"},
+	 {47, "RELAX_STALLS"},
+	 {48, "IFU_FB_FULL_REFETCHES"},
+	 {49, "EJTAG_INSN_TRIGGERS"},
+	 {50, "FSB_LESS_25_FULL"},
+	 {51, "FSB_OVER_50_FULL"},
+	 {52, "LDQ_LESS_25_FULL"},
+	 {53, "LDQ_OVER_50_FULL"},
+	 {54, "WBB_LESS_25_FULL"},
+	 {55, "WBB_OVER_50_FULL"},
+	 {56, "INTERVENTION_HIT_COUNT"},
+	 {57, "INVALIDATE_INTERVENTION_COUNT"},
+	 {58, "EVICTION_COUNT"},
+	 {59, "MESI_INVAL_COUNT"},
+	 {60, "MESI_MODIFIED_COUNT"},
+	 {61, "SELF_INTERVENTION_LATENCY"},
+	 {62, "READ_RESPONSE_LATENCY"},
+	 {63, "RESERVED"},
+	 {64, "SI_PCEVENT1"},
+	 {65, "SI_PCEVENT3"},
+	 {66, "SI_PCEVENT5"},
+	 {67, "SI_PCEVENT7"},
+	 {-1, "RESERVED"},
+	 /* 68 - 127 for Reserved */
+	 },
+	/* COUNT 1 */
+	{
+	 {0, "CPU_CYCLES"},
+	 {1, "CPU_INST"},
+	 {2, "MISPREDICTED_BRANCH_INSNS"},
+	 {3, "JR_31_MISPREDICTIONS"},
+	 {4, "JR_31_NO_PREDICTIONS"},
+	 {5, "ITLB_MISSES"},
+	 {6, "DTLB_MISSES"},
+	 {7, "JTLB_INSN_MISSES"},
+	 {8, "JTLB_DATA_MISSES"},
+	 {9, "ICACHE_MISSES"},
+	 {10, "DCACHE_WRITEBACKS"},
+	 {11, "DCACHE_MISSES"},
+	 {12, "RESERVED"},
+	 {13, "LOAD_MISS_INSNS"},
+	 {14, "FPU_INSNS"},
+	 {15, "STORE_INSNS"},
+	 {16, "MIPS16_INSNS"},
+	 {17, "INT_MUL_DIV_INSNS"},
+	 {18, "REPLAYED_INSNS"},
+	 {19, "SC_INSNS_FAILED"},
+	 {20, "CACHE_HIT_PREFETCH_INSNS"},
+	 {21, "L2_CACHE_ACCESSES"},
+	 {22, "L2_CACHE_SINGLE_BIT_ERRORS"},
+	 {23, "SINGLE_THREADED_CYCLES"},
+	 {24, "REFETCHED_INSNS"},
+	 {25, "ALU_STALLS"},
+	 {26, "ALU_DSP_SATURATION_INSNS"},
+	 {27, "MDU_DSP_SATURATION_INSNS"},
+	 {28, "CP2_EVENTS"},
+	 {29, "DSPRAM_EVENTS"},
+	 {30, "RESERVED"},
+	 {31, "ITC_EVENT"},
+	 {33, "UNCACHED_STORE_INSNS"},
+	 {34, "YIELD_IN_COMP"},
+	 {35, "CP2_TO_FROM_INSNS"},
+	 {36, "INTERVENTION_MISS_STALLS"},
+	 {37, "DCACHE_MISS_STALLS"},
+	 {38, "RESERVED"},
+	 /* 38 was listed in OPROFILE web page, but not listed 1004k mips spec */
+	 /* {38, "FSB_INDEX_CONFLICT_STALLS"}, */
+	 {39, "L2_CACHE_MISS_CYCLES"},
+	 {40, "ITC_STALLS"},
+	 {41, "FPU_STALLS"},
+	 {42, "COREEXTEND_STALLS"},
+	 {43, "DSPRAM_STALLS"},
+	 {45, "ALU_TO_AGEN_STALLS"},
+	 {46, "MISPREDICTION_STALLS"},
+	 {47, "RESERVED"},
+	 {48, "FB_ENTRY_ALLOCATED_CYCLES"},
+	 {49, "EJTAG_DATA_TRIGGERS"},
+	 {50, "FSB_25_50_FULL"},
+	 {51, "FSB_FULL_STALLS"},
+	 {52, "LDQ_25_50_FULL"},
+	 {53, "LDQ_FULL_STALLS"},
+	 {54, "WBB_25_50_FULL"},
+	 {55, "WBB_FULL_STALLS"},
+	 {56, "INTERVENTION_COUNT"},
+	 {57, "INVALID_INTERVENT_HIT_CNT"},
+	 {58, "WRITEBACK_COUNT"},
+	 {59, "MESI_EXCLUSIVE_COUNT"},
+	 {60, "MESI_SHARED_COUNT"},
+	 {61, "SELF_INTERVENTION_COUNT"},
+	 {62, "READ_RESPONSE_COUNT"},
+	 {63, "RESERVED"},
+	 {64, "SI_PCEVENT0"},
+	 {65, "SI_PCEVENT2"},
+	 {66, "SI_PCEVENT4"},
+	 {67, "SI_PCEVENT6"},
+	 {-1, "RESERVED"},
+	 },
+};
+
+#define MIPS_1004K_PMU_DESC_SIZE (sizeof(mips_1004k_pmu_desc[0]))
+#define MIPS_1004K_PMU_DESC_COUNT (sizeof(mips_1004k_pmu_desc) / MIPS_1004K_PMU_DESC_SIZE)
+
+#endif				/* _V8_PMU_NAME_H_ */
diff --git a/src/devtools/met-driver/4.14/common/mtk_emi_bm.c b/src/devtools/met-driver/4.14/common/mtk_emi_bm.c
new file mode 100644
index 0000000..3c61470
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/mtk_emi_bm.c
@@ -0,0 +1,253 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <asm/io.h>
+#include <mt-plat/sync_write.h>
+#include <mt-plat/mtk_io.h>
+/* #include "mtk_typedefs.h" */
+#include "core_plf_init.h"
+#include "mtk_emi_bm.h"
+#include "met_drv.h"
+#include "interface.h"
+#if defined(CONFIG_MTK_TINYSYS_SSPM_SUPPORT) && defined(ONDIEMET_SUPPORT)
+#include "sspm/ondiemet_sspm.h"
+#endif
+
+#undef	DEBUG
+
+#define	emi_readl		readl
+#define	emi_reg_sync_writel	mt_reg_sync_writel
+
+#define MASK_MASTER	0xFF
+#define MASK_TRANS_TYPE	0xFF
+
+static void __iomem *BaseAddrEMI;
+const unsigned int emi_config[] = {
+	EMI_BMEN,
+	EMI_MSEL,
+	EMI_MSEL2,
+	EMI_MSEL3,
+	EMI_MSEL4,
+	EMI_MSEL5,
+	EMI_MSEL6,
+	EMI_MSEL7,
+	EMI_MSEL8,
+	EMI_MSEL9,
+	EMI_MSEL10,
+	EMI_BMID0,
+	EMI_BMID1,
+	EMI_BMID2,
+	EMI_BMID3,
+	EMI_BMID4,
+	EMI_BMID5,
+	EMI_BMID6,
+	EMI_BMID7,
+	EMI_BMID8,
+	EMI_BMID9,
+	EMI_BMID10,
+	EMI_BMEN1,
+	EMI_BMEN2,
+	EMI_BMRW0,
+	EMI_BMRW1
+};
+#define EMI_CONFIG_MX_NR (sizeof(emi_config)/sizeof(unsigned int))
+static unsigned int emi_config_val[EMI_CONFIG_MX_NR];
+
+int MET_BM_Init(void)
+{
+	/*emi*/
+	if (mt_cen_emi_base_get_symbol) {
+		BaseAddrEMI = mt_cen_emi_base_get_symbol();
+	} else {
+		pr_debug("mt_cen_emi_base_get_symbol = NULL\n");
+		PR_BOOTMSG_ONCE("mt_cen_emi_base_get_symbol = NULL\n");
+		BaseAddrEMI = 0;
+	}
+
+	if (BaseAddrEMI == 0) {
+		pr_debug("BaseAddrEMI = 0\n");
+		PR_BOOTMSG_ONCE("BaseAddrEMI = 0\n");
+		return -1;
+	}
+	pr_debug("MET EMI: map emi to %p\n", BaseAddrEMI);
+	PR_BOOTMSG("MET EMI: map emi to %p\n", BaseAddrEMI);
+
+	return 0;
+}
+
+void MET_BM_DeInit(void)
+{
+}
+
+void MET_BM_SaveCfg(void)
+{
+	int i;
+
+	for (i = 0; i < EMI_CONFIG_MX_NR; i++)
+		emi_config_val[i] = emi_readl(IOMEM(ADDR_EMI + emi_config[i]));
+}
+
+void MET_BM_RestoreCfg(void)
+{
+	int i;
+
+	for (i = 0; i < EMI_CONFIG_MX_NR; i++)
+		emi_reg_sync_writel(emi_config_val[i], ADDR_EMI + emi_config[i]);
+}
+
+int MET_BM_SetMonitorCounter(const unsigned int counter_num,
+			     const unsigned int master, const unsigned int trans_type)
+{
+	unsigned int value, addr;
+	const unsigned int iMask = (MASK_TRANS_TYPE << 8) | MASK_MASTER;
+
+	if (counter_num < 1 || counter_num > BM_COUNTER_MAX)
+		return BM_ERR_WRONG_REQ;
+
+
+	if (counter_num == 1) {
+		addr = EMI_BMEN;
+		value = (emi_readl(IOMEM(ADDR_EMI + addr)) & ~(iMask << 16)) |
+			((trans_type & MASK_TRANS_TYPE) << 24) | ((master & MASK_MASTER) << 16);
+	} else {
+		addr = (counter_num <= 3) ? EMI_MSEL : (EMI_MSEL2 + (counter_num / 2 - 2) * 8);
+
+
+		value = emi_readl(IOMEM(ADDR_EMI + addr)) & ~(iMask << ((counter_num % 2) * 16));
+
+
+		value |= (((trans_type & MASK_TRANS_TYPE) << 8) |
+			  (master & MASK_MASTER)) << ((counter_num % 2) * 16);
+	}
+
+	emi_reg_sync_writel(value, ADDR_EMI + addr);
+
+	return BM_REQ_OK;
+}
+
+int MET_BM_SetTtypeCounterRW(unsigned int bmrw0_val, unsigned int bmrw1_val)
+{
+
+	unsigned int value_origin;
+
+	value_origin = emi_readl(IOMEM(ADDR_EMI + EMI_BMRW0));
+	MET_TRACE("[MET_EMI_settype1] value_origin: %x\n", value_origin);
+	if (value_origin != bmrw0_val) {
+		emi_reg_sync_writel(bmrw0_val, ADDR_EMI + EMI_BMRW0);
+		MET_TRACE("[MET_EMI_settype1] bmrw0_val: %x, value_origin: %x\n", bmrw0_val,
+			   value_origin);
+	}
+
+	value_origin = emi_readl(IOMEM(ADDR_EMI + EMI_BMRW1));
+	MET_TRACE("[MET_EMI_settype2] value_origin: %x\n", value_origin);
+	if (value_origin != bmrw1_val) {
+		emi_reg_sync_writel(bmrw1_val, ADDR_EMI + EMI_BMRW1);
+		MET_TRACE("[MET_EMI_settype2] bmrw0_val: %x, value_origin: %x\n", bmrw1_val,
+			   value_origin);
+
+	}
+	return BM_REQ_OK;
+}
+
+int MET_BM_Set_WsctTsct_id_sel(unsigned int counter_num, unsigned int enable)
+{
+	unsigned int value;
+
+	if (counter_num > 3)
+		return BM_ERR_WRONG_REQ;
+
+	value =
+	    ((emi_readl(IOMEM(ADDR_EMI + EMI_BMEN2)) & (~(1 << (28 + counter_num)))) |
+	     (enable << (28 + counter_num)));
+	emi_reg_sync_writel(value, ADDR_EMI + EMI_BMEN2);
+
+	return BM_REQ_OK;
+}
+
+int MET_BM_SetbusID_En(const unsigned int counter_num,
+		       const unsigned int enable)
+{
+	unsigned int value;
+
+	if ((counter_num < 1 || counter_num > BM_COUNTER_MAX) || (enable > 1))
+		return BM_ERR_WRONG_REQ;
+
+	if (enable == 0) {
+
+		value = (emi_readl(IOMEM(ADDR_EMI + EMI_BMEN2))
+			 & ~(1 << (counter_num - 1)));
+	} else {
+
+		value = (emi_readl(IOMEM(ADDR_EMI + EMI_BMEN2))
+			 | (1 << (counter_num - 1)));
+	}
+	emi_reg_sync_writel(value, ADDR_EMI + EMI_BMEN2);
+
+	return BM_REQ_OK;
+}
+
+int MET_BM_SetbusID(const unsigned int counter_num,
+		    const unsigned int id)
+{
+	unsigned int value, addr, shift_num;
+
+	if ((counter_num < 1 || counter_num > BM_COUNTER_MAX))
+		return BM_ERR_WRONG_REQ;
+
+
+	addr = EMI_BMID0 + (counter_num - 1) / 2 * 4;
+	shift_num = ((counter_num - 1) % 2) * 16;
+
+	value = emi_readl(IOMEM(ADDR_EMI + addr)) & ~(EMI_BMID_MASK << shift_num);
+
+
+	if (id <= 0xffff)
+		value |= id << shift_num;
+
+	emi_reg_sync_writel(value, ADDR_EMI + addr);
+
+	return BM_REQ_OK;
+}
+
+int MET_BM_SetUltraHighFilter(const unsigned int counter_num, const unsigned int enable)
+{
+	unsigned int value;
+
+	if ((counter_num < 1 || counter_num > BM_COUNTER_MAX) || (enable > 1))
+		return BM_ERR_WRONG_REQ;
+
+
+	value = (emi_readl(IOMEM(ADDR_EMI + EMI_BMEN1))
+		 & ~(1 << (counter_num - 1)))
+		| (enable << (counter_num - 1));
+
+	emi_reg_sync_writel(value, ADDR_EMI + EMI_BMEN1);
+
+	return BM_REQ_OK;
+}
+
+int MET_BM_SetLatencyCounter(unsigned int enable)
+{
+	unsigned int value;
+
+	value = emi_readl(IOMEM(ADDR_EMI + EMI_BMEN2)) & ~(0x3 << 24);
+	if (enable == 1)
+		value |= (0x2 << 24);
+
+	emi_reg_sync_writel(value, ADDR_EMI + EMI_BMEN2);
+
+	return BM_REQ_OK;
+}
diff --git a/src/devtools/met-driver/4.14/common/mtk_emi_bm.h b/src/devtools/met-driver/4.14/common/mtk_emi_bm.h
new file mode 100644
index 0000000..43295be
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/mtk_emi_bm.h
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __MT_MET_EMI_BM_H__
+#define __MT_MET_EMI_BM_H__
+
+
+#define	ADDR_EMI		((unsigned long) BaseAddrEMI)
+
+/*========================================================*/
+/*EMI configuration by project*/
+/*Change config start*/
+/*========================================================*/
+#define _GP_1_Default	(_M0 | _M1)
+#define _GP_2_Default	(_M2 | _M5)
+#define _GP_3_Default	(_M6 | _M7)
+
+
+/*========================================================*/
+/*Change config end*/
+/*========================================================*/
+
+
+#define _M0		(0x01)
+#define _M1		(0x02)
+#define _M2		(0x04)
+#define _M3		(0x08)
+#define _M4		(0x10)
+#define _M5		(0x20)
+#define _M6		(0x40)
+#define _M7		(0x80)
+#define _ALL	(0xFF)
+
+enum BM_RW_Type {
+	BM_BOTH_READ_WRITE,
+	BM_READ_ONLY,
+	BM_WRITE_ONLY
+};
+
+enum {
+	BM_TRANS_TYPE_1BEAT = 0x0,
+	BM_TRANS_TYPE_2BEAT,
+	BM_TRANS_TYPE_3BEAT,
+	BM_TRANS_TYPE_4BEAT,
+	BM_TRANS_TYPE_5BEAT,
+	BM_TRANS_TYPE_6BEAT,
+	BM_TRANS_TYPE_7BEAT,
+	BM_TRANS_TYPE_8BEAT,
+	BM_TRANS_TYPE_9BEAT,
+	BM_TRANS_TYPE_10BEAT,
+	BM_TRANS_TYPE_11BEAT,
+	BM_TRANS_TYPE_12BEAT,
+	BM_TRANS_TYPE_13BEAT,
+	BM_TRANS_TYPE_14BEAT,
+	BM_TRANS_TYPE_15BEAT,
+	BM_TRANS_TYPE_16BEAT,
+	BM_TRANS_TYPE_1Byte = 0 << 4,
+	BM_TRANS_TYPE_2Byte = 1 << 4,
+	BM_TRANS_TYPE_4Byte = 2 << 4,
+	BM_TRANS_TYPE_8Byte = 3 << 4,
+	BM_TRANS_TYPE_16Byte = 4 << 4,
+	BM_TRANS_TYPE_32Byte = 5 << 4,
+	BM_TRANS_TYPE_BURST_WRAP = 0 << 7,
+	BM_TRANS_TYPE_BURST_INCR = 1 << 7
+};
+
+enum {
+	BM_TRANS_RW_DEFAULT = 0x0,
+	BM_TRANS_RW_READONLY,
+	BM_TRANS_RW_WRITEONLY,
+	BM_TRANS_RW_RWBOTH
+};
+
+
+#define EMI_BMID_MASK				(0xFFFF)
+#define BM_COUNTER_MAX				(21)
+
+#define BM_REQ_OK						(0)
+#define BM_ERR_WRONG_REQ				(-1)
+#define BM_ERR_OVERRUN					(-2)
+
+#define BM_TTYPE1_16_ENABLE			(0)
+#define BM_TTYPE1_16_DISABLE			(-1)
+#define BM_TTYPE17_21_ENABLE			(0)
+#define BM_TTYPE17_21_DISABLE			(-1)
+
+#if defined(CONFIG_MTK_TINYSYS_SSPM_SUPPORT) && defined(ONDIEMET_SUPPORT)
+enum BM_EMI_IPI_Type {
+	SET_BASE_EMI = 0x0,
+	SET_EBM_CONFIGS1 = 0x7,
+	SET_EBM_CONFIGS2 = 0x8,
+	SET_REGISTER_CB = 0x9,
+};
+#endif
+
+#define	EMI_OFF			0x0000
+#define EMI_BMEN		(0x400-EMI_OFF)
+#define EMI_MSEL		(0x440-EMI_OFF)
+#define EMI_MSEL2		(0x468-EMI_OFF)
+#define EMI_MSEL3		(0x470-EMI_OFF)
+#define EMI_MSEL4		(0x478-EMI_OFF)
+#define EMI_MSEL5		(0x480-EMI_OFF)
+#define EMI_MSEL6		(0x488-EMI_OFF)
+#define EMI_MSEL7		(0x490-EMI_OFF)
+#define EMI_MSEL8		(0x498-EMI_OFF)
+#define EMI_MSEL9		(0x4A0-EMI_OFF)
+#define EMI_MSEL10		(0x4A8-EMI_OFF)
+
+#define EMI_BMID0		(0x4B0-EMI_OFF)
+#define EMI_BMID1		(0x4B4-EMI_OFF)
+#define EMI_BMID2		(0x4B8-EMI_OFF)
+#define EMI_BMID3		(0x4BC-EMI_OFF)
+#define EMI_BMID4		(0x4C0-EMI_OFF)
+#define EMI_BMID5		(0x4C4-EMI_OFF)
+#define EMI_BMID6		(0x4C8-EMI_OFF)
+#define EMI_BMID7		(0x4CC-EMI_OFF)
+#define EMI_BMID8		(0x4D0-EMI_OFF)
+#define EMI_BMID9		(0x4D4-EMI_OFF)
+#define EMI_BMID10		(0x4D8-EMI_OFF)
+
+#define EMI_BMEN1		(0x4E0-EMI_OFF)
+#define EMI_BMEN2		(0x4E8-EMI_OFF)
+#define EMI_BMRW0		(0x4F8-EMI_OFF)
+#define EMI_BMRW1		(0x4FC-EMI_OFF)
+
+
+extern int MET_BM_Init(void);
+extern void MET_BM_DeInit(void);
+extern void MET_BM_SaveCfg(void);
+extern void MET_BM_RestoreCfg(void);
+extern int MET_BM_SetMonitorCounter(const unsigned int counter_num,
+				    const unsigned int master, const unsigned int trans_type);
+extern int MET_BM_SetTtypeCounterRW(unsigned int bmrw0_val, unsigned int bmrw1_val);
+extern int MET_BM_Set_WsctTsct_id_sel(unsigned int counter_num, unsigned int enable);
+extern int MET_BM_SetbusID_En(const unsigned int counter_num,
+			      const unsigned int enable);
+extern int MET_BM_SetbusID(const unsigned int counter_num,
+			   const unsigned int id);
+extern int MET_BM_SetUltraHighFilter(const unsigned int counter_num, const unsigned int enable);
+extern int MET_BM_SetLatencyCounter(unsigned int enable);
+#endif				/* !__MT_MET_EMI_BM_H__ */
diff --git a/src/devtools/met-driver/4.14/common/mtk_emi_bm_35.c b/src/devtools/met-driver/4.14/common/mtk_emi_bm_35.c
new file mode 100644
index 0000000..d3945f1
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/mtk_emi_bm_35.c
@@ -0,0 +1,802 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/io.h>
+#include <mt-plat/sync_write.h>
+#include <mt-plat/mtk_io.h>
+#include "mtk_typedefs.h"
+#include "core_plf_init.h"
+#include "mtk_emi_bm_35.h"
+
+#include "met_drv.h"
+#include "interface.h"
+
+#undef	DEBUG
+#undef	debug_reg
+#ifdef	debug_reg
+static inline unsigned int emi_readl(void __iomem *padr)
+{
+	unsigned int tmp;
+
+	tmp = readl(padr);
+	MET_TRACE("[MET_EMI] RD_Reg: %p: %08x\n", padr, tmp);
+	return tmp;
+}
+
+static inline void __emi_reg_sync_writel(unsigned int data, void __iomem *padr)
+{
+	unsigned int tmp;
+
+	mt_reg_sync_writel(data, padr);
+	tmp = readl(padr);
+	MET_TRACE("[MET_EMI] WR_Reg: %p: %08x, %08x\n", padr, data, tmp);
+}
+
+#define emi_reg_sync_writel(data, adr)  __emi_reg_sync_writel(data, IOMEM(adr))
+
+#else
+#define emi_readl               readl
+#define emi_reg_sync_writel     mt_reg_sync_writel
+#endif
+
+#define MASK_MASTER     0xFF
+#define MASK_TRANS_TYPE 0xFF
+
+static int dram_chann_num;
+static void __iomem *BaseAddrEMI;
+static void __iomem *BaseAddrCHN_EMI[2];
+
+static int dramc0_dcm_enable;
+static int dramc1_dcm_enable;
+
+#define CH0_MISC_CG_CTRL0 (((unsigned long) BaseAddrDDRPHY_AO[0]) + 0x284)
+#define CH1_MISC_CG_CTRL0 (((unsigned long) BaseAddrDDRPHY_AO[1]) + 0x284)
+const unsigned int emi_config[] = {
+	EMI_BMEN,
+	EMI_MSEL,
+	EMI_MSEL2,
+	EMI_MSEL3,
+	EMI_MSEL4,
+	EMI_MSEL5,
+	EMI_MSEL6,
+	EMI_MSEL7,
+	EMI_MSEL8,
+	EMI_MSEL9,
+	EMI_MSEL10,
+	EMI_BMID0,
+	EMI_BMID1,
+	EMI_BMID2,
+	EMI_BMID3,
+	EMI_BMID4,
+	EMI_BMID5,
+	EMI_BMID6,
+	EMI_BMID7,
+	EMI_BMID8,
+	EMI_BMID9,
+	EMI_BMID10,
+	EMI_BMEN1,
+	EMI_BMEN2,
+	EMI_BMRW0,
+	EMI_BMRW1,
+	EMI_DBWA,
+	EMI_DBWB,
+	EMI_DBWC,
+	EMI_DBWD,
+	EMI_DBWE,
+	EMI_DBWF,
+	EMI_DBWI,
+	EMI_DBWJ,
+	EMI_DBWK,
+	EMI_DBWA_2ND,
+	EMI_DBWB_2ND,
+	EMI_DBWC_2ND,
+	EMI_DBWD_2ND,
+	EMI_DBWE_2ND,
+	EMI_DBWF_2ND,
+	EMI_TTYPE1_CONA,
+	EMI_TTYPE1_CONB,
+	EMI_TTYPE2_CONA,
+	EMI_TTYPE2_CONB,
+	EMI_TTYPE3_CONA,
+	EMI_TTYPE3_CONB,
+	EMI_TTYPE4_CONA,
+	EMI_TTYPE4_CONB,
+	EMI_TTYPE5_CONA,
+	EMI_TTYPE5_CONB,
+	EMI_TTYPE6_CONA,
+	EMI_TTYPE6_CONB,
+	EMI_TTYPE7_CONA,
+	EMI_TTYPE7_CONB,
+	EMI_TTYPE8_CONA,
+	EMI_TTYPE8_CONB,
+	EMI_TTYPE9_CONA,
+	EMI_TTYPE9_CONB,
+	EMI_TTYPE10_CONA,
+	EMI_TTYPE10_CONB,
+	EMI_TTYPE11_CONA,
+	EMI_TTYPE11_CONB,
+	EMI_TTYPE12_CONA,
+	EMI_TTYPE12_CONB,
+	EMI_TTYPE13_CONA,
+	EMI_TTYPE13_CONB,
+	EMI_TTYPE14_CONA,
+	EMI_TTYPE14_CONB,
+	EMI_TTYPE15_CONA,
+	EMI_TTYPE15_CONB,
+	EMI_TTYPE16_CONA,
+	EMI_TTYPE16_CONB,
+	EMI_TTYPE17_CONA,
+	EMI_TTYPE17_CONB,
+	EMI_TTYPE18_CONA,
+	EMI_TTYPE18_CONB,
+	EMI_TTYPE19_CONA,
+	EMI_TTYPE19_CONB,
+	EMI_TTYPE20_CONA,
+	EMI_TTYPE20_CONB,
+	EMI_TTYPE21_CONA,
+	EMI_TTYPE21_CONB
+};
+#define EMI_CONFIG_MX_NR (sizeof(emi_config)/sizeof(unsigned int))
+static unsigned int emi_config_val[EMI_CONFIG_MX_NR];
+
+
+
+static inline void MET_REG_BCLR(unsigned long reg, u32 shift)
+{
+	unsigned int read_val = 0;
+
+	read_val = emi_readl(IOMEM(reg));
+	emi_reg_sync_writel(read_val & (~((1 << shift) & 0xFFFFFFFF)), reg);
+}
+
+
+int MET_BM_Init(void)
+{
+	/*emi*/
+	if (mt_cen_emi_base_get_symbol) {
+		BaseAddrEMI = mt_cen_emi_base_get_symbol();
+	} else {
+		pr_debug("mt_cen_emi_base_get_symbol = NULL\n");
+		PR_BOOTMSG_ONCE("mt_cen_emi_base_get_symbol = NULL\n");
+		BaseAddrEMI = 0;
+	}
+
+	if (BaseAddrEMI == 0) {
+		pr_debug("BaseAddrEMI = 0\n");
+		PR_BOOTMSG_ONCE("BaseAddrEMI = 0\n");
+		return -1;
+	}
+	pr_debug("MET EMI: map emi to %p\n", BaseAddrEMI);
+	PR_BOOTMSG("MET EMI: map emi to %p\n", BaseAddrEMI);
+
+	return 0;
+}
+
+
+void MET_BM_DeInit(void)
+{
+}
+
+
+void MET_BM_SaveCfg(void)
+{
+	int i;
+
+	for (i = 0; i < EMI_CONFIG_MX_NR; i++)
+		emi_config_val[i] = emi_readl(IOMEM(ADDR_EMI + emi_config[i]));
+}
+
+
+void MET_BM_RestoreCfg(void)
+{
+	int i;
+
+	for (i = 0; i < EMI_CONFIG_MX_NR; i++)
+		emi_reg_sync_writel(emi_config_val[i], ADDR_EMI + emi_config[i]);
+}
+
+
+
+
+
+
+
+void MET_BM_SetReadWriteType(const unsigned int ReadWriteType)
+{
+	const unsigned int value = emi_readl(IOMEM(ADDR_EMI + EMI_BMEN));
+
+	/*
+	 * ReadWriteType: 00/11 --> both R/W
+	 *                   01 --> only R
+	 *                   10 --> only W
+	 */
+	emi_reg_sync_writel((value & 0xFFFFFFCF) | (ReadWriteType << 4), ADDR_EMI + EMI_BMEN);
+}
+
+
+
+
+int MET_BM_SetMonitorCounter(const unsigned int counter_num,
+			     const unsigned int master, const unsigned int trans_type)
+{
+	unsigned int value, addr;
+	const unsigned int iMask = (MASK_TRANS_TYPE << 8) | MASK_MASTER;
+
+	if (counter_num < 1 || counter_num > BM_COUNTER_MAX)
+		return BM_ERR_WRONG_REQ;
+
+
+	if (counter_num == 1) {
+		addr = EMI_BMEN;
+		value = (emi_readl(IOMEM(ADDR_EMI + addr)) & ~(iMask << 16)) |
+		    ((trans_type & MASK_TRANS_TYPE) << 24) | ((master & MASK_MASTER) << 16);
+	} else {
+		addr = (counter_num <= 3) ? EMI_MSEL : (EMI_MSEL2 + (counter_num / 2 - 2) * 8);
+
+		/* clear master and transaction type fields */
+		value = emi_readl(IOMEM(ADDR_EMI + addr)) & ~(iMask << ((counter_num % 2) * 16));
+
+		/* set master and transaction type fields */
+		value |= (((trans_type & MASK_TRANS_TYPE) << 8) |
+			  (master & MASK_MASTER)) << ((counter_num % 2) * 16);
+	}
+
+	emi_reg_sync_writel(value, ADDR_EMI + addr);
+
+	return BM_REQ_OK;
+}
+
+
+int MET_BM_SetTtypeCounterRW(unsigned int bmrw0_val, unsigned int bmrw1_val)
+{
+
+	unsigned int value_origin;
+
+	value_origin = emi_readl(IOMEM(ADDR_EMI + EMI_BMRW0));
+	MET_TRACE("[MET_EMI_settype1] value_origin: %x\n", value_origin);
+	if (value_origin != bmrw0_val) {
+		emi_reg_sync_writel(bmrw0_val, ADDR_EMI + EMI_BMRW0);
+		MET_TRACE("[MET_EMI_settype1] bmrw0_val: %x, value_origin: %x\n", bmrw0_val,
+			   value_origin);
+	}
+
+
+	value_origin = emi_readl(IOMEM(ADDR_EMI + EMI_BMRW1));
+	MET_TRACE("[MET_EMI_settype2] value_origin: %x\n", value_origin);
+	if (value_origin != bmrw1_val) {
+		emi_reg_sync_writel(bmrw1_val, ADDR_EMI + EMI_BMRW1);
+		MET_TRACE("[MET_EMI_settype2] bmrw0_val: %x, value_origin: %x\n", bmrw1_val,
+			   value_origin);
+
+	}
+	return BM_REQ_OK;
+}
+
+
+int MET_BM_Set_WsctTsct_id_sel(unsigned int counter_num, unsigned int enable)
+{
+	unsigned int value;
+
+	if (counter_num > 3)
+		return BM_ERR_WRONG_REQ;
+
+	value =
+	    ((emi_readl(IOMEM(ADDR_EMI + EMI_BMEN2)) & (~(1 << (28 + counter_num)))) |
+	     (enable << (28 + counter_num)));
+	emi_reg_sync_writel(value, ADDR_EMI + EMI_BMEN2);
+
+	return BM_REQ_OK;
+}
+
+
+int MET_BM_SetMaster(const unsigned int counter_num, const unsigned int master)
+{
+	unsigned int value, addr;
+	const unsigned int iMask = 0x7F;
+
+	if (counter_num < 1 || counter_num > BM_COUNTER_MAX)
+		return BM_ERR_WRONG_REQ;
+
+
+	if (counter_num == 1) {
+		addr = EMI_BMEN;
+		value =
+		    (emi_readl(IOMEM(ADDR_EMI + addr)) & ~(iMask << 16)) | ((master & iMask) << 16);
+	} else {
+		addr = (counter_num <= 3) ? EMI_MSEL : (EMI_MSEL2 + (counter_num / 2 - 2) * 8);
+
+		/* clear master and transaction type fields */
+		value = emi_readl(IOMEM(ADDR_EMI + addr)) & ~(iMask << ((counter_num % 2) * 16));
+
+		/* set master and transaction type fields */
+		value |= ((master & iMask) << ((counter_num % 2) * 16));
+	}
+
+	emi_reg_sync_writel(value, ADDR_EMI + addr);
+
+	return BM_REQ_OK;
+}
+
+
+int MET_BM_SetbusID_En(const unsigned int counter_num,
+		       const unsigned int enable)
+{
+	unsigned int value;
+
+	if ((counter_num < 1 || counter_num > BM_COUNTER_MAX) || (enable > 1))
+		return BM_ERR_WRONG_REQ;
+
+	if (enable == 0) {
+		/* clear  EMI ID selection Enabling   SEL_ID_EN */
+		value = (emi_readl(IOMEM(ADDR_EMI + EMI_BMEN2))
+			 & ~(1 << (counter_num - 1)));
+	} else {
+		/* enable  EMI ID selection Enabling   SEL_ID_EN */
+		value = (emi_readl(IOMEM(ADDR_EMI + EMI_BMEN2))
+			 | (1 << (counter_num - 1)));
+	}
+	emi_reg_sync_writel(value, ADDR_EMI + EMI_BMEN2);
+
+	return BM_REQ_OK;
+}
+
+
+int MET_BM_SetbusID(const unsigned int counter_num,
+		    const unsigned int id)
+{
+	unsigned int value, addr, shift_num;
+
+	if ((counter_num < 1 || counter_num > BM_COUNTER_MAX))
+		return BM_ERR_WRONG_REQ;
+
+	/* offset of EMI_BMIDx register */
+	addr = EMI_BMID0 + (counter_num - 1) / 2 * 4;
+	shift_num = ((counter_num - 1) % 2) * 16;
+	/* clear SELx_ID field */
+	value = emi_readl(IOMEM(ADDR_EMI + addr)) & ~(EMI_BMID_MASK << shift_num);
+
+	/* set SELx_ID field */
+	if (id <= 0xffff)       /*bigger then 0xff_ff : no select busid in master, reset busid as 0*/
+		value |= id << shift_num;
+
+	emi_reg_sync_writel(value, ADDR_EMI + addr);
+
+	return BM_REQ_OK;
+}
+
+
+int MET_BM_SetUltraHighFilter(const unsigned int counter_num, const unsigned int enable)
+{
+	unsigned int value;
+
+	if ((counter_num < 1 || counter_num > BM_COUNTER_MAX) || (enable > 1))
+		return BM_ERR_WRONG_REQ;
+
+
+	value = (emi_readl(IOMEM(ADDR_EMI + EMI_BMEN1))
+		 & ~(1 << (counter_num - 1)))
+		| (enable << (counter_num - 1));
+
+	emi_reg_sync_writel(value, ADDR_EMI + EMI_BMEN1);
+
+	return BM_REQ_OK;
+}
+
+
+int MET_BM_SetLatencyCounter(unsigned int enable)
+{
+	unsigned int value;
+
+	value = emi_readl(IOMEM(ADDR_EMI + EMI_BMEN2)) & ~(0x3 << 24);
+	/*
+	 * emi_ttype1 -- emi_ttype8 change as total latencies
+	 * for m0 -- m7,
+	 * and emi_ttype9 -- emi_ttype16 change as total transaction counts
+	 * for m0 -- m7
+	 */
+	if (enable == 1)
+		value |= (0x2 << 24);
+
+	emi_reg_sync_writel(value, ADDR_EMI + EMI_BMEN2);
+
+	return BM_REQ_OK;
+}
+
+
+
+unsigned int MET_EMI_GetDramChannNum(void)
+{
+	int num = -1;
+
+	if (BaseAddrEMI) {
+		num = emi_readl(IOMEM(ADDR_EMI + EMI_CONA));
+		num = ((num >> 8) & 0x0000003);
+	} else {
+		return 1;
+	}
+
+	if (num == M0_DOUBLE_HALF_BW_1CH)
+		return 1;
+	else if (num == M0_DOUBLE_HALF_BW_2CH)
+		return 2;
+	else if (num == M0_DOUBLE_HALF_BW_4CH)
+		return 4;
+	else                    /* default return single channel */
+		return 1;
+}
+
+
+unsigned int MET_EMI_GetDramRankNum(void)
+{
+	int dual_rank = 0;
+
+	if (BaseAddrEMI) {
+		dual_rank = emi_readl(IOMEM(ADDR_EMI + EMI_CONA));
+		dual_rank = ((dual_rank >> 17) & RANK_MASK);
+	} else {
+		return DUAL_RANK;
+	}
+
+	if (dual_rank == DISABLE_DUAL_RANK_MODE)
+		return ONE_RANK;
+	else			/* default return dual rank */
+		return DUAL_RANK;
+}
+
+
+unsigned int MET_EMI_GetDramRankNum_CHN1(void)
+{
+	int dual_rank = 0;
+
+	if (BaseAddrEMI) {
+		dual_rank = emi_readl(IOMEM(ADDR_EMI + EMI_CONA));
+		dual_rank = ((dual_rank >> 16) & RANK_MASK);
+	} else {
+		return DUAL_RANK;
+	}
+
+	if (dual_rank == DISABLE_DUAL_RANK_MODE)
+		return ONE_RANK;
+	else			/* default return dual rank */
+		return DUAL_RANK;
+}
+
+
+
+unsigned int MET_EMI_Get_BaseClock_Rate(void)
+{
+	unsigned int DRAM_TYPE;
+
+	if (get_cur_ddr_ratio_symbol)
+		return get_cur_ddr_ratio_symbol();
+	else {
+
+		if (get_ddr_type_symbol) {	
+			DRAM_TYPE = get_ddr_type_symbol();
+
+			if ((DRAM_TYPE == 2) || (DRAM_TYPE == 3))
+				return DRAM_EMI_BASECLOCK_RATE_LP4;
+			else
+				return DRAM_EMI_BASECLOCK_RATE_LP3;
+
+		} else {
+			return DRAM_EMI_BASECLOCK_RATE_LP4;
+		}
+	}
+}
+
+/* For SEDA3.5 wsct setting*/
+/* EMI_DBWX[15:8],    X=A~F   (SEL_MASTER) */
+/* RW:    EMI_DBWX[1:0],    X=A~F */
+int MET_BM_SetWSCT_master_rw(unsigned int *master , unsigned int *rw)
+{
+	unsigned int value, addr;
+	int i;
+
+	const unsigned int Mask_master = 0xFF;
+	const unsigned int offset_master = 8;
+
+	const unsigned int Mask_rw = 0x3;
+	const unsigned int offset_rw = 0;
+
+	for (i=0; i<WSCT_AMOUNT; i++) {
+		addr = EMI_DBWA + i*4;
+		value = emi_readl(IOMEM(ADDR_EMI + addr));
+
+		value = (value & ~(Mask_master << offset_master)) | ((*(master+i) & Mask_master) << offset_master);
+		value = (value & ~(Mask_rw << offset_rw)) | ((*(rw+i) & Mask_rw) << offset_rw);
+
+
+		emi_reg_sync_writel(value, ADDR_EMI + addr);
+	}
+
+	return BM_REQ_OK;
+}
+
+int MET_BM_SetWSCT_high_priority(unsigned int *disable, unsigned int *select)
+{
+	unsigned int value, addr;
+	int i;
+
+	const unsigned int Mask_disable = 0x1;
+	const unsigned int offset_disable  = 2;
+
+	const unsigned int Mask_select = 0xF;
+	const unsigned int offset_select  = 28;
+
+	for (i=0;i<WSCT_AMOUNT;i++) {
+		addr = EMI_DBWA + i*4;
+		value = emi_readl(IOMEM(ADDR_EMI + addr));
+
+		value = (value & ~(Mask_disable << offset_disable)) | ((*(disable+i) & Mask_disable) << offset_disable);
+		value = (value & ~(Mask_select << offset_select)) | ((*(select+i) & Mask_select) << offset_select);
+
+		emi_reg_sync_writel(value, ADDR_EMI + addr);
+	}
+	return BM_REQ_OK;
+}
+
+/* busid enbale: EMI_DBWX[3],    X=A~F */
+/* busid sel: EMI_DBWX[28:16],    X=A~F  (SEL_ID_TMP) */
+/* busid mask : EMI_DBWY[12:0]  或 EMI_DBWY[28:16],  Y=I~K    (SEL_ID_MSK) */
+int MET_BM_SetWSCT_busid_idmask(unsigned int *busid, unsigned int *idMask)
+{
+	unsigned int value, addr;
+	unsigned int enable_tmp, busid_tmp, idmask_tmp; 
+	int i;
+
+	const unsigned int Mask_busid = 0x1FFF;
+	const unsigned int offset_busid  = 16;
+
+	const unsigned int Mask_enable = 0x1;
+	const unsigned int offset_enable  = 3;
+
+	const unsigned int Mask_idMask = 0x1FFF;
+	const unsigned int offset_idMask_even  = 0;
+	const unsigned int offset_idMask_odd  = 16;
+
+	for (i=0;i<WSCT_AMOUNT;i++) {
+
+		/*enable, SEL_ID_TMP*/
+		if (*(busid+i)>0xffff) {
+			enable_tmp = 0;
+			busid_tmp = 0x1FFF;
+			idmask_tmp = 0x1FFF;
+		}
+		else {
+			enable_tmp = 1;
+			busid_tmp = *(busid+i) & Mask_busid;
+			idmask_tmp = *(idMask+i) & Mask_idMask;
+		}
+
+		
+		addr = EMI_DBWA + i*4;
+		value = emi_readl(IOMEM(ADDR_EMI + addr));
+
+		value = (value & ~(Mask_busid << offset_busid)) | (busid_tmp << offset_busid);
+		value = (value & ~(Mask_enable << offset_enable)) | (enable_tmp << offset_enable);
+
+		emi_reg_sync_writel(value, ADDR_EMI + addr);
+
+		/*SEL_ID_MSK*/
+		addr = EMI_DBWI + (i/2)*4;
+
+		value = emi_readl(IOMEM(ADDR_EMI + addr));
+
+		if (i%2==0)
+			value = (value & ~(Mask_idMask << offset_idMask_even)) | (idmask_tmp << offset_idMask_even);
+		else
+			value = (value & ~(Mask_idMask << offset_idMask_odd)) | (idmask_tmp << offset_idMask_odd);
+
+		emi_reg_sync_writel(value, ADDR_EMI + addr);
+	}
+
+
+	return BM_REQ_OK;
+}
+
+
+int MET_BM_SetWSCT_chn_rank_sel(unsigned int *chn_rank_sel)
+{
+	unsigned int value, addr;
+	int i;
+
+	const unsigned int Mask = 0xF;
+	const unsigned int offset  = 12;
+
+
+	for (i=0;i<WSCT_AMOUNT;i++) {
+		addr = EMI_DBWA_2ND + i*4;
+		value = emi_readl(IOMEM(ADDR_EMI + addr));
+
+		value = (value & ~(Mask << offset)) | ((*(chn_rank_sel+i) & Mask) << offset);
+
+		emi_reg_sync_writel(value, ADDR_EMI + addr);
+	}
+	return BM_REQ_OK;
+}
+
+int MET_BM_SetWSCT_burst_range(unsigned int *bnd_dis, unsigned int *low_bnd, unsigned int *up_bnd)
+{
+	unsigned int value, addr;
+	int i;
+
+	const unsigned int Mask_dis = 0x1, Mask_low_bnd = 0x1FF, Mask_up_bnd = 0x1FF;
+	const unsigned int offset_dis = 4, offset_low_bnd = 16 , offset_up_bnd = 0 ;
+
+
+	for (i=0;i<WSCT_AMOUNT;i++) {
+
+		addr = EMI_DBWA + i*4;
+		value = emi_readl(IOMEM(ADDR_EMI + addr));
+
+		value = (value & ~(Mask_dis << offset_dis)) | ((*(bnd_dis+i) & Mask_dis) << offset_dis);
+
+		emi_reg_sync_writel(value, ADDR_EMI + addr);
+
+
+		addr = EMI_DBWA_2ND + i*4;
+		value = emi_readl(IOMEM(ADDR_EMI + addr));
+
+		value = (value & ~(Mask_low_bnd << offset_low_bnd)) | ((*(low_bnd+i) & Mask_low_bnd) << offset_low_bnd);
+		value = (value & ~(Mask_up_bnd << offset_up_bnd)) | ((*(up_bnd+i) & Mask_up_bnd) << offset_up_bnd);
+
+		emi_reg_sync_writel(value, ADDR_EMI + addr);
+	}
+	return BM_REQ_OK;
+
+}
+
+int MET_BM_SetTSCT_busid_enable(unsigned int *enable)
+{
+	//MET_BM_Set_WsctTsct_id_sel
+
+	int i;
+
+	for (i=0;i<TSCT_AMOUNT;i++) {
+		MET_BM_Set_WsctTsct_id_sel(i, *(enable+i));
+	}
+
+	return BM_REQ_OK;
+}
+
+//use the origin together, MET_BM_SetUltraHighFilter()
+/* EMI_TTYPEN_CONA [23:20],  N=1~21   (HPRI_SEL) */
+int MET_BM_SetTtype_high_priority_sel(unsigned int _high_priority_filter, unsigned int *select)
+{
+	int i;
+	unsigned int enable;
+	unsigned int value, addr;
+
+	const unsigned int Mask_sel = 0xF;
+	const unsigned int offset_sel = 20;
+
+	for (i = 0; i < BM_COUNTER_MAX; i++) {
+		if ((_high_priority_filter & (1 << i)) == 0){
+			enable = 0;
+		}
+		else {
+			enable = 1;
+		}
+
+		MET_BM_SetUltraHighFilter(i + 1, enable);
+
+		/* ultra level select */
+		addr = EMI_TTYPE1_CONA + i*8;
+		value = emi_readl(IOMEM(ADDR_EMI + addr));
+
+		value = (value & ~(Mask_sel << offset_sel)) | ((*(select+i) & Mask_sel) << offset_sel);
+
+		emi_reg_sync_writel(value, ADDR_EMI + addr);
+
+	}
+
+	return BM_REQ_OK;
+}
+
+
+//always call this API to init the reg
+//related API, MET_BM_SetbusID, MET_BM_SetbusID_En
+int MET_BM_SetTtype_busid_idmask(unsigned int *busid, unsigned int *idMask, int _ttype1_16_en, int _ttype17_21_en)
+{
+	int i;
+	unsigned int value, addr;
+
+	const unsigned int Mask_idMask = 0x1FFF;
+	const unsigned int offset_idMask = 0;
+
+	if (_ttype1_16_en != BM_TTYPE1_16_ENABLE) {
+		/* mask set 0x1FFF , busid set disable*/
+		for (i = 1; i <= 16; i++) {
+			*(busid + i - 1) = 0xfffff;
+			*(idMask + i - 1) = 0x1FFF;
+		}
+	}
+
+	if (_ttype17_21_en != BM_TTYPE17_21_ENABLE) {
+		for (i = 17; i <= 21; i++) {
+			*(busid + i - 1) = 0xfffff;
+			*(idMask + i - 1) = 0x1FFF;
+		}
+	}
+
+	for (i = 1; i <= BM_COUNTER_MAX; i++) {
+		MET_BM_SetbusID(i, *(busid + i - 1));
+		MET_BM_SetbusID_En(i, ( *(busid + i - 1) > 0xffff) ? 0 : 1);
+
+		/* set idMask */
+		addr = EMI_TTYPE1_CONA + (i-1)*8;
+		value = emi_readl(IOMEM(ADDR_EMI + addr));
+
+		value = (value & ~(Mask_idMask << offset_idMask)) | ((*(idMask+i-1) & Mask_idMask) << offset_idMask);
+
+		emi_reg_sync_writel(value, ADDR_EMI + addr);
+
+	}
+
+	return BM_REQ_OK;
+}
+
+
+int MET_BM_SetTtype_chn_rank_sel(unsigned int *chn_rank_sel)
+{
+	unsigned int value, addr;
+	int i;
+
+	const unsigned int Mask = 0xF;
+	const unsigned int offset  = 16;
+
+
+	for (i=0;i<BM_COUNTER_MAX;i++) {
+		addr = EMI_TTYPE1_CONA + i*8;
+		value = emi_readl(IOMEM(ADDR_EMI + addr));
+
+		value = (value & ~(Mask << offset)) | ((*(chn_rank_sel+i) & Mask) << offset);
+
+		emi_reg_sync_writel(value, ADDR_EMI + addr);
+	}
+	return BM_REQ_OK;
+}
+
+int MET_BM_SetTtype_burst_range(unsigned int *bnd_dis, unsigned int *low_bnd, unsigned int *up_bnd)
+{
+	unsigned int value, addr;
+	int i;
+
+	const unsigned int Mask_dis = 0x1, Mask_low_bnd = 0x1FF, Mask_up_bnd = 0x1FF;
+	const unsigned int offset_dis = 24, offset_low_bnd = 16 , offset_up_bnd = 0 ;
+
+
+	for (i=0;i<BM_COUNTER_MAX;i++) {
+
+		/* set dis bit */
+		addr = EMI_TTYPE1_CONA + i*8;
+		value = emi_readl(IOMEM(ADDR_EMI + addr));
+
+		value = (value & ~(Mask_dis << offset_dis)) | ((*(bnd_dis+i) & Mask_dis) << offset_dis);
+
+		emi_reg_sync_writel(value, ADDR_EMI + addr);
+
+
+		addr = EMI_TTYPE1_CONB + i*8;
+		value = emi_readl(IOMEM(ADDR_EMI + addr));
+
+		value = (value & ~(Mask_low_bnd << offset_low_bnd)) | ((*(low_bnd+i) & Mask_low_bnd) << offset_low_bnd);
+		value = (value & ~(Mask_up_bnd << offset_up_bnd)) | ((*(up_bnd+i) & Mask_up_bnd) << offset_up_bnd);
+
+		emi_reg_sync_writel(value, ADDR_EMI + addr);
+	}
+	return BM_REQ_OK;
+}
\ No newline at end of file
diff --git a/src/devtools/met-driver/4.14/common/mtk_emi_bm_35.h b/src/devtools/met-driver/4.14/common/mtk_emi_bm_35.h
new file mode 100644
index 0000000..bae1307
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/mtk_emi_bm_35.h
@@ -0,0 +1,301 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#ifndef __MT_MET_EMI_BM_H__
+#define __MT_MET_EMI_BM_H__
+
+#define EMI_VER_MAJOR  3
+#define EMI_VER_MINOR  5
+
+#define FILE_NODE_DATA_LEN 512 
+#define WSCT_AMOUNT 6
+#define TSCT_AMOUNT 3
+
+
+#define DRAM_EMI_BASECLOCK_RATE_LP4     4
+#define DRAM_EMI_BASECLOCK_RATE_LP3     2
+
+#define DRAM_IO_BUS_WIDTH_LP4           16
+#define DRAM_IO_BUS_WIDTH_LP3           32
+
+#define DRAM_DATARATE   2
+
+#define ADDR_EMI        ((unsigned long)BaseAddrEMI)
+
+
+#define BM_MASTER_M0            (0x01)
+#define BM_MASTER_M1            (0x02)
+#define BM_MASTER_M2            (0x04)
+#define BM_MASTER_M3            (0x08)
+#define BM_MASTER_M4            (0x10)
+#define BM_MASTER_M5            (0x20)
+#define BM_MASTER_M6            (0x40)
+#define BM_MASTER_M7            (0x80)
+#define BM_MASTER_ALL           (0xFF)
+
+
+enum BM_RW_Type {
+	BM_BOTH_READ_WRITE,
+	BM_READ_ONLY,
+	BM_WRITE_ONLY
+};
+
+enum {
+	BM_TRANS_TYPE_1BEAT = 0x0,
+	BM_TRANS_TYPE_2BEAT,
+	BM_TRANS_TYPE_3BEAT,
+	BM_TRANS_TYPE_4BEAT,
+	BM_TRANS_TYPE_5BEAT,
+	BM_TRANS_TYPE_6BEAT,
+	BM_TRANS_TYPE_7BEAT,
+	BM_TRANS_TYPE_8BEAT,
+	BM_TRANS_TYPE_9BEAT,
+	BM_TRANS_TYPE_10BEAT,
+	BM_TRANS_TYPE_11BEAT,
+	BM_TRANS_TYPE_12BEAT,
+	BM_TRANS_TYPE_13BEAT,
+	BM_TRANS_TYPE_14BEAT,
+	BM_TRANS_TYPE_15BEAT,
+	BM_TRANS_TYPE_16BEAT,
+	BM_TRANS_TYPE_1Byte = 0 << 4,
+	BM_TRANS_TYPE_2Byte = 1 << 4,
+	BM_TRANS_TYPE_4Byte = 2 << 4,
+	BM_TRANS_TYPE_8Byte = 3 << 4,
+	BM_TRANS_TYPE_16Byte = 4 << 4,
+	BM_TRANS_TYPE_32Byte = 5 << 4,
+	BM_TRANS_TYPE_BURST_WRAP = 0 << 7,
+	BM_TRANS_TYPE_BURST_INCR = 1 << 7
+};
+
+enum {
+	BM_TRANS_RW_DEFAULT = 0x0,
+	BM_TRANS_RW_READONLY,
+	BM_TRANS_RW_WRITEONLY,
+	BM_TRANS_RW_RWBOTH
+};
+
+enum {
+	BM_WSCT_RW_DISABLE = 0x0,
+	BM_WSCT_RW_READONLY,
+	BM_WSCT_RW_WRITEONLY,
+	BM_WSCT_RW_RWBOTH
+};
+
+/*coda busid 12bit, but HW support 16 bit*/
+#define EMI_BMID_MASK				(0xFFFF)
+#define BM_COUNTER_MAX				(21)
+
+enum {
+	BUS_MON_EN_SHIFT = 0,
+	BUS_MON_PAUSE_SHIFT = 1,
+	BUS_MON_IDLE_SHIFT = 3,
+	BC_OVERRUN_SHIFT = 8,
+	DRAMC_CG_SHIFT = 9,
+};
+
+#define BM_REQ_OK				(0)
+#define BM_ERR_WRONG_REQ			(-1)
+#define BM_ERR_OVERRUN				(-2)
+
+#define BM_WSCT_TSCT_IDSEL_ENABLE		(0)
+#define BM_WSCT_TSCT_IDSEL_DISABLE		(-1)
+#define BM_TTYPE1_16_ENABLE			(0)
+#define BM_TTYPE1_16_DISABLE			(-1)
+#define BM_TTYPE17_21_ENABLE			(0)
+#define BM_TTYPE17_21_DISABLE			(-1)
+#define BM_BW_LIMITER_ENABLE			(0)
+#define BM_BW_LIMITER_DISABLE			(-1)
+
+#define M0_DOUBLE_HALF_BW_1CH	(0x0)
+#define M0_DOUBLE_HALF_BW_2CH	(0x1)
+#define M0_DOUBLE_HALF_BW_4CH	(0x2)
+
+/* EMI Rank configuration */
+enum {
+	DISABLE_DUAL_RANK_MODE = 0,
+	ENABLE_DUAL_RANK_MODE,
+};
+
+#define RANK_MASK 0x1
+#define ONE_RANK 1
+#define DUAL_RANK 2
+
+
+#if defined(CONFIG_MTK_TINYSYS_SSPM_SUPPORT) && defined(ONDIEMET_SUPPORT)
+enum BM_EMI_IPI_Type {
+	SET_BASE_EMI = 0x0,
+	SET_EBM_CONFIGS1 = 0x7,
+	SET_EBM_CONFIGS2 = 0x8,
+	SET_REGISTER_CB = 0x9,
+};
+#endif
+
+
+#define	EMI_OFF			0x0000
+#define EMI_CONA		(0x000-EMI_OFF)
+#define EMI_CONH		(0x038-EMI_OFF)
+#define EMI_CONH_2ND		(0x03C-EMI_OFF)
+#define EMI_CONM		(0x060-EMI_OFF)
+#define EMI_CONO		(0x070-EMI_OFF)
+
+#define EMI_MDCT		(0x078 - EMI_OFF)
+#define EMI_MDCT_2ND		(0x07C - EMI_OFF)
+
+#define EMI_ARBA		(0x100 - EMI_OFF)
+#define EMI_ARBB		(0x108 - EMI_OFF)
+#define EMI_ARBC		(0x110 - EMI_OFF)
+#define EMI_ARBD		(0x118 - EMI_OFF)
+#define EMI_ARBE		(0x120 - EMI_OFF)
+#define EMI_ARBF		(0x128 - EMI_OFF)
+#define EMI_ARBG		(0x130 - EMI_OFF)
+#define EMI_ARBG_2ND		(0x134 - EMI_OFF)
+#define EMI_ARBH		(0x138 - EMI_OFF)
+
+
+#define EMI_BMEN		(0x400-EMI_OFF)
+#define EMI_MSEL		(0x440 - EMI_OFF)
+#define EMI_MSEL2		(0x468 - EMI_OFF)
+#define EMI_MSEL3		(0x470 - EMI_OFF)
+#define EMI_MSEL4		(0x478 - EMI_OFF)
+#define EMI_MSEL5		(0x480 - EMI_OFF)
+#define EMI_MSEL6		(0x488 - EMI_OFF)
+#define EMI_MSEL7		(0x490 - EMI_OFF)
+#define EMI_MSEL8		(0x498 - EMI_OFF)
+#define EMI_MSEL9		(0x4A0 - EMI_OFF)
+#define EMI_MSEL10		(0x4A8 - EMI_OFF)
+
+#define EMI_BMID0		(0x4B0 - EMI_OFF)
+#define EMI_BMID1		(0x4B4 - EMI_OFF)
+#define EMI_BMID2		(0x4B8 - EMI_OFF)
+#define EMI_BMID3		(0x4BC - EMI_OFF)
+#define EMI_BMID4		(0x4C0 - EMI_OFF)
+#define EMI_BMID5		(0x4C4 - EMI_OFF)
+#define EMI_BMID6		(0x4C8 - EMI_OFF)
+#define EMI_BMID7		(0x4CC - EMI_OFF)
+#define EMI_BMID8		(0x4D0 - EMI_OFF)
+#define EMI_BMID9		(0x4D4 - EMI_OFF)
+#define EMI_BMID10		(0x4D8 - EMI_OFF)
+
+#define EMI_BMEN1		(0x4E0 - EMI_OFF)
+#define EMI_BMEN2		(0x4E8 - EMI_OFF)
+#define EMI_BMRW0		(0x4F8 - EMI_OFF)
+#define EMI_BMRW1		(0x4FC - EMI_OFF)
+
+
+/* SEDA 3.5 New! reg*/
+/* For WSCT setting*/
+#define EMI_DBWA (0xF00 - EMI_OFF)
+#define EMI_DBWB (0xF04 - EMI_OFF)
+#define EMI_DBWC (0xF08 - EMI_OFF)
+#define EMI_DBWD (0xF0C - EMI_OFF)
+#define EMI_DBWE (0xF10 - EMI_OFF)
+#define EMI_DBWF (0xF14 - EMI_OFF)
+
+
+#define EMI_DBWA_2ND (0xF2C - EMI_OFF)
+#define EMI_DBWB_2ND (0xF30 - EMI_OFF)
+#define EMI_DBWC_2ND (0xF34 - EMI_OFF)
+#define EMI_DBWD_2ND (0xF38 - EMI_OFF)
+#define EMI_DBWE_2ND (0xF3C - EMI_OFF)
+#define EMI_DBWF_2ND (0xF40 - EMI_OFF)
+
+#define EMI_DBWI (0xF20 - EMI_OFF) /* SEL_ID_MSK*/
+#define EMI_DBWJ (0xF24 - EMI_OFF)
+#define EMI_DBWK (0xF28 - EMI_OFF)
+
+/* For Ttype setting */
+#define EMI_TTYPE1_CONA (0xF50 - EMI_OFF)
+#define EMI_TTYPE1_CONB (0xF54 - EMI_OFF)
+#define EMI_TTYPE2_CONA (0xF58 - EMI_OFF)
+#define EMI_TTYPE2_CONB (0xF5C - EMI_OFF)
+#define EMI_TTYPE3_CONA (0xF60 - EMI_OFF)
+#define EMI_TTYPE3_CONB (0xF64 - EMI_OFF)
+#define EMI_TTYPE4_CONA (0xF68 - EMI_OFF)
+#define EMI_TTYPE4_CONB (0xF6C - EMI_OFF)
+#define EMI_TTYPE5_CONA (0xF70 - EMI_OFF)
+#define EMI_TTYPE5_CONB (0xF74 - EMI_OFF)
+#define EMI_TTYPE6_CONA (0xF78 - EMI_OFF)
+#define EMI_TTYPE6_CONB (0xF7C - EMI_OFF)
+#define EMI_TTYPE7_CONA (0xF80 - EMI_OFF)
+#define EMI_TTYPE7_CONB (0xF84 - EMI_OFF)
+#define EMI_TTYPE8_CONA (0xF88 - EMI_OFF)
+#define EMI_TTYPE8_CONB (0xF8C - EMI_OFF)
+#define EMI_TTYPE9_CONA (0xF90 - EMI_OFF)
+#define EMI_TTYPE9_CONB (0xF94 - EMI_OFF)
+#define EMI_TTYPE10_CONA (0xF98 - EMI_OFF)
+#define EMI_TTYPE10_CONB (0xF9C - EMI_OFF)
+#define EMI_TTYPE11_CONA (0xFA0 - EMI_OFF)
+#define EMI_TTYPE11_CONB (0xFA4 - EMI_OFF)
+#define EMI_TTYPE12_CONA (0xFA8 - EMI_OFF)
+#define EMI_TTYPE12_CONB (0xFAC - EMI_OFF)
+#define EMI_TTYPE13_CONA (0xFB0 - EMI_OFF)
+#define EMI_TTYPE13_CONB (0xFB4 - EMI_OFF)
+#define EMI_TTYPE14_CONA (0xFB8 - EMI_OFF)
+#define EMI_TTYPE14_CONB (0xFBC - EMI_OFF)
+#define EMI_TTYPE15_CONA (0xFC0 - EMI_OFF)
+#define EMI_TTYPE15_CONB (0xFC4 - EMI_OFF)
+#define EMI_TTYPE16_CONA (0xFC8 - EMI_OFF)
+#define EMI_TTYPE16_CONB (0xFCC - EMI_OFF)
+#define EMI_TTYPE17_CONA (0xFD0 - EMI_OFF)
+#define EMI_TTYPE17_CONB (0xFD4 - EMI_OFF)
+#define EMI_TTYPE18_CONA (0xFD8 - EMI_OFF)
+#define EMI_TTYPE18_CONB (0xFDC - EMI_OFF)
+#define EMI_TTYPE19_CONA (0xFE0 - EMI_OFF)
+#define EMI_TTYPE19_CONB (0xFE4 - EMI_OFF)
+#define EMI_TTYPE20_CONA (0xFE8 - EMI_OFF)
+#define EMI_TTYPE20_CONB (0xFEC - EMI_OFF)
+#define EMI_TTYPE21_CONA (0xFF0 - EMI_OFF)
+#define EMI_TTYPE21_CONB (0xFF4 - EMI_OFF)
+
+
+
+extern int MET_BM_Init(void);
+extern void MET_BM_DeInit(void);
+extern void MET_BM_SaveCfg(void);
+extern void MET_BM_RestoreCfg(void);
+
+
+
+extern int MET_BM_SetMonitorCounter(const unsigned int counter_num,
+				    const unsigned int master, const unsigned int trans_type);
+extern int MET_BM_SetTtypeCounterRW(unsigned int bmrw0_val, unsigned int bmrw1_val);
+extern int MET_BM_Set_WsctTsct_id_sel(unsigned int counter_num, unsigned int enable);
+extern int MET_BM_SetMaster(const unsigned int counter_num, const unsigned int master);
+extern int MET_BM_SetbusID_En(const unsigned int counter_num,
+			      const unsigned int enable);
+extern int MET_BM_SetbusID(const unsigned int counter_num,
+			   const unsigned int id);
+extern int MET_BM_SetUltraHighFilter(const unsigned int counter_num, const unsigned int enable);
+extern int MET_BM_SetLatencyCounter(unsigned int enable);
+extern void MET_BM_SetReadWriteType(const unsigned int ReadWriteType);
+
+extern unsigned int MET_EMI_GetDramRankNum(void);
+extern unsigned int MET_EMI_GetDramRankNum_CHN1(void);
+
+
+unsigned int MET_EMI_GetDramChannNum(void);
+
+
+/* SEDA 3.5 NEW */
+extern int MET_BM_SetWSCT_master_rw(unsigned int *master , unsigned int *rw);
+extern int MET_BM_SetWSCT_high_priority(unsigned int *disable, unsigned int *select);
+extern int MET_BM_SetWSCT_busid_idmask(unsigned int *busid, unsigned int *idMask);
+extern int MET_BM_SetWSCT_chn_rank_sel(unsigned int *chn_rank_sel);
+extern int MET_BM_SetWSCT_burst_range(unsigned int *bnd_dis, unsigned int *low_bnd, unsigned int *up_bnd);
+extern int MET_BM_SetTSCT_busid_enable(unsigned int *enable);
+extern int MET_BM_SetTtype_high_priority_sel(unsigned int _high_priority_filter, unsigned int *select);
+extern int MET_BM_SetTtype_busid_idmask(unsigned int *busid, unsigned int *idMask, int _ttype1_16_en, int _ttype17_21_en);
+extern int MET_BM_SetTtype_chn_rank_sel(unsigned int *chn_rank_sel);
+extern int MET_BM_SetTtype_burst_range(unsigned int *bnd_dis, unsigned int *low_bnd, unsigned int *up_bnd);
+extern unsigned int MET_EMI_Get_BaseClock_Rate(void);
+
+#endif                          /* !__MT_MET_EMI_BM_H__ */
diff --git a/src/devtools/met-driver/4.14/common/mtk_gpu_metmonitor.c b/src/devtools/met-driver/4.14/common/mtk_gpu_metmonitor.c
new file mode 100644
index 0000000..fea3388
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/mtk_gpu_metmonitor.c
@@ -0,0 +1,877 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <asm/page.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/syscalls.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <asm/uaccess.h>
+#include <linux/io.h>
+#include <linux/hrtimer.h>
+
+#include "met_drv.h"
+#include "trace.h"
+#include "interface.h"
+
+#include "mtk_gpu_metmonitor.h"
+#include "core_plf_init.h"
+#include "core_plf_trace.h"
+
+
+/*
+ * define if the hal implementation might re-schedule, cannot run inside softirq
+ * undefine this is better for sampling jitter if HAL support it
+ */
+#undef GPU_HAL_RUN_PREMPTIBLE
+
+#ifdef GPU_HAL_RUN_PREMPTIBLE
+static struct delayed_work gpu_dwork;
+static struct delayed_work gpu_pwr_dwork;
+#endif
+
+/* the mt_gpufreq_get_thermal_limit_freq use mutex_lock to do its job */
+/* so, change the gpu-dvfs implementation to dwork */
+static struct delayed_work gpu_dvfs_dwork;
+
+/*
+ * GPU monitor HAL comes from alps\mediatek\kernel\include\linux\mtk_gpu_utility.h
+ *
+ * mtk_get_gpu_memory_usage(unsigned int* pMemUsage) in unit of bytes
+ *
+ * mtk_get_gpu_xxx_loading are in unit of %
+*/
+
+enum MET_GPU_PROFILE_INDEX {
+	eMET_GPU_LOADING = 0,
+	eMET_GPU_BLOCK_LOADING,	/* 1 */
+	eMET_GPU_IDLE_LOADING,	/* 2 */
+	eMET_GPU_PROFILE_CNT
+};
+
+static unsigned long g_u4AvailableInfo;
+
+static unsigned int output_header_pmu_len;
+static unsigned int output_pmu_str_len;
+
+noinline void GPU_Loading(unsigned char cnt, unsigned int *value)
+{
+	switch (cnt) {
+	case 1:
+		MET_TRACE("%u\n", value[0]);
+		break;
+	case 2:
+		MET_TRACE("%u,%u\n", value[0], value[1]);
+		break;
+	case 3:
+		MET_TRACE("%u,%u,%u\n", value[0], value[1], value[2]);
+		break;
+	case 4:
+		MET_TRACE("%u,%u,%u,%u\n", value[0], value[1], value[2], value[3]);
+		break;
+	default:
+		break;
+	}
+
+}
+
+noinline void GPU_Sub_Loading(unsigned int loading)
+{
+	MET_TRACE("%u\n", loading);
+}
+
+noinline void GPU_3D_Fences_Count(int count)
+{
+	MET_TRACE("%d\n", count);
+}
+
+#ifdef GPU_HAL_RUN_PREMPTIBLE
+static void gpu_GPULoading(struct work_struct *work)
+{
+	unsigned int	pu4Value[eMET_GPU_PROFILE_CNT];
+	unsigned long	u4Index = 0;
+	unsigned int	loading = 0;
+	int		count = 0;
+
+	memset(pu4Value, 0x00, eMET_GPU_PROFILE_CNT);
+	if ((1 << eMET_GPU_LOADING) & g_u4AvailableInfo) {
+		if (mtk_get_gpu_loading_symbol && mtk_get_gpu_loading_symbol(&pu4Value[u4Index]))
+			u4Index += 1;
+	}
+
+	if ((1 << eMET_GPU_BLOCK_LOADING) & g_u4AvailableInfo) {
+		if (mtk_get_gpu_block_symbol && mtk_get_gpu_block_symbol(&pu4Value[u4Index]))
+			u4Index += 1;
+	}
+
+	if ((1 << eMET_GPU_IDLE_LOADING) & g_u4AvailableInfo) {
+		if (mtk_get_gpu_idle_symbol && mtk_get_gpu_idle_symbol(&pu4Value[u4Index]))
+			u4Index += 1;
+	}
+
+	if (g_u4AvailableInfo)
+		GPU_Loading(u4Index, pu4Value);
+
+	if (mtk_get_gpu_sub_loading_symbol && mtk_get_gpu_sub_loading_symbol(&loading))
+		GPU_Sub_Loading(loading);
+
+	if (mtk_get_3D_fences_count_symbol && mtk_get_3D_fences_count_symbol(&count))
+		GPU_3D_Fences_Count(count);
+}
+#else
+static void gpu_GPULoading(unsigned long long stamp, int cpu)
+{
+	unsigned int	pu4Value[eMET_GPU_PROFILE_CNT];
+	unsigned long	u4Index = 0;
+	unsigned int	loading = 0;
+	int		count = 0;
+
+	memset(pu4Value, 0x00, eMET_GPU_PROFILE_CNT);
+	if ((1 << eMET_GPU_LOADING) & g_u4AvailableInfo) {
+		if (mtk_get_gpu_loading_symbol) {
+			mtk_get_gpu_loading_symbol(&pu4Value[u4Index]);
+			u4Index += 1;
+		}
+	}
+
+	if ((1 << eMET_GPU_BLOCK_LOADING) & g_u4AvailableInfo) {
+		if (mtk_get_gpu_block_symbol) {
+			mtk_get_gpu_block_symbol(&pu4Value[u4Index]);
+			u4Index += 1;
+		}
+	}
+
+	if ((1 << eMET_GPU_IDLE_LOADING) & g_u4AvailableInfo) {
+		if (mtk_get_gpu_idle_symbol) {
+			mtk_get_gpu_idle_symbol(&pu4Value[u4Index]);
+			u4Index += 1;
+		}
+	}
+
+	if (g_u4AvailableInfo)
+		GPU_Loading(u4Index, pu4Value);
+
+	if (mtk_get_gpu_sub_loading_symbol) {
+		mtk_get_gpu_sub_loading_symbol(&loading);
+		GPU_Sub_Loading(loading);
+	}
+
+	if (mtk_get_3D_fences_count_symbol) {
+		mtk_get_3D_fences_count_symbol(&count);
+		GPU_3D_Fences_Count(count);
+	}
+}
+#endif
+
+static void gpu_monitor_start(void)
+{
+	if (mtk_get_gpu_loading_symbol)
+		g_u4AvailableInfo |= (1 << eMET_GPU_LOADING);
+	if (mtk_get_gpu_block_symbol)
+		g_u4AvailableInfo |= (1 << eMET_GPU_BLOCK_LOADING);
+	if (mtk_get_gpu_idle_symbol)
+		g_u4AvailableInfo |= (1 << eMET_GPU_IDLE_LOADING);
+
+#ifdef GPU_HAL_RUN_PREMPTIBLE
+	INIT_DELAYED_WORK(&gpu_dwork, gpu_GPULoading);
+#endif
+}
+
+#ifdef GPU_HAL_RUN_PREMPTIBLE
+static void gpu_monitor_stop(void)
+{
+	cancel_delayed_work_sync(&gpu_dwork);
+}
+
+static void GPULoadingNotify(unsigned long long stamp, int cpu)
+{
+	schedule_delayed_work(&gpu_dwork, 0);
+}
+#endif
+
+static char help[] =
+	"  --gpu					monitor gpu status\n";
+static int gpu_status_print_help(char *buf, int len)
+{
+	return snprintf(buf, PAGE_SIZE, help);
+}
+
+static char g_pComGPUStatusHeader[] =
+	"met-info [000] 0.0: met_gpu_loading_header: ";
+static int gpu_status_print_header(char *buf, int len)
+{
+	int ret = 0;
+
+	ret = snprintf(buf, PAGE_SIZE, "%s", g_pComGPUStatusHeader);
+
+	if ((1 << eMET_GPU_LOADING) & g_u4AvailableInfo)
+		ret += snprintf(buf+ret, PAGE_SIZE-ret, "%s", "Loading,");
+
+	if ((1 << eMET_GPU_BLOCK_LOADING) & g_u4AvailableInfo)
+		ret += snprintf(buf+ret, PAGE_SIZE-ret, "%s", "Blcok,");
+
+	if ((1 << eMET_GPU_IDLE_LOADING) & g_u4AvailableInfo)
+		ret += snprintf(buf+ret, PAGE_SIZE-ret, "%s", "Idle");
+
+	ret += snprintf(buf+ret, PAGE_SIZE-ret, "%s", "\n");
+
+	ret += snprintf(buf+ret, PAGE_SIZE-ret,
+			"met-info [000] 0.0: met_gpu_sub_loading_header: Loading\n");
+	ret += snprintf(buf+ret, PAGE_SIZE-ret,
+			"met-info [000] 0.0: met_gpu_3d_fences_count_header: Count\n");
+
+	return ret;
+}
+
+struct metdevice met_gpu = {
+	.name			= "gpu",
+	.owner			= THIS_MODULE,
+	.type			= MET_TYPE_BUS,
+	.cpu_related		= 0,
+	.start			= gpu_monitor_start,
+	.mode			= 0,
+	.polling_interval	= 1,	/* ms */
+#ifdef GPU_HAL_RUN_PREMPTIBLE
+	.timed_polling		= GPULoadingNotify,
+	.stop			= gpu_monitor_stop,
+#else
+	.timed_polling		= gpu_GPULoading,
+#endif
+	.print_help		= gpu_status_print_help,
+	.print_header		= gpu_status_print_header,
+};
+
+/*
+ * GPU DVFS Monitor
+ */
+#define	MTK_GPU_DVFS_TYPE_ITEM(type)	#type,
+static char *gpu_dvfs_type_name[] = MTK_GPU_DVFS_TYPE_LIST;
+#undef	MTK_GPU_DVFS_TYPE_ITEM
+
+static MTK_GPU_DVFS_TYPE gpu_dvfs_type_prev;
+static unsigned long gpu_dvfs_type_freq_prev;
+static unsigned int gpu_dvfs_type_freq[ARRAY_SIZE(gpu_dvfs_type_name)];
+
+noinline void GPU_DVFS(unsigned int Freq, unsigned int ThermalLimit,
+			unsigned long CustomBoost, unsigned long CustomUpbound)
+{
+	MET_TRACE("%u,%u,%lu,%lu\n", Freq, ThermalLimit, CustomBoost, CustomUpbound);
+}
+
+noinline void GPU_DVFS_TYPE(void)
+{
+	char	*SOB, *EOB;
+
+	MET_TRACE_GETBUF(&SOB, &EOB);
+	EOB = ms_formatD_EOL(EOB, ARRAY_SIZE(gpu_dvfs_type_freq), gpu_dvfs_type_freq);
+	MET_TRACE_PUTBUF(SOB, EOB);
+}
+
+noinline void GPU_DVFS_VSYNC(unsigned long freq)
+{
+	MET_TRACE("%lu\n", freq);
+}
+
+noinline void GPU_VSYNC_OFFSET_STATUS(unsigned int event_status, unsigned int debug_status)
+{
+	MET_TRACE("%u,%u\n", event_status, debug_status);
+}
+
+static void gpu_dvfs(void)
+{
+	unsigned int		freq = 0;
+	unsigned int		ThermalLimit = 0;
+	MTK_GPU_DVFS_TYPE	peType;
+	unsigned long		pulFreq = 0;
+	unsigned long		CustomBoost = 0;
+	unsigned long		CustomUpbound = 0;
+	unsigned int		event_status = 0;
+	unsigned int		debug_status = 0;
+
+	freq = mt_gpufreq_get_cur_freq_symbol ? mt_gpufreq_get_cur_freq_symbol() : 0;
+	ThermalLimit = mt_gpufreq_get_thermal_limit_freq_symbol ? mt_gpufreq_get_thermal_limit_freq_symbol() : 0;
+	if (mtk_get_custom_boost_gpu_freq_symbol)
+		mtk_get_custom_boost_gpu_freq_symbol(&CustomBoost);
+	if (mtk_get_custom_upbound_gpu_freq_symbol)
+		mtk_get_custom_upbound_gpu_freq_symbol(&CustomUpbound);
+	GPU_DVFS(freq, ThermalLimit, CustomBoost, CustomUpbound);
+
+	/* gpu dvfs type */
+	if (mtk_get_gpu_dvfs_from_symbol && mtk_get_gpu_dvfs_from_symbol(&peType, &pulFreq)) {
+		if (gpu_dvfs_type_prev != peType || gpu_dvfs_type_freq_prev != pulFreq) {
+			gpu_dvfs_type_freq[gpu_dvfs_type_prev] = 0;
+			gpu_dvfs_type_prev = peType;
+			gpu_dvfs_type_freq_prev = pulFreq;
+			gpu_dvfs_type_freq[gpu_dvfs_type_prev] = gpu_dvfs_type_freq_prev;
+			GPU_DVFS_TYPE();
+		}
+	}
+
+	if (mtk_get_vsync_based_target_freq_symbol && mtk_get_vsync_based_target_freq_symbol(&pulFreq))
+		GPU_DVFS_VSYNC(pulFreq);
+
+	if (mtk_get_vsync_offset_event_status_symbol && mtk_get_vsync_offset_debug_status_symbol) {
+		if (mtk_get_vsync_offset_event_status_symbol(&event_status)
+		    && mtk_get_vsync_offset_debug_status_symbol(&debug_status)) {
+			GPU_VSYNC_OFFSET_STATUS(event_status, debug_status);
+		}
+	}
+}
+
+static void gpu_dvfs_work(struct work_struct *work)
+{
+	gpu_dvfs();
+}
+
+static void gpu_dvfs_monitor_start(void)
+{
+	gpu_dvfs();
+	INIT_DELAYED_WORK(&gpu_dvfs_dwork, gpu_dvfs_work);
+}
+
+static void gpu_dvfs_monitor_stop(void)
+{
+	cancel_delayed_work_sync(&gpu_dvfs_dwork);
+	gpu_dvfs();
+
+	/* reset status */
+	gpu_dvfs_type_prev = 0;
+	gpu_dvfs_type_freq_prev = 0;
+}
+
+static void gpu_dvfs_monitor_polling(unsigned long long stamp, int cpu)
+{
+	schedule_delayed_work(&gpu_dvfs_dwork, 0);
+}
+
+static int gpu_dvfs_print_help(char *buf, int len)
+{
+	return snprintf(buf, PAGE_SIZE,
+			"  --gpu-dvfs				monitor gpu freq\n");
+}
+
+static int gpu_dvfs_print_header(char *buf, int len)
+{
+	int ret = 0;
+	int i = 0;
+
+	ret = snprintf(buf, PAGE_SIZE,
+			"met-info [000] 0.0: met_gpu_dvfs_header: ");
+	ret += snprintf(buf+ret, PAGE_SIZE-ret,
+			"Freq(kHz),ThermalLimit(kHz),CustomBoost,CustomUpbound\n");
+
+	ret += snprintf(buf+ret, PAGE_SIZE-ret,
+			"met-info [000] 0.0: met_gpu_dvfs_type_header: %s", gpu_dvfs_type_name[0]);
+	for (i = 1; i < ARRAY_SIZE(gpu_dvfs_type_name); i++)
+		ret += snprintf(buf+ret, PAGE_SIZE-ret, ",%s", gpu_dvfs_type_name[i]);
+	ret += snprintf(buf+ret, PAGE_SIZE-ret, "%s", "\n");
+
+	ret += snprintf(buf+ret, PAGE_SIZE-ret,
+			"met-info [000] 0.0: met_gpu_dvfs_vsync_header: VSYNC Based Freq\n");
+	ret += snprintf(buf+ret, PAGE_SIZE-ret,
+			"met-info [000] 0.0: met_gpu_vsync_offset_status_header: Event Status,Debug Status\n");
+
+	return ret;
+}
+
+struct metdevice met_gpudvfs = {
+	.name			= "gpu-dvfs",
+	.owner			= THIS_MODULE,
+	.type			= MET_TYPE_BUS,
+	.cpu_related		= 0,
+	.start			= gpu_dvfs_monitor_start,
+	.stop			= gpu_dvfs_monitor_stop,
+	.polling_interval	= 1,	/* ms */
+	.timed_polling		= gpu_dvfs_monitor_polling,
+	.print_help		= gpu_dvfs_print_help,
+	.print_header		= gpu_dvfs_print_header,
+	.ondiemet_mode		= 0,
+};
+
+/*
+ * GPU MEM monitor
+ */
+static unsigned long g_u4MemProfileIsOn;
+
+static void gpu_mem_monitor_start(void)
+{
+	if (!mtk_get_gpu_memory_usage_symbol)
+		return;
+
+#if 0
+	if (mtk_get_gpu_memory_usage_symbol(&u4Value))
+		g_u4MemProfileIsOn = 1;
+#endif
+	g_u4MemProfileIsOn = 1;
+}
+
+noinline void GPU_MEM(unsigned long long stamp, int cpu)
+{
+	unsigned int u4Value = 0;
+
+	if (!mtk_get_gpu_memory_usage_symbol)
+		return;
+
+	if (g_u4MemProfileIsOn == 1) {
+		mtk_get_gpu_memory_usage_symbol(&u4Value);
+		MET_TRACE("%d\n", u4Value);
+	}
+}
+
+static void gpu_mem_monitor_stop(void)
+{
+	g_u4MemProfileIsOn = 0;
+}
+
+static char help_mem[] =
+	"  --gpu-mem				monitor gpu memory status\n";
+static int gpu_mem_status_print_help(char *buf, int len)
+{
+	return snprintf(buf, PAGE_SIZE, help_mem);
+}
+
+static char g_pComGPUMemHeader[] =
+	"met-info [000] 0.0: met_gpu_mem_header: Usage\n";
+static int gpu_mem_status_print_header(char *buf, int len)
+{
+	return snprintf(buf, PAGE_SIZE, g_pComGPUMemHeader);
+}
+
+struct metdevice met_gpumem = {
+	.name			= "gpu-mem",
+	.owner			= THIS_MODULE,
+	.type			= MET_TYPE_BUS,
+	.cpu_related		= 0,
+	.start			= gpu_mem_monitor_start,
+	.stop			= gpu_mem_monitor_stop,
+	.mode			= 0,
+	.polling_interval	= 1,	/* ms */
+	.timed_polling		= GPU_MEM,
+	.print_help		= gpu_mem_status_print_help,
+	.print_header		= gpu_mem_status_print_header,
+};
+
+/*
+ * GPU power monitor
+ */
+static unsigned long g_u4PowerProfileIsOn;
+
+#ifdef GPU_HAL_RUN_PREMPTIBLE
+noinline void GPU_Power(struct work_struct *work)
+{
+	unsigned int u4Value = 0;
+
+	if (!mtk_get_gpu_power_loading_symbol)
+		return;
+
+	mtk_get_gpu_power_loading_symbol(&u4Value);
+	MET_TRACE("%d\n", u4Value);
+}
+
+static void GPU_PowerNotify(unsigned long long stamp, int cpu)
+{
+	if (g_u4PowerProfileIsOn == 1)
+		schedule_delayed_work(&gpu_pwr_dwork, 0);
+}
+#else
+noinline void GPU_Power(unsigned long long stamp, int cpu)
+{
+	unsigned int u4Value = 0;
+
+	if (!mtk_get_gpu_power_loading_symbol)
+		return;
+
+	if (g_u4PowerProfileIsOn == 1) {
+		mtk_get_gpu_power_loading_symbol(&u4Value);
+		MET_TRACE("%d\n", u4Value);
+	}
+}
+#endif
+
+static void gpu_Power_monitor_start(void)
+{
+	if (!mtk_get_gpu_power_loading_symbol)
+		return;
+
+#if 0
+	if (mtk_get_gpu_power_loading_symbol(&u4Value))
+		g_u4PowerProfileIsOn = 1;
+#endif
+	g_u4PowerProfileIsOn = 1;
+#ifdef GPU_HAL_RUN_PREMPTIBLE
+	INIT_DELAYED_WORK(&gpu_pwr_dwork, GPU_Power);
+#endif
+}
+
+static void gpu_Power_monitor_stop(void)
+{
+	g_u4PowerProfileIsOn = 0;
+
+#ifdef GPU_HAL_RUN_PREMPTIBLE
+	cancel_delayed_work_sync(&gpu_pwr_dwork);
+#endif
+}
+
+static char help_pwr[] =
+	"  --gpu-pwr				monitor gpu power status\n";
+static int gpu_Power_status_print_help(char *buf, int len)
+{
+	return snprintf(buf, PAGE_SIZE, help_pwr);
+}
+
+static char g_pComGPUPowerHeader[] =
+	"met-info [000] 0.0: met_gpu_power_header: Loading\n";
+static int gpu_Power_status_print_header(char *buf, int len)
+{
+	return snprintf(buf, PAGE_SIZE, g_pComGPUPowerHeader);
+}
+
+struct metdevice met_gpupwr = {
+	.name			= "gpu-pwr",
+	.owner			= THIS_MODULE,
+	.type			= MET_TYPE_BUS,
+	.cpu_related		= 0,
+	.start			= gpu_Power_monitor_start,
+	.stop			= gpu_Power_monitor_stop,
+	.mode			= 0,
+	.polling_interval	= 1,	/* ms */
+#ifdef GPU_HAL_RUN_PREMPTIBLE
+	.timed_polling		= GPU_PowerNotify,
+#else
+	.timed_polling		= GPU_Power,
+#endif
+	.print_help		= gpu_Power_status_print_help,
+	.print_header		= gpu_Power_status_print_header,
+};
+
+
+/*
+ * GPU PMU
+ */
+#define UNUSE_ARG(arg) ((void)arg)
+
+#ifdef GPU_HAL_RUN_PREMPTIBLE
+static struct delayed_work gpu_pmu_dwork;
+#endif
+
+#define MAX_PMU_STR_LEN (1024 * 5)
+
+static const char help_pmu[] = "  --gpu-pmu				monitor gpu pmu status";
+static const char header_pmu[] = "met-info [000] 0.0: met_gpu_pmu_header: ";
+static char pmu_str[MAX_PMU_STR_LEN];
+static int pmu_cnt;
+static int gpu_pwr_status = 1;
+static GPU_PMU *pmu_list;
+
+
+noinline void GPU_PMU_RAW(
+	unsigned long long stamp,
+	int cpu)
+{
+	bool ret;
+	int i = 0;
+	char *SOB, *EOB;
+	unsigned int value[pmu_cnt];
+
+	if (stamp == 0 && cpu == 0) {
+		for (i = 0; i < pmu_cnt; i++)
+			value[i] = 0;
+
+		MET_TRACE_GETBUF(&SOB, &EOB);
+		EOB = ms_formatH(EOB, pmu_cnt, value);
+		MET_TRACE_PUTBUF(SOB, EOB);
+		return;
+	}
+
+	if (mtk_get_gpu_pmu_swapnreset_symbol) {
+		ret = mtk_get_gpu_pmu_swapnreset_symbol(pmu_list, pmu_cnt);
+		if (ret) {
+			for (i = 0; i < pmu_cnt; i++) {
+				if (pmu_list[i].overflow)
+					pmu_list[i].value = 0xFFFFFFFF;
+				value[i] = pmu_list[i].value;
+			}
+			MET_TRACE_GETBUF(&SOB, &EOB);
+			EOB = ms_formatH(EOB, pmu_cnt, value);
+			MET_TRACE_PUTBUF(SOB, EOB);
+		}
+	}
+}
+
+static int create_gpu_pmu_list(void)
+{
+	int ret = 0;
+	int len = 0;
+	int i = 0;
+
+	if (mtk_get_gpu_pmu_init_symbol) {
+		ret = mtk_get_gpu_pmu_init(NULL, 0, &pmu_cnt);
+		if (pmu_cnt == 0 || ret == 0)
+			return 0;
+	} else
+		return 0;
+
+	pmu_list = kmalloc_array(pmu_cnt, sizeof(GPU_PMU), GFP_KERNEL);
+	if (pmu_list) {
+		memset(pmu_list, 0x00, sizeof(GPU_PMU)*pmu_cnt);
+		ret = mtk_get_gpu_pmu_init(pmu_list, pmu_cnt, NULL);
+
+		memset(pmu_str, 0x00, MAX_PMU_STR_LEN);
+		len = snprintf(pmu_str, MAX_PMU_STR_LEN, "%s", pmu_list[0].name);
+		for (i = 1; i < pmu_cnt; i++)
+			len += snprintf(pmu_str + len, MAX_PMU_STR_LEN - len, ",%s", pmu_list[i].name);
+
+		/*
+		* dummy read in order to reset GPU PMU counter
+		*/
+		if (mtk_get_gpu_pmu_swapnreset_symbol)
+			mtk_get_gpu_pmu_swapnreset_symbol(pmu_list, pmu_cnt);
+	}
+
+	/* init state */
+	met_gpu_pmu.header_read_again = 0;
+	output_header_pmu_len = 0;
+	output_pmu_str_len = 0;
+
+	return ret;
+}
+
+static void delete_gpu_pmu_list(void)
+{
+	kfree(pmu_list);
+	pmu_list = NULL;
+	pmu_cnt = 0;
+}
+
+static void gpu_pwr_status_cb(int on)
+{
+	MET_TRACE("on = %d\n", on);
+
+	if (on == 1) {
+		/*
+		* dummy read in order to reset GPU PMU counter
+		*/
+		if (mtk_get_gpu_pmu_swapnreset_symbol)
+			mtk_get_gpu_pmu_swapnreset_symbol(pmu_list, pmu_cnt);
+
+	} else {
+		GPU_PMU_RAW(1, 0);
+		GPU_PMU_RAW(0, 0);
+	}
+
+	gpu_pwr_status = on;
+}
+
+static void gpu_pmu_monitor_start(void)
+{
+	int ret;
+
+	ret = create_gpu_pmu_list();
+	if (ret == 0)
+		return;
+
+	if (mtk_register_gpu_power_change_symbol)
+		mtk_register_gpu_power_change_symbol("met_gpu", gpu_pwr_status_cb);
+
+#ifdef GPU_HAL_RUN_PREMPTIBLE
+	INIT_DELAYED_WORK(&gpu_pmu_dwork, GPU_PMU_RAW);
+#endif
+}
+
+static void gpu_pmu_monitor_stop(void)
+{
+#ifdef GPU_HAL_RUN_PREMPTIBLE
+	cancel_delayed_work_sync(&gpu_pmu_dwork);
+#endif
+
+	if (mtk_unregister_gpu_power_change_symbol)
+		mtk_unregister_gpu_power_change_symbol("met_gpu");
+	delete_gpu_pmu_list();
+
+#if 1
+	/* stop polling counter */
+	if (mtk_get_gpu_pmu_swapnreset_stop_symbol)
+		mtk_get_gpu_pmu_swapnreset_stop_symbol();
+	/* release resource */
+	if (mtk_get_gpu_pmu_deinit_symbol)
+		mtk_get_gpu_pmu_deinit_symbol();
+#endif
+}
+
+#ifdef GPU_HAL_RUN_PREMPTIBLE
+static void gpu_pmu_timed_polling_notify(
+	unsigned long long stamp,
+	int cpu)
+{
+	UNUSE_ARG(stamp);
+	UNUSE_ARG(cpu);
+
+	if (gpu_pwr_status == 1)
+		schedule_delayed_work(&gpu_pmu_dwork, 0);
+}
+#else
+static void gpu_pmu_timed_polling(
+	unsigned long long stamp,
+	int cpu)
+{
+	UNUSE_ARG(stamp);
+	UNUSE_ARG(cpu);
+
+	if (gpu_pwr_status == 1)
+		GPU_PMU_RAW(stamp, cpu);
+}
+#endif
+
+static int gpu_pmu_print_help(
+	char *buf,
+	int len)
+{
+	UNUSE_ARG(len);
+	return snprintf(buf, PAGE_SIZE, "%s\n", help_pmu);
+}
+
+static int gpu_pmu_print_header(
+	char *buf,
+	int len)
+{
+	if(output_header_pmu_len == 0){
+		len = snprintf(buf, PAGE_SIZE, "%s", header_pmu);
+		met_gpu_pmu.header_read_again = 1;
+		output_header_pmu_len = len;
+	}
+	else{
+		if( (strlen(pmu_str) - output_pmu_str_len) > PAGE_SIZE ){
+			char output_buf[PAGE_SIZE/4];
+
+			strncpy(output_buf, pmu_str+output_pmu_str_len, PAGE_SIZE/4);
+			len = snprintf(buf, PAGE_SIZE, "%s", output_buf);
+			output_pmu_str_len += len;
+		}
+		else{
+			len = snprintf(buf, PAGE_SIZE, "%s\n", pmu_str+output_pmu_str_len);
+
+			/* reset state */
+			met_gpu_pmu.header_read_again = 0;
+			output_header_pmu_len = 0;
+			output_pmu_str_len = 0;
+		}
+	}
+
+	return len;
+}
+
+struct metdevice met_gpu_pmu = {
+	.name			= "gpu-pmu",
+	.owner			= THIS_MODULE,
+	.type			= MET_TYPE_PMU,
+	.cpu_related		= 0,
+	.start			= gpu_pmu_monitor_start,
+	.stop			= gpu_pmu_monitor_stop,
+	.mode			= 0,
+	.polling_interval	= 1,	/* ms */
+#ifdef GPU_HAL_RUN_PREMPTIBLE
+	.timed_polling		= gpu_pmu_timed_polling_notify,
+#else
+	.timed_polling		= gpu_pmu_timed_polling,
+#endif
+	.print_help		= gpu_pmu_print_help,
+	.print_header		= gpu_pmu_print_header,
+};
+
+/*
+ * GPU stall counters
+ */
+#ifdef MET_GPU_STALL_MONITOR
+static void __iomem	*io_addr_gpu_stall;
+
+static int gpu_stall_create_subfs(struct kobject *parent)
+{
+	io_addr_gpu_stall = ioremap(IO_ADDR_GPU_STALL, IO_SIZE_GPU_STALL);
+	if (!io_addr_gpu_stall) {
+		PR_BOOTMSG("Failed to init GPU stall counters!!\n");
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+static void gpu_stall_delete_subfs(void)
+{
+	if (io_addr_gpu_stall) {
+		iounmap(io_addr_gpu_stall);
+		io_addr_gpu_stall = NULL;
+	}
+}
+
+static void gpu_stall_start(void)
+{
+	writel(0x00010001, io_addr_gpu_stall+OFFSET_STALL_GPU_M0_CHECK);
+	writel(0x00010001, io_addr_gpu_stall+OFFSET_STALL_GPU_M1_CHECK);
+	writel(0x00010001, io_addr_gpu_stall+OFFSET_STALL_GPU_M0_EMI_CHECK);
+	writel(0x00010001, io_addr_gpu_stall+OFFSET_STALL_GPU_M1_EMI_CHECK);
+}
+
+static void gpu_stall_stop(void)
+{
+	writel(0x00000000, io_addr_gpu_stall+OFFSET_STALL_GPU_M0_CHECK);
+	writel(0x00000000, io_addr_gpu_stall+OFFSET_STALL_GPU_M1_CHECK);
+	writel(0x00000000, io_addr_gpu_stall+OFFSET_STALL_GPU_M0_EMI_CHECK);
+	writel(0x00000000, io_addr_gpu_stall+OFFSET_STALL_GPU_M1_EMI_CHECK);
+}
+
+noinline void GPU_STALL_RAW(void)
+{
+	unsigned int	stall_counters[4];
+	char		*SOB, *EOB;
+
+	stall_counters[0] = (unsigned int)readl(io_addr_gpu_stall+OFFSET_STALL_GPU_M0_CHECK);
+	stall_counters[1] = (unsigned int)readl(io_addr_gpu_stall+OFFSET_STALL_GPU_M1_CHECK);
+	stall_counters[2] = (unsigned int)readl(io_addr_gpu_stall+OFFSET_STALL_GPU_M0_EMI_CHECK);
+	stall_counters[3] = (unsigned int)readl(io_addr_gpu_stall+OFFSET_STALL_GPU_M1_EMI_CHECK);
+
+	MET_TRACE_GETBUF(&SOB, &EOB);
+	EOB = ms_formatH(EOB, ARRAY_SIZE(stall_counters), stall_counters);
+	MET_TRACE_PUTBUF(SOB, EOB);
+}
+
+static void gpu_stall_timed_polling(unsigned long long stamp, int cpu)
+{
+	GPU_STALL_RAW();
+}
+
+static char g_pComGPUStallHeader[] =
+	"met-info [000] 0.0: met_gpu_stall_header: M0_STATUS_1,M1_STATUS_1,M0_STATUS_2,M1_STATUS_2\n";
+static int gpu_stall_print_header(char *buf, int len)
+{
+	return snprintf(buf, PAGE_SIZE, g_pComGPUStallHeader);
+}
+
+struct metdevice met_gpu_stall = {
+	.name			= "gpu-stall",
+	.owner			= THIS_MODULE,
+	.type			= MET_TYPE_BUS,
+	.cpu_related		= 0,
+	.create_subfs		= gpu_stall_create_subfs,
+	.delete_subfs		= gpu_stall_delete_subfs,
+	.start			= gpu_stall_start,
+	.stop			= gpu_stall_stop,
+	.mode			= 0,
+	.polling_interval	= 1,	/* ms */
+	.timed_polling		= gpu_stall_timed_polling,
+	.print_header		= gpu_stall_print_header,
+};
+#endif	/* MET_GPU_STALL_MONITOR */
diff --git a/src/devtools/met-driver/4.14/common/mtk_gpu_metmonitor.h b/src/devtools/met-driver/4.14/common/mtk_gpu_metmonitor.h
new file mode 100644
index 0000000..069c534
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/mtk_gpu_metmonitor.h
@@ -0,0 +1,18 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MT_GPU_METMONITOR_H_
+
+#define _MT_GPU_METMONITOR_H_
+
+#endif				/* _MT_GPU_METMONITOR_H_ */
diff --git a/src/devtools/met-driver/4.14/common/mtk_typedefs.h b/src/devtools/met-driver/4.14/common/mtk_typedefs.h
new file mode 100644
index 0000000..4424479
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/mtk_typedefs.h
@@ -0,0 +1,318 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MT_TYPEDEFS_H__
+
+/*
+ *  KOBJ ATTR Manipulations Macros
+ */
+
+#define KOBJ_ITEM_LIST(args...)		args
+
+/*
+ * Declaring KOBJ attributes
+ */
+#define DECLARE_KOBJ_ATTR(attr_name) \
+	static struct kobj_attribute attr_name##_attr = \
+		__ATTR(attr_name, 0664, attr_name##_show, attr_name##_store)
+
+#define DECLARE_KOBJ_ATTR_RO(attr_name) \
+	static struct kobj_attribute attr_name##_attr = \
+		__ATTR_RO(attr_name)
+
+/*
+ * Declaring KOBJ attributes with integer variable
+ */
+/* normal version */
+#define DECLARE_KOBJ_ATTR_SHOW_INT(attr_name, var_name) \
+	static ssize_t attr_name##_show( \
+		struct kobject *kobj, \
+		struct kobj_attribute *attr, \
+		char *buf) \
+	{ \
+		return snprintf(buf, PAGE_SIZE, "%d\n", var_name); \
+	}
+#define DECLARE_KOBJ_ATTR_STORE_INT(attr_name, var_name) \
+	static ssize_t attr_name##_store( \
+		struct kobject *kobj, \
+		struct kobj_attribute *attr, \
+		const char *buf, \
+		size_t n) \
+	{ \
+		int	val; \
+		if (kstrtoint(buf, 0, &val) != 0) { \
+			return -EINVAL; \
+		} \
+		var_name = val; \
+		return n; \
+	}
+#define DECLARE_KOBJ_ATTR_INT(attr_name, var_name) \
+	DECLARE_KOBJ_ATTR_SHOW_INT(attr_name, var_name) \
+	DECLARE_KOBJ_ATTR_STORE_INT(attr_name, var_name) \
+	DECLARE_KOBJ_ATTR(attr_name)
+#define DECLARE_KOBJ_ATTR_RO_INT(attr_name, var_name) \
+	DECLARE_KOBJ_ATTR_SHOW_INT(attr_name, var_name) \
+	DECLARE_KOBJ_ATTR_RO(attr_name)
+
+/* cond-check version */
+#define DECLARE_KOBJ_ATTR_STORE_INT_CHECK(attr_name, var_name, cond) \
+	static ssize_t attr_name##_store( \
+		struct kobject *kobj, \
+		struct kobj_attribute *attr, \
+		const char *buf, \
+		size_t n) \
+	{ \
+		int	var_name##temp = var_name; \
+		if (kstrtoint(buf, 0, &var_name) != 0) { \
+			var_name = var_name##temp; \
+			return -EINVAL; \
+		} \
+		if (cond) { \
+			return n; \
+		} else { \
+			var_name = var_name##temp; \
+			return -EINVAL; \
+		} \
+	}
+/* Note: the name of val in cond can NOT be the same as var_name */
+#define DECLARE_KOBJ_ATTR_INT_CHECK(attr_name, var_name, cond) \
+	DECLARE_KOBJ_ATTR_SHOW_INT(attr_name, var_name) \
+	DECLARE_KOBJ_ATTR_STORE_INT_CHECK(attr_name, var_name, cond) \
+	DECLARE_KOBJ_ATTR(attr_name)
+
+/* helper procedure version */
+#define DECLARE_KOBJ_ATTR_SHOW_INT_PROC(attr_name, var_name, func) \
+	static ssize_t attr_name##_show( \
+		struct kobject *kobj, \
+		struct kobj_attribute *attr, \
+		char *buf) \
+	{ \
+		return func(kobj, attr, buf, var_name); \
+	}
+#define DECLARE_KOBJ_ATTR_STORE_INT_PROC(attr_name, var_name, func) \
+	static ssize_t attr_name##_store( \
+		struct kobject *kobj, \
+		struct kobj_attribute *attr, \
+		const char *buf, \
+		size_t n) \
+	{ \
+		return func(kobj, attr, buf, n, &(var_name)); \
+	}
+#define DECLARE_KOBJ_ATTR_INT_PROC(attr_name, var_name, show, store) \
+	DECLARE_KOBJ_ATTR_SHOW_INT_PROC(attr_name, var_name, show) \
+	DECLARE_KOBJ_ATTR_STORE_INT_PROC(attr_name, var_name, store) \
+	DECLARE_KOBJ_ATTR(attr_name)
+
+/*
+ * Declaring KOBJ attributes with integer(hex) variable
+ */
+/* normal version */
+#define DECLARE_KOBJ_ATTR_SHOW_HEX(attr_name, var_name) \
+	static ssize_t attr_name##_show( \
+		struct kobject *kobj, \
+		struct kobj_attribute *attr, \
+		char *buf) \
+	{ \
+		return snprintf(buf, PAGE_SIZE, "%x\n", var_name); \
+	}
+#define DECLARE_KOBJ_ATTR_STORE_HEX(attr_name, var_name) \
+	static ssize_t attr_name##_store( \
+		struct kobject *kobj, \
+		struct kobj_attribute *attr, \
+		const char *buf, \
+		size_t n) \
+	{ \
+		unsigned int	val; \
+		if (kstrtouint(buf, 0, &val) != 0) { \
+			return -EINVAL; \
+		} \
+		var_name = val; \
+		return n; \
+	}
+#define DECLARE_KOBJ_ATTR_HEX(attr_name, var_name) \
+	DECLARE_KOBJ_ATTR_SHOW_HEX(attr_name, var_name) \
+	DECLARE_KOBJ_ATTR_STORE_HEX(attr_name, var_name) \
+	DECLARE_KOBJ_ATTR(attr_name)
+#define DECLARE_KOBJ_ATTR_RO_HEX(attr_name, var_name) \
+	DECLARE_KOBJ_ATTR_SHOW_HEX(attr_name, var_name) \
+	DECLARE_KOBJ_ATTR_RO(attr_name)
+
+/* cond-check version */
+#define DECLARE_KOBJ_ATTR_STORE_HEX_CHECK(attr_name, var_name, cond) \
+	static ssize_t attr_name##_store( \
+		struct kobject *kobj, \
+		struct kobj_attribute *attr, \
+		const char *buf, \
+		size_t n) \
+	{ \
+		unsigned int	var_name##temp = var_name; \
+		if (kstrtouint(buf, 0, &var_name) != 0) { \
+			var_name = var_name##temp; \
+			return -EINVAL; \
+		} \
+		if (cond) { \
+			return n; \
+		} else { \
+			var_name = var_name##temp; \
+			return -EINVAL; \
+		} \
+	}
+/* Note: the name of val in cond can NOT be the same as var_name */
+#define DECLARE_KOBJ_ATTR_HEX_CHECK(attr_name, var_name, cond) \
+	DECLARE_KOBJ_ATTR_SHOW_HEX(attr_name, var_name) \
+	DECLARE_KOBJ_ATTR_STORE_HEX_CHECK(attr_name, var_name, cond) \
+	DECLARE_KOBJ_ATTR(attr_name)
+
+/* helper procedure version */
+#define DECLARE_KOBJ_ATTR_SHOW_HEX_PROC(attr_name, var_name, func) \
+	static ssize_t attr_name##_show( \
+		struct kobject *kobj, \
+		struct kobj_attribute *attr, \
+		char *buf) \
+	{ \
+		return func(kobj, attr, buf, var_name); \
+	}
+#define DECLARE_KOBJ_ATTR_STORE_HEX_PROC(attr_name, var_name, func) \
+	static ssize_t attr_name##_store( \
+		struct kobject *kobj, \
+		struct kobj_attribute *attr, \
+		const char *buf, \
+		size_t n) \
+	{ \
+		return func(kobj, attr, buf, n, &(var_name)); \
+	}
+#define DECLARE_KOBJ_ATTR_HEX_PROC(attr_name, var_name, show, store) \
+	DECLARE_KOBJ_ATTR_SHOW_HEX_PROC(attr_name, var_name, show) \
+	DECLARE_KOBJ_ATTR_STORE_HEX_PROC(attr_name, var_name, store) \
+	DECLARE_KOBJ_ATTR(attr_name)
+
+/*
+ * Declaring KOBJ attributes with string variable
+ */
+#define DECLARE_KOBJ_ATTR_SHOW_STR(attr_name, var_name) \
+	static ssize_t attr_name##_show( \
+		struct kobject *kobj, \
+		struct kobj_attribute *attr, \
+		char *buf) \
+	{ \
+		return snprintf(buf, PAGE_SIZE, "%s", var_name); \
+	}
+
+#define DECLARE_KOBJ_ATTR_RO_STR(attr_name, var_name) \
+	DECLARE_KOBJ_ATTR_SHOW_STR(attr_name, var_name) \
+	DECLARE_KOBJ_ATTR_RO(attr_name)
+
+/*
+ * Declaring KOBJ attributes with integer list variable
+ */
+#define DECLARE_KOBJ_ATTR_INT_LIST_ITEM(list_name, list) \
+	static struct list_name##_list_item_t { \
+		int	key; \
+		int	val; \
+	} const list_name##_list_item[] = { list }
+#define DECLARE_KOBJ_ATTR_SHOW_INT_LIST(attr_name, var_name, list_name) \
+	static ssize_t attr_name##_show( \
+		struct kobject *kobj, \
+		struct kobj_attribute *attr, \
+		char *buf) \
+	{ \
+		int	i; \
+		for (i = 0; i < ARRAY_SIZE(list_name##_list_item); i++) { \
+			if (var_name == list_name##_list_item[i].key) { \
+				return snprintf(buf, \
+						PAGE_SIZE, \
+						"%d\n", \
+						list_name##_list_item[i].val); \
+			} \
+		} \
+		return snprintf(buf, PAGE_SIZE, "%d\n", -1); \
+	}
+#define DECLARE_KOBJ_ATTR_STORE_INT_LIST(attr_name, var_name, list_name) \
+	static ssize_t attr_name##_store( \
+		struct kobject *kobj, \
+		struct kobj_attribute *attr, \
+		const char *buf, \
+		size_t n) \
+	{ \
+		int	value; \
+		int	i; \
+		if (kstrtoint(buf, 10, &value) != 0) \
+			return -EINVAL; \
+		for (i = 0; i < ARRAY_SIZE(list_name##_list_item); i++) { \
+			if (value == list_name##_list_item[i].val) { \
+				var_name = list_name##_list_item[i].key; \
+				return n; \
+			} \
+		} \
+		return -EINVAL; \
+	}
+#define DECLARE_KOBJ_ATTR_INT_LIST(attr_name, var_name, list_name) \
+	DECLARE_KOBJ_ATTR_SHOW_INT_LIST(attr_name, var_name, list_name) \
+	DECLARE_KOBJ_ATTR_STORE_INT_LIST(attr_name, var_name, list_name) \
+	DECLARE_KOBJ_ATTR(attr_name)
+
+/*
+ * Declaring KOBJ attributes with string list variable
+ */
+#define DECLARE_KOBJ_ATTR_STR_LIST_ITEM(list_name, list) \
+	static struct list_name##_list_item_t { \
+		int	key; \
+		char	*val; \
+	} const list_name##_list_item[] = { list }
+#define DECLARE_KOBJ_ATTR_SHOW_STR_LIST(attr_name, var_name, list_name) \
+	static ssize_t attr_name##_show( \
+		struct kobject *kobj, \
+		struct kobj_attribute *attr, \
+		char *buf) \
+	{ \
+		int	i; \
+		for (i = 0; i < ARRAY_SIZE(list_name##_list_item); i++) { \
+			if (var_name == list_name##_list_item[i].key) { \
+				return snprintf(buf, \
+						PAGE_SIZE, \
+						"%s\n", \
+						list_name##_list_item[i].val); \
+			} \
+		} \
+		return snprintf(buf, PAGE_SIZE, "%s\n", "ERR"); \
+	}
+#define DECLARE_KOBJ_ATTR_STORE_STR_LIST(attr_name, var_name, list_name) \
+	static ssize_t attr_name##_store( \
+		struct kobject *kobj, \
+		struct kobj_attribute *attr, \
+		const char *buf, \
+		size_t n) \
+	{ \
+		int	i; \
+		for (i = 0; i < ARRAY_SIZE(list_name##_list_item); i++) { \
+			if (strncasecmp(buf, \
+					list_name##_list_item[i].val, \
+					strlen(list_name##_list_item[i].val)) == 0) { \
+				var_name = list_name##_list_item[i].key; \
+				return n; \
+			} \
+		} \
+		return -EINVAL; \
+	}
+#define DECLARE_KOBJ_ATTR_STR_LIST(attr_name, var_name, list_name) \
+	DECLARE_KOBJ_ATTR_SHOW_STR_LIST(attr_name, var_name, list_name) \
+	DECLARE_KOBJ_ATTR_STORE_STR_LIST(attr_name, var_name, list_name) \
+	DECLARE_KOBJ_ATTR(attr_name)
+
+/*
+ *  MET Debug Message
+ */
+#define METINFO(format, ...)	pr_debug("[MET]%s: "format, __func__, ##__VA_ARGS__)
+#define METERROR(format, ...)	pr_debug("[MET][ERR]%s: "format, __func__, ##__VA_ARGS__)
+
+#endif	/* _MT_TYPEDEFS_H__ */
diff --git a/src/devtools/met-driver/4.14/common/ondiemet.c b/src/devtools/met-driver/4.14/common/ondiemet.c
new file mode 100644
index 0000000..5247fa7
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/ondiemet.c
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "ondiemet.h"
+
+/* record enabled modules */
+unsigned int ondiemet_module[ONDIEMET_NUM];
+EXPORT_SYMBOL(ondiemet_module);
+
+void (*scp_start[ONDIEMET_NUM]) (void) = {
+sspm_start, NULL, NULL};
+
+void (*scp_stop[ONDIEMET_NUM]) (void) = {
+sspm_stop, NULL, NULL};
+
+void (*scp_extract[ONDIEMET_NUM]) (void) = {
+sspm_extract, NULL, NULL};
+
+/* record which MCU is started to generate data */
+int ondiemet_module_started[ONDIEMET_NUM];
+
+int ondiemet_attr_init(struct device *dev)
+{
+	int ret;
+
+	ret = sspm_attr_init(dev);
+	if (ret != 0) {
+		pr_debug("can not create device file: sspm related\n");
+		return ret;
+	}
+
+	return 0;
+
+}
+
+int ondiemet_attr_uninit(struct device *dev)
+{
+	int ret;
+
+	ret = sspm_attr_uninit(dev);
+	if (ret != 0) {
+		pr_debug("can not delete device file: sspm related\n");
+		return ret;
+	}
+
+	return 0;
+
+}
+
+void ondiemet_start(void)
+{
+	int i;
+
+	for (i = 0; i < ONDIEMET_NUM; i++) {
+		if (ondiemet_module[i] != 0) {
+			ondiemet_module_started[i] = 1;
+			(*scp_start[i]) ();
+		}
+	}
+}
+
+void ondiemet_stop(void)
+{
+	int i;
+
+	for (i = 0; i < ONDIEMET_NUM; i++) {
+		if (ondiemet_module[i] != 0) {
+			(*scp_stop[i]) ();
+			ondiemet_module_started[i] = 0;
+		}
+	}
+}
+
+void ondiemet_extract(void)
+{
+	int i;
+
+	for (i = 0; i < ONDIEMET_NUM; i++) {
+		if (ondiemet_module[i] != 0)
+			(*scp_extract[i]) ();
+	}
+}
diff --git a/src/devtools/met-driver/4.14/common/ondiemet.h b/src/devtools/met-driver/4.14/common/ondiemet.h
new file mode 100644
index 0000000..3fff604
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/ondiemet.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __ONDIEMET_H
+#define __ONDIEMET_H
+
+#include "ondiemet_log.h"
+
+extern void ondiemet_extract(void);
+extern void ondiemet_stop(void);
+extern void ondiemet_start(void);
+
+#define ONDIEMET_SSPM  0
+#define ONDIEMET_NUM  3		/* total number of supported */
+extern unsigned int ondiemet_module[];
+extern void sspm_start(void);
+extern void sspm_stop(void);
+extern void sspm_extract(void);
+extern int sspm_attr_init(struct device *dev);
+extern int sspm_attr_uninit(struct device *dev);
+
+extern int ondiemet_attr_init(struct device *dev);
+extern int ondiemet_attr_uninit(struct device *dev);
+
+extern int sspm_buffer_size;
+
+#endif				/* __ONDIEMET_H */
diff --git a/src/devtools/met-driver/4.14/common/ondiemet_log.c b/src/devtools/met-driver/4.14/common/ondiemet_log.c
new file mode 100644
index 0000000..fd1ca31
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/ondiemet_log.c
@@ -0,0 +1,517 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/debugfs.h>
+#include <linux/mutex.h>
+#include <linux/semaphore.h>
+#include <linux/freezer.h>
+#include <linux/uaccess.h>
+#include <linux/completion.h>
+
+#include "ondiemet_log.h"
+
+#define ONDIEMET_LOG_REQ 1
+/* TODO: abandon this constatnt */
+#define ONDIEMET_LOG_STOP 2
+
+#define PID_NONE (-1)
+
+#define ONDIEMET_LOG_STOP_MODE 0
+#define ONDIEMET_LOG_RUN_MODE 1
+#define ONDIEMET_LOG_DEBUG_MODE 2
+
+static int ondiemet_trace_run;
+static struct dentry *dbgfs_met_dir;
+
+struct mutex lock_tracef;
+struct ondiemet_log_req_q_t {
+	struct list_head listq;
+	struct mutex lockq;
+	/* struct semaphore new_evt_sema; */
+	struct completion new_evt_comp;
+	int closeq_flag;
+} ondiemet_log_req_q;
+
+struct ondiemet_log_req {
+	struct list_head list;
+	int cmd_type;
+	const char *src;
+	size_t num;
+
+	void (*on_fini_cb)(const void *p);
+	const void *param;
+};
+
+#define __ondiemet_log_req_init(req, cmd, s, n, pf, p)	\
+	do {						\
+		INIT_LIST_HEAD(&req->list);		\
+		req->cmd_type = cmd;			\
+		req->src = s;				\
+		req->num = n;				\
+		req->on_fini_cb = pf;			\
+		req->param = p;				\
+	} while (0)
+
+#define __ondiemet_log_req_fini(req)		        \
+	do {					        \
+		if (req->on_fini_cb)			\
+			req->on_fini_cb(req->param);	\
+		kfree(req);				\
+	} while (0)
+
+static void __ondiemet_log_req_q_init(struct ondiemet_log_req_q_t *q)
+{
+	INIT_LIST_HEAD(&q->listq);
+	mutex_init(&q->lockq);
+	/* sema_init(&q->new_evt_sema, 0); */
+	init_completion(&q->new_evt_comp);
+	q->closeq_flag = 1;
+}
+
+/* undequeue is seen as a roll-back operation, so it can be done even when the queue is closed */
+static void __ondiemet_log_req_undeq(struct ondiemet_log_req *req)
+{
+	mutex_lock(&ondiemet_log_req_q.lockq);
+	list_add(&req->list, &ondiemet_log_req_q.listq);
+	mutex_unlock(&ondiemet_log_req_q.lockq);
+
+	/* up(&ondiemet_log_req_q.new_evt_sema); */
+	complete(&ondiemet_log_req_q.new_evt_comp);
+}
+
+static int __ondiemet_log_req_enq(struct ondiemet_log_req *req)
+{
+	mutex_lock(&ondiemet_log_req_q.lockq);
+	if (ondiemet_log_req_q.closeq_flag) {
+		mutex_unlock(&ondiemet_log_req_q.lockq);
+		return -EBUSY;
+	}
+
+	list_add_tail(&req->list, &ondiemet_log_req_q.listq);
+	if (req->cmd_type == ONDIEMET_LOG_STOP)
+		ondiemet_log_req_q.closeq_flag = 1;
+	mutex_unlock(&ondiemet_log_req_q.lockq);
+
+	/* up(&ondiemet_log_req_q.new_evt_sema); */
+	complete(&ondiemet_log_req_q.new_evt_comp);
+
+	return 0;
+}
+
+int ondiemet_log_req_enq(const char *src, size_t num, void (*on_fini_cb)(const void *p), const void *param)
+{
+	struct ondiemet_log_req *req = kmalloc(sizeof(*req), GFP_KERNEL);
+
+	__ondiemet_log_req_init(req, ONDIEMET_LOG_REQ, src, num, on_fini_cb, param);
+	return __ondiemet_log_req_enq(req);
+}
+
+/*int down_freezable_interruptible(struct semaphore *sem) */
+int down_freezable_interruptible(struct completion *comp)
+{
+
+	int ret;
+
+	freezer_do_not_count();
+	/* ret = down_interruptible(sem); */
+	ret = wait_for_completion_interruptible(comp);
+	freezer_count();
+
+	return ret;
+}
+
+struct ondiemet_log_req *__ondiemet_log_req_deq(void)
+{
+	struct ondiemet_log_req *ret_req;
+
+	/*if (down_freezable_interruptible(&ondiemet_log_req_q.new_evt_sema))*/
+	if (down_freezable_interruptible(&ondiemet_log_req_q.new_evt_comp))
+		return NULL;
+
+	mutex_lock(&ondiemet_log_req_q.lockq);
+	ret_req = list_entry(ondiemet_log_req_q.listq.next, struct ondiemet_log_req, list);
+	list_del_init(&ret_req->list);
+	mutex_unlock(&ondiemet_log_req_q.lockq);
+
+	return ret_req;
+}
+
+void __ondiemet_log_req_open(void)
+{
+	mutex_lock(&ondiemet_log_req_q.lockq);
+	ondiemet_log_req_q.closeq_flag = 0;
+	mutex_unlock(&ondiemet_log_req_q.lockq);
+}
+
+int __ondiemet_log_req_closed(void)
+{
+	int ret;
+
+	mutex_lock(&ondiemet_log_req_q.lockq);
+	ret = ondiemet_log_req_q.closeq_flag && list_empty(&ondiemet_log_req_q.listq);
+	mutex_unlock(&ondiemet_log_req_q.lockq);
+
+	return ret;
+}
+
+int __ondiemet_log_req_working(void)
+{
+	int ret;
+
+	mutex_lock(&ondiemet_log_req_q.lockq);
+	ret = !ondiemet_log_req_q.closeq_flag;
+	mutex_unlock(&ondiemet_log_req_q.lockq);
+
+	return ret;
+}
+
+static void *__ondiemet_trace_seq_next(struct seq_file *seqf, loff_t *offset)
+{
+	struct ondiemet_log_req *next_req;
+
+	if (ondiemet_trace_run == ONDIEMET_LOG_DEBUG_MODE)
+		pr_debug("[met] __ondiemet_trace_seq_next: pid: %d\n", current->pid);
+
+	if (__ondiemet_log_req_closed())
+		return NULL;
+
+	next_req = __ondiemet_log_req_deq();
+
+	if (next_req == NULL)
+		return NULL;
+
+	if (next_req->cmd_type == ONDIEMET_LOG_STOP) {
+		__ondiemet_log_req_fini(next_req);
+		return NULL;
+	}
+
+	return (void *) next_req;
+}
+
+struct mutex lock_trace_owner_pid;
+pid_t trace_owner_pid = PID_NONE;
+static void *ondiemet_trace_seq_start(struct seq_file *seqf, loff_t *offset)
+{
+	void *ret;
+
+	if (ondiemet_trace_run == ONDIEMET_LOG_DEBUG_MODE) {
+		pr_debug("[met] ondiemet_trace_seq_start: locked_pid: %d, pid: %d, offset: %llu\n",
+			 trace_owner_pid, current->pid, *offset);
+	}
+
+	if (!mutex_trylock(&lock_tracef))
+		return NULL;
+
+	mutex_lock(&lock_trace_owner_pid);
+	trace_owner_pid = current->pid;
+	current->flags |= PF_NOFREEZE;
+	mutex_unlock(&lock_trace_owner_pid);
+
+	ret = __ondiemet_trace_seq_next(seqf, offset);
+
+	return ret;
+}
+
+static void *ondiemet_trace_seq_next(struct seq_file *seqf, void *p, loff_t *offset)
+{
+	if (ondiemet_trace_run == ONDIEMET_LOG_DEBUG_MODE)
+		pr_debug("[met] ondiemet_trace_seq_next: pid: %d\n", current->pid);
+
+	(*offset)++;
+	return __ondiemet_trace_seq_next(seqf, offset);
+}
+
+static int ondiemet_trace_seq_show(struct seq_file *seqf, void *p)
+{
+	struct ondiemet_log_req *req = (struct ondiemet_log_req *) p;
+	size_t l_sz;
+	size_t r_sz;
+	struct ondiemet_log_req *l_req;
+	struct ondiemet_log_req *r_req;
+	int ret;
+
+	if (ondiemet_trace_run == ONDIEMET_LOG_DEBUG_MODE)
+		pr_debug("[met] ondiemet_trace_seq_show: pid: %d\n", current->pid);
+
+	if (req->num >= seqf->size) {
+		l_req = kmalloc(sizeof(*req), GFP_KERNEL);
+		r_req = req;
+
+		l_sz = seqf->size >> 1;
+		r_sz = req->num - l_sz;
+		__ondiemet_log_req_init(l_req, ONDIEMET_LOG_REQ, req->src, l_sz, NULL, NULL);
+		__ondiemet_log_req_init(r_req, ONDIEMET_LOG_REQ, req->src + l_sz,
+					r_sz, req->on_fini_cb, req->param);
+
+		__ondiemet_log_req_undeq(r_req);
+		req = l_req;
+
+		if (ondiemet_trace_run == ONDIEMET_LOG_DEBUG_MODE)
+			pr_debug("[met] ondiemet_trace_seq_show: split request\n");
+	}
+
+	ret = seq_write(seqf, req->src, req->num);
+
+	if (ret) {
+		/* check if seq_file buffer overflows */
+		if (seqf->count == seqf->size) {
+			__ondiemet_log_req_undeq(req);
+		} else {
+			if (ondiemet_trace_run == ONDIEMET_LOG_DEBUG_MODE)
+				pr_debug("[met] ondiemet_trace_seq_show: reading trace record failed, some data may be lost or corrupted\n");
+			__ondiemet_log_req_fini(req);
+		}
+		return 0;
+	}
+
+	__ondiemet_log_req_fini(req);
+	return 0;
+}
+
+static void ondiemet_trace_seq_stop(struct seq_file *seqf, void *p)
+{
+	if (ondiemet_trace_run == ONDIEMET_LOG_DEBUG_MODE)
+		pr_debug("[met] ondiemet_trace_seq_stop: pid: %d\n", current->pid);
+
+	mutex_lock(&lock_trace_owner_pid);
+	if (current->pid == trace_owner_pid) {
+		trace_owner_pid = PID_NONE;
+		mutex_unlock(&lock_tracef);
+	}
+	mutex_unlock(&lock_trace_owner_pid);
+}
+
+static const struct seq_operations ondiemet_trace_seq_ops = {
+	.start = ondiemet_trace_seq_start,
+	.next = ondiemet_trace_seq_next,
+	.stop = ondiemet_trace_seq_stop,
+	.show = ondiemet_trace_seq_show
+};
+
+static int ondiemet_trace_open(struct inode *inode, struct file *fp)
+{
+	return seq_open(fp, &ondiemet_trace_seq_ops);
+}
+
+static const struct file_operations ondiemet_trace_fops = {
+	.owner = THIS_MODULE,
+	.open = ondiemet_trace_open,
+	.read = seq_read,
+	.llseek = seq_lseek,
+	.release = seq_release
+};
+
+/*struct semaphore log_start_sema;*/
+struct completion log_start_comp;
+int ondiemet_log_manager_start(void)
+{
+	int ret;
+
+	/* TODO: choose a better return value */
+	if (__ondiemet_log_req_working())
+		return -EINVAL;
+
+	if (!__ondiemet_log_req_closed()) {
+		/*ret = down_killable(&log_start_sema);*/
+		ret = wait_for_completion_killable(&log_start_comp);
+		if (ret)
+			return ret;
+	}
+
+	__ondiemet_log_req_open();
+
+	return 0;
+}
+
+/*struct semaphore log_stop_sema;*/
+struct completion log_stop_comp;
+static void __log_stop_cb(const void *p)
+{
+	/* up(&log_start_sema); */
+	/* up(&log_stop_sema); */
+	complete(&log_start_comp);
+	complete(&log_stop_comp);
+}
+
+int ondiemet_log_manager_stop(void)
+{
+	int ret;
+	struct ondiemet_log_req *req;
+
+	/* TODO: choose a better return value */
+	if (__ondiemet_log_req_closed())
+		return -EINVAL;
+
+	req = kmalloc(sizeof(*req), GFP_KERNEL);
+
+	__ondiemet_log_req_init(req, ONDIEMET_LOG_STOP, NULL, 0, __log_stop_cb, NULL);
+	/*sema_init(&log_start_sema, 0); */
+	/*sema_init(&log_stop_sema, 0); */
+	init_completion(&log_start_comp);
+	init_completion(&log_stop_comp);
+
+	ret = __ondiemet_log_req_enq(req);
+	if (ret)
+		return ret;
+
+	/* XXX: blocking may be break by SIGKILL */
+	/*return down_killable(&log_stop_sema);*/
+	return wait_for_completion_killable(&log_stop_comp);
+}
+
+int ondiemet_parse_num(const char *str, unsigned int *value, int len)
+{
+	int ret;
+
+	if (len <= 0)
+		return -1;
+
+	if ((len > 2) &&
+	    ((str[0] == '0') &&
+	     ((str[1] == 'x') || (str[1] == 'X')))) {
+		ret = kstrtouint(str, 16, value);
+	} else {
+		ret = kstrtouint(str, 10, value);
+	}
+
+	if (ret != 0)
+		return -1;
+
+	return 0;
+}
+
+/* XXX: seq_file will output only when a page is filled */
+static ssize_t ondiemet_log_write_store(struct device *dev,
+					struct device_attribute *attr,
+					const char *buf,
+					size_t count)
+{
+	char *plog = NULL;
+
+	plog = kmalloc_array(count, sizeof(*plog), GFP_KERNEL);
+	if (!plog) {
+		/* TODO: use a better error code */
+		return -EINVAL;
+	}
+
+	memcpy(plog, buf, count);
+
+	mutex_lock(&dev->mutex);
+	ondiemet_log_req_enq(plog, strnlen(plog, count), kfree, plog);
+	mutex_unlock(&dev->mutex);
+
+	return count;
+}
+
+static DEVICE_ATTR(ondiemet_log_write, 0664, NULL, ondiemet_log_write_store);
+
+static ssize_t ondiemet_log_run_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int sz;
+
+	mutex_lock(&dev->mutex);
+	sz = snprintf(buf, PAGE_SIZE, "%d\n", ondiemet_trace_run);
+	mutex_unlock(&dev->mutex);
+	return sz;
+}
+
+static ssize_t ondiemet_log_run_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+	int ret;
+	int prev_run_state;
+
+	mutex_lock(&dev->mutex);
+
+	prev_run_state = ondiemet_trace_run;
+
+	if (kstrtoint(buf, 10, &ondiemet_trace_run) != 0)
+		return -EINVAL;
+
+	if (ondiemet_trace_run <= ONDIEMET_LOG_STOP_MODE) {
+		ondiemet_trace_run = ONDIEMET_LOG_STOP_MODE;
+		ondiemet_log_manager_stop();
+
+		if (prev_run_state == ONDIEMET_LOG_DEBUG_MODE)
+			device_remove_file(dev, &dev_attr_ondiemet_log_write);
+	} else if (ondiemet_trace_run == ONDIEMET_LOG_RUN_MODE) {
+		ondiemet_trace_run = ONDIEMET_LOG_RUN_MODE;
+		ondiemet_log_manager_start();
+
+		if (prev_run_state == ONDIEMET_LOG_DEBUG_MODE)
+			device_remove_file(dev, &dev_attr_ondiemet_log_write);
+	} else {
+		ondiemet_trace_run = ONDIEMET_LOG_DEBUG_MODE;
+		ondiemet_log_manager_start();
+
+		if (prev_run_state != ONDIEMET_LOG_DEBUG_MODE) {
+			ret = device_create_file(dev, &dev_attr_ondiemet_log_write);
+			if (ret != 0)
+				pr_debug("[met] can not create device node: ondiemet_log_write\n");
+		}
+	}
+
+	mutex_unlock(&dev->mutex);
+
+	return count;
+}
+
+static DEVICE_ATTR(ondiemet_log_run, 0660, ondiemet_log_run_show, ondiemet_log_run_store);
+
+int ondiemet_log_manager_init(struct device *dev)
+{
+	int ret;
+	struct dentry *d;
+
+	mutex_init(&lock_tracef);
+
+	__ondiemet_log_req_q_init(&ondiemet_log_req_q);
+
+	/*sema_init(&log_start_sema, 0);*/
+	/*sema_init(&log_stop_sema, 0);*/
+	init_completion(&log_start_comp);
+	init_completion(&log_stop_comp);
+
+	dbgfs_met_dir = debugfs_create_dir("ondiemet", NULL);
+	if (!dbgfs_met_dir) {
+		pr_debug("[met] can not create debugfs directory: met\n");
+		return -ENOMEM;
+	}
+
+	mutex_init(&lock_trace_owner_pid);
+
+	d = debugfs_create_file("trace", 0644, dbgfs_met_dir, NULL, &ondiemet_trace_fops);
+	if (!d) {
+		pr_debug("[met] can not create devide node in debugfs: ondiemet_trace\n");
+		return -ENOMEM;
+	}
+
+	ondiemet_trace_run = __ondiemet_log_req_working();
+	ret = device_create_file(dev, &dev_attr_ondiemet_log_run);
+	if (ret != 0) {
+		pr_debug("[met] can not create device node: ondiemet_log_run\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+int ondiemet_log_manager_uninit(struct device *dev)
+{
+	device_remove_file(dev, &dev_attr_ondiemet_log_run);
+	debugfs_remove_recursive(dbgfs_met_dir);
+	return 0;
+}
diff --git a/src/devtools/met-driver/4.14/common/ondiemet_log.h b/src/devtools/met-driver/4.14/common/ondiemet_log.h
new file mode 100644
index 0000000..cfe8be9
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/ondiemet_log.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _ONDIEMET_LOG_H_
+#define _ONDIEMET_LOG_H_
+
+#include <linux/device.h>
+
+int ondiemet_log_manager_init(struct device *dev);
+int ondiemet_log_manager_uninit(struct device *dev);
+int ondiemet_log_manager_start(void);
+/* Log manager can be reactivated by inserting new requests, i.e., calling ondiemet_log_req_enq() */
+int ondiemet_log_manager_stop(void);
+int ondiemet_log_req_enq(const char *src, size_t num, void (*on_fini_cb) (const void *p),
+			 const void *param);
+
+#endif				/* _ONDIEMET_LOG_H_ */
diff --git a/src/devtools/met-driver/4.14/common/power.c b/src/devtools/met-driver/4.14/common/power.c
new file mode 100644
index 0000000..c57d907
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/power.c
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/cpufreq.h>
+#include <trace/events/power.h>
+
+#include "power.h"
+#include "met_drv.h"
+#include "met_kernel_symbol.h"
+
+noinline void cpu_frequency(unsigned int frequency, unsigned int cpu_id)
+{
+	/* suppose this symbol is available, otherwise, the met.ko will fail */
+	met_cpu_frequency_symbol(frequency, cpu_id);
+}
+
+void force_power_log(int cpu)
+{
+	struct cpufreq_policy *p;
+
+	if (cpu == POWER_LOG_ALL) {
+		for_each_possible_cpu(cpu) {
+			p = cpufreq_cpu_get(cpu);
+			if (p != NULL) {
+				cpu_frequency(p->cur, cpu);
+				cpufreq_cpu_put(p);
+			} else {
+				cpu_frequency(0, cpu);
+			}
+		}
+	} else {
+		p = cpufreq_cpu_get(cpu);
+		if (p != NULL) {
+			cpu_frequency(p->cur, cpu);
+			cpufreq_cpu_put(p);
+		} else {
+			cpu_frequency(0, cpu);
+		}
+	}
+}
+
+void force_power_log_val(unsigned int frequency, int cpu)
+{
+	cpu_frequency(frequency, cpu);
+}
diff --git a/src/devtools/met-driver/4.14/common/power.h b/src/devtools/met-driver/4.14/common/power.h
new file mode 100644
index 0000000..8a0e8f0
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/power.h
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _POWER_H_
+#define _POWER_H_
+
+#define POWER_LOG_ALL	-1
+void force_power_log(int cpu);
+void force_power_log_val(unsigned int frequency, int cpu);
+
+#endif				/* _POWER_H_ */
diff --git a/src/devtools/met-driver/4.14/common/sampler.c b/src/devtools/met-driver/4.14/common/sampler.c
new file mode 100644
index 0000000..e47ef19
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/sampler.c
@@ -0,0 +1,649 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/sched/clock.h>
+#include <linux/kernel.h>
+#include <linux/cpuhotplug.h>
+#include <linux/cpu.h>
+#include <linux/sched.h>
+#include <linux/notifier.h>
+#include <linux/module.h>
+#include <linux/irq.h>
+#if 0				/* fix me later, no such file on current tree */
+#include <mach/mt_cpuxgpt.h>
+#endif
+#include <asm/arch_timer.h>
+
+#define	MET_USER_EVENT_SUPPORT
+#include "interface.h"
+#include "sampler.h"
+#include "met_struct.h"
+#include "util.h"
+#include "switch.h"
+#include "trace.h"
+#include "met_drv.h"
+#include "met_tag.h" /* for tracing_mark_write */
+
+#include "cpu_pmu.h"	/* for using kernel perf PMU driver */
+
+#include "met_kernel_symbol.h"
+
+#undef	DEBUG_CPU_NOTIFY
+/* #define DEBUG_CPU_NOTIFY */
+#if	defined(DEBUG_CPU_NOTIFY)
+#ifdef CONFIG_MET_MODULE
+#define	dbg_met_tag_oneshot	met_tag_oneshot_real
+#else
+#define	dbg_met_tag_oneshot	met_tag_oneshot
+#endif /* CONFIG_MET_MODULE */
+#else
+#define	dbg_met_tag_oneshot(class_id, name, value)	({ 0; })
+#endif
+
+static int start;
+static unsigned int online_cpu_map;
+static int curr_polling_cpu;
+static int cpu_related_cnt;
+
+static int preferred_cpu_list[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
+
+static int calc_preferred_polling_cpu(unsigned int cpu_map)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(preferred_cpu_list); i++) {
+		if (cpu_map & (1 << preferred_cpu_list[i]))
+			return preferred_cpu_list[i];
+	}
+
+	return -1;
+}
+
+static void wq_sync_buffer(struct work_struct *work)
+{
+	int cpu;
+	struct delayed_work *dw = container_of(work, struct delayed_work, work);
+	struct met_cpu_struct *met_cpu_ptr = container_of(dw, struct met_cpu_struct, dwork);
+
+	cpu = smp_processor_id();
+	if (met_cpu_ptr->cpu != cpu) {
+		/* panic("ERROR"); */
+		return;
+	}
+
+	/* sync_samples(cpu); */
+	/* don't re-add the work if we're shutting down */
+	if (met_cpu_ptr->work_enabled)
+		schedule_delayed_work(dw, DEFAULT_TIMER_EXPIRE);
+}
+
+static enum hrtimer_restart met_hrtimer_notify(struct hrtimer *hrtimer)
+{
+	int cpu;
+	int *count;
+	unsigned long long stamp;
+	struct met_cpu_struct *met_cpu_ptr = container_of(hrtimer, struct met_cpu_struct, hrtimer);
+	struct metdevice *c;
+#if	defined(DEBUG_CPU_NOTIFY)
+	char msg[32];
+#endif
+
+	cpu = smp_processor_id();
+#if	defined(DEBUG_CPU_NOTIFY)
+	{
+		char msg[32];
+
+		snprintf(msg, sizeof(msg), "met_hrtimer notify_%d", cpu);
+		dbg_met_tag_oneshot(0, msg, 1);
+	}
+#endif
+
+	if (met_cpu_ptr->cpu != cpu) {
+		/* panic("ERROR2"); */
+		dbg_met_tag_oneshot(0, msg, -3);
+		return HRTIMER_NORESTART;
+	}
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->ondiemet_mode == 0) {
+			if ((c->mode == 0) || (c->timed_polling == NULL))
+				continue;
+		} else if (c->ondiemet_mode == 1) {
+			if ((c->mode == 0) || (c->ondiemet_timed_polling == NULL))
+				continue;
+		} else if (c->ondiemet_mode == 2) {
+			if ((c->mode == 0) || ((c->timed_polling == NULL)
+					       && (c->ondiemet_timed_polling == NULL)))
+				continue;
+		}
+
+		count = per_cpu_ptr(c->polling_count, cpu);
+		if ((*count) > 0) {
+			(*count)--;
+			continue;
+		}
+
+		*(count) = c->polling_count_reload;
+
+		stamp = cpu_clock(cpu);
+
+		if (c->cpu_related == 0) {
+			if (cpu == curr_polling_cpu) {
+				if (c->ondiemet_mode == 0) {
+					c->timed_polling(stamp, 0);
+				} else if (c->ondiemet_mode == 1) {
+					c->ondiemet_timed_polling(stamp, 0);
+				} else if (c->ondiemet_mode == 2) {
+					if (c->timed_polling)
+						c->timed_polling(stamp, 0);
+					if (c->ondiemet_timed_polling)
+						c->ondiemet_timed_polling(stamp, 0);
+				}
+			}
+		} else {
+			if (c->ondiemet_mode == 0) {
+				c->timed_polling(stamp, cpu);
+			} else if (c->ondiemet_mode == 1) {
+				c->ondiemet_timed_polling(stamp, cpu);
+			} else if (c->ondiemet_mode == 2) {
+				if (c->timed_polling)
+					c->timed_polling(stamp, 0);
+				if (c->ondiemet_timed_polling)
+					c->ondiemet_timed_polling(stamp, 0);
+			}
+		}
+	}
+
+	if (met_cpu_ptr->hrtimer_online_check) {
+		online_cpu_map |= (1 << cpu);
+		met_cpu_ptr->hrtimer_online_check = 0;
+		dbg_met_tag_oneshot(0, "met_online check done", cpu);
+		if (calc_preferred_polling_cpu(online_cpu_map) == cpu) {
+			curr_polling_cpu = cpu;
+			dbg_met_tag_oneshot(0, "met_curr polling cpu", cpu);
+		}
+	}
+
+	if (met_cpu_ptr->work_enabled) {
+		hrtimer_forward_now(hrtimer, ns_to_ktime(DEFAULT_HRTIMER_EXPIRE));
+		dbg_met_tag_oneshot(0, msg, 0);
+		return HRTIMER_RESTART;
+	}
+	dbg_met_tag_oneshot(0, msg, 0);
+	return HRTIMER_NORESTART;
+}
+
+static void __met_hrtimer_start(void *unused)
+{
+	struct met_cpu_struct *met_cpu_ptr = NULL;
+	struct hrtimer *hrtimer = NULL;
+	/* struct delayed_work *dw; */
+	struct metdevice *c;
+
+	met_cpu_ptr = this_cpu_ptr(&met_cpu);
+#if	defined(DEBUG_CPU_NOTIFY)
+	{
+		char msg[32];
+
+		snprintf(msg, sizeof(msg), "met_hrtimer status_%d", met_cpu_ptr->cpu);
+		dbg_met_tag_oneshot(0, msg, 1);
+	}
+#endif
+	/*
+	 * do not open HRtimer when EVENT timer enable
+	 */
+	if (!(met_switch.mode & MT_SWITCH_EVENT_TIMER)) {
+		hrtimer = &met_cpu_ptr->hrtimer;
+		/* dw = &met_cpu_ptr->dwork; */
+
+		hrtimer_init(hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+		hrtimer->function = met_hrtimer_notify;
+	}
+
+	list_for_each_entry(c, &met_list, list) {
+		*(this_cpu_ptr(c->polling_count)) = 0;
+		if (c->ondiemet_mode == 0) {
+			if ((c->cpu_related) && (c->mode) && (c->start))
+				c->start();
+		} else if (c->ondiemet_mode == 1) {
+			if (((c->cpu_related)) && (c->mode) && (c->ondiemet_start))
+				c->ondiemet_start();
+		} else if (c->ondiemet_mode == 2) {
+			if ((c->cpu_related) && (c->mode) && (c->start))
+				c->start();
+			if (((c->cpu_related)) && (c->mode) && (c->ondiemet_start))
+				c->ondiemet_start();
+		}
+	}
+	/*
+	 * do not open HRtimer when EVENT timer enable
+	 */
+	if (!(met_switch.mode & MT_SWITCH_EVENT_TIMER)) {
+		if (DEFAULT_HRTIMER_EXPIRE) {
+			met_cpu_ptr->work_enabled = 1;
+			/* schedule_delayed_work_on(smp_processor_id(), dw, DEFAULT_TIMER_EXPIRE); */
+			hrtimer_start(hrtimer, ns_to_ktime(DEFAULT_HRTIMER_EXPIRE),
+				      HRTIMER_MODE_REL_PINNED);
+		}
+	}
+}
+
+static void __met_hrtimer_stop(void *unused)
+{
+	struct met_cpu_struct *met_cpu_ptr;
+	struct hrtimer *hrtimer;
+	/* struct delayed_work *dw; */
+	struct metdevice *c;
+
+	met_cpu_ptr = this_cpu_ptr(&met_cpu);
+#if	defined(DEBUG_CPU_NOTIFY)
+	{
+		char msg[32];
+
+		snprintf(msg, sizeof(msg), "met_hrtimer status_%d", met_cpu_ptr->cpu);
+		dbg_met_tag_oneshot(0, msg, 0);
+	}
+#endif
+	/*
+	 * do not open HRtimer when EVENT timer enable
+	 */
+	if (!(met_switch.mode & MT_SWITCH_EVENT_TIMER)) {
+		hrtimer = &met_cpu_ptr->hrtimer;
+		/* dw = &met_cpu_ptr->dwork; */
+
+		met_cpu_ptr->work_enabled = 0;
+		hrtimer_cancel(hrtimer);
+
+		/* cancel_delayed_work_sync(dw); */
+	}
+	list_for_each_entry(c, &met_list, list) {
+		if (c->ondiemet_mode == 0) {
+			if ((c->cpu_related) && (c->mode) && (c->stop))
+				c->stop();
+		} else if (c->ondiemet_mode == 1) {
+			if ((c->cpu_related) && (c->mode) && (c->ondiemet_stop))
+				c->ondiemet_stop();
+		} else if (c->ondiemet_mode == 2) {
+			if ((c->cpu_related) && (c->mode) && (c->stop))
+				c->stop();
+			if ((c->cpu_related) && (c->mode) && (c->ondiemet_stop))
+				c->ondiemet_stop();
+		}
+		*(this_cpu_ptr(c->polling_count)) = 0;
+	}
+}
+
+static int met_pmu_cpu_notify(enum met_action action, unsigned int cpu)
+{
+	struct met_cpu_struct *met_cpu_ptr;
+	struct delayed_work *dw;
+	int preferred_polling_cpu;
+	struct metdevice *c;
+
+	if (start == 0)
+		return NOTIFY_OK;
+
+#if	defined(DEBUG_CPU_NOTIFY)
+	{
+		char msg[32];
+
+		snprintf(msg, sizeof(msg), "met_cpu notify_%d", cpu);
+		dbg_met_tag_oneshot(0, msg, action);
+	}
+#elif	defined(PR_CPU_NOTIFY)
+	{
+		char msg[32];
+
+		if (met_cpu_notify) {
+			snprintf(msg, sizeof(msg), "met_cpu notify_%d", cpu);
+			dbg_met_tag_oneshot(0, msg, action);
+		}
+	}
+#endif
+
+	if (cpu < 0 || cpu >= NR_CPUS)
+		return NOTIFY_OK;
+
+	switch (action) {
+	case MET_CPU_ONLINE:
+		met_cpu_ptr = &per_cpu(met_cpu, cpu);
+		met_cpu_ptr->hrtimer_online_check = 1;
+		dbg_met_tag_oneshot(0, "met_online check", cpu);
+
+		if (cpu_related_cnt == 0) {
+			/*pr_info("%s, %d: curr_polling_cpu is alive = %d\n",
+			 *		__func__, __LINE__, online_cpu_map & (1 << curr_polling_cpu));
+			 */
+
+			online_cpu_map |= (1 << cpu);
+
+			/* check curr_polling_cpu is alive, if it is down,
+			 * start current cpu hrtimer, and change it to be currr_pollling_cpu
+			 */
+			if ((online_cpu_map & (1 << curr_polling_cpu)) == 0) {
+				met_smp_call_function_single_symbol(cpu, __met_hrtimer_start, NULL, 1);
+				curr_polling_cpu = cpu;
+			}
+		} else
+			met_smp_call_function_single_symbol(cpu, __met_hrtimer_start, NULL, 1);
+
+#ifdef CONFIG_CPU_FREQ
+		force_power_log(cpu);
+#endif
+		list_for_each_entry(c, &met_list, list) {
+			if (c->cpu_state_notify)
+				c->cpu_state_notify(cpu, action);
+		}
+		break;
+
+	case MET_CPU_OFFLINE:
+		list_for_each_entry(c, &met_list, list) {
+			if (c->cpu_state_notify)
+				c->cpu_state_notify(cpu, action);
+		}
+
+		online_cpu_map &= ~(1 << cpu);
+		dbg_met_tag_oneshot(0, "met_offline cpu", cpu);
+		if (cpu == curr_polling_cpu) {
+			/* pr_info("%s, %d: curr_polling_cpu %d is down\n",
+			 *		__func__, __LINE__, curr_polling_cpu);
+			 */
+			preferred_polling_cpu = calc_preferred_polling_cpu(online_cpu_map);
+			/* pr_info("%s, %d: preferred_polling_cpu = %d\n",
+			 *		__func__, __LINE__, preferred_polling_cpu);
+			 */
+			if (preferred_polling_cpu != -1) {
+				curr_polling_cpu = preferred_polling_cpu;
+				dbg_met_tag_oneshot(0, "met_curr polling cpu", curr_polling_cpu);
+
+				if (cpu_related_cnt == 0)
+					/* pr_info("%s, %d: start cpu %d hrtimer start\n",
+					 *		__func__, __LINE__, curr_polling_cpu);
+					 */
+					met_smp_call_function_single_symbol(curr_polling_cpu, __met_hrtimer_start, NULL, 1);
+			}
+		}
+
+		met_smp_call_function_single_symbol(cpu, __met_hrtimer_stop, NULL, 1);
+
+		met_cpu_ptr = &per_cpu(met_cpu, cpu);
+		dw = &met_cpu_ptr->dwork;
+		cancel_delayed_work_sync(dw);
+
+		/* sync_samples(cpu); */
+		break;
+	default:
+		list_for_each_entry(c, &met_list, list) {
+			if (c->cpu_state_notify)
+				c->cpu_state_notify(cpu, action);
+		}
+	}
+
+	return NOTIFY_OK;
+}
+
+static int _met_pmu_cpu_notify_online(unsigned int cpu)
+{
+	met_pmu_cpu_notify(MET_CPU_ONLINE, cpu);
+
+	return 0;
+}
+
+static int _met_pmu_cpu_notify_offline(unsigned int cpu)
+{
+	met_pmu_cpu_notify(MET_CPU_OFFLINE, cpu);
+
+	return 0;
+}
+
+int sampler_start(void)
+{
+	int ret, cpu;
+	struct met_cpu_struct *met_cpu_ptr;
+	struct metdevice *c;
+	int preferred_polling_cpu;
+
+	met_set_suspend_notify(0);
+
+#ifdef	CONFIG_CPU_FREQ
+	force_power_log(POWER_LOG_ALL);
+#endif
+
+	for_each_possible_cpu(cpu) {
+		met_cpu_ptr = &per_cpu(met_cpu, cpu);
+		met_cpu_ptr->work_enabled = 0;
+		met_cpu_ptr->hrtimer_online_check = 0;
+		hrtimer_init(&met_cpu_ptr->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+		met_cpu_ptr->hrtimer.function = met_hrtimer_notify;
+		INIT_DELAYED_WORK(&met_cpu_ptr->dwork, wq_sync_buffer);
+	}
+
+	start = 0;
+	ret = cpuhp_setup_state_nocalls(CPUHP_AP_ONLINE_DYN,
+						   "met:online",
+						   _met_pmu_cpu_notify_online,
+						   _met_pmu_cpu_notify_offline);	
+
+	list_for_each_entry(c, &met_list, list) {
+
+		if (try_module_get(c->owner) == 0)
+			continue;
+
+		if ((c->mode) && (c->cpu_related == 1))
+			cpu_related_cnt = 1;
+
+		if (c->ondiemet_mode == 0) {
+			if ((!(c->cpu_related)) && (c->mode) && (c->start))
+				c->start();
+			else if ((c->cpu_related) && (c->mode) && (c->uniq_start))
+				c->uniq_start();
+		} else if (c->ondiemet_mode == 1) {
+			if ((!(c->cpu_related)) && (c->mode) && (c->ondiemet_start))
+				c->ondiemet_start();
+		} else if (c->ondiemet_mode == 2) {
+			if ((!(c->cpu_related)) && (c->mode) && (c->start))
+				c->start();
+			else if ((c->cpu_related) && (c->mode) && (c->uniq_start))
+				c->uniq_start();
+
+			if ((!(c->cpu_related)) && (c->mode) && (c->ondiemet_start))
+				c->ondiemet_start();
+		}
+	}
+
+	get_online_cpus();
+	online_cpu_map = 0;
+	for_each_online_cpu(cpu) {
+		online_cpu_map |= (1 << cpu);
+	}
+	dbg_met_tag_oneshot(0, "met_online cpu map", online_cpu_map);
+
+	preferred_polling_cpu = calc_preferred_polling_cpu(online_cpu_map);
+	if (preferred_polling_cpu != -1)
+		curr_polling_cpu = preferred_polling_cpu;
+	dbg_met_tag_oneshot(0, "met_curr polling cpu", curr_polling_cpu);
+	start = 1;
+
+	if (cpu_related_cnt == 0)
+		met_smp_call_function_single_symbol(curr_polling_cpu, __met_hrtimer_start, NULL, 1);
+	else {
+		//on_each_cpu(__met_hrtimer_start, NULL, 1);
+		for_each_online_cpu(cpu) {
+			met_smp_call_function_single_symbol(cpu, __met_hrtimer_start, NULL, 1);
+		}
+	}
+	put_online_cpus();
+
+	return ret;
+}
+
+void sampler_stop(void)
+{
+	int cpu;
+	struct met_cpu_struct *met_cpu_ptr;
+	struct metdevice *c;
+	struct delayed_work *dw;
+
+
+	get_online_cpus();
+	//on_each_cpu(__met_hrtimer_stop, NULL, 1);
+	online_cpu_map = 0;
+	for_each_online_cpu(cpu) {
+		online_cpu_map |= (1 << cpu);
+	}
+
+	for_each_online_cpu(cpu) {
+		met_smp_call_function_single_symbol(cpu, __met_hrtimer_stop, NULL, 1);
+	}
+
+	/* for_each_online_cpu(cpu) { */
+	for_each_possible_cpu(cpu) {	/* Just for case */
+		met_cpu_ptr = &per_cpu(met_cpu, cpu);
+		dw = &met_cpu_ptr->dwork;
+		cancel_delayed_work_sync(dw);
+		/* sync_samples(cpu); */
+	}
+	start = 0;
+	put_online_cpus();
+
+	cpuhp_remove_state_nocalls(CPUHP_AP_ONLINE_DYN);
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->ondiemet_mode == 0) {
+			if ((!(c->cpu_related)) && (c->mode) && (c->stop))
+				c->stop();
+			else if ((c->cpu_related) && (c->mode) && (c->uniq_stop))
+				c->uniq_stop();
+		} else if (c->ondiemet_mode == 1) {
+			if ((!(c->cpu_related)) && (c->mode) && (c->ondiemet_stop))
+				c->ondiemet_stop();
+		} else if (c->ondiemet_mode == 2) {
+			if ((!(c->cpu_related)) && (c->mode) && (c->stop))
+				c->stop();
+			else if ((c->cpu_related) && (c->mode) && (c->uniq_stop))
+				c->uniq_stop();
+
+			if ((!(c->cpu_related)) && (c->mode) && (c->ondiemet_stop))
+				c->ondiemet_stop();
+		}
+		module_put(c->owner);
+	}
+
+	cpu_related_cnt = 0;
+}
+
+#if 0 /* cann't use static now */
+enum {
+	MET_SUSPEND = 1,
+	MET_RESUME = 2,
+};
+
+static noinline void tracing_mark_write(int op)
+{
+	switch (op) {
+	case MET_SUSPEND:
+		MET_TRACE("C|0|MET_SUSPEND|1");
+		break;
+	case MET_RESUME:
+		MET_TRACE("C|0|MET_SUSPEND|0");
+		break;
+	}
+}
+#endif
+
+int met_hrtimer_suspend(void)
+{
+	struct metdevice *c;
+
+	met_set_suspend_notify(1);
+	/* tracing_mark_write(MET_SUSPEND); */
+	tracing_mark_write(TYPE_MET_SUSPEND, 0, 0, 0, 0, 0);
+	if (start == 0)
+		return 0;
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->suspend)
+			c->suspend();
+	}
+
+	/* get current COUNT */
+	MET_TRACE("TS: %llu GPT: %llX", sched_clock(), arch_counter_get_cntvct());
+	return 0;
+}
+
+void met_hrtimer_resume(void)
+{
+	struct metdevice *c;
+
+	/* get current COUNT */
+	MET_TRACE("TS: %llu GPT: %llX", sched_clock(), arch_counter_get_cntvct());
+
+	/* tracing_mark_write(MET_RESUME); */
+	tracing_mark_write(TYPE_MET_RESUME, 0, 0, 0, 0, 0);
+	if (start == 0)
+		return;
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->resume)
+			c->resume();
+	}
+}
+
+/*
+ * event timer:
+ * register IRQ, sched_switch event to monitor Polling count
+ * count can be printed at any live cpu.
+ */
+void met_event_timer_notify(void)
+{
+	unsigned long long stamp;
+	struct metdevice *c;
+	int cpu = -1;
+
+	if (start == 0)
+		return;
+
+	cpu = smp_processor_id();
+	list_for_each_entry(c, &met_list, list) {
+		stamp = local_clock();
+
+		if (c->prev_stamp == 0)
+			c->prev_stamp = stamp;
+
+		/* Critical Section Start */
+		/* try spinlock to prevent a event print twice between config time interval */
+		if (!spin_trylock(&(c->my_lock)))
+			continue;
+
+		/*
+		 * DEFAULT_HRTIMER_EXPIRE (met_hrtimer_expire):
+		 * sample_rate == 0 --> always print
+		 * sample_rate == 1000 --> print interval larger than 1 ms
+		 */
+		if (DEFAULT_HRTIMER_EXPIRE == 0 || (stamp - c->prev_stamp) < DEFAULT_HRTIMER_EXPIRE) {
+			spin_unlock(&(c->my_lock));
+			continue;
+		}
+
+		c->prev_stamp = stamp;
+		spin_unlock(&(c->my_lock));
+		/* Critical Section End */
+
+		if ((c->mode == 0) || (c->timed_polling == NULL))
+			continue;
+
+		stamp = local_clock();
+		c->timed_polling(stamp, cpu);
+	}
+}
+
diff --git a/src/devtools/met-driver/4.14/common/sampler.h b/src/devtools/met-driver/4.14/common/sampler.h
new file mode 100644
index 0000000..6089c2d
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/sampler.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _SAMPLER_H_
+#define _SAMPLER_H_
+
+/*
+ * sampling rate: 1ms
+ * log generating rate: 10ms
+ */
+#if 0
+#define DEFAULT_TIMER_EXPIRE (HZ / 100)
+#define DEFAULT_HRTIMER_EXPIRE (TICK_NSEC / 10)
+#else
+extern int met_timer_expire;	/* in jiffies */
+extern int met_hrtimer_expire;	/* in us */
+#define DEFAULT_TIMER_EXPIRE (met_timer_expire)
+#define DEFAULT_HRTIMER_EXPIRE (met_hrtimer_expire)
+#endif
+/*
+ * sampling rate: 10ms
+ * log generating rate: 100ms
+ */
+/* #define DEFAULT_TIMER_EXPIRE (HZ / 10) */
+/* #define DEFAULT_HRTIMER_EXPIRE (TICK_NSEC / 1) */
+
+int met_hrtimer_start(void);
+void met_hrtimer_stop(void);
+int sampler_start(void);
+void sampler_stop(void);
+
+extern struct list_head met_list;
+extern void add_cookie(struct pt_regs *regs, int cpu);
+extern int met_hrtimer_suspend(void);
+extern void met_hrtimer_resume(void);
+extern void met_event_timer_notify(void);
+
+#ifdef CONFIG_CPU_FREQ
+#include "power.h"
+#endif
+
+#endif				/* _SAMPLER_H_ */
diff --git a/src/devtools/met-driver/4.14/common/spmtwam/ap/met_spmtwam.c b/src/devtools/met-driver/4.14/common/spmtwam/ap/met_spmtwam.c
new file mode 100644
index 0000000..7ce45ed
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/spmtwam/ap/met_spmtwam.c
@@ -0,0 +1,690 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/random.h>
+#include <linux/fs.h>
+#include <linux/ctype.h>
+
+#include <mtk_spm.h>
+#include "met_drv.h"
+#include "trace.h"
+#include "core_plf_init.h"
+#include "interface.h"
+#include "met_spmtwam.h"
+
+/* #define SPM_TWAM_DEBUG */
+
+#define INFRA_FMEM_DCM_BASE 0x1020E000
+#define INFRA_FMEM_DCM_SIZE 0x1000
+#define FMEM_MUX_ADDR_OFFSET 0x200
+#define FMEM_MUX_VALUE 0x780
+#define TWAM_DBG_SIG_BASE 0x0D0A0000
+#define TWAM_DBG_SIG_SIZE 0x100
+#define TWAM_DBG_SIG_OFFSET 0x94
+
+
+struct metdevice met_spmtwam;
+static struct kobject *kobj_spmtwam;
+/* static void __iomem *fmem_dcm_base; */
+static void __iomem *twam_dbg_signal_base;
+static struct met_spmtwam_para spmtwam_para[MAX_EVENT_COUNT];
+
+#ifdef SPM_TWAM_DEBUG
+static unsigned int debug_signal_val;
+#endif
+
+static struct twam_sig twamsig;
+static struct twam_sig montype;			/* b'00: rising, b'01: falling, b'10: high, b'11: low */
+static struct twam_sig dbgout;
+static int used_count;
+static int start;
+static bool twam_clock_mode = TWAM_SPEED_MODE;		/* true:speed mode, false:normal mode */
+static unsigned int window_len = 1300000;	/* 50 ms in 26 MHz */
+static unsigned int idle_sel;
+
+#define MONTYPE_SHOW_IMPLEMENT(num) \
+	do { \
+		int i; \
+		i = snprintf(buf, PAGE_SIZE, "%d\n", montype.sig ## num); \
+		return i; \
+	} while (0)
+
+#define MONTYPE_STORE_IMPLEMENT(num) \
+	do { \
+		int value; \
+		if ((n == 0) || (buf == NULL)) \
+			return -EINVAL; \
+		if (kstrtoint(buf, 10, &value) != 0) \
+			return -EINVAL; \
+		if (value < 0 || value > 3) \
+			return -EINVAL; \
+		montype.sig ## num= value; \
+		return n; \
+	} while (0)
+
+#define DBGOUT_SHOW_IMPLEMENT(num) \
+	do { \
+		int i; \
+		i = snprintf(buf, PAGE_SIZE, "%d\n", dbgout.sig ## num); \
+		return i; \
+	} while (0)
+
+#define DBGOUT_STORE_IMPLEMENT(num) \
+	do { \
+		int value; \
+		if ((n == 0) || (buf == NULL)) \
+			return -EINVAL; \
+		if (kstrtoint(buf, 10, &value) != 0) \
+			return -EINVAL; \
+		if (value < 0 || value > 127) \
+			return -EINVAL; \
+		dbgout.sig ## num = value; \
+		return n; \
+	} while (0)
+
+
+static ssize_t montype0_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	MONTYPE_SHOW_IMPLEMENT(0);
+}
+
+static ssize_t montype0_store(struct kobject *kobj,
+			struct kobj_attribute *attr,
+			const char *buf,
+			size_t n)
+{
+	MONTYPE_STORE_IMPLEMENT(0);
+}
+
+static ssize_t montype1_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	MONTYPE_SHOW_IMPLEMENT(1);
+}
+
+static ssize_t montype1_store(struct kobject *kobj,
+			struct kobj_attribute *attr,
+			const char *buf,
+			size_t n)
+{
+	MONTYPE_STORE_IMPLEMENT(1);
+}
+
+static ssize_t montype2_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	MONTYPE_SHOW_IMPLEMENT(2);
+}
+
+static ssize_t montype2_store(struct kobject *kobj,
+			struct kobj_attribute *attr,
+			const char *buf,
+			size_t n)
+{
+	MONTYPE_STORE_IMPLEMENT(2);
+}
+
+static ssize_t montype3_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	MONTYPE_SHOW_IMPLEMENT(3);
+}
+
+static ssize_t montype3_store(struct kobject *kobj,
+			struct kobj_attribute *attr,
+			const char *buf,
+			size_t n)
+{
+	MONTYPE_STORE_IMPLEMENT(3);
+}
+
+static ssize_t window_len_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	int i;
+
+	i = snprintf(buf, PAGE_SIZE, "%d\n", window_len);
+
+	return i;
+}
+
+static ssize_t window_len_store(struct kobject *kobj,
+			struct kobj_attribute *attr,
+			const char *buf,
+			size_t n)
+{
+	int value;
+
+	if ((n == 0) || (buf == NULL))
+		return -EINVAL;
+
+	if (kstrtoint(buf, 10, &value) != 0)
+		return -EINVAL;
+
+	if (value < 0)
+		return -EINVAL;
+
+	window_len = value;
+
+	return n;
+}
+
+static ssize_t dbgout0_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	DBGOUT_SHOW_IMPLEMENT(0);
+}
+
+static ssize_t dbgout0_store(struct kobject *kobj,
+			struct kobj_attribute *attr,
+			const char *buf,
+			size_t n)
+{
+	DBGOUT_STORE_IMPLEMENT(0);
+}
+
+static ssize_t dbgout1_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	DBGOUT_SHOW_IMPLEMENT(1);
+}
+
+static ssize_t dbgout1_store(struct kobject *kobj,
+			struct kobj_attribute *attr,
+			const char *buf,
+			size_t n)
+{
+	DBGOUT_STORE_IMPLEMENT(1);
+}
+
+static ssize_t dbgout2_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	DBGOUT_SHOW_IMPLEMENT(2);
+}
+
+static ssize_t dbgout2_store(struct kobject *kobj,
+			struct kobj_attribute *attr,
+			const char *buf,
+			size_t n)
+{
+	DBGOUT_STORE_IMPLEMENT(2);
+}
+
+static ssize_t dbgout3_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	DBGOUT_SHOW_IMPLEMENT(3);
+}
+
+static ssize_t dbgout3_store(struct kobject *kobj,
+			struct kobj_attribute *attr,
+			const char *buf,
+			size_t n)
+{
+	DBGOUT_STORE_IMPLEMENT(3);
+}
+
+#ifdef SPM_TWAM_DEBUG
+/* extern void *mt_spm_base_get(void); */
+static ssize_t debug_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	int ret;
+
+	ret = snprintf(buf, PAGE_SIZE, "0x%x\n", debug_signal_val);
+	ret += snprintf(buf+ret, PAGE_SIZE-ret, "%d, %d, %d, %d\n",
+			twam_sig_size[0], twam_sig_size[1], twam_sig_size[2], twam_sig_size[3]);
+/*	ret += snprintf(buf+ret, PAGE_SIZE-ret, "spm_base_addr: %p\n", mt_spm_base_get()); */
+
+	return ret;
+}
+static struct kobj_attribute debug_attr = __ATTR_RO(debug);
+#endif
+
+static struct kobj_attribute montype0_attr = __ATTR(montype0, 0664, montype0_show, montype0_store);
+static struct kobj_attribute montype1_attr = __ATTR(montype1, 0664, montype1_show, montype1_store);
+static struct kobj_attribute montype2_attr = __ATTR(montype2, 0664, montype2_show, montype2_store);
+static struct kobj_attribute montype3_attr = __ATTR(montype3, 0664, montype3_show, montype3_store);
+static struct kobj_attribute window_len_attr = __ATTR(window_len, 0664, window_len_show, window_len_store);
+static struct kobj_attribute dbgout0_attr = __ATTR(dbgout0, 0664, dbgout0_show, dbgout0_store);
+static struct kobj_attribute dbgout1_attr = __ATTR(dbgout1, 0664, dbgout1_show, dbgout1_store);
+static struct kobj_attribute dbgout2_attr = __ATTR(dbgout2, 0664, dbgout2_show, dbgout2_store);
+static struct kobj_attribute dbgout3_attr = __ATTR(dbgout3, 0664, dbgout3_show, dbgout3_store);
+
+
+/* create spmtwam related kobj node */
+#define KOBJ_ATTR_LIST \
+	do { \
+		KOBJ_ATTR_ITEM(montype0); \
+		KOBJ_ATTR_ITEM(montype1); \
+		KOBJ_ATTR_ITEM(montype2); \
+		KOBJ_ATTR_ITEM(montype3); \
+		KOBJ_ATTR_ITEM(window_len); \
+		KOBJ_ATTR_ITEM(dbgout0); \
+		KOBJ_ATTR_ITEM(dbgout1); \
+		KOBJ_ATTR_ITEM(dbgout2); \
+		KOBJ_ATTR_ITEM(dbgout3); \
+	} while (0)
+
+static int met_spmtwam_create(struct kobject *parent)
+{
+	int ret = 0;
+
+	kobj_spmtwam = parent;
+
+#define KOBJ_ATTR_ITEM(attr_name) \
+	do { \
+		ret = sysfs_create_file(kobj_spmtwam, &attr_name ## _attr.attr); \
+		if (ret != 0) { \
+			pr_notice("Failed to create " #attr_name " in sysfs\n"); \
+			return ret; \
+		} \
+	} while (0)
+	KOBJ_ATTR_LIST;
+#undef  KOBJ_ATTR_ITEM
+
+#ifdef SPM_TWAM_DEBUG
+	ret = sysfs_create_file(kobj_spmtwam, &debug_attr.attr);
+	if (ret != 0) {
+		pr_debug("Failed to create debug in sysfs\n");
+		return ret;
+	}
+#endif
+
+	/* init. */
+	montype.sig0 = 0x2;
+	montype.sig1 = 0x2;
+	montype.sig2 = 0x2;
+	montype.sig3 = 0x2;
+
+#if 0
+	dbgout.sig0 = 87;
+	dbgout.sig1 = 89;
+	dbgout.sig2 = 91;
+	dbgout.sig3 = 106;
+#endif
+
+	return ret;
+}
+
+static void met_spmtwam_delete(void)
+{
+#define KOBJ_ATTR_ITEM(attr_name) \
+	sysfs_remove_file(kobj_spmtwam, &attr_name ## _attr.attr)
+
+	if (kobj_spmtwam != NULL) {
+		KOBJ_ATTR_LIST;
+		kobj_spmtwam = NULL;
+	}
+#undef  KOBJ_ATTR_ITEM
+
+#ifdef SPM_TWAM_DEBUG
+	sysfs_remove_file(kobj_spmtwam, &debug_attr.attr);
+#endif
+}
+
+void ms_spmtwam(struct twam_sig *ts)
+{
+	switch (used_count) {
+	case 1:
+		MET_TRACE(MP_FMT1,
+			(ts->sig0));
+		break;
+	case 2:
+		MET_TRACE(MP_FMT2,
+			(ts->sig0),
+			(ts->sig1));
+		break;
+	case 3:
+		MET_TRACE(MP_FMT3,
+			(ts->sig0),
+			(ts->sig1),
+			(ts->sig2));
+		break;
+	case 4:
+		MET_TRACE(MP_FMT4,
+			(ts->sig0),
+			(ts->sig1),
+			(ts->sig2),
+			(ts->sig3));
+		break;
+	default:
+		MET_SPMTWAM_ERR("No assign profile event\n");
+		break;
+	}
+}
+
+static int reset_driver_stat(void)
+{
+	met_spmtwam.mode = 0;
+	used_count = 0;
+	start = 0;
+	return 0;
+}
+
+void spm_twam_enable_debug_out(struct twam_sig *sig, void *addr)
+{
+	int value = 0;
+
+	value |= ((1 << 31) | (sig->sig3 << 24) | (sig->sig2 << 16) | (sig->sig1 << 8) | (sig->sig0 << 0));
+
+#ifdef SPM_TWAM_DEBUG
+	debug_signal_val = value;
+#endif
+
+	writel(value, addr);
+}
+
+void spm_twam_disable_debug_out(void *addr)
+{
+	unsigned int value = 0;
+
+	value = readl(addr);
+	value &= (((unsigned int)(1 << 31)) - 1);
+
+#ifdef SPM_TWAM_DEBUG
+	debug_signal_val = value;
+#endif
+
+	writel(value, addr);
+}
+
+/*
+ * Called from "met-cmd --start"
+ */
+static void spmtwam_start(void)
+{
+	if (idle_sel == 3) {
+#if 0
+		/* swithc idle signal D id0 pimux to fmem */
+		if (fmem_dcm_base == NULL) {
+			fmem_dcm_base = ioremap_nocache(INFRA_FMEM_DCM_BASE, INFRA_FMEM_DCM_SIZE);
+			if (!fmem_dcm_base) {
+				pr_debug("fmem_dcm_base ioremap fail...");
+				return;
+			}
+
+			writel(FMEM_MUX_VALUE, (fmem_dcm_base + FMEM_MUX_ADDR_OFFSET));
+		}
+#endif
+	} else if (idle_sel == 2) {
+		/* debug signal mapping */
+		if (twam_dbg_signal_base == NULL) {
+			twam_dbg_signal_base = ioremap_nocache(TWAM_DBG_SIG_BASE, TWAM_DBG_SIG_SIZE);
+			if (!twam_dbg_signal_base) {
+				pr_debug("twam_dbg_signal_base ioremap fail...");
+				return;
+			}
+		}
+
+		spm_twam_enable_debug_out(&dbgout, (twam_dbg_signal_base + TWAM_DBG_SIG_OFFSET));
+	}
+
+	if (spm_twam_set_mon_type_symbol)
+		spm_twam_set_mon_type_symbol(&montype);
+	else {
+		MET_SPMTWAM_ERR("spm_twam_set_mon_type_symbol is NULL\n");
+		return;
+	}
+
+	if (spm_twam_set_window_length_symbol)
+		spm_twam_set_window_length_symbol(window_len);
+	else {
+		MET_SPMTWAM_ERR("spm_twam_set_window_length_symbol is NULL\n");
+		return;
+	}
+
+	if (spm_twam_set_idle_select_symbol)
+		spm_twam_set_idle_select_symbol(idle_sel);
+	else {
+		MET_SPMTWAM_ERR("spm_twam_set_idle_select_symbol is NULL\n");
+		return;
+	}
+
+	if (spm_twam_register_handler_symbol)
+		spm_twam_register_handler_symbol(ms_spmtwam);
+	else {
+		MET_SPMTWAM_ERR("spm_twam_register_handler_symbol is NULL\n");
+		return;
+	}
+
+	if (spm_twam_enable_monitor_symbol)
+		spm_twam_enable_monitor_symbol(&twamsig, twam_clock_mode);
+	else {
+		MET_SPMTWAM_ERR("spm_twam_enable_monitor_symbol is NULL\n");
+		return;
+	}
+
+	start = 1;
+}
+
+/*
+ * Called from "met-cmd --stop"
+ */
+static void spmtwam_stop(void)
+{
+	if (idle_sel == 3) {
+#if 0
+		if (fmem_dcm_base)
+			iounmap(fmem_dcm_base);
+#endif
+	} else if (idle_sel == 2) {
+		spm_twam_disable_debug_out(twam_dbg_signal_base + TWAM_DBG_SIG_OFFSET);
+
+		if (twam_dbg_signal_base) {
+			iounmap(twam_dbg_signal_base);
+			twam_dbg_signal_base = NULL;
+		}
+	}
+
+	if (spm_twam_register_handler_symbol)
+		spm_twam_register_handler_symbol(NULL);
+	else {
+		MET_SPMTWAM_ERR("spm_twam_register_handler_symbol is NULL\n");
+		return;
+	}
+
+	if (spm_twam_disable_monitor_symbol)
+		spm_twam_disable_monitor_symbol();
+	else {
+		MET_SPMTWAM_ERR("spm_twam_disable_monitor_symbol is NULL\n");
+		return;
+	}
+}
+
+static const char header[] = "met-info [000] 0.0: ms_spmtwam_header: ";
+
+/*
+ * It will be called back when run "met-cmd --extract" and mode is 1
+ */
+static int spmtwam_print_header(char *buf, int len)
+{
+    int i, total_size;
+	char idle_sig;
+    unsigned int event;
+
+    total_size = snprintf(buf, PAGE_SIZE, header);
+
+    for (i = 0; i < used_count; i++) {
+		idle_sig = spmtwam_para[i].idle_sig;
+		event = spmtwam_para[i].event;
+
+		total_size += snprintf(buf + total_size, PAGE_SIZE - total_size,
+                        "signal_%c_%02u,", idle_sig, event);
+    }
+
+	/* cut the last comma */
+	buf[total_size - 1] = '\n';
+
+    total_size += snprintf(buf + total_size, PAGE_SIZE - total_size, "met-info [000] 0.0: spmtwam_clock_mode: %s\n",
+            twam_clock_mode == TWAM_SPEED_MODE ? "speed" : "normal");
+
+#ifdef SPMTWAM_SINGLE_IDLE_SIGNAL
+    total_size += snprintf(buf + total_size, PAGE_SIZE - total_size, "met-info [000] 0.0: spmtwam_idle_signal_support: %s\n",
+					TWAM_SINGLE_IDLE_SIGNAL);
+#endif
+
+#ifdef SPMTWAM_MULTIPLE_IDLE_SIGNAL
+    total_size += snprintf(buf + total_size, PAGE_SIZE - total_size, "met-info [000] 0.0: spmtwam_idle_signal_support: %s\n",
+					TWAM_MULTIPLE_IDLE_SIGNAL);
+#endif
+
+    reset_driver_stat();
+    return total_size;
+}
+
+static int assign_slot(char idle_sig, unsigned int event)
+{
+	int i;
+	int sig2int;
+
+	if (used_count == MAX_EVENT_COUNT) {
+		PR_BOOTMSG("%s exceed max used event count\n", MET_SPMTWAM_TAG);
+		return -1;
+	}
+
+	/* check duplicated */
+	for (i = 0; i < used_count; i++) {
+		if ((spmtwam_para[i].idle_sig == idle_sig) &&
+			(spmtwam_para[i].event == event)) {
+			PR_BOOTMSG("%s input duplicated event %u\n", MET_SPMTWAM_TAG, event);
+			return -2;
+		}
+	}
+
+	/* check idle_sig range in a~d or A~D */
+	if (tolower(idle_sig) < 'a' || tolower(idle_sig) > 'd') {
+		PR_BOOTMSG("%s input idle signal %c is not in a~d range\n",
+					MET_SPMTWAM_TAG, idle_sig);
+		return -3;
+	}
+
+	/* check event no */
+	if (event > MAX_TWAM_EVENT_COUNT) {
+		PR_BOOTMSG("%s input event %u exceed max twam event %u\n",
+					MET_SPMTWAM_TAG, event, MAX_TWAM_EVENT_COUNT);
+		return -4;
+	}
+
+#ifdef SPMTWAM_SINGLE_IDLE_SIGNAL
+	if (used_count > 0) {
+		for (i = 0; i < used_count; i++) {
+			if (idle_sig != spmtwam_para[i].idle_sig) {
+				PR_BOOTMSG("%s %c idle signal is defferent previous, only support one idle signal\n",
+							MET_SPMTWAM_TAG, idle_sig);
+				return -5;
+			}
+		}
+	}
+#endif
+
+	spmtwam_para[used_count].idle_sig = idle_sig;
+	spmtwam_para[used_count].event = event;
+
+	sig2int = (int) (tolower(idle_sig) - 'a');
+#ifdef SPMTWAM_SINGLE_IDLE_SIGNAL
+	idle_sel = sig2int;
+#endif
+
+	switch (used_count) {
+	case 0:
+		twamsig.sig0 = event;
+		break;
+	case 1:
+		twamsig.sig1 = event;
+		break;
+	case 2:
+		twamsig.sig2 = event;
+		break;
+	case 3:
+		twamsig.sig3 = event;
+		break;
+	}
+
+	used_count++;
+
+	return 0;
+}
+
+static char help[] =
+	"  --spmtwam=clock:[speed|normal]	default is normal\n"
+	"					normal mode monitors 4 channels\n"
+	"					speed mode monitors 4 channels\n"
+	"  --spmtwam=signal:selx\n"
+	"					signal= a ~ d for idle signal A~D select\n"
+	"					selx= 0 ~ 31 for for channel event\n";
+
+/*
+ * Called from "met-cmd --help"
+ */
+static int spmtwam_print_help(char *buf, int len)
+{
+	return snprintf(buf, PAGE_SIZE, help);
+}
+
+static int spmtwam_process_argument(const char *arg, int len)
+{
+	if (start == 1)
+		reset_driver_stat();
+
+	if (strncmp(arg, "clock:", 6) == 0) {
+		if (strncmp(&(arg[6]), "speed", 5) == 0) {
+			twam_clock_mode = TWAM_SPEED_MODE;
+		} else if (strncmp(&(arg[6]), "normal", 6) == 0) {
+			twam_clock_mode = TWAM_NORMAL_MODE;
+		} else {
+			PR_BOOTMSG("%s unknown clock mode\n", MET_SPMTWAM_TAG);
+
+			goto error;
+		}
+	} else {
+		char signal;
+		int event;
+		int ret;
+
+		if (len < 3) {
+			PR_BOOTMSG("%s input parameter is too short !!!\n", MET_SPMTWAM_TAG);
+			goto error;
+		}
+
+		/*
+		 * parse arguments
+		 */
+		ret = sscanf(arg, "%c:%u", &signal, &event);
+		if (ret < 2) {
+			PR_BOOTMSG("%s input parameter is wrong format !!!\n", MET_SPMTWAM_TAG);
+			goto error;
+		}
+
+		if (assign_slot(signal, event) < 0) {
+			goto error;
+		}
+	}
+
+	met_spmtwam.mode = 1;
+	return 0;
+
+error:
+	reset_driver_stat();
+	return -1;
+}
+
+struct metdevice met_spmtwam = {
+	.name = "spmtwam",
+	.owner = THIS_MODULE,
+	.type = MET_TYPE_BUS,
+	.create_subfs = met_spmtwam_create,
+	.delete_subfs = met_spmtwam_delete,
+	.cpu_related = 0,
+	.start = spmtwam_start,
+	.stop = spmtwam_stop,
+	.reset = reset_driver_stat,
+	.print_help = spmtwam_print_help,
+	.print_header = spmtwam_print_header,
+	.process_argument = spmtwam_process_argument
+};
+EXPORT_SYMBOL(met_spmtwam);
diff --git a/src/devtools/met-driver/4.14/common/spmtwam/include/met_spmtwam.h b/src/devtools/met-driver/4.14/common/spmtwam/include/met_spmtwam.h
new file mode 100644
index 0000000..d889cfd
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/spmtwam/include/met_spmtwam.h
@@ -0,0 +1,30 @@
+#ifndef __MET_SPMTWAM_H__
+#define __MET_SPMTWAM_H__
+
+#define MET_SPMTWAM_TAG "[met_spmtwam]"
+#define MET_SPMTWAM_ERR(format, ...) \
+	do { \
+		MET_TRACE(MET_SPMTWAM_TAG format, ##__VA_ARGS__); \
+		PR_BOOTMSG(MET_SPMTWAM_TAG format, ##__VA_ARGS__); \
+	} while (0)
+
+#define TWAM_ENABLE true
+#define TWAM_DISABLE false
+#define	TWAM_SPEED_MODE true
+#define	TWAM_NORMAL_MODE false
+#define TWAM_DEBUG_SIG_ENABLE 1
+#define TWAM_DEBUG_SIG_DISABLE 0
+#define TWAM_SINGLE_IDLE_SIGNAL "single"
+#define TWAM_MULTIPLE_IDLE_SIGNAL "multiple"
+
+struct met_spmtwam_para {
+	char idle_sig;
+	int event;
+};
+
+
+/* event counters by HW spec */
+#define MAX_EVENT_COUNT 4
+#define MAX_TWAM_EVENT_COUNT 32
+
+#endif
diff --git a/src/devtools/met-driver/4.14/common/spmtwam/sspm/met_spmtwam.c b/src/devtools/met-driver/4.14/common/spmtwam/sspm/met_spmtwam.c
new file mode 100644
index 0000000..0542fff
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/spmtwam/sspm/met_spmtwam.c
@@ -0,0 +1,477 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/random.h>
+#include <linux/fs.h>
+#include <linux/ctype.h>
+
+#include <mtk_spm.h>
+#include "met_drv.h"
+#include "trace.h"
+#include "core_plf_init.h"
+#include "interface.h"
+#include "met_spmtwam.h"
+
+
+struct metdevice met_spmtwam;
+static struct kobject *kobj_spmtwam;
+
+static struct twam_cfg twam_config;
+static struct met_spmtwam_para spmtwam_para[MAX_EVENT_COUNT];
+
+static unsigned int twam_dbg_enable = TWAM_DEBUG_SIG_DISABLE;
+static bool twam_clock_mode = TWAM_SPEED_MODE;		/* true:speed mode, false:normal mode */
+static unsigned int window_len = 1300000;	/* 50 ms in 26 MHz */
+
+static int used_count;
+static int start;
+
+
+#define MONTYPE_SHOW_IMPLEMENT(cfg) \
+	do { \
+		int i; \
+		i = snprintf(buf, PAGE_SIZE, "%d\n", cfg.monitor_type); \
+		return i; \
+	} while (0)
+
+#define MONTYPE_STORE_IMPLEMENT(cfg) \
+	do { \
+		int value; \
+		if ((n == 0) || (buf == NULL)) \
+			return -EINVAL; \
+		if (kstrtoint(buf, 10, &value) != 0) \
+			return -EINVAL; \
+		if (value < 0 || value > 3) \
+			return -EINVAL; \
+		cfg.monitor_type = value; \
+		return n; \
+	} while (0)
+
+static ssize_t montype0_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	MONTYPE_SHOW_IMPLEMENT(twam_config.byte[0]);
+}
+
+static ssize_t montype0_store(struct kobject *kobj,
+			struct kobj_attribute *attr,
+			const char *buf,
+			size_t n)
+{
+	MONTYPE_STORE_IMPLEMENT(twam_config.byte[0]);
+}
+
+static ssize_t montype1_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	MONTYPE_SHOW_IMPLEMENT(twam_config.byte[1]);
+}
+
+static ssize_t montype1_store(struct kobject *kobj,
+			struct kobj_attribute *attr,
+			const char *buf,
+			size_t n)
+{
+	MONTYPE_STORE_IMPLEMENT(twam_config.byte[1]);
+}
+
+static ssize_t montype2_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	MONTYPE_SHOW_IMPLEMENT(twam_config.byte[2]);
+}
+
+static ssize_t montype2_store(struct kobject *kobj,
+			struct kobj_attribute *attr,
+			const char *buf,
+			size_t n)
+{
+	MONTYPE_STORE_IMPLEMENT(twam_config.byte[2]);
+}
+
+static ssize_t montype3_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	MONTYPE_SHOW_IMPLEMENT(twam_config.byte[3]);
+}
+
+static ssize_t montype3_store(struct kobject *kobj,
+			struct kobj_attribute *attr,
+			const char *buf,
+			size_t n)
+{
+	MONTYPE_STORE_IMPLEMENT(twam_config.byte[3]);
+}
+
+static ssize_t window_len_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	int i;
+
+	i = snprintf(buf, PAGE_SIZE, "%d\n", window_len);
+
+	return i;
+}
+
+static ssize_t window_len_store(struct kobject *kobj,
+			struct kobj_attribute *attr,
+			const char *buf,
+			size_t n)
+{
+	int value;
+
+	if ((n == 0) || (buf == NULL))
+		return -EINVAL;
+
+	if (kstrtoint(buf, 10, &value) != 0)
+		return -EINVAL;
+
+	if (value < 0)
+		return -EINVAL;
+
+	window_len = value;
+
+	return n;
+}
+
+static struct kobj_attribute montype0_attr = __ATTR(montype0, 0664, montype0_show, montype0_store);
+static struct kobj_attribute montype1_attr = __ATTR(montype1, 0664, montype1_show, montype1_store);
+static struct kobj_attribute montype2_attr = __ATTR(montype2, 0664, montype2_show, montype2_store);
+static struct kobj_attribute montype3_attr = __ATTR(montype3, 0664, montype3_show, montype3_store);
+static struct kobj_attribute window_len_attr = __ATTR(window_len, 0664, window_len_show, window_len_store);
+
+
+/* create spmtwam related kobj node */
+#define KOBJ_ATTR_LIST \
+	do { \
+		KOBJ_ATTR_ITEM(montype0); \
+		KOBJ_ATTR_ITEM(montype1); \
+		KOBJ_ATTR_ITEM(montype2); \
+		KOBJ_ATTR_ITEM(montype3); \
+		KOBJ_ATTR_ITEM(window_len); \
+	} while (0)
+
+static int met_spmtwam_create(struct kobject *parent)
+{
+	int i;
+	int ret = 0;
+
+	kobj_spmtwam = parent;
+
+#define KOBJ_ATTR_ITEM(attr_name) \
+	do { \
+		ret = sysfs_create_file(kobj_spmtwam, &attr_name ## _attr.attr); \
+		if (ret != 0) { \
+			pr_notice("Failed to create " #attr_name " in sysfs\n"); \
+			return ret; \
+		} \
+	} while (0)
+	KOBJ_ATTR_LIST;
+#undef  KOBJ_ATTR_ITEM
+
+	/* init. */
+	for (i = 0; i < MAX_EVENT_COUNT; i++)
+		twam_config.byte[i].monitor_type = 0x2;
+
+	return ret;
+}
+
+static void met_spmtwam_delete(void)
+{
+#define KOBJ_ATTR_ITEM(attr_name) \
+	sysfs_remove_file(kobj_spmtwam, &attr_name ## _attr.attr)
+
+	if (kobj_spmtwam != NULL) {
+		KOBJ_ATTR_LIST;
+		kobj_spmtwam = NULL;
+	}
+#undef  KOBJ_ATTR_ITEM
+}
+
+void ms_spmtwam(struct twam_cfg *cfg, struct twam_select *twam_sel)
+{
+	switch (used_count) {
+	case 1:
+		MET_TRACE(MP_FMT1,
+			(cfg->byte[0].id));
+		break;
+	case 2:
+		MET_TRACE(MP_FMT2,
+			(cfg->byte[0].id),
+			(cfg->byte[1].id));
+		break;
+	case 3:
+		MET_TRACE(MP_FMT3,
+			(cfg->byte[0].id),
+			(cfg->byte[1].id),
+			(cfg->byte[2].id));
+		break;
+	case 4:
+		MET_TRACE(MP_FMT4,
+			(cfg->byte[0].id),
+			(cfg->byte[1].id),
+			(cfg->byte[2].id),
+			(cfg->byte[3].id));
+		break;
+	default:
+		MET_SPMTWAM_ERR("No assign profile event\n");
+		break;
+	}
+}
+
+static int reset_driver_stat(void)
+{
+	met_spmtwam.mode = 0;
+	used_count = 0;
+	start = 0;
+
+	return 0;
+}
+
+#if 0
+void sspm_twam_debug(void)
+{
+/*
+	PR_BOOTMSG("[MET_TWAM] byte0 idle=%d, event=%d, type=%d \n",twam_config.byte[0].signal,twam_config.byte[0].id,twam_config.byte[0].monitor_type);
+	PR_BOOTMSG("[MET_TWAM] byte1 idle=%d, event=%d, type=%d \n",twam_config.byte[1].signal,twam_config.byte[1].id,twam_config.byte[1].monitor_type);
+	PR_BOOTMSG("[MET_TWAM] byte2 idle=%d, event=%d, type=%d \n",twam_config.byte[2].signal,twam_config.byte[2].id,twam_config.byte[2].monitor_type);
+	PR_BOOTMSG("[MET_TWAM] byte3 idle=%d, event=%d, type=%d \n",twam_config.byte[3].signal,twam_config.byte[3].id,twam_config.byte[3].monitor_type);
+	PR_BOOTMSG("[MET_TWAM] twam_clock_mode=%d, window_len=%d \n",twam_clock_mode, window_len);
+*/
+}
+#endif
+
+/*
+ * Called from "met-cmd --start"
+ */
+static void spmtwam_start(void)
+{
+	if (spm_twam_met_enable_symbol) {
+		if (true == spm_twam_met_enable_symbol()) {
+			if (spm_twam_enable_monitor_symbol)
+				spm_twam_enable_monitor_symbol(TWAM_DISABLE, TWAM_DEBUG_SIG_DISABLE, NULL);
+			else {
+				MET_SPMTWAM_ERR("spm_twam_enable_monitor_symbol is NULL\n");
+				return;
+			}
+		}
+	} else {
+		MET_SPMTWAM_ERR("spm_twam_met_enable_symbol is NULL\n");
+		return;
+	}
+
+	if (spm_twam_config_channel_symbol)
+		spm_twam_config_channel_symbol(&twam_config, twam_clock_mode, window_len);
+	else {
+		MET_SPMTWAM_ERR("spm_twam_config_channel_symbol is NULL\n");
+		return;
+	}
+
+	if (spm_twam_enable_monitor_symbol) {
+		spm_twam_enable_monitor_symbol(TWAM_ENABLE, twam_dbg_enable, ms_spmtwam);
+		/* sspm_twam_debug(); */
+	} else {
+		MET_SPMTWAM_ERR("spm_twam_enable_monitor_symbol is NULL\n");
+		return;
+	}
+
+	start = 1;
+}
+
+/*
+ * Called from "met-cmd --stop"
+ */
+static void spmtwam_stop(void)
+{
+	if (spm_twam_enable_monitor_symbol)
+		spm_twam_enable_monitor_symbol(TWAM_DISABLE, TWAM_DEBUG_SIG_DISABLE, NULL);
+	else {
+		MET_SPMTWAM_ERR("spm_twam_enable_monitor_symbol is NULL\n");
+		return;
+	}
+}
+
+static const char header[] = "met-info [000] 0.0: ms_spmtwam_header: ";
+
+/*
+ * It will be called back when run "met-cmd --extract" and mode is 1
+ */
+static int spmtwam_print_header(char *buf, int len)
+{
+    int i, total_size;
+	char idle_sig;
+    unsigned int event;
+
+    total_size = snprintf(buf, PAGE_SIZE, header);
+
+    for (i = 0; i < used_count; i++) {
+		idle_sig = spmtwam_para[i].idle_sig;
+		event = spmtwam_para[i].event;
+
+		total_size += snprintf(buf + total_size, PAGE_SIZE - total_size,
+                        "signal_%c_%02u,", idle_sig, event);
+    }
+
+	/* cut the last comma */
+	buf[total_size - 1] = '\n';
+
+    total_size += snprintf(buf + total_size, PAGE_SIZE - total_size, "met-info [000] 0.0: spmtwam_clock_mode: %s\n",
+            twam_clock_mode == TWAM_SPEED_MODE ? "speed" : "normal");
+
+#ifdef SPMTWAM_SINGLE_IDLE_SIGNAL
+    total_size += snprintf(buf + total_size, PAGE_SIZE - total_size, "met-info [000] 0.0: spmtwam_idle_signal_support: %s\n",
+					TWAM_SINGLE_IDLE_SIGNAL);
+#endif
+
+#ifdef SPMTWAM_MULTIPLE_IDLE_SIGNAL
+    total_size += snprintf(buf + total_size, PAGE_SIZE - total_size, "met-info [000] 0.0: spmtwam_idle_signal_support: %s\n",
+					TWAM_MULTIPLE_IDLE_SIGNAL);
+#endif
+
+    reset_driver_stat();
+    return total_size;
+}
+
+static int assign_slot_sspm_twam(char idle_sig, unsigned int event)
+{
+	int i;
+	int sig2int;
+
+	if (used_count == MAX_EVENT_COUNT) {
+		PR_BOOTMSG("%s exceed max used event count\n", MET_SPMTWAM_TAG);
+		return -1;
+	}
+
+	/* check duplicated */
+	for (i = 0; i < used_count; i++) {
+		if ((spmtwam_para[i].idle_sig == idle_sig) &&
+			(spmtwam_para[i].event == event)) {
+			PR_BOOTMSG("%s input duplicated event %u\n", MET_SPMTWAM_TAG, event);
+			return -2;
+		}
+	}
+
+	/* check idle_sig range in a~d or A~D */
+	if (tolower(idle_sig) < 'a' || tolower(idle_sig) > 'd') {
+		PR_BOOTMSG("%s input idle signal %c is not in a~d range\n",
+					MET_SPMTWAM_TAG, idle_sig);
+		return -3;
+	}
+
+	/* check event no */
+	if (event > MAX_TWAM_EVENT_COUNT) {
+		PR_BOOTMSG("%s input event %u exceed max twam event %u\n",
+					MET_SPMTWAM_TAG, event, MAX_TWAM_EVENT_COUNT);
+		return -4;
+	}
+
+#ifdef SPMTWAM_SINGLE_IDLE_SIGNAL
+	if (used_count > 0) {
+		for (i = 0; i < used_count; i++) {
+			if (idle_sig != spmtwam_para[i].idle_sig) {
+				PR_BOOTMSG("%s %c idle signal is defferent previous, only support one idle signal\n",
+							MET_SPMTWAM_TAG, idle_sig);
+				return -5;
+			}
+		}
+	}
+#endif
+
+	spmtwam_para[used_count].idle_sig = idle_sig;
+	spmtwam_para[used_count].event = event;
+
+	sig2int = (int) (tolower(idle_sig) - 'a');
+
+	twam_config.byte[used_count].id = event;
+	twam_config.byte[used_count].signal = sig2int;
+
+	used_count++;
+
+	return 0;
+}
+
+static char help[] =
+	"  --spmtwam=clock:[speed|normal]	default is normal\n"
+	"					normal mode monitors 4 channels\n"
+	"					speed mode monitors 4 channels\n"
+	"  --spmtwam=signal:selx\n"
+	"					signal= a ~ d for idle signal A~D select\n"
+	"					selx= 0 ~ 31 for for channel event\n";
+
+/*
+ * Called from "met-cmd --help"
+ */
+static int spmtwam_print_help(char *buf, int len)
+{
+	return snprintf(buf, PAGE_SIZE, help);
+}
+
+static int spmtwam_process_argument(const char *arg, int len)
+{
+	if (start == 1)
+		reset_driver_stat();
+
+	if (strncmp(arg, "clock:", 6) == 0) {
+		if (strncmp(&(arg[6]), "speed", 5) == 0) {
+			twam_clock_mode = TWAM_SPEED_MODE;
+		} else if (strncmp(&(arg[6]), "normal", 6) == 0) {
+			twam_clock_mode = TWAM_NORMAL_MODE;
+		} else {
+			PR_BOOTMSG("%s unknown clock mode\n", MET_SPMTWAM_TAG);
+
+			goto error;
+		}
+	} else {
+		char signal;
+		int event;
+		int ret;
+
+		if (len < 3) {
+			PR_BOOTMSG("%s input parameter is too short !!!\n", MET_SPMTWAM_TAG);
+			goto error;
+		}
+
+		/*
+		 * parse arguments
+		 */
+		ret = sscanf(arg, "%c:%u", &signal, &event);
+		if (ret < 2) {
+			PR_BOOTMSG("%s input parameter is wrong format !!!\n", MET_SPMTWAM_TAG);
+			goto error;
+		}
+
+		if (assign_slot_sspm_twam(signal, event) < 0) {
+			goto error;
+		}
+	}
+
+	met_spmtwam.mode = 1;
+	return 0;
+
+error:
+	reset_driver_stat();
+	return -1;
+}
+
+struct metdevice met_spmtwam = {
+	.name = "spmtwam",
+	.owner = THIS_MODULE,
+	.type = MET_TYPE_BUS,
+	.create_subfs = met_spmtwam_create,
+	.delete_subfs = met_spmtwam_delete,
+	.cpu_related = 0,
+	.start = spmtwam_start,
+	.stop = spmtwam_stop,
+	.reset = reset_driver_stat,
+	.print_help = spmtwam_print_help,
+	.print_header = spmtwam_print_header,
+	.process_argument = spmtwam_process_argument
+};
+EXPORT_SYMBOL(met_spmtwam);
diff --git a/src/devtools/met-driver/4.14/common/sspm/ondiemet_sspm.c b/src/devtools/met-driver/4.14/common/sspm/ondiemet_sspm.c
new file mode 100644
index 0000000..8c5711b
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/sspm/ondiemet_sspm.c
@@ -0,0 +1,494 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/delay.h>
+#include <linux/module.h> /* symbol_get */
+
+#define MET_USER_EVENT_SUPPORT
+#include "met_drv.h"
+
+#if defined(CONFIG_MTK_TINYSYS_SSPM_SUPPORT) && defined(ONDIEMET_SUPPORT)
+#include "ondiemet_sspm.h"
+
+#if defined(CONFIG_MTK_GMO_RAM_OPTIMIZE) || defined(CONFIG_MTK_MET_MEM_ALLOC)
+#ifdef CONFIG_MET_ARM_32BIT
+#include <asm/dma-mapping.h> /* arm_coherent_dma_ops */
+#else /* CONFIG_MET_ARM_32BIT */
+#include <linux/dma-mapping.h>
+#endif /* CONFIG_MET_ARM_32BIT */
+#else /* CONFIG_MTK_GMO_RAM_OPTIMIZE */
+#include "sspm_reservedmem.h"
+#include "sspm_reservedmem_define.h"
+#endif /* CONFIG_MTK_GMO_RAM_OPTIMIZE */
+
+dma_addr_t ondiemet_sspm_log_phy_addr;
+void *ondiemet_sspm_log_virt_addr;
+uint32_t ondiemet_sspm_log_size = 0x400000;
+
+/* SSPM_LOG_FILE 0 */
+/* SSPM_LOG_SRAM 1 */
+/* SSPM_LOG_DRAM 2 */
+int sspm_log_mode;
+/* SSPM_RUN_NORMAL mode 0 */
+/* SSPM_RUN_CONTINUOUS mode 1 */
+int sspm_run_mode;
+int met_sspm_log_discard = -1;
+int sspm_log_size = 500;
+
+int sspm_buffer_size;
+int sspm_buf_available;
+EXPORT_SYMBOL(sspm_buf_available);
+int sspm_buf_mapped = -1; /* get buffer by MET itself */
+
+static ssize_t sspm_buffer_size_show(struct device *dev, struct device_attribute *attr, char *buf);
+static DEVICE_ATTR(sspm_buffer_size, 0444, sspm_buffer_size_show, NULL);
+
+static ssize_t sspm_available_show(struct device *dev, struct device_attribute *attr, char *buf);
+static DEVICE_ATTR(sspm_available, 0444, sspm_available_show, NULL);
+
+static ssize_t sspm_log_discard_show(struct device *dev, struct device_attribute *attr, char *buf);
+static DEVICE_ATTR(sspm_log_discard, 0444, sspm_log_discard_show, NULL);
+
+static ssize_t sspm_log_mode_show(struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t sspm_log_mode_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
+static DEVICE_ATTR(sspm_log_mode, 0664, sspm_log_mode_show, sspm_log_mode_store);
+
+static ssize_t sspm_log_size_show(struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t sspm_log_size_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
+static DEVICE_ATTR(sspm_log_size, 0664, sspm_log_size_show, sspm_log_size_store);
+
+
+static ssize_t sspm_run_mode_show(struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t sspm_run_mode_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
+static DEVICE_ATTR(sspm_run_mode, 0664, sspm_run_mode_show, sspm_run_mode_store);
+
+static ssize_t sspm_modules_show(struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t sspm_modules_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
+static DEVICE_ATTR(sspm_modules, 0664, sspm_modules_show, sspm_modules_store);
+
+static ssize_t sspm_op_ctrl_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
+static DEVICE_ATTR(sspm_op_ctrl, 0220, NULL, sspm_op_ctrl_store);
+
+static ssize_t sspm_buffer_size_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int i;
+
+	mutex_lock(&dev->mutex);
+	i = snprintf(buf, PAGE_SIZE, "%d\n", sspm_buffer_size);
+	mutex_unlock(&dev->mutex);
+	return i;
+}
+
+static ssize_t sspm_available_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int i;
+
+	mutex_lock(&dev->mutex);
+	i = snprintf(buf, PAGE_SIZE, "%d\n", 1);
+	mutex_unlock(&dev->mutex);
+	return i;
+}
+
+static ssize_t sspm_log_discard_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int i;
+
+	mutex_lock(&dev->mutex);
+	i = snprintf(buf, PAGE_SIZE, "%d\n", met_sspm_log_discard);
+	mutex_unlock(&dev->mutex);
+	return i;
+}
+
+static ssize_t sspm_log_mode_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int i;
+
+	mutex_lock(&dev->mutex);
+	i = snprintf(buf, PAGE_SIZE, "%d\n", sspm_log_mode);
+	mutex_unlock(&dev->mutex);
+	return i;
+}
+
+static ssize_t sspm_log_mode_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+	int value;
+
+	if (kstrtoint(buf, 0, &value) != 0)
+		return -EINVAL;
+	mutex_lock(&dev->mutex);
+	sspm_log_mode = value;
+	mutex_unlock(&dev->mutex);
+	return count;
+}
+
+
+static ssize_t sspm_log_size_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int i;
+
+	mutex_lock(&dev->mutex);
+	i = snprintf(buf, PAGE_SIZE, "%d\n", sspm_log_size);
+	mutex_unlock(&dev->mutex);
+	return i;
+}
+
+static ssize_t sspm_log_size_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+	int value;
+
+	if (kstrtoint(buf, 0, &value) != 0)
+		return -EINVAL;
+	mutex_lock(&dev->mutex);
+	sspm_log_size = value;
+	mutex_unlock(&dev->mutex);
+	return count;
+}
+
+
+static ssize_t sspm_run_mode_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int i;
+
+	mutex_lock(&dev->mutex);
+	i = snprintf(buf, PAGE_SIZE, "%d\n", sspm_run_mode);
+	mutex_unlock(&dev->mutex);
+	return i;
+}
+
+static ssize_t sspm_run_mode_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+	int value;
+
+	if (kstrtoint(buf, 0, &value) != 0)
+		return -EINVAL;
+	mutex_lock(&dev->mutex);
+	sspm_run_mode = value;
+	mutex_unlock(&dev->mutex);
+	return count;
+}
+
+static ssize_t sspm_op_ctrl_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+	int value;
+
+	if (kstrtoint(buf, 0, &value) != 0)
+		return -EINVAL;
+	mutex_lock(&dev->mutex);
+	if (value == 1)
+		sspm_start();
+	else if (value == 2)
+		sspm_stop();
+	else if (value == 3)
+		sspm_extract();
+	else if (value == 4)
+		sspm_flush();
+	mutex_unlock(&dev->mutex);
+	return count;
+}
+
+static ssize_t sspm_modules_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int i;
+
+	mutex_lock(&dev->mutex);
+	i = snprintf(buf, PAGE_SIZE, "%x\n", ondiemet_module[ONDIEMET_SSPM]);
+	mutex_unlock(&dev->mutex);
+	return i;
+}
+
+static ssize_t sspm_modules_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+	uint32_t value;
+
+	if (kstrtouint(buf, 0, &value) != 0)
+		return -EINVAL;
+	mutex_lock(&dev->mutex);
+	ondiemet_module[ONDIEMET_SSPM] = value;
+	mutex_unlock(&dev->mutex);
+	return count;
+}
+
+int sspm_attr_init(struct device *dev)
+{
+	int ret;
+
+#if defined(CONFIG_MTK_GMO_RAM_OPTIMIZE) || defined(CONFIG_MTK_MET_MEM_ALLOC)
+#ifdef CONFIG_MET_ARM_32BIT
+	struct dma_map_ops *ops = (struct dma_map_ops *)symbol_get(arm_coherent_dma_ops);
+
+	if (ops && ops->alloc) {
+		dev->coherent_dma_mask = DMA_BIT_MASK(32);
+		ondiemet_sspm_log_virt_addr = ops->alloc(dev,
+						ondiemet_sspm_log_size,
+						&ondiemet_sspm_log_phy_addr,
+						GFP_KERNEL,
+						0);
+	}
+#else /* CONFIG_MET_ARM_32BIT */
+	/* dma_alloc */
+	ondiemet_sspm_log_virt_addr = dma_alloc_coherent(dev,
+			ondiemet_sspm_log_size,
+			&ondiemet_sspm_log_phy_addr,
+			GFP_KERNEL);
+#endif /* CONFIG_MET_ARM_32BIT */
+#else /* CONFIG_MTK_GMO_RAM_OPTIMIZE */
+
+	phys_addr_t (*sspm_reserve_mem_get_phys_sym)(unsigned int id) = NULL;
+	phys_addr_t (*sspm_reserve_mem_get_virt_sym)(unsigned int id) = NULL;
+	phys_addr_t (*sspm_reserve_mem_get_size_sym)(unsigned int id) = NULL;
+
+	sspm_reserve_mem_get_phys_sym = (phys_addr_t (*)(unsigned int id))symbol_get(sspm_reserve_mem_get_virt);
+	sspm_reserve_mem_get_virt_sym = (phys_addr_t (*)(unsigned int id))symbol_get(sspm_reserve_mem_get_phys);
+	sspm_reserve_mem_get_size_sym = (phys_addr_t (*)(unsigned int id))symbol_get(sspm_reserve_mem_get_size);
+	if (sspm_reserve_mem_get_phys_sym)
+		ondiemet_sspm_log_virt_addr = (void*)sspm_reserve_mem_get_virt(MET_MEM_ID);
+	if (sspm_reserve_mem_get_virt_sym)
+		ondiemet_sspm_log_phy_addr = sspm_reserve_mem_get_phys(MET_MEM_ID);
+	if (sspm_reserve_mem_get_size_sym)
+		ondiemet_sspm_log_size = sspm_reserve_mem_get_size(MET_MEM_ID);
+#endif /* CONFIG_MTK_GMO_RAM_OPTIMIZE */
+
+	ret = device_create_file(dev, &dev_attr_sspm_buffer_size);
+	if (ret != 0) {
+		pr_debug("can not create device file: sspm_buffer_size\n");
+		return ret;
+	}
+
+	ret = device_create_file(dev, &dev_attr_sspm_available);
+	if (ret != 0) {
+		pr_debug("can not create device file: sspm_available\n");
+		return ret;
+	}
+
+	ret = device_create_file(dev, &dev_attr_sspm_log_discard);
+	if (ret != 0) {
+		pr_debug("can not create device file: sspm_log_discard\n");
+		return ret;
+	}
+	ret = device_create_file(dev, &dev_attr_sspm_log_mode);
+	if (ret != 0) {
+		pr_debug("can not create device file: sspm_log_mode\n");
+		return ret;
+	}
+	ret = device_create_file(dev, &dev_attr_sspm_log_size);
+	if (ret != 0) {
+		pr_debug("can not create device file: sspm_log_size\n");
+		return ret;
+	}
+	ret = device_create_file(dev, &dev_attr_sspm_run_mode);
+	if (ret != 0) {
+		pr_debug("can not create device file: sspm_run_mode\n");
+		return ret;
+	}
+	ret = device_create_file(dev, &dev_attr_sspm_op_ctrl);
+	if (ret != 0) {
+		pr_debug("can not create device file: sspm_op_ctrl\n");
+		return ret;
+	}
+	ret = device_create_file(dev, &dev_attr_sspm_modules);
+	if (ret != 0) {
+		pr_debug("can not create device file: sspm_modules\n");
+		return ret;
+	}
+
+	if (ondiemet_sspm_log_virt_addr != NULL) {
+		start_sspm_ipi_recv_thread();
+		sspm_buf_available = 1;
+		sspm_buffer_size = ondiemet_sspm_log_size;
+	} else {
+		sspm_buf_available = 0;
+		sspm_buffer_size = -1;
+	}
+
+	return 0;
+}
+
+int sspm_attr_uninit(struct device *dev)
+{
+	/* dma_free */
+	if (ondiemet_sspm_log_virt_addr != NULL) {
+#if defined(CONFIG_MTK_GMO_RAM_OPTIMIZE) || defined(CONFIG_MTK_MET_MEM_ALLOC)
+#ifdef CONFIG_MET_ARM_32BIT
+		struct dma_map_ops *ops = (struct dma_map_ops *)symbol_get(arm_coherent_dma_ops);
+
+		if (ops && ops->free) {
+			ops->free(dev,
+				ondiemet_sspm_log_size,
+				ondiemet_sspm_log_virt_addr,
+				ondiemet_sspm_log_phy_addr,
+				0);
+		}
+#else /* CONFIG_MET_ARM_32BIT */
+		dma_free_coherent(dev,
+			ondiemet_sspm_log_size,
+			ondiemet_sspm_log_virt_addr,
+			ondiemet_sspm_log_phy_addr);
+#endif /* CONFIG_MET_ARM_32BIT */
+#endif /* CONFIG_MTK_GMO_RAM_OPTIMIZE */
+		ondiemet_sspm_log_virt_addr = NULL;
+		stop_sspm_ipi_recv_thread();
+	}
+
+	device_remove_file(dev, &dev_attr_sspm_buffer_size);
+	device_remove_file(dev, &dev_attr_sspm_available);
+	device_remove_file(dev, &dev_attr_sspm_log_discard);
+	device_remove_file(dev, &dev_attr_sspm_log_mode);
+	device_remove_file(dev, &dev_attr_sspm_log_size);
+	device_remove_file(dev, &dev_attr_sspm_run_mode);
+	device_remove_file(dev, &dev_attr_sspm_op_ctrl);
+	device_remove_file(dev, &dev_attr_sspm_modules);
+
+	return 0;
+}
+
+#if 0 /* move to sspm_attr_init() */
+void sspm_get_buffer_info(void)
+{
+	if (ondiemet_sspm_log_virt_addr != NULL) {
+		sspm_buf_available = 1;
+		sspm_buffer_size = ondiemet_sspm_log_size;
+	} else {
+		sspm_buf_available = 0;
+		sspm_buffer_size = -1;
+	}
+}
+#endif
+
+extern const char *met_get_platform_name(void);
+void sspm_start(void)
+{
+	int32_t ret = 0;
+	uint32_t rdata;
+	uint32_t ipi_buf[4];
+	const char* platform_name = NULL;
+	unsigned int platform_id = 0;
+	met_sspm_log_discard = -1;
+
+	/* clear DRAM buffer */
+	if (ondiemet_sspm_log_virt_addr != NULL)
+		memset_io((void *)ondiemet_sspm_log_virt_addr, 0, ondiemet_sspm_log_size);
+	else
+		return;
+
+	platform_name = met_get_platform_name();
+	if (platform_name) {
+		char buf[5];
+
+		memset(buf, 0x0, 5);
+		memcpy(buf, &platform_name[2], 4);
+		ret = kstrtouint(buf, 10, &platform_id);
+	}
+
+	/* send DRAM physical address */
+	ipi_buf[0] = MET_MAIN_ID | MET_BUFFER_INFO;
+	ipi_buf[1] = (unsigned int)ondiemet_sspm_log_phy_addr; /* address */
+	if (ret == 0)
+		ipi_buf[2] = platform_id;
+	else
+		ipi_buf[2] = 0;
+	ipi_buf[3] = 0;
+	ret = sspm_ipi_send_sync(IPI_ID_MET, IPI_OPT_WAIT, (void *)ipi_buf, 0, &rdata, 1);
+
+	/* start ondiemet now */
+	ipi_buf[0] = MET_MAIN_ID | MET_OP | MET_OP_START;
+	ipi_buf[1] = ondiemet_module[ONDIEMET_SSPM];
+	ipi_buf[2] = sspm_log_mode;
+	ipi_buf[3] = sspm_run_mode;
+	ret = sspm_ipi_send_sync(IPI_ID_MET, IPI_OPT_WAIT, (void *)ipi_buf, 0, &rdata, 1);
+}
+
+void sspm_stop(void)
+{
+	int32_t ret;
+	uint32_t rdata;
+	uint32_t ipi_buf[4];
+
+	if (sspm_buf_available == 1) {
+		ipi_buf[0] = MET_MAIN_ID|MET_OP|MET_OP_STOP;
+		ipi_buf[1] = 0;
+		ipi_buf[2] = 0;
+		ipi_buf[3] = 0;
+		ret = sspm_ipi_send_sync(IPI_ID_MET, IPI_OPT_WAIT, (void *)ipi_buf, 0, &rdata, 1);
+	}
+}
+
+void sspm_extract(void)
+{
+	int32_t ret;
+	uint32_t rdata;
+	uint32_t ipi_buf[4];
+	int32_t count;
+
+	count = 20;
+	if (sspm_buf_available == 1) {
+		while ((sspm_buffer_dumping == 1) && (count != 0)) {
+			msleep(50);
+			count--;
+		}
+		ipi_buf[0] = MET_MAIN_ID|MET_OP|MET_OP_EXTRACT;
+		ipi_buf[1] = 0;
+		ipi_buf[2] = 0;
+		ipi_buf[3] = 0;
+		ret = sspm_ipi_send_sync(IPI_ID_MET, IPI_OPT_WAIT, (void *)ipi_buf, 0, &rdata, 1);
+	}
+
+	if (sspm_run_mode == SSPM_RUN_NORMAL)
+		ondiemet_module[ONDIEMET_SSPM] = 0;
+}
+
+void sspm_flush(void)
+{
+	int32_t ret;
+	uint32_t rdata;
+	uint32_t ipi_buf[4];
+
+	if (sspm_buf_available == 1) {
+		ipi_buf[0] = MET_MAIN_ID|MET_OP|MET_OP_FLUSH;
+		ipi_buf[1] = 0;
+		ipi_buf[2] = 0;
+		ipi_buf[3] = 0;
+		ret = sspm_ipi_send_sync(IPI_ID_MET, IPI_OPT_WAIT, (void *)ipi_buf, 0, &rdata, 1);
+	}
+
+	if (sspm_run_mode == SSPM_RUN_NORMAL)
+		ondiemet_module[ONDIEMET_SSPM] = 0;
+}
+#else /* CONFIG_MTK_TINYSYS_SSPM_SUPPORT && ONDIEMET_SUPPORT */
+int sspm_buffer_size = -1;
+
+int sspm_attr_init(struct device *dev)
+{
+	return 0;
+}
+
+int sspm_attr_uninit(struct device *dev)
+{
+	return 0;
+}
+
+void sspm_start(void)
+{
+}
+
+void sspm_stop(void)
+{
+}
+
+void sspm_extract(void)
+{
+}
+
+void sspm_flush(void)
+{
+}
+
+#endif /* CONFIG_MTK_TINYSYS_SSPM_SUPPORT && ONDIEMET_SUPPORT */
diff --git a/src/devtools/met-driver/4.14/common/sspm/ondiemet_sspm.h b/src/devtools/met-driver/4.14/common/sspm/ondiemet_sspm.h
new file mode 100644
index 0000000..64dfcbd
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/sspm/ondiemet_sspm.h
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __ONDIEMET_SSPM_H
+#define __ONDIEMET_SSPM_H
+
+#if defined(CONFIG_MTK_TINYSYS_SSPM_SUPPORT) && defined(ONDIEMET_SUPPORT)
+#include "ondiemet.h"
+#include "sspm_ipi.h"
+#include <linux/dma-mapping.h>
+
+/* we may use IPI_ID_PLATFORM for mt6759 to reduce SRAM */
+#ifndef IPI_ID_MET
+/* #define IPI_ID_MET IPI_ID_TST1 */
+#define IPI_ID_MET IPI_ID_PLATFORM
+#endif
+
+/* MET IPI command definition: mbox 0 */
+/* main func ID: bit[31-24]; sub func ID: bit[23-18]; argu 0: bit[17-0] */
+#define MET_MAIN_ID_MASK        0xff000000 /* bit 31 - 24 */
+#define MET_SUB_ID_MASK         0x00fc0000 /* bit 23 - 18 */
+#define MET_ARGU0_MASK          0x0003ffff /* bit 17 - 0 */
+#define FUNC_BIT_SHIFT          18
+#define MID_BIT_SHIFT           9
+#define MET_MAIN_ID             0x06000000
+/* handle argument and attribute */
+#define PROCESS_ARGU            0x01
+#define PROCESS_ATTR            0x02
+#define MODULE_ID_MASK          0x3fe00 /* bit 9 - 17 */
+#define ARGUMENT_MASK           0x01ff  /* bit 0 - 8 */
+
+/* the following command is used for AP to MD32 */
+#define MET_OP            (1 << FUNC_BIT_SHIFT)
+/* argu 0: start: 0x01; stop: 0x02; extract: 0x03 */
+#define MET_OP_START        0x00000001
+#define MET_OP_STOP         0x00000002
+#define MET_OP_EXTRACT      0x00000003
+#define MET_OP_FLUSH        0x00000004
+#define MET_SR            (2 << FUNC_BIT_SHIFT) /* sample rate */
+#define MET_MODULE        (3 << FUNC_BIT_SHIFT) /* module enable/disable */
+#define MET_ARGU          (4 << FUNC_BIT_SHIFT) /* argument passing */
+#define MET_ATTR          (5 << FUNC_BIT_SHIFT) /* attribute passing */
+/* system memory information for on-die-met log data */
+#define MET_BUFFER_INFO   (6 << FUNC_BIT_SHIFT)
+#define MET_TIMESTAMP     (7 << FUNC_BIT_SHIFT) /* timestamp info */
+#define MET_GPT           (8 << FUNC_BIT_SHIFT) /* GPT counter reading */
+#define MET_REQ_AP2MD     (9 << FUNC_BIT_SHIFT) /* user defined command */
+#define MET_RESP_AP2MD    (10 << FUNC_BIT_SHIFT) /* may no need */
+/* mode: bit 15 - 0: */
+/*  Bit 0: MD32 SRAM mode; Bit 1: System DRAM mode */
+/*  value: 0: output to next level of storage; 1: loop in its own storage */
+#define MET_DATA_MODE     (11 << FUNC_BIT_SHIFT) /* log output mode */
+/* start/stop read data into MD32 SRAM buffer; both DMA and met_printf() */
+#define MET_DATA_OP       (12 << FUNC_BIT_SHIFT) /* data read operation */
+/* the following command is used for MD32 to AP */
+#define MET_DUMP_BUFFER   (13 << FUNC_BIT_SHIFT)
+#define MET_REQ_MD2AP     (14 << FUNC_BIT_SHIFT) /* user defined command */
+#define MET_CLOSE_FILE    (15 << FUNC_BIT_SHIFT) /* Inform to close the SD file */
+#define MET_RESP_MD2AP    (16 << FUNC_BIT_SHIFT)
+#define MET_RUN_MODE      (17 << FUNC_BIT_SHIFT)
+
+/* Note: the module ID and its bit pattern should be fixed as below */
+/* DMA based module first */
+enum {
+	MID_PMQOS = 0,
+	MID_VCORE_DVFS,
+	MID_EMI,
+	MID_THERMAL_CPU,
+	MID_WALL_TIME,
+	MID_CPU_DVFS,
+	MID_GPU_DVFS,
+	MID_PTPOD,
+	MID_SPM,
+	MID_PROFILE,
+	MID_MET_TAG,
+	MID_TS,
+	MID_TS_ISR,
+	MID_TS_LAST,
+	MID_TS_ISR_LAST,
+	MID_SRAM_INFO,
+	MID_MET_STOP,
+	MID_IOP_MON,
+	MID_CPU_INFO_MAPPING,
+	MID_SMI,
+
+	MID_COMMON = 0x1F
+};
+
+#define ID_PMQOS       (1 << MID_PMQOS)
+#define ID_SMI         (1 << MID_SMI)
+#define ID_EMI         (1 << MID_EMI)
+#define ID_THERMAL_CPU (1 << MID_THERMAL_CPU)
+#define ID_WALL_TIME   (1 << MID_WALL_TIME)
+#define ID_CPU_DVFS    (1 << MID_CPU_DVFS)
+#define ID_GPU_DVFS    (1 << MID_GPU_DVFS)
+#define ID_VCORE_DVFS  (1 << MID_VCORE_DVFS)
+#define ID_PTPOD       (1 << MID_PTPOD)
+#define ID_SPM         (1 << MID_SPM)
+#define ID_PROFILE     (1 << MID_PROFILE)
+#define ID_COMMON      (1 << MID_COMMON)
+#define ID_CPU_INFO_MAPPING      (1 << MID_CPU_INFO_MAPPING)
+#define ID_SMI      (1 << MID_SMI)
+
+extern void ondiemet_extract(void);
+extern void ondiemet_stop(void);
+extern void ondiemet_start(void);
+
+extern void start_sspm_ipi_recv_thread(void);
+extern void stop_sspm_ipi_recv_thread(void);
+
+extern unsigned int ondiemet_ipi_buf[];
+
+/* extern phys_addr_t ondiemet_sspm_log_phy_addr, ondiemet_sspm_log_virt_addr; */
+extern dma_addr_t ondiemet_sspm_log_phy_addr;
+
+extern void *ondiemet_sspm_log_virt_addr;
+extern uint32_t ondiemet_sspm_log_size;
+
+extern int ondiemet_attr_init(struct device *dev);
+extern int ondiemet_attr_uninit(struct device *dev);
+extern int met_sspm_log_discard;
+
+#define SSPM_LOG_FILE 0
+#define SSPM_LOG_SRAM 1
+#define SSPM_LOG_DRAM 2
+extern int sspm_log_mode;
+#define SSPM_RUN_NORMAL 0
+#define SSPM_RUN_CONTINUOUS 1
+extern int sspm_run_mode;
+
+/* extern void sspm_get_buffer_info(void); */
+extern int sspm_buf_available;
+extern int sspm_buffer_dumping;
+
+void sspm_flush(void);
+
+#endif /* CONFIG_MTK_TINYSYS_SSPM_SUPPORT && ONDIEMET_SUPPORT */
+#endif /* __ONDIEMET_SSPM_H */
diff --git a/src/devtools/met-driver/4.14/common/sspm/sspm_common.c b/src/devtools/met-driver/4.14/common/sspm/sspm_common.c
new file mode 100644
index 0000000..b3e1066
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/sspm/sspm_common.c
@@ -0,0 +1,266 @@
+#include <linux/string.h>

+#include <linux/slab.h>

+#include <linux/kernel.h>

+

+#include "met_drv.h"

+#include "ondiemet_sspm.h"

+

+

+struct sspm_met_evnet_header {

+	unsigned int rts_event_id;

+	char *rts_event_name;

+	char *chart_line_name;

+	char *key_list;

+};

+

+

+enum {

+	#ifdef MET_SSPM_RTS_EVNET

+	#undef MET_SSPM_RTS_EVNET

+	#endif

+	#define MET_SSPM_RTS_EVNET(rts_event_id, key_list) rts_event_id,

+	#include "met_sspm_rts_event.h"

+

+	/**********************/

+	CUR_MET_RTS_EVENT_NUM,

+	MAX_MET_RTS_EVENT_NUM = 128

+};

+

+

+struct sspm_met_evnet_header met_evnet_header[MAX_MET_RTS_EVENT_NUM] = {

+	#ifdef MET_SSPM_RTS_EVNET

+	#undef MET_SSPM_RTS_EVNET

+	#endif

+	#define MET_SSPM_RTS_EVNET(rts_event_id, key_list) {rts_event_id, #rts_event_id, #rts_event_id, key_list},

+	#include "met_sspm_rts_event.h"

+};

+

+

+static void ondiemet_sspm_start(void);

+static void ondiemet_sspm_stop(void);

+static int ondiemet_sspm_print_help(char *buf, int len);

+static int ondiemet_sspm_process_argument(const char *arg, int len);

+static int ondiemet_sspm_print_header(char *buf, int len);

+

+

+static unsigned int event_id_flag0;

+static unsigned int event_id_flag1;

+static unsigned int event_id_flag2;

+static unsigned int event_id_flag3;

+static char *update_rts_event_tbl[MAX_MET_RTS_EVENT_NUM];

+static char sspm_help[] = "  --sspm_common=rts_event_name\n";

+static char header[] = 	"met-info [000] 0.0: sspm_common_header: ";

+

+struct metdevice met_sspm_common = {

+	.name = "sspm_common",

+	.owner = THIS_MODULE,

+	.type = MET_TYPE_BUS,

+	.cpu_related = 0,

+	.ondiemet_mode = 1,

+	.ondiemet_start = ondiemet_sspm_start,

+	.ondiemet_stop = ondiemet_sspm_stop,

+	.ondiemet_process_argument = ondiemet_sspm_process_argument,

+	.ondiemet_print_help = ondiemet_sspm_print_help,

+	.ondiemet_print_header = ondiemet_sspm_print_header,

+};

+

+

+static int ondiemet_sspm_print_help(char *buf, int len)

+{

+	return snprintf(buf, PAGE_SIZE, sspm_help);

+}

+

+

+static int ondiemet_sspm_print_header(char *buf, int len)

+{

+	int i;

+	int write_len;

+	int flag = 0;

+	static int is_dump_header = 0;

+	static int read_idx = 0;

+

+	len = 0;

+	met_sspm_common.header_read_again = 0;

+	if (is_dump_header == 0) {

+		len = snprintf(buf, PAGE_SIZE, "%s", header);

+		is_dump_header = 1;

+	}

+

+	for (i=read_idx; i<MAX_MET_RTS_EVENT_NUM; i++) {

+		if (met_evnet_header[i].chart_line_name) {

+			if (i <32) {

+				flag = 1<<i;

+				flag = event_id_flag0 & flag;

+			} else if (i >=32 && i < 64) {

+				flag = 1<<(i-32);

+				flag = event_id_flag1 & flag;

+			} else if (i >=64 && i < 96) {

+				flag = 1<<(i-64);

+				flag = event_id_flag2 & flag;

+			} else if (i >=96 && i < 128) {

+				flag = 1<<(i-96);

+				flag = event_id_flag3 & flag;

+			}

+			if (flag == 0)

+				continue;

+

+			write_len = strlen(met_evnet_header[i].chart_line_name) + strlen(met_evnet_header[i].key_list) + 3;

+			if ((len+write_len) < PAGE_SIZE) {

+				len += snprintf(buf+len, PAGE_SIZE-len, "%u,%s,%s;",

+					met_evnet_header[i].rts_event_id,

+					met_evnet_header[i].chart_line_name,

+					met_evnet_header[i].key_list);

+			} else {

+				met_sspm_common.header_read_again = 1;

+				read_idx = i;

+				return len;

+			}

+		}

+	}

+

+	if (i == MAX_MET_RTS_EVENT_NUM) {

+		is_dump_header = 0;

+		read_idx = 0;

+		buf[len-1] = '\n';

+		for (i=0; i<MAX_MET_RTS_EVENT_NUM; i++) {

+			if (update_rts_event_tbl[i]) {

+				kfree(update_rts_event_tbl[i]);

+				update_rts_event_tbl[i] = NULL;

+			}

+		}

+		met_sspm_common.mode = 0;

+		event_id_flag0 = 0;

+		event_id_flag1 = 0;

+		event_id_flag2 = 0;

+		event_id_flag3 = 0;

+	}

+

+	return len;

+}

+

+

+static void ondiemet_sspm_start(void)

+{

+	if (sspm_buf_available == 0)

+		return ;

+

+	/* ID_COMMON = 1<<MID_COMMON */

+	ondiemet_module[ONDIEMET_SSPM] |= ID_COMMON;

+}

+

+

+static void ondiemet_sspm_stop(void)

+{

+	if (sspm_buf_available == 0)

+		return ;

+}

+

+

+static void update_event_id_flag(int event_id)

+{

+	unsigned int ipi_buf[4];

+	unsigned int rdata;

+	unsigned int res;

+

+	if (sspm_buf_available == 0)

+		return ;

+

+	/* main func ID: bit[31-24]; sub func ID: bit[23-18]; argu 0: bit[17-0]

+	   #define MET_MAIN_ID_MASK		0xff000000

+	   #define FUNC_BIT_SHIFT		  18

+	   #define MET_ARGU				(4 << FUNC_BIT_SHIFT)

+	   #define MID_BIT_SHIFT		   9

+	*/

+	if (event_id <32) {

+		event_id_flag0 |= 1<<event_id;

+		ipi_buf[0] = MET_MAIN_ID | MET_ARGU | MID_COMMON<<MID_BIT_SHIFT | 1;

+		ipi_buf[1] = 0;

+		ipi_buf[2] = event_id_flag0;

+		ipi_buf[3] = 0;

+		res = sspm_ipi_send_sync(IPI_ID_MET, IPI_OPT_WAIT, (void *)ipi_buf, 0, &rdata, 1);

+	} else if (event_id >=32 && event_id < 64) {

+		event_id_flag1 |= 1<<(event_id-32);

+		ipi_buf[0] = MET_MAIN_ID | MET_ARGU | MID_COMMON<<MID_BIT_SHIFT | 1;

+		ipi_buf[1] = 1;

+		ipi_buf[2] = event_id_flag1;

+		ipi_buf[3] = 0;

+		res = sspm_ipi_send_sync(IPI_ID_MET, IPI_OPT_WAIT, (void *)ipi_buf, 0, &rdata, 1);

+	} else if (event_id >=64 && event_id < 96) {

+		event_id_flag2 |= 1<<(event_id-64);

+		ipi_buf[0] = MET_MAIN_ID | MET_ARGU | MID_COMMON<<MID_BIT_SHIFT | 1;

+		ipi_buf[1] = 2;

+		ipi_buf[2] = event_id_flag2;

+		ipi_buf[3] = 0;

+		res = sspm_ipi_send_sync(IPI_ID_MET, IPI_OPT_WAIT, (void *)ipi_buf, 0, &rdata, 1);

+	} else if (event_id >=96 && event_id < 128) {

+		event_id_flag3 = 1<<(event_id-96);

+		ipi_buf[0] |= MET_MAIN_ID | MET_ARGU | MID_COMMON<<MID_BIT_SHIFT | 1;

+		ipi_buf[1] = 3;

+		ipi_buf[2] = event_id_flag3;

+		ipi_buf[3] = 0;

+		res = sspm_ipi_send_sync(IPI_ID_MET, IPI_OPT_WAIT, (void *)ipi_buf, 0, &rdata, 1);

+	}

+	met_sspm_common.mode = 1;

+}

+

+

+static char *strdup(const char *s)

+{

+	char *p = kmalloc(strlen(s) + 1, GFP_KERNEL);

+

+	if (p)

+		strcpy(p, s);

+	return p;

+}

+

+

+static int ondiemet_sspm_process_argument(const char *arg, int len)

+{

+	int i;

+	int rts_event_id = -1;

+	int res;

+	char *line;

+	char *token;

+

+	for (i=0; met_evnet_header[i].rts_event_name && i<MAX_MET_RTS_EVENT_NUM; i++) {

+		if (strcmp(met_evnet_header[i].rts_event_name, arg) == 0) {

+			rts_event_id = i;

+			break;

+		}

+	}

+	if (strstarts(arg, "update_rts_event_tbl")) {

+		char *ptr;

+

+		/* update_rts_event_tbl=rts_event_id;rts_event_name;chart_line_name;key_list*/

+		line = strdup(arg);

+		if (line == NULL)

+			return -1;

+		ptr = line;

+		token = strsep(&line, "=");

+

+		/* rts_event_id, */

+		token = strsep(&line, ";");

+		res = kstrtoint(token, 10, &rts_event_id);

+		met_evnet_header[rts_event_id].rts_event_id = rts_event_id;

+

+		/* rts_event_name */

+		token = strsep(&line, ";");

+		met_evnet_header[rts_event_id].rts_event_name = token;

+

+		/* chart_line_name */

+		token = strsep(&line, ";");

+		met_evnet_header[rts_event_id].chart_line_name = token;

+

+		/* key_list */

+		token = strsep(&line, ";\n");

+		met_evnet_header[rts_event_id].key_list = token;

+

+		update_rts_event_tbl[rts_event_id] = ptr;

+	}

+

+	if (rts_event_id >=0)

+		update_event_id_flag(rts_event_id);

+

+	return 0;

+}

+EXPORT_SYMBOL(met_sspm_common);

diff --git a/src/devtools/met-driver/4.14/common/sspm/sspm_ipi_handle.c b/src/devtools/met-driver/4.14/common/sspm/sspm_ipi_handle.c
new file mode 100644
index 0000000..161d752
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/sspm/sspm_ipi_handle.c
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kthread.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/signal.h>
+#include <linux/semaphore.h>
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/syscalls.h>
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/fcntl.h>
+/* #include <asm/uaccess.h> */
+#include <linux/uaccess.h>
+#include <linux/delay.h>
+
+#include "ondiemet_sspm.h"
+#include "ondiemet_log.h"
+#include "interface.h"
+
+static struct ipi_action ondiemet_sspm_isr;
+static uint32_t log_size;
+static uint32_t recv_buf[4];
+static struct task_struct *ondiemet_sspm_recv_task;
+static int sspm_ipi_thread_started;
+int sspm_buffer_dumping;
+int sspm_recv_thread_comp;
+
+void log_done_cb(const void *p)
+{
+	uint32_t ret;
+	uint32_t rdata = 0;
+	uint32_t ipi_buf[4];
+	uint32_t opt = (p != NULL);
+
+	if (opt == 0) {
+		ipi_buf[0] = MET_MAIN_ID | MET_RESP_AP2MD;
+		ipi_buf[1] = MET_DUMP_BUFFER;
+		ipi_buf[2] = 0;
+		ipi_buf[3] = 0;
+		ret = sspm_ipi_send_sync(IPI_ID_MET, IPI_OPT_WAIT, (void *)ipi_buf, 0, &rdata, 1);
+	}
+}
+
+int ondiemet_sspm_recv_thread(void *data)
+{
+	uint32_t rdata = 0, cmd, ret;
+	uint32_t ridx, widx, wlen;
+
+	ondiemet_sspm_isr.data = (void *)recv_buf;
+	ret = sspm_ipi_recv_registration(IPI_ID_TST1, &ondiemet_sspm_isr);
+	do {
+		sspm_ipi_recv_wait(IPI_ID_TST1);
+		if (sspm_recv_thread_comp == 1) {
+			while (!kthread_should_stop())
+				;
+			return 0;
+		}
+		cmd = recv_buf[0] & MET_SUB_ID_MASK;
+		switch (cmd) {
+		case MET_DUMP_BUFFER:	/* mbox 1: start index; 2: size */
+			sspm_buffer_dumping = 1;
+			ridx = recv_buf[1];
+			widx = recv_buf[2];
+			log_size = recv_buf[3];
+			sspm_ipi_send_ack(IPI_ID_TST1, &rdata);
+			if (widx < ridx) {	/* wrapping occurs */
+				wlen = log_size - ridx;
+				ondiemet_log_req_enq((char *)(ondiemet_sspm_log_virt_addr) + (ridx << 2),
+				wlen * 4, log_done_cb, (void *)1);
+				ondiemet_log_req_enq((char *)(ondiemet_sspm_log_virt_addr),
+						     widx * 4, log_done_cb, (void *)0);
+			} else {
+				wlen = widx - ridx;
+				ondiemet_log_req_enq((char *)(ondiemet_sspm_log_virt_addr) + (ridx << 2),
+				wlen * 4, log_done_cb, (void *)0);
+			}
+			break;
+		case MET_CLOSE_FILE:	/* no argument */
+			/* do close file */
+			ridx = recv_buf[1];
+			widx = recv_buf[2];
+			met_sspm_log_discard = recv_buf[3];
+			if (widx < ridx) {	/* wrapping occurs */
+				wlen = log_size - ridx;
+				ondiemet_log_req_enq((char *)(ondiemet_sspm_log_virt_addr) + (ridx << 2),
+				wlen * 4, log_done_cb, (void *)1);
+				ondiemet_log_req_enq((char *)(ondiemet_sspm_log_virt_addr),
+						     widx * 4, log_done_cb, (void *)1);
+			} else {
+				wlen = widx - ridx;
+				ondiemet_log_req_enq((char *)(ondiemet_sspm_log_virt_addr) + (ridx << 2),
+				wlen * 4, log_done_cb, (void *)1);
+			}
+			ret = ondiemet_log_manager_stop();
+			/* pr_debug("MET_CLOSE_FILE: ret=%d log_discard=%d\n", ret, met_sspm_log_discard); */
+			sspm_ipi_send_ack(IPI_ID_TST1, &rdata);
+			if (sspm_run_mode == SSPM_RUN_CONTINUOUS) {
+				/* clear the memory */
+				memset_io((void *)ondiemet_sspm_log_virt_addr, 0,
+					  ondiemet_sspm_log_size);
+				/* re-start ondiemet again */
+				sspm_start();
+			}
+			break;
+		case MET_RESP_MD2AP:
+			sspm_ipi_send_ack(IPI_ID_TST1, &rdata);
+			sspm_buffer_dumping = 0;
+			break;
+		default:
+			sspm_ipi_send_ack(IPI_ID_TST1, &rdata);
+			break;
+		}
+	} while (!kthread_should_stop());
+	return 0;
+}
+
+void start_sspm_ipi_recv_thread(void)
+{
+	if (sspm_ipi_thread_started != 1) {
+		sspm_recv_thread_comp = 0;
+		ondiemet_sspm_recv_task =
+		    kthread_run(ondiemet_sspm_recv_thread, NULL, "ondiemet_sspm_recv");
+		if (IS_ERR(ondiemet_sspm_recv_task))
+			pr_debug("MET: Can not create ondiemet_sspm_recv\n");
+		else
+			sspm_ipi_thread_started = 1;
+	}
+}
+
+void stop_sspm_ipi_recv_thread(void)
+{
+	if (ondiemet_sspm_recv_task) {
+		sspm_recv_thread_comp = 1;
+		sspm_ipi_recv_complete(IPI_ID_TST1);
+		kthread_stop(ondiemet_sspm_recv_task);
+		ondiemet_sspm_recv_task = NULL;
+		sspm_ipi_thread_started = 0;
+		sspm_ipi_recv_unregistration(IPI_ID_TST1);
+	}
+}
+
diff --git a/src/devtools/met-driver/4.14/common/sspm/sspm_met_smi.c b/src/devtools/met-driver/4.14/common/sspm/sspm_met_smi.c
new file mode 100644
index 0000000..f4e185b
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/sspm/sspm_met_smi.c
@@ -0,0 +1,529 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/ctype.h>
+#include <mt-plat/sync_write.h>
+#include <mt-plat/mtk_io.h>
+
+#include "met_drv.h"
+#include "trace.h"
+
+#include "mtk_typedefs.h"
+#include "sspm_mtk_smi.h"
+#include "sspm_met_smi.h"
+#include "sspm_met_smi_name.h"
+#include "core_plf_trace.h"
+#include "core_plf_init.h"
+#include "interface.h"
+#include "sspm/ondiemet_sspm.h"
+
+#define MET_SMI_DEBUG		1
+#define MET_SMI_BUF_SIZE	128
+#define MET_SMI_DEBUGBUF_SIZE	512
+#define NPORT_IN_PM		4
+
+// SMI Encode -- Master
+// bit15~bit16
+#define MET_SMI_BIT_REQ_LARB	15
+// bit13~bit14
+#define MET_SMI_BIT_REQ_COMM	13
+// bit12:Parallel Mode */
+#define MET_SMI_BIT_PM		12
+// bit9~bit8:Destination */
+#define MET_SMI_BIT_DST		8
+/* bit5~bit4:Request Type */
+#define MET_SMI_BIT_REQ		4
+/* bit3~bit0:Master */
+#define MET_SMI_BIT_MASTER	0
+
+// SMI Encode -- Metadata
+/* bit6~bit5:RW */
+#define MET_SMI_BIT_RW		5
+/* bit4~bit0:Port */
+#define MET_SMI_BIT_PORT	0
+
+/*
+*declare smi: larb0-larbn:
+*real define table in met_smi_name.h
+*/
+/*MET_SMI_DESC_DEFINE();*/
+/**/
+
+/*======================================================================*/
+/*	Global variable definitions					*/
+/*======================================================================*/
+#define MAX_CONFIG_ARRAY_SIZE 20
+struct metdevice met_sspm_smi;
+struct met_smi_conf smi_conf_array[MAX_CONFIG_ARRAY_SIZE];
+int smi_array_index;
+
+//static unsigned int smi_met_larb_number = SMI_LARB_NUMBER;
+static int count = SMI_LARB_NUMBER + SMI_COMM_NUMBER;
+static struct kobject *kobj_smi;
+
+/* Request type */
+static unsigned int larb_req_type = SMI_REQ_ALL;
+static unsigned int comm_req_type = SMI_REQ_ALL;
+
+/* Parallel mode */
+static unsigned int parallel_mode;
+
+/* Read/Write type in parallel mode */
+static int comm_pm_rw_type[SMI_COMM_NUMBER][NPORT_IN_PM];
+/* Error message */
+static char err_msg[MET_SMI_BUF_SIZE];
+static char debug_msg[MET_SMI_DEBUGBUF_SIZE];
+
+/*======================================================================*/
+/*	KOBJ Declarations						*/
+/*======================================================================*/
+/* KOBJ: larb_req_type */
+DECLARE_KOBJ_ATTR_INT(larb_req_type, larb_req_type);
+
+/* KOBJ : comm_req_type */
+DECLARE_KOBJ_ATTR_INT(comm_req_type, comm_req_type);
+
+/* KOBJ : enable_parallel_mode */
+DECLARE_KOBJ_ATTR_INT(enable_parallel_mode, parallel_mode);
+
+/* KOBJ : pm_rwtypeX */
+DECLARE_KOBJ_ATTR_STR_LIST_ITEM(
+	pm_rwtype,
+	KOBJ_ITEM_LIST(
+		{SMI_READ_ONLY,		"READ"},
+		{SMI_WRITE_ONLY,	"WRITE"}
+	)
+);
+DECLARE_KOBJ_ATTR_STR_LIST(pm_rwtype1, comm_pm_rw_type[0][0], pm_rwtype);
+DECLARE_KOBJ_ATTR_STR_LIST(pm_rwtype2, comm_pm_rw_type[0][1], pm_rwtype);
+DECLARE_KOBJ_ATTR_STR_LIST(pm_rwtype3, comm_pm_rw_type[0][2], pm_rwtype);
+DECLARE_KOBJ_ATTR_STR_LIST(pm_rwtype4, comm_pm_rw_type[0][3], pm_rwtype);
+
+/* KOBJ : count */
+DECLARE_KOBJ_ATTR_RO_INT(count, count);
+
+/* KOBJ : err_msg */
+DECLARE_KOBJ_ATTR_RO_STR(err_msg, err_msg);
+
+#define KOBJ_ATTR_LIST \
+do { \
+	KOBJ_ATTR_ITEM(larb_req_type); \
+	KOBJ_ATTR_ITEM(comm_req_type); \
+	KOBJ_ATTR_ITEM(enable_parallel_mode); \
+	KOBJ_ATTR_ITEM(pm_rwtype1); \
+	KOBJ_ATTR_ITEM(pm_rwtype2); \
+	KOBJ_ATTR_ITEM(pm_rwtype3); \
+	KOBJ_ATTR_ITEM(pm_rwtype4); \
+	KOBJ_ATTR_ITEM(count); \
+	KOBJ_ATTR_ITEM(err_msg); \
+} while (0)
+
+/*======================================================================*/
+/*	SMI Operations							*/
+/*======================================================================*/
+static void met_smi_debug(char *debug_log)
+{
+	MET_TRACE("%s", debug_log);
+}
+
+static int do_smi(void)
+{
+	return met_sspm_smi.mode;
+}
+
+static void smi_init_value(void)
+{
+	int i;
+
+	smi_array_index = 0;
+	for (i = 0; i < MAX_CONFIG_ARRAY_SIZE; i++) {
+		smi_conf_array[i].master = 0;
+		smi_conf_array[i].port[0] = -1;
+		smi_conf_array[i].port[1] = -1;
+		smi_conf_array[i].port[2] = -1;
+		smi_conf_array[i].port[3] = -1;
+		smi_conf_array[i].rwtype[0] = SMI_RW_ALL;
+		smi_conf_array[i].rwtype[1] = SMI_RW_ALL;
+		smi_conf_array[i].rwtype[2] = SMI_RW_ALL;
+		smi_conf_array[i].rwtype[3] = SMI_RW_ALL;
+		smi_conf_array[i].desttype = SMI_DEST_EMI;
+		smi_conf_array[i].reqtype = SMI_REQ_ALL;
+	}
+}
+
+static int smi_init(void)
+{
+	int i;
+
+	if (smi_array_index < MAX_CONFIG_ARRAY_SIZE) {
+		for (i = 0; i < (smi_array_index + 1); i++) {
+			snprintf(debug_msg, MET_SMI_DEBUGBUF_SIZE,
+				"===SMI config: parallel_mode = %d, master = %d, port0 = %d, port1 = %d, port2 = %d, port3 = %d, rwtype0 = %d, rwtype1 = %d, rwtype2 = %d, rwtype3 = %d, desttype = %d, reqtype(larb) = %d, reqtype(comm) = %d\n",
+				parallel_mode,
+				smi_conf_array[i].master,
+				smi_conf_array[i].port[0],
+				smi_conf_array[i].port[1],
+				smi_conf_array[i].port[2],
+				smi_conf_array[i].port[3],
+				smi_conf_array[i].rwtype[0],
+				smi_conf_array[i].rwtype[1],
+				smi_conf_array[i].rwtype[2],
+				smi_conf_array[i].rwtype[3],
+				smi_conf_array[i].desttype,
+				smi_conf_array[i].reqtype >> MET_SMI_BIT_REQ_LARB,
+				smi_conf_array[i].reqtype >> MET_SMI_BIT_REQ_COMM);
+			met_smi_debug(debug_msg);
+		}
+	} else {
+		met_smi_debug("smi_init() FAIL : smi_array_index overflow\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+static int met_smi_create(struct kobject *parent)
+{
+#define	KOBJ_ATTR_ITEM(attr_name) \
+do { \
+	ret = sysfs_create_file(kobj_smi, &attr_name##_attr.attr); \
+	if (ret != 0) { \
+		pr_debug("Failed to create " #attr_name " in sysfs\n"); \
+		return ret; \
+	} \
+} while (0)
+
+	int	j;
+	int	ret = 0;
+
+	pr_debug(" met_smi_create\n  met_smi_create\n  met_smi_create\n  met_smi_create\n  met_smi_create\n  met_smi_create\n  met_smi_create\n  met_smi_create\n");
+
+	/* Init. */
+
+	smi_init_value();
+
+	larb_req_type = SMI_REQ_ALL;
+	comm_req_type = SMI_REQ_ALL;
+	parallel_mode = 0;
+
+	for (j = 0; j < NPORT_IN_PM; j++)
+		comm_pm_rw_type[0][j] = SMI_READ_ONLY;
+
+	kobj_smi = parent;
+
+	KOBJ_ATTR_LIST;
+
+	return ret;
+
+#undef	KOBJ_ATTR_ITEM
+}
+
+void met_smi_delete(void)
+{
+#define	KOBJ_ATTR_ITEM(attr_name) \
+	sysfs_remove_file(kobj_smi, &attr_name##_attr.attr)
+
+
+	if (kobj_smi != NULL) {
+		KOBJ_ATTR_LIST;
+		kobj_smi = NULL;
+	}
+
+#undef	KOBJ_ATTR_ITEM
+}
+
+static void met_smi_start(void)
+{
+	if (do_smi()) {
+		if (smi_init() != 0) {
+			met_sspm_smi.mode = 0;
+			smi_init_value();
+			return;
+		}
+	}
+}
+
+static void met_smi_stop(void)
+{
+	int j;
+
+	if (do_smi()) {
+
+		/* Reset */
+		smi_init_value();
+		
+		larb_req_type = SMI_REQ_ALL;
+		comm_req_type = SMI_REQ_ALL;
+		parallel_mode = 0;
+		
+		for (j = 0; j < NPORT_IN_PM; j++)
+			comm_pm_rw_type[0][j] = SMI_READ_ONLY;
+		
+		
+		met_sspm_smi.mode = 0;
+	}
+	return ;
+}
+
+static char help[] =
+"  --smi=master:port:rw:dest:bus         monitor specified SMI banwidth\n"
+"  --smi=master:p1[:p2][:p3][:p4]        monitor parallel mode\n";
+
+static int smi_print_help(char *buf, int len)
+{
+	return snprintf(buf, PAGE_SIZE, "%s", help);
+}
+
+static int get_num(const char *__restrict__ dc, int *pValue)
+{
+	int	value = 0, digit;
+	int	i = 0;
+
+	while (((*dc) >= '0') && ((*dc) <= '9')) {
+		digit = (int)(*dc - '0');
+		value = value * 10 + digit;
+		dc++;
+		i++;
+	}
+
+	if (i == 0)
+		return 0;
+	*pValue = value;
+
+	return i;
+}
+
+/*
+ * There are serveal cases as follows:
+ *
+ * 1. "met-cmd --start --smi=master:port:rwtype:desttype:bustype" => Can assign multiple master
+ * 2. "met-cmd --start --smi=master:port[:port1][:port2][:port3]" ==> parael mode
+ *
+ */
+static int smi_process_argument(const char *__restrict__ arg, int len)
+{
+	int	args[5];
+	int	i, array_index;
+	int	idx;
+	unsigned int smi_conf_index = 0;
+	struct met_smi_conf smi_conf;
+
+	uint32_t ipi_buf[4];
+	uint32_t ret;
+	uint32_t rdata;
+	uint16_t sspm_master = 0;
+	uint32_t sspm_meta = 0;
+
+	if (len < 3)
+		return -1;
+
+	/*reset local config structure*/
+	memset(err_msg, 0, MET_SMI_BUF_SIZE);
+	for (i = 0; i < 4; i++) {
+		smi_conf.port[i] = -1;
+		smi_conf.rwtype[i] = SMI_RW_ALL;
+	}
+	smi_conf.master = 0;
+	smi_conf.reqtype = SMI_REQ_ALL;
+	smi_conf.desttype = SMI_DEST_EMI;
+
+	if (met_sspm_smi.mode != 0 && met_sspm_smi.mode != 1)
+		return -1;
+
+	/*
+	 * parse arguments
+	 * arg[0] = master
+	 * arg[1] = port or port1
+	 * arg[2] = rwtype or port2
+	 * arg[3] = desttype or port3
+	 * arg[4] = bustype or port4
+	 */
+	for (i = 0; i < ARRAY_SIZE(args); i++)
+		args[i] = -1;
+	idx = 0;
+	for (i = 0; i < ARRAY_SIZE(args); i++) {
+		ret = get_num(&(arg[idx]), &(args[i]));
+		if (ret == 0)
+			break;
+		idx += ret;
+		if (arg[idx] != ':')
+			break;
+		idx++;
+	}
+
+	pr_debug("===SMI process argu: args[0](%d), args[1](%d), args[2](%d), args[3](%d), args[4](%d)\n",
+		args[0],
+		args[1],
+		args[2],
+		args[3],
+		args[4]);
+
+	/*fill local config structure*/
+	smi_conf.master = args[0];
+	smi_conf.reqtype = (larb_req_type << MET_SMI_BIT_REQ_LARB) | (comm_req_type << MET_SMI_BIT_REQ_COMM);
+	if (parallel_mode == 0) {			/*legacy mode*/
+		smi_conf.rwtype[0] = args[2];
+		smi_conf.desttype = args[3];
+		smi_conf.port[0] = args[1];
+	} else {							/*parallel mode*/
+		smi_conf.desttype = SMI_DEST_EMI;
+		for (i = 0; i < 4; i++) {
+			if (args[i+1] < 0)
+				break;
+			smi_conf.port[i] = args[i+1];
+			smi_conf.rwtype[i] = comm_pm_rw_type[0][i];
+		}
+	}
+
+/*debug log to ftrace*/
+#ifdef MET_SMI_DEBUG
+	snprintf(debug_msg, MET_SMI_DEBUGBUF_SIZE,
+		"(argu)===SMI process argu Master[%d]: parallel_mode = %d, master = %d, port0 = %d, port1 = %d, port2 = %d, port3 = %d, rwtype0 = %d, rwtype1 = %d, rwtype2 = %d, rwtype3 = %d, desttype = %d, reqtype(larb) = %d, reqtype(comm) = %d\n",
+		args[0],
+		parallel_mode,
+		smi_conf.master,
+		smi_conf.port[0],
+		smi_conf.port[1],
+		smi_conf.port[2],
+		smi_conf.port[3],
+		smi_conf.rwtype[0],
+		smi_conf.rwtype[1],
+		smi_conf.rwtype[2],
+		smi_conf.rwtype[3],
+		smi_conf.desttype,
+		(smi_conf.reqtype >> MET_SMI_BIT_REQ_LARB) & 0x3,
+		(smi_conf.reqtype >> MET_SMI_BIT_REQ_COMM) & 0x3);	
+		met_smi_debug(debug_msg);
+#endif
+
+	/*find the empty conf_array*/
+	for (i = 0; i < MAX_CONFIG_ARRAY_SIZE; i++) {
+		if ((smi_conf_array[i].master == smi_conf.master) && (smi_conf_array[i].port[0] != -1))
+			break;
+	}
+	if (i >= MAX_CONFIG_ARRAY_SIZE) {
+		if (smi_conf_array[0].port[0] == -1) {
+			smi_array_index = 0;
+			array_index = 0;
+		} else {
+			smi_array_index = smi_array_index + 1;
+			array_index = smi_array_index;
+		}
+	} else {
+		array_index = i;
+	}
+
+	if ((smi_array_index >= MAX_CONFIG_ARRAY_SIZE) || (array_index >= MAX_CONFIG_ARRAY_SIZE)) {
+		snprintf(err_msg, MET_SMI_BUF_SIZE,
+			"===Setting Master[%d]: check smi_array_index=%d, array_index=%d overflow (> %d)\n",
+			args[0], smi_array_index, array_index, MAX_CONFIG_ARRAY_SIZE);
+		return -1;
+	}
+
+	smi_conf_array[array_index].master = smi_conf.master;
+
+
+	if (parallel_mode == 0) {	/* Legacy mode */
+		smi_conf_array[array_index].port[0] = smi_conf.port[0];
+	} else {					/* Parallel mode */
+		for (i = 0; i < NPORT_IN_PM; i++) {
+			if (smi_conf_array[array_index].port[i] == -1)
+				smi_conf_array[array_index].port[i] = smi_conf.port[smi_conf_index++];
+		}
+	}
+	smi_conf_array[array_index].rwtype[0] = smi_conf.rwtype[0];
+	smi_conf_array[array_index].rwtype[1] = smi_conf.rwtype[1];
+	smi_conf_array[array_index].rwtype[2] = smi_conf.rwtype[2];
+	smi_conf_array[array_index].rwtype[3] = smi_conf.rwtype[3];
+	smi_conf_array[array_index].desttype = smi_conf.desttype;
+	smi_conf_array[array_index].reqtype = smi_conf.reqtype;
+
+	pr_debug("===SMI process argu Master[%d]: parallel_mode = %d, master = %d, port0 = %d, port1 = %d, port2 = %d, port3 = %d, rwtype0 = %d, rwtype1 = %d, rwtype2 = %d, rwtype3 = %d, desttype = %d, reqtype(larb) = %d, reqtype(comm) = %d\n",
+		args[0],
+		parallel_mode,
+		smi_conf.master,
+		smi_conf.port[0],
+		smi_conf.port[1],
+		smi_conf.port[2],
+		smi_conf.port[3],
+		smi_conf.rwtype[0],
+		smi_conf.rwtype[1],
+		smi_conf.rwtype[2],
+		smi_conf.rwtype[3],
+		smi_conf.desttype,
+		(smi_conf.reqtype >> MET_SMI_BIT_REQ_LARB) & 0x3,
+		(smi_conf.reqtype >> MET_SMI_BIT_REQ_COMM) & 0x3);
+
+	// Encode SMI config: Master (request format from SMI driver)
+	sspm_master = sspm_master | (smi_conf_array[array_index].master << MET_SMI_BIT_MASTER);
+	sspm_master = sspm_master | 0 << MET_SMI_BIT_REQ; //reqtype value will be update in sspm side
+	sspm_master = sspm_master | (smi_conf_array[array_index].desttype << MET_SMI_BIT_DST);
+	sspm_master = sspm_master | (parallel_mode << MET_SMI_BIT_PM);
+	// Extrace info for larb and comm reqestType since of unable to recognize master belong to larb or comm.
+	// BIT13~BIT14: comm reqtype
+	// BIT15~BIT16: larb reqtype
+	sspm_master = sspm_master | smi_conf_array[array_index].reqtype;
+
+
+	// Encode SMI config: Meta (request format from SMI driver)
+	// Encode 4 Metadata into 1 unsigned int
+	// BIT0~BIT4: port
+	// BIT5~BIT6: rwtype
+	if(parallel_mode == 0){
+		sspm_meta = sspm_meta | (smi_conf_array[array_index].port[0] << MET_SMI_BIT_PORT);
+		sspm_meta = sspm_meta | (smi_conf_array[array_index].rwtype[0] << MET_SMI_BIT_RW);
+	}
+	else{
+		for(i = 0; i < 4; i++){
+			if(smi_conf_array[array_index].port[i] == 0xFFFFFFFF){
+				smi_conf_array[array_index].port[i] = USHRT_MAX;
+			}
+			sspm_meta = sspm_meta | (smi_conf_array[array_index].port[i] << (MET_SMI_BIT_PORT+8*i));
+			sspm_meta = sspm_meta | (smi_conf_array[array_index].rwtype[i] << (MET_SMI_BIT_RW+8*i));
+		}
+	}
+
+	// Transfer to SSPM side
+	if (sspm_buf_available == 1)
+	{
+		ipi_buf[0] = MET_MAIN_ID | MET_ARGU | MID_SMI << MID_BIT_SHIFT | 1;
+		ipi_buf[1] = sspm_master;
+		ipi_buf[2] = sspm_meta;
+		ipi_buf[3] = 0;
+		ret = sspm_ipi_send_sync(IPI_ID_MET, IPI_OPT_WAIT, (void *)ipi_buf, 0, &rdata, 1);
+
+		/* Set mode */
+		met_sspm_smi.mode = 1;
+		ondiemet_module[ONDIEMET_SSPM] |= ID_CPU_INFO_MAPPING;
+	}
+
+	return 0;
+}
+
+struct metdevice met_sspm_smi = {
+	.name						= "smi",
+	.owner						= THIS_MODULE,
+	.type						= MET_TYPE_BUS,
+	.create_subfs				= met_smi_create,
+	.delete_subfs				= met_smi_delete,
+	.cpu_related				= 0,
+	.ondiemet_mode 				= 1,	
+	.ondiemet_start				= met_smi_start,
+	.ondiemet_stop				= met_smi_stop,
+	.ondiemet_process_argument 	= smi_process_argument,
+	.ondiemet_print_help		= smi_print_help,	
+};
+
diff --git a/src/devtools/met-driver/4.14/common/sspm/sspm_met_smi.h b/src/devtools/met-driver/4.14/common/sspm/sspm_met_smi.h
new file mode 100644
index 0000000..918a5a5
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/sspm/sspm_met_smi.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _SMI_H_
+#define _SMI_H_
+
+#include <linux/device.h>
+
+struct met_smi {
+	int mode;
+	int master;
+	unsigned int port;
+	unsigned int rwtype;	/* 0 for R+W, 1 for read, 2 for write */
+	unsigned int desttype;	/* 0 for EMI+internal mem, 1 for EMI, 3 for internal mem */
+	unsigned int bustype;	/* 0 for GMC, 1 for AXI */
+	/* unsigned long requesttype;// 0:All, 1:ultra high, 2:pre-ultrahigh, 3:normal. */
+	struct kobject *kobj_bus_smi;
+};
+
+struct smi_cfg {
+	int master;
+	unsigned int port;
+	unsigned int rwtype;	/* 0 for R+W, 1 for read, 2 for write */
+	unsigned int desttype;	/* 0 for EMI+internal mem, 1 for EMI, 3 for internal mem */
+	unsigned int bustype;	/*0 for GMC, 1 for AXI */
+	/*unsigned long requesttype;// 0:All, 1:ultra high, 2:pre-ultrahigh, 3:normal. */
+};
+
+struct smi_mon_con {
+	unsigned int requesttype;	/* 0:All, 1:ultra high, 2:pre-ultrahigh, 3:normal. */
+};
+
+/* ====================== SMI/EMI Interface ================================ */
+
+struct met_smi_conf {
+	unsigned int master;	/*Ex : Whitney: 0~8 for larb0~larb8,  9 for common larb*/
+	int	port[4];	/* port select : [0] only for legacy mode, [0~3] ports for parallel mode, -1 no select*/
+	unsigned int reqtype; /* Selects request type : 0 for all,1 for ultra,2 for preultra,3 for normal*/
+	unsigned int rwtype[4]; /* Selects read/write:  0 for R+W,  1 for read,  2 for write;*/
+				/* [0] for legacy and parallel larb0~8, [0~3] for parallel mode common*/
+	unsigned int desttype; /* Selects destination: 0 and 3 for all memory, 1 for External,2 for internal*/
+};
+
+#endif				/* _SMI_H_ */
diff --git a/src/devtools/met-driver/4.14/common/sspm/sspm_met_smi_name.h b/src/devtools/met-driver/4.14/common/sspm/sspm_met_smi_name.h
new file mode 100644
index 0000000..fd9197e
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/sspm/sspm_met_smi_name.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _SMI_NAME_H_
+#define _SMI_NAME_H_
+#include "mtk_smi.h"
+
+enum SMI_DEST {
+	SMI_DEST_ALL		= 0,
+	SMI_DEST_EMI		= 1,
+	SMI_DEST_INTERNAL	= 2,
+	SMI_DEST_NONE		= 9
+};
+
+enum SMI_RW {
+	SMI_RW_ALL		= 0,
+	SMI_READ_ONLY		= 1,
+	SMI_WRITE_ONLY		= 2,
+	SMI_RW_RESPECTIVE	= 3,
+	SMI_RW_NONE		= 9
+};
+
+enum SMI_BUS {
+	SMI_BUS_GMC		= 0,
+	SMI_BUS_AXI		= 1,
+	SMI_BUS_NONE		= 9
+};
+
+enum SMI_REQUEST {
+	SMI_REQ_ALL		= 0,
+	SMI_REQ_ULTRA		= 1,
+	SMI_REQ_PREULTRA	= 2,
+	SMI_NORMAL_ULTRA	= 3,
+	SMI_REQ_NONE		= 9
+};
+
+struct smi_desc {
+	unsigned long port;
+	enum SMI_DEST desttype;
+	enum SMI_RW rwtype;
+	enum SMI_BUS bustype;
+};
+#endif	/* _SMI_NAME_H_ */
diff --git a/src/devtools/met-driver/4.14/common/sspm/sspm_mtk_smi.h b/src/devtools/met-driver/4.14/common/sspm/sspm_mtk_smi.h
new file mode 100644
index 0000000..9e76a0b
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/sspm/sspm_mtk_smi.h
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __MT_SMI_H__
+#define __MT_SMI_H__
+
+/* the default value, but the real number will get from symbol function*/
+#define SMI_LARB_NUMBER		9
+#define SMI_COMM_NUMBER		1
+#define SMI_LARB_MONITOR_NUMBER    1
+#define SMI_COMM_MONITOR_NUMBER    1
+
+#define SMI_REQ_OK		    (0)
+#define SMI_ERR_WRONG_REQ	(-1)
+#define SMI_ERR_OVERRUN		(-2)
+
+#define SMI_IOMEM_ADDR(b, off)	((void __iomem *)(((unsigned long)b)+off))
+
+#define SMI_LARB_MON_ENA(b)		    SMI_IOMEM_ADDR((b), 0x400)
+#define SMI_LARB_MON_CLR(b)		    SMI_IOMEM_ADDR((b), 0x404)
+#define SMI_LARB_MON_PORT(b)		SMI_IOMEM_ADDR((b), 0x408)
+#define SMI_LARB_MON_CON(b)		    SMI_IOMEM_ADDR((b), 0x40C)
+
+#define SMI_LARB_MON_ACT_CNT(b)		SMI_IOMEM_ADDR((b), 0x410)
+#define SMI_LARB_MON_REQ_CNT(b)		SMI_IOMEM_ADDR((b), 0x414)
+#define SMI_LARB_MON_BEA_CNT(b)		SMI_IOMEM_ADDR((b), 0x418)
+#define SMI_LARB_MON_BYT_CNT(b)		SMI_IOMEM_ADDR((b), 0x41C)
+#define SMI_LARB_MON_CP_CNT(b)		SMI_IOMEM_ADDR((b), 0x420)
+#define SMI_LARB_MON_DP_CNT(b)		SMI_IOMEM_ADDR((b), 0x424)
+#define SMI_LARB_MON_OSTD_CNT(b)	SMI_IOMEM_ADDR((b), 0x428)
+#define SMI_LARB_MON_CP_MAX(b)		SMI_IOMEM_ADDR((b), 0x430)
+#define SMI_LARB_MON_OSTD_MAX(b)	SMI_IOMEM_ADDR((b), 0x434)
+
+#define SMI_COMM_MON_ENA(b)		    SMI_IOMEM_ADDR((b), 0x1A0)
+#define SMI_COMM_MON_CLR(b)		    SMI_IOMEM_ADDR((b), 0x1A4)
+#define SMI_COMM_MON_TYPE(b)		SMI_IOMEM_ADDR((b), 0x1AC)
+#define SMI_COMM_MON_CON(b)		    SMI_IOMEM_ADDR((b), 0x1B0)
+#define SMI_COMM_MON_ACT_CNT(b)		SMI_IOMEM_ADDR((b), 0x1C0)
+#define SMI_COMM_MON_REQ_CNT(b)		SMI_IOMEM_ADDR((b), 0x1C4)
+#define SMI_COMM_MON_OSTD_CNT(b)	SMI_IOMEM_ADDR((b), 0x1C8)
+#define SMI_COMM_MON_BEA_CNT(b)		SMI_IOMEM_ADDR((b), 0x1CC)
+#define SMI_COMM_MON_BYT_CNT(b)		SMI_IOMEM_ADDR((b), 0x1D0)
+#define SMI_COMM_MON_CP_CNT(b)		SMI_IOMEM_ADDR((b), 0x1D4)
+#define SMI_COMM_MON_DP_CNT(b)		SMI_IOMEM_ADDR((b), 0x1D8)
+#define SMI_COMM_MON_CP_MAX(b)		SMI_IOMEM_ADDR((b), 0x1DC)
+#define SMI_COMM_MON_OSTD_MAX(b)	SMI_IOMEM_ADDR((b), 0x1E0)
+
+
+/*ondiemet smi ipi command*/
+enum MET_SMI_IPI_Type {
+	SMI_DRIVER_INITIAL_VALUE = 0x0,
+	SMI_DRIVER_RESET_VALUE,
+	SET_BASE_SMI,
+	SMI_ASSIGN_PORT_START,
+	SMI_ASSIGN_PORT_I,
+	SMI_ASSIGN_PORT_II,
+	SMI_ASSIGN_PORT_III,
+	SMI_ASSIGN_PORT_END,
+};
+
+
+
+
+void MET_SMI_IPI_baseaddr(void);
+int MET_SMI_Init(void);
+void MET_SMI_Fini(void);
+void MET_SMI_Enable(int larbno);
+void MET_SMI_Disable(int larbno);
+void MET_SMI_Pause(int larbno);
+void MET_SMI_Clear(int larbno);
+int MET_SMI_PowerOn(unsigned int master);
+void MET_SMI_PowerOff(unsigned int master);
+int MET_SMI_LARB_SetCfg(int larbno,
+			unsigned int pm,
+			unsigned int reqtype, unsigned int rwtype, unsigned int dsttype);
+int MET_SMI_LARB_SetPortNo(int larbno, unsigned int idx, unsigned int port);
+int MET_SMI_COMM_SetCfg(int commonno, unsigned int pm, unsigned int reqtype);
+int MET_SMI_COMM_SetPortNo(int commonno, unsigned int idx, unsigned int port);
+int MET_SMI_COMM_SetRWType(int commonno, unsigned int idx, unsigned int rw);
+
+/* config */
+int MET_SMI_GetEna(int larbno);
+int MET_SMI_GetClr(int larbno);
+int MET_SMI_GetPortNo(int larbno);
+int MET_SMI_GetCon(int larbno);
+
+/* cnt */
+int MET_SMI_GetActiveCnt(int larbno);
+int MET_SMI_GetRequestCnt(int larbno);
+int MET_SMI_GetBeatCnt(int larbno);
+int MET_SMI_GetByteCnt(int larbno);
+int MET_SMI_GetCPCnt(int larbno);
+int MET_SMI_GetDPCnt(int larbno);
+int MET_SMI_GetOSTDCnt(int larbno);
+int MET_SMI_GetCP_MAX(int larbno);
+int MET_SMI_GetOSTD_MAX(int larbno);
+
+/* common */
+void MET_SMI_Comm_Init(void);
+void MET_SMI_Comm_Enable(int commonno);
+void MET_SMI_Comm_Disable(int commonno);
+void MET_SMI_Pause(int commonno);
+void MET_SMI_Comm_Clear(int commonno);
+
+/* common config */
+int MET_SMI_Comm_GetEna(int commonno);
+int MET_SMI_Comm_GetClr(int commonno);
+int MET_SMI_Comm_GetType(int commonno);
+int MET_SMI_Comm_GetCon(int commonno);
+
+/* cnt */
+int MET_SMI_Comm_GetPortNo(int commonno);
+int MET_SMI_Comm_GetActiveCnt(int commonno);
+int MET_SMI_Comm_GetRequestCnt(int commonno);
+int MET_SMI_Comm_GetBeatCnt(int commonno);
+int MET_SMI_Comm_GetByteCnt(int commonno);
+int MET_SMI_Comm_GetCPCnt(int commonno);
+int MET_SMI_Comm_GetDPCnt(int commonno);
+int MET_SMI_Comm_GetOSTDCnt(int commonno);
+int MET_SMI_Comm_GetCP_MAX(int commonno);
+int MET_SMI_Comm_GetOSTD_MAX(int commonno);
+
+#endif				/* __MT_SMI_H__ */
diff --git a/src/devtools/met-driver/4.14/common/sspm/sspm_walltime.c b/src/devtools/met-driver/4.14/common/sspm/sspm_walltime.c
new file mode 100644
index 0000000..cc12b8b
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/sspm/sspm_walltime.c
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/random.h>
+#include <linux/fs.h>
+
+#include "met_drv.h"
+#include "trace.h"
+/* #include "plf_init.h" */
+#include "sspm/ondiemet_sspm.h"
+
+static void sspm_walltime_start(void)
+{
+	ondiemet_module[ONDIEMET_SSPM] |= ID_WALL_TIME;
+}
+
+static void sspm_walltime_stop(void)
+{
+}
+
+static const char sspm_walltime_header[] = "met-info [000] 0.0: sspm WCLK: freq\n";
+
+static int sspm_walltime_print_header(char *buf, int len)
+{
+	return snprintf(buf, PAGE_SIZE, sspm_walltime_header);
+}
+
+struct metdevice met_sspm_walltime = {
+	.name = "sspm_wall_time",
+	.owner = THIS_MODULE,
+	.type = MET_TYPE_BUS,
+	.cpu_related = 0,
+	.ondiemet_mode = 0,
+	.print_header = sspm_walltime_print_header,
+	.ondiemet_start = sspm_walltime_start,
+	.ondiemet_stop = sspm_walltime_stop,
+	.ondiemet_print_header = sspm_walltime_print_header,
+};
+EXPORT_SYMBOL(met_sspm_walltime);
diff --git a/src/devtools/met-driver/4.14/common/stat.c b/src/devtools/met-driver/4.14/common/stat.c
new file mode 100644
index 0000000..af7b738
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/stat.c
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/cpu.h>
+#include <linux/cpumask.h>
+/* #include <linux/fs.h> */
+#include <linux/init.h>
+#include <linux/version.h>
+#include <linux/interrupt.h>
+#include <linux/kernel_stat.h>
+/* #include <linux/proc_fs.h> */
+#include <linux/sched.h>
+/* #include <linux/seq_file.h> */
+/* #include <linux/slab.h> */
+#include <linux/time.h>
+#include <linux/irqnr.h>
+#include <linux/vmalloc.h>
+/* #include <asm/cputime.h> */
+/* #include <asm-generic/cputime.h> */
+#include <linux/tick.h>
+#include <linux/jiffies.h>
+
+#include <asm/page.h>
+#include <linux/slab.h>
+
+#include "stat.h"
+#include "met_drv.h"
+#include "trace.h"
+
+#define MS_STAT_FMT	"%5lu.%06lu"
+#define FMTLX7		",%llx,%llx,%llx,%llx,%llx,%llx,%llx\n"
+#define FMTLX10		",%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx\n"
+
+
+static DEFINE_PER_CPU(int, cpu_status);
+
+
+/* void ms_st(unsigned long long timestamp, unsigned char cnt, unsigned int *value) */
+noinline void ms_st(unsigned long long timestamp, unsigned char cnt, u64 *value)
+{
+	unsigned long nano_rem = do_div(timestamp, 1000000000);
+
+	switch (cnt) {
+	case 10:
+		MET_TRACE(MS_STAT_FMT FMTLX10, (unsigned long)(timestamp), (nano_rem/1000),
+			value[0], value[1], value[2], value[3], value[4],
+			value[5], value[6], value[7], value[8], value[9]);
+		break;
+	case 7:
+		MET_TRACE(MS_STAT_FMT FMTLX7, (unsigned long)(timestamp), (nano_rem/1000),
+			value[0], value[1], value[2], value[3], value[4],
+			value[5], value[6]);
+		break;
+	}
+}
+
+static void met_stat_start(void)
+{
+	int	cpu = raw_smp_processor_id();
+
+	if (get_ctrl_flags() & 1)
+		met_stat.mode = 0;
+
+	per_cpu(cpu_status, cpu) = MET_CPU_ONLINE;
+}
+
+static void met_stat_stop(void)
+{
+}
+
+static int do_stat(void)
+{
+	return met_stat.mode;
+}
+
+u64 met_usecs_to_cputime64(u64 n)
+{
+#if (NSEC_PER_SEC % HZ) == 0
+	/* Common case, HZ = 100, 128, 200, 250, 256, 500, 512, 1000 etc. */
+	return div_u64(n, NSEC_PER_SEC / HZ);
+#elif (HZ % 512) == 0
+	/* overflow after 292 years if HZ = 1024 */
+	return div_u64(n * HZ / 512, NSEC_PER_SEC / 512);
+#else
+	/*
+	 * Generic case - optimized for cases where HZ is a multiple of 3.
+	 * overflow after 64.99 years, exact for HZ = 60, 72, 90, 120 etc.
+	 */
+	return div_u64(n * 9, (9ull * NSEC_PER_SEC + HZ / 2) / HZ);
+#endif
+}
+
+static u64 get_idle_time(int cpu)
+{
+	u64 idle, idle_time = get_cpu_idle_time_us(cpu, NULL);
+
+	if (idle_time == -1ULL) {
+		/* !NO_HZ so we can rely on cpustat.idle */
+		idle = kcpustat_cpu(cpu).cpustat[CPUTIME_IDLE];
+	} else
+		idle = met_usecs_to_cputime64(idle_time);
+
+	return idle;
+}
+
+static u64 get_iowait_time(int cpu)
+{
+	u64 iowait, iowait_time = get_cpu_iowait_time_us(cpu, NULL);
+
+	if (iowait_time == -1ULL) {
+		/* !NO_HZ so we can rely on cpustat.iowait */
+		iowait = kcpustat_cpu(cpu).cpustat[CPUTIME_IOWAIT];
+	} else
+		iowait = met_usecs_to_cputime64(iowait_time);
+
+	return iowait;
+}
+
+
+static unsigned int stat_os_polling(u64 *value, int i)
+{
+	int j = -1;
+
+	/* Copy values here to work around gcc-2.95.3, gcc-2.96 */
+	value[++j] = jiffies_64_to_clock_t(kcpustat_cpu(i).cpustat[CPUTIME_USER]);	/* user */
+	value[++j] = jiffies_64_to_clock_t(kcpustat_cpu(i).cpustat[CPUTIME_NICE]);	/* nice */
+	value[++j] = jiffies_64_to_clock_t(kcpustat_cpu(i).cpustat[CPUTIME_SYSTEM]);	/* system */
+	value[++j] = jiffies_64_to_clock_t(get_idle_time(i));	/* idle */
+	value[++j] = jiffies_64_to_clock_t(get_iowait_time(i));	/* iowait */
+	value[++j] = jiffies_64_to_clock_t(kcpustat_cpu(i).cpustat[CPUTIME_IRQ]);	/* irq */
+	value[++j] = jiffies_64_to_clock_t(kcpustat_cpu(i).cpustat[CPUTIME_SOFTIRQ]);	/* softirq */
+	value[++j] = jiffies_64_to_clock_t(kcpustat_cpu(i).cpustat[CPUTIME_STEAL]);	/* steal */
+	value[++j] = jiffies_64_to_clock_t(kcpustat_cpu(i).cpustat[CPUTIME_GUEST]);	/* guest */
+	value[++j] = jiffies_64_to_clock_t(kcpustat_cpu(i).cpustat[CPUTIME_GUEST_NICE]);	/* guest_nice */
+
+	return j + 1;
+}
+
+static void met_stat_polling(unsigned long long stamp, int cpu)
+{
+	unsigned char count;
+	u64 value[10] = {0};
+
+	if (per_cpu(cpu_status, cpu) != MET_CPU_ONLINE)
+		return;
+
+	/* return; */
+	if (do_stat()) {
+		count = stat_os_polling(value, cpu);
+		if (count)
+			ms_st(stamp, count, value);
+	}
+}
+
+static const char header[] =
+	"met-info [000] 0.0: met_st_header: user,nice,system,idle,iowait,irq,softirq,steal,guest,guest_nice\n";
+
+static const char help[] = "  --stat                                monitor stat\n";
+
+
+static int met_stat_print_help(char *buf, int len)
+{
+	return snprintf(buf, PAGE_SIZE, help);
+}
+
+static int met_stat_print_header(char *buf, int len)
+{
+	return snprintf(buf, PAGE_SIZE, header);
+}
+
+static void met_stat_cpu_state_notify(long cpu, unsigned long action)
+{
+	per_cpu(cpu_status, cpu) = action;
+}
+
+
+struct metdevice met_stat = {
+	.name = "stat",
+	.type = MET_TYPE_PMU,
+	.cpu_related = 1,
+	.start = met_stat_start,
+	.stop = met_stat_stop,
+	.polling_interval = 30,
+	.timed_polling = met_stat_polling,
+	.print_help = met_stat_print_help,
+	.print_header = met_stat_print_header,
+	.cpu_state_notify = met_stat_cpu_state_notify,
+};
diff --git a/src/devtools/met-driver/4.14/common/stat.h b/src/devtools/met-driver/4.14/common/stat.h
new file mode 100644
index 0000000..8aabc5c
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/stat.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _STAT_H_
+#define _STAT_H_
+
+#include <linux/device.h>
+
+extern struct metdevice met_stat;
+
+int stat_reg(struct kobject *parent);
+void stat_unreg(void);
+
+void stat_start(void);
+void stat_stop(void);
+void stat_polling(unsigned long long stamp, int cpu);
+
+unsigned int get_ctrl_flags(void);
+
+#endif				/* _STAT_H_ */
diff --git a/src/devtools/met-driver/4.14/common/switch.c b/src/devtools/met-driver/4.14/common/switch.c
new file mode 100644
index 0000000..f634e16
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/switch.c
@@ -0,0 +1,302 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/* include <asm/percpu.h> */
+#include <trace/events/sched.h>
+#include <linux/module.h>
+#include <trace/events/irq.h>
+#include <trace/events/power.h>
+
+#include "interface.h"
+#include "met_drv.h"
+#include "cpu_pmu.h"
+#include "switch.h"
+#include "sampler.h"
+#include "met_kernel_symbol.h"
+/* #include "trace.h" */
+
+/*
+ * IRQ_TIRGGER and CPU_IDLE_TRIGGER
+ */
+/* #define IRQ_TRIGGER */
+/* #define CPU_IDLE_TRIGGER */
+
+static DEFINE_PER_CPU(unsigned int, first_log);
+
+#ifdef __aarch64__
+/* #include <asm/compat.h> */
+#include <linux/compat.h>
+#endif
+
+noinline void mt_switch(struct task_struct *prev, struct task_struct *next)
+{
+	int cpu;
+	int prev_state = 0, next_state = 0;
+
+#ifdef __aarch64__
+	prev_state = !(is_compat_thread(task_thread_info(prev)));
+	next_state = !(is_compat_thread(task_thread_info(next)));
+#endif
+
+	cpu = smp_processor_id();
+	if (per_cpu(first_log, cpu)) {
+		MET_TRACE("%d, %d, %d, %d\n", prev->pid, prev_state, next->pid, next_state);
+		per_cpu(first_log, cpu) = 0;
+	}
+	else if (prev_state != next_state)
+		MET_TRACE("%d, %d, %d, %d\n", prev->pid, prev_state, next->pid, next_state);
+}
+
+
+#if 0 /* move to kernel space */
+MET_DEFINE_PROBE(sched_switch,
+		 TP_PROTO(bool preempt, struct task_struct *prev, struct task_struct *next))
+{
+	/* speedup sched_switch callback handle */
+	if (met_switch.mode == 0)
+		return;
+
+	if (met_switch.mode & MT_SWITCH_EVENT_TIMER)
+		met_event_timer_notify();
+
+	if (met_switch.mode & MT_SWITCH_64_32BIT)
+		mt_switch(prev, next);
+
+	if (met_switch.mode & MT_SWITCH_SCHEDSWITCH) {
+		if (get_pmu_profiling_version() == 1)
+			cpupmu_polling(0, smp_processor_id());
+#ifdef MET_SUPPORT_CPUPMU_V2
+		else if (get_pmu_profiling_version() == 2)
+			cpupmu_polling_v2(0, smp_processor_id());
+#endif
+	}
+}
+#endif
+
+void met_sched_switch(struct task_struct *prev, struct task_struct *next)
+{
+	/* speedup sched_switch callback handle */
+	if (met_switch.mode == 0)
+		return;
+
+	if (met_switch.mode & MT_SWITCH_EVENT_TIMER)
+		met_event_timer_notify();
+
+	if (met_switch.mode & MT_SWITCH_64_32BIT)
+		mt_switch(prev, next);
+
+	/* met_perf_cpupmu_status: 0: stop, others: polling */
+	if ((met_switch.mode & MT_SWITCH_SCHEDSWITCH) && met_perf_cpupmu_status)
+		met_perf_cpupmu_polling(0, smp_processor_id());
+}
+
+#ifdef IRQ_TRIGGER
+MET_DEFINE_PROBE(irq_handler_entry, TP_PROTO(int irq, struct irqaction *action))
+{
+	if (met_switch.mode & MT_SWITCH_EVENT_TIMER) {
+		met_event_timer_notify();
+		return;
+	}
+}
+#endif
+
+#ifdef CPU_IDLE_TRIGGER
+MET_DEFINE_PROBE(cpu_idle, TP_PROTO(unsigned int state, unsigned int cpu_id))
+{
+	if (met_switch.mode & MT_SWITCH_EVENT_TIMER) {
+		met_event_timer_notify();
+		return;
+	}
+}
+#endif
+
+#ifdef MET_ANYTIME
+/*
+ * create related subfs file node
+ */
+
+static ssize_t default_on_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "1\n");
+}
+
+static struct kobj_attribute default_on_attr = __ATTR(default_on, 0664, default_on_show, NULL);
+static struct kobject *kobj_cpu;
+#endif
+
+static int met_switch_create_subfs(struct kobject *parent)
+{
+	int ret = 0;
+
+	/* register tracepoints */
+#if 0
+	if (MET_REGISTER_TRACE(sched_switch)) {
+		pr_debug("can not register callback of sched_switch\n");
+		return -ENODEV;
+	}
+#else
+	if (met_reg_switch_symbol)
+		ret = met_reg_switch_symbol();
+#endif
+#ifdef CPU_IDLE_TRIGGER
+	if (MET_REGISTER_TRACE(cpu_idle)) {
+		pr_debug("can not register callback of irq_handler_entry\n");
+		return -ENODEV;
+	}
+#endif
+#ifdef IRQ_TRIGGER
+	if (MET_REGISTER_TRACE(irq_handler_entry)) {
+		pr_debug("can not register callback of irq_handler_entry\n");
+		return -ENODEV;
+	}
+#endif
+
+#ifdef MET_ANYTIME
+	/*
+	 * to create default_on file node
+	 * let user space can know we can support MET default on
+	 */
+	kobj_cpu = parent;
+	ret = sysfs_create_file(kobj_cpu, &default_on_attr.attr);
+	if (ret != 0) {
+		pr_debug("Failed to create default_on in sysfs\n");
+		return -1;
+	}
+#endif
+
+	return ret;
+}
+
+
+static void met_switch_delete_subfs(void)
+{
+#ifdef MET_ANYTIME
+	if (kobj_cpu != NULL) {
+		sysfs_remove_file(kobj_cpu, &default_on_attr.attr);
+		kobj_cpu = NULL;
+	}
+#endif
+#ifdef IRQ_TRIGGER
+	MET_UNREGISTER_TRACE(irq_handler_entry);
+#endif
+#ifdef CPU_IDLE_TRIGGER
+	MET_UNREGISTER_TRACE(cpu_idle);
+#endif
+#if 0
+	MET_UNREGISTER_TRACE(sched_switch);
+#else
+	if (met_unreg_switch_symbol)
+		met_unreg_switch_symbol();
+#endif
+
+}
+
+
+static void (*cpu_timed_polling)(unsigned long long stamp, int cpu);
+/* static void (*cpu_tagged_polling)(unsigned long long stamp, int cpu); */
+
+static void met_switch_start(void)
+{
+	int cpu;
+
+	if (met_switch.mode & MT_SWITCH_SCHEDSWITCH) {
+		cpu_timed_polling = met_cpupmu.timed_polling;
+		/* cpu_tagged_polling = met_cpupmu.tagged_polling; */
+		met_cpupmu.timed_polling = NULL;
+		/* met_cpupmu.tagged_polling = NULL; */
+	}
+
+	for_each_possible_cpu(cpu) {
+		per_cpu(first_log, cpu) = 1;
+	}
+
+}
+
+
+static void met_switch_stop(void)
+{
+	int cpu;
+
+	if (met_switch.mode & MT_SWITCH_SCHEDSWITCH) {
+		met_cpupmu.timed_polling = cpu_timed_polling;
+		/* met_cpupmu.tagged_polling = cpu_tagged_polling; */
+	}
+
+	for_each_possible_cpu(cpu) {
+		per_cpu(first_log, cpu) = 0;
+	}
+
+}
+
+
+static int met_switch_process_argument(const char *arg, int len)
+{
+	unsigned int value;
+	/*ex: mxitem is 0x0005, max value should be (5-1) + (5-2) = 0x100 + 0x11 = 7 */
+	unsigned int max_value = ((MT_SWITCH_MX_ITEM * 2) - 3);
+
+
+	if (met_parse_num(arg, &value, len) < 0)
+		goto arg_switch_exit;
+
+	if ((value < 1) || (value > max_value))
+		goto arg_switch_exit;
+
+	met_switch.mode = value;
+	return 0;
+
+arg_switch_exit:
+	met_switch.mode = 0;
+	return -EINVAL;
+}
+
+static const char header[] =
+	"met-info [000] 0.0: met_switch_header: prev_pid,prev_state,next_pid,next_state\n";
+
+static const char help[] =
+"  --switch=mode                         mode:0x1 - output CPUPMU whenever sched_switch\n"
+"                                        mode:0x2 - output Aarch 32/64 state whenever state changed (no CPUPMU)\n"
+"                                        mode:0x4 - force output count at tag_start/tag_end\n"
+"                                        mode:0x8 - task switch timer\n"
+"                                        mode:0xF - mode 0x1 + 0x2 + 04 + 08\n";
+
+static int met_switch_print_help(char *buf, int len)
+{
+	return snprintf(buf, PAGE_SIZE, help);
+}
+
+static int met_switch_print_header(char *buf, int len)
+{
+	int ret = 0;
+
+	ret =
+	    snprintf(buf, PAGE_SIZE, "met-info [000] 0.0: mp_cpu_switch_base: %d\n",
+		     met_switch.mode);
+	if (met_switch.mode & MT_SWITCH_64_32BIT)
+		ret += snprintf(buf + ret, PAGE_SIZE, header);
+
+	return ret;
+}
+
+
+struct metdevice met_switch = {
+	.name = "switch",
+	.type = MET_TYPE_PMU,
+	.create_subfs = met_switch_create_subfs,
+	.delete_subfs = met_switch_delete_subfs,
+	.start = met_switch_start,
+	.stop = met_switch_stop,
+	.process_argument = met_switch_process_argument,
+	.print_help = met_switch_print_help,
+	.print_header = met_switch_print_header,
+};
diff --git a/src/devtools/met-driver/4.14/common/switch.h b/src/devtools/met-driver/4.14/common/switch.h
new file mode 100644
index 0000000..14397d7
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/switch.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __MT_SWITCH__
+#define __MT_SWITCH__
+/*
+ * =========================
+ * !!!!!!!!!!!NOTICE!!!!!!!!
+ * =========================
+ * MT_SWITCH OPTION must delcare as Mask value
+ * And sort them from smallest to largest
+ * MT_SWITCH_MX_ITEM was used to determine argument range
+*/
+enum {
+	/* =================== */
+	/* user define mt switch event */
+	/* =================== */
+	MT_SWITCH_SCHEDSWITCH = 0x0001,
+	MT_SWITCH_64_32BIT = 0x0002,
+	MT_SWITCH_TAGPOLLING = 0x0004,
+	MT_SWITCH_EVENT_TIMER = 0x0008,
+	/* =================== */
+	MT_SWITCH_MX_ITEM
+};
+
+extern struct metdevice met_switch;
+#endif
diff --git a/src/devtools/met-driver/4.14/common/trace.h b/src/devtools/met-driver/4.14/common/trace.h
new file mode 100644
index 0000000..f259b7a
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/trace.h
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _TRACE_H_
+#define _TRACE_H_
+
+
+extern void (*mp_cp_ptr)(unsigned long long timestamp,
+	       struct task_struct *task,
+	       unsigned long program_counter,
+	       unsigned long dcookie,
+	       unsigned long offset,
+	       unsigned char cnt, unsigned int *value);
+
+#define MP_FMT1	"%x\n"
+#define MP_FMT2	"%x,%x\n"
+#define MP_FMT3	"%x,%x,%x\n"
+#define MP_FMT4	"%x,%x,%x,%x\n"
+#define MP_FMT5	"%x,%x,%x,%x,%x\n"
+#define MP_FMT6	"%x,%x,%x,%x,%x,%x\n"
+#define MP_FMT7	"%x,%x,%x,%x,%x,%x,%x\n"
+#define MP_FMT8	"%x,%x,%x,%x,%x,%x,%x,%x\n"
+#define MP_FMT9	"%x,%x,%x,%x,%x,%x,%x,%x,%x\n"
+#define MP_FMT10 "%x,%x,%x,%x,%x,%x,%x,%x,%x,%x\n"
+#define MP_FMT11 "%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x\n"
+#define MP_FMT12 "%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x\n"
+#define MP_FMT13 "%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x\n"
+#define MP_FMT14 "%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x\n"
+#define MP_FMT15 "%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x\n"
+
+#define MET_GENERAL_PRINT(FUNC, count, value) \
+do { \
+	switch (count) { \
+	case 1: { \
+		FUNC(MP_FMT1, value[0]); \
+		} \
+		break; \
+	case 2: { \
+		FUNC(MP_FMT2, value[0], value[1]); \
+		} \
+		break; \
+	case 3: { \
+		FUNC(MP_FMT3, value[0], value[1], value[2]); \
+		} \
+		break; \
+	case 4: { \
+		FUNC(MP_FMT4, value[0], value[1], value[2], value[3]); \
+		} \
+		break; \
+	case 5: { \
+		FUNC(MP_FMT5, value[0], value[1], value[2], value[3], value[4]); \
+		} \
+		break; \
+	case 6: { \
+		FUNC(MP_FMT6, value[0], value[1], value[2], value[3], value[4], value[5]); \
+		} \
+		break; \
+	case 7: { \
+		FUNC(MP_FMT7, value[0], value[1], value[2], value[3], value[4], value[5], value[6]); \
+		} \
+		break; \
+	case 8: { \
+		FUNC(MP_FMT8, value[0], value[1], value[2], value[3], value[4], value[5], value[6], value[7]); \
+		} \
+		break; \
+	case 9: { \
+		FUNC(MP_FMT9, value[0], value[1], value[2], value[3], value[4], value[5], value[6], value[7], \
+				value[8]); \
+		} \
+		break; \
+	case 10: { \
+		FUNC(MP_FMT10, value[0], value[1], value[2], value[3], value[4], value[5], value[6], value[7], \
+				value[8], value[9]); \
+		} \
+		break; \
+	case 11: { \
+		FUNC(MP_FMT11, value[0], value[1], value[2], value[3], value[4], value[5], value[6], value[7], \
+				value[8], value[9], value[10]); \
+		} \
+		break; \
+	case 12: { \
+		FUNC(MP_FMT12, value[0], value[1], value[2], value[3], value[4], value[5], value[6], value[7], \
+				value[8], value[9], value[10], value[11]); \
+		} \
+		break; \
+	case 13: { \
+		FUNC(MP_FMT13, value[0], value[1], value[2], value[3], value[4], value[5], value[6], value[7], \
+				value[8], value[9], value[10], value[11], value[12]); \
+		} \
+		break; \
+	case 14: { \
+		FUNC(MP_FMT14, value[0], value[1], value[2], value[3], value[4], value[5], value[6], value[7], \
+				value[8], value[9], value[10], value[11], value[12], value[13]); \
+		} \
+		break; \
+	case 15: { \
+		FUNC(MP_FMT15, value[0], value[1], value[2], value[3], value[4], value[5], value[6], value[7], \
+				value[8], value[9], value[10], value[11], value[12], value[13], value[14]); \
+		} \
+		break; \
+	} \
+} while (0)
+#endif /* _TRACE_H_ */
diff --git a/src/devtools/met-driver/4.14/common/trace_event.c b/src/devtools/met-driver/4.14/common/trace_event.c
new file mode 100644
index 0000000..203708c
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/trace_event.c
@@ -0,0 +1,274 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <asm/page.h>
+#include "interface.h"
+#include "met_drv.h"
+
+#ifdef	CONFIG_GPU_TRACEPOINTS
+#include <trace/events/gpu.h>
+
+#define show_secs_from_ns(ns) \
+	({ \
+		u64 t = ns + (NSEC_PER_USEC / 2); \
+		do_div(t, NSEC_PER_SEC); \
+		t; \
+	})
+
+#define show_usecs_from_ns(ns) \
+	({ \
+		u64 t = ns + (NSEC_PER_USEC / 2) ; \
+		u32 rem; \
+		do_div(t, NSEC_PER_USEC); \
+		rem = do_div(t, USEC_PER_SEC); \
+	})
+
+static int event_gpu_registered;
+static int event_gpu_enabled;
+
+noinline void gpu_sched_switch(const char *gpu_name, u64 timestamp,
+				      u32 next_ctx_id, s32 next_prio, u32 next_job_id)
+{
+	MET_TRACE("gpu_name=%s ts=%llu.%06lu next_ctx_id=%lu next_prio=%ld next_job_id=%lu\n",
+		   gpu_name,
+		   (unsigned long long)show_secs_from_ns(timestamp),
+		   (unsigned long)show_usecs_from_ns(timestamp),
+		   (unsigned long)next_ctx_id, (long)next_prio, (unsigned long)next_job_id);
+}
+
+MET_DEFINE_PROBE(gpu_sched_switch, TP_PROTO(const char *gpu_name, u64 timestamp,
+		u32 next_ctx_id, s32 next_prio, u32 next_job_id))
+{
+	gpu_sched_switch(gpu_name, timestamp, next_ctx_id, next_prio, next_job_id);
+}
+
+noinline void gpu_job_enqueue(u32 ctx_id, u32 job_id, const char *type)
+{
+	MET_TRACE("ctx_id=%lu job_id=%lu type=%s",
+		   (unsigned long)ctx_id, (unsigned long)job_id, type);
+}
+
+MET_DEFINE_PROBE(gpu_job_enqueue, TP_PROTO(u32 ctx_id, u32 job_id, const char *type))
+{
+	gpu_job_enqueue(ctx_id, job_id, type);
+}
+#endif
+
+
+#ifdef  MET_EVENT_POWER_SUPPORT
+#include "met_power.h"
+#include "met_kernel_symbol.h"
+
+static int event_power_registered;
+static int event_power_enabled;
+
+const char *
+met_trace_print_symbols_seq(char* pclass_name, unsigned long val,
+			const struct trace_print_flags *symbol_array)
+{
+	int i;
+    size_t new_fsize=0;
+    char _buf[32];
+	const char *ret = pclass_name;
+
+	for (i = 0;  symbol_array[i].name; i++) {
+
+		if (val != symbol_array[i].mask)
+			continue;
+
+		new_fsize = sprintf(pclass_name, symbol_array[i].name, strlen(symbol_array[i].name));
+		break;
+	}
+
+	if (new_fsize == 0) {
+		snprintf(_buf, 32, "0x%lx", val);
+		new_fsize = sprintf(pclass_name, _buf, strlen(_buf));
+	}
+
+	return ret;
+}
+
+#define __print_symbolic(pclass_name, value, symbol_array...)			\
+	({								\
+		static const struct trace_print_flags symbols[] =	\
+			{ symbol_array, { -1, NULL }};			\
+		met_trace_print_symbols_seq(pclass_name, value, symbols);		\
+	})
+
+#ifdef pm_qos_update_request
+#undef pm_qos_update_request
+#endif
+void pm_qos_update_request(int pm_qos_class, s32 value, char *owner)
+{
+	char class_name[64];
+	MET_TRACE("pm_qos_class=%s value=%d owner=%s\n",
+	  __print_symbolic(class_name, pm_qos_class,
+		{ _PM_QOS_CPU_DMA_LATENCY,	"CPU_DMA_LATENCY" },
+		{ _PM_QOS_NETWORK_LATENCY,	"NETWORK_LATENCY" },
+		{ _PM_QOS_NETWORK_THROUGHPUT,	"NETWORK_THROUGHPUT" }),
+	  value, owner);
+}
+//#endif
+
+#ifdef pm_qos_update_target
+#undef pm_qos_update_target
+#endif
+void pm_qos_update_target(unsigned int action, int prev_value, int curr_value)
+{
+	char class_name[64];
+
+	MET_TRACE("action=%s prev_value=%d curr_value=%d\n",
+	  __print_symbolic(class_name, action,
+		{ _PM_QOS_ADD_REQ,	"ADD_REQ" },
+		{ _PM_QOS_UPDATE_REQ,	"UPDATE_REQ" },
+		{ _PM_QOS_REMOVE_REQ,	"REMOVE_REQ" }),
+	  prev_value, curr_value);
+}
+#endif
+//#endif
+
+static int reset_driver_stat(void)
+{
+#ifdef	CONFIG_GPU_TRACEPOINTS
+	event_gpu_enabled = 0;
+#endif
+#ifdef MET_EVENT_POWER_SUPPORT
+	event_power_enabled = 0;
+#endif
+
+	met_trace_event.mode = 0;
+	return 0;
+}
+
+
+
+static void met_event_start(void)
+{
+#ifdef	CONFIG_GPU_TRACEPOINTS
+	/* register trace event for gpu */
+	do {
+		if (!event_gpu_enabled)
+			break;
+		if (MET_REGISTER_TRACE(gpu_sched_switch)) {
+			pr_debug("can not register callback of gpu_sched_switch\n");
+			break;
+		}
+		if (MET_REGISTER_TRACE(gpu_job_enqueue)) {
+			pr_debug("can not register callback of gpu_job_enqueue\n");
+			MET_UNREGISTER_TRACE(gpu_sched_switch);
+			break;
+		}
+		event_gpu_registered = 1;
+	} while (0);
+#endif
+
+#ifdef  MET_EVENT_POWER_SUPPORT
+	/* register trace event for power */
+	do {
+		if (!event_power_enabled)
+			break;
+		if (met_reg_event_power_symbol)
+			if (met_reg_event_power_symbol()) {
+				pr_debug("can not register callback of met_reg_event_power\n");
+				break;
+			}
+		event_power_registered = 1;
+	} while (0);
+#endif
+
+}
+
+static void met_event_stop(void)
+{
+#ifdef	CONFIG_GPU_TRACEPOINTS
+	/* unregister trace event for gpu */
+	if (event_gpu_registered) {
+		MET_UNREGISTER_TRACE(gpu_job_enqueue);
+		MET_UNREGISTER_TRACE(gpu_sched_switch);
+		event_gpu_registered = 0;
+	}
+#endif
+
+#ifdef  MET_EVENT_POWER_SUPPORT
+	/* unregister trace event for power */
+	if (event_power_registered) {
+		if (met_unreg_event_power_symbol)
+			met_unreg_event_power_symbol();
+		event_power_registered = 0;
+	}
+#endif
+}
+
+static int met_event_process_argument(const char *arg, int len)
+{
+	int	ret = -1;
+
+#ifdef	CONFIG_GPU_TRACEPOINTS
+	if (strcasecmp(arg, "gpu") == 0) {
+		event_gpu_enabled = 1;
+		met_trace_event.mode = 1;
+		ret = 0;
+	}
+#endif
+#ifdef  MET_EVENT_POWER_SUPPORT
+	if (strcasecmp(arg, "power") == 0) {
+		event_power_enabled = 1;
+		met_trace_event.mode = 1;
+		ret = 0;
+	}
+#endif
+	return ret;
+}
+
+static const char help[] = "\b"
+#ifdef	CONFIG_GPU_TRACEPOINTS
+	"  --event=gpu                           output gpu trace events\n"
+#endif
+#ifdef  MET_EVENT_POWER_SUPPORT
+	"  --event=power						 output pmqos trace events\n"
+#endif
+	;
+
+static int met_event_print_help(char *buf, int len)
+{
+	return snprintf(buf, PAGE_SIZE, help);
+}
+
+static const char header[] =
+	"met-info [000] 0.0: met_ftrace_event:"
+#ifdef	CONFIG_GPU_TRACEPOINTS
+	" gpu:gpu_sched_switch gpu:gpu_job_enqueue"
+#endif
+#ifdef  MET_EVENT_POWER_SUPPORT
+	" power:pm_qos_update_request power:pm_qos_update_target"
+#endif
+	"\n";
+
+static int met_event_print_header(char *buf, int len)
+{
+	int	ret;
+
+	ret = snprintf(buf, PAGE_SIZE, header);
+	return ret;
+}
+
+struct metdevice met_trace_event = {
+	.name			= "event",
+	.type			= MET_TYPE_PMU,
+	.start			= met_event_start,
+	.stop			= met_event_stop,
+	.reset			= reset_driver_stat,
+	.process_argument	= met_event_process_argument,
+	.print_help		= met_event_print_help,
+	.print_header		= met_event_print_header,
+};
diff --git a/src/devtools/met-driver/4.14/common/util.c b/src/devtools/met-driver/4.14/common/util.c
new file mode 100644
index 0000000..051a3bd
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/util.c
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "util.h"
+#include <linux/fs.h>
+#include <linux/kernel.h>
+/* #include <asm/uaccess.h> */
+#include <linux/uaccess.h>
+
+#ifdef FILELOG
+
+static char tmp[1000] = { 0 };
+
+ /*TODO*/
+/**
+ * open file
+ * @param name path to open
+ * @return file pointer
+ */
+struct file *open_file(const char *name)
+{
+	struct file *fp = NULL;
+
+	fp = filp_open(name, O_WRONLY | O_APPEND /*| O_TRUNC */  | O_CREAT, 0664);
+	if (unlikely(fp == NULL)) {
+		pr_debug(KERNEL_INFO "can not open result file");
+		return NULL;
+	}
+	return fp;
+}
+
+/**
+ * write to file
+ * @param fp file pointer
+ * @param format format string
+ * @param ... variable-length subsequent arguments
+ */
+void write_file(struct file *fp, const char *format, ...)
+{
+	va_list va;
+	mm_segment_t fs = get_fs();
+
+	va_start(va, format);
+	vsnprintf(tmp, sizeof(tmp), format, va);
+	set_fs(KERNEL_DS);
+	vfs_write(fp, tmp, strlen(tmp), &(fp->f_pos));
+	set_fs(fs);
+	va_end(va);
+}
+
+/**
+ * close file
+ * @param fp file pointer
+ * @return exit code
+ */
+int close_file(struct file *fp)
+{
+	if (likely(fp != NULL)) {
+		filp_close(fp, NULL);
+		fp = NULL;
+		return 0;
+	}
+	pr_debug("cannot close file pointer:%p\n", fp);
+	return -1;
+}
+
+void filelog(char *str)
+{
+	struct file *fp;
+
+	fp = open_file("/data/met.log");
+	if (fp != NULL) {
+		write_file(fp, "%s", str);
+		close_file(fp);
+	}
+}
+
+#endif				/* FILELOG */
diff --git a/src/devtools/met-driver/4.14/common/util.h b/src/devtools/met-driver/4.14/common/util.h
new file mode 100644
index 0000000..5730376
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/util.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _SRC_UTIL_H_
+#define _SRC_UTIL_H_
+
+/* #define FILELOG 1 */
+
+#ifdef FILELOG
+void filelog(char *str);
+#else
+#define filelog(str)
+#endif
+
+#endif				/* _SRC_UTIL_H_ */
diff --git a/src/devtools/met-driver/4.14/common/v6_pmu_hw.c b/src/devtools/met-driver/4.14/common/v6_pmu_hw.c
new file mode 100644
index 0000000..24b133c
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/v6_pmu_hw.c
@@ -0,0 +1,242 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "cpu_pmu.h"
+
+/*******************************
+ *      ARM v6 operations      *
+ *******************************/
+#define ARMV6_PMCR_ENABLE               (1 << 0)
+#define ARMV6_PMCR_CTR01_RESET          (1 << 1)
+#define ARMV6_PMCR_CCOUNT_RESET         (1 << 2)
+#define ARMV6_PMCR_CCOUNT_DIV           (1 << 3)
+#define ARMV6_PMCR_COUNT0_IEN           (1 << 4)
+#define ARMV6_PMCR_COUNT1_IEN           (1 << 5)
+#define ARMV6_PMCR_CCOUNT_IEN           (1 << 6)
+#define ARMV6_PMCR_COUNT0_OVERFLOW      (1 << 8)
+#define ARMV6_PMCR_COUNT1_OVERFLOW      (1 << 9)
+#define ARMV6_PMCR_CCOUNT_OVERFLOW      (1 << 10)
+#define ARMV6_PMCR_EVT_COUNT0_SHIFT     20
+#define ARMV6_PMCR_EVT_COUNT0_MASK      (0xFF << ARMV6_PMCR_EVT_COUNT0_SHIFT)
+#define ARMV6_PMCR_EVT_COUNT1_SHIFT     12
+#define ARMV6_PMCR_EVT_COUNT1_MASK      (0xFF << ARMV6_PMCR_EVT_COUNT1_SHIFT)
+
+#define ARMV6_PMCR_OVERFLOWED_MASK \
+	(ARMV6_PMCR_COUNT0_OVERFLOW | ARMV6_PMCR_COUNT1_OVERFLOW | \
+	ARMV6_PMCR_CCOUNT_OVERFLOW)
+
+enum armv6_counters {
+	ARMV6_COUNTER0 = 0,
+	ARMV6_COUNTER1,
+	ARMV6_CYCLE_COUNTER,
+};
+
+static inline unsigned long armv6_pmcr_read(void)
+{
+	u32 val;
+
+	asm volatile ("mrc   p15, 0, %0, c15, c12, 0":"=r" (val));
+	return val;
+}
+
+static inline void armv6_pmcr_write(unsigned long val)
+{
+	asm volatile ("mcr   p15, 0, %0, c15, c12, 0"::"r" (val));
+}
+
+static inline unsigned int armv6_pmu_read_count(unsigned int idx)
+{
+	unsigned long value = 0;
+
+	if (idx == ARMV6_CYCLE_COUNTER)
+		asm volatile ("mrc   p15, 0, %0, c15, c12, 1":"=r" (value));
+	else if (idx == ARMV6_COUNTER0)
+		asm volatile ("mrc   p15, 0, %0, c15, c12, 2":"=r" (value));
+	else if (idx == ARMV6_COUNTER1)
+		asm volatile ("mrc   p15, 0, %0, c15, c12, 3":"=r" (value));
+
+	return value;
+}
+
+static inline void armv6_pmu_overflow(void)
+{
+	unsigned int val;
+
+	val = armv6_pmcr_read();
+	val |= ARMV6_PMCR_OVERFLOWED_MASK;
+	armv6_pmcr_write(val);
+}
+
+static inline unsigned int armv6_pmu_control_read(void)
+{
+	u32 val;
+
+	asm volatile ("mrc   p15, 0, %0, c15, c12, 0":"=r" (val));
+	return val;
+}
+
+static inline void armv6_pmu_control_write(unsigned int setting)
+{
+	unsigned long val;
+
+	val = armv6_pmcr_read();
+	val |= setting;
+	armv6_pmcr_write(val);
+}
+
+static void armv6_pmu_hw_reset_all(void)
+{
+	unsigned long val;
+
+	val = armv6_pmcr_read();
+	val &= ~ARMV6_PMCR_ENABLE;	/* disable all counters */
+	val |= (ARMV6_PMCR_CTR01_RESET | ARMV6_PMCR_CCOUNT_RESET);	/* reset CCNT, PMNC1/2 counter to zero */
+	armv6_pmcr_write(val);
+
+	armv6_pmu_overflow();
+}
+
+static void armv6pmu_enable_event(int idx, unsigned short config)
+{
+	unsigned long val, mask, evt;
+
+	if (idx == ARMV6_CYCLE_COUNTER) {
+		mask = 0;
+		evt = ARMV6_PMCR_CCOUNT_IEN;
+	} else if (idx == ARMV6_COUNTER0) {
+		mask = ARMV6_PMCR_EVT_COUNT0_MASK;
+		evt = (config << ARMV6_PMCR_EVT_COUNT0_SHIFT) | ARMV6_PMCR_COUNT0_IEN;
+	} else if (idx == ARMV6_COUNTER1) {
+		mask = ARMV6_PMCR_EVT_COUNT1_MASK;
+		evt = (config << ARMV6_PMCR_EVT_COUNT1_SHIFT) | ARMV6_PMCR_COUNT1_IEN;
+	} else {
+		pr_debug("invalid counter number (%d)\n", idx);
+		return;
+	}
+
+	/*
+	 * Mask out the current event and set the counter to count the event
+	 * that we're interested in.
+	 */
+	val = armv6_pmcr_read();
+	val &= ~mask;
+	val |= evt;
+	armv6_pmcr_write(val);
+}
+
+/***********************************
+ *      MET ARM v6 operations      *
+ ***********************************/
+enum ARM_TYPE {
+	ARM1136 = 0xB36,
+	ARM1156 = 0xB56,
+	ARM1176 = 0xB76,
+	CHIP_UNKNOWN = 0xFFF
+};
+
+struct chip_pmu {
+	enum ARM_TYPE type;
+};
+
+static struct chip_pmu chips[] = {
+	{ARM1136},
+	{ARM1156},
+	{ARM1176},
+};
+
+static int armv6_pmu_hw_check_event(struct met_pmu *pmu, int idx, int event)
+{
+	int i;
+
+	/* Check if event is duplicate */
+	for (i = 0; i < idx; i++) {
+		if (pmu[i].event == event)
+			break;
+	}
+	if (i < idx) {
+		/* pr_debug("++++++ found duplicate event 0x%02x i=%d\n", event, i); */
+		return -1;
+	}
+
+	return 0;
+}
+
+static void armv6_pmu_hw_start(struct met_pmu *pmu, int count)
+{
+	int i;
+	int generic = count - 1;
+
+	armv6_pmu_hw_reset_all();
+
+	for (i = 0; i < generic; i++) {
+		if (pmu[i].mode == MODE_POLLING)
+			armv6pmu_enable_event(i, pmu[i].event);
+	}
+
+	if (pmu[count - 1].mode == MODE_POLLING)
+		armv6pmu_enable_event(2, pmu[2].event);
+
+	armv6_pmu_control_write(ARMV6_PMCR_ENABLE);
+}
+
+static void armv6_pmu_hw_stop(int count)
+{
+	armv6_pmu_hw_reset_all();
+}
+
+static unsigned int armv6_pmu_hw_polling(struct met_pmu *pmu, int count, unsigned int *pmu_value)
+{
+	int i, cnt = 0;
+	int generic = count - 1;
+
+	for (i = 0; i < generic; i++) {
+		if (pmu[i].mode == MODE_POLLING) {
+			pmu_value[cnt] = armv6_pmu_read_count(i);
+			cnt++;
+		}
+	}
+
+	if (pmu[count - 1].mode == MODE_POLLING) {
+		pmu_value[cnt] = armv6_pmu_read_count(2);
+		cnt++;
+	}
+
+	armv6_pmu_control_write(ARMV6_PMCR_ENABLE | ARMV6_PMCR_CTR01_RESET |
+				ARMV6_PMCR_CCOUNT_RESET);
+
+	return cnt;
+}
+
+struct cpu_pmu_hw armv6_pmu = {
+	.name = "armv6_pmu",
+	.check_event = armv6_pmu_hw_check_event,
+	.start = armv6_pmu_hw_start,
+	.stop = armv6_pmu_hw_stop,
+	.polling = armv6_pmu_hw_polling,
+};
+
+struct cpu_pmu_hw *v6_cpu_pmu_hw_init(int typeid)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(chips); i++)
+		if (chips[i].type == typeid)
+			break;
+
+	if (i == ARRAY_SIZE(chips))
+		return NULL;
+
+	armv6_pmu.nr_cnt = 3;
+
+	return &armv6_pmu;
+}
diff --git a/src/devtools/met-driver/4.14/common/v6_pmu_hw.h b/src/devtools/met-driver/4.14/common/v6_pmu_hw.h
new file mode 100644
index 0000000..a532f13
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/v6_pmu_hw.h
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+
+#ifndef __V6_PMU_HW_H__
+#define __V6_PMU_HW_H__
+
+extern struct cpu_pmu_hw armv6_pmu;
+extern struct cpu_pmu_hw *v6_cpu_pmu_hw_init(int typeid);
+
+#endif
diff --git a/src/devtools/met-driver/4.14/common/v7_pmu_hw.c b/src/devtools/met-driver/4.14/common/v7_pmu_hw.c
new file mode 100644
index 0000000..931d25e
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/v7_pmu_hw.c
@@ -0,0 +1,269 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "cpu_pmu.h"
+#include "v6_pmu_hw.h"
+
+/*******************************
+ *      ARM v7 operations      *
+ *******************************/
+#define ARMV7_PMCR_E		(1 << 0)	/* enable all counters */
+#define ARMV7_PMCR_P		(1 << 1)
+#define ARMV7_PMCR_C		(1 << 2)
+#define ARMV7_PMCR_D		(1 << 3)
+#define ARMV7_PMCR_X		(1 << 4)
+#define ARMV7_PMCR_DP		(1 << 5)
+#define ARMV7_PMCR_N_SHIFT	11		/* Number of counters supported */
+#define ARMV7_PMCR_N_MASK	0x1f
+#define ARMV7_PMCR_MASK		0x3f		/* mask for writable bits */
+
+static unsigned int armv7_get_ic(void)
+{
+	unsigned int value;
+	/* Read Main ID Register */
+	asm volatile ("mrc p15, 0, %0, c0, c0, 0":"=r" (value));
+
+	value = (value & 0xffff) >> 4;	/* primary part number */
+	return value;
+}
+
+static inline void armv7_pmu_counter_select(unsigned int idx)
+{
+	asm volatile ("mcr p15, 0, %0, c9, c12, 5"::"r" (idx));
+	isb();
+}
+
+static inline void armv7_pmu_type_select(unsigned int idx, unsigned int type)
+{
+	armv7_pmu_counter_select(idx);
+	asm volatile ("mcr p15, 0, %0, c9, c13, 1"::"r" (type));
+}
+
+static inline unsigned int armv7_pmu_read_count(unsigned int idx)
+{
+	unsigned int value;
+
+	if (idx == 31) {
+		asm volatile ("mrc p15, 0, %0, c9, c13, 0":"=r" (value));
+	} else {
+		armv7_pmu_counter_select(idx);
+		asm volatile ("mrc p15, 0, %0, c9, c13, 2":"=r" (value));
+	}
+	return value;
+}
+
+static inline void armv7_pmu_write_count(int idx, u32 value)
+{
+	if (idx == 31) {
+		asm volatile ("mcr p15, 0, %0, c9, c13, 0"::"r" (value));
+	} else {
+		armv7_pmu_counter_select(idx);
+		asm volatile ("mcr p15, 0, %0, c9, c13, 2"::"r" (value));
+	}
+}
+
+static inline void armv7_pmu_enable_count(unsigned int idx)
+{
+	asm volatile ("mcr p15, 0, %0, c9, c12, 1"::"r" (1 << idx));
+}
+
+static inline void armv7_pmu_disable_count(unsigned int idx)
+{
+	asm volatile ("mcr p15, 0, %0, c9, c12, 2"::"r" (1 << idx));
+}
+
+static inline void armv7_pmu_enable_intr(unsigned int idx)
+{
+	asm volatile ("mcr p15, 0, %0, c9, c14, 1"::"r" (1 << idx));
+}
+
+static inline void armv7_pmu_disable_intr(unsigned int idx)
+{
+	asm volatile ("mcr p15, 0, %0, c9, c14, 2"::"r" (1 << idx));
+}
+
+static inline unsigned int armv7_pmu_overflow(void)
+{
+	unsigned int val;
+
+	asm volatile ("mrc p15, 0, %0, c9, c12, 3":"=r" (val));	/* read */
+	asm volatile ("mcr p15, 0, %0, c9, c12, 3"::"r" (val));
+	return val;
+}
+
+static inline unsigned int armv7_pmu_control_read(void)
+{
+	u32 val;
+
+	asm volatile ("mrc p15, 0, %0, c9, c12, 0":"=r" (val));
+	return val;
+}
+
+static inline void armv7_pmu_control_write(unsigned int val)
+{
+	val &= ARMV7_PMCR_MASK;
+	isb();
+	asm volatile ("mcr p15, 0, %0, c9, c12, 0"::"r" (val));
+}
+
+static int armv7_pmu_hw_get_counters(void)
+{
+	int count = armv7_pmu_control_read();
+	/* N, bits[15:11] */
+	count = ((count >> ARMV7_PMCR_N_SHIFT) & ARMV7_PMCR_N_MASK);
+	return count;
+}
+
+static void armv7_pmu_hw_reset_all(int generic_counters)
+{
+	int i;
+
+	armv7_pmu_control_write(ARMV7_PMCR_C | ARMV7_PMCR_P);
+	/* generic counter */
+	for (i = 0; i < generic_counters; i++) {
+		armv7_pmu_disable_intr(i);
+		armv7_pmu_disable_count(i);
+	}
+	/* cycle counter */
+	armv7_pmu_disable_intr(31);
+	armv7_pmu_disable_count(31);
+	armv7_pmu_overflow();	/* clear overflow */
+}
+
+/***********************************
+ *      MET ARM v7 operations      *
+ ***********************************/
+enum ARM_TYPE {
+	CORTEX_A7 = 0xC07,
+	CORTEX_A9 = 0xC09,
+	CORTEX_A12 = 0xC0D,
+	CORTEX_A15 = 0xC0F,
+	CORTEX_A17 = 0xC0E,
+	CORTEX_A53 = 0xD03,
+	CORTEX_A57 = 0xD07,
+	CHIP_UNKNOWN = 0xFFF
+};
+
+struct chip_pmu {
+	enum ARM_TYPE type;
+};
+
+static struct chip_pmu chips[] = {
+	{CORTEX_A7},
+	{CORTEX_A9},
+	{CORTEX_A12},
+	{CORTEX_A15},
+	{CORTEX_A17},
+	{CORTEX_A53},
+	{CORTEX_A57},
+};
+
+static int armv7_pmu_hw_check_event(struct met_pmu *pmu, int idx, int event)
+{
+	int i;
+
+	/* Check if event is duplicate */
+	for (i = 0; i < idx; i++) {
+		if (pmu[i].event == event)
+			break;
+	}
+	if (i < idx) {
+		/* pr_debug("++++++ found duplicate event 0x%02x i=%d\n", event, i); */
+		return -1;
+	}
+
+	return 0;
+}
+
+static void armv7_pmu_hw_start(struct met_pmu *pmu, int count)
+{
+	int i;
+	int generic = count - 1;
+
+	armv7_pmu_hw_reset_all(generic);
+	for (i = 0; i < generic; i++) {
+		if (pmu[i].mode == MODE_POLLING) {
+			armv7_pmu_type_select(i, pmu[i].event);
+			armv7_pmu_enable_count(i);
+		}
+	}
+	if (pmu[count - 1].mode == MODE_POLLING) {	/* cycle counter */
+		armv7_pmu_enable_count(31);
+	}
+	armv7_pmu_control_write(ARMV7_PMCR_E);
+}
+
+static void armv7_pmu_hw_stop(int count)
+{
+	int generic = count - 1;
+
+	armv7_pmu_hw_reset_all(generic);
+}
+
+static unsigned int armv7_pmu_hw_polling(struct met_pmu *pmu, int count, unsigned int *pmu_value)
+{
+	int i, cnt = 0;
+	int generic = count - 1;
+
+	for (i = 0; i < generic; i++) {
+		if (pmu[i].mode == MODE_POLLING) {
+			pmu_value[cnt] = armv7_pmu_read_count(i);
+			cnt++;
+		}
+	}
+	if (pmu[count - 1].mode == MODE_POLLING) {
+		pmu_value[cnt] = armv7_pmu_read_count(31);
+		cnt++;
+	}
+	armv7_pmu_control_write(ARMV7_PMCR_C | ARMV7_PMCR_P | ARMV7_PMCR_E);
+
+	return cnt;
+}
+
+static struct met_pmu	pmus[MXNR_CPU][MXNR_PMU_EVENTS];
+
+struct cpu_pmu_hw armv7_pmu = {
+	.name = "armv7_pmu",
+	.check_event = armv7_pmu_hw_check_event,
+	.start = armv7_pmu_hw_start,
+	.stop = armv7_pmu_hw_stop,
+	.polling = armv7_pmu_hw_polling,
+};
+
+struct cpu_pmu_hw *cpu_pmu_hw_init(void)
+{
+	int i;
+	enum ARM_TYPE type;
+	struct cpu_pmu_hw *pmu;
+
+	type = (enum ARM_TYPE)armv7_get_ic();
+	for (i = 0; i < ARRAY_SIZE(chips); i++)
+		if (chips[i].type == type)
+			break;
+
+	if (i < ARRAY_SIZE(chips)) {
+		armv7_pmu.nr_cnt = armv7_pmu_hw_get_counters() + 1;
+		pmu = &armv7_pmu;
+	} else
+		pmu = v6_cpu_pmu_hw_init(type);
+
+	if (pmu == NULL)
+		return NULL;
+
+	for (i = 0; i < MXNR_CPU; i++) {
+		pmu->event_count[i] = pmu->nr_cnt;
+		pmu->pmu[i] = pmus[i];
+	}
+
+	return pmu;
+}
diff --git a/src/devtools/met-driver/4.14/common/v8_dsu_hw.c b/src/devtools/met-driver/4.14/common/v8_dsu_hw.c
new file mode 100644
index 0000000..0b58c9a
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/v8_dsu_hw.c
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <asm/cpu.h>
+#include "met_kernel_symbol.h"
+#include "cpu_dsu.h"
+
+//dsu support 6 event
+#define DSU_EVENT_MAX_CNT 6
+
+static int armv8_dsu_hw_check_event(struct met_dsu *pmu, int idx, int event)
+{
+	int i;
+
+	/* Check if event is duplicate */
+	for (i = 0; i < idx; i++) {
+		if (pmu[i].event == event)
+			break;
+	}
+	if (i < idx) {
+		/* pr_debug("++++++ found duplicate event 0x%02x i=%d\n", event, i); */
+		return -1;
+	}
+	return 0;
+}
+
+
+static struct met_dsu	pmus[MXNR_DSU_EVENTS];
+
+struct cpu_dsu_hw armv8_dsu = {
+	.name = "armv8_dsu",
+	.check_event = armv8_dsu_hw_check_event,
+};
+
+static void init_dsu(void)
+{
+	armv8_dsu.event_count = DSU_EVENT_MAX_CNT;
+}
+
+struct cpu_dsu_hw *cpu_dsu_hw_init(void)
+{
+
+	init_dsu();
+	armv8_dsu.pmu = pmus;
+	return &armv8_dsu;
+}
diff --git a/src/devtools/met-driver/4.14/common/v8_pmu_hw.c b/src/devtools/met-driver/4.14/common/v8_pmu_hw.c
new file mode 100644
index 0000000..ebfd249
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/v8_pmu_hw.c
@@ -0,0 +1,259 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <asm/cpu.h>
+#include "met_kernel_symbol.h"
+#include "cpu_pmu.h"
+
+/*******************************
+ *      ARM v8 operations      *
+ *******************************/
+/*
+ * Per-CPU PMCR: config reg
+ */
+#define ARMV8_PMCR_E		(1 << 0)	/* Enable all counters */
+#define ARMV8_PMCR_P		(1 << 1)	/* Reset all counters */
+#define ARMV8_PMCR_C		(1 << 2)	/* Cycle counter reset */
+#define ARMV8_PMCR_D		(1 << 3)	/* CCNT counts every 64th cpu cycle */
+#define ARMV8_PMCR_X		(1 << 4)	/* Export to ETM */
+#define ARMV8_PMCR_DP		(1 << 5)	/* Disable CCNT if non-invasive debug */
+#define	ARMV8_PMCR_N_SHIFT	11		/* Number of counters supported */
+#define	ARMV8_PMCR_N_MASK	0x1f
+#define	ARMV8_PMCR_MASK		0x3f		/* Mask for writable bits */
+
+/*
+ * PMOVSR: counters overflow flag status reg
+ */
+#define	ARMV8_OVSR_MASK		0xffffffff	/* Mask for writable bits */
+#define	ARMV8_OVERFLOWED_MASK	ARMV8_OVSR_MASK
+
+static inline void armv8_pmu_counter_select(unsigned int idx)
+{
+	asm volatile ("msr pmselr_el0, %0"::"r" (idx));
+	isb();
+}
+
+static inline void armv8_pmu_type_select(unsigned int idx, unsigned int type)
+{
+	armv8_pmu_counter_select(idx);
+	asm volatile ("msr pmxevtyper_el0, %0"::"r" (type));
+}
+
+static inline unsigned int armv8_pmu_read_count(unsigned int idx)
+{
+	unsigned int value;
+
+	if (idx == 31) {
+		asm volatile ("mrs %0, pmccntr_el0":"=r" (value));
+	} else {
+		armv8_pmu_counter_select(idx);
+		asm volatile ("mrs %0, pmxevcntr_el0":"=r" (value));
+	}
+	return value;
+}
+
+static inline void armv8_pmu_enable_count(unsigned int idx)
+{
+	asm volatile ("msr pmcntenset_el0, %0"::"r" (1 << idx));
+}
+
+static inline void armv8_pmu_disable_count(unsigned int idx)
+{
+	asm volatile ("msr pmcntenclr_el0, %0"::"r" (1 << idx));
+}
+
+static inline void armv8_pmu_enable_intr(unsigned int idx)
+{
+	asm volatile ("msr pmintenset_el1, %0"::"r" (1 << idx));
+}
+
+static inline void armv8_pmu_disable_intr(unsigned int idx)
+{
+	asm volatile ("msr pmintenclr_el1, %0"::"r" (1 << idx));
+	isb();
+	asm volatile ("msr pmovsclr_el0, %0"::"r" (1 << idx));
+	isb();
+}
+
+static inline unsigned int armv8_pmu_overflow(void)
+{
+	unsigned int val;
+
+	asm volatile ("mrs %0, pmovsclr_el0":"=r" (val));	/* read */
+	val &= ARMV8_OVSR_MASK;
+	asm volatile ("mrs %0, pmovsclr_el0"::"r" (val));
+	return val;
+}
+
+static inline unsigned int armv8_pmu_control_read(void)
+{
+	unsigned int val;
+
+	asm volatile ("mrs %0, pmcr_el0":"=r" (val));
+	return val;
+}
+
+static inline void armv8_pmu_control_write(u32 val)
+{
+	val &= ARMV8_PMCR_MASK;
+	isb();
+	asm volatile ("msr pmcr_el0, %0"::"r" (val));
+}
+
+static void armv8_pmu_hw_reset_all(int generic_counters)
+{
+	int i;
+
+	armv8_pmu_control_write(ARMV8_PMCR_C | ARMV8_PMCR_P);
+	/* generic counter */
+	for (i = 0; i < generic_counters; i++) {
+		armv8_pmu_disable_intr(i);
+		armv8_pmu_disable_count(i);
+	}
+	/* cycle counter */
+	armv8_pmu_disable_intr(31);
+	armv8_pmu_disable_count(31);
+	armv8_pmu_overflow();	/* clear overflow */
+}
+
+/***********************************
+ *      MET ARM v8 operations      *
+ ***********************************/
+enum ARM_TYPE {
+	CORTEX_A53 = 0xD03,
+	CORTEX_A35 = 0xD04,
+	CORTEX_A55 = 0xD05,
+	CORTEX_A57 = 0xD07,
+	CORTEX_A72 = 0xD08,
+	CORTEX_A73 = 0xD09,
+	CORTEX_A75 = 0xD0A,
+	CHIP_UNKNOWN = 0xFFF
+};
+
+struct chip_pmu {
+	enum ARM_TYPE	type;
+	unsigned int	event_count;
+};
+
+static struct chip_pmu	chips[] = {
+	{CORTEX_A35, 6+1},
+	{CORTEX_A53, 6+1},
+	{CORTEX_A55, 6+1},
+	{CORTEX_A57, 6+1},
+	{CORTEX_A72, 6+1},
+	{CORTEX_A73, 6+1},
+	{CORTEX_A75, 6+1},
+};
+
+static int armv8_pmu_hw_check_event(struct met_pmu *pmu, int idx, int event)
+{
+	int i;
+
+	/* Check if event is duplicate */
+	for (i = 0; i < idx; i++) {
+		if (pmu[i].event == event)
+			break;
+	}
+	if (i < idx) {
+		/* pr_debug("++++++ found duplicate event 0x%02x i=%d\n", event, i); */
+		return -1;
+	}
+
+	return 0;
+}
+
+static void armv8_pmu_hw_start(struct met_pmu *pmu, int count)
+{
+	int i;
+	int generic = count - 1;
+
+	armv8_pmu_hw_reset_all(generic);
+	for (i = 0; i < generic; i++) {
+		if (pmu[i].mode == MODE_POLLING) {
+			armv8_pmu_type_select(i, pmu[i].event);
+			armv8_pmu_enable_count(i);
+		}
+	}
+	if (pmu[count - 1].mode == MODE_POLLING) {	/* cycle counter */
+		armv8_pmu_enable_count(31);
+	}
+	armv8_pmu_control_write(ARMV8_PMCR_E);
+}
+
+static void armv8_pmu_hw_stop(int count)
+{
+	int generic = count - 1;
+
+	armv8_pmu_hw_reset_all(generic);
+}
+
+static unsigned int armv8_pmu_hw_polling(struct met_pmu *pmu, int count, unsigned int *pmu_value)
+{
+	int i, cnt = 0;
+	int generic = count - 1;
+
+	for (i = 0; i < generic; i++) {
+		if (pmu[i].mode == MODE_POLLING) {
+			pmu_value[cnt] = armv8_pmu_read_count(i);
+			cnt++;
+		}
+	}
+	if (pmu[count - 1].mode == MODE_POLLING) {
+		pmu_value[cnt] = armv8_pmu_read_count(31);
+		cnt++;
+	}
+	armv8_pmu_control_write(ARMV8_PMCR_C | ARMV8_PMCR_P | ARMV8_PMCR_E);
+
+	return cnt;
+}
+
+static struct met_pmu	pmus[MXNR_CPU][MXNR_PMU_EVENTS];
+
+struct cpu_pmu_hw armv8_pmu = {
+	.name = "armv8_pmu",
+	.check_event = armv8_pmu_hw_check_event,
+	.start = armv8_pmu_hw_start,
+	.stop = armv8_pmu_hw_stop,
+	.polling = armv8_pmu_hw_polling,
+};
+
+static void init_pmus(void)
+{
+	int	cpu;
+	int	i;
+
+	for_each_possible_cpu(cpu) {
+		struct cpuinfo_arm64 *cpuinfo;
+		if (cpu >= MXNR_CPU)
+			continue;
+		met_get_cpuinfo_symbol(cpu, &cpuinfo);
+		/* PR_BOOTMSG("CPU[%d]: reg_midr = %x\n", cpu, cpuinfo->reg_midr); */
+		for (i = 0; i < ARRAY_SIZE(chips); i++) {
+			if (chips[i].type == (cpuinfo->reg_midr & 0xffff) >> 4) {
+				armv8_pmu.event_count[cpu] = chips[i].event_count;
+				break;
+			}
+		}
+	}
+}
+
+struct cpu_pmu_hw *cpu_pmu_hw_init(void)
+{
+	int	cpu;
+
+	init_pmus();
+	for (cpu = 0; cpu < MXNR_CPU; cpu++)
+		armv8_pmu.pmu[cpu] = pmus[cpu];
+
+	return &armv8_pmu;
+}
diff --git a/src/devtools/met-driver/4.14/common/version.h b/src/devtools/met-driver/4.14/common/version.h
new file mode 100644
index 0000000..ae304ec
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/version.h
@@ -0,0 +1,14 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define MET_BACKEND_VERSION "6.2.0"
diff --git a/src/devtools/met-driver/4.14/default/Kbuild b/src/devtools/met-driver/4.14/default/Kbuild
new file mode 100644
index 0000000..9b2bb87
--- /dev/null
+++ b/src/devtools/met-driver/4.14/default/Kbuild
@@ -0,0 +1,6 @@
+obj-m := met.o
+
+ccflags-y += -I$(srctree)/include/
+
+met-y := default/met_main.o
+
diff --git a/src/devtools/met-driver/4.14/default/met_main.c b/src/devtools/met-driver/4.14/default/met_main.c
new file mode 100644
index 0000000..6537e38
--- /dev/null
+++ b/src/devtools/met-driver/4.14/default/met_main.c
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/hrtimer.h>
+#include <linux/cpu.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/profile.h>
+#include <linux/dcache.h>
+#include <linux/types.h>
+#include <linux/dcookies.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+
+static int __init met_drv_init(void)
+{
+	pr_info("Hello MET default module\n");
+	return 0;
+}
+
+static void __exit met_drv_exit(void)
+{
+}
+module_init(met_drv_init);
+module_exit(met_drv_exit);
+
+MODULE_AUTHOR("DT_DM5");
+MODULE_DESCRIPTION("MET_DEFAULT");
+MODULE_LICENSE("GPL");
diff --git a/src/devtools/met-driver/4.14/mt2731/Kbuild.platform.include b/src/devtools/met-driver/4.14/mt2731/Kbuild.platform.include
new file mode 100644
index 0000000..1061e98
--- /dev/null
+++ b/src/devtools/met-driver/4.14/mt2731/Kbuild.platform.include
@@ -0,0 +1,33 @@
+################################################################################
+# Include Path
+################################################################################
+MET_VCOREDVFS_INC := $(KERNEL_SRC)/drivers/misc/mediatek/base/power/include/vcorefs_v3
+MET_PTPOD_INC := $(KERNEL_SRC)/drivers/misc/mediatek/base/power/cpufreq_v1/src/mach/$(MTK_PLATFORM)/
+
+
+################################################################################
+# Feature Spec
+# CPUPMU_VERSION: V8_0/V8_2
+# EMI_SEDA_VERSION: SEDA2/SEDA3/SEDA3_5
+# EMI_DRAMC_VERSION: V1/V2
+################################################################################
+CPUPMU_VERSION := V8_0
+EMI_SEDA_VERSION := SEDA3_5
+EMI_DRAMC_VERSION := V2
+
+
+################################################################################
+# Feature On/Off
+################################################################################
+FEATURE_SPMTWAM := n
+FEATURE_CPUDSU := n
+FEATURE_SSPM_EMI := n
+FEATURE_AP_EMI := y
+FEATURE_GPU := n
+FEATURE_VCOREDVFS := n
+FEATURE_PTPOD := n
+FEATURE_WALLTIME := n
+FEATURE_SMI=n
+FEATURE_EVENT_POWER=n
+FEATURE_ONDIEMET := n
+
diff --git a/src/devtools/met-driver/4.14/mt2731/Kbuild.yocto b/src/devtools/met-driver/4.14/mt2731/Kbuild.yocto
new file mode 100644
index 0000000..57f6fa6
--- /dev/null
+++ b/src/devtools/met-driver/4.14/mt2731/Kbuild.yocto
@@ -0,0 +1,45 @@
+MET_PLF := $(MTK_PLATFORM)
+
+met-y += $(MET_PLF)/plf_trace.o
+
+################################################################################
+# MET_AP_EMI
+################################################################################
+FEATURE_AP_EMI := $(if $(FEATURE_AP_EMI),$(FEATURE_AP_EMI),y)
+$(info FEATURE_AP_EMI = $(FEATURE_AP_EMI))
+ifneq ($(FEATURE_AP_EMI), n)
+    MET_AP_EMI := y
+    # for mtk_dramc.h
+    ifneq ("$(wildcard $(srctree)/drivers/misc/mediatek/dramc/$(MTK_PLATFORM)/mtk_dramc.h)","")
+        ccflags-y += -I$(srctree)/drivers/misc/mediatek/dramc/$(MTK_PLATFORM)/
+    else
+        MET_AP_EMI = n
+        $(info ======== Missing $(srctree)/drivers/misc/mediatek/dramc/$(MTK_PLATFORM)/mtk_dramc.h ========)
+        $(info ======== disable MET_AP_EMI ========)
+    endif
+else
+    MET_AP_EMI := n
+endif
+
+$(info EMI_SEDA_VERSION = $(EMI_SEDA_VERSION))
+$(info EMI_DRAMC_VERSION = $(EMI_DRAMC_VERSION))
+ifeq ("$(EMI_DRAMC_VERSION)", "V1")
+    ccflags-y += -I$(MET_PLF_DIR)/dramc/V1/
+    met-$(MET_AP_EMI) += $(MET_PLF)/dramc/V1/mtk_dramc.o
+endif
+ifeq ("$(EMI_DRAMC_VERSION)", "V2")
+    ccflags-y += -I$(MET_PLF_DIR)/dramc/V2/
+    met-$(MET_AP_EMI) += $(MET_PLF)/dramc/V2/mtk_dramc.o
+endif
+ifeq ("$(EMI_SEDA_VERSION)", "SEDA2")
+    met-$(MET_AP_EMI) += $(MET_PLF)/emi/SEDA2/met_emi.o
+    met-$(MET_AP_EMI) += $(MET_PLF)/emi/SEDA2/mtk_emi_bm.o
+endif
+ifeq ("$(EMI_SEDA_VERSION)", "SEDA3")
+    met-$(MET_AP_EMI) += $(MET_PLF)/emi/SEDA3/met_emi.o
+    met-$(MET_AP_EMI) += $(MET_PLF)/emi/SEDA3/mtk_emi_bm.o
+endif
+ifeq ("$(EMI_SEDA_VERSION)", "SEDA3_5")
+    met-$(MET_AP_EMI) += $(MET_PLF)/emi/SEDA3_5/met_emi.o
+    met-$(MET_AP_EMI) += $(MET_PLF)/emi/SEDA3_5/mtk_emi_bm.o
+endif
diff --git a/src/devtools/met-driver/4.14/mt2731/dramc/V1/mtk_dramc.c b/src/devtools/met-driver/4.14/mt2731/dramc/V1/mtk_dramc.c
new file mode 100644
index 0000000..210f200
--- /dev/null
+++ b/src/devtools/met-driver/4.14/mt2731/dramc/V1/mtk_dramc.c
@@ -0,0 +1,293 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/io.h>
+#include <mt-plat/sync_write.h>
+#include <mt-plat/mtk_io.h>
+
+#include "mtk_dramc_reg.h"
+
+#define BM_REQ_OK				(0)
+#define BM_ERR_WRONG_REQ			(-1)
+#define BM_ERR_OVERRUN				(-2)
+
+
+void __iomem *BaseAddrDRAMC[MAX_DRAMC_CHANN];
+void __iomem *BaseAddrDDRPHY_AO[2];
+void __iomem *BaseAddrDRAMC0_AO;
+
+
+unsigned int MET_DRAMC_GetPageHitCount(enum DRAMC_Cnt_Type CountType, int chann)
+{
+	unsigned int iCount;
+	unsigned long addr_base = ADDR_DRAMC0;
+
+	if (chann == 0)
+		addr_base = ADDR_DRAMC0;
+	else if (chann == 1)
+		addr_base = ADDR_DRAMC1;
+	else if (chann == 2)
+		addr_base = ADDR_DRAMC2;
+	else if (chann == 3)
+		addr_base = ADDR_DRAMC3;
+	else
+		return BM_ERR_WRONG_REQ;
+
+	switch (CountType) {
+	case DRAMC_R2R:
+		iCount = readl(IOMEM(addr_base + DRAMC_R2R_PAGE_HIT));
+		break;
+	case DRAMC_R2W:
+		iCount = readl(IOMEM(addr_base + DRAMC_R2W_PAGE_HIT));
+		break;
+	case DRAMC_W2R:
+		iCount = readl(IOMEM(addr_base + DRAMC_W2R_PAGE_HIT));
+		break;
+	case DRAMC_W2W:
+		iCount = readl(IOMEM(addr_base + DRAMC_W2W_PAGE_HIT));
+		break;
+	case DRAMC_ALL:
+		iCount = readl(IOMEM(addr_base + DRAMC_R2R_PAGE_HIT)) +
+			 readl(IOMEM(addr_base + DRAMC_R2W_PAGE_HIT)) +
+			 readl(IOMEM(addr_base + DRAMC_W2R_PAGE_HIT)) +
+			 readl(IOMEM(addr_base + DRAMC_W2W_PAGE_HIT));
+		break;
+	default:
+		return BM_ERR_WRONG_REQ;
+	}
+
+	return iCount;
+}
+
+unsigned int MET_DRAMC_GetPageMissCount(enum DRAMC_Cnt_Type CountType, int chann)
+{
+	unsigned int iCount;
+	unsigned long addr_base = ADDR_DRAMC0;
+
+	if (chann == 0)
+		addr_base = ADDR_DRAMC0;
+	else if (chann == 1)
+		addr_base = ADDR_DRAMC1;
+	else if (chann == 2)
+		addr_base = ADDR_DRAMC2;
+	else if (chann == 3)
+		addr_base = ADDR_DRAMC3;
+	else
+		return BM_ERR_WRONG_REQ;
+
+	switch (CountType) {
+	case DRAMC_R2R:
+		iCount = readl(IOMEM(addr_base + DRAMC_R2R_PAGE_MISS));
+		break;
+	case DRAMC_R2W:
+		iCount = readl(IOMEM(addr_base + DRAMC_R2W_PAGE_MISS));
+		break;
+	case DRAMC_W2R:
+		iCount = readl(IOMEM(addr_base + DRAMC_W2R_PAGE_MISS));
+		break;
+	case DRAMC_W2W:
+		iCount = readl(IOMEM(addr_base + DRAMC_W2W_PAGE_MISS));
+		break;
+	case DRAMC_ALL:
+		iCount = readl(IOMEM(addr_base + DRAMC_R2R_PAGE_MISS)) +
+			 readl(IOMEM(addr_base + DRAMC_R2W_PAGE_MISS)) +
+			 readl(IOMEM(addr_base + DRAMC_W2R_PAGE_MISS)) +
+			 readl(IOMEM(addr_base + DRAMC_W2W_PAGE_MISS));
+		break;
+	default:
+		return BM_ERR_WRONG_REQ;
+	}
+
+	return iCount;
+}
+
+
+unsigned int MET_DRAMC_GetInterbankCount(enum DRAMC_Cnt_Type CountType, int chann)
+{
+	unsigned int iCount;
+	unsigned long addr_base = ADDR_DRAMC0;
+
+	if (chann == 0)
+		addr_base = ADDR_DRAMC0;
+	else if (chann == 1)
+		addr_base = ADDR_DRAMC1;
+	else if (chann == 2)
+		addr_base = ADDR_DRAMC2;
+	else if (chann == 3)
+		addr_base = ADDR_DRAMC3;
+	else
+		return BM_ERR_WRONG_REQ;
+
+	switch (CountType) {
+	case DRAMC_R2R:
+		iCount = readl(IOMEM(addr_base + DRAMC_R2R_INTERBANK));
+		break;
+	case DRAMC_R2W:
+		iCount = readl(IOMEM(addr_base + DRAMC_R2W_INTERBANK));
+		break;
+	case DRAMC_W2R:
+		iCount = readl(IOMEM(addr_base + DRAMC_W2R_INTERBANK));
+		break;
+	case DRAMC_W2W:
+		iCount = readl(IOMEM(addr_base + DRAMC_W2W_INTERBANK));
+		break;
+	case DRAMC_ALL:
+		iCount = readl(IOMEM(addr_base + DRAMC_R2R_INTERBANK)) +
+			 readl(IOMEM(addr_base + DRAMC_R2W_INTERBANK)) +
+			 readl(IOMEM(addr_base + DRAMC_W2R_INTERBANK)) +
+			 readl(IOMEM(addr_base + DRAMC_W2W_INTERBANK));
+		break;
+	default:
+		return BM_ERR_WRONG_REQ;
+	}
+
+	return iCount;
+}
+
+
+unsigned int MET_DRAMC_GetIdleCount(int chann)
+{
+	unsigned long addr_base = ADDR_DRAMC0;
+
+	if (chann == 0)
+		addr_base = ADDR_DRAMC0;
+	else if (chann == 1)
+		addr_base = ADDR_DRAMC1;
+	else if (chann == 2)
+		addr_base = ADDR_DRAMC2;
+	else if (chann == 3)
+		addr_base = ADDR_DRAMC3;
+	else
+		return BM_ERR_WRONG_REQ;
+
+	return readl(IOMEM(addr_base + DRAMC_IDLE_COUNT));
+}
+
+
+unsigned int MET_DRAMC_Misc_Status(int chann)
+{
+	return 0;
+}
+
+
+unsigned int MET_DRAMC_SPCMDRESP(int chann)
+{
+	unsigned long addr_base = ADDR_DRAMC0;
+
+	if (chann == 0)
+		addr_base = ADDR_DRAMC0;
+	else if (chann == 1)
+		addr_base = ADDR_DRAMC1;
+	else if (chann == 2)
+		addr_base = ADDR_DRAMC2;
+	else if (chann == 3)
+		addr_base = ADDR_DRAMC3;
+	else
+		return BM_ERR_WRONG_REQ;
+
+	return readl(IOMEM(addr_base + DRAMC_MISC_SPCMDRESP));
+}
+
+
+unsigned int MET_DRAMC_RefPop(int chann)
+{
+	unsigned long addr_base = ADDR_DRAMC0;
+
+	if (chann == 0)
+		addr_base = ADDR_DRAMC0;
+	else if (chann == 1)
+		addr_base = ADDR_DRAMC1;
+	else if (chann == 2)
+		addr_base = ADDR_DRAMC2;
+	else if (chann == 3)
+		addr_base = ADDR_DRAMC3;
+	else
+		return BM_ERR_WRONG_REQ;
+
+	return readl(IOMEM(addr_base + DRAMC_REFRESH_POP));
+}
+
+
+unsigned int MET_DRAMC_Free26M(int chann)
+{
+	unsigned long addr_base = ADDR_DRAMC0;
+
+	if (chann == 0)
+		addr_base = ADDR_DRAMC0;
+	else if (chann == 1)
+		addr_base = ADDR_DRAMC1;
+	else if (chann == 2)
+		addr_base = ADDR_DRAMC2;
+	else if (chann == 3)
+		addr_base = ADDR_DRAMC3;
+	else
+		return BM_ERR_WRONG_REQ;
+
+	return readl(IOMEM(addr_base + DRAMC_FREERUN_26M));
+}
+
+
+unsigned int MET_DRAMC_RByte(int chann)
+{
+	return 0;
+}
+
+
+unsigned int MET_DRAMC_WByte(int chann)
+{
+	return 0;
+}
+
+
+unsigned int MET_DRAMC_DCM_CTRL(int chann)
+{
+	return 0;
+}
+
+
+void MET_DRAMC_GetDebugCounter(int *value, int chann)
+{
+	int i;
+	unsigned long addr_base = ADDR_DRAMC0;
+
+	for (i = 0; i < chann; i++) {
+		if (i == 0)
+			addr_base = ADDR_DRAMC0;
+		else if (i == 1)
+			addr_base = ADDR_DRAMC1;
+		else if (i == 2)
+			addr_base = ADDR_DRAMC2;
+		else if (i == 3)
+			addr_base = ADDR_DRAMC3;
+		else
+			return;
+
+		value[DRAMC_Debug_MAX_CNT * i + BM_FREERUN_26M] = MET_DRAMC_Free26M(i);
+
+		/*not support these register , set the initial value 0*/
+		value[DRAMC_Debug_MAX_CNT * i + BM_RK0_PRE_STANDBY] =   0;
+		value[DRAMC_Debug_MAX_CNT * i + BM_RK0_PRE_POWERDOWN] = 0;
+		value[DRAMC_Debug_MAX_CNT * i + BM_RK0_ACT_STANDBY] =   0;
+		value[DRAMC_Debug_MAX_CNT * i + BM_RK0_ACT_POWERDOWN] = 0;
+		value[DRAMC_Debug_MAX_CNT * i + BM_RK1_PRE_STANDBY] =   0;
+		value[DRAMC_Debug_MAX_CNT * i + BM_RK1_PRE_POWERDOWN] = 0;
+		value[DRAMC_Debug_MAX_CNT * i + BM_RK1_ACT_STANDBY] =   0;
+		value[DRAMC_Debug_MAX_CNT * i + BM_RK1_ACT_POWERDOWN] = 0;
+		value[DRAMC_Debug_MAX_CNT * i + BM_RK2_PRE_STANDBY] =   0;
+		value[DRAMC_Debug_MAX_CNT * i + BM_RK2_PRE_POWERDOWN] = 0;
+		value[DRAMC_Debug_MAX_CNT * i + BM_RK2_ACT_STANDBY] =   0;
+		value[DRAMC_Debug_MAX_CNT * i + BM_RK2_ACT_POWERDOWN] = 0;
+	}
+}
diff --git a/src/devtools/met-driver/4.14/mt2731/dramc/V1/mtk_dramc_reg.h b/src/devtools/met-driver/4.14/mt2731/dramc/V1/mtk_dramc_reg.h
new file mode 100644
index 0000000..5a2c7c8
--- /dev/null
+++ b/src/devtools/met-driver/4.14/mt2731/dramc/V1/mtk_dramc_reg.h
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#ifndef __MTK_DRAMC_REG_H__
+#define __MTK_DRAMC_REG_H__
+#include <linux/compiler.h>
+
+#define DRAMC_VER 1
+
+#define MAX_DRAMC_CHANN 4
+extern void __iomem *BaseAddrDRAMC[MAX_DRAMC_CHANN];
+extern void __iomem *BaseAddrDDRPHY_AO[2];
+extern void __iomem *BaseAddrDRAMC0_AO;
+
+enum BM_DRAMC_DTS_INDEX {
+	DRAMC_DTS_DRAMC0_AO = 0x0,
+	DRAMC_DTS_DRAMC0_NAO = 0x4,
+	DRAMC_DTS_DRAMC1_NAO = 0x5,
+	DRAMC_DTS_DRAMC2_NAO = 0x6,
+	DRAMC_DTS_DRAMC3_NAO = 0x7,
+	DRAMC_DTS_DDRPHY0_AO = 0x8,
+};
+
+enum DRAMC_Debug_Type {
+	BM_FREERUN_26M = 0x0,
+	BM_RK0_PRE_STANDBY,
+	BM_RK0_PRE_POWERDOWN,
+	BM_RK0_ACT_STANDBY,
+	BM_RK0_ACT_POWERDOWN,
+	BM_RK1_PRE_STANDBY,
+	BM_RK1_PRE_POWERDOWN,
+	BM_RK1_ACT_STANDBY,
+	BM_RK1_ACT_POWERDOWN,
+	BM_RK2_PRE_STANDBY,
+	BM_RK2_PRE_POWERDOWN,
+	BM_RK2_ACT_STANDBY,
+	BM_RK2_ACT_POWERDOWN,
+	DRAMC_Debug_MAX_CNT
+};
+
+enum DRAMC_Cnt_Type {
+	DRAMC_R2R,
+	DRAMC_R2W,
+	DRAMC_W2R,
+	DRAMC_W2W,
+	DRAMC_ALL
+};
+
+#define ADDR_DRAMC0     ((unsigned long)BaseAddrDRAMC[0])
+#define ADDR_DRAMC1     ((unsigned long)BaseAddrDRAMC[1])
+#define ADDR_DRAMC2     ((unsigned long)BaseAddrDRAMC[2])
+#define ADDR_DRAMC3     ((unsigned long)BaseAddrDRAMC[3])
+#define DRAMC_REFRESH_POP       0x2b8
+#define DRAMC_FREERUN_26M       0x2b4
+#define DRAMC_R2R_PAGE_HIT      0x280
+#define DRAMC_R2R_PAGE_MISS     0x284
+#define DRAMC_R2R_INTERBANK     0x288
+#define DRAMC_R2W_PAGE_HIT      0x28c
+#define DRAMC_R2W_PAGE_MISS     0x290
+#define DRAMC_R2W_INTERBANK     0x294
+#define DRAMC_W2R_PAGE_HIT      0x298
+#define DRAMC_W2R_PAGE_MISS     0x29c
+#define DRAMC_W2R_INTERBANK     0x2a0
+#define DRAMC_W2W_PAGE_HIT      0x2a4
+#define DRAMC_W2W_PAGE_MISS     0x2a8
+#define DRAMC_W2W_INTERBANK     0x2ac
+#define DRAMC_IDLE_COUNT        0x2b0
+#define DRAMC_MISC_SPCMDRESP    0x3b8
+
+extern unsigned int MET_DRAMC_GetPageHitCount(enum DRAMC_Cnt_Type CountType, int chann);
+extern unsigned int MET_DRAMC_GetPageMissCount(enum DRAMC_Cnt_Type CountType, int chann);
+extern unsigned int MET_DRAMC_GetInterbankCount(enum DRAMC_Cnt_Type CountType, int chann);
+extern unsigned int MET_DRAMC_GetIdleCount(int chann);
+extern unsigned int MET_DRAMC_Misc_Status(int chann);
+extern unsigned int MET_DRAMC_SPCMDRESP(int chann);
+extern unsigned int MET_DRAMC_RefPop(int chann);
+extern unsigned int MET_DRAMC_Free26M(int chann);
+extern unsigned int MET_DRAMC_RByte(int chann);
+extern unsigned int MET_DRAMC_WByte(int chann);
+extern unsigned int MET_DRAMC_DCM_CTRL(int chann);
+
+/* Debug Counter status */
+extern void MET_DRAMC_GetDebugCounter(int *value, int chann);
+
+
+#endif /* __MTK_DRAMC_REG_H__ */
diff --git a/src/devtools/met-driver/4.14/mt2731/dramc/V2/mtk_dramc.c b/src/devtools/met-driver/4.14/mt2731/dramc/V2/mtk_dramc.c
new file mode 100644
index 0000000..7d4762c
--- /dev/null
+++ b/src/devtools/met-driver/4.14/mt2731/dramc/V2/mtk_dramc.c
@@ -0,0 +1,355 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/io.h>
+#include <mt-plat/sync_write.h>
+#include <mt-plat/mtk_io.h>
+
+#include "mtk_dramc_reg.h"
+
+#define BM_REQ_OK				(0)
+#define BM_ERR_WRONG_REQ			(-1)
+#define BM_ERR_OVERRUN				(-2)
+
+
+void __iomem *BaseAddrDRAMC[MAX_DRAMC_CHANN];
+void __iomem *BaseAddrDDRPHY_AO[2];
+void __iomem *BaseAddrDRAMC0_AO;
+
+
+unsigned int MET_DRAMC_GetPageHitCount(enum DRAMC_Cnt_Type CountType, int chann)
+{
+	unsigned int iCount;
+	unsigned long addr_base = ADDR_DRAMC0;
+
+	if (chann == 0)
+		addr_base = ADDR_DRAMC0;
+	else if (chann == 1)
+		addr_base = ADDR_DRAMC1;
+	else if (chann == 2)
+		addr_base = ADDR_DRAMC2;
+	else if (chann == 3)
+		addr_base = ADDR_DRAMC3;
+	else
+		return BM_ERR_WRONG_REQ;
+
+	switch (CountType) {
+	case DRAMC_R2R:
+		iCount = readl(IOMEM(addr_base + DRAMC_R2R_PAGE_HIT));
+		break;
+	case DRAMC_R2W:
+		iCount = readl(IOMEM(addr_base + DRAMC_R2W_PAGE_HIT));
+		break;
+	case DRAMC_W2R:
+		iCount = readl(IOMEM(addr_base + DRAMC_W2R_PAGE_HIT));
+		break;
+	case DRAMC_W2W:
+		iCount = readl(IOMEM(addr_base + DRAMC_W2W_PAGE_HIT));
+		break;
+	case DRAMC_ALL:
+		iCount = readl(IOMEM(addr_base + DRAMC_R2R_PAGE_HIT)) +
+			 readl(IOMEM(addr_base + DRAMC_R2W_PAGE_HIT)) +
+			 readl(IOMEM(addr_base + DRAMC_W2R_PAGE_HIT)) +
+			 readl(IOMEM(addr_base + DRAMC_W2W_PAGE_HIT));
+		break;
+	default:
+		return BM_ERR_WRONG_REQ;
+	}
+
+	return iCount;
+}
+
+unsigned int MET_DRAMC_GetPageMissCount(enum DRAMC_Cnt_Type CountType, int chann)
+{
+	unsigned int iCount;
+	unsigned long addr_base = ADDR_DRAMC0;
+
+	if (chann == 0)
+		addr_base = ADDR_DRAMC0;
+	else if (chann == 1)
+		addr_base = ADDR_DRAMC1;
+	else if (chann == 2)
+		addr_base = ADDR_DRAMC2;
+	else if (chann == 3)
+		addr_base = ADDR_DRAMC3;
+	else
+		return BM_ERR_WRONG_REQ;
+
+	switch (CountType) {
+	case DRAMC_R2R:
+		iCount = readl(IOMEM(addr_base + DRAMC_R2R_PAGE_MISS));
+		break;
+	case DRAMC_R2W:
+		iCount = readl(IOMEM(addr_base + DRAMC_R2W_PAGE_MISS));
+		break;
+	case DRAMC_W2R:
+		iCount = readl(IOMEM(addr_base + DRAMC_W2R_PAGE_MISS));
+		break;
+	case DRAMC_W2W:
+		iCount = readl(IOMEM(addr_base + DRAMC_W2W_PAGE_MISS));
+		break;
+	case DRAMC_ALL:
+		iCount = readl(IOMEM(addr_base + DRAMC_R2R_PAGE_MISS)) +
+			 readl(IOMEM(addr_base + DRAMC_R2W_PAGE_MISS)) +
+			 readl(IOMEM(addr_base + DRAMC_W2R_PAGE_MISS)) +
+			 readl(IOMEM(addr_base + DRAMC_W2W_PAGE_MISS));
+		break;
+	default:
+		return BM_ERR_WRONG_REQ;
+	}
+
+	return iCount;
+}
+
+
+unsigned int MET_DRAMC_GetInterbankCount(enum DRAMC_Cnt_Type CountType, int chann)
+{
+	unsigned int iCount;
+	unsigned long addr_base = ADDR_DRAMC0;
+
+	if (chann == 0)
+		addr_base = ADDR_DRAMC0;
+	else if (chann == 1)
+		addr_base = ADDR_DRAMC1;
+	else if (chann == 2)
+		addr_base = ADDR_DRAMC2;
+	else if (chann == 3)
+		addr_base = ADDR_DRAMC3;
+	else
+		return BM_ERR_WRONG_REQ;
+
+	switch (CountType) {
+	case DRAMC_R2R:
+		iCount = readl(IOMEM(addr_base + DRAMC_R2R_INTERBANK));
+		break;
+	case DRAMC_R2W:
+		iCount = readl(IOMEM(addr_base + DRAMC_R2W_INTERBANK));
+		break;
+	case DRAMC_W2R:
+		iCount = readl(IOMEM(addr_base + DRAMC_W2R_INTERBANK));
+		break;
+	case DRAMC_W2W:
+		iCount = readl(IOMEM(addr_base + DRAMC_W2W_INTERBANK));
+		break;
+	case DRAMC_ALL:
+		iCount = readl(IOMEM(addr_base + DRAMC_R2R_INTERBANK)) +
+			 readl(IOMEM(addr_base + DRAMC_R2W_INTERBANK)) +
+			 readl(IOMEM(addr_base + DRAMC_W2R_INTERBANK)) +
+			 readl(IOMEM(addr_base + DRAMC_W2W_INTERBANK));
+		break;
+	default:
+		return BM_ERR_WRONG_REQ;
+	}
+
+	return iCount;
+}
+
+
+unsigned int MET_DRAMC_GetIdleCount(int chann)
+{
+	unsigned long addr_base = ADDR_DRAMC0;
+
+	if (chann == 0)
+		addr_base = ADDR_DRAMC0;
+	else if (chann == 1)
+		addr_base = ADDR_DRAMC1;
+	else if (chann == 2)
+		addr_base = ADDR_DRAMC2;
+	else if (chann == 3)
+		addr_base = ADDR_DRAMC3;
+	else
+		return BM_ERR_WRONG_REQ;
+
+	return readl(IOMEM(addr_base + DRAMC_IDLE_COUNT));
+}
+
+
+unsigned int MET_DRAMC_Misc_Status(int chann)
+{
+	unsigned long addr_base = ADDR_DRAMC0;
+
+	if (chann == 0)
+		addr_base = ADDR_DRAMC0;
+	else if (chann == 1)
+		addr_base = ADDR_DRAMC1;
+	else if (chann == 2)
+		addr_base = ADDR_DRAMC2;
+	else if (chann == 3)
+		addr_base = ADDR_DRAMC3;
+	else
+		return BM_ERR_WRONG_REQ;
+
+	return readl(IOMEM(addr_base + DRAMC_MISC_STATUSA));
+}
+
+
+unsigned int MET_DRAMC_SPCMDRESP(int chann)
+{
+	unsigned long addr_base = ADDR_DRAMC0;
+
+	if (chann == 0)
+		addr_base = ADDR_DRAMC0;
+	else if (chann == 1)
+		addr_base = ADDR_DRAMC1;
+	else if (chann == 2)
+		addr_base = ADDR_DRAMC2;
+	else if (chann == 3)
+		addr_base = ADDR_DRAMC3;
+	else
+		return BM_ERR_WRONG_REQ;
+
+	return readl(IOMEM(addr_base + DRAMC_MISC_SPCMDRESP));
+}
+
+
+unsigned int MET_DRAMC_RefPop(int chann)
+{
+	unsigned long addr_base = ADDR_DRAMC0;
+
+	if (chann == 0)
+		addr_base = ADDR_DRAMC0;
+	else if (chann == 1)
+		addr_base = ADDR_DRAMC1;
+	else if (chann == 2)
+		addr_base = ADDR_DRAMC2;
+	else if (chann == 3)
+		addr_base = ADDR_DRAMC3;
+	else
+		return BM_ERR_WRONG_REQ;
+
+	return readl(IOMEM(addr_base + DRAMC_REFRESH_POP));
+}
+
+
+unsigned int MET_DRAMC_Free26M(int chann)
+{
+	unsigned long addr_base = ADDR_DRAMC0;
+
+	if (chann == 0)
+		addr_base = ADDR_DRAMC0;
+	else if (chann == 1)
+		addr_base = ADDR_DRAMC1;
+	else if (chann == 2)
+		addr_base = ADDR_DRAMC2;
+	else if (chann == 3)
+		addr_base = ADDR_DRAMC3;
+	else
+		return BM_ERR_WRONG_REQ;
+
+	return readl(IOMEM(addr_base + DRAMC_FREERUN_26M));
+}
+
+
+unsigned int MET_DRAMC_RByte(int chann)
+{
+	unsigned long addr_base = ADDR_DRAMC0;
+
+	if (chann == 0)
+		addr_base = ADDR_DRAMC0;
+	else if (chann == 1)
+		addr_base = ADDR_DRAMC1;
+	else if (chann == 2)
+		addr_base = ADDR_DRAMC2;
+	else if (chann == 3)
+		addr_base = ADDR_DRAMC3;
+	else
+		return BM_ERR_WRONG_REQ;
+
+	return readl(IOMEM(addr_base + DRAMC_READ_BYTES));
+}
+
+
+unsigned int MET_DRAMC_WByte(int chann)
+{
+	unsigned long addr_base = ADDR_DRAMC0;
+
+	if (chann == 0)
+		addr_base = ADDR_DRAMC0;
+	else if (chann == 1)
+		addr_base = ADDR_DRAMC1;
+	else if (chann == 2)
+		addr_base = ADDR_DRAMC2;
+	else if (chann == 3)
+		addr_base = ADDR_DRAMC3;
+	else
+		return BM_ERR_WRONG_REQ;
+
+	return readl(IOMEM(addr_base + DRAMC_WRITE_BYTES));
+}
+
+
+unsigned int MET_DRAMC_DCM_CTRL(int chann)
+{
+	unsigned long addr_base;
+
+	if (chann == 0)
+		addr_base = (unsigned long)BaseAddrDDRPHY_AO[0];
+	else if (chann == 1)
+		addr_base = (unsigned long)BaseAddrDDRPHY_AO[1];
+	else
+		return BM_ERR_WRONG_REQ;
+
+	/* CH0_MEM_DCM_CTRL[25:21] DCM slowdown factor */
+	return ((readl(IOMEM(addr_base + DRAMC_DCM_CTRL)) & 0x03E00000) >> 21);
+}
+
+
+void MET_DRAMC_GetDebugCounter(int *value, int chann)
+{
+	int i;
+	unsigned long addr_base = ADDR_DRAMC0;
+
+	for (i = 0; i < chann; i++) {
+		if (i == 0)
+			addr_base = ADDR_DRAMC0;
+		else if (i == 1)
+			addr_base = ADDR_DRAMC1;
+		else if (i == 2)
+			addr_base = ADDR_DRAMC2;
+		else if (i == 3)
+			addr_base = ADDR_DRAMC3;
+		else
+			return;
+
+		value[DRAMC_Debug_MAX_CNT * i + BM_FREERUN_26M] = MET_DRAMC_Free26M(i);
+
+		value[DRAMC_Debug_MAX_CNT * i + BM_RK0_PRE_STANDBY] =
+			readl(IOMEM(addr_base + DRAMC_RK0_PRE_STANDBY));
+		value[DRAMC_Debug_MAX_CNT * i + BM_RK0_PRE_POWERDOWN] =
+			readl(IOMEM(addr_base + DRAMC_RK0_PRE_POWERDOWN));
+		value[DRAMC_Debug_MAX_CNT * i + BM_RK0_ACT_STANDBY] =
+			readl(IOMEM(addr_base + DRAMC_RK0_ACT_STANDBY));
+		value[DRAMC_Debug_MAX_CNT * i + BM_RK0_ACT_POWERDOWN] =
+			readl(IOMEM(addr_base + DRAMC_RK0_ACT_POWERDOWN));
+
+		value[DRAMC_Debug_MAX_CNT * i + BM_RK1_PRE_STANDBY] =
+			readl(IOMEM(addr_base + DRAMC_RK1_PRE_STANDBY));
+		value[DRAMC_Debug_MAX_CNT * i + BM_RK1_PRE_POWERDOWN] =
+			readl(IOMEM(addr_base + DRAMC_RK1_PRE_POWERDOWN));
+		value[DRAMC_Debug_MAX_CNT * i + BM_RK1_ACT_STANDBY] =
+			readl(IOMEM(addr_base + DRAMC_RK1_ACT_STANDBY));
+		value[DRAMC_Debug_MAX_CNT * i + BM_RK1_ACT_POWERDOWN] =
+			readl(IOMEM(addr_base + DRAMC_RK1_ACT_POWERDOWN));
+
+		value[DRAMC_Debug_MAX_CNT * i + BM_RK2_PRE_STANDBY] =
+			readl(IOMEM(addr_base + DRAMC_RK2_PRE_STANDBY));
+		value[DRAMC_Debug_MAX_CNT * i + BM_RK2_PRE_POWERDOWN] =
+			readl(IOMEM(addr_base + DRAMC_RK2_PRE_POWERDOWN));
+		value[DRAMC_Debug_MAX_CNT * i + BM_RK2_ACT_STANDBY] =
+			readl(IOMEM(addr_base + DRAMC_RK2_ACT_STANDBY));
+		value[DRAMC_Debug_MAX_CNT * i + BM_RK2_ACT_POWERDOWN] =
+			readl(IOMEM(addr_base + DRAMC_RK2_ACT_POWERDOWN));
+	}
+}
diff --git a/src/devtools/met-driver/4.14/mt2731/dramc/V2/mtk_dramc_reg.h b/src/devtools/met-driver/4.14/mt2731/dramc/V2/mtk_dramc_reg.h
new file mode 100644
index 0000000..44c30fe
--- /dev/null
+++ b/src/devtools/met-driver/4.14/mt2731/dramc/V2/mtk_dramc_reg.h
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#ifndef __MTK_DRAMC_REG_H__
+#define __MTK_DRAMC_REG_H__
+#include <linux/compiler.h>
+
+#define DRAMC_VER 2
+
+#define MAX_DRAMC_CHANN 4
+extern void __iomem *BaseAddrDRAMC[MAX_DRAMC_CHANN];
+extern void __iomem *BaseAddrDDRPHY_AO[2];
+extern void __iomem *BaseAddrDRAMC0_AO;
+
+enum BM_DRAMC_DTS_INDEX {
+	DRAMC_DTS_DRAMC0_AO = 0x0,
+	DRAMC_DTS_DRAMC0_NAO = 0x4,
+	DRAMC_DTS_DRAMC1_NAO = 0x5,
+	DRAMC_DTS_DRAMC2_NAO = 0x6,
+	DRAMC_DTS_DRAMC3_NAO = 0x7,
+	DRAMC_DTS_DDRPHY0_AO = 0x8,
+};
+
+enum DRAMC_Debug_Type {
+	BM_FREERUN_26M = 0x0,
+	BM_RK0_PRE_STANDBY,
+	BM_RK0_PRE_POWERDOWN,
+	BM_RK0_ACT_STANDBY,
+	BM_RK0_ACT_POWERDOWN,
+	BM_RK1_PRE_STANDBY,
+	BM_RK1_PRE_POWERDOWN,
+	BM_RK1_ACT_STANDBY,
+	BM_RK1_ACT_POWERDOWN,
+	BM_RK2_PRE_STANDBY,
+	BM_RK2_PRE_POWERDOWN,
+	BM_RK2_ACT_STANDBY,
+	BM_RK2_ACT_POWERDOWN,
+	DRAMC_Debug_MAX_CNT
+};
+
+enum DRAMC_Cnt_Type {
+	DRAMC_R2R,
+	DRAMC_R2W,
+	DRAMC_W2R,
+	DRAMC_W2W,
+	DRAMC_ALL
+};
+
+#define ADDR_DRAMC0     ((unsigned long)BaseAddrDRAMC[0])
+#define ADDR_DRAMC1     ((unsigned long)BaseAddrDRAMC[1])
+#define ADDR_DRAMC2     ((unsigned long)BaseAddrDRAMC[2])
+#define ADDR_DRAMC3     ((unsigned long)BaseAddrDRAMC[3])
+
+#define DRAMC_MISC_STATUSA      0x80
+#define DRAMC_DCM_CTRL		0x28C
+#define DRAMC_REFRESH_POP       0x300
+#define DRAMC_FREERUN_26M       0x304
+#define DRAMC_R2R_PAGE_HIT      0x30C
+#define DRAMC_R2R_PAGE_MISS     0x310
+#define DRAMC_R2R_INTERBANK     0x314
+#define DRAMC_R2W_PAGE_HIT      0x318
+#define DRAMC_R2W_PAGE_MISS     0x31C
+#define DRAMC_R2W_INTERBANK     0x320
+#define DRAMC_W2R_PAGE_HIT      0x324
+#define DRAMC_W2R_PAGE_MISS     0x328
+#define DRAMC_W2R_INTERBANK     0x32C
+#define DRAMC_W2W_PAGE_HIT      0x330
+#define DRAMC_W2W_PAGE_MISS     0x334
+#define DRAMC_W2W_INTERBANK     0x338
+#define DRAMC_IDLE_COUNT        0x308
+#define DRAMC_RK0_PRE_STANDBY   0x33c
+#define DRAMC_RK0_PRE_POWERDOWN 0x340
+#define DRAMC_RK0_ACT_STANDBY   0x344
+#define DRAMC_RK0_ACT_POWERDOWN 0x348
+#define DRAMC_RK1_PRE_STANDBY   0x34c
+#define DRAMC_RK1_PRE_POWERDOWN 0x350
+#define DRAMC_RK1_ACT_STANDBY   0x354
+#define DRAMC_RK1_ACT_POWERDOWN 0x358
+#define DRAMC_RK2_PRE_STANDBY   0x35c
+#define DRAMC_RK2_PRE_POWERDOWN 0x360
+#define DRAMC_RK2_ACT_STANDBY   0x364
+#define DRAMC_RK2_ACT_POWERDOWN 0x368
+#define DRAMC_READ_BYTES        0x38c
+#define DRAMC_WRITE_BYTES       0x390
+#define DRAMC_MISC_SPCMDRESP    0x3b8
+
+extern unsigned int MET_DRAMC_GetPageHitCount(enum DRAMC_Cnt_Type CountType, int chann);
+extern unsigned int MET_DRAMC_GetPageMissCount(enum DRAMC_Cnt_Type CountType, int chann);
+extern unsigned int MET_DRAMC_GetInterbankCount(enum DRAMC_Cnt_Type CountType, int chann);
+extern unsigned int MET_DRAMC_GetIdleCount(int chann);
+extern unsigned int MET_DRAMC_Misc_Status(int chann);
+extern unsigned int MET_DRAMC_SPCMDRESP(int chann);
+extern unsigned int MET_DRAMC_RefPop(int chann);
+extern unsigned int MET_DRAMC_Free26M(int chann);
+extern unsigned int MET_DRAMC_RByte(int chann);
+extern unsigned int MET_DRAMC_WByte(int chann);
+extern unsigned int MET_DRAMC_DCM_CTRL(int chann);
+
+/* Debug Counter status */
+extern void MET_DRAMC_GetDebugCounter(int *value, int chann);
+
+
+#endif /* __MTK_DRAMC_REG_H__ */
diff --git a/src/devtools/met-driver/4.14/mt2731/emi/SEDA2/met_emi.c b/src/devtools/met-driver/4.14/mt2731/emi/SEDA2/met_emi.c
new file mode 100644
index 0000000..554fc05
--- /dev/null
+++ b/src/devtools/met-driver/4.14/mt2731/emi/SEDA2/met_emi.c
@@ -0,0 +1,1549 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/device.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/string.h>
+
+#ifdef CONFIG_ARM
+//#include <asm/dma-mapping.h> /* arm_coherent_dma_ops */
+#endif /* CONFIG_ARM */
+#include <linux/dma-mapping.h>
+
+#define MET_USER_EVENT_SUPPORT
+#include "met_drv.h"
+#include "trace.h"
+
+#include "mtk_typedefs.h"
+#include "plf_init.h"
+#include "plf_trace.h"
+#include "mtk_emi_bm.h"
+#include "mtk_dramc_reg.h"
+#include "interface.h"
+
+#if defined(CONFIG_MTK_TINYSYS_SSPM_SUPPORT) && defined(ONDIEMET_SUPPORT)
+#include "sspm/ondiemet_sspm.h"
+#endif
+
+
+/*======================================================================*/
+/*	Global variable definitions					*/
+/*======================================================================*/
+/*ondiemet emi sampling interval in us */
+int emi_tsct_enable;
+int emi_mdct_enable;
+int emi_TP_busfiltr_enable;
+
+int emi_use_ondiemet;
+int metemi_func_opt;
+
+int met_emi_regdump;
+/* Dynamic MonitorCounter selection !!!EXPERIMENT!!! */
+static int msel_enable;
+static unsigned int msel_group1 = BM_MASTER_ALL;
+static unsigned int msel_group2 = BM_MASTER_ALL;
+static unsigned int msel_group3 = BM_MASTER_ALL;
+
+/* CVS Added changeable buffer for testing */
+static int mdmcu_sel_enable;
+static unsigned int rd_mdmcu_rsv_num = 0x5;
+
+/* Global variables */
+static struct kobject *kobj_emi;
+static int rwtype = BM_BOTH_READ_WRITE;
+
+/* BW Limiter */
+/*#define CNT_COUNTDOWN	(1000-1)*/		/* 1000 * 1ms = 1sec */
+#define CNT_COUNTDOWN   (0)                     /* 1ms */
+static int countdown;
+static int bw_limiter_enable = BM_BW_LIMITER_ENABLE;
+
+/* TTYPE counter */
+static int ttype1_16_en = BM_TTYPE1_16_DISABLE;
+static int ttype17_21_en = BM_TTYPE17_21_DISABLE;
+
+static int dramc_pdir_enable;
+static int dram_chann_num = 1;
+
+enum SSPM_Mode {
+	CUSTOMER_MODE = 0x0,
+	UNDEFINE_MODE = 0x1,
+	INTERNAL_MODE = 0X2780
+};
+
+
+/*======================================================================*/
+/*	EMI Test Operations						*/
+/*======================================================================*/
+static int times;
+
+static ssize_t test_apmcu_store(struct kobject *kobj,
+				struct kobj_attribute *attr,
+				const char *buf,
+				size_t n)
+{
+	int i;
+	unsigned int    *src_addr_v = NULL;
+	dma_addr_t src_addr_p;
+#ifdef CONFIG_ARM
+	struct dma_map_ops *ops = (struct dma_map_ops *)symbol_get(arm_dma_ops);
+#endif /* CONFIG_ARM */
+
+	if ((n == 0) || (buf == NULL))
+		return -EINVAL;
+	if (kstrtoint(buf, 10, &times) != 0)
+		return -EINVAL;
+	if (times < 0)
+		return -EINVAL;
+
+	if (times > 5000)       /* Less than 20MB */
+		return -EINVAL;
+
+#ifdef CONFIG_ARM
+	if (ops && ops->alloc) {
+		(met_device.this_device)->coherent_dma_mask = DMA_BIT_MASK(32);
+		src_addr_v = ops->alloc(met_device.this_device,
+						PAGE_SIZE,
+						&src_addr_p,
+						GFP_KERNEL,
+						0);
+	}
+#endif /* CONFIG_ARM */
+ 
+#ifdef CONFIG_ARM64
+	/* dma_alloc */
+ 	src_addr_v = dma_alloc_coherent(met_device.this_device,
+ 					PAGE_SIZE,
+ 					&src_addr_p,
+ 					GFP_KERNEL);
+#endif /* CONFIG_ARM64 */
+
+	if (src_addr_v == NULL) {
+#ifdef CONFIG_MET_MODULE
+		met_tag_oneshot_real(0, "test_apmcu dma alloc fail", PAGE_SIZE);
+#else
+		met_tag_oneshot(0, "test_apmcu dma alloc fail", PAGE_SIZE);
+#endif
+		return -ENOMEM;
+	}
+	/* testing */
+	preempt_disable();
+#ifdef CONFIG_MET_MODULE
+	met_tag_start_real(0, "TEST_EMI_APMCU");
+#else
+	met_tag_start(0, "TEST_EMI_APMCU");
+#endif
+	for (i = 0; i < times; i++) {
+		memset(src_addr_v, 2 * i, PAGE_SIZE);
+#ifdef CONFIG_MET_MODULE
+		met_tag_oneshot_real(0, "TEST_EMI_APMCU", PAGE_SIZE);
+#else
+		met_tag_oneshot(0, "TEST_EMI_APMCU", PAGE_SIZE);
+#endif
+	}
+#ifdef CONFIG_MET_MODULE
+	met_tag_end_real(0, "TEST_EMI_APMCU");
+#else
+	met_tag_end(0, "TEST_EMI_APMCU");
+#endif
+	/* the following function has no defined if MET is built as module */
+	/* preempt_enable_no_resched(); */
+	/* use this one to replace it: see met_drv.h */
+	my_preempt_enable();
+
+#ifdef CONFIG_ARM
+	/* dma_free */
+	if (ops && ops->free) {
+		ops->free(met_device.this_device,
+				  PAGE_SIZE,
+				  src_addr_v,
+				  src_addr_p,
+			0);
+	}
+#endif /* CONFIG_ARM */
+
+#ifdef CONFIG_ARM64
+	/* dma_free */
+ 	if (src_addr_v != NULL)
+ 		dma_free_coherent(met_device.this_device,
+ 				  PAGE_SIZE,
+ 				  src_addr_v,
+				  src_addr_p);
+#endif /* CONFIG_ARM64 */
+
+	return n;
+}
+
+/*======================================================================*/
+/*	KOBJ Declarations						*/
+/*======================================================================*/
+DECLARE_KOBJ_ATTR_INT(emi_tsct_enable, emi_tsct_enable);
+DECLARE_KOBJ_ATTR_INT(emi_mdct_enable, emi_mdct_enable);
+DECLARE_KOBJ_ATTR_INT(emi_TP_busfiltr_enable, emi_TP_busfiltr_enable);
+DECLARE_KOBJ_ATTR_INT(metemi_func_opt, metemi_func_opt);
+DECLARE_KOBJ_ATTR_INT(emi_regdump, met_emi_regdump);
+DECLARE_KOBJ_ATTR_INT(msel_enable, msel_enable);
+DECLARE_KOBJ_ATTR_HEX_CHECK(msel_group1, msel_group1, msel_group1 > 0 && msel_group1 <= BM_MASTER_ALL);
+DECLARE_KOBJ_ATTR_HEX_CHECK(msel_group2, msel_group2, msel_group2 > 0 && msel_group2 <= BM_MASTER_ALL);
+DECLARE_KOBJ_ATTR_HEX_CHECK(msel_group3, msel_group3, msel_group3 > 0 && msel_group3 <= BM_MASTER_ALL);
+DECLARE_KOBJ_ATTR_INT(mdmcu_sel_enable, mdmcu_sel_enable);
+DECLARE_KOBJ_ATTR_INT(rd_mdmcu_rsv_num, rd_mdmcu_rsv_num);
+
+
+/* KOBJ: rwtype */
+DECLARE_KOBJ_ATTR_INT_CHECK(rwtype, rwtype, rwtype >= 0 && rwtype <= BM_WRITE_ONLY);
+
+static unsigned int get_emi_clock_rate(unsigned int dram_data_rate_MHz)
+{
+	/*
+	 *	the ddr type define :
+	 *	enum DDRTYPE {
+	 *	TYPE_LPDDR3 = 1,
+	 *	TYPE_LPDDR4,
+	 *	TYPE_LPDDR4X,
+	 *	TYPE_LPDDR2
+	 *	};
+	 */
+
+	unsigned int DRAM_TYPE;
+
+	if (get_ddr_type_symbol) {
+		DRAM_TYPE = get_ddr_type_symbol();
+
+		if ((DRAM_TYPE == 2) || (DRAM_TYPE == 3))
+			return dram_data_rate_MHz / DRAM_EMI_BASECLOCK_RATE_LP4 / DRAM_DATARATE;
+		else
+			return dram_data_rate_MHz / DRAM_EMI_BASECLOCK_RATE_LP3 / DRAM_DATARATE;
+	} else {
+		METERROR("[%s][%d]get_ddr_type_symbol = NULL , use the TYPE_LPDDR3 setting\n", __func__, __LINE__);
+		return dram_data_rate_MHz / DRAM_EMI_BASECLOCK_RATE_LP3 / DRAM_DATARATE;
+	}
+}
+
+/* KOBJ: emi_clock_rate */
+static ssize_t emi_clock_rate_show(struct kobject *kobj,
+				   struct kobj_attribute *attr,
+				   char *buf)
+{
+	unsigned int dram_data_rate_MHz;
+
+	if (get_dram_data_rate_symbol) {
+		dram_data_rate_MHz = get_dram_data_rate_symbol();
+	} else {
+		METERROR("get_dram_data_rate_symbol = NULL\n");
+		dram_data_rate_MHz = 0;
+	}
+
+	return snprintf(buf, PAGE_SIZE, "%d\n",
+			get_emi_clock_rate(dram_data_rate_MHz));
+}
+
+DECLARE_KOBJ_ATTR_RO(emi_clock_rate);
+
+/* KOBJ: ttype1_16_en */
+DECLARE_KOBJ_ATTR_STR_LIST_ITEM(
+	ttype1_16_en,
+	KOBJ_ITEM_LIST(
+		{ BM_TTYPE1_16_ENABLE,   "ENABLE" },
+		{ BM_TTYPE1_16_DISABLE,  "DISABLE" }
+		)
+	);
+DECLARE_KOBJ_ATTR_STR_LIST(ttype1_16_en, ttype1_16_en, ttype1_16_en);
+
+/* KOBJ: ttype17_21_en */
+DECLARE_KOBJ_ATTR_STR_LIST_ITEM(
+	ttype17_21_en,
+	KOBJ_ITEM_LIST(
+		{ BM_TTYPE17_21_ENABLE,  "ENABLE" },
+		{ BM_TTYPE17_21_DISABLE, "DISABLE" }
+		)
+	);
+DECLARE_KOBJ_ATTR_STR_LIST(ttype17_21_en, ttype17_21_en, ttype17_21_en);
+
+/* KOBJ: bw_limiter_enable */
+DECLARE_KOBJ_ATTR_STR_LIST_ITEM(
+	bw_limiter_enable,
+	KOBJ_ITEM_LIST(
+		{ BM_BW_LIMITER_ENABLE,  "ENABLE" },
+		{ BM_BW_LIMITER_DISABLE, "DISABLE" }
+		)
+	);
+
+DECLARE_KOBJ_ATTR_STR_LIST(bw_limiter_enable, bw_limiter_enable, bw_limiter_enable);
+
+/* KOBJ: ttype_master */
+DECLARE_KOBJ_ATTR_STR_LIST_ITEM(
+	ttype_master,
+	KOBJ_ITEM_LIST(
+		{ BM_MASTER_M0,  "M0" },
+		{ BM_MASTER_M1,  "M1" },
+		{ BM_MASTER_M2,  "M2" },
+		{ BM_MASTER_M3,  "M3" },
+		{ BM_MASTER_M4,  "M4" },
+		{ BM_MASTER_M5,  "M5" },
+		{ BM_MASTER_M6,  "M6" },
+		{ BM_MASTER_M7,  "M7" }
+		)
+	);
+
+
+/* KOBJ: ttypeX_nbeat, ttypeX_nbyte, ttypeX_burst */
+DECLARE_KOBJ_ATTR_INT_LIST_ITEM(
+	ttype_nbeat,
+	KOBJ_ITEM_LIST(
+		{ BM_TRANS_TYPE_1BEAT,   1 },
+		{ BM_TRANS_TYPE_2BEAT,   2 },
+		{ BM_TRANS_TYPE_3BEAT,   3 },
+		{ BM_TRANS_TYPE_4BEAT,   4 },
+		{ BM_TRANS_TYPE_5BEAT,   5 },
+		{ BM_TRANS_TYPE_6BEAT,   6 },
+		{ BM_TRANS_TYPE_7BEAT,   7 },
+		{ BM_TRANS_TYPE_8BEAT,   8 },
+		{ BM_TRANS_TYPE_9BEAT,   9 },
+		{ BM_TRANS_TYPE_10BEAT,  10 },
+		{ BM_TRANS_TYPE_11BEAT,  11 },
+		{ BM_TRANS_TYPE_12BEAT,  12 },
+		{ BM_TRANS_TYPE_13BEAT,  13 },
+		{ BM_TRANS_TYPE_14BEAT,  14 },
+		{ BM_TRANS_TYPE_15BEAT,  15 },
+		{ BM_TRANS_TYPE_16BEAT,  16 }
+		)
+	);
+DECLARE_KOBJ_ATTR_INT_LIST_ITEM(
+	ttype_nbyte,
+	KOBJ_ITEM_LIST(
+		{ BM_TRANS_TYPE_1Byte,   1 },
+		{ BM_TRANS_TYPE_2Byte,   2 },
+		{ BM_TRANS_TYPE_4Byte,   4 },
+		{ BM_TRANS_TYPE_8Byte,   8 },
+		{ BM_TRANS_TYPE_16Byte,  16 },
+		{ BM_TRANS_TYPE_32Byte,  32 }
+		)
+	);
+DECLARE_KOBJ_ATTR_STR_LIST_ITEM(
+	ttype_burst,
+	KOBJ_ITEM_LIST(
+		{ BM_TRANS_TYPE_BURST_INCR,      "INCR" },
+		{ BM_TRANS_TYPE_BURST_WRAP,      "WRAP" }
+		)
+	);
+
+DECLARE_KOBJ_ATTR_STR_LIST_ITEM(
+	ttype_rw,
+	KOBJ_ITEM_LIST(
+		{ BM_TRANS_RW_DEFAULT,   "DEFAULT" },
+		{ BM_TRANS_RW_READONLY,  "R" },
+		{ BM_TRANS_RW_WRITEONLY, "W" },
+		{ BM_TRANS_RW_RWBOTH,    "BOTH" }
+		)
+	);
+
+/* KOBJ: test_apmcu */
+DECLARE_KOBJ_ATTR_SHOW_INT(test_apmcu, times);
+/* please refer to session: "EMI Test Operations" for store operation */
+DECLARE_KOBJ_ATTR(test_apmcu);
+
+DECLARE_KOBJ_ATTR_INT(dramc_pdir_enable, dramc_pdir_enable);
+
+/*enable high priority filter*/
+static int high_priority_filter;
+DECLARE_KOBJ_ATTR_HEX(high_priority_filter, high_priority_filter);
+
+
+/**/
+static int ttype_master_val[21];
+static int ttype_busid_val[21];
+static int ttype_nbeat_val[21];
+static int ttype_nbyte_val[21];
+static int ttype_burst_val[21];
+static int ttype_rw_val[21];
+
+#define DECLARE_KOBJ_TTYPE_MASTER(nr) \
+	DECLARE_KOBJ_ATTR_STR_LIST(ttype ## nr ## _master, ttype_master_val[nr - 1], ttype_master)
+
+#define DECLARE_KOBJ_TTYPE_NBEAT(nr) \
+	DECLARE_KOBJ_ATTR_INT_LIST(ttype ## nr ## _nbeat, ttype_nbeat_val[nr - 1], ttype_nbeat)
+
+#define DECLARE_KOBJ_TTYPE_NBYTE(nr) \
+	DECLARE_KOBJ_ATTR_INT_LIST(ttype ## nr ## _nbyte, ttype_nbyte_val[nr - 1], ttype_nbyte)
+
+#define DECLARE_KOBJ_TTYPE_BURST(nr) \
+	DECLARE_KOBJ_ATTR_STR_LIST(ttype ## nr ## _burst, ttype_burst_val[nr - 1], ttype_burst)
+
+#define DECLARE_KOBJ_TTYPE_RW(nr) \
+	DECLARE_KOBJ_ATTR_STR_LIST(ttype ## nr ## _rw, ttype_rw_val[nr - 1], ttype_rw)
+
+#define DECLARE_KOBJ_TTYPE_BUSID_VAL(nr) \
+	DECLARE_KOBJ_ATTR_HEX(ttype ## nr ## _busid, ttype_busid_val[nr - 1])
+
+DECLARE_KOBJ_TTYPE_MASTER(1);
+DECLARE_KOBJ_TTYPE_NBEAT(1);
+DECLARE_KOBJ_TTYPE_NBYTE(1);
+DECLARE_KOBJ_TTYPE_BURST(1);
+DECLARE_KOBJ_TTYPE_RW(1);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(1);
+
+DECLARE_KOBJ_TTYPE_MASTER(2);
+DECLARE_KOBJ_TTYPE_NBEAT(2);
+DECLARE_KOBJ_TTYPE_NBYTE(2);
+DECLARE_KOBJ_TTYPE_BURST(2);
+DECLARE_KOBJ_TTYPE_RW(2);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(2);
+
+DECLARE_KOBJ_TTYPE_MASTER(3);
+DECLARE_KOBJ_TTYPE_NBEAT(3);
+DECLARE_KOBJ_TTYPE_NBYTE(3);
+DECLARE_KOBJ_TTYPE_BURST(3);
+DECLARE_KOBJ_TTYPE_RW(3);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(3);
+
+DECLARE_KOBJ_TTYPE_MASTER(4);
+DECLARE_KOBJ_TTYPE_NBEAT(4);
+DECLARE_KOBJ_TTYPE_NBYTE(4);
+DECLARE_KOBJ_TTYPE_BURST(4);
+DECLARE_KOBJ_TTYPE_RW(4);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(4);
+
+DECLARE_KOBJ_TTYPE_MASTER(5);
+DECLARE_KOBJ_TTYPE_NBEAT(5);
+DECLARE_KOBJ_TTYPE_NBYTE(5);
+DECLARE_KOBJ_TTYPE_BURST(5);
+DECLARE_KOBJ_TTYPE_RW(5);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(5);
+
+DECLARE_KOBJ_TTYPE_MASTER(6);
+DECLARE_KOBJ_TTYPE_NBEAT(6);
+DECLARE_KOBJ_TTYPE_NBYTE(6);
+DECLARE_KOBJ_TTYPE_BURST(6);
+DECLARE_KOBJ_TTYPE_RW(6);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(6);
+
+DECLARE_KOBJ_TTYPE_MASTER(7);
+DECLARE_KOBJ_TTYPE_NBEAT(7);
+DECLARE_KOBJ_TTYPE_NBYTE(7);
+DECLARE_KOBJ_TTYPE_BURST(7);
+DECLARE_KOBJ_TTYPE_RW(7);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(7);
+
+DECLARE_KOBJ_TTYPE_MASTER(8);
+DECLARE_KOBJ_TTYPE_NBEAT(8);
+DECLARE_KOBJ_TTYPE_NBYTE(8);
+DECLARE_KOBJ_TTYPE_BURST(8);
+DECLARE_KOBJ_TTYPE_RW(8);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(8);
+
+DECLARE_KOBJ_TTYPE_MASTER(9);
+DECLARE_KOBJ_TTYPE_NBEAT(9);
+DECLARE_KOBJ_TTYPE_NBYTE(9);
+DECLARE_KOBJ_TTYPE_BURST(9);
+DECLARE_KOBJ_TTYPE_RW(9);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(9);
+
+DECLARE_KOBJ_TTYPE_MASTER(10);
+DECLARE_KOBJ_TTYPE_NBEAT(10);
+DECLARE_KOBJ_TTYPE_NBYTE(10);
+DECLARE_KOBJ_TTYPE_BURST(10);
+DECLARE_KOBJ_TTYPE_RW(10);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(10);
+
+DECLARE_KOBJ_TTYPE_MASTER(11);
+DECLARE_KOBJ_TTYPE_NBEAT(11);
+DECLARE_KOBJ_TTYPE_NBYTE(11);
+DECLARE_KOBJ_TTYPE_BURST(11);
+DECLARE_KOBJ_TTYPE_RW(11);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(11);
+
+DECLARE_KOBJ_TTYPE_MASTER(12);
+DECLARE_KOBJ_TTYPE_NBEAT(12);
+DECLARE_KOBJ_TTYPE_NBYTE(12);
+DECLARE_KOBJ_TTYPE_BURST(12);
+DECLARE_KOBJ_TTYPE_RW(12);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(12);
+
+DECLARE_KOBJ_TTYPE_MASTER(13);
+DECLARE_KOBJ_TTYPE_NBEAT(13);
+DECLARE_KOBJ_TTYPE_NBYTE(13);
+DECLARE_KOBJ_TTYPE_BURST(13);
+DECLARE_KOBJ_TTYPE_RW(13);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(13);
+
+DECLARE_KOBJ_TTYPE_MASTER(14);
+DECLARE_KOBJ_TTYPE_NBEAT(14);
+DECLARE_KOBJ_TTYPE_NBYTE(14);
+DECLARE_KOBJ_TTYPE_BURST(14);
+DECLARE_KOBJ_TTYPE_RW(14);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(14);
+
+DECLARE_KOBJ_TTYPE_MASTER(15);
+DECLARE_KOBJ_TTYPE_NBEAT(15);
+DECLARE_KOBJ_TTYPE_NBYTE(15);
+DECLARE_KOBJ_TTYPE_BURST(15);
+DECLARE_KOBJ_TTYPE_RW(15);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(15);
+
+DECLARE_KOBJ_TTYPE_MASTER(16);
+DECLARE_KOBJ_TTYPE_NBEAT(16);
+DECLARE_KOBJ_TTYPE_NBYTE(16);
+DECLARE_KOBJ_TTYPE_BURST(16);
+DECLARE_KOBJ_TTYPE_RW(16);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(16);
+
+DECLARE_KOBJ_TTYPE_MASTER(17);
+DECLARE_KOBJ_TTYPE_NBEAT(17);
+DECLARE_KOBJ_TTYPE_NBYTE(17);
+DECLARE_KOBJ_TTYPE_BURST(17);
+DECLARE_KOBJ_TTYPE_RW(17);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(17);
+
+DECLARE_KOBJ_TTYPE_MASTER(18);
+DECLARE_KOBJ_TTYPE_NBEAT(18);
+DECLARE_KOBJ_TTYPE_NBYTE(18);
+DECLARE_KOBJ_TTYPE_BURST(18);
+DECLARE_KOBJ_TTYPE_RW(18);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(18);
+
+DECLARE_KOBJ_TTYPE_MASTER(19);
+DECLARE_KOBJ_TTYPE_NBEAT(19);
+DECLARE_KOBJ_TTYPE_NBYTE(19);
+DECLARE_KOBJ_TTYPE_BURST(19);
+DECLARE_KOBJ_TTYPE_RW(19);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(19);
+
+DECLARE_KOBJ_TTYPE_MASTER(20);
+DECLARE_KOBJ_TTYPE_NBEAT(20);
+DECLARE_KOBJ_TTYPE_NBYTE(20);
+DECLARE_KOBJ_TTYPE_BURST(20);
+DECLARE_KOBJ_TTYPE_RW(20);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(20);
+
+DECLARE_KOBJ_TTYPE_MASTER(21);
+DECLARE_KOBJ_TTYPE_NBEAT(21);
+DECLARE_KOBJ_TTYPE_NBYTE(21);
+DECLARE_KOBJ_TTYPE_BURST(21);
+DECLARE_KOBJ_TTYPE_RW(21);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(21);
+
+/**/
+#define KOBJ_ATTR_ITEM_SERIAL_FNODE(nr) \
+	do { \
+		KOBJ_ATTR_ITEM(ttype ## nr ## _master); \
+		KOBJ_ATTR_ITEM(ttype ## nr ## _nbeat); \
+		KOBJ_ATTR_ITEM(ttype ## nr ## _nbyte); \
+		KOBJ_ATTR_ITEM(ttype ## nr ## _burst); \
+		KOBJ_ATTR_ITEM(ttype ## nr ## _busid); \
+		KOBJ_ATTR_ITEM(ttype ## nr ## _rw); \
+	} while (0)
+
+#define KOBJ_ATTR_LIST \
+	do { \
+		KOBJ_ATTR_ITEM(high_priority_filter); \
+		KOBJ_ATTR_ITEM(metemi_func_opt); \
+		KOBJ_ATTR_ITEM(emi_tsct_enable); \
+		KOBJ_ATTR_ITEM(emi_mdct_enable); \
+		KOBJ_ATTR_ITEM(emi_TP_busfiltr_enable); \
+		KOBJ_ATTR_ITEM(emi_regdump); \
+		KOBJ_ATTR_ITEM(msel_enable); \
+		KOBJ_ATTR_ITEM(msel_group1); \
+		KOBJ_ATTR_ITEM(msel_group2); \
+		KOBJ_ATTR_ITEM(msel_group3); \
+		KOBJ_ATTR_ITEM(emi_clock_rate); \
+		KOBJ_ATTR_ITEM(rwtype); \
+		KOBJ_ATTR_ITEM(ttype17_21_en); \
+		KOBJ_ATTR_ITEM(ttype1_16_en); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(1); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(2); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(3); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(4); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(5); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(6); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(7); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(8); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(9); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(10); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(11); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(12); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(13); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(14); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(15); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(16); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(17); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(18); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(19); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(20); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(21); \
+		KOBJ_ATTR_ITEM(test_apmcu); \
+		KOBJ_ATTR_ITEM(bw_limiter_enable); \
+		KOBJ_ATTR_ITEM(dramc_pdir_enable); \
+		KOBJ_ATTR_ITEM(mdmcu_sel_enable); \
+		KOBJ_ATTR_ITEM(rd_mdmcu_rsv_num); \
+	} while (0)
+
+
+/*======================================================================*/
+/*	EMI Operations							*/
+/*======================================================================*/
+static void emi_init(void)
+{
+	unsigned int bmrw0_val, bmrw1_val, i, enable;
+	unsigned int msel_group_val[4];
+
+	/*save origianl EMI config*/
+	MET_BM_SaveCfg();
+
+	/* get dram channel number */
+	dram_chann_num = MET_EMI_GetDramChannNum();
+
+	/* Init. EMI bus monitor */
+	MET_BM_SetReadWriteType(rwtype);
+
+	if ((ttype1_16_en != BM_TTYPE1_16_ENABLE) && (emi_TP_busfiltr_enable != 1)) {
+		if (msel_enable) {
+			msel_group_val[0] = BM_MASTER_ALL;
+			msel_group_val[1] = msel_group1;
+			msel_group_val[2] = msel_group2;
+			msel_group_val[3] = msel_group3;
+		} else {
+			msel_group_val[0] = BM_MASTER_ALL;
+			msel_group_val[1] = BM_MASTER_ALL;
+			msel_group_val[2] = BM_MASTER_ALL;
+			msel_group_val[3] = BM_MASTER_ALL;
+		}
+
+		MET_BM_SetLatencyCounter(1);    /*enable latency count*/
+
+		for (i = 1; i <= 4; i++) {
+			MET_BM_SetMonitorCounter(i,
+						 msel_group_val[i - 1] & BM_MASTER_ALL,
+						 BM_TRANS_TYPE_4BEAT |
+						 BM_TRANS_TYPE_8Byte |
+						 BM_TRANS_TYPE_BURST_WRAP);
+			MET_BM_SetbusID(i, 0);
+			MET_BM_SetbusID_En(i, 0);       /*disable ttype bus sel*/
+		}
+		for (i = 0; i < 4; i++)
+			MET_BM_Set_WsctTsct_id_sel(i, 0);       /*disable tp filter*/
+
+	} else if ((ttype1_16_en != BM_TTYPE1_16_ENABLE) && (emi_TP_busfiltr_enable == 1)) {
+		MET_BM_SetLatencyCounter(1);    /*enable latency count*/
+
+		for (i = 1; i <= 4; i++) {
+			MET_BM_SetMonitorCounter(i,
+						 ttype_master_val[i - 1],
+						 ttype_nbeat_val[i - 1] |
+						 ttype_nbyte_val[i - 1] |
+						 ttype_burst_val[i - 1]);
+			MET_BM_SetbusID(i, ttype_busid_val[i - 1]);
+			MET_BM_SetbusID_En(i, 0);       /*disable ttype bus sel*/
+		}
+		for (i = 0; i < 4; i++)
+			MET_BM_Set_WsctTsct_id_sel(i, 1);       /*enable tp filter*/
+
+	} else if ((ttype1_16_en == BM_TTYPE1_16_ENABLE) && (emi_TP_busfiltr_enable != 1)) {
+		MET_BM_SetLatencyCounter(0);    /*disable latency count*/
+
+		for (i = 1; i <= 16; i++) {
+			MET_BM_SetMonitorCounter(i,
+						 ttype_master_val[i - 1],
+						 ttype_nbeat_val[i - 1] |
+						 ttype_nbyte_val[i - 1] |
+						 ttype_burst_val[i - 1]);
+
+			MET_BM_SetbusID(i, ttype_busid_val[i - 1]);
+			/*disenable ttype bus sel if busid > 0xff_ff*/
+			MET_BM_SetbusID_En(i, (ttype_busid_val[i - 1] > 0xffff) ? 0 : 1);
+		}
+		for (i = 0; i < 4; i++)
+			MET_BM_Set_WsctTsct_id_sel(i, 0);       /*disable tp filter*/
+	} else {	/* (ttype1_16_en == BM_TTYPE1_16_ENABLE)  &&  (emi_TP_busfiltr_enable == 1) */
+		MET_BM_SetLatencyCounter(0);    /*disable latency count*/
+
+		for (i = 1; i <= 16; i++) {
+			MET_BM_SetMonitorCounter(i,
+						 ttype_master_val[i - 1],
+						 ttype_nbeat_val[i - 1] |
+						 ttype_nbyte_val[i - 1] |
+						 ttype_burst_val[i - 1]);
+
+			MET_BM_SetbusID(i, ttype_busid_val[i - 1]);
+			/*disable ttype bus sel if busid > 0xff_ff*/
+			MET_BM_SetbusID_En(i, (ttype_busid_val[i - 1] > 0xffff) ? 0 : 1);
+		}
+		for (i = 0; i < 4; i++)
+			MET_BM_Set_WsctTsct_id_sel(i, 1);       /*enable tp filter*/
+	}
+
+	if (ttype17_21_en == BM_TTYPE17_21_ENABLE) {
+		for (i = 17; i <= 21; i++) {
+			MET_BM_SetMonitorCounter(i,
+						 ttype_master_val[i - 1],
+						 ttype_nbeat_val[i - 1] |
+						 ttype_nbyte_val[i - 1] |
+						 ttype_burst_val[i - 1]);
+			MET_BM_SetbusID(i, ttype_busid_val[i - 1]);
+			/*disable ttype bus sel if busid > 0xff_ff*/
+			MET_BM_SetbusID_En(i, (ttype_busid_val[i - 1] > 0xffff) ? 0 : 1);
+		}
+	}
+
+	bmrw0_val = 0;
+	for (i = 0; i < 16; i++)
+		bmrw0_val |= (ttype_rw_val[i] << (i * 2));
+
+	bmrw1_val = 0;
+	for (i = 16; i < 21; i++)
+		bmrw1_val |= (ttype_rw_val[i] << ((i-16) * 2));
+
+	MET_BM_SetTtypeCounterRW(bmrw0_val, bmrw1_val);
+
+	for (i = 0; i < BM_COUNTER_MAX; i++) {
+		if ((high_priority_filter & (1 << i)) == 0)
+			enable = 0;
+		else
+			enable = 1;
+
+		MET_BM_SetUltraHighFilter(i + 1, enable);
+	}
+
+	met_record_dramc_dcm_enable_flag();
+
+	if (met_emi_regdump == 1)
+		emi_dump_reg();
+}
+
+
+static void emi_uninit(void)
+{
+	MET_BM_RestoreCfg();
+}
+
+static inline void emi_start(void)
+{
+	MET_BM_Enable(1);
+}
+
+
+static inline void emi_stop(void)
+{
+	MET_BM_Enable(0);
+}
+
+
+static inline int do_emi(void)
+{
+	return met_emi.mode;
+}
+
+
+noinline void DRAM_DVFS(unsigned int dram_data_rate_MHz)
+{
+	MET_TRACE("%u\n", dram_data_rate_MHz);
+}
+
+
+static unsigned int emi_bw_limiter(unsigned int *__restrict__ array)
+{
+	int idx = 0;
+	unsigned int dram_data_rate_MHz;
+
+	if (get_dram_data_rate_symbol) {
+		dram_data_rate_MHz = get_dram_data_rate_symbol();
+	} else {
+		METERROR("get_dram_data_rate_symbol = NULL\n");
+		dram_data_rate_MHz = 0;
+	}
+
+	/* print dram data rate */
+	DRAM_DVFS(dram_data_rate_MHz);
+
+	/* get correct dram_clock_rate */
+	array[idx++] = dram_data_rate_MHz;
+
+	/* get correct ARB A->LAST */
+	array[idx++] = MET_EMI_GetARBA();
+	array[idx++] = MET_EMI_GetARBB();
+	array[idx++] = MET_EMI_GetARBC();
+	array[idx++] = MET_EMI_GetARBD();
+	array[idx++] = MET_EMI_GetARBE();
+	array[idx++] = MET_EMI_GetARBF();
+	array[idx++] = MET_EMI_GetARBG();
+	array[idx++] = MET_EMI_GetARBH();
+	/* EMI Total BW Thresholds */
+	array[idx++] = MET_EMI_GetBWCT0();
+	array[idx++] = MET_EMI_GetBWCT1();
+	array[idx++] = MET_EMI_GetBWCT2();
+	array[idx++] = MET_EMI_GetBWCT3();
+	array[idx++] = MET_EMI_GetBWCT4();
+	array[idx++] = MET_EMI_GetBWST0();
+	array[idx++] = MET_EMI_GetBWST1();
+	/* EMI C+G BW Thresholds */
+	array[idx++] = MET_EMI_GetBWCT0_2ND();
+	array[idx++] = MET_EMI_GetBWCT1_2ND();
+	array[idx++] = MET_EMI_GetBWST_2ND();
+
+	return idx;
+}
+
+
+static void _ms_dramc(unsigned int *__restrict__ dramc_pdir_value, int dram_chann_num)
+{
+	MET_DRAMC_GetDebugCounter(dramc_pdir_value, dram_chann_num);
+}
+
+
+static unsigned int emi_polling(unsigned int *__restrict__ emi_value, unsigned int *__restrict__ emi_tsct,
+				unsigned int *__restrict__ emi_ttype_value, unsigned int *__restrict__ dramc_pdir_value,
+				unsigned int *__restrict__ emi_mdct_value)
+{
+	int j = 4;              /* skip 4 WSCTs */
+	int i = 0;              /* ttype start at 0 */
+	int k = 0;              /* tsct start at 0 */
+	int n;
+
+	MET_BM_Pause();
+
+	/* Get Word Count */
+
+	emi_value[0] = MET_BM_GetWordCount(1);  /* All */
+	emi_value[1] = MET_BM_GetWordCount(2);  /* Group 1 */
+	emi_value[2] = MET_BM_GetWordCount(3);  /* Group 2 */
+	emi_value[3] = MET_BM_GetWordCount(4);  /* Group 3 */
+
+	if (ttype1_16_en != BM_TTYPE1_16_ENABLE) {      /*1~21 NOT for ttype*/
+		/* Get Latency */
+		j += MET_BM_GetLatencyCycle(emi_value + j, 1, 8);
+
+		/* Get Trans. */
+		j += MET_BM_GetLatencyCycle(emi_value + j, 9, 16);
+	} else {
+		for (n = 4; n < 20; n++)
+			emi_value[n] = 0;
+		j = 20;
+
+		i += MET_BM_GetLatencyCycle(emi_ttype_value + i, 1, 8);
+
+		/* Get Trans. */
+		i += MET_BM_GetLatencyCycle(emi_ttype_value + i, 9, 16);
+	}
+
+	/* Get BACT/BSCT/BCNT/WACT/DCM_CTRL */
+	emi_value[j++] = MET_BM_GetBandwidthWordCount(); /* 20 */
+	emi_value[j++] = MET_BM_GetOverheadWordCount();
+	emi_value[j++] = MET_BM_GetBusCycCount();
+	emi_value[j++] = MET_BM_GetWordAllCount();
+	emi_value[j++] = MET_DRAMC_DCM_CTRL(0);
+
+	/* Get TACT */
+	emi_value[j++] = MET_BM_GetTransAllCount();
+
+	/* Get PageHist/PageMiss/InterBank/Idle */
+	for (n = 0; n < dram_chann_num; n++) {
+		emi_value[j++] = MET_DRAMC_GetPageHitCount(DRAMC_ALL, n);
+		emi_value[j++] = MET_DRAMC_GetPageMissCount(DRAMC_ALL, n);
+		emi_value[j++] = MET_DRAMC_GetInterbankCount(DRAMC_ALL, n);
+		emi_value[j++] = MET_DRAMC_GetIdleCount(n);
+		emi_value[j++] = ((MET_DRAMC_SPCMDRESP(n) >> 8) & 0x7); /* refresh rate */
+		emi_value[j++] = MET_DRAMC_RefPop(n);
+		emi_value[j++] = MET_DRAMC_Free26M(n);
+		emi_value[j++] = MET_DRAMC_RByte(n);
+		emi_value[j++] = MET_DRAMC_WByte(n);
+	}
+	/* TTYPE */
+	if (ttype17_21_en == BM_TTYPE17_21_ENABLE)    /*17~21 for ttype*/
+		MET_BM_GetLatencyCycle(emi_ttype_value + 16, 17, 21);
+
+	/* Get tsct */
+	if (emi_tsct_enable == 1) {
+		emi_tsct[k++] = MET_BM_GetTransCount(1);
+		emi_tsct[k++] = MET_BM_GetTransCount(2);
+		emi_tsct[k++] = MET_BM_GetTransCount(3);
+	}
+	/*get mdct rsv buffer*/
+	if (emi_mdct_enable == 1) {
+		emi_mdct_value[0] = (MET_BM_GetMDCT() >> 16) & 0x7;
+		emi_mdct_value[1] = (MET_BM_GetMDCT_2() & 0x7);
+	}
+
+	if (dramc_pdir_enable == 1 && DRAMC_VER >= 2 )
+		_ms_dramc(dramc_pdir_value, dram_chann_num);
+
+	MET_BM_Continue();
+	MET_BM_Clear_Start();
+
+	return j;
+}
+
+
+/*======================================================================*/
+/*	MET Device Operations						*/
+/*======================================================================*/
+static int emi_inited;
+
+static int met_emi_create(struct kobject *parent)
+{
+	int ret = 0;
+	int i;
+
+	for (i = 0; i < 21; i++) {
+		ttype_master_val[i] = BM_MASTER_M0;
+		ttype_nbeat_val[i] = BM_TRANS_TYPE_1BEAT;
+		ttype_nbyte_val[i] = BM_TRANS_TYPE_8Byte;
+		ttype_burst_val[i] = BM_TRANS_TYPE_BURST_INCR;
+		ttype_busid_val[i] = 0xfffff;   /*default disable ttype bus sel if busid > 0xff_ff */
+		ttype_rw_val[i] =  BM_TRANS_RW_DEFAULT;
+	}
+
+	ret = MET_BM_Init();
+	if (ret != 0) {
+		pr_notice("MET_BM_Init failed!!!\n");
+		ret = 0;        /* will retry later */
+	} else {
+		emi_inited = 1;
+	}
+
+	kobj_emi = parent;
+
+#define KOBJ_ATTR_ITEM(attr_name) \
+	do { \
+		ret = sysfs_create_file(kobj_emi, &attr_name ## _attr.attr); \
+		if (ret != 0) { \
+			pr_notice("Failed to create " #attr_name " in sysfs\n"); \
+			return ret; \
+		} \
+	} while (0)
+	KOBJ_ATTR_LIST;
+#undef  KOBJ_ATTR_ITEM
+
+	return ret;
+}
+
+
+static void met_emi_delete(void)
+{
+#define KOBJ_ATTR_ITEM(attr_name) \
+	sysfs_remove_file(kobj_emi, &attr_name ## _attr.attr)
+	if (kobj_emi != NULL) {
+		KOBJ_ATTR_LIST;
+		kobj_emi = NULL;
+	}
+#undef  KOBJ_ATTR_ITEM
+
+	if (emi_inited)
+		MET_BM_DeInit();
+}
+
+
+static void met_emi_start(void)
+{
+	unsigned int bw_limiter[NIDX_BL];
+
+	if (!emi_inited) {
+		if (MET_BM_Init() != 0) {
+			met_emi.mode = 0;
+			pr_notice("MET_BM_Init failed!!!\n");
+			return;
+		}
+		emi_inited = 1;
+	}
+
+	if (do_emi()) {
+		emi_init();
+		MET_BM_Clear_Start();
+
+		/* Draw the first BW Limiter point */
+		if (bw_limiter_enable == BM_BW_LIMITER_ENABLE) {
+			emi_bw_limiter(bw_limiter);
+			ms_bw_limiter(NIDX_BL, bw_limiter);
+			/* init countdown value */
+			countdown = CNT_COUNTDOWN;
+		}
+	}
+}
+
+
+static void met_emi_stop(void)
+{
+	unsigned int bw_limiter[NIDX_BL];
+
+	if (!emi_inited)
+		return;
+
+	if (met_emi_regdump == 1)
+		emi_dump_reg();
+
+	if (do_emi()) {
+		/* Draw the last BW Limiter point */
+		if (bw_limiter_enable == BM_BW_LIMITER_ENABLE) {
+			/*
+			 * Skip drawing when we just draw
+			 * the point at last polling.
+			 */
+			if (countdown < CNT_COUNTDOWN) {
+				emi_bw_limiter(bw_limiter);
+				ms_bw_limiter(NIDX_BL, bw_limiter);
+			}
+		}
+
+		emi_stop();
+		emi_uninit();
+	}
+}
+
+
+static void met_emi_polling(unsigned long long stamp, int cpu)
+{
+	unsigned int emi_value[NIDX];
+	unsigned int emi_tsct[3];
+	unsigned int emi_ttype_value[21];
+	unsigned int dramc_pdir_value[DRAMC_Debug_MAX_CNT * NCH];
+	unsigned int emi_mdct_value[2];
+	unsigned int chn_emi_drs[6];
+
+	if (!do_emi())
+		return;
+
+	/* get emi & dramc counters */
+	emi_value[0] = 0;       /* 0: pure linux MET , 0xa5: OnDieMET*/
+	emi_value[1] = 0;       /* EBM pause duration (ns)*/
+	emi_polling(emi_value + 2, emi_tsct, emi_ttype_value, dramc_pdir_value, emi_mdct_value);
+
+	/* get and output BW Limiter */
+	if (bw_limiter_enable == BM_BW_LIMITER_ENABLE) {
+		unsigned int bw_limiter[NIDX_BL];
+
+		if (countdown > 0) {
+			countdown--;
+		} else {
+			emi_bw_limiter(bw_limiter);
+			ms_bw_limiter(NIDX_BL, bw_limiter);
+			/* reload countdown value */
+			countdown = CNT_COUNTDOWN;
+		}
+	}
+
+	/* output emi */
+	ms_emi(NIDX_EMI - NTTYPE + (NCNT * dram_chann_num), emi_value);
+
+	/* output tsct*/
+	if (emi_tsct_enable == 1)
+		ms_emi_tsct(3, emi_tsct);
+
+	/* output mdct*/
+	if (emi_mdct_enable == 1)
+		ms_emi_mdct(2, emi_mdct_value);
+
+	/* output dramc*/
+	if (dramc_pdir_enable == 1 && DRAMC_VER >= 2 )
+		ms_dramc(DRAMC_Debug_MAX_CNT * dram_chann_num, dramc_pdir_value);
+
+	/* output ms_ttype */
+	if (ttype17_21_en == BM_TTYPE17_21_ENABLE)
+		ms_ttype(21, emi_ttype_value);
+
+	/* adjust MDMCU buffer */
+	if (mdmcu_sel_enable == 1)
+		MET_BM_SetMDCT_MDMCU(rd_mdmcu_rsv_num);
+}
+
+
+static void met_emi_resume(void)
+{
+	/* return directly when emi was closed */
+	if (!do_emi())
+		return;
+
+	/* remap EMI_BM related reg*/
+	emi_init();
+
+	/* restarn counting */
+	MET_BM_Clear_Start();
+}
+
+
+static const char help[] = "  --emi                                 monitor EMI banwidth\n";
+static int emi_print_help(char *buf, int len)
+{
+	return snprintf(buf, PAGE_SIZE, help);
+}
+
+
+#define TTYPE_NAME_STR_LEN  64
+/* static char ttype_name[21][TTYPE_NAME_STR_LEN]; */
+static int emi_print_header(char *buf, int len)
+{
+	int ret = 0;
+/*	int ret_m[21]; */
+	int i = 0;
+	unsigned int dram_data_rate_MHz;
+	unsigned int DRAM_TYPE;
+
+#if 0
+	/*ttype header info*/
+	for (i = 0; i < 21; i++) {
+		int k;
+
+		/*busid > 0xffff    not specific bus id , show all on specificmaster*/
+		if (ttype_busid_val[i] > 0xffff) {
+			int j;
+
+			for (j = 0; j < ARRAY_SIZE(ttype_master_list_item); j++) {
+				if (ttype_master_val[i] == ttype_master_list_item[j].key) {
+					ret_m[i] = snprintf(ttype_name[i], TTYPE_NAME_STR_LEN, "ttype%d_%s",
+							    i + 1, ttype_master_list_item[j].val);/*master*/
+					break;
+				}
+			}
+			if (j == ARRAY_SIZE(ttype_master_list_item))
+				ret_m[i] = snprintf(ttype_name[i], TTYPE_NAME_STR_LEN, "ttype%d_%s",
+						    i + 1, "unknown");
+		} else {
+			ret_m[i] = snprintf(ttype_name[i], TTYPE_NAME_STR_LEN, "ttype%d_%x",
+					    i + 1, ttype_busid_val[i]);/*busID*/
+		}
+
+		/*show beat type*/
+		for (k = 0; k < ARRAY_SIZE(ttype_nbeat_list_item); k++) {
+
+			if (ttype_nbeat_val[i] == ttype_nbeat_list_item[k].key)
+				ret_m[i] += snprintf(ttype_name[i] + ret_m[i], TTYPE_NAME_STR_LEN - ret_m[i], "_%d",
+						     ttype_nbeat_list_item[k].val); /*beat*/
+		}
+
+		/*show byte type*/
+		for (k = 0; k < ARRAY_SIZE(ttype_nbyte_list_item); k++) {
+
+			if (ttype_nbyte_val[i] == ttype_nbyte_list_item[k].key)
+				ret_m[i] += snprintf(ttype_name[i] + ret_m[i], TTYPE_NAME_STR_LEN - ret_m[i], "x%d",
+						     ttype_nbyte_list_item[k].val); /*byte*/
+		}
+
+		/*show burst type*/
+		for (k = 0; k < ARRAY_SIZE(ttype_burst_list_item); k++) {
+
+			if (ttype_burst_val[i] == ttype_burst_list_item[k].key)
+				ret_m[i] += snprintf(ttype_name[i] + ret_m[i], TTYPE_NAME_STR_LEN - ret_m[i], "_%s",
+						     ttype_burst_list_item[k].val); /*burst*/
+		}
+
+		/*show rw type*/
+		for (k = 0; k < ARRAY_SIZE(ttype_rw_list_item); k++) {
+
+			if (ttype_rw_val[i] == ttype_rw_list_item[k].key)
+				ret_m[i] += snprintf(ttype_name[i] + ret_m[i], TTYPE_NAME_STR_LEN - ret_m[i], "_%s",
+						     ttype_rw_list_item[k].val); /*rw*/
+		}
+	}
+#endif
+
+	if ((ttype1_16_en != BM_TTYPE1_16_ENABLE) && (emi_TP_busfiltr_enable != 1)) {
+		/* master selection header */
+		if (msel_enable) {
+			ret += snprintf(buf + ret, PAGE_SIZE - ret,
+					"met-info [000] 0.0: met_emi_msel: %x,%x,%x\n",
+					msel_group1 & BM_MASTER_ALL,
+					msel_group2 & BM_MASTER_ALL,
+					msel_group3 & BM_MASTER_ALL);
+		} else {
+			ret += snprintf(buf + ret, PAGE_SIZE - ret,
+					"met-info [000] 0.0: met_emi_msel: %x,%x,%x\n",
+					BM_MASTER_ALL & BM_MASTER_ALL,
+					BM_MASTER_ALL & BM_MASTER_ALL,
+					BM_MASTER_ALL & BM_MASTER_ALL);
+		}
+	} else {
+		/*ttype master if BM_TTYPE1_16_ENABLE or emi_TP_busfiltr_enable*/
+		ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"met-info [000] 0.0: met_emi_ttype_master: %x,%x,%x,%x\n",
+				ttype_master_val[0], ttype_master_val[1], ttype_master_val[2], ttype_master_val[3]);
+
+		if (emi_TP_busfiltr_enable == 1) {
+			/* busID if emi_TP_busfiltr_enable*/
+			ret += snprintf(buf + ret, PAGE_SIZE - ret,
+					"met-info [000] 0.0: met_emi_ttype_busid: %x,%x,%x,%x\n",
+					ttype_busid_val[0], ttype_busid_val[1], ttype_busid_val[2], ttype_busid_val[3]);
+		}
+	}
+
+	/*RW type header*/
+	ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_emi_rw_cfg: ");
+	if (rwtype == BM_READ_ONLY)
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "R");
+	else if (rwtype == BM_WRITE_ONLY)
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "W");
+	else
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "BOTH");
+
+	for (i = 0; i < 21; i++) {
+		if (ttype_rw_val[i] == BM_TRANS_RW_DEFAULT)
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, ",DEFAULT");
+		else if (ttype_rw_val[i] == BM_TRANS_RW_READONLY)
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, ",R");
+		else if (ttype_rw_val[i] == BM_TRANS_RW_WRITEONLY)
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, ",W");
+		else    /*BM_TRANS_RW_RWBOTH*/
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, ",BOTH");
+	}
+	ret += snprintf(buf + ret, PAGE_SIZE - ret, "\n");
+
+	/*ultra header*/
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"met-info [000] 0.0: met_emi_ultra_filter: %x\n", high_priority_filter);
+
+#if 0
+	/*ttype header (user define format)*/
+	if ((ttype1_16_en == BM_TTYPE1_16_ENABLE) && (ttype17_21_en == BM_TTYPE17_21_ENABLE)) {
+		/*header = ttype1~21t*/
+		int i;
+
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: ms_ud_sys_header: ms_ttype,");
+		for (i = 0; i < 21; i++)
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, "%s,", ttype_name[i]);
+
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x\n");
+
+	} else if (ttype17_21_en == BM_TTYPE17_21_ENABLE) {
+		/*header = ttype17~21t*/
+		int i;
+
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: ms_ud_sys_header: ms_ttype,");
+
+		for (i = 16; i < 21; i++)
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, "%s,", ttype_name[i]);
+
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "x,x,x,x,x\n");
+	}
+#else
+#if 1
+	/* ttype header */
+	if (ttype17_21_en == BM_TTYPE17_21_ENABLE) {
+		int i = 0;
+		int j = 0;
+
+		/* ttype master list */
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_emi_ttype_master_list: ");
+		for (i = 0; i < 21; i++) {
+			for (j = 0; j < ARRAY_SIZE(ttype_master_list_item); j++) {
+				if (ttype_master_val[i] == ttype_master_list_item[j].key) {
+					ret += snprintf(buf + ret, PAGE_SIZE - ret, "%s,", ttype_master_list_item[j].val);
+				}
+			}
+		}
+		/* remove the last comma */
+		snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+		/* ttype busid list */
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_emi_ttype_busid_list: ");
+		for (i = 0; i < 21; i++)
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, "%x,", ttype_busid_val[i]);
+
+		snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+		/* ttype nbeat list */
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_emi_ttype_nbeat_list: ");
+		for (i = 0; i < 21; i++) {
+			for (j = 0; j < ARRAY_SIZE(ttype_nbeat_list_item); j++) {
+				if (ttype_nbeat_val[i] == ttype_nbeat_list_item[j].key) {
+					ret += snprintf(buf + ret, PAGE_SIZE - ret, "%d,", ttype_nbeat_list_item[j].val);
+				}
+			}
+		}
+		snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+		/* ttype nbyte list */
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_emi_ttype_nbyte_list: ");
+		for (i = 0; i < 21; i++) {
+			for (j = 0; j < ARRAY_SIZE(ttype_nbyte_list_item); j++) {
+				if (ttype_nbyte_val[i] == ttype_nbyte_list_item[j].key) {
+					ret += snprintf(buf + ret, PAGE_SIZE - ret, "%d,", ttype_nbyte_list_item[j].val);
+				}
+			}
+		}
+		snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+		/* ttype burst list */
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_emi_ttype_burst_list: ");
+		for (i = 0; i < 21; i++) {
+			for (j = 0; j < ARRAY_SIZE(ttype_burst_list_item); j++) {
+				if (ttype_burst_val[i] == ttype_burst_list_item[j].key) {
+					ret += snprintf(buf + ret, PAGE_SIZE - ret, "%s,", ttype_burst_list_item[j].val);
+				}
+			}
+		}
+		snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+		/* ttype enable */
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_emi_ttype_enable: %d,%d\n",ttype1_16_en, ttype17_21_en);
+
+
+	}
+#endif
+#endif
+	/*IP version*/
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"met-info [000] 0.0: DRAMC_VER: %d\n", DRAMC_VER);
+
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"met-info [000] 0.0: EMI_VER: %d.%d\n", EMI_VER_MAJOR, EMI_VER_MINOR);
+
+#ifndef CONFIG_MTK_TINYSYS_SSPM_SUPPORT
+	dram_chann_num = MET_EMI_GetDramChannNum();
+	/*	met_dram_chann_num_header
+	 *	channel number
+	 *	LP4: 2, LP3: 1
+	 */
+
+	/*
+	 *	the ddr type define :
+	 *	enum DDRTYPE {
+	 *	TYPE_LPDDR3 = 1,
+	 *	TYPE_LPDDR4,
+	 *	TYPE_LPDDR4X,
+	 *	TYPE_LPDDR2
+	 *	};
+	 */
+	if (get_ddr_type_symbol) {
+		DRAM_TYPE = get_ddr_type_symbol();
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_dram_type: %d\n", DRAM_TYPE);
+
+		if ((DRAM_TYPE == 2) || (DRAM_TYPE == 3))
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_dram_chann_num_header: %d,%d,%d,%d\n",
+					dram_chann_num, DRAM_EMI_BASECLOCK_RATE_LP4,
+					DRAM_IO_BUS_WIDTH_LP4, DRAM_DATARATE);
+		else
+			ret += snprintf(buf + ret , PAGE_SIZE -ret, "met-info [000] 0.0: met_dram_chann_num_header: %d,%d,%d,%d\n",
+					dram_chann_num, DRAM_EMI_BASECLOCK_RATE_LP3,
+					DRAM_IO_BUS_WIDTH_LP3, DRAM_DATARATE);
+	} else
+		METERROR("[%s][%d]get_ddr_type_symbol = NULL , use the TYPE_LPDDR3 setting\n", __func__, __LINE__);
+
+	/* metemi_func_opt for middleware */
+	ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: metemi_func_opt_header: %d\n",
+			metemi_func_opt);
+
+	/* met_emi_clockrate */
+	if (get_dram_data_rate_symbol) {
+		dram_data_rate_MHz = get_dram_data_rate_symbol();
+	} else {
+		METERROR("get_dram_data_rate_symbol = NULL\n");
+		dram_data_rate_MHz = 0;
+	}
+
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"met-info [000] 0.0: met_dram_clockrate: %d\n",
+			dram_data_rate_MHz);
+
+	/* 1 : by ondiemet, 0: by pure linux */
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"met-info [000] 0.0: emi_use_ondiemet: %u\n",
+			emi_use_ondiemet);
+
+	/*dram bank num*/
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"met-info [000] 0.0: met_dram_rank_num_header: %u,%u\n", MET_EMI_GetDramRankNum(),
+				MET_EMI_GetDramRankNum());
+
+	/* ms_emi header */
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"# ms_emi: TS0,TS1,GP0_WSCT,GP1_WSCT,GP2_WSCT,GP3_WSCT,");
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"M0_LATENCY,M1_LATENCY,M2_LATENCY,M3_LATENCY,M4_LATENCY,M5_LATENCY,M6_LATENCY,M7_LATENCY,");
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"M0_TRANS,M1_TRANS,M2_TRANS,M3_TRANS,M4_TRANS,M5_TRANS,M6_TRANS,M7_TRANS,");
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"BACT,BSCT,BCNT,WACT,DCM_CTRL,TACT,");
+
+	for (i = 0; i < dram_chann_num; i++) {
+		if (i != 0)
+			ret += snprintf(buf + ret, PAGE_SIZE - ret,
+					",");
+		ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"PageHit_%d,PageMiss_%d,InterBank_%d,Idle_%d,", i, i, i, i);
+		ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"mr4_%d,refresh_pop_%d,freerun_26m_%d,", i, i, i);
+		ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"read_bytes_%d,write_bytes_%d", i, i);
+	}
+	ret += snprintf(buf + ret, PAGE_SIZE - ret, "\n");
+
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"met-info [000] 0.0: met_emi_header: TS0,TS1,GP0_WSCT,GP1_WSCT,GP2_WSCT,GP3_WSCT,");
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"M0_LATENCY,M1_LATENCY,M2_LATENCY,M3_LATENCY,M4_LATENCY,M5_LATENCY,M6_LATENCY,M7_LATENCY,");
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"M0_TRANS,M1_TRANS,M2_TRANS,M3_TRANS,M4_TRANS,M5_TRANS,M6_TRANS,M7_TRANS,");
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"BACT,BSCT,BCNT,WACT,DCM_CTRL,TACT,");
+
+	for (i = 0; i < dram_chann_num; i++) {
+		if (i != 0)
+			ret += snprintf(buf + ret, PAGE_SIZE - ret,
+					",");
+		ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"PageHit_%d,PageMiss_%d,InterBank_%d,Idle_%d,", i, i, i, i);
+		ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"mr4_%d,refresh_pop_%d,freerun_26m_%d,", i, i, i);
+		ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"read_bytes_%d,write_bytes_%d", i, i);
+	}
+	ret += snprintf(buf + ret, PAGE_SIZE - ret, "\n");
+
+	/*TSCT header*/
+	if (emi_tsct_enable == 1) {
+		ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"met-info [000] 0.0: ms_emi_tsct_header: ms_emi_tsct,");
+		ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"tsct1,tsct2,tsct3\n");
+	}
+
+	/*MDCT header*/
+	if (emi_mdct_enable == 1) {
+		ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"met-info [000] 0.0: ms_emi_mdct_header: ms_emi_mdct,");
+		ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"RD_ULTRA,RD_MDMCU\n");
+	}
+
+	/* met_bw_limiter_header */
+	if (bw_limiter_enable == BM_BW_LIMITER_ENABLE) {
+		ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"met-info [000] 0.0: met_bw_limiter_header: CLK,");
+		ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"ARBA,ARBB,ARBC,ARBD,ARBE,ARBF,ARBG,ARBH,BWCT0,BWCT1,BWCT2,BWCT3,BWCT4,BWST0,BWST1,BWCT0_2ND,BWCT1_2ND,BWST_2ND\n");
+	}
+
+	/* DRAM DVFS header */
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"met-info [000] 0.0: DRAM_DVFS_header: datarate(MHz)\n");
+
+	/*PDIR met_dramc_header*/
+	if (dramc_pdir_enable == 1 && DRAMC_VER >= 2 ) {
+		ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"met-info [000] 0.0: met_dramc_header: ");
+		for (i = 0; i < dram_chann_num; i++) {
+			if (i != 0)
+				ret += snprintf(buf + ret, PAGE_SIZE - ret,
+						",");
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, "freerun_26m_%d,", i);
+			ret += snprintf(buf + ret, PAGE_SIZE - ret,
+					"rk0_pre_sb_%d,rk0_pre_pd_%d,rk0_act_sb_%d,rk0_act_pd_%d,", i, i, i, i);
+			ret += snprintf(buf + ret, PAGE_SIZE - ret,
+					"rk1_pre_sb_%d,rk1_pre_pd_%d,rk1_act_sb_%d,rk1_act_pd_%d,", i, i, i, i);
+			ret += snprintf(buf + ret, PAGE_SIZE - ret,
+					"rk2_pre_sb_%d,rk2_pre_pd_%d,rk2_act_sb_%d,rk2_act_pd_%d", i, i, i, i);
+		}
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "\n");
+	}
+#endif
+
+	return ret;
+}
+
+
+#if defined(CONFIG_MTK_TINYSYS_SSPM_SUPPORT) && defined(ONDIEMET_SUPPORT)
+static int ondiemet_emi_print_header(char *buf, int len)
+{
+	emi_use_ondiemet = 1;
+	/* return emi_print_ondiemet_header(buf, len); */
+	return emi_print_header(buf, len);
+}
+
+
+static void MET_BM_IPI_REGISTER_CB(void)
+{
+	int ret, i;
+	unsigned int rdata;
+	unsigned int ipi_buf[4];
+
+	for (i = 0; i < 4; i++)
+		ipi_buf[i] = 0;
+
+	if (sspm_buf_available == 1) {
+		ipi_buf[0] = MET_MAIN_ID | (MID_EMI << MID_BIT_SHIFT) | MET_ARGU | SET_REGISTER_CB;
+		ret = sspm_ipi_send_sync(IPI_ID_MET, IPI_OPT_WAIT, (void *)ipi_buf, 0, &rdata, 1);
+	}
+}
+
+
+static void MET_BM_IPI_configs(void)
+{
+	int ret, i;
+	unsigned int rdata;
+	unsigned int ipi_buf[4];
+
+	for (i = 0; i < 4; i++)
+		ipi_buf[i] = 0;
+
+	if (sspm_buf_available == 1) {
+		ipi_buf[0] = MET_MAIN_ID | (MID_EMI << MID_BIT_SHIFT) | MET_ARGU | SET_EBM_CONFIGS1;
+		ipi_buf[1] = INTERNAL_MODE << 16;
+		ipi_buf[2] = EMI_VER_MAJOR << 24 | EMI_VER_MINOR << 16 | DRAMC_VER << 8 | 0;
+		ret = sspm_ipi_send_sync(IPI_ID_MET, IPI_OPT_WAIT, (void *)ipi_buf, 0, &rdata, 1);
+	}
+}
+
+
+static void ondiemet_emi_start(void)
+{
+	MET_BM_IPI_REGISTER_CB();
+	if (!emi_inited) {
+		if (MET_BM_Init() != 0) {
+			met_emi.mode = 0;
+			pr_notice("MET_BM_Init failed!!!\n");
+			return;
+		}
+		emi_inited = 1;
+	}
+	MET_BM_IPI_configs();
+
+	if (do_emi())
+		emi_init();
+
+	ondiemet_module[ONDIEMET_SSPM] |= ID_EMI;
+}
+
+
+static void ondiemet_emi_stop(void)
+{
+	if (!emi_inited)
+		return;
+
+	if (do_emi())
+		emi_uninit();
+}
+#endif
+
+
+struct metdevice met_emi = {
+	.name			= "emi",
+	.owner			= THIS_MODULE,
+	.type			= MET_TYPE_BUS,
+	.create_subfs		= met_emi_create,
+	.delete_subfs		= met_emi_delete,
+	.cpu_related		= 0,
+	.start			= met_emi_start,
+	.stop			= met_emi_stop,
+	.resume			= met_emi_resume,
+	.timed_polling		= met_emi_polling,
+	.print_help		= emi_print_help,
+	.print_header		= emi_print_header,
+#if defined(CONFIG_MTK_TINYSYS_SSPM_SUPPORT) && defined(ONDIEMET_SUPPORT)
+	.ondiemet_mode		= 1,
+	.ondiemet_start		= ondiemet_emi_start,
+	.ondiemet_stop		= ondiemet_emi_stop,
+	.ondiemet_print_help	= emi_print_help,
+	.ondiemet_print_header	= ondiemet_emi_print_header,
+#else
+	.ondiemet_mode		= 0,
+#endif
+};
diff --git a/src/devtools/met-driver/4.14/mt2731/emi/SEDA2/mtk_emi_bm.c b/src/devtools/met-driver/4.14/mt2731/emi/SEDA2/mtk_emi_bm.c
new file mode 100644
index 0000000..3ef0c99
--- /dev/null
+++ b/src/devtools/met-driver/4.14/mt2731/emi/SEDA2/mtk_emi_bm.c
@@ -0,0 +1,1038 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/io.h>
+#include <mt-plat/sync_write.h>
+#include <mt-plat/mtk_io.h>
+#include "mtk_typedefs.h"
+#include "plf_init.h"
+#include "mtk_emi_bm.h"
+#include "mtk_dramc_reg.h"
+#include "met_drv.h"
+#include "interface.h"
+
+#undef	DEBUG
+#undef	debug_reg
+#ifdef	debug_reg
+static inline unsigned int emi_readl(void __iomem *padr)
+{
+	unsigned int tmp;
+
+	tmp = readl(padr);
+	MET_TRACE("[MET_EMI] RD_Reg: %p: %08x\n", padr, tmp);
+	return tmp;
+}
+
+
+static inline void __emi_reg_sync_writel(unsigned int data, void __iomem *padr)
+{
+	unsigned int tmp;
+
+	mt_reg_sync_writel(data, padr);
+	tmp = readl(padr);
+	MET_TRACE("[MET_EMI] WR_Reg: %p: %08x, %08x\n", padr, data, tmp);
+}
+
+
+#define emi_reg_sync_writel(data, adr)  __emi_reg_sync_writel(data, IOMEM(adr))
+
+#else
+#define emi_readl               readl
+#define emi_reg_sync_writel     mt_reg_sync_writel
+#endif
+
+#define MASK_MASTER     0xFF
+#define MASK_TRANS_TYPE 0xFF
+
+static int dram_chann_num;
+static void __iomem *BaseAddrEMI;
+static void __iomem *BaseAddrCHN_EMI[2];
+
+static int dramc0_dcm_enable;
+static int dramc1_dcm_enable;
+
+#define CH0_MISC_CG_CTRL0 (((unsigned long) BaseAddrDDRPHY_AO[0]) + 0x284)
+#define CH1_MISC_CG_CTRL0 (((unsigned long) BaseAddrDDRPHY_AO[1]) + 0x284)
+const unsigned int emi_config[] = {
+	EMI_BMEN,
+	EMI_MSEL,
+	EMI_MSEL2,
+	EMI_MSEL3,
+	EMI_MSEL4,
+	EMI_MSEL5,
+	EMI_MSEL6,
+	EMI_MSEL7,
+	EMI_MSEL8,
+	EMI_MSEL9,
+	EMI_MSEL10,
+	EMI_BMID0,
+	EMI_BMID1,
+	EMI_BMID2,
+	EMI_BMID3,
+	EMI_BMID4,
+	EMI_BMID5,
+	EMI_BMID6,
+	EMI_BMID7,
+	EMI_BMID8,
+	EMI_BMID9,
+	EMI_BMID10,
+	EMI_BMEN1,
+	EMI_BMEN2,
+	EMI_BMRW0,
+	EMI_BMRW1
+};
+#define EMI_CONFIG_MX_NR (sizeof(emi_config)/sizeof(unsigned int))
+static unsigned int emi_config_val[EMI_CONFIG_MX_NR];
+
+
+/*
+ *   MET_REG_BSET/MET_REG_BCLR:
+ *   reading value before set and clear
+ */
+static inline void MET_REG_BSET(unsigned long reg, u32 shift)
+{
+	unsigned int read_val = 0;
+
+	read_val = emi_readl(IOMEM(reg));
+	emi_reg_sync_writel(read_val | (1 << shift), reg);
+}
+
+
+static inline void MET_REG_BCLR(unsigned long reg, u32 shift)
+{
+	unsigned int read_val = 0;
+
+	read_val = emi_readl(IOMEM(reg));
+	emi_reg_sync_writel(read_val & (~((1 << shift) & 0xFFFFFFFF)), reg);
+}
+
+
+int MET_BM_Init(void)
+{
+	int i;
+	int idx;
+
+	/*emi*/
+	if (!mt_cen_emi_base_get_symbol) {
+		METERROR("[%d]mt_cen_emi_base_get_symbol = NULL\n", __LINE__);
+		PR_BOOTMSG_ONCE("[%d]mt_cen_emi_base_get_symbol = NULL\n", __LINE__);
+		return -1;
+	}
+
+	BaseAddrEMI = mt_cen_emi_base_get_symbol();
+	if (BaseAddrEMI == 0) {
+		METERROR("BaseAddrEMI = 0\n");
+		PR_BOOTMSG_ONCE("BaseAddrEMI = 0\n");
+		return -1;
+	}
+
+	METINFO("MET EMI: map emi to %p\n", BaseAddrEMI);
+	PR_BOOTMSG("MET EMI: map emi to %p\n", BaseAddrEMI);
+
+	/* get dram channel number */
+	dram_chann_num = MET_EMI_GetDramChannNum();
+	METINFO("[%s][%d]dram_chann_num = %d\n", __func__, __LINE__, dram_chann_num);
+
+	if (dram_chann_num > MAX_DRAMC_CHANN) {
+		METERROR("dram_chann_num %d > %d\n", dram_chann_num, MAX_DRAMC_CHANN);
+		PR_BOOTMSG("dram_chann_num %d > %d\n", dram_chann_num, MAX_DRAMC_CHANN);
+		return -1;
+	}
+
+	if (!mt_dramc_nao_chn_base_get_symbol) {
+		METERROR("mt_dramc_nao_cha_base_get = NULL\n");
+		PR_BOOTMSG_ONCE("mt_dramc_nao_cha_base_get = NULL\n");
+		return -1;
+	}
+
+	for (i = 0; i < dram_chann_num; i++) {
+		BaseAddrDRAMC[i] = mt_dramc_nao_chn_base_get_symbol(i);
+		if (BaseAddrDRAMC[i] == 0) {
+			METERROR("BaseAddrDRAMC%d = 0\n", i);
+			PR_BOOTMSG_ONCE("BaseAddrDRAMC%d = 0\n", i);
+			return -1;
+		}
+
+		METINFO("MET EMI: map nao dramc%c to %p\n",'A'+i, BaseAddrDRAMC[i]);
+		PR_BOOTMSG("MET EMI: map nao dramc%c to %p\n", 'A'+i, BaseAddrDRAMC[i]);
+	}
+
+	/*dram DRAMC_DTS_DDRPHY_AO*/
+	/* get DRS base address */
+	if (!mt_ddrphy_chn_base_get_symbol) {
+		METERROR("mt_ddrphy_chn_base_get = NULL\n");
+		PR_BOOTMSG_ONCE("mt_ddrphy_chn_base_get = NULL\n");
+		return -1;
+	}
+
+	if (!mt_chn_emi_base_get_symbol) {
+		METERROR("mt_chn_emi_base_get_symbol = NULL\n");
+		PR_BOOTMSG_ONCE("mt_chn_emi_base_get_symbol = NULL\n");
+		return -1;
+	}
+
+	for (i = 1; i <= dram_chann_num && i < 3; i++) {
+		idx = i - 1;
+		BaseAddrDDRPHY_AO[idx] = mt_ddrphy_chn_base_get_symbol(idx);
+		if (BaseAddrDDRPHY_AO[idx] == 0) {
+			METERROR("BaseAddrDDRPHY_AO[%d] = 0\n", idx);
+			PR_BOOTMSG_ONCE("BaseAddrDDRPHY_AO[%d] = 0\n", idx);
+			return -1;
+		}
+
+		METINFO("MET EMI: map ddrphy%d AO to %p\n", idx, BaseAddrDDRPHY_AO[idx]);
+		PR_BOOTMSG("MET EMI: map ddrphy%d AO to %p\n", idx, BaseAddrDDRPHY_AO[idx]);
+
+		BaseAddrCHN_EMI[idx] = mt_chn_emi_base_get_symbol(idx);
+		if (BaseAddrCHN_EMI[idx] == 0) {
+			METERROR("BaseAddrCHN_EMI[%d] = 0\n", idx);
+			PR_BOOTMSG_ONCE("BaseAddrCHN_EMI[%d] = 0\n", idx);
+			return -1;
+		}
+
+		METINFO("MET EMI: map BaseAddrCHN_EMI[%d] to %p\n", idx, BaseAddrCHN_EMI[idx]);
+		PR_BOOTMSG("MET EMI: map BaseAddrCHN_EMI[%d] to %p\n", idx, BaseAddrCHN_EMI[idx]);
+	}
+
+	/*dram DRAMC_DTS_DRAMC0_AO*/
+	if (!mt_dramc_chn_base_get_symbol) {
+		METERROR("mt_dramc_chn_base_get = NULL\n");
+		PR_BOOTMSG_ONCE("mt_dramc_chn_base_get = NULL\n");
+		return -1;
+	}
+
+	BaseAddrDRAMC0_AO = mt_dramc_chn_base_get_symbol(0);
+	if (BaseAddrDRAMC0_AO == 0) {
+		METERROR("BaseAddrDRAMC0_AO = 0\n");
+		PR_BOOTMSG_ONCE("BaseAddrDRAMC0_AO = 0\n");
+		return -1;
+	}
+
+	METINFO("MET EMI: map AO dramcA to %p\n", BaseAddrDRAMC0_AO);
+	PR_BOOTMSG("MET EMI: map AO dramcA to %p\n", BaseAddrDRAMC0_AO);
+
+	return 0;
+}
+
+
+void MET_BM_DeInit(void)
+{
+}
+
+
+void MET_BM_SaveCfg(void)
+{
+	int i;
+
+	for (i = 0; i < EMI_CONFIG_MX_NR; i++)
+		emi_config_val[i] = emi_readl(IOMEM(ADDR_EMI + emi_config[i]));
+}
+
+
+void MET_BM_RestoreCfg(void)
+{
+	int i;
+
+	for (i = 0; i < EMI_CONFIG_MX_NR; i++)
+		emi_reg_sync_writel(emi_config_val[i], ADDR_EMI + emi_config[i]);
+}
+
+
+void MET_BM_Clear_Start(void)
+{
+	/* Force EMI idle low */
+	MET_REG_BSET(ADDR_EMI + EMI_BMEN, BUS_MON_IDLE_SHIFT);
+
+	/* Disable dramc dcm */
+	switch (dram_chann_num) {
+	case 1:
+		MET_REG_BSET(CH0_MISC_CG_CTRL0, DRAMC_CG_SHIFT);
+		break;
+	case 2:
+		MET_REG_BSET(CH0_MISC_CG_CTRL0, DRAMC_CG_SHIFT);
+		MET_REG_BSET(CH1_MISC_CG_CTRL0, DRAMC_CG_SHIFT);
+		break;
+	default:
+		METERROR("Error: dram_chann_num = %d\n", dram_chann_num);
+	}
+
+	/* Disable EBM */
+	MET_REG_BCLR(ADDR_EMI + EMI_BMEN, BUS_MON_EN_SHIFT);
+
+	/* Enable EBM */
+	MET_REG_BSET(ADDR_EMI + EMI_BMEN, BUS_MON_EN_SHIFT);
+
+	/* Enable EMI dcm */
+	MET_REG_BCLR(ADDR_EMI + EMI_BMEN, BUS_MON_IDLE_SHIFT);
+
+	/* restore dramc dcm */
+	switch (dram_chann_num) {
+	case 1:
+		if (dramc0_dcm_enable)
+			MET_REG_BCLR(CH0_MISC_CG_CTRL0, DRAMC_CG_SHIFT);
+		else
+			MET_REG_BSET(CH0_MISC_CG_CTRL0, DRAMC_CG_SHIFT);
+		break;
+	case 2:
+		if (dramc0_dcm_enable)
+			MET_REG_BCLR(CH0_MISC_CG_CTRL0, DRAMC_CG_SHIFT);
+		else
+			MET_REG_BSET(CH0_MISC_CG_CTRL0, DRAMC_CG_SHIFT);
+
+		if (dramc1_dcm_enable)
+			MET_REG_BCLR(CH1_MISC_CG_CTRL0, DRAMC_CG_SHIFT);
+		else
+			MET_REG_BSET(CH1_MISC_CG_CTRL0, DRAMC_CG_SHIFT);
+		break;
+	default:
+		METERROR("Error: dram_chann_num = %d\n", dram_chann_num);
+	}
+}
+
+
+void MET_BM_Enable(const unsigned int enable)
+{
+	unsigned long int value_check;
+	int i = 0;
+
+	while (i < 100) {
+		/* Force EMI idle low */
+		MET_REG_BSET(ADDR_EMI + EMI_BMEN, BUS_MON_IDLE_SHIFT);
+
+		/* disable dramc dcm */
+		switch (dram_chann_num) {
+		case 1:
+			MET_REG_BSET(CH0_MISC_CG_CTRL0, DRAMC_CG_SHIFT);
+			break;
+		case 2:
+			MET_REG_BSET(CH0_MISC_CG_CTRL0, DRAMC_CG_SHIFT);
+			MET_REG_BSET(CH1_MISC_CG_CTRL0, DRAMC_CG_SHIFT);
+			break;
+		default:
+			METERROR("Error: dram_chann_num = %d\n", dram_chann_num);
+		}
+
+		if (enable == 0)
+			/* Disable EBM */
+			MET_REG_BCLR(ADDR_EMI + EMI_BMEN, BUS_MON_EN_SHIFT);
+		else
+			/* Enable EBM */
+			MET_REG_BSET(ADDR_EMI + EMI_BMEN, BUS_MON_EN_SHIFT);
+
+		/* Enable EMI dcm */
+		MET_REG_BCLR(ADDR_EMI + EMI_BMEN, BUS_MON_IDLE_SHIFT);
+
+		/* restore dramc dcm */
+		switch (dram_chann_num) {
+		case 1:
+			if (dramc0_dcm_enable)
+				MET_REG_BCLR(CH0_MISC_CG_CTRL0, DRAMC_CG_SHIFT);
+			else
+				MET_REG_BSET(CH0_MISC_CG_CTRL0, DRAMC_CG_SHIFT);
+			break;
+		case 2:
+			if (dramc0_dcm_enable)
+				MET_REG_BCLR(CH0_MISC_CG_CTRL0, DRAMC_CG_SHIFT);
+			else
+				MET_REG_BSET(CH0_MISC_CG_CTRL0, DRAMC_CG_SHIFT);
+
+			if (dramc1_dcm_enable)
+				MET_REG_BCLR(CH1_MISC_CG_CTRL0, DRAMC_CG_SHIFT);
+			else
+				MET_REG_BSET(CH1_MISC_CG_CTRL0, DRAMC_CG_SHIFT);
+			break;
+		default:
+			METERROR("Error: dram_chann_num = %d\n", dram_chann_num);
+		}
+
+		value_check = emi_readl(IOMEM(ADDR_EMI + EMI_BMEN));
+
+		if (enable == 0) {
+			/* EN == 0, IDLE == 0 when EMI RESET */
+			if (!test_bit(BUS_MON_EN_SHIFT, &value_check)
+			    && !test_bit(BUS_MON_IDLE_SHIFT, &value_check)) {
+				break;
+			}
+		} else {
+			/* EN == 1, IDLE == 0 when EMI START */
+			if (test_bit(BUS_MON_EN_SHIFT, &value_check)
+			    && !test_bit(BUS_MON_IDLE_SHIFT, &value_check)) {
+				break;
+			}
+		}
+		i++;
+	}
+
+	/*MET_TRACE("[MET_BM_ENABLE] value_check: %lx, enable = %d\n", value_check, enable); */
+
+}
+
+
+#if 0
+void BM_Disable(void)
+{
+	const unsigned int value = emi_readl(IOMEM(ADDR_EMI + EMI_BMEN));
+
+	emi_reg_sync_writel(value & (~BUS_MON_EN), ADDR_EMI + EMI_BMEN);
+}
+#endif
+
+
+void MET_BM_Pause(void)
+{
+	const unsigned int value = emi_readl(IOMEM(ADDR_EMI + EMI_BMEN));
+
+	emi_reg_sync_writel(value | (1 << BUS_MON_PAUSE_SHIFT), ADDR_EMI + EMI_BMEN);
+}
+
+
+void MET_BM_Continue(void)
+{
+	const unsigned int value = emi_readl(IOMEM(ADDR_EMI + EMI_BMEN));
+
+	emi_reg_sync_writel(value & (~(1 << BUS_MON_PAUSE_SHIFT)), ADDR_EMI + EMI_BMEN);
+}
+
+
+unsigned int MET_BM_IsOverrun(void)
+{
+	/*
+	 * return 0 if EMI_BCNT(bus cycle counts) or
+	 * EMI_WACT(total word counts) is overrun,
+	 * otherwise return an !0 value
+	 */
+	const unsigned int value = emi_readl(IOMEM(ADDR_EMI + EMI_BMEN));
+
+	return (value & (1 << BC_OVERRUN_SHIFT));
+}
+
+
+unsigned int MET_BM_GetReadWriteType(void)
+{
+	const unsigned int value = emi_readl(IOMEM(ADDR_EMI + EMI_BMEN));
+
+	return ((value & 0xFFFFFFCF) >> 4);
+}
+
+
+void MET_BM_SetReadWriteType(const unsigned int ReadWriteType)
+{
+	const unsigned int value = emi_readl(IOMEM(ADDR_EMI + EMI_BMEN));
+
+	/*
+	 * ReadWriteType: 00/11 --> both R/W
+	 *                   01 --> only R
+	 *                   10 --> only W
+	 */
+	emi_reg_sync_writel((value & 0xFFFFFFCF) | (ReadWriteType << 4), ADDR_EMI + EMI_BMEN);
+}
+
+
+int MET_BM_GetBusCycCount(void)
+{
+	return MET_BM_IsOverrun() ? BM_ERR_OVERRUN : emi_readl(IOMEM(ADDR_EMI + EMI_BCNT));	/*Bus cycle counter */
+}
+
+
+unsigned int MET_BM_GetTransAllCount(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI + EMI_TACT));
+}
+
+
+int MET_BM_GetTransCount(const unsigned int counter_num)
+{
+	unsigned int iCount;
+
+	switch (counter_num) {
+	case 1:
+		iCount = emi_readl(IOMEM(ADDR_EMI + EMI_TSCT));
+		break;
+
+	case 2:
+		iCount = emi_readl(IOMEM(ADDR_EMI + EMI_TSCT2));
+		break;
+
+	case 3:
+		iCount = emi_readl(IOMEM(ADDR_EMI + EMI_TSCT3));
+		break;
+
+	default:
+		return BM_ERR_WRONG_REQ;
+	}
+
+	return iCount;
+}
+
+
+int MET_BM_GetWordAllCount(void)
+{
+	return MET_BM_IsOverrun() ? BM_ERR_OVERRUN : emi_readl(IOMEM(ADDR_EMI + EMI_WACT));
+}
+
+
+int MET_BM_GetWordCount(const unsigned int counter_num)
+{
+	unsigned int iCount;
+
+	switch (counter_num) {
+	case 1:
+		iCount = emi_readl(IOMEM(ADDR_EMI + EMI_WSCT));
+		break;
+
+	case 2:
+		iCount = emi_readl(IOMEM(ADDR_EMI + EMI_WSCT2));
+		break;
+
+	case 3:
+		iCount = emi_readl(IOMEM(ADDR_EMI + EMI_WSCT3));
+		break;
+
+	case 4:
+		iCount = emi_readl(IOMEM(ADDR_EMI + EMI_WSCT4));
+		break;
+
+	default:
+		return BM_ERR_WRONG_REQ;
+	}
+
+	return iCount;
+}
+
+
+unsigned int MET_BM_GetBandwidthWordCount(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI + EMI_BACT));	/*Bandwidth counter for access */
+}
+
+
+unsigned int MET_BM_GetOverheadWordCount(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI + EMI_BSCT));	/*Overhead counter */
+}
+
+
+int MET_BM_GetTransTypeCount(const unsigned int counter_num)
+{
+	return (counter_num < 1 || counter_num > BM_COUNTER_MAX)
+	    ? BM_ERR_WRONG_REQ : emi_readl(IOMEM(ADDR_EMI + EMI_TTYPE1 + (counter_num - 1) * 8));
+}
+
+
+int MET_BM_GetMDCT(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI + EMI_MDCT));
+}
+
+
+int MET_BM_GetMDCT_2(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI + EMI_MDCT_2ND));
+}
+
+
+int MET_BM_SetMDCT_MDMCU(unsigned int mdmcu_rd_buf)
+{
+	unsigned int value_origin;
+
+	value_origin = emi_readl(IOMEM(ADDR_EMI + EMI_MDCT_2ND));
+	MET_TRACE("[MET_BM_SetMDCT_MDMCU] value_origin: %x\n", value_origin);
+
+	value_origin = value_origin & ~(0x7);
+	value_origin = value_origin | ((mdmcu_rd_buf) & 0x7);
+
+	emi_reg_sync_writel(value_origin, ADDR_EMI + EMI_MDCT_2ND);
+
+	return BM_REQ_OK;
+}
+
+
+int MET_BM_GetMonitorCounter(const unsigned int counter_num,
+			     unsigned int *master, unsigned int *trans_type)
+{
+	unsigned int value, addr;
+
+	if (counter_num < 1 || counter_num > BM_COUNTER_MAX)
+		return BM_ERR_WRONG_REQ;
+
+
+	if (counter_num == 1) {
+		addr = EMI_BMEN;
+		value = emi_readl(IOMEM(ADDR_EMI + addr));
+		*master = (value >> 16) & MASK_MASTER;
+		*trans_type = (value >> 24) & MASK_TRANS_TYPE;
+	} else {
+		addr = (counter_num <= 3) ? EMI_MSEL : (EMI_MSEL2 + (counter_num / 2 - 2) * 8);
+		value = emi_readl(IOMEM(ADDR_EMI + addr)) >> ((counter_num % 2) * 16);
+		*master = value & MASK_MASTER;
+		*trans_type = (value >> 8) & MASK_TRANS_TYPE;
+	}
+
+	return BM_REQ_OK;
+}
+
+
+int MET_BM_SetMonitorCounter(const unsigned int counter_num,
+			     const unsigned int master, const unsigned int trans_type)
+{
+	unsigned int value, addr;
+	const unsigned int iMask = (MASK_TRANS_TYPE << 8) | MASK_MASTER;
+
+	if (counter_num < 1 || counter_num > BM_COUNTER_MAX)
+		return BM_ERR_WRONG_REQ;
+
+
+	if (counter_num == 1) {
+		addr = EMI_BMEN;
+		value = (emi_readl(IOMEM(ADDR_EMI + addr)) & ~(iMask << 16)) |
+		    ((trans_type & MASK_TRANS_TYPE) << 24) | ((master & MASK_MASTER) << 16);
+	} else {
+		addr = (counter_num <= 3) ? EMI_MSEL : (EMI_MSEL2 + (counter_num / 2 - 2) * 8);
+
+		/* clear master and transaction type fields */
+		value = emi_readl(IOMEM(ADDR_EMI + addr)) & ~(iMask << ((counter_num % 2) * 16));
+
+		/* set master and transaction type fields */
+		value |= (((trans_type & MASK_TRANS_TYPE) << 8) |
+			  (master & MASK_MASTER)) << ((counter_num % 2) * 16);
+	}
+
+	emi_reg_sync_writel(value, ADDR_EMI + addr);
+
+	return BM_REQ_OK;
+}
+
+
+int MET_BM_SetTtypeCounterRW(unsigned int bmrw0_val, unsigned int bmrw1_val)
+{
+
+	unsigned int value_origin;
+
+	value_origin = emi_readl(IOMEM(ADDR_EMI + EMI_BMRW0));
+	MET_TRACE("[MET_EMI_settype1] value_origin: %x\n", value_origin);
+	if (value_origin != bmrw0_val) {
+		emi_reg_sync_writel(bmrw0_val, ADDR_EMI + EMI_BMRW0);
+		MET_TRACE("[MET_EMI_settype1] bmrw0_val: %x, value_origin: %x\n", bmrw0_val,
+			   value_origin);
+	}
+
+
+	value_origin = emi_readl(IOMEM(ADDR_EMI + EMI_BMRW1));
+	MET_TRACE("[MET_EMI_settype2] value_origin: %x\n", value_origin);
+	if (value_origin != bmrw1_val) {
+		emi_reg_sync_writel(bmrw1_val, ADDR_EMI + EMI_BMRW1);
+		MET_TRACE("[MET_EMI_settype2] bmrw0_val: %x, value_origin: %x\n", bmrw1_val,
+			   value_origin);
+
+	}
+	return BM_REQ_OK;
+}
+
+
+int MET_BM_Set_WsctTsct_id_sel(unsigned int counter_num, unsigned int enable)
+{
+	unsigned int value;
+
+	if (counter_num > 3)
+		return BM_ERR_WRONG_REQ;
+
+	value =
+	    ((emi_readl(IOMEM(ADDR_EMI + EMI_BMEN2)) & (~(1 << (28 + counter_num)))) |
+	     (enable << (28 + counter_num)));
+	emi_reg_sync_writel(value, ADDR_EMI + EMI_BMEN2);
+
+	return BM_REQ_OK;
+}
+
+
+int MET_BM_SetMaster(const unsigned int counter_num, const unsigned int master)
+{
+	unsigned int value, addr;
+	const unsigned int iMask = 0x7F;
+
+	if (counter_num < 1 || counter_num > BM_COUNTER_MAX)
+		return BM_ERR_WRONG_REQ;
+
+
+	if (counter_num == 1) {
+		addr = EMI_BMEN;
+		value =
+		    (emi_readl(IOMEM(ADDR_EMI + addr)) & ~(iMask << 16)) | ((master & iMask) << 16);
+	} else {
+		addr = (counter_num <= 3) ? EMI_MSEL : (EMI_MSEL2 + (counter_num / 2 - 2) * 8);
+
+		/* clear master and transaction type fields */
+		value = emi_readl(IOMEM(ADDR_EMI + addr)) & ~(iMask << ((counter_num % 2) * 16));
+
+		/* set master and transaction type fields */
+		value |= ((master & iMask) << ((counter_num % 2) * 16));
+	}
+
+	emi_reg_sync_writel(value, ADDR_EMI + addr);
+
+	return BM_REQ_OK;
+}
+
+
+int MET_BM_SetbusID_En(const unsigned int counter_num,
+		       const unsigned int enable)
+{
+	unsigned int value;
+
+	if ((counter_num < 1 || counter_num > BM_COUNTER_MAX) || (enable > 1))
+		return BM_ERR_WRONG_REQ;
+
+	if (enable == 0) {
+		/* clear  EMI ID selection Enabling   SEL_ID_EN */
+		value = (emi_readl(IOMEM(ADDR_EMI + EMI_BMEN2))
+			 & ~(1 << (counter_num - 1)));
+	} else {
+		/* enable  EMI ID selection Enabling   SEL_ID_EN */
+		value = (emi_readl(IOMEM(ADDR_EMI + EMI_BMEN2))
+			 | (1 << (counter_num - 1)));
+	}
+	emi_reg_sync_writel(value, ADDR_EMI + EMI_BMEN2);
+
+	return BM_REQ_OK;
+}
+
+
+int MET_BM_SetbusID(const unsigned int counter_num,
+		    const unsigned int id)
+{
+	unsigned int value, addr, shift_num;
+
+	if ((counter_num < 1 || counter_num > BM_COUNTER_MAX))
+		return BM_ERR_WRONG_REQ;
+
+	/* offset of EMI_BMIDx register */
+	addr = EMI_BMID0 + (counter_num - 1) / 2 * 4;
+	shift_num = ((counter_num - 1) % 2) * 16;
+	/* clear SELx_ID field */
+	value = emi_readl(IOMEM(ADDR_EMI + addr)) & ~(EMI_BMID_MASK << shift_num);
+
+	/* set SELx_ID field */
+	if (id <= 0xffff)       /*bigger then 0xff_ff : no select busid in master, reset busid as 0*/
+		value |= id << shift_num;
+
+	emi_reg_sync_writel(value, ADDR_EMI + addr);
+
+	return BM_REQ_OK;
+}
+
+
+int MET_BM_SetUltraHighFilter(const unsigned int counter_num, const unsigned int enable)
+{
+	unsigned int value;
+
+	if ((counter_num < 1 || counter_num > BM_COUNTER_MAX) || (enable > 1))
+		return BM_ERR_WRONG_REQ;
+
+
+	value = (emi_readl(IOMEM(ADDR_EMI + EMI_BMEN1))
+		 & ~(1 << (counter_num - 1)))
+		| (enable << (counter_num - 1));
+
+	emi_reg_sync_writel(value, ADDR_EMI + EMI_BMEN1);
+
+	return BM_REQ_OK;
+}
+
+
+int MET_BM_SetLatencyCounter(unsigned int enable)
+{
+	unsigned int value;
+
+	value = emi_readl(IOMEM(ADDR_EMI + EMI_BMEN2)) & ~(0x3 << 24);
+	/*
+	 * emi_ttype1 -- emi_ttype8 change as total latencies
+	 * for m0 -- m7,
+	 * and emi_ttype9 -- emi_ttype16 change as total transaction counts
+	 * for m0 -- m7
+	 */
+	if (enable == 1)
+		value |= (0x2 << 24);
+
+	emi_reg_sync_writel(value, ADDR_EMI + EMI_BMEN2);
+
+	return BM_REQ_OK;
+}
+
+
+int MET_BM_GetLatencyCycle(unsigned int *__restrict__ emi_value,
+				const unsigned int begin, const unsigned int end)
+{
+	int i, j = 0;
+
+	for (i = begin - 1; i < end; i++)
+		emi_value[j++] = emi_readl(IOMEM(ADDR_EMI + (EMI_TTYPE1 + i * 8)));
+
+	return j;
+}
+
+
+unsigned int MET_BM_GetEmiDcm(void)
+{
+	return (emi_readl(IOMEM(ADDR_EMI + EMI_CONM)) >> 24);
+}
+
+
+int MET_BM_SetEmiDcm(const unsigned int setting)
+{
+	unsigned int value;
+
+	value = emi_readl(IOMEM(ADDR_EMI + EMI_CONM));
+	emi_reg_sync_writel((value & 0x00FFFFFF) | (setting << 24), ADDR_EMI + EMI_CONM);
+
+	return BM_REQ_OK;
+}
+
+
+unsigned int MET_EMI_GetMDCT(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI + EMI_MDCT));
+}
+
+
+unsigned int MET_EMI_GetMDCT_2ND(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI + EMI_MDCT_2ND));
+}
+
+
+unsigned int MET_EMI_GetARBA(void)
+{
+	/* EMI_ARBA EMI Bandwidth Filter Control M0/1 */
+	return emi_readl(IOMEM(ADDR_EMI + EMI_ARBA));
+}
+
+
+unsigned int MET_EMI_GetARBB(void)
+{
+	/* need the ATBB for mt6739 */
+	return emi_readl(IOMEM(ADDR_EMI+EMI_ARBB));
+}
+
+
+unsigned int MET_EMI_GetARBC(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI + EMI_ARBC));
+}
+
+
+unsigned int MET_EMI_GetARBD(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI + EMI_ARBD));
+}
+
+
+unsigned int MET_EMI_GetARBE(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI + EMI_ARBE));
+}
+
+
+unsigned int MET_EMI_GetARBF(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI + EMI_ARBF));
+}
+
+
+unsigned int MET_EMI_GetARBG(void)
+{
+	return 0;
+}
+
+
+unsigned int MET_EMI_GetARBH(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI + EMI_ARBH));
+}
+
+
+/* Total BW status*/
+unsigned int MET_EMI_GetBWCT0(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI + EMI_BWCT0));
+}
+
+
+/* Total BW status*/
+unsigned int MET_EMI_GetBWCT1(void)
+{
+	return 0;
+}
+
+
+/* Total BW status*/
+unsigned int MET_EMI_GetBWCT2(void)
+{
+	return 0;
+}
+
+
+/* Total BW status*/
+unsigned int MET_EMI_GetBWCT3(void)
+{
+	return 0;
+}
+
+
+/* Total BW status*/
+unsigned int MET_EMI_GetBWCT4(void)
+{
+	return 0;
+}
+
+
+/* Total BW status*/
+unsigned int MET_EMI_GetBWST0(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI + EMI_BWST0));
+}
+
+
+/* Total BW status*/
+unsigned int MET_EMI_GetBWST1(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI + EMI_BWST1));
+}
+
+
+/* C+G BW status*/
+unsigned int MET_EMI_GetBWCT0_2ND(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI + EMI_BWCT0_2ND));
+}
+
+
+/* C+G BW status*/
+unsigned int MET_EMI_GetBWCT1_2ND(void)
+{
+	return 0;
+}
+
+
+/* C+G BW status*/
+unsigned int MET_EMI_GetBWST_2ND(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI + EMI_BWST_2ND));
+}
+
+
+unsigned int MET_EMI_GetBMRW0(void)
+{
+	return readl(IOMEM(ADDR_EMI + EMI_BMRW0));
+}
+
+
+void emi_dump_reg(void)
+{
+	int i;
+
+	MET_TRACE("[emi_regdump]\n");
+	for (i = 0x400; i < 0x500; i = i + 16)
+		MET_TRACE("%4x__ %8x %8x %8x %8x\n", i, readl(IOMEM(ADDR_EMI + i)),
+			   readl(IOMEM(ADDR_EMI + i + 4)), readl(IOMEM(ADDR_EMI + i + 8)),
+			   readl(IOMEM(ADDR_EMI + i + 12)));
+}
+
+
+unsigned int MET_EMI_GetDramChannNum(void)
+{
+	int num = -1;
+
+	if (BaseAddrEMI) {
+		num = emi_readl(IOMEM(ADDR_EMI + EMI_CONA));
+		num = ((num >> 8) & 0x0000003);
+	} else {
+		return 1;
+	}
+
+	if (num == M0_DOUBLE_HALF_BW_1CH)
+		return 1;
+	else if (num == M0_DOUBLE_HALF_BW_2CH)
+		return 2;
+	else if (num == M0_DOUBLE_HALF_BW_4CH)
+		return 4;
+	else                    /* default return single channel */
+		return 1;
+}
+
+
+unsigned int MET_EMI_GetDramRankNum(void)
+{
+	int dual_rank = 0;
+
+	if (BaseAddrEMI) {
+		dual_rank = emi_readl(IOMEM(ADDR_EMI + EMI_CONA));
+		dual_rank = ((dual_rank >> 17) & RANK_MASK);
+	} else {
+		return DUAL_RANK;
+	}
+
+	if (dual_rank == DISABLE_DUAL_RANK_MODE)
+		return ONE_RANK;
+	else			/* default return dual rank */
+		return DUAL_RANK;
+}
+
+
+/*
+ * Dual Channel:
+ * Enables two rank for CHB
+ * Quad Channel:
+ * Enables two rank  for CHC and CHD
+ * 0: Disable dual rank mode
+ * 1: Enable dual rank mode
+ */
+unsigned int MET_EMI_GetDramRankNum_CHN1(void)
+{
+	int dual_rank = 0;
+
+	if (BaseAddrEMI) {
+		dual_rank = emi_readl(IOMEM(ADDR_EMI + EMI_CONA));
+		dual_rank = ((dual_rank >> 16) & RANK_MASK);
+	} else {
+		return DUAL_RANK;
+	}
+
+	if (dual_rank == DISABLE_DUAL_RANK_MODE)
+		return ONE_RANK;
+	else			/* default return dual rank */
+		return DUAL_RANK;
+}
+
+
+void met_record_dramc_dcm_enable_flag(void)
+{
+	int reg_val;
+
+	switch (dram_chann_num) {
+	case 1:
+		reg_val = emi_readl(IOMEM(CH0_MISC_CG_CTRL0));
+		dramc0_dcm_enable = !((reg_val >> DRAMC_CG_SHIFT) & 0x1);
+		break;
+	case 2:
+		reg_val = emi_readl(IOMEM(CH0_MISC_CG_CTRL0));
+		dramc0_dcm_enable = !((reg_val >> DRAMC_CG_SHIFT) & 0x1);
+
+		reg_val = emi_readl(IOMEM(CH1_MISC_CG_CTRL0));
+		dramc1_dcm_enable = !((reg_val >> DRAMC_CG_SHIFT) & 0x1);
+		break;
+	default:
+		METERROR("Error: dram_chann_num = %d\n", dram_chann_num);
+	}
+}
diff --git a/src/devtools/met-driver/4.14/mt2731/emi/SEDA2/mtk_emi_bm.h b/src/devtools/met-driver/4.14/mt2731/emi/SEDA2/mtk_emi_bm.h
new file mode 100644
index 0000000..514b396
--- /dev/null
+++ b/src/devtools/met-driver/4.14/mt2731/emi/SEDA2/mtk_emi_bm.h
@@ -0,0 +1,336 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#ifndef __MT_MET_EMI_BM_H__
+#define __MT_MET_EMI_BM_H__
+
+#define EMI_VER_MAJOR  2
+#define EMI_VER_MINOR  0
+
+#define DEF_BM_RW_TYPE          (BM_BOTH_READ_WRITE)
+#define NTS                     2
+#define NWSCT                   4
+#define NLATENCY                8
+#define NTRANS                  8
+#define NALL                    (3 + 2 + 1)
+#define NTTYPE                  5
+#define NIDX_EMI                (NTS + NWSCT + NLATENCY + NTRANS + NALL + NTTYPE)
+
+#define NCNT                    9
+#define NCH                     2
+#define NIDX_DRAMC              (NCNT * NCH)
+#define NIDX                    (NIDX_EMI + NIDX_DRAMC)
+
+#define NCLK                    1
+#define NARB                    8
+#define NBW                     10
+#define NIDX_BL                 (NCLK + NARB + NBW)
+
+/* 1000 To Khz and 4x freq & 2x data rate for LPDDR4 */
+/* 1000 To Khz and 2x freq & 2x data rate for LPDDR3*/
+/* TBD: calculate emi clock rate from DRAM DATA RATE */
+
+/*dram baseclock/EMI clock  :	LP4=4	LP3=2	*/
+#define DRAM_EMI_BASECLOCK_RATE_LP4     4
+#define DRAM_EMI_BASECLOCK_RATE_LP3     2
+/*dram io width  :	LP4=x16		LP3=x32 */
+#define DRAM_IO_BUS_WIDTH_LP4           16
+#define DRAM_IO_BUS_WIDTH_LP3           32
+/*dram datarate  :	DDR=double */
+#define DRAM_DATARATE   2
+
+#define ADDR_EMI        ((unsigned long)BaseAddrEMI)
+
+static const char of_emi_desc[] = "mediatek,emi";
+static const char of_chn_emi_desc[] = "mediatek,chn_emi";
+static const char of_dramc_desc[] = "mediatek,dramc";
+
+#define BM_MASTER_M0            (0x01)
+#define BM_MASTER_M1            (0x02)
+#define BM_MASTER_M2            (0x04)
+#define BM_MASTER_M3            (0x08)
+#define BM_MASTER_M4            (0x10)
+#define BM_MASTER_M5            (0x20)
+#define BM_MASTER_M6            (0x40)
+#define BM_MASTER_M7            (0x80)
+#define BM_MASTER_ALL           (0xFF)
+
+
+enum BM_RW_Type {
+	BM_BOTH_READ_WRITE,
+	BM_READ_ONLY,
+	BM_WRITE_ONLY
+};
+
+enum {
+	BM_TRANS_TYPE_1BEAT = 0x0,
+	BM_TRANS_TYPE_2BEAT,
+	BM_TRANS_TYPE_3BEAT,
+	BM_TRANS_TYPE_4BEAT,
+	BM_TRANS_TYPE_5BEAT,
+	BM_TRANS_TYPE_6BEAT,
+	BM_TRANS_TYPE_7BEAT,
+	BM_TRANS_TYPE_8BEAT,
+	BM_TRANS_TYPE_9BEAT,
+	BM_TRANS_TYPE_10BEAT,
+	BM_TRANS_TYPE_11BEAT,
+	BM_TRANS_TYPE_12BEAT,
+	BM_TRANS_TYPE_13BEAT,
+	BM_TRANS_TYPE_14BEAT,
+	BM_TRANS_TYPE_15BEAT,
+	BM_TRANS_TYPE_16BEAT,
+	BM_TRANS_TYPE_1Byte = 0 << 4,
+	BM_TRANS_TYPE_2Byte = 1 << 4,
+	BM_TRANS_TYPE_4Byte = 2 << 4,
+	BM_TRANS_TYPE_8Byte = 3 << 4,
+	BM_TRANS_TYPE_16Byte = 4 << 4,
+	BM_TRANS_TYPE_32Byte = 5 << 4,
+	BM_TRANS_TYPE_BURST_WRAP = 0 << 7,
+	BM_TRANS_TYPE_BURST_INCR = 1 << 7
+};
+
+enum {
+	BM_TRANS_RW_DEFAULT = 0x0,
+	BM_TRANS_RW_READONLY,
+	BM_TRANS_RW_WRITEONLY,
+	BM_TRANS_RW_RWBOTH
+};
+
+
+/*coda busid 12bit, but HW support 16 bit*/
+#define EMI_BMID_MASK				(0xFFFF)
+#define BM_COUNTER_MAX				(21)
+
+/*
+*#define BUS_MON_EN		(0x00000001)
+*#define BUS_MON_PAUSE		(0x00000002)
+*#define BUS_MON_IDLE		(0x00000008)
+*#define BC_OVERRUN		(0x00000100)
+*/
+enum {
+	BUS_MON_EN_SHIFT = 0,
+	BUS_MON_PAUSE_SHIFT = 1,
+	BUS_MON_IDLE_SHIFT = 3,
+	BC_OVERRUN_SHIFT = 8,
+	DRAMC_CG_SHIFT = 9,
+};
+
+#define BM_REQ_OK				(0)
+#define BM_ERR_WRONG_REQ			(-1)
+#define BM_ERR_OVERRUN				(-2)
+
+#define BM_WSCT_TSCT_IDSEL_ENABLE		(0)
+#define BM_WSCT_TSCT_IDSEL_DISABLE		(-1)
+#define BM_TTYPE1_16_ENABLE			(0)
+#define BM_TTYPE1_16_DISABLE			(-1)
+#define BM_TTYPE17_21_ENABLE			(0)
+#define BM_TTYPE17_21_DISABLE			(-1)
+#define BM_BW_LIMITER_ENABLE			(0)
+#define BM_BW_LIMITER_DISABLE			(-1)
+
+#define M0_DOUBLE_HALF_BW_1CH	(0x0)
+#define M0_DOUBLE_HALF_BW_2CH	(0x1)
+#define M0_DOUBLE_HALF_BW_4CH	(0x2)
+
+/* EMI Rank configuration */
+enum {
+	DISABLE_DUAL_RANK_MODE = 0,
+	ENABLE_DUAL_RANK_MODE,
+};
+
+#define RANK_MASK 0x1
+#define ONE_RANK 1
+#define DUAL_RANK 2
+
+
+#if defined(CONFIG_MTK_TINYSYS_SSPM_SUPPORT) && defined(ONDIEMET_SUPPORT)
+/*ondiemet emi ipi command*/
+enum BM_EMI_IPI_Type {
+	SET_BASE_EMI = 0x0,
+	SET_BASE_DRAMC0,
+	SET_BASE_DRAMC1,
+	SET_BASE_DRAMC2,
+	SET_BASE_DRAMC3,
+	SET_BASE_DDRPHY0AO,
+	SET_BASE_DRAMC0_AO,
+	SET_EBM_CONFIGS1,
+	SET_EBM_CONFIGS2,
+	SET_REGISTER_CB,
+};
+#endif
+
+#define	EMI_OFF			0x0000
+#define EMI_CONA		(0x000-EMI_OFF)
+#define EMI_CONH		(0x038-EMI_OFF)
+#define EMI_CONM		(0x060-EMI_OFF)
+#define EMI_CONO		(0x070-EMI_OFF)
+
+#define EMI_MDCT		(0x078 - EMI_OFF)
+#define EMI_MDCT_2ND		(0x07C - EMI_OFF)
+
+#define EMI_ARBA                (0x100 - EMI_OFF)
+#define EMI_ARBB                (0x108 - EMI_OFF)
+#define EMI_ARBC                (0x110 - EMI_OFF)
+#define EMI_ARBD                (0x118 - EMI_OFF)
+#define EMI_ARBE                (0x120 - EMI_OFF)
+#define EMI_ARBF                (0x128 - EMI_OFF)
+#define EMI_ARBH                (0x138 - EMI_OFF)
+
+#define EMI_BMEN                (0x400 - EMI_OFF)
+#define EMI_BCNT                (0x408 - EMI_OFF)
+#define EMI_TACT                (0x410 - EMI_OFF)
+#define EMI_TSCT                (0x418 - EMI_OFF)
+#define EMI_WACT                (0x420 - EMI_OFF)
+#define EMI_WSCT                (0x428 - EMI_OFF)
+#define EMI_BACT                (0x430 - EMI_OFF)
+#define EMI_BSCT                (0x438 - EMI_OFF)
+
+#define EMI_MSEL                (0x440 - EMI_OFF)
+#define EMI_TSCT2               (0x448 - EMI_OFF)
+#define EMI_TSCT3               (0x450 - EMI_OFF)
+#define EMI_WSCT2               (0x458 - EMI_OFF)
+#define EMI_WSCT3               (0x460 - EMI_OFF)
+#define EMI_WSCT4               (0x464 - EMI_OFF)
+#define EMI_MSEL2               (0x468 - EMI_OFF)
+#define EMI_MSEL3               (0x470 - EMI_OFF)
+#define EMI_MSEL4               (0x478 - EMI_OFF)
+#define EMI_MSEL5               (0x480 - EMI_OFF)
+#define EMI_MSEL6               (0x488 - EMI_OFF)
+#define EMI_MSEL7               (0x490 - EMI_OFF)
+#define EMI_MSEL8               (0x498 - EMI_OFF)
+#define EMI_MSEL9               (0x4A0 - EMI_OFF)
+#define EMI_MSEL10              (0x4A8 - EMI_OFF)
+
+#define EMI_BMID0               (0x4B0 - EMI_OFF)
+#define EMI_BMID1               (0x4B4 - EMI_OFF)
+#define EMI_BMID2               (0x4B8 - EMI_OFF)
+#define EMI_BMID3               (0x4BC - EMI_OFF)
+#define EMI_BMID4               (0x4C0 - EMI_OFF)
+#define EMI_BMID5               (0x4C4 - EMI_OFF)
+#define EMI_BMID6               (0x4C8 - EMI_OFF)
+#define EMI_BMID7               (0x4CC - EMI_OFF)
+#define EMI_BMID8               (0x4D0 - EMI_OFF)
+#define EMI_BMID9               (0x4D4 - EMI_OFF)
+#define EMI_BMID10              (0x4D8 - EMI_OFF)
+
+#define EMI_BMEN1               (0x4E0 - EMI_OFF)
+#define EMI_BMEN2               (0x4E8 - EMI_OFF)
+#define EMI_BMRW0               (0x4F8 - EMI_OFF)
+#define EMI_BMRW1               (0x4FC - EMI_OFF)
+#define EMI_TTYPE1              (0x500 - EMI_OFF)
+#define EMI_TTYPE2              (0x508 - EMI_OFF)
+#define EMI_TTYPE3              (0x510 - EMI_OFF)
+#define EMI_TTYPE4              (0x518 - EMI_OFF)
+#define EMI_TTYPE5              (0x520 - EMI_OFF)
+#define EMI_TTYPE6              (0x528 - EMI_OFF)
+#define EMI_TTYPE7              (0x530 - EMI_OFF)
+#define EMI_TTYPE8              (0x538 - EMI_OFF)
+#define EMI_TTYPE9              (0x540 - EMI_OFF)
+#define EMI_TTYPE10             (0x548 - EMI_OFF)
+#define EMI_TTYPE11             (0x550 - EMI_OFF)
+#define EMI_TTYPE12             (0x558 - EMI_OFF)
+#define EMI_TTYPE13             (0x560 - EMI_OFF)
+#define EMI_TTYPE14             (0x568 - EMI_OFF)
+#define EMI_TTYPE15             (0x570 - EMI_OFF)
+#define EMI_TTYPE16             (0x578 - EMI_OFF)
+#define EMI_TTYPE17             (0x580 - EMI_OFF)
+#define EMI_TTYPE18             (0x588 - EMI_OFF)
+#define EMI_TTYPE19             (0x590 - EMI_OFF)
+#define EMI_TTYPE20             (0x598 - EMI_OFF)
+#define EMI_TTYPE21             (0x5A0 - EMI_OFF)
+
+#define EMI_BWCT0               (0x5B0 - EMI_OFF)
+#define EMI_BWST0               (0x5C4 - EMI_OFF)
+#define EMI_BWST1               (0x5C8 - EMI_OFF)
+
+#define EMI_BWCT0_2ND   (0x6A0 - EMI_OFF)
+#define EMI_BWST_2ND    (0x6A8 - EMI_OFF)
+
+
+extern void emi_dump_reg(void);
+extern int MET_BM_Init(void);
+extern void MET_BM_DeInit(void);
+extern void MET_BM_SaveCfg(void);
+extern void MET_BM_RestoreCfg(void);
+extern void MET_BM_Enable(const unsigned int enable);
+extern void MET_BM_Pause(void);
+extern void MET_BM_Continue(void);
+extern unsigned int MET_BM_IsOverrun(void);
+extern unsigned int MET_BM_GetReadWriteType(void);
+extern void MET_BM_SetReadWriteType(const unsigned int ReadWriteType);
+extern int MET_BM_GetBusCycCount(void);
+extern unsigned int MET_BM_GetTransAllCount(void);
+extern int MET_BM_GetTransCount(const unsigned int counter_num);
+extern int MET_BM_GetWordAllCount(void);
+extern int MET_BM_GetWordCount(const unsigned int counter_num);
+extern unsigned int MET_BM_GetBandwidthWordCount(void);
+extern unsigned int MET_BM_GetOverheadWordCount(void);
+extern int MET_BM_GetTransTypeCount(const unsigned int counter_num);
+extern int MET_BM_GetMDCT(void);
+extern int MET_BM_GetMDCT_2(void);
+extern int MET_BM_GetMonitorCounter(const unsigned int counter_num,
+				    unsigned int *master, unsigned int *trans_type);
+extern int MET_BM_SetMDCT_MDMCU(unsigned int mdmcu_rd_buf);
+extern int MET_BM_SetMonitorCounter(const unsigned int counter_num,
+				    const unsigned int master, const unsigned int trans_type);
+extern int MET_BM_SetTtypeCounterRW(unsigned int bmrw0_val, unsigned int bmrw1_val);
+extern int MET_BM_Set_WsctTsct_id_sel(unsigned int counter_num, unsigned int enable);
+extern int MET_BM_SetMaster(const unsigned int counter_num, const unsigned int master);
+extern int MET_BM_SetbusID_En(const unsigned int counter_num,
+			      const unsigned int enable);
+extern int MET_BM_SetbusID(const unsigned int counter_num,
+			   const unsigned int id);
+extern int MET_BM_SetUltraHighFilter(const unsigned int counter_num, const unsigned int enable);
+extern int MET_BM_SetLatencyCounter(unsigned int enable);
+int MET_BM_GetLatencyCycle(unsigned int *__restrict__ emi_value,
+				const unsigned int begin, const unsigned int end);
+extern unsigned int MET_BM_GetEmiDcm(void);
+extern int MET_BM_SetEmiDcm(const unsigned int setting);
+extern void MET_BM_Clear_Start(void);
+extern unsigned int MET_EMI_GetDramRankNum(void);
+extern unsigned int MET_EMI_GetDramRankNum_CHN1(void);
+
+
+/* Config */
+unsigned int MET_EMI_GetARBA(void);
+unsigned int MET_EMI_GetARBB(void);
+unsigned int MET_EMI_GetARBC(void);
+unsigned int MET_EMI_GetARBD(void);
+unsigned int MET_EMI_GetARBE(void);
+unsigned int MET_EMI_GetARBF(void);
+unsigned int MET_EMI_GetARBG(void);
+unsigned int MET_EMI_GetARBH(void);
+
+/* Total BW status */
+extern unsigned int MET_EMI_GetBWCT0(void);
+extern unsigned int MET_EMI_GetBWCT1(void);
+extern unsigned int MET_EMI_GetBWCT2(void);
+extern unsigned int MET_EMI_GetBWCT3(void);
+extern unsigned int MET_EMI_GetBWCT4(void);
+extern unsigned int MET_EMI_GetBWST0(void);
+extern unsigned int MET_EMI_GetBWST1(void);
+/* C+G BW */
+extern unsigned int MET_EMI_GetBWCT0_2ND(void);
+extern unsigned int MET_EMI_GetBWCT1_2ND(void);
+extern unsigned int MET_EMI_GetBWST_2ND(void);
+
+unsigned int MET_EMI_GetBMRW0(void);
+unsigned int MET_EMI_GetDramChannNum(void);
+
+/* ondiemet*/
+void MET_BM_IPI_baseaddr(void);
+void met_emi_phyaddr_debug(void);
+
+
+extern void met_record_dramc_dcm_enable_flag(void);
+
+#endif                          /* !__MT_MET_EMI_BM_H__ */
diff --git a/src/devtools/met-driver/4.14/mt2731/emi/SEDA3/met_emi.c b/src/devtools/met-driver/4.14/mt2731/emi/SEDA3/met_emi.c
new file mode 100644
index 0000000..a529ae5
--- /dev/null
+++ b/src/devtools/met-driver/4.14/mt2731/emi/SEDA3/met_emi.c
@@ -0,0 +1,1570 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/device.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/string.h>
+
+#ifdef CONFIG_ARM
+//#include <asm/dma-mapping.h> /* arm_coherent_dma_ops */
+#endif /* CONFIG_ARM */
+
+#include <linux/dma-mapping.h>
+
+#define MET_USER_EVENT_SUPPORT
+#include "met_drv.h"
+#include "trace.h"
+
+#include "mtk_typedefs.h"
+#include "plf_init.h"
+#include "plf_trace.h"
+#include "mtk_emi_bm.h"
+#include "mtk_dramc_reg.h"
+#include "interface.h"
+
+#if defined(CONFIG_MTK_TINYSYS_SSPM_SUPPORT) && defined(ONDIEMET_SUPPORT)
+#include "sspm/ondiemet_sspm.h"
+#endif
+
+
+/*======================================================================*/
+/*	Global variable definitions					*/
+/*======================================================================*/
+/*ondiemet emi sampling interval in us */
+int emi_tsct_enable;
+int emi_mdct_enable;
+int emi_TP_busfiltr_enable;
+
+int emi_use_ondiemet;
+int metemi_func_opt;
+
+int met_emi_regdump;
+/* Dynamic MonitorCounter selection !!!EXPERIMENT!!! */
+static int msel_enable;
+static unsigned int msel_group1 = BM_MASTER_ALL;
+static unsigned int msel_group2 = BM_MASTER_ALL;
+static unsigned int msel_group3 = BM_MASTER_ALL;
+
+/* CVS Added changeable buffer for testing */
+static int mdmcu_sel_enable;
+static unsigned int rd_mdmcu_rsv_num = 0x5;
+
+/* Global variables */
+static struct kobject *kobj_emi;
+static int rwtype = BM_BOTH_READ_WRITE;
+
+/* BW Limiter */
+/*#define CNT_COUNTDOWN	(1000-1)*/		/* 1000 * 1ms = 1sec */
+#define CNT_COUNTDOWN   (0)                     /* 1ms */
+static int countdown;
+static int bw_limiter_enable = BM_BW_LIMITER_ENABLE;
+
+/* TTYPE counter */
+static int ttype1_16_en = BM_TTYPE1_16_DISABLE;
+static int ttype17_21_en = BM_TTYPE17_21_DISABLE;
+
+static int dramc_pdir_enable;
+static int dram_chann_num = 1;
+
+enum SSPM_Mode {
+	CUSTOMER_MODE = 0x0,
+	UNDEFINE_MODE = 0x1,
+	INTERNAL_MODE = 0X2780
+};
+
+
+/*======================================================================*/
+/*	EMI Test Operations						*/
+/*======================================================================*/
+static int times;
+
+static ssize_t test_apmcu_store(struct kobject *kobj,
+				struct kobj_attribute *attr,
+				const char *buf,
+				size_t n)
+{
+	int i;
+	unsigned int    *src_addr_v = NULL;
+	dma_addr_t src_addr_p;
+#ifdef CONFIG_ARM
+	struct dma_map_ops *ops = (struct dma_map_ops *)symbol_get(arm_dma_ops);
+#endif /* CONFIG_ARM */
+
+	if ((n == 0) || (buf == NULL))
+		return -EINVAL;
+	if (kstrtoint(buf, 10, &times) != 0)
+		return -EINVAL;
+	if (times < 0)
+		return -EINVAL;
+
+	if (times > 5000)       /* Less than 20MB */
+		return -EINVAL;
+
+#ifdef CONFIG_ARM
+	if (ops && ops->alloc) {
+		(met_device.this_device)->coherent_dma_mask = DMA_BIT_MASK(32);
+		src_addr_v = ops->alloc(met_device.this_device,
+						PAGE_SIZE,
+						&src_addr_p,
+						GFP_KERNEL,
+						0);
+	}
+#endif /* CONFIG_ARM */
+ 
+#ifdef CONFIG_ARM64
+	/* dma_alloc */
+ 	src_addr_v = dma_alloc_coherent(met_device.this_device,
+ 					PAGE_SIZE,
+ 					&src_addr_p,
+ 					GFP_KERNEL);
+#endif /* CONFIG_ARM64 */
+
+	if (src_addr_v == NULL) {
+#ifdef CONFIG_MET_MODULE
+		met_tag_oneshot_real(0, "test_apmcu dma alloc fail", PAGE_SIZE);
+#else
+		met_tag_oneshot(0, "test_apmcu dma alloc fail", PAGE_SIZE);
+#endif
+		return -ENOMEM;
+	}
+	/* testing */
+	preempt_disable();
+#ifdef CONFIG_MET_MODULE
+	met_tag_start_real(0, "TEST_EMI_APMCU");
+#else
+	met_tag_start(0, "TEST_EMI_APMCU");
+#endif
+	for (i = 0; i < times; i++) {
+		memset(src_addr_v, 2 * i, PAGE_SIZE);
+#ifdef CONFIG_MET_MODULE
+		met_tag_oneshot_real(0, "TEST_EMI_APMCU", PAGE_SIZE);
+#else
+		met_tag_oneshot(0, "TEST_EMI_APMCU", PAGE_SIZE);
+#endif
+	}
+#ifdef CONFIG_MET_MODULE
+	met_tag_end_real(0, "TEST_EMI_APMCU");
+#else
+	met_tag_end(0, "TEST_EMI_APMCU");
+#endif
+	/* the following function has no defined if MET is built as module */
+	/* preempt_enable_no_resched(); */
+	/* use this one to replace it: see met_drv.h */
+	my_preempt_enable();
+
+#ifdef CONFIG_ARM
+	/* dma_free */
+	if (ops && ops->free) {
+		ops->free(met_device.this_device,
+				  PAGE_SIZE,
+				  src_addr_v,
+				  src_addr_p,
+			0);
+	}
+#endif /* CONFIG_ARM */
+
+#ifdef CONFIG_ARM64
+	/* dma_free */
+ 	if (src_addr_v != NULL)
+ 		dma_free_coherent(met_device.this_device,
+ 				  PAGE_SIZE,
+ 				  src_addr_v,
+				  src_addr_p);
+#endif /* CONFIG_ARM64 */
+
+	return n;
+}
+
+/*======================================================================*/
+/*	KOBJ Declarations						*/
+/*======================================================================*/
+DECLARE_KOBJ_ATTR_INT(emi_tsct_enable, emi_tsct_enable);
+DECLARE_KOBJ_ATTR_INT(emi_mdct_enable, emi_mdct_enable);
+DECLARE_KOBJ_ATTR_INT(emi_TP_busfiltr_enable, emi_TP_busfiltr_enable);
+DECLARE_KOBJ_ATTR_INT(metemi_func_opt, metemi_func_opt);
+DECLARE_KOBJ_ATTR_INT(emi_regdump, met_emi_regdump);
+DECLARE_KOBJ_ATTR_INT(msel_enable, msel_enable);
+DECLARE_KOBJ_ATTR_HEX_CHECK(msel_group1, msel_group1, msel_group1 > 0 && msel_group1 <= BM_MASTER_ALL);
+DECLARE_KOBJ_ATTR_HEX_CHECK(msel_group2, msel_group2, msel_group2 > 0 && msel_group2 <= BM_MASTER_ALL);
+DECLARE_KOBJ_ATTR_HEX_CHECK(msel_group3, msel_group3, msel_group3 > 0 && msel_group3 <= BM_MASTER_ALL);
+DECLARE_KOBJ_ATTR_INT(mdmcu_sel_enable, mdmcu_sel_enable);
+DECLARE_KOBJ_ATTR_INT(rd_mdmcu_rsv_num, rd_mdmcu_rsv_num);
+
+
+/* KOBJ: rwtype */
+DECLARE_KOBJ_ATTR_INT_CHECK(rwtype, rwtype, rwtype >= 0 && rwtype <= BM_WRITE_ONLY);
+
+static unsigned int get_emi_clock_rate(unsigned int dram_data_rate_MHz)
+{
+	/*
+	 *	the ddr type define :
+	 *	enum DDRTYPE {
+	 *	TYPE_LPDDR3 = 1,
+	 *	TYPE_LPDDR4,
+	 *	TYPE_LPDDR4X,
+	 *	TYPE_LPDDR2
+	 *	};
+	 */
+
+	unsigned int DRAM_TYPE;
+
+	if (get_ddr_type_symbol) {
+		DRAM_TYPE = get_ddr_type_symbol();
+
+		if ((DRAM_TYPE == 2) || (DRAM_TYPE == 3))
+			return dram_data_rate_MHz / DRAM_EMI_BASECLOCK_RATE_LP4 / DRAM_DATARATE;
+		else
+			return dram_data_rate_MHz / DRAM_EMI_BASECLOCK_RATE_LP3 / DRAM_DATARATE;
+	} else {
+		METERROR("[%s][%d]get_ddr_type_symbol = NULL , use the TYPE_LPDDR3 setting\n", __func__, __LINE__);
+		return dram_data_rate_MHz / DRAM_EMI_BASECLOCK_RATE_LP3 / DRAM_DATARATE;
+	}
+}
+
+/* KOBJ: emi_clock_rate */
+static ssize_t emi_clock_rate_show(struct kobject *kobj,
+				   struct kobj_attribute *attr,
+				   char *buf)
+{
+	unsigned int dram_data_rate_MHz;
+
+	if (get_dram_data_rate_symbol) {
+		dram_data_rate_MHz = get_dram_data_rate_symbol();
+	} else {
+		METERROR("get_dram_data_rate_symbol = NULL\n");
+		dram_data_rate_MHz = 0;
+	}
+
+	return snprintf(buf, PAGE_SIZE, "%d\n",
+			get_emi_clock_rate(dram_data_rate_MHz));
+}
+
+DECLARE_KOBJ_ATTR_RO(emi_clock_rate);
+
+/* KOBJ: ttype1_16_en */
+DECLARE_KOBJ_ATTR_STR_LIST_ITEM(
+	ttype1_16_en,
+	KOBJ_ITEM_LIST(
+		{ BM_TTYPE1_16_ENABLE,   "ENABLE" },
+		{ BM_TTYPE1_16_DISABLE,  "DISABLE" }
+		)
+	);
+DECLARE_KOBJ_ATTR_STR_LIST(ttype1_16_en, ttype1_16_en, ttype1_16_en);
+
+/* KOBJ: ttype17_21_en */
+DECLARE_KOBJ_ATTR_STR_LIST_ITEM(
+	ttype17_21_en,
+	KOBJ_ITEM_LIST(
+		{ BM_TTYPE17_21_ENABLE,  "ENABLE" },
+		{ BM_TTYPE17_21_DISABLE, "DISABLE" }
+		)
+	);
+DECLARE_KOBJ_ATTR_STR_LIST(ttype17_21_en, ttype17_21_en, ttype17_21_en);
+
+/* KOBJ: bw_limiter_enable */
+DECLARE_KOBJ_ATTR_STR_LIST_ITEM(
+	bw_limiter_enable,
+	KOBJ_ITEM_LIST(
+		{ BM_BW_LIMITER_ENABLE,  "ENABLE" },
+		{ BM_BW_LIMITER_DISABLE, "DISABLE" }
+		)
+	);
+
+DECLARE_KOBJ_ATTR_STR_LIST(bw_limiter_enable, bw_limiter_enable, bw_limiter_enable);
+
+/* KOBJ: ttype_master */
+DECLARE_KOBJ_ATTR_STR_LIST_ITEM(
+	ttype_master,
+	KOBJ_ITEM_LIST(
+		{ BM_MASTER_M0,  "M0" },
+		{ BM_MASTER_M1,  "M1" },
+		{ BM_MASTER_M2,  "M2" },
+		{ BM_MASTER_M3,  "M3" },
+		{ BM_MASTER_M4,  "M4" },
+		{ BM_MASTER_M5,  "M5" },
+		{ BM_MASTER_M6,  "M6" },
+		{ BM_MASTER_M7,  "M7" }
+		)
+	);
+
+
+/* KOBJ: ttypeX_nbeat, ttypeX_nbyte, ttypeX_burst */
+DECLARE_KOBJ_ATTR_INT_LIST_ITEM(
+	ttype_nbeat,
+	KOBJ_ITEM_LIST(
+		{ BM_TRANS_TYPE_1BEAT,   1 },
+		{ BM_TRANS_TYPE_2BEAT,   2 },
+		{ BM_TRANS_TYPE_3BEAT,   3 },
+		{ BM_TRANS_TYPE_4BEAT,   4 },
+		{ BM_TRANS_TYPE_5BEAT,   5 },
+		{ BM_TRANS_TYPE_6BEAT,   6 },
+		{ BM_TRANS_TYPE_7BEAT,   7 },
+		{ BM_TRANS_TYPE_8BEAT,   8 },
+		{ BM_TRANS_TYPE_9BEAT,   9 },
+		{ BM_TRANS_TYPE_10BEAT,  10 },
+		{ BM_TRANS_TYPE_11BEAT,  11 },
+		{ BM_TRANS_TYPE_12BEAT,  12 },
+		{ BM_TRANS_TYPE_13BEAT,  13 },
+		{ BM_TRANS_TYPE_14BEAT,  14 },
+		{ BM_TRANS_TYPE_15BEAT,  15 },
+		{ BM_TRANS_TYPE_16BEAT,  16 }
+		)
+	);
+DECLARE_KOBJ_ATTR_INT_LIST_ITEM(
+	ttype_nbyte,
+	KOBJ_ITEM_LIST(
+		{ BM_TRANS_TYPE_1Byte,   1 },
+		{ BM_TRANS_TYPE_2Byte,   2 },
+		{ BM_TRANS_TYPE_4Byte,   4 },
+		{ BM_TRANS_TYPE_8Byte,   8 },
+		{ BM_TRANS_TYPE_16Byte,  16 },
+		{ BM_TRANS_TYPE_32Byte,  32 }
+		)
+	);
+DECLARE_KOBJ_ATTR_STR_LIST_ITEM(
+	ttype_burst,
+	KOBJ_ITEM_LIST(
+		{ BM_TRANS_TYPE_BURST_INCR,      "INCR" },
+		{ BM_TRANS_TYPE_BURST_WRAP,      "WRAP" }
+		)
+	);
+
+DECLARE_KOBJ_ATTR_STR_LIST_ITEM(
+	ttype_rw,
+	KOBJ_ITEM_LIST(
+		{ BM_TRANS_RW_DEFAULT,   "DEFAULT" },
+		{ BM_TRANS_RW_READONLY,  "R" },
+		{ BM_TRANS_RW_WRITEONLY, "W" },
+		{ BM_TRANS_RW_RWBOTH,    "BOTH" }
+		)
+	);
+
+/* KOBJ: test_apmcu */
+DECLARE_KOBJ_ATTR_SHOW_INT(test_apmcu, times);
+/* please refer to session: "EMI Test Operations" for store operation */
+DECLARE_KOBJ_ATTR(test_apmcu);
+
+DECLARE_KOBJ_ATTR_INT(dramc_pdir_enable, dramc_pdir_enable);
+
+/*enable high priority filter*/
+static int high_priority_filter;
+DECLARE_KOBJ_ATTR_HEX(high_priority_filter, high_priority_filter);
+
+
+/**/
+static int ttype_master_val[21];
+static int ttype_busid_val[21];
+static int ttype_nbeat_val[21];
+static int ttype_nbyte_val[21];
+static int ttype_burst_val[21];
+static int ttype_rw_val[21];
+
+#define DECLARE_KOBJ_TTYPE_MASTER(nr) \
+	DECLARE_KOBJ_ATTR_STR_LIST(ttype ## nr ## _master, ttype_master_val[nr - 1], ttype_master)
+
+#define DECLARE_KOBJ_TTYPE_NBEAT(nr) \
+	DECLARE_KOBJ_ATTR_INT_LIST(ttype ## nr ## _nbeat, ttype_nbeat_val[nr - 1], ttype_nbeat)
+
+#define DECLARE_KOBJ_TTYPE_NBYTE(nr) \
+	DECLARE_KOBJ_ATTR_INT_LIST(ttype ## nr ## _nbyte, ttype_nbyte_val[nr - 1], ttype_nbyte)
+
+#define DECLARE_KOBJ_TTYPE_BURST(nr) \
+	DECLARE_KOBJ_ATTR_STR_LIST(ttype ## nr ## _burst, ttype_burst_val[nr - 1], ttype_burst)
+
+#define DECLARE_KOBJ_TTYPE_RW(nr) \
+	DECLARE_KOBJ_ATTR_STR_LIST(ttype ## nr ## _rw, ttype_rw_val[nr - 1], ttype_rw)
+
+#define DECLARE_KOBJ_TTYPE_BUSID_VAL(nr) \
+	DECLARE_KOBJ_ATTR_HEX(ttype ## nr ## _busid, ttype_busid_val[nr - 1])
+
+DECLARE_KOBJ_TTYPE_MASTER(1);
+DECLARE_KOBJ_TTYPE_NBEAT(1);
+DECLARE_KOBJ_TTYPE_NBYTE(1);
+DECLARE_KOBJ_TTYPE_BURST(1);
+DECLARE_KOBJ_TTYPE_RW(1);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(1);
+
+DECLARE_KOBJ_TTYPE_MASTER(2);
+DECLARE_KOBJ_TTYPE_NBEAT(2);
+DECLARE_KOBJ_TTYPE_NBYTE(2);
+DECLARE_KOBJ_TTYPE_BURST(2);
+DECLARE_KOBJ_TTYPE_RW(2);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(2);
+
+DECLARE_KOBJ_TTYPE_MASTER(3);
+DECLARE_KOBJ_TTYPE_NBEAT(3);
+DECLARE_KOBJ_TTYPE_NBYTE(3);
+DECLARE_KOBJ_TTYPE_BURST(3);
+DECLARE_KOBJ_TTYPE_RW(3);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(3);
+
+DECLARE_KOBJ_TTYPE_MASTER(4);
+DECLARE_KOBJ_TTYPE_NBEAT(4);
+DECLARE_KOBJ_TTYPE_NBYTE(4);
+DECLARE_KOBJ_TTYPE_BURST(4);
+DECLARE_KOBJ_TTYPE_RW(4);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(4);
+
+DECLARE_KOBJ_TTYPE_MASTER(5);
+DECLARE_KOBJ_TTYPE_NBEAT(5);
+DECLARE_KOBJ_TTYPE_NBYTE(5);
+DECLARE_KOBJ_TTYPE_BURST(5);
+DECLARE_KOBJ_TTYPE_RW(5);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(5);
+
+DECLARE_KOBJ_TTYPE_MASTER(6);
+DECLARE_KOBJ_TTYPE_NBEAT(6);
+DECLARE_KOBJ_TTYPE_NBYTE(6);
+DECLARE_KOBJ_TTYPE_BURST(6);
+DECLARE_KOBJ_TTYPE_RW(6);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(6);
+
+DECLARE_KOBJ_TTYPE_MASTER(7);
+DECLARE_KOBJ_TTYPE_NBEAT(7);
+DECLARE_KOBJ_TTYPE_NBYTE(7);
+DECLARE_KOBJ_TTYPE_BURST(7);
+DECLARE_KOBJ_TTYPE_RW(7);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(7);
+
+DECLARE_KOBJ_TTYPE_MASTER(8);
+DECLARE_KOBJ_TTYPE_NBEAT(8);
+DECLARE_KOBJ_TTYPE_NBYTE(8);
+DECLARE_KOBJ_TTYPE_BURST(8);
+DECLARE_KOBJ_TTYPE_RW(8);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(8);
+
+DECLARE_KOBJ_TTYPE_MASTER(9);
+DECLARE_KOBJ_TTYPE_NBEAT(9);
+DECLARE_KOBJ_TTYPE_NBYTE(9);
+DECLARE_KOBJ_TTYPE_BURST(9);
+DECLARE_KOBJ_TTYPE_RW(9);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(9);
+
+DECLARE_KOBJ_TTYPE_MASTER(10);
+DECLARE_KOBJ_TTYPE_NBEAT(10);
+DECLARE_KOBJ_TTYPE_NBYTE(10);
+DECLARE_KOBJ_TTYPE_BURST(10);
+DECLARE_KOBJ_TTYPE_RW(10);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(10);
+
+DECLARE_KOBJ_TTYPE_MASTER(11);
+DECLARE_KOBJ_TTYPE_NBEAT(11);
+DECLARE_KOBJ_TTYPE_NBYTE(11);
+DECLARE_KOBJ_TTYPE_BURST(11);
+DECLARE_KOBJ_TTYPE_RW(11);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(11);
+
+DECLARE_KOBJ_TTYPE_MASTER(12);
+DECLARE_KOBJ_TTYPE_NBEAT(12);
+DECLARE_KOBJ_TTYPE_NBYTE(12);
+DECLARE_KOBJ_TTYPE_BURST(12);
+DECLARE_KOBJ_TTYPE_RW(12);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(12);
+
+DECLARE_KOBJ_TTYPE_MASTER(13);
+DECLARE_KOBJ_TTYPE_NBEAT(13);
+DECLARE_KOBJ_TTYPE_NBYTE(13);
+DECLARE_KOBJ_TTYPE_BURST(13);
+DECLARE_KOBJ_TTYPE_RW(13);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(13);
+
+DECLARE_KOBJ_TTYPE_MASTER(14);
+DECLARE_KOBJ_TTYPE_NBEAT(14);
+DECLARE_KOBJ_TTYPE_NBYTE(14);
+DECLARE_KOBJ_TTYPE_BURST(14);
+DECLARE_KOBJ_TTYPE_RW(14);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(14);
+
+DECLARE_KOBJ_TTYPE_MASTER(15);
+DECLARE_KOBJ_TTYPE_NBEAT(15);
+DECLARE_KOBJ_TTYPE_NBYTE(15);
+DECLARE_KOBJ_TTYPE_BURST(15);
+DECLARE_KOBJ_TTYPE_RW(15);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(15);
+
+DECLARE_KOBJ_TTYPE_MASTER(16);
+DECLARE_KOBJ_TTYPE_NBEAT(16);
+DECLARE_KOBJ_TTYPE_NBYTE(16);
+DECLARE_KOBJ_TTYPE_BURST(16);
+DECLARE_KOBJ_TTYPE_RW(16);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(16);
+
+DECLARE_KOBJ_TTYPE_MASTER(17);
+DECLARE_KOBJ_TTYPE_NBEAT(17);
+DECLARE_KOBJ_TTYPE_NBYTE(17);
+DECLARE_KOBJ_TTYPE_BURST(17);
+DECLARE_KOBJ_TTYPE_RW(17);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(17);
+
+DECLARE_KOBJ_TTYPE_MASTER(18);
+DECLARE_KOBJ_TTYPE_NBEAT(18);
+DECLARE_KOBJ_TTYPE_NBYTE(18);
+DECLARE_KOBJ_TTYPE_BURST(18);
+DECLARE_KOBJ_TTYPE_RW(18);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(18);
+
+DECLARE_KOBJ_TTYPE_MASTER(19);
+DECLARE_KOBJ_TTYPE_NBEAT(19);
+DECLARE_KOBJ_TTYPE_NBYTE(19);
+DECLARE_KOBJ_TTYPE_BURST(19);
+DECLARE_KOBJ_TTYPE_RW(19);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(19);
+
+DECLARE_KOBJ_TTYPE_MASTER(20);
+DECLARE_KOBJ_TTYPE_NBEAT(20);
+DECLARE_KOBJ_TTYPE_NBYTE(20);
+DECLARE_KOBJ_TTYPE_BURST(20);
+DECLARE_KOBJ_TTYPE_RW(20);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(20);
+
+DECLARE_KOBJ_TTYPE_MASTER(21);
+DECLARE_KOBJ_TTYPE_NBEAT(21);
+DECLARE_KOBJ_TTYPE_NBYTE(21);
+DECLARE_KOBJ_TTYPE_BURST(21);
+DECLARE_KOBJ_TTYPE_RW(21);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(21);
+
+/**/
+#define KOBJ_ATTR_ITEM_SERIAL_FNODE(nr) \
+	do { \
+		KOBJ_ATTR_ITEM(ttype ## nr ## _master); \
+		KOBJ_ATTR_ITEM(ttype ## nr ## _nbeat); \
+		KOBJ_ATTR_ITEM(ttype ## nr ## _nbyte); \
+		KOBJ_ATTR_ITEM(ttype ## nr ## _burst); \
+		KOBJ_ATTR_ITEM(ttype ## nr ## _busid); \
+		KOBJ_ATTR_ITEM(ttype ## nr ## _rw); \
+	} while (0)
+
+#define KOBJ_ATTR_LIST \
+	do { \
+		KOBJ_ATTR_ITEM(high_priority_filter); \
+		KOBJ_ATTR_ITEM(metemi_func_opt); \
+		KOBJ_ATTR_ITEM(emi_tsct_enable); \
+		KOBJ_ATTR_ITEM(emi_mdct_enable); \
+		KOBJ_ATTR_ITEM(emi_TP_busfiltr_enable); \
+		KOBJ_ATTR_ITEM(emi_regdump); \
+		KOBJ_ATTR_ITEM(msel_enable); \
+		KOBJ_ATTR_ITEM(msel_group1); \
+		KOBJ_ATTR_ITEM(msel_group2); \
+		KOBJ_ATTR_ITEM(msel_group3); \
+		KOBJ_ATTR_ITEM(emi_clock_rate); \
+		KOBJ_ATTR_ITEM(rwtype); \
+		KOBJ_ATTR_ITEM(ttype17_21_en); \
+		KOBJ_ATTR_ITEM(ttype1_16_en); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(1); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(2); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(3); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(4); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(5); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(6); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(7); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(8); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(9); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(10); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(11); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(12); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(13); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(14); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(15); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(16); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(17); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(18); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(19); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(20); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(21); \
+		KOBJ_ATTR_ITEM(test_apmcu); \
+		KOBJ_ATTR_ITEM(bw_limiter_enable); \
+		KOBJ_ATTR_ITEM(dramc_pdir_enable); \
+		KOBJ_ATTR_ITEM(mdmcu_sel_enable); \
+		KOBJ_ATTR_ITEM(rd_mdmcu_rsv_num); \
+	} while (0)
+
+
+/*======================================================================*/
+/*	EMI Operations							*/
+/*======================================================================*/
+static void emi_init(void)
+{
+	unsigned int bmrw0_val, bmrw1_val, i, enable;
+	unsigned int msel_group_val[4];
+
+	/*save origianl EMI config*/
+	MET_BM_SaveCfg();
+
+	/* get dram channel number */
+	dram_chann_num = MET_EMI_GetDramChannNum();
+
+	/* Init. EMI bus monitor */
+	MET_BM_SetReadWriteType(rwtype);
+
+	if ((ttype1_16_en != BM_TTYPE1_16_ENABLE) && (emi_TP_busfiltr_enable != 1)) {
+		if (msel_enable) {
+			msel_group_val[0] = BM_MASTER_ALL;
+			msel_group_val[1] = msel_group1;
+			msel_group_val[2] = msel_group2;
+			msel_group_val[3] = msel_group3;
+		} else {
+			msel_group_val[0] = BM_MASTER_ALL;
+			msel_group_val[1] = BM_MASTER_ALL;
+			msel_group_val[2] = BM_MASTER_ALL;
+			msel_group_val[3] = BM_MASTER_ALL;
+		}
+
+		MET_BM_SetLatencyCounter(1);    /*enable latency count*/
+
+		for (i = 1; i <= 4; i++) {
+			MET_BM_SetMonitorCounter(i,
+						 msel_group_val[i - 1] & BM_MASTER_ALL,
+						 BM_TRANS_TYPE_4BEAT |
+						 BM_TRANS_TYPE_8Byte |
+						 BM_TRANS_TYPE_BURST_WRAP);
+			MET_BM_SetbusID(i, 0);
+			MET_BM_SetbusID_En(i, 0);       /*disable ttype bus sel*/
+		}
+		for (i = 0; i < 4; i++)
+			MET_BM_Set_WsctTsct_id_sel(i, 0);       /*disable tp filter*/
+
+	} else if ((ttype1_16_en != BM_TTYPE1_16_ENABLE) && (emi_TP_busfiltr_enable == 1)) {
+		MET_BM_SetLatencyCounter(1);    /*enable latency count*/
+
+		for (i = 1; i <= 4; i++) {
+			MET_BM_SetMonitorCounter(i,
+						 ttype_master_val[i - 1],
+						 ttype_nbeat_val[i - 1] |
+						 ttype_nbyte_val[i - 1] |
+						 ttype_burst_val[i - 1]);
+			MET_BM_SetbusID(i, ttype_busid_val[i - 1]);
+			MET_BM_SetbusID_En(i, 0);       /*disable ttype bus sel*/
+		}
+		for (i = 0; i < 4; i++)
+			MET_BM_Set_WsctTsct_id_sel(i, 1);       /*enable tp filter*/
+
+	} else if ((ttype1_16_en == BM_TTYPE1_16_ENABLE) && (emi_TP_busfiltr_enable != 1)) {
+		MET_BM_SetLatencyCounter(0);    /*disable latency count*/
+
+		for (i = 1; i <= 16; i++) {
+			MET_BM_SetMonitorCounter(i,
+						 ttype_master_val[i - 1],
+						 ttype_nbeat_val[i - 1] |
+						 ttype_nbyte_val[i - 1] |
+						 ttype_burst_val[i - 1]);
+
+			MET_BM_SetbusID(i, ttype_busid_val[i - 1]);
+			/*disenable ttype bus sel if busid > 0xff_ff*/
+			MET_BM_SetbusID_En(i, (ttype_busid_val[i - 1] > 0xffff) ? 0 : 1);
+		}
+		for (i = 0; i < 4; i++)
+			MET_BM_Set_WsctTsct_id_sel(i, 0);       /*disable tp filter*/
+	} else {	/* (ttype1_16_en == BM_TTYPE1_16_ENABLE)  &&  (emi_TP_busfiltr_enable == 1) */
+		MET_BM_SetLatencyCounter(0);    /*disable latency count*/
+
+		for (i = 1; i <= 16; i++) {
+			MET_BM_SetMonitorCounter(i,
+						 ttype_master_val[i - 1],
+						 ttype_nbeat_val[i - 1] |
+						 ttype_nbyte_val[i - 1] |
+						 ttype_burst_val[i - 1]);
+
+			MET_BM_SetbusID(i, ttype_busid_val[i - 1]);
+			/*disable ttype bus sel if busid > 0xff_ff*/
+			MET_BM_SetbusID_En(i, (ttype_busid_val[i - 1] > 0xffff) ? 0 : 1);
+		}
+		for (i = 0; i < 4; i++)
+			MET_BM_Set_WsctTsct_id_sel(i, 1);       /*enable tp filter*/
+	}
+
+	if (ttype17_21_en == BM_TTYPE17_21_ENABLE) {
+		for (i = 17; i <= 21; i++) {
+			MET_BM_SetMonitorCounter(i,
+						 ttype_master_val[i - 1],
+						 ttype_nbeat_val[i - 1] |
+						 ttype_nbyte_val[i - 1] |
+						 ttype_burst_val[i - 1]);
+			MET_BM_SetbusID(i, ttype_busid_val[i - 1]);
+			/*disable ttype bus sel if busid > 0xff_ff*/
+			MET_BM_SetbusID_En(i, (ttype_busid_val[i - 1] > 0xffff) ? 0 : 1);
+		}
+	}
+
+	bmrw0_val = 0;
+	for (i = 0; i < 16; i++)
+		bmrw0_val |= (ttype_rw_val[i] << (i * 2));
+
+	bmrw1_val = 0;
+	for (i = 16; i < 21; i++)
+		bmrw1_val |= (ttype_rw_val[i] << ((i-16) * 2));
+
+	MET_BM_SetTtypeCounterRW(bmrw0_val, bmrw1_val);
+
+	for (i = 0; i < BM_COUNTER_MAX; i++) {
+		if ((high_priority_filter & (1 << i)) == 0)
+			enable = 0;
+		else
+			enable = 1;
+
+		MET_BM_SetUltraHighFilter(i + 1, enable);
+	}
+
+	met_record_dramc_dcm_enable_flag();
+
+	if (met_emi_regdump == 1)
+		emi_dump_reg();
+}
+
+
+static void emi_uninit(void)
+{
+	MET_BM_RestoreCfg();
+}
+
+static inline void emi_start(void)
+{
+	MET_BM_Enable(1);
+}
+
+
+static inline void emi_stop(void)
+{
+	MET_BM_Enable(0);
+}
+
+
+static inline int do_emi(void)
+{
+	return met_emi.mode;
+}
+
+
+noinline void DRAM_DVFS(unsigned int dram_data_rate_MHz)
+{
+	MET_TRACE("%u\n", dram_data_rate_MHz);
+}
+
+
+static unsigned int emi_bw_limiter(unsigned int *__restrict__ array)
+{
+	int idx = 0;
+	unsigned int dram_data_rate_MHz;
+
+	if (get_dram_data_rate_symbol) {
+		dram_data_rate_MHz = get_dram_data_rate_symbol();
+	} else {
+		METERROR("get_dram_data_rate_symbol = NULL\n");
+		dram_data_rate_MHz = 0;
+	}
+
+	/* print dram data rate */
+	DRAM_DVFS(dram_data_rate_MHz);
+
+	/* get correct dram_clock_rate */
+	array[idx++] = dram_data_rate_MHz;
+
+	/* get correct ARB A->LAST */
+	array[idx++] = MET_EMI_GetARBA();
+	array[idx++] = MET_EMI_GetARBB();
+	array[idx++] = MET_EMI_GetARBC();
+	array[idx++] = MET_EMI_GetARBD();
+	array[idx++] = MET_EMI_GetARBE();
+	array[idx++] = MET_EMI_GetARBF();
+	array[idx++] = MET_EMI_GetARBG();
+	array[idx++] = MET_EMI_GetARBH();
+	/* EMI Total BW Thresholds */
+	array[idx++] = MET_EMI_GetBWCT0();
+	array[idx++] = MET_EMI_GetBWCT1();
+	array[idx++] = MET_EMI_GetBWCT2();
+	array[idx++] = MET_EMI_GetBWCT3();
+	array[idx++] = MET_EMI_GetBWCT4();
+	array[idx++] = MET_EMI_GetBWST0();
+	array[idx++] = MET_EMI_GetBWST1();
+	/* EMI C+G BW Thresholds */
+	array[idx++] = MET_EMI_GetBWCT0_2ND();
+	array[idx++] = MET_EMI_GetBWCT1_2ND();
+	array[idx++] = MET_EMI_GetBWST_2ND();
+
+	return idx;
+}
+
+
+static void _ms_dramc(unsigned int *__restrict__ dramc_pdir_value, int dram_chann_num)
+{
+	MET_DRAMC_GetDebugCounter(dramc_pdir_value, dram_chann_num);
+}
+
+
+static unsigned int emi_polling(unsigned int *__restrict__ emi_value, unsigned int *__restrict__ emi_tsct,
+				unsigned int *__restrict__ emi_ttype_value, unsigned int *__restrict__ dramc_pdir_value,
+				unsigned int *__restrict__ emi_mdct_value)
+{
+	int j = 4;              /* skip 4 WSCTs */
+	int i = 0;              /* ttype start at 0 */
+	int k = 0;              /* tsct start at 0 */
+	int n;
+
+	MET_BM_Pause();
+
+	/* Get Word Count */
+
+	emi_value[0] = MET_BM_GetWordCount(1);  /* All */
+	emi_value[1] = MET_BM_GetWordCount(2);  /* Group 1 */
+	emi_value[2] = MET_BM_GetWordCount(3);  /* Group 2 */
+	emi_value[3] = MET_BM_GetWordCount(4);  /* Group 3 */
+
+	if (ttype1_16_en != BM_TTYPE1_16_ENABLE) {      /*1~21 NOT for ttype*/
+		/* Get Latency */
+		j += MET_BM_GetLatencyCycle(emi_value + j, 1, 8);
+
+		/* Get Trans. */
+		j += MET_BM_GetLatencyCycle(emi_value + j, 9, 16);
+	} else {
+		for (n = 4; n < 20; n++)
+			emi_value[n] = 0;
+		j = 20;
+
+		i += MET_BM_GetLatencyCycle(emi_ttype_value + i, 1, 8);
+
+		/* Get Trans. */
+		i += MET_BM_GetLatencyCycle(emi_ttype_value + i, 9, 16);
+	}
+
+	/* Get BACT/BSCT/BCNT/WACT/DCM_CTRL */
+	emi_value[j++] = MET_BM_GetBandwidthWordCount(); /* 20 */
+	emi_value[j++] = MET_BM_GetOverheadWordCount();
+	emi_value[j++] = MET_BM_GetBusCycCount();
+	emi_value[j++] = MET_BM_GetWordAllCount();
+	emi_value[j++] = MET_DRAMC_DCM_CTRL(0);
+
+	/* Get TACT */
+	emi_value[j++] = MET_BM_GetTransAllCount();
+
+	/* Get PageHist/PageMiss/InterBank/Idle */
+	for (n = 0; n < dram_chann_num; n++) {
+		emi_value[j++] = MET_DRAMC_GetPageHitCount(DRAMC_ALL, n);
+		emi_value[j++] = MET_DRAMC_GetPageMissCount(DRAMC_ALL, n);
+		emi_value[j++] = MET_DRAMC_GetInterbankCount(DRAMC_ALL, n);
+		emi_value[j++] = MET_DRAMC_GetIdleCount(n);
+		emi_value[j++] = ((MET_DRAMC_SPCMDRESP(n) >> 8) & 0x7); /* refresh rate */
+		emi_value[j++] = MET_DRAMC_RefPop(n);
+		emi_value[j++] = MET_DRAMC_Free26M(n);
+		emi_value[j++] = MET_DRAMC_RByte(n);
+		emi_value[j++] = MET_DRAMC_WByte(n);
+	}
+	/* TTYPE */
+	if (ttype17_21_en == BM_TTYPE17_21_ENABLE)    /*17~21 for ttype*/
+		MET_BM_GetLatencyCycle(emi_ttype_value + 16, 17, 21);
+
+	/* Get tsct */
+	if (emi_tsct_enable == 1) {
+		emi_tsct[k++] = MET_BM_GetTransCount(1);
+		emi_tsct[k++] = MET_BM_GetTransCount(2);
+		emi_tsct[k++] = MET_BM_GetTransCount(3);
+	}
+	/*get mdct rsv buffer*/
+	if (emi_mdct_enable == 1) {
+		emi_mdct_value[0] = (MET_BM_GetMDCT() >> 16) & 0x7;
+		emi_mdct_value[1] = (MET_BM_GetMDCT_2() & 0x7);
+	}
+
+	if (dramc_pdir_enable == 1 && DRAMC_VER >= 2 )
+		_ms_dramc(dramc_pdir_value, dram_chann_num);
+
+	MET_BM_Continue();
+	MET_BM_Clear_Start();
+
+	return j;
+}
+
+
+/*======================================================================*/
+/*	MET Device Operations						*/
+/*======================================================================*/
+static int emi_inited;
+
+static int met_emi_create(struct kobject *parent)
+{
+	int ret = 0;
+	int i;
+
+	for (i = 0; i < 21; i++) {
+		ttype_master_val[i] = BM_MASTER_M0;
+		ttype_nbeat_val[i] = BM_TRANS_TYPE_1BEAT;
+		ttype_nbyte_val[i] = BM_TRANS_TYPE_8Byte;
+		ttype_burst_val[i] = BM_TRANS_TYPE_BURST_INCR;
+		ttype_busid_val[i] = 0xfffff;   /*default disable ttype bus sel if busid > 0xff_ff */
+		ttype_rw_val[i] =  BM_TRANS_RW_DEFAULT;
+	}
+
+	ret = MET_BM_Init();
+	if (ret != 0) {
+		pr_notice("MET_BM_Init failed!!!\n");
+		ret = 0;        /* will retry later */
+	} else {
+		emi_inited = 1;
+	}
+
+	kobj_emi = parent;
+
+#define KOBJ_ATTR_ITEM(attr_name) \
+	do { \
+		ret = sysfs_create_file(kobj_emi, &attr_name ## _attr.attr); \
+		if (ret != 0) { \
+			pr_notice("Failed to create " #attr_name " in sysfs\n"); \
+			return ret; \
+		} \
+	} while (0)
+	KOBJ_ATTR_LIST;
+#undef  KOBJ_ATTR_ITEM
+
+	return ret;
+}
+
+
+static void met_emi_delete(void)
+{
+#define KOBJ_ATTR_ITEM(attr_name) \
+	sysfs_remove_file(kobj_emi, &attr_name ## _attr.attr)
+	if (kobj_emi != NULL) {
+		KOBJ_ATTR_LIST;
+		kobj_emi = NULL;
+	}
+#undef  KOBJ_ATTR_ITEM
+
+	if (emi_inited)
+		MET_BM_DeInit();
+}
+
+
+static void met_emi_start(void)
+{
+	unsigned int bw_limiter[NIDX_BL];
+
+	if (!emi_inited) {
+		if (MET_BM_Init() != 0) {
+			met_emi.mode = 0;
+			pr_notice("MET_BM_Init failed!!!\n");
+			return;
+		}
+		emi_inited = 1;
+	}
+
+	if (do_emi()) {
+		emi_init();
+		MET_BM_Clear_Start();
+
+		/* Draw the first BW Limiter point */
+		if (bw_limiter_enable == BM_BW_LIMITER_ENABLE) {
+			emi_bw_limiter(bw_limiter);
+			ms_bw_limiter(NIDX_BL, bw_limiter);
+			/* init countdown value */
+			countdown = CNT_COUNTDOWN;
+		}
+	}
+}
+
+
+static void met_emi_stop(void)
+{
+	unsigned int bw_limiter[NIDX_BL];
+
+	if (!emi_inited)
+		return;
+
+	if (met_emi_regdump == 1)
+		emi_dump_reg();
+
+	if (do_emi()) {
+		/* Draw the last BW Limiter point */
+		if (bw_limiter_enable == BM_BW_LIMITER_ENABLE) {
+			/*
+			 * Skip drawing when we just draw
+			 * the point at last polling.
+			 */
+			if (countdown < CNT_COUNTDOWN) {
+				emi_bw_limiter(bw_limiter);
+				ms_bw_limiter(NIDX_BL, bw_limiter);
+			}
+		}
+
+		emi_stop();
+		emi_uninit();
+	}
+}
+
+
+/* DRS output */
+noinline void emi_drs(uint32_t *value)
+{
+	MET_TRACE("%u,%u,%u,%u,%u,%u\n", value[0], value[1], value[2], value[3], value[4], value[5]);
+}
+
+
+static void met_emi_polling(unsigned long long stamp, int cpu)
+{
+	unsigned int emi_value[NIDX];
+	unsigned int emi_tsct[3];
+	unsigned int emi_ttype_value[21];
+	unsigned int dramc_pdir_value[DRAMC_Debug_MAX_CNT * NCH];
+	unsigned int emi_mdct_value[2];
+	unsigned int chn_emi_drs[6];
+
+	if (!do_emi())
+		return;
+
+	/* get emi & dramc counters */
+	emi_value[0] = 0;       /* 0: pure linux MET , 0xa5: OnDieMET*/
+	emi_value[1] = 0;       /* EBM pause duration (ns)*/
+	emi_polling(emi_value + 2, emi_tsct, emi_ttype_value, dramc_pdir_value, emi_mdct_value);
+
+	/* get and output BW Limiter */
+	if (bw_limiter_enable == BM_BW_LIMITER_ENABLE) {
+		unsigned int bw_limiter[NIDX_BL];
+
+		if (countdown > 0) {
+			countdown--;
+		} else {
+			emi_bw_limiter(bw_limiter);
+			ms_bw_limiter(NIDX_BL, bw_limiter);
+			/* reload countdown value */
+			countdown = CNT_COUNTDOWN;
+		}
+	}
+
+	/* output emi */
+	ms_emi(NIDX_EMI - NTTYPE + (NCNT * dram_chann_num), emi_value);
+
+	/* output tsct*/
+	if (emi_tsct_enable == 1)
+		ms_emi_tsct(3, emi_tsct);
+
+	/* output mdct*/
+	if (emi_mdct_enable == 1)
+		ms_emi_mdct(2, emi_mdct_value);
+
+	/* output dramc*/
+	if (dramc_pdir_enable == 1 && DRAMC_VER >= 2 )
+		ms_dramc(DRAMC_Debug_MAX_CNT * dram_chann_num, dramc_pdir_value);
+
+	/* output ms_ttype */
+	if (ttype17_21_en == BM_TTYPE17_21_ENABLE)
+		ms_ttype(21, emi_ttype_value);
+
+	/* adjust MDMCU buffer */
+	if (mdmcu_sel_enable == 1)
+		MET_BM_SetMDCT_MDMCU(rd_mdmcu_rsv_num);
+
+	/* DRS handling */
+	met_get_drs_registers(chn_emi_drs);
+	emi_drs(chn_emi_drs);
+}
+
+
+static void met_emi_resume(void)
+{
+	/* return directly when emi was closed */
+	if (!do_emi())
+		return;
+
+	/* remap EMI_BM related reg*/
+	emi_init();
+
+	/* restarn counting */
+	MET_BM_Clear_Start();
+}
+
+
+static const char help[] = "  --emi                                 monitor EMI banwidth\n";
+static int emi_print_help(char *buf, int len)
+{
+	return snprintf(buf, PAGE_SIZE, help);
+}
+
+
+#define TTYPE_NAME_STR_LEN  64
+/* static char ttype_name[21][TTYPE_NAME_STR_LEN]; */
+static int emi_print_header(char *buf, int len)
+{
+	int ret = 0;
+/*	int ret_m[21]; */
+	int i = 0;
+
+#if 1 /* move to AP side print header */
+//#ifndef CONFIG_MTK_TINYSYS_SSPM_SUPPORT
+	unsigned int dram_data_rate_MHz;
+	unsigned int DRAM_TYPE;
+#endif
+
+#if 0
+	/*ttype header info*/
+	for (i = 0; i < 21; i++) {
+		int k;
+
+		/*busid > 0xffff    not specific bus id , show all on specificmaster*/
+		if (ttype_busid_val[i] > 0xffff) {
+			int j;
+
+			for (j = 0; j < ARRAY_SIZE(ttype_master_list_item); j++) {
+				if (ttype_master_val[i] == ttype_master_list_item[j].key) {
+					ret_m[i] = snprintf(ttype_name[i], TTYPE_NAME_STR_LEN, "ttype%d_%s",
+							    i + 1, ttype_master_list_item[j].val);/*master*/
+					break;
+				}
+			}
+			if (j == ARRAY_SIZE(ttype_master_list_item))
+				ret_m[i] = snprintf(ttype_name[i], TTYPE_NAME_STR_LEN, "ttype%d_%s",
+						    i + 1, "unknown");
+		} else {
+			ret_m[i] = snprintf(ttype_name[i], TTYPE_NAME_STR_LEN, "ttype%d_%x",
+					    i + 1, ttype_busid_val[i]);/*busID*/
+		}
+
+		/*show beat type*/
+		for (k = 0; k < ARRAY_SIZE(ttype_nbeat_list_item); k++) {
+
+			if (ttype_nbeat_val[i] == ttype_nbeat_list_item[k].key)
+				ret_m[i] += snprintf(ttype_name[i] + ret_m[i], TTYPE_NAME_STR_LEN - ret_m[i], "_%d",
+						     ttype_nbeat_list_item[k].val); /*beat*/
+		}
+
+		/*show byte type*/
+		for (k = 0; k < ARRAY_SIZE(ttype_nbyte_list_item); k++) {
+
+			if (ttype_nbyte_val[i] == ttype_nbyte_list_item[k].key)
+				ret_m[i] += snprintf(ttype_name[i] + ret_m[i], TTYPE_NAME_STR_LEN - ret_m[i], "x%d",
+						     ttype_nbyte_list_item[k].val); /*byte*/
+		}
+
+		/*show burst type*/
+		for (k = 0; k < ARRAY_SIZE(ttype_burst_list_item); k++) {
+
+			if (ttype_burst_val[i] == ttype_burst_list_item[k].key)
+				ret_m[i] += snprintf(ttype_name[i] + ret_m[i], TTYPE_NAME_STR_LEN - ret_m[i], "_%s",
+						     ttype_burst_list_item[k].val); /*burst*/
+		}
+
+		/*show rw type*/
+		for (k = 0; k < ARRAY_SIZE(ttype_rw_list_item); k++) {
+
+			if (ttype_rw_val[i] == ttype_rw_list_item[k].key)
+				ret_m[i] += snprintf(ttype_name[i] + ret_m[i], TTYPE_NAME_STR_LEN - ret_m[i], "_%s",
+						     ttype_rw_list_item[k].val); /*rw*/
+		}
+	}
+#endif
+
+	if ((ttype1_16_en != BM_TTYPE1_16_ENABLE) && (emi_TP_busfiltr_enable != 1)) {
+		/* master selection header */
+		if (msel_enable) {
+			ret += snprintf(buf + ret, PAGE_SIZE - ret,
+					"met-info [000] 0.0: met_emi_msel: %x,%x,%x\n",
+					msel_group1 & BM_MASTER_ALL,
+					msel_group2 & BM_MASTER_ALL,
+					msel_group3 & BM_MASTER_ALL);
+		} else {
+			ret += snprintf(buf + ret, PAGE_SIZE - ret,
+					"met-info [000] 0.0: met_emi_msel: %x,%x,%x\n",
+					BM_MASTER_ALL & BM_MASTER_ALL,
+					BM_MASTER_ALL & BM_MASTER_ALL,
+					BM_MASTER_ALL & BM_MASTER_ALL);
+		}
+	} else {
+		/*ttype master if BM_TTYPE1_16_ENABLE or emi_TP_busfiltr_enable*/
+		ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"met-info [000] 0.0: met_emi_ttype_master: %x,%x,%x,%x\n",
+				ttype_master_val[0], ttype_master_val[1], ttype_master_val[2], ttype_master_val[3]);
+
+		if (emi_TP_busfiltr_enable == 1) {
+			/* busID if emi_TP_busfiltr_enable*/
+			ret += snprintf(buf + ret, PAGE_SIZE - ret,
+					"met-info [000] 0.0: met_emi_ttype_busid: %x,%x,%x,%x\n",
+					ttype_busid_val[0], ttype_busid_val[1], ttype_busid_val[2], ttype_busid_val[3]);
+		}
+	}
+
+	/*RW type header*/
+	ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_emi_rw_cfg: ");
+	if (rwtype == BM_READ_ONLY)
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "R");
+	else if (rwtype == BM_WRITE_ONLY)
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "W");
+	else
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "BOTH");
+
+	for (i = 0; i < 21; i++) {
+		if (ttype_rw_val[i] == BM_TRANS_RW_DEFAULT)
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, ",DEFAULT");
+		else if (ttype_rw_val[i] == BM_TRANS_RW_READONLY)
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, ",R");
+		else if (ttype_rw_val[i] == BM_TRANS_RW_WRITEONLY)
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, ",W");
+		else    /*BM_TRANS_RW_RWBOTH*/
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, ",BOTH");
+	}
+	ret += snprintf(buf + ret, PAGE_SIZE - ret, "\n");
+
+	/*ultra header*/
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"met-info [000] 0.0: met_emi_ultra_filter: %x\n", high_priority_filter);
+
+#if 0
+	/*ttype header (user define format)*/
+	if ((ttype1_16_en == BM_TTYPE1_16_ENABLE) && (ttype17_21_en == BM_TTYPE17_21_ENABLE)) {
+		/*header = ttype1~21t*/
+		int i;
+
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: ms_ud_sys_header: ms_ttype,");
+		for (i = 0; i < 21; i++)
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, "%s,", ttype_name[i]);
+
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x\n");
+
+	} else if (ttype17_21_en == BM_TTYPE17_21_ENABLE) {
+		/*header = ttype17~21t*/
+		int i;
+
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: ms_ud_sys_header: ms_ttype,");
+
+		for (i = 16; i < 21; i++)
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, "%s,", ttype_name[i]);
+
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "x,x,x,x,x\n");
+	}
+#else
+#if 1
+	/* ttype header */
+	if (ttype17_21_en == BM_TTYPE17_21_ENABLE) {
+		int i = 0;
+		int j = 0;
+
+		/* ttype master list */
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_emi_ttype_master_list: ");
+		for (i = 0; i < 21; i++) {
+			for (j = 0; j < ARRAY_SIZE(ttype_master_list_item); j++) {
+				if (ttype_master_val[i] == ttype_master_list_item[j].key) {
+					ret += snprintf(buf + ret, PAGE_SIZE - ret, "%s,", ttype_master_list_item[j].val);
+				}
+			}
+		}
+		/* remove the last comma */
+		snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+		/* ttype busid list */
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_emi_ttype_busid_list: ");
+		for (i = 0; i < 21; i++)
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, "%x,", ttype_busid_val[i]);
+
+		snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+		/* ttype nbeat list */
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_emi_ttype_nbeat_list: ");
+		for (i = 0; i < 21; i++) {
+			for (j = 0; j < ARRAY_SIZE(ttype_nbeat_list_item); j++) {
+				if (ttype_nbeat_val[i] == ttype_nbeat_list_item[j].key) {
+					ret += snprintf(buf + ret, PAGE_SIZE - ret, "%d,", ttype_nbeat_list_item[j].val);
+				}
+			}
+		}
+		snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+		/* ttype nbyte list */
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_emi_ttype_nbyte_list: ");
+		for (i = 0; i < 21; i++) {
+			for (j = 0; j < ARRAY_SIZE(ttype_nbyte_list_item); j++) {
+				if (ttype_nbyte_val[i] == ttype_nbyte_list_item[j].key) {
+					ret += snprintf(buf + ret, PAGE_SIZE - ret, "%d,", ttype_nbyte_list_item[j].val);
+				}
+			}
+		}
+		snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+		/* ttype burst list */
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_emi_ttype_burst_list: ");
+		for (i = 0; i < 21; i++) {
+			for (j = 0; j < ARRAY_SIZE(ttype_burst_list_item); j++) {
+				if (ttype_burst_val[i] == ttype_burst_list_item[j].key) {
+					ret += snprintf(buf + ret, PAGE_SIZE - ret, "%s,", ttype_burst_list_item[j].val);
+				}
+			}
+		}
+		snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+		/* ttype enable */
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_emi_ttype_enable: %d,%d\n",ttype1_16_en, ttype17_21_en);
+
+
+	}
+#endif
+#endif
+	/*IP version*/
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"met-info [000] 0.0: DRAMC_VER: %d\n", DRAMC_VER);
+
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"met-info [000] 0.0: EMI_VER: %d.%d\n", EMI_VER_MAJOR, EMI_VER_MINOR);
+
+#if 1 /* move to AP side print header */
+//#ifndef CONFIG_MTK_TINYSYS_SSPM_SUPPORT
+	dram_chann_num = MET_EMI_GetDramChannNum();
+	/*	met_dram_chann_num_header
+	 *	channel number
+	 *	LP4: 2, LP3: 1
+	 */
+
+	/*
+	 *	the ddr type define :
+	 *	enum DDRTYPE {
+	 *	TYPE_LPDDR3 = 1,
+	 *	TYPE_LPDDR4,
+	 *	TYPE_LPDDR4X,
+	 *	TYPE_LPDDR2
+	 *	};
+	 */
+	if (get_ddr_type_symbol) {
+		DRAM_TYPE = get_ddr_type_symbol();
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_dram_type: %d\n", DRAM_TYPE);
+
+		if ((DRAM_TYPE == 2) || (DRAM_TYPE == 3))
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_dram_chann_num_header: %d,%d,%d,%d\n",
+					dram_chann_num, DRAM_EMI_BASECLOCK_RATE_LP4,
+					DRAM_IO_BUS_WIDTH_LP4, DRAM_DATARATE);
+		else
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_dram_chann_num_header: %d,%d,%d,%d\n",
+					dram_chann_num, DRAM_EMI_BASECLOCK_RATE_LP3,
+					DRAM_IO_BUS_WIDTH_LP3, DRAM_DATARATE);
+	} else
+		METERROR("[%s][%d]get_ddr_type_symbol = NULL , use the TYPE_LPDDR3 setting\n", __func__, __LINE__);
+
+	/* metemi_func_opt for middleware */
+	ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: metemi_func_opt_header: %d\n",
+			metemi_func_opt);
+
+	/* met_emi_clockrate */
+	if (get_dram_data_rate_symbol) {
+		dram_data_rate_MHz = get_dram_data_rate_symbol();
+	} else {
+		METERROR("get_dram_data_rate_symbol = NULL\n");
+		dram_data_rate_MHz = 0;
+	}
+
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"met-info [000] 0.0: met_dram_clockrate: %d\n",
+			dram_data_rate_MHz);
+
+	/* 1 : by ondiemet, 0: by pure linux */
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"met-info [000] 0.0: emi_use_ondiemet: %u\n",
+			emi_use_ondiemet);
+
+	/*dram bank num*/
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"met-info [000] 0.0: met_dram_rank_num_header: %u,%u\n", MET_EMI_GetDramRankNum(),
+				MET_EMI_GetDramRankNum());
+
+	/* ms_emi header */
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"# ms_emi: TS0,TS1,GP0_WSCT,GP1_WSCT,GP2_WSCT,GP3_WSCT,");
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"M0_LATENCY,M1_LATENCY,M2_LATENCY,M3_LATENCY,M4_LATENCY,M5_LATENCY,M6_LATENCY,M7_LATENCY,");
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"M0_TRANS,M1_TRANS,M2_TRANS,M3_TRANS,M4_TRANS,M5_TRANS,M6_TRANS,M7_TRANS,");
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"BACT,BSCT,BCNT,WACT,DCM_CTRL,TACT,");
+
+	for (i = 0; i < dram_chann_num; i++) {
+		if (i != 0)
+			ret += snprintf(buf + ret, PAGE_SIZE - ret,
+					",");
+		ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"PageHit_%d,PageMiss_%d,InterBank_%d,Idle_%d,", i, i, i, i);
+		ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"mr4_%d,refresh_pop_%d,freerun_26m_%d,", i, i, i);
+		ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"read_bytes_%d,write_bytes_%d", i, i);
+	}
+	ret += snprintf(buf + ret, PAGE_SIZE - ret, "\n");
+
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"met-info [000] 0.0: met_emi_header: TS0,TS1,GP0_WSCT,GP1_WSCT,GP2_WSCT,GP3_WSCT,");
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"M0_LATENCY,M1_LATENCY,M2_LATENCY,M3_LATENCY,M4_LATENCY,M5_LATENCY,M6_LATENCY,M7_LATENCY,");
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"M0_TRANS,M1_TRANS,M2_TRANS,M3_TRANS,M4_TRANS,M5_TRANS,M6_TRANS,M7_TRANS,");
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"BACT,BSCT,BCNT,WACT,DCM_CTRL,TACT,");
+
+	for (i = 0; i < dram_chann_num; i++) {
+		if (i != 0)
+			ret += snprintf(buf + ret, PAGE_SIZE - ret,
+					",");
+		ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"PageHit_%d,PageMiss_%d,InterBank_%d,Idle_%d,", i, i, i, i);
+		ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"mr4_%d,refresh_pop_%d,freerun_26m_%d,", i, i, i);
+		ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"read_bytes_%d,write_bytes_%d", i, i);
+	}
+	ret += snprintf(buf + ret, PAGE_SIZE - ret, "\n");
+
+	/*TSCT header*/
+	if (emi_tsct_enable == 1) {
+		ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"met-info [000] 0.0: ms_emi_tsct_header: ms_emi_tsct,");
+		ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"tsct1,tsct2,tsct3\n");
+	}
+
+	/*MDCT header*/
+	if (emi_mdct_enable == 1) {
+		ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"met-info [000] 0.0: ms_emi_mdct_header: ms_emi_mdct,");
+		ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"RD_ULTRA,RD_MDMCU\n");
+	}
+
+	/* met_bw_limiter_header */
+	if (bw_limiter_enable == BM_BW_LIMITER_ENABLE) {
+		ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"met-info [000] 0.0: met_bw_limiter_header: CLK,");
+		ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"ARBA,ARBB,ARBC,ARBD,ARBE,ARBF,ARBG,ARBH,BWCT0,BWCT1,BWCT2,BWCT3,BWCT4,BWST0,BWST1,BWCT0_2ND,BWCT1_2ND,BWST_2ND\n");
+	}
+
+	/* DRAM DVFS header */
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"met-info [000] 0.0: DRAM_DVFS_header: datarate(MHz)\n");
+
+	/*PDIR met_dramc_header*/
+	if (dramc_pdir_enable == 1 && DRAMC_VER >= 2 ) {
+		ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"met-info [000] 0.0: met_dramc_header: ");
+		for (i = 0; i < dram_chann_num; i++) {
+			if (i != 0)
+				ret += snprintf(buf + ret, PAGE_SIZE - ret,
+						",");
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, "freerun_26m_%d,", i);
+			ret += snprintf(buf + ret, PAGE_SIZE - ret,
+					"rk0_pre_sb_%d,rk0_pre_pd_%d,rk0_act_sb_%d,rk0_act_pd_%d,", i, i, i, i);
+			ret += snprintf(buf + ret, PAGE_SIZE - ret,
+					"rk1_pre_sb_%d,rk1_pre_pd_%d,rk1_act_sb_%d,rk1_act_pd_%d,", i, i, i, i);
+			ret += snprintf(buf + ret, PAGE_SIZE - ret,
+					"rk2_pre_sb_%d,rk2_pre_pd_%d,rk2_act_sb_%d,rk2_act_pd_%d", i, i, i, i);
+		}
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "\n");
+	}
+
+	/* DRS header */
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"met-info [000] 0.0: emi_drs_header: ch0_RANK1_GP(%%),ch0_RANK1_SF(%%),ch0_ALL_SF(%%),ch1_RANK1_GP(%%),ch1_RANK1_SF(%%),ch1_ALL_SF(%%)\n");
+#endif
+
+	return ret;
+}
+
+
+#if defined(CONFIG_MTK_TINYSYS_SSPM_SUPPORT) && defined(ONDIEMET_SUPPORT)
+static int ondiemet_emi_print_header(char *buf, int len)
+{
+	emi_use_ondiemet = 1;
+	/* return emi_print_ondiemet_header(buf, len); */
+	return emi_print_header(buf, len);
+}
+
+
+static void MET_BM_IPI_REGISTER_CB(void)
+{
+	int ret, i;
+	unsigned int rdata;
+	unsigned int ipi_buf[4];
+
+	for (i = 0; i < 4; i++)
+		ipi_buf[i] = 0;
+
+	if (sspm_buf_available == 1) {
+		ipi_buf[0] = MET_MAIN_ID | (MID_EMI << MID_BIT_SHIFT) | MET_ARGU | SET_REGISTER_CB;
+		ret = sspm_ipi_send_sync(IPI_ID_MET, IPI_OPT_WAIT, (void *)ipi_buf, 0, &rdata, 1);
+	}
+}
+
+
+static void MET_BM_IPI_configs(void)
+{
+	int ret, i;
+	unsigned int rdata;
+	unsigned int ipi_buf[4];
+
+	for (i = 0; i < 4; i++)
+		ipi_buf[i] = 0;
+
+	if (sspm_buf_available == 1) {
+		ipi_buf[0] = MET_MAIN_ID | (MID_EMI << MID_BIT_SHIFT) | MET_ARGU | SET_EBM_CONFIGS1;
+		ipi_buf[1] = INTERNAL_MODE << 16;
+		ipi_buf[2] = EMI_VER_MAJOR << 24 | EMI_VER_MINOR << 16 | DRAMC_VER << 8 | 0;
+		ret = sspm_ipi_send_sync(IPI_ID_MET, IPI_OPT_WAIT, (void *)ipi_buf, 0, &rdata, 1);
+	}
+}
+
+
+static void ondiemet_emi_start(void)
+{
+	MET_BM_IPI_REGISTER_CB();
+	if (!emi_inited) {
+		if (MET_BM_Init() != 0) {
+			met_emi.mode = 0;
+			pr_notice("MET_BM_Init failed!!!\n");
+			return;
+		}
+		emi_inited = 1;
+	}
+	MET_BM_IPI_configs();
+
+	if (do_emi())
+		emi_init();
+
+	ondiemet_module[ONDIEMET_SSPM] |= ID_EMI;
+}
+
+
+static void ondiemet_emi_stop(void)
+{
+	if (!emi_inited)
+		return;
+
+	if (do_emi())
+		emi_uninit();
+}
+#endif
+
+
+struct metdevice met_emi = {
+	.name			= "emi",
+	.owner			= THIS_MODULE,
+	.type			= MET_TYPE_BUS,
+	.create_subfs		= met_emi_create,
+	.delete_subfs		= met_emi_delete,
+	.cpu_related		= 0,
+	.start			= met_emi_start,
+	.stop			= met_emi_stop,
+	.resume			= met_emi_resume,
+	.timed_polling		= met_emi_polling,
+	.print_help		= emi_print_help,
+	.print_header		= emi_print_header,
+#if defined(CONFIG_MTK_TINYSYS_SSPM_SUPPORT) && defined(ONDIEMET_SUPPORT)
+	.ondiemet_mode		= 1,
+	.ondiemet_start		= ondiemet_emi_start,
+	.ondiemet_stop		= ondiemet_emi_stop,
+	.ondiemet_print_help	= emi_print_help,
+	.ondiemet_print_header	= ondiemet_emi_print_header,
+#else
+	.ondiemet_mode		= 0,
+#endif
+};
diff --git a/src/devtools/met-driver/4.14/mt2731/emi/SEDA3/mtk_emi_bm.c b/src/devtools/met-driver/4.14/mt2731/emi/SEDA3/mtk_emi_bm.c
new file mode 100644
index 0000000..a77be27
--- /dev/null
+++ b/src/devtools/met-driver/4.14/mt2731/emi/SEDA3/mtk_emi_bm.c
@@ -0,0 +1,1067 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/io.h>
+#include <mt-plat/sync_write.h>
+#include <mt-plat/mtk_io.h>
+#include "mtk_typedefs.h"
+#include "plf_init.h"
+#include "mtk_emi_bm.h"
+#include "mtk_dramc_reg.h"
+#include "met_drv.h"
+#include "interface.h"
+
+#undef	DEBUG
+#undef	debug_reg
+#ifdef	debug_reg
+static inline unsigned int emi_readl(void __iomem *padr)
+{
+	unsigned int tmp;
+
+	tmp = readl(padr);
+	MET_TRACE("[MET_EMI] RD_Reg: %p: %08x\n", padr, tmp);
+	return tmp;
+}
+
+static inline void __emi_reg_sync_writel(unsigned int data, void __iomem *padr)
+{
+	unsigned int tmp;
+
+	mt_reg_sync_writel(data, padr);
+	tmp = readl(padr);
+	MET_TRACE("[MET_EMI] WR_Reg: %p: %08x, %08x\n", padr, data, tmp);
+}
+
+#define emi_reg_sync_writel(data, adr)  __emi_reg_sync_writel(data, IOMEM(adr))
+
+#else
+#define emi_readl               readl
+#define emi_reg_sync_writel     mt_reg_sync_writel
+#endif
+
+#define MASK_MASTER     0xFF
+#define MASK_TRANS_TYPE 0xFF
+
+static int dram_chann_num;
+static void __iomem *BaseAddrEMI;
+static void __iomem *BaseAddrCHN_EMI[2];
+
+static int dramc0_dcm_enable;
+static int dramc1_dcm_enable;
+
+#define CH0_MISC_CG_CTRL0 (((unsigned long) BaseAddrDDRPHY_AO[0]) + 0x284)
+#define CH1_MISC_CG_CTRL0 (((unsigned long) BaseAddrDDRPHY_AO[1]) + 0x284)
+const unsigned int emi_config[] = {
+	EMI_BMEN,
+	EMI_MSEL,
+	EMI_MSEL2,
+	EMI_MSEL3,
+	EMI_MSEL4,
+	EMI_MSEL5,
+	EMI_MSEL6,
+	EMI_MSEL7,
+	EMI_MSEL8,
+	EMI_MSEL9,
+	EMI_MSEL10,
+	EMI_BMID0,
+	EMI_BMID1,
+	EMI_BMID2,
+	EMI_BMID3,
+	EMI_BMID4,
+	EMI_BMID5,
+	EMI_BMID6,
+	EMI_BMID7,
+	EMI_BMID8,
+	EMI_BMID9,
+	EMI_BMID10,
+	EMI_BMEN1,
+	EMI_BMEN2,
+	EMI_BMRW0,
+	EMI_BMRW1
+};
+#define EMI_CONFIG_MX_NR (sizeof(emi_config)/sizeof(unsigned int))
+static unsigned int emi_config_val[EMI_CONFIG_MX_NR];
+
+/*
+ *   MET_REG_BSET/MET_REG_BCLR:
+ *   reading value before set and clear
+ */
+static inline void MET_REG_BSET(unsigned long reg, u32 shift)
+{
+	unsigned int read_val = 0;
+
+	read_val = emi_readl(IOMEM(reg));
+	emi_reg_sync_writel(read_val | (1 << shift), reg);
+}
+
+
+static inline void MET_REG_BCLR(unsigned long reg, u32 shift)
+{
+	unsigned int read_val = 0;
+
+	read_val = emi_readl(IOMEM(reg));
+	emi_reg_sync_writel(read_val & (~((1 << shift) & 0xFFFFFFFF)), reg);
+}
+
+
+int MET_BM_Init(void)
+{
+	int i;
+	int idx;
+
+	/*emi*/
+	if (!mt_cen_emi_base_get_symbol) {
+		METERROR("[%d]mt_cen_emi_base_get_symbol = NULL\n", __LINE__);
+		PR_BOOTMSG_ONCE("[%d]mt_cen_emi_base_get_symbol = NULL\n", __LINE__);
+		return -1;
+	}
+
+	BaseAddrEMI = mt_cen_emi_base_get_symbol();
+	if (BaseAddrEMI == 0) {
+		METERROR("BaseAddrEMI = 0\n");
+		PR_BOOTMSG_ONCE("BaseAddrEMI = 0\n");
+		return -1;
+	}
+
+	METINFO("MET EMI: map emi to %p\n", BaseAddrEMI);
+	PR_BOOTMSG("MET EMI: map emi to %p\n", BaseAddrEMI);
+
+	/* get dram channel number */
+	dram_chann_num = MET_EMI_GetDramChannNum();
+	METINFO("[%s][%d]dram_chann_num = %d\n", __func__, __LINE__, dram_chann_num);
+
+	if (dram_chann_num > MAX_DRAMC_CHANN) {
+		METERROR("dram_chann_num %d > %d\n", dram_chann_num, MAX_DRAMC_CHANN);
+		PR_BOOTMSG("dram_chann_num %d > %d\n", dram_chann_num, MAX_DRAMC_CHANN);
+		return -1;
+	}
+
+	if (!mt_dramc_nao_chn_base_get_symbol) {
+		METERROR("mt_dramc_nao_cha_base_get = NULL\n");
+		PR_BOOTMSG_ONCE("mt_dramc_nao_cha_base_get = NULL\n");
+		return -1;
+	}
+
+	for (i = 0; i < dram_chann_num; i++) {
+		BaseAddrDRAMC[i] = mt_dramc_nao_chn_base_get_symbol(i);
+		if (BaseAddrDRAMC[i] == 0) {
+			METERROR("BaseAddrDRAMC%d = 0\n", i);
+			PR_BOOTMSG_ONCE("BaseAddrDRAMC%d = 0\n", i);
+			return -1;
+		}
+
+		METINFO("MET EMI: map nao dramc%c to %p\n",'A'+i, BaseAddrDRAMC[i]);
+		PR_BOOTMSG("MET EMI: map nao dramc%c to %p\n", 'A'+i, BaseAddrDRAMC[i]);
+	}
+
+	/*dram DRAMC_DTS_DDRPHY_AO*/
+	/* get DRS base address */
+	if (!mt_ddrphy_chn_base_get_symbol) {
+		METERROR("mt_ddrphy_chn_base_get = NULL\n");
+		PR_BOOTMSG_ONCE("mt_ddrphy_chn_base_get = NULL\n");
+		return -1;
+	}
+
+	if (!mt_chn_emi_base_get_symbol) {
+		METERROR("mt_chn_emi_base_get_symbol = NULL\n");
+		PR_BOOTMSG_ONCE("mt_chn_emi_base_get_symbol = NULL\n");
+		return -1;
+	}
+
+	for (i = 1; i <= dram_chann_num && i < 3; i++) {
+		idx = i - 1;
+		BaseAddrDDRPHY_AO[idx] = mt_ddrphy_chn_base_get_symbol(idx);
+		if (BaseAddrDDRPHY_AO[idx] == 0) {
+			METERROR("BaseAddrDDRPHY_AO[%d] = 0\n", idx);
+			PR_BOOTMSG_ONCE("BaseAddrDDRPHY_AO[%d] = 0\n", idx);
+			return -1;
+		}
+
+		METINFO("MET EMI: map ddrphy%d AO to %p\n", idx, BaseAddrDDRPHY_AO[idx]);
+		PR_BOOTMSG("MET EMI: map ddrphy%d AO to %p\n", idx, BaseAddrDDRPHY_AO[idx]);
+
+		BaseAddrCHN_EMI[idx] = mt_chn_emi_base_get_symbol(idx);
+		if (BaseAddrCHN_EMI[idx] == 0) {
+			METERROR("BaseAddrCHN_EMI[%d] = 0\n", idx);
+			PR_BOOTMSG_ONCE("BaseAddrCHN_EMI[%d] = 0\n", idx);
+			return -1;
+		}
+
+		METINFO("MET EMI: map BaseAddrCHN_EMI[%d] to %p\n", idx, BaseAddrCHN_EMI[idx]);
+		PR_BOOTMSG("MET EMI: map BaseAddrCHN_EMI[%d] to %p\n", idx, BaseAddrCHN_EMI[idx]);
+	}
+
+	/*dram DRAMC_DTS_DRAMC0_AO*/
+	if (!mt_dramc_chn_base_get_symbol) {
+		METERROR("mt_dramc_chn_base_get = NULL\n");
+		PR_BOOTMSG_ONCE("mt_dramc_chn_base_get = NULL\n");
+		return -1;
+	}
+
+	BaseAddrDRAMC0_AO = mt_dramc_chn_base_get_symbol(0);
+	if (BaseAddrDRAMC0_AO == 0) {
+		METERROR("BaseAddrDRAMC0_AO = 0\n");
+		PR_BOOTMSG_ONCE("BaseAddrDRAMC0_AO = 0\n");
+		return -1;
+	}
+
+	METINFO("MET EMI: map AO dramcA to %p\n", BaseAddrDRAMC0_AO);
+	PR_BOOTMSG("MET EMI: map AO dramcA to %p\n", BaseAddrDRAMC0_AO);
+
+	return 0;
+}
+
+
+void MET_BM_DeInit(void)
+{
+}
+
+
+void MET_BM_SaveCfg(void)
+{
+	int i;
+
+	for (i = 0; i < EMI_CONFIG_MX_NR; i++)
+		emi_config_val[i] = emi_readl(IOMEM(ADDR_EMI + emi_config[i]));
+}
+
+
+void MET_BM_RestoreCfg(void)
+{
+	int i;
+
+	for (i = 0; i < EMI_CONFIG_MX_NR; i++)
+		emi_reg_sync_writel(emi_config_val[i], ADDR_EMI + emi_config[i]);
+}
+
+
+void MET_BM_Clear_Start(void)
+{
+	/* Force EMI idle low */
+	MET_REG_BSET(ADDR_EMI + EMI_BMEN, BUS_MON_IDLE_SHIFT);
+
+	/* Disable dramc dcm */
+	switch (dram_chann_num) {
+	case 1:
+		MET_REG_BSET(CH0_MISC_CG_CTRL0, DRAMC_CG_SHIFT);
+		break;
+	case 2:
+		MET_REG_BSET(CH0_MISC_CG_CTRL0, DRAMC_CG_SHIFT);
+		MET_REG_BSET(CH1_MISC_CG_CTRL0, DRAMC_CG_SHIFT);
+		break;
+	default:
+		METERROR("Error: dram_chann_num = %d\n", dram_chann_num);
+	}
+
+	/* Disable EBM */
+	MET_REG_BCLR(ADDR_EMI + EMI_BMEN, BUS_MON_EN_SHIFT);
+
+	/* Enable EBM */
+	MET_REG_BSET(ADDR_EMI + EMI_BMEN, BUS_MON_EN_SHIFT);
+
+	/* Enable EMI dcm */
+	MET_REG_BCLR(ADDR_EMI + EMI_BMEN, BUS_MON_IDLE_SHIFT);
+
+	/* restore dramc dcm */
+	switch (dram_chann_num) {
+	case 1:
+		if (dramc0_dcm_enable)
+			MET_REG_BCLR(CH0_MISC_CG_CTRL0, DRAMC_CG_SHIFT);
+		else
+			MET_REG_BSET(CH0_MISC_CG_CTRL0, DRAMC_CG_SHIFT);
+		break;
+	case 2:
+		if (dramc0_dcm_enable)
+			MET_REG_BCLR(CH0_MISC_CG_CTRL0, DRAMC_CG_SHIFT);
+		else
+			MET_REG_BSET(CH0_MISC_CG_CTRL0, DRAMC_CG_SHIFT);
+
+		if (dramc1_dcm_enable)
+			MET_REG_BCLR(CH1_MISC_CG_CTRL0, DRAMC_CG_SHIFT);
+		else
+			MET_REG_BSET(CH1_MISC_CG_CTRL0, DRAMC_CG_SHIFT);
+		break;
+	default:
+		METERROR("Error: dram_chann_num = %d\n", dram_chann_num);
+	}
+}
+
+
+void MET_BM_Enable(const unsigned int enable)
+{
+	unsigned long int value_check;
+	int i = 0;
+
+	while (i < 100) {
+		/* Force EMI idle low */
+		MET_REG_BSET(ADDR_EMI + EMI_BMEN, BUS_MON_IDLE_SHIFT);
+
+		/* disable dramc dcm */
+		switch (dram_chann_num) {
+		case 1:
+			MET_REG_BSET(CH0_MISC_CG_CTRL0, DRAMC_CG_SHIFT);
+			break;
+		case 2:
+			MET_REG_BSET(CH0_MISC_CG_CTRL0, DRAMC_CG_SHIFT);
+			MET_REG_BSET(CH1_MISC_CG_CTRL0, DRAMC_CG_SHIFT);
+			break;
+		default:
+			METERROR("Error: dram_chann_num = %d\n", dram_chann_num);
+		}
+
+		if (enable == 0)
+			/* Disable EBM */
+			MET_REG_BCLR(ADDR_EMI + EMI_BMEN, BUS_MON_EN_SHIFT);
+		else
+			/* Enable EBM */
+			MET_REG_BSET(ADDR_EMI + EMI_BMEN, BUS_MON_EN_SHIFT);
+
+		/* Enable EMI dcm */
+		MET_REG_BCLR(ADDR_EMI + EMI_BMEN, BUS_MON_IDLE_SHIFT);
+
+		/* restore dramc dcm */
+		switch (dram_chann_num) {
+		case 1:
+			if (dramc0_dcm_enable)
+				MET_REG_BCLR(CH0_MISC_CG_CTRL0, DRAMC_CG_SHIFT);
+			else
+				MET_REG_BSET(CH0_MISC_CG_CTRL0, DRAMC_CG_SHIFT);
+			break;
+		case 2:
+			if (dramc0_dcm_enable)
+				MET_REG_BCLR(CH0_MISC_CG_CTRL0, DRAMC_CG_SHIFT);
+			else
+				MET_REG_BSET(CH0_MISC_CG_CTRL0, DRAMC_CG_SHIFT);
+
+			if (dramc1_dcm_enable)
+				MET_REG_BCLR(CH1_MISC_CG_CTRL0, DRAMC_CG_SHIFT);
+			else
+				MET_REG_BSET(CH1_MISC_CG_CTRL0, DRAMC_CG_SHIFT);
+			break;
+		default:
+			METERROR("Error: dram_chann_num = %d\n", dram_chann_num);
+		}
+
+		value_check = emi_readl(IOMEM(ADDR_EMI + EMI_BMEN));
+
+		if (enable == 0) {
+			/* EN == 0, IDLE == 0 when EMI RESET */
+			if (!test_bit(BUS_MON_EN_SHIFT, &value_check)
+			    && !test_bit(BUS_MON_IDLE_SHIFT, &value_check)) {
+				break;
+			}
+		} else {
+			/* EN == 1, IDLE == 0 when EMI START */
+			if (test_bit(BUS_MON_EN_SHIFT, &value_check)
+			    && !test_bit(BUS_MON_IDLE_SHIFT, &value_check)) {
+				break;
+			}
+		}
+		i++;
+	}
+
+	/*MET_TRACE("[MET_BM_ENABLE] value_check: %lx, enable = %d\n", value_check, enable); */
+
+}
+
+
+#if 0
+void BM_Disable(void)
+{
+	const unsigned int value = emi_readl(IOMEM(ADDR_EMI + EMI_BMEN));
+
+	emi_reg_sync_writel(value & (~BUS_MON_EN), ADDR_EMI + EMI_BMEN);
+}
+#endif
+
+
+void MET_BM_Pause(void)
+{
+	const unsigned int value = emi_readl(IOMEM(ADDR_EMI + EMI_BMEN));
+
+	emi_reg_sync_writel(value | (1 << BUS_MON_PAUSE_SHIFT), ADDR_EMI + EMI_BMEN);
+}
+
+
+void MET_BM_Continue(void)
+{
+	const unsigned int value = emi_readl(IOMEM(ADDR_EMI + EMI_BMEN));
+
+	emi_reg_sync_writel(value & (~(1 << BUS_MON_PAUSE_SHIFT)), ADDR_EMI + EMI_BMEN);
+}
+
+
+unsigned int MET_BM_IsOverrun(void)
+{
+	/*
+	 * return 0 if EMI_BCNT(bus cycle counts) or
+	 * EMI_WACT(total word counts) is overrun,
+	 * otherwise return an !0 value
+	 */
+	const unsigned int value = emi_readl(IOMEM(ADDR_EMI + EMI_BMEN));
+
+	return (value & (1 << BC_OVERRUN_SHIFT));
+}
+
+
+unsigned int MET_BM_GetReadWriteType(void)
+{
+	const unsigned int value = emi_readl(IOMEM(ADDR_EMI + EMI_BMEN));
+
+	return ((value & 0xFFFFFFCF) >> 4);
+}
+
+
+void MET_BM_SetReadWriteType(const unsigned int ReadWriteType)
+{
+	const unsigned int value = emi_readl(IOMEM(ADDR_EMI + EMI_BMEN));
+
+	/*
+	 * ReadWriteType: 00/11 --> both R/W
+	 *                   01 --> only R
+	 *                   10 --> only W
+	 */
+	emi_reg_sync_writel((value & 0xFFFFFFCF) | (ReadWriteType << 4), ADDR_EMI + EMI_BMEN);
+}
+
+
+int MET_BM_GetBusCycCount(void)
+{
+	return MET_BM_IsOverrun() ? BM_ERR_OVERRUN : emi_readl(IOMEM(ADDR_EMI + EMI_BCNT));	/*Bus cycle counter */
+}
+
+
+unsigned int MET_BM_GetTransAllCount(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI + EMI_TACT));
+}
+
+
+int MET_BM_GetTransCount(const unsigned int counter_num)
+{
+	unsigned int iCount;
+
+	switch (counter_num) {
+	case 1:
+		iCount = emi_readl(IOMEM(ADDR_EMI + EMI_TSCT));
+		break;
+
+	case 2:
+		iCount = emi_readl(IOMEM(ADDR_EMI + EMI_TSCT2));
+		break;
+
+	case 3:
+		iCount = emi_readl(IOMEM(ADDR_EMI + EMI_TSCT3));
+		break;
+
+	default:
+		return BM_ERR_WRONG_REQ;
+	}
+
+	return iCount;
+}
+
+
+int MET_BM_GetWordAllCount(void)
+{
+	return MET_BM_IsOverrun() ? BM_ERR_OVERRUN : emi_readl(IOMEM(ADDR_EMI + EMI_WACT));
+}
+
+
+int MET_BM_GetWordCount(const unsigned int counter_num)
+{
+	unsigned int iCount;
+
+	switch (counter_num) {
+	case 1:
+		iCount = emi_readl(IOMEM(ADDR_EMI + EMI_WSCT));
+		break;
+
+	case 2:
+		iCount = emi_readl(IOMEM(ADDR_EMI + EMI_WSCT2));
+		break;
+
+	case 3:
+		iCount = emi_readl(IOMEM(ADDR_EMI + EMI_WSCT3));
+		break;
+
+	case 4:
+		iCount = emi_readl(IOMEM(ADDR_EMI + EMI_WSCT4));
+		break;
+
+	default:
+		return BM_ERR_WRONG_REQ;
+	}
+
+	return iCount;
+}
+
+
+unsigned int MET_BM_GetBandwidthWordCount(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI + EMI_BACT));	/*Bandwidth counter for access */
+}
+
+
+unsigned int MET_BM_GetOverheadWordCount(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI + EMI_BSCT));	/*Overhead counter */
+}
+
+
+int MET_BM_GetTransTypeCount(const unsigned int counter_num)
+{
+	return (counter_num < 1 || counter_num > BM_COUNTER_MAX)
+	    ? BM_ERR_WRONG_REQ : emi_readl(IOMEM(ADDR_EMI + EMI_TTYPE1 + (counter_num - 1) * 8));
+}
+
+
+int MET_BM_GetMDCT(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI + EMI_MDCT));
+}
+
+
+int MET_BM_GetMDCT_2(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI + EMI_MDCT_2ND));
+}
+
+
+int MET_BM_SetMDCT_MDMCU(unsigned int mdmcu_rd_buf)
+{
+	unsigned int value_origin;
+
+	value_origin = emi_readl(IOMEM(ADDR_EMI + EMI_MDCT_2ND));
+	MET_TRACE("[MET_BM_SetMDCT_MDMCU] value_origin: %x\n", value_origin);
+
+	value_origin = value_origin & ~(0x7);
+	value_origin = value_origin | ((mdmcu_rd_buf) & 0x7);
+
+	emi_reg_sync_writel(value_origin, ADDR_EMI + EMI_MDCT_2ND);
+
+	return BM_REQ_OK;
+}
+
+
+int MET_BM_GetMonitorCounter(const unsigned int counter_num,
+			     unsigned int *master, unsigned int *trans_type)
+{
+	unsigned int value, addr;
+
+	if (counter_num < 1 || counter_num > BM_COUNTER_MAX)
+		return BM_ERR_WRONG_REQ;
+
+
+	if (counter_num == 1) {
+		addr = EMI_BMEN;
+		value = emi_readl(IOMEM(ADDR_EMI + addr));
+		*master = (value >> 16) & MASK_MASTER;
+		*trans_type = (value >> 24) & MASK_TRANS_TYPE;
+	} else {
+		addr = (counter_num <= 3) ? EMI_MSEL : (EMI_MSEL2 + (counter_num / 2 - 2) * 8);
+		value = emi_readl(IOMEM(ADDR_EMI + addr)) >> ((counter_num % 2) * 16);
+		*master = value & MASK_MASTER;
+		*trans_type = (value >> 8) & MASK_TRANS_TYPE;
+	}
+
+	return BM_REQ_OK;
+}
+
+
+int MET_BM_SetMonitorCounter(const unsigned int counter_num,
+			     const unsigned int master, const unsigned int trans_type)
+{
+	unsigned int value, addr;
+	const unsigned int iMask = (MASK_TRANS_TYPE << 8) | MASK_MASTER;
+
+	if (counter_num < 1 || counter_num > BM_COUNTER_MAX)
+		return BM_ERR_WRONG_REQ;
+
+
+	if (counter_num == 1) {
+		addr = EMI_BMEN;
+		value = (emi_readl(IOMEM(ADDR_EMI + addr)) & ~(iMask << 16)) |
+		    ((trans_type & MASK_TRANS_TYPE) << 24) | ((master & MASK_MASTER) << 16);
+	} else {
+		addr = (counter_num <= 3) ? EMI_MSEL : (EMI_MSEL2 + (counter_num / 2 - 2) * 8);
+
+		/* clear master and transaction type fields */
+		value = emi_readl(IOMEM(ADDR_EMI + addr)) & ~(iMask << ((counter_num % 2) * 16));
+
+		/* set master and transaction type fields */
+		value |= (((trans_type & MASK_TRANS_TYPE) << 8) |
+			  (master & MASK_MASTER)) << ((counter_num % 2) * 16);
+	}
+
+	emi_reg_sync_writel(value, ADDR_EMI + addr);
+
+	return BM_REQ_OK;
+}
+
+
+int MET_BM_SetTtypeCounterRW(unsigned int bmrw0_val, unsigned int bmrw1_val)
+{
+
+	unsigned int value_origin;
+
+	value_origin = emi_readl(IOMEM(ADDR_EMI + EMI_BMRW0));
+	MET_TRACE("[MET_EMI_settype1] value_origin: %x\n", value_origin);
+	if (value_origin != bmrw0_val) {
+		emi_reg_sync_writel(bmrw0_val, ADDR_EMI + EMI_BMRW0);
+		MET_TRACE("[MET_EMI_settype1] bmrw0_val: %x, value_origin: %x\n", bmrw0_val,
+			   value_origin);
+	}
+
+
+	value_origin = emi_readl(IOMEM(ADDR_EMI + EMI_BMRW1));
+	MET_TRACE("[MET_EMI_settype2] value_origin: %x\n", value_origin);
+	if (value_origin != bmrw1_val) {
+		emi_reg_sync_writel(bmrw1_val, ADDR_EMI + EMI_BMRW1);
+		MET_TRACE("[MET_EMI_settype2] bmrw0_val: %x, value_origin: %x\n", bmrw1_val,
+			   value_origin);
+
+	}
+	return BM_REQ_OK;
+}
+
+
+int MET_BM_Set_WsctTsct_id_sel(unsigned int counter_num, unsigned int enable)
+{
+	unsigned int value;
+
+	if (counter_num > 3)
+		return BM_ERR_WRONG_REQ;
+
+	value =
+	    ((emi_readl(IOMEM(ADDR_EMI + EMI_BMEN2)) & (~(1 << (28 + counter_num)))) |
+	     (enable << (28 + counter_num)));
+	emi_reg_sync_writel(value, ADDR_EMI + EMI_BMEN2);
+
+	return BM_REQ_OK;
+}
+
+
+int MET_BM_SetMaster(const unsigned int counter_num, const unsigned int master)
+{
+	unsigned int value, addr;
+	const unsigned int iMask = 0x7F;
+
+	if (counter_num < 1 || counter_num > BM_COUNTER_MAX)
+		return BM_ERR_WRONG_REQ;
+
+
+	if (counter_num == 1) {
+		addr = EMI_BMEN;
+		value =
+		    (emi_readl(IOMEM(ADDR_EMI + addr)) & ~(iMask << 16)) | ((master & iMask) << 16);
+	} else {
+		addr = (counter_num <= 3) ? EMI_MSEL : (EMI_MSEL2 + (counter_num / 2 - 2) * 8);
+
+		/* clear master and transaction type fields */
+		value = emi_readl(IOMEM(ADDR_EMI + addr)) & ~(iMask << ((counter_num % 2) * 16));
+
+		/* set master and transaction type fields */
+		value |= ((master & iMask) << ((counter_num % 2) * 16));
+	}
+
+	emi_reg_sync_writel(value, ADDR_EMI + addr);
+
+	return BM_REQ_OK;
+}
+
+
+int MET_BM_SetbusID_En(const unsigned int counter_num,
+		       const unsigned int enable)
+{
+	unsigned int value;
+
+	if ((counter_num < 1 || counter_num > BM_COUNTER_MAX) || (enable > 1))
+		return BM_ERR_WRONG_REQ;
+
+	if (enable == 0) {
+		/* clear  EMI ID selection Enabling   SEL_ID_EN */
+		value = (emi_readl(IOMEM(ADDR_EMI + EMI_BMEN2))
+			 & ~(1 << (counter_num - 1)));
+	} else {
+		/* enable  EMI ID selection Enabling   SEL_ID_EN */
+		value = (emi_readl(IOMEM(ADDR_EMI + EMI_BMEN2))
+			 | (1 << (counter_num - 1)));
+	}
+	emi_reg_sync_writel(value, ADDR_EMI + EMI_BMEN2);
+
+	return BM_REQ_OK;
+}
+
+
+int MET_BM_SetbusID(const unsigned int counter_num,
+		    const unsigned int id)
+{
+	unsigned int value, addr, shift_num;
+
+	if ((counter_num < 1 || counter_num > BM_COUNTER_MAX))
+		return BM_ERR_WRONG_REQ;
+
+	/* offset of EMI_BMIDx register */
+	addr = EMI_BMID0 + (counter_num - 1) / 2 * 4;
+	shift_num = ((counter_num - 1) % 2) * 16;
+	/* clear SELx_ID field */
+	value = emi_readl(IOMEM(ADDR_EMI + addr)) & ~(EMI_BMID_MASK << shift_num);
+
+	/* set SELx_ID field */
+	if (id <= 0xffff)       /*bigger then 0xff_ff : no select busid in master, reset busid as 0*/
+		value |= id << shift_num;
+
+	emi_reg_sync_writel(value, ADDR_EMI + addr);
+
+	return BM_REQ_OK;
+}
+
+
+int MET_BM_SetUltraHighFilter(const unsigned int counter_num, const unsigned int enable)
+{
+	unsigned int value;
+
+	if ((counter_num < 1 || counter_num > BM_COUNTER_MAX) || (enable > 1))
+		return BM_ERR_WRONG_REQ;
+
+
+	value = (emi_readl(IOMEM(ADDR_EMI + EMI_BMEN1))
+		 & ~(1 << (counter_num - 1)))
+		| (enable << (counter_num - 1));
+
+	emi_reg_sync_writel(value, ADDR_EMI + EMI_BMEN1);
+
+	return BM_REQ_OK;
+}
+
+
+int MET_BM_SetLatencyCounter(unsigned int enable)
+{
+	unsigned int value;
+
+	value = emi_readl(IOMEM(ADDR_EMI + EMI_BMEN2)) & ~(0x3 << 24);
+	/*
+	 * emi_ttype1 -- emi_ttype8 change as total latencies
+	 * for m0 -- m7,
+	 * and emi_ttype9 -- emi_ttype16 change as total transaction counts
+	 * for m0 -- m7
+	 */
+	if (enable == 1)
+		value |= (0x2 << 24);
+
+	emi_reg_sync_writel(value, ADDR_EMI + EMI_BMEN2);
+
+	return BM_REQ_OK;
+}
+
+
+int MET_BM_GetLatencyCycle(unsigned int *__restrict__ emi_value,
+				const unsigned int begin, const unsigned int end)
+{
+	int i, j = 0;
+
+	for (i = begin - 1; i < end; i++)
+		emi_value[j++] = emi_readl(IOMEM(ADDR_EMI + (EMI_TTYPE1 + i * 8)));
+
+	return j;
+}
+
+
+unsigned int MET_BM_GetEmiDcm(void)
+{
+	return (emi_readl(IOMEM(ADDR_EMI + EMI_CONM)) >> 24);
+}
+
+
+int MET_BM_SetEmiDcm(const unsigned int setting)
+{
+	unsigned int value;
+
+	value = emi_readl(IOMEM(ADDR_EMI + EMI_CONM));
+	emi_reg_sync_writel((value & 0x00FFFFFF) | (setting << 24), ADDR_EMI + EMI_CONM);
+
+	return BM_REQ_OK;
+}
+
+
+unsigned int MET_EMI_GetMDCT(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI + EMI_MDCT));
+}
+
+
+unsigned int MET_EMI_GetMDCT_2ND(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI + EMI_MDCT_2ND));
+}
+
+
+unsigned int MET_EMI_GetARBA(void)
+{
+	/* EMI_ARBA EMI Bandwidth Filter Control M0/1 */
+	return emi_readl(IOMEM(ADDR_EMI + EMI_ARBA));
+}
+
+
+unsigned int MET_EMI_GetARBB(void)
+{
+	/* need the ATBB for mt6739 */
+	return emi_readl(IOMEM(ADDR_EMI+EMI_ARBB));
+}
+
+
+unsigned int MET_EMI_GetARBC(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI + EMI_ARBC));
+}
+
+
+unsigned int MET_EMI_GetARBD(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI + EMI_ARBD));
+}
+
+
+unsigned int MET_EMI_GetARBE(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI + EMI_ARBE));
+}
+
+
+unsigned int MET_EMI_GetARBF(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI + EMI_ARBF));
+}
+
+
+unsigned int MET_EMI_GetARBG(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI + EMI_ARBG));
+}
+
+
+unsigned int MET_EMI_GetARBH(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI + EMI_ARBH));
+}
+
+
+/* Total BW status*/
+unsigned int MET_EMI_GetBWCT0(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI + EMI_BWCT0));
+}
+
+
+/* Total BW status*/
+unsigned int MET_EMI_GetBWCT1(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI + EMI_BWCT1));
+}
+
+
+/* Total BW status*/
+unsigned int MET_EMI_GetBWCT2(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI + EMI_BWCT2));
+}
+
+
+/* Total BW status*/
+unsigned int MET_EMI_GetBWCT3(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI + EMI_BWCT3));
+}
+
+
+/* Total BW status*/
+unsigned int MET_EMI_GetBWCT4(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI + EMI_BWCT4));
+}
+
+
+/* Total BW status*/
+unsigned int MET_EMI_GetBWST0(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI + EMI_BWST0));
+}
+
+/* Total BW status*/
+unsigned int MET_EMI_GetBWST1(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI + EMI_BWST1));
+}
+
+
+/* C+G BW status*/
+unsigned int MET_EMI_GetBWCT0_2ND(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI + EMI_BWCT0_2ND));
+}
+
+/* C+G BW status*/
+unsigned int MET_EMI_GetBWCT1_2ND(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI + EMI_BWCT1_2ND));
+}
+
+
+/* C+G BW status*/
+unsigned int MET_EMI_GetBWST_2ND(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI + EMI_BWST_2ND));
+}
+
+
+unsigned int MET_EMI_GetBMRW0(void)
+{
+	return readl(IOMEM(ADDR_EMI + EMI_BMRW0));
+}
+
+
+void emi_dump_reg(void)
+{
+	int i;
+
+	MET_TRACE("[emi_regdump]\n");
+	for (i = 0x400; i < 0x500; i = i + 16)
+		MET_TRACE("%4x__ %8x %8x %8x %8x\n", i, readl(IOMEM(ADDR_EMI + i)),
+			   readl(IOMEM(ADDR_EMI + i + 4)), readl(IOMEM(ADDR_EMI + i + 8)),
+			   readl(IOMEM(ADDR_EMI + i + 12)));
+}
+
+
+unsigned int MET_EMI_GetDramChannNum(void)
+{
+	int num = -1;
+
+	if (BaseAddrEMI) {
+		num = emi_readl(IOMEM(ADDR_EMI + EMI_CONA));
+		num = ((num >> 8) & 0x0000003);
+	} else {
+		return 1;
+	}
+
+	if (num == M0_DOUBLE_HALF_BW_1CH)
+		return 1;
+	else if (num == M0_DOUBLE_HALF_BW_2CH)
+		return 2;
+	else if (num == M0_DOUBLE_HALF_BW_4CH)
+		return 4;
+	else                    /* default return single channel */
+		return 1;
+}
+
+
+unsigned int MET_EMI_GetDramRankNum(void)
+{
+	int dual_rank = 0;
+
+	if (BaseAddrEMI) {
+		dual_rank = emi_readl(IOMEM(ADDR_EMI + EMI_CONA));
+		dual_rank = ((dual_rank >> 17) & RANK_MASK);
+	} else {
+		return DUAL_RANK;
+	}
+
+	if (dual_rank == DISABLE_DUAL_RANK_MODE)
+		return ONE_RANK;
+	else			/* default return dual rank */
+		return DUAL_RANK;
+}
+
+
+/*
+ * Dual Channel:
+ * Enables two rank for CHB
+ * Quad Channel:
+ * Enables two rank  for CHC and CHD
+ * 0: Disable dual rank mode
+ * 1: Enable dual rank mode
+ */
+unsigned int MET_EMI_GetDramRankNum_CHN1(void)
+{
+	int dual_rank = 0;
+
+	if (BaseAddrEMI) {
+		dual_rank = emi_readl(IOMEM(ADDR_EMI + EMI_CONA));
+		dual_rank = ((dual_rank >> 16) & RANK_MASK);
+	} else {
+		return DUAL_RANK;
+	}
+
+	if (dual_rank == DISABLE_DUAL_RANK_MODE)
+		return ONE_RANK;
+	else			/* default return dual rank */
+		return DUAL_RANK;
+}
+
+
+void met_get_drs_registers(uint32_t *value)
+{
+	uint32_t tmp;
+
+	memset(value, 0, sizeof(uint32_t) * 6);
+
+	if (BaseAddrCHN_EMI[0] == NULL)
+		return;
+	if (dram_chann_num > 1 && BaseAddrCHN_EMI[1] == NULL)
+		return;
+
+	tmp = emi_readl(IOMEM(BaseAddrCHN_EMI[0] + CHN_EMI_DRS_ST2)) & ((1 << 23) - 1);
+	/* working unit:38.5ns, the counter is update every 16ms */
+	value[0] = (tmp * 3850) / 16000000;
+
+	tmp = emi_readl(IOMEM(BaseAddrCHN_EMI[0] + CHN_EMI_DRS_ST3)) & ((1 << 23) - 1);
+	value[1] = (tmp * 3850) / 16000000;
+
+	tmp = emi_readl(IOMEM(BaseAddrCHN_EMI[0] + CHN_EMI_DRS_ST4)) & ((1 << 23) - 1);
+	value[2] = (tmp * 3850) / 16000000;
+
+	if (dram_chann_num > 1) {
+		tmp = emi_readl(IOMEM(BaseAddrCHN_EMI[1] + CHN_EMI_DRS_ST2)) & ((1 << 23) - 1);
+		value[3] = (tmp * 3850) / 16000000;
+
+		tmp = emi_readl(IOMEM(BaseAddrCHN_EMI[1] + CHN_EMI_DRS_ST3)) & ((1 << 23) - 1);
+		value[4] = (tmp * 3850) / 16000000;
+
+		tmp = emi_readl(IOMEM(BaseAddrCHN_EMI[1] + CHN_EMI_DRS_ST4)) & ((1 << 23) - 1);
+		value[5] = (tmp * 3850) / 16000000;
+	}
+}
+
+
+void met_record_dramc_dcm_enable_flag(void)
+{
+	int reg_val;
+
+	switch (dram_chann_num) {
+	case 1:
+		reg_val = emi_readl(IOMEM(CH0_MISC_CG_CTRL0));
+		dramc0_dcm_enable = !((reg_val >> DRAMC_CG_SHIFT) & 0x1);
+		break;
+	case 2:
+		reg_val = emi_readl(IOMEM(CH0_MISC_CG_CTRL0));
+		dramc0_dcm_enable = !((reg_val >> DRAMC_CG_SHIFT) & 0x1);
+
+		reg_val = emi_readl(IOMEM(CH1_MISC_CG_CTRL0));
+		dramc1_dcm_enable = !((reg_val >> DRAMC_CG_SHIFT) & 0x1);
+		break;
+	default:
+		METERROR("Error: dram_chann_num = %d\n", dram_chann_num);
+	}
+}
diff --git a/src/devtools/met-driver/4.14/mt2731/emi/SEDA3/mtk_emi_bm.h b/src/devtools/met-driver/4.14/mt2731/emi/SEDA3/mtk_emi_bm.h
new file mode 100644
index 0000000..1a7a632
--- /dev/null
+++ b/src/devtools/met-driver/4.14/mt2731/emi/SEDA3/mtk_emi_bm.h
@@ -0,0 +1,349 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#ifndef __MT_MET_EMI_BM_H__
+#define __MT_MET_EMI_BM_H__
+
+#define EMI_VER_MAJOR  3
+#define EMI_VER_MINOR  0
+
+#define DEF_BM_RW_TYPE          (BM_BOTH_READ_WRITE)
+#define NTS                     2
+#define NWSCT                   4
+#define NLATENCY                8
+#define NTRANS                  8
+#define NALL                    (3 + 2 + 1)
+#define NTTYPE                  5
+#define NIDX_EMI                (NTS + NWSCT + NLATENCY + NTRANS + NALL + NTTYPE)
+
+#define NCNT                    9
+#define NCH                     2
+#define NIDX_DRAMC              (NCNT * NCH)
+#define NIDX                            (NIDX_EMI + NIDX_DRAMC)
+
+#define NCLK                    1
+#define NARB                    8
+#define NBW                     10
+#define NIDX_BL                 (NCLK + NARB + NBW)
+
+/* 1000 To Khz and 4x freq & 2x data rate for LPDDR4 */
+/* 1000 To Khz and 2x freq & 2x data rate for LPDDR3*/
+/* TBD: calculate emi clock rate from DRAM DATA RATE */
+
+/*dram baseclock/EMI clock  :	LP4=4	LP3=2	*/
+#define DRAM_EMI_BASECLOCK_RATE_LP4     4
+#define DRAM_EMI_BASECLOCK_RATE_LP3     2
+/*dram io width  :	LP4=x16		LP3=x32 */
+#define DRAM_IO_BUS_WIDTH_LP4           16
+#define DRAM_IO_BUS_WIDTH_LP3           32
+/*dram datarate  :	DDR=double */
+#define DRAM_DATARATE   2
+
+#define ADDR_EMI        ((unsigned long)BaseAddrEMI)
+
+static const char of_emi_desc[] = "mediatek,emi";
+static const char of_chn_emi_desc[] = "mediatek,chn_emi";
+static const char of_dramc_desc[] = "mediatek,dramc";
+
+#define BM_MASTER_M0            (0x01)
+#define BM_MASTER_M1            (0x02)
+#define BM_MASTER_M2            (0x04)
+#define BM_MASTER_M3            (0x08)
+#define BM_MASTER_M4            (0x10)
+#define BM_MASTER_M5            (0x20)
+#define BM_MASTER_M6            (0x40)
+#define BM_MASTER_M7            (0x80)
+#define BM_MASTER_ALL           (0xFF)
+
+
+enum BM_RW_Type {
+	BM_BOTH_READ_WRITE,
+	BM_READ_ONLY,
+	BM_WRITE_ONLY
+};
+
+enum {
+	BM_TRANS_TYPE_1BEAT = 0x0,
+	BM_TRANS_TYPE_2BEAT,
+	BM_TRANS_TYPE_3BEAT,
+	BM_TRANS_TYPE_4BEAT,
+	BM_TRANS_TYPE_5BEAT,
+	BM_TRANS_TYPE_6BEAT,
+	BM_TRANS_TYPE_7BEAT,
+	BM_TRANS_TYPE_8BEAT,
+	BM_TRANS_TYPE_9BEAT,
+	BM_TRANS_TYPE_10BEAT,
+	BM_TRANS_TYPE_11BEAT,
+	BM_TRANS_TYPE_12BEAT,
+	BM_TRANS_TYPE_13BEAT,
+	BM_TRANS_TYPE_14BEAT,
+	BM_TRANS_TYPE_15BEAT,
+	BM_TRANS_TYPE_16BEAT,
+	BM_TRANS_TYPE_1Byte = 0 << 4,
+	BM_TRANS_TYPE_2Byte = 1 << 4,
+	BM_TRANS_TYPE_4Byte = 2 << 4,
+	BM_TRANS_TYPE_8Byte = 3 << 4,
+	BM_TRANS_TYPE_16Byte = 4 << 4,
+	BM_TRANS_TYPE_32Byte = 5 << 4,
+	BM_TRANS_TYPE_BURST_WRAP = 0 << 7,
+	BM_TRANS_TYPE_BURST_INCR = 1 << 7
+};
+
+enum {
+	BM_TRANS_RW_DEFAULT = 0x0,
+	BM_TRANS_RW_READONLY,
+	BM_TRANS_RW_WRITEONLY,
+	BM_TRANS_RW_RWBOTH
+};
+
+
+/*coda busid 12bit, but HW support 16 bit*/
+#define EMI_BMID_MASK				(0xFFFF)
+#define BM_COUNTER_MAX				(21)
+
+/*
+*#define BUS_MON_EN		(0x00000001)
+*#define BUS_MON_PAUSE		(0x00000002)
+*#define BUS_MON_IDLE		(0x00000008)
+*#define BC_OVERRUN		(0x00000100)
+*/
+enum {
+	BUS_MON_EN_SHIFT = 0,
+	BUS_MON_PAUSE_SHIFT = 1,
+	BUS_MON_IDLE_SHIFT = 3,
+	BC_OVERRUN_SHIFT = 8,
+	DRAMC_CG_SHIFT = 9,
+};
+
+#define BM_REQ_OK				(0)
+#define BM_ERR_WRONG_REQ			(-1)
+#define BM_ERR_OVERRUN				(-2)
+
+#define BM_WSCT_TSCT_IDSEL_ENABLE		(0)
+#define BM_WSCT_TSCT_IDSEL_DISABLE		(-1)
+#define BM_TTYPE1_16_ENABLE			(0)
+#define BM_TTYPE1_16_DISABLE			(-1)
+#define BM_TTYPE17_21_ENABLE			(0)
+#define BM_TTYPE17_21_DISABLE			(-1)
+#define BM_BW_LIMITER_ENABLE			(0)
+#define BM_BW_LIMITER_DISABLE			(-1)
+
+#define M0_DOUBLE_HALF_BW_1CH	(0x0)
+#define M0_DOUBLE_HALF_BW_2CH	(0x1)
+#define M0_DOUBLE_HALF_BW_4CH	(0x2)
+
+/* EMI Rank configuration */
+enum {
+	DISABLE_DUAL_RANK_MODE = 0,
+	ENABLE_DUAL_RANK_MODE,
+};
+
+#define RANK_MASK 0x1
+#define ONE_RANK 1
+#define DUAL_RANK 2
+
+
+#if defined(CONFIG_MTK_TINYSYS_SSPM_SUPPORT) && defined(ONDIEMET_SUPPORT)
+/*ondiemet emi ipi command*/
+enum BM_EMI_IPI_Type {
+	SET_BASE_EMI = 0x0,
+	SET_BASE_DRAMC0,
+	SET_BASE_DRAMC1,
+	SET_BASE_DRAMC2,
+	SET_BASE_DRAMC3,
+	SET_BASE_DDRPHY0AO,
+	SET_BASE_DRAMC0_AO,
+	SET_EBM_CONFIGS1,
+	SET_EBM_CONFIGS2,
+	SET_REGISTER_CB,
+};
+#endif
+
+
+#define	EMI_OFF			0x0000
+#define EMI_CONA		(0x000-EMI_OFF)
+#define EMI_CONH		(0x038-EMI_OFF)
+#define EMI_CONH_2ND		(0x03C-EMI_OFF)
+#define EMI_CONM		(0x060-EMI_OFF)
+#define EMI_CONO		(0x070-EMI_OFF)
+
+#define EMI_MDCT		(0x078 - EMI_OFF)
+#define EMI_MDCT_2ND		(0x07C - EMI_OFF)
+
+#define EMI_ARBA		(0x100 - EMI_OFF)
+#define EMI_ARBB		(0x108 - EMI_OFF)
+#define EMI_ARBC		(0x110 - EMI_OFF)
+#define EMI_ARBD		(0x118 - EMI_OFF)
+#define EMI_ARBE		(0x120 - EMI_OFF)
+#define EMI_ARBF		(0x128 - EMI_OFF)
+#define EMI_ARBG		(0x130 - EMI_OFF)
+#define EMI_ARBG_2ND		(0x134 - EMI_OFF)
+#define EMI_ARBH		(0x138 - EMI_OFF)
+
+#define EMI_BMEN		(0x400 - EMI_OFF)
+#define EMI_BCNT		(0x408 - EMI_OFF)
+#define EMI_TACT		(0x410 - EMI_OFF)
+#define EMI_TSCT		(0x418 - EMI_OFF)
+#define EMI_WACT		(0x420 - EMI_OFF)
+#define EMI_WSCT		(0x428 - EMI_OFF)
+#define EMI_BACT		(0x430 - EMI_OFF)
+#define EMI_BSCT		(0x438 - EMI_OFF)
+
+#define EMI_MSEL		(0x440 - EMI_OFF)
+#define EMI_TSCT2		(0x448 - EMI_OFF)
+#define EMI_TSCT3		(0x450 - EMI_OFF)
+#define EMI_WSCT2		(0x458 - EMI_OFF)
+#define EMI_WSCT3		(0x460 - EMI_OFF)
+#define EMI_WSCT4		(0x464 - EMI_OFF)
+#define EMI_MSEL2		(0x468 - EMI_OFF)
+#define EMI_MSEL3		(0x470 - EMI_OFF)
+#define EMI_MSEL4		(0x478 - EMI_OFF)
+#define EMI_MSEL5		(0x480 - EMI_OFF)
+#define EMI_MSEL6		(0x488 - EMI_OFF)
+#define EMI_MSEL7		(0x490 - EMI_OFF)
+#define EMI_MSEL8		(0x498 - EMI_OFF)
+#define EMI_MSEL9		(0x4A0 - EMI_OFF)
+#define EMI_MSEL10		(0x4A8 - EMI_OFF)
+
+#define EMI_BMID0		(0x4B0 - EMI_OFF)
+#define EMI_BMID1		(0x4B4 - EMI_OFF)
+#define EMI_BMID2		(0x4B8 - EMI_OFF)
+#define EMI_BMID3		(0x4BC - EMI_OFF)
+#define EMI_BMID4		(0x4C0 - EMI_OFF)
+#define EMI_BMID5		(0x4C4 - EMI_OFF)
+#define EMI_BMID6		(0x4C8 - EMI_OFF)
+#define EMI_BMID7		(0x4CC - EMI_OFF)
+#define EMI_BMID8		(0x4D0 - EMI_OFF)
+#define EMI_BMID9		(0x4D4 - EMI_OFF)
+#define EMI_BMID10		(0x4D8 - EMI_OFF)
+
+#define EMI_BMEN1		(0x4E0 - EMI_OFF)
+#define EMI_BMEN2		(0x4E8 - EMI_OFF)
+#define EMI_BMRW0		(0x4F8 - EMI_OFF)
+#define EMI_BMRW1		(0x4FC - EMI_OFF)
+#define EMI_TTYPE1		(0x500 - EMI_OFF)
+#define EMI_TTYPE2		(0x508 - EMI_OFF)
+#define EMI_TTYPE3		(0x510 - EMI_OFF)
+#define EMI_TTYPE4		(0x518 - EMI_OFF)
+#define EMI_TTYPE5		(0x520 - EMI_OFF)
+#define EMI_TTYPE6		(0x528 - EMI_OFF)
+#define EMI_TTYPE7		(0x530 - EMI_OFF)
+#define EMI_TTYPE8		(0x538 - EMI_OFF)
+#define EMI_TTYPE9		(0x540 - EMI_OFF)
+#define EMI_TTYPE10		(0x548 - EMI_OFF)
+#define EMI_TTYPE11		(0x550 - EMI_OFF)
+#define EMI_TTYPE12		(0x558 - EMI_OFF)
+#define EMI_TTYPE13		(0x560 - EMI_OFF)
+#define EMI_TTYPE14		(0x568 - EMI_OFF)
+#define EMI_TTYPE15		(0x570 - EMI_OFF)
+#define EMI_TTYPE16		(0x578 - EMI_OFF)
+#define EMI_TTYPE17		(0x580 - EMI_OFF)
+#define EMI_TTYPE18		(0x588 - EMI_OFF)
+#define EMI_TTYPE19		(0x590 - EMI_OFF)
+#define EMI_TTYPE20		(0x598 - EMI_OFF)
+#define EMI_TTYPE21		(0x5A0 - EMI_OFF)
+
+#define EMI_BWCT0		(0x5B0 - EMI_OFF)
+#define EMI_BWCT1		(0x5B4 - EMI_OFF)
+#define EMI_BWCT2		(0x5B8 - EMI_OFF)
+#define EMI_BWCT3		(0x5BC - EMI_OFF)
+#define EMI_BWCT4		(0x5C0 - EMI_OFF)
+#define EMI_BWST0		(0x5C4 - EMI_OFF)
+#define EMI_BWST1		(0x5C8 - EMI_OFF)
+
+#define EMI_BWCT0_2ND		(0x6A0 - EMI_OFF)
+#define EMI_BWCT1_2ND		(0x6A4 - EMI_OFF)
+#define EMI_BWST_2ND		(0x6A8 - EMI_OFF)
+
+
+extern void emi_dump_reg(void);
+extern int MET_BM_Init(void);
+extern void MET_BM_DeInit(void);
+extern void MET_BM_SaveCfg(void);
+extern void MET_BM_RestoreCfg(void);
+extern void MET_BM_Enable(const unsigned int enable);
+extern void MET_BM_Pause(void);
+extern void MET_BM_Continue(void);
+extern unsigned int MET_BM_IsOverrun(void);
+extern unsigned int MET_BM_GetReadWriteType(void);
+extern void MET_BM_SetReadWriteType(const unsigned int ReadWriteType);
+extern int MET_BM_GetBusCycCount(void);
+extern unsigned int MET_BM_GetTransAllCount(void);
+extern int MET_BM_GetTransCount(const unsigned int counter_num);
+extern int MET_BM_GetWordAllCount(void);
+extern int MET_BM_GetWordCount(const unsigned int counter_num);
+extern unsigned int MET_BM_GetBandwidthWordCount(void);
+extern unsigned int MET_BM_GetOverheadWordCount(void);
+extern int MET_BM_GetTransTypeCount(const unsigned int counter_num);
+extern int MET_BM_GetMDCT(void);
+extern int MET_BM_GetMDCT_2(void);
+extern int MET_BM_GetMonitorCounter(const unsigned int counter_num,
+				    unsigned int *master, unsigned int *trans_type);
+extern int MET_BM_SetMDCT_MDMCU(unsigned int mdmcu_rd_buf);
+extern int MET_BM_SetMonitorCounter(const unsigned int counter_num,
+				    const unsigned int master, const unsigned int trans_type);
+extern int MET_BM_SetTtypeCounterRW(unsigned int bmrw0_val, unsigned int bmrw1_val);
+extern int MET_BM_Set_WsctTsct_id_sel(unsigned int counter_num, unsigned int enable);
+extern int MET_BM_SetMaster(const unsigned int counter_num, const unsigned int master);
+extern int MET_BM_SetbusID_En(const unsigned int counter_num,
+			      const unsigned int enable);
+extern int MET_BM_SetbusID(const unsigned int counter_num,
+			   const unsigned int id);
+extern int MET_BM_SetUltraHighFilter(const unsigned int counter_num, const unsigned int enable);
+extern int MET_BM_SetLatencyCounter(unsigned int enable);
+int MET_BM_GetLatencyCycle(unsigned int *__restrict__ emi_value,
+				const unsigned int begin, const unsigned int end);
+extern unsigned int MET_BM_GetEmiDcm(void);
+extern int MET_BM_SetEmiDcm(const unsigned int setting);
+extern void MET_BM_Clear_Start(void);
+extern unsigned int MET_EMI_GetDramRankNum(void);
+extern unsigned int MET_EMI_GetDramRankNum_CHN1(void);
+
+/* Config */
+unsigned int MET_EMI_GetARBA(void);
+unsigned int MET_EMI_GetARBB(void);
+unsigned int MET_EMI_GetARBC(void);
+unsigned int MET_EMI_GetARBD(void);
+unsigned int MET_EMI_GetARBE(void);
+unsigned int MET_EMI_GetARBF(void);
+unsigned int MET_EMI_GetARBG(void);
+unsigned int MET_EMI_GetARBH(void);
+
+/* Total BW status */
+extern unsigned int MET_EMI_GetBWCT0(void);
+extern unsigned int MET_EMI_GetBWCT1(void);
+extern unsigned int MET_EMI_GetBWCT2(void);
+extern unsigned int MET_EMI_GetBWCT3(void);
+extern unsigned int MET_EMI_GetBWCT4(void);
+extern unsigned int MET_EMI_GetBWST0(void);
+extern unsigned int MET_EMI_GetBWST1(void);
+/* C+G BW */
+extern unsigned int MET_EMI_GetBWCT0_2ND(void);
+extern unsigned int MET_EMI_GetBWCT1_2ND(void);
+extern unsigned int MET_EMI_GetBWST_2ND(void);
+
+unsigned int MET_EMI_GetBMRW0(void);
+unsigned int MET_EMI_GetDramChannNum(void);
+
+/* ondiemet*/
+void MET_BM_IPI_baseaddr(void);
+void met_emi_phyaddr_debug(void);
+
+extern void met_record_dramc_dcm_enable_flag(void);
+
+/* get DRS registers content */
+#define CHN_EMI_DRS_ST2 0x17C
+#define CHN_EMI_DRS_ST3 0x180
+#define CHN_EMI_DRS_ST4 0x184
+extern void met_get_drs_registers(uint32_t *value);
+
+#endif                          /* !__MT_MET_EMI_BM_H__ */
diff --git a/src/devtools/met-driver/4.14/mt2731/emi/SEDA3_5/met_emi.c b/src/devtools/met-driver/4.14/mt2731/emi/SEDA3_5/met_emi.c
new file mode 100644
index 0000000..b00dc8b
--- /dev/null
+++ b/src/devtools/met-driver/4.14/mt2731/emi/SEDA3_5/met_emi.c
@@ -0,0 +1,2887 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/device.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/string.h>
+
+#ifdef CONFIG_ARM
+//#include <asm/dma-mapping.h> /* arm_coherent_dma_ops */
+#endif /* CONFIG_ARM */
+
+#include <linux/dma-mapping.h>
+
+#define MET_USER_EVENT_SUPPORT
+#include "met_drv.h"
+#include "trace.h"
+
+#include "mtk_typedefs.h"
+#include "plf_init.h"
+#include "plf_trace.h"
+#include "mtk_emi_bm.h"
+#include "mtk_dramc_reg.h"
+#include "interface.h"
+
+#if defined(CONFIG_MTK_TINYSYS_SSPM_SUPPORT) && defined(ONDIEMET_SUPPORT)
+#include "sspm/ondiemet_sspm.h"
+#endif
+
+#define FILE_NODE_DBG
+
+/*======================================================================*/
+/*	Global variable definitions					*/
+/*======================================================================*/
+/*ondiemet emi sampling interval in us */
+int emi_tsct_enable;
+int emi_mdct_enable;
+int emi_TP_busfiltr_enable;
+
+int emi_use_ondiemet;
+int metemi_func_opt;
+
+int met_emi_regdump;
+/* Dynamic MonitorCounter selection !!!EXPERIMENT!!! */
+static int msel_enable;
+static unsigned int msel_group1 = BM_MASTER_ALL;
+static unsigned int msel_group2 = BM_MASTER_ALL;
+static unsigned int msel_group3 = BM_MASTER_ALL;
+
+/* CVS Added changeable buffer for testing */
+static int mdmcu_sel_enable;
+static unsigned int rd_mdmcu_rsv_num = 0x5;
+
+/* Global variables */
+static struct kobject *kobj_emi;
+static int rwtype = BM_BOTH_READ_WRITE;
+
+/* BW Limiter */
+/*#define CNT_COUNTDOWN	(1000-1)*/		/* 1000 * 1ms = 1sec */
+#define CNT_COUNTDOWN   (0)                     /* 1ms */
+static int countdown;
+static int bw_limiter_enable = BM_BW_LIMITER_ENABLE;
+
+/* TTYPE counter */
+static int ttype1_16_en = BM_TTYPE1_16_DISABLE;
+static int ttype17_21_en = BM_TTYPE17_21_DISABLE;
+
+static int dramc_pdir_enable;
+static int dram_chann_num = 1;
+
+enum SSPM_Mode {
+	CUSTOMER_MODE = 0x0,
+	UNDEFINE_MODE = 0x1,
+	INTERNAL_MODE = 0X2780
+};
+
+
+/*======================================================================*/
+/*	EMI Test Operations						*/
+/*======================================================================*/
+static int times;
+
+static ssize_t test_apmcu_store(struct kobject *kobj,
+				struct kobj_attribute *attr,
+				const char *buf,
+				size_t n)
+{
+	int i;
+	unsigned int    *src_addr_v = NULL;
+	dma_addr_t src_addr_p;
+#ifdef CONFIG_ARM
+	struct dma_map_ops *ops = (struct dma_map_ops *)symbol_get(arm_dma_ops);
+#endif /* CONFIG_ARM */
+
+	if ((n == 0) || (buf == NULL))
+		return -EINVAL;
+	if (kstrtoint(buf, 10, &times) != 0)
+		return -EINVAL;
+	if (times < 0)
+		return -EINVAL;
+
+	if (times > 5000)       /* Less than 20MB */
+		return -EINVAL;
+
+#ifdef CONFIG_ARM
+	if (ops && ops->alloc) {
+		(met_device.this_device)->coherent_dma_mask = DMA_BIT_MASK(32);
+		src_addr_v = ops->alloc(met_device.this_device,
+						PAGE_SIZE,
+						&src_addr_p,
+						GFP_KERNEL,
+						0);
+	}
+#endif /* CONFIG_ARM */
+
+#ifdef CONFIG_ARM64
+	/* dma_alloc */
+ 	src_addr_v = dma_alloc_coherent(met_device.this_device,
+ 					PAGE_SIZE,
+ 					&src_addr_p,
+ 					GFP_KERNEL);
+#endif /* CONFIG_ARM64 */
+
+	if (src_addr_v == NULL) {
+#ifdef CONFIG_MET_MODULE
+		met_tag_oneshot_real(0, "test_apmcu dma alloc fail", PAGE_SIZE);
+#else
+		met_tag_oneshot(0, "test_apmcu dma alloc fail", PAGE_SIZE);
+#endif
+		return -ENOMEM;
+	}
+	/* testing */
+	preempt_disable();
+#ifdef CONFIG_MET_MODULE
+	met_tag_start_real(0, "TEST_EMI_APMCU");
+#else
+	met_tag_start(0, "TEST_EMI_APMCU");
+#endif
+	for (i = 0; i < times; i++) {
+		memset(src_addr_v, 2 * i, PAGE_SIZE);
+#ifdef CONFIG_MET_MODULE
+		met_tag_oneshot_real(0, "TEST_EMI_APMCU", PAGE_SIZE);
+#else
+		met_tag_oneshot(0, "TEST_EMI_APMCU", PAGE_SIZE);
+#endif
+	}
+#ifdef CONFIG_MET_MODULE
+	met_tag_end_real(0, "TEST_EMI_APMCU");
+#else
+	met_tag_end(0, "TEST_EMI_APMCU");
+#endif
+	/* the following function has no defined if MET is built as module */
+	/* preempt_enable_no_resched(); */
+	/* use this one to replace it: see met_drv.h */
+	my_preempt_enable();
+
+#ifdef CONFIG_ARM
+	/* dma_free */
+	if (ops && ops->free) {
+		ops->free(met_device.this_device,
+				  PAGE_SIZE,
+				  src_addr_v,
+				  src_addr_p,
+			0);
+	}
+#endif /* CONFIG_ARM */
+
+#ifdef CONFIG_ARM64
+	/* dma_free */
+ 	if (src_addr_v != NULL)
+ 		dma_free_coherent(met_device.this_device,
+ 				  PAGE_SIZE,
+ 				  src_addr_v,
+				  src_addr_p);
+#endif /* CONFIG_ARM64 */
+
+	return n;
+}
+
+/*======================================================================*/
+/*	KOBJ Declarations						*/
+/*======================================================================*/
+DECLARE_KOBJ_ATTR_INT(emi_tsct_enable, emi_tsct_enable);
+DECLARE_KOBJ_ATTR_INT(emi_mdct_enable, emi_mdct_enable);
+DECLARE_KOBJ_ATTR_INT(emi_TP_busfiltr_enable, emi_TP_busfiltr_enable);
+DECLARE_KOBJ_ATTR_INT(metemi_func_opt, metemi_func_opt);
+DECLARE_KOBJ_ATTR_INT(emi_regdump, met_emi_regdump);
+DECLARE_KOBJ_ATTR_INT(msel_enable, msel_enable);
+DECLARE_KOBJ_ATTR_HEX_CHECK(msel_group1, msel_group1, msel_group1 > 0 && msel_group1 <= BM_MASTER_ALL);
+DECLARE_KOBJ_ATTR_HEX_CHECK(msel_group2, msel_group2, msel_group2 > 0 && msel_group2 <= BM_MASTER_ALL);
+DECLARE_KOBJ_ATTR_HEX_CHECK(msel_group3, msel_group3, msel_group3 > 0 && msel_group3 <= BM_MASTER_ALL);
+DECLARE_KOBJ_ATTR_INT(mdmcu_sel_enable, mdmcu_sel_enable);
+DECLARE_KOBJ_ATTR_INT(rd_mdmcu_rsv_num, rd_mdmcu_rsv_num);
+
+
+/* KOBJ: rwtype */
+DECLARE_KOBJ_ATTR_INT_CHECK(rwtype, rwtype, rwtype >= 0 && rwtype <= BM_WRITE_ONLY);
+
+static unsigned int get_emi_clock_rate(unsigned int dram_data_rate_MHz)
+{
+	/*
+	 *	the ddr type define :
+	 *	enum DDRTYPE {
+	 *	TYPE_LPDDR3 = 1,
+	 *	TYPE_LPDDR4,
+	 *	TYPE_LPDDR4X,
+	 *	TYPE_LPDDR2
+	 *	};
+	 */
+
+	unsigned int DRAM_TYPE;
+
+	if (get_ddr_type_symbol) {
+		DRAM_TYPE = get_ddr_type_symbol();
+
+		if ((DRAM_TYPE == 2) || (DRAM_TYPE == 3))
+			return dram_data_rate_MHz / DRAM_EMI_BASECLOCK_RATE_LP4 / DRAM_DATARATE;
+		else
+			return dram_data_rate_MHz / DRAM_EMI_BASECLOCK_RATE_LP3 / DRAM_DATARATE;
+	} else {
+		METERROR("[%s][%d]get_ddr_type_symbol = NULL , use the TYPE_LPDDR3 setting\n", __func__, __LINE__);
+		return dram_data_rate_MHz / DRAM_EMI_BASECLOCK_RATE_LP3 / DRAM_DATARATE;
+	}
+}
+
+/* KOBJ: emi_clock_rate */
+static ssize_t emi_clock_rate_show(struct kobject *kobj,
+				   struct kobj_attribute *attr,
+				   char *buf)
+{
+	unsigned int dram_data_rate_MHz;
+
+	if (get_dram_data_rate_symbol) {
+		dram_data_rate_MHz = get_dram_data_rate_symbol();
+	} else {
+		METERROR("get_dram_data_rate_symbol = NULL\n");
+		dram_data_rate_MHz = 0;
+	}
+
+	return snprintf(buf, PAGE_SIZE, "%d\n",
+			get_emi_clock_rate(dram_data_rate_MHz));
+}
+
+DECLARE_KOBJ_ATTR_RO(emi_clock_rate);
+
+/* KOBJ: ttype1_16_en */
+DECLARE_KOBJ_ATTR_STR_LIST_ITEM(
+	ttype1_16_en,
+	KOBJ_ITEM_LIST(
+		{ BM_TTYPE1_16_ENABLE,   "ENABLE" },
+		{ BM_TTYPE1_16_DISABLE,  "DISABLE" }
+		)
+	);
+DECLARE_KOBJ_ATTR_STR_LIST(ttype1_16_en, ttype1_16_en, ttype1_16_en);
+
+/* KOBJ: ttype17_21_en */
+DECLARE_KOBJ_ATTR_STR_LIST_ITEM(
+	ttype17_21_en,
+	KOBJ_ITEM_LIST(
+		{ BM_TTYPE17_21_ENABLE,  "ENABLE" },
+		{ BM_TTYPE17_21_DISABLE, "DISABLE" }
+		)
+	);
+DECLARE_KOBJ_ATTR_STR_LIST(ttype17_21_en, ttype17_21_en, ttype17_21_en);
+
+/* KOBJ: bw_limiter_enable */
+DECLARE_KOBJ_ATTR_STR_LIST_ITEM(
+	bw_limiter_enable,
+	KOBJ_ITEM_LIST(
+		{ BM_BW_LIMITER_ENABLE,  "ENABLE" },
+		{ BM_BW_LIMITER_DISABLE, "DISABLE" }
+		)
+	);
+
+DECLARE_KOBJ_ATTR_STR_LIST(bw_limiter_enable, bw_limiter_enable, bw_limiter_enable);
+
+/* KOBJ: ttype_master */
+DECLARE_KOBJ_ATTR_STR_LIST_ITEM(
+	ttype_master,
+	KOBJ_ITEM_LIST(
+		{ BM_MASTER_M0,  "M0" },
+		{ BM_MASTER_M1,  "M1" },
+		{ BM_MASTER_M2,  "M2" },
+		{ BM_MASTER_M3,  "M3" },
+		{ BM_MASTER_M4,  "M4" },
+		{ BM_MASTER_M5,  "M5" },
+		{ BM_MASTER_M6,  "M6" },
+		{ BM_MASTER_M7,  "M7" }
+		)
+	);
+
+
+/* KOBJ: ttypeX_nbeat, ttypeX_nbyte, ttypeX_burst */
+DECLARE_KOBJ_ATTR_INT_LIST_ITEM(
+	ttype_nbeat,
+	KOBJ_ITEM_LIST(
+		{ BM_TRANS_TYPE_1BEAT,   1 },
+		{ BM_TRANS_TYPE_2BEAT,   2 },
+		{ BM_TRANS_TYPE_3BEAT,   3 },
+		{ BM_TRANS_TYPE_4BEAT,   4 },
+		{ BM_TRANS_TYPE_5BEAT,   5 },
+		{ BM_TRANS_TYPE_6BEAT,   6 },
+		{ BM_TRANS_TYPE_7BEAT,   7 },
+		{ BM_TRANS_TYPE_8BEAT,   8 },
+		{ BM_TRANS_TYPE_9BEAT,   9 },
+		{ BM_TRANS_TYPE_10BEAT,  10 },
+		{ BM_TRANS_TYPE_11BEAT,  11 },
+		{ BM_TRANS_TYPE_12BEAT,  12 },
+		{ BM_TRANS_TYPE_13BEAT,  13 },
+		{ BM_TRANS_TYPE_14BEAT,  14 },
+		{ BM_TRANS_TYPE_15BEAT,  15 },
+		{ BM_TRANS_TYPE_16BEAT,  16 }
+		)
+	);
+DECLARE_KOBJ_ATTR_INT_LIST_ITEM(
+	ttype_nbyte,
+	KOBJ_ITEM_LIST(
+		{ BM_TRANS_TYPE_1Byte,   1 },
+		{ BM_TRANS_TYPE_2Byte,   2 },
+		{ BM_TRANS_TYPE_4Byte,   4 },
+		{ BM_TRANS_TYPE_8Byte,   8 },
+		{ BM_TRANS_TYPE_16Byte,  16 },
+		{ BM_TRANS_TYPE_32Byte,  32 }
+		)
+	);
+DECLARE_KOBJ_ATTR_STR_LIST_ITEM(
+	ttype_burst,
+	KOBJ_ITEM_LIST(
+		{ BM_TRANS_TYPE_BURST_INCR,      "INCR" },
+		{ BM_TRANS_TYPE_BURST_WRAP,      "WRAP" }
+		)
+	);
+
+DECLARE_KOBJ_ATTR_STR_LIST_ITEM(
+	ttype_rw,
+	KOBJ_ITEM_LIST(
+		{ BM_TRANS_RW_DEFAULT,   "DEFAULT" },
+		{ BM_TRANS_RW_READONLY,  "R" },
+		{ BM_TRANS_RW_WRITEONLY, "W" },
+		{ BM_TRANS_RW_RWBOTH,    "BOTH" }
+		)
+	);
+
+/* KOBJ: test_apmcu */
+DECLARE_KOBJ_ATTR_SHOW_INT(test_apmcu, times);
+/* please refer to session: "EMI Test Operations" for store operation */
+DECLARE_KOBJ_ATTR(test_apmcu);
+
+DECLARE_KOBJ_ATTR_INT(dramc_pdir_enable, dramc_pdir_enable);
+
+/*enable high priority filter*/
+static int high_priority_filter;
+DECLARE_KOBJ_ATTR_HEX(high_priority_filter, high_priority_filter);
+
+
+
+/**/
+static int ttype_master_val[21];
+static int ttype_busid_val[21];
+static int ttype_nbeat_val[21];
+static int ttype_nbyte_val[21];
+static int ttype_burst_val[21];
+static int ttype_rw_val[21];
+
+#define DECLARE_KOBJ_TTYPE_MASTER(nr) \
+	DECLARE_KOBJ_ATTR_STR_LIST(ttype ## nr ## _master, ttype_master_val[nr - 1], ttype_master)
+
+#define DECLARE_KOBJ_TTYPE_NBEAT(nr) \
+	DECLARE_KOBJ_ATTR_INT_LIST(ttype ## nr ## _nbeat, ttype_nbeat_val[nr - 1], ttype_nbeat)
+
+#define DECLARE_KOBJ_TTYPE_NBYTE(nr) \
+	DECLARE_KOBJ_ATTR_INT_LIST(ttype ## nr ## _nbyte, ttype_nbyte_val[nr - 1], ttype_nbyte)
+
+#define DECLARE_KOBJ_TTYPE_BURST(nr) \
+	DECLARE_KOBJ_ATTR_STR_LIST(ttype ## nr ## _burst, ttype_burst_val[nr - 1], ttype_burst)
+
+#define DECLARE_KOBJ_TTYPE_RW(nr) \
+	DECLARE_KOBJ_ATTR_STR_LIST(ttype ## nr ## _rw, ttype_rw_val[nr - 1], ttype_rw)
+
+#define DECLARE_KOBJ_TTYPE_BUSID_VAL(nr) \
+	DECLARE_KOBJ_ATTR_HEX(ttype ## nr ## _busid, ttype_busid_val[nr - 1])
+
+DECLARE_KOBJ_TTYPE_MASTER(1);
+DECLARE_KOBJ_TTYPE_NBEAT(1);
+DECLARE_KOBJ_TTYPE_NBYTE(1);
+DECLARE_KOBJ_TTYPE_BURST(1);
+DECLARE_KOBJ_TTYPE_RW(1);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(1);
+
+DECLARE_KOBJ_TTYPE_MASTER(2);
+DECLARE_KOBJ_TTYPE_NBEAT(2);
+DECLARE_KOBJ_TTYPE_NBYTE(2);
+DECLARE_KOBJ_TTYPE_BURST(2);
+DECLARE_KOBJ_TTYPE_RW(2);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(2);
+
+DECLARE_KOBJ_TTYPE_MASTER(3);
+DECLARE_KOBJ_TTYPE_NBEAT(3);
+DECLARE_KOBJ_TTYPE_NBYTE(3);
+DECLARE_KOBJ_TTYPE_BURST(3);
+DECLARE_KOBJ_TTYPE_RW(3);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(3);
+
+DECLARE_KOBJ_TTYPE_MASTER(4);
+DECLARE_KOBJ_TTYPE_NBEAT(4);
+DECLARE_KOBJ_TTYPE_NBYTE(4);
+DECLARE_KOBJ_TTYPE_BURST(4);
+DECLARE_KOBJ_TTYPE_RW(4);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(4);
+
+DECLARE_KOBJ_TTYPE_MASTER(5);
+DECLARE_KOBJ_TTYPE_NBEAT(5);
+DECLARE_KOBJ_TTYPE_NBYTE(5);
+DECLARE_KOBJ_TTYPE_BURST(5);
+DECLARE_KOBJ_TTYPE_RW(5);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(5);
+
+DECLARE_KOBJ_TTYPE_MASTER(6);
+DECLARE_KOBJ_TTYPE_NBEAT(6);
+DECLARE_KOBJ_TTYPE_NBYTE(6);
+DECLARE_KOBJ_TTYPE_BURST(6);
+DECLARE_KOBJ_TTYPE_RW(6);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(6);
+
+DECLARE_KOBJ_TTYPE_MASTER(7);
+DECLARE_KOBJ_TTYPE_NBEAT(7);
+DECLARE_KOBJ_TTYPE_NBYTE(7);
+DECLARE_KOBJ_TTYPE_BURST(7);
+DECLARE_KOBJ_TTYPE_RW(7);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(7);
+
+DECLARE_KOBJ_TTYPE_MASTER(8);
+DECLARE_KOBJ_TTYPE_NBEAT(8);
+DECLARE_KOBJ_TTYPE_NBYTE(8);
+DECLARE_KOBJ_TTYPE_BURST(8);
+DECLARE_KOBJ_TTYPE_RW(8);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(8);
+
+DECLARE_KOBJ_TTYPE_MASTER(9);
+DECLARE_KOBJ_TTYPE_NBEAT(9);
+DECLARE_KOBJ_TTYPE_NBYTE(9);
+DECLARE_KOBJ_TTYPE_BURST(9);
+DECLARE_KOBJ_TTYPE_RW(9);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(9);
+
+DECLARE_KOBJ_TTYPE_MASTER(10);
+DECLARE_KOBJ_TTYPE_NBEAT(10);
+DECLARE_KOBJ_TTYPE_NBYTE(10);
+DECLARE_KOBJ_TTYPE_BURST(10);
+DECLARE_KOBJ_TTYPE_RW(10);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(10);
+
+DECLARE_KOBJ_TTYPE_MASTER(11);
+DECLARE_KOBJ_TTYPE_NBEAT(11);
+DECLARE_KOBJ_TTYPE_NBYTE(11);
+DECLARE_KOBJ_TTYPE_BURST(11);
+DECLARE_KOBJ_TTYPE_RW(11);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(11);
+
+DECLARE_KOBJ_TTYPE_MASTER(12);
+DECLARE_KOBJ_TTYPE_NBEAT(12);
+DECLARE_KOBJ_TTYPE_NBYTE(12);
+DECLARE_KOBJ_TTYPE_BURST(12);
+DECLARE_KOBJ_TTYPE_RW(12);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(12);
+
+DECLARE_KOBJ_TTYPE_MASTER(13);
+DECLARE_KOBJ_TTYPE_NBEAT(13);
+DECLARE_KOBJ_TTYPE_NBYTE(13);
+DECLARE_KOBJ_TTYPE_BURST(13);
+DECLARE_KOBJ_TTYPE_RW(13);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(13);
+
+DECLARE_KOBJ_TTYPE_MASTER(14);
+DECLARE_KOBJ_TTYPE_NBEAT(14);
+DECLARE_KOBJ_TTYPE_NBYTE(14);
+DECLARE_KOBJ_TTYPE_BURST(14);
+DECLARE_KOBJ_TTYPE_RW(14);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(14);
+
+DECLARE_KOBJ_TTYPE_MASTER(15);
+DECLARE_KOBJ_TTYPE_NBEAT(15);
+DECLARE_KOBJ_TTYPE_NBYTE(15);
+DECLARE_KOBJ_TTYPE_BURST(15);
+DECLARE_KOBJ_TTYPE_RW(15);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(15);
+
+DECLARE_KOBJ_TTYPE_MASTER(16);
+DECLARE_KOBJ_TTYPE_NBEAT(16);
+DECLARE_KOBJ_TTYPE_NBYTE(16);
+DECLARE_KOBJ_TTYPE_BURST(16);
+DECLARE_KOBJ_TTYPE_RW(16);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(16);
+
+DECLARE_KOBJ_TTYPE_MASTER(17);
+DECLARE_KOBJ_TTYPE_NBEAT(17);
+DECLARE_KOBJ_TTYPE_NBYTE(17);
+DECLARE_KOBJ_TTYPE_BURST(17);
+DECLARE_KOBJ_TTYPE_RW(17);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(17);
+
+DECLARE_KOBJ_TTYPE_MASTER(18);
+DECLARE_KOBJ_TTYPE_NBEAT(18);
+DECLARE_KOBJ_TTYPE_NBYTE(18);
+DECLARE_KOBJ_TTYPE_BURST(18);
+DECLARE_KOBJ_TTYPE_RW(18);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(18);
+
+DECLARE_KOBJ_TTYPE_MASTER(19);
+DECLARE_KOBJ_TTYPE_NBEAT(19);
+DECLARE_KOBJ_TTYPE_NBYTE(19);
+DECLARE_KOBJ_TTYPE_BURST(19);
+DECLARE_KOBJ_TTYPE_RW(19);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(19);
+
+DECLARE_KOBJ_TTYPE_MASTER(20);
+DECLARE_KOBJ_TTYPE_NBEAT(20);
+DECLARE_KOBJ_TTYPE_NBYTE(20);
+DECLARE_KOBJ_TTYPE_BURST(20);
+DECLARE_KOBJ_TTYPE_RW(20);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(20);
+
+DECLARE_KOBJ_TTYPE_MASTER(21);
+DECLARE_KOBJ_TTYPE_NBEAT(21);
+DECLARE_KOBJ_TTYPE_NBYTE(21);
+DECLARE_KOBJ_TTYPE_BURST(21);
+DECLARE_KOBJ_TTYPE_RW(21);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(21);
+
+
+/* SEDA 3.5 ext */
+static unsigned int msel_group_ext_val[WSCT_AMOUNT];
+static unsigned int wsct_rw_val[WSCT_AMOUNT];
+
+char* const delim_comma = ",";
+char* const delim_coclon = ":";
+
+
+char msel_group_ext[FILE_NODE_DATA_LEN] = {'\0'};
+
+static void _clear_msel_group_ext(void) {
+	int i;
+
+	for (i=0;i<WSCT_AMOUNT;i++) {
+		msel_group_ext_val[i] = BM_MASTER_ALL;
+	}
+
+	/*WSCT 4~5 default is ultra, pre-ultra total*/
+	msel_group_ext[0] = '\0';
+}
+
+static ssize_t msel_group_ext_store(struct kobject *kobj,
+		struct kobj_attribute *attr,
+		const char *buf,
+		size_t n)
+{
+	/*parse wsct_id:group,
+	1. split data  by ","
+	2. split subdata by ":"
+	3. check the value is OK
+
+	don't clear the setting, do this by echo 1 > clear_setting
+	*/
+
+	char *token, *cur= msel_group_ext;
+	char *_id = NULL, *_master_group = NULL;
+	int id_int = 0;
+
+	_clear_msel_group_ext();
+
+	snprintf(msel_group_ext, FILE_NODE_DATA_LEN, "%s", buf);
+	msel_group_ext[n-1]='\0';
+
+
+	while (cur != NULL) {
+		token = strsep(&cur, delim_comma);
+		PR_BOOTMSG("token: %s\n",token);
+		/*token EX: 4:0xff , (ID,master_group)*/
+
+		_id = strsep(&token, delim_coclon); // ID
+		_master_group = strsep(&token, delim_coclon);
+
+		PR_BOOTMSG("_id[%s] _master_group[%s]\n",_id,_master_group);
+
+		if (_id == NULL || _master_group == NULL) {
+			PR_BOOTMSG("err: _id[%s] _master_group[%s], para can't be NULL\n",_id,_master_group);
+			_clear_msel_group_ext();
+			return -EINVAL;
+		}
+
+		if (kstrtouint(_id, 0, &id_int) != 0) {
+			PR_BOOTMSG("_id[%s] trans to hex err\n",_id);
+			_clear_msel_group_ext();
+			return -EINVAL;
+		}
+
+
+		if ( id_int >= 0 && id_int < WSCT_AMOUNT) {
+			if (kstrtouint(_master_group, 0, &msel_group_ext_val[id_int]) != 0) {
+				PR_BOOTMSG("master_group[%s] trans to hex err\n",_master_group);
+				_clear_msel_group_ext();
+				return -EINVAL;
+			}
+		} else {
+			PR_BOOTMSG("id[%d] exceed the range, it must be 0~%d\n",id_int, WSCT_AMOUNT-1);
+			_clear_msel_group_ext();
+			return -EINVAL;
+		}
+	}
+#ifdef FILE_NODE_DBG
+	PR_BOOTMSG("input data [%s]\n",msel_group_ext);
+	/*PR_BOOTMSG("msel_group_ext_store size para n[%d]\n",n);*/
+	int i;
+	PR_BOOTMSG("save data\n");
+	for (i=0;i<WSCT_AMOUNT;i++) {
+		PR_BOOTMSG("id[%d]=%X\n",i,msel_group_ext_val[i]);
+	}
+#endif
+	return n;
+}
+
+static ssize_t msel_group_ext_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%s\n", msel_group_ext);
+}
+
+
+char wsct_rw[FILE_NODE_DATA_LEN] = {'\0'};
+
+static void _clear_wsct_rw(void) {
+	int i;
+
+	for (i=0;i<WSCT_AMOUNT;i++) {
+		wsct_rw_val[i] = BM_WSCT_RW_RWBOTH;
+	}
+	wsct_rw[0] = '\0';
+}
+
+static ssize_t wsct_rw_store(struct kobject *kobj,
+		struct kobj_attribute *attr,
+		const char *buf,
+		size_t n)
+{
+	char *token, *cur= wsct_rw;
+	char *_id = NULL, *_rw_type = NULL;
+	int id_int = 0;
+
+	_clear_wsct_rw();
+
+	snprintf(wsct_rw, FILE_NODE_DATA_LEN, "%s", buf);
+	wsct_rw[n-1]='\0';
+
+	while (cur != NULL) {
+		token = strsep(&cur, delim_comma);
+		PR_BOOTMSG("token: %s\n",token);
+		/*token EX: 4:R , 5:W (ID,RW)*/
+
+		_id = strsep(&token, delim_coclon); // ID
+		_rw_type = strsep(&token, delim_coclon);
+
+		if (_id == NULL || _rw_type == NULL) {
+			PR_BOOTMSG("err: _id[%s] _rw_type[%s], para can't be NULL\n",_id, _rw_type);
+			_clear_wsct_rw();
+			return -EINVAL;
+		}
+
+		PR_BOOTMSG("_id[%s] _rw_type[%s]\n",_id, _rw_type);
+		if (kstrtouint(_id, 0, &id_int) != 0) {
+			PR_BOOTMSG("_id[%s] trans to hex err\n",_id);
+			_clear_wsct_rw();
+			return -EINVAL;
+		}
+
+
+		if ( id_int >= 0 && id_int < WSCT_AMOUNT) {
+			if ( 0 == strncmp("NONE",_rw_type,4))
+				wsct_rw_val[id_int] = BM_WSCT_RW_DISABLE;
+			else if (0 == strncmp("R",_rw_type,4))
+				wsct_rw_val[id_int] = BM_WSCT_RW_READONLY;
+			else if (0 == strncmp("W",_rw_type,4))
+				wsct_rw_val[id_int] = BM_WSCT_RW_WRITEONLY;
+			else if (0 == strncmp("RW",_rw_type,4))
+				wsct_rw_val[id_int] = BM_WSCT_RW_RWBOTH;
+			else {
+				PR_BOOTMSG("_id[%s] has err rwtype[%s]\n", _id, _rw_type);
+				_clear_wsct_rw();
+				return -EINVAL;
+			}
+
+		} else {
+			PR_BOOTMSG("id[%d] exceed the range, it must be 0~%d\n",id_int, WSCT_AMOUNT-1);
+			_clear_wsct_rw();
+			return -EINVAL;
+		}
+	}
+
+#ifdef FILE_NODE_DBG
+	PR_BOOTMSG("wsct_rw_store input data [%s]\n",wsct_rw);
+	int i;
+	PR_BOOTMSG("rwtype save data\n");
+	for (i=0;i<WSCT_AMOUNT;i++) {
+		PR_BOOTMSG("id[%d]=%d\n",i,wsct_rw_val[i]);
+	}
+#endif
+	return n;
+}
+
+static ssize_t wsct_rw_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%s\n", wsct_rw);
+}
+
+
+static unsigned int WSCT_HPRI_DIS[WSCT_AMOUNT];
+static unsigned int WSCT_HPRI_SEL[WSCT_AMOUNT];
+char wsct_high_priority_enable[FILE_NODE_DATA_LEN] = {'\0'};
+
+static void _clear_wsct_high_priority_enable(void) {
+	int i;
+
+	for (i=0;i<WSCT_AMOUNT;i++) {
+		WSCT_HPRI_DIS[i] = 1;
+		WSCT_HPRI_SEL[i] = 0xF;
+	}
+
+	WSCT_HPRI_DIS[4] = 0;
+	WSCT_HPRI_SEL[4] = 0x8;  /* ultra */
+
+	WSCT_HPRI_DIS[5] = 0;
+	WSCT_HPRI_SEL[5] = 0x4; /* pre_ultra */
+
+
+	wsct_high_priority_enable[0] = '\0';
+}
+
+static ssize_t wsct_high_priority_enable_store(struct kobject *kobj,
+		struct kobj_attribute *attr,
+		const char *buf,
+		size_t n)
+{
+	char *token, *cur= wsct_high_priority_enable;
+	char *_id = NULL, *_enable = NULL,  *_level = NULL;
+	int  id_int = 0, level_int = 0;
+
+	_clear_wsct_high_priority_enable();
+
+	snprintf(wsct_high_priority_enable, FILE_NODE_DATA_LEN, "%s", buf);
+	wsct_high_priority_enable[n-1]='\0';
+
+	while (cur != NULL) {
+		token = strsep(&cur, delim_comma);
+		PR_BOOTMSG("token: %s\n",token);
+		/*token EX: 4:R , 5:W (ID,RW)*/
+
+		_id = strsep(&token, delim_coclon); // ID
+		_enable = strsep(&token, delim_coclon);
+		_level = strsep(&token, delim_coclon);
+
+		PR_BOOTMSG("_id[%s] _enable[%s] _level[%s]\n",_id, _enable, _level);
+
+		if (_id == NULL || _enable == NULL || _level == NULL ) {
+			PR_BOOTMSG("err : _id[%s] _enable[%s] _level[%s], para can't be NULL\n",_id, _enable, _level);
+			_clear_wsct_high_priority_enable();
+			return -EINVAL;
+		}
+
+		if (kstrtouint(_id, 0, &id_int) != 0) {
+			PR_BOOTMSG("_id[%s] trans to hex err\n",_id);
+			_clear_wsct_high_priority_enable();
+			return -EINVAL;
+		}
+
+
+		if ( id_int >= 0 && id_int < WSCT_AMOUNT) {
+			if ( 0 == strncmp("disable", _enable, 7)) {
+
+				WSCT_HPRI_DIS[id_int] = 1;
+				WSCT_HPRI_SEL[id_int] = 0xf;
+			} else if ( 0 == strncmp("enable", _enable, 6)) {
+
+				WSCT_HPRI_DIS[id_int] = 0;
+				if (kstrtouint(_level, 0, &level_int) != 0) {
+					PR_BOOTMSG("_id[%s] trans ultraLevel[%s] to hex err\n",_id, _level);
+					_clear_wsct_high_priority_enable();
+					return -EINVAL;
+				}
+				WSCT_HPRI_SEL[id_int] = level_int & 0xF;
+			} else {
+				PR_BOOTMSG("_id[%s] has err enable[%s] (enable/disable)\n", _id, _enable);
+				_clear_wsct_high_priority_enable();
+				return -EINVAL;
+			}
+
+		} else {
+			PR_BOOTMSG("id[%d] exceed the range, it must be 0~%d\n",id_int, WSCT_AMOUNT-1);
+			_clear_wsct_high_priority_enable();
+			return -EINVAL;
+		}
+	}
+#ifdef FILE_NODE_DBG
+	PR_BOOTMSG("input data [%s]\n",wsct_high_priority_enable);
+	int i;
+	PR_BOOTMSG("wsct_high_priority_enable save data\n");
+	for (i=0;i<WSCT_AMOUNT;i++) {
+		PR_BOOTMSG("id[%d]=(%X,%X)\n", i, WSCT_HPRI_DIS[i], WSCT_HPRI_SEL[i]);
+	}
+#endif
+	return n;
+}
+
+static ssize_t wsct_high_priority_enable_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%s\n", wsct_high_priority_enable);
+}
+
+
+static unsigned int wsct_busid_val[WSCT_AMOUNT];
+static unsigned int wsct_idMask_val[WSCT_AMOUNT];
+
+char wsct_busid[FILE_NODE_DATA_LEN] = {'\0'};
+
+static void _clear_wsct_busid(void) {
+	int i;
+
+	for (i=0;i<WSCT_AMOUNT;i++) {
+		wsct_busid_val[i] = 0xfffff;
+		wsct_idMask_val[i] = 0x1FFF;
+	}
+	wsct_busid[0] = '\0';
+}
+
+static ssize_t wsct_busid_store(struct kobject *kobj,
+		struct kobj_attribute *attr,
+		const char *buf,
+		size_t n)
+{
+	char *token, *cur= wsct_busid;
+
+	char *_id = NULL, *_busid = NULL, *_idMask = NULL;
+	int id_int = 0, busid_int = 0, idMask_int = 0;
+
+	_clear_wsct_busid();
+
+	snprintf(wsct_busid, FILE_NODE_DATA_LEN, "%s", buf);
+	wsct_busid[n-1]='\0';
+
+	while (cur != NULL) {
+		token = strsep(&cur, delim_comma);
+		PR_BOOTMSG("token: %s\n",token);
+		/*token EX: 4:R , 5:W (ID,RW)*/
+
+		_id = strsep(&token, delim_coclon); // ID
+		_busid = strsep(&token, delim_coclon);
+		_idMask = strsep(&token, delim_coclon);
+
+		PR_BOOTMSG("_id[%s] _busid[%s] _idMask[%s]\n",_id, _busid, _idMask);
+
+		if (_id == NULL || _busid == NULL || _idMask == NULL) {
+			PR_BOOTMSG("err: _id[%s] _busid[%s] _idMask[%s] ,parameter can't be NULL\n",_id, _busid, _idMask);
+			_clear_wsct_busid();
+			return -EINVAL;
+		}
+
+
+		if (kstrtouint(_id, 0, &id_int) != 0) {
+			PR_BOOTMSG("_id[%s] trans to hex err\n",_id);
+			_clear_wsct_busid();
+			return -EINVAL;
+		}
+		if (kstrtouint(_busid, 0, &busid_int) != 0) {
+			PR_BOOTMSG("_busid[%s] trans to hex err\n",_busid);
+			_clear_wsct_busid();
+			return -EINVAL;
+		}
+		if (kstrtouint(_idMask, 0, &idMask_int) != 0) {
+			PR_BOOTMSG("_idMask[%s] trans to hex err\n",_idMask);
+			_clear_wsct_busid();
+			return -EINVAL;
+		}
+
+
+		if ( id_int >= 0 && id_int < WSCT_AMOUNT) {
+			wsct_busid_val[id_int] = busid_int;
+			wsct_idMask_val[id_int] = idMask_int;
+
+		} else {
+			PR_BOOTMSG("id[%d] exceed the range, it must be 0~%d\n",id_int, WSCT_AMOUNT-1);
+			_clear_wsct_busid();
+			return -EINVAL;
+		}
+	}
+#ifdef FILE_NODE_DBG
+	PR_BOOTMSG("input data [%s]\n",wsct_busid);
+	int i;
+	PR_BOOTMSG("wsct_busid save data\n");
+	for (i=0;i<WSCT_AMOUNT;i++) {
+		PR_BOOTMSG("id[%d](busid,idMask)=(%X,%X)\n", i, wsct_busid_val[i], wsct_idMask_val[i]);
+	}
+#endif
+	return n;
+}
+
+static ssize_t wsct_busid_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%s\n", wsct_busid);
+}
+
+
+static unsigned int  wsct_chn_rank_sel_val[WSCT_AMOUNT];
+char wsct_chn_rank_sel[FILE_NODE_DATA_LEN] = {'\0'};
+
+static void _clear_wsct_chn_rank_sel(void) {
+	int i;
+
+	for (i=0;i<WSCT_AMOUNT;i++) {
+		wsct_chn_rank_sel_val[i] = 0xF;
+	}
+	wsct_chn_rank_sel[0] = '\0';
+}
+
+static ssize_t wsct_chn_rank_sel_store(struct kobject *kobj,
+		struct kobj_attribute *attr,
+		const char *buf,
+		size_t n)
+{
+	char *token, *cur= wsct_chn_rank_sel;
+	char *_id = NULL, *_chn_rank = NULL;
+	int id_int = 0, chn_rank_int = 0;
+
+	_clear_wsct_chn_rank_sel();
+
+	snprintf(wsct_chn_rank_sel, FILE_NODE_DATA_LEN, "%s", buf);
+	wsct_chn_rank_sel[n-1]='\0';
+
+
+	while (cur != NULL) {
+		token = strsep(&cur, delim_comma);
+		PR_BOOTMSG("token: %s\n",token);
+		/*token EX: 4:f , 5:C (ID,chn_rnk_sel)*/
+
+		_id = strsep(&token, delim_coclon); // ID
+		_chn_rank = strsep(&token, delim_coclon);
+
+		PR_BOOTMSG("_id[%s] _chn_rank[%s]\n",_id, _chn_rank);
+
+		if (_id == NULL || _chn_rank == NULL) {
+			PR_BOOTMSG("err : _id[%s] _chn_rank[%s], para can't be NULL\n",_id, _chn_rank);
+			_clear_wsct_chn_rank_sel();
+			return -EINVAL;
+		}
+
+
+		if (kstrtouint(_id, 0, &id_int) != 0) {
+			PR_BOOTMSG("_id[%s] trans to hex err\n",_id);
+			_clear_wsct_chn_rank_sel();
+			return -EINVAL;
+		}
+		if (kstrtouint(_chn_rank, 0, &chn_rank_int) != 0) {
+			PR_BOOTMSG("_chn_rank[%s] trans to hex err\n",_id);
+			_clear_wsct_chn_rank_sel();
+			return -EINVAL;
+		}
+
+		if ( id_int >= 0 && id_int < WSCT_AMOUNT) {
+			wsct_chn_rank_sel_val[id_int] = chn_rank_int;
+
+		} else {
+			PR_BOOTMSG("id[%d] exceed the range, it must be 0~%d\n",id_int, WSCT_AMOUNT-1);
+			_clear_wsct_chn_rank_sel();
+			return -EINVAL;
+		}
+	}
+
+#ifdef FILE_NODE_DBG
+	PR_BOOTMSG("wsct_chn_rank_sel input data [%s]\n",wsct_chn_rank_sel);
+	int i;
+	PR_BOOTMSG("wsct_chn_rank_sel_val save data\n");
+	for (i=0;i<WSCT_AMOUNT;i++) {
+		PR_BOOTMSG("id[%d]=%X\n",i,wsct_chn_rank_sel_val[i]);
+	}
+#endif
+	return n;
+}
+
+static ssize_t wsct_chn_rank_sel_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%s\n", wsct_chn_rank_sel);
+}
+
+static unsigned int  wsct_byte_low_bnd_val[WSCT_AMOUNT];
+static unsigned int  wsct_byte_up_bnd_val[WSCT_AMOUNT];
+static unsigned int  wsct_byte_bnd_dis[WSCT_AMOUNT];
+char wsct_burst_range[FILE_NODE_DATA_LEN] = {'\0'};
+
+static void _clear_wsct_burst_range(void) {
+	int i;
+
+	for (i=0;i<WSCT_AMOUNT;i++) {
+		wsct_byte_low_bnd_val[i] = 0x0;
+		wsct_byte_up_bnd_val[i] = 0x1FF;
+		wsct_byte_bnd_dis[i] = 1;
+	}
+	wsct_burst_range[0] = '\0';
+}
+
+static ssize_t wsct_burst_range_store(struct kobject *kobj,
+		struct kobj_attribute *attr,
+		const char *buf,
+		size_t n)
+{
+	char *token, *cur= wsct_burst_range;
+	char *_id = NULL, *_low_bnd = NULL, *_up_bnd = NULL;
+	int id_int = 0, low_bnd_int = 0, up_bnd_int = 0;
+
+	_clear_wsct_burst_range();
+
+	snprintf(wsct_burst_range, FILE_NODE_DATA_LEN, "%s", buf);
+	wsct_burst_range[n-1]='\0';
+
+
+	while (cur != NULL) {
+		token = strsep(&cur, delim_comma);
+		PR_BOOTMSG("token: %s\n",token);
+		/*token EX: 4:f , 5:C (ID,chn_rnk_sel)*/
+
+		_id = strsep(&token, delim_coclon); // ID
+		_low_bnd = strsep(&token, delim_coclon);
+		_up_bnd = strsep(&token, delim_coclon);
+
+		PR_BOOTMSG("_id[%s] _low_bnd[%s] _up_bnd[%s]\n",_id, _low_bnd, _up_bnd);
+
+		if (_id == NULL || _low_bnd == NULL || _up_bnd == NULL) {
+			PR_BOOTMSG("err : _id[%s] _low_bnd[%s] _up_bnd[%s], para can't be NULL\n",_id, _low_bnd, _up_bnd);
+			_clear_wsct_burst_range();
+			return -EINVAL;
+		}
+
+		if (kstrtouint(_id, 0, &id_int) != 0) {
+			PR_BOOTMSG("_id[%s] trans to hex err\n",_id);
+			_clear_wsct_burst_range();
+			return -EINVAL;
+		}
+		if (kstrtouint(_low_bnd, 0, &low_bnd_int) != 0) {
+			PR_BOOTMSG("_low_bnd[%s] trans to hex err\n",_id);
+			_clear_wsct_burst_range();
+			return -EINVAL;
+		}
+		if (kstrtouint(_up_bnd, 0, &up_bnd_int) != 0) {
+			PR_BOOTMSG("_up_bnd[%s] trans to hex err\n",_id);
+			_clear_wsct_burst_range();
+			return -EINVAL;
+		}
+
+		if ( id_int >= 0 && id_int < WSCT_AMOUNT) {
+			wsct_byte_low_bnd_val[id_int] = low_bnd_int;
+			wsct_byte_up_bnd_val[id_int] = up_bnd_int;
+			wsct_byte_bnd_dis[id_int] = 0;
+		} else {
+			PR_BOOTMSG("id[%d] exceed the range, it must be 0~%d\n",id_int, WSCT_AMOUNT-1);
+			_clear_wsct_burst_range();
+			return -EINVAL;
+		}
+	}
+
+#ifdef FILE_NODE_DBG
+	PR_BOOTMSG("wsct_burst_range_store input data [%s]\n",wsct_burst_range);
+	int i;
+	PR_BOOTMSG("wsct_burst_range save data\n");
+	for (i=0;i<WSCT_AMOUNT;i++) {
+		PR_BOOTMSG("id[%d](low_bnd,up_bnd)=(%X,%X)\n",i,wsct_byte_low_bnd_val[i],wsct_byte_up_bnd_val[i]);
+	}
+#endif
+	return n;
+}
+
+static ssize_t wsct_burst_range_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%s\n", wsct_burst_range);
+}
+
+
+
+static unsigned int tsct_busid_enable_val[TSCT_AMOUNT];
+char tsct_busid_enable[FILE_NODE_DATA_LEN] = {'\0'};
+
+static void _clear_tsct_busid_enable(void) {
+	int i;
+
+	for (i=0;i<TSCT_AMOUNT;i++) {
+		tsct_busid_enable_val[i] = 0;
+	}
+	tsct_busid_enable[0] = '\0';
+}
+
+static ssize_t tsct_busid_enable_store(struct kobject *kobj,
+		struct kobj_attribute *attr,
+		const char *buf,
+		size_t n)
+{
+	char *token, *cur= tsct_busid_enable;
+	char *_id = NULL, *_enable = NULL;
+	int  id_int = 0;
+
+	_clear_tsct_busid_enable();
+
+	snprintf(tsct_busid_enable, FILE_NODE_DATA_LEN, "%s", buf);
+	tsct_busid_enable[n-1]='\0';
+
+
+	while (cur != NULL) {
+		token = strsep(&cur, delim_comma);
+		PR_BOOTMSG("token: %s\n",token);
+		/*token EX: 4:R , 5:W (ID,RW)*/
+
+		_id = strsep(&token, delim_coclon); // ID
+		_enable = strsep(&token, delim_coclon);
+
+
+		PR_BOOTMSG("_id[%s] _enable[%s]\n",_id, _enable);
+
+		if (_id == NULL || _enable == NULL) {
+			PR_BOOTMSG("err : _id[%s] _enable[%s], para can't be NULL\n",_id, _enable);
+			_clear_tsct_busid_enable();
+			return -EINVAL;
+		}
+
+		if (kstrtouint(_id, 0, &id_int) != 0) {
+			PR_BOOTMSG("_id[%s] trans to hex err\n",_id);
+			_clear_tsct_busid_enable();
+			return -EINVAL;
+		}
+
+
+		if ( id_int >= 0 && id_int < TSCT_AMOUNT) {
+			if ( 0 == strncmp("disable", _enable, 7)) {
+				tsct_busid_enable_val[id_int] = 0;
+			} else if ( 0 == strncmp("enable", _enable, 6)) {
+				tsct_busid_enable_val[id_int] = 1;
+			} else {
+				PR_BOOTMSG("_id[%s] has err enable[%s] (enable/disable)\n", _id, _enable);
+				_clear_tsct_busid_enable();
+				return -EINVAL;
+			}
+
+		} else {
+			PR_BOOTMSG("id[%d] exceed the range, it must be 0~%d\n",id_int, TSCT_AMOUNT-1);
+			_clear_tsct_busid_enable();
+			return -EINVAL;
+		}
+	}
+#ifdef FILE_NODE_DBG
+	PR_BOOTMSG("tsct_busid_enable input data [%s]\n",tsct_busid_enable);
+	int i;
+	PR_BOOTMSG("wsct_high_priority_enable save data\n");
+	for (i=0;i<TSCT_AMOUNT;i++) {
+		PR_BOOTMSG("id[%d]=(%d)\n", i, tsct_busid_enable_val[i]);
+	}
+#endif
+	return n;
+}
+
+static ssize_t tsct_busid_enable_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%s\n", tsct_busid_enable);
+}
+
+
+/* use the origin para high_priority_filter to save the en/dis setting */
+static unsigned int TTYPE_HPRI_SEL[BM_COUNTER_MAX];
+char ttype_high_priority_ext[FILE_NODE_DATA_LEN] = {'\0'};
+
+static void _clear_ttype_high_priority_ext(void) {
+	int i;
+
+	for (i=0;i<BM_COUNTER_MAX;i++) {
+		TTYPE_HPRI_SEL[i] = 0xf;
+	}
+
+	high_priority_filter = 0x0;
+	ttype_high_priority_ext[0] = '\0';
+}
+
+static ssize_t ttype_high_priority_ext_store(struct kobject *kobj,
+		struct kobj_attribute *attr,
+		const char *buf,
+		size_t n)
+{
+	char *token, *cur= ttype_high_priority_ext;
+	char *_id = NULL, *_enable = NULL,  *_level = NULL;
+	int  id_int = 0, level_int = 0;
+
+	_clear_ttype_high_priority_ext();
+
+	snprintf(ttype_high_priority_ext, FILE_NODE_DATA_LEN, "%s", buf);
+	ttype_high_priority_ext[n-1]='\0';
+
+	while (cur != NULL) {
+		token = strsep(&cur, delim_comma);
+		PR_BOOTMSG("token: %s\n",token);
+		/*token EX: 4:R , 5:W (ID,RW)*/
+
+		_id = strsep(&token, delim_coclon); // ID
+		_enable = strsep(&token, delim_coclon);
+		_level = strsep(&token, delim_coclon);
+
+		PR_BOOTMSG("_id[%s] _enable[%s] _level[%s]\n",_id, _enable, _level);
+
+		if (_id == NULL || _enable == NULL || _level == NULL ) {
+			PR_BOOTMSG("err : _id[%s] _enable[%s] _level[%s], para can't be NULL\n",_id, _enable, _level);
+			_clear_ttype_high_priority_ext();
+			return -EINVAL;
+		}
+
+		if (kstrtouint(_id, 0, &id_int) != 0) {
+			PR_BOOTMSG("_id[%s] trans to hex err\n",_id);
+			_clear_ttype_high_priority_ext();
+			return -EINVAL;
+		}
+
+		id_int = id_int - 1;
+		if ( id_int >= 0 && id_int < BM_COUNTER_MAX) {
+			if ( 0 == strncmp("disable", _enable, 7)) {
+
+				high_priority_filter = ( high_priority_filter & ~(1<<id_int) ) | ( 0<<id_int );
+				TTYPE_HPRI_SEL[id_int] = 0xf;
+			} else if ( 0 == strncmp("enable", _enable, 6)) {
+
+				high_priority_filter = ( high_priority_filter & ~(1<<id_int) ) | ( 1<<id_int );
+				if (kstrtouint(_level, 0, &level_int) != 0) {
+					PR_BOOTMSG("_id[%s] trans ultraLevel[%s] to hex err\n",_id, _level);
+					_clear_ttype_high_priority_ext();
+					return -EINVAL;
+				}
+				TTYPE_HPRI_SEL[id_int] = level_int & 0xF;
+			} else {
+				PR_BOOTMSG("ttype_high_priority_ext: _id[%s] has err enable[%s] (enable/disable)\n", _id, _enable);
+				_clear_ttype_high_priority_ext();
+				return -EINVAL;
+			}
+
+		} else {
+			PR_BOOTMSG("id[%d] exceed the range, it must be 1~%d\n",id_int+1, BM_COUNTER_MAX);
+			_clear_ttype_high_priority_ext();
+			return -EINVAL;
+		}
+	}
+#ifdef FILE_NODE_DBG
+	PR_BOOTMSG("input data [%s]\n",ttype_high_priority_ext);
+
+	int i;
+	PR_BOOTMSG("wsct_high_priority_enable save data\n");
+	for (i=0;i<BM_COUNTER_MAX;i++) {
+		PR_BOOTMSG("id[%d]=(%X,%X)\n", i+1, high_priority_filter>>i & 0x1, TTYPE_HPRI_SEL[i]);
+	}
+#endif
+	return n;
+}
+
+static ssize_t ttype_high_priority_ext_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%s\n", ttype_high_priority_ext);
+}
+
+
+static unsigned int ttype_idMask_val[BM_COUNTER_MAX];
+char ttype_busid_ext[FILE_NODE_DATA_LEN] = {'\0'};
+
+static void _clear_ttype_busid_ext(void) {
+	int i;
+
+	for (i=0;i<BM_COUNTER_MAX;i++) {
+		ttype_busid_val[i] = 0xfffff;
+		ttype_idMask_val[i] = 0x1FFF;
+	}
+	ttype_busid_ext[0] = '\0';
+}
+
+/*id: 1~21*/
+static ssize_t ttype_busid_ext_store(struct kobject *kobj,
+		struct kobj_attribute *attr,
+		const char *buf,
+		size_t n)
+{
+	char *token, *cur= ttype_busid_ext;
+
+	char *_id = NULL, *_busid = NULL, *_idMask = NULL;
+	int id_int = 0, busid_int = 0, idMask_int = 0;
+
+	_clear_ttype_busid_ext();
+
+	snprintf(ttype_busid_ext, FILE_NODE_DATA_LEN, "%s", buf);
+	ttype_busid_ext[n-1]='\0';
+
+	while (cur != NULL) {
+		token = strsep(&cur, delim_comma);
+		PR_BOOTMSG("token: %s\n",token);
+		/*token EX: 4:R , 5:W (ID,RW)*/
+
+		_id = strsep(&token, delim_coclon); // ID
+		_busid = strsep(&token, delim_coclon);
+		_idMask = strsep(&token, delim_coclon);
+
+		PR_BOOTMSG("_id[%s] _busid[%s] _idMask[%s]\n",_id, _busid, _idMask);
+
+		if (_id == NULL || _busid == NULL || _idMask == NULL) {
+			PR_BOOTMSG("err: ttype_busid_ext _id[%s] _busid[%s] _idMask[%s] ,parameter can't be NULL\n",_id, _busid, _idMask);
+			_clear_ttype_busid_ext();
+			return -EINVAL;
+		}
+
+		if (kstrtouint(_id, 0, &id_int) != 0) {
+			PR_BOOTMSG("_id[%s] trans to hex err\n",_id);
+			_clear_ttype_busid_ext();
+			return -EINVAL;
+		}
+		if (kstrtouint(_busid, 0, &busid_int) != 0) {
+			PR_BOOTMSG("_busid[%s] trans to hex err\n",_busid);
+			_clear_ttype_busid_ext();
+			return -EINVAL;
+		}
+		if (kstrtouint(_idMask, 0, &idMask_int) != 0) {
+			PR_BOOTMSG("_idMask[%s] trans to hex err\n",_idMask);
+			_clear_ttype_busid_ext();
+			return -EINVAL;
+		}
+
+		id_int = id_int - 1;
+		if ( id_int >= 0 && id_int < BM_COUNTER_MAX) {
+			ttype_busid_val[id_int] = busid_int;
+			ttype_idMask_val[id_int] = idMask_int;
+
+		} else {
+			PR_BOOTMSG("ttype_busid_ext id[%d] exceed the range, it must be 1~%d\n",id_int+1, BM_COUNTER_MAX);
+			_clear_ttype_busid_ext();
+			return -EINVAL;
+		}
+	}
+#ifdef FILE_NODE_DBG
+	PR_BOOTMSG("ttype_busid_ext input data [%s]\n",ttype_busid_ext);
+
+	int i;
+	PR_BOOTMSG("ttype_busid_ext save data\n");
+	for (i=0;i<BM_COUNTER_MAX;i++) {
+		PR_BOOTMSG("id[%d](busid,idMask)=(%X,%X)\n", i+1, ttype_busid_val[i], ttype_idMask_val[i]);
+	}
+#endif
+	return n;
+}
+
+static ssize_t ttype_busid_ext_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%s\n", ttype_busid_ext);
+}
+
+
+static unsigned int  ttype_chn_rank_sel_val[BM_COUNTER_MAX];
+char ttype_chn_rank_sel[FILE_NODE_DATA_LEN] = {'\0'};
+
+static void _clear_ttype_chn_rank_sel(void) {
+	int i;
+
+	for (i=0;i<BM_COUNTER_MAX;i++) {
+		ttype_chn_rank_sel_val[i] = 0xF;
+	}
+	ttype_chn_rank_sel[0] = '\0';
+}
+
+static ssize_t ttype_chn_rank_sel_store(struct kobject *kobj,
+		struct kobj_attribute *attr,
+		const char *buf,
+		size_t n)
+{
+	char *token, *cur= ttype_chn_rank_sel;
+	char *_id = NULL, *_chn_rank = NULL;
+	int id_int = 0, chn_rank_int = 0;
+
+	_clear_ttype_chn_rank_sel();
+
+	snprintf(ttype_chn_rank_sel, FILE_NODE_DATA_LEN, "%s", buf);
+	ttype_chn_rank_sel[n-1]='\0';
+
+	while (cur != NULL) {
+		token = strsep(&cur, delim_comma);
+		PR_BOOTMSG("token: %s\n",token);
+		/*token EX: 4:f , 5:C (ID,chn_rnk_sel)*/
+
+		_id = strsep(&token, delim_coclon); // ID
+		_chn_rank = strsep(&token, delim_coclon);
+
+		PR_BOOTMSG("_id[%s] _chn_rank[%s]\n",_id, _chn_rank);
+
+		if (_id == NULL || _chn_rank == NULL) {
+			PR_BOOTMSG("err (ttype_chn_rank_sel): _id[%s] _chn_rank[%s], para can't be NULL\n",_id, _chn_rank);
+			_clear_ttype_chn_rank_sel();
+			return -EINVAL;
+		}
+
+
+		if (kstrtouint(_id, 0, &id_int) != 0) {
+			PR_BOOTMSG("_id[%s] trans to hex err\n",_id);
+			_clear_ttype_chn_rank_sel();
+			return -EINVAL;
+		}
+		if (kstrtouint(_chn_rank, 0, &chn_rank_int) != 0) {
+			PR_BOOTMSG("_chn_rank[%s] trans to hex err\n",_id);
+			_clear_ttype_chn_rank_sel();
+			return -EINVAL;
+		}
+
+		id_int = id_int -1;
+
+		if ( id_int >= 0 && id_int < BM_COUNTER_MAX) {
+			ttype_chn_rank_sel[id_int] = chn_rank_int;
+
+		} else {
+			PR_BOOTMSG("id[%d] exceed the range, it must be 1~%d\n",id_int+1, BM_COUNTER_MAX);
+			_clear_ttype_chn_rank_sel();
+			return -EINVAL;
+		}
+	}
+
+#ifdef FILE_NODE_DBG
+	PR_BOOTMSG("ttype_chn_rank_sel input data [%s]\n",ttype_chn_rank_sel);
+
+	int i;
+	PR_BOOTMSG("wsct_chn_rank_sel_val save data\n");
+	for (i=0;i<BM_COUNTER_MAX;i++) {
+		PR_BOOTMSG("id[%d]=%X\n",i+1,ttype_chn_rank_sel[i]);
+	}
+#endif
+	return n;
+}
+
+static ssize_t ttype_chn_rank_sel_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%s\n", ttype_chn_rank_sel);
+}
+
+
+static unsigned int  ttype_byte_low_bnd_val[BM_COUNTER_MAX];
+static unsigned int  ttype_byte_up_bnd_val[BM_COUNTER_MAX];
+static unsigned int  ttype_byte_bnd_dis[BM_COUNTER_MAX];
+char ttype_burst_range[FILE_NODE_DATA_LEN] = {'\0'};
+
+static void _clear_ttype_burst_range(void) {
+	int i;
+
+	for (i=0;i<BM_COUNTER_MAX;i++) {
+		ttype_byte_low_bnd_val[i] = 0x0;
+		ttype_byte_up_bnd_val[i] = 0x1FF;
+		ttype_byte_bnd_dis[i] = 1;
+	}
+	ttype_burst_range[0] = '\0';
+}
+
+static ssize_t ttype_burst_range_store(struct kobject *kobj,
+		struct kobj_attribute *attr,
+		const char *buf,
+		size_t n)
+{
+	char *token, *cur= ttype_burst_range;
+	char *_id = NULL, *_low_bnd = NULL, *_up_bnd = NULL;
+	int id_int = 0, low_bnd_int = 0, up_bnd_int = 0;
+
+	_clear_ttype_burst_range();
+
+	snprintf(ttype_burst_range, FILE_NODE_DATA_LEN, "%s", buf);
+	ttype_burst_range[n-1]='\0';
+
+
+	while (cur != NULL) {
+		token = strsep(&cur, delim_comma);
+		PR_BOOTMSG("token: %s\n",token);
+		/*token EX: 4:f , 5:C (ID,chn_rnk_sel)*/
+
+		_id = strsep(&token, delim_coclon); // ID
+		_low_bnd = strsep(&token, delim_coclon);
+		_up_bnd = strsep(&token, delim_coclon);
+
+		PR_BOOTMSG("_id[%s] _low_bnd[%s] _up_bnd[%s]\n",_id, _low_bnd, _up_bnd);
+
+		if (_id == NULL || _low_bnd == NULL || _up_bnd == NULL) {
+			PR_BOOTMSG("err (ttype_burst_range): _id[%s] _low_bnd[%s] _up_bnd[%s], para can't be NULL\n",
+				        _id, _low_bnd, _up_bnd);
+			_clear_ttype_burst_range();
+			return -EINVAL;
+		}
+
+		if (kstrtouint(_id, 0, &id_int) != 0) {
+			PR_BOOTMSG("_id[%s] trans to hex err\n",_id);
+			_clear_ttype_burst_range();
+			return -EINVAL;
+		}
+		if (kstrtouint(_low_bnd, 0, &low_bnd_int) != 0) {
+			PR_BOOTMSG("_low_bnd[%s] trans to hex err\n",_id);
+			_clear_ttype_burst_range();
+			return -EINVAL;
+		}
+		if (kstrtouint(_up_bnd, 0, &up_bnd_int) != 0) {
+			PR_BOOTMSG("_up_bnd[%s] trans to hex err\n",_id);
+			_clear_ttype_burst_range();
+			return -EINVAL;
+		}
+
+		id_int = id_int - 1;
+		if ( id_int >= 0 && id_int < BM_COUNTER_MAX) {
+			ttype_byte_low_bnd_val[id_int] = low_bnd_int;
+			ttype_byte_up_bnd_val[id_int] = up_bnd_int;
+			ttype_byte_bnd_dis[id_int] = 0;
+		} else {
+			PR_BOOTMSG("id[%d] exceed the range, it must be 1~%d\n",id_int, BM_COUNTER_MAX);
+			_clear_ttype_burst_range();
+			return -EINVAL;
+		}
+	}
+
+#ifdef FILE_NODE_DBG
+	PR_BOOTMSG("ttype_burst_range_store input data [%s]\n",ttype_burst_range);
+
+	int i;
+	PR_BOOTMSG("ttype_burst_range save data\n");
+	for (i=0;i<BM_COUNTER_MAX;i++) {
+		PR_BOOTMSG("id[%d](low_bnd,up_bnd)=(%X,%X)\n",i+1,ttype_byte_low_bnd_val[i],ttype_byte_up_bnd_val[i]);
+	}
+#endif
+	return n;
+}
+
+static ssize_t ttype_burst_range_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%s\n", ttype_burst_range);
+}
+
+static int reserve_wsct_setting;
+DECLARE_KOBJ_ATTR_INT(reserve_wsct_setting, reserve_wsct_setting);
+
+static void _clear_setting(void) {
+	/*clear all file node para here*/
+
+
+	PR_BOOTMSG("clear EMI file node setting\n");
+
+	_clear_msel_group_ext();
+	_clear_wsct_rw();
+	_clear_wsct_high_priority_enable();
+	_clear_wsct_busid();
+	_clear_wsct_chn_rank_sel();
+	_clear_wsct_burst_range();
+
+	_clear_tsct_busid_enable();
+	_clear_ttype_high_priority_ext();
+	_clear_ttype_busid_ext();
+	_clear_ttype_chn_rank_sel();
+	_clear_ttype_burst_range();
+	reserve_wsct_setting = 0;
+
+	emi_tsct_enable = 0;
+	emi_mdct_enable = 0;
+	emi_TP_busfiltr_enable = 0;
+	metemi_func_opt = 0xf00e;
+	high_priority_filter = 0x0;
+	rwtype = BM_BOTH_READ_WRITE;
+	dramc_pdir_enable = 1;
+
+	met_emi_regdump = 0;
+	rd_mdmcu_rsv_num = 0;
+
+	msel_enable = 0;
+	msel_group1 = BM_MASTER_ALL;
+	msel_group2 = BM_MASTER_ALL;
+	msel_group3 = BM_MASTER_ALL;
+
+	mdmcu_sel_enable = 0;
+
+	bw_limiter_enable = BM_BW_LIMITER_ENABLE;
+	ttype1_16_en = BM_TTYPE1_16_DISABLE;
+	ttype17_21_en = BM_TTYPE17_21_DISABLE;
+
+
+
+	/*ttype TBD*/
+}
+
+static ssize_t clear_setting_store(struct kobject *kobj,
+			struct kobj_attribute *attr,
+			const char *buf,
+			size_t n)
+{
+	int value;
+
+	if ((n == 0) || (buf == NULL))
+		return -EINVAL;
+
+	if (kstrtoint(buf, 0, &value) != 0)
+		return -EINVAL;
+
+	if (value == 1)
+		_clear_setting();
+
+	return n;
+}
+
+static struct kobj_attribute clear_setting_attr = __ATTR_WO(clear_setting); // OK
+static struct kobj_attribute msel_group_ext_attr = __ATTR(msel_group_ext, 0664, msel_group_ext_show, msel_group_ext_store); //OK
+static struct kobj_attribute wsct_rw_attr = __ATTR(wsct_rw, 0664, wsct_rw_show, wsct_rw_store);
+static struct kobj_attribute wsct_high_priority_enable_attr = __ATTR(wsct_high_priority_enable, 0664, wsct_high_priority_enable_show, wsct_high_priority_enable_store);
+static struct kobj_attribute wsct_busid_attr = __ATTR(wsct_busid, 0664, wsct_busid_show, wsct_busid_store);
+static struct kobj_attribute wsct_chn_rank_sel_attr = __ATTR(wsct_chn_rank_sel, 0664, wsct_chn_rank_sel_show, wsct_chn_rank_sel_store);
+static struct kobj_attribute wsct_burst_range_attr = __ATTR(wsct_burst_range, 0664, wsct_burst_range_show, wsct_burst_range_store);
+static struct kobj_attribute tsct_busid_enable_attr = __ATTR(tsct_busid_enable, 0664, tsct_busid_enable_show, tsct_busid_enable_store);
+static struct kobj_attribute ttype_high_priority_ext_attr = __ATTR(ttype_high_priority_ext, 0664, ttype_high_priority_ext_show, ttype_high_priority_ext_store);
+static struct kobj_attribute ttype_busid_ext_attr = __ATTR(ttype_busid_ext, 0664, ttype_busid_ext_show, ttype_busid_ext_store);
+static struct kobj_attribute ttype_chn_rank_sel_attr = __ATTR(ttype_chn_rank_sel, 0664, ttype_chn_rank_sel_show, ttype_chn_rank_sel_store);
+static struct kobj_attribute ttype_burst_range_attr = __ATTR(ttype_burst_range, 0664, ttype_burst_range_show, ttype_burst_range_store);
+
+
+
+
+
+
+/**/
+#define KOBJ_ATTR_ITEM_SERIAL_FNODE(nr) \
+	do { \
+		KOBJ_ATTR_ITEM(ttype ## nr ## _master); \
+		KOBJ_ATTR_ITEM(ttype ## nr ## _nbeat); \
+		KOBJ_ATTR_ITEM(ttype ## nr ## _nbyte); \
+		KOBJ_ATTR_ITEM(ttype ## nr ## _burst); \
+		KOBJ_ATTR_ITEM(ttype ## nr ## _busid); \
+		KOBJ_ATTR_ITEM(ttype ## nr ## _rw); \
+	} while (0)
+
+#define KOBJ_ATTR_LIST \
+	do { \
+		KOBJ_ATTR_ITEM(high_priority_filter); \
+		KOBJ_ATTR_ITEM(metemi_func_opt); \
+		KOBJ_ATTR_ITEM(emi_tsct_enable); \
+		KOBJ_ATTR_ITEM(emi_mdct_enable); \
+		KOBJ_ATTR_ITEM(emi_TP_busfiltr_enable); \
+		KOBJ_ATTR_ITEM(emi_regdump); \
+		KOBJ_ATTR_ITEM(msel_enable); \
+		KOBJ_ATTR_ITEM(msel_group1); \
+		KOBJ_ATTR_ITEM(msel_group2); \
+		KOBJ_ATTR_ITEM(msel_group3); \
+		KOBJ_ATTR_ITEM(emi_clock_rate); \
+		KOBJ_ATTR_ITEM(rwtype); \
+		KOBJ_ATTR_ITEM(ttype17_21_en); \
+		KOBJ_ATTR_ITEM(ttype1_16_en); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(1); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(2); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(3); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(4); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(5); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(6); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(7); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(8); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(9); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(10); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(11); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(12); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(13); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(14); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(15); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(16); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(17); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(18); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(19); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(20); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(21); \
+		KOBJ_ATTR_ITEM(test_apmcu); \
+		KOBJ_ATTR_ITEM(bw_limiter_enable); \
+		KOBJ_ATTR_ITEM(dramc_pdir_enable); \
+		KOBJ_ATTR_ITEM(mdmcu_sel_enable); \
+		KOBJ_ATTR_ITEM(rd_mdmcu_rsv_num); \
+		KOBJ_ATTR_ITEM(clear_setting);\
+		KOBJ_ATTR_ITEM(msel_group_ext);\
+		KOBJ_ATTR_ITEM(wsct_rw);\
+		KOBJ_ATTR_ITEM(wsct_high_priority_enable);\
+		KOBJ_ATTR_ITEM(wsct_busid);\
+		KOBJ_ATTR_ITEM(wsct_chn_rank_sel);\
+		KOBJ_ATTR_ITEM(wsct_burst_range);\
+		KOBJ_ATTR_ITEM(tsct_busid_enable);\
+		KOBJ_ATTR_ITEM(ttype_high_priority_ext);\
+		KOBJ_ATTR_ITEM(ttype_busid_ext);\
+		KOBJ_ATTR_ITEM(ttype_chn_rank_sel);\
+		KOBJ_ATTR_ITEM(ttype_burst_range);\
+		KOBJ_ATTR_ITEM(reserve_wsct_setting);\
+	} while (0)
+
+
+
+/*======================================================================*/
+/*	EMI Operations							*/
+/*======================================================================*/
+static void emi_init(void)
+{
+	unsigned int bmrw0_val, bmrw1_val, i, enable;
+	/*unsigned int msel_group_val[4];*/
+
+	/*save origianl EMI config*/
+	MET_BM_SaveCfg();
+
+	/* get dram channel number */
+	dram_chann_num = MET_EMI_GetDramChannNum();
+
+	/* Init. EMI bus monitor */
+	MET_BM_SetReadWriteType(rwtype);
+
+#if 1 /* SEDA 3.5 */
+	/*handle the ori */
+
+	if (ttype1_16_en != BM_TTYPE1_16_ENABLE) {
+		MET_BM_SetLatencyCounter(1);    /*enable latency count*/
+	}
+	else {
+		MET_BM_SetLatencyCounter(0);    /*disable latency count*/
+
+		for (i = 1; i <= 16; i++) {
+			MET_BM_SetMonitorCounter(i,
+						 ttype_master_val[i - 1],
+						 ttype_nbeat_val[i - 1] |
+						 ttype_nbyte_val[i - 1] |
+						 ttype_burst_val[i - 1]);
+		}
+	}
+
+	if (ttype17_21_en == BM_TTYPE17_21_ENABLE) {
+		for (i = 17; i <= 21; i++) {
+			MET_BM_SetMonitorCounter(i,
+						 ttype_master_val[i - 1],
+						 ttype_nbeat_val[i - 1] |
+						 ttype_nbyte_val[i - 1] |
+						 ttype_burst_val[i - 1]);
+		}
+	}
+
+	PR_BOOTMSG("[%s]reserve_wsct_setting=%d\n",__func__,reserve_wsct_setting);
+
+	if (reserve_wsct_setting == 0) {
+		/* wsct 0 : total-all*/
+		msel_group_ext_val[0] = BM_MASTER_ALL;
+		wsct_rw_val[0] = BM_WSCT_RW_RWBOTH;
+		WSCT_HPRI_DIS[0] = 1;
+		WSCT_HPRI_SEL[0] = 0xF;
+		wsct_busid_val[0] = 0xFFFFF;
+		wsct_idMask_val[0] = 0x1FFF;
+		wsct_chn_rank_sel_val[0] = 0xF;
+		wsct_byte_bnd_dis[0] = 1;
+
+		/* wsct 4 : total-ultra*/
+		msel_group_ext_val[4] = BM_MASTER_ALL;
+		wsct_rw_val[4] = BM_WSCT_RW_RWBOTH;
+		WSCT_HPRI_DIS[4] = 0;
+		WSCT_HPRI_SEL[4] = 0x8;  /* ultra */
+		wsct_busid_val[4] = 0xFFFFF;
+		wsct_idMask_val[4] = 0x1FFF;
+		wsct_chn_rank_sel_val[4] = 0xF;
+		wsct_byte_bnd_dis[4] = 1;
+
+
+		/* wsct 5 : total-pre_ultra*/
+		msel_group_ext_val[5] = BM_MASTER_ALL;
+		wsct_rw_val[5] = BM_WSCT_RW_RWBOTH;
+		WSCT_HPRI_DIS[5] = 0;
+		WSCT_HPRI_SEL[5] = 0x4; /* pre_ultra */
+		wsct_busid_val[5] = 0xFFFFF;
+		wsct_idMask_val[5] = 0x1FFF;
+		wsct_chn_rank_sel_val[5] = 0xF;
+		wsct_byte_bnd_dis[5] = 1;
+	}
+
+	if (msel_enable) {
+		/* if ole file node set, use the value */
+		if ( msel_group1 != BM_MASTER_ALL )
+			msel_group_ext_val[1] = msel_group1;
+
+		if ( msel_group2 != BM_MASTER_ALL )
+			msel_group_ext_val[2] = msel_group2;
+
+		if ( msel_group3 != BM_MASTER_ALL )
+			msel_group_ext_val[3] = msel_group3;
+
+	} else {
+		for ( i=1; i<=3; i++) {
+			msel_group_ext_val[i] = BM_MASTER_ALL;
+		}
+	}
+
+	MET_BM_SetWSCT_master_rw(msel_group_ext_val, wsct_rw_val);
+	MET_BM_SetWSCT_high_priority(WSCT_HPRI_DIS, WSCT_HPRI_SEL);
+	MET_BM_SetWSCT_busid_idmask(wsct_busid_val, wsct_idMask_val);
+	MET_BM_SetWSCT_chn_rank_sel(wsct_chn_rank_sel_val);
+	MET_BM_SetWSCT_burst_range(wsct_byte_bnd_dis, wsct_byte_low_bnd_val, wsct_byte_up_bnd_val);
+	MET_BM_SetTSCT_busid_enable(tsct_busid_enable_val);
+
+	MET_BM_SetTtype_high_priority_sel(high_priority_filter, TTYPE_HPRI_SEL);
+	MET_BM_SetTtype_busid_idmask(ttype_busid_val, ttype_idMask_val, ttype1_16_en, ttype17_21_en);
+	MET_BM_SetTtype_chn_rank_sel(ttype_chn_rank_sel_val);
+	MET_BM_SetTtype_burst_range(ttype_byte_bnd_dis, ttype_byte_low_bnd_val, ttype_byte_up_bnd_val);
+
+
+	bmrw0_val = 0;
+	for (i = 0; i < 16; i++)
+		bmrw0_val |= (ttype_rw_val[i] << (i * 2));
+
+	bmrw1_val = 0;
+	for (i = 16; i < 21; i++)
+		bmrw1_val |= (ttype_rw_val[i] << ((i-16) * 2));
+
+	MET_BM_SetTtypeCounterRW(bmrw0_val, bmrw1_val);
+
+#else
+	if ((ttype1_16_en != BM_TTYPE1_16_ENABLE) && (emi_TP_busfiltr_enable != 1)) {
+		if (msel_enable) {
+			msel_group_val[0] = BM_MASTER_ALL;
+			msel_group_val[1] = msel_group1;
+			msel_group_val[2] = msel_group2;
+			msel_group_val[3] = msel_group3;
+		} else {
+			msel_group_val[0] = BM_MASTER_ALL;
+			msel_group_val[1] = BM_MASTER_ALL;
+			msel_group_val[2] = BM_MASTER_ALL;
+			msel_group_val[3] = BM_MASTER_ALL;
+		}
+
+		MET_BM_SetLatencyCounter(1);    /*enable latency count*/
+
+		for (i = 1; i <= 4; i++) {
+			MET_BM_SetMonitorCounter(i,
+						 msel_group_val[i - 1] & BM_MASTER_ALL,
+						 BM_TRANS_TYPE_4BEAT |
+						 BM_TRANS_TYPE_8Byte |
+						 BM_TRANS_TYPE_BURST_WRAP);
+			MET_BM_SetbusID(i, 0);
+			MET_BM_SetbusID_En(i, 0);       /*disable ttype bus sel*/
+		}
+		for (i = 0; i < 4; i++)
+			MET_BM_Set_WsctTsct_id_sel(i, 0);       /*disable tp filter*/
+
+	} else if ((ttype1_16_en != BM_TTYPE1_16_ENABLE) && (emi_TP_busfiltr_enable == 1)) {
+		MET_BM_SetLatencyCounter(1);    /*enable latency count*/
+
+		for (i = 1; i <= 4; i++) {
+			MET_BM_SetMonitorCounter(i,
+						 ttype_master_val[i - 1],
+						 ttype_nbeat_val[i - 1] |
+						 ttype_nbyte_val[i - 1] |
+						 ttype_burst_val[i - 1]);
+			MET_BM_SetbusID(i, ttype_busid_val[i - 1]);
+			MET_BM_SetbusID_En(i, 0);       /*disable ttype bus sel*/
+		}
+		for (i = 0; i < 4; i++)
+			MET_BM_Set_WsctTsct_id_sel(i, 1);       /*enable tp filter*/
+
+	} else if ((ttype1_16_en == BM_TTYPE1_16_ENABLE) && (emi_TP_busfiltr_enable != 1)) {
+		MET_BM_SetLatencyCounter(0);    /*disable latency count*/
+
+		for (i = 1; i <= 16; i++) {
+			MET_BM_SetMonitorCounter(i,
+						 ttype_master_val[i - 1],
+						 ttype_nbeat_val[i - 1] |
+						 ttype_nbyte_val[i - 1] |
+						 ttype_burst_val[i - 1]);
+
+			MET_BM_SetbusID(i, ttype_busid_val[i - 1]);
+			/*disenable ttype bus sel if busid > 0xff_ff*/
+			MET_BM_SetbusID_En(i, (ttype_busid_val[i - 1] > 0xffff) ? 0 : 1);
+		}
+		for (i = 0; i < 4; i++)
+			MET_BM_Set_WsctTsct_id_sel(i, 0);       /*disable tp filter*/
+	} else {	/* (ttype1_16_en == BM_TTYPE1_16_ENABLE)  &&  (emi_TP_busfiltr_enable == 1) */
+		MET_BM_SetLatencyCounter(0);    /*disable latency count*/
+
+		for (i = 1; i <= 16; i++) {
+			MET_BM_SetMonitorCounter(i,
+						 ttype_master_val[i - 1],
+						 ttype_nbeat_val[i - 1] |
+						 ttype_nbyte_val[i - 1] |
+						 ttype_burst_val[i - 1]);
+
+			MET_BM_SetbusID(i, ttype_busid_val[i - 1]);
+			/*disable ttype bus sel if busid > 0xff_ff*/
+			MET_BM_SetbusID_En(i, (ttype_busid_val[i - 1] > 0xffff) ? 0 : 1);
+		}
+		for (i = 0; i < 4; i++)
+			MET_BM_Set_WsctTsct_id_sel(i, 1);       /*enable tp filter*/
+	}
+
+	if (ttype17_21_en == BM_TTYPE17_21_ENABLE) {
+		for (i = 17; i <= 21; i++) {
+			MET_BM_SetMonitorCounter(i,
+						 ttype_master_val[i - 1],
+						 ttype_nbeat_val[i - 1] |
+						 ttype_nbyte_val[i - 1] |
+						 ttype_burst_val[i - 1]);
+			MET_BM_SetbusID(i, ttype_busid_val[i - 1]);
+			/*disable ttype bus sel if busid > 0xff_ff*/
+			MET_BM_SetbusID_En(i, (ttype_busid_val[i - 1] > 0xffff) ? 0 : 1);
+		}
+	}
+
+	bmrw0_val = 0;
+	for (i = 0; i < 16; i++)
+		bmrw0_val |= (ttype_rw_val[i] << (i * 2));
+
+	bmrw1_val = 0;
+	for (i = 16; i < 21; i++)
+		bmrw1_val |= (ttype_rw_val[i] << ((i-16) * 2));
+
+	MET_BM_SetTtypeCounterRW(bmrw0_val, bmrw1_val);
+
+	for (i = 0; i < BM_COUNTER_MAX; i++) {
+		if ((high_priority_filter & (1 << i)) == 0)
+			enable = 0;
+		else
+			enable = 1;
+
+		MET_BM_SetUltraHighFilter(i + 1, enable);
+	}
+
+
+#endif
+	met_record_dramc_dcm_enable_flag();
+
+	if (met_emi_regdump == 1)
+		emi_dump_reg();
+}
+
+
+static void emi_uninit(void)
+{
+	MET_BM_RestoreCfg();
+}
+
+static inline void emi_start(void)
+{
+	MET_BM_Enable(1);
+}
+
+
+static inline void emi_stop(void)
+{
+	MET_BM_Enable(0);
+}
+
+
+static inline int do_emi(void)
+{
+	return met_emi.mode;
+}
+
+
+noinline void DRAM_DVFS(unsigned int dram_data_rate_MHz)
+{
+	MET_TRACE("%u\n", dram_data_rate_MHz);
+}
+
+
+static unsigned int emi_bw_limiter(unsigned int *__restrict__ array)
+{
+	int idx = 0;
+	unsigned int dram_data_rate_MHz;
+
+	if (get_dram_data_rate_symbol) {
+		dram_data_rate_MHz = get_dram_data_rate_symbol();
+	} else {
+		METERROR("get_dram_data_rate_symbol = NULL\n");
+		dram_data_rate_MHz = 0;
+	}
+
+	/* print dram data rate */
+	DRAM_DVFS(dram_data_rate_MHz);
+
+	/* get correct dram_clock_rate */
+	array[idx++] = dram_data_rate_MHz;
+
+	/* get correct ARB A->LAST */
+	array[idx++] = MET_EMI_GetARBA();
+	array[idx++] = MET_EMI_GetARBB();
+	array[idx++] = MET_EMI_GetARBC();
+	array[idx++] = MET_EMI_GetARBD();
+	array[idx++] = MET_EMI_GetARBE();
+	array[idx++] = MET_EMI_GetARBF();
+	array[idx++] = MET_EMI_GetARBG();
+	array[idx++] = MET_EMI_GetARBH();
+	/* EMI Total BW Thresholds */
+	array[idx++] = MET_EMI_GetBWCT0();
+	array[idx++] = MET_EMI_GetBWCT1();
+	array[idx++] = MET_EMI_GetBWCT2();
+	array[idx++] = MET_EMI_GetBWCT3();
+	array[idx++] = MET_EMI_GetBWCT4();
+	array[idx++] = MET_EMI_GetBWST0();
+	array[idx++] = MET_EMI_GetBWST1();
+	/* EMI C+G BW Thresholds */
+	array[idx++] = MET_EMI_GetBWCT0_2ND();
+	array[idx++] = MET_EMI_GetBWCT1_2ND();
+	array[idx++] = MET_EMI_GetBWST_2ND();
+
+	return idx;
+}
+
+
+static void _ms_dramc(unsigned int *__restrict__ dramc_pdir_value, int dram_chann_num)
+{
+	MET_DRAMC_GetDebugCounter(dramc_pdir_value, dram_chann_num);
+}
+
+static unsigned int emi_polling_ext(unsigned int *__restrict__ emi_value_ext)
+{
+	int i = 0;
+	int emi_value_ext_amount = WSCT_AMOUNT - NWSCT;
+
+
+	for ( i=0; i< emi_value_ext_amount; i++) {
+		emi_value_ext[i] = MET_BM_GetWordCount(NWSCT + i + 1);
+	}
+
+	return i;
+}
+
+static unsigned int emi_polling(unsigned int *__restrict__ emi_value, unsigned int *__restrict__ emi_tsct,
+				unsigned int *__restrict__ emi_ttype_value, unsigned int *__restrict__ dramc_pdir_value,
+				unsigned int *__restrict__ emi_mdct_value)
+{
+	int j = 4;              /* skip 4 WSCTs */
+	int i = 0;              /* ttype start at 0 */
+	int k = 0;              /* tsct start at 0 */
+	int n;
+
+	/* Get Word Count */
+
+	emi_value[0] = MET_BM_GetWordCount(1);  /* All */
+	emi_value[1] = MET_BM_GetWordCount(2);  /* Group 1 */
+	emi_value[2] = MET_BM_GetWordCount(3);  /* Group 2 */
+	emi_value[3] = MET_BM_GetWordCount(4);  /* Group 3 */
+
+	if (ttype1_16_en != BM_TTYPE1_16_ENABLE) {      /*1~21 NOT for ttype*/
+		/* Get Latency */
+		j += MET_BM_GetLatencyCycle(emi_value + j, 1, 8);
+
+		/* Get Trans. */
+		j += MET_BM_GetLatencyCycle(emi_value + j, 9, 16);
+	} else {
+		for (n = 4; n < 20; n++)
+			emi_value[n] = 0;
+		j = 20;
+
+		i += MET_BM_GetLatencyCycle(emi_ttype_value + i, 1, 8);
+
+		/* Get Trans. */
+		i += MET_BM_GetLatencyCycle(emi_ttype_value + i, 9, 16);
+	}
+
+	/* Get BACT/BSCT/BCNT/WACT/DCM_CTRL */
+	emi_value[j++] = MET_BM_GetBandwidthWordCount(); /* 20 */
+	emi_value[j++] = MET_BM_GetOverheadWordCount();
+	emi_value[j++] = MET_BM_GetBusCycCount();
+	emi_value[j++] = MET_BM_GetWordAllCount();
+	emi_value[j++] = MET_DRAMC_DCM_CTRL(0);
+
+	/* Get TACT */
+	emi_value[j++] = MET_BM_GetTransAllCount();
+
+	/* Get PageHist/PageMiss/InterBank/Idle */
+	for (n = 0; n < dram_chann_num; n++) {
+		emi_value[j++] = MET_DRAMC_GetPageHitCount(DRAMC_ALL, n);
+		emi_value[j++] = MET_DRAMC_GetPageMissCount(DRAMC_ALL, n);
+		emi_value[j++] = MET_DRAMC_GetInterbankCount(DRAMC_ALL, n);
+		emi_value[j++] = MET_DRAMC_GetIdleCount(n);
+		emi_value[j++] = ((MET_DRAMC_Misc_Status(n) >> 8) & 0x7); /* refresh rate */
+		emi_value[j++] = MET_DRAMC_RefPop(n);
+		emi_value[j++] = MET_DRAMC_Free26M(n);
+		emi_value[j++] = MET_DRAMC_RByte(n);
+		emi_value[j++] = MET_DRAMC_WByte(n);
+	}
+	/* TTYPE */
+	if (ttype17_21_en == BM_TTYPE17_21_ENABLE)    /*17~21 for ttype*/
+		MET_BM_GetLatencyCycle(emi_ttype_value + 16, 17, 21);
+
+	/* Get tsct */
+	if (emi_tsct_enable == 1) {
+		emi_tsct[k++] = MET_BM_GetTransCount(1);
+		emi_tsct[k++] = MET_BM_GetTransCount(2);
+		emi_tsct[k++] = MET_BM_GetTransCount(3);
+	}
+	/*get mdct rsv buffer*/
+	if (emi_mdct_enable == 1) {
+		emi_mdct_value[0] = (MET_BM_GetMDCT() >> 16) & 0x7;
+		emi_mdct_value[1] = (MET_BM_GetMDCT_2() & 0x7);
+	}
+
+	if (dramc_pdir_enable == 1 && DRAMC_VER >= 2 )
+		_ms_dramc(dramc_pdir_value, dram_chann_num);
+
+	return j;
+}
+
+
+/*======================================================================*/
+/*	MET Device Operations						*/
+/*======================================================================*/
+static int emi_inited;
+
+static int met_emi_create(struct kobject *parent)
+{
+	int ret = 0;
+	int i;
+
+	for (i = 0; i < 21; i++) {
+		ttype_master_val[i] = BM_MASTER_M0;
+		ttype_nbeat_val[i] = BM_TRANS_TYPE_1BEAT;
+		ttype_nbyte_val[i] = BM_TRANS_TYPE_8Byte;
+		ttype_burst_val[i] = BM_TRANS_TYPE_BURST_INCR;
+		ttype_busid_val[i] = 0xfffff;   /*default disable ttype bus sel if busid > 0xff_ff */
+		ttype_rw_val[i] =  BM_TRANS_RW_DEFAULT;
+	}
+
+	_clear_msel_group_ext();
+	_clear_wsct_rw();
+	_clear_wsct_high_priority_enable();
+	_clear_wsct_busid();
+	_clear_wsct_chn_rank_sel();
+	_clear_wsct_burst_range();
+
+	_clear_tsct_busid_enable();
+	_clear_ttype_high_priority_ext();
+	_clear_ttype_high_priority_ext();
+	_clear_ttype_busid_ext();
+	_clear_ttype_chn_rank_sel();
+	_clear_ttype_burst_range();
+
+	reserve_wsct_setting = 0;
+
+
+	ret = MET_BM_Init();
+	if (ret != 0) {
+		pr_notice("MET_BM_Init failed!!!\n");
+		ret = 0;        /* will retry later */
+	} else {
+		emi_inited = 1;
+	}
+
+	kobj_emi = parent;
+
+#define KOBJ_ATTR_ITEM(attr_name) \
+	do { \
+		ret = sysfs_create_file(kobj_emi, &attr_name ## _attr.attr); \
+		if (ret != 0) { \
+			pr_notice("Failed to create " #attr_name " in sysfs\n"); \
+			return ret; \
+		} \
+	} while (0)
+	KOBJ_ATTR_LIST;
+#undef  KOBJ_ATTR_ITEM
+
+	return ret;
+}
+
+
+static void met_emi_delete(void)
+{
+#define KOBJ_ATTR_ITEM(attr_name) \
+	sysfs_remove_file(kobj_emi, &attr_name ## _attr.attr)
+	if (kobj_emi != NULL) {
+		KOBJ_ATTR_LIST;
+		kobj_emi = NULL;
+	}
+#undef  KOBJ_ATTR_ITEM
+
+	if (emi_inited)
+		MET_BM_DeInit();
+}
+
+
+static void met_emi_start(void)
+{
+	unsigned int bw_limiter[NIDX_BL];
+
+	if (!emi_inited) {
+		if (MET_BM_Init() != 0) {
+			met_emi.mode = 0;
+			pr_notice("MET_BM_Init failed!!!\n");
+			return;
+		}
+		emi_inited = 1;
+	}
+
+	if (do_emi()) {
+		emi_init();
+		MET_BM_Clear_Start();
+		/* Draw the first BW Limiter point */
+		if (bw_limiter_enable == BM_BW_LIMITER_ENABLE) {
+			emi_bw_limiter(bw_limiter);
+			ms_bw_limiter(NIDX_BL, bw_limiter);
+			/* init countdown value */
+			countdown = CNT_COUNTDOWN;
+		}
+	}
+}
+
+
+static void met_emi_stop(void)
+{
+	unsigned int bw_limiter[NIDX_BL];
+
+	if (!emi_inited)
+		return;
+
+	if (met_emi_regdump == 1)
+		emi_dump_reg();
+
+	if (do_emi()) {
+		/* Draw the last BW Limiter point */
+		if (bw_limiter_enable == BM_BW_LIMITER_ENABLE) {
+			/*
+			 * Skip drawing when we just draw
+			 * the point at last polling.
+			 */
+			if (countdown < CNT_COUNTDOWN) {
+				emi_bw_limiter(bw_limiter);
+				ms_bw_limiter(NIDX_BL, bw_limiter);
+			}
+		}
+
+		emi_stop();
+		emi_uninit();
+	}
+}
+
+
+/* DRS output */
+noinline void emi_drs(uint32_t *value)
+{
+	MET_TRACE("%u,%u,%u,%u,%u,%u\n", value[0], value[1], value[2], value[3], value[4], value[5]);
+}
+
+/* DRS output */
+noinline void emi_ddr_ratio(unsigned int ddr_ratio)
+{
+	MET_TRACE("%u\n", ddr_ratio);
+}
+
+static void met_emi_polling(unsigned long long stamp, int cpu)
+{
+	unsigned int emi_value[NIDX];
+	unsigned int emi_tsct[3];
+	unsigned int emi_ttype_value[21];
+	unsigned int dramc_pdir_value[DRAMC_Debug_MAX_CNT * NCH];
+	unsigned int emi_mdct_value[2];
+	unsigned int chn_emi_drs[6];
+	unsigned int emi_value_ext[WSCT_AMOUNT - NWSCT];
+	unsigned int emi_ddr_ratio_val;
+
+	if (!do_emi())
+		return;
+
+	/* print the dram div ratio first */
+	emi_ddr_ratio_val = MET_EMI_Get_BaseClock_Rate();
+	emi_ddr_ratio(emi_ddr_ratio_val);
+
+
+	/* get emi & dramc counters */
+	emi_value[0] = 0;       /* 0: pure linux MET , 0xa5: OnDieMET*/
+	emi_value[1] = 0;       /* EBM pause duration (ns)*/
+
+	MET_BM_Pause();
+
+	emi_polling(emi_value + 2, emi_tsct, emi_ttype_value, dramc_pdir_value, emi_mdct_value);
+	emi_polling_ext(emi_value_ext);
+
+	MET_BM_Continue();
+	MET_BM_Clear_Start();
+
+	/* get and output BW Limiter */
+	if (bw_limiter_enable == BM_BW_LIMITER_ENABLE) {
+		unsigned int bw_limiter[NIDX_BL];
+
+		if (countdown > 0) {
+			countdown--;
+		} else {
+			emi_bw_limiter(bw_limiter);
+			ms_bw_limiter(NIDX_BL, bw_limiter);
+			/* reload countdown value */
+			countdown = CNT_COUNTDOWN;
+		}
+	}
+
+	/* output emi */
+	ms_emi(NIDX_EMI - NTTYPE + (NCNT * dram_chann_num), emi_value);
+	ms_emi_ext(WSCT_AMOUNT - NWSCT, emi_value_ext);
+
+	/* output tsct*/
+	if (emi_tsct_enable == 1)
+		ms_emi_tsct(TSCT_AMOUNT, emi_tsct);
+
+	/* output mdct*/
+	if (emi_mdct_enable == 1)
+		ms_emi_mdct(2, emi_mdct_value);
+
+	/* output dramc*/
+	if (dramc_pdir_enable == 1 && DRAMC_VER >= 2 )
+		ms_dramc(DRAMC_Debug_MAX_CNT * dram_chann_num, dramc_pdir_value);
+
+	/* output ms_ttype */
+	if (ttype17_21_en == BM_TTYPE17_21_ENABLE)
+		ms_ttype(21, emi_ttype_value);
+
+	/* adjust MDMCU buffer */
+	if (mdmcu_sel_enable == 1)
+		MET_BM_SetMDCT_MDMCU(rd_mdmcu_rsv_num);
+
+	/* DRS handling */
+	met_get_drs_registers(chn_emi_drs);
+	emi_drs(chn_emi_drs);
+}
+
+
+static void met_emi_resume(void)
+{
+	/* return directly when emi was closed */
+	if (!do_emi())
+		return;
+
+	/* remap EMI_BM related reg*/
+	emi_init();
+
+	/* restarn counting */
+	MET_BM_Clear_Start();
+}
+
+
+static const char help[] = "  --emi                                 monitor EMI banwidth\n";
+static int emi_print_help(char *buf, int len)
+{
+	return snprintf(buf, PAGE_SIZE, help);
+}
+
+
+#define TTYPE_NAME_STR_LEN  64
+/* static char ttype_name[21][TTYPE_NAME_STR_LEN]; */
+static int emi_print_header(char *buf, int len)
+{
+	int ret = 0;
+/*	int ret_m[21]; */
+	int i = 0;
+
+#if 1 /* move to AP side print header */
+/*#ifndef CONFIG_MTK_TINYSYS_SSPM_SUPPORT*/
+	unsigned int dram_data_rate_MHz;
+	unsigned int DRAM_TYPE;
+	unsigned int base_clock_rate;
+#endif
+
+#if 0 /* SEDA 3.0 and early*/
+	if ((ttype1_16_en != BM_TTYPE1_16_ENABLE) && (emi_TP_busfiltr_enable != 1)) {
+		/* master selection header */
+		if (msel_enable) {
+			ret += snprintf(buf + ret, PAGE_SIZE - ret,
+					"met-info [000] 0.0: met_emi_msel: %x,%x,%x\n",
+					msel_group1 & BM_MASTER_ALL,
+					msel_group2 & BM_MASTER_ALL,
+					msel_group3 & BM_MASTER_ALL);
+		} else {
+			ret += snprintf(buf + ret, PAGE_SIZE - ret,
+					"met-info [000] 0.0: met_emi_msel: %x,%x,%x\n",
+					BM_MASTER_ALL & BM_MASTER_ALL,
+					BM_MASTER_ALL & BM_MASTER_ALL,
+					BM_MASTER_ALL & BM_MASTER_ALL);
+		}
+	} else {
+		/*ttype master if BM_TTYPE1_16_ENABLE or emi_TP_busfiltr_enable*/
+		ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"met-info [000] 0.0: met_emi_ttype_master: %x,%x,%x,%x\n",
+				ttype_master_val[0], ttype_master_val[1], ttype_master_val[2], ttype_master_val[3]);
+
+		if (emi_TP_busfiltr_enable == 1) {
+			/* busID if emi_TP_busfiltr_enable*/
+			ret += snprintf(buf + ret, PAGE_SIZE - ret,
+					"met-info [000] 0.0: met_emi_ttype_busid: %x,%x,%x,%x\n",
+					ttype_busid_val[0], ttype_busid_val[1], ttype_busid_val[2], ttype_busid_val[3]);
+		}
+	}
+#endif
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+		"met-info [000] 0.0: met_emi_wsct_amount: %d\n",WSCT_AMOUNT);
+
+	/* master selection header */
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+		"met-info [000] 0.0: met_emi_msel: %x,%x,%x\n",
+		msel_group_ext_val[1] & BM_MASTER_ALL,
+		msel_group_ext_val[2] & BM_MASTER_ALL,
+		msel_group_ext_val[3] & BM_MASTER_ALL);
+
+	/*Ttype RW type header*/
+	PR_BOOTMSG("rwtype=%d\n",rwtype);
+	ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_emi_rw_cfg: ");
+	if (rwtype == BM_READ_ONLY)
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "R");
+	else if (rwtype == BM_WRITE_ONLY)
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "W");
+	else
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "BOTH");
+
+	for (i = 0; i < 21; i++) {
+		if (ttype_rw_val[i] == BM_TRANS_RW_DEFAULT)
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, ",DEFAULT");
+		else if (ttype_rw_val[i] == BM_TRANS_RW_READONLY)
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, ",R");
+		else if (ttype_rw_val[i] == BM_TRANS_RW_WRITEONLY)
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, ",W");
+		else    /*BM_TRANS_RW_RWBOTH*/
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, ",BOTH");
+	}
+	ret += snprintf(buf + ret, PAGE_SIZE - ret, "\n");
+
+	/*ultra header*/
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"met-info [000] 0.0: met_emi_ultra_filter: %x\n", high_priority_filter);
+
+	/* ttype header */
+	if (ttype17_21_en == BM_TTYPE17_21_ENABLE) {
+		int i = 0;
+		int j = 0;
+
+		/* ttype master list */
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_emi_ttype_master_list: ");
+		for (i = 0; i < 21; i++) {
+			for (j = 0; j < ARRAY_SIZE(ttype_master_list_item); j++) {
+				if (ttype_master_val[i] == ttype_master_list_item[j].key) {
+					ret += snprintf(buf + ret, PAGE_SIZE - ret, "%s,", ttype_master_list_item[j].val);
+				}
+			}
+		}
+		/* remove the last comma */
+		snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+		/* ttype busid list */
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_emi_ttype_busid_list: ");
+		for (i = 0; i < 21; i++)
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, "%x,", ttype_busid_val[i]);
+
+		snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+		/* ttype nbeat list */
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_emi_ttype_nbeat_list: ");
+		for (i = 0; i < 21; i++) {
+			for (j = 0; j < ARRAY_SIZE(ttype_nbeat_list_item); j++) {
+				if (ttype_nbeat_val[i] == ttype_nbeat_list_item[j].key) {
+					ret += snprintf(buf + ret, PAGE_SIZE - ret, "%d,", ttype_nbeat_list_item[j].val);
+				}
+			}
+		}
+		snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+		/* ttype nbyte list */
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_emi_ttype_nbyte_list: ");
+		for (i = 0; i < 21; i++) {
+			for (j = 0; j < ARRAY_SIZE(ttype_nbyte_list_item); j++) {
+				if (ttype_nbyte_val[i] == ttype_nbyte_list_item[j].key) {
+					ret += snprintf(buf + ret, PAGE_SIZE - ret, "%d,", ttype_nbyte_list_item[j].val);
+				}
+			}
+		}
+		snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+		/* ttype burst list */
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_emi_ttype_burst_list: ");
+		for (i = 0; i < 21; i++) {
+			for (j = 0; j < ARRAY_SIZE(ttype_burst_list_item); j++) {
+				if (ttype_burst_val[i] == ttype_burst_list_item[j].key) {
+					ret += snprintf(buf + ret, PAGE_SIZE - ret, "%s,", ttype_burst_list_item[j].val);
+				}
+			}
+		}
+		snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+	}
+	/* ttype enable */
+	ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_emi_ttype_enable: %d,%d\n",ttype1_16_en, ttype17_21_en);
+
+
+#if 1 /*SEDA 3.5*/
+
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+		"met-info [000] 0.0: met_emi_msel_ext: %x,%x,%x\n",
+		msel_group_ext_val[0] & BM_MASTER_ALL,
+		msel_group_ext_val[4] & BM_MASTER_ALL,
+		msel_group_ext_val[5] & BM_MASTER_ALL);
+
+	ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_emi_wsct_rw: ");
+
+	for (i=0;i<WSCT_AMOUNT;i++) {
+		if (wsct_rw_val[i] == BM_WSCT_RW_RWBOTH)
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, "RW,");
+		else if (wsct_rw_val[i] == BM_WSCT_RW_READONLY)
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, "R,");
+		else if (wsct_rw_val[i] == BM_WSCT_RW_WRITEONLY)
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, "W,");
+		else    /*disable*/
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, "NONE,");
+	}
+	snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+
+	ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_emi_wsct_HPRI_DIS: ");
+	for (i=0;i<WSCT_AMOUNT;i++) {
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "%d,",WSCT_HPRI_DIS[i]);
+	}
+	snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+
+	ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_emi_wsct_HPRI_SEL: ");
+	for (i=0;i<WSCT_AMOUNT;i++) {
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "%x,",WSCT_HPRI_SEL[i]);
+	}
+	snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+	ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_emi_wsct_busid: ");
+	for (i=0;i<WSCT_AMOUNT;i++) {
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "%x,",wsct_busid_val[i]);
+	}
+	snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+
+	ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_emi_wsct_idMask: ");
+	for (i=0;i<WSCT_AMOUNT;i++) {
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "%x,",wsct_idMask_val[i]);
+	}
+	snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+	ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: wsct_chn_rank_sel: ");
+	for (i=0;i<WSCT_AMOUNT;i++) {
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "%x,",wsct_chn_rank_sel_val[i]);
+	}
+	snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+	ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: wsct_byte_bnd_dis: ");
+	for (i=0;i<WSCT_AMOUNT;i++) {
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "%d,",wsct_byte_bnd_dis[i]);
+	}
+	snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+
+	ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: wsct_byte_low_bnd: ");
+	for (i=0;i<WSCT_AMOUNT;i++) {
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "%x,",wsct_byte_low_bnd_val[i]);
+	}
+	snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+	ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: wsct_byte_up_bnd: ");
+	for (i=0;i<WSCT_AMOUNT;i++) {
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "%x,",wsct_byte_up_bnd_val[i]);
+	}
+	snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+	ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: tsct_busid_enable: ");
+	for (i=0;i<TSCT_AMOUNT;i++) {
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "%d,",tsct_busid_enable_val[i]);
+	}
+	snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+	/***************************** ttype ****************************************/
+	if (ttype17_21_en == BM_TTYPE17_21_ENABLE) {
+
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: TTYPE_HPRI_SEL: ");
+		for (i=0;i<BM_COUNTER_MAX;i++) {
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, "%x,",TTYPE_HPRI_SEL[i]);
+		}
+		snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: ttype_idMask: ");
+		for (i=0;i<BM_COUNTER_MAX;i++) {
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, "%x,",ttype_idMask_val[i]);
+		}
+		snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: ttype_chn_rank_sel: ");
+		for (i=0;i<BM_COUNTER_MAX;i++) {
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, "%x,",ttype_chn_rank_sel_val[i]);
+		}
+		snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: ttype_byte_bnd_dis: ");
+		for (i=0;i<BM_COUNTER_MAX;i++) {
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, "%d,",ttype_byte_bnd_dis[i]);
+		}
+		snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: ttype_byte_low_bnd_val: ");
+		for (i=0;i<BM_COUNTER_MAX;i++) {
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, "%x,",ttype_byte_low_bnd_val[i]);
+		}
+		snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: ttype_byte_up_bnd_val: ");
+		for (i=0;i<BM_COUNTER_MAX;i++) {
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, "%x,",ttype_byte_up_bnd_val[i]);
+		}
+		snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+	}
+#endif
+
+	/*IP version*/
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"met-info [000] 0.0: DRAMC_VER: %d\n", DRAMC_VER);
+
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"met-info [000] 0.0: EMI_VER: %d.%d\n", EMI_VER_MAJOR, EMI_VER_MINOR);
+
+#if 1 /* SEDA3.5 header print move to AP side */
+/*#ifndef CONFIG_MTK_TINYSYS_SSPM_SUPPORT*/
+	dram_chann_num = MET_EMI_GetDramChannNum();
+	/*	met_dram_chann_num_header
+	 *	channel number
+	 *	LP4: 2, LP3: 1
+	 */
+
+	/*
+	 *	the ddr type define :
+	 *	enum DDRTYPE {
+	 *	TYPE_LPDDR3 = 1,
+	 *	TYPE_LPDDR4,
+	 *	TYPE_LPDDR4X,
+	 *	TYPE_LPDDR2
+	 *	};
+	 */
+	if (!get_cur_ddr_ratio_symbol)
+		PR_BOOTMSG("[%s][%d]get_cur_ddr_ratio_symbol = NULL , use the TYPE_LPDDR4 get_cur_ddr_ratio_symbol\n", __func__, __LINE__);
+
+	if (get_ddr_type_symbol) {
+		DRAM_TYPE = get_ddr_type_symbol();
+
+		base_clock_rate = MET_EMI_Get_BaseClock_Rate();
+
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_dram_type: %d\n", DRAM_TYPE);
+
+		if ((DRAM_TYPE == 2) || (DRAM_TYPE == 3))
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_dram_chann_num_header: %d,%d,%d,%d\n",
+					dram_chann_num, base_clock_rate,
+					DRAM_IO_BUS_WIDTH_LP4, DRAM_DATARATE);
+		else
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_dram_chann_num_header: %d,%d,%d,%d\n",
+					dram_chann_num, base_clock_rate,
+					DRAM_IO_BUS_WIDTH_LP3, DRAM_DATARATE);
+	} else
+		METERROR("[%s][%d]get_ddr_type_symbol = NULL , use the TYPE_LPDDR3 setting\n", __func__, __LINE__);
+
+	/* metemi_func_opt for middleware */
+	ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: metemi_func_opt_header: %d\n",
+			metemi_func_opt);
+
+	/* met_emi_clockrate */
+	if (get_dram_data_rate_symbol) {
+		dram_data_rate_MHz = get_dram_data_rate_symbol();
+	} else {
+		METERROR("get_dram_data_rate_symbol = NULL\n");
+		dram_data_rate_MHz = 0;
+	}
+
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"met-info [000] 0.0: met_dram_clockrate: %d\n",
+			dram_data_rate_MHz);
+
+	/* 1 : by ondiemet, 0: by pure linux */
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"met-info [000] 0.0: emi_use_ondiemet: %u\n",
+			emi_use_ondiemet);
+
+	/*dram bank num*/
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"met-info [000] 0.0: met_dram_rank_num_header: %u,%u\n", MET_EMI_GetDramRankNum(),
+				MET_EMI_GetDramRankNum());
+
+	/* ms_emi header */
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"# ms_emi: TS0,TS1,GP0_WSCT,GP1_WSCT,GP2_WSCT,GP3_WSCT,");
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"M0_LATENCY,M1_LATENCY,M2_LATENCY,M3_LATENCY,M4_LATENCY,M5_LATENCY,M6_LATENCY,M7_LATENCY,");
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"M0_TRANS,M1_TRANS,M2_TRANS,M3_TRANS,M4_TRANS,M5_TRANS,M6_TRANS,M7_TRANS,");
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"BACT,BSCT,BCNT,WACT,DCM_CTRL,TACT,");
+
+	for (i = 0; i < dram_chann_num; i++) {
+		if (i != 0)
+			ret += snprintf(buf + ret, PAGE_SIZE - ret,
+					",");
+		ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"PageHit_%d,PageMiss_%d,InterBank_%d,Idle_%d,", i, i, i, i);
+		ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"mr4_%d,refresh_pop_%d,freerun_26m_%d,", i, i, i);
+		ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"read_bytes_%d,write_bytes_%d", i, i);
+	}
+	ret += snprintf(buf + ret, PAGE_SIZE - ret, "\n");
+
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"met-info [000] 0.0: met_emi_header: TS0,TS1,GP0_WSCT,GP1_WSCT,GP2_WSCT,GP3_WSCT,");
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"M0_LATENCY,M1_LATENCY,M2_LATENCY,M3_LATENCY,M4_LATENCY,M5_LATENCY,M6_LATENCY,M7_LATENCY,");
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"M0_TRANS,M1_TRANS,M2_TRANS,M3_TRANS,M4_TRANS,M5_TRANS,M6_TRANS,M7_TRANS,");
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"BACT,BSCT,BCNT,WACT,DCM_CTRL,TACT,");
+
+	for (i = 0; i < dram_chann_num; i++) {
+		if (i != 0)
+			ret += snprintf(buf + ret, PAGE_SIZE - ret,
+					",");
+		ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"PageHit_%d,PageMiss_%d,InterBank_%d,Idle_%d,", i, i, i, i);
+		ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"mr4_%d,refresh_pop_%d,freerun_26m_%d,", i, i, i);
+		ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"read_bytes_%d,write_bytes_%d", i, i);
+	}
+	ret += snprintf(buf + ret, PAGE_SIZE - ret, "\n");
+
+	/*TSCT header*/
+	if (emi_tsct_enable == 1) {
+		ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"met-info [000] 0.0: ms_emi_tsct_header: ms_emi_tsct,");
+		ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"tsct1,tsct2,tsct3\n");
+	}
+
+	/*MDCT header*/
+	if (emi_mdct_enable == 1) {
+		ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"met-info [000] 0.0: ms_emi_mdct_header: ms_emi_mdct,");
+		ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"RD_ULTRA,RD_MDMCU\n");
+	}
+
+	/* met_bw_limiter_header */
+	if (bw_limiter_enable == BM_BW_LIMITER_ENABLE) {
+		ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"met-info [000] 0.0: met_bw_limiter_header: CLK,");
+		ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"ARBA,ARBB,ARBC,ARBD,ARBE,ARBF,ARBG,ARBH,BWCT0,BWCT1,BWCT2,BWCT3,BWCT4,BWST0,BWST1,BWCT0_2ND,BWCT1_2ND,BWST_2ND\n");
+	}
+
+	/* DRAM DVFS header */
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"met-info [000] 0.0: DRAM_DVFS_header: datarate(MHz)\n");
+
+	/*PDIR met_dramc_header*/
+	if (dramc_pdir_enable == 1 && DRAMC_VER >= 2 ) {
+		ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"met-info [000] 0.0: met_dramc_header: ");
+		for (i = 0; i < dram_chann_num; i++) {
+			if (i != 0)
+				ret += snprintf(buf + ret, PAGE_SIZE - ret,
+						",");
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, "freerun_26m_%d,", i);
+			ret += snprintf(buf + ret, PAGE_SIZE - ret,
+					"rk0_pre_sb_%d,rk0_pre_pd_%d,rk0_act_sb_%d,rk0_act_pd_%d,", i, i, i, i);
+			ret += snprintf(buf + ret, PAGE_SIZE - ret,
+					"rk1_pre_sb_%d,rk1_pre_pd_%d,rk1_act_sb_%d,rk1_act_pd_%d,", i, i, i, i);
+			ret += snprintf(buf + ret, PAGE_SIZE - ret,
+					"rk2_pre_sb_%d,rk2_pre_pd_%d,rk2_act_sb_%d,rk2_act_pd_%d", i, i, i, i);
+		}
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "\n");
+	}
+
+	/* DRS header */
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"met-info [000] 0.0: emi_drs_header: ch0_RANK1_GP(%%),ch0_RANK1_SF(%%),ch0_ALL_SF(%%),ch1_RANK1_GP(%%),ch1_RANK1_SF(%%),ch1_ALL_SF(%%)\n");
+#endif
+
+	return ret;
+}
+
+
+#if defined(CONFIG_MTK_TINYSYS_SSPM_SUPPORT) && defined(ONDIEMET_SUPPORT)
+static int ondiemet_emi_print_header(char *buf, int len)
+{
+	emi_use_ondiemet = 1;
+	/* return emi_print_ondiemet_header(buf, len); */
+	return emi_print_header(buf, len);
+}
+
+
+static void MET_BM_IPI_REGISTER_CB(void)
+{
+	int ret, i;
+	unsigned int rdata;
+	unsigned int ipi_buf[4];
+
+	for (i = 0; i < 4; i++)
+		ipi_buf[i] = 0;
+
+	if (sspm_buf_available == 1) {
+		ipi_buf[0] = MET_MAIN_ID | (MID_EMI << MID_BIT_SHIFT) | MET_ARGU | SET_REGISTER_CB;
+		ret = sspm_ipi_send_sync(IPI_ID_MET, IPI_OPT_WAIT, (void *)ipi_buf, 0, &rdata, 1);
+	}
+}
+
+
+static void MET_BM_IPI_configs(void)
+{
+	int ret, i;
+	unsigned int rdata;
+	unsigned int ipi_buf[4];
+
+	for (i = 0; i < 4; i++)
+		ipi_buf[i] = 0;
+
+	if (sspm_buf_available == 1) {
+		ipi_buf[0] = MET_MAIN_ID | (MID_EMI << MID_BIT_SHIFT) | MET_ARGU | SET_EBM_CONFIGS1;
+		ipi_buf[1] = INTERNAL_MODE << 16;
+		ipi_buf[2] = EMI_VER_MAJOR << 24 | EMI_VER_MINOR << 16 | DRAMC_VER << 8 | 0;
+		ret = sspm_ipi_send_sync(IPI_ID_MET, IPI_OPT_WAIT, (void *)ipi_buf, 0, &rdata, 1);
+	}
+}
+
+
+static void ondiemet_emi_start(void)
+{
+	MET_BM_IPI_REGISTER_CB();
+	if (!emi_inited) {
+		if (MET_BM_Init() != 0) {
+			met_emi.mode = 0;
+			pr_notice("MET_BM_Init failed!!!\n");
+			return;
+		}
+		emi_inited = 1;
+	}
+	MET_BM_IPI_configs();
+
+	if (do_emi())
+		emi_init();
+
+	ondiemet_module[ONDIEMET_SSPM] |= ID_EMI;
+}
+
+
+static void ondiemet_emi_stop(void)
+{
+	if (!emi_inited)
+		return;
+
+	if (met_emi_regdump == 1)
+		emi_dump_reg();
+
+	if (do_emi())
+		emi_uninit();
+
+	if (met_emi_regdump == 1)
+		emi_dump_reg();
+}
+#endif
+
+
+struct metdevice met_emi = {
+	.name			= "emi",
+	.owner			= THIS_MODULE,
+	.type			= MET_TYPE_BUS,
+	.create_subfs		= met_emi_create,
+	.delete_subfs		= met_emi_delete,
+	.cpu_related		= 0,
+	.start			= met_emi_start,
+	.stop			= met_emi_stop,
+	.resume			= met_emi_resume,
+	.timed_polling		= met_emi_polling,
+	.print_help		= emi_print_help,
+	.print_header		= emi_print_header,
+#if defined(CONFIG_MTK_TINYSYS_SSPM_SUPPORT) && defined(ONDIEMET_SUPPORT)
+	.ondiemet_mode		= 1,
+	.ondiemet_start		= ondiemet_emi_start,
+	.ondiemet_stop		= ondiemet_emi_stop,
+	.ondiemet_print_help	= emi_print_help,
+	.ondiemet_print_header	= ondiemet_emi_print_header,
+#else
+	.ondiemet_mode		= 0,
+#endif
+};
diff --git a/src/devtools/met-driver/4.14/mt2731/emi/SEDA3_5/mtk_emi_bm.c b/src/devtools/met-driver/4.14/mt2731/emi/SEDA3_5/mtk_emi_bm.c
new file mode 100644
index 0000000..d965349
--- /dev/null
+++ b/src/devtools/met-driver/4.14/mt2731/emi/SEDA3_5/mtk_emi_bm.c
@@ -0,0 +1,1470 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/io.h>
+#include <mt-plat/sync_write.h>
+#include <mt-plat/mtk_io.h>
+#include "mtk_typedefs.h"
+#include "plf_init.h"
+#include "mtk_emi_bm.h"
+#include "mtk_dramc_reg.h"
+#include "met_drv.h"
+#include "interface.h"
+
+#undef	DEBUG
+#undef	debug_reg
+#ifdef	debug_reg
+static inline unsigned int emi_readl(void __iomem *padr)
+{
+	unsigned int tmp;
+
+	tmp = readl(padr);
+	MET_TRACE("[MET_EMI] RD_Reg: %p: %08x\n", padr, tmp);
+	return tmp;
+}
+
+static inline void __emi_reg_sync_writel(unsigned int data, void __iomem *padr)
+{
+	unsigned int tmp;
+
+	mt_reg_sync_writel(data, padr);
+	tmp = readl(padr);
+	MET_TRACE("[MET_EMI] WR_Reg: %p: %08x, %08x\n", padr, data, tmp);
+}
+
+#define emi_reg_sync_writel(data, adr)  __emi_reg_sync_writel(data, IOMEM(adr))
+
+#else
+#define emi_readl               readl
+#define emi_reg_sync_writel     mt_reg_sync_writel
+#endif
+
+#define MASK_MASTER     0xFF
+#define MASK_TRANS_TYPE 0xFF
+
+static int dram_chann_num;
+static void __iomem *BaseAddrEMI;
+static void __iomem *BaseAddrCHN_EMI[2];
+
+static int dramc0_dcm_enable;
+static int dramc1_dcm_enable;
+
+#define CH0_MISC_CG_CTRL0 (((unsigned long) BaseAddrDDRPHY_AO[0]) + 0x284)
+#define CH1_MISC_CG_CTRL0 (((unsigned long) BaseAddrDDRPHY_AO[1]) + 0x284)
+const unsigned int emi_config[] = {
+	EMI_BMEN,
+	EMI_MSEL,
+	EMI_MSEL2,
+	EMI_MSEL3,
+	EMI_MSEL4,
+	EMI_MSEL5,
+	EMI_MSEL6,
+	EMI_MSEL7,
+	EMI_MSEL8,
+	EMI_MSEL9,
+	EMI_MSEL10,
+	EMI_BMID0,
+	EMI_BMID1,
+	EMI_BMID2,
+	EMI_BMID3,
+	EMI_BMID4,
+	EMI_BMID5,
+	EMI_BMID6,
+	EMI_BMID7,
+	EMI_BMID8,
+	EMI_BMID9,
+	EMI_BMID10,
+	EMI_BMEN1,
+	EMI_BMEN2,
+	EMI_BMRW0,
+	EMI_BMRW1,
+	EMI_DBWA,
+	EMI_DBWB,
+	EMI_DBWC,
+	EMI_DBWD,
+	EMI_DBWE,
+	EMI_DBWF,
+	EMI_DBWI,
+	EMI_DBWJ,
+	EMI_DBWK,
+	EMI_DBWA_2ND,
+	EMI_DBWB_2ND,
+	EMI_DBWC_2ND,
+	EMI_DBWD_2ND,
+	EMI_DBWE_2ND,
+	EMI_DBWF_2ND,
+	EMI_TTYPE1_CONA,
+	EMI_TTYPE1_CONB,
+	EMI_TTYPE2_CONA,
+	EMI_TTYPE2_CONB,
+	EMI_TTYPE3_CONA,
+	EMI_TTYPE3_CONB,
+	EMI_TTYPE4_CONA,
+	EMI_TTYPE4_CONB,
+	EMI_TTYPE5_CONA,
+	EMI_TTYPE5_CONB,
+	EMI_TTYPE6_CONA,
+	EMI_TTYPE6_CONB,
+	EMI_TTYPE7_CONA,
+	EMI_TTYPE7_CONB,
+	EMI_TTYPE8_CONA,
+	EMI_TTYPE8_CONB,
+	EMI_TTYPE9_CONA,
+	EMI_TTYPE9_CONB,
+	EMI_TTYPE10_CONA,
+	EMI_TTYPE10_CONB,
+	EMI_TTYPE11_CONA,
+	EMI_TTYPE11_CONB,
+	EMI_TTYPE12_CONA,
+	EMI_TTYPE12_CONB,
+	EMI_TTYPE13_CONA,
+	EMI_TTYPE13_CONB,
+	EMI_TTYPE14_CONA,
+	EMI_TTYPE14_CONB,
+	EMI_TTYPE15_CONA,
+	EMI_TTYPE15_CONB,
+	EMI_TTYPE16_CONA,
+	EMI_TTYPE16_CONB,
+	EMI_TTYPE17_CONA,
+	EMI_TTYPE17_CONB,
+	EMI_TTYPE18_CONA,
+	EMI_TTYPE18_CONB,
+	EMI_TTYPE19_CONA,
+	EMI_TTYPE19_CONB,
+	EMI_TTYPE20_CONA,
+	EMI_TTYPE20_CONB,
+	EMI_TTYPE21_CONA,
+	EMI_TTYPE21_CONB
+};
+#define EMI_CONFIG_MX_NR (sizeof(emi_config)/sizeof(unsigned int))
+static unsigned int emi_config_val[EMI_CONFIG_MX_NR];
+
+/*
+ *   MET_REG_BSET/MET_REG_BCLR:
+ *   reading value before set and clear
+ */
+static inline void MET_REG_BSET(unsigned long reg, u32 shift)
+{
+	unsigned int read_val = 0;
+
+	read_val = emi_readl(IOMEM(reg));
+	emi_reg_sync_writel(read_val | (1 << shift), reg);
+}
+
+
+static inline void MET_REG_BCLR(unsigned long reg, u32 shift)
+{
+	unsigned int read_val = 0;
+
+	read_val = emi_readl(IOMEM(reg));
+	emi_reg_sync_writel(read_val & (~((1 << shift) & 0xFFFFFFFF)), reg);
+}
+
+
+int MET_BM_Init(void)
+{
+	int i;
+	int idx;
+
+	/*emi*/
+	if (!mt_cen_emi_base_get_symbol) {
+		METERROR("[%d]mt_cen_emi_base_get_symbol = NULL\n", __LINE__);
+		PR_BOOTMSG_ONCE("[%d]mt_cen_emi_base_get_symbol = NULL\n", __LINE__);
+		return -1;
+	}
+
+	BaseAddrEMI = mt_cen_emi_base_get_symbol();
+	if (BaseAddrEMI == 0) {
+		METERROR("BaseAddrEMI = 0\n");
+		PR_BOOTMSG_ONCE("BaseAddrEMI = 0\n");
+		return -1;
+	}
+
+	METINFO("MET EMI: map emi to %p\n", BaseAddrEMI);
+	PR_BOOTMSG("MET EMI: map emi to %p\n", BaseAddrEMI);
+
+	/* get dram channel number */
+	dram_chann_num = MET_EMI_GetDramChannNum();
+	METINFO("[%s][%d]dram_chann_num = %d\n", __func__, __LINE__, dram_chann_num);
+
+	if (dram_chann_num > MAX_DRAMC_CHANN) {
+		METERROR("dram_chann_num %d > %d\n", dram_chann_num, MAX_DRAMC_CHANN);
+		PR_BOOTMSG("dram_chann_num %d > %d\n", dram_chann_num, MAX_DRAMC_CHANN);
+		return -1;
+	}
+
+	if (!mt_dramc_nao_chn_base_get_symbol) {
+		METERROR("mt_dramc_nao_cha_base_get = NULL\n");
+		PR_BOOTMSG_ONCE("mt_dramc_nao_cha_base_get = NULL\n");
+		return -1;
+	}
+
+	for (i = 0; i < dram_chann_num; i++) {
+		BaseAddrDRAMC[i] = mt_dramc_nao_chn_base_get_symbol(i);
+		if (BaseAddrDRAMC[i] == 0) {
+			METERROR("BaseAddrDRAMC%d = 0\n", i);
+			PR_BOOTMSG_ONCE("BaseAddrDRAMC%d = 0\n", i);
+			return -1;
+		}
+
+		METINFO("MET EMI: map nao dramc%c to %p\n",'A'+i, BaseAddrDRAMC[i]);
+		PR_BOOTMSG("MET EMI: map nao dramc%c to %p\n", 'A'+i, BaseAddrDRAMC[i]);
+	}
+
+	/*dram DRAMC_DTS_DDRPHY_AO*/
+	/* get DRS base address */
+	if (!mt_ddrphy_chn_base_get_symbol) {
+		METERROR("mt_ddrphy_chn_base_get = NULL\n");
+		PR_BOOTMSG_ONCE("mt_ddrphy_chn_base_get = NULL\n");
+		return -1;
+	}
+
+	if (!mt_chn_emi_base_get_symbol) {
+		METERROR("mt_chn_emi_base_get_symbol = NULL\n");
+		PR_BOOTMSG_ONCE("mt_chn_emi_base_get_symbol = NULL\n");
+		return -1;
+	}
+
+	for (i = 1; i <= dram_chann_num && i < 3; i++) {
+		idx = i - 1;
+		BaseAddrDDRPHY_AO[idx] = mt_ddrphy_chn_base_get_symbol(idx);
+		if (BaseAddrDDRPHY_AO[idx] == 0) {
+			METERROR("BaseAddrDDRPHY_AO[%d] = 0\n", idx);
+			PR_BOOTMSG_ONCE("BaseAddrDDRPHY_AO[%d] = 0\n", idx);
+			return -1;
+		}
+
+		METINFO("MET EMI: map ddrphy%d AO to %p\n", idx, BaseAddrDDRPHY_AO[idx]);
+		PR_BOOTMSG("MET EMI: map ddrphy%d AO to %p\n", idx, BaseAddrDDRPHY_AO[idx]);
+
+		BaseAddrCHN_EMI[idx] = mt_chn_emi_base_get_symbol(idx);
+		if (BaseAddrCHN_EMI[idx] == 0) {
+			METERROR("BaseAddrCHN_EMI[%d] = 0\n", idx);
+			PR_BOOTMSG_ONCE("BaseAddrCHN_EMI[%d] = 0\n", idx);
+			return -1;
+		}
+
+		METINFO("MET EMI: map BaseAddrCHN_EMI[%d] to %p\n", idx, BaseAddrCHN_EMI[idx]);
+		PR_BOOTMSG("MET EMI: map BaseAddrCHN_EMI[%d] to %p\n", idx, BaseAddrCHN_EMI[idx]);
+	}
+
+	/*dram DRAMC_DTS_DRAMC0_AO*/
+	if (!mt_dramc_chn_base_get_symbol) {
+		METERROR("mt_dramc_chn_base_get = NULL\n");
+		PR_BOOTMSG_ONCE("mt_dramc_chn_base_get = NULL\n");
+		return -1;
+	}
+
+	BaseAddrDRAMC0_AO = mt_dramc_chn_base_get_symbol(0);
+	if (BaseAddrDRAMC0_AO == 0) {
+		METERROR("BaseAddrDRAMC0_AO = 0\n");
+		PR_BOOTMSG_ONCE("BaseAddrDRAMC0_AO = 0\n");
+		return -1;
+	}
+
+	METINFO("MET EMI: map AO dramcA to %p\n", BaseAddrDRAMC0_AO);
+	PR_BOOTMSG("MET EMI: map AO dramcA to %p\n", BaseAddrDRAMC0_AO);
+
+	return 0;
+}
+
+
+void MET_BM_DeInit(void)
+{
+}
+
+
+void MET_BM_SaveCfg(void)
+{
+	int i;
+
+	for (i = 0; i < EMI_CONFIG_MX_NR; i++)
+		emi_config_val[i] = emi_readl(IOMEM(ADDR_EMI + emi_config[i]));
+}
+
+
+void MET_BM_RestoreCfg(void)
+{
+	int i;
+
+	for (i = 0; i < EMI_CONFIG_MX_NR; i++)
+		emi_reg_sync_writel(emi_config_val[i], ADDR_EMI + emi_config[i]);
+}
+
+
+void MET_BM_Clear_Start(void)
+{
+	/* Force EMI idle low */
+	MET_REG_BSET(ADDR_EMI + EMI_BMEN, BUS_MON_IDLE_SHIFT);
+
+	/* Disable dramc dcm */
+	switch (dram_chann_num) {
+	case 1:
+		MET_REG_BSET(CH0_MISC_CG_CTRL0, DRAMC_CG_SHIFT);
+		break;
+	case 2:
+		MET_REG_BSET(CH0_MISC_CG_CTRL0, DRAMC_CG_SHIFT);
+		MET_REG_BSET(CH1_MISC_CG_CTRL0, DRAMC_CG_SHIFT);
+		break;
+	default:
+		METERROR("Error: dram_chann_num = %d\n", dram_chann_num);
+	}
+
+	/* Disable EBM */
+	MET_REG_BCLR(ADDR_EMI + EMI_BMEN, BUS_MON_EN_SHIFT);
+
+	/* Enable EBM */
+	MET_REG_BSET(ADDR_EMI + EMI_BMEN, BUS_MON_EN_SHIFT);
+
+	/* Enable EMI dcm */
+	MET_REG_BCLR(ADDR_EMI + EMI_BMEN, BUS_MON_IDLE_SHIFT);
+
+	/* restore dramc dcm */
+	switch (dram_chann_num) {
+	case 1:
+		if (dramc0_dcm_enable)
+			MET_REG_BCLR(CH0_MISC_CG_CTRL0, DRAMC_CG_SHIFT);
+		else
+			MET_REG_BSET(CH0_MISC_CG_CTRL0, DRAMC_CG_SHIFT);
+		break;
+	case 2:
+		if (dramc0_dcm_enable)
+			MET_REG_BCLR(CH0_MISC_CG_CTRL0, DRAMC_CG_SHIFT);
+		else
+			MET_REG_BSET(CH0_MISC_CG_CTRL0, DRAMC_CG_SHIFT);
+
+		if (dramc1_dcm_enable)
+			MET_REG_BCLR(CH1_MISC_CG_CTRL0, DRAMC_CG_SHIFT);
+		else
+			MET_REG_BSET(CH1_MISC_CG_CTRL0, DRAMC_CG_SHIFT);
+		break;
+	default:
+		METERROR("Error: dram_chann_num = %d\n", dram_chann_num);
+	}
+}
+
+
+void MET_BM_Enable(const unsigned int enable)
+{
+	unsigned long int value_check;
+	int i = 0;
+
+	while (i < 100) {
+		/* Force EMI idle low */
+		MET_REG_BSET(ADDR_EMI + EMI_BMEN, BUS_MON_IDLE_SHIFT);
+
+		/* disable dramc dcm */
+		switch (dram_chann_num) {
+		case 1:
+			MET_REG_BSET(CH0_MISC_CG_CTRL0, DRAMC_CG_SHIFT);
+			break;
+		case 2:
+			MET_REG_BSET(CH0_MISC_CG_CTRL0, DRAMC_CG_SHIFT);
+			MET_REG_BSET(CH1_MISC_CG_CTRL0, DRAMC_CG_SHIFT);
+			break;
+		default:
+			METERROR("Error: dram_chann_num = %d\n", dram_chann_num);
+		}
+
+		if (enable == 0)
+			/* Disable EBM */
+			MET_REG_BCLR(ADDR_EMI + EMI_BMEN, BUS_MON_EN_SHIFT);
+		else
+			/* Enable EBM */
+			MET_REG_BSET(ADDR_EMI + EMI_BMEN, BUS_MON_EN_SHIFT);
+
+		/* Enable EMI dcm */
+		MET_REG_BCLR(ADDR_EMI + EMI_BMEN, BUS_MON_IDLE_SHIFT);
+
+		/* restore dramc dcm */
+		switch (dram_chann_num) {
+		case 1:
+			if (dramc0_dcm_enable)
+				MET_REG_BCLR(CH0_MISC_CG_CTRL0, DRAMC_CG_SHIFT);
+			else
+				MET_REG_BSET(CH0_MISC_CG_CTRL0, DRAMC_CG_SHIFT);
+			break;
+		case 2:
+			if (dramc0_dcm_enable)
+				MET_REG_BCLR(CH0_MISC_CG_CTRL0, DRAMC_CG_SHIFT);
+			else
+				MET_REG_BSET(CH0_MISC_CG_CTRL0, DRAMC_CG_SHIFT);
+
+			if (dramc1_dcm_enable)
+				MET_REG_BCLR(CH1_MISC_CG_CTRL0, DRAMC_CG_SHIFT);
+			else
+				MET_REG_BSET(CH1_MISC_CG_CTRL0, DRAMC_CG_SHIFT);
+			break;
+		default:
+			METERROR("Error: dram_chann_num = %d\n", dram_chann_num);
+		}
+
+		value_check = emi_readl(IOMEM(ADDR_EMI + EMI_BMEN));
+
+		if (enable == 0) {
+			/* EN == 0, IDLE == 0 when EMI RESET */
+			if (!test_bit(BUS_MON_EN_SHIFT, &value_check)
+			    && !test_bit(BUS_MON_IDLE_SHIFT, &value_check)) {
+				break;
+			}
+		} else {
+			/* EN == 1, IDLE == 0 when EMI START */
+			if (test_bit(BUS_MON_EN_SHIFT, &value_check)
+			    && !test_bit(BUS_MON_IDLE_SHIFT, &value_check)) {
+				break;
+			}
+		}
+		i++;
+	}
+
+	/*MET_TRACE("[MET_BM_ENABLE] value_check: %lx, enable = %d\n", value_check, enable); */
+
+}
+
+
+#if 0
+void BM_Disable(void)
+{
+	const unsigned int value = emi_readl(IOMEM(ADDR_EMI + EMI_BMEN));
+
+	emi_reg_sync_writel(value & (~BUS_MON_EN), ADDR_EMI + EMI_BMEN);
+}
+#endif
+
+
+void MET_BM_Pause(void)
+{
+	const unsigned int value = emi_readl(IOMEM(ADDR_EMI + EMI_BMEN));
+
+	emi_reg_sync_writel(value | (1 << BUS_MON_PAUSE_SHIFT), ADDR_EMI + EMI_BMEN);
+}
+
+
+void MET_BM_Continue(void)
+{
+	const unsigned int value = emi_readl(IOMEM(ADDR_EMI + EMI_BMEN));
+
+	emi_reg_sync_writel(value & (~(1 << BUS_MON_PAUSE_SHIFT)), ADDR_EMI + EMI_BMEN);
+}
+
+
+unsigned int MET_BM_IsOverrun(void)
+{
+	/*
+	 * return 0 if EMI_BCNT(bus cycle counts) or
+	 * EMI_WACT(total word counts) is overrun,
+	 * otherwise return an !0 value
+	 */
+	const unsigned int value = emi_readl(IOMEM(ADDR_EMI + EMI_BMEN));
+
+	return (value & (1 << BC_OVERRUN_SHIFT));
+}
+
+
+unsigned int MET_BM_GetReadWriteType(void)
+{
+	const unsigned int value = emi_readl(IOMEM(ADDR_EMI + EMI_BMEN));
+
+	return ((value & 0xFFFFFFCF) >> 4);
+}
+
+
+void MET_BM_SetReadWriteType(const unsigned int ReadWriteType)
+{
+	const unsigned int value = emi_readl(IOMEM(ADDR_EMI + EMI_BMEN));
+
+	/*
+	 * ReadWriteType: 00/11 --> both R/W
+	 *                   01 --> only R
+	 *                   10 --> only W
+	 */
+	emi_reg_sync_writel((value & 0xFFFFFFCF) | (ReadWriteType << 4), ADDR_EMI + EMI_BMEN);
+}
+
+
+int MET_BM_GetBusCycCount(void)
+{
+	return MET_BM_IsOverrun() ? BM_ERR_OVERRUN : emi_readl(IOMEM(ADDR_EMI + EMI_BCNT));	/*Bus cycle counter */
+}
+
+
+unsigned int MET_BM_GetTransAllCount(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI + EMI_TACT));
+}
+
+
+int MET_BM_GetTransCount(const unsigned int counter_num)
+{
+	unsigned int iCount;
+
+	switch (counter_num) {
+	case 1:
+		iCount = emi_readl(IOMEM(ADDR_EMI + EMI_TSCT));
+		break;
+
+	case 2:
+		iCount = emi_readl(IOMEM(ADDR_EMI + EMI_TSCT2));
+		break;
+
+	case 3:
+		iCount = emi_readl(IOMEM(ADDR_EMI + EMI_TSCT3));
+		break;
+
+	default:
+		return BM_ERR_WRONG_REQ;
+	}
+
+	return iCount;
+}
+
+
+int MET_BM_GetWordAllCount(void)
+{
+	return MET_BM_IsOverrun() ? BM_ERR_OVERRUN : emi_readl(IOMEM(ADDR_EMI + EMI_WACT));
+}
+
+
+int MET_BM_GetWordCount(const unsigned int counter_num)
+{
+	unsigned int iCount;
+
+	switch (counter_num) {
+	case 1:
+		iCount = emi_readl(IOMEM(ADDR_EMI + EMI_WSCT));
+		break;
+
+	case 2:
+		iCount = emi_readl(IOMEM(ADDR_EMI + EMI_WSCT2));
+		break;
+
+	case 3:
+		iCount = emi_readl(IOMEM(ADDR_EMI + EMI_WSCT3));
+		break;
+
+	case 4:
+		iCount = emi_readl(IOMEM(ADDR_EMI + EMI_WSCT4));
+		break;
+
+	case 5:
+		iCount = emi_readl(IOMEM(ADDR_EMI + EMI_DBWG));
+		break;
+
+	case 6:
+		iCount = emi_readl(IOMEM(ADDR_EMI + EMI_DBWH));
+		break;
+
+
+	default:
+		return BM_ERR_WRONG_REQ;
+	}
+
+	return iCount;
+}
+
+
+unsigned int MET_BM_GetBandwidthWordCount(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI + EMI_BACT));	/*Bandwidth counter for access */
+}
+
+
+unsigned int MET_BM_GetOverheadWordCount(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI + EMI_BSCT));	/*Overhead counter */
+}
+
+
+int MET_BM_GetTransTypeCount(const unsigned int counter_num)
+{
+	return (counter_num < 1 || counter_num > BM_COUNTER_MAX)
+	    ? BM_ERR_WRONG_REQ : emi_readl(IOMEM(ADDR_EMI + EMI_TTYPE1 + (counter_num - 1) * 8));
+}
+
+
+int MET_BM_GetMDCT(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI + EMI_MDCT));
+}
+
+
+int MET_BM_GetMDCT_2(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI + EMI_MDCT_2ND));
+}
+
+
+int MET_BM_SetMDCT_MDMCU(unsigned int mdmcu_rd_buf)
+{
+	unsigned int value_origin;
+
+	value_origin = emi_readl(IOMEM(ADDR_EMI + EMI_MDCT_2ND));
+	MET_TRACE("[MET_BM_SetMDCT_MDMCU] value_origin: %x\n", value_origin);
+
+	value_origin = value_origin & ~(0x7);
+	value_origin = value_origin | ((mdmcu_rd_buf) & 0x7);
+
+	emi_reg_sync_writel(value_origin, ADDR_EMI + EMI_MDCT_2ND);
+
+	return BM_REQ_OK;
+}
+
+
+int MET_BM_GetMonitorCounter(const unsigned int counter_num,
+			     unsigned int *master, unsigned int *trans_type)
+{
+	unsigned int value, addr;
+
+	if (counter_num < 1 || counter_num > BM_COUNTER_MAX)
+		return BM_ERR_WRONG_REQ;
+
+
+	if (counter_num == 1) {
+		addr = EMI_BMEN;
+		value = emi_readl(IOMEM(ADDR_EMI + addr));
+		*master = (value >> 16) & MASK_MASTER;
+		*trans_type = (value >> 24) & MASK_TRANS_TYPE;
+	} else {
+		addr = (counter_num <= 3) ? EMI_MSEL : (EMI_MSEL2 + (counter_num / 2 - 2) * 8);
+		value = emi_readl(IOMEM(ADDR_EMI + addr)) >> ((counter_num % 2) * 16);
+		*master = value & MASK_MASTER;
+		*trans_type = (value >> 8) & MASK_TRANS_TYPE;
+	}
+
+	return BM_REQ_OK;
+}
+
+
+int MET_BM_SetMonitorCounter(const unsigned int counter_num,
+			     const unsigned int master, const unsigned int trans_type)
+{
+	unsigned int value, addr;
+	const unsigned int iMask = (MASK_TRANS_TYPE << 8) | MASK_MASTER;
+
+	if (counter_num < 1 || counter_num > BM_COUNTER_MAX)
+		return BM_ERR_WRONG_REQ;
+
+
+	if (counter_num == 1) {
+		addr = EMI_BMEN;
+		value = (emi_readl(IOMEM(ADDR_EMI + addr)) & ~(iMask << 16)) |
+		    ((trans_type & MASK_TRANS_TYPE) << 24) | ((master & MASK_MASTER) << 16);
+	} else {
+		addr = (counter_num <= 3) ? EMI_MSEL : (EMI_MSEL2 + (counter_num / 2 - 2) * 8);
+
+		/* clear master and transaction type fields */
+		value = emi_readl(IOMEM(ADDR_EMI + addr)) & ~(iMask << ((counter_num % 2) * 16));
+
+		/* set master and transaction type fields */
+		value |= (((trans_type & MASK_TRANS_TYPE) << 8) |
+			  (master & MASK_MASTER)) << ((counter_num % 2) * 16);
+	}
+
+	emi_reg_sync_writel(value, ADDR_EMI + addr);
+
+	return BM_REQ_OK;
+}
+
+
+int MET_BM_SetTtypeCounterRW(unsigned int bmrw0_val, unsigned int bmrw1_val)
+{
+
+	unsigned int value_origin;
+
+	value_origin = emi_readl(IOMEM(ADDR_EMI + EMI_BMRW0));
+	MET_TRACE("[MET_EMI_settype1] value_origin: %x\n", value_origin);
+	if (value_origin != bmrw0_val) {
+		emi_reg_sync_writel(bmrw0_val, ADDR_EMI + EMI_BMRW0);
+		MET_TRACE("[MET_EMI_settype1] bmrw0_val: %x, value_origin: %x\n", bmrw0_val,
+			   value_origin);
+	}
+
+
+	value_origin = emi_readl(IOMEM(ADDR_EMI + EMI_BMRW1));
+	MET_TRACE("[MET_EMI_settype2] value_origin: %x\n", value_origin);
+	if (value_origin != bmrw1_val) {
+		emi_reg_sync_writel(bmrw1_val, ADDR_EMI + EMI_BMRW1);
+		MET_TRACE("[MET_EMI_settype2] bmrw0_val: %x, value_origin: %x\n", bmrw1_val,
+			   value_origin);
+
+	}
+	return BM_REQ_OK;
+}
+
+
+int MET_BM_Set_WsctTsct_id_sel(unsigned int counter_num, unsigned int enable)
+{
+	unsigned int value;
+
+	if (counter_num > 3)
+		return BM_ERR_WRONG_REQ;
+
+	value =
+	    ((emi_readl(IOMEM(ADDR_EMI + EMI_BMEN2)) & (~(1 << (28 + counter_num)))) |
+	     (enable << (28 + counter_num)));
+	emi_reg_sync_writel(value, ADDR_EMI + EMI_BMEN2);
+
+	return BM_REQ_OK;
+}
+
+
+int MET_BM_SetMaster(const unsigned int counter_num, const unsigned int master)
+{
+	unsigned int value, addr;
+	const unsigned int iMask = 0x7F;
+
+	if (counter_num < 1 || counter_num > BM_COUNTER_MAX)
+		return BM_ERR_WRONG_REQ;
+
+
+	if (counter_num == 1) {
+		addr = EMI_BMEN;
+		value =
+		    (emi_readl(IOMEM(ADDR_EMI + addr)) & ~(iMask << 16)) | ((master & iMask) << 16);
+	} else {
+		addr = (counter_num <= 3) ? EMI_MSEL : (EMI_MSEL2 + (counter_num / 2 - 2) * 8);
+
+		/* clear master and transaction type fields */
+		value = emi_readl(IOMEM(ADDR_EMI + addr)) & ~(iMask << ((counter_num % 2) * 16));
+
+		/* set master and transaction type fields */
+		value |= ((master & iMask) << ((counter_num % 2) * 16));
+	}
+
+	emi_reg_sync_writel(value, ADDR_EMI + addr);
+
+	return BM_REQ_OK;
+}
+
+
+int MET_BM_SetbusID_En(const unsigned int counter_num,
+		       const unsigned int enable)
+{
+	unsigned int value;
+
+	if ((counter_num < 1 || counter_num > BM_COUNTER_MAX) || (enable > 1))
+		return BM_ERR_WRONG_REQ;
+
+	if (enable == 0) {
+		/* clear  EMI ID selection Enabling   SEL_ID_EN */
+		value = (emi_readl(IOMEM(ADDR_EMI + EMI_BMEN2))
+			 & ~(1 << (counter_num - 1)));
+	} else {
+		/* enable  EMI ID selection Enabling   SEL_ID_EN */
+		value = (emi_readl(IOMEM(ADDR_EMI + EMI_BMEN2))
+			 | (1 << (counter_num - 1)));
+	}
+	emi_reg_sync_writel(value, ADDR_EMI + EMI_BMEN2);
+
+	return BM_REQ_OK;
+}
+
+
+int MET_BM_SetbusID(const unsigned int counter_num,
+		    const unsigned int id)
+{
+	unsigned int value, addr, shift_num;
+
+	if ((counter_num < 1 || counter_num > BM_COUNTER_MAX))
+		return BM_ERR_WRONG_REQ;
+
+	/* offset of EMI_BMIDx register */
+	addr = EMI_BMID0 + (counter_num - 1) / 2 * 4;
+	shift_num = ((counter_num - 1) % 2) * 16;
+	/* clear SELx_ID field */
+	value = emi_readl(IOMEM(ADDR_EMI + addr)) & ~(EMI_BMID_MASK << shift_num);
+
+	/* set SELx_ID field */
+	if (id <= 0xffff)       /*bigger then 0xff_ff : no select busid in master, reset busid as 0*/
+		value |= id << shift_num;
+
+	emi_reg_sync_writel(value, ADDR_EMI + addr);
+
+	return BM_REQ_OK;
+}
+
+
+int MET_BM_SetUltraHighFilter(const unsigned int counter_num, const unsigned int enable)
+{
+	unsigned int value;
+
+	if ((counter_num < 1 || counter_num > BM_COUNTER_MAX) || (enable > 1))
+		return BM_ERR_WRONG_REQ;
+
+
+	value = (emi_readl(IOMEM(ADDR_EMI + EMI_BMEN1))
+		 & ~(1 << (counter_num - 1)))
+		| (enable << (counter_num - 1));
+
+	emi_reg_sync_writel(value, ADDR_EMI + EMI_BMEN1);
+
+	return BM_REQ_OK;
+}
+
+
+int MET_BM_SetLatencyCounter(unsigned int enable)
+{
+	unsigned int value;
+
+	value = emi_readl(IOMEM(ADDR_EMI + EMI_BMEN2)) & ~(0x3 << 24);
+	/*
+	 * emi_ttype1 -- emi_ttype8 change as total latencies
+	 * for m0 -- m7,
+	 * and emi_ttype9 -- emi_ttype16 change as total transaction counts
+	 * for m0 -- m7
+	 */
+	if (enable == 1)
+		value |= (0x2 << 24);
+
+	emi_reg_sync_writel(value, ADDR_EMI + EMI_BMEN2);
+
+	return BM_REQ_OK;
+}
+
+
+int MET_BM_GetLatencyCycle(unsigned int *__restrict__ emi_value,
+				const unsigned int begin, const unsigned int end)
+{
+	int i, j = 0;
+
+	for (i = begin - 1; i < end; i++)
+		emi_value[j++] = emi_readl(IOMEM(ADDR_EMI + (EMI_TTYPE1 + i * 8)));
+
+	return j;
+}
+
+
+unsigned int MET_BM_GetEmiDcm(void)
+{
+	return (emi_readl(IOMEM(ADDR_EMI + EMI_CONM)) >> 24);
+}
+
+
+int MET_BM_SetEmiDcm(const unsigned int setting)
+{
+	unsigned int value;
+
+	value = emi_readl(IOMEM(ADDR_EMI + EMI_CONM));
+	emi_reg_sync_writel((value & 0x00FFFFFF) | (setting << 24), ADDR_EMI + EMI_CONM);
+
+	return BM_REQ_OK;
+}
+
+
+unsigned int MET_EMI_GetMDCT(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI + EMI_MDCT));
+}
+
+
+unsigned int MET_EMI_GetMDCT_2ND(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI + EMI_MDCT_2ND));
+}
+
+
+unsigned int MET_EMI_GetARBA(void)
+{
+	/* EMI_ARBA EMI Bandwidth Filter Control M0/1 */
+	return emi_readl(IOMEM(ADDR_EMI + EMI_ARBA));
+}
+
+
+unsigned int MET_EMI_GetARBB(void)
+{
+	return 0;
+}
+
+
+unsigned int MET_EMI_GetARBC(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI + EMI_ARBC));
+}
+
+
+unsigned int MET_EMI_GetARBD(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI + EMI_ARBD));
+}
+
+
+unsigned int MET_EMI_GetARBE(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI + EMI_ARBE));
+}
+
+
+unsigned int MET_EMI_GetARBF(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI + EMI_ARBF));
+}
+
+
+unsigned int MET_EMI_GetARBG(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI + EMI_ARBG));
+}
+
+
+unsigned int MET_EMI_GetARBH(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI + EMI_ARBH));
+}
+
+
+/* Total BW status*/
+unsigned int MET_EMI_GetBWCT0(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI + EMI_BWCT0));
+}
+
+
+/* Total BW status*/
+unsigned int MET_EMI_GetBWCT1(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI + EMI_BWCT1));
+}
+
+
+/* Total BW status*/
+unsigned int MET_EMI_GetBWCT2(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI + EMI_BWCT2));
+}
+
+
+/* Total BW status*/
+unsigned int MET_EMI_GetBWCT3(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI + EMI_BWCT3));
+}
+
+
+/* Total BW status*/
+unsigned int MET_EMI_GetBWCT4(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI + EMI_BWCT4));
+}
+
+
+/* Total BW status*/
+unsigned int MET_EMI_GetBWST0(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI + EMI_BWST0));
+}
+
+/* Total BW status*/
+unsigned int MET_EMI_GetBWST1(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI + EMI_BWST1));
+}
+
+
+/* C+G BW status*/
+unsigned int MET_EMI_GetBWCT0_2ND(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI + EMI_BWCT0_2ND));
+}
+
+/* C+G BW status*/
+unsigned int MET_EMI_GetBWCT1_2ND(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI + EMI_BWCT1_2ND));
+}
+
+
+/* C+G BW status*/
+unsigned int MET_EMI_GetBWST_2ND(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI + EMI_BWST_2ND));
+}
+
+
+unsigned int MET_EMI_GetBMRW0(void)
+{
+	return readl(IOMEM(ADDR_EMI + EMI_BMRW0));
+}
+
+
+void emi_dump_reg(void)
+{
+	int i;
+
+	MET_TRACE("[emi_regdump]\n");
+	for (i = 0x400; i < 0x500; i = i + 16)
+		MET_TRACE("%4x__ %8x %8x %8x %8x\n", i, readl(IOMEM(ADDR_EMI + i)),
+			   readl(IOMEM(ADDR_EMI + i + 4)), readl(IOMEM(ADDR_EMI + i + 8)),
+			   readl(IOMEM(ADDR_EMI + i + 12)));
+
+	for (i = 0xF00; i < 0xFF0; i = i + 16)
+		MET_TRACE("%4x__ %8x %8x %8x %8x\n", i, readl(IOMEM(ADDR_EMI + i)),
+			   readl(IOMEM(ADDR_EMI + i + 4)), readl(IOMEM(ADDR_EMI + i + 8)),
+			   readl(IOMEM(ADDR_EMI + i + 12)));
+
+	MET_TRACE("FF4__ %8x",readl(IOMEM(ADDR_EMI + 0xff4)));
+}
+
+
+unsigned int MET_EMI_GetDramChannNum(void)
+{
+	int num = -1;
+
+	if (BaseAddrEMI) {
+		num = emi_readl(IOMEM(ADDR_EMI + EMI_CONA));
+		num = ((num >> 8) & 0x0000003);
+	} else {
+		return 1;
+	}
+
+	if (num == M0_DOUBLE_HALF_BW_1CH)
+		return 1;
+	else if (num == M0_DOUBLE_HALF_BW_2CH)
+		return 2;
+	else if (num == M0_DOUBLE_HALF_BW_4CH)
+		return 4;
+	else                    /* default return single channel */
+		return 1;
+}
+
+
+unsigned int MET_EMI_GetDramRankNum(void)
+{
+	int dual_rank = 0;
+
+	if (BaseAddrEMI) {
+		dual_rank = emi_readl(IOMEM(ADDR_EMI + EMI_CONA));
+		dual_rank = ((dual_rank >> 17) & RANK_MASK);
+	} else {
+		return DUAL_RANK;
+	}
+
+	if (dual_rank == DISABLE_DUAL_RANK_MODE)
+		return ONE_RANK;
+	else			/* default return dual rank */
+		return DUAL_RANK;
+}
+
+
+/*
+ * Dual Channel:
+ * Enables two rank for CHB
+ * Quad Channel:
+ * Enables two rank  for CHC and CHD
+ * 0: Disable dual rank mode
+ * 1: Enable dual rank mode
+ */
+unsigned int MET_EMI_GetDramRankNum_CHN1(void)
+{
+	int dual_rank = 0;
+
+	if (BaseAddrEMI) {
+		dual_rank = emi_readl(IOMEM(ADDR_EMI + EMI_CONA));
+		dual_rank = ((dual_rank >> 16) & RANK_MASK);
+	} else {
+		return DUAL_RANK;
+	}
+
+	if (dual_rank == DISABLE_DUAL_RANK_MODE)
+		return ONE_RANK;
+	else			/* default return dual rank */
+		return DUAL_RANK;
+}
+
+
+void met_get_drs_registers(uint32_t *value)
+{
+	uint32_t tmp;
+
+	memset(value, 0, sizeof(uint32_t) * 6);
+
+	if (BaseAddrCHN_EMI[0] == NULL)
+		return;
+	if (dram_chann_num > 1 && BaseAddrCHN_EMI[1] == NULL)
+		return;
+
+	tmp = emi_readl(IOMEM(BaseAddrCHN_EMI[0] + CHN_EMI_DRS_ST2)) & ((1 << 23) - 1);
+	/* working unit:38.5ns, the counter is update every 16ms */
+	value[0] = (tmp * 3850) / 16000000;
+
+	tmp = emi_readl(IOMEM(BaseAddrCHN_EMI[0] + CHN_EMI_DRS_ST3)) & ((1 << 23) - 1);
+	value[1] = (tmp * 3850) / 16000000;
+
+	tmp = emi_readl(IOMEM(BaseAddrCHN_EMI[0] + CHN_EMI_DRS_ST4)) & ((1 << 23) - 1);
+	value[2] = (tmp * 3850) / 16000000;
+
+	if (dram_chann_num > 1) {
+		tmp = emi_readl(IOMEM(BaseAddrCHN_EMI[1] + CHN_EMI_DRS_ST2)) & ((1 << 23) - 1);
+		value[3] = (tmp * 3850) / 16000000;
+
+		tmp = emi_readl(IOMEM(BaseAddrCHN_EMI[1] + CHN_EMI_DRS_ST3)) & ((1 << 23) - 1);
+		value[4] = (tmp * 3850) / 16000000;
+
+		tmp = emi_readl(IOMEM(BaseAddrCHN_EMI[1] + CHN_EMI_DRS_ST4)) & ((1 << 23) - 1);
+		value[5] = (tmp * 3850) / 16000000;
+	}
+}
+
+
+void met_record_dramc_dcm_enable_flag(void)
+{
+	int reg_val;
+
+	switch (dram_chann_num) {
+	case 1:
+		reg_val = emi_readl(IOMEM(CH0_MISC_CG_CTRL0));
+		dramc0_dcm_enable = !((reg_val >> DRAMC_CG_SHIFT) & 0x1);
+		break;
+	case 2:
+		reg_val = emi_readl(IOMEM(CH0_MISC_CG_CTRL0));
+		dramc0_dcm_enable = !((reg_val >> DRAMC_CG_SHIFT) & 0x1);
+
+		reg_val = emi_readl(IOMEM(CH1_MISC_CG_CTRL0));
+		dramc1_dcm_enable = !((reg_val >> DRAMC_CG_SHIFT) & 0x1);
+		break;
+	default:
+		METERROR("Error: dram_chann_num = %d\n", dram_chann_num);
+	}
+}
+
+unsigned int MET_EMI_Get_BaseClock_Rate(void)
+{
+	unsigned int DRAM_TYPE;
+
+	if (get_cur_ddr_ratio_symbol)
+		return get_cur_ddr_ratio_symbol();
+	else {
+
+		if (get_ddr_type_symbol) {
+			DRAM_TYPE = get_ddr_type_symbol();
+
+			if ((DRAM_TYPE == 2) || (DRAM_TYPE == 3))
+				return DRAM_EMI_BASECLOCK_RATE_LP4;
+			else
+				return DRAM_EMI_BASECLOCK_RATE_LP3;
+
+		} else {
+			return DRAM_EMI_BASECLOCK_RATE_LP4;
+		}
+	}
+}
+
+/* For SEDA3.5 wsct setting*/
+/* EMI_DBWX[15:8],    X=A~F   (SEL_MASTER) */
+/* RW:    EMI_DBWX[1:0],    X=A~F */
+int MET_BM_SetWSCT_master_rw(unsigned int *master , unsigned int *rw)
+{
+	unsigned int value, addr;
+	int i;
+
+	const unsigned int Mask_master = 0xFF;
+	const unsigned int offset_master = 8;
+
+	const unsigned int Mask_rw = 0x3;
+	const unsigned int offset_rw = 0;
+
+	for (i=0; i<WSCT_AMOUNT; i++) {
+		addr = EMI_DBWA + i*4;
+		value = emi_readl(IOMEM(ADDR_EMI + addr));
+
+		value = (value & ~(Mask_master << offset_master)) | ((*(master+i) & Mask_master) << offset_master);
+		value = (value & ~(Mask_rw << offset_rw)) | ((*(rw+i) & Mask_rw) << offset_rw);
+
+
+		emi_reg_sync_writel(value, ADDR_EMI + addr);
+	}
+
+	return BM_REQ_OK;
+}
+
+int MET_BM_SetWSCT_high_priority(unsigned int *disable, unsigned int *select)
+{
+	unsigned int value, addr;
+	int i;
+
+	const unsigned int Mask_disable = 0x1;
+	const unsigned int offset_disable  = 2;
+
+	const unsigned int Mask_select = 0xF;
+	const unsigned int offset_select  = 28;
+
+	for (i=0;i<WSCT_AMOUNT;i++) {
+		addr = EMI_DBWA + i*4;
+		value = emi_readl(IOMEM(ADDR_EMI + addr));
+
+		value = (value & ~(Mask_disable << offset_disable)) | ((*(disable+i) & Mask_disable) << offset_disable);
+		emi_reg_sync_writel(value, ADDR_EMI + addr);
+
+		/* ultra level setting */
+		addr = EMI_DBWA_2ND + i*4;
+		value = emi_readl(IOMEM(ADDR_EMI + addr));
+
+		value = (value & ~(Mask_select << offset_select)) | ((*(select+i) & Mask_select) << offset_select);
+		emi_reg_sync_writel(value, ADDR_EMI + addr);
+	}
+	return BM_REQ_OK;
+}
+
+/* busid enbale: EMI_DBWX[3],    X=A~F */
+/* busid sel: EMI_DBWX[28:16],    X=A~F  (SEL_ID_TMP) */
+/* busid mask : EMI_DBWY[12:0]  或 EMI_DBWY[28:16],  Y=I~K    (SEL_ID_MSK) */
+int MET_BM_SetWSCT_busid_idmask(unsigned int *busid, unsigned int *idMask)
+{
+	unsigned int value, addr;
+	unsigned int enable_tmp, busid_tmp, idmask_tmp;
+	int i;
+
+	const unsigned int Mask_busid = 0x1FFF;
+	const unsigned int offset_busid  = 16;
+
+	const unsigned int Mask_enable = 0x1;
+	const unsigned int offset_enable  = 3;
+
+	const unsigned int Mask_idMask = 0x1FFF;
+	const unsigned int offset_idMask_even  = 0;
+	const unsigned int offset_idMask_odd  = 16;
+
+	for (i=0;i<WSCT_AMOUNT;i++) {
+
+		/*enable, SEL_ID_TMP*/
+		if (*(busid+i)>0xffff) {
+			enable_tmp = 0;
+			busid_tmp = 0x1FFF;
+			idmask_tmp = 0x1FFF;
+		}
+		else {
+			enable_tmp = 1;
+			busid_tmp = *(busid+i) & Mask_busid;
+			idmask_tmp = *(idMask+i) & Mask_idMask;
+		}
+
+
+		addr = EMI_DBWA + i*4;
+		value = emi_readl(IOMEM(ADDR_EMI + addr));
+
+		value = (value & ~(Mask_busid << offset_busid)) | (busid_tmp << offset_busid);
+		value = (value & ~(Mask_enable << offset_enable)) | (enable_tmp << offset_enable);
+
+		emi_reg_sync_writel(value, ADDR_EMI + addr);
+
+		/*SEL_ID_MSK*/
+		addr = EMI_DBWI + (i/2)*4;
+
+		value = emi_readl(IOMEM(ADDR_EMI + addr));
+
+		if (i%2==0)
+			value = (value & ~(Mask_idMask << offset_idMask_even)) | (idmask_tmp << offset_idMask_even);
+		else
+			value = (value & ~(Mask_idMask << offset_idMask_odd)) | (idmask_tmp << offset_idMask_odd);
+
+		emi_reg_sync_writel(value, ADDR_EMI + addr);
+	}
+
+
+	return BM_REQ_OK;
+}
+
+
+int MET_BM_SetWSCT_chn_rank_sel(unsigned int *chn_rank_sel)
+{
+	unsigned int value, addr;
+	int i;
+
+	const unsigned int Mask = 0xF;
+	const unsigned int offset  = 12;
+
+
+	for (i=0;i<WSCT_AMOUNT;i++) {
+		addr = EMI_DBWA_2ND + i*4;
+		value = emi_readl(IOMEM(ADDR_EMI + addr));
+
+		value = (value & ~(Mask << offset)) | ((*(chn_rank_sel+i) & Mask) << offset);
+
+		emi_reg_sync_writel(value, ADDR_EMI + addr);
+	}
+	return BM_REQ_OK;
+}
+
+int MET_BM_SetWSCT_burst_range(unsigned int *bnd_dis, unsigned int *low_bnd, unsigned int *up_bnd)
+{
+	unsigned int value, addr;
+	int i;
+
+	const unsigned int Mask_dis = 0x1, Mask_low_bnd = 0x1FF, Mask_up_bnd = 0x1FF;
+	const unsigned int offset_dis = 4, offset_low_bnd = 16 , offset_up_bnd = 0 ;
+
+
+	for (i=0;i<WSCT_AMOUNT;i++) {
+
+		addr = EMI_DBWA + i*4;
+		value = emi_readl(IOMEM(ADDR_EMI + addr));
+
+		value = (value & ~(Mask_dis << offset_dis)) | ((*(bnd_dis+i) & Mask_dis) << offset_dis);
+
+		emi_reg_sync_writel(value, ADDR_EMI + addr);
+
+
+		addr = EMI_DBWA_2ND + i*4;
+		value = emi_readl(IOMEM(ADDR_EMI + addr));
+
+		value = (value & ~(Mask_low_bnd << offset_low_bnd)) | ((*(low_bnd+i) & Mask_low_bnd) << offset_low_bnd);
+		value = (value & ~(Mask_up_bnd << offset_up_bnd)) | ((*(up_bnd+i) & Mask_up_bnd) << offset_up_bnd);
+
+		emi_reg_sync_writel(value, ADDR_EMI + addr);
+	}
+	return BM_REQ_OK;
+
+}
+
+int MET_BM_SetTSCT_busid_enable(unsigned int *enable)
+{
+	//MET_BM_Set_WsctTsct_id_sel
+
+	int i;
+
+	for (i=0;i<TSCT_AMOUNT;i++) {
+		MET_BM_Set_WsctTsct_id_sel(i, *(enable+i));
+	}
+
+	return BM_REQ_OK;
+}
+
+//use the origin together, MET_BM_SetUltraHighFilter()
+/* EMI_TTYPEN_CONA [23:20],  N=1~21   (HPRI_SEL) */
+int MET_BM_SetTtype_high_priority_sel(unsigned int _high_priority_filter, unsigned int *select)
+{
+	int i;
+	unsigned int enable;
+	unsigned int value, addr;
+
+	const unsigned int Mask_sel = 0xF;
+	const unsigned int offset_sel = 20;
+
+	for (i = 0; i < BM_COUNTER_MAX; i++) {
+		if ((_high_priority_filter & (1 << i)) == 0){
+			enable = 0;
+		}
+		else {
+			enable = 1;
+		}
+
+		MET_BM_SetUltraHighFilter(i + 1, enable);
+
+		/* ultra level select */
+		addr = EMI_TTYPE1_CONA + i*8;
+		value = emi_readl(IOMEM(ADDR_EMI + addr));
+
+		value = (value & ~(Mask_sel << offset_sel)) | ((*(select+i) & Mask_sel) << offset_sel);
+
+		emi_reg_sync_writel(value, ADDR_EMI + addr);
+
+	}
+
+	return BM_REQ_OK;
+}
+
+
+//always call this API to init the reg
+//related API, MET_BM_SetbusID, MET_BM_SetbusID_En
+int MET_BM_SetTtype_busid_idmask(unsigned int *busid, unsigned int *idMask, int _ttype1_16_en, int _ttype17_21_en)
+{
+	int i;
+	unsigned int value, addr;
+
+	const unsigned int Mask_idMask = 0x1FFF;
+	const unsigned int offset_idMask = 0;
+
+	if (_ttype1_16_en != BM_TTYPE1_16_ENABLE) {
+		/* mask set 0x1FFF , busid set disable*/
+		for (i = 1; i <= 16; i++) {
+			*(busid + i - 1) = 0xfffff;
+			*(idMask + i - 1) = 0x1FFF;
+		}
+	}
+
+	if (_ttype17_21_en != BM_TTYPE17_21_ENABLE) {
+		for (i = 17; i <= 21; i++) {
+			*(busid + i - 1) = 0xfffff;
+			*(idMask + i - 1) = 0x1FFF;
+		}
+	}
+
+	for (i = 1; i <= BM_COUNTER_MAX; i++) {
+		MET_BM_SetbusID(i, *(busid + i - 1));
+		MET_BM_SetbusID_En(i, ( *(busid + i - 1) > 0xffff) ? 0 : 1);
+
+		/* set idMask */
+		addr = EMI_TTYPE1_CONA + (i-1)*8;
+		value = emi_readl(IOMEM(ADDR_EMI + addr));
+
+		value = (value & ~(Mask_idMask << offset_idMask)) | ((*(idMask+i-1) & Mask_idMask) << offset_idMask);
+
+		emi_reg_sync_writel(value, ADDR_EMI + addr);
+
+	}
+
+	return BM_REQ_OK;
+}
+
+
+int MET_BM_SetTtype_chn_rank_sel(unsigned int *chn_rank_sel)
+{
+	unsigned int value, addr;
+	int i;
+
+	const unsigned int Mask = 0xF;
+	const unsigned int offset  = 16;
+
+
+	for (i=0;i<BM_COUNTER_MAX;i++) {
+		addr = EMI_TTYPE1_CONA + i*8;
+		value = emi_readl(IOMEM(ADDR_EMI + addr));
+
+		value = (value & ~(Mask << offset)) | ((*(chn_rank_sel+i) & Mask) << offset);
+
+		emi_reg_sync_writel(value, ADDR_EMI + addr);
+	}
+	return BM_REQ_OK;
+}
+
+int MET_BM_SetTtype_burst_range(unsigned int *bnd_dis, unsigned int *low_bnd, unsigned int *up_bnd)
+{
+	unsigned int value, addr;
+	int i;
+
+	const unsigned int Mask_dis = 0x1, Mask_low_bnd = 0x1FF, Mask_up_bnd = 0x1FF;
+	const unsigned int offset_dis = 24, offset_low_bnd = 16 , offset_up_bnd = 0 ;
+
+
+	for (i=0;i<BM_COUNTER_MAX;i++) {
+
+		/* set dis bit */
+		addr = EMI_TTYPE1_CONA + i*8;
+		value = emi_readl(IOMEM(ADDR_EMI + addr));
+
+		value = (value & ~(Mask_dis << offset_dis)) | ((*(bnd_dis+i) & Mask_dis) << offset_dis);
+
+		emi_reg_sync_writel(value, ADDR_EMI + addr);
+
+
+		addr = EMI_TTYPE1_CONB + i*8;
+		value = emi_readl(IOMEM(ADDR_EMI + addr));
+
+		value = (value & ~(Mask_low_bnd << offset_low_bnd)) | ((*(low_bnd+i) & Mask_low_bnd) << offset_low_bnd);
+		value = (value & ~(Mask_up_bnd << offset_up_bnd)) | ((*(up_bnd+i) & Mask_up_bnd) << offset_up_bnd);
+
+		emi_reg_sync_writel(value, ADDR_EMI + addr);
+	}
+	return BM_REQ_OK;
+}
diff --git a/src/devtools/met-driver/4.14/mt2731/emi/SEDA3_5/mtk_emi_bm.h b/src/devtools/met-driver/4.14/mt2731/emi/SEDA3_5/mtk_emi_bm.h
new file mode 100644
index 0000000..bd8dba2
--- /dev/null
+++ b/src/devtools/met-driver/4.14/mt2731/emi/SEDA3_5/mtk_emi_bm.h
@@ -0,0 +1,439 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#ifndef __MT_MET_EMI_BM_H__
+#define __MT_MET_EMI_BM_H__
+
+#define EMI_VER_MAJOR  3
+#define EMI_VER_MINOR  5
+
+#define FILE_NODE_DATA_LEN 512 
+#define WSCT_AMOUNT 6
+#define TSCT_AMOUNT 3
+
+#define DEF_BM_RW_TYPE          (BM_BOTH_READ_WRITE)
+#define NTS                     2
+#define NWSCT                   4
+#define NLATENCY                8
+#define NTRANS                  8
+#define NALL                    (3 + 2 + 1)
+#define NTTYPE                  5
+#define NIDX_EMI                (NTS + NWSCT + NLATENCY + NTRANS + NALL + NTTYPE)
+
+#define NCNT                    9
+#define NCH                     2
+#define NIDX_DRAMC              (NCNT * NCH)
+#define NIDX                            (NIDX_EMI + NIDX_DRAMC)
+
+#define NCLK                    1
+#define NARB                    8
+#define NBW                     10
+#define NIDX_BL                 (NCLK + NARB + NBW)
+
+/* 1000 To Khz and 4x freq & 2x data rate for LPDDR4 */
+/* 1000 To Khz and 2x freq & 2x data rate for LPDDR3*/
+/* TBD: calculate emi clock rate from DRAM DATA RATE */
+
+/*dram baseclock/EMI clock  :	LP4=4	LP3=2	*/
+#define DRAM_EMI_BASECLOCK_RATE_LP4     4
+#define DRAM_EMI_BASECLOCK_RATE_LP3     2
+/*dram io width  :	LP4=x16		LP3=x32 */
+#define DRAM_IO_BUS_WIDTH_LP4           16
+#define DRAM_IO_BUS_WIDTH_LP3           32
+/*dram datarate  :	DDR=double */
+#define DRAM_DATARATE   2
+
+#define ADDR_EMI        ((unsigned long)BaseAddrEMI)
+
+static const char of_emi_desc[] = "mediatek,emi";
+static const char of_chn_emi_desc[] = "mediatek,chn_emi";
+static const char of_dramc_desc[] = "mediatek,dramc";
+
+#define BM_MASTER_M0            (0x01)
+#define BM_MASTER_M1            (0x02)
+#define BM_MASTER_M2            (0x04)
+#define BM_MASTER_M3            (0x08)
+#define BM_MASTER_M4            (0x10)
+#define BM_MASTER_M5            (0x20)
+#define BM_MASTER_M6            (0x40)
+#define BM_MASTER_M7            (0x80)
+#define BM_MASTER_ALL           (0xFF)
+
+
+enum BM_RW_Type {
+	BM_BOTH_READ_WRITE,
+	BM_READ_ONLY,
+	BM_WRITE_ONLY
+};
+
+enum {
+	BM_TRANS_TYPE_1BEAT = 0x0,
+	BM_TRANS_TYPE_2BEAT,
+	BM_TRANS_TYPE_3BEAT,
+	BM_TRANS_TYPE_4BEAT,
+	BM_TRANS_TYPE_5BEAT,
+	BM_TRANS_TYPE_6BEAT,
+	BM_TRANS_TYPE_7BEAT,
+	BM_TRANS_TYPE_8BEAT,
+	BM_TRANS_TYPE_9BEAT,
+	BM_TRANS_TYPE_10BEAT,
+	BM_TRANS_TYPE_11BEAT,
+	BM_TRANS_TYPE_12BEAT,
+	BM_TRANS_TYPE_13BEAT,
+	BM_TRANS_TYPE_14BEAT,
+	BM_TRANS_TYPE_15BEAT,
+	BM_TRANS_TYPE_16BEAT,
+	BM_TRANS_TYPE_1Byte = 0 << 4,
+	BM_TRANS_TYPE_2Byte = 1 << 4,
+	BM_TRANS_TYPE_4Byte = 2 << 4,
+	BM_TRANS_TYPE_8Byte = 3 << 4,
+	BM_TRANS_TYPE_16Byte = 4 << 4,
+	BM_TRANS_TYPE_32Byte = 5 << 4,
+	BM_TRANS_TYPE_BURST_WRAP = 0 << 7,
+	BM_TRANS_TYPE_BURST_INCR = 1 << 7
+};
+
+enum {
+	BM_TRANS_RW_DEFAULT = 0x0,
+	BM_TRANS_RW_READONLY,
+	BM_TRANS_RW_WRITEONLY,
+	BM_TRANS_RW_RWBOTH
+};
+
+enum {
+	BM_WSCT_RW_DISABLE = 0x0,
+	BM_WSCT_RW_READONLY,
+	BM_WSCT_RW_WRITEONLY,
+	BM_WSCT_RW_RWBOTH
+};
+
+/*coda busid 12bit, but HW support 16 bit*/
+#define EMI_BMID_MASK				(0xFFFF)
+#define BM_COUNTER_MAX				(21)
+
+/*
+*#define BUS_MON_EN		(0x00000001)
+*#define BUS_MON_PAUSE		(0x00000002)
+*#define BUS_MON_IDLE		(0x00000008)
+*#define BC_OVERRUN		(0x00000100)
+*/
+enum {
+	BUS_MON_EN_SHIFT = 0,
+	BUS_MON_PAUSE_SHIFT = 1,
+	BUS_MON_IDLE_SHIFT = 3,
+	BC_OVERRUN_SHIFT = 8,
+	DRAMC_CG_SHIFT = 9,
+};
+
+#define BM_REQ_OK				(0)
+#define BM_ERR_WRONG_REQ			(-1)
+#define BM_ERR_OVERRUN				(-2)
+
+#define BM_WSCT_TSCT_IDSEL_ENABLE		(0)
+#define BM_WSCT_TSCT_IDSEL_DISABLE		(-1)
+#define BM_TTYPE1_16_ENABLE			(0)
+#define BM_TTYPE1_16_DISABLE			(-1)
+#define BM_TTYPE17_21_ENABLE			(0)
+#define BM_TTYPE17_21_DISABLE			(-1)
+#define BM_BW_LIMITER_ENABLE			(0)
+#define BM_BW_LIMITER_DISABLE			(-1)
+
+#define M0_DOUBLE_HALF_BW_1CH	(0x0)
+#define M0_DOUBLE_HALF_BW_2CH	(0x1)
+#define M0_DOUBLE_HALF_BW_4CH	(0x2)
+
+/* EMI Rank configuration */
+enum {
+	DISABLE_DUAL_RANK_MODE = 0,
+	ENABLE_DUAL_RANK_MODE,
+};
+
+#define RANK_MASK 0x1
+#define ONE_RANK 1
+#define DUAL_RANK 2
+
+
+#if defined(CONFIG_MTK_TINYSYS_SSPM_SUPPORT) && defined(ONDIEMET_SUPPORT)
+/*ondiemet emi ipi command*/
+enum BM_EMI_IPI_Type {
+	SET_BASE_EMI = 0x0,
+	SET_BASE_DRAMC0,
+	SET_BASE_DRAMC1,
+	SET_BASE_DRAMC2,
+	SET_BASE_DRAMC3,
+	SET_BASE_DDRPHY0AO,
+	SET_BASE_DRAMC0_AO,
+	SET_EBM_CONFIGS1,
+	SET_EBM_CONFIGS2,
+	SET_REGISTER_CB,
+};
+#endif
+
+
+#define	EMI_OFF			0x0000
+#define EMI_CONA		(0x000-EMI_OFF)
+#define EMI_CONH		(0x038-EMI_OFF)
+#define EMI_CONH_2ND		(0x03C-EMI_OFF)
+#define EMI_CONM		(0x060-EMI_OFF)
+#define EMI_CONO		(0x070-EMI_OFF)
+
+#define EMI_MDCT		(0x078 - EMI_OFF)
+#define EMI_MDCT_2ND		(0x07C - EMI_OFF)
+
+#define EMI_ARBA		(0x100 - EMI_OFF)
+#define EMI_ARBB		(0x108 - EMI_OFF)
+#define EMI_ARBC		(0x110 - EMI_OFF)
+#define EMI_ARBD		(0x118 - EMI_OFF)
+#define EMI_ARBE		(0x120 - EMI_OFF)
+#define EMI_ARBF		(0x128 - EMI_OFF)
+#define EMI_ARBG		(0x130 - EMI_OFF)
+#define EMI_ARBG_2ND		(0x134 - EMI_OFF)
+#define EMI_ARBH		(0x138 - EMI_OFF)
+
+#define EMI_BMEN		(0x400 - EMI_OFF)
+#define EMI_BCNT		(0x408 - EMI_OFF)
+#define EMI_TACT		(0x410 - EMI_OFF)
+#define EMI_TSCT		(0x418 - EMI_OFF)
+#define EMI_WACT		(0x420 - EMI_OFF)
+#define EMI_WSCT		(0x428 - EMI_OFF)
+#define EMI_BACT		(0x430 - EMI_OFF)
+#define EMI_BSCT		(0x438 - EMI_OFF)
+
+#define EMI_MSEL		(0x440 - EMI_OFF)
+#define EMI_TSCT2		(0x448 - EMI_OFF)
+#define EMI_TSCT3		(0x450 - EMI_OFF)
+#define EMI_WSCT2		(0x458 - EMI_OFF)
+#define EMI_WSCT3		(0x460 - EMI_OFF)
+#define EMI_WSCT4		(0x464 - EMI_OFF)
+#define EMI_MSEL2		(0x468 - EMI_OFF)
+#define EMI_MSEL3		(0x470 - EMI_OFF)
+#define EMI_MSEL4		(0x478 - EMI_OFF)
+#define EMI_MSEL5		(0x480 - EMI_OFF)
+#define EMI_MSEL6		(0x488 - EMI_OFF)
+#define EMI_MSEL7		(0x490 - EMI_OFF)
+#define EMI_MSEL8		(0x498 - EMI_OFF)
+#define EMI_MSEL9		(0x4A0 - EMI_OFF)
+#define EMI_MSEL10		(0x4A8 - EMI_OFF)
+
+#define EMI_BMID0		(0x4B0 - EMI_OFF)
+#define EMI_BMID1		(0x4B4 - EMI_OFF)
+#define EMI_BMID2		(0x4B8 - EMI_OFF)
+#define EMI_BMID3		(0x4BC - EMI_OFF)
+#define EMI_BMID4		(0x4C0 - EMI_OFF)
+#define EMI_BMID5		(0x4C4 - EMI_OFF)
+#define EMI_BMID6		(0x4C8 - EMI_OFF)
+#define EMI_BMID7		(0x4CC - EMI_OFF)
+#define EMI_BMID8		(0x4D0 - EMI_OFF)
+#define EMI_BMID9		(0x4D4 - EMI_OFF)
+#define EMI_BMID10		(0x4D8 - EMI_OFF)
+
+#define EMI_BMEN1		(0x4E0 - EMI_OFF)
+#define EMI_BMEN2		(0x4E8 - EMI_OFF)
+#define EMI_BMRW0		(0x4F8 - EMI_OFF)
+#define EMI_BMRW1		(0x4FC - EMI_OFF)
+#define EMI_TTYPE1		(0x500 - EMI_OFF)
+#define EMI_TTYPE2		(0x508 - EMI_OFF)
+#define EMI_TTYPE3		(0x510 - EMI_OFF)
+#define EMI_TTYPE4		(0x518 - EMI_OFF)
+#define EMI_TTYPE5		(0x520 - EMI_OFF)
+#define EMI_TTYPE6		(0x528 - EMI_OFF)
+#define EMI_TTYPE7		(0x530 - EMI_OFF)
+#define EMI_TTYPE8		(0x538 - EMI_OFF)
+#define EMI_TTYPE9		(0x540 - EMI_OFF)
+#define EMI_TTYPE10		(0x548 - EMI_OFF)
+#define EMI_TTYPE11		(0x550 - EMI_OFF)
+#define EMI_TTYPE12		(0x558 - EMI_OFF)
+#define EMI_TTYPE13		(0x560 - EMI_OFF)
+#define EMI_TTYPE14		(0x568 - EMI_OFF)
+#define EMI_TTYPE15		(0x570 - EMI_OFF)
+#define EMI_TTYPE16		(0x578 - EMI_OFF)
+#define EMI_TTYPE17		(0x580 - EMI_OFF)
+#define EMI_TTYPE18		(0x588 - EMI_OFF)
+#define EMI_TTYPE19		(0x590 - EMI_OFF)
+#define EMI_TTYPE20		(0x598 - EMI_OFF)
+#define EMI_TTYPE21		(0x5A0 - EMI_OFF)
+
+#define EMI_BWCT0		(0x5B0 - EMI_OFF)
+#define EMI_BWCT1		(0x5B4 - EMI_OFF)
+#define EMI_BWCT2		(0x5B8 - EMI_OFF)
+#define EMI_BWCT3		(0x5BC - EMI_OFF)
+#define EMI_BWCT4		(0x5C0 - EMI_OFF)
+#define EMI_BWST0		(0x5C4 - EMI_OFF)
+#define EMI_BWST1		(0x5C8 - EMI_OFF)
+
+#define EMI_BWCT0_2ND		(0x6A0 - EMI_OFF)
+#define EMI_BWCT1_2ND		(0x6A4 - EMI_OFF)
+#define EMI_BWST_2ND		(0x6A8 - EMI_OFF)
+
+/* SEDA 3.5 New! reg*/
+/* For WSCT setting*/
+#define EMI_DBWA (0xF00 - EMI_OFF)
+#define EMI_DBWB (0xF04 - EMI_OFF)
+#define EMI_DBWC (0xF08 - EMI_OFF)
+#define EMI_DBWD (0xF0C - EMI_OFF)
+#define EMI_DBWE (0xF10 - EMI_OFF)
+#define EMI_DBWF (0xF14 - EMI_OFF)
+
+#define EMI_DBWG (0xF18 - EMI_OFF) /* WSCT5 */
+#define EMI_DBWH (0xF1C - EMI_OFF) /* WSCT6 */
+
+#define EMI_DBWA_2ND (0xF2C - EMI_OFF)
+#define EMI_DBWB_2ND (0xF30 - EMI_OFF)
+#define EMI_DBWC_2ND (0xF34 - EMI_OFF)
+#define EMI_DBWD_2ND (0xF38 - EMI_OFF)
+#define EMI_DBWE_2ND (0xF3C - EMI_OFF)
+#define EMI_DBWF_2ND (0xF40 - EMI_OFF)
+
+#define EMI_DBWI (0xF20 - EMI_OFF) /* SEL_ID_MSK*/
+#define EMI_DBWJ (0xF24 - EMI_OFF)
+#define EMI_DBWK (0xF28 - EMI_OFF)
+
+/* For Ttype setting */
+#define EMI_TTYPE1_CONA (0xF50 - EMI_OFF)
+#define EMI_TTYPE1_CONB (0xF54 - EMI_OFF)
+#define EMI_TTYPE2_CONA (0xF58 - EMI_OFF)
+#define EMI_TTYPE2_CONB (0xF5C - EMI_OFF)
+#define EMI_TTYPE3_CONA (0xF60 - EMI_OFF)
+#define EMI_TTYPE3_CONB (0xF64 - EMI_OFF)
+#define EMI_TTYPE4_CONA (0xF68 - EMI_OFF)
+#define EMI_TTYPE4_CONB (0xF6C - EMI_OFF)
+#define EMI_TTYPE5_CONA (0xF70 - EMI_OFF)
+#define EMI_TTYPE5_CONB (0xF74 - EMI_OFF)
+#define EMI_TTYPE6_CONA (0xF78 - EMI_OFF)
+#define EMI_TTYPE6_CONB (0xF7C - EMI_OFF)
+#define EMI_TTYPE7_CONA (0xF80 - EMI_OFF)
+#define EMI_TTYPE7_CONB (0xF84 - EMI_OFF)
+#define EMI_TTYPE8_CONA (0xF88 - EMI_OFF)
+#define EMI_TTYPE8_CONB (0xF8C - EMI_OFF)
+#define EMI_TTYPE9_CONA (0xF90 - EMI_OFF)
+#define EMI_TTYPE9_CONB (0xF94 - EMI_OFF)
+#define EMI_TTYPE10_CONA (0xF98 - EMI_OFF)
+#define EMI_TTYPE10_CONB (0xF9C - EMI_OFF)
+#define EMI_TTYPE11_CONA (0xFA0 - EMI_OFF)
+#define EMI_TTYPE11_CONB (0xFA4 - EMI_OFF)
+#define EMI_TTYPE12_CONA (0xFA8 - EMI_OFF)
+#define EMI_TTYPE12_CONB (0xFAC - EMI_OFF)
+#define EMI_TTYPE13_CONA (0xFB0 - EMI_OFF)
+#define EMI_TTYPE13_CONB (0xFB4 - EMI_OFF)
+#define EMI_TTYPE14_CONA (0xFB8 - EMI_OFF)
+#define EMI_TTYPE14_CONB (0xFBC - EMI_OFF)
+#define EMI_TTYPE15_CONA (0xFC0 - EMI_OFF)
+#define EMI_TTYPE15_CONB (0xFC4 - EMI_OFF)
+#define EMI_TTYPE16_CONA (0xFC8 - EMI_OFF)
+#define EMI_TTYPE16_CONB (0xFCC - EMI_OFF)
+#define EMI_TTYPE17_CONA (0xFD0 - EMI_OFF)
+#define EMI_TTYPE17_CONB (0xFD4 - EMI_OFF)
+#define EMI_TTYPE18_CONA (0xFD8 - EMI_OFF)
+#define EMI_TTYPE18_CONB (0xFDC - EMI_OFF)
+#define EMI_TTYPE19_CONA (0xFE0 - EMI_OFF)
+#define EMI_TTYPE19_CONB (0xFE4 - EMI_OFF)
+#define EMI_TTYPE20_CONA (0xFE8 - EMI_OFF)
+#define EMI_TTYPE20_CONB (0xFEC - EMI_OFF)
+#define EMI_TTYPE21_CONA (0xFF0 - EMI_OFF)
+#define EMI_TTYPE21_CONB (0xFF4 - EMI_OFF)
+
+
+extern void emi_dump_reg(void);
+extern int MET_BM_Init(void);
+extern void MET_BM_DeInit(void);
+extern void MET_BM_SaveCfg(void);
+extern void MET_BM_RestoreCfg(void);
+extern void MET_BM_Enable(const unsigned int enable);
+extern void MET_BM_Pause(void);
+extern void MET_BM_Continue(void);
+extern unsigned int MET_BM_IsOverrun(void);
+extern unsigned int MET_BM_GetReadWriteType(void);
+extern void MET_BM_SetReadWriteType(const unsigned int ReadWriteType);
+extern int MET_BM_GetBusCycCount(void);
+extern unsigned int MET_BM_GetTransAllCount(void);
+extern int MET_BM_GetTransCount(const unsigned int counter_num);
+extern int MET_BM_GetWordAllCount(void);
+extern int MET_BM_GetWordCount(const unsigned int counter_num);
+extern unsigned int MET_BM_GetBandwidthWordCount(void);
+extern unsigned int MET_BM_GetOverheadWordCount(void);
+extern int MET_BM_GetTransTypeCount(const unsigned int counter_num);
+extern int MET_BM_GetMDCT(void);
+extern int MET_BM_GetMDCT_2(void);
+extern int MET_BM_GetMonitorCounter(const unsigned int counter_num,
+				    unsigned int *master, unsigned int *trans_type);
+extern int MET_BM_SetMDCT_MDMCU(unsigned int mdmcu_rd_buf);
+extern int MET_BM_SetMonitorCounter(const unsigned int counter_num,
+				    const unsigned int master, const unsigned int trans_type);
+extern int MET_BM_SetTtypeCounterRW(unsigned int bmrw0_val, unsigned int bmrw1_val);
+extern int MET_BM_Set_WsctTsct_id_sel(unsigned int counter_num, unsigned int enable);
+extern int MET_BM_SetMaster(const unsigned int counter_num, const unsigned int master);
+extern int MET_BM_SetbusID_En(const unsigned int counter_num,
+			      const unsigned int enable);
+extern int MET_BM_SetbusID(const unsigned int counter_num,
+			   const unsigned int id);
+extern int MET_BM_SetUltraHighFilter(const unsigned int counter_num, const unsigned int enable);
+extern int MET_BM_SetLatencyCounter(unsigned int enable);
+int MET_BM_GetLatencyCycle(unsigned int *__restrict__ emi_value,
+				const unsigned int begin, const unsigned int end);
+extern unsigned int MET_BM_GetEmiDcm(void);
+extern int MET_BM_SetEmiDcm(const unsigned int setting);
+extern void MET_BM_Clear_Start(void);
+extern unsigned int MET_EMI_GetDramRankNum(void);
+extern unsigned int MET_EMI_GetDramRankNum_CHN1(void);
+
+/* Config */
+unsigned int MET_EMI_GetARBA(void);
+unsigned int MET_EMI_GetARBB(void);
+unsigned int MET_EMI_GetARBC(void);
+unsigned int MET_EMI_GetARBD(void);
+unsigned int MET_EMI_GetARBE(void);
+unsigned int MET_EMI_GetARBF(void);
+unsigned int MET_EMI_GetARBG(void);
+unsigned int MET_EMI_GetARBH(void);
+
+/* Total BW status */
+extern unsigned int MET_EMI_GetBWCT0(void);
+extern unsigned int MET_EMI_GetBWCT1(void);
+extern unsigned int MET_EMI_GetBWCT2(void);
+extern unsigned int MET_EMI_GetBWCT3(void);
+extern unsigned int MET_EMI_GetBWCT4(void);
+extern unsigned int MET_EMI_GetBWST0(void);
+extern unsigned int MET_EMI_GetBWST1(void);
+/* C+G BW */
+extern unsigned int MET_EMI_GetBWCT0_2ND(void);
+extern unsigned int MET_EMI_GetBWCT1_2ND(void);
+extern unsigned int MET_EMI_GetBWST_2ND(void);
+
+unsigned int MET_EMI_GetBMRW0(void);
+unsigned int MET_EMI_GetDramChannNum(void);
+
+/* ondiemet*/
+void MET_BM_IPI_baseaddr(void);
+void met_emi_phyaddr_debug(void);
+
+extern void met_record_dramc_dcm_enable_flag(void);
+
+/* get DRS registers content */
+#define CHN_EMI_DRS_ST2 0x17C
+#define CHN_EMI_DRS_ST3 0x180
+#define CHN_EMI_DRS_ST4 0x184
+extern void met_get_drs_registers(uint32_t *value);
+
+/* SEDA 3.5 NEW */
+extern int MET_BM_SetWSCT_master_rw(unsigned int *master , unsigned int *rw);
+extern int MET_BM_SetWSCT_high_priority(unsigned int *disable, unsigned int *select);
+extern int MET_BM_SetWSCT_busid_idmask(unsigned int *busid, unsigned int *idMask);
+extern int MET_BM_SetWSCT_chn_rank_sel(unsigned int *chn_rank_sel);
+extern int MET_BM_SetWSCT_burst_range(unsigned int *bnd_dis, unsigned int *low_bnd, unsigned int *up_bnd);
+extern int MET_BM_SetTSCT_busid_enable(unsigned int *enable);
+extern int MET_BM_SetTtype_high_priority_sel(unsigned int _high_priority_filter, unsigned int *select);
+extern int MET_BM_SetTtype_busid_idmask(unsigned int *busid, unsigned int *idMask, int _ttype1_16_en, int _ttype17_21_en);
+extern int MET_BM_SetTtype_chn_rank_sel(unsigned int *chn_rank_sel);
+extern int MET_BM_SetTtype_burst_range(unsigned int *bnd_dis, unsigned int *low_bnd, unsigned int *up_bnd);
+extern unsigned int MET_EMI_Get_BaseClock_Rate(void);
+
+#endif                          /* !__MT_MET_EMI_BM_H__ */
diff --git a/src/devtools/met-driver/4.14/mt2731/met_dramc.h b/src/devtools/met-driver/4.14/mt2731/met_dramc.h
new file mode 100644
index 0000000..a126815
--- /dev/null
+++ b/src/devtools/met-driver/4.14/mt2731/met_dramc.h
@@ -0,0 +1,7 @@
+#ifndef __MTK_DRAMC_H__
+#define __MTK_DRAMC_H__
+
+#define DRAMC_VER 2
+
+
+#endif /* __MTK_DRAMC_REG_H__ */
\ No newline at end of file
diff --git a/src/devtools/met-driver/4.14/mt2731/met_gpu_monitor.h b/src/devtools/met-driver/4.14/mt2731/met_gpu_monitor.h
new file mode 100644
index 0000000..3b6f7cd
--- /dev/null
+++ b/src/devtools/met-driver/4.14/mt2731/met_gpu_monitor.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2016 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See http://www.gnu.org/licenses/gpl-2.0.html for more details.
+ */
+
+#ifndef __MET_GPU_MONITOR_H__
+#define __MET_GPU_MONITOR_H__
+
+#define	MET_GPU_STALL_MONITOR
+#define IO_ADDR_GPU_STALL		0x1021c000
+#define IO_SIZE_GPU_STALL		0x1000
+#define OFFSET_STALL_GPU_M0_CHECK	0x200
+#define OFFSET_STALL_GPU_M1_CHECK	0x204
+#define OFFSET_STALL_GPU_M0_EMI_CHECK	0x208
+#define OFFSET_STALL_GPU_M1_EMI_CHECK	0x20c
+
+#endif	/* __MET_GPU_MONITOR_H__ */
diff --git a/src/devtools/met-driver/4.14/mt2731/met_sspm_rts_event.h b/src/devtools/met-driver/4.14/mt2731/met_sspm_rts_event.h
new file mode 100644
index 0000000..bcccfcd
--- /dev/null
+++ b/src/devtools/met-driver/4.14/mt2731/met_sspm_rts_event.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2016 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See http://www.gnu.org/licenses/gpl-2.0.html for more details.
+ */
+MET_SSPM_RTS_EVNET(SSPM_PTPOD, "_id,voltage")
+MET_SSPM_RTS_EVNET(SSPM_MET_UNIT_TEST, "test")
+MET_SSPM_RTS_EVNET(SSPM_QOS_BOUND_STATE, "idx,state,num,event,emibw_mon_total,emibw_mon_cpu,emibw_mon_gpu,emibw_mon_mm,emibw_mon_md,emibw_req_total,emibw_req_cpu,emibw_req_gpu,emibw_req_mm,emibw_req_md,smibw_mon_venc,smibw_mon_cam,smibw_mon_img,smibw_mon_mdp,smibw_mon_gpu,smibw_mon_apu,smibw_mon_vpu0,smibw_mon_vpu1,smibw_mon_mdla,smibw_req_venc,smibw_req_cam,smibw_req_img,smibw_req_mdp,smibw_req_gpu,smibw_req_apu,smibw_req_vpu0,smibw_req_vpu1,smibw_req_mdla,lat_mon_cpu,lat_mon_vpu0,lat_mon_vpu1,lat_mon_mdla")
+MET_SSPM_RTS_EVNET(SSPM_CM_MGR_NON_WFX, "non_wfx_0,non_wfx_1,non_wfx_2,non_wfx_3,non_wfx_4,non_wfx_5,non_wfx_6,non_wfx_7")
+MET_SSPM_RTS_EVNET(SSPM_CM_MGR_LOADING, "ratio,cps")
+MET_SSPM_RTS_EVNET(SSPM_CM_MGR_POWER, "c_up_array_0,c_up_array_1,c_down_array_0,c_down_array_1,c_up_0,c_up_1,c_down_0,c_dwon_1,c_up,c_down,v_up,v_down,v2f_0,v2f_1")
+MET_SSPM_RTS_EVNET(SSPM_CM_MGR_OPP, "v_dram_opp,v_dram_opp_cur,c_opp_cur_0,c_opp_cur_1,d_times_up,d_times_down")
+MET_SSPM_RTS_EVNET(SSPM_CM_MGR_RATIO, "ratio_max_0,ratio_max_1,ratio_0,ratio_1,ratio_2,ratio_3,ratio_4,ratio_5,ratio_6,ratio_7")
+MET_SSPM_RTS_EVNET(SSPM_CM_MGR_BW, "total_bw")
+MET_SSPM_RTS_EVNET(SSPM_CM_MGR_CP_RATIO, "up0,up1,up2,up3,down0,down1,down2,down3")
+MET_SSPM_RTS_EVNET(SSPM_CM_MGR_VP_RATIO, "up0,up1,up2,up3,down0,down1,down2,down3")
+MET_SSPM_RTS_EVNET(SSPM_CM_MGR_DE_TIMES, "up0,up1,up2,up3,down0,down1,down2,down3,reset")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_CPU__CORE_ACTIVE_RATIO, "cpu0,cpu1,cpu2,cpu3,cpu4,cpu5,cpu6,cpu7")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_CPU__CORE_IDLE_RATIO, "cpu0,cpu1,cpu2,cpu3,cpu4,cpu5,cpu6,cpu7")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_CPU__CORE_OFF_RATIO, "cpu0,cpu1,cpu2,cpu3,cpu4,cpu5,cpu6,cpu7")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_CPU__CORE_STALL_RATIO, "cpu0,cpu1,cpu2,cpu3,cpu4,cpu5,cpu6,cpu7")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_CPU__CORE_PMU_L3DC, "cpu0,cpu1,cpu2,cpu3,cpu4,cpu5,cpu6,cpu7")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_CPU__CORE_PMU_INST_SPEC, "cpu0,cpu1,cpu2,cpu3,cpu4,cpu5,cpu6,cpu7")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_CPU__CORE_PMU_CYCLES, "cpu0,cpu1,cpu2,cpu3,cpu4,cpu5,cpu6,cpu7")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_CPU__CORE_NON_WFX_CTR, "cpu0,cpu1,cpu2,cpu3,cpu4,cpu5,cpu6,cpu7")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_CPU__DSU_STATE_RATIO, "active,idle,off")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_CPU__DSU_L3_BW, "L3_BW")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_CPU__MCUSYS_STATE_RATIO, "active,idle,off")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_CPU__MCUSYS_EMI_BW, "cpu_emi_bw")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_CPU__DVFS, "vproc2,vproc1,cpuL_freq,cpuB_freq,cpu_L_opp,cpu_B_opp,cci_volt,cci_freq,cci_opp")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_CPU__LKG_POWER, "cpu_L,cpu_B,dsu")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_CPU__POWER, "cpu_L,cpu_B,dsu,mcusys")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_GPU__GPU_STATE_RATIO, "active,idle,off")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_GPU__DVFS, "vgpu,gpu_freq")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_GPU__URATE, "tex,alu")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_GPU__POWER, "gpu")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_CORE__CAM_STATE_RATIO, "RAW_A_active,RAW_B_active,RAW_C_active,idle,off")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_CORE__IMG_STATE_RATIO, "P2_active,P2_idle,MFB_active,WPE_active,off")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_CORE__IPE_STATE_RATIO, "FDVT_active,DVP_active,DVS_active,DV_idle,off")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_CORE__MDP_STATE_RATIO, "active,off")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_CORE__DISP_STATE_RATIO, "active,off")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_CORE__ADSP_STATE_RATIO, "active,off")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_CORE__VENC_STATE_RATIO, "active,idle,off")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_CORE__VDEC_STATE_RATIO, "active,idle,off")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_CORE__INFRA_STATE_RATIO, "dact,cact,idle,dcm")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_CORE__VDO_CODING_TYPE, "venc,vdec")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_CORE__DVFS, "vcore,cam_freq,img_freq,ipe_freq,dpe_freq,venc_freq,vdec_freq")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_CORE__POWER, "cam,img,ipe,mdp,disp,adsp,venc,vdec,dramc,infra_top,aphy_vcore")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_DRAM__MEM_IDX, "read_bw,write_bw,srr_pct,pdir_pct,phr_pct,acc_util,mr4")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_DRAM__DVFS, "ddr_freq")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_DRAM__POWER, "aphy_vddq_0p6v,aphy_vm_1p1v,aphy_vio_1p8v,dram_vddq_0p6v,dram_vdd2_1p1v,dram_vdd1_1p8v")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_VPU__VPU0_STATE_RATIO, "active,idle,off")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_VPU__VPU1_STATE_RATIO, "active,idle,off")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_VPU__DVFS, "vvpu,vpu0_freq,vpu1_freq")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_VPU__POWER, "vpu")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_MDLA__ACTIVE_RATIO, "pool,dw,fc,conv,ewe,sb")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_MDLA__TOTAL_CYCLES, "total_cycles")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_MDLA__DVFS, "vmdla,mdla_freq")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_MDLA__POWER, "mdla")
+MET_SSPM_RTS_EVNET(__SSPM_APUSYS_QOS_CNT__, "bw_VPU0,bw_VPU1,bw_MDLA0,bw_MDAL1,lt_VPU0,lt_VPU1,lt_MDLA0,lt_MDLA1")
+MET_SSPM_RTS_EVNET(__SSPM_GPU_APU_SSC_CNT__, "GPU_0_R,GPU_0_W,APU_0_R,APU_0_W,GPU_1_R,GPU_1_W,APU_1_R,APU_1_W")
diff --git a/src/devtools/met-driver/4.14/mt2731/plf_init.h b/src/devtools/met-driver/4.14/mt2731/plf_init.h
new file mode 100644
index 0000000..81fde68
--- /dev/null
+++ b/src/devtools/met-driver/4.14/mt2731/plf_init.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __PLF_INIT_H__
+#define __PLF_INIT_H__
+
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/miscdevice.h>
+#include <linux/kallsyms.h>
+
+/*
+ *   MET External Symbol
+ */
+extern struct miscdevice met_device;
+
+#ifdef MET_AP_EMI
+#include <mtk_dramc.h>
+extern unsigned int get_dram_data_rate(void);		/* in Mhz */
+extern int get_ddr_type(void);
+extern void *mt_cen_emi_base_get(void);
+extern void *mt_chn_emi_base_get(void);
+extern void *get_cur_ddr_ratio(void);
+
+/* New APIs for mt_dramc_nao base get */
+extern void *mt_dramc_nao_chn_base_get(int channel);
+extern void *mt_ddrphy_chn_base_get(int channel);
+extern void *mt_dramc_chn_base_get(int channel);
+
+extern unsigned int (*get_dram_data_rate_symbol)(void); /* in Mhz */
+extern unsigned int (*get_ddr_type_symbol)(void);       /* in Mhz */
+
+extern void *(*mt_cen_emi_base_get_symbol)(void);
+extern void *(*mt_chn_emi_base_get_symbol)(int chn);
+
+/* New APIs for mt_dramc_nao base get */
+extern void *(*mt_dramc_nao_chn_base_get_symbol)(int channel);
+extern void *(*mt_ddrphy_chn_base_get_symbol)(int channel);
+extern void *(*mt_dramc_chn_base_get_symbol)(int channel);
+
+extern unsigned int (*get_cur_ddr_ratio_symbol)(void);
+
+#endif
+
+/*
+ *   MET devices declaration
+ */
+
+#ifdef MET_AP_EMI
+extern struct metdevice met_emi;
+extern struct metdevice met_sspm_emi;
+#endif
+
+#endif /*__PLF_INIT_H__*/
diff --git a/src/devtools/met-driver/4.14/mt2731/plf_trace.c b/src/devtools/met-driver/4.14/mt2731/plf_trace.c
new file mode 100644
index 0000000..b3a87fd
--- /dev/null
+++ b/src/devtools/met-driver/4.14/mt2731/plf_trace.c
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+#include "met_drv.h"
+#include "interface.h"
+#include "trace.h"
+#include "plf_trace.h"
+
+
+noinline void ms_emi(const unsigned char cnt, unsigned int *value)
+{
+	char *SOB, *EOB;
+
+	MET_TRACE_GETBUF(&SOB, &EOB);
+	// coverity[var_deref_op : FALSE]
+	EOB = ms_formatH_EOL(EOB, cnt, value);
+	MET_TRACE_PUTBUF(SOB, EOB);
+}
+
+noinline void ms_emi_ext(const unsigned char cnt, unsigned int *value)
+{
+	char *SOB, *EOB;
+
+	MET_TRACE_GETBUF(&SOB, &EOB);
+	// coverity[var_deref_op : FALSE]
+	EOB = ms_formatH_EOL(EOB, cnt, value);
+	MET_TRACE_PUTBUF(SOB, EOB);
+}
+
+noinline void ms_emi_tsct(const unsigned char cnt, unsigned int *value)
+{
+	char *SOB, *EOB;
+
+	MET_TRACE_GETBUF(&SOB, &EOB);
+	// coverity[var_deref_op : FALSE]
+	EOB = ms_formatH_EOL(EOB, cnt, value);
+	MET_TRACE_PUTBUF(SOB, EOB);
+}
+
+noinline void ms_emi_mdct(const unsigned char cnt, unsigned int *value)
+{
+	char *SOB, *EOB;
+
+	MET_TRACE_GETBUF(&SOB, &EOB);
+	// coverity[var_deref_op : FALSE]
+	EOB = ms_formatH_EOL(EOB, cnt, value);
+	MET_TRACE_PUTBUF(SOB, EOB);
+}
+
+
+noinline void ms_ttype(const unsigned char cnt, unsigned int *value)
+{
+	char *SOB, *EOB;
+
+	MET_TRACE_GETBUF(&SOB, &EOB);
+	// coverity[var_deref_op : FALSE]
+	EOB = ms_formatH_EOL(EOB, cnt, value);
+	MET_TRACE_PUTBUF(SOB, EOB);
+}
+
+noinline void ms_dramc(const unsigned char cnt, unsigned int *value)
+{
+	char *SOB, *EOB;
+
+	MET_TRACE_GETBUF(&SOB, &EOB);
+	// coverity[var_deref_op : FALSE]
+	EOB = ms_formatH_EOL(EOB, cnt, value);
+	MET_TRACE_PUTBUF(SOB, EOB);
+}
+
+noinline void ms_bw_limiter(const unsigned char cnt, unsigned int *value)
+{
+	char *SOB, *EOB;
+
+	MET_TRACE_GETBUF(&SOB, &EOB);
+	// coverity[var_deref_op : FALSE]
+	EOB = ms_formatH_EOL(EOB, cnt, value);
+	MET_TRACE_PUTBUF(SOB, EOB);
+}
diff --git a/src/devtools/met-driver/4.14/mt2731/plf_trace.h b/src/devtools/met-driver/4.14/mt2731/plf_trace.h
new file mode 100644
index 0000000..3d7bd0c
--- /dev/null
+++ b/src/devtools/met-driver/4.14/mt2731/plf_trace.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _PLF_TRACE_H_
+#define _PLF_TRACE_H_
+
+#include "core_plf_trace.h"
+
+#define	HVALUE_SIZE	9	/* 8 chars (max value ffffffff) + 1 char (',' or NULL) */
+#define	DVALUE_SIZE	12	/* 10 chars (max value 4,294,967,295) + 1 char (',' or NULL) */
+
+void ms_emi(const unsigned char cnt, unsigned int *value);
+void ms_emi_tsct(const unsigned char cnt, unsigned int *value);
+void ms_emi_mdct(const unsigned char cnt, unsigned int *value);
+
+void ms_ttype(const unsigned char cnt, unsigned int *value);
+void ms_bw_limiter(const unsigned char cnt, unsigned int *value);
+
+void ms_dramc(const unsigned char cnt, unsigned int *value);
+
+void ms_emi_ext(const unsigned char cnt, unsigned int *value);
+
+#endif				/* _PLF_TRACE_H_ */
diff --git a/src/devtools/met-driver/4.14/mt8168/Kbuild.platform.include b/src/devtools/met-driver/4.14/mt8168/Kbuild.platform.include
new file mode 100644
index 0000000..4c27d64
--- /dev/null
+++ b/src/devtools/met-driver/4.14/mt8168/Kbuild.platform.include
@@ -0,0 +1,33 @@
+################################################################################
+# Include Path
+################################################################################
+MET_VCOREDVFS_INC := $(srctree)/drivers/misc/mediatek/base/power/include/vcorefs_v3
+MET_PTPOD_INC := $(srctree)/drivers/misc/mediatek/base/power/cpufreq_v1/src/mach/$(MTK_PLATFORM)/
+
+################################################################################
+# Feature Spec
+# CPUPMU_VERSION: V8_0/V8_2
+# EMI_SEDA_VERSION: SEDA2/SEDA3/SEDA3_5
+# SPMTWAM_VERSION: ap/sspm
+# SPMTWAM_IDLE_SIGNAL_SUPPORT: single/mutilple
+################################################################################
+CPUPMU_VERSION := V8_0
+EMI_SEDA_VERSION := SEDA3
+EMI_DRAMC_VERSION := V2
+
+################################################################################
+# Feature On/Off
+################################################################################
+FEATURE_WALLTIME = y
+FEATURE_GPU := y
+
+FEATURE_VCOREDVFS := n
+FEATURE_ONDIEMET := n
+FEATURE_SPMTWAM := n
+FEATURE_BACKLIGHT := n
+FEATURE_SSPM_EMI := n
+FEATURE_PTPOD := n
+FEATURE_ONDIEMET_WALLTIME := n
+FEATURE_CPUDSU := n
+FEATURE_SMI = n
+FEATURE_EMI := n
\ No newline at end of file
diff --git a/src/devtools/met-driver/4.14/mt8168/Kbuild.yocto b/src/devtools/met-driver/4.14/mt8168/Kbuild.yocto
new file mode 100644
index 0000000..b37838f
--- /dev/null
+++ b/src/devtools/met-driver/4.14/mt8168/Kbuild.yocto
@@ -0,0 +1,3 @@
+MET_PLF := $(MTK_PLATFORM)
+
+met-y +=
diff --git a/src/devtools/met-driver/4.14/mt8168/met_ddr.c b/src/devtools/met-driver/4.14/mt8168/met_ddr.c
new file mode 100644
index 0000000..256deab
--- /dev/null
+++ b/src/devtools/met-driver/4.14/mt8168/met_ddr.c
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+//#include <linux/module.h>
+#include <linux/string.h>
+
+#include "mtk_dramc.h"
+#include "met_ddr.h"
+
+int met_check_ddr_type(unsigned int DRAM_TYPE)
+{
+	switch (DRAM_TYPE) {
+		case TYPE_PCDDR3:
+		case TYPE_LPDDR3:
+			return MET_DDRTYPE_DDR3;
+		case TYPE_PCDDR4:
+		case TYPE_LPDDR4:
+		case TYPE_LPDDR4X:
+		case TYPE_LPDDR4P:
+			return MET_DDRTYPE_DDR4;
+	}
+	return MET_DDRTYPE_UNKNOWN;
+}
diff --git a/src/devtools/met-driver/4.14/mt8168/met_dramc.h b/src/devtools/met-driver/4.14/mt8168/met_dramc.h
new file mode 100644
index 0000000..a126815
--- /dev/null
+++ b/src/devtools/met-driver/4.14/mt8168/met_dramc.h
@@ -0,0 +1,7 @@
+#ifndef __MTK_DRAMC_H__
+#define __MTK_DRAMC_H__
+
+#define DRAMC_VER 2
+
+
+#endif /* __MTK_DRAMC_REG_H__ */
\ No newline at end of file
diff --git a/src/devtools/met-driver/4.14/mt8168/met_gpu_monitor.h b/src/devtools/met-driver/4.14/mt8168/met_gpu_monitor.h
new file mode 100644
index 0000000..3b6f7cd
--- /dev/null
+++ b/src/devtools/met-driver/4.14/mt8168/met_gpu_monitor.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2016 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See http://www.gnu.org/licenses/gpl-2.0.html for more details.
+ */
+
+#ifndef __MET_GPU_MONITOR_H__
+#define __MET_GPU_MONITOR_H__
+
+#define	MET_GPU_STALL_MONITOR
+#define IO_ADDR_GPU_STALL		0x1021c000
+#define IO_SIZE_GPU_STALL		0x1000
+#define OFFSET_STALL_GPU_M0_CHECK	0x200
+#define OFFSET_STALL_GPU_M1_CHECK	0x204
+#define OFFSET_STALL_GPU_M0_EMI_CHECK	0x208
+#define OFFSET_STALL_GPU_M1_EMI_CHECK	0x20c
+
+#endif	/* __MET_GPU_MONITOR_H__ */
diff --git a/src/devtools/met-driver/4.14/mt8168/met_sspm_rts_event.h b/src/devtools/met-driver/4.14/mt8168/met_sspm_rts_event.h
new file mode 100644
index 0000000..bcccfcd
--- /dev/null
+++ b/src/devtools/met-driver/4.14/mt8168/met_sspm_rts_event.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2016 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See http://www.gnu.org/licenses/gpl-2.0.html for more details.
+ */
+MET_SSPM_RTS_EVNET(SSPM_PTPOD, "_id,voltage")
+MET_SSPM_RTS_EVNET(SSPM_MET_UNIT_TEST, "test")
+MET_SSPM_RTS_EVNET(SSPM_QOS_BOUND_STATE, "idx,state,num,event,emibw_mon_total,emibw_mon_cpu,emibw_mon_gpu,emibw_mon_mm,emibw_mon_md,emibw_req_total,emibw_req_cpu,emibw_req_gpu,emibw_req_mm,emibw_req_md,smibw_mon_venc,smibw_mon_cam,smibw_mon_img,smibw_mon_mdp,smibw_mon_gpu,smibw_mon_apu,smibw_mon_vpu0,smibw_mon_vpu1,smibw_mon_mdla,smibw_req_venc,smibw_req_cam,smibw_req_img,smibw_req_mdp,smibw_req_gpu,smibw_req_apu,smibw_req_vpu0,smibw_req_vpu1,smibw_req_mdla,lat_mon_cpu,lat_mon_vpu0,lat_mon_vpu1,lat_mon_mdla")
+MET_SSPM_RTS_EVNET(SSPM_CM_MGR_NON_WFX, "non_wfx_0,non_wfx_1,non_wfx_2,non_wfx_3,non_wfx_4,non_wfx_5,non_wfx_6,non_wfx_7")
+MET_SSPM_RTS_EVNET(SSPM_CM_MGR_LOADING, "ratio,cps")
+MET_SSPM_RTS_EVNET(SSPM_CM_MGR_POWER, "c_up_array_0,c_up_array_1,c_down_array_0,c_down_array_1,c_up_0,c_up_1,c_down_0,c_dwon_1,c_up,c_down,v_up,v_down,v2f_0,v2f_1")
+MET_SSPM_RTS_EVNET(SSPM_CM_MGR_OPP, "v_dram_opp,v_dram_opp_cur,c_opp_cur_0,c_opp_cur_1,d_times_up,d_times_down")
+MET_SSPM_RTS_EVNET(SSPM_CM_MGR_RATIO, "ratio_max_0,ratio_max_1,ratio_0,ratio_1,ratio_2,ratio_3,ratio_4,ratio_5,ratio_6,ratio_7")
+MET_SSPM_RTS_EVNET(SSPM_CM_MGR_BW, "total_bw")
+MET_SSPM_RTS_EVNET(SSPM_CM_MGR_CP_RATIO, "up0,up1,up2,up3,down0,down1,down2,down3")
+MET_SSPM_RTS_EVNET(SSPM_CM_MGR_VP_RATIO, "up0,up1,up2,up3,down0,down1,down2,down3")
+MET_SSPM_RTS_EVNET(SSPM_CM_MGR_DE_TIMES, "up0,up1,up2,up3,down0,down1,down2,down3,reset")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_CPU__CORE_ACTIVE_RATIO, "cpu0,cpu1,cpu2,cpu3,cpu4,cpu5,cpu6,cpu7")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_CPU__CORE_IDLE_RATIO, "cpu0,cpu1,cpu2,cpu3,cpu4,cpu5,cpu6,cpu7")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_CPU__CORE_OFF_RATIO, "cpu0,cpu1,cpu2,cpu3,cpu4,cpu5,cpu6,cpu7")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_CPU__CORE_STALL_RATIO, "cpu0,cpu1,cpu2,cpu3,cpu4,cpu5,cpu6,cpu7")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_CPU__CORE_PMU_L3DC, "cpu0,cpu1,cpu2,cpu3,cpu4,cpu5,cpu6,cpu7")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_CPU__CORE_PMU_INST_SPEC, "cpu0,cpu1,cpu2,cpu3,cpu4,cpu5,cpu6,cpu7")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_CPU__CORE_PMU_CYCLES, "cpu0,cpu1,cpu2,cpu3,cpu4,cpu5,cpu6,cpu7")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_CPU__CORE_NON_WFX_CTR, "cpu0,cpu1,cpu2,cpu3,cpu4,cpu5,cpu6,cpu7")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_CPU__DSU_STATE_RATIO, "active,idle,off")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_CPU__DSU_L3_BW, "L3_BW")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_CPU__MCUSYS_STATE_RATIO, "active,idle,off")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_CPU__MCUSYS_EMI_BW, "cpu_emi_bw")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_CPU__DVFS, "vproc2,vproc1,cpuL_freq,cpuB_freq,cpu_L_opp,cpu_B_opp,cci_volt,cci_freq,cci_opp")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_CPU__LKG_POWER, "cpu_L,cpu_B,dsu")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_CPU__POWER, "cpu_L,cpu_B,dsu,mcusys")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_GPU__GPU_STATE_RATIO, "active,idle,off")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_GPU__DVFS, "vgpu,gpu_freq")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_GPU__URATE, "tex,alu")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_GPU__POWER, "gpu")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_CORE__CAM_STATE_RATIO, "RAW_A_active,RAW_B_active,RAW_C_active,idle,off")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_CORE__IMG_STATE_RATIO, "P2_active,P2_idle,MFB_active,WPE_active,off")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_CORE__IPE_STATE_RATIO, "FDVT_active,DVP_active,DVS_active,DV_idle,off")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_CORE__MDP_STATE_RATIO, "active,off")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_CORE__DISP_STATE_RATIO, "active,off")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_CORE__ADSP_STATE_RATIO, "active,off")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_CORE__VENC_STATE_RATIO, "active,idle,off")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_CORE__VDEC_STATE_RATIO, "active,idle,off")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_CORE__INFRA_STATE_RATIO, "dact,cact,idle,dcm")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_CORE__VDO_CODING_TYPE, "venc,vdec")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_CORE__DVFS, "vcore,cam_freq,img_freq,ipe_freq,dpe_freq,venc_freq,vdec_freq")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_CORE__POWER, "cam,img,ipe,mdp,disp,adsp,venc,vdec,dramc,infra_top,aphy_vcore")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_DRAM__MEM_IDX, "read_bw,write_bw,srr_pct,pdir_pct,phr_pct,acc_util,mr4")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_DRAM__DVFS, "ddr_freq")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_DRAM__POWER, "aphy_vddq_0p6v,aphy_vm_1p1v,aphy_vio_1p8v,dram_vddq_0p6v,dram_vdd2_1p1v,dram_vdd1_1p8v")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_VPU__VPU0_STATE_RATIO, "active,idle,off")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_VPU__VPU1_STATE_RATIO, "active,idle,off")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_VPU__DVFS, "vvpu,vpu0_freq,vpu1_freq")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_VPU__POWER, "vpu")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_MDLA__ACTIVE_RATIO, "pool,dw,fc,conv,ewe,sb")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_MDLA__TOTAL_CYCLES, "total_cycles")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_MDLA__DVFS, "vmdla,mdla_freq")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_MDLA__POWER, "mdla")
+MET_SSPM_RTS_EVNET(__SSPM_APUSYS_QOS_CNT__, "bw_VPU0,bw_VPU1,bw_MDLA0,bw_MDAL1,lt_VPU0,lt_VPU1,lt_MDLA0,lt_MDLA1")
+MET_SSPM_RTS_EVNET(__SSPM_GPU_APU_SSC_CNT__, "GPU_0_R,GPU_0_W,APU_0_R,APU_0_W,GPU_1_R,GPU_1_W,APU_1_R,APU_1_W")
diff --git a/src/devtools/met-driver/4.14/mt8168/plf_init.h b/src/devtools/met-driver/4.14/mt8168/plf_init.h
new file mode 100644
index 0000000..81fde68
--- /dev/null
+++ b/src/devtools/met-driver/4.14/mt8168/plf_init.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __PLF_INIT_H__
+#define __PLF_INIT_H__
+
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/miscdevice.h>
+#include <linux/kallsyms.h>
+
+/*
+ *   MET External Symbol
+ */
+extern struct miscdevice met_device;
+
+#ifdef MET_AP_EMI
+#include <mtk_dramc.h>
+extern unsigned int get_dram_data_rate(void);		/* in Mhz */
+extern int get_ddr_type(void);
+extern void *mt_cen_emi_base_get(void);
+extern void *mt_chn_emi_base_get(void);
+extern void *get_cur_ddr_ratio(void);
+
+/* New APIs for mt_dramc_nao base get */
+extern void *mt_dramc_nao_chn_base_get(int channel);
+extern void *mt_ddrphy_chn_base_get(int channel);
+extern void *mt_dramc_chn_base_get(int channel);
+
+extern unsigned int (*get_dram_data_rate_symbol)(void); /* in Mhz */
+extern unsigned int (*get_ddr_type_symbol)(void);       /* in Mhz */
+
+extern void *(*mt_cen_emi_base_get_symbol)(void);
+extern void *(*mt_chn_emi_base_get_symbol)(int chn);
+
+/* New APIs for mt_dramc_nao base get */
+extern void *(*mt_dramc_nao_chn_base_get_symbol)(int channel);
+extern void *(*mt_ddrphy_chn_base_get_symbol)(int channel);
+extern void *(*mt_dramc_chn_base_get_symbol)(int channel);
+
+extern unsigned int (*get_cur_ddr_ratio_symbol)(void);
+
+#endif
+
+/*
+ *   MET devices declaration
+ */
+
+#ifdef MET_AP_EMI
+extern struct metdevice met_emi;
+extern struct metdevice met_sspm_emi;
+#endif
+
+#endif /*__PLF_INIT_H__*/
diff --git a/src/devtools/met-driver/4.14/mt8168/plf_trace.h b/src/devtools/met-driver/4.14/mt8168/plf_trace.h
new file mode 100644
index 0000000..3d7bd0c
--- /dev/null
+++ b/src/devtools/met-driver/4.14/mt8168/plf_trace.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _PLF_TRACE_H_
+#define _PLF_TRACE_H_
+
+#include "core_plf_trace.h"
+
+#define	HVALUE_SIZE	9	/* 8 chars (max value ffffffff) + 1 char (',' or NULL) */
+#define	DVALUE_SIZE	12	/* 10 chars (max value 4,294,967,295) + 1 char (',' or NULL) */
+
+void ms_emi(const unsigned char cnt, unsigned int *value);
+void ms_emi_tsct(const unsigned char cnt, unsigned int *value);
+void ms_emi_mdct(const unsigned char cnt, unsigned int *value);
+
+void ms_ttype(const unsigned char cnt, unsigned int *value);
+void ms_bw_limiter(const unsigned char cnt, unsigned int *value);
+
+void ms_dramc(const unsigned char cnt, unsigned int *value);
+
+void ms_emi_ext(const unsigned char cnt, unsigned int *value);
+
+#endif				/* _PLF_TRACE_H_ */
diff --git a/src/devtools/met-driver/4.19/Android.mk b/src/devtools/met-driver/4.19/Android.mk
new file mode 100644
index 0000000..3093bfd
--- /dev/null
+++ b/src/devtools/met-driver/4.19/Android.mk
@@ -0,0 +1,14 @@
+LOCAL_PATH := $(call my-dir)
+
+ifneq (,$(filter $(word 2,$(subst -, ,$(LINUX_KERNEL_VERSION))),$(subst /, ,$(LOCAL_PATH))))
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := met.ko
+LOCAL_STRIP_MODULE := true
+
+$(warning TARGET_DEVICE=$(TARGET_DEVICE))
+$(warning TARGET_BOARD_PLATFORM=$(TARGET_BOARD_PLATFORM))
+
+include $(MTK_KERNEL_MODULE)
+
+endif # Kernel version matches current path
diff --git a/src/devtools/met-driver/4.19/Makefile b/src/devtools/met-driver/4.19/Makefile
new file mode 100644
index 0000000..0c0b473
--- /dev/null
+++ b/src/devtools/met-driver/4.19/Makefile
@@ -0,0 +1,63 @@
+ifeq ($(CONFIG_BUILD_YOCTO),y)
+    MTK_PLATFORM := $(subst ",,$(CONFIG_MTK_PLATFORM))
+    MET_ROOT_DIR := $(MET_DRIVER_DIR)
+    MET_COMMON_DIR := $(MET_DRIVER_DIR)/common
+    MET_PLF_DIR := $(MET_DRIVER_DIR)/$(MTK_PLATFORM)
+    MET_BUILD_DEFAULT := n
+
+    ifneq ($(KERNEL_OUT),)
+        include $(KERNEL_OUT)/.config
+        ccflags-y += -imacros $(KERNEL_OUT)/include/generated/autoconf.h
+    endif
+else
+    #$(info TARGET_BOARD_PLATFORM=$(TARGET_BOARD_PLATFORM))
+    #$(info TARGET_PRODUCT=$(TARGET_PRODUCT))
+    #MTK_PLATFORM := $(subst ",,$(TARGET_BOARD_PLATFORM))
+    #MTK_PLATFORM := $(TARGET_DEVICE)
+    MTK_PLATFORM := mt2712
+    mkfile_path := $(abspath $(lastword $(MAKEFILE_LIST)))
+    MET_ROOT_DIR := $(patsubst %/,%,$(dir $(mkfile_path)))
+    MET_COMMON_DIR := $(wildcard $(MET_ROOT_DIR)/common)
+    MET_PLF_DIR := $(wildcard $(MET_ROOT_DIR)/$(MTK_PLATFORM))
+    MET_BUILD_DEFAULT := n
+
+    #$(info MTK_PLATFORM=$(TARGET_DEVICE))
+    #$(info mkfile_path=$(mkfile_path))
+    #$(info MET_ROOT_DIR=$(MET_ROOT_DIR))
+    #$(info MET_COMMON_DIR=$(MET_COMMON_DIR))
+    #$(info MET_PLF_DIR=$(MET_PLF_DIR))
+endif
+
+ifeq ($(CONFIG_MODULES),y)
+
+ifeq ($(CONFIG_FTRACE),y)
+    ifeq ($(CONFIG_TRACING),y)
+        FTRACE_READY := y
+    endif
+endif
+
+$(info ******** Start to build met_drv for $(MTK_PLATFORM) ********)
+ifneq ($(MET_PLF_DIR),)
+    ifeq ($(FTRACE_READY),y)
+        ifeq ($(CONFIG_BUILD_YOCTO),y)
+            include $(MET_COMMON_DIR)/Kbuild.yocto
+        else
+            include $(MET_COMMON_DIR)/Kbuild
+        endif
+    else
+        $(warning Not building met.ko due to CONFIG_FTRACE/CONFIG_TRACING is not set, build met default)
+        MET_BUILD_DEFAULT = y
+    endif
+else
+    $(warning not support $(MTK_PLATFORM), build met default)
+    MET_BUILD_DEFAULT = y
+endif
+else #CONFIG_MODULES = n
+    $(warning Not building met.ko due to CONFIG_MODULES is not set, build met default)
+    MET_BUILD_DEFAULT := y
+endif
+
+ifeq ($(MET_BUILD_DEFAULT),y)
+    MET_DEF_DIR := $(MET_ROOT_DIR)/default
+    include $(MET_DEF_DIR)/Kbuild
+endif
diff --git a/src/devtools/met-driver/4.19/Makefile.yocto b/src/devtools/met-driver/4.19/Makefile.yocto
new file mode 100644
index 0000000..14e4add
--- /dev/null
+++ b/src/devtools/met-driver/4.19/Makefile.yocto
@@ -0,0 +1,15 @@
+MODULE_NAME := met
+
+##############################################################
+# Compile settings
+##############################################################
+all:
+	make -C $(KERNEL_OUT) M=$(MET_DRIVER_DIR) modules
+
+clean:
+	make -C $(KERNEL_OUT) M=$(MET_DRIVER_DIR) clean
+	if [ -e $(MET_DRIVER_DIR)/$(MODULE_NAME).ko ]; then rm $(MET_DRIVER_DIR)/$(MODULE_NAME).ko; fi;
+
+.PHONY: all clean
+
+
diff --git a/src/devtools/met-driver/4.19/common/Kbuild b/src/devtools/met-driver/4.19/common/Kbuild
new file mode 100644
index 0000000..e4df42b
--- /dev/null
+++ b/src/devtools/met-driver/4.19/common/Kbuild
@@ -0,0 +1,786 @@
+################################################################################
+#   MET Kernel module mode
+################################################################################
+ifneq ($(CONFIG_MTK_MET_BUILT_IN),y)
+$(info ======== Build met.ko ... ========)
+MET_CORE := common
+obj-m := met.o
+
+ifneq ($(wildcard $(MET_PLF_DIR)/Kbuild.platform.h),)
+    include $(MET_PLF_DIR)/Kbuild.platform.h
+else
+    $(info ======= Missing $(MET_PLF_DIR)/Kbuild.platform.h ========)
+endif
+
+ccflags-y += -DCONFIG_MET_MODULE
+ccflags-y += -DMET_PLF_USE
+ccflags-y += -I$(MET_COMMON_DIR)
+ccflags-y += -I$(MET_PLF_DIR)
+ccflags-y += -I$(srctree)/include/
+ccflags-y += -I$(srctree)/drivers/misc/mediatek/include/
+ccflags-y += -I$(srctree)/drivers/misc/mediatek/include/mt-plat/
+ccflags-y += -I$(srctree)/drivers/misc/mediatek/include/mt-plat/$(MTK_PLATFORM)/include/
+ccflags-y += -I$(srctree)/drivers/misc/mediatek/base/power/$(MTK_PLATFORM)/
+
+ccflags-y += $(EXTRA_ARGS) $(EXTRA_CFLAGS)
+ccflags-y += -DMTK_PLATFORM=$(MTK_PLATFORM)
+
+met-y := $(MET_CORE)/met_main.o \
+    $(MET_CORE)/met_backlight.o \
+    $(MET_CORE)/met_tag_ex.o \
+    $(MET_CORE)/interface.o \
+    $(MET_CORE)/sampler.o \
+    $(MET_CORE)/dummy_header.o \
+    $(MET_CORE)/util.o \
+    $(MET_CORE)/stat.o \
+    $(MET_CORE)/cookie.o \
+    $(MET_CORE)/mem_stat.o \
+    $(MET_CORE)/switch.o \
+    $(MET_CORE)/trace_event.o \
+    $(MET_CORE)/core_plf_init.o \
+    $(MET_CORE)/core_plf_trace.o \
+    $(MET_CORE)/ondiemet.o \
+    $(MET_CORE)/ondiemet_log.o \
+    $(MET_CORE)/sspm/ondiemet_sspm.o
+
+CFLAGS_interface.o += -DMET_USER_EVENT_SUPPORT
+CFLAGS_met_tag_ex.o += -DMET_USER_EVENT_SUPPORT
+
+$(info CPUPMU_VERSION = $(CPUPMU_VERSION))
+ifeq ("$(CPUPMU_VERSION)", "V8_2")
+    ccflags-y += -DCPUPMU_V8_2
+endif
+
+$(info ARCH = $(ARCH))
+ifeq ($(ARCH), mips)
+    met-y += $(MET_CORE)/mips_pmu_hw.o
+endif #ifeq ($(ARCH), mips)
+
+ifeq ($(ARCH), arm)
+    ccflags-y += -DCONFIG_MET_ARM_32BIT
+    met-y += $(MET_CORE)/cpu_pmu.o
+    met-y += $(MET_CORE)/v7_pmu_hw.o
+    met-y += $(MET_CORE)/v6_pmu_hw.o
+endif #ifeq ($(ARCH), arm)
+
+ifeq ($(ARCH), arm64)
+    met-y += $(MET_CORE)/cpu_pmu.o
+    met-y += $(MET_CORE)/v8_pmu_hw.o
+endif
+
+$(info CONFIG_CPU_FREQ = $(CONFIG_CPU_FREQ))
+ifeq ($(CONFIG_CPU_FREQ),y)
+    met-y += $(MET_CORE)/power.o
+endif
+
+################################################################################
+# MET_SPM_TWAM
+################################################################################
+FEATURE_SPMTWAM := $(if $(FEATURE_SPMTWAM),$(FEATURE_SPMTWAM),y)
+$(info FEATURE_SPMTWAM = $(FEATURE_SPMTWAM))
+
+ifneq ($(FEATURE_SPMTWAM), n)
+    MET_SPM_TWAM := y
+
+    # for mtk_spm.h
+    ifneq ("$(wildcard $(srctree)/drivers/misc/mediatek/base/power/include/mtk_spm.h)","")
+        ccflags-y += -I$(srctree)/drivers/misc/mediatek/base/power/include/
+        ccflags-y += -I$(MET_COMMON_DIR)/spmtwam/include/
+    else
+        MET_SPM_TWAM = n
+        $(info ========= Missing $(srctree)/drivers/misc/mediatek/base/power/include/mtk_spm.h ========)
+        $(info ======== disable MET_SPM_TWAM ========)
+    endif
+else
+    MET_SPM_TWAM := n
+endif
+
+$(info SPMTWAM_VERSION = $(SPMTWAM_VERSION))
+$(info SPMTWAM_IDLE_SIGNAL_SUPPORT = $(SPMTWAM_IDLE_SIGNAL_SUPPORT))
+
+ifeq ("$(SPMTWAM_IDLE_SIGNAL_SUPPORT)", "single")
+    ccflags-y += -DSPMTWAM_SINGLE_IDLE_SIGNAL
+endif
+
+ifeq ("$(SPMTWAM_IDLE_SIGNAL_SUPPORT)", "multiple")
+    ccflags-y += -DSPMTWAM_MULTIPLE_IDLE_SIGNAL
+endif
+
+ifeq ("$(SPMTWAM_VERSION)", "ap")
+    ccflags-y += -DSPMTWAM_AP
+    met-$(MET_SPM_TWAM) += $(MET_CORE)/spmtwam/ap/met_spmtwam.o
+endif
+
+ifeq ("$(SPMTWAM_VERSION)", "sspm")
+    ccflags-y += -DSPMTWAM_SSPM
+    met-$(MET_SPM_TWAM) += $(MET_CORE)/spmtwam/sspm/met_spmtwam.o
+endif
+
+################################################################################
+# MET_EMI
+################################################################################
+FEATURE_SSPM_EMI := $(if $(FEATURE_SSPM_EMI),$(FEATURE_SSPM_EMI),y)
+$(info FEATURE_SSPM_EMI = $(FEATURE_SSPM_EMI))
+
+ifeq ($(CONFIG_MTK_TINYSYS_SSPM_SUPPORT),y)
+    ifneq ($(FEATURE_ONDIEMET), n)
+        MET_EMI := $(if $(filter n,$(FEATURE_SSPM_EMI)),n,y)
+        ifeq ("$(EMI_SEDA_VERSION)", "SEDA3_5")
+            met-$(MET_EMI) += $(MET_CORE)/emi/SEDA3_5/met_emi.o \
+                   $(MET_CORE)/emi/SEDA3_5/mtk_emi_bm.o
+        else ifeq ("$(EMI_SEDA_VERSION)", "SEDA3_6")
+            met-$(MET_EMI) += $(MET_CORE)/emi/SEDA3_6/met_emi.o \
+                   $(MET_CORE)/emi/SEDA3_6/mtk_emi_bm.o
+        else
+            met-$(MET_EMI) += $(MET_CORE)/emi/SEDA3/met_emi.o \
+                   $(MET_CORE)/emi/SEDA3/mtk_emi_bm.o
+        endif
+    endif
+endif
+
+################################################################################
+# MET_GPU
+################################################################################
+FEATURE_GPU := $(if $(FEATURE_GPU),$(FEATURE_GPU),y)
+$(info FEATURE_GPU = $(FEATURE_GPU))
+
+ifneq ($(FEATURE_GPU), n)
+    MET_GPU := y
+
+    # for mtk_gpufreq.h
+    ifneq ("$(wildcard $(srctree)/drivers/misc/mediatek/base/power/$(MTK_PLATFORM)/mtk_gpufreq.h)","")
+        ccflags-y += -I$(srctree)/drivers/misc/mediatek/base/power/$(MTK_PLATFORM)/
+    else ifneq ("$(wildcard $(srctree)/drivers/misc/mediatek/base/power/include/mtk_gpufreq.h)","")
+        ccflags-y += -I$(srctree)/drivers/misc/mediatek/base/power/include/
+    else
+        MET_GPU = n
+        $(info ======= Missing $(srctree)/drivers/misc/mediatek/base/power/$(MTK_PLATFORM)/mtk_gpufreq.h ========)
+        $(info ======= Missing $(srctree)/drivers/misc/mediatek/base/power/include/mtk_gpufreq.h ========)
+        $(info ======== disable MET_GPU ========)
+    endif
+
+    # for mtk_gpu_utility.h
+    ifneq ("$(wildcard $(srctree)/drivers/misc/mediatek/include/mt-plat/mtk_gpu_utility.h)","")
+        ccflags-y += -I$(srctree)/drivers/misc/mediatek/include/mt-plat/
+    else
+        MET_GPU = n
+        $(info ======== Missing $(srctree)/drivers/misc/mediatek/include/mt-plat/mtk_gpu_utility.h ========)
+        $(info ======== disable MET_GPU ========)
+    endif
+
+    ifneq ($(CONFIG_MTK_GPU_SUPPORT), y)
+        MET_GPU = n
+        $(info ======== CONFIG_MTK_GPU_SUPPORT = n ========)
+        $(info ======== disable MET_GPU ========)
+    endif
+
+    GPU_STALL_CNT_TYPE := $(if $(GPU_STALL_CNT_TYPE),$(GPU_STALL_CNT_TYPE),multiple)
+    $(info GPU_STALL_CNT_TYPE = $(GPU_STALL_CNT_TYPE))
+
+    ifeq ("$(GPU_STALL_CNT_TYPE)", "single")
+        ccflags-y += -DGPU_STALL_CNT_SINGLE
+    endif
+else
+    MET_GPU := n
+endif
+
+met-$(MET_GPU) += $(MET_CORE)/mtk_gpu_metmonitor.o
+
+
+################################################################################
+# MET_VCOREDVFS
+################################################################################
+FEATURE_VCOREDVFS := $(if $(FEATURE_VCOREDVFS),$(FEATURE_VCOREDVFS),y)
+$(info FEATURE_VCOREDVFS = $(FEATURE_VCOREDVFS))
+
+ifneq ($(FEATURE_VCOREDVFS), n)
+
+    MET_VCOREDVFS := y
+
+    # for mtk_vcorefs_manager.h
+    ifneq ("$(wildcard $(MET_VCOREDVFS_INC)/mtk_vcorefs_manager.h)","")
+        ccflags-y += -I$(MET_VCOREDVFS_INC)/
+    else
+        MET_VCOREDVFS = n
+        $(info ======== Missing $(MET_VCOREDVFS_INC)/mtk_vcorefs_manager.h ========)
+        $(info ======== disable MET_VCOREDVFS ========)
+    endif
+
+    # for mtk_vcorefs_governor.h
+    ifneq ("$(wildcard $(MET_VCOREDVFS_INC)/mtk_vcorefs_governor.h)","")
+        ccflags-y += -I$(MET_VCOREDVFS_INC)
+    else
+        MET_VCOREDVFS = n
+        $(info ======== Missing $(MET_VCOREDVFS_INC)/mtk_vcorefs_governor.h ========)
+        $(info ======== disable MET_VCOREDVFS ========)
+    endif
+
+    ifneq ($(VCOREDVFS_OLD_VER),y)
+        # for helio-dvfsrc.h
+        ifneq ("$(wildcard $(srctree)/drivers/devfreq/helio-dvfsrc.h)","")
+            ccflags-y += -I$(srctree)/drivers/devfreq/
+        else
+            MET_VCOREDVFS = n
+            $(info ======== Missing $(srctree)/drivers/devfreq/helio-dvfsrc.h ========)
+            $(info ======== disable MET_VCOREDVFS ========)
+        endif
+    endif
+else
+    MET_VCOREDVFS := n
+endif
+
+ifneq ($(VCOREDVFS_OLD_VER),y)
+    met-$(MET_VCOREDVFS) += $(MET_CORE)/met_vcoredvfs.o
+else
+    ccflags-y += -DVCOREDVFS_OLD_VER
+    met-$(MET_VCOREDVFS) += $(MET_CORE)/met_vcoredvfs_44.o
+endif
+
+################################################################################
+# MET_PTPOD
+################################################################################
+FEATURE_PTPOD := $(if $(FEATURE_PTPOD),$(FEATURE_PTPOD),y)
+$(info FEATURE_PTPOD = $(FEATURE_PTPOD))
+
+ifneq ($(FEATURE_PTPOD), n)
+    MET_PTPOD := y
+
+    # for mtk_gpufreq.h
+    ifneq ("$(wildcard $(srctree)/drivers/misc/mediatek/base/power/$(MTK_PLATFORM)/mtk_gpufreq.h)","")
+        ccflags-y += -I$(srctree)/drivers/misc/mediatek/base/power/$(MTK_PLATFORM)/
+    else ifneq ("$(wildcard $(srctree)/drivers/misc/mediatek/base/power/include/mtk_gpufreq.h)","")
+        ccflags-y += -I$(srctree)/drivers/misc/mediatek/base/power/include/
+    else
+        MET_PTPOD = n
+        $(info ======== Missing $(srctree)/drivers/misc/mediatek/base/power/$(MTK_PLATFORM)/mtk_gpufreq.h ========)
+        $(info ======== Missing $(srctree)/drivers/misc/mediatek/base/power/include/mtk_gpufreq.h ========)
+        $(info ======== disable MET_PTPOD ========)
+    endif
+
+    # for mtk_cpufreq_api.h
+    ifneq ("$(wildcard $(srctree)/drivers/misc/mediatek/include/mt-plat/$(MTK_PLATFORM)/include/mach/mtk_cpufreq_api.h)","")
+        ccflags-y += -I$(srctree)/drivers/misc/mediatek/include/mt-plat/$(MTK_PLATFORM)/include/
+    else
+        MET_PTPOD = n
+        $(info ======== Missing $(srctree)/drivers/misc/mediatek/include/mt-plat/$(MTK_PLATFORM)/include/mach/mtk_cpufreq_api.h ========)
+        $(info ======== disable MET_PTPOD ========)
+    endif
+
+    # for mtk_cpufreq_config.h
+    ifneq ("$(wildcard $(MET_PTPOD_INC)/mtk_cpufreq_config.h)","")
+        ccflags-y += -I$(MET_PTPOD_INC)
+    else
+        MET_PTPOD = n
+        $(info ======== Missing $(MET_PTPOD_INC)/mtk_cpufreq_config.h ========)
+        $(info ======== disable MET_PTPOD ========)
+    endif
+else
+    MET_PTPOD := n
+endif
+
+met-$(MET_PTPOD) += $(MET_CORE)/met_ptpod.o
+
+
+################################################################################
+# MET_CPUDSU
+################################################################################
+FEATURE_CPUDSU := $(if $(FEATURE_CPUDSU),$(FEATURE_CPUDSU),y)
+$(info FEATURE_CPUDSU = $(FEATURE_CPUDSU))
+
+MET_CPUDSU := $(if $(filter n,$(FEATURE_CPUDSU)),n,y)
+
+met-$(MET_CPUDSU) += $(MET_CORE)/cpu_dsu.o \
+                     $(MET_CORE)/v8_dsu_hw.o
+
+################################################################################
+# MET_WALLTIME
+################################################################################
+FEATURE_WALLTIME := $(if $(FEATURE_WALLTIME),$(FEATURE_WALLTIME),y)
+$(info FEATURE_WALLTIME = $(FEATURE_WALLTIME))
+
+MET_WALLTIME := $(if $(filter n,$(FEATURE_WALLTIME)),n,y)
+
+met-$(MET_WALLTIME) += $(MET_CORE)/met_wall_time.o
+
+################################################################################
+# MET_SMI
+################################################################################
+FEATURE_SMI := $(if $(FEATURE_SMI),$(FEATURE_SMI),y)
+$(info FEATURE_SMI = $(FEATURE_SMI))
+
+#MET_SMI := $(if $(filter n,$(FEATURE_SMI)),n,y)
+
+#met-$(MET_SMI) += $(MET_CORE)/sspm/sspm_met_smi.o
+
+################################################################################
+# EVENT_POWER
+################################################################################
+FEATURE_EVENT_POWER := $(if $(FEATURE_EVENT_POWER),$(FEATURE_EVENT_POWER),y)
+$(info FEATURE_EVENT_POWER = $(FEATURE_EVENT_POWER))
+
+ifeq ($(FEATURE_EVENT_POWER), y)
+    ccflags-y += -DMET_EVENT_POWER_SUPPORT
+endif
+
+################################################################################
+# On-die-met SSPM only module
+################################################################################
+FEATURE_ONDIEMET := $(if $(FEATURE_ONDIEMET),$(FEATURE_ONDIEMET),y)
+ifeq ($(FEATURE_ONDIEMET), y)
+    FEATURE_ONDIEMET_WALLTIME := $(if $(FEATURE_ONDIEMET_WALLTIME),$(FEATURE_ONDIEMET_WALLTIME),y)
+else
+    FEATURE_ONDIEMET_WALLTIME := n
+endif
+
+$(info FEATURE_ONDIEMET = $(FEATURE_ONDIEMET))
+$(info FEATURE_ONDIEMET_WALLTIME = $(FEATURE_ONDIEMET_WALLTIME))
+
+ifneq ($(FEATURE_ONDIEMET), n)
+    subdir-ccflags-y += -DONDIEMET_SUPPORT
+
+    ifeq ($(CONFIG_MTK_TINYSYS_SSPM_SUPPORT),)
+        $(info CONFIG_MTK_TINYSYS_SSPM_SUPPORT = n)
+    else
+        $(info CONFIG_MTK_TINYSYS_SSPM_SUPPORT = $(CONFIG_MTK_TINYSYS_SSPM_SUPPORT))
+    endif
+
+    ifeq ($(CONFIG_MTK_TINYSYS_SSPM_SUPPORT),y)
+        subdir-ccflags-y += -I$(srctree)/drivers/misc/mediatek/sspm
+        subdir-ccflags-y += -I$(srctree)/drivers/misc/mediatek/sspm/$(MTK_PLATFORM)
+        met-y += $(MET_CORE)/sspm/sspm_ipi_handle.o
+        met-y += $(MET_CORE)/sspm/sspm_common.o
+        ccflags-y += -DMTK_TINYSYS_SSPM_SUPPORT
+
+        SSPM_VERSION := $(if $(SSPM_VERSION),$(SSPM_VERSION),v1)
+        $(info SSPM_VERSION = $(SSPM_VERSION))
+
+        ifneq ($(SSPM_VERSION), v2)
+            MET_SSPM_COMM_INC := $(srctree)/drivers/misc/mediatek/sspm/$(SSPM_VERSION)
+            MET_SSPM_IF_INC := $(srctree)/drivers/misc/mediatek/sspm/$(SSPM_VERSION)
+            MET_SSPM_IF := sspm_ipi.h
+            MET_SSPM_IPI := sspm_ipi_define.h
+        else
+            MET_SSPM_COMM_INC := $(srctree)/drivers/misc/mediatek/sspm/$(SSPM_VERSION)
+            MET_SSPM_IF_INC := $(srctree)/drivers/misc/mediatek/include/mt-plat/
+            MET_SSPM_IF := mtk_tinysys_ipi.h
+            MET_SSPM_IPI := sspm_ipi_table.h
+        endif
+
+
+        ifneq ("$(wildcard $(srctree)/drivers/misc/mediatek/sspm/$(MTK_PLATFORM)/$(MET_SSPM_IPI))","")
+            subdir-ccflags-y += -I$(srctree)/drivers/misc/mediatek/sspm \
+                    -I$(srctree)/drivers/misc/mediatek/sspm/$(MTK_PLATFORM)
+
+            SYS_SSPM_READY := y
+        else
+            $(info ======== Missing $(srctree)/drivers/misc/mediatek/sspm/$(MTK_PLATFORM)/$(MET_SSPM_IPI)========)
+            $(info ======== disable ALL ondiemet feature ========)
+
+            SYS_SSPM_READY := n
+        endif
+
+        # for smi_met_conf format
+        ifeq ($(SSPM_VERSION), v2)
+            ccflags-y += -DSSPM_VERSION_V2
+            ccflags-y += -DSMI_MASTER_8BIT
+        endif
+
+        # for sspm ipi interface
+        ifneq ("$(wildcard $(MET_SSPM_IF_INC)/$(MET_SSPM_IF))","")
+            ccflags-y += -I$(MET_SSPM_IF_INC) \
+                    -I$(MET_SSPM_COMM_INC)
+        else
+            $(info ======== Missing $(MET_SSPM_IF_INC)/$(MET_SSPM_IF) ========)
+            $(info ======== disable ALL ondiemet feature ========)
+
+            SYS_SSPM_READY := n
+        endif
+    else
+        $(info ======== CONFIG_MTK_TINYSYS_SSPM_SUPPORT = n ========)
+        $(info ======== disable ALL ondiemet feature ========)
+
+        SYS_SSPM_READY := n
+    endif
+
+    ifeq ($(SYS_SSPM_READY), y)
+        MET_SSPM_WALLTIME := $(if $(filter n,$(FEATURE_ONDIEMET_WALLTIME)),n,y)
+        met-$(MET_SSPM_WALLTIME) += $(MET_CORE)/sspm/sspm_walltime.o
+
+        MET_SMI := $(if $(filter n,$(FEATURE_SMI)),n,y)
+        met-$(MET_SMI) += $(MET_CORE)/sspm/sspm_met_smi.o
+    endif
+endif
+################################################################################
+#   MET built in mode
+################################################################################
+else # ifneq ($(CONFIG_MTK_MET_BUILT_IN),y)
+$(info ======== MET Built in ... ========)
+MET_DRV_DIR := ../../../../../vendor/mediatek/kernel_modules/met_drv/$(CONFIG_KERVER_VERSION)
+MET_CORE := $(MET_DRV_DIR)/common
+
+ifneq ($(wildcard $(MET_PLF_DIR)/Kbuild.platform.h),)
+    include $(MET_PLF_DIR)/Kbuild.platform.h
+else
+    $(info ======= Missing $(MET_PLF_DIR)/Kbuild.platform.h ========)
+endif
+
+ccflags-y += -DCONFIG_MET_MODULE
+ccflags-y += -DMET_PLF_USE
+ccflags-y += -I$(MET_COMMON_DIR)
+ccflags-y += -I$(MET_PLF_DIR)
+ccflags-y += -I$(srctree)/include/
+ccflags-y += -I$(srctree)/drivers/misc/mediatek/include/
+ccflags-y += -I$(srctree)/drivers/misc/mediatek/include/mt-plat/
+ccflags-y += -I$(srctree)/drivers/misc/mediatek/include/mt-plat/$(MTK_PLATFORM)/include/
+ccflags-y += -I$(srctree)/drivers/misc/mediatek/base/power/$(MTK_PLATFORM)/
+
+ccflags-y += $(EXTRA_ARGS) $(EXTRA_CFLAGS)
+ccflags-y += -DMTK_PLATFORM=$(MTK_PLATFORM)
+
+obj-y += $(MET_CORE)/met_main.o \
+    $(MET_CORE)/met_backlight.o \
+    $(MET_CORE)/met_tag_ex.o \
+    $(MET_CORE)/interface.o \
+    $(MET_CORE)/sampler.o \
+    $(MET_CORE)/dummy_header.o \
+    $(MET_CORE)/util.o \
+    $(MET_CORE)/stat.o \
+    $(MET_CORE)/cookie.o \
+    $(MET_CORE)/mem_stat.o \
+    $(MET_CORE)/switch.o \
+    $(MET_CORE)/trace_event.o \
+    $(MET_CORE)/core_plf_init.o \
+    $(MET_CORE)/core_plf_trace.o \
+    $(MET_CORE)/ondiemet.o \
+    $(MET_CORE)/ondiemet_log.o \
+    $(MET_CORE)/sspm/ondiemet_sspm.o
+
+CFLAGS_interface.o += -DMET_USER_EVENT_SUPPORT
+CFLAGS_met_tag_ex.o += -DMET_USER_EVENT_SUPPORT
+
+$(info CPUPMU_VERSION = $(CPUPMU_VERSION))
+ifeq ("$(CPUPMU_VERSION)", "V8_2")
+    ccflags-y += -DCPUPMU_V8_2
+endif
+
+$(info ARCH = $(ARCH))
+ifeq ($(ARCH), mips)
+    obj-y += $(MET_CORE)/mips_pmu_hw.o
+endif #ifeq ($(ARCH), mips)
+
+ifeq ($(ARCH), arm)
+    ccflags-y += -DCONFIG_MET_ARM_32BIT
+    obj-y += $(MET_CORE)/cpu_pmu.o
+    obj-y += $(MET_CORE)/v7_pmu_hw.o
+    obj-y += $(MET_CORE)/v6_pmu_hw.o
+endif #ifeq ($(ARCH), arm)
+
+ifeq ($(ARCH), arm64)
+    obj-y += $(MET_CORE)/cpu_pmu.o
+    obj-y += $(MET_CORE)/v8_pmu_hw.o
+endif
+
+$(info CONFIG_CPU_FREQ = $(CONFIG_CPU_FREQ))
+ifeq ($(CONFIG_CPU_FREQ),y)
+    obj-y += $(MET_CORE)/power.o
+endif
+
+
+################################################################################
+# MET_EMI
+################################################################################
+FEATURE_SSPM_EMI := $(if $(FEATURE_SSPM_EMI),$(FEATURE_SSPM_EMI),y)
+$(info FEATURE_SSPM_EMI = $(FEATURE_SSPM_EMI))
+
+ifeq ($(CONFIG_MTK_TINYSYS_SSPM_SUPPORT),y)
+    ifneq ($(FEATURE_ONDIEMET), n)
+        MET_EMI := $(if $(filter n,$(FEATURE_SSPM_EMI)),n,y)
+        ifeq ("$(EMI_SEDA_VERSION)", "SEDA3_5")
+            met-$(MET_EMI) += $(MET_CORE)/emi/SEDA3_5/met_emi.o \
+                   $(MET_CORE)/emi/SEDA3_5/mtk_emi_bm.o
+        else ifeq ("$(EMI_SEDA_VERSION)", "SEDA3_6")
+            met-$(MET_EMI) += $(MET_CORE)/emi/SEDA3_6/met_emi.o \
+                   $(MET_CORE)/emi/SEDA3_6/mtk_emi_bm.o
+        else
+            met-$(MET_EMI) += $(MET_CORE)/emi/SEDA3/met_emi.o \
+                   $(MET_CORE)/emi/SEDA3/mtk_emi_bm.o
+        endif
+    endif
+endif
+################################################################################
+# MET_GPU
+################################################################################
+FEATURE_GPU := $(if $(FEATURE_GPU),$(FEATURE_GPU),y)
+$(info FEATURE_GPU = $(FEATURE_GPU))
+
+ifneq ($(FEATURE_GPU), n)
+    MET_GPU := y
+
+    # for mtk_gpufreq.h
+    ifneq ("$(wildcard $(srctree)/drivers/misc/mediatek/base/power/$(MTK_PLATFORM)/mtk_gpufreq.h)","")
+        ccflags-y += -I$(srctree)/drivers/misc/mediatek/base/power/$(MTK_PLATFORM)/
+    else ifneq ("$(wildcard $(srctree)/drivers/misc/mediatek/base/power/include/mtk_gpufreq.h)","")
+        ccflags-y += -I$(srctree)/drivers/misc/mediatek/base/power/include/
+    else
+        MET_GPU = n
+        $(info ======= Missing $(srctree)/drivers/misc/mediatek/base/power/$(MTK_PLATFORM)/mtk_gpufreq.h ========)
+        $(info ======= Missing $(srctree)/drivers/misc/mediatek/base/power/include/mtk_gpufreq.h ========)
+        $(info ======== disable MET_GPU ========)
+    endif
+
+    # for mtk_gpu_utility.h
+    ifneq ("$(wildcard $(srctree)/drivers/misc/mediatek/include/mt-plat/mtk_gpu_utility.h)","")
+        ccflags-y += -I$(srctree)/drivers/misc/mediatek/include/mt-plat/
+    else
+        MET_GPU = n
+        $(info ======== Missing $(srctree)/drivers/misc/mediatek/include/mt-plat/mtk_gpu_utility.h ========)
+        $(info ======== disable MET_GPU ========)
+    endif
+
+    ifneq ($(CONFIG_MTK_GPU_SUPPORT), y)
+        MET_GPU = n
+        $(info ======== CONFIG_MTK_GPU_SUPPORT = n ========)
+        $(info ======== disable MET_GPU ========)
+    endif
+
+    GPU_STALL_CNT_TYPE := $(if $(GPU_STALL_CNT_TYPE),$(GPU_STALL_CNT_TYPE),multiple)
+    $(info GPU_STALL_CNT_TYPE = $(GPU_STALL_CNT_TYPE))
+
+    ifeq ("$(GPU_STALL_CNT_TYPE)", "single")
+        ccflags-y += -DGPU_STALL_CNT_SINGLE
+    endif
+else
+    MET_GPU := n
+endif
+
+obj-$(MET_GPU) += $(MET_CORE)/mtk_gpu_metmonitor.o
+
+
+################################################################################
+# MET_VCOREDVFS
+################################################################################
+FEATURE_VCOREDVFS := $(if $(FEATURE_VCOREDVFS),$(FEATURE_VCOREDVFS),y)
+$(info FEATURE_VCOREDVFS = $(FEATURE_VCOREDVFS))
+
+ifneq ($(FEATURE_VCOREDVFS), n)
+    MET_VCOREDVFS := y
+
+    # for mtk_vcorefs_manager.h
+    ifneq ("$(wildcard $(MET_VCOREDVFS_INC)/mtk_vcorefs_manager.h)","")
+        ccflags-y += -I$(MET_VCOREDVFS_INC)/
+    else
+        MET_VCOREDVFS = n
+        $(info ======== Missing $(MET_VCOREDVFS_INC)/mtk_vcorefs_manager.h ========)
+        $(info ======== disable MET_VCOREDVFS ========)
+    endif
+
+    # for mtk_vcorefs_governor.h
+    ifneq ("$(wildcard $(MET_VCOREDVFS_INC)/mtk_vcorefs_governor.h)","")
+        ccflags-y += -I$(MET_VCOREDVFS_INC)
+    else
+        MET_VCOREDVFS = n
+        $(info ======== Missing $(MET_VCOREDVFS_INC)/mtk_vcorefs_governor.h ========)
+        $(info ======== disable MET_VCOREDVFS ========)
+    endif
+
+    # for helio-dvfsrc.h
+    ifneq ("$(wildcard $(srctree)/drivers/devfreq/helio-dvfsrc.h)","")
+        ccflags-y += -I$(srctree)/drivers/devfreq/
+    else
+        MET_VCOREDVFS = n
+        $(info ======== Missing $(srctree)/drivers/devfreq/helio-dvfsrc.h ========)
+        $(info ======== disable MET_VCOREDVFS ========)
+    endif
+else
+    MET_VCOREDVFS := n
+endif
+
+obj-$(MET_VCOREDVFS) += $(MET_CORE)/met_vcoredvfs.o
+
+
+################################################################################
+# MET_PTPOD
+################################################################################
+FEATURE_PTPOD := $(if $(FEATURE_PTPOD),$(FEATURE_PTPOD),y)
+$(info FEATURE_PTPOD = $(FEATURE_PTPOD))
+
+ifneq ($(FEATURE_PTPOD), n)
+    MET_PTPOD := y
+
+    # for mtk_gpufreq.h
+    ifneq ("$(wildcard $(srctree)/drivers/misc/mediatek/base/power/$(MTK_PLATFORM)/mtk_gpufreq.h)","")
+        ccflags-y += -I$(srctree)/drivers/misc/mediatek/base/power/$(MTK_PLATFORM)/
+    else ifneq ("$(wildcard $(srctree)/drivers/misc/mediatek/base/power/include/mtk_gpufreq.h)","")
+        ccflags-y += -I$(srctree)/drivers/misc/mediatek/base/power/include/
+    else
+        MET_PTPOD = n
+        $(info ======== Missing $(srctree)/drivers/misc/mediatek/base/power/$(MTK_PLATFORM)/mtk_gpufreq.h ========)
+        $(info ======== Missing $(srctree)/drivers/misc/mediatek/base/power/include/mtk_gpufreq.h ========)
+        $(info ======== disable MET_PTPOD ========)
+    endif
+
+    # for mtk_cpufreq_api.h
+    ifneq ("$(wildcard $(srctree)/drivers/misc/mediatek/include/mt-plat/$(MTK_PLATFORM)/include/mach/mtk_cpufreq_api.h)","")
+        ccflags-y += -I$(srctree)/drivers/misc/mediatek/include/mt-plat/$(MTK_PLATFORM)/include/
+    else
+        MET_PTPOD = n
+        $(info ======== Missing $(srctree)/drivers/misc/mediatek/include/mt-plat/$(MTK_PLATFORM)/include/mach/mtk_cpufreq_api.h ========)
+        $(info ======== disable MET_PTPOD ========)
+    endif
+
+    # for mtk_cpufreq_config.h
+    ifneq ("$(wildcard $(MET_PTPOD_INC)/mtk_cpufreq_config.h)","")
+        ccflags-y += -I$(MET_PTPOD_INC)
+    else
+        MET_PTPOD = n
+        $(info ======== Missing $(MET_PTPOD_INC)/mtk_cpufreq_config.h ========)
+        $(info ======== disable MET_PTPOD ========)
+    endif
+else
+    MET_PTPOD := n
+endif
+
+obj-$(MET_PTPOD) += $(MET_CORE)/met_ptpod.o
+
+
+################################################################################
+# MET_CPUDSU
+################################################################################
+FEATURE_CPUDSU := $(if $(FEATURE_CPUDSU),$(FEATURE_CPUDSU),y)
+$(info FEATURE_CPUDSU = $(FEATURE_CPUDSU))
+
+MET_CPUDSU := $(if $(filter n,$(FEATURE_CPUDSU)),n,y)
+
+obj-$(MET_CPUDSU) += $(MET_CORE)/cpu_dsu.o \
+                     $(MET_CORE)/v8_dsu_hw.o
+
+################################################################################
+# MET_WALLTIME
+################################################################################
+FEATURE_WALLTIME := $(if $(FEATURE_WALLTIME),$(FEATURE_WALLTIME),y)
+$(info FEATURE_WALLTIME = $(FEATURE_WALLTIME))
+
+MET_WALLTIME := $(if $(filter n,$(FEATURE_WALLTIME)),n,y)
+
+obj-$(MET_WALLTIME) += $(MET_CORE)/met_wall_time.o
+
+################################################################################
+# MET_SMI
+################################################################################
+FEATURE_SMI := $(if $(FEATURE_SMI),$(FEATURE_SMI),y)
+$(info FEATURE_SMI = $(FEATURE_SMI))
+
+#MET_SMI := $(if $(filter n,$(FEATURE_SMI)),n,y)
+
+#obj-$(MET_SMI) += $(MET_CORE)/sspm/sspm_met_smi.o
+
+
+################################################################################
+# EVENT_POWER
+################################################################################
+FEATURE_EVENT_POWER := $(if $(FEATURE_EVENT_POWER),$(FEATURE_EVENT_POWER),y)
+$(info FEATURE_EVENT_POWER = $(FEATURE_EVENT_POWER))
+
+ifeq ($(FEATURE_EVENT_POWER), y)
+    ccflags-y += -DMET_EVENT_POWER_SUPPORT
+endif
+
+################################################################################
+# On-die-met SSPM only module
+################################################################################
+FEATURE_ONDIEMET := $(if $(FEATURE_ONDIEMET),$(FEATURE_ONDIEMET),y)
+ifeq ($(FEATURE_ONDIEMET), y)
+    FEATURE_ONDIEMET_WALLTIME := $(if $(FEATURE_ONDIEMET_WALLTIME),$(FEATURE_ONDIEMET_WALLTIME),y)
+else
+    FEATURE_ONDIEMET_WALLTIME := n
+endif
+
+$(info FEATURE_ONDIEMET = $(FEATURE_ONDIEMET))
+$(info FEATURE_ONDIEMET_WALLTIME = $(FEATURE_ONDIEMET_WALLTIME))
+
+ifneq ($(FEATURE_ONDIEMET), n)
+    subdir-ccflags-y += -DONDIEMET_SUPPORT
+
+    ifeq ($(CONFIG_MTK_TINYSYS_SSPM_SUPPORT),)
+        $(info CONFIG_MTK_TINYSYS_SSPM_SUPPORT = n)
+    else
+        $(info CONFIG_MTK_TINYSYS_SSPM_SUPPORT = $(CONFIG_MTK_TINYSYS_SSPM_SUPPORT))
+    endif
+
+    ifeq ($(CONFIG_MTK_TINYSYS_SSPM_SUPPORT),y)
+        subdir-ccflags-y += -I$(srctree)/drivers/misc/mediatek/sspm
+        subdir-ccflags-y += -I$(srctree)/drivers/misc/mediatek/sspm/$(MTK_PLATFORM)
+        obj-y += $(MET_CORE)/sspm/sspm_ipi_handle.o
+        obj-y += $(MET_CORE)/sspm/sspm_common.o
+        ccflags-y += -DMTK_TINYSYS_SSPM_SUPPORT
+
+        SSPM_VERSION := $(if $(SSPM_VERSION),$(SSPM_VERSION),v1)
+        $(info SSPM_VERSION = $(SSPM_VERSION))
+
+        ifneq ($(SSPM_VERSION), v2)
+            MET_SSPM_COMM_INC := $(srctree)/drivers/misc/mediatek/sspm/$(SSPM_VERSION)
+            MET_SSPM_IF_INC := $(srctree)/drivers/misc/mediatek/sspm/$(SSPM_VERSION)
+            MET_SSPM_IF := sspm_ipi.h
+            MET_SSPM_IPI := sspm_ipi_define.h
+        else
+            MET_SSPM_COMM_INC := $(srctree)/drivers/misc/mediatek/sspm/$(SSPM_VERSION)
+            MET_SSPM_IF_INC := $(srctree)/drivers/misc/mediatek/include/mt-plat/
+            MET_SSPM_IF := mtk_tinysys_ipi.h
+            MET_SSPM_IPI := sspm_ipi_table.h
+        endif
+
+
+        ifneq ("$(wildcard $(srctree)/drivers/misc/mediatek/sspm/$(MTK_PLATFORM)/$(MET_SSPM_IPI))","")
+            subdir-ccflags-y += -I$(srctree)/drivers/misc/mediatek/sspm \
+                    -I$(srctree)/drivers/misc/mediatek/sspm/$(MTK_PLATFORM)
+
+            SYS_SSPM_READY := y
+        else
+            $(info ======== Missing $(srctree)/drivers/misc/mediatek/sspm/$(MTK_PLATFORM)/$(MET_SSPM_IPI)========)
+            $(info ======== disable ALL ondiemet feature ========)
+
+            SYS_SSPM_READY := n
+        endif
+
+        # for sspm ipi interface
+        ifneq ("$(wildcard $(MET_SSPM_IF_INC)/$(MET_SSPM_IF))","")
+            ccflags-y += -I$(MET_SSPM_IF_INC) \
+                    -I$(MET_SSPM_COMM_INC)
+        else
+            $(info ======== Missing $(MET_SSPM_IF_INC)/$(MET_SSPM_IF) ========)
+            $(info ======== disable ALL ondiemet feature ========)
+
+            SYS_SSPM_READY := n
+        endif
+    else
+        $(info ======== CONFIG_MTK_TINYSYS_SSPM_SUPPORT = n ========)
+        $(info ======== disable ALL ondiemet feature ========)
+
+        SYS_SSPM_READY := n
+    endif
+
+    ifeq ($(SYS_SSPM_READY), y)
+        MET_SSPM_WALLTIME := $(if $(filter n,$(FEATURE_ONDIEMET_WALLTIME)),n,y)
+        obj-$(MET_SSPM_WALLTIME) += $(MET_CORE)/sspm/sspm_walltime.o
+
+        MET_SMI := $(if $(filter n,$(FEATURE_SMI)),n,y)
+        met-$(MET_SMI) += $(MET_CORE)/sspm/sspm_met_smi.o
+    endif
+endif
+
+endif # end of ifneq ($(CONFIG_MTK_MET_BUILT_IN),y)
+
+
+##############################################################################################
+# include $(MET_PLF_DIR)/Kbuild
+##############################################################################################
+ifneq ($(wildcard $(MET_PLF_DIR)/Kbuild),)
+    include $(MET_PLF_DIR)/Kbuild
+else
+    $(info ======= Missing $(MET_PLF_DIR)/Kbuild ========)
+endif
+
+#################################################################################
+# add met_device flags
+#################################################################################
+ccflags-y += $(foreach v, $(filter MET_%,$(.VARIABLES)), $(if $(filter $($(v)),y),-D$(v)))
diff --git a/src/devtools/met-driver/4.19/common/Kbuild.yocto b/src/devtools/met-driver/4.19/common/Kbuild.yocto
new file mode 100644
index 0000000..a0425d3
--- /dev/null
+++ b/src/devtools/met-driver/4.19/common/Kbuild.yocto
@@ -0,0 +1,370 @@
+################################################################################
+#   MET Kernel module mode
+################################################################################
+$(info ======== Build met.ko ... ========)
+MET_CORE := common
+obj-m := met.o
+
+ifneq ($(wildcard $(MET_PLF_DIR)/Kbuild.platform.h),)
+    include $(MET_PLF_DIR)/Kbuild.platform.h
+else
+    $(info ======= Missing $(MET_PLF_DIR)/Kbuild.platform.h ========)
+endif
+
+ccflags-y += -DCONFIG_MET_MODULE
+ccflags-y += -DMET_PLF_USE
+ccflags-y += -I$(MET_COMMON_DIR)
+ccflags-y += -I$(MET_PLF_DIR)
+ccflags-y += -I$(KERNEL_SRC)/include/
+ccflags-y += -I$(KERNEL_SRC)/drivers/misc/mediatek/include/
+ccflags-y += -I$(KERNEL_SRC)/drivers/misc/mediatek/include/mt-plat/
+ccflags-y += -I$(KERNEL_SRC)/drivers/misc/mediatek/include/mt-plat/$(MTK_PLATFORM)/include/
+
+ccflags-y += $(EXTRA_ARGS) $(EXTRA_CFLAGS)
+ccflags-y += -DMTK_PLATFORM=$(MTK_PLATFORM)
+
+met-y := $(MET_CORE)/met_main.o \
+    $(MET_CORE)/met_backlight.o \
+    $(MET_CORE)/met_tag_ex.o \
+    $(MET_CORE)/interface.o \
+    $(MET_CORE)/sampler.o \
+    $(MET_CORE)/dummy_header.o \
+    $(MET_CORE)/util.o \
+    $(MET_CORE)/stat.o \
+    $(MET_CORE)/cookie.o \
+    $(MET_CORE)/mem_stat.o \
+    $(MET_CORE)/switch.o \
+    $(MET_CORE)/trace_event.o \
+    $(MET_CORE)/core_plf_init.o \
+    $(MET_CORE)/core_plf_trace.o \
+    $(MET_CORE)/ondiemet.o \
+    $(MET_CORE)/ondiemet_log.o \
+    $(MET_CORE)/sspm/ondiemet_sspm.o
+
+CFLAGS_interface.o += -DMET_USER_EVENT_SUPPORT
+CFLAGS_met_tag_ex.o += -DMET_USER_EVENT_SUPPORT
+
+$(info CPUPMU_VERSION = $(CPUPMU_VERSION))
+ifeq ("$(CPUPMU_VERSION)", "V8_2")
+    ccflags-y += -DCPUPMU_V8_2
+endif
+
+$(info ARCH = $(ARCH))
+ifeq ($(ARCH), mips)
+    met-y += $(MET_CORE)/mips_pmu_hw.o
+endif #ifeq ($(ARCH), mips)
+
+ifeq ($(ARCH), arm)
+    ccflags-y += -DCONFIG_MET_ARM_32BIT
+    met-y += $(MET_CORE)/cpu_pmu.o
+    met-y += $(MET_CORE)/v7_pmu_hw.o
+    met-y += $(MET_CORE)/v6_pmu_hw.o
+endif #ifeq ($(ARCH), arm)
+
+ifeq ($(ARCH), arm64)
+    met-y += $(MET_CORE)/cpu_pmu.o
+    met-y += $(MET_CORE)/v8_pmu_hw.o
+endif
+
+$(info CONFIG_CPU_FREQ = $(CONFIG_CPU_FREQ))
+ifeq ($(CONFIG_CPU_FREQ),y)
+    met-y += $(MET_CORE)/power.o
+endif
+
+################################################################################
+# MET_SPM_TWAM
+################################################################################
+FEATURE_SPMTWAM := $(if $(FEATURE_SPMTWAM),$(FEATURE_SPMTWAM),y)
+$(info FEATURE_SPMTWAM = $(FEATURE_SPMTWAM))
+
+ifneq ($(FEATURE_SPMTWAM), n)
+    MET_SPM_TWAM := y
+
+    # for mtk_spm.h
+    ifneq ("$(wildcard $(KERNEL_SRC)/drivers/misc/mediatek/base/power/include/mtk_spm.h)","")
+        ccflags-y += -I$(KERNEL_SRC)/drivers/misc/mediatek/base/power/include/
+        ccflags-y += -I$(MET_COMMON_DIR)/spmtwam/include/
+    else
+        MET_SPM_TWAM = n
+        $(info ========= Missing $(KERNEL_SRC)/drivers/misc/mediatek/base/power/include/mtk_spm.h ========)
+        $(info ======== disable MET_SPM_TWAM ========)
+    endif
+else
+    MET_SPM_TWAM := n
+endif
+
+$(info SPMTWAM_VERSION = $(SPMTWAM_VERSION))
+$(info SPMTWAM_IDLE_SIGNAL_SUPPORT = $(SPMTWAM_IDLE_SIGNAL_SUPPORT))
+
+ifeq ("$(SPMTWAM_IDLE_SIGNAL_SUPPORT)", "single")
+    ccflags-y += -DSPMTWAM_SINGLE_IDLE_SIGNAL
+endif
+
+ifeq ("$(SPMTWAM_IDLE_SIGNAL_SUPPORT)", "multiple")
+    ccflags-y += -DSPMTWAM_MULTIPLE_IDLE_SIGNAL
+endif
+
+ifeq ("$(SPMTWAM_VERSION)", "ap")
+    ccflags-y += -DSPMTWAM_AP
+    met-$(MET_SPM_TWAM) += $(MET_CORE)/spmtwam/ap/met_spmtwam.o
+endif
+
+ifeq ("$(SPMTWAM_VERSION)", "sspm")
+    ccflags-y += -DSPMTWAM_SSPM
+    met-$(MET_SPM_TWAM) += $(MET_CORE)/spmtwam/sspm/met_spmtwam.o
+endif
+
+################################################################################
+# MET_SSPM_EMI
+################################################################################
+FEATURE_SSPM_EMI := $(if $(FEATURE_SSPM_EMI),$(FEATURE_SSPM_EMI),y)
+$(info FEATURE_SSPM_EMI = $(FEATURE_SSPM_EMI))
+
+ifeq ($(CONFIG_MTK_TINYSYS_SSPM_SUPPORT),y)
+    ifneq ($(FEATURE_ONDIEMET), n)
+        MET_SSPM_EMI := $(if $(filter n,$(FEATURE_SSPM_EMI)),n,y)
+        ifeq ("$(EMI_SEDA_VERSION)", "SEDA3_5")
+            met-$(MET_SSPM_EMI) += $(MET_CORE)/emi/SEDA3_5/met_emi.o \
+                   $(MET_CORE)/emi/SEDA3_5/mtk_emi_bm.o
+        else ifeq ("$(EMI_SEDA_VERSION)", "SEDA3_6")
+            met-$(MET_SSPM_EMI) += $(MET_CORE)/emi/SEDA3_6/met_emi.o \
+                   $(MET_CORE)/emi/SEDA3_6/mtk_emi_bm.o
+        else
+            met-$(MET_SSPM_EMI) += $(MET_CORE)/emi/SEDA3/met_emi.o \
+                   $(MET_CORE)/emi/SEDA3/mtk_emi_bm.o
+        endif
+    endif
+endif
+
+################################################################################
+# MET_GPU
+################################################################################
+FEATURE_GPU := $(if $(FEATURE_GPU),$(FEATURE_GPU),y)
+$(info FEATURE_GPU = $(FEATURE_GPU))
+
+ifneq ($(FEATURE_GPU), n)
+    MET_GPU := y
+
+    # for mtk_gpufreq.h
+    ifneq ("$(wildcard $(KERNEL_SRC)/drivers/misc/mediatek/base/power/$(MTK_PLATFORM)/mtk_gpufreq.h)","")
+        ccflags-y += -I$(KERNEL_SRC)/drivers/misc/mediatek/base/power/$(MTK_PLATFORM)/
+    else
+        MET_GPU = n
+        $(info ======= Missing $(KERNEL_SRC)/drivers/misc/mediatek/base/power/$(MTK_PLATFORM)/mtk_gpufreq.h ========)
+        $(info ======== disable MET_GPU ========)
+    endif
+
+    # for mtk_gpu_utility.h
+    ifneq ("$(wildcard $(KERNEL_SRC)/drivers/misc/mediatek/include/mt-plat/mtk_gpu_utility.h)","")
+        ccflags-y += -I$(KERNEL_SRC)/drivers/misc/mediatek/include/mt-plat/
+    else
+        MET_GPU = n
+        $(info ======== Missing $(KERNEL_SRC)/drivers/misc/mediatek/include/mt-plat/mtk_gpu_utility.h ========)
+        $(info ======== disable MET_GPU ========)
+    endif
+
+    ifneq ($(CONFIG_MTK_GPU_SUPPORT), y)
+        MET_GPU = n
+        $(info ======== CONFIG_MTK_GPU_SUPPORT = n ========)
+        $(info ======== disable MET_GPU ========)
+    endif
+else
+    MET_GPU := n
+endif
+
+met-$(MET_GPU) += $(MET_CORE)/mtk_gpu_metmonitor.o
+
+
+################################################################################
+# MET_VCOREDVFS
+################################################################################
+FEATURE_VCOREDVFS := $(if $(FEATURE_VCOREDVFS),$(FEATURE_VCOREDVFS),y)
+$(info FEATURE_VCOREDVFS = $(FEATURE_VCOREDVFS))
+
+ifneq ($(FEATURE_VCOREDVFS), n)
+    MET_VCOREDVFS := y
+
+    # for mtk_vcorefs_manager.h
+    ifneq ("$(wildcard $(MET_VCOREDVFS_INC)/mtk_vcorefs_manager.h)","")
+        ccflags-y += -I$(MET_VCOREDVFS_INC)/
+    else
+        MET_VCOREDVFS = n
+        $(info ======== Missing $(MET_VCOREDVFS_INC)/mtk_vcorefs_manager.h ========)
+        $(info ======== disable MET_VCOREDVFS ========)
+    endif
+
+    # for mtk_vcorefs_governor.h
+    ifneq ("$(wildcard $(MET_VCOREDVFS_INC)/mtk_vcorefs_governor.h)","")
+        ccflags-y += -I$(MET_VCOREDVFS_INC)
+    else
+        MET_VCOREDVFS = n
+        $(info ======== Missing $(MET_VCOREDVFS_INC)/mtk_vcorefs_governor.h ========)
+        $(info ======== disable MET_VCOREDVFS ========)
+    endif
+
+    # for helio-dvfsrc.h
+    ifneq ("$(wildcard $(KERNEL_SRC)/drivers/devfreq/helio-dvfsrc.h)","")
+        ccflags-y += -I$(KERNEL_SRC)/drivers/devfreq/
+    else
+        MET_VCOREDVFS = n
+        $(info ======== Missing $(KERNEL_SRC)/drivers/devfreq/helio-dvfsrc.h ========)
+        $(info ======== disable MET_VCOREDVFS ========)
+    endif
+else
+    MET_VCOREDVFS := n
+endif
+
+met-$(MET_VCOREDVFS) += $(MET_CORE)/met_vcoredvfs.o
+
+
+################################################################################
+# MET_PTPOD
+################################################################################
+FEATURE_PTPOD := $(if $(FEATURE_PTPOD),$(FEATURE_PTPOD),y)
+$(info FEATURE_PTPOD = $(FEATURE_PTPOD))
+
+ifneq ($(FEATURE_PTPOD), n)
+    MET_PTPOD := y
+
+    # for mtk_gpufreq.h
+    ifneq ("$(wildcard $(KERNEL_SRC)/drivers/misc/mediatek/base/power/$(MTK_PLATFORM)/mtk_gpufreq.h)","")
+        ccflags-y += -I$(KERNEL_SRC)/drivers/misc/mediatek/base/power/$(MTK_PLATFORM)/
+    else
+        MET_PTPOD = n
+        $(info ======== Missing $(KERNEL_SRC)/drivers/misc/mediatek/base/power/$(MTK_PLATFORM)/mtk_gpufreq.h ========)
+        $(info ======== disable MET_PTPOD ========)
+    endif
+
+    # for mtk_cpufreq_api.h
+    ifneq ("$(wildcard $(KERNEL_SRC)/drivers/misc/mediatek/include/mt-plat/$(MTK_PLATFORM)/include/mach/mtk_cpufreq_api.h)","")
+        ccflags-y += -I$(KERNEL_SRC)/drivers/misc/mediatek/include/mt-plat/$(MTK_PLATFORM)/include/
+    else
+        MET_PTPOD = n
+        $(info ======== Missing $(KERNEL_SRC)/drivers/misc/mediatek/include/mt-plat/$(MTK_PLATFORM)/include/mach/mtk_cpufreq_api.h ========)
+        $(info ======== disable MET_PTPOD ========)
+    endif
+
+    # for mtk_cpufreq_config.h
+    ifneq ("$(wildcard $(MET_PTPOD_INC)/mtk_cpufreq_config.h)","")
+        ccflags-y += -I$(MET_PTPOD_INC)
+    else
+        MET_PTPOD = n
+        $(info ======== Missing $(MET_PTPOD_INC)/mtk_cpufreq_config.h ========)
+        $(info ======== disable MET_PTPOD ========)
+    endif
+else
+    MET_PTPOD := n
+endif
+
+met-$(MET_PTPOD) += $(MET_CORE)/met_ptpod.o
+
+
+################################################################################
+# MET_CPUDSU
+################################################################################
+FEATURE_CPUDSU := $(if $(FEATURE_CPUDSU),$(FEATURE_CPUDSU),y)
+$(info FEATURE_CPUDSU = $(FEATURE_CPUDSU))
+
+MET_CPUDSU := $(if $(filter n,$(FEATURE_CPUDSU)),n,y)
+
+met-$(MET_CPUDSU) += $(MET_CORE)/cpu_dsu.o \
+                     $(MET_CORE)/v8_dsu_hw.o
+
+################################################################################
+# MET_WALLTIME
+################################################################################
+FEATURE_WALLTIME := $(if $(FEATURE_WALLTIME),$(FEATURE_WALLTIME),y)
+$(info FEATURE_WALLTIME = $(FEATURE_WALLTIME))
+
+MET_WALLTIME := $(if $(filter n,$(FEATURE_WALLTIME)),n,y)
+
+met-$(MET_WALLTIME) += $(MET_CORE)/met_wall_time.o
+
+################################################################################
+# MET_SSPM_SMI
+################################################################################
+FEATURE_SSPM_SMI := $(if $(FEATURE_SSPM_SMI),$(FEATURE_SSPM_SMI),y)
+$(info FEATURE_SSPM_SMI = $(FEATURE_SSPM_SMI))
+
+MET_SSPM_SMI := $(if $(filter n,$(FEATURE_SSPM_SMI)),n,y)
+
+met-$(MET_SSPM_SMI) += $(MET_CORE)/sspm/sspm_met_smi.o
+
+################################################################################
+# EVENT_POWER
+################################################################################
+FEATURE_EVENT_POWER := $(if $(FEATURE_EVENT_POWER),$(FEATURE_EVENT_POWER),y)
+$(info FEATURE_EVENT_POWER = $(FEATURE_EVENT_POWER))
+
+ifeq ($(FEATURE_EVENT_POWER), y)
+	ccflags-y += -DMET_EVENT_POWER_SUPPORT
+endif
+
+################################################################################
+# On-die-met SSPM only module
+################################################################################
+FEATURE_ONDIEMET := $(if $(FEATURE_ONDIEMET),$(FEATURE_ONDIEMET),y)
+ifeq ($(FEATURE_ONDIEMET), y)
+    FEATURE_ONDIEMET_WALLTIME := $(if $(FEATURE_ONDIEMET_WALLTIME),$(FEATURE_ONDIEMET_WALLTIME),y)
+else
+    FEATURE_ONDIEMET_WALLTIME := n
+endif
+
+$(info FEATURE_ONDIEMET = $(FEATURE_ONDIEMET))
+$(info FEATURE_ONDIEMET_WALLTIME = $(FEATURE_ONDIEMET_WALLTIME))
+
+ifneq ($(FEATURE_ONDIEMET), n)
+    subdir-ccflags-y += -DONDIEMET_SUPPORT
+
+    ifeq ($(CONFIG_MTK_TINYSYS_SSPM_SUPPORT),)
+        $(info CONFIG_MTK_TINYSYS_SSPM_SUPPORT = n)
+    else
+        $(info CONFIG_MTK_TINYSYS_SSPM_SUPPORT = $(CONFIG_MTK_TINYSYS_SSPM_SUPPORT))
+    endif
+
+    ifeq ($(CONFIG_MTK_TINYSYS_SSPM_SUPPORT),y)
+        # for sspm_ipi.h
+        subdir-ccflags-y += -I$(KERNEL_SRC)/drivers/misc/mediatek/sspm
+        subdir-ccflags-y += -I$(KERNEL_SRC)/drivers/misc/mediatek/sspm/$(MTK_PLATFORM)
+        met-y += $(MET_CORE)/sspm/sspm_ipi_handle.o
+        met-y += $(MET_CORE)/sspm/sspm_common.o
+        ccflags-y += -DMTK_TINYSYS_SSPM_SUPPORT
+
+        ifneq ("$(wildcard $(KERNEL_SRC)/drivers/misc/mediatek/sspm/$(MTK_PLATFORM)/sspm_ipi_define.h)","")
+            subdir-ccflags-y += -I$(KERNEL_SRC)/drivers/misc/mediatek/sspm \
+                    -I$(KERNEL_SRC)/drivers/misc/mediatek/sspm/$(MTK_PLATFORM)
+
+            SYS_SSPM_READY := y
+        else
+            $(info ======== Missing $(KERNEL_SRC)/drivers/misc/mediatek/sspm/$(MTK_PLATFORM)/sspm_ipi_define.h========)
+            $(info ======== disable ALL ondiemet feature ========)
+
+            SYS_SSPM_READY := n
+        endif
+    else
+        $(info ======== CONFIG_MTK_TINYSYS_SSPM_SUPPORT = n ========)
+        $(info ======== disable ALL ondiemet feature ========)
+
+        SYS_SSPM_READY := n
+    endif
+
+    ifeq ($(SYS_SSPM_READY), y)
+        MET_SSPM_WALLTIME := $(if $(filter n,$(FEATURE_ONDIEMET_WALLTIME)),n,y)
+
+        met-$(MET_SSPM_WALLTIME) += $(MET_CORE)/sspm/sspm_walltime.o
+    endif
+endif
+
+##############################################################################################
+# include $(MET_PLF_DIR)/Kbuild
+##############################################################################################
+ifneq ($(wildcard $(MET_PLF_DIR)/Kbuild.yocto),)
+	include $(MET_PLF_DIR)/Kbuild.yocto
+else
+	$(info ======= Missing $(MET_PLF_DIR)/Kbuild.yocto ========)
+endif
+
+#################################################################################
+# add met_device flags
+#################################################################################
+ccflags-y += $(foreach v, $(filter MET_%,$(.VARIABLES)), $(if $(filter $($(v)),y),-D$(v)))
diff --git a/src/devtools/met-driver/4.19/common/cookie.c b/src/devtools/met-driver/4.19/common/cookie.c
new file mode 100644
index 0000000..e2c3f0c
--- /dev/null
+++ b/src/devtools/met-driver/4.19/common/cookie.c
@@ -0,0 +1,281 @@
+/*
+ * Copyright (C) 2019 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/cpu.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/cpu.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <asm/irq_regs.h>
+#include <asm/stacktrace.h>
+#include <linux/stacktrace.h>
+#include "interface.h"
+#include "met_drv.h"
+
+#define LINE_SIZE	256
+
+struct cookie_info {
+	int depth;
+	int strlen;
+	char strbuf[LINE_SIZE];
+};
+
+static unsigned int back_trace_depth;
+static DEFINE_PER_CPU(struct cookie_info, info);
+static DEFINE_PER_CPU(int, cpu_status);
+
+static int reset_driver_stat(void)
+{
+	back_trace_depth = 0;
+	met_cookie.mode = 0;
+	return 0;
+}
+
+
+noinline void cookie(char *strbuf)
+{
+	MET_TRACE("%s\n", strbuf);
+}
+
+noinline void cookie2(char *strbuf)
+{
+	MET_TRACE("%s\n", strbuf);
+}
+
+static void get_kernel_cookie(unsigned long pc, struct cookie_info *pinfo)
+{
+	int ret;
+#ifdef CONFIG_MODULES
+	off_t off;
+	struct module *mod = __module_address(pc);
+
+	if (mod) {
+		off = pc - (unsigned long)mod->core_layout.base;
+		ret = snprintf(pinfo->strbuf + pinfo->strlen, LINE_SIZE - pinfo->strlen,
+			       ",%s,%lx", mod->name, off);
+		pinfo->strlen += ret;
+		/* cookie(current->comm, pc, mod->name, off, 1); */
+	} else
+#endif
+	{
+		ret =
+		    snprintf(pinfo->strbuf + pinfo->strlen, LINE_SIZE - pinfo->strlen,
+			     ",vmlinux,%lx", pc);
+		pinfo->strlen += ret;
+		/* cookie(current->comm, pc, "vmlinux", pc, 0); */
+	}
+}
+
+#if defined(__arm__)
+static int report_trace(struct stackframe *frame, void *d)
+{
+	struct cookie_info *pinfo = d;
+	unsigned long pc = frame->pc;
+
+	if (pinfo->depth > 0) {
+		get_kernel_cookie(pc, pinfo);
+		pinfo->depth--;
+		return 0;
+	}
+	return 1;
+}
+#endif
+
+static void kernel_backtrace(struct pt_regs *const regs, struct cookie_info *pinfo)
+{
+#if defined(__arm__)
+	struct stackframe frame;
+
+	frame.fp = regs->ARM_fp;
+	frame.sp = regs->ARM_sp;
+	frame.lr = regs->ARM_lr;
+	frame.pc = regs->ARM_pc;
+	walk_stackframe(&frame, report_trace, pinfo);
+#else
+	return;
+#endif
+}
+
+
+void met_cookie_polling(unsigned long long stamp, int cpu)
+{
+	struct pt_regs *regs;
+	struct cookie_info *pinfo;
+	unsigned long pc;
+	int ret, outflag = 0;
+	off_t off;
+
+	if (per_cpu(cpu_status, cpu) != MET_CPU_ONLINE)
+		return;
+
+	regs = get_irq_regs();
+
+	if (regs == 0)
+		return;
+
+	pc = profile_pc(regs);
+
+	pinfo = &(per_cpu(info, cpu));
+	pinfo->strlen = snprintf(pinfo->strbuf, LINE_SIZE, "%s,%lx", current->comm, pc);
+
+	if (user_mode(regs)) {
+		struct mm_struct *mm;
+		struct vm_area_struct *vma;
+		struct path *ppath;
+
+		mm = current->mm;
+		for (vma = find_vma(mm, pc); vma; vma = vma->vm_next) {
+
+			if (pc < vma->vm_start || pc >= vma->vm_end)
+				continue;
+
+			if (vma->vm_file) {
+				ppath = &(vma->vm_file->f_path);
+
+				if (vma->vm_flags & VM_DENYWRITE)
+					off = pc;
+				else
+					off = (vma->vm_pgoff << PAGE_SHIFT) + pc - vma->vm_start;
+
+				ret =
+				    snprintf(pinfo->strbuf + pinfo->strlen,
+					     LINE_SIZE - pinfo->strlen, ",%s,%lx",
+					     (char *)(ppath->dentry->d_name.name), off);
+				pinfo->strlen += ret;
+				outflag = 1;
+			} else {
+				/* must be an anonymous map */
+				ret =
+				    snprintf(pinfo->strbuf + pinfo->strlen,
+					     LINE_SIZE - pinfo->strlen, ",nofile,%lx", pc);
+				pinfo->strlen += ret;
+				outflag = 1;
+			}
+			break;
+		}
+	} else {
+		/* kernel mode code */
+		if (back_trace_depth > 0) {
+			pinfo->depth = back_trace_depth + 1;
+			kernel_backtrace(regs, pinfo);
+		} else
+			get_kernel_cookie(pc, pinfo);
+		outflag = 1;
+	}
+
+	/* check task is resolvable */
+	if (outflag == 0)
+		return;
+
+	if (back_trace_depth == 0)
+		cookie(pinfo->strbuf);
+	else
+		cookie2(pinfo->strbuf);
+}
+
+
+static void met_cookie_start(void)
+{
+	int	cpu = raw_smp_processor_id();
+	per_cpu(cpu_status, cpu) = MET_CPU_ONLINE;
+	/* return; */
+}
+
+static void met_cookie_stop(void)
+{
+	/* return; */
+}
+
+
+static int met_cookie_process_argument(const char *arg, int len)
+{
+	unsigned int value;
+
+	if (met_parse_num(arg, &value, len) < 0) {
+		met_cookie.mode = 0;
+		return -EINVAL;
+	}
+
+	back_trace_depth = value;
+	met_cookie.mode = 1;
+
+	return 0;
+}
+
+static const char help[] =
+"  --cookie                              enable sampling task and PC\n"
+"  --cookie=N                            enable back trace (depth is N)\n";
+
+static int met_cookie_print_help(char *buf, int len)
+{
+	len = snprintf(buf, PAGE_SIZE, help);
+	return len;
+}
+
+
+static const char header[] =
+"# cookie: task_name,PC,cookie_name,offset\n"
+"met-info [000] 0.0: cookie_header: task_name,PC,cookie_name,offset\n";
+
+static const char header2_1[] = "# cookie2: task_name,PC,cookie,offset";
+static const char header2_2[] = "met-info [000] 0.0: cookie2_header: task_name,PC,cookie,offset";
+
+static int met_cookie_print_header(char *buf, int len)
+{
+	int i, ret;
+
+	if (back_trace_depth == 0) {
+		len = snprintf(buf, PAGE_SIZE, header);
+	} else {
+		len = snprintf(buf, PAGE_SIZE, header2_1);
+		for (i = 0; i < back_trace_depth; i++) {
+			ret = snprintf(buf + len, PAGE_SIZE, ",cookie%d,offset%d", i + 1, i + 1);
+			len += ret;
+		}
+		ret = snprintf(buf + len, PAGE_SIZE, "\n");
+		len += ret;
+
+		ret = snprintf(buf + len, PAGE_SIZE, header2_2);
+		len += ret;
+		for (i = 0; i < back_trace_depth; i++) {
+			ret = snprintf(buf + len, PAGE_SIZE, ",cookie%d,offset%d", i + 1, i + 1);
+			len += ret;
+		}
+		ret = snprintf(buf + len, PAGE_SIZE, "\n");
+		len += ret;
+	}
+
+	return len;
+}
+
+static void met_cookie_cpu_state_notify(long cpu, unsigned long action)
+{
+	per_cpu(cpu_status, cpu) = action;
+}
+
+struct metdevice met_cookie = {
+	.name = "cookie",
+	.type = MET_TYPE_PMU,
+	.cpu_related = 1,
+	.start = met_cookie_start,
+	.stop = met_cookie_stop,
+	.reset = reset_driver_stat,
+	.polling_interval = 1,
+	.timed_polling = met_cookie_polling,
+	.process_argument = met_cookie_process_argument,
+	.print_help = met_cookie_print_help,
+	.print_header = met_cookie_print_header,
+	.cpu_state_notify = met_cookie_cpu_state_notify,
+};
diff --git a/src/devtools/met-driver/4.19/common/core_plf_init.c b/src/devtools/met-driver/4.19/common/core_plf_init.c
new file mode 100644
index 0000000..a4ebb54
--- /dev/null
+++ b/src/devtools/met-driver/4.19/common/core_plf_init.c
@@ -0,0 +1,421 @@
+/*
+ * Copyright (C) 2019 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/kallsyms.h>
+#include "met_drv.h"
+#include "met_api_tbl.h"
+#include "interface.h"
+#include "core_plf_init.h"
+
+#undef	DEBUG
+
+#ifdef MET_GPU
+/*
+ *   GPU
+ */
+bool (*mtk_get_gpu_loading_symbol)(unsigned int *pLoading);
+bool (*mtk_get_gpu_block_symbol)(unsigned int *pBlock);
+bool (*mtk_get_gpu_idle_symbol)(unsigned int *pIdle);
+bool (*mtk_get_gpu_dvfs_from_symbol)(MTK_GPU_DVFS_TYPE *peType, unsigned long *pulFreq);
+bool (*mtk_get_gpu_sub_loading_symbol)(unsigned int *pLoading);
+bool (*mtk_get_3D_fences_count_symbol)(int *pi32Count);
+bool (*mtk_get_gpu_memory_usage_symbol)(unsigned int *pMemUsage);
+bool (*mtk_get_gpu_power_loading_symbol)(unsigned int *pLoading);
+bool (*mtk_get_custom_boost_gpu_freq_symbol)(unsigned long *pulFreq);
+bool (*mtk_get_custom_upbound_gpu_freq_symbol)(unsigned long *pulFreq);
+bool (*mtk_get_vsync_based_target_freq_symbol)(unsigned long *pulFreq);
+bool (*mtk_get_vsync_offset_event_status_symbol)(unsigned int *pui32EventStatus);
+bool (*mtk_get_vsync_offset_debug_status_symbol)(unsigned int *pui32EventStatus);
+bool (*mtk_enable_gpu_perf_monitor_symbol)(bool enable);
+bool (*mtk_get_gpu_pmu_init_symbol)(GPU_PMU *pmus, int pmu_size, int *ret_size);
+bool (*mtk_get_gpu_pmu_swapnreset_symbol)(GPU_PMU *pmus, int pmu_size);
+#if 1
+bool (*mtk_get_gpu_pmu_deinit_symbol)(void);
+bool (*mtk_get_gpu_pmu_swapnreset_stop_symbol)(void);
+#endif
+unsigned int (*mt_gpufreq_get_cur_freq_symbol)(void);
+unsigned int (*mt_gpufreq_get_thermal_limit_freq_symbol)(void);
+bool (*mtk_register_gpu_power_change_symbol)(const char *name, gpu_power_change_notify_fp callback);
+bool (*mtk_unregister_gpu_power_change_symbol)(const char *name);
+#endif /* MET_GPU */
+
+#ifdef MET_VCOREDVFS
+/*
+ *   VCORE DVFS
+ */
+#ifdef VCOREDVFS_OLD_VER
+
+#include <mtk_vcorefs_governor.h>
+#include <mtk_spm_vcore_dvfs.h>
+
+u32 (*spm_vcorefs_get_MD_status_symbol)(void);
+void (*spm_vcorefs_register_handler_symbol)(vcorefs_handler_t handler);
+void (*vcorefs_register_req_notify_symbol)(vcorefs_req_handler_t handler);
+char *(*governor_get_kicker_name_symbol)(int id);
+int (*vcorefs_enable_debug_isr_symbol)(bool);
+int (*vcorefs_get_hw_opp_symbol)(void);
+int (*vcorefs_get_curr_vcore_symbol)(void);
+int (*vcorefs_get_curr_ddr_symbol)(void);
+int *kicker_table_symbol;
+#else
+
+#include <helio-dvfsrc.h>
+
+#endif /* end else VCOREDVFS_OLD_VER*/
+int  (*vcorefs_get_opp_info_num_symbol)(void);
+char ** (*vcorefs_get_opp_info_name_symbol)(void);
+unsigned int * (*vcorefs_get_opp_info_symbol)(void);
+int  (*vcorefs_get_src_req_num_symbol)(void);
+char ** (*vcorefs_get_src_req_name_symbol)(void);
+unsigned int * (*vcorefs_get_src_req_symbol)(void);
+int (*vcorefs_get_num_opp_symbol)(void);
+
+#endif /* MET_VCOREDVFS */
+
+
+#ifdef MET_SSPM_EMI
+void *(*mt_cen_emi_base_get_symbol)(void);
+unsigned int (*mtk_dramc_get_data_rate_symbol)(void);
+unsigned int (*mtk_dramc_get_ddr_type_symbol)(void);
+unsigned int (*get_cur_ddr_ratio_symbol)(void);
+#endif /* MET_SSPM_EMI */
+
+#ifdef MET_PTPOD
+unsigned int (*mt_gpufreq_get_cur_volt_symbol)(void);
+unsigned int (*mt_cpufreq_get_cur_volt_symbol)(unsigned int cluster_id);
+#endif /* MET_PTPOD */
+
+#ifdef MET_SPM_TWAM
+/* ap side used */
+#ifdef SPMTWAM_AP
+void (*spm_twam_enable_monitor_symbol)(const struct twam_sig *twamsig, bool speed_mode);
+void (*spm_twam_register_handler_symbol)(twam_handler_t handler);
+void (*spm_twam_disable_monitor_symbol)(void);
+void (*spm_twam_set_idle_select_symbol)(unsigned int sel);
+void (*spm_twam_set_window_length_symbol)(unsigned int len);
+void (*spm_twam_set_mon_type_symbol)(struct twam_sig *mon);
+#endif
+
+/* sspm side used */
+#ifdef SPMTWAM_SSPM
+void (*spm_twam_enable_monitor_symbol)(bool en_monitor, bool debug_signal, twam_handler_t cb_handler);
+bool (*spm_twam_met_enable_symbol)(void);
+void (*spm_twam_config_channel_symbol)(struct twam_cfg *cfg, bool speed_mode, unsigned int window_len_hz);
+#endif
+#endif
+
+
+static int met_symbol_get(void)
+{
+#define _MET_SYMBOL_GET(_func_name_) \
+	do { \
+		_func_name_##_symbol = (void *)symbol_get(_func_name_); \
+		if (_func_name_##_symbol == NULL) { \
+			pr_debug("MET ext. symbol : %s is not found!\n", #_func_name_); \
+			PR_BOOTMSG_ONCE("MET ext. symbol : %s is not found!\n", #_func_name_); \
+		} \
+	} while (0)
+
+
+#ifdef MET_GPU
+	_MET_SYMBOL_GET(mtk_get_gpu_loading);
+	_MET_SYMBOL_GET(mtk_get_gpu_block);
+	_MET_SYMBOL_GET(mtk_get_gpu_idle);
+	_MET_SYMBOL_GET(mtk_get_gpu_dvfs_from);
+	_MET_SYMBOL_GET(mtk_get_gpu_sub_loading);
+	_MET_SYMBOL_GET(mtk_get_3D_fences_count);
+	_MET_SYMBOL_GET(mtk_get_gpu_memory_usage);
+	_MET_SYMBOL_GET(mtk_get_gpu_power_loading);
+	_MET_SYMBOL_GET(mtk_get_custom_boost_gpu_freq);
+	_MET_SYMBOL_GET(mtk_get_custom_upbound_gpu_freq);
+	_MET_SYMBOL_GET(mtk_get_vsync_based_target_freq);
+	_MET_SYMBOL_GET(mtk_get_vsync_offset_event_status);
+	_MET_SYMBOL_GET(mtk_get_vsync_offset_debug_status);
+	_MET_SYMBOL_GET(mtk_enable_gpu_perf_monitor);
+	_MET_SYMBOL_GET(mtk_get_gpu_pmu_init);
+	_MET_SYMBOL_GET(mtk_get_gpu_pmu_swapnreset);
+	_MET_SYMBOL_GET(mt_gpufreq_get_cur_freq);
+	_MET_SYMBOL_GET(mt_gpufreq_get_thermal_limit_freq);
+	_MET_SYMBOL_GET(mtk_register_gpu_power_change);
+	_MET_SYMBOL_GET(mtk_unregister_gpu_power_change);
+#if 1
+	_MET_SYMBOL_GET(mtk_get_gpu_pmu_swapnreset_stop);
+	_MET_SYMBOL_GET(mtk_get_gpu_pmu_deinit);
+#endif
+#endif /* MET_GPU */
+
+#ifdef MET_VCOREDVFS
+	_MET_SYMBOL_GET(vcorefs_get_num_opp);
+	_MET_SYMBOL_GET(vcorefs_get_opp_info_num);
+	_MET_SYMBOL_GET(vcorefs_get_opp_info_name);
+	_MET_SYMBOL_GET(vcorefs_get_opp_info);
+	_MET_SYMBOL_GET(vcorefs_get_src_req_num);
+	_MET_SYMBOL_GET(vcorefs_get_src_req_name);
+	_MET_SYMBOL_GET(vcorefs_get_src_req);
+
+#ifdef VCOREDVFS_OLD_VER
+	_MET_SYMBOL_GET(spm_vcorefs_get_MD_status);
+	_MET_SYMBOL_GET(spm_vcorefs_register_handler);
+	_MET_SYMBOL_GET(vcorefs_register_req_notify);
+	_MET_SYMBOL_GET(governor_get_kicker_name);
+	_MET_SYMBOL_GET(vcorefs_enable_debug_isr);
+	_MET_SYMBOL_GET(vcorefs_get_hw_opp);
+	_MET_SYMBOL_GET(vcorefs_get_curr_vcore);
+	_MET_SYMBOL_GET(vcorefs_get_curr_ddr);
+	_MET_SYMBOL_GET(kicker_table);
+#endif
+
+#endif
+
+#ifdef MET_SSPM_EMI
+	_MET_SYMBOL_GET(mt_cen_emi_base_get);
+	_MET_SYMBOL_GET(mtk_dramc_get_data_rate);
+	_MET_SYMBOL_GET(mtk_dramc_get_ddr_type);
+	_MET_SYMBOL_GET(get_cur_ddr_ratio);
+#endif
+
+#ifdef MET_PTPOD
+	_MET_SYMBOL_GET(mt_gpufreq_get_cur_volt);
+	_MET_SYMBOL_GET(mt_cpufreq_get_cur_volt);
+#endif
+
+#ifdef MET_SPM_TWAM
+		/* ap side used */
+#ifdef SPMTWAM_AP
+		_MET_SYMBOL_GET(spm_twam_enable_monitor);
+		_MET_SYMBOL_GET(spm_twam_register_handler);
+		_MET_SYMBOL_GET(spm_twam_disable_monitor);
+		_MET_SYMBOL_GET(spm_twam_set_idle_select);
+		_MET_SYMBOL_GET(spm_twam_set_window_length);
+		_MET_SYMBOL_GET(spm_twam_set_mon_type);
+#endif
+
+		/* sspm side used */
+#ifdef SPMTWAM_SSPM
+		_MET_SYMBOL_GET(spm_twam_enable_monitor);
+		_MET_SYMBOL_GET(spm_twam_met_enable);
+		_MET_SYMBOL_GET(spm_twam_config_channel);
+#endif
+#endif
+
+	return 0;
+}
+
+static int met_symbol_put(void)
+{
+#define _MET_SYMBOL_PUT(_func_name_) { \
+		if (_func_name_##_symbol) { \
+			symbol_put(_func_name_); \
+			_func_name_##_symbol = NULL; \
+		} \
+	}
+
+#ifdef MET_GPU
+	_MET_SYMBOL_PUT(mtk_get_gpu_loading);
+	_MET_SYMBOL_PUT(mtk_get_gpu_block);
+	_MET_SYMBOL_PUT(mtk_get_gpu_idle);
+	_MET_SYMBOL_PUT(mtk_get_gpu_dvfs_from);
+	_MET_SYMBOL_PUT(mtk_get_gpu_sub_loading);
+	_MET_SYMBOL_PUT(mtk_get_3D_fences_count);
+	_MET_SYMBOL_PUT(mtk_get_gpu_memory_usage);
+	_MET_SYMBOL_PUT(mtk_get_gpu_power_loading);
+	_MET_SYMBOL_PUT(mtk_get_custom_boost_gpu_freq);
+	_MET_SYMBOL_PUT(mtk_get_custom_upbound_gpu_freq);
+	_MET_SYMBOL_PUT(mtk_get_vsync_based_target_freq);
+	_MET_SYMBOL_PUT(mtk_get_vsync_offset_event_status);
+	_MET_SYMBOL_PUT(mtk_get_vsync_offset_debug_status);
+	_MET_SYMBOL_PUT(mtk_enable_gpu_perf_monitor);
+	_MET_SYMBOL_PUT(mtk_get_gpu_pmu_init);
+	_MET_SYMBOL_PUT(mtk_get_gpu_pmu_swapnreset);
+	_MET_SYMBOL_PUT(mt_gpufreq_get_cur_freq);
+	_MET_SYMBOL_PUT(mt_gpufreq_get_thermal_limit_freq);
+	_MET_SYMBOL_PUT(mtk_register_gpu_power_change);
+	_MET_SYMBOL_PUT(mtk_unregister_gpu_power_change);
+#if 1
+	_MET_SYMBOL_PUT(mtk_get_gpu_pmu_swapnreset_stop);
+	_MET_SYMBOL_PUT(mtk_get_gpu_pmu_deinit);
+#endif
+#endif /* MET_GPU */
+
+#ifdef MET_VCOREDVFS
+	_MET_SYMBOL_PUT(vcorefs_get_num_opp);
+	_MET_SYMBOL_PUT(vcorefs_get_opp_info_num);
+	_MET_SYMBOL_PUT(vcorefs_get_opp_info_name);
+	_MET_SYMBOL_PUT(vcorefs_get_opp_info);
+	_MET_SYMBOL_PUT(vcorefs_get_src_req_num);
+	_MET_SYMBOL_PUT(vcorefs_get_src_req_name);
+	_MET_SYMBOL_PUT(vcorefs_get_src_req);
+
+#ifdef VCOREDVFS_OLD_VER
+	_MET_SYMBOL_PUT(spm_vcorefs_get_MD_status);
+	_MET_SYMBOL_PUT(spm_vcorefs_register_handler);
+	_MET_SYMBOL_PUT(vcorefs_register_req_notify);
+	_MET_SYMBOL_PUT(governor_get_kicker_name);
+	_MET_SYMBOL_PUT(vcorefs_enable_debug_isr);
+	_MET_SYMBOL_PUT(vcorefs_get_hw_opp);
+	_MET_SYMBOL_PUT(vcorefs_get_curr_vcore);
+	_MET_SYMBOL_PUT(vcorefs_get_curr_ddr);
+	_MET_SYMBOL_PUT(kicker_table);
+#endif
+
+#endif
+
+#ifdef MET_SSPM_EMI
+	_MET_SYMBOL_PUT(mt_cen_emi_base_get);
+	_MET_SYMBOL_PUT(mtk_dramc_get_data_rate);
+	_MET_SYMBOL_PUT(mtk_dramc_get_ddr_type);
+	_MET_SYMBOL_PUT(get_cur_ddr_ratio);
+#endif
+
+#ifdef MET_PTPOD
+	_MET_SYMBOL_PUT(mt_gpufreq_get_cur_volt);
+	_MET_SYMBOL_PUT(mt_cpufreq_get_cur_volt);
+#endif
+
+#ifdef MET_SPM_TWAM
+		/* ap side used */
+#ifdef SPMTWAM_AP
+		_MET_SYMBOL_PUT(spm_twam_enable_monitor);
+		_MET_SYMBOL_PUT(spm_twam_register_handler);
+		_MET_SYMBOL_PUT(spm_twam_disable_monitor);
+		_MET_SYMBOL_PUT(spm_twam_set_idle_select);
+		_MET_SYMBOL_PUT(spm_twam_set_window_length);
+		_MET_SYMBOL_PUT(spm_twam_set_mon_type);
+#endif
+
+		/* sspm side used */
+#ifdef SPMTWAM_SSPM
+		_MET_SYMBOL_PUT(spm_twam_enable_monitor);
+		_MET_SYMBOL_PUT(spm_twam_met_enable);
+		_MET_SYMBOL_PUT(spm_twam_config_channel);
+#endif
+#endif
+
+	return 0;
+}
+
+int core_plf_init(void)
+{
+	/*initial met external symbol*/
+	met_symbol_get();
+
+#ifdef MET_GPU
+	met_register(&met_gpu);
+	met_register(&met_gpudvfs);
+	met_register(&met_gpumem);
+	met_register(&met_gpupwr);
+	met_register(&met_gpu_pmu);
+#ifdef MET_GPU_STALL_MONITOR
+	met_register(&met_gpu_stall);
+#endif
+#endif
+
+#ifdef MET_VCOREDVFS
+	met_register(&met_vcoredvfs);
+#endif
+
+#ifdef MET_SSPM_EMI
+	met_register(&met_sspm_emi);
+#endif
+
+#ifdef MET_SMI
+	met_register(&met_sspm_smi);
+#endif
+
+#ifdef MET_PTPOD
+	met_register(&met_ptpod);
+#endif
+
+#ifdef MET_WALLTIME
+	met_register(&met_wall_time);
+#endif
+
+#ifdef MTK_TINYSYS_SSPM_SUPPORT
+	met_register(&met_sspm_common);
+#endif
+
+#if defined(CONFIG_MTK_TINYSYS_SSPM_SUPPORT) && defined(ONDIEMET_SUPPORT)
+#ifdef MET_SSPM_WALLTIME
+	met_register(&met_sspm_walltime);
+#endif
+#endif /* CONFIG_MTK_TINYSYS_SSPM_SUPPORT && ONDIEMET_SUPPORT */
+
+#ifdef MET_CPUDSU
+	met_register(&met_cpudsu);
+#endif
+
+#ifdef MET_SPM_TWAM
+	met_register(&met_spmtwam);
+#endif
+
+	return 0;
+}
+
+void core_plf_exit(void)
+{
+	/*release met external symbol*/
+	met_symbol_put();
+
+#ifdef MET_GPU
+	met_deregister(&met_gpu);
+	met_deregister(&met_gpudvfs);
+	met_deregister(&met_gpumem);
+	met_deregister(&met_gpupwr);
+	met_deregister(&met_gpu_pmu);
+#ifdef MET_GPU_STALL_MONITOR
+	met_deregister(&met_gpu_stall);
+#endif
+#endif
+
+#ifdef MET_VCOREDVFS
+	met_deregister(&met_vcoredvfs);
+#endif
+
+#ifdef MET_SSPM_EMI
+	met_deregister(&met_sspm_emi);
+#endif
+
+#ifdef MET_SMI
+	met_deregister(&met_sspm_smi);
+#endif
+
+#ifdef MET_PTPOD
+	met_deregister(&met_ptpod);
+#endif
+
+#ifdef MET_WALLTIME
+	met_deregister(&met_wall_time);
+#endif
+
+#ifdef MTK_TINYSYS_SSPM_SUPPORT
+	met_deregister(&met_sspm_common);
+#endif
+
+#if defined(CONFIG_MTK_TINYSYS_SSPM_SUPPORT) && defined(ONDIEMET_SUPPORT)
+#ifdef MET_SSPM_WALLTIME
+	met_deregister(&met_sspm_walltime);
+#endif
+#endif /* CONFIG_MTK_TINYSYS_SSPM_SUPPORT && ONDIEMET_SUPPORT */
+
+#ifdef MET_CPUDSU
+	met_deregister(&met_cpudsu);
+#endif
+
+#ifdef MET_SPM_TWAM
+	met_deregister(&met_spmtwam);
+#endif
+
+}
diff --git a/src/devtools/met-driver/4.19/common/core_plf_init.h b/src/devtools/met-driver/4.19/common/core_plf_init.h
new file mode 100644
index 0000000..307b1c3
--- /dev/null
+++ b/src/devtools/met-driver/4.19/common/core_plf_init.h
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2019 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __CORE_PLF_INIT_H__
+#define __CORE_PLF_INIT_H__
+
+extern struct miscdevice met_device;
+
+/*
+ *   MET External Symbol
+ */
+
+#ifdef MET_GPU
+/*
+ *   GPU
+ */
+#include <mtk_gpu_utility.h>
+#include <mtk_gpufreq.h>
+#include "met_gpu_monitor.h"
+
+extern bool mtk_get_gpu_loading(unsigned int *pLoading);
+extern bool mtk_get_gpu_block(unsigned int *pBlock);
+extern bool mtk_get_gpu_idle(unsigned int *pIdle);
+extern bool mtk_get_gpu_dvfs_from(MTK_GPU_DVFS_TYPE *peType, unsigned long *pulFreq);
+extern bool mtk_get_gpu_sub_loading(unsigned int *pLoading);
+extern bool mtk_get_3D_fences_count(int *pi32Count);
+extern bool mtk_get_gpu_memory_usage(unsigned int *pMemUsage);
+extern bool mtk_get_gpu_power_loading(unsigned int *pLoading);
+extern bool mtk_get_custom_boost_gpu_freq(unsigned int *pui32FreqLevel);
+extern bool mtk_get_custom_upbound_gpu_freq(unsigned int *pui32FreqLevel);
+extern bool mtk_get_vsync_based_target_freq(unsigned long *pulFreq);
+extern bool mtk_get_vsync_offset_event_status(unsigned int *pui32EventStatus);
+extern bool mtk_get_vsync_offset_debug_status(unsigned int *pui32EventStatus);
+extern bool mtk_enable_gpu_perf_monitor(bool enable);
+extern bool mtk_get_gpu_pmu_init(GPU_PMU *pmus, int pmu_size, int *ret_size);
+extern bool mtk_get_gpu_pmu_swapnreset(GPU_PMU *pmus, int pmu_size);
+extern bool mtk_get_gpu_pmu_deinit(void);
+extern bool mtk_get_gpu_pmu_swapnreset_stop(void);
+
+extern bool (*mtk_get_gpu_loading_symbol)(unsigned int *pLoading);
+extern bool (*mtk_get_gpu_block_symbol)(unsigned int *pBlock);
+extern bool (*mtk_get_gpu_idle_symbol)(unsigned int *pIdle);
+extern bool (*mtk_get_gpu_dvfs_from_symbol)(MTK_GPU_DVFS_TYPE *peType, unsigned long *pulFreq);
+extern bool (*mtk_get_gpu_sub_loading_symbol)(unsigned int *pLoading);
+extern bool (*mtk_get_3D_fences_count_symbol)(int *pi32Count);
+extern bool (*mtk_get_gpu_memory_usage_symbol)(unsigned int *pMemUsage);
+extern bool (*mtk_get_gpu_power_loading_symbol)(unsigned int *pLoading);
+extern bool (*mtk_get_custom_boost_gpu_freq_symbol)(unsigned long *pulFreq);
+extern bool (*mtk_get_custom_upbound_gpu_freq_symbol)(unsigned long *pulFreq);
+extern bool (*mtk_get_vsync_based_target_freq_symbol)(unsigned long *pulFreq);
+extern bool (*mtk_get_vsync_offset_event_status_symbol)(unsigned int *pui32EventStatus);
+extern bool (*mtk_get_vsync_offset_debug_status_symbol)(unsigned int *pui32EventStatus);
+extern bool (*mtk_enable_gpu_perf_monitor_symbol)(bool enable);
+extern bool (*mtk_get_gpu_pmu_init_symbol)(GPU_PMU *pmus, int pmu_size, int *ret_size);
+extern bool (*mtk_get_gpu_pmu_swapnreset_symbol)(GPU_PMU *pmus, int pmu_size);
+extern bool (*mtk_get_gpu_pmu_deinit_symbol)(void);
+extern bool (*mtk_get_gpu_pmu_swapnreset_stop_symbol)(void);
+
+extern bool mtk_register_gpu_power_change(const char *name, gpu_power_change_notify_fp callback);
+extern bool mtk_unregister_gpu_power_change(const char *name);
+extern bool (*mtk_register_gpu_power_change_symbol)(const char *name,
+					gpu_power_change_notify_fp callback);
+extern bool (*mtk_unregister_gpu_power_change_symbol)(const char *name);
+
+
+extern unsigned int mt_gpufreq_get_cur_freq(void);
+extern unsigned int mt_gpufreq_get_thermal_limit_freq(void);
+extern unsigned int (*mt_gpufreq_get_cur_freq_symbol)(void);
+extern unsigned int (*mt_gpufreq_get_thermal_limit_freq_symbol)(void);
+
+extern struct metdevice met_gpu;
+extern struct metdevice met_gpudvfs;
+extern struct metdevice met_gpumem;
+extern struct metdevice met_gpupwr;
+extern struct metdevice met_gpu_pmu;
+#ifdef MET_GPU_STALL_MONITOR
+extern struct metdevice met_gpu_stall;
+#endif
+#endif /* MET_GPU */
+
+
+#ifdef MET_VCOREDVFS
+/*
+ *   VCORE DVFS
+ */
+extern int vcorefs_get_num_opp(void);
+extern int  vcorefs_get_opp_info_num(void);
+extern char ** vcorefs_get_opp_info_name(void);
+extern unsigned int * vcorefs_get_opp_info(void);
+extern int  vcorefs_get_src_req_num(void);
+extern char ** vcorefs_get_src_req_name(void);
+extern unsigned int * vcorefs_get_src_req(void);
+
+extern int (*vcorefs_get_num_opp_symbol)(void);
+extern int  (*vcorefs_get_opp_info_num_symbol)(void);
+extern char ** (*vcorefs_get_opp_info_name_symbol)(void);
+extern unsigned int * (*vcorefs_get_opp_info_symbol)(void);
+extern int  (*vcorefs_get_src_req_num_symbol)(void);
+extern char ** (*vcorefs_get_src_req_name_symbol)(void);
+extern unsigned int * (*vcorefs_get_src_req_symbol)(void);
+
+
+#ifdef VCOREDVFS_OLD_VER
+
+#include <mtk_spm.h>
+#include <mtk_vcorefs_manager.h>
+
+extern char *governor_get_kicker_name(int id);
+extern int vcorefs_enable_debug_isr(bool);
+
+extern u32 (*spm_vcorefs_get_MD_status_symbol)(void);
+extern void (*spm_vcorefs_register_handler_symbol)(vcorefs_handler_t handler);
+extern void (*vcorefs_register_req_notify_symbol)(vcorefs_req_handler_t handler);
+extern char *(*governor_get_kicker_name_symbol)(int id);
+extern int (*vcorefs_enable_debug_isr_symbol)(bool);
+extern int (*vcorefs_get_hw_opp_symbol)(void);
+extern int (*vcorefs_get_curr_vcore_symbol)(void);
+extern int (*vcorefs_get_curr_ddr_symbol)(void);
+extern int *kicker_table_symbol;
+
+#endif /* VCOREDVFS_OLD_VER */
+
+extern struct metdevice met_vcoredvfs;
+
+#endif /* MET_VCOREDVFS */
+
+
+#ifdef MET_SSPM_EMI
+extern void *mt_cen_emi_base_get(void);
+extern unsigned int mtk_dramc_get_data_rate(void);      /* in Mhz */
+extern int mtk_dramc_get_ddr_type(void);
+extern int get_cur_ddr_ratio(void);
+
+extern void *(*mt_cen_emi_base_get_symbol)(void);
+extern unsigned int (*mtk_dramc_get_data_rate_symbol)(void); /* in Mhz */
+extern unsigned int (*mtk_dramc_get_ddr_type_symbol)(void);
+extern unsigned int (*get_cur_ddr_ratio_symbol)(void);
+
+
+
+extern struct metdevice met_sspm_emi;
+#endif /* MET_SSPM_EMI */
+
+#ifdef MET_SMI
+extern struct metdevice met_sspm_smi;
+#endif
+
+#ifdef MET_PTPOD
+#include <mtk_gpufreq.h>
+#include <mach/mtk_cpufreq_api.h>
+#include <mtk_cpufreq_config.h>
+
+extern unsigned int mt_gpufreq_get_cur_volt(void);
+extern unsigned int mt_cpufreq_get_cur_volt(unsigned int cluster_id);
+extern unsigned int (*mt_gpufreq_get_cur_volt_symbol)(void);
+extern unsigned int (*mt_cpufreq_get_cur_volt_symbol)(unsigned int cluster_id);
+
+extern struct metdevice met_ptpod;
+#endif /* MET_PTPOD */
+
+#ifdef MET_WALLTIME
+extern struct metdevice met_wall_time;
+#endif
+
+#ifdef MTK_TINYSYS_SSPM_SUPPORT
+extern struct metdevice met_sspm_common;
+#endif /* MTK_TINYSYS_SSPM_SUPPORT */
+
+#if defined(CONFIG_MTK_TINYSYS_SSPM_SUPPORT) && defined(ONDIEMET_SUPPORT)
+#ifdef MET_SSPM_WALLTIME
+extern struct metdevice met_sspm_walltime;
+#endif
+#endif /* CONFIG_MTK_TINYSYS_SSPM_SUPPORT */
+
+#ifdef MET_CPUDSU
+extern struct metdevice met_cpudsu;
+#endif
+
+#ifdef MET_SPM_TWAM
+#include "mtk_spm.h"
+
+/* ap side used */
+#ifdef SPMTWAM_AP
+extern void spm_twam_enable_monitor(const struct twam_sig *twamsig, bool speed_mode);
+extern void spm_twam_register_handler(twam_handler_t handler);
+extern void spm_twam_disable_monitor(void);
+extern void spm_twam_set_idle_select(unsigned int sel);
+extern void spm_twam_set_window_length(unsigned int len);
+extern void spm_twam_set_mon_type(struct twam_sig *mon);
+
+extern void (*spm_twam_enable_monitor_symbol)(const struct twam_sig *twamsig, bool speed_mode);
+extern void (*spm_twam_register_handler_symbol)(twam_handler_t handler);
+extern void (*spm_twam_disable_monitor_symbol)(void);
+extern void (*spm_twam_set_idle_select_symbol)(unsigned int sel);
+extern void (*spm_twam_set_window_length_symbol)(unsigned int len);
+extern void (*spm_twam_set_mon_type_symbol)(struct twam_sig *mon);
+#endif
+
+/* sspm side used */
+#ifdef SPMTWAM_SSPM
+extern void spm_twam_enable_monitor(bool en_monitor, bool debug_signal, twam_handler_t cb_handler);
+extern bool spm_twam_met_enable(void);
+extern void spm_twam_config_channel(struct twam_cfg *cfg, bool speed_mode, unsigned int window_len_hz);
+
+extern void (*spm_twam_enable_monitor_symbol)(bool en_monitor, bool debug_signal, twam_handler_t cb_handler);
+extern bool (*spm_twam_met_enable_symbol)(void);
+extern void (*spm_twam_config_channel_symbol)(struct twam_cfg *cfg, bool speed_mode, unsigned int window_len_hz);
+#endif
+
+extern struct metdevice met_spmtwam;
+#endif
+
+
+#endif /*__CORE_PLF_INIT_H__*/
diff --git a/src/devtools/met-driver/4.19/common/core_plf_trace.c b/src/devtools/met-driver/4.19/common/core_plf_trace.c
new file mode 100644
index 0000000..bdea47e
--- /dev/null
+++ b/src/devtools/met-driver/4.19/common/core_plf_trace.c
@@ -0,0 +1,322 @@
+/*
+ * Copyright (C) 2019 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+#include "met_drv.h"
+#include "interface.h"
+#include "trace.h"
+
+char *ms_formatH(char *__restrict__ buf, unsigned char cnt, unsigned int *__restrict__ value)
+{
+	char *s = buf;
+	int len;
+
+	if (cnt == 0) {
+		buf[0] = '\0';
+		return buf;
+	}
+
+	switch (cnt % 4) {
+	case 1:
+		len = sprintf(s, "%x", value[0]);
+		s += len;
+		value += 1;
+		cnt -= 1;
+		break;
+	case 2:
+		len = sprintf(s, "%x,%x", value[0], value[1]);
+		s += len;
+		value += 2;
+		cnt -= 2;
+		break;
+	case 3:
+		len = sprintf(s, "%x,%x,%x", value[0], value[1], value[2]);
+		s += len;
+		value += 3;
+		cnt -= 3;
+		break;
+	case 0:
+		len = sprintf(s, "%x,%x,%x,%x", value[0], value[1], value[2], value[3]);
+		s += len;
+		value += 4;
+		cnt -= 4;
+		break;
+	}
+
+	while (cnt) {
+		len = sprintf(s, ",%x,%x,%x,%x", value[0], value[1], value[2], value[3]);
+		s += len;
+		value += 4;
+		cnt -= 4;
+	}
+
+	s[0] = '\0';
+
+	return s;
+}
+EXPORT_SYMBOL(ms_formatH);
+
+char *ms_formatD(char *__restrict__ buf, unsigned char cnt, unsigned int *__restrict__ value)
+{
+	char *s = buf;
+	int len;
+
+	if (cnt == 0) {
+		buf[0] = '\0';
+		return buf;
+	}
+
+	switch (cnt % 4) {
+	case 1:
+		len = sprintf(s, "%u", value[0]);
+		s += len;
+		value += 1;
+		cnt -= 1;
+		break;
+	case 2:
+		len = sprintf(s, "%u,%u", value[0], value[1]);
+		s += len;
+		value += 2;
+		cnt -= 2;
+		break;
+	case 3:
+		len = sprintf(s, "%u,%u,%u", value[0], value[1], value[2]);
+		s += len;
+		value += 3;
+		cnt -= 3;
+		break;
+	case 0:
+		len = sprintf(s, "%u,%u,%u,%u", value[0], value[1], value[2], value[3]);
+		s += len;
+		value += 4;
+		cnt -= 4;
+		break;
+	}
+
+	while (cnt) {
+		len = sprintf(s, ",%u,%u,%u,%u", value[0], value[1], value[2], value[3]);
+		s += len;
+		value += 4;
+		cnt -= 4;
+	}
+
+	s[0] = '\0';
+
+	return s;
+}
+EXPORT_SYMBOL(ms_formatD);
+
+char *ms_formatH_ulong(char *__restrict__ buf, unsigned char cnt, unsigned long *__restrict__ value)
+{
+	char *s = buf;
+	int len;
+
+	if (cnt == 0) {
+		buf[0] = '\0';
+		return buf;
+	}
+
+	switch (cnt % 4) {
+	case 1:
+		len = sprintf(s, "%lx", value[0]);
+		s += len;
+		value += 1;
+		cnt -= 1;
+		break;
+	case 2:
+		len = sprintf(s, "%lx,%lx", value[0], value[1]);
+		s += len;
+		value += 2;
+		cnt -= 2;
+		break;
+	case 3:
+		len = sprintf(s, "%lx,%lx,%lx", value[0], value[1], value[2]);
+		s += len;
+		value += 3;
+		cnt -= 3;
+		break;
+	case 0:
+		len = sprintf(s, "%lx,%lx,%lx,%lx", value[0], value[1], value[2], value[3]);
+		s += len;
+		value += 4;
+		cnt -= 4;
+		break;
+	}
+
+	while (cnt) {
+		len = sprintf(s, ",%lx,%lx,%lx,%lx", value[0], value[1], value[2], value[3]);
+		s += len;
+		value += 4;
+		cnt -= 4;
+	}
+
+	s[0] = '\0';
+
+	return buf;
+}
+EXPORT_SYMBOL(ms_formatH_ulong);
+
+char *ms_formatD_ulong(char *__restrict__ buf, unsigned char cnt, unsigned long *__restrict__ value)
+{
+	char *s = buf;
+	int len;
+
+	if (cnt == 0) {
+		buf[0] = '\0';
+		return buf;
+	}
+
+	switch (cnt % 4) {
+	case 1:
+		len = sprintf(s, "%lu", value[0]);
+		s += len;
+		value += 1;
+		cnt -= 1;
+		break;
+	case 2:
+		len = sprintf(s, "%lu,%lu", value[0], value[1]);
+		s += len;
+		value += 2;
+		cnt -= 2;
+		break;
+	case 3:
+		len = sprintf(s, "%lu,%lu,%lu", value[0], value[1], value[2]);
+		s += len;
+		value += 3;
+		cnt -= 3;
+		break;
+	case 0:
+		len = sprintf(s, "%lu,%lu,%lu,%lu", value[0], value[1], value[2], value[3]);
+		s += len;
+		value += 4;
+		cnt -= 4;
+		break;
+	}
+
+	while (cnt) {
+		len = sprintf(s, ",%lu,%lu,%lu,%lu", value[0], value[1], value[2], value[3]);
+		s += len;
+		value += 4;
+		cnt -= 4;
+	}
+
+	s[0] = '\0';
+
+	return buf;
+}
+EXPORT_SYMBOL(ms_formatD_ulong);
+
+char *ms_formatH_EOL(char *__restrict__ buf, unsigned char cnt, unsigned int *__restrict__ value)
+{
+	char *s = buf;
+	int len;
+
+	if (cnt == 0) {
+		buf[0] = '\0';
+		return buf;
+	}
+
+	switch (cnt % 4) {
+	case 1:
+		len = sprintf(s, "%x", value[0]);
+		s += len;
+		value += 1;
+		cnt -= 1;
+		break;
+	case 2:
+		len = sprintf(s, "%x,%x", value[0], value[1]);
+		s += len;
+		value += 2;
+		cnt -= 2;
+		break;
+	case 3:
+		len = sprintf(s, "%x,%x,%x", value[0], value[1], value[2]);
+		s += len;
+		value += 3;
+		cnt -= 3;
+		break;
+	case 0:
+		len = sprintf(s, "%x,%x,%x,%x", value[0], value[1], value[2], value[3]);
+		s += len;
+		value += 4;
+		cnt -= 4;
+		break;
+	}
+
+	while (cnt) {
+		len = sprintf(s, ",%x,%x,%x,%x", value[0], value[1], value[2], value[3]);
+		s += len;
+		value += 4;
+		cnt -= 4;
+	}
+
+	s[0] = '\n';
+	s[1] = '\0';
+
+	return s + 1;
+}
+EXPORT_SYMBOL(ms_formatH_EOL);
+
+char *ms_formatD_EOL(char *__restrict__ buf, unsigned char cnt, unsigned int *__restrict__ value)
+{
+	char *s = buf;
+	int len;
+
+	if (cnt == 0) {
+		buf[0] = '\0';
+		return buf;
+	}
+
+	switch (cnt % 4) {
+	case 1:
+		len = sprintf(s, "%u", value[0]);
+		s += len;
+		value += 1;
+		cnt -= 1;
+		break;
+	case 2:
+		len = sprintf(s, "%u,%u", value[0], value[1]);
+		s += len;
+		value += 2;
+		cnt -= 2;
+		break;
+	case 3:
+		len = sprintf(s, "%u,%u,%u", value[0], value[1], value[2]);
+		s += len;
+		value += 3;
+		cnt -= 3;
+		break;
+	case 0:
+		len = sprintf(s, "%u,%u,%u,%u", value[0], value[1], value[2], value[3]);
+		s += len;
+		value += 4;
+		cnt -= 4;
+		break;
+	}
+
+	while (cnt) {
+		len = sprintf(s, ",%u,%u,%u,%u", value[0], value[1], value[2], value[3]);
+		s += len;
+		value += 4;
+		cnt -= 4;
+	}
+
+	s[0] = '\n';
+	s[1] = '\0';
+
+	return s + 1;
+}
+EXPORT_SYMBOL(ms_formatD_EOL);
+
diff --git a/src/devtools/met-driver/4.19/common/core_plf_trace.h b/src/devtools/met-driver/4.19/common/core_plf_trace.h
new file mode 100644
index 0000000..59dabbb
--- /dev/null
+++ b/src/devtools/met-driver/4.19/common/core_plf_trace.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2019 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _CORE_PLF_TRACE_H_
+#define _CORE_PLF_TRACE_H_
+
+#define	HVALUE_SIZE	9	/* 8 chars (max value ffffffff) + 1 char (',' or NULL) */
+#define	DVALUE_SIZE	12	/* 10 chars (max value 4,294,967,295) + 1 char (',' or NULL) */
+
+char *ms_formatH(char *__restrict__ buf, unsigned char cnt, unsigned int *__restrict__ value);
+char *core_ms_formatD(char *__restrict__ buf, unsigned char cnt, unsigned int *__restrict__ value);
+char *ms_formatH_ulong(char *__restrict__ buf, unsigned char cnt,
+		       unsigned long *__restrict__ value);
+char *ms_formatD_ulong(char *__restrict__ buf, unsigned char cnt,
+		       unsigned long *__restrict__ value);
+char *ms_formatH_EOL(char *__restrict__ buf, unsigned char cnt, unsigned int *__restrict__ value);
+char *ms_formatD_EOL(char *__restrict__ buf, unsigned char cnt, unsigned int *__restrict__ value);
+
+#endif	/* _CORE_PLF_TRACE_H_ */
diff --git a/src/devtools/met-driver/4.19/common/cpu_dsu.c b/src/devtools/met-driver/4.19/common/cpu_dsu.c
new file mode 100644
index 0000000..aaf0035
--- /dev/null
+++ b/src/devtools/met-driver/4.19/common/cpu_dsu.c
@@ -0,0 +1,362 @@
+/*
+ * Copyright (C) 2019 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/perf_event.h>
+#include "met_drv.h"
+#include "met_kernel_symbol.h"
+#include "interface.h"
+#include "trace.h"
+#include "cpu_dsu.h"
+#include "core_plf_init.h"
+
+
+struct cpu_dsu_hw *cpu_dsu;
+static int counter_cnt;
+static struct kobject *kobj_dsu;
+static int nr_arg;
+static unsigned long long perfCurr[MXNR_DSU_EVENTS];
+static unsigned long long perfPrev[MXNR_DSU_EVENTS];
+static int perfCntFirst[MXNR_DSU_EVENTS];
+static struct perf_event * pevent[MXNR_DSU_EVENTS];
+static struct perf_event_attr pevent_attr[MXNR_DSU_EVENTS];
+static unsigned int perf_device_type = 7;
+static ssize_t perf_type_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%d\n", perf_device_type);
+}
+
+static ssize_t perf_type_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t n)
+{
+	if (kstrtouint(buf, 0, &perf_device_type) != 0)
+		return -EINVAL;
+
+	return n;
+}
+static struct kobj_attribute perf_type_attr = __ATTR(perf_type, 0664, perf_type_show, perf_type_store);
+
+noinline void mp_dsu(unsigned char cnt, unsigned int *value)
+{
+	MET_GENERAL_PRINT(MET_TRACE, cnt, value);
+}
+
+static void dummy_handler(struct perf_event *event, struct perf_sample_data *data,
+			  struct pt_regs *regs)
+{
+	/*
+	 * Required as perf_event_create_kernel_counter() requires an overflow handler,
+	 * even though all we do is poll.
+	 */
+}
+
+static void perf_cpudsu_polling(unsigned long long stamp, int cpu)
+{
+	int	event_count = cpu_dsu->event_count;
+	struct met_dsu	*pmu = cpu_dsu->pmu;
+	int	i, count;
+	unsigned long long	delta;
+	struct perf_event	*ev;
+	unsigned int pmu_value[MXNR_DSU_EVENTS];
+	u64 value;
+
+	count = 0;
+	for (i = 0; i < event_count; i++) {
+		if (pmu[i].mode == 0)
+			continue;
+
+		ev = pevent[i];
+		if ((ev != NULL) && (ev->state == PERF_EVENT_STATE_ACTIVE)) {
+			met_perf_event_read_local_symbol(ev, &value);
+			perfCurr[i] = value;
+			delta = (perfCurr[i] - perfPrev[i]);
+			perfPrev[i] = perfCurr[i];
+			if (perfCntFirst[i] == 1) {
+				/* we shall omit delta counter when we get first counter */
+				perfCntFirst[i] = 0;
+				continue;
+			}
+			pmu_value[count] = (unsigned int)delta;
+			count++;
+		}
+	}
+
+	if (count == counter_cnt)
+		mp_dsu(count, pmu_value);
+}
+
+static int perf_thread_set_perf_events(unsigned int cpu)
+{
+	int			i, size;
+	struct perf_event	*ev;
+	struct perf_event_attr	*ev_attr;
+	int event_count = cpu_dsu->event_count;
+	struct met_dsu *pmu = cpu_dsu->pmu;
+
+	size = sizeof(struct perf_event_attr);
+
+	for (i = 0; i < event_count; i++) {
+		pevent[i] = NULL;
+		if (!pmu[i].mode)
+			continue;	/* Skip disabled counters */
+		perfPrev[i] = 0;
+		perfCurr[i] = 0;
+		ev_attr = pevent_attr+i;
+		memset(ev_attr, 0, size);
+		ev_attr->config = pmu[i].event;
+		ev_attr->type = perf_device_type;
+		ev_attr->size = size;
+		ev_attr->sample_period = 0;
+		ev_attr->pinned = 1;
+
+		ev = perf_event_create_kernel_counter(ev_attr, cpu, NULL, dummy_handler, NULL);
+		if (IS_ERR(ev))
+			continue;
+		if (ev->state != PERF_EVENT_STATE_ACTIVE) {
+			perf_event_release_kernel(ev);
+			continue;
+		}
+		pevent[i] = ev;
+		if (ev != NULL)
+			perf_event_enable(ev);
+	}	/* for all PMU counter */
+	return 0;
+}
+
+void met_perf_cpudsu_down(void)
+{
+	int i;
+	struct perf_event *ev;
+	int event_count;
+	struct met_dsu *pmu;
+
+	if (met_cpudsu.mode == 0)
+		return;
+	event_count = cpu_dsu->event_count;
+	pmu = cpu_dsu->pmu;
+	for (i = 0; i < event_count; i++) {
+		if (!pmu[i].mode)
+			continue;
+		ev = pevent[i];
+		if ((ev != NULL) && (ev->state == PERF_EVENT_STATE_ACTIVE)) {
+			perf_event_disable(ev);
+			perf_event_release_kernel(ev);
+		}
+		pevent[i] = NULL;
+	}
+	//perf_delayed_work_setup = NULL;
+}
+
+inline static void met_perf_cpudsu_start(int cpu)
+{
+	if (met_cpudsu.mode == 0)
+		return;
+	if (cpu != 0)
+		return;
+	perf_thread_set_perf_events(cpu);
+}
+
+static int cpudsu_create_subfs(struct kobject *parent)
+{
+	int ret = 0;
+	cpu_dsu = cpu_dsu_hw_init();
+	if (cpu_dsu == NULL) {
+		PR_BOOTMSG("Failed to init CPU PMU HW!!\n");
+		return -ENODEV;
+	}
+	kobj_dsu = parent;
+	ret = sysfs_create_file(kobj_dsu, &perf_type_attr.attr);
+	if (ret != 0) {
+		PR_BOOTMSG("Failed to create perf_type in sysfs\n");
+		goto out;
+	}
+ out:
+	return ret;
+}
+
+static void cpudsu_delete_subfs(void)
+{
+}
+
+void met_perf_cpudsu_polling(unsigned long long stamp, int cpu)
+{
+	perf_cpudsu_polling(stamp, cpu);
+}
+
+static void cpudsu_start(void)
+{
+	int	cpu = raw_smp_processor_id();
+	for_each_online_cpu(cpu)
+		met_perf_cpudsu_start(cpu);
+}
+
+static void cpudsu_stop(void)
+{
+	met_perf_cpudsu_down();
+}
+
+static const char header[] =
+	"met-info [000] 0.0: met_dsu_pmu_header: DSU";
+
+static const char help[] =
+	"  --dsu=EVENT         select DSU-PMU events.\n"
+	"                      you can enable at most \"%d general purpose events\"\n";
+
+static int cpudsu_print_help(char *buf, int len)
+{
+	return snprintf(buf, PAGE_SIZE, help, cpu_dsu->event_count);
+}
+
+static int reset_driver_stat(void)
+{
+	int		i;
+	int		event_count;
+	struct met_dsu	*pmu;
+
+	met_cpudsu.mode = 0;
+	event_count = cpu_dsu->event_count;
+	pmu = cpu_dsu->pmu;
+	counter_cnt = 0;
+	nr_arg = 0;
+	for (i = 0; i < event_count; i++) {
+		pmu[i].mode = MODE_DISABLED;
+		pmu[i].event = 0;
+		pmu[i].freq = 0;
+	}
+	return 0;
+}
+
+static int cpudsu_print_header(char *buf, int len)
+{
+	int		first;
+	int		i, ret;
+	int		event_count;
+	struct met_dsu	*pmu;
+	ret = 0;
+
+	ret += snprintf(buf + ret, PAGE_SIZE - ret, "# mp_dsu: pmu_value1, ...\n");
+	event_count = cpu_dsu->event_count;
+	pmu = cpu_dsu->pmu;
+	first = 1;
+	for (i = 0; i < event_count; i++) {
+		if (pmu[i].mode == 0)
+			continue;
+		if (first) {
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, header);
+			first = 0;
+		}
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, ",0x%x", pmu[i].event);
+		pmu[i].mode = 0;
+	}
+	ret += snprintf(buf + ret, PAGE_SIZE - ret, "\n");
+	reset_driver_stat();
+	return ret;
+}
+
+static int met_parse_num_list(char *arg, int len, int *list, int list_cnt)
+{
+	int	nr_num = 0;
+	char	*num;
+	int	num_len;
+
+	/* search ',' as the splitter */
+	while (len) {
+		num = arg;
+		num_len = 0;
+		if (list_cnt <= 0)
+			return -1;
+		while (len) {
+			len--;
+			if (*arg == ',') {
+				*(arg++) = '\0';
+				break;
+			}
+			arg++;
+			num_len++;
+		}
+		if (met_parse_num(num, list, num_len) < 0)
+			return -1;
+		list++;
+		list_cnt--;
+		nr_num++;
+	}
+	return nr_num;
+}
+
+static int cpudsu_process_argument(const char *arg, int len)
+{
+	int		nr_events, event_list[MXNR_DSU_EVENTS];
+	int		i;
+	int		nr_counters;
+	struct met_dsu	*pmu;
+	int		arg_nr;
+	int		counters;
+	int		event_no;
+
+	/* get event_list */
+	if ((nr_events = met_parse_num_list((char*)arg, len, event_list, ARRAY_SIZE(event_list))) <= 0)
+		goto arg_out;
+
+	/* for each cpu in cpu_list, add all the events in event_list */
+	nr_counters = cpu_dsu->event_count;
+	pmu = cpu_dsu->pmu;
+	arg_nr = nr_arg;
+
+	/*
+	 * setup nr_counters for linux native perf mode.
+	 * because the selected events are stored in pmu,
+	 * so nr_counters can't large then event count in pmu.
+	 */
+	counters = perf_num_counters();
+	if (counters < nr_counters)
+		nr_counters = counters;
+
+	if (nr_counters == 0)
+		goto arg_out;
+
+	for (i = 0; i < nr_events; i++) {
+		event_no = event_list[i];
+		/*
+		 * check if event is duplicate,
+		 * but may not include 0xff when met_cpu_dsu_method == 0.
+		 */
+		if (cpu_dsu->check_event(pmu, arg_nr, event_no) < 0)
+			goto arg_out;
+		if (arg_nr >= nr_counters)
+			goto arg_out;
+		pmu[arg_nr].mode = MODE_POLLING;
+		pmu[arg_nr].event = event_no;
+		pmu[arg_nr].freq = 0;
+		arg_nr++;
+		counter_cnt++;
+	}
+	nr_arg = arg_nr;
+	met_cpudsu.mode = 1;
+	return 0;
+
+arg_out:
+	reset_driver_stat();
+	return -EINVAL;
+}
+
+struct metdevice met_cpudsu = {
+	.name = "dsu",
+	.type = MET_TYPE_PMU,
+	.cpu_related = 0,
+	.create_subfs = cpudsu_create_subfs,
+	.delete_subfs = cpudsu_delete_subfs,
+	.start = cpudsu_start,
+	.stop = cpudsu_stop,
+	.polling_interval = 1,
+	.timed_polling = met_perf_cpudsu_polling,
+	.print_help = cpudsu_print_help,
+	.print_header = cpudsu_print_header,
+	.process_argument = cpudsu_process_argument
+};
diff --git a/src/devtools/met-driver/4.19/common/cpu_dsu.h b/src/devtools/met-driver/4.19/common/cpu_dsu.h
new file mode 100644
index 0000000..99f16cc
--- /dev/null
+++ b/src/devtools/met-driver/4.19/common/cpu_dsu.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2019 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _CPU_DSU_H_
+#define _CPU_DSU_H_
+
+#include <linux/device.h>
+
+#define MODE_DISABLED	0
+#define MODE_INTERRUPT	1
+#define MODE_POLLING	2
+
+#define MXNR_CPU	NR_CPUS
+
+#define	MXNR_DSU_EVENTS	8	/* max number of pmu counter for armv8 is 6+1 */
+struct met_dsu {
+	unsigned char mode;
+	unsigned short event;
+	unsigned long freq;
+	struct kobject *kobj_cpu_dsu;
+};
+
+struct cpu_dsu_hw {
+	const char *name;
+	int nr_cnt;
+	int (*check_event)(struct met_dsu *pmu, int idx, int event);
+	void (*start)(struct met_dsu *pmu, int count);
+	void (*stop)(int count);
+	unsigned int (*polling)(struct met_dsu *pmu, int count, unsigned int *pmu_value);
+	struct met_dsu *pmu;
+	int event_count;
+};
+
+
+struct cpu_dsu_hw *cpu_dsu_hw_init(void);
+
+
+
+#endif	/* _CPU_DSU_H_ */
diff --git a/src/devtools/met-driver/4.19/common/cpu_pmu.c b/src/devtools/met-driver/4.19/common/cpu_pmu.c
new file mode 100644
index 0000000..e84018a
--- /dev/null
+++ b/src/devtools/met-driver/4.19/common/cpu_pmu.c
@@ -0,0 +1,1167 @@
+/*
+ * Copyright (C) 2019 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/cpu.h>
+#include <linux/cpu_pm.h>
+#include <linux/perf_event.h>
+
+#if (defined(CONFIG_ARM64) || defined(CONFIG_ARM))
+#include <linux/platform_device.h>
+#include <linux/perf/arm_pmu.h>
+#endif
+
+#include <linux/kernel.h>
+#include <linux/mutex.h>
+#include <linux/perf/arm_pmu.h>
+#include <linux/irqreturn.h>
+#include <linux/irq_work.h>
+#include "met_drv.h"
+#include "met_kernel_symbol.h"
+#include "interface.h"
+#include "trace.h"
+#include "cpu_pmu.h"
+#include "mtk_typedefs.h"
+
+#if defined(CONFIG_MTK_TINYSYS_SSPM_SUPPORT) && defined(ONDIEMET_SUPPORT)
+#include "sspm/ondiemet_sspm.h"
+#endif
+
+struct cpu_pmu_hw *cpu_pmu;
+static int counter_cnt[MXNR_CPU];
+static int nr_arg[MXNR_CPU];
+
+int met_perf_cpupmu_status;
+
+static int mtk_pmu_event_enable = 0;
+static struct kobject *kobj_cpu;
+DECLARE_KOBJ_ATTR_INT(mtk_pmu_event_enable, mtk_pmu_event_enable);
+#define KOBJ_ATTR_LIST \
+	do { \
+		KOBJ_ATTR_ITEM(mtk_pmu_event_enable); \
+	} while (0)
+
+DEFINE_MUTEX(handle_irq_lock);
+irqreturn_t (*handle_irq_orig)(int irq_num, void *dev);
+
+#ifdef CONFIG_CPU_PM
+static int use_cpu_pm_pmu_notifier = 0;
+
+/* helper notifier for maintaining pmu states before cpu state transition */
+static int cpu_pm_pmu_notify(struct notifier_block *b,
+			     unsigned long cmd,
+			     void *p)
+{
+	int ii;
+	int cpu, count;
+	unsigned int pmu_value[MXNR_PMU_EVENTS];
+
+	if (!met_perf_cpupmu_status)
+		return NOTIFY_OK;
+
+	cpu = raw_smp_processor_id();
+
+	switch (cmd) {
+	case CPU_PM_ENTER:
+		count = cpu_pmu->polling(cpu_pmu->pmu[cpu], cpu_pmu->event_count[cpu], pmu_value);
+		for (ii = 0; ii < count; ii ++)
+			cpu_pmu->cpu_pm_unpolled_loss[cpu][ii] += pmu_value[ii];
+
+		cpu_pmu->stop(cpu_pmu->event_count[cpu]);
+		break;
+	case CPU_PM_ENTER_FAILED:
+	case CPU_PM_EXIT:
+		cpu_pmu->start(cpu_pmu->pmu[cpu], cpu_pmu->event_count[cpu]);
+		break;
+	default:
+		return NOTIFY_DONE;
+	}
+	return NOTIFY_OK;
+}
+
+struct notifier_block cpu_pm_pmu_notifier = {
+	.notifier_call = cpu_pm_pmu_notify,
+};
+#endif
+
+static DEFINE_PER_CPU(unsigned long long[MXNR_PMU_EVENTS], perfCurr);
+static DEFINE_PER_CPU(unsigned long long[MXNR_PMU_EVENTS], perfPrev);
+static DEFINE_PER_CPU(int[MXNR_PMU_EVENTS], perfCntFirst);
+static DEFINE_PER_CPU(struct perf_event * [MXNR_PMU_EVENTS], pevent);
+static DEFINE_PER_CPU(struct perf_event_attr [MXNR_PMU_EVENTS], pevent_attr);
+static DEFINE_PER_CPU(int, perfSet);
+static DEFINE_PER_CPU(int, cpu_status);
+
+#ifdef CPUPMU_V8_2
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <mt-plat/sync_write.h>
+#include <mt-plat/mtk_io.h>
+
+static char mcucfg_desc[] = "mediatek,mcucfg";
+static void __iomem *mcucfg_base = NULL;
+#define DBG_CONTROL_CPU6	((unsigned long)mcucfg_base + 0x3000 + 0x308)  /* DBG_CONTROL */
+#define DBG_CONTROL_CPU7	((unsigned long)mcucfg_base + 0x3800 + 0x308)  /* DBG_CONTROL */
+#define ENABLE_MTK_PMU_EVENTS_OFFSET 1
+static int restore_dbg_ctrl_cpu6;
+static int restore_dbg_ctrl_cpu7;
+
+int cpu_pmu_debug_init(void)
+{
+	struct device_node  *node = NULL;
+	unsigned int value6,value7;
+
+	 /*for A75 MTK internal event*/
+	 if (mcucfg_base == NULL) {
+		node = of_find_compatible_node(NULL, NULL, mcucfg_desc);
+		if (node == NULL) {
+			MET_TRACE("[MET_PMU_DB] of_find node == NULL\n");
+			pr_debug("[MET_PMU_DB] of_find node == NULL\n");
+			goto out;
+		}
+		mcucfg_base = of_iomap(node, 0);
+		of_node_put(node);
+		if (mcucfg_base == NULL) {
+			MET_TRACE("[MET_PMU_DB] mcucfg_base == NULL\n");
+			pr_debug("[MET_PMU_DB] mcucfg_base == NULL\n");
+			goto out;
+		}
+		MET_TRACE("[MET_PMU_DB] regbase %08lx\n", DBG_CONTROL_CPU7);
+		pr_debug("[MET_PMU_DB] regbase %08lx\n", DBG_CONTROL_CPU7);
+	}
+
+	value6 = readl(IOMEM(DBG_CONTROL_CPU6));
+	if (value6 & (1 << ENABLE_MTK_PMU_EVENTS_OFFSET)) {
+		restore_dbg_ctrl_cpu6 = 1;
+	} else {
+		restore_dbg_ctrl_cpu6 = 0;
+		mt_reg_sync_writel(value6 | (1 << ENABLE_MTK_PMU_EVENTS_OFFSET), DBG_CONTROL_CPU6);
+	}
+
+	value7 = readl(IOMEM(DBG_CONTROL_CPU7));
+	if (value7 & (1 << ENABLE_MTK_PMU_EVENTS_OFFSET)) {
+		restore_dbg_ctrl_cpu7 = 1;
+	} else {
+		restore_dbg_ctrl_cpu7 = 0;
+		mt_reg_sync_writel(value7 | (1 << ENABLE_MTK_PMU_EVENTS_OFFSET), DBG_CONTROL_CPU7);
+	}
+
+	value6 = readl(IOMEM(DBG_CONTROL_CPU6));
+	value7 = readl(IOMEM(DBG_CONTROL_CPU7));
+	MET_TRACE("[MET_PMU_DB]DBG_CONTROL_CPU6 = %08x,  DBG_CONTROL_CPU7 = %08x\n", value6, value7);
+	pr_debug("[MET_PMU_DB]DBG_CONTROL_CPU6 = %08x,  DBG_CONTROL_CPU7 = %08x\n", value6, value7);
+	return 1;
+
+out:
+	if (mcucfg_base != NULL) {
+		iounmap(mcucfg_base);
+		mcucfg_base = NULL;
+	}
+	MET_TRACE("[MET_PMU_DB]DBG_CONTROL init error");
+	pr_debug("[MET_PMU_DB]DBG_CONTROL init error");
+	return 0;
+}
+
+int cpu_pmu_debug_uninit(void)
+{
+	unsigned int value6,value7;
+
+	if (restore_dbg_ctrl_cpu6 == 0) {
+		value6 = readl(IOMEM(DBG_CONTROL_CPU6));
+		mt_reg_sync_writel(value6 & (~(1 << ENABLE_MTK_PMU_EVENTS_OFFSET)), DBG_CONTROL_CPU6);
+	}
+	if (restore_dbg_ctrl_cpu7 == 0) {
+		value7 = readl(IOMEM(DBG_CONTROL_CPU7));
+		mt_reg_sync_writel(value7 & (~(1 << ENABLE_MTK_PMU_EVENTS_OFFSET)), DBG_CONTROL_CPU7);
+	}
+
+	value6 = readl(IOMEM(DBG_CONTROL_CPU6));
+	value7 = readl(IOMEM(DBG_CONTROL_CPU7));
+	MET_TRACE("[MET_PMU_DB]DBG_CONTROL_CPU6 = %08x,  DBG_CONTROL_CPU7 = %08x\n", value6, value7);
+	pr_debug("[MET_PMU_DB]DBG_CONTROL_CPU6 = %08x,  DBG_CONTROL_CPU7 = %08x\n", value6, value7);
+
+	if (mcucfg_base != NULL) {
+		iounmap(mcucfg_base);
+		mcucfg_base = NULL;
+	}
+	restore_dbg_ctrl_cpu6 = 0;
+	restore_dbg_ctrl_cpu7 = 0;
+	return 1;
+}
+#endif
+
+
+
+
+noinline void mp_cpu(unsigned char cnt, unsigned int *value)
+{
+	MET_GENERAL_PRINT(MET_TRACE, cnt, value);
+}
+
+static void dummy_handler(struct perf_event *event, struct perf_sample_data *data,
+			  struct pt_regs *regs)
+{
+	/*
+	 * Required as perf_event_create_kernel_counter() requires an overflow handler,
+	 * even though all we do is poll.
+	 */
+}
+
+static void perf_cpupmu_polling(unsigned long long stamp, int cpu)
+{
+	int			event_count = cpu_pmu->event_count[cpu];
+	struct met_pmu		*pmu = cpu_pmu->pmu[cpu];
+	int			i, count;
+	unsigned long long	delta;
+	struct perf_event	*ev;
+	unsigned int		pmu_value[MXNR_PMU_EVENTS];
+	u64 value;
+
+	if (per_cpu(perfSet, cpu) == 0)
+		return;
+
+	count = 0;
+	for (i = 0; i < event_count; i++) {
+		if (pmu[i].mode == 0)
+			continue;
+
+		ev = per_cpu(pevent, cpu)[i];
+		if ((ev != NULL) && (ev->state == PERF_EVENT_STATE_ACTIVE)) {
+			met_perf_event_read_local_symbol(ev, &value);
+			per_cpu(perfCurr, cpu)[i] = value;
+			delta = (per_cpu(perfCurr, cpu)[i] - per_cpu(perfPrev, cpu)[i]);
+			per_cpu(perfPrev, cpu)[i] = per_cpu(perfCurr, cpu)[i];
+			if (per_cpu(perfCntFirst, cpu)[i] == 1) {
+				/* we shall omit delta counter when we get first counter */
+				per_cpu(perfCntFirst, cpu)[i] = 0;
+				continue;
+			}
+			pmu_value[count] = (unsigned int)delta;
+			count++;
+		}
+	}
+
+	if (count == counter_cnt[cpu])
+		mp_cpu(count, pmu_value);
+}
+
+static struct perf_event* perf_event_create(int cpu, unsigned short event, int count)
+{
+	struct perf_event_attr	*ev_attr;
+	struct perf_event	*ev;
+
+	ev_attr = per_cpu(pevent_attr, cpu)+count;
+	memset(ev_attr, 0, sizeof(*ev_attr));
+	if (event == 0xff) {
+		ev_attr->config = PERF_COUNT_HW_CPU_CYCLES;
+		ev_attr->type = PERF_TYPE_HARDWARE;
+	} else {
+		ev_attr->config = event;
+		ev_attr->type = PERF_TYPE_RAW;
+	}
+	ev_attr->size = sizeof(*ev_attr);
+	ev_attr->sample_period = 0;
+	ev_attr->pinned = 1;
+
+	ev = perf_event_create_kernel_counter(ev_attr, cpu, NULL, dummy_handler, NULL);
+	if (IS_ERR(ev))
+		return NULL;
+	do {
+		if (ev->state == PERF_EVENT_STATE_ACTIVE)
+			break;
+		if (ev->state == PERF_EVENT_STATE_ERROR) {
+			perf_event_enable(ev);
+			if (ev->state == PERF_EVENT_STATE_ACTIVE)
+				break;
+		}
+		perf_event_release_kernel(ev);
+		return NULL;
+	} while (0);
+
+	return ev;
+}
+
+static void perf_event_release(int cpu, struct perf_event *ev)
+{
+	if (ev->state == PERF_EVENT_STATE_ACTIVE)
+		perf_event_disable(ev);
+	perf_event_release_kernel(ev);
+}
+
+#if defined(CONFIG_MTK_TINYSYS_SSPM_SUPPORT) && defined(ONDIEMET_SUPPORT)
+#define	PMU_OVERFLOWED_MASK	0xffffffff
+
+static inline int pmu_has_overflowed(u32 pmovsr)
+{
+	return pmovsr & PMU_OVERFLOWED_MASK;
+}
+
+static irqreturn_t perf_event_handle_irq_ignore_overflow(int irq_num, void *dev)
+{
+	u32 pmovsr;
+
+	pmovsr = cpu_pmu->pmu_read_clear_overflow_flag();
+
+	if (!pmu_has_overflowed(pmovsr)) {
+		return IRQ_NONE;
+	}
+	else {
+		irq_work_run();
+		return IRQ_HANDLED;
+	}
+}
+#endif
+
+static int perf_thread_set_perf_events(int cpu)
+{
+	int			i, size;
+	struct perf_event	*ev;
+
+	size = sizeof(struct perf_event_attr);
+	if (per_cpu(perfSet, cpu) == 0) {
+		int event_count = cpu_pmu->event_count[cpu];
+		struct met_pmu *pmu = cpu_pmu->pmu[cpu];
+		for (i = 0; i < event_count; i++) {
+			if (!pmu[i].mode)
+				continue;	/* Skip disabled counters */
+			ev = perf_event_create(cpu, pmu[i].event, i);
+			if (ev == NULL) {
+				met_cpupmu.mode = 0;
+				met_perf_cpupmu_status = 0;
+
+				MET_TRACE("[MET_PMU] cpu %d failed to register pmu event %4x\n", cpu, pmu[i].event);
+				pr_notice("[MET_PMU] cpu %d failed to register pmu event %4x\n", cpu, pmu[i].event);
+				continue;
+			}
+
+			/*
+			 * in perf-event implementation, hardware pmu slot and cycle counter
+			 * was mapped to perf_event::hw::idx as follows:
+			 *
+			 * | idx | hardware slot |
+			 * |-----+---------------|
+			 * |   0 | pmccntr_el0   |
+			 * |   1 | 0             |
+			 * |   2 | 1             |
+			 * |   3 | 2             |
+			 * |   4 | 3             |
+			 * |   5 | 4             |
+			 * |   6 | 5             |
+			 */
+			if (ev->hw.idx != 0) {
+				MET_TRACE("[MET_PMU] cpu %d registered in pmu slot: [%d] evt=%#04x\n",
+					  cpu, ev->hw.idx-1, pmu[i].event);
+				pr_debug("[MET_PMU] cpu %d registered in pmu slot: [%d] evt=%#04x\n",
+					 cpu, ev->hw.idx-1, pmu[i].event);
+			} else if (ev->hw.idx == 0) {
+				MET_TRACE("[MET_PMU] cpu %d registered cycle count evt=%#04x\n",
+					  cpu, pmu[i].event);
+				pr_debug("[MET_PMU] cpu %d registered cycle count evt=%#04x\n",
+					 cpu, pmu[i].event);
+			}
+
+			per_cpu(pevent, cpu)[i] = ev;
+			per_cpu(perfPrev, cpu)[i] = 0;
+			per_cpu(perfCurr, cpu)[i] = 0;
+			perf_event_enable(ev);
+			per_cpu(perfCntFirst, cpu)[i] = 1;
+
+#if defined(CONFIG_MTK_TINYSYS_SSPM_SUPPORT) && defined(ONDIEMET_SUPPORT)
+			if (met_cpupmu.ondiemet_mode) {
+				struct arm_pmu *armpmu;
+				armpmu = container_of(ev->pmu, struct arm_pmu, pmu);
+				mutex_lock(&handle_irq_lock);
+				if (armpmu && armpmu->handle_irq != perf_event_handle_irq_ignore_overflow) {
+					pr_debug("[MET_PMU] replaced original handle_irq=%p with dummy function\n",
+						 armpmu->handle_irq);
+					handle_irq_orig = armpmu->handle_irq;
+					armpmu->handle_irq = perf_event_handle_irq_ignore_overflow;
+				}
+				mutex_unlock(&handle_irq_lock);
+			}
+#endif
+		}	/* for all PMU counter */
+		per_cpu(perfSet, cpu) = 1;
+	}	/* for perfSet */
+
+	return 0;
+}
+
+static void met_perf_cpupmu_start(int cpu)
+{
+	if (met_cpupmu.mode == 0)
+		return;
+
+	perf_thread_set_perf_events(cpu);
+}
+
+static void perf_thread_down(int cpu)
+{
+	int			i;
+	struct perf_event	*ev;
+	int			event_count;
+	struct met_pmu		*pmu;
+
+	if (per_cpu(perfSet, cpu) == 0)
+		return;
+
+	per_cpu(perfSet, cpu) = 0;
+	event_count = cpu_pmu->event_count[cpu];
+	pmu = cpu_pmu->pmu[cpu];
+	for (i = 0; i < event_count; i++) {
+		ev = per_cpu(pevent, cpu)[i];
+		if (ev != NULL) {
+
+#if defined(CONFIG_MTK_TINYSYS_SSPM_SUPPORT) && defined(ONDIEMET_SUPPORT)
+			if (met_cpupmu.ondiemet_mode) {
+				struct arm_pmu *armpmu;
+				armpmu = container_of(ev->pmu, struct arm_pmu, pmu);
+				mutex_lock(&handle_irq_lock);
+				if (armpmu && armpmu->handle_irq == perf_event_handle_irq_ignore_overflow) {
+					pr_debug("[MET_PMU] restore original handle_irq=%p\n", handle_irq_orig);
+					armpmu->handle_irq = handle_irq_orig;
+					handle_irq_orig = NULL;
+				}
+				mutex_unlock(&handle_irq_lock);
+			}
+#endif
+
+			perf_event_release(cpu, ev);
+			per_cpu(pevent, cpu)[i] = NULL;
+		}
+	}
+}
+
+static void met_perf_cpupmu_stop(int cpu)
+{
+	perf_thread_down(cpu);
+}
+
+static int cpupmu_create_subfs(struct kobject *parent)
+{
+	int ret = 0;
+
+	cpu_pmu = cpu_pmu_hw_init();
+	if (cpu_pmu == NULL) {
+		PR_BOOTMSG("Failed to init CPU PMU HW!!\n");
+		return -ENODEV;
+	}
+
+	kobj_cpu = parent;
+
+#define KOBJ_ATTR_ITEM(attr_name) \
+	do { \
+		ret = sysfs_create_file(kobj_cpu, &attr_name ## _attr.attr); \
+		if (ret != 0) { \
+			pr_notice("Failed to create " #attr_name " in sysfs\n"); \
+			return ret; \
+		} \
+	} while (0)
+	KOBJ_ATTR_LIST;
+#undef  KOBJ_ATTR_ITEM
+
+	return 0;
+}
+
+static void cpupmu_delete_subfs(void)
+{
+#define KOBJ_ATTR_ITEM(attr_name) \
+	sysfs_remove_file(kobj_cpu, &attr_name ## _attr.attr)
+
+	if (kobj_cpu != NULL) {
+		KOBJ_ATTR_LIST;
+		kobj_cpu = NULL;
+	}
+#undef  KOBJ_ATTR_ITEM
+}
+
+void met_perf_cpupmu_polling(unsigned long long stamp, int cpu)
+{
+	int count;
+	unsigned int pmu_value[MXNR_PMU_EVENTS];
+
+	if (per_cpu(cpu_status, cpu) != MET_CPU_ONLINE)
+		return;
+
+	if (met_cpu_pmu_method) {
+		perf_cpupmu_polling(stamp, cpu);
+	} else {
+		count = cpu_pmu->polling(cpu_pmu->pmu[cpu], cpu_pmu->event_count[cpu], pmu_value);
+
+#ifdef CONFIG_CPU_PM
+		if (met_cpu_pm_pmu_reconfig) {
+			int ii;
+			for (ii = 0; ii < count; ii ++)
+				pmu_value[ii] += cpu_pmu->cpu_pm_unpolled_loss[cpu][ii];
+		}
+#endif
+
+		mp_cpu(count, pmu_value);
+
+#ifdef CONFIG_CPU_PM
+		if (met_cpu_pm_pmu_reconfig) {
+			memset(cpu_pmu->cpu_pm_unpolled_loss[cpu], 0, sizeof (cpu_pmu->cpu_pm_unpolled_loss[0]));
+		}
+#endif
+	}
+}
+
+static void cpupmu_start(void)
+{
+	int	cpu = raw_smp_processor_id();
+
+	if (!met_cpu_pmu_method) {
+		nr_arg[cpu] = 0;
+		cpu_pmu->start(cpu_pmu->pmu[cpu], cpu_pmu->event_count[cpu]);
+
+		met_perf_cpupmu_status = 1;
+		per_cpu(cpu_status, cpu) = MET_CPU_ONLINE;
+	}
+}
+
+
+static void cpupmu_unique_start(void)
+{
+	int cpu;
+
+#ifdef CPUPMU_V8_2
+	int ret = 0;
+	if (mtk_pmu_event_enable == 1){
+		ret = cpu_pmu_debug_init();
+		if (ret == 0)
+			PR_BOOTMSG("Failed to init CPU PMU debug!!\n");
+	}
+#endif
+
+#ifdef CONFIG_CPU_PM
+	use_cpu_pm_pmu_notifier = 0;
+	if (met_cpu_pm_pmu_reconfig) {
+		if (met_cpu_pmu_method) {
+			met_cpu_pm_pmu_reconfig = 0;
+			MET_TRACE("[MET_PMU] met_cpu_pmu_method=%d, met_cpu_pm_pmu_reconfig forced disabled\n", met_cpu_pmu_method);
+			pr_debug("[MET_PMU] met_cpu_pmu_method=%d, met_cpu_pm_pmu_reconfig forced disabled\n", met_cpu_pmu_method);
+		} else {
+			memset(cpu_pmu->cpu_pm_unpolled_loss, 0, sizeof (cpu_pmu->cpu_pm_unpolled_loss));
+			cpu_pm_register_notifier(&cpu_pm_pmu_notifier);
+			use_cpu_pm_pmu_notifier = 1;
+		}
+	}
+#else
+	if (met_cpu_pm_pmu_reconfig) {
+		met_cpu_pm_pmu_reconfig = 0;
+		MET_TRACE("[MET_PMU] CONFIG_CPU_PM=%d, met_cpu_pm_pmu_reconfig forced disabled\n", CONFIG_CPU_PM);
+		pr_debug("[MET_PMU] CONFIG_CPU_PM=%d, met_cpu_pm_pmu_reconfig forced disabled\n", CONFIG_CPU_PM);
+	}
+#endif
+	MET_TRACE("[MET_PMU] met_cpu_pm_pmu_reconfig=%u\n", met_cpu_pm_pmu_reconfig);
+	pr_debug("[MET_PMU] met_cpu_pm_pmu_reconfig=%u\n", met_cpu_pm_pmu_reconfig);
+
+	if (met_cpu_pmu_method) {
+		for_each_possible_cpu(cpu) {
+			met_perf_cpupmu_start(cpu);
+
+			met_perf_cpupmu_status = 1;
+			per_cpu(cpu_status, cpu) = MET_CPU_ONLINE;
+		}
+	}
+
+	return;
+}
+
+static void cpupmu_stop(void)
+{
+	int	cpu = raw_smp_processor_id();
+
+	met_perf_cpupmu_status = 0;
+
+	if (!met_cpu_pmu_method)
+		cpu_pmu->stop(cpu_pmu->event_count[cpu]);
+}
+
+static void cpupmu_unique_stop(void)
+{
+	int cpu;
+
+	if (met_cpu_pmu_method) {
+		for_each_possible_cpu(cpu) {
+			met_perf_cpupmu_stop(cpu);
+		}
+	}
+
+#ifdef CPUPMU_V8_2
+	if (mtk_pmu_event_enable == 1)
+		cpu_pmu_debug_uninit();
+#endif
+
+#ifdef CONFIG_CPU_PM
+	if (use_cpu_pm_pmu_notifier) {
+		cpu_pm_unregister_notifier(&cpu_pm_pmu_notifier);
+	}
+#endif
+	return;
+}
+
+static const char cache_line_header[] =
+	"met-info [000] 0.0: met_cpu_cache_line_size: %d\n";
+static const char header[] =
+	"met-info [000] 0.0: met_cpu_header_v2: %d";
+
+static const char help[] =
+	"  --pmu-cpu-evt=[cpu_list:]event_list   select CPU-PMU events in %s\n"
+	"                                        cpu_list: specify the cpu_id list or apply to all the cores\n"
+	"                                            example: 0,1,2\n"
+	"                                        event_list: specify the event number\n"
+	"                                            example: 0x8,0xff\n";
+
+static int cpupmu_print_help(char *buf, int len)
+{
+	return snprintf(buf, PAGE_SIZE, help, cpu_pmu->cpu_name);
+}
+
+static int reset_driver_stat(void)
+{
+	int		cpu, i;
+	int		event_count;
+	struct met_pmu	*pmu;
+
+	met_cpupmu.mode = 0;
+	for_each_possible_cpu(cpu) {
+		event_count = cpu_pmu->event_count[cpu];
+		pmu = cpu_pmu->pmu[cpu];
+		counter_cnt[cpu] = 0;
+		nr_arg[cpu] = 0;
+		for (i = 0; i < event_count; i++) {
+			pmu[i].mode = MODE_DISABLED;
+			pmu[i].event = 0;
+			pmu[i].freq = 0;
+		}
+	}
+
+	return 0;
+}
+
+static int cpupmu_print_header(char *buf, int len)
+{
+	int		cpu, i, ret, first;
+	int		event_count;
+	struct met_pmu	*pmu;
+
+	ret = 0;
+
+	/*append CPU PMU access method*/
+	if (met_cpu_pmu_method)
+		ret += snprintf(buf + ret, len,
+			"met-info [000] 0.0: CPU_PMU_method: perf APIs\n");
+	else
+		ret += snprintf(buf + ret, len,
+			"met-info [000] 0.0: CPU_PMU_method: MET pmu driver\n");
+
+	/*append cache line size*/
+	ret += snprintf(buf + ret, len - ret, cache_line_header, cache_line_size());
+	ret += snprintf(buf + ret, len - ret, "# mp_cpu: pmu_value1, ...\n");
+
+	for_each_possible_cpu(cpu) {
+		event_count = cpu_pmu->event_count[cpu];
+		pmu = cpu_pmu->pmu[cpu];
+		first = 1;
+		for (i = 0; i < event_count; i++) {
+			if (pmu[i].mode == 0)
+				continue;
+			if (first) {
+				ret += snprintf(buf + ret, len - ret, header, cpu);
+				first = 0;
+			}
+			ret += snprintf(buf + ret, len - ret, ",0x%x", pmu[i].event);
+			pmu[i].mode = 0;
+		}
+		if (!first)
+			ret += snprintf(buf + ret, len - ret, "\n");
+	}
+
+	reset_driver_stat();
+
+	return ret;
+}
+
+static int met_parse_num_list(char *arg, int len, int *list, int list_cnt)
+{
+	int	nr_num = 0;
+	char	*num;
+	int	num_len;
+
+	/* search ',' as the splitter */
+	while (len) {
+		num = arg;
+		num_len = 0;
+		if (list_cnt <= 0)
+			return -1;
+		while (len) {
+			len--;
+			if (*arg == ',') {
+				*(arg++) = '\0';
+				break;
+			}
+			arg++;
+			num_len++;
+		}
+		if (met_parse_num(num, list, num_len) < 0)
+			return -1;
+		list++;
+		list_cnt--;
+		nr_num++;
+	}
+
+	return nr_num;
+}
+
+static const struct perf_pmu_events_attr *
+perf_event_get_evt_attr_by_name(const struct perf_event *ev,
+			       const char *name) {
+	struct arm_pmu *arm_pmu;
+	struct attribute **attrp;
+	struct device_attribute *dev_attr_p;
+	struct perf_pmu_events_attr *ev_attr_p;
+
+	arm_pmu = container_of(ev->pmu, struct arm_pmu, pmu);
+
+	for (attrp = arm_pmu->attr_groups[ARMPMU_ATTR_GROUP_EVENTS]->attrs;
+	     *attrp != NULL;
+	     attrp ++) {
+
+		dev_attr_p = container_of(*attrp, struct device_attribute, attr);
+		ev_attr_p = container_of(dev_attr_p, struct perf_pmu_events_attr, attr);
+
+		if (0 == strcmp((*attrp)->name, name)) {
+			return ev_attr_p;
+		}
+	}
+
+	return NULL;
+}
+
+static int cpupmu_process_argument(const char *arg, int len)
+{
+	char		*arg1 = (char*)arg;
+	int		len1 = len;
+	int		cpu, cpu_list[MXNR_CPU];
+	int		nr_events, event_list[MXNR_PMU_EVENTS];
+	int		i;
+	int		nr_counters;
+	struct met_pmu	*pmu;
+	int		arg_nr;
+	int		event_no;
+	int		is_cpu_cycle_evt;
+	const struct perf_pmu_events_attr *ev_attr_p;
+
+	/*
+	 * split cpu_list and event_list by ':'
+	 *   arg, len: cpu_list when found (i < len)
+	 *   arg1, len1: event_list
+	 */
+	for (i = 0; i < len; i++) {
+		if (arg[i] == ':') {
+			arg1[i] = '\0';
+			arg1 += i+1;
+			len1 = len - i - 1;
+			len = i;
+			break;
+		}
+	}
+
+	/*
+	 * setup cpu_list array
+	 *   1: selected
+	 *   0: unselected
+	 */
+	if (arg1 != arg) {	/* is cpu_id list specified? */
+		int list[MXNR_CPU], cnt;
+		int cpu_id;
+		if ((cnt = met_parse_num_list((char*)arg, len, list, ARRAY_SIZE(list))) <= 0)
+			goto arg_out;
+		memset(cpu_list, 0, sizeof(cpu_list));
+		for (i = 0; i < cnt; i++) {
+			cpu_id = list[i];
+			if (cpu_id < 0 || cpu_id >= ARRAY_SIZE(cpu_list))
+				goto arg_out;
+			cpu_list[cpu_id] = 1;
+		}
+	}
+	else
+		memset(cpu_list, 1, sizeof(cpu_list));
+
+	/* get event_list */
+	if ((nr_events = met_parse_num_list(arg1, len1, event_list, ARRAY_SIZE(event_list))) <= 0)
+		goto arg_out;
+
+	/* for each cpu in cpu_list, add all the events in event_list */
+	for_each_possible_cpu(cpu) {
+		pmu = cpu_pmu->pmu[cpu];
+		arg_nr = nr_arg[cpu];
+
+		if (cpu_list[cpu] == 0)
+			continue;
+
+		if (met_cpu_pmu_method) {
+			nr_counters = perf_num_counters();
+		} else {
+			nr_counters = cpu_pmu->event_count[cpu];
+		}
+
+		pr_debug("[MET_PMU] pmu slot count=%d\n", nr_counters);
+
+		if (nr_counters == 0)
+			goto arg_out;
+
+		for (i = 0; i < nr_events; i++) {
+			event_no = event_list[i];
+			is_cpu_cycle_evt = 0;
+			/*
+			 * check if event is duplicate, but does not include 0xff
+			 */
+			if (cpu_pmu->check_event(pmu, arg_nr, event_no) < 0)
+				goto arg_out;
+
+			/*
+			 * test if this event is available when in perf_APIs mode
+			 */
+			if (met_cpu_pmu_method) {
+				struct perf_event *ev;
+
+				if (!cpu_pmu->perf_event_get_evttype) {
+					MET_TRACE("[MET_PMU] cpu_pmu->perf_event_get_evttype=NULL, "
+						  "met pmu on perf-event was not supported on this platform\n");
+					pr_debug("[MET_PMU] cpu_pmu->perf_event_get_evttype=NULL, "
+						 "met pmu on perf-event was not supported on this platform\n");
+					goto arg_out;
+				}
+
+				ev = perf_event_create(cpu, event_no, arg_nr);
+				if (ev == NULL) {
+					pr_debug("!!!!!!!! [MET_PMU] failed pmu alloction test (event_no=%#04x)\n", event_no);
+					goto arg_out;
+				} else {
+					perf_event_release(cpu, ev);
+				}
+
+				ev_attr_p = perf_event_get_evt_attr_by_name(ev, "cpu_cycles");
+				if (ev_attr_p && cpu_pmu->perf_event_get_evttype(ev) == ev_attr_p->id)
+					is_cpu_cycle_evt = 1;
+			}
+
+			if (met_cpu_pmu_method) {
+				if (is_cpu_cycle_evt) {
+					if (pmu[nr_counters-1].mode == MODE_POLLING)
+						goto arg_out;
+					pmu[nr_counters-1].mode = MODE_POLLING;
+					pmu[nr_counters-1].event = event_no;
+					pmu[nr_counters-1].freq = 0;
+				} else {
+					if (arg_nr >= (nr_counters - 1))
+						goto arg_out;
+					pmu[arg_nr].mode = MODE_POLLING;
+					pmu[arg_nr].event = event_no;
+					pmu[arg_nr].freq = 0;
+					arg_nr++;
+				}
+			} else {
+				if (event_no == 0xff) {
+					if (pmu[nr_counters-1].mode == MODE_POLLING)
+						goto arg_out;
+					pmu[nr_counters-1].mode = MODE_POLLING;
+					pmu[nr_counters-1].event = 0xff;
+					pmu[nr_counters-1].freq = 0;
+				} else {
+					if (arg_nr >= (nr_counters - 1))
+						goto arg_out;
+					pmu[arg_nr].mode = MODE_POLLING;
+					pmu[arg_nr].event = event_no;
+					pmu[arg_nr].freq = 0;
+					arg_nr++;
+				}
+			}
+			counter_cnt[cpu]++;
+		}
+		nr_arg[cpu] = arg_nr;
+	}
+
+	met_cpupmu.mode = 1;
+	return 0;
+
+arg_out:
+	reset_driver_stat();
+	return -EINVAL;
+}
+
+static void cpupmu_cpu_state_notify(long cpu, unsigned long action)
+{
+	per_cpu(cpu_status, cpu) = action;
+
+#if (defined(CONFIG_ARM64) || defined(CONFIG_ARM))
+	if (met_cpu_pmu_method && action == MET_CPU_OFFLINE) {
+		struct perf_event *event = NULL;
+		struct arm_pmu *armpmu = NULL;
+		struct platform_device *pmu_device = NULL;
+		int irq = 0;
+
+		event = per_cpu(pevent, cpu)[0];
+		if (event)
+			armpmu = to_arm_pmu(event->pmu);
+		pr_debug("!!!!!!!! %s_%ld, event=%p\n", __FUNCTION__, cpu, event);
+
+		if (armpmu)
+			pmu_device = armpmu->plat_device;
+		pr_debug("!!!!!!!! %s_%ld, armpmu=%p\n", __FUNCTION__, cpu, armpmu);
+
+		if (pmu_device)
+			irq = platform_get_irq(pmu_device, 0);
+		pr_debug("!!!!!!!! %s_%ld, pmu_device=%p\n", __FUNCTION__, cpu, pmu_device);
+
+		if (irq > 0)
+			disable_percpu_irq(irq);
+		pr_debug("!!!!!!!! %s_%ld, irq=%d\n", __FUNCTION__, cpu, irq);
+	}
+#endif
+}
+
+#if defined(CONFIG_MTK_TINYSYS_SSPM_SUPPORT) && defined(ONDIEMET_SUPPORT)
+static void sspm_pmu_start(void)
+{
+	ondiemet_module[ONDIEMET_SSPM] |= ID_PMU;
+
+	if (met_cpupmu.ondiemet_mode == 1)
+		cpupmu_start();
+}
+
+static int cycle_count_mode_enabled(int cpu) {
+
+	int event_cnt;
+	struct met_pmu	*pmu;
+
+	pmu = cpu_pmu->pmu[cpu];
+
+	if (met_cpu_pmu_method) {
+		event_cnt = perf_num_counters();
+	} else {
+		event_cnt = cpu_pmu->event_count[cpu];
+	}
+
+	return pmu[event_cnt-1].mode == MODE_POLLING;
+}
+
+static void ipi_config_pmu_counter_cnt(void) {
+
+	int ret, cpu, ii, cnt_num;
+	unsigned int rdata;
+	unsigned int ipi_buf[4];
+	struct hw_perf_event *hwc;
+	unsigned int base_offset;
+
+	for_each_possible_cpu(cpu) {
+		for (ii = 0; ii < 4; ii++)
+			ipi_buf[ii] = 0;
+
+		ipi_buf[0] = MET_MAIN_ID | (MID_PMU << MID_BIT_SHIFT) | MET_ARGU | SET_PMU_EVT_CNT;
+		/*
+		 *  XXX: on sspm side, cycle counter was not counted in
+		 *       total event number `counter_cnt', but controlled by
+		 *       an addtional argument `SET_PMU_CYCCNT_ENABLE' instead
+		 */
+		cnt_num = (cycle_count_mode_enabled(cpu) ?
+			   (counter_cnt[cpu]-1) : counter_cnt[cpu]);
+		ipi_buf[1] = (cpu << 16) | (cnt_num & 0xffff);
+
+		MET_TRACE("[MET_PMU][IPI_CONFIG] core=%d, pmu_counter_cnt=%d\n", cpu, cnt_num);
+		pr_debug("[MET_PMU][IPI_CONFIG] core=%d, pmu_counter_cnt=%d\n", cpu, cnt_num);
+
+		MET_TRACE("[MET_PMU][IPI_CONFIG] sspm_buf_available=%d, in_interrupt()=%lu\n", sspm_buf_available, in_interrupt());
+		pr_debug("[MET_PMU][IPI_CONFIG] sspm_buf_available=%d, in_interrupt()=%lu\n", sspm_buf_available, in_interrupt());
+
+		if (sspm_buf_available == 1) {
+			ret = met_ipi_to_sspm_command((void *) ipi_buf, 0, &rdata, 1);
+		}
+
+		for (ii = 0; ii < 4; ii++)
+			ipi_buf[ii] = 0;
+
+		if (per_cpu(pevent, cpu)[0]) {
+			hwc = &(per_cpu(pevent, cpu)[0]->hw);
+			base_offset = hwc->idx-1;
+		} else {
+			base_offset = 0;
+		}
+
+		ipi_buf[0] = MET_MAIN_ID | (MID_PMU << MID_BIT_SHIFT) | MET_ARGU | SET_PMU_BASE_OFFSET;
+		ipi_buf[1] = (cpu << 16) | (base_offset & 0xffff);
+
+		MET_TRACE("[MET_PMU][IPI_CONFIG] core=%d, base offset set to %lu\n", cpu, base_offset);
+		pr_debug("[MET_PMU][IPI_CONFIG] core=%d, base offset set to %lu\n", cpu, base_offset);
+
+		if (sspm_buf_available == 1) {
+			ret = met_ipi_to_sspm_command((void *) ipi_buf, 0, &rdata, 1);
+		}
+
+		if (cycle_count_mode_enabled(cpu)) {
+
+			for (ii = 0; ii < 4; ii++)
+				ipi_buf[ii] = 0;
+
+			ipi_buf[0] = MET_MAIN_ID | (MID_PMU << MID_BIT_SHIFT) | MET_ARGU | SET_PMU_CYCCNT_ENABLE;
+			ipi_buf[1] = cpu & 0xffff;
+
+			MET_TRACE("[MET_PMU][IPI_CONFIG] core=%d, pmu cycle cnt enable\n", cpu);
+			pr_debug("[MET_PMU][IPI_CONFIG] core=%d, pmu cycle cnt enable\n", cpu);
+
+			if (sspm_buf_available == 1) {
+				ret = met_ipi_to_sspm_command((void *) ipi_buf, 0, &rdata, 1);
+			}
+		}
+	}
+}
+
+static int __is_perf_event_hw_slot_seq_order(int cpu) {
+
+	struct hw_perf_event *hwc, *hwc_prev;
+	int event_count = cpu_pmu->event_count[cpu];
+	int ii;
+
+	/*
+	 * perf-event descriptor list would not have any hole
+	 * (excepts special 0xff, which will always be the last element)
+	 */
+	if (per_cpu(pevent, cpu)[0] == NULL)
+		return 1;
+
+	/*
+	 * XXX: no need to check the last slot,
+	 *      which is reserved for 0xff
+	 */
+	for (ii = 1; ii < event_count - 1; ii++) {
+
+		if (per_cpu(pevent, cpu)[ii] == NULL)
+			return 1;
+
+		hwc = &(per_cpu(pevent, cpu)[ii]->hw);
+		hwc_prev = &(per_cpu(pevent, cpu)[ii-1]->hw);
+
+		if (hwc->idx != hwc_prev->idx + 1)
+			return 0;
+	}
+
+	return 1;
+}
+
+static int __validate_sspm_compatibility(void) {
+
+	int cpu;
+
+	for_each_possible_cpu(cpu) {
+
+		if (!__is_perf_event_hw_slot_seq_order(cpu)) {
+			MET_TRACE("[MET_PMU] pmu not sequentially allocated on cpu %d\n"
+				  ,cpu);
+			pr_debug("[MET_PMU] pmu not sequentially allocated on cpu %d\n"
+				 ,cpu);
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+static void sspm_pmu_unique_start(void) {
+
+	if (met_cpupmu.ondiemet_mode == 1)
+		cpupmu_unique_start();
+
+	if (met_cpupmu.ondiemet_mode == 1) {
+		if (__validate_sspm_compatibility() == -1) {
+			MET_TRACE("[MET_PMU] turned off sspm side polling\n");
+			pr_debug("[MET_PMU] turned off sspm side polling\n");
+			/* return without sending init IPIs, leaving sspm side to poll nothing */
+			return;
+		}
+	}
+
+	ipi_config_pmu_counter_cnt();
+}
+
+static void sspm_pmu_unique_stop(void)
+{
+	if (met_cpupmu.ondiemet_mode == 1)
+		cpupmu_unique_stop();
+	return;
+}
+
+static void sspm_pmu_stop(void)
+{
+	if (met_cpupmu.ondiemet_mode == 1)
+		cpupmu_stop();
+}
+
+static const char sspm_pmu_header[] = "met-info [000] 0.0: pmu_sampler: sspm\n";
+
+static int sspm_pmu_print_header(char *buf, int len)
+{
+	int ret;
+
+	ret = snprintf(buf, len, sspm_pmu_header);
+
+	if (met_cpupmu.ondiemet_mode == 1)
+		ret += cpupmu_print_header(buf + ret, len - ret);
+
+	return ret;
+}
+
+static int sspm_pmu_process_argument(const char *arg, int len)
+{
+	if (met_cpupmu.ondiemet_mode == 1) {
+
+		if (!cpu_pmu->pmu_read_clear_overflow_flag) {
+			MET_TRACE("[MET_PMU] cpu_pmu->pmu_read_clear_overflow_flag=NULL, "
+				  "pmu on sspm was not supported on this platform\n");
+			pr_debug("[MET_PMU] cpu_pmu->pmu_read_clear_overflow_flag=NULL, "
+				 "pmu on sspm was not supported on this platform\n");
+			return -EINVAL;
+		}
+
+		return cpupmu_process_argument(arg, len);
+	}
+	return 0;
+}
+#endif
+
+struct metdevice met_cpupmu = {
+	.name = "cpu",
+	.type = MET_TYPE_PMU,
+	.cpu_related = 1,
+	.create_subfs = cpupmu_create_subfs,
+	.delete_subfs = cpupmu_delete_subfs,
+	.start = cpupmu_start,
+	.uniq_start = cpupmu_unique_start,
+	.stop = cpupmu_stop,
+	.uniq_stop = cpupmu_unique_stop,
+	.polling_interval = 1,
+	.timed_polling = met_perf_cpupmu_polling,
+	.print_help = cpupmu_print_help,
+	.print_header = cpupmu_print_header,
+	.process_argument = cpupmu_process_argument,
+	.cpu_state_notify = cpupmu_cpu_state_notify,
+
+#if defined(CONFIG_MTK_TINYSYS_SSPM_SUPPORT) && defined(ONDIEMET_SUPPORT)
+	.ondiemet_mode = 1,
+	.ondiemet_start = sspm_pmu_start,
+	.uniq_ondiemet_start = sspm_pmu_unique_start,
+	.uniq_ondiemet_stop = sspm_pmu_unique_stop,
+	.ondiemet_stop = sspm_pmu_stop,
+	.ondiemet_print_header = sspm_pmu_print_header,
+	.ondiemet_process_argument = sspm_pmu_process_argument
+#endif
+};
diff --git a/src/devtools/met-driver/4.19/common/cpu_pmu.h b/src/devtools/met-driver/4.19/common/cpu_pmu.h
new file mode 100644
index 0000000..45f7893
--- /dev/null
+++ b/src/devtools/met-driver/4.19/common/cpu_pmu.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2019 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _CPU_PMU_H_
+#define _CPU_PMU_H_
+
+#include <linux/device.h>
+#include <linux/perf_event.h>
+
+#define MODE_DISABLED	0
+#define MODE_INTERRUPT	1
+#define MODE_POLLING	2
+
+#define MXSIZE_PMU_DESC 32
+#define MXNR_CPU	NR_CPUS
+
+#define	MXNR_PMU_EVENTS	8	/* max number of pmu counter for armv8 is 6+1 */
+struct met_pmu {
+	unsigned char mode;
+	unsigned short event;
+	unsigned long freq;
+	struct kobject *kobj_cpu_pmu;
+};
+
+struct cpu_pmu_hw {
+	const char *name;
+	const char *cpu_name;
+	int nr_cnt;
+	int (*get_event_desc)(int idx, int event, char *event_desc);
+	int (*check_event)(struct met_pmu *pmu, int idx, int event);
+	void (*start)(struct met_pmu *pmu, int count);
+	void (*stop)(int count);
+	unsigned int (*polling)(struct met_pmu *pmu, int count, unsigned int *pmu_value);
+	unsigned long (*perf_event_get_evttype)(struct perf_event *ev);
+	u32 (*pmu_read_clear_overflow_flag)(void);
+	struct met_pmu *pmu[MXNR_CPU];
+	int event_count[MXNR_CPU];
+	/*
+	 * used for compensation of pmu counter loss
+	 * between end of polling and start of cpu pm
+	 */
+	unsigned int cpu_pm_unpolled_loss[MXNR_CPU][MXNR_PMU_EVENTS];
+};
+
+struct pmu_desc {
+	unsigned int event;
+	char name[MXSIZE_PMU_DESC];
+};
+
+typedef enum {
+	SET_PMU_EVT_CNT = 0x0,
+	SET_PMU_CYCCNT_ENABLE = 0x1,
+	SET_PMU_BASE_OFFSET = 0x02
+} PMU_IPI_Type;
+
+struct cpu_pmu_hw *cpu_pmu_hw_init(void);
+
+extern struct cpu_pmu_hw *cpu_pmu;
+extern noinline void mp_cpu(unsigned char cnt, unsigned int *value);
+
+extern int met_perf_cpupmu_status;
+extern void met_perf_cpupmu_polling(unsigned long long stamp, int cpu);
+
+#endif	/* _CPU_PMU_H_ */
diff --git a/src/devtools/met-driver/4.19/common/dummy_header.c b/src/devtools/met-driver/4.19/common/dummy_header.c
new file mode 100644
index 0000000..ea9c12a
--- /dev/null
+++ b/src/devtools/met-driver/4.19/common/dummy_header.c
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2019 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+
+#include "interface.h"
+#include "met_drv.h"
+
+static struct kobject *kobj_met_dummy;
+static char header_str[PAGE_SIZE];
+static int header_str_len;
+struct metdevice met_dummy_header;
+
+static ssize_t dummy_str_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf);
+static ssize_t dummy_str_store(struct kobject *kobj,
+			       struct kobj_attribute *attr, const char *buf, size_t n);
+static struct kobj_attribute dummy_attr = __ATTR(dummy_str, 0664, dummy_str_show, dummy_str_store);
+
+
+static ssize_t dummy_str_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	int ret;
+
+	ret = snprintf(buf, PAGE_SIZE, "%s", header_str);
+
+	return ret;
+}
+
+static ssize_t dummy_str_store(struct kobject *kobj,
+			       struct kobj_attribute *attr, const char *buf, size_t n)
+{
+	int ret = 0;
+	char *ptr = header_str;
+
+	if ((header_str_len + strlen(buf)) < PAGE_SIZE) {
+		ret = snprintf(ptr + header_str_len, PAGE_SIZE - header_str_len, "%s\n", buf);
+		header_str_len += ret;
+	}
+	met_dummy_header.mode = 1;
+
+	return n;
+}
+
+static int dummy_reset(void)
+{
+	met_dummy_header.mode = 0;
+	memset(header_str, 0x00, PAGE_SIZE);
+	header_str_len = 0;
+
+	return 0;
+}
+
+static int dummy_print_header(char *buf, int len)
+{
+	if (header_str_len > 0)
+		len = snprintf(buf, PAGE_SIZE, "%s", header_str);
+	else
+		len = snprintf(buf, PAGE_SIZE, "# dummy header is empty\n");
+
+	return len;
+}
+
+static int dummy_create(struct kobject *parent)
+{
+	int ret = 0;
+
+	kobj_met_dummy = parent;
+	ret = sysfs_create_file(kobj_met_dummy, &dummy_attr.attr);
+	if (ret != 0) {
+		pr_debug("Failed to create montype0 in sysfs\n");
+		return ret;
+	}
+
+	return ret;
+}
+
+static void dummy_delete(void)
+{
+	sysfs_remove_file(kobj_met_dummy, &dummy_attr.attr);
+	kobj_met_dummy = NULL;
+}
+
+struct metdevice met_dummy_header = {
+	.name = "dummy_header",
+	.type = MET_TYPE_MISC,
+	.cpu_related = 0,
+	.start = NULL,
+	.stop = NULL,
+	.reset = dummy_reset,
+	.polling_interval = 0,
+	.timed_polling = NULL,
+	.print_help = NULL,
+	.print_header = dummy_print_header,
+	.create_subfs = dummy_create,
+	.delete_subfs = dummy_delete,
+};
diff --git a/src/devtools/met-driver/4.19/common/emi/SEDA3/met_emi.c b/src/devtools/met-driver/4.19/common/emi/SEDA3/met_emi.c
new file mode 100644
index 0000000..b3e09d3
--- /dev/null
+++ b/src/devtools/met-driver/4.19/common/emi/SEDA3/met_emi.c
@@ -0,0 +1,1038 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2019 MediaTek Inc.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/device.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/string.h>
+
+#ifdef CONFIG_ARM
+//#include <asm/dma-mapping.h> /* arm_coherent_dma_ops */
+#endif /* CONFIG_ARM */
+
+#include <linux/dma-mapping.h>
+
+#define MET_USER_EVENT_SUPPORT
+#include "met_drv.h"
+#include "trace.h"
+
+#include "mtk_typedefs.h"
+#include "core_plf_init.h"
+/* #include "plf_trace.h" */
+#include "mtk_emi_bm.h"
+#include "interface.h"
+#include "met_dramc.h"
+
+#if defined(CONFIG_MTK_TINYSYS_SSPM_SUPPORT) && defined(ONDIEMET_SUPPORT)
+#include "sspm/ondiemet_sspm.h"
+#endif
+
+/*======================================================================*/
+/*	Global variable definitions					*/
+/*======================================================================*/
+int emi_TP_busfiltr_enable;
+int emi_tsct_enable = 1;
+int emi_mdct_enable = 1;
+
+static int bw_limiter_enable = BM_BW_LIMITER_ENABLE;
+
+static int msel_enable;
+static unsigned int msel_group1 = _GP_1_Default;
+static unsigned int msel_group2 = _GP_2_Default;
+static unsigned int msel_group3 = _GP_3_Default;
+
+/* Global variables */
+static struct kobject *kobj_emi;
+
+static int ttype1_16_en = BM_TTYPE1_16_DISABLE;
+static int ttype17_21_en = BM_TTYPE17_21_DISABLE;
+
+static int dramc_pdir_enable = 1;
+static int dram_chann_num = 1;
+
+static int times;
+static ssize_t test_store(struct kobject *kobj,
+				struct kobj_attribute *attr,
+				const char *buf,
+				size_t n)
+{
+	int i;
+	unsigned int    *src_addr_v = NULL;
+	dma_addr_t src_addr_p;
+#ifdef CONFIG_ARM
+	struct dma_map_ops *ops = (struct dma_map_ops *)symbol_get(arm_dma_ops);
+#endif /* CONFIG_ARM */
+
+	if ((n == 0) || (buf == NULL))
+		return -EINVAL;
+	if (kstrtoint(buf, 10, &times) != 0)
+		return -EINVAL;
+	if (times < 0)
+		return -EINVAL;
+
+	if (times > 5000)
+		return -EINVAL;
+
+#ifdef CONFIG_ARM
+	if (ops && ops->alloc) {
+		(met_device.this_device)->coherent_dma_mask = DMA_BIT_MASK(32);
+		src_addr_v = ops->alloc(met_device.this_device,
+						PAGE_SIZE,
+						&src_addr_p,
+						GFP_KERNEL,
+						0);
+	}
+#endif /* CONFIG_ARM */
+
+#ifdef CONFIG_ARM64
+	/* dma_alloc */
+	src_addr_v = dma_alloc_coherent(met_device.this_device,
+					PAGE_SIZE,
+					&src_addr_p,
+					GFP_KERNEL);
+#endif /* CONFIG_ARM64 */
+
+	if (src_addr_v == NULL) {
+#ifdef CONFIG_MET_MODULE
+		met_tag_oneshot_real(0, "test dma alloc fail", PAGE_SIZE);
+#else
+		met_tag_oneshot(0, "test dma alloc fail", PAGE_SIZE);
+#endif
+		return -ENOMEM;
+	}
+
+	preempt_disable();
+#ifdef CONFIG_MET_MODULE
+	met_tag_start_real(0, "TEST_EMI");
+#else
+	met_tag_start(0, "TEST_EMI");
+#endif
+	for (i = 0; i < times; i++) {
+		memset(src_addr_v, 2*i, PAGE_SIZE);
+#ifdef CONFIG_MET_MODULE
+		met_tag_oneshot_real(0, "TEST_EMI", PAGE_SIZE);
+#else
+		met_tag_oneshot(0, "TEST_EMI", PAGE_SIZE);
+#endif
+	}
+#ifdef CONFIG_MET_MODULE
+	met_tag_end_real(0, "TEST_EMI");
+#else
+	met_tag_end(0, "TEST_EMI");
+#endif
+
+	my_preempt_enable();
+
+#ifdef CONFIG_ARM
+	/* dma_free */
+	if (ops && ops->free) {
+		ops->free(met_device.this_device,
+				  PAGE_SIZE,
+				  src_addr_v,
+				  src_addr_p,
+			0);
+	}
+#endif /* CONFIG_ARM */
+
+#ifdef CONFIG_ARM64
+	/* dma_free */
+	if (src_addr_v != NULL)
+		dma_free_coherent(met_device.this_device,
+				  PAGE_SIZE,
+				  src_addr_v,
+				  src_addr_p);
+#endif /* CONFIG_ARM64 */
+
+	return n;
+}
+
+/*======================================================================*/
+/*	KOBJ Declarations						*/
+/*======================================================================*/
+DECLARE_KOBJ_ATTR_INT(emi_TP_busfiltr_enable, emi_TP_busfiltr_enable);
+DECLARE_KOBJ_ATTR_INT(msel_enable, msel_enable);
+DECLARE_KOBJ_ATTR_HEX_CHECK(msel_group1, msel_group1, msel_group1 > 0 && msel_group1 <= _ALL);
+DECLARE_KOBJ_ATTR_HEX_CHECK(msel_group2, msel_group2, msel_group2 > 0 && msel_group2 <= _ALL);
+DECLARE_KOBJ_ATTR_HEX_CHECK(msel_group3, msel_group3, msel_group3 > 0 && msel_group3 <= _ALL);
+
+
+DECLARE_KOBJ_ATTR_STR_LIST_ITEM(
+	ttype1_16_en,
+	KOBJ_ITEM_LIST(
+		{ BM_TTYPE1_16_ENABLE,   "ENABLE" },
+		{ BM_TTYPE1_16_DISABLE,  "DISABLE" }
+		)
+	);
+DECLARE_KOBJ_ATTR_STR_LIST(ttype1_16_en, ttype1_16_en, ttype1_16_en);
+
+
+DECLARE_KOBJ_ATTR_STR_LIST_ITEM(
+	ttype17_21_en,
+	KOBJ_ITEM_LIST(
+		{ BM_TTYPE17_21_ENABLE,  "ENABLE" },
+		{ BM_TTYPE17_21_DISABLE, "DISABLE" }
+		)
+	);
+DECLARE_KOBJ_ATTR_STR_LIST(ttype17_21_en, ttype17_21_en, ttype17_21_en);
+
+DECLARE_KOBJ_ATTR_STR_LIST_ITEM(
+	ttype_master,
+	KOBJ_ITEM_LIST(
+		{ _M0,  "M0" },
+		{ _M1,  "M1" },
+		{ _M2,  "M2" },
+		{ _M3,  "M3" },
+		{ _M4,  "M4" },
+		{ _M5,  "M5" },
+		{ _M6,  "M6" },
+		{ _M7,  "M7" }
+		)
+	);
+
+
+DECLARE_KOBJ_ATTR_INT_LIST_ITEM(
+	ttype_nbeat,
+	KOBJ_ITEM_LIST(
+		{ BM_TRANS_TYPE_1BEAT,   1 },
+		{ BM_TRANS_TYPE_2BEAT,   2 },
+		{ BM_TRANS_TYPE_3BEAT,   3 },
+		{ BM_TRANS_TYPE_4BEAT,   4 },
+		{ BM_TRANS_TYPE_5BEAT,   5 },
+		{ BM_TRANS_TYPE_6BEAT,   6 },
+		{ BM_TRANS_TYPE_7BEAT,   7 },
+		{ BM_TRANS_TYPE_8BEAT,   8 },
+		{ BM_TRANS_TYPE_9BEAT,   9 },
+		{ BM_TRANS_TYPE_10BEAT,  10 },
+		{ BM_TRANS_TYPE_11BEAT,  11 },
+		{ BM_TRANS_TYPE_12BEAT,  12 },
+		{ BM_TRANS_TYPE_13BEAT,  13 },
+		{ BM_TRANS_TYPE_14BEAT,  14 },
+		{ BM_TRANS_TYPE_15BEAT,  15 },
+		{ BM_TRANS_TYPE_16BEAT,  16 }
+		)
+	);
+DECLARE_KOBJ_ATTR_INT_LIST_ITEM(
+	ttype_nbyte,
+	KOBJ_ITEM_LIST(
+		{ BM_TRANS_TYPE_1Byte,   1 },
+		{ BM_TRANS_TYPE_2Byte,   2 },
+		{ BM_TRANS_TYPE_4Byte,   4 },
+		{ BM_TRANS_TYPE_8Byte,   8 },
+		{ BM_TRANS_TYPE_16Byte,  16 },
+		{ BM_TRANS_TYPE_32Byte,  32 }
+		)
+	);
+DECLARE_KOBJ_ATTR_STR_LIST_ITEM(
+	ttype_burst,
+	KOBJ_ITEM_LIST(
+		{ BM_TRANS_TYPE_BURST_INCR,      "INCR" },
+		{ BM_TRANS_TYPE_BURST_WRAP,      "WRAP" }
+		)
+	);
+
+DECLARE_KOBJ_ATTR_STR_LIST_ITEM(
+	ttype_rw,
+	KOBJ_ITEM_LIST(
+		{ BM_TRANS_RW_DEFAULT,   "DEFAULT" },
+		{ BM_TRANS_RW_READONLY,  "R" },
+		{ BM_TRANS_RW_WRITEONLY, "W" },
+		{ BM_TRANS_RW_RWBOTH,    "BOTH" }
+		)
+	);
+
+
+DECLARE_KOBJ_ATTR_SHOW_INT(test, times);
+
+DECLARE_KOBJ_ATTR(test);
+
+
+static int high_priority_filter;
+DECLARE_KOBJ_ATTR_HEX(high_priority_filter, high_priority_filter);
+
+
+static int ttype_master_val[21];
+static int ttype_busid_val[21];
+static int ttype_nbeat_val[21];
+static int ttype_nbyte_val[21];
+static int ttype_burst_val[21];
+static int ttype_rw_val[21];
+
+#define DECLARE_KOBJ_TTYPE_MASTER(nr) \
+	DECLARE_KOBJ_ATTR_STR_LIST(ttype ## nr ## _master, ttype_master_val[nr - 1], ttype_master)
+
+#define DECLARE_KOBJ_TTYPE_NBEAT(nr) \
+	DECLARE_KOBJ_ATTR_INT_LIST(ttype ## nr ## _nbeat, ttype_nbeat_val[nr - 1], ttype_nbeat)
+
+#define DECLARE_KOBJ_TTYPE_NBYTE(nr) \
+	DECLARE_KOBJ_ATTR_INT_LIST(ttype ## nr ## _nbyte, ttype_nbyte_val[nr - 1], ttype_nbyte)
+
+#define DECLARE_KOBJ_TTYPE_BURST(nr) \
+	DECLARE_KOBJ_ATTR_STR_LIST(ttype ## nr ## _burst, ttype_burst_val[nr - 1], ttype_burst)
+
+#define DECLARE_KOBJ_TTYPE_RW(nr) \
+	DECLARE_KOBJ_ATTR_STR_LIST(ttype ## nr ## _rw, ttype_rw_val[nr - 1], ttype_rw)
+
+#define DECLARE_KOBJ_TTYPE_BUSID_VAL(nr) \
+	DECLARE_KOBJ_ATTR_HEX(ttype ## nr ## _busid, ttype_busid_val[nr - 1])
+
+DECLARE_KOBJ_TTYPE_MASTER(1);
+DECLARE_KOBJ_TTYPE_NBEAT(1);
+DECLARE_KOBJ_TTYPE_NBYTE(1);
+DECLARE_KOBJ_TTYPE_BURST(1);
+DECLARE_KOBJ_TTYPE_RW(1);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(1);
+
+DECLARE_KOBJ_TTYPE_MASTER(2);
+DECLARE_KOBJ_TTYPE_NBEAT(2);
+DECLARE_KOBJ_TTYPE_NBYTE(2);
+DECLARE_KOBJ_TTYPE_BURST(2);
+DECLARE_KOBJ_TTYPE_RW(2);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(2);
+
+DECLARE_KOBJ_TTYPE_MASTER(3);
+DECLARE_KOBJ_TTYPE_NBEAT(3);
+DECLARE_KOBJ_TTYPE_NBYTE(3);
+DECLARE_KOBJ_TTYPE_BURST(3);
+DECLARE_KOBJ_TTYPE_RW(3);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(3);
+
+DECLARE_KOBJ_TTYPE_MASTER(4);
+DECLARE_KOBJ_TTYPE_NBEAT(4);
+DECLARE_KOBJ_TTYPE_NBYTE(4);
+DECLARE_KOBJ_TTYPE_BURST(4);
+DECLARE_KOBJ_TTYPE_RW(4);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(4);
+
+DECLARE_KOBJ_TTYPE_MASTER(5);
+DECLARE_KOBJ_TTYPE_NBEAT(5);
+DECLARE_KOBJ_TTYPE_NBYTE(5);
+DECLARE_KOBJ_TTYPE_BURST(5);
+DECLARE_KOBJ_TTYPE_RW(5);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(5);
+
+DECLARE_KOBJ_TTYPE_MASTER(6);
+DECLARE_KOBJ_TTYPE_NBEAT(6);
+DECLARE_KOBJ_TTYPE_NBYTE(6);
+DECLARE_KOBJ_TTYPE_BURST(6);
+DECLARE_KOBJ_TTYPE_RW(6);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(6);
+
+DECLARE_KOBJ_TTYPE_MASTER(7);
+DECLARE_KOBJ_TTYPE_NBEAT(7);
+DECLARE_KOBJ_TTYPE_NBYTE(7);
+DECLARE_KOBJ_TTYPE_BURST(7);
+DECLARE_KOBJ_TTYPE_RW(7);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(7);
+
+DECLARE_KOBJ_TTYPE_MASTER(8);
+DECLARE_KOBJ_TTYPE_NBEAT(8);
+DECLARE_KOBJ_TTYPE_NBYTE(8);
+DECLARE_KOBJ_TTYPE_BURST(8);
+DECLARE_KOBJ_TTYPE_RW(8);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(8);
+
+DECLARE_KOBJ_TTYPE_MASTER(9);
+DECLARE_KOBJ_TTYPE_NBEAT(9);
+DECLARE_KOBJ_TTYPE_NBYTE(9);
+DECLARE_KOBJ_TTYPE_BURST(9);
+DECLARE_KOBJ_TTYPE_RW(9);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(9);
+
+DECLARE_KOBJ_TTYPE_MASTER(10);
+DECLARE_KOBJ_TTYPE_NBEAT(10);
+DECLARE_KOBJ_TTYPE_NBYTE(10);
+DECLARE_KOBJ_TTYPE_BURST(10);
+DECLARE_KOBJ_TTYPE_RW(10);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(10);
+
+DECLARE_KOBJ_TTYPE_MASTER(11);
+DECLARE_KOBJ_TTYPE_NBEAT(11);
+DECLARE_KOBJ_TTYPE_NBYTE(11);
+DECLARE_KOBJ_TTYPE_BURST(11);
+DECLARE_KOBJ_TTYPE_RW(11);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(11);
+
+DECLARE_KOBJ_TTYPE_MASTER(12);
+DECLARE_KOBJ_TTYPE_NBEAT(12);
+DECLARE_KOBJ_TTYPE_NBYTE(12);
+DECLARE_KOBJ_TTYPE_BURST(12);
+DECLARE_KOBJ_TTYPE_RW(12);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(12);
+
+DECLARE_KOBJ_TTYPE_MASTER(13);
+DECLARE_KOBJ_TTYPE_NBEAT(13);
+DECLARE_KOBJ_TTYPE_NBYTE(13);
+DECLARE_KOBJ_TTYPE_BURST(13);
+DECLARE_KOBJ_TTYPE_RW(13);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(13);
+
+DECLARE_KOBJ_TTYPE_MASTER(14);
+DECLARE_KOBJ_TTYPE_NBEAT(14);
+DECLARE_KOBJ_TTYPE_NBYTE(14);
+DECLARE_KOBJ_TTYPE_BURST(14);
+DECLARE_KOBJ_TTYPE_RW(14);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(14);
+
+DECLARE_KOBJ_TTYPE_MASTER(15);
+DECLARE_KOBJ_TTYPE_NBEAT(15);
+DECLARE_KOBJ_TTYPE_NBYTE(15);
+DECLARE_KOBJ_TTYPE_BURST(15);
+DECLARE_KOBJ_TTYPE_RW(15);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(15);
+
+DECLARE_KOBJ_TTYPE_MASTER(16);
+DECLARE_KOBJ_TTYPE_NBEAT(16);
+DECLARE_KOBJ_TTYPE_NBYTE(16);
+DECLARE_KOBJ_TTYPE_BURST(16);
+DECLARE_KOBJ_TTYPE_RW(16);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(16);
+
+DECLARE_KOBJ_TTYPE_MASTER(17);
+DECLARE_KOBJ_TTYPE_NBEAT(17);
+DECLARE_KOBJ_TTYPE_NBYTE(17);
+DECLARE_KOBJ_TTYPE_BURST(17);
+DECLARE_KOBJ_TTYPE_RW(17);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(17);
+
+DECLARE_KOBJ_TTYPE_MASTER(18);
+DECLARE_KOBJ_TTYPE_NBEAT(18);
+DECLARE_KOBJ_TTYPE_NBYTE(18);
+DECLARE_KOBJ_TTYPE_BURST(18);
+DECLARE_KOBJ_TTYPE_RW(18);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(18);
+
+DECLARE_KOBJ_TTYPE_MASTER(19);
+DECLARE_KOBJ_TTYPE_NBEAT(19);
+DECLARE_KOBJ_TTYPE_NBYTE(19);
+DECLARE_KOBJ_TTYPE_BURST(19);
+DECLARE_KOBJ_TTYPE_RW(19);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(19);
+
+DECLARE_KOBJ_TTYPE_MASTER(20);
+DECLARE_KOBJ_TTYPE_NBEAT(20);
+DECLARE_KOBJ_TTYPE_NBYTE(20);
+DECLARE_KOBJ_TTYPE_BURST(20);
+DECLARE_KOBJ_TTYPE_RW(20);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(20);
+
+DECLARE_KOBJ_TTYPE_MASTER(21);
+DECLARE_KOBJ_TTYPE_NBEAT(21);
+DECLARE_KOBJ_TTYPE_NBYTE(21);
+DECLARE_KOBJ_TTYPE_BURST(21);
+DECLARE_KOBJ_TTYPE_RW(21);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(21);
+
+#define KOBJ_ATTR_ITEM_SERIAL_FNODE(nr) \
+	do { \
+		KOBJ_ATTR_ITEM(ttype ## nr ## _master); \
+		KOBJ_ATTR_ITEM(ttype ## nr ## _nbeat); \
+		KOBJ_ATTR_ITEM(ttype ## nr ## _nbyte); \
+		KOBJ_ATTR_ITEM(ttype ## nr ## _burst); \
+		KOBJ_ATTR_ITEM(ttype ## nr ## _busid); \
+		KOBJ_ATTR_ITEM(ttype ## nr ## _rw); \
+	} while (0)
+
+#define KOBJ_ATTR_LIST \
+	do { \
+		KOBJ_ATTR_ITEM(high_priority_filter); \
+		KOBJ_ATTR_ITEM(emi_TP_busfiltr_enable); \
+		KOBJ_ATTR_ITEM(msel_enable); \
+		KOBJ_ATTR_ITEM(msel_group1); \
+		KOBJ_ATTR_ITEM(msel_group2); \
+		KOBJ_ATTR_ITEM(msel_group3); \
+		KOBJ_ATTR_ITEM(ttype17_21_en); \
+		KOBJ_ATTR_ITEM(ttype1_16_en); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(1); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(2); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(3); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(4); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(5); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(6); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(7); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(8); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(9); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(10); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(11); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(12); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(13); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(14); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(15); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(16); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(17); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(18); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(19); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(20); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(21); \
+		KOBJ_ATTR_ITEM(test); \
+	} while (0)
+
+/*======================================================================*/
+/*	EMI Operations							*/
+/*======================================================================*/
+static void emi_init(void)
+{
+	unsigned int bmrw0_val = 0, bmrw1_val = 0, i, enable;
+	unsigned int msel_group_val[4];
+
+	MET_BM_SaveCfg();
+
+	/* get dram channel number */
+	dram_chann_num = MET_EMI_GetDramChannNum();
+
+	if ((ttype1_16_en != BM_TTYPE1_16_ENABLE) && (emi_TP_busfiltr_enable != 1)) {
+		if (msel_enable) {
+			msel_group_val[0] = _ALL;
+			msel_group_val[1] = msel_group1;
+			msel_group_val[2] = msel_group2;
+			msel_group_val[3] = msel_group3;
+		} else {
+			msel_group_val[0] = _ALL;
+			msel_group_val[1] = _ALL;
+			msel_group_val[2] = _ALL;
+			msel_group_val[3] = _ALL;
+		}
+
+		MET_BM_SetLatencyCounter(1);
+
+		for (i = 1; i <= 4; i++) {
+			MET_BM_SetMonitorCounter(i,
+						 msel_group_val[i - 1] & _ALL,
+						 BM_TRANS_TYPE_4BEAT |
+						 BM_TRANS_TYPE_8Byte |
+						 BM_TRANS_TYPE_BURST_WRAP);
+			MET_BM_SetbusID(i, 0);
+			MET_BM_SetbusID_En(i, 0);
+		}
+		for (i = 0; i < 4; i++)
+			MET_BM_Set_WsctTsct_id_sel(i, 0);
+
+	} else if ((ttype1_16_en != BM_TTYPE1_16_ENABLE) && (emi_TP_busfiltr_enable == 1)) {
+		MET_BM_SetLatencyCounter(1);
+
+		for (i = 1; i <= 4; i++) {
+			MET_BM_SetMonitorCounter(i,
+						 ttype_master_val[i - 1],
+						 ttype_nbeat_val[i - 1] |
+						 ttype_nbyte_val[i - 1] |
+						 ttype_burst_val[i - 1]);
+			MET_BM_SetbusID(i, ttype_busid_val[i - 1]);
+			MET_BM_SetbusID_En(i, 0);
+		}
+		for (i = 0; i < 4; i++)
+			MET_BM_Set_WsctTsct_id_sel(i, 1);
+
+	} else if ((ttype1_16_en == BM_TTYPE1_16_ENABLE) && (emi_TP_busfiltr_enable != 1)) {
+		MET_BM_SetLatencyCounter(0);
+
+		for (i = 1; i <= 16; i++) {
+			MET_BM_SetMonitorCounter(i,
+						 ttype_master_val[i - 1],
+						 ttype_nbeat_val[i - 1] |
+						 ttype_nbyte_val[i - 1] |
+						 ttype_burst_val[i - 1]);
+
+			MET_BM_SetbusID(i, ttype_busid_val[i - 1]);
+
+			MET_BM_SetbusID_En(i, (ttype_busid_val[i - 1] > 0xffff) ? 0 : 1);
+		}
+		for (i = 0; i < 4; i++)
+			MET_BM_Set_WsctTsct_id_sel(i, 0);
+	} else {	/* (ttype1_16_en == BM_TTYPE1_16_ENABLE)  &&  (emi_TP_busfiltr_enable == 1) */
+
+		MET_BM_SetLatencyCounter(0);
+
+		for (i = 1; i <= 16; i++) {
+			MET_BM_SetMonitorCounter(i,
+						 ttype_master_val[i - 1],
+						 ttype_nbeat_val[i - 1] |
+						 ttype_nbyte_val[i - 1] |
+						 ttype_burst_val[i - 1]);
+
+			MET_BM_SetbusID(i, ttype_busid_val[i - 1]);
+
+			MET_BM_SetbusID_En(i, (ttype_busid_val[i - 1] > 0xffff) ? 0 : 1);
+		}
+		for (i = 0; i < 4; i++)
+			MET_BM_Set_WsctTsct_id_sel(i, 1);
+	}
+
+	if (ttype17_21_en == BM_TTYPE17_21_ENABLE) {
+		for (i = 17; i <= 21; i++) {
+			MET_BM_SetMonitorCounter(i,
+						 ttype_master_val[i - 1],
+						 ttype_nbeat_val[i - 1] |
+						 ttype_nbyte_val[i - 1] |
+						 ttype_burst_val[i - 1]);
+			MET_BM_SetbusID(i, ttype_busid_val[i - 1]);
+
+			MET_BM_SetbusID_En(i, (ttype_busid_val[i - 1] > 0xffff) ? 0 : 1);
+		}
+	}
+
+	bmrw0_val = (
+		(ttype_rw_val[0] << 0) | (ttype_rw_val[1] << 2) |
+		(ttype_rw_val[2] << 4) | (ttype_rw_val[3] << 6) |
+		(ttype_rw_val[4] << 8) | (ttype_rw_val[5] << 10) |
+		(ttype_rw_val[6] << 12) | (ttype_rw_val[7] << 14) |
+		(ttype_rw_val[8] << 16) | (ttype_rw_val[9] << 18) |
+		(ttype_rw_val[10] << 20) | (ttype_rw_val[11] << 22) |
+		(ttype_rw_val[12] << 24) | (ttype_rw_val[13] << 26) |
+		(ttype_rw_val[14] << 28) | (ttype_rw_val[15] << 30));
+
+	bmrw1_val = (
+		(ttype_rw_val[16] << 0) | (ttype_rw_val[17] << 2) |
+		(ttype_rw_val[18] << 4) | (ttype_rw_val[19] << 6) |
+		(ttype_rw_val[20] << 8));
+
+	MET_BM_SetTtypeCounterRW(bmrw0_val, bmrw1_val);
+
+	for (i = 0; i < BM_COUNTER_MAX; i++) {
+		if ((high_priority_filter & (1 << i)) == 0)
+			enable = 0;
+		else
+			enable = 1;
+
+		MET_BM_SetUltraHighFilter(i + 1, enable);
+	}
+
+}
+
+static inline int do_emi(void)
+{
+	return met_sspm_emi.mode;
+}
+
+/*======================================================================*/
+/*	MET Device Operations						*/
+/*======================================================================*/
+static int emi_inited;
+static int met_emi_create(struct kobject *parent)
+{
+	int ret = 0;
+	int i;
+
+	for (i = 0; i < 21; i++) {
+		ttype_master_val[i] = _M0;
+		ttype_nbeat_val[i] = BM_TRANS_TYPE_1BEAT;
+		ttype_nbyte_val[i] = BM_TRANS_TYPE_8Byte;
+		ttype_burst_val[i] = BM_TRANS_TYPE_BURST_INCR;
+		ttype_busid_val[i] = 0xfffff;
+		ttype_rw_val[i] =  BM_TRANS_RW_DEFAULT;
+	}
+	ret = MET_BM_Init();
+	if (ret != 0) {
+		pr_debug("MET_BM_Init failed!!!\n");
+		ret = 0;
+	} else
+		emi_inited = 1;
+
+	kobj_emi = parent;
+
+#define	KOBJ_ATTR_ITEM(attr_name) \
+do { \
+	ret = sysfs_create_file(kobj_emi, &attr_name##_attr.attr); \
+	if (ret != 0) { \
+		pr_debug("Failed to create " #attr_name " in sysfs\n"); \
+		return ret; \
+	} \
+} while (0)
+	KOBJ_ATTR_LIST;
+#undef	KOBJ_ATTR_ITEM
+
+	return ret;
+}
+
+static void met_emi_delete(void)
+{
+#define	KOBJ_ATTR_ITEM(attr_name) \
+	sysfs_remove_file(kobj_emi, &attr_name##_attr.attr)
+	if (kobj_emi != NULL) {
+		KOBJ_ATTR_LIST;
+		kobj_emi = NULL;
+	}
+#undef	KOBJ_ATTR_ITEM
+
+	if (emi_inited)
+		MET_BM_DeInit();
+}
+
+
+static void met_emi_resume(void)
+{
+	if (!do_emi())
+		return;
+
+	emi_init();
+}
+
+
+#if defined(CONFIG_MTK_TINYSYS_SSPM_SUPPORT) && defined(ONDIEMET_SUPPORT)
+static const char help[] = "  --emi                                 monitor EMI banwidth\n";
+
+#define TTYPE_NAME_STR_LEN  64
+/* static char ttype_name[21][TTYPE_NAME_STR_LEN]; */
+
+static int emi_print_help(char *buf, int len)
+{
+	return snprintf(buf, PAGE_SIZE, help);
+}
+
+static int emi_print_header(char *buf, int len)
+{
+	int ret = 0;
+/*	int ret_m[21]; */
+	int i = 0;
+	unsigned int dram_data_rate_MHz;
+	unsigned int DRAM_TYPE;
+
+	if ((ttype1_16_en != BM_TTYPE1_16_ENABLE) && (emi_TP_busfiltr_enable != 1)) {
+		if (msel_enable) {
+			ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"met-info [000] 0.0: met_emi_msel: %x,%x,%x\n",
+				msel_group1 & _ALL,
+				msel_group2 & _ALL,
+				msel_group3 & _ALL);
+		} else {
+			ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"met-info [000] 0.0: met_emi_msel: %x,%x,%x\n",
+				_ALL & _ALL,
+				_ALL & _ALL,
+				_ALL & _ALL);
+		}
+	} else {
+		ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"met-info [000] 0.0: met_emi_ttype_master: %x,%x,%x,%x\n",
+				ttype_master_val[0], ttype_master_val[1], ttype_master_val[2], ttype_master_val[3]);
+
+		if (emi_TP_busfiltr_enable == 1) {
+
+			ret += snprintf(buf + ret, PAGE_SIZE - ret,
+					"met-info [000] 0.0: met_emi_ttype_busid: %x,%x,%x,%x\n",
+					ttype_busid_val[0], ttype_busid_val[1], ttype_busid_val[2], ttype_busid_val[3]);
+		}
+	}
+
+	ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_emi_rw_cfg: ");
+	ret += snprintf(buf + ret, PAGE_SIZE - ret, "BOTH");
+
+	for (i = 0; i < 21; i++) {
+		if (ttype_rw_val[i] == BM_TRANS_RW_DEFAULT)
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, ",DEFAULT");
+		else if (ttype_rw_val[i] == BM_TRANS_RW_READONLY)
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, ",R");
+		else if (ttype_rw_val[i] == BM_TRANS_RW_WRITEONLY)
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, ",W");
+		else
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, ",BOTH");
+	}
+	ret += snprintf(buf + ret, PAGE_SIZE - ret, "\n");
+
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"met-info [000] 0.0: met_emi_ultra_filter: %x\n", high_priority_filter);
+
+	/* ttype header */
+	if (ttype17_21_en == BM_TTYPE17_21_ENABLE) {
+		int i = 0;
+		int j = 0;
+
+		/* ttype master list */
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_emi_ttype_master_list: ");
+		for (i = 0; i < 21; i++) {
+			for (j = 0; j < ARRAY_SIZE(ttype_master_list_item); j++) {
+				if (ttype_master_val[i] == ttype_master_list_item[j].key) {
+					ret += snprintf(buf + ret, PAGE_SIZE - ret, "%s,", ttype_master_list_item[j].val);
+				}
+			}
+		}
+		/* remove the last comma */
+		snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+		/* ttype busid list */
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_emi_ttype_busid_list: ");
+		for (i = 0; i < 21; i++)
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, "%x,", ttype_busid_val[i]);
+
+		snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+		/* ttype nbeat list */
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_emi_ttype_nbeat_list: ");
+		for (i = 0; i < 21; i++) {
+			for (j = 0; j < ARRAY_SIZE(ttype_nbeat_list_item); j++) {
+				if (ttype_nbeat_val[i] == ttype_nbeat_list_item[j].key) {
+					ret += snprintf(buf + ret, PAGE_SIZE - ret, "%d,", ttype_nbeat_list_item[j].val);
+				}
+			}
+		}
+		snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+		/* ttype nbyte list */
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_emi_ttype_nbyte_list: ");
+		for (i = 0; i < 21; i++) {
+			for (j = 0; j < ARRAY_SIZE(ttype_nbyte_list_item); j++) {
+				if (ttype_nbyte_val[i] == ttype_nbyte_list_item[j].key) {
+					ret += snprintf(buf + ret, PAGE_SIZE - ret, "%d,", ttype_nbyte_list_item[j].val);
+				}
+			}
+		}
+		snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+		/* ttype burst list */
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_emi_ttype_burst_list: ");
+		for (i = 0; i < 21; i++) {
+			for (j = 0; j < ARRAY_SIZE(ttype_burst_list_item); j++) {
+				if (ttype_burst_val[i] == ttype_burst_list_item[j].key) {
+					ret += snprintf(buf + ret, PAGE_SIZE - ret, "%s,", ttype_burst_list_item[j].val);
+				}
+			}
+		}
+		snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+		/* ttype enable */
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_emi_ttype_enable: %d,%d\n",ttype1_16_en, ttype17_21_en);
+	}
+	/*IP version*/
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"met-info [000] 0.0: DRAMC_VER: %d\n", DRAMC_VER);
+
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"met-info [000] 0.0: EMI_VER: %d.%d\n", EMI_VER_MAJOR, EMI_VER_MINOR);
+#if 1 /* move to AP side print header */
+//#ifndef CONFIG_MTK_TINYSYS_SSPM_SUPPORT
+	dram_chann_num = MET_EMI_GetDramChannNum();
+	/*	met_dram_chann_num_header
+	 *	channel number
+	 *	LP4: 2, LP3: 1
+	 */
+
+	/*
+	 *	the ddr type define :
+	 *	enum DDRTYPE {
+	 *	TYPE_LPDDR3 = 1,
+	 *	TYPE_LPDDR4,
+	 *	TYPE_LPDDR4X,
+	 *	TYPE_LPDDR2
+	 *	};
+	 */
+	if (get_ddr_type_symbol) {
+		DRAM_TYPE = get_ddr_type_symbol();
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_dram_type: %d\n", DRAM_TYPE);
+
+		if ((DRAM_TYPE == 2) || (DRAM_TYPE == 3))
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_dram_chann_num_header: %d,%d,%d,%d\n",
+					dram_chann_num, DRAM_EMI_BASECLOCK_RATE_LP4,
+					DRAM_IO_BUS_WIDTH_LP4, DRAM_DATARATE);
+		else
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_dram_chann_num_header: %d,%d,%d,%d\n",
+					dram_chann_num, DRAM_EMI_BASECLOCK_RATE_LP3,
+					DRAM_IO_BUS_WIDTH_LP3, DRAM_DATARATE);
+	} else
+		METERROR("[%s][%d]get_ddr_type_symbol = NULL , use the TYPE_LPDDR3 setting\n", __func__, __LINE__);
+
+
+	/* met_emi_clockrate */
+	if (get_dram_data_rate_symbol) {
+		dram_data_rate_MHz = get_dram_data_rate_symbol();
+	} else {
+		METERROR("get_dram_data_rate_symbol = NULL\n");
+		dram_data_rate_MHz = 0;
+	}
+
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"met-info [000] 0.0: met_dram_clockrate: %d\n",
+			dram_data_rate_MHz);
+
+	/*dram bank num*/
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"met-info [000] 0.0: met_dram_rank_num_header: %u,%u\n", MET_EMI_GetDramRankNum(),
+				MET_EMI_GetDramRankNum());
+#if 0
+	/* ms_emi header */
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"# ms_emi: TS0,TS1,GP0_WSCT,GP1_WSCT,GP2_WSCT,GP3_WSCT,");
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"M0_LATENCY,M1_LATENCY,M2_LATENCY,M3_LATENCY,M4_LATENCY,M5_LATENCY,M6_LATENCY,M7_LATENCY,");
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"M0_TRANS,M1_TRANS,M2_TRANS,M3_TRANS,M4_TRANS,M5_TRANS,M6_TRANS,M7_TRANS,");
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"BACT,BSCT,BCNT,WACT,DCM_CTRL,TACT,");
+
+	for (i = 0; i < dram_chann_num; i++) {
+		if (i != 0)
+			ret += snprintf(buf + ret, PAGE_SIZE - ret,
+					",");
+		ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"PageHit_%d,PageMiss_%d,InterBank_%d,Idle_%d,", i, i, i, i);
+		ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"mr4_%d,refresh_pop_%d,freerun_26m_%d,", i, i, i);
+		ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"read_bytes_%d,write_bytes_%d", i, i);
+	}
+	ret += snprintf(buf + ret, PAGE_SIZE - ret, "\n");
+#endif
+
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"met-info [000] 0.0: met_emi_header: TS0,TS1,GP0_WSCT,GP1_WSCT,GP2_WSCT,GP3_WSCT,");
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"M0_LATENCY,M1_LATENCY,M2_LATENCY,M3_LATENCY,M4_LATENCY,M5_LATENCY,M6_LATENCY,M7_LATENCY,");
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"M0_TRANS,M1_TRANS,M2_TRANS,M3_TRANS,M4_TRANS,M5_TRANS,M6_TRANS,M7_TRANS,");
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"BACT,BSCT,BCNT,WACT,DCM_CTRL,TACT,");
+
+	for (i = 0; i < dram_chann_num; i++) {
+		if (i != 0)
+			ret += snprintf(buf + ret, PAGE_SIZE - ret,
+					",");
+		ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"PageHit_%d,PageMiss_%d,InterBank_%d,Idle_%d,", i, i, i, i);
+		ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"mr4_%d,refresh_pop_%d,freerun_26m_%d,", i, i, i);
+		ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"read_bytes_%d,write_bytes_%d", i, i);
+	}
+	ret += snprintf(buf + ret, PAGE_SIZE - ret, "\n");
+
+	/*TSCT header*/
+	if (emi_tsct_enable == 1) {
+		ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"met-info [000] 0.0: ms_emi_tsct_header: ms_emi_tsct,");
+		ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"tsct1,tsct2,tsct3\n");
+	}
+
+	/*MDCT header*/
+	if (emi_mdct_enable == 1) {
+		ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"met-info [000] 0.0: ms_emi_mdct_header: ms_emi_mdct,");
+		ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"RD_ULTRA,RD_MDMCU\n");
+	}
+
+	/* met_bw_limiter_header */
+	if (bw_limiter_enable == BM_BW_LIMITER_ENABLE) {
+		ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"met-info [000] 0.0: met_bw_limiter_header: CLK,");
+		ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"ARBA,ARBB,ARBC,ARBD,ARBE,ARBF,ARBG,ARBH,BWCT0,BWCT1,BWCT2,BWCT3,BWCT4,BWST0,BWST1,BWCT0_2ND,BWCT1_2ND,BWST_2ND\n");
+	}
+
+	/* DRAM DVFS header */
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"met-info [000] 0.0: DRAM_DVFS_header: datarate(MHz)\n");
+
+	/*PDIR met_dramc_header*/
+	if (dramc_pdir_enable == 1 && DRAMC_VER >= 2 ) {
+		ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"met-info [000] 0.0: met_dramc_header: ");
+		for (i = 0; i < dram_chann_num; i++) {
+			if (i != 0)
+				ret += snprintf(buf + ret, PAGE_SIZE - ret,
+						",");
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, "freerun_26m_%d,", i);
+			ret += snprintf(buf + ret, PAGE_SIZE - ret,
+					"rk0_pre_sb_%d,rk0_pre_pd_%d,rk0_act_sb_%d,rk0_act_pd_%d,", i, i, i, i);
+			ret += snprintf(buf + ret, PAGE_SIZE - ret,
+					"rk1_pre_sb_%d,rk1_pre_pd_%d,rk1_act_sb_%d,rk1_act_pd_%d,", i, i, i, i);
+			ret += snprintf(buf + ret, PAGE_SIZE - ret,
+					"rk2_pre_sb_%d,rk2_pre_pd_%d,rk2_act_sb_%d,rk2_act_pd_%d", i, i, i, i);
+		}
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "\n");
+	}
+
+	/* DRS header */
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"met-info [000] 0.0: emi_drs_header: ch0_RANK1_GP(%%),ch0_RANK1_SF(%%),ch0_ALL_SF(%%),ch1_RANK1_GP(%%),ch1_RANK1_SF(%%),ch1_ALL_SF(%%)\n");
+#endif
+	return ret;
+}
+
+static int ondiemet_emi_print_header(char *buf, int len)
+{
+	return emi_print_header(buf, len);
+}
+
+static void MET_BM_IPI_REGISTER_CB(void)
+{
+	int ret, i;
+	unsigned int rdata;
+	unsigned int ipi_buf[4];
+
+	for (i = 0; i < 4; i++)
+		ipi_buf[i] = 0;
+
+	if (sspm_buf_available == 1) {
+		ipi_buf[0] = MET_MAIN_ID | (MID_EMI << MID_BIT_SHIFT) | MET_ARGU | SET_REGISTER_CB;
+		ret = met_ipi_to_sspm_command((void *)ipi_buf, 0, &rdata, 1);
+	}
+}
+
+static void MET_BM_IPI_configs(void)
+{
+	int ret, i;
+	unsigned int rdata;
+	unsigned int ipi_buf[4];
+
+	for (i = 0; i < 4; i++)
+		ipi_buf[i] = 0;
+
+	if (sspm_buf_available == 1) {
+		ipi_buf[0] = MET_MAIN_ID | (MID_EMI << MID_BIT_SHIFT) | MET_ARGU | SET_EBM_CONFIGS1;
+		ret = met_ipi_to_sspm_command((void *)ipi_buf, 0, &rdata, 1);
+	}
+}
+
+static void ondiemet_emi_start(void)
+{
+	MET_BM_IPI_REGISTER_CB();
+	if (!emi_inited) {
+		if (MET_BM_Init() != 0) {
+			met_sspm_emi.mode = 0;
+			pr_notice("MET_BM_Init failed!!!\n");
+			return;
+		}
+		emi_inited = 1;
+	}
+	MET_BM_IPI_configs();
+
+	if (do_emi())
+		emi_init();
+
+	ondiemet_module[ONDIEMET_SSPM] |= ID_EMI;
+}
+
+static void emi_uninit(void)
+{
+	MET_BM_RestoreCfg();
+}
+
+static void ondiemet_emi_stop(void)
+{
+	if (!emi_inited)
+		return;
+
+	if (do_emi())
+		emi_uninit();
+}
+#endif
+
+struct metdevice met_sspm_emi = {
+	.name			= "emi",
+	.owner			= THIS_MODULE,
+	.type			= MET_TYPE_BUS,
+	.create_subfs		= met_emi_create,
+	.delete_subfs		= met_emi_delete,
+	.resume			= met_emi_resume,
+#if defined(CONFIG_MTK_TINYSYS_SSPM_SUPPORT) && defined(ONDIEMET_SUPPORT)
+	.ondiemet_start		= ondiemet_emi_start,
+	.ondiemet_stop		= ondiemet_emi_stop,
+	.ondiemet_print_help	= emi_print_help,
+	.ondiemet_print_header	= ondiemet_emi_print_header,
+#endif
+	.ondiemet_mode		= 1,
+};
+EXPORT_SYMBOL(met_sspm_emi);
diff --git a/src/devtools/met-driver/4.19/common/emi/SEDA3/mtk_emi_bm.c b/src/devtools/met-driver/4.19/common/emi/SEDA3/mtk_emi_bm.c
new file mode 100644
index 0000000..5cee9b1
--- /dev/null
+++ b/src/devtools/met-driver/4.19/common/emi/SEDA3/mtk_emi_bm.c
@@ -0,0 +1,325 @@
+// SPDX-License-Identifier: GPL-2.0 
+/*
+ * Copyright (C) 2019 MediaTek Inc.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <asm/io.h>
+#include <mt-plat/sync_write.h>
+#include <mt-plat/mtk_io.h>
+/* #include "mtk_typedefs.h" */
+#include "core_plf_init.h"
+#include "mtk_emi_bm.h"
+#include "met_drv.h"
+#include "interface.h"
+#if defined(CONFIG_MTK_TINYSYS_SSPM_SUPPORT) && defined(ONDIEMET_SUPPORT)
+#include "sspm/ondiemet_sspm.h"
+#endif
+
+#undef	DEBUG
+
+#define	emi_readl		readl
+#define	emi_reg_sync_writel	mt_reg_sync_writel
+
+#define MASK_MASTER	0xFF
+#define MASK_TRANS_TYPE	0xFF
+
+static void __iomem *BaseAddrEMI;
+const unsigned int emi_config[] = {
+	EMI_BMEN,
+	EMI_MSEL,
+	EMI_MSEL2,
+	EMI_MSEL3,
+	EMI_MSEL4,
+	EMI_MSEL5,
+	EMI_MSEL6,
+	EMI_MSEL7,
+	EMI_MSEL8,
+	EMI_MSEL9,
+	EMI_MSEL10,
+	EMI_BMID0,
+	EMI_BMID1,
+	EMI_BMID2,
+	EMI_BMID3,
+	EMI_BMID4,
+	EMI_BMID5,
+	EMI_BMID6,
+	EMI_BMID7,
+	EMI_BMID8,
+	EMI_BMID9,
+	EMI_BMID10,
+	EMI_BMEN1,
+	EMI_BMEN2,
+	EMI_BMRW0,
+	EMI_BMRW1
+};
+#define EMI_CONFIG_MX_NR (sizeof(emi_config)/sizeof(unsigned int))
+static unsigned int emi_config_val[EMI_CONFIG_MX_NR];
+
+int MET_BM_Init(void)
+{
+	/*emi*/
+	if (mt_cen_emi_base_get_symbol) {
+		BaseAddrEMI = mt_cen_emi_base_get_symbol();
+	} else {
+		pr_debug("mt_cen_emi_base_get_symbol = NULL\n");
+		PR_BOOTMSG_ONCE("mt_cen_emi_base_get_symbol = NULL\n");
+		BaseAddrEMI = 0;
+	}
+
+	if (BaseAddrEMI == 0) {
+		pr_debug("BaseAddrEMI = 0\n");
+		PR_BOOTMSG_ONCE("BaseAddrEMI = 0\n");
+		return -1;
+	}
+	pr_debug("MET EMI: map emi to %p\n", BaseAddrEMI);
+	PR_BOOTMSG("MET EMI: map emi to %p\n", BaseAddrEMI);
+
+	return 0;
+}
+
+void MET_BM_DeInit(void)
+{
+}
+
+void MET_BM_SaveCfg(void)
+{
+	int i;
+
+	for (i = 0; i < EMI_CONFIG_MX_NR; i++)
+		emi_config_val[i] = emi_readl(IOMEM(ADDR_EMI + emi_config[i]));
+}
+
+void MET_BM_RestoreCfg(void)
+{
+	int i;
+
+	for (i = 0; i < EMI_CONFIG_MX_NR; i++)
+		emi_reg_sync_writel(emi_config_val[i], ADDR_EMI + emi_config[i]);
+}
+
+int MET_BM_SetMonitorCounter(const unsigned int counter_num,
+			     const unsigned int master, const unsigned int trans_type)
+{
+	unsigned int value, addr;
+	const unsigned int iMask = (MASK_TRANS_TYPE << 8) | MASK_MASTER;
+
+	if (counter_num < 1 || counter_num > BM_COUNTER_MAX)
+		return BM_ERR_WRONG_REQ;
+
+
+	if (counter_num == 1) {
+		addr = EMI_BMEN;
+		value = (emi_readl(IOMEM(ADDR_EMI + addr)) & ~(iMask << 16)) |
+			((trans_type & MASK_TRANS_TYPE) << 24) | ((master & MASK_MASTER) << 16);
+	} else {
+		addr = (counter_num <= 3) ? EMI_MSEL : (EMI_MSEL2 + (counter_num / 2 - 2) * 8);
+
+
+		value = emi_readl(IOMEM(ADDR_EMI + addr)) & ~(iMask << ((counter_num % 2) * 16));
+
+
+		value |= (((trans_type & MASK_TRANS_TYPE) << 8) |
+			  (master & MASK_MASTER)) << ((counter_num % 2) * 16);
+	}
+
+	emi_reg_sync_writel(value, ADDR_EMI + addr);
+
+	return BM_REQ_OK;
+}
+
+int MET_BM_SetTtypeCounterRW(unsigned int bmrw0_val, unsigned int bmrw1_val)
+{
+
+	unsigned int value_origin;
+
+	value_origin = emi_readl(IOMEM(ADDR_EMI + EMI_BMRW0));
+	MET_TRACE("[MET_EMI_settype1] value_origin: %x\n", value_origin);
+	if (value_origin != bmrw0_val) {
+		emi_reg_sync_writel(bmrw0_val, ADDR_EMI + EMI_BMRW0);
+		MET_TRACE("[MET_EMI_settype1] bmrw0_val: %x, value_origin: %x\n", bmrw0_val,
+			   value_origin);
+	}
+
+	value_origin = emi_readl(IOMEM(ADDR_EMI + EMI_BMRW1));
+	MET_TRACE("[MET_EMI_settype2] value_origin: %x\n", value_origin);
+	if (value_origin != bmrw1_val) {
+		emi_reg_sync_writel(bmrw1_val, ADDR_EMI + EMI_BMRW1);
+		MET_TRACE("[MET_EMI_settype2] bmrw0_val: %x, value_origin: %x\n", bmrw1_val,
+			   value_origin);
+
+	}
+	return BM_REQ_OK;
+}
+
+int MET_BM_Set_WsctTsct_id_sel(unsigned int counter_num, unsigned int enable)
+{
+	unsigned int value;
+
+	if (counter_num > 3)
+		return BM_ERR_WRONG_REQ;
+
+	value =
+	    ((emi_readl(IOMEM(ADDR_EMI + EMI_BMEN2)) & (~(1 << (28 + counter_num)))) |
+	     (enable << (28 + counter_num)));
+	emi_reg_sync_writel(value, ADDR_EMI + EMI_BMEN2);
+
+	return BM_REQ_OK;
+}
+
+int MET_BM_SetbusID_En(const unsigned int counter_num,
+		       const unsigned int enable)
+{
+	unsigned int value;
+
+	if ((counter_num < 1 || counter_num > BM_COUNTER_MAX) || (enable > 1))
+		return BM_ERR_WRONG_REQ;
+
+	if (enable == 0) {
+
+		value = (emi_readl(IOMEM(ADDR_EMI + EMI_BMEN2))
+			 & ~(1 << (counter_num - 1)));
+	} else {
+
+		value = (emi_readl(IOMEM(ADDR_EMI + EMI_BMEN2))
+			 | (1 << (counter_num - 1)));
+	}
+	emi_reg_sync_writel(value, ADDR_EMI + EMI_BMEN2);
+
+	return BM_REQ_OK;
+}
+
+int MET_BM_SetbusID(const unsigned int counter_num,
+		    const unsigned int id)
+{
+	unsigned int value, addr, shift_num;
+
+	if ((counter_num < 1 || counter_num > BM_COUNTER_MAX))
+		return BM_ERR_WRONG_REQ;
+
+
+	addr = EMI_BMID0 + (counter_num - 1) / 2 * 4;
+	shift_num = ((counter_num - 1) % 2) * 16;
+
+	value = emi_readl(IOMEM(ADDR_EMI + addr)) & ~(EMI_BMID_MASK << shift_num);
+
+
+	if (id <= 0xffff)
+		value |= id << shift_num;
+
+	emi_reg_sync_writel(value, ADDR_EMI + addr);
+
+	return BM_REQ_OK;
+}
+
+int MET_BM_SetUltraHighFilter(const unsigned int counter_num, const unsigned int enable)
+{
+	unsigned int value;
+
+	if ((counter_num < 1 || counter_num > BM_COUNTER_MAX) || (enable > 1))
+		return BM_ERR_WRONG_REQ;
+
+
+	value = (emi_readl(IOMEM(ADDR_EMI + EMI_BMEN1))
+		 & ~(1 << (counter_num - 1)))
+		| (enable << (counter_num - 1));
+
+	emi_reg_sync_writel(value, ADDR_EMI + EMI_BMEN1);
+
+	return BM_REQ_OK;
+}
+
+int MET_BM_SetLatencyCounter(unsigned int enable)
+{
+	unsigned int value;
+
+	value = emi_readl(IOMEM(ADDR_EMI + EMI_BMEN2)) & ~(0x3 << 24);
+	if (enable == 1)
+		value |= (0x2 << 24);
+
+	emi_reg_sync_writel(value, ADDR_EMI + EMI_BMEN2);
+
+	return BM_REQ_OK;
+}
+
+unsigned int MET_EMI_GetDramChannNum(void)
+{
+	int num = -1;
+
+	if (BaseAddrEMI) {
+		num = emi_readl(IOMEM(ADDR_EMI + EMI_CONA));
+		num = ((num >> 8) & 0x0000003);
+	} else {
+		return 1;
+	}
+
+	if (num == M0_DOUBLE_HALF_BW_1CH)
+		return 1;
+	else if (num == M0_DOUBLE_HALF_BW_2CH)
+		return 2;
+	else if (num == M0_DOUBLE_HALF_BW_4CH)
+		return 4;
+	else                    /* default return single channel */
+		return 1;
+}
+
+
+unsigned int MET_EMI_GetDramRankNum(void)
+{
+	int dual_rank = 0;
+
+	if (BaseAddrEMI) {
+		dual_rank = emi_readl(IOMEM(ADDR_EMI + EMI_CONA));
+		dual_rank = ((dual_rank >> 17) & RANK_MASK);
+	} else {
+		return DUAL_RANK;
+	}
+
+	if (dual_rank == DISABLE_DUAL_RANK_MODE)
+		return ONE_RANK;
+	else			/* default return dual rank */
+		return DUAL_RANK;
+}
+
+
+unsigned int MET_EMI_GetDramRankNum_CHN1(void)
+{
+	int dual_rank = 0;
+
+	if (BaseAddrEMI) {
+		dual_rank = emi_readl(IOMEM(ADDR_EMI + EMI_CONA));
+		dual_rank = ((dual_rank >> 16) & RANK_MASK);
+	} else {
+		return DUAL_RANK;
+	}
+
+	if (dual_rank == DISABLE_DUAL_RANK_MODE)
+		return ONE_RANK;
+	else			/* default return dual rank */
+		return DUAL_RANK;
+}
+
+unsigned int MET_EMI_Get_BaseClock_Rate(void)
+{
+	unsigned int DRAM_TYPE;
+
+	if (get_cur_ddr_ratio_symbol)
+		return get_cur_ddr_ratio_symbol();
+	else {
+
+		if (get_ddr_type_symbol) {	
+			DRAM_TYPE = get_ddr_type_symbol();
+
+			if ((DRAM_TYPE == 2) || (DRAM_TYPE == 3))
+				return DRAM_EMI_BASECLOCK_RATE_LP4;
+			else
+				return DRAM_EMI_BASECLOCK_RATE_LP3;
+
+		} else {
+			return DRAM_EMI_BASECLOCK_RATE_LP4;
+		}
+	}
+}
\ No newline at end of file
diff --git a/src/devtools/met-driver/4.19/common/emi/SEDA3/mtk_emi_bm.h b/src/devtools/met-driver/4.19/common/emi/SEDA3/mtk_emi_bm.h
new file mode 100644
index 0000000..e0dc218
--- /dev/null
+++ b/src/devtools/met-driver/4.19/common/emi/SEDA3/mtk_emi_bm.h
@@ -0,0 +1,180 @@
+/*  SPDX-License-Identifier: GPL-2.0 */  
+/*
+ * Copyright (C) 2019 MediaTek Inc.
+ *
+ */
+
+#ifndef __MT_MET_EMI_BM_H__
+#define __MT_MET_EMI_BM_H__
+
+#define EMI_VER_MAJOR  3
+#define EMI_VER_MINOR  0
+
+
+#define DRAM_EMI_BASECLOCK_RATE_LP4     4
+#define DRAM_EMI_BASECLOCK_RATE_LP3     2
+
+#define DRAM_IO_BUS_WIDTH_LP4           16
+#define DRAM_IO_BUS_WIDTH_LP3           32
+
+#define DRAM_DATARATE   2
+
+#define	ADDR_EMI		((unsigned long) BaseAddrEMI)
+
+/*========================================================*/
+/*EMI configuration by project*/
+/*Change config start*/
+/*========================================================*/
+#define _GP_1_Default	(_M0 | _M1)
+#define _GP_2_Default	(_M2 | _M5)
+#define _GP_3_Default	(_M6 | _M7)
+
+
+/*========================================================*/
+/*Change config end*/
+/*========================================================*/
+
+
+#define _M0		(0x01)
+#define _M1		(0x02)
+#define _M2		(0x04)
+#define _M3		(0x08)
+#define _M4		(0x10)
+#define _M5		(0x20)
+#define _M6		(0x40)
+#define _M7		(0x80)
+#define _ALL	(0xFF)
+
+enum BM_RW_Type {
+	BM_BOTH_READ_WRITE,
+	BM_READ_ONLY,
+	BM_WRITE_ONLY
+};
+
+enum {
+	BM_TRANS_TYPE_1BEAT = 0x0,
+	BM_TRANS_TYPE_2BEAT,
+	BM_TRANS_TYPE_3BEAT,
+	BM_TRANS_TYPE_4BEAT,
+	BM_TRANS_TYPE_5BEAT,
+	BM_TRANS_TYPE_6BEAT,
+	BM_TRANS_TYPE_7BEAT,
+	BM_TRANS_TYPE_8BEAT,
+	BM_TRANS_TYPE_9BEAT,
+	BM_TRANS_TYPE_10BEAT,
+	BM_TRANS_TYPE_11BEAT,
+	BM_TRANS_TYPE_12BEAT,
+	BM_TRANS_TYPE_13BEAT,
+	BM_TRANS_TYPE_14BEAT,
+	BM_TRANS_TYPE_15BEAT,
+	BM_TRANS_TYPE_16BEAT,
+	BM_TRANS_TYPE_1Byte = 0 << 4,
+	BM_TRANS_TYPE_2Byte = 1 << 4,
+	BM_TRANS_TYPE_4Byte = 2 << 4,
+	BM_TRANS_TYPE_8Byte = 3 << 4,
+	BM_TRANS_TYPE_16Byte = 4 << 4,
+	BM_TRANS_TYPE_32Byte = 5 << 4,
+	BM_TRANS_TYPE_BURST_WRAP = 0 << 7,
+	BM_TRANS_TYPE_BURST_INCR = 1 << 7
+};
+
+enum {
+	BM_TRANS_RW_DEFAULT = 0x0,
+	BM_TRANS_RW_READONLY,
+	BM_TRANS_RW_WRITEONLY,
+	BM_TRANS_RW_RWBOTH
+};
+
+
+#define EMI_BMID_MASK				(0xFFFF)
+#define BM_COUNTER_MAX				(21)
+
+#define BM_REQ_OK						(0)
+#define BM_ERR_WRONG_REQ				(-1)
+#define BM_ERR_OVERRUN					(-2)
+
+#define BM_TTYPE1_16_ENABLE			(0)
+#define BM_TTYPE1_16_DISABLE			(-1)
+#define BM_TTYPE17_21_ENABLE			(0)
+#define BM_TTYPE17_21_DISABLE			(-1)
+
+#define BM_BW_LIMITER_ENABLE			(0)
+#define BM_BW_LIMITER_DISABLE			(-1)
+
+#define M0_DOUBLE_HALF_BW_1CH	(0x0)
+#define M0_DOUBLE_HALF_BW_2CH	(0x1)
+#define M0_DOUBLE_HALF_BW_4CH	(0x2)
+
+/* EMI Rank configuration */
+enum {
+	DISABLE_DUAL_RANK_MODE = 0,
+	ENABLE_DUAL_RANK_MODE,
+};
+
+#define RANK_MASK 0x1
+#define ONE_RANK 1
+#define DUAL_RANK 2
+
+#if defined(CONFIG_MTK_TINYSYS_SSPM_SUPPORT) && defined(ONDIEMET_SUPPORT)
+enum BM_EMI_IPI_Type {
+	SET_BASE_EMI = 0x0,
+	SET_EBM_CONFIGS1 = 0x7,
+	SET_EBM_CONFIGS2 = 0x8,
+	SET_REGISTER_CB = 0x9,
+};
+#endif
+
+#define	EMI_OFF			0x0000
+#define EMI_CONA		(0x000-EMI_OFF)
+#define EMI_BMEN		(0x400-EMI_OFF)
+#define EMI_MSEL		(0x440-EMI_OFF)
+#define EMI_MSEL2		(0x468-EMI_OFF)
+#define EMI_MSEL3		(0x470-EMI_OFF)
+#define EMI_MSEL4		(0x478-EMI_OFF)
+#define EMI_MSEL5		(0x480-EMI_OFF)
+#define EMI_MSEL6		(0x488-EMI_OFF)
+#define EMI_MSEL7		(0x490-EMI_OFF)
+#define EMI_MSEL8		(0x498-EMI_OFF)
+#define EMI_MSEL9		(0x4A0-EMI_OFF)
+#define EMI_MSEL10		(0x4A8-EMI_OFF)
+
+#define EMI_BMID0		(0x4B0-EMI_OFF)
+#define EMI_BMID1		(0x4B4-EMI_OFF)
+#define EMI_BMID2		(0x4B8-EMI_OFF)
+#define EMI_BMID3		(0x4BC-EMI_OFF)
+#define EMI_BMID4		(0x4C0-EMI_OFF)
+#define EMI_BMID5		(0x4C4-EMI_OFF)
+#define EMI_BMID6		(0x4C8-EMI_OFF)
+#define EMI_BMID7		(0x4CC-EMI_OFF)
+#define EMI_BMID8		(0x4D0-EMI_OFF)
+#define EMI_BMID9		(0x4D4-EMI_OFF)
+#define EMI_BMID10		(0x4D8-EMI_OFF)
+
+#define EMI_BMEN1		(0x4E0-EMI_OFF)
+#define EMI_BMEN2		(0x4E8-EMI_OFF)
+#define EMI_BMRW0		(0x4F8-EMI_OFF)
+#define EMI_BMRW1		(0x4FC-EMI_OFF)
+
+
+extern unsigned int MET_EMI_GetDramRankNum(void);
+extern unsigned int MET_EMI_GetDramRankNum_CHN1(void);
+
+
+unsigned int MET_EMI_GetDramChannNum(void);
+
+extern int MET_BM_Init(void);
+extern void MET_BM_DeInit(void);
+extern void MET_BM_SaveCfg(void);
+extern void MET_BM_RestoreCfg(void);
+extern int MET_BM_SetMonitorCounter(const unsigned int counter_num,
+				    const unsigned int master, const unsigned int trans_type);
+extern int MET_BM_SetTtypeCounterRW(unsigned int bmrw0_val, unsigned int bmrw1_val);
+extern int MET_BM_Set_WsctTsct_id_sel(unsigned int counter_num, unsigned int enable);
+extern int MET_BM_SetbusID_En(const unsigned int counter_num,
+			      const unsigned int enable);
+extern int MET_BM_SetbusID(const unsigned int counter_num,
+			   const unsigned int id);
+extern int MET_BM_SetUltraHighFilter(const unsigned int counter_num, const unsigned int enable);
+extern int MET_BM_SetLatencyCounter(unsigned int enable);
+extern unsigned int MET_EMI_Get_BaseClock_Rate(void);
+#endif				/* !__MT_MET_EMI_BM_H__ */
diff --git a/src/devtools/met-driver/4.19/common/emi/SEDA3_5/met_emi.c b/src/devtools/met-driver/4.19/common/emi/SEDA3_5/met_emi.c
new file mode 100644
index 0000000..f1b890f
--- /dev/null
+++ b/src/devtools/met-driver/4.19/common/emi/SEDA3_5/met_emi.c
@@ -0,0 +1,2266 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2019 MediaTek Inc.
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/device.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/dma-mapping.h>
+#include <linux/string.h>
+
+#define MET_USER_EVENT_SUPPORT
+#include "met_drv.h"
+#include "trace.h"
+
+#include "mtk_typedefs.h"
+#include "core_plf_init.h"
+#include "mtk_emi_bm.h"
+#include "interface.h"
+#include "met_dramc.h"
+
+#if defined(CONFIG_MTK_TINYSYS_SSPM_SUPPORT) && defined(ONDIEMET_SUPPORT)
+#include "sspm/ondiemet_sspm.h"
+#endif
+
+/* #define FILE_NODE_DBG */
+
+/*======================================================================*/
+/*	Global variable definitions					*/
+/*======================================================================*/
+/*ondiemet emi sampling interval in us */
+int emi_tsct_enable = 1;
+int emi_mdct_enable = 1;
+int emi_TP_busfiltr_enable;
+
+
+/* Dynamic MonitorCounter selection !!!EXPERIMENT!!! */
+static int msel_enable;
+static unsigned int msel_group1 = BM_MASTER_ALL;
+static unsigned int msel_group2 = BM_MASTER_ALL;
+static unsigned int msel_group3 = BM_MASTER_ALL;
+
+
+/* Global variables */
+static struct kobject *kobj_emi;
+static int rwtype = BM_BOTH_READ_WRITE;
+
+/* BW Limiter */
+/*#define CNT_COUNTDOWN	(1000-1)*/		/* 1000 * 1ms = 1sec */
+#define CNT_COUNTDOWN   (0)                     /* 1ms */
+/* static int countdown; */
+static int bw_limiter_enable = BM_BW_LIMITER_ENABLE;
+
+/* TTYPE counter */
+static int ttype1_16_en = BM_TTYPE1_16_DISABLE;
+static int ttype17_21_en = BM_TTYPE17_21_DISABLE;
+
+static int dramc_pdir_enable;
+static int dram_chann_num = 1;
+
+enum SSPM_Mode {
+	CUSTOMER_MODE = 0x0,
+	UNDEFINE_MODE = 0x1,
+	INTERNAL_MODE = 0X2780
+};
+
+
+
+/*======================================================================*/
+/*	KOBJ Declarations						*/
+/*======================================================================*/
+
+
+DECLARE_KOBJ_ATTR_INT(emi_TP_busfiltr_enable, emi_TP_busfiltr_enable);
+
+
+DECLARE_KOBJ_ATTR_INT(msel_enable, msel_enable);
+DECLARE_KOBJ_ATTR_HEX_CHECK(msel_group1, msel_group1, msel_group1 > 0 && msel_group1 <= BM_MASTER_ALL);
+DECLARE_KOBJ_ATTR_HEX_CHECK(msel_group2, msel_group2, msel_group2 > 0 && msel_group2 <= BM_MASTER_ALL);
+DECLARE_KOBJ_ATTR_HEX_CHECK(msel_group3, msel_group3, msel_group3 > 0 && msel_group3 <= BM_MASTER_ALL);
+
+
+
+/* KOBJ: rwtype */
+DECLARE_KOBJ_ATTR_INT_CHECK(rwtype, rwtype, rwtype >= 0 && rwtype <= BM_WRITE_ONLY);
+
+/*
+static unsigned int get_emi_clock_rate(unsigned int dram_data_rate_MHz)
+{
+	unsigned int DRAM_TYPE;
+
+	if (get_ddr_type_symbol) {
+		DRAM_TYPE = get_ddr_type_symbol();
+
+		if ((DRAM_TYPE == 2) || (DRAM_TYPE == 3))
+			return dram_data_rate_MHz / DRAM_EMI_BASECLOCK_RATE_LP4 / DRAM_DATARATE;
+		else
+			return dram_data_rate_MHz / DRAM_EMI_BASECLOCK_RATE_LP3 / DRAM_DATARATE;
+	} else {
+		METERROR("[%s][%d]get_ddr_type_symbol = NULL , use the TYPE_LPDDR3 setting\n", __func__, __LINE__);
+		return dram_data_rate_MHz / DRAM_EMI_BASECLOCK_RATE_LP3 / DRAM_DATARATE;
+	}
+}
+*/
+
+/* KOBJ: ttype1_16_en */
+DECLARE_KOBJ_ATTR_STR_LIST_ITEM(
+	ttype1_16_en,
+	KOBJ_ITEM_LIST(
+		{ BM_TTYPE1_16_ENABLE,   "ENABLE" },
+		{ BM_TTYPE1_16_DISABLE,  "DISABLE" }
+		)
+	);
+DECLARE_KOBJ_ATTR_STR_LIST(ttype1_16_en, ttype1_16_en, ttype1_16_en);
+
+/* KOBJ: ttype17_21_en */
+DECLARE_KOBJ_ATTR_STR_LIST_ITEM(
+	ttype17_21_en,
+	KOBJ_ITEM_LIST(
+		{ BM_TTYPE17_21_ENABLE,  "ENABLE" },
+		{ BM_TTYPE17_21_DISABLE, "DISABLE" }
+		)
+	);
+DECLARE_KOBJ_ATTR_STR_LIST(ttype17_21_en, ttype17_21_en, ttype17_21_en);
+
+/* KOBJ: bw_limiter_enable */
+DECLARE_KOBJ_ATTR_STR_LIST_ITEM(
+	bw_limiter_enable,
+	KOBJ_ITEM_LIST(
+		{ BM_BW_LIMITER_ENABLE,  "ENABLE" },
+		{ BM_BW_LIMITER_DISABLE, "DISABLE" }
+		)
+	);
+
+DECLARE_KOBJ_ATTR_STR_LIST(bw_limiter_enable, bw_limiter_enable, bw_limiter_enable);
+
+/* KOBJ: ttype_master */
+DECLARE_KOBJ_ATTR_STR_LIST_ITEM(
+	ttype_master,
+	KOBJ_ITEM_LIST(
+		{ BM_MASTER_M0,  "M0" },
+		{ BM_MASTER_M1,  "M1" },
+		{ BM_MASTER_M2,  "M2" },
+		{ BM_MASTER_M3,  "M3" },
+		{ BM_MASTER_M4,  "M4" },
+		{ BM_MASTER_M5,  "M5" },
+		{ BM_MASTER_M6,  "M6" },
+		{ BM_MASTER_M7,  "M7" }
+		)
+	);
+
+
+/* KOBJ: ttypeX_nbeat, ttypeX_nbyte, ttypeX_burst */
+DECLARE_KOBJ_ATTR_INT_LIST_ITEM(
+	ttype_nbeat,
+	KOBJ_ITEM_LIST(
+		{ BM_TRANS_TYPE_1BEAT,   1 },
+		{ BM_TRANS_TYPE_2BEAT,   2 },
+		{ BM_TRANS_TYPE_3BEAT,   3 },
+		{ BM_TRANS_TYPE_4BEAT,   4 },
+		{ BM_TRANS_TYPE_5BEAT,   5 },
+		{ BM_TRANS_TYPE_6BEAT,   6 },
+		{ BM_TRANS_TYPE_7BEAT,   7 },
+		{ BM_TRANS_TYPE_8BEAT,   8 },
+		{ BM_TRANS_TYPE_9BEAT,   9 },
+		{ BM_TRANS_TYPE_10BEAT,  10 },
+		{ BM_TRANS_TYPE_11BEAT,  11 },
+		{ BM_TRANS_TYPE_12BEAT,  12 },
+		{ BM_TRANS_TYPE_13BEAT,  13 },
+		{ BM_TRANS_TYPE_14BEAT,  14 },
+		{ BM_TRANS_TYPE_15BEAT,  15 },
+		{ BM_TRANS_TYPE_16BEAT,  16 }
+		)
+	);
+DECLARE_KOBJ_ATTR_INT_LIST_ITEM(
+	ttype_nbyte,
+	KOBJ_ITEM_LIST(
+		{ BM_TRANS_TYPE_1Byte,   1 },
+		{ BM_TRANS_TYPE_2Byte,   2 },
+		{ BM_TRANS_TYPE_4Byte,   4 },
+		{ BM_TRANS_TYPE_8Byte,   8 },
+		{ BM_TRANS_TYPE_16Byte,  16 },
+		{ BM_TRANS_TYPE_32Byte,  32 }
+		)
+	);
+DECLARE_KOBJ_ATTR_STR_LIST_ITEM(
+	ttype_burst,
+	KOBJ_ITEM_LIST(
+		{ BM_TRANS_TYPE_BURST_INCR,      "INCR" },
+		{ BM_TRANS_TYPE_BURST_WRAP,      "WRAP" }
+		)
+	);
+
+DECLARE_KOBJ_ATTR_STR_LIST_ITEM(
+	ttype_rw,
+	KOBJ_ITEM_LIST(
+		{ BM_TRANS_RW_DEFAULT,   "DEFAULT" },
+		{ BM_TRANS_RW_READONLY,  "R" },
+		{ BM_TRANS_RW_WRITEONLY, "W" },
+		{ BM_TRANS_RW_RWBOTH,    "BOTH" }
+		)
+	);
+
+
+DECLARE_KOBJ_ATTR_INT(dramc_pdir_enable, dramc_pdir_enable);
+
+/*enable high priority filter*/
+static int high_priority_filter;
+DECLARE_KOBJ_ATTR_HEX(high_priority_filter, high_priority_filter);
+
+
+
+/**/
+static int ttype_master_val[21];
+static int ttype_busid_val[21];
+static int ttype_nbeat_val[21];
+static int ttype_nbyte_val[21];
+static int ttype_burst_val[21];
+static int ttype_rw_val[21];
+
+#define DECLARE_KOBJ_TTYPE_MASTER(nr) \
+	DECLARE_KOBJ_ATTR_STR_LIST(ttype ## nr ## _master, ttype_master_val[nr - 1], ttype_master)
+
+#define DECLARE_KOBJ_TTYPE_NBEAT(nr) \
+	DECLARE_KOBJ_ATTR_INT_LIST(ttype ## nr ## _nbeat, ttype_nbeat_val[nr - 1], ttype_nbeat)
+
+#define DECLARE_KOBJ_TTYPE_NBYTE(nr) \
+	DECLARE_KOBJ_ATTR_INT_LIST(ttype ## nr ## _nbyte, ttype_nbyte_val[nr - 1], ttype_nbyte)
+
+#define DECLARE_KOBJ_TTYPE_BURST(nr) \
+	DECLARE_KOBJ_ATTR_STR_LIST(ttype ## nr ## _burst, ttype_burst_val[nr - 1], ttype_burst)
+
+#define DECLARE_KOBJ_TTYPE_RW(nr) \
+	DECLARE_KOBJ_ATTR_STR_LIST(ttype ## nr ## _rw, ttype_rw_val[nr - 1], ttype_rw)
+
+#define DECLARE_KOBJ_TTYPE_BUSID_VAL(nr) \
+	DECLARE_KOBJ_ATTR_HEX(ttype ## nr ## _busid, ttype_busid_val[nr - 1])
+
+DECLARE_KOBJ_TTYPE_MASTER(1);
+DECLARE_KOBJ_TTYPE_NBEAT(1);
+DECLARE_KOBJ_TTYPE_NBYTE(1);
+DECLARE_KOBJ_TTYPE_BURST(1);
+DECLARE_KOBJ_TTYPE_RW(1);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(1);
+
+DECLARE_KOBJ_TTYPE_MASTER(2);
+DECLARE_KOBJ_TTYPE_NBEAT(2);
+DECLARE_KOBJ_TTYPE_NBYTE(2);
+DECLARE_KOBJ_TTYPE_BURST(2);
+DECLARE_KOBJ_TTYPE_RW(2);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(2);
+
+DECLARE_KOBJ_TTYPE_MASTER(3);
+DECLARE_KOBJ_TTYPE_NBEAT(3);
+DECLARE_KOBJ_TTYPE_NBYTE(3);
+DECLARE_KOBJ_TTYPE_BURST(3);
+DECLARE_KOBJ_TTYPE_RW(3);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(3);
+
+DECLARE_KOBJ_TTYPE_MASTER(4);
+DECLARE_KOBJ_TTYPE_NBEAT(4);
+DECLARE_KOBJ_TTYPE_NBYTE(4);
+DECLARE_KOBJ_TTYPE_BURST(4);
+DECLARE_KOBJ_TTYPE_RW(4);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(4);
+
+DECLARE_KOBJ_TTYPE_MASTER(5);
+DECLARE_KOBJ_TTYPE_NBEAT(5);
+DECLARE_KOBJ_TTYPE_NBYTE(5);
+DECLARE_KOBJ_TTYPE_BURST(5);
+DECLARE_KOBJ_TTYPE_RW(5);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(5);
+
+DECLARE_KOBJ_TTYPE_MASTER(6);
+DECLARE_KOBJ_TTYPE_NBEAT(6);
+DECLARE_KOBJ_TTYPE_NBYTE(6);
+DECLARE_KOBJ_TTYPE_BURST(6);
+DECLARE_KOBJ_TTYPE_RW(6);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(6);
+
+DECLARE_KOBJ_TTYPE_MASTER(7);
+DECLARE_KOBJ_TTYPE_NBEAT(7);
+DECLARE_KOBJ_TTYPE_NBYTE(7);
+DECLARE_KOBJ_TTYPE_BURST(7);
+DECLARE_KOBJ_TTYPE_RW(7);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(7);
+
+DECLARE_KOBJ_TTYPE_MASTER(8);
+DECLARE_KOBJ_TTYPE_NBEAT(8);
+DECLARE_KOBJ_TTYPE_NBYTE(8);
+DECLARE_KOBJ_TTYPE_BURST(8);
+DECLARE_KOBJ_TTYPE_RW(8);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(8);
+
+DECLARE_KOBJ_TTYPE_MASTER(9);
+DECLARE_KOBJ_TTYPE_NBEAT(9);
+DECLARE_KOBJ_TTYPE_NBYTE(9);
+DECLARE_KOBJ_TTYPE_BURST(9);
+DECLARE_KOBJ_TTYPE_RW(9);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(9);
+
+DECLARE_KOBJ_TTYPE_MASTER(10);
+DECLARE_KOBJ_TTYPE_NBEAT(10);
+DECLARE_KOBJ_TTYPE_NBYTE(10);
+DECLARE_KOBJ_TTYPE_BURST(10);
+DECLARE_KOBJ_TTYPE_RW(10);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(10);
+
+DECLARE_KOBJ_TTYPE_MASTER(11);
+DECLARE_KOBJ_TTYPE_NBEAT(11);
+DECLARE_KOBJ_TTYPE_NBYTE(11);
+DECLARE_KOBJ_TTYPE_BURST(11);
+DECLARE_KOBJ_TTYPE_RW(11);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(11);
+
+DECLARE_KOBJ_TTYPE_MASTER(12);
+DECLARE_KOBJ_TTYPE_NBEAT(12);
+DECLARE_KOBJ_TTYPE_NBYTE(12);
+DECLARE_KOBJ_TTYPE_BURST(12);
+DECLARE_KOBJ_TTYPE_RW(12);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(12);
+
+DECLARE_KOBJ_TTYPE_MASTER(13);
+DECLARE_KOBJ_TTYPE_NBEAT(13);
+DECLARE_KOBJ_TTYPE_NBYTE(13);
+DECLARE_KOBJ_TTYPE_BURST(13);
+DECLARE_KOBJ_TTYPE_RW(13);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(13);
+
+DECLARE_KOBJ_TTYPE_MASTER(14);
+DECLARE_KOBJ_TTYPE_NBEAT(14);
+DECLARE_KOBJ_TTYPE_NBYTE(14);
+DECLARE_KOBJ_TTYPE_BURST(14);
+DECLARE_KOBJ_TTYPE_RW(14);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(14);
+
+DECLARE_KOBJ_TTYPE_MASTER(15);
+DECLARE_KOBJ_TTYPE_NBEAT(15);
+DECLARE_KOBJ_TTYPE_NBYTE(15);
+DECLARE_KOBJ_TTYPE_BURST(15);
+DECLARE_KOBJ_TTYPE_RW(15);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(15);
+
+DECLARE_KOBJ_TTYPE_MASTER(16);
+DECLARE_KOBJ_TTYPE_NBEAT(16);
+DECLARE_KOBJ_TTYPE_NBYTE(16);
+DECLARE_KOBJ_TTYPE_BURST(16);
+DECLARE_KOBJ_TTYPE_RW(16);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(16);
+
+DECLARE_KOBJ_TTYPE_MASTER(17);
+DECLARE_KOBJ_TTYPE_NBEAT(17);
+DECLARE_KOBJ_TTYPE_NBYTE(17);
+DECLARE_KOBJ_TTYPE_BURST(17);
+DECLARE_KOBJ_TTYPE_RW(17);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(17);
+
+DECLARE_KOBJ_TTYPE_MASTER(18);
+DECLARE_KOBJ_TTYPE_NBEAT(18);
+DECLARE_KOBJ_TTYPE_NBYTE(18);
+DECLARE_KOBJ_TTYPE_BURST(18);
+DECLARE_KOBJ_TTYPE_RW(18);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(18);
+
+DECLARE_KOBJ_TTYPE_MASTER(19);
+DECLARE_KOBJ_TTYPE_NBEAT(19);
+DECLARE_KOBJ_TTYPE_NBYTE(19);
+DECLARE_KOBJ_TTYPE_BURST(19);
+DECLARE_KOBJ_TTYPE_RW(19);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(19);
+
+DECLARE_KOBJ_TTYPE_MASTER(20);
+DECLARE_KOBJ_TTYPE_NBEAT(20);
+DECLARE_KOBJ_TTYPE_NBYTE(20);
+DECLARE_KOBJ_TTYPE_BURST(20);
+DECLARE_KOBJ_TTYPE_RW(20);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(20);
+
+DECLARE_KOBJ_TTYPE_MASTER(21);
+DECLARE_KOBJ_TTYPE_NBEAT(21);
+DECLARE_KOBJ_TTYPE_NBYTE(21);
+DECLARE_KOBJ_TTYPE_BURST(21);
+DECLARE_KOBJ_TTYPE_RW(21);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(21);
+
+
+/* SEDA 3.5 ext */
+static unsigned int msel_group_ext_val[WSCT_AMOUNT];
+static unsigned int wsct_rw_val[WSCT_AMOUNT];
+
+char* const delim_comma = ",";
+char* const delim_coclon = ":";
+
+
+char msel_group_ext[FILE_NODE_DATA_LEN] = {'\0'};
+
+static void _clear_msel_group_ext(void) {
+	int i;
+
+	for (i=0;i<WSCT_AMOUNT;i++) {
+		msel_group_ext_val[i] = BM_MASTER_ALL;
+	}
+	
+	/*WSCT 4~5 default is ultra, pre-ultra total*/
+	msel_group_ext[0] = '\0';
+}
+
+static ssize_t msel_group_ext_store(struct kobject *kobj,
+		struct kobj_attribute *attr,
+		const char *buf,
+		size_t n)
+{
+	/*parse wsct_id:group,
+	1. split data  by ","
+	2. split subdata by ":"
+	3. check the value is OK
+
+	don't clear the setting, do this by echo 1 > clear_setting
+	*/
+
+	char *token, *cur= msel_group_ext;
+	char *_id = NULL, *_master_group = NULL;
+	int id_int = 0;
+
+	_clear_msel_group_ext();
+
+	snprintf(msel_group_ext, FILE_NODE_DATA_LEN, "%s", buf);
+	msel_group_ext[n-1]='\0';
+	
+
+	while (cur != NULL) {
+		token = strsep(&cur, delim_comma);
+		PR_BOOTMSG("token: %s\n",token);
+		/*token EX: 4:0xff , (ID,master_group)*/
+
+		_id = strsep(&token, delim_coclon); // ID
+		_master_group = strsep(&token, delim_coclon);
+
+		PR_BOOTMSG("_id[%s] _master_group[%s]\n",_id,_master_group);
+
+		if (_id == NULL || _master_group == NULL) {
+			PR_BOOTMSG("err: _id[%s] _master_group[%s], para can't be NULL\n",_id,_master_group);
+			_clear_msel_group_ext();
+			return -EINVAL;
+		}
+
+		if (kstrtouint(_id, 0, &id_int) != 0) {
+			PR_BOOTMSG("_id[%s] trans to hex err\n",_id);
+			_clear_msel_group_ext();
+			return -EINVAL;
+		}
+
+
+		if ( id_int >= 0 && id_int < WSCT_AMOUNT) {
+			if (kstrtouint(_master_group, 0, &msel_group_ext_val[id_int]) != 0) {
+				PR_BOOTMSG("master_group[%s] trans to hex err\n",_master_group);
+				_clear_msel_group_ext();
+				return -EINVAL;
+			}
+		} else {
+			PR_BOOTMSG("id[%d] exceed the range, it must be 0~%d\n",id_int, WSCT_AMOUNT-1);
+			_clear_msel_group_ext();
+			return -EINVAL;
+		}
+	}
+#ifdef FILE_NODE_DBG	
+	PR_BOOTMSG("input data [%s]\n",msel_group_ext);
+	/*PR_BOOTMSG("msel_group_ext_store size para n[%d]\n",n);*/
+	int i;
+	PR_BOOTMSG("save data\n");
+	for (i=0;i<WSCT_AMOUNT;i++) {
+		PR_BOOTMSG("id[%d]=%X\n",i,msel_group_ext_val[i]);
+	}
+#endif	
+	return n;
+}
+
+static ssize_t msel_group_ext_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%s\n", msel_group_ext);
+}
+
+
+char wsct_rw[FILE_NODE_DATA_LEN] = {'\0'};
+
+static void _clear_wsct_rw(void) {
+	int i;
+
+	for (i=0;i<WSCT_AMOUNT;i++) {
+		wsct_rw_val[i] = BM_WSCT_RW_RWBOTH;
+	}
+	wsct_rw[0] = '\0';
+}
+
+static ssize_t wsct_rw_store(struct kobject *kobj,
+		struct kobj_attribute *attr,
+		const char *buf,
+		size_t n)
+{
+	char *token, *cur= wsct_rw;
+	char *_id = NULL, *_rw_type = NULL;
+	int id_int = 0;
+
+	_clear_wsct_rw();
+
+	snprintf(wsct_rw, FILE_NODE_DATA_LEN, "%s", buf);
+	wsct_rw[n-1]='\0';
+
+	while (cur != NULL) {
+		token = strsep(&cur, delim_comma);
+		PR_BOOTMSG("token: %s\n",token);
+		/*token EX: 4:R , 5:W (ID,RW)*/
+
+		_id = strsep(&token, delim_coclon); // ID
+		_rw_type = strsep(&token, delim_coclon);
+
+		if (_id == NULL || _rw_type == NULL) {
+			PR_BOOTMSG("err: _id[%s] _rw_type[%s], para can't be NULL\n",_id, _rw_type);
+			_clear_wsct_rw();
+			return -EINVAL;
+		}
+
+		PR_BOOTMSG("_id[%s] _rw_type[%s]\n",_id, _rw_type);
+		if (kstrtouint(_id, 0, &id_int) != 0) {
+			PR_BOOTMSG("_id[%s] trans to hex err\n",_id);
+			_clear_wsct_rw();
+			return -EINVAL;
+		}
+
+
+		if ( id_int >= 0 && id_int < WSCT_AMOUNT) {
+			if ( 0 == strncmp("NONE",_rw_type,4))
+				wsct_rw_val[id_int] = BM_WSCT_RW_DISABLE;
+			else if (0 == strncmp("R",_rw_type,4))
+				wsct_rw_val[id_int] = BM_WSCT_RW_READONLY;
+			else if (0 == strncmp("W",_rw_type,4))
+				wsct_rw_val[id_int] = BM_WSCT_RW_WRITEONLY;
+			else if (0 == strncmp("RW",_rw_type,4))
+				wsct_rw_val[id_int] = BM_WSCT_RW_RWBOTH;
+			else {
+				PR_BOOTMSG("_id[%s] has err rwtype[%s]\n", _id, _rw_type);
+				_clear_wsct_rw();
+				return -EINVAL;
+			}
+
+		} else {
+			PR_BOOTMSG("id[%d] exceed the range, it must be 0~%d\n",id_int, WSCT_AMOUNT-1);
+			_clear_wsct_rw();
+			return -EINVAL;
+		}
+	}
+
+#ifdef FILE_NODE_DBG
+	PR_BOOTMSG("wsct_rw_store input data [%s]\n",wsct_rw);
+	int i;
+	PR_BOOTMSG("rwtype save data\n");
+	for (i=0;i<WSCT_AMOUNT;i++) {
+		PR_BOOTMSG("id[%d]=%d\n",i,wsct_rw_val[i]);
+	}
+#endif
+	return n;
+}
+
+static ssize_t wsct_rw_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%s\n", wsct_rw);
+}
+
+
+static unsigned int WSCT_HPRI_DIS[WSCT_AMOUNT];
+static unsigned int WSCT_HPRI_SEL[WSCT_AMOUNT];
+char wsct_high_priority_enable[FILE_NODE_DATA_LEN] = {'\0'};
+
+static void _clear_wsct_high_priority_enable(void) {
+	int i;
+
+	for (i=0;i<WSCT_AMOUNT;i++) {
+		WSCT_HPRI_DIS[i] = 1;
+		WSCT_HPRI_SEL[i] = 0xF;
+	}
+
+	WSCT_HPRI_DIS[4] = 0;
+	WSCT_HPRI_SEL[4] = 0x8;  /* ultra */
+
+	WSCT_HPRI_DIS[5] = 0;
+	WSCT_HPRI_SEL[5] = 0x4; /* pre_ultra */
+
+
+	wsct_high_priority_enable[0] = '\0';
+}
+
+static ssize_t wsct_high_priority_enable_store(struct kobject *kobj,
+		struct kobj_attribute *attr,
+		const char *buf,
+		size_t n)
+{
+	char *token, *cur= wsct_high_priority_enable;
+	char *_id = NULL, *_enable = NULL,  *_level = NULL;
+	int  id_int = 0, level_int = 0;
+
+	_clear_wsct_high_priority_enable();
+
+	snprintf(wsct_high_priority_enable, FILE_NODE_DATA_LEN, "%s", buf);
+	wsct_high_priority_enable[n-1]='\0';
+	
+	while (cur != NULL) {
+		token = strsep(&cur, delim_comma);
+		PR_BOOTMSG("token: %s\n",token);
+		/*token EX: 4:R , 5:W (ID,RW)*/
+
+		_id = strsep(&token, delim_coclon); // ID
+		_enable = strsep(&token, delim_coclon);
+		_level = strsep(&token, delim_coclon);
+
+		PR_BOOTMSG("_id[%s] _enable[%s] _level[%s]\n",_id, _enable, _level);
+
+		if (_id == NULL || _enable == NULL || _level == NULL ) {
+			PR_BOOTMSG("err : _id[%s] _enable[%s] _level[%s], para can't be NULL\n",_id, _enable, _level);
+			_clear_wsct_high_priority_enable();
+			return -EINVAL;
+		}
+
+		if (kstrtouint(_id, 0, &id_int) != 0) {
+			PR_BOOTMSG("_id[%s] trans to hex err\n",_id);
+			_clear_wsct_high_priority_enable();
+			return -EINVAL;
+		}
+
+
+		if ( id_int >= 0 && id_int < WSCT_AMOUNT) {
+			if ( 0 == strncmp("disable", _enable, 7)) {
+
+				WSCT_HPRI_DIS[id_int] = 1;
+				WSCT_HPRI_SEL[id_int] = 0xf;
+			} else if ( 0 == strncmp("enable", _enable, 6)) {
+
+				WSCT_HPRI_DIS[id_int] = 0;
+				if (kstrtouint(_level, 0, &level_int) != 0) {
+					PR_BOOTMSG("_id[%s] trans ultraLevel[%s] to hex err\n",_id, _level);
+					_clear_wsct_high_priority_enable();
+					return -EINVAL;
+				}
+				WSCT_HPRI_SEL[id_int] = level_int & 0xF;				
+			} else {
+				PR_BOOTMSG("_id[%s] has err enable[%s] (enable/disable)\n", _id, _enable);
+				_clear_wsct_high_priority_enable();
+				return -EINVAL;
+			}
+
+		} else {
+			PR_BOOTMSG("id[%d] exceed the range, it must be 0~%d\n",id_int, WSCT_AMOUNT-1);
+			_clear_wsct_high_priority_enable();
+			return -EINVAL;
+		}
+	}
+#ifdef FILE_NODE_DBG	
+	PR_BOOTMSG("input data [%s]\n",wsct_high_priority_enable);
+	int i;
+	PR_BOOTMSG("wsct_high_priority_enable save data\n");
+	for (i=0;i<WSCT_AMOUNT;i++) {
+		PR_BOOTMSG("id[%d]=(%X,%X)\n", i, WSCT_HPRI_DIS[i], WSCT_HPRI_SEL[i]);
+	}
+#endif
+	return n;
+}
+
+static ssize_t wsct_high_priority_enable_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%s\n", wsct_high_priority_enable);
+}
+
+
+static unsigned int wsct_busid_val[WSCT_AMOUNT];
+static unsigned int wsct_idMask_val[WSCT_AMOUNT];
+
+char wsct_busid[FILE_NODE_DATA_LEN] = {'\0'};
+
+static void _clear_wsct_busid(void) {
+	int i;
+
+	for (i=0;i<WSCT_AMOUNT;i++) {
+		wsct_busid_val[i] = 0xfffff;
+		wsct_idMask_val[i] = 0x1FFF;
+	}
+	wsct_busid[0] = '\0';
+}
+
+static ssize_t wsct_busid_store(struct kobject *kobj,
+		struct kobj_attribute *attr,
+		const char *buf,
+		size_t n)
+{
+	char *token, *cur= wsct_busid;
+
+	char *_id = NULL, *_busid = NULL, *_idMask = NULL;
+	int id_int = 0, busid_int = 0, idMask_int = 0;
+
+	_clear_wsct_busid();
+
+	snprintf(wsct_busid, FILE_NODE_DATA_LEN, "%s", buf);
+	wsct_busid[n-1]='\0';
+	
+	while (cur != NULL) {
+		token = strsep(&cur, delim_comma);
+		PR_BOOTMSG("token: %s\n",token);
+		/*token EX: 4:R , 5:W (ID,RW)*/
+
+		_id = strsep(&token, delim_coclon); // ID
+		_busid = strsep(&token, delim_coclon);
+		_idMask = strsep(&token, delim_coclon);
+
+		PR_BOOTMSG("_id[%s] _busid[%s] _idMask[%s]\n",_id, _busid, _idMask);
+
+		if (_id == NULL || _busid == NULL || _idMask == NULL) {
+			PR_BOOTMSG("err: _id[%s] _busid[%s] _idMask[%s] ,parameter can't be NULL\n",_id, _busid, _idMask);
+			_clear_wsct_busid();
+			return -EINVAL;
+		}
+
+		
+		if (kstrtouint(_id, 0, &id_int) != 0) {
+			PR_BOOTMSG("_id[%s] trans to hex err\n",_id);
+			_clear_wsct_busid();
+			return -EINVAL;
+		}
+		if (kstrtouint(_busid, 0, &busid_int) != 0) {
+			PR_BOOTMSG("_busid[%s] trans to hex err\n",_busid);
+			_clear_wsct_busid();
+			return -EINVAL;
+		}
+		if (kstrtouint(_idMask, 0, &idMask_int) != 0) {
+			PR_BOOTMSG("_idMask[%s] trans to hex err\n",_idMask);
+			_clear_wsct_busid();
+			return -EINVAL;
+		}
+
+
+		if ( id_int >= 0 && id_int < WSCT_AMOUNT) {
+			wsct_busid_val[id_int] = busid_int;
+			wsct_idMask_val[id_int] = idMask_int;
+
+		} else {
+			PR_BOOTMSG("id[%d] exceed the range, it must be 0~%d\n",id_int, WSCT_AMOUNT-1);
+			_clear_wsct_busid();
+			return -EINVAL;
+		}
+	}
+#ifdef FILE_NODE_DBG	
+	PR_BOOTMSG("input data [%s]\n",wsct_busid);
+	int i;
+	PR_BOOTMSG("wsct_busid save data\n");
+	for (i=0;i<WSCT_AMOUNT;i++) {
+		PR_BOOTMSG("id[%d](busid,idMask)=(%X,%X)\n", i, wsct_busid_val[i], wsct_idMask_val[i]);
+	}
+#endif
+	return n;
+}
+
+static ssize_t wsct_busid_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%s\n", wsct_busid);
+}
+
+
+static unsigned int  wsct_chn_rank_sel_val[WSCT_AMOUNT];
+char wsct_chn_rank_sel[FILE_NODE_DATA_LEN] = {'\0'};
+
+static void _clear_wsct_chn_rank_sel(void) {
+	int i;
+
+	for (i=0;i<WSCT_AMOUNT;i++) {
+		wsct_chn_rank_sel_val[i] = 0xF;
+	}
+	wsct_chn_rank_sel[0] = '\0';
+}
+
+static ssize_t wsct_chn_rank_sel_store(struct kobject *kobj,
+		struct kobj_attribute *attr,
+		const char *buf,
+		size_t n)
+{
+	char *token, *cur= wsct_chn_rank_sel;
+	char *_id = NULL, *_chn_rank = NULL;
+	int id_int = 0, chn_rank_int = 0;
+
+	_clear_wsct_chn_rank_sel();
+
+	snprintf(wsct_chn_rank_sel, FILE_NODE_DATA_LEN, "%s", buf);
+	wsct_chn_rank_sel[n-1]='\0';
+
+
+	while (cur != NULL) {
+		token = strsep(&cur, delim_comma);
+		PR_BOOTMSG("token: %s\n",token);
+		/*token EX: 4:f , 5:C (ID,chn_rnk_sel)*/
+
+		_id = strsep(&token, delim_coclon); // ID
+		_chn_rank = strsep(&token, delim_coclon);
+
+		PR_BOOTMSG("_id[%s] _chn_rank[%s]\n",_id, _chn_rank);
+
+		if (_id == NULL || _chn_rank == NULL) {
+			PR_BOOTMSG("err : _id[%s] _chn_rank[%s], para can't be NULL\n",_id, _chn_rank);
+			_clear_wsct_chn_rank_sel();
+			return -EINVAL;
+		}
+
+		
+		if (kstrtouint(_id, 0, &id_int) != 0) {
+			PR_BOOTMSG("_id[%s] trans to hex err\n",_id);
+			_clear_wsct_chn_rank_sel();
+			return -EINVAL;
+		}
+		if (kstrtouint(_chn_rank, 0, &chn_rank_int) != 0) {
+			PR_BOOTMSG("_chn_rank[%s] trans to hex err\n",_id);
+			_clear_wsct_chn_rank_sel();
+			return -EINVAL;
+		}
+
+		if ( id_int >= 0 && id_int < WSCT_AMOUNT) {
+			wsct_chn_rank_sel_val[id_int] = chn_rank_int;
+
+		} else {
+			PR_BOOTMSG("id[%d] exceed the range, it must be 0~%d\n",id_int, WSCT_AMOUNT-1);
+			_clear_wsct_chn_rank_sel();
+			return -EINVAL;
+		}
+	}
+
+#ifdef FILE_NODE_DBG	
+	PR_BOOTMSG("wsct_chn_rank_sel input data [%s]\n",wsct_chn_rank_sel);
+	int i;
+	PR_BOOTMSG("wsct_chn_rank_sel_val save data\n");
+	for (i=0;i<WSCT_AMOUNT;i++) {
+		PR_BOOTMSG("id[%d]=%X\n",i,wsct_chn_rank_sel_val[i]);
+	}
+#endif
+	return n;
+}
+
+static ssize_t wsct_chn_rank_sel_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%s\n", wsct_chn_rank_sel);
+}
+
+static unsigned int  wsct_byte_low_bnd_val[WSCT_AMOUNT];
+static unsigned int  wsct_byte_up_bnd_val[WSCT_AMOUNT];
+static unsigned int  wsct_byte_bnd_dis[WSCT_AMOUNT];
+char wsct_burst_range[FILE_NODE_DATA_LEN] = {'\0'};
+
+static void _clear_wsct_burst_range(void) {
+	int i;
+
+	for (i=0;i<WSCT_AMOUNT;i++) {
+		wsct_byte_low_bnd_val[i] = 0x0;
+		wsct_byte_up_bnd_val[i] = 0x1FF;
+		wsct_byte_bnd_dis[i] = 1;
+	}
+	wsct_burst_range[0] = '\0';
+}
+
+static ssize_t wsct_burst_range_store(struct kobject *kobj,
+		struct kobj_attribute *attr,
+		const char *buf,
+		size_t n)
+{
+	char *token, *cur= wsct_burst_range;
+	char *_id = NULL, *_low_bnd = NULL, *_up_bnd = NULL;
+	int id_int = 0, low_bnd_int = 0, up_bnd_int = 0;
+
+	_clear_wsct_burst_range();
+	
+	snprintf(wsct_burst_range, FILE_NODE_DATA_LEN, "%s", buf);
+	wsct_burst_range[n-1]='\0';
+
+
+	while (cur != NULL) {
+		token = strsep(&cur, delim_comma);
+		PR_BOOTMSG("token: %s\n",token);
+		/*token EX: 4:f , 5:C (ID,chn_rnk_sel)*/
+
+		_id = strsep(&token, delim_coclon); // ID
+		_low_bnd = strsep(&token, delim_coclon);
+		_up_bnd = strsep(&token, delim_coclon);
+
+		PR_BOOTMSG("_id[%s] _low_bnd[%s] _up_bnd[%s]\n",_id, _low_bnd, _up_bnd);
+
+		if (_id == NULL || _low_bnd == NULL || _up_bnd == NULL) {
+			PR_BOOTMSG("err : _id[%s] _low_bnd[%s] _up_bnd[%s], para can't be NULL\n",_id, _low_bnd, _up_bnd);
+			_clear_wsct_burst_range();
+			return -EINVAL;
+		}
+
+		if (kstrtouint(_id, 0, &id_int) != 0) {
+			PR_BOOTMSG("_id[%s] trans to hex err\n",_id);
+			_clear_wsct_burst_range();
+			return -EINVAL;
+		}
+		if (kstrtouint(_low_bnd, 0, &low_bnd_int) != 0) {
+			PR_BOOTMSG("_low_bnd[%s] trans to hex err\n",_id);
+			_clear_wsct_burst_range();
+			return -EINVAL;
+		}
+		if (kstrtouint(_up_bnd, 0, &up_bnd_int) != 0) {
+			PR_BOOTMSG("_up_bnd[%s] trans to hex err\n",_id);
+			_clear_wsct_burst_range();
+			return -EINVAL;
+		}
+
+		if ( id_int >= 0 && id_int < WSCT_AMOUNT) {
+			wsct_byte_low_bnd_val[id_int] = low_bnd_int;
+			wsct_byte_up_bnd_val[id_int] = up_bnd_int;
+			wsct_byte_bnd_dis[id_int] = 0;
+		} else {
+			PR_BOOTMSG("id[%d] exceed the range, it must be 0~%d\n",id_int, WSCT_AMOUNT-1);
+			_clear_wsct_burst_range();
+			return -EINVAL;
+		}
+	}
+
+#ifdef FILE_NODE_DBG
+	PR_BOOTMSG("wsct_burst_range_store input data [%s]\n",wsct_burst_range);
+	int i;
+	PR_BOOTMSG("wsct_burst_range save data\n");
+	for (i=0;i<WSCT_AMOUNT;i++) {
+		PR_BOOTMSG("id[%d](low_bnd,up_bnd)=(%X,%X)\n",i,wsct_byte_low_bnd_val[i],wsct_byte_up_bnd_val[i]);
+	}
+#endif
+	return n;
+}
+
+static ssize_t wsct_burst_range_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%s\n", wsct_burst_range);
+}
+
+
+
+static unsigned int tsct_busid_enable_val[TSCT_AMOUNT];
+char tsct_busid_enable[FILE_NODE_DATA_LEN] = {'\0'};
+
+static void _clear_tsct_busid_enable(void) {
+	int i;
+
+	for (i=0;i<TSCT_AMOUNT;i++) {
+		tsct_busid_enable_val[i] = 0;
+	}
+	tsct_busid_enable[0] = '\0';
+}
+
+static ssize_t tsct_busid_enable_store(struct kobject *kobj,
+		struct kobj_attribute *attr,
+		const char *buf,
+		size_t n)
+{
+	char *token, *cur= tsct_busid_enable;
+	char *_id = NULL, *_enable = NULL;
+	int  id_int = 0;
+
+	_clear_tsct_busid_enable();
+
+	snprintf(tsct_busid_enable, FILE_NODE_DATA_LEN, "%s", buf);
+	tsct_busid_enable[n-1]='\0';
+
+
+	while (cur != NULL) {
+		token = strsep(&cur, delim_comma);
+		PR_BOOTMSG("token: %s\n",token);
+		/*token EX: 4:R , 5:W (ID,RW)*/
+
+		_id = strsep(&token, delim_coclon); // ID
+		_enable = strsep(&token, delim_coclon);
+
+
+		PR_BOOTMSG("_id[%s] _enable[%s]\n",_id, _enable);
+
+		if (_id == NULL || _enable == NULL) {
+			PR_BOOTMSG("err : _id[%s] _enable[%s], para can't be NULL\n",_id, _enable);
+			_clear_tsct_busid_enable();
+			return -EINVAL;
+		}
+
+		if (kstrtouint(_id, 0, &id_int) != 0) {
+			PR_BOOTMSG("_id[%s] trans to hex err\n",_id);
+			_clear_tsct_busid_enable();
+			return -EINVAL;
+		}
+
+
+		if ( id_int >= 0 && id_int < TSCT_AMOUNT) {
+			if ( 0 == strncmp("disable", _enable, 7)) {
+				tsct_busid_enable_val[id_int] = 0;
+			} else if ( 0 == strncmp("enable", _enable, 6)) {
+				tsct_busid_enable_val[id_int] = 1;
+			} else {
+				PR_BOOTMSG("_id[%s] has err enable[%s] (enable/disable)\n", _id, _enable);
+				_clear_tsct_busid_enable();
+				return -EINVAL;
+			}
+
+		} else {
+			PR_BOOTMSG("id[%d] exceed the range, it must be 0~%d\n",id_int, TSCT_AMOUNT-1);
+			_clear_tsct_busid_enable();
+			return -EINVAL;
+		}
+	}
+#ifdef FILE_NODE_DBG	
+	PR_BOOTMSG("tsct_busid_enable input data [%s]\n",tsct_busid_enable);
+	int i;
+	PR_BOOTMSG("wsct_high_priority_enable save data\n");
+	for (i=0;i<TSCT_AMOUNT;i++) {
+		PR_BOOTMSG("id[%d]=(%d)\n", i, tsct_busid_enable_val[i]);
+	}
+#endif
+	return n;
+}
+
+static ssize_t tsct_busid_enable_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%s\n", tsct_busid_enable);
+}
+
+
+/* use the origin para high_priority_filter to save the en/dis setting */
+static unsigned int TTYPE_HPRI_SEL[BM_COUNTER_MAX];
+char ttype_high_priority_ext[FILE_NODE_DATA_LEN] = {'\0'};
+
+static void _clear_ttype_high_priority_ext(void) {
+	int i;
+
+	for (i=0;i<BM_COUNTER_MAX;i++) {
+		TTYPE_HPRI_SEL[i] = 0xf;
+	}
+
+	high_priority_filter = 0x0;
+	ttype_high_priority_ext[0] = '\0';
+}
+
+static ssize_t ttype_high_priority_ext_store(struct kobject *kobj,
+		struct kobj_attribute *attr,
+		const char *buf,
+		size_t n)
+{
+	char *token, *cur= ttype_high_priority_ext;
+	char *_id = NULL, *_enable = NULL,  *_level = NULL;
+	int  id_int = 0, level_int = 0;
+
+	_clear_ttype_high_priority_ext();
+
+	snprintf(ttype_high_priority_ext, FILE_NODE_DATA_LEN, "%s", buf);
+	ttype_high_priority_ext[n-1]='\0';
+
+	while (cur != NULL) {
+		token = strsep(&cur, delim_comma);
+		PR_BOOTMSG("token: %s\n",token);
+		/*token EX: 4:R , 5:W (ID,RW)*/
+
+		_id = strsep(&token, delim_coclon); // ID
+		_enable = strsep(&token, delim_coclon);
+		_level = strsep(&token, delim_coclon);
+
+		PR_BOOTMSG("_id[%s] _enable[%s] _level[%s]\n",_id, _enable, _level);
+
+		if (_id == NULL || _enable == NULL || _level == NULL ) {
+			PR_BOOTMSG("err : _id[%s] _enable[%s] _level[%s], para can't be NULL\n",_id, _enable, _level);
+			_clear_ttype_high_priority_ext();
+			return -EINVAL;
+		}
+
+		if (kstrtouint(_id, 0, &id_int) != 0) {
+			PR_BOOTMSG("_id[%s] trans to hex err\n",_id);
+			_clear_ttype_high_priority_ext();
+			return -EINVAL;
+		}
+
+		id_int = id_int - 1;
+		if ( id_int >= 0 && id_int < BM_COUNTER_MAX) {
+			if ( 0 == strncmp("disable", _enable, 7)) {
+
+				high_priority_filter = ( high_priority_filter & ~(1<<id_int) ) | ( 0<<id_int );
+				TTYPE_HPRI_SEL[id_int] = 0xf;
+			} else if ( 0 == strncmp("enable", _enable, 6)) {
+
+				high_priority_filter = ( high_priority_filter & ~(1<<id_int) ) | ( 1<<id_int );
+				if (kstrtouint(_level, 0, &level_int) != 0) {
+					PR_BOOTMSG("_id[%s] trans ultraLevel[%s] to hex err\n",_id, _level);
+					_clear_ttype_high_priority_ext();
+					return -EINVAL;
+				}
+				TTYPE_HPRI_SEL[id_int] = level_int & 0xF;				
+			} else {
+				PR_BOOTMSG("ttype_high_priority_ext: _id[%s] has err enable[%s] (enable/disable)\n", _id, _enable);
+				_clear_ttype_high_priority_ext();
+				return -EINVAL;
+			}
+
+		} else {
+			PR_BOOTMSG("id[%d] exceed the range, it must be 1~%d\n",id_int+1, BM_COUNTER_MAX);
+			_clear_ttype_high_priority_ext();
+			return -EINVAL;
+		}
+	}
+#ifdef FILE_NODE_DBG
+	PR_BOOTMSG("input data [%s]\n",ttype_high_priority_ext);
+
+	int i;
+	PR_BOOTMSG("wsct_high_priority_enable save data\n");
+	for (i=0;i<BM_COUNTER_MAX;i++) {
+		PR_BOOTMSG("id[%d]=(%X,%X)\n", i+1, high_priority_filter>>i & 0x1, TTYPE_HPRI_SEL[i]);
+	}
+#endif
+	return n;
+}
+
+static ssize_t ttype_high_priority_ext_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%s\n", ttype_high_priority_ext);
+}
+
+
+static unsigned int ttype_idMask_val[BM_COUNTER_MAX];
+char ttype_busid_ext[FILE_NODE_DATA_LEN] = {'\0'};
+
+static void _clear_ttype_busid_ext(void) {
+	int i;
+
+	for (i=0;i<BM_COUNTER_MAX;i++) {
+		ttype_busid_val[i] = 0xfffff;
+		ttype_idMask_val[i] = 0x1FFF;	
+	}
+	ttype_busid_ext[0] = '\0';
+}
+
+/*id: 1~21*/
+static ssize_t ttype_busid_ext_store(struct kobject *kobj,
+		struct kobj_attribute *attr,
+		const char *buf,
+		size_t n)
+{
+	char *token, *cur= ttype_busid_ext;
+
+	char *_id = NULL, *_busid = NULL, *_idMask = NULL;
+	int id_int = 0, busid_int = 0, idMask_int = 0;
+
+	_clear_ttype_busid_ext();
+
+	snprintf(ttype_busid_ext, FILE_NODE_DATA_LEN, "%s", buf);
+	ttype_busid_ext[n-1]='\0';
+
+	while (cur != NULL) {
+		token = strsep(&cur, delim_comma);
+		PR_BOOTMSG("token: %s\n",token);
+		/*token EX: 4:R , 5:W (ID,RW)*/
+
+		_id = strsep(&token, delim_coclon); // ID
+		_busid = strsep(&token, delim_coclon);
+		_idMask = strsep(&token, delim_coclon);
+
+		PR_BOOTMSG("_id[%s] _busid[%s] _idMask[%s]\n",_id, _busid, _idMask);
+
+		if (_id == NULL || _busid == NULL || _idMask == NULL) {
+			PR_BOOTMSG("err: ttype_busid_ext _id[%s] _busid[%s] _idMask[%s] ,parameter can't be NULL\n",_id, _busid, _idMask);
+			_clear_ttype_busid_ext();
+			return -EINVAL;
+		}
+	
+		if (kstrtouint(_id, 0, &id_int) != 0) {
+			PR_BOOTMSG("_id[%s] trans to hex err\n",_id);
+			_clear_ttype_busid_ext();
+			return -EINVAL;
+		}
+		if (kstrtouint(_busid, 0, &busid_int) != 0) {
+			PR_BOOTMSG("_busid[%s] trans to hex err\n",_busid);
+			_clear_ttype_busid_ext();
+			return -EINVAL;
+		}
+		if (kstrtouint(_idMask, 0, &idMask_int) != 0) {
+			PR_BOOTMSG("_idMask[%s] trans to hex err\n",_idMask);
+			_clear_ttype_busid_ext();
+			return -EINVAL;
+		}
+
+		id_int = id_int - 1;
+		if ( id_int >= 0 && id_int < BM_COUNTER_MAX) {
+			ttype_busid_val[id_int] = busid_int;
+			ttype_idMask_val[id_int] = idMask_int;
+
+		} else {
+			PR_BOOTMSG("ttype_busid_ext id[%d] exceed the range, it must be 1~%d\n",id_int+1, BM_COUNTER_MAX);
+			_clear_ttype_busid_ext();
+			return -EINVAL;
+		}
+	}
+#ifdef FILE_NODE_DBG
+	PR_BOOTMSG("ttype_busid_ext input data [%s]\n",ttype_busid_ext);
+
+	int i;
+	PR_BOOTMSG("ttype_busid_ext save data\n");
+	for (i=0;i<BM_COUNTER_MAX;i++) {
+		PR_BOOTMSG("id[%d](busid,idMask)=(%X,%X)\n", i+1, ttype_busid_val[i], ttype_idMask_val[i]);
+	}
+#endif
+	return n;
+}
+
+static ssize_t ttype_busid_ext_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%s\n", ttype_busid_ext);
+}
+
+
+static unsigned int  ttype_chn_rank_sel_val[BM_COUNTER_MAX];
+char ttype_chn_rank_sel[FILE_NODE_DATA_LEN] = {'\0'};
+
+static void _clear_ttype_chn_rank_sel(void) {
+	int i;
+
+	for (i=0;i<BM_COUNTER_MAX;i++) {
+		ttype_chn_rank_sel_val[i] = 0xF;
+	}
+	ttype_chn_rank_sel[0] = '\0';
+}
+
+static ssize_t ttype_chn_rank_sel_store(struct kobject *kobj,
+		struct kobj_attribute *attr,
+		const char *buf,
+		size_t n)
+{
+	char *token, *cur= ttype_chn_rank_sel;
+	char *_id = NULL, *_chn_rank = NULL;
+	int id_int = 0, chn_rank_int = 0;
+
+	_clear_ttype_chn_rank_sel();
+
+	snprintf(ttype_chn_rank_sel, FILE_NODE_DATA_LEN, "%s", buf);
+	ttype_chn_rank_sel[n-1]='\0';
+
+	while (cur != NULL) {
+		token = strsep(&cur, delim_comma);
+		PR_BOOTMSG("token: %s\n",token);
+		/*token EX: 4:f , 5:C (ID,chn_rnk_sel)*/
+
+		_id = strsep(&token, delim_coclon); // ID
+		_chn_rank = strsep(&token, delim_coclon);
+
+		PR_BOOTMSG("_id[%s] _chn_rank[%s]\n",_id, _chn_rank);
+
+		if (_id == NULL || _chn_rank == NULL) {
+			PR_BOOTMSG("err (ttype_chn_rank_sel): _id[%s] _chn_rank[%s], para can't be NULL\n",_id, _chn_rank);
+			_clear_ttype_chn_rank_sel();
+			return -EINVAL;
+		}
+
+		
+		if (kstrtouint(_id, 0, &id_int) != 0) {
+			PR_BOOTMSG("_id[%s] trans to hex err\n",_id);
+			_clear_ttype_chn_rank_sel();
+			return -EINVAL;
+		}
+		if (kstrtouint(_chn_rank, 0, &chn_rank_int) != 0) {
+			PR_BOOTMSG("_chn_rank[%s] trans to hex err\n",_id);
+			_clear_ttype_chn_rank_sel();
+			return -EINVAL;
+		}
+
+		id_int = id_int -1;
+
+		if ( id_int >= 0 && id_int < BM_COUNTER_MAX) {
+			ttype_chn_rank_sel[id_int] = chn_rank_int;
+
+		} else {
+			PR_BOOTMSG("id[%d] exceed the range, it must be 1~%d\n",id_int+1, BM_COUNTER_MAX);
+			_clear_ttype_chn_rank_sel();
+			return -EINVAL;
+		}
+	}
+
+#ifdef FILE_NODE_DBG
+	PR_BOOTMSG("ttype_chn_rank_sel input data [%s]\n",ttype_chn_rank_sel);
+
+	int i;
+	PR_BOOTMSG("wsct_chn_rank_sel_val save data\n");
+	for (i=0;i<BM_COUNTER_MAX;i++) {
+		PR_BOOTMSG("id[%d]=%X\n",i+1,ttype_chn_rank_sel[i]);
+	}
+#endif
+	return n;
+}
+
+static ssize_t ttype_chn_rank_sel_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%s\n", ttype_chn_rank_sel);
+}
+
+
+static unsigned int  ttype_byte_low_bnd_val[BM_COUNTER_MAX];
+static unsigned int  ttype_byte_up_bnd_val[BM_COUNTER_MAX];
+static unsigned int  ttype_byte_bnd_dis[BM_COUNTER_MAX];
+char ttype_burst_range[FILE_NODE_DATA_LEN] = {'\0'};
+
+static void _clear_ttype_burst_range(void) {
+	int i;
+
+	for (i=0;i<BM_COUNTER_MAX;i++) {
+		ttype_byte_low_bnd_val[i] = 0x0;
+		ttype_byte_up_bnd_val[i] = 0x1FF;
+		ttype_byte_bnd_dis[i] = 1;
+	}
+	ttype_burst_range[0] = '\0';
+}
+
+static ssize_t ttype_burst_range_store(struct kobject *kobj,
+		struct kobj_attribute *attr,
+		const char *buf,
+		size_t n)
+{
+	char *token, *cur= ttype_burst_range;
+	char *_id = NULL, *_low_bnd = NULL, *_up_bnd = NULL;
+	int id_int = 0, low_bnd_int = 0, up_bnd_int = 0;
+
+	_clear_ttype_burst_range();
+	
+	snprintf(ttype_burst_range, FILE_NODE_DATA_LEN, "%s", buf);
+	ttype_burst_range[n-1]='\0';
+
+
+	while (cur != NULL) {
+		token = strsep(&cur, delim_comma);
+		PR_BOOTMSG("token: %s\n",token);
+		/*token EX: 4:f , 5:C (ID,chn_rnk_sel)*/
+
+		_id = strsep(&token, delim_coclon); // ID
+		_low_bnd = strsep(&token, delim_coclon);
+		_up_bnd = strsep(&token, delim_coclon);
+
+		PR_BOOTMSG("_id[%s] _low_bnd[%s] _up_bnd[%s]\n",_id, _low_bnd, _up_bnd);
+
+		if (_id == NULL || _low_bnd == NULL || _up_bnd == NULL) {
+			PR_BOOTMSG("err (ttype_burst_range): _id[%s] _low_bnd[%s] _up_bnd[%s], para can't be NULL\n",
+				        _id, _low_bnd, _up_bnd);
+			_clear_ttype_burst_range();
+			return -EINVAL;
+		}
+
+		if (kstrtouint(_id, 0, &id_int) != 0) {
+			PR_BOOTMSG("_id[%s] trans to hex err\n",_id);
+			_clear_ttype_burst_range();
+			return -EINVAL;
+		}
+		if (kstrtouint(_low_bnd, 0, &low_bnd_int) != 0) {
+			PR_BOOTMSG("_low_bnd[%s] trans to hex err\n",_id);
+			_clear_ttype_burst_range();
+			return -EINVAL;
+		}
+		if (kstrtouint(_up_bnd, 0, &up_bnd_int) != 0) {
+			PR_BOOTMSG("_up_bnd[%s] trans to hex err\n",_id);
+			_clear_ttype_burst_range();
+			return -EINVAL;
+		}
+
+		id_int = id_int - 1;
+		if ( id_int >= 0 && id_int < BM_COUNTER_MAX) {
+			ttype_byte_low_bnd_val[id_int] = low_bnd_int;
+			ttype_byte_up_bnd_val[id_int] = up_bnd_int;
+			ttype_byte_bnd_dis[id_int] = 0;
+		} else {
+			PR_BOOTMSG("id[%d] exceed the range, it must be 1~%d\n",id_int, BM_COUNTER_MAX);
+			_clear_ttype_burst_range();
+			return -EINVAL;
+		}
+	}
+
+#ifdef FILE_NODE_DBG
+	PR_BOOTMSG("ttype_burst_range_store input data [%s]\n",ttype_burst_range);
+
+	int i;
+	PR_BOOTMSG("ttype_burst_range save data\n");
+	for (i=0;i<BM_COUNTER_MAX;i++) {
+		PR_BOOTMSG("id[%d](low_bnd,up_bnd)=(%X,%X)\n",i+1,ttype_byte_low_bnd_val[i],ttype_byte_up_bnd_val[i]);
+	}
+#endif
+	return n;
+}
+
+static ssize_t ttype_burst_range_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%s\n", ttype_burst_range);
+}
+
+static int reserve_wsct_setting;
+DECLARE_KOBJ_ATTR_INT(reserve_wsct_setting, reserve_wsct_setting);
+
+static void _clear_setting(void) {
+	/*clear all file node para here*/
+	
+	PR_BOOTMSG("clear EMI file node setting\n");
+
+	_clear_msel_group_ext();
+	_clear_wsct_rw();
+	_clear_wsct_high_priority_enable();
+	_clear_wsct_busid();
+	_clear_wsct_chn_rank_sel();
+	_clear_wsct_burst_range();
+
+	_clear_tsct_busid_enable();
+	_clear_ttype_high_priority_ext();
+	_clear_ttype_busid_ext();
+	_clear_ttype_chn_rank_sel();
+	_clear_ttype_burst_range();
+	reserve_wsct_setting = 0;
+
+
+
+	emi_TP_busfiltr_enable = 0;
+
+	high_priority_filter = 0x0;
+	rwtype = BM_BOTH_READ_WRITE;
+	dramc_pdir_enable = 1;
+
+
+	msel_enable = 0;
+	msel_group1 = BM_MASTER_ALL;
+	msel_group2 = BM_MASTER_ALL;
+	msel_group3 = BM_MASTER_ALL;
+
+
+	bw_limiter_enable = BM_BW_LIMITER_ENABLE;
+	ttype1_16_en = BM_TTYPE1_16_DISABLE;
+	ttype17_21_en = BM_TTYPE17_21_DISABLE;
+
+}
+
+static ssize_t clear_setting_store(struct kobject *kobj,
+			struct kobj_attribute *attr,
+			const char *buf,
+			size_t n)
+{
+	int value;
+
+	if ((n == 0) || (buf == NULL))
+		return -EINVAL;
+
+	if (kstrtoint(buf, 0, &value) != 0)
+		return -EINVAL;
+
+	if (value == 1)
+		_clear_setting();
+
+	return n;
+}
+
+static struct kobj_attribute clear_setting_attr = __ATTR_WO(clear_setting); // OK
+static struct kobj_attribute msel_group_ext_attr = __ATTR(msel_group_ext, 0664, msel_group_ext_show, msel_group_ext_store); //OK
+static struct kobj_attribute wsct_rw_attr = __ATTR(wsct_rw, 0664, wsct_rw_show, wsct_rw_store);
+static struct kobj_attribute wsct_high_priority_enable_attr = __ATTR(wsct_high_priority_enable, 0664, wsct_high_priority_enable_show, wsct_high_priority_enable_store);
+static struct kobj_attribute wsct_busid_attr = __ATTR(wsct_busid, 0664, wsct_busid_show, wsct_busid_store);
+static struct kobj_attribute wsct_chn_rank_sel_attr = __ATTR(wsct_chn_rank_sel, 0664, wsct_chn_rank_sel_show, wsct_chn_rank_sel_store);
+static struct kobj_attribute wsct_burst_range_attr = __ATTR(wsct_burst_range, 0664, wsct_burst_range_show, wsct_burst_range_store);
+static struct kobj_attribute tsct_busid_enable_attr = __ATTR(tsct_busid_enable, 0664, tsct_busid_enable_show, tsct_busid_enable_store);
+static struct kobj_attribute ttype_high_priority_ext_attr = __ATTR(ttype_high_priority_ext, 0664, ttype_high_priority_ext_show, ttype_high_priority_ext_store);
+static struct kobj_attribute ttype_busid_ext_attr = __ATTR(ttype_busid_ext, 0664, ttype_busid_ext_show, ttype_busid_ext_store);
+static struct kobj_attribute ttype_chn_rank_sel_attr = __ATTR(ttype_chn_rank_sel, 0664, ttype_chn_rank_sel_show, ttype_chn_rank_sel_store);
+static struct kobj_attribute ttype_burst_range_attr = __ATTR(ttype_burst_range, 0664, ttype_burst_range_show, ttype_burst_range_store);
+
+
+
+
+
+
+/**/
+#define KOBJ_ATTR_ITEM_SERIAL_FNODE(nr) \
+	do { \
+		KOBJ_ATTR_ITEM(ttype ## nr ## _master); \
+		KOBJ_ATTR_ITEM(ttype ## nr ## _nbeat); \
+		KOBJ_ATTR_ITEM(ttype ## nr ## _nbyte); \
+		KOBJ_ATTR_ITEM(ttype ## nr ## _burst); \
+		KOBJ_ATTR_ITEM(ttype ## nr ## _busid); \
+		KOBJ_ATTR_ITEM(ttype ## nr ## _rw); \
+	} while (0)
+
+#define KOBJ_ATTR_LIST \
+	do { \
+		KOBJ_ATTR_ITEM(high_priority_filter); \
+		KOBJ_ATTR_ITEM(emi_TP_busfiltr_enable); \
+		KOBJ_ATTR_ITEM(msel_enable); \
+		KOBJ_ATTR_ITEM(msel_group1); \
+		KOBJ_ATTR_ITEM(msel_group2); \
+		KOBJ_ATTR_ITEM(msel_group3); \
+		KOBJ_ATTR_ITEM(rwtype); \
+		KOBJ_ATTR_ITEM(ttype17_21_en); \
+		KOBJ_ATTR_ITEM(ttype1_16_en); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(1); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(2); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(3); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(4); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(5); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(6); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(7); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(8); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(9); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(10); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(11); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(12); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(13); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(14); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(15); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(16); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(17); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(18); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(19); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(20); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(21); \
+		KOBJ_ATTR_ITEM(bw_limiter_enable); \
+		KOBJ_ATTR_ITEM(dramc_pdir_enable); \
+		KOBJ_ATTR_ITEM(clear_setting);\
+		KOBJ_ATTR_ITEM(msel_group_ext);\
+		KOBJ_ATTR_ITEM(wsct_rw);\
+		KOBJ_ATTR_ITEM(wsct_high_priority_enable);\
+		KOBJ_ATTR_ITEM(wsct_busid);\
+		KOBJ_ATTR_ITEM(wsct_chn_rank_sel);\
+		KOBJ_ATTR_ITEM(wsct_burst_range);\
+		KOBJ_ATTR_ITEM(tsct_busid_enable);\
+		KOBJ_ATTR_ITEM(ttype_high_priority_ext);\
+		KOBJ_ATTR_ITEM(ttype_busid_ext);\
+		KOBJ_ATTR_ITEM(ttype_chn_rank_sel);\
+		KOBJ_ATTR_ITEM(ttype_burst_range);\
+		KOBJ_ATTR_ITEM(reserve_wsct_setting);\
+	} while (0)
+
+
+
+/*======================================================================*/
+/*	EMI Operations							*/
+/*======================================================================*/
+static void emi_init(void)
+{
+	unsigned int bmrw0_val, bmrw1_val, i;
+	/*unsigned int msel_group_val[4];*/
+
+	/*save origianl EMI config*/
+	MET_BM_SaveCfg();
+
+	/* get dram channel number */
+	dram_chann_num = MET_EMI_GetDramChannNum();
+
+	/* Init. EMI bus monitor */
+	MET_BM_SetReadWriteType(rwtype);
+
+	/*handle the ori */
+
+	if (ttype1_16_en != BM_TTYPE1_16_ENABLE) {
+		MET_BM_SetLatencyCounter(1);    /*enable latency count*/
+	}
+	else {
+		MET_BM_SetLatencyCounter(0);    /*disable latency count*/
+
+		for (i = 1; i <= 16; i++) {
+			MET_BM_SetMonitorCounter(i,
+						 ttype_master_val[i - 1],
+						 ttype_nbeat_val[i - 1] |
+						 ttype_nbyte_val[i - 1] |
+						 ttype_burst_val[i - 1]);
+		}
+	}
+	
+	if (ttype17_21_en == BM_TTYPE17_21_ENABLE) {
+		for (i = 17; i <= 21; i++) {
+			MET_BM_SetMonitorCounter(i,
+						 ttype_master_val[i - 1],
+						 ttype_nbeat_val[i - 1] |
+						 ttype_nbyte_val[i - 1] |
+						 ttype_burst_val[i - 1]);
+		}
+	}
+
+	PR_BOOTMSG("[%s]reserve_wsct_setting=%d\n",__func__,reserve_wsct_setting);
+
+	if (reserve_wsct_setting == 0) {
+		/* wsct 0 : total-all*/
+		msel_group_ext_val[0] = BM_MASTER_ALL;
+		wsct_rw_val[0] = BM_WSCT_RW_RWBOTH;
+		WSCT_HPRI_DIS[0] = 1;
+		WSCT_HPRI_SEL[0] = 0xF;
+		wsct_busid_val[0] = 0xFFFFF;
+		wsct_idMask_val[0] = 0x1FFF;
+		wsct_chn_rank_sel_val[0] = 0xF;
+		wsct_byte_bnd_dis[0] = 1;
+
+		/* wsct 4 : total-ultra*/
+		msel_group_ext_val[4] = BM_MASTER_ALL;
+		wsct_rw_val[4] = BM_WSCT_RW_RWBOTH;		
+		WSCT_HPRI_DIS[4] = 0;
+		WSCT_HPRI_SEL[4] = 0x8;  /* ultra */
+		wsct_busid_val[4] = 0xFFFFF;
+		wsct_idMask_val[4] = 0x1FFF;
+		wsct_chn_rank_sel_val[4] = 0xF;
+		wsct_byte_bnd_dis[4] = 1;
+
+		/* wsct 5 : total-pre_ultra*/
+		msel_group_ext_val[5] = BM_MASTER_ALL;
+		wsct_rw_val[5] = BM_WSCT_RW_RWBOTH;
+		WSCT_HPRI_DIS[5] = 0;
+		WSCT_HPRI_SEL[5] = 0x4; /* pre_ultra */
+		wsct_busid_val[5] = 0xFFFFF;
+		wsct_idMask_val[5] = 0x1FFF;
+		wsct_chn_rank_sel_val[5] = 0xF;
+		wsct_byte_bnd_dis[5] = 1;
+	}
+
+	if (msel_enable) {
+		/* if ole file node set, use the value */
+		if ( msel_group1 != BM_MASTER_ALL )
+			msel_group_ext_val[1] = msel_group1;
+
+		if ( msel_group2 != BM_MASTER_ALL )
+			msel_group_ext_val[2] = msel_group2;
+
+		if ( msel_group3 != BM_MASTER_ALL )
+			msel_group_ext_val[3] = msel_group3;
+
+	} else {
+		for ( i=1; i<=3; i++) {
+			msel_group_ext_val[i] = BM_MASTER_ALL;
+		}
+	}
+
+	MET_BM_SetWSCT_master_rw(msel_group_ext_val, wsct_rw_val);
+	MET_BM_SetWSCT_high_priority(WSCT_HPRI_DIS, WSCT_HPRI_SEL);
+	MET_BM_SetWSCT_busid_idmask(wsct_busid_val, wsct_idMask_val);
+	MET_BM_SetWSCT_chn_rank_sel(wsct_chn_rank_sel_val);
+	MET_BM_SetWSCT_burst_range(wsct_byte_bnd_dis, wsct_byte_low_bnd_val, wsct_byte_up_bnd_val);
+	MET_BM_SetTSCT_busid_enable(tsct_busid_enable_val);
+
+	MET_BM_SetTtype_high_priority_sel(high_priority_filter, TTYPE_HPRI_SEL);
+	MET_BM_SetTtype_busid_idmask(ttype_busid_val, ttype_idMask_val, ttype1_16_en, ttype17_21_en);
+	MET_BM_SetTtype_chn_rank_sel(ttype_chn_rank_sel_val);
+	MET_BM_SetTtype_burst_range(ttype_byte_bnd_dis, ttype_byte_low_bnd_val, ttype_byte_up_bnd_val);
+
+
+	bmrw0_val = 0;
+	for (i = 0; i < 16; i++)
+		bmrw0_val |= (ttype_rw_val[i] << (i * 2));
+
+	bmrw1_val = 0;
+	for (i = 16; i < 21; i++)
+		bmrw1_val |= (ttype_rw_val[i] << ((i-16) * 2));
+
+	MET_BM_SetTtypeCounterRW(bmrw0_val, bmrw1_val);
+
+}
+
+
+static void emi_uninit(void)
+{
+	MET_BM_RestoreCfg();
+}
+
+
+static inline int do_emi(void)
+{
+	return met_sspm_emi.mode;
+}
+
+
+
+
+
+
+
+/*======================================================================*/
+/*	MET Device Operations						*/
+/*======================================================================*/
+static int emi_inited;
+
+static int met_emi_create(struct kobject *parent)
+{
+	int ret = 0;
+	int i;
+
+	for (i = 0; i < 21; i++) {
+		ttype_master_val[i] = BM_MASTER_M0;
+		ttype_nbeat_val[i] = BM_TRANS_TYPE_1BEAT;
+		ttype_nbyte_val[i] = BM_TRANS_TYPE_8Byte;
+		ttype_burst_val[i] = BM_TRANS_TYPE_BURST_INCR;
+		ttype_busid_val[i] = 0xfffff;   /*default disable ttype bus sel if busid > 0xff_ff */
+		ttype_rw_val[i] =  BM_TRANS_RW_DEFAULT;
+	}
+
+	_clear_msel_group_ext();
+	_clear_wsct_rw();
+	_clear_wsct_high_priority_enable();
+	_clear_wsct_busid();
+	_clear_wsct_chn_rank_sel();
+	_clear_wsct_burst_range();
+
+	_clear_tsct_busid_enable();
+	_clear_ttype_high_priority_ext();
+	_clear_ttype_high_priority_ext();
+	_clear_ttype_busid_ext();
+	_clear_ttype_chn_rank_sel();
+	_clear_ttype_burst_range();
+
+	reserve_wsct_setting = 0;
+
+
+	ret = MET_BM_Init();
+	if (ret != 0) {
+		pr_notice("MET_BM_Init failed!!!\n");
+		ret = 0;        /* will retry later */
+	} else {
+		emi_inited = 1;
+	}
+
+	kobj_emi = parent;
+
+#define KOBJ_ATTR_ITEM(attr_name) \
+	do { \
+		ret = sysfs_create_file(kobj_emi, &attr_name ## _attr.attr); \
+		if (ret != 0) { \
+			pr_notice("Failed to create " #attr_name " in sysfs\n"); \
+			return ret; \
+		} \
+	} while (0)
+	KOBJ_ATTR_LIST;
+#undef  KOBJ_ATTR_ITEM
+
+	return ret;
+}
+
+
+static void met_emi_delete(void)
+{
+#define	KOBJ_ATTR_ITEM(attr_name) \
+	sysfs_remove_file(kobj_emi, &attr_name##_attr.attr)
+	if (kobj_emi != NULL) {
+		KOBJ_ATTR_LIST;
+		kobj_emi = NULL;
+	}
+#undef	KOBJ_ATTR_ITEM
+
+	if (emi_inited)
+		MET_BM_DeInit();
+}
+
+
+
+static void met_emi_resume(void)
+{
+	if (!do_emi())
+		return;
+
+	emi_init();
+}
+
+
+static const char help[] = "  --emi                                 monitor EMI banwidth\n";
+static int emi_print_help(char *buf, int len)
+{
+	return snprintf(buf, PAGE_SIZE, help);
+}
+
+
+#define TTYPE_NAME_STR_LEN  64
+/* static char ttype_name[21][TTYPE_NAME_STR_LEN]; */
+static int emi_print_header(char *buf, int len)
+{
+	int ret = 0;
+/*	int ret_m[21]; */
+	int i = 0;
+
+#if 1 /* move to AP side print header */
+/*#ifndef CONFIG_MTK_TINYSYS_SSPM_SUPPORT*/
+	unsigned int dram_data_rate_MHz;
+	unsigned int DRAM_TYPE;
+	unsigned int base_clock_rate;
+#endif
+
+
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+		"met-info [000] 0.0: met_emi_wsct_amount: %d\n",WSCT_AMOUNT);
+
+	/* master selection header */
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+		"met-info [000] 0.0: met_emi_msel: %x,%x,%x\n",
+		msel_group_ext_val[1] & BM_MASTER_ALL,
+		msel_group_ext_val[2] & BM_MASTER_ALL,
+		msel_group_ext_val[3] & BM_MASTER_ALL);
+	
+	/*Ttype RW type header*/
+	PR_BOOTMSG("rwtype=%d\n",rwtype);
+	ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_emi_rw_cfg: ");
+	if (rwtype == BM_READ_ONLY)
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "R");
+	else if (rwtype == BM_WRITE_ONLY)
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "W");
+	else
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "BOTH");
+
+	for (i = 0; i < 21; i++) {
+		if (ttype_rw_val[i] == BM_TRANS_RW_DEFAULT)
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, ",DEFAULT");
+		else if (ttype_rw_val[i] == BM_TRANS_RW_READONLY)
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, ",R");
+		else if (ttype_rw_val[i] == BM_TRANS_RW_WRITEONLY)
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, ",W");
+		else    /*BM_TRANS_RW_RWBOTH*/
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, ",BOTH");
+	}
+	ret += snprintf(buf + ret, PAGE_SIZE - ret, "\n");
+
+	/*ultra header*/
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"met-info [000] 0.0: met_emi_ultra_filter: %x\n", high_priority_filter);
+
+	/* ttype header */
+	if (ttype17_21_en == BM_TTYPE17_21_ENABLE) {
+		int i = 0;
+		int j = 0;
+
+		/* ttype master list */
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_emi_ttype_master_list: ");
+		for (i = 0; i < 21; i++) {
+			for (j = 0; j < ARRAY_SIZE(ttype_master_list_item); j++) {
+				if (ttype_master_val[i] == ttype_master_list_item[j].key) {
+					ret += snprintf(buf + ret, PAGE_SIZE - ret, "%s,", ttype_master_list_item[j].val);
+				}
+			}
+		}
+		/* remove the last comma */
+		snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+		/* ttype busid list */
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_emi_ttype_busid_list: ");
+		for (i = 0; i < 21; i++)
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, "%x,", ttype_busid_val[i]);
+
+		snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+		/* ttype nbeat list */
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_emi_ttype_nbeat_list: ");
+		for (i = 0; i < 21; i++) {
+			for (j = 0; j < ARRAY_SIZE(ttype_nbeat_list_item); j++) {
+				if (ttype_nbeat_val[i] == ttype_nbeat_list_item[j].key) {
+					ret += snprintf(buf + ret, PAGE_SIZE - ret, "%d,", ttype_nbeat_list_item[j].val);
+				}
+			}
+		}
+		snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+		/* ttype nbyte list */
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_emi_ttype_nbyte_list: ");
+		for (i = 0; i < 21; i++) {
+			for (j = 0; j < ARRAY_SIZE(ttype_nbyte_list_item); j++) {
+				if (ttype_nbyte_val[i] == ttype_nbyte_list_item[j].key) {
+					ret += snprintf(buf + ret, PAGE_SIZE - ret, "%d,", ttype_nbyte_list_item[j].val);
+				}
+			}
+		}
+		snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+		/* ttype burst list */
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_emi_ttype_burst_list: ");
+		for (i = 0; i < 21; i++) {
+			for (j = 0; j < ARRAY_SIZE(ttype_burst_list_item); j++) {
+				if (ttype_burst_val[i] == ttype_burst_list_item[j].key) {
+					ret += snprintf(buf + ret, PAGE_SIZE - ret, "%s,", ttype_burst_list_item[j].val);
+				}
+			}
+		}
+		snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+	}
+	/* ttype enable */
+	ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_emi_ttype_enable: %d,%d\n",ttype1_16_en, ttype17_21_en);
+
+
+#if 1 /*SEDA 3.5*/
+
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+		"met-info [000] 0.0: met_emi_msel_ext: %x,%x,%x\n",
+		msel_group_ext_val[0] & BM_MASTER_ALL,
+		msel_group_ext_val[4] & BM_MASTER_ALL,
+		msel_group_ext_val[5] & BM_MASTER_ALL);
+
+	ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_emi_wsct_rw: ");
+
+	for (i=0;i<WSCT_AMOUNT;i++) {
+		if (wsct_rw_val[i] == BM_WSCT_RW_RWBOTH)
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, "RW,");
+		else if (wsct_rw_val[i] == BM_WSCT_RW_READONLY)
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, "R,");
+		else if (wsct_rw_val[i] == BM_WSCT_RW_WRITEONLY)
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, "W,");
+		else    /*disable*/
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, "NONE,");
+	}
+	snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+
+	ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_emi_wsct_HPRI_DIS: ");
+	for (i=0;i<WSCT_AMOUNT;i++) {
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "%d,",WSCT_HPRI_DIS[i]);
+	}
+	snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+
+	ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_emi_wsct_HPRI_SEL: ");
+	for (i=0;i<WSCT_AMOUNT;i++) {
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "%x,",WSCT_HPRI_SEL[i]);
+	}
+	snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+	ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_emi_wsct_busid: ");
+	for (i=0;i<WSCT_AMOUNT;i++) {
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "%x,",wsct_busid_val[i]);
+	}
+	snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+
+	ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_emi_wsct_idMask: ");
+	for (i=0;i<WSCT_AMOUNT;i++) {
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "%x,",wsct_idMask_val[i]);
+	}
+	snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+	ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: wsct_chn_rank_sel: ");
+	for (i=0;i<WSCT_AMOUNT;i++) {
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "%x,",wsct_chn_rank_sel_val[i]);
+	}
+	snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+	ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: wsct_byte_bnd_dis: ");
+	for (i=0;i<WSCT_AMOUNT;i++) {
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "%d,",wsct_byte_bnd_dis[i]);
+	}
+	snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+
+	ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: wsct_byte_low_bnd: ");
+	for (i=0;i<WSCT_AMOUNT;i++) {
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "%x,",wsct_byte_low_bnd_val[i]);
+	}
+	snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+	ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: wsct_byte_up_bnd: ");
+	for (i=0;i<WSCT_AMOUNT;i++) {
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "%x,",wsct_byte_up_bnd_val[i]);
+	}
+	snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+	ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: tsct_busid_enable: ");
+	for (i=0;i<TSCT_AMOUNT;i++) {
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "%d,",tsct_busid_enable_val[i]);
+	}
+	snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+	/***************************** ttype ****************************************/
+	if (ttype17_21_en == BM_TTYPE17_21_ENABLE) {
+
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: TTYPE_HPRI_SEL: ");
+		for (i=0;i<BM_COUNTER_MAX;i++) {
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, "%x,",TTYPE_HPRI_SEL[i]);
+		}
+		snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: ttype_idMask: ");
+		for (i=0;i<BM_COUNTER_MAX;i++) {
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, "%x,",ttype_idMask_val[i]);
+		}
+		snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: ttype_chn_rank_sel: ");
+		for (i=0;i<BM_COUNTER_MAX;i++) {
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, "%x,",ttype_chn_rank_sel_val[i]);
+		}
+		snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: ttype_byte_bnd_dis: ");
+		for (i=0;i<BM_COUNTER_MAX;i++) {
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, "%d,",ttype_byte_bnd_dis[i]);
+		}
+		snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: ttype_byte_low_bnd_val: ");
+		for (i=0;i<BM_COUNTER_MAX;i++) {
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, "%x,",ttype_byte_low_bnd_val[i]);
+		}
+		snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: ttype_byte_up_bnd_val: ");
+		for (i=0;i<BM_COUNTER_MAX;i++) {
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, "%x,",ttype_byte_up_bnd_val[i]);
+		}
+		snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+	}
+#endif
+
+	/*IP version*/
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"met-info [000] 0.0: DRAMC_VER: %d\n", DRAMC_VER);
+
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"met-info [000] 0.0: EMI_VER: %d.%d\n", EMI_VER_MAJOR, EMI_VER_MINOR);
+
+#if 1 /* SEDA3.5 header print move to AP side */
+
+	dram_chann_num = MET_EMI_GetDramChannNum();
+
+	if (!get_cur_ddr_ratio_symbol)
+		PR_BOOTMSG("[%s][%d]get_cur_ddr_ratio_symbol = NULL , use the TYPE_LPDDR4 get_cur_ddr_ratio_symbol\n", __func__, __LINE__);
+
+	if (get_ddr_type_symbol) {
+		DRAM_TYPE = get_ddr_type_symbol();
+
+		base_clock_rate = MET_EMI_Get_BaseClock_Rate();
+
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_dram_type: %d\n", DRAM_TYPE);
+
+		if ((DRAM_TYPE == 2) || (DRAM_TYPE == 3))
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_dram_chann_num_header: %d,%d,%d,%d\n",
+					dram_chann_num, base_clock_rate,
+					DRAM_IO_BUS_WIDTH_LP4, DRAM_DATARATE);
+		else
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_dram_chann_num_header: %d,%d,%d,%d\n",
+					dram_chann_num, base_clock_rate,
+					DRAM_IO_BUS_WIDTH_LP3, DRAM_DATARATE);
+	} else
+		METERROR("[%s][%d]get_ddr_type_symbol = NULL , use the TYPE_LPDDR3 setting\n", __func__, __LINE__);
+
+
+	/* met_emi_clockrate */
+	if (get_dram_data_rate_symbol) {
+		dram_data_rate_MHz = get_dram_data_rate_symbol();
+	} else {
+		METERROR("get_dram_data_rate_symbol = NULL\n");
+		dram_data_rate_MHz = 0;
+	}
+
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"met-info [000] 0.0: met_dram_clockrate: %d\n",
+			dram_data_rate_MHz);
+
+
+	/*dram bank num*/
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"met-info [000] 0.0: met_dram_rank_num_header: %u,%u\n", MET_EMI_GetDramRankNum(),
+				MET_EMI_GetDramRankNum());
+#if 0
+	/* ms_emi header */
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"# ms_emi: TS0,TS1,GP0_WSCT,GP1_WSCT,GP2_WSCT,GP3_WSCT,");
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"M0_LATENCY,M1_LATENCY,M2_LATENCY,M3_LATENCY,M4_LATENCY,M5_LATENCY,M6_LATENCY,M7_LATENCY,");
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"M0_TRANS,M1_TRANS,M2_TRANS,M3_TRANS,M4_TRANS,M5_TRANS,M6_TRANS,M7_TRANS,");
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"BACT,BSCT,BCNT,WACT,DCM_CTRL,TACT,");
+
+	for (i = 0; i < dram_chann_num; i++) {
+		if (i != 0)
+			ret += snprintf(buf + ret, PAGE_SIZE - ret,
+					",");
+		ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"PageHit_%d,PageMiss_%d,InterBank_%d,Idle_%d,", i, i, i, i);
+		ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"mr4_%d,refresh_pop_%d,freerun_26m_%d,", i, i, i);
+		ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"read_bytes_%d,write_bytes_%d", i, i);
+	}
+	ret += snprintf(buf + ret, PAGE_SIZE - ret, "\n");
+#endif
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"met-info [000] 0.0: met_emi_header: TS0,TS1,GP0_WSCT,GP1_WSCT,GP2_WSCT,GP3_WSCT,");
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"M0_LATENCY,M1_LATENCY,M2_LATENCY,M3_LATENCY,M4_LATENCY,M5_LATENCY,M6_LATENCY,M7_LATENCY,");
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"M0_TRANS,M1_TRANS,M2_TRANS,M3_TRANS,M4_TRANS,M5_TRANS,M6_TRANS,M7_TRANS,");
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"BACT,BSCT,BCNT,WACT,DCM_CTRL,TACT,");
+
+	for (i = 0; i < dram_chann_num; i++) {
+		if (i != 0)
+			ret += snprintf(buf + ret, PAGE_SIZE - ret,
+					",");
+		ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"PageHit_%d,PageMiss_%d,InterBank_%d,Idle_%d,", i, i, i, i);
+		ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"mr4_%d,refresh_pop_%d,freerun_26m_%d,", i, i, i);
+		ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"read_bytes_%d,write_bytes_%d", i, i);
+	}
+	ret += snprintf(buf + ret, PAGE_SIZE - ret, "\n");
+
+	/*TSCT header*/
+	if (emi_tsct_enable == 1) {
+		ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"met-info [000] 0.0: ms_emi_tsct_header: ms_emi_tsct,");
+		ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"tsct1,tsct2,tsct3\n");
+	}
+
+	/*MDCT header*/
+	if (emi_mdct_enable == 1) {
+		ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"met-info [000] 0.0: ms_emi_mdct_header: ms_emi_mdct,");
+		ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"RD_ULTRA,RD_MDMCU\n");
+	}
+#if 0
+	/* met_bw_limiter_header */
+	if (bw_limiter_enable == BM_BW_LIMITER_ENABLE) {
+		ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"met-info [000] 0.0: met_bw_limiter_header: CLK,");
+		ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"ARBA,ARBB,ARBC,ARBD,ARBE,ARBF,ARBG,ARBH,BWCT0,BWCT1,BWCT2,BWCT3,BWCT4,BWST0,BWST1,BWCT0_2ND,BWCT1_2ND,BWST_2ND\n");
+	}
+#endif
+	/* met_bw_rgtor_header */
+
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,	"met-info [000] 0.0: met_bw_rgtor_unit_header: %x\n", MET_EMI_Get_CONH_2ND());
+
+#if 0
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"met-info [000] 0.0: met_bw_rgtor_bgt_header: ");
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"EMI_RGU_BGT_1ST_W_01,EMI_RGU_BGT_1ST_W_2,EMI_RGU_BGT_1ST_W_3,EMI_RGU_BGT_1ST_W_4,EMI_RGU_BGT_1ST_W_5,EMI_RGU_BGT_1ST_W_67,");
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"EMI_RGU_BGT_2ND_W_01,EMI_RGU_BGT_2ND_W_2,EMI_RGU_BGT_2ND_W_3,EMI_RGU_BGT_2ND_W_4,EMI_RGU_BGT_2ND_W_5,EMI_RGU_BGT_2ND_W_67,");
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"EMI_RGU_BGT_1ST_R_01,EMI_RGU_BGT_1ST_R_2,EMI_RGU_BGT_1ST_R_3,EMI_RGU_BGT_1ST_R_4,EMI_RGU_BGT_1ST_R_5,EMI_RGU_BGT_1ST_R_67,");
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"EMI_RGU_BGT_2ND_R_01,EMI_RGU_BGT_2ND_R_2,EMI_RGU_BGT_2ND_R_3,EMI_RGU_BGT_2ND_R_4,EMI_RGU_BGT_2ND_R_5,EMI_RGU_BGT_2ND_R_67\n");
+
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"met-info [000] 0.0: met_bw_rgtor_upbnd_header: ");
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"EMI_RGU_UPBND_1ST_W_01,EMI_RGU_UPBND_1ST_W_2,EMI_RGU_UPBND_1ST_W_3,EMI_RGU_UPBND_1ST_W_4,EMI_RGU_UPBND_1ST_W_5,EMI_RGU_UPBND_1ST_W_67,");
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"EMI_RGU_UPBND_2ND_W_01,EMI_RGU_UPBND_2ND_W_2,EMI_RGU_UPBND_2ND_W_3,EMI_RGU_UPBND_2ND_W_4,EMI_RGU_UPBND_2ND_W_5,EMI_RGU_UPBND_2ND_W_67,");
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"EMI_RGU_UPBND_1ST_R_01,EMI_RGU_UPBND_1ST_R_2,EMI_RGU_UPBND_1ST_R_3,EMI_RGU_UPBND_1ST_R_4,EMI_RGU_UPBND_1ST_R_5,EMI_RGU_UPBND_1ST_R_67,");
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"EMI_RGU_UPBND_2ND_R_01,EMI_RGU_UPBND_2ND_R_2,EMI_RGU_UPBND_2ND_R_3,EMI_RGU_UPBND_2ND_R_4,EMI_RGU_UPBND_2ND_R_5,EMI_RGU_UPBND_2ND_R_67\n");
+#endif
+
+	/* DRAM DVFS header */
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"met-info [000] 0.0: DRAM_DVFS_header: datarate(MHz)\n");
+
+	/*PDIR met_dramc_header*/
+	if (dramc_pdir_enable == 1 && DRAMC_VER >= 2 ) {
+		ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"met-info [000] 0.0: met_dramc_header: ");
+		for (i = 0; i < dram_chann_num; i++) {
+			if (i != 0)
+				ret += snprintf(buf + ret, PAGE_SIZE - ret,
+						",");
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, "freerun_26m_%d,", i);
+			ret += snprintf(buf + ret, PAGE_SIZE - ret,
+					"rk0_pre_sb_%d,rk0_pre_pd_%d,rk0_act_sb_%d,rk0_act_pd_%d,", i, i, i, i);
+			ret += snprintf(buf + ret, PAGE_SIZE - ret,
+					"rk1_pre_sb_%d,rk1_pre_pd_%d,rk1_act_sb_%d,rk1_act_pd_%d,", i, i, i, i);
+			ret += snprintf(buf + ret, PAGE_SIZE - ret,
+					"rk2_pre_sb_%d,rk2_pre_pd_%d,rk2_act_sb_%d,rk2_act_pd_%d", i, i, i, i);
+		}
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "\n");
+	}
+
+	/* DRS header */
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"met-info [000] 0.0: emi_drs_header: ch0_RANK1_GP(%%),ch0_RANK1_SF(%%),ch0_ALL_SF(%%),ch1_RANK1_GP(%%),ch1_RANK1_SF(%%),ch1_ALL_SF(%%)\n");
+#endif
+
+	return ret;
+}
+
+
+
+static int ondiemet_emi_print_header(char *buf, int len)
+{
+	return emi_print_header(buf, len);
+}
+
+
+
+static void MET_BM_IPI_REGISTER_CB(void)
+{
+	int ret, i;
+	unsigned int rdata;
+	unsigned int ipi_buf[4];
+
+	for (i = 0; i < 4; i++)
+		ipi_buf[i] = 0;
+
+	if (sspm_buf_available == 1) {
+		ipi_buf[0] = MET_MAIN_ID | (MID_EMI << MID_BIT_SHIFT) | MET_ARGU | SET_REGISTER_CB;
+		ret = met_ipi_to_sspm_command((void *)ipi_buf, 0, &rdata, 1);
+	}
+}
+
+
+static void MET_BM_IPI_configs(void)
+{
+	int ret, i;
+	unsigned int rdata;
+	unsigned int ipi_buf[4];
+
+	for (i = 0; i < 4; i++)
+		ipi_buf[i] = 0;
+
+	if (sspm_buf_available == 1) {
+		ipi_buf[0] = MET_MAIN_ID | (MID_EMI << MID_BIT_SHIFT) | MET_ARGU | SET_EBM_CONFIGS1;
+		ipi_buf[2] = EMI_VER_MAJOR << 24 | EMI_VER_MINOR << 16 | DRAMC_VER << 8 | 0;
+		ret = met_ipi_to_sspm_command((void *)ipi_buf, 0, &rdata, 1);
+	}
+}
+
+
+static void ondiemet_emi_start(void)
+{
+	MET_BM_IPI_REGISTER_CB();
+	if (!emi_inited) {
+		if (MET_BM_Init() != 0) {
+			met_sspm_emi.mode = 0;
+			pr_notice("MET_BM_Init failed!!!\n");
+			return;
+		}
+		emi_inited = 1;
+	}
+	MET_BM_IPI_configs();
+
+	if (do_emi())
+		emi_init();
+
+	ondiemet_module[ONDIEMET_SSPM] |= ID_EMI;
+}
+
+static void ondiemet_emi_stop(void)
+{
+	if (!emi_inited)
+		return;
+
+	if (do_emi())
+		emi_uninit();
+}
+
+
+
+struct metdevice met_sspm_emi = {
+	.name			= "emi",
+	.owner			= THIS_MODULE,
+	.type			= MET_TYPE_BUS,
+	.create_subfs		= met_emi_create,
+	.delete_subfs		= met_emi_delete,
+	.resume			= met_emi_resume,
+#if defined(CONFIG_MTK_TINYSYS_SSPM_SUPPORT) && defined(ONDIEMET_SUPPORT)
+	.ondiemet_start		= ondiemet_emi_start,
+	.ondiemet_stop		= ondiemet_emi_stop,
+	.ondiemet_print_help	= emi_print_help,
+	.ondiemet_print_header	= ondiemet_emi_print_header,
+#endif
+	.ondiemet_mode		= 1,
+};
+EXPORT_SYMBOL(met_sspm_emi);
\ No newline at end of file
diff --git a/src/devtools/met-driver/4.19/common/emi/SEDA3_5/mtk_emi_bm.c b/src/devtools/met-driver/4.19/common/emi/SEDA3_5/mtk_emi_bm.c
new file mode 100644
index 0000000..b1a1b02
--- /dev/null
+++ b/src/devtools/met-driver/4.19/common/emi/SEDA3_5/mtk_emi_bm.c
@@ -0,0 +1,803 @@
+// SPDX-License-Identifier: GPL-2.0 
+/*
+ * Copyright (C) 2019 MediaTek Inc.
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/io.h>
+#include <mt-plat/sync_write.h>
+#include <mt-plat/mtk_io.h>
+#include "mtk_typedefs.h"
+#include "core_plf_init.h"
+#include "mtk_emi_bm.h"
+
+#include "met_drv.h"
+#include "interface.h"
+
+#undef	DEBUG
+#undef	debug_reg
+#ifdef	debug_reg
+static inline unsigned int emi_readl(void __iomem *padr)
+{
+	unsigned int tmp;
+
+	tmp = readl(padr);
+	MET_TRACE("[MET_EMI] RD_Reg: %p: %08x\n", padr, tmp);
+	return tmp;
+}
+
+static inline void __emi_reg_sync_writel(unsigned int data, void __iomem *padr)
+{
+	unsigned int tmp;
+
+	mt_reg_sync_writel(data, padr);
+	tmp = readl(padr);
+	MET_TRACE("[MET_EMI] WR_Reg: %p: %08x, %08x\n", padr, data, tmp);
+}
+
+#define emi_reg_sync_writel(data, adr)  __emi_reg_sync_writel(data, IOMEM(adr))
+
+#else
+#define emi_readl               readl
+#define emi_reg_sync_writel     mt_reg_sync_writel
+#endif
+
+#define MASK_MASTER     0xFF
+#define MASK_TRANS_TYPE 0xFF
+
+/* static int dram_chann_num; */
+static void __iomem *BaseAddrEMI;
+/* static void __iomem *BaseAddrCHN_EMI[2]; */
+
+/* static int dramc0_dcm_enable;
+static int dramc1_dcm_enable; */
+
+#define CH0_MISC_CG_CTRL0 (((unsigned long) BaseAddrDDRPHY_AO[0]) + 0x284)
+#define CH1_MISC_CG_CTRL0 (((unsigned long) BaseAddrDDRPHY_AO[1]) + 0x284)
+const unsigned int emi_config[] = {
+	EMI_BMEN,
+	EMI_MSEL,
+	EMI_MSEL2,
+	EMI_MSEL3,
+	EMI_MSEL4,
+	EMI_MSEL5,
+	EMI_MSEL6,
+	EMI_MSEL7,
+	EMI_MSEL8,
+	EMI_MSEL9,
+	EMI_MSEL10,
+	EMI_BMID0,
+	EMI_BMID1,
+	EMI_BMID2,
+	EMI_BMID3,
+	EMI_BMID4,
+	EMI_BMID5,
+	EMI_BMID6,
+	EMI_BMID7,
+	EMI_BMID8,
+	EMI_BMID9,
+	EMI_BMID10,
+	EMI_BMEN1,
+	EMI_BMEN2,
+	EMI_BMRW0,
+	EMI_BMRW1,
+	EMI_DBWA,
+	EMI_DBWB,
+	EMI_DBWC,
+	EMI_DBWD,
+	EMI_DBWE,
+	EMI_DBWF,
+	EMI_DBWI,
+	EMI_DBWJ,
+	EMI_DBWK,
+	EMI_DBWA_2ND,
+	EMI_DBWB_2ND,
+	EMI_DBWC_2ND,
+	EMI_DBWD_2ND,
+	EMI_DBWE_2ND,
+	EMI_DBWF_2ND,
+	EMI_TTYPE1_CONA,
+	EMI_TTYPE1_CONB,
+	EMI_TTYPE2_CONA,
+	EMI_TTYPE2_CONB,
+	EMI_TTYPE3_CONA,
+	EMI_TTYPE3_CONB,
+	EMI_TTYPE4_CONA,
+	EMI_TTYPE4_CONB,
+	EMI_TTYPE5_CONA,
+	EMI_TTYPE5_CONB,
+	EMI_TTYPE6_CONA,
+	EMI_TTYPE6_CONB,
+	EMI_TTYPE7_CONA,
+	EMI_TTYPE7_CONB,
+	EMI_TTYPE8_CONA,
+	EMI_TTYPE8_CONB,
+	EMI_TTYPE9_CONA,
+	EMI_TTYPE9_CONB,
+	EMI_TTYPE10_CONA,
+	EMI_TTYPE10_CONB,
+	EMI_TTYPE11_CONA,
+	EMI_TTYPE11_CONB,
+	EMI_TTYPE12_CONA,
+	EMI_TTYPE12_CONB,
+	EMI_TTYPE13_CONA,
+	EMI_TTYPE13_CONB,
+	EMI_TTYPE14_CONA,
+	EMI_TTYPE14_CONB,
+	EMI_TTYPE15_CONA,
+	EMI_TTYPE15_CONB,
+	EMI_TTYPE16_CONA,
+	EMI_TTYPE16_CONB,
+	EMI_TTYPE17_CONA,
+	EMI_TTYPE17_CONB,
+	EMI_TTYPE18_CONA,
+	EMI_TTYPE18_CONB,
+	EMI_TTYPE19_CONA,
+	EMI_TTYPE19_CONB,
+	EMI_TTYPE20_CONA,
+	EMI_TTYPE20_CONB,
+	EMI_TTYPE21_CONA,
+	EMI_TTYPE21_CONB
+};
+#define EMI_CONFIG_MX_NR (sizeof(emi_config)/sizeof(unsigned int))
+static unsigned int emi_config_val[EMI_CONFIG_MX_NR];
+
+
+
+static inline void MET_REG_BCLR(unsigned long reg, u32 shift)
+{
+	unsigned int read_val = 0;
+
+	read_val = emi_readl(IOMEM(reg));
+	emi_reg_sync_writel(read_val & (~((1 << shift) & 0xFFFFFFFF)), reg);
+}
+
+
+int MET_BM_Init(void)
+{
+	/*emi*/
+	if (mt_cen_emi_base_get_symbol) {
+		BaseAddrEMI = mt_cen_emi_base_get_symbol();
+	} else {
+		pr_debug("mt_cen_emi_base_get_symbol = NULL\n");
+		PR_BOOTMSG_ONCE("mt_cen_emi_base_get_symbol = NULL\n");
+		BaseAddrEMI = 0;
+	}
+
+	if (BaseAddrEMI == 0) {
+		pr_debug("BaseAddrEMI = 0\n");
+		PR_BOOTMSG_ONCE("BaseAddrEMI = 0\n");
+		return -1;
+	}
+	pr_debug("MET EMI: map emi to %p\n", BaseAddrEMI);
+	PR_BOOTMSG("MET EMI: map emi to %p\n", BaseAddrEMI);
+
+	return 0;
+}
+
+
+void MET_BM_DeInit(void)
+{
+}
+
+
+void MET_BM_SaveCfg(void)
+{
+	int i;
+
+	for (i = 0; i < EMI_CONFIG_MX_NR; i++)
+		emi_config_val[i] = emi_readl(IOMEM(ADDR_EMI + emi_config[i]));
+}
+
+
+void MET_BM_RestoreCfg(void)
+{
+	int i;
+
+	for (i = 0; i < EMI_CONFIG_MX_NR; i++)
+		emi_reg_sync_writel(emi_config_val[i], ADDR_EMI + emi_config[i]);
+}
+
+
+
+
+
+
+
+void MET_BM_SetReadWriteType(const unsigned int ReadWriteType)
+{
+	const unsigned int value = emi_readl(IOMEM(ADDR_EMI + EMI_BMEN));
+
+	/*
+	 * ReadWriteType: 00/11 --> both R/W
+	 *                   01 --> only R
+	 *                   10 --> only W
+	 */
+	emi_reg_sync_writel((value & 0xFFFFFFCF) | (ReadWriteType << 4), ADDR_EMI + EMI_BMEN);
+}
+
+
+
+
+int MET_BM_SetMonitorCounter(const unsigned int counter_num,
+			     const unsigned int master, const unsigned int trans_type)
+{
+	unsigned int value, addr;
+	const unsigned int iMask = (MASK_TRANS_TYPE << 8) | MASK_MASTER;
+
+	if (counter_num < 1 || counter_num > BM_COUNTER_MAX)
+		return BM_ERR_WRONG_REQ;
+
+
+	if (counter_num == 1) {
+		addr = EMI_BMEN;
+		value = (emi_readl(IOMEM(ADDR_EMI + addr)) & ~(iMask << 16)) |
+		    ((trans_type & MASK_TRANS_TYPE) << 24) | ((master & MASK_MASTER) << 16);
+	} else {
+		addr = (counter_num <= 3) ? EMI_MSEL : (EMI_MSEL2 + (counter_num / 2 - 2) * 8);
+
+		/* clear master and transaction type fields */
+		value = emi_readl(IOMEM(ADDR_EMI + addr)) & ~(iMask << ((counter_num % 2) * 16));
+
+		/* set master and transaction type fields */
+		value |= (((trans_type & MASK_TRANS_TYPE) << 8) |
+			  (master & MASK_MASTER)) << ((counter_num % 2) * 16);
+	}
+
+	emi_reg_sync_writel(value, ADDR_EMI + addr);
+
+	return BM_REQ_OK;
+}
+
+
+int MET_BM_SetTtypeCounterRW(unsigned int bmrw0_val, unsigned int bmrw1_val)
+{
+
+	unsigned int value_origin;
+
+	value_origin = emi_readl(IOMEM(ADDR_EMI + EMI_BMRW0));
+	MET_TRACE("[MET_EMI_settype1] value_origin: %x\n", value_origin);
+	if (value_origin != bmrw0_val) {
+		emi_reg_sync_writel(bmrw0_val, ADDR_EMI + EMI_BMRW0);
+		MET_TRACE("[MET_EMI_settype1] bmrw0_val: %x, value_origin: %x\n", bmrw0_val,
+			   value_origin);
+	}
+
+
+	value_origin = emi_readl(IOMEM(ADDR_EMI + EMI_BMRW1));
+	MET_TRACE("[MET_EMI_settype2] value_origin: %x\n", value_origin);
+	if (value_origin != bmrw1_val) {
+		emi_reg_sync_writel(bmrw1_val, ADDR_EMI + EMI_BMRW1);
+		MET_TRACE("[MET_EMI_settype2] bmrw0_val: %x, value_origin: %x\n", bmrw1_val,
+			   value_origin);
+
+	}
+	return BM_REQ_OK;
+}
+
+
+int MET_BM_Set_WsctTsct_id_sel(unsigned int counter_num, unsigned int enable)
+{
+	unsigned int value;
+
+	if (counter_num > 3)
+		return BM_ERR_WRONG_REQ;
+
+	value =
+	    ((emi_readl(IOMEM(ADDR_EMI + EMI_BMEN2)) & (~(1 << (28 + counter_num)))) |
+	     (enable << (28 + counter_num)));
+	emi_reg_sync_writel(value, ADDR_EMI + EMI_BMEN2);
+
+	return BM_REQ_OK;
+}
+
+
+int MET_BM_SetMaster(const unsigned int counter_num, const unsigned int master)
+{
+	unsigned int value, addr;
+	const unsigned int iMask = 0x7F;
+
+	if (counter_num < 1 || counter_num > BM_COUNTER_MAX)
+		return BM_ERR_WRONG_REQ;
+
+
+	if (counter_num == 1) {
+		addr = EMI_BMEN;
+		value =
+		    (emi_readl(IOMEM(ADDR_EMI + addr)) & ~(iMask << 16)) | ((master & iMask) << 16);
+	} else {
+		addr = (counter_num <= 3) ? EMI_MSEL : (EMI_MSEL2 + (counter_num / 2 - 2) * 8);
+
+		/* clear master and transaction type fields */
+		value = emi_readl(IOMEM(ADDR_EMI + addr)) & ~(iMask << ((counter_num % 2) * 16));
+
+		/* set master and transaction type fields */
+		value |= ((master & iMask) << ((counter_num % 2) * 16));
+	}
+
+	emi_reg_sync_writel(value, ADDR_EMI + addr);
+
+	return BM_REQ_OK;
+}
+
+
+int MET_BM_SetbusID_En(const unsigned int counter_num,
+		       const unsigned int enable)
+{
+	unsigned int value;
+
+	if ((counter_num < 1 || counter_num > BM_COUNTER_MAX) || (enable > 1))
+		return BM_ERR_WRONG_REQ;
+
+	if (enable == 0) {
+		/* clear  EMI ID selection Enabling   SEL_ID_EN */
+		value = (emi_readl(IOMEM(ADDR_EMI + EMI_BMEN2))
+			 & ~(1 << (counter_num - 1)));
+	} else {
+		/* enable  EMI ID selection Enabling   SEL_ID_EN */
+		value = (emi_readl(IOMEM(ADDR_EMI + EMI_BMEN2))
+			 | (1 << (counter_num - 1)));
+	}
+	emi_reg_sync_writel(value, ADDR_EMI + EMI_BMEN2);
+
+	return BM_REQ_OK;
+}
+
+
+int MET_BM_SetbusID(const unsigned int counter_num,
+		    const unsigned int id)
+{
+	unsigned int value, addr, shift_num;
+
+	if ((counter_num < 1 || counter_num > BM_COUNTER_MAX))
+		return BM_ERR_WRONG_REQ;
+
+	/* offset of EMI_BMIDx register */
+	addr = EMI_BMID0 + (counter_num - 1) / 2 * 4;
+	shift_num = ((counter_num - 1) % 2) * 16;
+	/* clear SELx_ID field */
+	value = emi_readl(IOMEM(ADDR_EMI + addr)) & ~(EMI_BMID_MASK << shift_num);
+
+	/* set SELx_ID field */
+	if (id <= 0xffff)       /*bigger then 0xff_ff : no select busid in master, reset busid as 0*/
+		value |= id << shift_num;
+
+	emi_reg_sync_writel(value, ADDR_EMI + addr);
+
+	return BM_REQ_OK;
+}
+
+
+int MET_BM_SetUltraHighFilter(const unsigned int counter_num, const unsigned int enable)
+{
+	unsigned int value;
+
+	if ((counter_num < 1 || counter_num > BM_COUNTER_MAX) || (enable > 1))
+		return BM_ERR_WRONG_REQ;
+
+
+	value = (emi_readl(IOMEM(ADDR_EMI + EMI_BMEN1))
+		 & ~(1 << (counter_num - 1)))
+		| (enable << (counter_num - 1));
+
+	emi_reg_sync_writel(value, ADDR_EMI + EMI_BMEN1);
+
+	return BM_REQ_OK;
+}
+
+
+int MET_BM_SetLatencyCounter(unsigned int enable)
+{
+	unsigned int value;
+
+	value = emi_readl(IOMEM(ADDR_EMI + EMI_BMEN2)) & ~(0x3 << 24);
+	/*
+	 * emi_ttype1 -- emi_ttype8 change as total latencies
+	 * for m0 -- m7,
+	 * and emi_ttype9 -- emi_ttype16 change as total transaction counts
+	 * for m0 -- m7
+	 */
+	if (enable == 1)
+		value |= (0x2 << 24);
+
+	emi_reg_sync_writel(value, ADDR_EMI + EMI_BMEN2);
+
+	return BM_REQ_OK;
+}
+
+
+
+unsigned int MET_EMI_GetDramChannNum(void)
+{
+	int num = -1;
+
+	if (BaseAddrEMI) {
+		num = emi_readl(IOMEM(ADDR_EMI + EMI_CONA));
+		num = ((num >> 8) & 0x0000003);
+	} else {
+		return 1;
+	}
+
+	if (num == M0_DOUBLE_HALF_BW_1CH)
+		return 1;
+	else if (num == M0_DOUBLE_HALF_BW_2CH)
+		return 2;
+	else if (num == M0_DOUBLE_HALF_BW_4CH)
+		return 4;
+	else                    /* default return single channel */
+		return 1;
+}
+
+
+unsigned int MET_EMI_GetDramRankNum(void)
+{
+	int dual_rank = 0;
+
+	if (BaseAddrEMI) {
+		dual_rank = emi_readl(IOMEM(ADDR_EMI + EMI_CONA));
+		dual_rank = ((dual_rank >> 17) & RANK_MASK);
+	} else {
+		return DUAL_RANK;
+	}
+
+	if (dual_rank == DISABLE_DUAL_RANK_MODE)
+		return ONE_RANK;
+	else			/* default return dual rank */
+		return DUAL_RANK;
+}
+
+
+unsigned int MET_EMI_GetDramRankNum_CHN1(void)
+{
+	int dual_rank = 0;
+
+	if (BaseAddrEMI) {
+		dual_rank = emi_readl(IOMEM(ADDR_EMI + EMI_CONA));
+		dual_rank = ((dual_rank >> 16) & RANK_MASK);
+	} else {
+		return DUAL_RANK;
+	}
+
+	if (dual_rank == DISABLE_DUAL_RANK_MODE)
+		return ONE_RANK;
+	else			/* default return dual rank */
+		return DUAL_RANK;
+}
+
+
+
+unsigned int MET_EMI_Get_BaseClock_Rate(void)
+{
+	unsigned int DRAM_TYPE;
+
+	if (get_cur_ddr_ratio_symbol)
+		return get_cur_ddr_ratio_symbol();
+	else {
+
+		if (get_ddr_type_symbol) {	
+			DRAM_TYPE = get_ddr_type_symbol();
+
+			if ((DRAM_TYPE == 2) || (DRAM_TYPE == 3))
+				return DRAM_EMI_BASECLOCK_RATE_LP4;
+			else
+				return DRAM_EMI_BASECLOCK_RATE_LP3;
+
+		} else {
+			return DRAM_EMI_BASECLOCK_RATE_LP4;
+		}
+	}
+}
+
+/* For SEDA3.5 wsct setting*/
+/* EMI_DBWX[15:8],    X=A~F   (SEL_MASTER) */
+/* RW:    EMI_DBWX[1:0],    X=A~F */
+int MET_BM_SetWSCT_master_rw(unsigned int *master , unsigned int *rw)
+{
+	unsigned int value, addr;
+	int i;
+
+	const unsigned int Mask_master = 0xFF;
+	const unsigned int offset_master = 8;
+
+	const unsigned int Mask_rw = 0x3;
+	const unsigned int offset_rw = 0;
+
+	for (i=0; i<WSCT_AMOUNT; i++) {
+		addr = EMI_DBWA + i*4;
+		value = emi_readl(IOMEM(ADDR_EMI + addr));
+
+		value = (value & ~(Mask_master << offset_master)) | ((*(master+i) & Mask_master) << offset_master);
+		value = (value & ~(Mask_rw << offset_rw)) | ((*(rw+i) & Mask_rw) << offset_rw);
+
+
+		emi_reg_sync_writel(value, ADDR_EMI + addr);
+	}
+
+	return BM_REQ_OK;
+}
+
+int MET_BM_SetWSCT_high_priority(unsigned int *disable, unsigned int *select)
+{
+	unsigned int value, addr;
+	int i;
+
+	const unsigned int Mask_disable = 0x1;
+	const unsigned int offset_disable  = 2;
+
+	const unsigned int Mask_select = 0xF;
+	const unsigned int offset_select  = 28;
+
+	for (i=0;i<WSCT_AMOUNT;i++) {
+		addr = EMI_DBWA + i*4;
+		value = emi_readl(IOMEM(ADDR_EMI + addr));
+		value = (value & ~(Mask_disable << offset_disable)) | ((*(disable+i) & Mask_disable) << offset_disable);
+		emi_reg_sync_writel(value, ADDR_EMI + addr);
+
+		/* ultra level setting */
+		addr = EMI_DBWA_2ND + i*4;
+		value = emi_readl(IOMEM(ADDR_EMI + addr));
+		value = (value & ~(Mask_select << offset_select)) | ((*(select+i) & Mask_select) << offset_select);
+		emi_reg_sync_writel(value, ADDR_EMI + addr);
+	}
+	return BM_REQ_OK;
+}
+
+/* busid enbale: EMI_DBWX[3],    X=A~F */
+/* busid sel: EMI_DBWX[28:16],    X=A~F  (SEL_ID_TMP) */
+/* busid mask : EMI_DBWY[12:0]  或 EMI_DBWY[28:16],  Y=I~K    (SEL_ID_MSK) */
+int MET_BM_SetWSCT_busid_idmask(unsigned int *busid, unsigned int *idMask)
+{
+	unsigned int value, addr;
+	unsigned int enable_tmp, busid_tmp, idmask_tmp; 
+	int i;
+
+	const unsigned int Mask_busid = 0x1FFF;
+	const unsigned int offset_busid  = 16;
+
+	const unsigned int Mask_enable = 0x1;
+	const unsigned int offset_enable  = 3;
+
+	const unsigned int Mask_idMask = 0x1FFF;
+	const unsigned int offset_idMask_even  = 0;
+	const unsigned int offset_idMask_odd  = 16;
+
+	for (i=0;i<WSCT_AMOUNT;i++) {
+
+		/*enable, SEL_ID_TMP*/
+		if (*(busid+i)>0xffff) {
+			enable_tmp = 0;
+			busid_tmp = 0x1FFF;
+			idmask_tmp = 0x1FFF;
+		}
+		else {
+			enable_tmp = 1;
+			busid_tmp = *(busid+i) & Mask_busid;
+			idmask_tmp = *(idMask+i) & Mask_idMask;
+		}
+
+		
+		addr = EMI_DBWA + i*4;
+		value = emi_readl(IOMEM(ADDR_EMI + addr));
+
+		value = (value & ~(Mask_busid << offset_busid)) | (busid_tmp << offset_busid);
+		value = (value & ~(Mask_enable << offset_enable)) | (enable_tmp << offset_enable);
+
+		emi_reg_sync_writel(value, ADDR_EMI + addr);
+
+		/*SEL_ID_MSK*/
+		addr = EMI_DBWI + (i/2)*4;
+
+		value = emi_readl(IOMEM(ADDR_EMI + addr));
+
+		if (i%2==0)
+			value = (value & ~(Mask_idMask << offset_idMask_even)) | (idmask_tmp << offset_idMask_even);
+		else
+			value = (value & ~(Mask_idMask << offset_idMask_odd)) | (idmask_tmp << offset_idMask_odd);
+
+		emi_reg_sync_writel(value, ADDR_EMI + addr);
+	}
+
+
+	return BM_REQ_OK;
+}
+
+
+int MET_BM_SetWSCT_chn_rank_sel(unsigned int *chn_rank_sel)
+{
+	unsigned int value, addr;
+	int i;
+
+	const unsigned int Mask = 0xF;
+	const unsigned int offset  = 12;
+
+
+	for (i=0;i<WSCT_AMOUNT;i++) {
+		addr = EMI_DBWA_2ND + i*4;
+		value = emi_readl(IOMEM(ADDR_EMI + addr));
+
+		value = (value & ~(Mask << offset)) | ((*(chn_rank_sel+i) & Mask) << offset);
+
+		emi_reg_sync_writel(value, ADDR_EMI + addr);
+	}
+	return BM_REQ_OK;
+}
+
+int MET_BM_SetWSCT_burst_range(unsigned int *bnd_dis, unsigned int *low_bnd, unsigned int *up_bnd)
+{
+	unsigned int value, addr;
+	int i;
+
+	const unsigned int Mask_dis = 0x1, Mask_low_bnd = 0x1FF, Mask_up_bnd = 0x1FF;
+	const unsigned int offset_dis = 4, offset_low_bnd = 16 , offset_up_bnd = 0 ;
+
+
+	for (i=0;i<WSCT_AMOUNT;i++) {
+
+		addr = EMI_DBWA + i*4;
+		value = emi_readl(IOMEM(ADDR_EMI + addr));
+
+		value = (value & ~(Mask_dis << offset_dis)) | ((*(bnd_dis+i) & Mask_dis) << offset_dis);
+
+		emi_reg_sync_writel(value, ADDR_EMI + addr);
+
+
+		addr = EMI_DBWA_2ND + i*4;
+		value = emi_readl(IOMEM(ADDR_EMI + addr));
+
+		value = (value & ~(Mask_low_bnd << offset_low_bnd)) | ((*(low_bnd+i) & Mask_low_bnd) << offset_low_bnd);
+		value = (value & ~(Mask_up_bnd << offset_up_bnd)) | ((*(up_bnd+i) & Mask_up_bnd) << offset_up_bnd);
+
+		emi_reg_sync_writel(value, ADDR_EMI + addr);
+	}
+	return BM_REQ_OK;
+
+}
+
+int MET_BM_SetTSCT_busid_enable(unsigned int *enable)
+{
+	//MET_BM_Set_WsctTsct_id_sel
+
+	int i;
+
+	for (i=0;i<TSCT_AMOUNT;i++) {
+		MET_BM_Set_WsctTsct_id_sel(i, *(enable+i));
+	}
+
+	return BM_REQ_OK;
+}
+
+//use the origin together, MET_BM_SetUltraHighFilter()
+/* EMI_TTYPEN_CONA [23:20],  N=1~21   (HPRI_SEL) */
+int MET_BM_SetTtype_high_priority_sel(unsigned int _high_priority_filter, unsigned int *select)
+{
+	int i;
+	unsigned int enable;
+	unsigned int value, addr;
+
+	const unsigned int Mask_sel = 0xF;
+	const unsigned int offset_sel = 20;
+
+	for (i = 0; i < BM_COUNTER_MAX; i++) {
+		if ((_high_priority_filter & (1 << i)) == 0){
+			enable = 0;
+		}
+		else {
+			enable = 1;
+		}
+
+		MET_BM_SetUltraHighFilter(i + 1, enable);
+
+		/* ultra level select */
+		addr = EMI_TTYPE1_CONA + i*8;
+		value = emi_readl(IOMEM(ADDR_EMI + addr));
+
+		value = (value & ~(Mask_sel << offset_sel)) | ((*(select+i) & Mask_sel) << offset_sel);
+
+		emi_reg_sync_writel(value, ADDR_EMI + addr);
+
+	}
+
+	return BM_REQ_OK;
+}
+
+
+//always call this API to init the reg
+//related API, MET_BM_SetbusID, MET_BM_SetbusID_En
+int MET_BM_SetTtype_busid_idmask(unsigned int *busid, unsigned int *idMask, int _ttype1_16_en, int _ttype17_21_en)
+{
+	int i;
+	unsigned int value, addr;
+
+	const unsigned int Mask_idMask = 0x1FFF;
+	const unsigned int offset_idMask = 0;
+
+	if (_ttype1_16_en != BM_TTYPE1_16_ENABLE) {
+		/* mask set 0x1FFF , busid set disable*/
+		for (i = 1; i <= 16; i++) {
+			*(busid + i - 1) = 0xfffff;
+			*(idMask + i - 1) = 0x1FFF;
+		}
+	}
+
+	if (_ttype17_21_en != BM_TTYPE17_21_ENABLE) {
+		for (i = 17; i <= 21; i++) {
+			*(busid + i - 1) = 0xfffff;
+			*(idMask + i - 1) = 0x1FFF;
+		}
+	}
+
+	for (i = 1; i <= BM_COUNTER_MAX; i++) {
+		MET_BM_SetbusID(i, *(busid + i - 1));
+		MET_BM_SetbusID_En(i, ( *(busid + i - 1) > 0xffff) ? 0 : 1);
+
+		/* set idMask */
+		addr = EMI_TTYPE1_CONA + (i-1)*8;
+		value = emi_readl(IOMEM(ADDR_EMI + addr));
+
+		value = (value & ~(Mask_idMask << offset_idMask)) | ((*(idMask+i-1) & Mask_idMask) << offset_idMask);
+
+		emi_reg_sync_writel(value, ADDR_EMI + addr);
+
+	}
+
+	return BM_REQ_OK;
+}
+
+
+int MET_BM_SetTtype_chn_rank_sel(unsigned int *chn_rank_sel)
+{
+	unsigned int value, addr;
+	int i;
+
+	const unsigned int Mask = 0xF;
+	const unsigned int offset  = 16;
+
+
+	for (i=0;i<BM_COUNTER_MAX;i++) {
+		addr = EMI_TTYPE1_CONA + i*8;
+		value = emi_readl(IOMEM(ADDR_EMI + addr));
+
+		value = (value & ~(Mask << offset)) | ((*(chn_rank_sel+i) & Mask) << offset);
+
+		emi_reg_sync_writel(value, ADDR_EMI + addr);
+	}
+	return BM_REQ_OK;
+}
+
+int MET_BM_SetTtype_burst_range(unsigned int *bnd_dis, unsigned int *low_bnd, unsigned int *up_bnd)
+{
+	unsigned int value, addr;
+	int i;
+
+	const unsigned int Mask_dis = 0x1, Mask_low_bnd = 0x1FF, Mask_up_bnd = 0x1FF;
+	const unsigned int offset_dis = 24, offset_low_bnd = 16 , offset_up_bnd = 0 ;
+
+
+	for (i=0;i<BM_COUNTER_MAX;i++) {
+
+		/* set dis bit */
+		addr = EMI_TTYPE1_CONA + i*8;
+		value = emi_readl(IOMEM(ADDR_EMI + addr));
+
+		value = (value & ~(Mask_dis << offset_dis)) | ((*(bnd_dis+i) & Mask_dis) << offset_dis);
+
+		emi_reg_sync_writel(value, ADDR_EMI + addr);
+
+
+		addr = EMI_TTYPE1_CONB + i*8;
+		value = emi_readl(IOMEM(ADDR_EMI + addr));
+
+		value = (value & ~(Mask_low_bnd << offset_low_bnd)) | ((*(low_bnd+i) & Mask_low_bnd) << offset_low_bnd);
+		value = (value & ~(Mask_up_bnd << offset_up_bnd)) | ((*(up_bnd+i) & Mask_up_bnd) << offset_up_bnd);
+
+		emi_reg_sync_writel(value, ADDR_EMI + addr);
+	}
+	return BM_REQ_OK;
+}
+
+unsigned int MET_EMI_Get_CONH_2ND(void)
+{
+	return readl(IOMEM(ADDR_EMI + EMI_CONH_2ND));
+}
diff --git a/src/devtools/met-driver/4.19/common/emi/SEDA3_5/mtk_emi_bm.h b/src/devtools/met-driver/4.19/common/emi/SEDA3_5/mtk_emi_bm.h
new file mode 100644
index 0000000..b1e2ade
--- /dev/null
+++ b/src/devtools/met-driver/4.19/common/emi/SEDA3_5/mtk_emi_bm.h
@@ -0,0 +1,294 @@
+/*  SPDX-License-Identifier: GPL-2.0 */  
+/*
+ * Copyright (C) 2019 MediaTek Inc.
+ *
+ */
+#ifndef __MT_MET_EMI_BM_H__
+#define __MT_MET_EMI_BM_H__
+
+#define EMI_VER_MAJOR  3
+#define EMI_VER_MINOR  5
+
+#define FILE_NODE_DATA_LEN 512 
+#define WSCT_AMOUNT 6
+#define TSCT_AMOUNT 3
+
+
+#define DRAM_EMI_BASECLOCK_RATE_LP4     4
+#define DRAM_EMI_BASECLOCK_RATE_LP3     2
+
+#define DRAM_IO_BUS_WIDTH_LP4           16
+#define DRAM_IO_BUS_WIDTH_LP3           32
+
+#define DRAM_DATARATE   2
+
+#define ADDR_EMI        ((unsigned long)BaseAddrEMI)
+
+
+#define BM_MASTER_M0            (0x01)
+#define BM_MASTER_M1            (0x02)
+#define BM_MASTER_M2            (0x04)
+#define BM_MASTER_M3            (0x08)
+#define BM_MASTER_M4            (0x10)
+#define BM_MASTER_M5            (0x20)
+#define BM_MASTER_M6            (0x40)
+#define BM_MASTER_M7            (0x80)
+#define BM_MASTER_ALL           (0xFF)
+
+
+enum BM_RW_Type {
+	BM_BOTH_READ_WRITE,
+	BM_READ_ONLY,
+	BM_WRITE_ONLY
+};
+
+enum {
+	BM_TRANS_TYPE_1BEAT = 0x0,
+	BM_TRANS_TYPE_2BEAT,
+	BM_TRANS_TYPE_3BEAT,
+	BM_TRANS_TYPE_4BEAT,
+	BM_TRANS_TYPE_5BEAT,
+	BM_TRANS_TYPE_6BEAT,
+	BM_TRANS_TYPE_7BEAT,
+	BM_TRANS_TYPE_8BEAT,
+	BM_TRANS_TYPE_9BEAT,
+	BM_TRANS_TYPE_10BEAT,
+	BM_TRANS_TYPE_11BEAT,
+	BM_TRANS_TYPE_12BEAT,
+	BM_TRANS_TYPE_13BEAT,
+	BM_TRANS_TYPE_14BEAT,
+	BM_TRANS_TYPE_15BEAT,
+	BM_TRANS_TYPE_16BEAT,
+	BM_TRANS_TYPE_1Byte = 0 << 4,
+	BM_TRANS_TYPE_2Byte = 1 << 4,
+	BM_TRANS_TYPE_4Byte = 2 << 4,
+	BM_TRANS_TYPE_8Byte = 3 << 4,
+	BM_TRANS_TYPE_16Byte = 4 << 4,
+	BM_TRANS_TYPE_32Byte = 5 << 4,
+	BM_TRANS_TYPE_BURST_WRAP = 0 << 7,
+	BM_TRANS_TYPE_BURST_INCR = 1 << 7
+};
+
+enum {
+	BM_TRANS_RW_DEFAULT = 0x0,
+	BM_TRANS_RW_READONLY,
+	BM_TRANS_RW_WRITEONLY,
+	BM_TRANS_RW_RWBOTH
+};
+
+enum {
+	BM_WSCT_RW_DISABLE = 0x0,
+	BM_WSCT_RW_READONLY,
+	BM_WSCT_RW_WRITEONLY,
+	BM_WSCT_RW_RWBOTH
+};
+
+/*coda busid 12bit, but HW support 16 bit*/
+#define EMI_BMID_MASK				(0xFFFF)
+#define BM_COUNTER_MAX				(21)
+
+enum {
+	BUS_MON_EN_SHIFT = 0,
+	BUS_MON_PAUSE_SHIFT = 1,
+	BUS_MON_IDLE_SHIFT = 3,
+	BC_OVERRUN_SHIFT = 8,
+	DRAMC_CG_SHIFT = 9,
+};
+
+#define BM_REQ_OK				(0)
+#define BM_ERR_WRONG_REQ			(-1)
+#define BM_ERR_OVERRUN				(-2)
+
+#define BM_WSCT_TSCT_IDSEL_ENABLE		(0)
+#define BM_WSCT_TSCT_IDSEL_DISABLE		(-1)
+#define BM_TTYPE1_16_ENABLE			(0)
+#define BM_TTYPE1_16_DISABLE			(-1)
+#define BM_TTYPE17_21_ENABLE			(0)
+#define BM_TTYPE17_21_DISABLE			(-1)
+#define BM_BW_LIMITER_ENABLE			(0)
+#define BM_BW_LIMITER_DISABLE			(-1)
+
+#define M0_DOUBLE_HALF_BW_1CH	(0x0)
+#define M0_DOUBLE_HALF_BW_2CH	(0x1)
+#define M0_DOUBLE_HALF_BW_4CH	(0x2)
+
+/* EMI Rank configuration */
+enum {
+	DISABLE_DUAL_RANK_MODE = 0,
+	ENABLE_DUAL_RANK_MODE,
+};
+
+#define RANK_MASK 0x1
+#define ONE_RANK 1
+#define DUAL_RANK 2
+
+
+#if defined(CONFIG_MTK_TINYSYS_SSPM_SUPPORT) && defined(ONDIEMET_SUPPORT)
+enum BM_EMI_IPI_Type {
+	SET_BASE_EMI = 0x0,
+	SET_EBM_CONFIGS1 = 0x7,
+	SET_EBM_CONFIGS2 = 0x8,
+	SET_REGISTER_CB = 0x9,
+};
+#endif
+
+
+#define	EMI_OFF			0x0000
+#define EMI_CONA		(0x000-EMI_OFF)
+#define EMI_CONH		(0x038-EMI_OFF)
+#define EMI_CONH_2ND		(0x03C-EMI_OFF)
+#define EMI_CONM		(0x060-EMI_OFF)
+#define EMI_CONO		(0x070-EMI_OFF)
+
+#define EMI_MDCT		(0x078 - EMI_OFF)
+#define EMI_MDCT_2ND		(0x07C - EMI_OFF)
+
+#define EMI_ARBA		(0x100 - EMI_OFF)
+#define EMI_ARBB		(0x108 - EMI_OFF)
+#define EMI_ARBC		(0x110 - EMI_OFF)
+#define EMI_ARBD		(0x118 - EMI_OFF)
+#define EMI_ARBE		(0x120 - EMI_OFF)
+#define EMI_ARBF		(0x128 - EMI_OFF)
+#define EMI_ARBG		(0x130 - EMI_OFF)
+#define EMI_ARBG_2ND		(0x134 - EMI_OFF)
+#define EMI_ARBH		(0x138 - EMI_OFF)
+
+
+#define EMI_BMEN		(0x400-EMI_OFF)
+#define EMI_MSEL		(0x440 - EMI_OFF)
+#define EMI_MSEL2		(0x468 - EMI_OFF)
+#define EMI_MSEL3		(0x470 - EMI_OFF)
+#define EMI_MSEL4		(0x478 - EMI_OFF)
+#define EMI_MSEL5		(0x480 - EMI_OFF)
+#define EMI_MSEL6		(0x488 - EMI_OFF)
+#define EMI_MSEL7		(0x490 - EMI_OFF)
+#define EMI_MSEL8		(0x498 - EMI_OFF)
+#define EMI_MSEL9		(0x4A0 - EMI_OFF)
+#define EMI_MSEL10		(0x4A8 - EMI_OFF)
+
+#define EMI_BMID0		(0x4B0 - EMI_OFF)
+#define EMI_BMID1		(0x4B4 - EMI_OFF)
+#define EMI_BMID2		(0x4B8 - EMI_OFF)
+#define EMI_BMID3		(0x4BC - EMI_OFF)
+#define EMI_BMID4		(0x4C0 - EMI_OFF)
+#define EMI_BMID5		(0x4C4 - EMI_OFF)
+#define EMI_BMID6		(0x4C8 - EMI_OFF)
+#define EMI_BMID7		(0x4CC - EMI_OFF)
+#define EMI_BMID8		(0x4D0 - EMI_OFF)
+#define EMI_BMID9		(0x4D4 - EMI_OFF)
+#define EMI_BMID10		(0x4D8 - EMI_OFF)
+
+#define EMI_BMEN1		(0x4E0 - EMI_OFF)
+#define EMI_BMEN2		(0x4E8 - EMI_OFF)
+#define EMI_BMRW0		(0x4F8 - EMI_OFF)
+#define EMI_BMRW1		(0x4FC - EMI_OFF)
+
+
+/* SEDA 3.5 New! reg*/
+/* For WSCT setting*/
+#define EMI_DBWA (0xF00 - EMI_OFF)
+#define EMI_DBWB (0xF04 - EMI_OFF)
+#define EMI_DBWC (0xF08 - EMI_OFF)
+#define EMI_DBWD (0xF0C - EMI_OFF)
+#define EMI_DBWE (0xF10 - EMI_OFF)
+#define EMI_DBWF (0xF14 - EMI_OFF)
+
+
+#define EMI_DBWA_2ND (0xF2C - EMI_OFF)
+#define EMI_DBWB_2ND (0xF30 - EMI_OFF)
+#define EMI_DBWC_2ND (0xF34 - EMI_OFF)
+#define EMI_DBWD_2ND (0xF38 - EMI_OFF)
+#define EMI_DBWE_2ND (0xF3C - EMI_OFF)
+#define EMI_DBWF_2ND (0xF40 - EMI_OFF)
+
+#define EMI_DBWI (0xF20 - EMI_OFF) /* SEL_ID_MSK*/
+#define EMI_DBWJ (0xF24 - EMI_OFF)
+#define EMI_DBWK (0xF28 - EMI_OFF)
+
+/* For Ttype setting */
+#define EMI_TTYPE1_CONA (0xF50 - EMI_OFF)
+#define EMI_TTYPE1_CONB (0xF54 - EMI_OFF)
+#define EMI_TTYPE2_CONA (0xF58 - EMI_OFF)
+#define EMI_TTYPE2_CONB (0xF5C - EMI_OFF)
+#define EMI_TTYPE3_CONA (0xF60 - EMI_OFF)
+#define EMI_TTYPE3_CONB (0xF64 - EMI_OFF)
+#define EMI_TTYPE4_CONA (0xF68 - EMI_OFF)
+#define EMI_TTYPE4_CONB (0xF6C - EMI_OFF)
+#define EMI_TTYPE5_CONA (0xF70 - EMI_OFF)
+#define EMI_TTYPE5_CONB (0xF74 - EMI_OFF)
+#define EMI_TTYPE6_CONA (0xF78 - EMI_OFF)
+#define EMI_TTYPE6_CONB (0xF7C - EMI_OFF)
+#define EMI_TTYPE7_CONA (0xF80 - EMI_OFF)
+#define EMI_TTYPE7_CONB (0xF84 - EMI_OFF)
+#define EMI_TTYPE8_CONA (0xF88 - EMI_OFF)
+#define EMI_TTYPE8_CONB (0xF8C - EMI_OFF)
+#define EMI_TTYPE9_CONA (0xF90 - EMI_OFF)
+#define EMI_TTYPE9_CONB (0xF94 - EMI_OFF)
+#define EMI_TTYPE10_CONA (0xF98 - EMI_OFF)
+#define EMI_TTYPE10_CONB (0xF9C - EMI_OFF)
+#define EMI_TTYPE11_CONA (0xFA0 - EMI_OFF)
+#define EMI_TTYPE11_CONB (0xFA4 - EMI_OFF)
+#define EMI_TTYPE12_CONA (0xFA8 - EMI_OFF)
+#define EMI_TTYPE12_CONB (0xFAC - EMI_OFF)
+#define EMI_TTYPE13_CONA (0xFB0 - EMI_OFF)
+#define EMI_TTYPE13_CONB (0xFB4 - EMI_OFF)
+#define EMI_TTYPE14_CONA (0xFB8 - EMI_OFF)
+#define EMI_TTYPE14_CONB (0xFBC - EMI_OFF)
+#define EMI_TTYPE15_CONA (0xFC0 - EMI_OFF)
+#define EMI_TTYPE15_CONB (0xFC4 - EMI_OFF)
+#define EMI_TTYPE16_CONA (0xFC8 - EMI_OFF)
+#define EMI_TTYPE16_CONB (0xFCC - EMI_OFF)
+#define EMI_TTYPE17_CONA (0xFD0 - EMI_OFF)
+#define EMI_TTYPE17_CONB (0xFD4 - EMI_OFF)
+#define EMI_TTYPE18_CONA (0xFD8 - EMI_OFF)
+#define EMI_TTYPE18_CONB (0xFDC - EMI_OFF)
+#define EMI_TTYPE19_CONA (0xFE0 - EMI_OFF)
+#define EMI_TTYPE19_CONB (0xFE4 - EMI_OFF)
+#define EMI_TTYPE20_CONA (0xFE8 - EMI_OFF)
+#define EMI_TTYPE20_CONB (0xFEC - EMI_OFF)
+#define EMI_TTYPE21_CONA (0xFF0 - EMI_OFF)
+#define EMI_TTYPE21_CONB (0xFF4 - EMI_OFF)
+
+
+
+extern int MET_BM_Init(void);
+extern void MET_BM_DeInit(void);
+extern void MET_BM_SaveCfg(void);
+extern void MET_BM_RestoreCfg(void);
+
+
+
+extern int MET_BM_SetMonitorCounter(const unsigned int counter_num,
+				    const unsigned int master, const unsigned int trans_type);
+extern int MET_BM_SetTtypeCounterRW(unsigned int bmrw0_val, unsigned int bmrw1_val);
+extern int MET_BM_Set_WsctTsct_id_sel(unsigned int counter_num, unsigned int enable);
+extern int MET_BM_SetMaster(const unsigned int counter_num, const unsigned int master);
+extern int MET_BM_SetbusID_En(const unsigned int counter_num,
+			      const unsigned int enable);
+extern int MET_BM_SetbusID(const unsigned int counter_num,
+			   const unsigned int id);
+extern int MET_BM_SetUltraHighFilter(const unsigned int counter_num, const unsigned int enable);
+extern int MET_BM_SetLatencyCounter(unsigned int enable);
+extern void MET_BM_SetReadWriteType(const unsigned int ReadWriteType);
+
+extern unsigned int MET_EMI_GetDramRankNum(void);
+extern unsigned int MET_EMI_GetDramRankNum_CHN1(void);
+
+
+unsigned int MET_EMI_GetDramChannNum(void);
+unsigned int MET_EMI_Get_CONH_2ND(void);
+
+/* SEDA 3.5 NEW */
+extern int MET_BM_SetWSCT_master_rw(unsigned int *master , unsigned int *rw);
+extern int MET_BM_SetWSCT_high_priority(unsigned int *disable, unsigned int *select);
+extern int MET_BM_SetWSCT_busid_idmask(unsigned int *busid, unsigned int *idMask);
+extern int MET_BM_SetWSCT_chn_rank_sel(unsigned int *chn_rank_sel);
+extern int MET_BM_SetWSCT_burst_range(unsigned int *bnd_dis, unsigned int *low_bnd, unsigned int *up_bnd);
+extern int MET_BM_SetTSCT_busid_enable(unsigned int *enable);
+extern int MET_BM_SetTtype_high_priority_sel(unsigned int _high_priority_filter, unsigned int *select);
+extern int MET_BM_SetTtype_busid_idmask(unsigned int *busid, unsigned int *idMask, int _ttype1_16_en, int _ttype17_21_en);
+extern int MET_BM_SetTtype_chn_rank_sel(unsigned int *chn_rank_sel);
+extern int MET_BM_SetTtype_burst_range(unsigned int *bnd_dis, unsigned int *low_bnd, unsigned int *up_bnd);
+extern unsigned int MET_EMI_Get_BaseClock_Rate(void);
+
+#endif                          /* !__MT_MET_EMI_BM_H__ */
diff --git a/src/devtools/met-driver/4.19/common/emi/SEDA3_6/met_emi.c b/src/devtools/met-driver/4.19/common/emi/SEDA3_6/met_emi.c
new file mode 100644
index 0000000..12782c8
--- /dev/null
+++ b/src/devtools/met-driver/4.19/common/emi/SEDA3_6/met_emi.c
@@ -0,0 +1,2306 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2019 MediaTek Inc.
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/device.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/dma-mapping.h>
+#include <linux/string.h>
+
+#define MET_USER_EVENT_SUPPORT
+#include "met_drv.h"
+#include "trace.h"
+
+#include "mtk_typedefs.h"
+#include "core_plf_init.h"
+#include "mtk_emi_bm.h"
+#include "interface.h"
+#include "met_dramc.h"
+
+#if defined(CONFIG_MTK_TINYSYS_SSPM_SUPPORT) && defined(ONDIEMET_SUPPORT)
+#include "sspm/ondiemet_sspm.h"
+#endif
+
+#define MAX_HEADER_LEN (1024 * 6)
+static char header_str[MAX_HEADER_LEN];
+
+static unsigned int output_header_len;
+static unsigned int output_str_len;
+
+/* #define FILE_NODE_DBG */
+
+/*======================================================================*/
+/*	Global variable definitions					*/
+/*======================================================================*/
+/*ondiemet emi sampling interval in us */
+int emi_tsct_enable = 1;
+int emi_mdct_enable = 1;
+int emi_TP_busfiltr_enable;
+
+
+/* Dynamic MonitorCounter selection !!!EXPERIMENT!!! */
+static int msel_enable;
+static unsigned int msel_group1 = BM_MASTER_ALL;
+static unsigned int msel_group2 = BM_MASTER_ALL;
+static unsigned int msel_group3 = BM_MASTER_ALL;
+
+
+/* Global variables */
+static struct kobject *kobj_emi;
+static int rwtype = BM_BOTH_READ_WRITE;
+
+/* BW Limiter */
+/*#define CNT_COUNTDOWN	(1000-1)*/		/* 1000 * 1ms = 1sec */
+#define CNT_COUNTDOWN   (0)                     /* 1ms */
+/* static int countdown; */
+static int bw_limiter_enable = BM_BW_LIMITER_ENABLE;
+
+/* TTYPE counter */
+static int ttype1_16_en = BM_TTYPE1_16_DISABLE;
+static int ttype17_21_en = BM_TTYPE17_21_DISABLE;
+
+static int dramc_pdir_enable;
+static int dram_chann_num = 1;
+
+enum SSPM_Mode {
+	CUSTOMER_MODE = 0x0,
+	UNDEFINE_MODE = 0x1,
+	INTERNAL_MODE = 0X2780
+};
+
+
+
+/*======================================================================*/
+/*	KOBJ Declarations						*/
+/*======================================================================*/
+
+
+DECLARE_KOBJ_ATTR_INT(emi_TP_busfiltr_enable, emi_TP_busfiltr_enable);
+
+
+DECLARE_KOBJ_ATTR_INT(msel_enable, msel_enable);
+DECLARE_KOBJ_ATTR_HEX_CHECK(msel_group1, msel_group1, msel_group1 > 0 && msel_group1 <= BM_MASTER_ALL);
+DECLARE_KOBJ_ATTR_HEX_CHECK(msel_group2, msel_group2, msel_group2 > 0 && msel_group2 <= BM_MASTER_ALL);
+DECLARE_KOBJ_ATTR_HEX_CHECK(msel_group3, msel_group3, msel_group3 > 0 && msel_group3 <= BM_MASTER_ALL);
+
+
+
+/* KOBJ: rwtype */
+DECLARE_KOBJ_ATTR_INT_CHECK(rwtype, rwtype, rwtype >= 0 && rwtype <= BM_WRITE_ONLY);
+
+/*
+static unsigned int get_emi_clock_rate(unsigned int dram_data_rate_MHz)
+{
+	unsigned int DRAM_TYPE;
+
+	if (mtk_dramc_get_ddr_type_symbol) {
+		DRAM_TYPE = mtk_dramc_get_ddr_type_symbol();
+
+		if ((DRAM_TYPE == 2) || (DRAM_TYPE == 3))
+			return dram_data_rate_MHz / DRAM_EMI_BASECLOCK_RATE_LP4 / DRAM_DATARATE;
+		else
+			return dram_data_rate_MHz / DRAM_EMI_BASECLOCK_RATE_LP3 / DRAM_DATARATE;
+	} else {
+		METERROR("[%s][%d]mtk_dramc_get_ddr_type_symbol = NULL , use the TYPE_LPDDR4 setting\n", __func__, __LINE__);
+		return dram_data_rate_MHz / DRAM_EMI_BASECLOCK_RATE_LP4 / DRAM_DATARATE;
+	}
+}
+*/
+
+/* KOBJ: ttype1_16_en */
+DECLARE_KOBJ_ATTR_STR_LIST_ITEM(
+	ttype1_16_en,
+	KOBJ_ITEM_LIST(
+		{ BM_TTYPE1_16_ENABLE,   "ENABLE" },
+		{ BM_TTYPE1_16_DISABLE,  "DISABLE" }
+		)
+	);
+DECLARE_KOBJ_ATTR_STR_LIST(ttype1_16_en, ttype1_16_en, ttype1_16_en);
+
+/* KOBJ: ttype17_21_en */
+DECLARE_KOBJ_ATTR_STR_LIST_ITEM(
+	ttype17_21_en,
+	KOBJ_ITEM_LIST(
+		{ BM_TTYPE17_21_ENABLE,  "ENABLE" },
+		{ BM_TTYPE17_21_DISABLE, "DISABLE" }
+		)
+	);
+DECLARE_KOBJ_ATTR_STR_LIST(ttype17_21_en, ttype17_21_en, ttype17_21_en);
+
+/* KOBJ: bw_limiter_enable */
+DECLARE_KOBJ_ATTR_STR_LIST_ITEM(
+	bw_limiter_enable,
+	KOBJ_ITEM_LIST(
+		{ BM_BW_LIMITER_ENABLE,  "ENABLE" },
+		{ BM_BW_LIMITER_DISABLE, "DISABLE" }
+		)
+	);
+
+DECLARE_KOBJ_ATTR_STR_LIST(bw_limiter_enable, bw_limiter_enable, bw_limiter_enable);
+
+/* KOBJ: ttype_master */
+DECLARE_KOBJ_ATTR_STR_LIST_ITEM(
+	ttype_master,
+	KOBJ_ITEM_LIST(
+		{ BM_MASTER_M0,  "M0" },
+		{ BM_MASTER_M1,  "M1" },
+		{ BM_MASTER_M2,  "M2" },
+		{ BM_MASTER_M3,  "M3" },
+		{ BM_MASTER_M4,  "M4" },
+		{ BM_MASTER_M5,  "M5" },
+		{ BM_MASTER_M6,  "M6" },
+		{ BM_MASTER_M7,  "M7" }
+		)
+	);
+
+
+/* KOBJ: ttypeX_nbeat, ttypeX_nbyte, ttypeX_burst */
+DECLARE_KOBJ_ATTR_INT_LIST_ITEM(
+	ttype_nbeat,
+	KOBJ_ITEM_LIST(
+		{ BM_TRANS_TYPE_1BEAT,   1 },
+		{ BM_TRANS_TYPE_2BEAT,   2 },
+		{ BM_TRANS_TYPE_3BEAT,   3 },
+		{ BM_TRANS_TYPE_4BEAT,   4 },
+		{ BM_TRANS_TYPE_5BEAT,   5 },
+		{ BM_TRANS_TYPE_6BEAT,   6 },
+		{ BM_TRANS_TYPE_7BEAT,   7 },
+		{ BM_TRANS_TYPE_8BEAT,   8 },
+		{ BM_TRANS_TYPE_9BEAT,   9 },
+		{ BM_TRANS_TYPE_10BEAT,  10 },
+		{ BM_TRANS_TYPE_11BEAT,  11 },
+		{ BM_TRANS_TYPE_12BEAT,  12 },
+		{ BM_TRANS_TYPE_13BEAT,  13 },
+		{ BM_TRANS_TYPE_14BEAT,  14 },
+		{ BM_TRANS_TYPE_15BEAT,  15 },
+		{ BM_TRANS_TYPE_16BEAT,  16 }
+		)
+	);
+DECLARE_KOBJ_ATTR_INT_LIST_ITEM(
+	ttype_nbyte,
+	KOBJ_ITEM_LIST(
+		{ BM_TRANS_TYPE_1Byte,   1 },
+		{ BM_TRANS_TYPE_2Byte,   2 },
+		{ BM_TRANS_TYPE_4Byte,   4 },
+		{ BM_TRANS_TYPE_8Byte,   8 },
+		{ BM_TRANS_TYPE_16Byte,  16 },
+		{ BM_TRANS_TYPE_32Byte,  32 }
+		)
+	);
+DECLARE_KOBJ_ATTR_STR_LIST_ITEM(
+	ttype_burst,
+	KOBJ_ITEM_LIST(
+		{ BM_TRANS_TYPE_BURST_INCR,      "INCR" },
+		{ BM_TRANS_TYPE_BURST_WRAP,      "WRAP" }
+		)
+	);
+
+DECLARE_KOBJ_ATTR_STR_LIST_ITEM(
+	ttype_rw,
+	KOBJ_ITEM_LIST(
+		{ BM_TRANS_RW_DEFAULT,   "DEFAULT" },
+		{ BM_TRANS_RW_READONLY,  "R" },
+		{ BM_TRANS_RW_WRITEONLY, "W" },
+		{ BM_TRANS_RW_RWBOTH,    "BOTH" }
+		)
+	);
+
+
+DECLARE_KOBJ_ATTR_INT(dramc_pdir_enable, dramc_pdir_enable);
+
+/*enable high priority filter*/
+static int high_priority_filter;
+DECLARE_KOBJ_ATTR_HEX(high_priority_filter, high_priority_filter);
+
+
+
+/**/
+static int ttype_master_val[21];
+static int ttype_busid_val[21];
+static int ttype_nbeat_val[21];
+static int ttype_nbyte_val[21];
+static int ttype_burst_val[21];
+static int ttype_rw_val[21];
+
+#define DECLARE_KOBJ_TTYPE_MASTER(nr) \
+	DECLARE_KOBJ_ATTR_STR_LIST(ttype ## nr ## _master, ttype_master_val[nr - 1], ttype_master)
+
+#define DECLARE_KOBJ_TTYPE_NBEAT(nr) \
+	DECLARE_KOBJ_ATTR_INT_LIST(ttype ## nr ## _nbeat, ttype_nbeat_val[nr - 1], ttype_nbeat)
+
+#define DECLARE_KOBJ_TTYPE_NBYTE(nr) \
+	DECLARE_KOBJ_ATTR_INT_LIST(ttype ## nr ## _nbyte, ttype_nbyte_val[nr - 1], ttype_nbyte)
+
+#define DECLARE_KOBJ_TTYPE_BURST(nr) \
+	DECLARE_KOBJ_ATTR_STR_LIST(ttype ## nr ## _burst, ttype_burst_val[nr - 1], ttype_burst)
+
+#define DECLARE_KOBJ_TTYPE_RW(nr) \
+	DECLARE_KOBJ_ATTR_STR_LIST(ttype ## nr ## _rw, ttype_rw_val[nr - 1], ttype_rw)
+
+#define DECLARE_KOBJ_TTYPE_BUSID_VAL(nr) \
+	DECLARE_KOBJ_ATTR_HEX(ttype ## nr ## _busid, ttype_busid_val[nr - 1])
+
+DECLARE_KOBJ_TTYPE_MASTER(1);
+DECLARE_KOBJ_TTYPE_NBEAT(1);
+DECLARE_KOBJ_TTYPE_NBYTE(1);
+DECLARE_KOBJ_TTYPE_BURST(1);
+DECLARE_KOBJ_TTYPE_RW(1);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(1);
+
+DECLARE_KOBJ_TTYPE_MASTER(2);
+DECLARE_KOBJ_TTYPE_NBEAT(2);
+DECLARE_KOBJ_TTYPE_NBYTE(2);
+DECLARE_KOBJ_TTYPE_BURST(2);
+DECLARE_KOBJ_TTYPE_RW(2);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(2);
+
+DECLARE_KOBJ_TTYPE_MASTER(3);
+DECLARE_KOBJ_TTYPE_NBEAT(3);
+DECLARE_KOBJ_TTYPE_NBYTE(3);
+DECLARE_KOBJ_TTYPE_BURST(3);
+DECLARE_KOBJ_TTYPE_RW(3);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(3);
+
+DECLARE_KOBJ_TTYPE_MASTER(4);
+DECLARE_KOBJ_TTYPE_NBEAT(4);
+DECLARE_KOBJ_TTYPE_NBYTE(4);
+DECLARE_KOBJ_TTYPE_BURST(4);
+DECLARE_KOBJ_TTYPE_RW(4);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(4);
+
+DECLARE_KOBJ_TTYPE_MASTER(5);
+DECLARE_KOBJ_TTYPE_NBEAT(5);
+DECLARE_KOBJ_TTYPE_NBYTE(5);
+DECLARE_KOBJ_TTYPE_BURST(5);
+DECLARE_KOBJ_TTYPE_RW(5);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(5);
+
+DECLARE_KOBJ_TTYPE_MASTER(6);
+DECLARE_KOBJ_TTYPE_NBEAT(6);
+DECLARE_KOBJ_TTYPE_NBYTE(6);
+DECLARE_KOBJ_TTYPE_BURST(6);
+DECLARE_KOBJ_TTYPE_RW(6);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(6);
+
+DECLARE_KOBJ_TTYPE_MASTER(7);
+DECLARE_KOBJ_TTYPE_NBEAT(7);
+DECLARE_KOBJ_TTYPE_NBYTE(7);
+DECLARE_KOBJ_TTYPE_BURST(7);
+DECLARE_KOBJ_TTYPE_RW(7);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(7);
+
+DECLARE_KOBJ_TTYPE_MASTER(8);
+DECLARE_KOBJ_TTYPE_NBEAT(8);
+DECLARE_KOBJ_TTYPE_NBYTE(8);
+DECLARE_KOBJ_TTYPE_BURST(8);
+DECLARE_KOBJ_TTYPE_RW(8);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(8);
+
+DECLARE_KOBJ_TTYPE_MASTER(9);
+DECLARE_KOBJ_TTYPE_NBEAT(9);
+DECLARE_KOBJ_TTYPE_NBYTE(9);
+DECLARE_KOBJ_TTYPE_BURST(9);
+DECLARE_KOBJ_TTYPE_RW(9);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(9);
+
+DECLARE_KOBJ_TTYPE_MASTER(10);
+DECLARE_KOBJ_TTYPE_NBEAT(10);
+DECLARE_KOBJ_TTYPE_NBYTE(10);
+DECLARE_KOBJ_TTYPE_BURST(10);
+DECLARE_KOBJ_TTYPE_RW(10);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(10);
+
+DECLARE_KOBJ_TTYPE_MASTER(11);
+DECLARE_KOBJ_TTYPE_NBEAT(11);
+DECLARE_KOBJ_TTYPE_NBYTE(11);
+DECLARE_KOBJ_TTYPE_BURST(11);
+DECLARE_KOBJ_TTYPE_RW(11);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(11);
+
+DECLARE_KOBJ_TTYPE_MASTER(12);
+DECLARE_KOBJ_TTYPE_NBEAT(12);
+DECLARE_KOBJ_TTYPE_NBYTE(12);
+DECLARE_KOBJ_TTYPE_BURST(12);
+DECLARE_KOBJ_TTYPE_RW(12);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(12);
+
+DECLARE_KOBJ_TTYPE_MASTER(13);
+DECLARE_KOBJ_TTYPE_NBEAT(13);
+DECLARE_KOBJ_TTYPE_NBYTE(13);
+DECLARE_KOBJ_TTYPE_BURST(13);
+DECLARE_KOBJ_TTYPE_RW(13);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(13);
+
+DECLARE_KOBJ_TTYPE_MASTER(14);
+DECLARE_KOBJ_TTYPE_NBEAT(14);
+DECLARE_KOBJ_TTYPE_NBYTE(14);
+DECLARE_KOBJ_TTYPE_BURST(14);
+DECLARE_KOBJ_TTYPE_RW(14);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(14);
+
+DECLARE_KOBJ_TTYPE_MASTER(15);
+DECLARE_KOBJ_TTYPE_NBEAT(15);
+DECLARE_KOBJ_TTYPE_NBYTE(15);
+DECLARE_KOBJ_TTYPE_BURST(15);
+DECLARE_KOBJ_TTYPE_RW(15);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(15);
+
+DECLARE_KOBJ_TTYPE_MASTER(16);
+DECLARE_KOBJ_TTYPE_NBEAT(16);
+DECLARE_KOBJ_TTYPE_NBYTE(16);
+DECLARE_KOBJ_TTYPE_BURST(16);
+DECLARE_KOBJ_TTYPE_RW(16);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(16);
+
+DECLARE_KOBJ_TTYPE_MASTER(17);
+DECLARE_KOBJ_TTYPE_NBEAT(17);
+DECLARE_KOBJ_TTYPE_NBYTE(17);
+DECLARE_KOBJ_TTYPE_BURST(17);
+DECLARE_KOBJ_TTYPE_RW(17);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(17);
+
+DECLARE_KOBJ_TTYPE_MASTER(18);
+DECLARE_KOBJ_TTYPE_NBEAT(18);
+DECLARE_KOBJ_TTYPE_NBYTE(18);
+DECLARE_KOBJ_TTYPE_BURST(18);
+DECLARE_KOBJ_TTYPE_RW(18);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(18);
+
+DECLARE_KOBJ_TTYPE_MASTER(19);
+DECLARE_KOBJ_TTYPE_NBEAT(19);
+DECLARE_KOBJ_TTYPE_NBYTE(19);
+DECLARE_KOBJ_TTYPE_BURST(19);
+DECLARE_KOBJ_TTYPE_RW(19);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(19);
+
+DECLARE_KOBJ_TTYPE_MASTER(20);
+DECLARE_KOBJ_TTYPE_NBEAT(20);
+DECLARE_KOBJ_TTYPE_NBYTE(20);
+DECLARE_KOBJ_TTYPE_BURST(20);
+DECLARE_KOBJ_TTYPE_RW(20);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(20);
+
+DECLARE_KOBJ_TTYPE_MASTER(21);
+DECLARE_KOBJ_TTYPE_NBEAT(21);
+DECLARE_KOBJ_TTYPE_NBYTE(21);
+DECLARE_KOBJ_TTYPE_BURST(21);
+DECLARE_KOBJ_TTYPE_RW(21);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(21);
+
+
+static unsigned int get_sspm_support_feature(void)
+{
+	unsigned int rdata=0;
+#if defined(CONFIG_MTK_TINYSYS_SSPM_SUPPORT) && defined(ONDIEMET_SUPPORT)
+	int ret, i;
+	unsigned int ipi_buf[4];
+
+	for (i = 0; i < 4; i++)
+		ipi_buf[i] = 0;
+
+	if (sspm_buf_available == 1) {
+		ipi_buf[0] = MET_MAIN_ID | (MID_EMI << MID_BIT_SHIFT) | MET_REQ_AP2MD ;
+		ret = met_ipi_to_sspm_command((void *)ipi_buf, 0, &rdata, 1);
+	}
+#endif
+	return rdata;
+}
+
+/* SEDA 3.5 ext */
+static unsigned int msel_group_ext_val[WSCT_AMOUNT];
+static unsigned int wsct_rw_val[WSCT_AMOUNT];
+
+char* const delim_comma = ",";
+char* const delim_coclon = ":";
+
+
+char msel_group_ext[FILE_NODE_DATA_LEN] = {'\0'};
+
+static void _clear_msel_group_ext(void) {
+	int i;
+
+	for (i=0;i<WSCT_AMOUNT;i++) {
+		msel_group_ext_val[i] = BM_MASTER_ALL;
+	}
+	
+	/*WSCT 4~5 default is ultra, pre-ultra total*/
+	msel_group_ext[0] = '\0';
+}
+
+static ssize_t msel_group_ext_store(struct kobject *kobj,
+		struct kobj_attribute *attr,
+		const char *buf,
+		size_t n)
+{
+	/*parse wsct_id:group,
+	1. split data  by ","
+	2. split subdata by ":"
+	3. check the value is OK
+
+	don't clear the setting, do this by echo 1 > clear_setting
+	*/
+
+	char *token, *cur= msel_group_ext;
+	char *_id = NULL, *_master_group = NULL;
+	int id_int = 0;
+
+	_clear_msel_group_ext();
+
+	snprintf(msel_group_ext, FILE_NODE_DATA_LEN, "%s", buf);
+	msel_group_ext[n-1]='\0';
+	
+
+	while (cur != NULL) {
+		token = strsep(&cur, delim_comma);
+		PR_BOOTMSG("token: %s\n",token);
+		/*token EX: 4:0xff , (ID,master_group)*/
+
+		_id = strsep(&token, delim_coclon); // ID
+		_master_group = strsep(&token, delim_coclon);
+
+		PR_BOOTMSG("_id[%s] _master_group[%s]\n",_id,_master_group);
+
+		if (_id == NULL || _master_group == NULL) {
+			PR_BOOTMSG("err: _id[%s] _master_group[%s], para can't be NULL\n",_id,_master_group);
+			_clear_msel_group_ext();
+			return -EINVAL;
+		}
+
+		if (kstrtouint(_id, 0, &id_int) != 0) {
+			PR_BOOTMSG("_id[%s] trans to hex err\n",_id);
+			_clear_msel_group_ext();
+			return -EINVAL;
+		}
+
+
+		if ( id_int >= 0 && id_int < WSCT_AMOUNT) {
+			if (kstrtouint(_master_group, 0, &msel_group_ext_val[id_int]) != 0) {
+				PR_BOOTMSG("master_group[%s] trans to hex err\n",_master_group);
+				_clear_msel_group_ext();
+				return -EINVAL;
+			}
+		} else {
+			PR_BOOTMSG("id[%d] exceed the range, it must be 0~%d\n",id_int, WSCT_AMOUNT-1);
+			_clear_msel_group_ext();
+			return -EINVAL;
+		}
+	}
+#ifdef FILE_NODE_DBG	
+	PR_BOOTMSG("input data [%s]\n",msel_group_ext);
+	/*PR_BOOTMSG("msel_group_ext_store size para n[%d]\n",n);*/
+	int i;
+	PR_BOOTMSG("save data\n");
+	for (i=0;i<WSCT_AMOUNT;i++) {
+		PR_BOOTMSG("id[%d]=%X\n",i,msel_group_ext_val[i]);
+	}
+#endif	
+	return n;
+}
+
+static ssize_t msel_group_ext_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%s\n", msel_group_ext);
+}
+
+
+char wsct_rw[FILE_NODE_DATA_LEN] = {'\0'};
+
+static void _clear_wsct_rw(void) {
+	int i;
+
+	for (i=0;i<WSCT_AMOUNT;i++) {
+		wsct_rw_val[i] = BM_WSCT_RW_RWBOTH;
+	}
+	wsct_rw[0] = '\0';
+}
+
+static ssize_t wsct_rw_store(struct kobject *kobj,
+		struct kobj_attribute *attr,
+		const char *buf,
+		size_t n)
+{
+	char *token, *cur= wsct_rw;
+	char *_id = NULL, *_rw_type = NULL;
+	int id_int = 0;
+
+	_clear_wsct_rw();
+
+	snprintf(wsct_rw, FILE_NODE_DATA_LEN, "%s", buf);
+	wsct_rw[n-1]='\0';
+
+	while (cur != NULL) {
+		token = strsep(&cur, delim_comma);
+		PR_BOOTMSG("token: %s\n",token);
+		/*token EX: 4:R , 5:W (ID,RW)*/
+
+		_id = strsep(&token, delim_coclon); // ID
+		_rw_type = strsep(&token, delim_coclon);
+
+		if (_id == NULL || _rw_type == NULL) {
+			PR_BOOTMSG("err: _id[%s] _rw_type[%s], para can't be NULL\n",_id, _rw_type);
+			_clear_wsct_rw();
+			return -EINVAL;
+		}
+
+		PR_BOOTMSG("_id[%s] _rw_type[%s]\n",_id, _rw_type);
+		if (kstrtouint(_id, 0, &id_int) != 0) {
+			PR_BOOTMSG("_id[%s] trans to hex err\n",_id);
+			_clear_wsct_rw();
+			return -EINVAL;
+		}
+
+
+		if ( id_int >= 0 && id_int < WSCT_AMOUNT) {
+			if ( 0 == strncmp("NONE",_rw_type,4))
+				wsct_rw_val[id_int] = BM_WSCT_RW_DISABLE;
+			else if (0 == strncmp("R",_rw_type,4))
+				wsct_rw_val[id_int] = BM_WSCT_RW_READONLY;
+			else if (0 == strncmp("W",_rw_type,4))
+				wsct_rw_val[id_int] = BM_WSCT_RW_WRITEONLY;
+			else if (0 == strncmp("RW",_rw_type,4))
+				wsct_rw_val[id_int] = BM_WSCT_RW_RWBOTH;
+			else {
+				PR_BOOTMSG("_id[%s] has err rwtype[%s]\n", _id, _rw_type);
+				_clear_wsct_rw();
+				return -EINVAL;
+			}
+
+		} else {
+			PR_BOOTMSG("id[%d] exceed the range, it must be 0~%d\n",id_int, WSCT_AMOUNT-1);
+			_clear_wsct_rw();
+			return -EINVAL;
+		}
+	}
+
+#ifdef FILE_NODE_DBG
+	PR_BOOTMSG("wsct_rw_store input data [%s]\n",wsct_rw);
+	int i;
+	PR_BOOTMSG("rwtype save data\n");
+	for (i=0;i<WSCT_AMOUNT;i++) {
+		PR_BOOTMSG("id[%d]=%d\n",i,wsct_rw_val[i]);
+	}
+#endif
+	return n;
+}
+
+static ssize_t wsct_rw_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%s\n", wsct_rw);
+}
+
+
+static unsigned int WSCT_HPRI_DIS[WSCT_AMOUNT];
+static unsigned int WSCT_HPRI_SEL[WSCT_AMOUNT];
+char wsct_high_priority_enable[FILE_NODE_DATA_LEN] = {'\0'};
+
+static void _clear_wsct_high_priority_enable(void) {
+	int i;
+
+	for (i=0;i<WSCT_AMOUNT;i++) {
+		WSCT_HPRI_DIS[i] = 1;
+		WSCT_HPRI_SEL[i] = 0xF;
+	}
+
+	WSCT_HPRI_DIS[4] = 0;
+	WSCT_HPRI_SEL[4] = 0x8;  /* ultra */
+
+	WSCT_HPRI_DIS[5] = 0;
+	WSCT_HPRI_SEL[5] = 0x4; /* pre_ultra */
+
+
+	wsct_high_priority_enable[0] = '\0';
+}
+
+static ssize_t wsct_high_priority_enable_store(struct kobject *kobj,
+		struct kobj_attribute *attr,
+		const char *buf,
+		size_t n)
+{
+	char *token, *cur= wsct_high_priority_enable;
+	char *_id = NULL, *_enable = NULL,  *_level = NULL;
+	int  id_int = 0, level_int = 0;
+
+	_clear_wsct_high_priority_enable();
+
+	snprintf(wsct_high_priority_enable, FILE_NODE_DATA_LEN, "%s", buf);
+	wsct_high_priority_enable[n-1]='\0';
+	
+	while (cur != NULL) {
+		token = strsep(&cur, delim_comma);
+		PR_BOOTMSG("token: %s\n",token);
+		/*token EX: 4:R , 5:W (ID,RW)*/
+
+		_id = strsep(&token, delim_coclon); // ID
+		_enable = strsep(&token, delim_coclon);
+		_level = strsep(&token, delim_coclon);
+
+		PR_BOOTMSG("_id[%s] _enable[%s] _level[%s]\n",_id, _enable, _level);
+
+		if (_id == NULL || _enable == NULL || _level == NULL ) {
+			PR_BOOTMSG("err : _id[%s] _enable[%s] _level[%s], para can't be NULL\n",_id, _enable, _level);
+			_clear_wsct_high_priority_enable();
+			return -EINVAL;
+		}
+
+		if (kstrtouint(_id, 0, &id_int) != 0) {
+			PR_BOOTMSG("_id[%s] trans to hex err\n",_id);
+			_clear_wsct_high_priority_enable();
+			return -EINVAL;
+		}
+
+
+		if ( id_int >= 0 && id_int < WSCT_AMOUNT) {
+			if ( 0 == strncmp("disable", _enable, 7)) {
+
+				WSCT_HPRI_DIS[id_int] = 1;
+				WSCT_HPRI_SEL[id_int] = 0xf;
+			} else if ( 0 == strncmp("enable", _enable, 6)) {
+
+				WSCT_HPRI_DIS[id_int] = 0;
+				if (kstrtouint(_level, 0, &level_int) != 0) {
+					PR_BOOTMSG("_id[%s] trans ultraLevel[%s] to hex err\n",_id, _level);
+					_clear_wsct_high_priority_enable();
+					return -EINVAL;
+				}
+				WSCT_HPRI_SEL[id_int] = level_int & 0xF;				
+			} else {
+				PR_BOOTMSG("_id[%s] has err enable[%s] (enable/disable)\n", _id, _enable);
+				_clear_wsct_high_priority_enable();
+				return -EINVAL;
+			}
+
+		} else {
+			PR_BOOTMSG("id[%d] exceed the range, it must be 0~%d\n",id_int, WSCT_AMOUNT-1);
+			_clear_wsct_high_priority_enable();
+			return -EINVAL;
+		}
+	}
+#ifdef FILE_NODE_DBG	
+	PR_BOOTMSG("input data [%s]\n",wsct_high_priority_enable);
+	int i;
+	PR_BOOTMSG("wsct_high_priority_enable save data\n");
+	for (i=0;i<WSCT_AMOUNT;i++) {
+		PR_BOOTMSG("id[%d]=(%X,%X)\n", i, WSCT_HPRI_DIS[i], WSCT_HPRI_SEL[i]);
+	}
+#endif
+	return n;
+}
+
+static ssize_t wsct_high_priority_enable_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%s\n", wsct_high_priority_enable);
+}
+
+
+static unsigned int wsct_busid_val[WSCT_AMOUNT];
+static unsigned int wsct_idMask_val[WSCT_AMOUNT];
+
+char wsct_busid[FILE_NODE_DATA_LEN] = {'\0'};
+
+static void _clear_wsct_busid(void) {
+	int i;
+
+	for (i=0;i<WSCT_AMOUNT;i++) {
+		wsct_busid_val[i] = 0xfffff;
+		wsct_idMask_val[i] = 0x1FFF;
+	}
+	wsct_busid[0] = '\0';
+}
+
+static ssize_t wsct_busid_store(struct kobject *kobj,
+		struct kobj_attribute *attr,
+		const char *buf,
+		size_t n)
+{
+	char *token, *cur= wsct_busid;
+
+	char *_id = NULL, *_busid = NULL, *_idMask = NULL;
+	int id_int = 0, busid_int = 0, idMask_int = 0;
+
+	_clear_wsct_busid();
+
+	snprintf(wsct_busid, FILE_NODE_DATA_LEN, "%s", buf);
+	wsct_busid[n-1]='\0';
+	
+	while (cur != NULL) {
+		token = strsep(&cur, delim_comma);
+		PR_BOOTMSG("token: %s\n",token);
+		/*token EX: 4:R , 5:W (ID,RW)*/
+
+		_id = strsep(&token, delim_coclon); // ID
+		_busid = strsep(&token, delim_coclon);
+		_idMask = strsep(&token, delim_coclon);
+
+		PR_BOOTMSG("_id[%s] _busid[%s] _idMask[%s]\n",_id, _busid, _idMask);
+
+		if (_id == NULL || _busid == NULL || _idMask == NULL) {
+			PR_BOOTMSG("err: _id[%s] _busid[%s] _idMask[%s] ,parameter can't be NULL\n",_id, _busid, _idMask);
+			_clear_wsct_busid();
+			return -EINVAL;
+		}
+
+		
+		if (kstrtouint(_id, 0, &id_int) != 0) {
+			PR_BOOTMSG("_id[%s] trans to hex err\n",_id);
+			_clear_wsct_busid();
+			return -EINVAL;
+		}
+		if (kstrtouint(_busid, 0, &busid_int) != 0) {
+			PR_BOOTMSG("_busid[%s] trans to hex err\n",_busid);
+			_clear_wsct_busid();
+			return -EINVAL;
+		}
+		if (kstrtouint(_idMask, 0, &idMask_int) != 0) {
+			PR_BOOTMSG("_idMask[%s] trans to hex err\n",_idMask);
+			_clear_wsct_busid();
+			return -EINVAL;
+		}
+
+
+		if ( id_int >= 0 && id_int < WSCT_AMOUNT) {
+			wsct_busid_val[id_int] = busid_int;
+			wsct_idMask_val[id_int] = idMask_int;
+
+		} else {
+			PR_BOOTMSG("id[%d] exceed the range, it must be 0~%d\n",id_int, WSCT_AMOUNT-1);
+			_clear_wsct_busid();
+			return -EINVAL;
+		}
+	}
+#ifdef FILE_NODE_DBG	
+	PR_BOOTMSG("input data [%s]\n",wsct_busid);
+	int i;
+	PR_BOOTMSG("wsct_busid save data\n");
+	for (i=0;i<WSCT_AMOUNT;i++) {
+		PR_BOOTMSG("id[%d](busid,idMask)=(%X,%X)\n", i, wsct_busid_val[i], wsct_idMask_val[i]);
+	}
+#endif
+	return n;
+}
+
+static ssize_t wsct_busid_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%s\n", wsct_busid);
+}
+
+
+static unsigned int  wsct_chn_rank_sel_val[WSCT_AMOUNT];
+char wsct_chn_rank_sel[FILE_NODE_DATA_LEN] = {'\0'};
+
+static void _clear_wsct_chn_rank_sel(void) {
+	int i;
+
+	for (i=0;i<WSCT_AMOUNT;i++) {
+		wsct_chn_rank_sel_val[i] = 0xF;
+	}
+	wsct_chn_rank_sel[0] = '\0';
+}
+
+static ssize_t wsct_chn_rank_sel_store(struct kobject *kobj,
+		struct kobj_attribute *attr,
+		const char *buf,
+		size_t n)
+{
+	char *token, *cur= wsct_chn_rank_sel;
+	char *_id = NULL, *_chn_rank = NULL;
+	int id_int = 0, chn_rank_int = 0;
+
+	_clear_wsct_chn_rank_sel();
+
+	snprintf(wsct_chn_rank_sel, FILE_NODE_DATA_LEN, "%s", buf);
+	wsct_chn_rank_sel[n-1]='\0';
+
+
+	while (cur != NULL) {
+		token = strsep(&cur, delim_comma);
+		PR_BOOTMSG("token: %s\n",token);
+		/*token EX: 4:f , 5:C (ID,chn_rnk_sel)*/
+
+		_id = strsep(&token, delim_coclon); // ID
+		_chn_rank = strsep(&token, delim_coclon);
+
+		PR_BOOTMSG("_id[%s] _chn_rank[%s]\n",_id, _chn_rank);
+
+		if (_id == NULL || _chn_rank == NULL) {
+			PR_BOOTMSG("err : _id[%s] _chn_rank[%s], para can't be NULL\n",_id, _chn_rank);
+			_clear_wsct_chn_rank_sel();
+			return -EINVAL;
+		}
+
+		
+		if (kstrtouint(_id, 0, &id_int) != 0) {
+			PR_BOOTMSG("_id[%s] trans to hex err\n",_id);
+			_clear_wsct_chn_rank_sel();
+			return -EINVAL;
+		}
+		if (kstrtouint(_chn_rank, 0, &chn_rank_int) != 0) {
+			PR_BOOTMSG("_chn_rank[%s] trans to hex err\n",_id);
+			_clear_wsct_chn_rank_sel();
+			return -EINVAL;
+		}
+
+		if ( id_int >= 0 && id_int < WSCT_AMOUNT) {
+			wsct_chn_rank_sel_val[id_int] = chn_rank_int;
+
+		} else {
+			PR_BOOTMSG("id[%d] exceed the range, it must be 0~%d\n",id_int, WSCT_AMOUNT-1);
+			_clear_wsct_chn_rank_sel();
+			return -EINVAL;
+		}
+	}
+
+#ifdef FILE_NODE_DBG	
+	PR_BOOTMSG("wsct_chn_rank_sel input data [%s]\n",wsct_chn_rank_sel);
+	int i;
+	PR_BOOTMSG("wsct_chn_rank_sel_val save data\n");
+	for (i=0;i<WSCT_AMOUNT;i++) {
+		PR_BOOTMSG("id[%d]=%X\n",i,wsct_chn_rank_sel_val[i]);
+	}
+#endif
+	return n;
+}
+
+static ssize_t wsct_chn_rank_sel_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%s\n", wsct_chn_rank_sel);
+}
+
+static unsigned int  wsct_byte_low_bnd_val[WSCT_AMOUNT];
+static unsigned int  wsct_byte_up_bnd_val[WSCT_AMOUNT];
+static unsigned int  wsct_byte_bnd_dis[WSCT_AMOUNT];
+char wsct_burst_range[FILE_NODE_DATA_LEN] = {'\0'};
+
+static void _clear_wsct_burst_range(void) {
+	int i;
+
+	for (i=0;i<WSCT_AMOUNT;i++) {
+		wsct_byte_low_bnd_val[i] = 0x0;
+		wsct_byte_up_bnd_val[i] = 0x1FF;
+		wsct_byte_bnd_dis[i] = 1;
+	}
+	wsct_burst_range[0] = '\0';
+}
+
+static ssize_t wsct_burst_range_store(struct kobject *kobj,
+		struct kobj_attribute *attr,
+		const char *buf,
+		size_t n)
+{
+	char *token, *cur= wsct_burst_range;
+	char *_id = NULL, *_low_bnd = NULL, *_up_bnd = NULL;
+	int id_int = 0, low_bnd_int = 0, up_bnd_int = 0;
+
+	_clear_wsct_burst_range();
+	
+	snprintf(wsct_burst_range, FILE_NODE_DATA_LEN, "%s", buf);
+	wsct_burst_range[n-1]='\0';
+
+
+	while (cur != NULL) {
+		token = strsep(&cur, delim_comma);
+		PR_BOOTMSG("token: %s\n",token);
+		/*token EX: 4:f , 5:C (ID,chn_rnk_sel)*/
+
+		_id = strsep(&token, delim_coclon); // ID
+		_low_bnd = strsep(&token, delim_coclon);
+		_up_bnd = strsep(&token, delim_coclon);
+
+		PR_BOOTMSG("_id[%s] _low_bnd[%s] _up_bnd[%s]\n",_id, _low_bnd, _up_bnd);
+
+		if (_id == NULL || _low_bnd == NULL || _up_bnd == NULL) {
+			PR_BOOTMSG("err : _id[%s] _low_bnd[%s] _up_bnd[%s], para can't be NULL\n",_id, _low_bnd, _up_bnd);
+			_clear_wsct_burst_range();
+			return -EINVAL;
+		}
+
+		if (kstrtouint(_id, 0, &id_int) != 0) {
+			PR_BOOTMSG("_id[%s] trans to hex err\n",_id);
+			_clear_wsct_burst_range();
+			return -EINVAL;
+		}
+		if (kstrtouint(_low_bnd, 0, &low_bnd_int) != 0) {
+			PR_BOOTMSG("_low_bnd[%s] trans to hex err\n",_id);
+			_clear_wsct_burst_range();
+			return -EINVAL;
+		}
+		if (kstrtouint(_up_bnd, 0, &up_bnd_int) != 0) {
+			PR_BOOTMSG("_up_bnd[%s] trans to hex err\n",_id);
+			_clear_wsct_burst_range();
+			return -EINVAL;
+		}
+
+		if ( id_int >= 0 && id_int < WSCT_AMOUNT) {
+			wsct_byte_low_bnd_val[id_int] = low_bnd_int;
+			wsct_byte_up_bnd_val[id_int] = up_bnd_int;
+			wsct_byte_bnd_dis[id_int] = 0;
+		} else {
+			PR_BOOTMSG("id[%d] exceed the range, it must be 0~%d\n",id_int, WSCT_AMOUNT-1);
+			_clear_wsct_burst_range();
+			return -EINVAL;
+		}
+	}
+
+#ifdef FILE_NODE_DBG
+	PR_BOOTMSG("wsct_burst_range_store input data [%s]\n",wsct_burst_range);
+	int i;
+	PR_BOOTMSG("wsct_burst_range save data\n");
+	for (i=0;i<WSCT_AMOUNT;i++) {
+		PR_BOOTMSG("id[%d](low_bnd,up_bnd)=(%X,%X)\n",i,wsct_byte_low_bnd_val[i],wsct_byte_up_bnd_val[i]);
+	}
+#endif
+	return n;
+}
+
+static ssize_t wsct_burst_range_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%s\n", wsct_burst_range);
+}
+
+
+
+static unsigned int tsct_busid_enable_val[TSCT_AMOUNT];
+char tsct_busid_enable[FILE_NODE_DATA_LEN] = {'\0'};
+
+static void _clear_tsct_busid_enable(void) {
+	int i;
+
+	for (i=0;i<TSCT_AMOUNT;i++) {
+		tsct_busid_enable_val[i] = 0;
+	}
+	tsct_busid_enable[0] = '\0';
+}
+
+static ssize_t tsct_busid_enable_store(struct kobject *kobj,
+		struct kobj_attribute *attr,
+		const char *buf,
+		size_t n)
+{
+	char *token, *cur= tsct_busid_enable;
+	char *_id = NULL, *_enable = NULL;
+	int  id_int = 0;
+
+	_clear_tsct_busid_enable();
+
+	snprintf(tsct_busid_enable, FILE_NODE_DATA_LEN, "%s", buf);
+	tsct_busid_enable[n-1]='\0';
+
+
+	while (cur != NULL) {
+		token = strsep(&cur, delim_comma);
+		PR_BOOTMSG("token: %s\n",token);
+		/*token EX: 4:R , 5:W (ID,RW)*/
+
+		_id = strsep(&token, delim_coclon); // ID
+		_enable = strsep(&token, delim_coclon);
+
+
+		PR_BOOTMSG("_id[%s] _enable[%s]\n",_id, _enable);
+
+		if (_id == NULL || _enable == NULL) {
+			PR_BOOTMSG("err : _id[%s] _enable[%s], para can't be NULL\n",_id, _enable);
+			_clear_tsct_busid_enable();
+			return -EINVAL;
+		}
+
+		if (kstrtouint(_id, 0, &id_int) != 0) {
+			PR_BOOTMSG("_id[%s] trans to hex err\n",_id);
+			_clear_tsct_busid_enable();
+			return -EINVAL;
+		}
+
+
+		if ( id_int >= 0 && id_int < TSCT_AMOUNT) {
+			if ( 0 == strncmp("disable", _enable, 7)) {
+				tsct_busid_enable_val[id_int] = 0;
+			} else if ( 0 == strncmp("enable", _enable, 6)) {
+				tsct_busid_enable_val[id_int] = 1;
+			} else {
+				PR_BOOTMSG("_id[%s] has err enable[%s] (enable/disable)\n", _id, _enable);
+				_clear_tsct_busid_enable();
+				return -EINVAL;
+			}
+
+		} else {
+			PR_BOOTMSG("id[%d] exceed the range, it must be 0~%d\n",id_int, TSCT_AMOUNT-1);
+			_clear_tsct_busid_enable();
+			return -EINVAL;
+		}
+	}
+#ifdef FILE_NODE_DBG	
+	PR_BOOTMSG("tsct_busid_enable input data [%s]\n",tsct_busid_enable);
+	int i;
+	PR_BOOTMSG("wsct_high_priority_enable save data\n");
+	for (i=0;i<TSCT_AMOUNT;i++) {
+		PR_BOOTMSG("id[%d]=(%d)\n", i, tsct_busid_enable_val[i]);
+	}
+#endif
+	return n;
+}
+
+static ssize_t tsct_busid_enable_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%s\n", tsct_busid_enable);
+}
+
+
+/* use the origin para high_priority_filter to save the en/dis setting */
+static unsigned int TTYPE_HPRI_SEL[BM_COUNTER_MAX];
+char ttype_high_priority_ext[FILE_NODE_DATA_LEN] = {'\0'};
+
+static void _clear_ttype_high_priority_ext(void) {
+	int i;
+
+	for (i=0;i<BM_COUNTER_MAX;i++) {
+		TTYPE_HPRI_SEL[i] = 0xf;
+	}
+
+	high_priority_filter = 0x0;
+	ttype_high_priority_ext[0] = '\0';
+}
+
+static ssize_t ttype_high_priority_ext_store(struct kobject *kobj,
+		struct kobj_attribute *attr,
+		const char *buf,
+		size_t n)
+{
+	char *token, *cur= ttype_high_priority_ext;
+	char *_id = NULL, *_enable = NULL,  *_level = NULL;
+	int  id_int = 0, level_int = 0;
+
+	_clear_ttype_high_priority_ext();
+
+	snprintf(ttype_high_priority_ext, FILE_NODE_DATA_LEN, "%s", buf);
+	ttype_high_priority_ext[n-1]='\0';
+
+	while (cur != NULL) {
+		token = strsep(&cur, delim_comma);
+		PR_BOOTMSG("token: %s\n",token);
+		/*token EX: 4:R , 5:W (ID,RW)*/
+
+		_id = strsep(&token, delim_coclon); // ID
+		_enable = strsep(&token, delim_coclon);
+		_level = strsep(&token, delim_coclon);
+
+		PR_BOOTMSG("_id[%s] _enable[%s] _level[%s]\n",_id, _enable, _level);
+
+		if (_id == NULL || _enable == NULL || _level == NULL ) {
+			PR_BOOTMSG("err : _id[%s] _enable[%s] _level[%s], para can't be NULL\n",_id, _enable, _level);
+			_clear_ttype_high_priority_ext();
+			return -EINVAL;
+		}
+
+		if (kstrtouint(_id, 0, &id_int) != 0) {
+			PR_BOOTMSG("_id[%s] trans to hex err\n",_id);
+			_clear_ttype_high_priority_ext();
+			return -EINVAL;
+		}
+
+		id_int = id_int - 1;
+		if ( id_int >= 0 && id_int < BM_COUNTER_MAX) {
+			if ( 0 == strncmp("disable", _enable, 7)) {
+
+				high_priority_filter = ( high_priority_filter & ~(1<<id_int) ) | ( 0<<id_int );
+				TTYPE_HPRI_SEL[id_int] = 0xf;
+			} else if ( 0 == strncmp("enable", _enable, 6)) {
+
+				high_priority_filter = ( high_priority_filter & ~(1<<id_int) ) | ( 1<<id_int );
+				if (kstrtouint(_level, 0, &level_int) != 0) {
+					PR_BOOTMSG("_id[%s] trans ultraLevel[%s] to hex err\n",_id, _level);
+					_clear_ttype_high_priority_ext();
+					return -EINVAL;
+				}
+				TTYPE_HPRI_SEL[id_int] = level_int & 0xF;				
+			} else {
+				PR_BOOTMSG("ttype_high_priority_ext: _id[%s] has err enable[%s] (enable/disable)\n", _id, _enable);
+				_clear_ttype_high_priority_ext();
+				return -EINVAL;
+			}
+
+		} else {
+			PR_BOOTMSG("id[%d] exceed the range, it must be 1~%d\n",id_int+1, BM_COUNTER_MAX);
+			_clear_ttype_high_priority_ext();
+			return -EINVAL;
+		}
+	}
+#ifdef FILE_NODE_DBG
+	PR_BOOTMSG("input data [%s]\n",ttype_high_priority_ext);
+
+	int i;
+	PR_BOOTMSG("wsct_high_priority_enable save data\n");
+	for (i=0;i<BM_COUNTER_MAX;i++) {
+		PR_BOOTMSG("id[%d]=(%X,%X)\n", i+1, high_priority_filter>>i & 0x1, TTYPE_HPRI_SEL[i]);
+	}
+#endif
+	return n;
+}
+
+static ssize_t ttype_high_priority_ext_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%s\n", ttype_high_priority_ext);
+}
+
+
+static unsigned int ttype_idMask_val[BM_COUNTER_MAX];
+char ttype_busid_ext[FILE_NODE_DATA_LEN] = {'\0'};
+
+static void _clear_ttype_busid_ext(void) {
+	int i;
+
+	for (i=0;i<BM_COUNTER_MAX;i++) {
+		ttype_busid_val[i] = 0xfffff;
+		ttype_idMask_val[i] = 0x1FFF;	
+	}
+	ttype_busid_ext[0] = '\0';
+}
+
+/*id: 1~21*/
+static ssize_t ttype_busid_ext_store(struct kobject *kobj,
+		struct kobj_attribute *attr,
+		const char *buf,
+		size_t n)
+{
+	char *token, *cur= ttype_busid_ext;
+
+	char *_id = NULL, *_busid = NULL, *_idMask = NULL;
+	int id_int = 0, busid_int = 0, idMask_int = 0;
+
+	_clear_ttype_busid_ext();
+
+	snprintf(ttype_busid_ext, FILE_NODE_DATA_LEN, "%s", buf);
+	ttype_busid_ext[n-1]='\0';
+
+	while (cur != NULL) {
+		token = strsep(&cur, delim_comma);
+		PR_BOOTMSG("token: %s\n",token);
+		/*token EX: 4:R , 5:W (ID,RW)*/
+
+		_id = strsep(&token, delim_coclon); // ID
+		_busid = strsep(&token, delim_coclon);
+		_idMask = strsep(&token, delim_coclon);
+
+		PR_BOOTMSG("_id[%s] _busid[%s] _idMask[%s]\n",_id, _busid, _idMask);
+
+		if (_id == NULL || _busid == NULL || _idMask == NULL) {
+			PR_BOOTMSG("err: ttype_busid_ext _id[%s] _busid[%s] _idMask[%s] ,parameter can't be NULL\n",_id, _busid, _idMask);
+			_clear_ttype_busid_ext();
+			return -EINVAL;
+		}
+	
+		if (kstrtouint(_id, 0, &id_int) != 0) {
+			PR_BOOTMSG("_id[%s] trans to hex err\n",_id);
+			_clear_ttype_busid_ext();
+			return -EINVAL;
+		}
+		if (kstrtouint(_busid, 0, &busid_int) != 0) {
+			PR_BOOTMSG("_busid[%s] trans to hex err\n",_busid);
+			_clear_ttype_busid_ext();
+			return -EINVAL;
+		}
+		if (kstrtouint(_idMask, 0, &idMask_int) != 0) {
+			PR_BOOTMSG("_idMask[%s] trans to hex err\n",_idMask);
+			_clear_ttype_busid_ext();
+			return -EINVAL;
+		}
+
+		id_int = id_int - 1;
+		if ( id_int >= 0 && id_int < BM_COUNTER_MAX) {
+			ttype_busid_val[id_int] = busid_int;
+			ttype_idMask_val[id_int] = idMask_int;
+
+		} else {
+			PR_BOOTMSG("ttype_busid_ext id[%d] exceed the range, it must be 1~%d\n",id_int+1, BM_COUNTER_MAX);
+			_clear_ttype_busid_ext();
+			return -EINVAL;
+		}
+	}
+#ifdef FILE_NODE_DBG
+	PR_BOOTMSG("ttype_busid_ext input data [%s]\n",ttype_busid_ext);
+
+	int i;
+	PR_BOOTMSG("ttype_busid_ext save data\n");
+	for (i=0;i<BM_COUNTER_MAX;i++) {
+		PR_BOOTMSG("id[%d](busid,idMask)=(%X,%X)\n", i+1, ttype_busid_val[i], ttype_idMask_val[i]);
+	}
+#endif
+	return n;
+}
+
+static ssize_t ttype_busid_ext_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%s\n", ttype_busid_ext);
+}
+
+
+static unsigned int  ttype_chn_rank_sel_val[BM_COUNTER_MAX];
+char ttype_chn_rank_sel[FILE_NODE_DATA_LEN] = {'\0'};
+
+static void _clear_ttype_chn_rank_sel(void) {
+	int i;
+
+	for (i=0;i<BM_COUNTER_MAX;i++) {
+		ttype_chn_rank_sel_val[i] = 0xF;
+	}
+	ttype_chn_rank_sel[0] = '\0';
+}
+
+static ssize_t ttype_chn_rank_sel_store(struct kobject *kobj,
+		struct kobj_attribute *attr,
+		const char *buf,
+		size_t n)
+{
+	char *token, *cur= ttype_chn_rank_sel;
+	char *_id = NULL, *_chn_rank = NULL;
+	int id_int = 0, chn_rank_int = 0;
+
+	_clear_ttype_chn_rank_sel();
+
+	snprintf(ttype_chn_rank_sel, FILE_NODE_DATA_LEN, "%s", buf);
+	ttype_chn_rank_sel[n-1]='\0';
+
+	while (cur != NULL) {
+		token = strsep(&cur, delim_comma);
+		PR_BOOTMSG("token: %s\n",token);
+		/*token EX: 4:f , 5:C (ID,chn_rnk_sel)*/
+
+		_id = strsep(&token, delim_coclon); // ID
+		_chn_rank = strsep(&token, delim_coclon);
+
+		PR_BOOTMSG("_id[%s] _chn_rank[%s]\n",_id, _chn_rank);
+
+		if (_id == NULL || _chn_rank == NULL) {
+			PR_BOOTMSG("err (ttype_chn_rank_sel): _id[%s] _chn_rank[%s], para can't be NULL\n",_id, _chn_rank);
+			_clear_ttype_chn_rank_sel();
+			return -EINVAL;
+		}
+
+		
+		if (kstrtouint(_id, 0, &id_int) != 0) {
+			PR_BOOTMSG("_id[%s] trans to hex err\n",_id);
+			_clear_ttype_chn_rank_sel();
+			return -EINVAL;
+		}
+		if (kstrtouint(_chn_rank, 0, &chn_rank_int) != 0) {
+			PR_BOOTMSG("_chn_rank[%s] trans to hex err\n",_id);
+			_clear_ttype_chn_rank_sel();
+			return -EINVAL;
+		}
+
+		id_int = id_int -1;
+
+		if ( id_int >= 0 && id_int < BM_COUNTER_MAX) {
+			ttype_chn_rank_sel[id_int] = chn_rank_int;
+
+		} else {
+			PR_BOOTMSG("id[%d] exceed the range, it must be 1~%d\n",id_int+1, BM_COUNTER_MAX);
+			_clear_ttype_chn_rank_sel();
+			return -EINVAL;
+		}
+	}
+
+#ifdef FILE_NODE_DBG
+	PR_BOOTMSG("ttype_chn_rank_sel input data [%s]\n",ttype_chn_rank_sel);
+
+	int i;
+	PR_BOOTMSG("wsct_chn_rank_sel_val save data\n");
+	for (i=0;i<BM_COUNTER_MAX;i++) {
+		PR_BOOTMSG("id[%d]=%X\n",i+1,ttype_chn_rank_sel[i]);
+	}
+#endif
+	return n;
+}
+
+static ssize_t ttype_chn_rank_sel_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%s\n", ttype_chn_rank_sel);
+}
+
+
+static unsigned int  ttype_byte_low_bnd_val[BM_COUNTER_MAX];
+static unsigned int  ttype_byte_up_bnd_val[BM_COUNTER_MAX];
+static unsigned int  ttype_byte_bnd_dis[BM_COUNTER_MAX];
+char ttype_burst_range[FILE_NODE_DATA_LEN] = {'\0'};
+
+static void _clear_ttype_burst_range(void) {
+	int i;
+
+	for (i=0;i<BM_COUNTER_MAX;i++) {
+		ttype_byte_low_bnd_val[i] = 0x0;
+		ttype_byte_up_bnd_val[i] = 0x1FF;
+		ttype_byte_bnd_dis[i] = 1;
+	}
+	ttype_burst_range[0] = '\0';
+}
+
+static ssize_t ttype_burst_range_store(struct kobject *kobj,
+		struct kobj_attribute *attr,
+		const char *buf,
+		size_t n)
+{
+	char *token, *cur= ttype_burst_range;
+	char *_id = NULL, *_low_bnd = NULL, *_up_bnd = NULL;
+	int id_int = 0, low_bnd_int = 0, up_bnd_int = 0;
+
+	_clear_ttype_burst_range();
+	
+	snprintf(ttype_burst_range, FILE_NODE_DATA_LEN, "%s", buf);
+	ttype_burst_range[n-1]='\0';
+
+
+	while (cur != NULL) {
+		token = strsep(&cur, delim_comma);
+		PR_BOOTMSG("token: %s\n",token);
+		/*token EX: 4:f , 5:C (ID,chn_rnk_sel)*/
+
+		_id = strsep(&token, delim_coclon); // ID
+		_low_bnd = strsep(&token, delim_coclon);
+		_up_bnd = strsep(&token, delim_coclon);
+
+		PR_BOOTMSG("_id[%s] _low_bnd[%s] _up_bnd[%s]\n",_id, _low_bnd, _up_bnd);
+
+		if (_id == NULL || _low_bnd == NULL || _up_bnd == NULL) {
+			PR_BOOTMSG("err (ttype_burst_range): _id[%s] _low_bnd[%s] _up_bnd[%s], para can't be NULL\n",
+				        _id, _low_bnd, _up_bnd);
+			_clear_ttype_burst_range();
+			return -EINVAL;
+		}
+
+		if (kstrtouint(_id, 0, &id_int) != 0) {
+			PR_BOOTMSG("_id[%s] trans to hex err\n",_id);
+			_clear_ttype_burst_range();
+			return -EINVAL;
+		}
+		if (kstrtouint(_low_bnd, 0, &low_bnd_int) != 0) {
+			PR_BOOTMSG("_low_bnd[%s] trans to hex err\n",_id);
+			_clear_ttype_burst_range();
+			return -EINVAL;
+		}
+		if (kstrtouint(_up_bnd, 0, &up_bnd_int) != 0) {
+			PR_BOOTMSG("_up_bnd[%s] trans to hex err\n",_id);
+			_clear_ttype_burst_range();
+			return -EINVAL;
+		}
+
+		id_int = id_int - 1;
+		if ( id_int >= 0 && id_int < BM_COUNTER_MAX) {
+			ttype_byte_low_bnd_val[id_int] = low_bnd_int;
+			ttype_byte_up_bnd_val[id_int] = up_bnd_int;
+			ttype_byte_bnd_dis[id_int] = 0;
+		} else {
+			PR_BOOTMSG("id[%d] exceed the range, it must be 1~%d\n",id_int, BM_COUNTER_MAX);
+			_clear_ttype_burst_range();
+			return -EINVAL;
+		}
+	}
+
+#ifdef FILE_NODE_DBG
+	PR_BOOTMSG("ttype_burst_range_store input data [%s]\n",ttype_burst_range);
+
+	int i;
+	PR_BOOTMSG("ttype_burst_range save data\n");
+	for (i=0;i<BM_COUNTER_MAX;i++) {
+		PR_BOOTMSG("id[%d](low_bnd,up_bnd)=(%X,%X)\n",i+1,ttype_byte_low_bnd_val[i],ttype_byte_up_bnd_val[i]);
+	}
+#endif
+	return n;
+}
+
+static ssize_t ttype_burst_range_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%s\n", ttype_burst_range);
+}
+
+static int reserve_wsct_setting;
+DECLARE_KOBJ_ATTR_INT(reserve_wsct_setting, reserve_wsct_setting);
+
+static void _clear_setting(void) {
+	/*clear all file node para here*/
+	
+	PR_BOOTMSG("clear EMI file node setting\n");
+
+	_clear_msel_group_ext();
+	_clear_wsct_rw();
+	_clear_wsct_high_priority_enable();
+	_clear_wsct_busid();
+	_clear_wsct_chn_rank_sel();
+	_clear_wsct_burst_range();
+
+	_clear_tsct_busid_enable();
+	_clear_ttype_high_priority_ext();
+	_clear_ttype_busid_ext();
+	_clear_ttype_chn_rank_sel();
+	_clear_ttype_burst_range();
+	reserve_wsct_setting = 0;
+
+
+
+	emi_TP_busfiltr_enable = 0;
+
+	high_priority_filter = 0x0;
+	rwtype = BM_BOTH_READ_WRITE;
+	dramc_pdir_enable = 1;
+
+
+	msel_enable = 0;
+	msel_group1 = BM_MASTER_ALL;
+	msel_group2 = BM_MASTER_ALL;
+	msel_group3 = BM_MASTER_ALL;
+
+
+	bw_limiter_enable = BM_BW_LIMITER_ENABLE;
+	ttype1_16_en = BM_TTYPE1_16_DISABLE;
+	ttype17_21_en = BM_TTYPE17_21_DISABLE;
+
+}
+
+static ssize_t clear_setting_store(struct kobject *kobj,
+			struct kobj_attribute *attr,
+			const char *buf,
+			size_t n)
+{
+	int value;
+
+	if ((n == 0) || (buf == NULL))
+		return -EINVAL;
+
+	if (kstrtoint(buf, 0, &value) != 0)
+		return -EINVAL;
+
+	if (value == 1)
+		_clear_setting();
+
+	return n;
+}
+
+static struct kobj_attribute clear_setting_attr = __ATTR_WO(clear_setting); // OK
+static struct kobj_attribute msel_group_ext_attr = __ATTR(msel_group_ext, 0664, msel_group_ext_show, msel_group_ext_store); //OK
+static struct kobj_attribute wsct_rw_attr = __ATTR(wsct_rw, 0664, wsct_rw_show, wsct_rw_store);
+static struct kobj_attribute wsct_high_priority_enable_attr = __ATTR(wsct_high_priority_enable, 0664, wsct_high_priority_enable_show, wsct_high_priority_enable_store);
+static struct kobj_attribute wsct_busid_attr = __ATTR(wsct_busid, 0664, wsct_busid_show, wsct_busid_store);
+static struct kobj_attribute wsct_chn_rank_sel_attr = __ATTR(wsct_chn_rank_sel, 0664, wsct_chn_rank_sel_show, wsct_chn_rank_sel_store);
+static struct kobj_attribute wsct_burst_range_attr = __ATTR(wsct_burst_range, 0664, wsct_burst_range_show, wsct_burst_range_store);
+static struct kobj_attribute tsct_busid_enable_attr = __ATTR(tsct_busid_enable, 0664, tsct_busid_enable_show, tsct_busid_enable_store);
+static struct kobj_attribute ttype_high_priority_ext_attr = __ATTR(ttype_high_priority_ext, 0664, ttype_high_priority_ext_show, ttype_high_priority_ext_store);
+static struct kobj_attribute ttype_busid_ext_attr = __ATTR(ttype_busid_ext, 0664, ttype_busid_ext_show, ttype_busid_ext_store);
+static struct kobj_attribute ttype_chn_rank_sel_attr = __ATTR(ttype_chn_rank_sel, 0664, ttype_chn_rank_sel_show, ttype_chn_rank_sel_store);
+static struct kobj_attribute ttype_burst_range_attr = __ATTR(ttype_burst_range, 0664, ttype_burst_range_show, ttype_burst_range_store);
+
+
+
+
+
+
+/**/
+#define KOBJ_ATTR_ITEM_SERIAL_FNODE(nr) \
+	do { \
+		KOBJ_ATTR_ITEM(ttype ## nr ## _master); \
+		KOBJ_ATTR_ITEM(ttype ## nr ## _nbeat); \
+		KOBJ_ATTR_ITEM(ttype ## nr ## _nbyte); \
+		KOBJ_ATTR_ITEM(ttype ## nr ## _burst); \
+		KOBJ_ATTR_ITEM(ttype ## nr ## _busid); \
+		KOBJ_ATTR_ITEM(ttype ## nr ## _rw); \
+	} while (0)
+
+#define KOBJ_ATTR_LIST \
+	do { \
+		KOBJ_ATTR_ITEM(high_priority_filter); \
+		KOBJ_ATTR_ITEM(emi_TP_busfiltr_enable); \
+		KOBJ_ATTR_ITEM(msel_enable); \
+		KOBJ_ATTR_ITEM(msel_group1); \
+		KOBJ_ATTR_ITEM(msel_group2); \
+		KOBJ_ATTR_ITEM(msel_group3); \
+		KOBJ_ATTR_ITEM(rwtype); \
+		KOBJ_ATTR_ITEM(ttype17_21_en); \
+		KOBJ_ATTR_ITEM(ttype1_16_en); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(1); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(2); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(3); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(4); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(5); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(6); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(7); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(8); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(9); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(10); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(11); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(12); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(13); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(14); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(15); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(16); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(17); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(18); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(19); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(20); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(21); \
+		KOBJ_ATTR_ITEM(bw_limiter_enable); \
+		KOBJ_ATTR_ITEM(dramc_pdir_enable); \
+		KOBJ_ATTR_ITEM(clear_setting);\
+		KOBJ_ATTR_ITEM(msel_group_ext);\
+		KOBJ_ATTR_ITEM(wsct_rw);\
+		KOBJ_ATTR_ITEM(wsct_high_priority_enable);\
+		KOBJ_ATTR_ITEM(wsct_busid);\
+		KOBJ_ATTR_ITEM(wsct_chn_rank_sel);\
+		KOBJ_ATTR_ITEM(wsct_burst_range);\
+		KOBJ_ATTR_ITEM(tsct_busid_enable);\
+		KOBJ_ATTR_ITEM(ttype_high_priority_ext);\
+		KOBJ_ATTR_ITEM(ttype_busid_ext);\
+		KOBJ_ATTR_ITEM(ttype_chn_rank_sel);\
+		KOBJ_ATTR_ITEM(ttype_burst_range);\
+		KOBJ_ATTR_ITEM(reserve_wsct_setting);\
+	} while (0)
+
+
+
+/*======================================================================*/
+/*	EMI Operations							*/
+/*======================================================================*/
+static void emi_init(void)
+{
+	unsigned int bmrw0_val, bmrw1_val, i;
+	/*unsigned int msel_group_val[4];*/
+
+	/*save origianl EMI config*/
+	MET_BM_SaveCfg();
+
+	/* get dram channel number */
+	dram_chann_num = MET_EMI_GetDramChannNum(0);
+
+	/* Init. EMI bus monitor */
+	MET_BM_SetReadWriteType(rwtype);
+
+	/*handle the ori */
+
+	if (ttype1_16_en != BM_TTYPE1_16_ENABLE) {
+		MET_BM_SetLatencyCounter(1);    /*enable latency count*/
+	}
+	else {
+		MET_BM_SetLatencyCounter(0);    /*disable latency count*/
+
+		for (i = 1; i <= 16; i++) {
+			MET_BM_SetMonitorCounter(i,
+						 ttype_master_val[i - 1],
+						 ttype_nbeat_val[i - 1] |
+						 ttype_nbyte_val[i - 1] |
+						 ttype_burst_val[i - 1]);
+		}
+	}
+	
+	if (ttype17_21_en == BM_TTYPE17_21_ENABLE) {
+		for (i = 17; i <= 21; i++) {
+			MET_BM_SetMonitorCounter(i,
+						 ttype_master_val[i - 1],
+						 ttype_nbeat_val[i - 1] |
+						 ttype_nbyte_val[i - 1] |
+						 ttype_burst_val[i - 1]);
+		}
+	}
+
+	PR_BOOTMSG("[%s]reserve_wsct_setting=%d\n",__func__,reserve_wsct_setting);
+
+	if (reserve_wsct_setting == 0) {
+		/* wsct 0 : total-all*/
+		msel_group_ext_val[0] = BM_MASTER_ALL;
+		wsct_rw_val[0] = BM_WSCT_RW_RWBOTH;
+		WSCT_HPRI_DIS[0] = 1;
+		WSCT_HPRI_SEL[0] = 0xF;
+		wsct_busid_val[0] = 0xFFFFF;
+		wsct_idMask_val[0] = 0x1FFF;
+		wsct_chn_rank_sel_val[0] = 0xF;
+		wsct_byte_bnd_dis[0] = 1;
+
+		/* wsct 4 : total-ultra*/
+		msel_group_ext_val[4] = BM_MASTER_ALL;
+		wsct_rw_val[4] = BM_WSCT_RW_RWBOTH;		
+		WSCT_HPRI_DIS[4] = 0;
+		WSCT_HPRI_SEL[4] = 0x8;  /* ultra */
+		wsct_busid_val[4] = 0xFFFFF;
+		wsct_idMask_val[4] = 0x1FFF;
+		wsct_chn_rank_sel_val[4] = 0xF;
+		wsct_byte_bnd_dis[4] = 1;
+
+		/* wsct 5 : total-pre_ultra*/
+		msel_group_ext_val[5] = BM_MASTER_ALL;
+		wsct_rw_val[5] = BM_WSCT_RW_RWBOTH;
+		WSCT_HPRI_DIS[5] = 0;
+		WSCT_HPRI_SEL[5] = 0x4; /* pre_ultra */
+		wsct_busid_val[5] = 0xFFFFF;
+		wsct_idMask_val[5] = 0x1FFF;
+		wsct_chn_rank_sel_val[5] = 0xF;
+		wsct_byte_bnd_dis[5] = 1;
+	}
+
+	if (msel_enable) {
+		/* if ole file node set, use the value */
+		if ( msel_group1 != BM_MASTER_ALL )
+			msel_group_ext_val[1] = msel_group1;
+
+		if ( msel_group2 != BM_MASTER_ALL )
+			msel_group_ext_val[2] = msel_group2;
+
+		if ( msel_group3 != BM_MASTER_ALL )
+			msel_group_ext_val[3] = msel_group3;
+
+	} else {
+		for ( i=1; i<=3; i++) {
+			msel_group_ext_val[i] = BM_MASTER_ALL;
+		}
+	}
+
+	MET_BM_SetWSCT_master_rw(msel_group_ext_val, wsct_rw_val);
+	MET_BM_SetWSCT_high_priority(WSCT_HPRI_DIS, WSCT_HPRI_SEL);
+	MET_BM_SetWSCT_busid_idmask(wsct_busid_val, wsct_idMask_val);
+	MET_BM_SetWSCT_chn_rank_sel(wsct_chn_rank_sel_val);
+	MET_BM_SetWSCT_burst_range(wsct_byte_bnd_dis, wsct_byte_low_bnd_val, wsct_byte_up_bnd_val);
+	MET_BM_SetTSCT_busid_enable(tsct_busid_enable_val);
+
+	MET_BM_SetTtype_high_priority_sel(high_priority_filter, TTYPE_HPRI_SEL);
+	MET_BM_SetTtype_busid_idmask(ttype_busid_val, ttype_idMask_val, ttype1_16_en, ttype17_21_en);
+	MET_BM_SetTtype_chn_rank_sel(ttype_chn_rank_sel_val);
+	MET_BM_SetTtype_burst_range(ttype_byte_bnd_dis, ttype_byte_low_bnd_val, ttype_byte_up_bnd_val);
+
+
+	bmrw0_val = 0;
+	for (i = 0; i < 16; i++)
+		bmrw0_val |= (ttype_rw_val[i] << (i * 2));
+
+	bmrw1_val = 0;
+	for (i = 16; i < 21; i++)
+		bmrw1_val |= (ttype_rw_val[i] << ((i-16) * 2));
+
+	MET_BM_SetTtypeCounterRW(bmrw0_val, bmrw1_val);
+
+}
+
+
+static void emi_uninit(void)
+{
+	MET_BM_RestoreCfg();
+}
+
+
+static inline int do_emi(void)
+{
+	return met_sspm_emi.mode;
+}
+
+
+
+
+
+
+
+/*======================================================================*/
+/*	MET Device Operations						*/
+/*======================================================================*/
+static int emi_inited;
+
+static int met_emi_create(struct kobject *parent)
+{
+	int ret = 0;
+	int i;
+
+	for (i = 0; i < 21; i++) {
+		ttype_master_val[i] = BM_MASTER_M0;
+		ttype_nbeat_val[i] = BM_TRANS_TYPE_1BEAT;
+		ttype_nbyte_val[i] = BM_TRANS_TYPE_8Byte;
+		ttype_burst_val[i] = BM_TRANS_TYPE_BURST_INCR;
+		ttype_busid_val[i] = 0xfffff;   /*default disable ttype bus sel if busid > 0xff_ff */
+		ttype_rw_val[i] =  BM_TRANS_RW_DEFAULT;
+	}
+
+	_clear_msel_group_ext();
+	_clear_wsct_rw();
+	_clear_wsct_high_priority_enable();
+	_clear_wsct_busid();
+	_clear_wsct_chn_rank_sel();
+	_clear_wsct_burst_range();
+
+	_clear_tsct_busid_enable();
+	_clear_ttype_high_priority_ext();
+	_clear_ttype_high_priority_ext();
+	_clear_ttype_busid_ext();
+	_clear_ttype_chn_rank_sel();
+	_clear_ttype_burst_range();
+
+	reserve_wsct_setting = 0;
+
+
+	ret = MET_BM_Init();
+	if (ret != 0) {
+		pr_notice("MET_BM_Init failed!!!\n");
+		ret = 0;        /* will retry later */
+	} else {
+		emi_inited = 1;
+	}
+
+	kobj_emi = parent;
+
+#define KOBJ_ATTR_ITEM(attr_name) \
+	do { \
+		ret = sysfs_create_file(kobj_emi, &attr_name ## _attr.attr); \
+		if (ret != 0) { \
+			pr_notice("Failed to create " #attr_name " in sysfs\n"); \
+			return ret; \
+		} \
+	} while (0)
+	KOBJ_ATTR_LIST;
+#undef  KOBJ_ATTR_ITEM
+
+	return ret;
+}
+
+
+static void met_emi_delete(void)
+{
+#define	KOBJ_ATTR_ITEM(attr_name) \
+	sysfs_remove_file(kobj_emi, &attr_name##_attr.attr)
+	if (kobj_emi != NULL) {
+		KOBJ_ATTR_LIST;
+		kobj_emi = NULL;
+	}
+#undef	KOBJ_ATTR_ITEM
+
+	if (emi_inited)
+		MET_BM_DeInit();
+}
+
+
+
+static void met_emi_resume(void)
+{
+	if (!do_emi())
+		return;
+
+	emi_init();
+}
+
+
+static const char help[] = "  --emi                                 monitor EMI banwidth\n";
+static int emi_print_help(char *buf, int len)
+{
+	return snprintf(buf, PAGE_SIZE, help);
+}
+
+
+static int emi_print_header(char *buf, int len)
+{
+
+	if( (strlen(header_str) - output_str_len) > PAGE_SIZE ){
+		char output_buf[PAGE_SIZE/4];
+
+		strncpy(output_buf, header_str+output_str_len, (PAGE_SIZE/4) -1);
+		output_buf[(PAGE_SIZE/4) - 1] = '\0';
+
+		len = snprintf(buf, PAGE_SIZE, "%s", output_buf);
+		output_str_len += len;
+		met_sspm_emi.header_read_again = 1;
+
+		PR_BOOTMSG("EMI header read again!\n");
+	}
+	else{
+		len = snprintf(buf, PAGE_SIZE, "%s\n", header_str+output_str_len);
+
+		/* reset state */
+		met_sspm_emi.header_read_again = 0;
+		output_header_len = 0;
+		output_str_len = 0;
+	}
+	
+
+	return len;
+}
+
+
+#define TTYPE_NAME_STR_LEN  64
+/* static char ttype_name[21][TTYPE_NAME_STR_LEN]; */
+
+static int emi_create_header(char *buf, int buf_len)
+{
+	int ret = 0;
+/*	int ret_m[21]; */
+	int i = 0;
+
+#if 1 /* move to AP side print header */
+/*#ifndef CONFIG_MTK_TINYSYS_SSPM_SUPPORT*/
+	unsigned int dram_data_rate_MHz;
+	unsigned int DRAM_TYPE;
+	unsigned int base_clock_rate;
+#endif
+
+
+	ret += snprintf(buf + ret, buf_len - ret,
+		"met-info [000] 0.0: met_emi_wsct_amount: %d\n",WSCT_AMOUNT);
+
+	/* master selection header */
+	ret += snprintf(buf + ret, buf_len - ret,
+		"met-info [000] 0.0: met_emi_msel: %x,%x,%x\n",
+		msel_group_ext_val[1] & BM_MASTER_ALL,
+		msel_group_ext_val[2] & BM_MASTER_ALL,
+		msel_group_ext_val[3] & BM_MASTER_ALL);
+	
+	/*Ttype RW type header*/
+	PR_BOOTMSG("rwtype=%d\n",rwtype);
+	ret += snprintf(buf + ret, buf_len - ret, "met-info [000] 0.0: met_emi_rw_cfg: ");
+	if (rwtype == BM_READ_ONLY)
+		ret += snprintf(buf + ret, buf_len - ret, "R");
+	else if (rwtype == BM_WRITE_ONLY)
+		ret += snprintf(buf + ret, buf_len - ret, "W");
+	else
+		ret += snprintf(buf + ret, buf_len - ret, "BOTH");
+
+	for (i = 0; i < 21; i++) {
+		if (ttype_rw_val[i] == BM_TRANS_RW_DEFAULT)
+			ret += snprintf(buf + ret, buf_len - ret, ",DEFAULT");
+		else if (ttype_rw_val[i] == BM_TRANS_RW_READONLY)
+			ret += snprintf(buf + ret, buf_len - ret, ",R");
+		else if (ttype_rw_val[i] == BM_TRANS_RW_WRITEONLY)
+			ret += snprintf(buf + ret, buf_len - ret, ",W");
+		else    /*BM_TRANS_RW_RWBOTH*/
+			ret += snprintf(buf + ret, buf_len - ret, ",BOTH");
+	}
+	ret += snprintf(buf + ret, buf_len - ret, "\n");
+
+	/*ultra header*/
+	ret += snprintf(buf + ret, buf_len - ret,
+			"met-info [000] 0.0: met_emi_ultra_filter: %x\n", high_priority_filter);
+
+	/* ttype header */
+	if (ttype17_21_en == BM_TTYPE17_21_ENABLE) {
+		int i = 0;
+		int j = 0;
+
+		/* ttype master list */
+		ret += snprintf(buf + ret, buf_len - ret, "met-info [000] 0.0: met_emi_ttype_master_list: ");
+		for (i = 0; i < 21; i++) {
+			for (j = 0; j < ARRAY_SIZE(ttype_master_list_item); j++) {
+				if (ttype_master_val[i] == ttype_master_list_item[j].key) {
+					ret += snprintf(buf + ret, buf_len - ret, "%s,", ttype_master_list_item[j].val);
+				}
+			}
+		}
+		/* remove the last comma */
+		snprintf(buf + ret -1, buf_len - ret + 1, "\n");
+
+		/* ttype busid list */
+		ret += snprintf(buf + ret, buf_len - ret, "met-info [000] 0.0: met_emi_ttype_busid_list: ");
+		for (i = 0; i < 21; i++)
+			ret += snprintf(buf + ret, buf_len - ret, "%x,", ttype_busid_val[i]);
+
+		snprintf(buf + ret -1, buf_len - ret + 1, "\n");
+
+		/* ttype nbeat list */
+		ret += snprintf(buf + ret, buf_len - ret, "met-info [000] 0.0: met_emi_ttype_nbeat_list: ");
+		for (i = 0; i < 21; i++) {
+			for (j = 0; j < ARRAY_SIZE(ttype_nbeat_list_item); j++) {
+				if (ttype_nbeat_val[i] == ttype_nbeat_list_item[j].key) {
+					ret += snprintf(buf + ret, buf_len - ret, "%d,", ttype_nbeat_list_item[j].val);
+				}
+			}
+		}
+		snprintf(buf + ret -1, buf_len - ret + 1, "\n");
+
+		/* ttype nbyte list */
+		ret += snprintf(buf + ret, buf_len - ret, "met-info [000] 0.0: met_emi_ttype_nbyte_list: ");
+		for (i = 0; i < 21; i++) {
+			for (j = 0; j < ARRAY_SIZE(ttype_nbyte_list_item); j++) {
+				if (ttype_nbyte_val[i] == ttype_nbyte_list_item[j].key) {
+					ret += snprintf(buf + ret, buf_len - ret, "%d,", ttype_nbyte_list_item[j].val);
+				}
+			}
+		}
+		snprintf(buf + ret -1, buf_len - ret + 1, "\n");
+
+		/* ttype burst list */
+		ret += snprintf(buf + ret, buf_len - ret, "met-info [000] 0.0: met_emi_ttype_burst_list: ");
+		for (i = 0; i < 21; i++) {
+			for (j = 0; j < ARRAY_SIZE(ttype_burst_list_item); j++) {
+				if (ttype_burst_val[i] == ttype_burst_list_item[j].key) {
+					ret += snprintf(buf + ret, buf_len - ret, "%s,", ttype_burst_list_item[j].val);
+				}
+			}
+		}
+		snprintf(buf + ret -1, buf_len - ret + 1, "\n");
+
+	}
+	/* ttype enable */
+	ret += snprintf(buf + ret, buf_len - ret, "met-info [000] 0.0: met_emi_ttype_enable: %d,%d\n",ttype1_16_en, ttype17_21_en);
+
+
+#if 1 /*SEDA 3.5*/
+
+	ret += snprintf(buf + ret, buf_len - ret,
+		"met-info [000] 0.0: met_emi_msel_ext: %x,%x,%x\n",
+		msel_group_ext_val[0] & BM_MASTER_ALL,
+		msel_group_ext_val[4] & BM_MASTER_ALL,
+		msel_group_ext_val[5] & BM_MASTER_ALL);
+
+	ret += snprintf(buf + ret, buf_len - ret, "met-info [000] 0.0: met_emi_wsct_rw: ");
+
+	for (i=0;i<WSCT_AMOUNT;i++) {
+		if (wsct_rw_val[i] == BM_WSCT_RW_RWBOTH)
+			ret += snprintf(buf + ret, buf_len - ret, "RW,");
+		else if (wsct_rw_val[i] == BM_WSCT_RW_READONLY)
+			ret += snprintf(buf + ret, buf_len - ret, "R,");
+		else if (wsct_rw_val[i] == BM_WSCT_RW_WRITEONLY)
+			ret += snprintf(buf + ret, buf_len - ret, "W,");
+		else    /*disable*/
+			ret += snprintf(buf + ret, buf_len - ret, "NONE,");
+	}
+	snprintf(buf + ret -1, buf_len - ret + 1, "\n");
+
+
+	ret += snprintf(buf + ret, buf_len - ret, "met-info [000] 0.0: met_emi_wsct_HPRI_DIS: ");
+	for (i=0;i<WSCT_AMOUNT;i++) {
+		ret += snprintf(buf + ret, buf_len - ret, "%d,",WSCT_HPRI_DIS[i]);
+	}
+	snprintf(buf + ret -1, buf_len - ret + 1, "\n");
+
+
+	ret += snprintf(buf + ret, buf_len - ret, "met-info [000] 0.0: met_emi_wsct_HPRI_SEL: ");
+	for (i=0;i<WSCT_AMOUNT;i++) {
+		ret += snprintf(buf + ret, buf_len - ret, "%x,",WSCT_HPRI_SEL[i]);
+	}
+	snprintf(buf + ret -1, buf_len - ret + 1, "\n");
+
+	ret += snprintf(buf + ret, buf_len - ret, "met-info [000] 0.0: met_emi_wsct_busid: ");
+	for (i=0;i<WSCT_AMOUNT;i++) {
+		ret += snprintf(buf + ret, buf_len - ret, "%x,",wsct_busid_val[i]);
+	}
+	snprintf(buf + ret -1, buf_len - ret + 1, "\n");
+
+
+	ret += snprintf(buf + ret, buf_len - ret, "met-info [000] 0.0: met_emi_wsct_idMask: ");
+	for (i=0;i<WSCT_AMOUNT;i++) {
+		ret += snprintf(buf + ret, buf_len - ret, "%x,",wsct_idMask_val[i]);
+	}
+	snprintf(buf + ret -1, buf_len - ret + 1, "\n");
+
+	ret += snprintf(buf + ret, buf_len - ret, "met-info [000] 0.0: wsct_chn_rank_sel: ");
+	for (i=0;i<WSCT_AMOUNT;i++) {
+		ret += snprintf(buf + ret, buf_len - ret, "%x,",wsct_chn_rank_sel_val[i]);
+	}
+	snprintf(buf + ret -1, buf_len - ret + 1, "\n");
+
+	ret += snprintf(buf + ret, buf_len - ret, "met-info [000] 0.0: wsct_byte_bnd_dis: ");
+	for (i=0;i<WSCT_AMOUNT;i++) {
+		ret += snprintf(buf + ret, buf_len - ret, "%d,",wsct_byte_bnd_dis[i]);
+	}
+	snprintf(buf + ret -1, buf_len - ret + 1, "\n");
+
+
+	ret += snprintf(buf + ret, buf_len - ret, "met-info [000] 0.0: wsct_byte_low_bnd: ");
+	for (i=0;i<WSCT_AMOUNT;i++) {
+		ret += snprintf(buf + ret, buf_len - ret, "%x,",wsct_byte_low_bnd_val[i]);
+	}
+	snprintf(buf + ret -1, buf_len - ret + 1, "\n");
+
+	ret += snprintf(buf + ret, buf_len - ret, "met-info [000] 0.0: wsct_byte_up_bnd: ");
+	for (i=0;i<WSCT_AMOUNT;i++) {
+		ret += snprintf(buf + ret, buf_len - ret, "%x,",wsct_byte_up_bnd_val[i]);
+	}
+	snprintf(buf + ret -1, buf_len - ret + 1, "\n");
+
+	ret += snprintf(buf + ret, buf_len - ret, "met-info [000] 0.0: tsct_busid_enable: ");
+	for (i=0;i<TSCT_AMOUNT;i++) {
+		ret += snprintf(buf + ret, buf_len - ret, "%d,",tsct_busid_enable_val[i]);
+	}
+	snprintf(buf + ret -1, buf_len - ret + 1, "\n");
+
+	/***************************** ttype ****************************************/
+	if (ttype17_21_en == BM_TTYPE17_21_ENABLE) {
+
+		ret += snprintf(buf + ret, buf_len - ret, "met-info [000] 0.0: TTYPE_HPRI_SEL: ");
+		for (i=0;i<BM_COUNTER_MAX;i++) {
+			ret += snprintf(buf + ret, buf_len - ret, "%x,",TTYPE_HPRI_SEL[i]);
+		}
+		snprintf(buf + ret -1, buf_len - ret + 1, "\n");
+
+		ret += snprintf(buf + ret, buf_len - ret, "met-info [000] 0.0: ttype_idMask: ");
+		for (i=0;i<BM_COUNTER_MAX;i++) {
+			ret += snprintf(buf + ret, buf_len - ret, "%x,",ttype_idMask_val[i]);
+		}
+		snprintf(buf + ret -1, buf_len - ret + 1, "\n");
+
+		ret += snprintf(buf + ret, buf_len - ret, "met-info [000] 0.0: ttype_chn_rank_sel: ");
+		for (i=0;i<BM_COUNTER_MAX;i++) {
+			ret += snprintf(buf + ret, buf_len - ret, "%x,",ttype_chn_rank_sel_val[i]);
+		}
+		snprintf(buf + ret -1, buf_len - ret + 1, "\n");
+
+		ret += snprintf(buf + ret, buf_len - ret, "met-info [000] 0.0: ttype_byte_bnd_dis: ");
+		for (i=0;i<BM_COUNTER_MAX;i++) {
+			ret += snprintf(buf + ret, buf_len - ret, "%d,",ttype_byte_bnd_dis[i]);
+		}
+		snprintf(buf + ret -1, buf_len - ret + 1, "\n");
+
+		ret += snprintf(buf + ret, buf_len - ret, "met-info [000] 0.0: ttype_byte_low_bnd_val: ");
+		for (i=0;i<BM_COUNTER_MAX;i++) {
+			ret += snprintf(buf + ret, buf_len - ret, "%x,",ttype_byte_low_bnd_val[i]);
+		}
+		snprintf(buf + ret -1, buf_len - ret + 1, "\n");
+
+		ret += snprintf(buf + ret, buf_len - ret, "met-info [000] 0.0: ttype_byte_up_bnd_val: ");
+		for (i=0;i<BM_COUNTER_MAX;i++) {
+			ret += snprintf(buf + ret, buf_len - ret, "%x,",ttype_byte_up_bnd_val[i]);
+		}
+		snprintf(buf + ret -1, buf_len - ret + 1, "\n");
+	}
+#endif
+
+#ifdef EMI_NUM
+	ret += snprintf(buf + ret, buf_len - ret,
+			"met-info [000] 0.0: EMI_NUM: %d\n", EMI_NUM);
+#endif
+	/*IP version*/
+	ret += snprintf(buf + ret, buf_len - ret,
+			"met-info [000] 0.0: DRAMC_VER: %d\n", DRAMC_VER);
+
+	ret += snprintf(buf + ret, buf_len - ret,
+			"met-info [000] 0.0: EMI_VER: %d.%d\n", EMI_VER_MAJOR, EMI_VER_MINOR);
+
+#if 1 /* SEDA3.5 header print move to AP side */
+/*#ifndef CONFIG_MTK_TINYSYS_SSPM_SUPPORT*/
+	dram_chann_num = MET_EMI_GetDramChannNum(0);
+	/*	met_dram_chann_num_header
+	 *	channel number
+	 *	LP4: 2, LP3: 1
+	 */
+
+	/*
+	 *	the ddr type define :
+	 *	enum DDRTYPE {
+	 *	TYPE_LPDDR3 = 1,
+	 *	TYPE_LPDDR4,
+	 *	TYPE_LPDDR4X,
+	 *	TYPE_LPDDR2
+	 *	};
+	 */
+	if (!get_cur_ddr_ratio_symbol){
+		PR_BOOTMSG("[%s][%d]get_cur_ddr_ratio_symbol = NULL , use the TYPE_LPDDR4 setting\n", __func__, __LINE__);
+		ret += snprintf(buf + ret, buf_len - ret, "met-info [000] 0.0: ##_EMI_warning: get_cur_ddr_ratio_symbol = NULL , use the TYPE_LPDDR4 setting\n");
+	}
+
+	if (mtk_dramc_get_ddr_type_symbol) {
+		DRAM_TYPE = mtk_dramc_get_ddr_type_symbol();
+
+		base_clock_rate = MET_EMI_Get_BaseClock_Rate();
+
+		ret += snprintf(buf + ret, buf_len - ret, "met-info [000] 0.0: met_dram_type: %d\n", DRAM_TYPE);
+
+		if ((DRAM_TYPE == 2) || (DRAM_TYPE == 3))
+			ret += snprintf(buf + ret, buf_len - ret, "met-info [000] 0.0: met_dram_chann_num_header: %d,%d,%d,%d\n",
+					dram_chann_num, base_clock_rate,
+					DRAM_IO_BUS_WIDTH_LP4, DRAM_DATARATE);
+		else
+			ret += snprintf(buf + ret, buf_len - ret, "met-info [000] 0.0: met_dram_chann_num_header: %d,%d,%d,%d\n",
+					dram_chann_num, base_clock_rate,
+					DRAM_IO_BUS_WIDTH_LP3, DRAM_DATARATE);
+	} else {
+		METERROR("[%s][%d]mtk_dramc_get_ddr_type_symbol = NULL , use the TYPE_LPDDR4 setting\n", __func__, __LINE__);
+		ret += snprintf(buf + ret, buf_len - ret, "met-info [000] 0.0: ##_EMI_warning: mtk_dramc_get_ddr_type_symbol = NULL , use the TYPE_LPDDR4 setting\n");
+		
+		ret += snprintf(buf + ret, buf_len - ret, "met-info [000] 0.0: met_dram_chann_num_header: %d,%d,%d,%d\n",
+					dram_chann_num, DDR_RATIO_DEFAULT,
+					DRAM_IO_BUS_WIDTH_LP4, DRAM_DATARATE);
+	}
+
+
+
+	/* met_emi_clockrate */
+	if (mtk_dramc_get_data_rate_symbol) {
+		dram_data_rate_MHz = mtk_dramc_get_data_rate_symbol();
+	} else {
+		METERROR("mtk_dramc_get_data_rate_symbol = NULL\n");
+		ret += snprintf(buf + ret, buf_len - ret, "met-info [000] 0.0: ##_EMI_warning: mtk_dramc_get_data_rate_symbol = NULL\n");
+		dram_data_rate_MHz = DRAM_FREQ_DEFAULT;
+	}
+
+	ret += snprintf(buf + ret, buf_len - ret,
+			"met-info [000] 0.0: met_dram_clockrate: %d\n",
+			dram_data_rate_MHz);
+
+	/* 1 : by ondiemet, 0: by pure linux */
+	ret += snprintf(buf + ret, buf_len - ret,
+			"met-info [000] 0.0: ##_emi_use_ondiemet: 1,%X\n",
+			get_sspm_support_feature());
+
+	/*dram bank num*/
+	ret += snprintf(buf + ret, buf_len - ret,
+			"met-info [000] 0.0: met_dram_rank_num_header: %u,%u\n", MET_EMI_GetDramRankNum(0),
+				MET_EMI_GetDramRankNum(0));
+
+	ret += snprintf(buf + ret, buf_len - ret,
+			"met-info [000] 0.0: met_emi_header: TS0,TS1,GP0_WSCT,GP1_WSCT,GP2_WSCT,GP3_WSCT,");
+	ret += snprintf(buf + ret, buf_len - ret,
+			"M0_LATENCY,M1_LATENCY,M2_LATENCY,M3_LATENCY,M4_LATENCY,M5_LATENCY,M6_LATENCY,M7_LATENCY,");
+	ret += snprintf(buf + ret, buf_len - ret,
+			"M0_TRANS,M1_TRANS,M2_TRANS,M3_TRANS,M4_TRANS,M5_TRANS,M6_TRANS,M7_TRANS,");
+	ret += snprintf(buf + ret, buf_len - ret,
+			"BACT,BSCT,BCNT,WACT,DCM_CTRL,TACT,");
+
+	for (i = 0; i < dram_chann_num; i++) {
+		if (i != 0)
+			ret += snprintf(buf + ret, buf_len - ret,
+					",");
+		ret += snprintf(buf + ret, buf_len - ret,
+				"PageHit_%d,PageMiss_%d,InterBank_%d,Idle_%d,", i, i, i, i);
+		ret += snprintf(buf + ret, buf_len - ret,
+				"mr4_%d,refresh_pop_%d,freerun_26m_%d,", i, i, i);
+		ret += snprintf(buf + ret, buf_len - ret,
+				"read_bytes_%d,write_bytes_%d", i, i);
+	}
+	ret += snprintf(buf + ret, buf_len - ret, "\n");
+
+	/*TSCT header*/
+	if (emi_tsct_enable == 1) {
+		ret += snprintf(buf + ret, buf_len - ret,
+				"met-info [000] 0.0: ms_emi_tsct_header: ms_emi_tsct,");
+		ret += snprintf(buf + ret, buf_len - ret,
+				"tsct1,tsct2,tsct3\n");
+	}
+
+	/*MDCT header*/
+	if (emi_mdct_enable == 1) {
+		ret += snprintf(buf + ret, buf_len - ret,
+				"met-info [000] 0.0: ms_emi_mdct_header: ms_emi_mdct,");
+		ret += snprintf(buf + ret, buf_len - ret,
+				"RD_ULTRA,RD_MDMCU\n");
+	}
+
+
+
+	/* met_bw_rgtor_header */
+	ret += snprintf(buf + ret, buf_len - ret,	"met-info [000] 0.0: met_bw_rgtor_unit_header: %x\n", MET_EMI_Get_CONH_2ND(0));
+
+	ret += snprintf(buf + ret, buf_len - ret,
+			"met-info [000] 0.0: met_bw_rglr_header: metadata\n");
+
+
+	/* DRAM DVFS header */
+	ret += snprintf(buf + ret, buf_len - ret,
+			"met-info [000] 0.0: DRAM_DVFS_header: datarate(MHz)\n");
+
+	/*PDIR met_dramc_header*/
+	if (dramc_pdir_enable == 1 && DRAMC_VER >= 2 ) {
+		ret += snprintf(buf + ret, buf_len - ret,
+				"met-info [000] 0.0: met_dramc_header: ");
+		for (i = 0; i < dram_chann_num; i++) {
+			if (i != 0)
+				ret += snprintf(buf + ret, buf_len - ret,
+						",");
+			ret += snprintf(buf + ret, buf_len - ret, "freerun_26m_%d,", i);
+			ret += snprintf(buf + ret, buf_len - ret,
+					"rk0_pre_sb_%d,rk0_pre_pd_%d,rk0_act_sb_%d,rk0_act_pd_%d,", i, i, i, i);
+			ret += snprintf(buf + ret, buf_len - ret,
+					"rk1_pre_sb_%d,rk1_pre_pd_%d,rk1_act_sb_%d,rk1_act_pd_%d,", i, i, i, i);
+			ret += snprintf(buf + ret, buf_len - ret,
+					"rk2_pre_sb_%d,rk2_pre_pd_%d,rk2_act_sb_%d,rk2_act_pd_%d", i, i, i, i);
+		}
+		ret += snprintf(buf + ret, buf_len - ret, "\n");
+	}
+
+	/* DRS header */
+	ret += snprintf(buf + ret, buf_len - ret,
+			"met-info [000] 0.0: emi_drs_header: ch0_RANK1_GP(%%),ch0_RANK1_SF(%%),ch0_ALL_SF(%%),ch1_RANK1_GP(%%),ch1_RANK1_SF(%%),ch1_ALL_SF(%%)\n");
+#endif
+
+	met_sspm_emi.header_read_again = 0;
+	output_header_len = 0;
+	output_str_len = 0;
+
+	return ret;
+}
+
+
+static int ondiemet_emi_print_header(char *buf, int len)
+{
+	return emi_print_header(buf, len);
+}
+
+
+
+static void MET_BM_IPI_REGISTER_CB(void)
+{
+	int ret, i;
+	unsigned int rdata;
+	unsigned int ipi_buf[4];
+
+	for (i = 0; i < 4; i++)
+		ipi_buf[i] = 0;
+
+	if (sspm_buf_available == 1) {
+		ipi_buf[0] = MET_MAIN_ID | (MID_EMI << MID_BIT_SHIFT) | MET_ARGU | SET_REGISTER_CB;
+		ret = met_ipi_to_sspm_command((void *)ipi_buf, 0, &rdata, 1);
+	}
+}
+
+
+static void MET_BM_IPI_configs(void)
+{
+	int ret, i;
+	unsigned int rdata;
+	unsigned int ipi_buf[4];
+
+	for (i = 0; i < 4; i++)
+		ipi_buf[i] = 0;
+
+	if (sspm_buf_available == 1) {
+		ipi_buf[0] = MET_MAIN_ID | (MID_EMI << MID_BIT_SHIFT) | MET_ARGU | SET_EBM_CONFIGS1;
+		ipi_buf[2] = EMI_VER_MAJOR << 24 | EMI_VER_MINOR << 16 | DRAMC_VER << 8 | 0;
+		ret = met_ipi_to_sspm_command((void *)ipi_buf, 0, &rdata, 1);
+	}
+}
+
+
+static void ondiemet_emi_start(void)
+{
+	MET_BM_IPI_REGISTER_CB();
+	if (!emi_inited) {
+		if (MET_BM_Init() != 0) {
+			met_sspm_emi.mode = 0;
+			pr_notice("MET_BM_Init failed!!!\n");
+			return;
+		}
+		emi_inited = 1;
+	}
+	MET_BM_IPI_configs();
+
+	if (do_emi())
+		emi_init();
+
+	ondiemet_module[ONDIEMET_SSPM] |= ID_EMI;
+
+	emi_create_header(header_str, MAX_HEADER_LEN);
+}
+
+static void ondiemet_emi_stop(void)
+{
+	if (!emi_inited)
+		return;
+
+	if (do_emi())
+		emi_uninit();
+}
+
+
+
+struct metdevice met_sspm_emi = {
+	.name			= "emi",
+	.owner			= THIS_MODULE,
+	.type			= MET_TYPE_BUS,
+	.create_subfs		= met_emi_create,
+	.delete_subfs		= met_emi_delete,
+	.resume			= met_emi_resume,
+#if defined(CONFIG_MTK_TINYSYS_SSPM_SUPPORT) && defined(ONDIEMET_SUPPORT)
+	.ondiemet_start		= ondiemet_emi_start,
+	.ondiemet_stop		= ondiemet_emi_stop,
+	.ondiemet_print_help	= emi_print_help,
+	.ondiemet_print_header	= ondiemet_emi_print_header,
+#endif
+	.ondiemet_mode		= 1,
+};
+EXPORT_SYMBOL(met_sspm_emi);
diff --git a/src/devtools/met-driver/4.19/common/emi/SEDA3_6/mtk_emi_bm.c b/src/devtools/met-driver/4.19/common/emi/SEDA3_6/mtk_emi_bm.c
new file mode 100644
index 0000000..839ddd1
--- /dev/null
+++ b/src/devtools/met-driver/4.19/common/emi/SEDA3_6/mtk_emi_bm.c
@@ -0,0 +1,915 @@
+// SPDX-License-Identifier: GPL-2.0 
+/*
+ * Copyright (C) 2019 MediaTek Inc.
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/io.h>
+#include <mt-plat/sync_write.h>
+#include <mt-plat/mtk_io.h>
+#include "mtk_typedefs.h"
+#include "core_plf_init.h"
+#include "mtk_emi_bm.h"
+#include "met_reg_addr.h"
+
+#include "met_drv.h"
+#include "interface.h"
+
+#undef	DEBUG
+#undef	debug_reg
+#ifdef	debug_reg
+static inline unsigned int emi_readl(void __iomem *padr)
+{
+	unsigned int tmp;
+
+	tmp = readl(padr);
+	MET_TRACE("[MET_EMI] RD_Reg: %p: %08x\n", padr, tmp);
+	return tmp;
+}
+
+static inline void __emi_reg_sync_writel(unsigned int data, void __iomem *padr)
+{
+	unsigned int tmp;
+
+	mt_reg_sync_writel(data, padr);
+	tmp = readl(padr);
+	MET_TRACE("[MET_EMI] WR_Reg: %p: %08x, %08x\n", padr, data, tmp);
+}
+
+#define emi_reg_sync_writel(data, adr)  __emi_reg_sync_writel(data, IOMEM(adr))
+
+#else
+#define emi_readl               readl
+#define emi_reg_sync_writel     mt_reg_sync_writel
+#endif
+
+#define MASK_MASTER     0xFF
+#define MASK_TRANS_TYPE 0xFF
+
+/* static int dram_chann_num; */
+static void __iomem *BaseAddrEMI[EMI_NUM_MAX];
+/* static void __iomem *BaseAddrCHN_EMI[2]; */
+
+/* static int dramc0_dcm_enable;
+static int dramc1_dcm_enable; */
+
+#define CH0_MISC_CG_CTRL0 (((unsigned long) BaseAddrDDRPHY_AO[0]) + 0x284)
+#define CH1_MISC_CG_CTRL0 (((unsigned long) BaseAddrDDRPHY_AO[1]) + 0x284)
+#define CH2_MISC_CG_CTRL0 (((unsigned long) BaseAddrDDRPHY_AO[2]) + 0x284)
+#define CH3_MISC_CG_CTRL0 (((unsigned long) BaseAddrDDRPHY_AO[3]) + 0x284)
+
+
+const unsigned int emi_config[] = {
+	EMI_BMEN,
+	EMI_MSEL,
+	EMI_MSEL2,
+	EMI_MSEL3,
+	EMI_MSEL4,
+	EMI_MSEL5,
+	EMI_MSEL6,
+	EMI_MSEL7,
+	EMI_MSEL8,
+	EMI_MSEL9,
+	EMI_MSEL10,
+	EMI_BMID0,
+	EMI_BMID1,
+	EMI_BMID2,
+	EMI_BMID3,
+	EMI_BMID4,
+	EMI_BMID5,
+	EMI_BMID6,
+	EMI_BMID7,
+	EMI_BMID8,
+	EMI_BMID9,
+	EMI_BMID10,
+	EMI_BMEN1,
+	EMI_BMEN2,
+	EMI_BMRW0,
+	EMI_BMRW1,
+	EMI_DBWA,
+	EMI_DBWB,
+	EMI_DBWC,
+	EMI_DBWD,
+	EMI_DBWE,
+	EMI_DBWF,
+	EMI_DBWI,
+	EMI_DBWJ,
+	EMI_DBWK,
+	EMI_DBWA_2ND,
+	EMI_DBWB_2ND,
+	EMI_DBWC_2ND,
+	EMI_DBWD_2ND,
+	EMI_DBWE_2ND,
+	EMI_DBWF_2ND,
+	EMI_TTYPE1_CONA,
+	EMI_TTYPE1_CONB,
+	EMI_TTYPE2_CONA,
+	EMI_TTYPE2_CONB,
+	EMI_TTYPE3_CONA,
+	EMI_TTYPE3_CONB,
+	EMI_TTYPE4_CONA,
+	EMI_TTYPE4_CONB,
+	EMI_TTYPE5_CONA,
+	EMI_TTYPE5_CONB,
+	EMI_TTYPE6_CONA,
+	EMI_TTYPE6_CONB,
+	EMI_TTYPE7_CONA,
+	EMI_TTYPE7_CONB,
+	EMI_TTYPE8_CONA,
+	EMI_TTYPE8_CONB,
+	EMI_TTYPE9_CONA,
+	EMI_TTYPE9_CONB,
+	EMI_TTYPE10_CONA,
+	EMI_TTYPE10_CONB,
+	EMI_TTYPE11_CONA,
+	EMI_TTYPE11_CONB,
+	EMI_TTYPE12_CONA,
+	EMI_TTYPE12_CONB,
+	EMI_TTYPE13_CONA,
+	EMI_TTYPE13_CONB,
+	EMI_TTYPE14_CONA,
+	EMI_TTYPE14_CONB,
+	EMI_TTYPE15_CONA,
+	EMI_TTYPE15_CONB,
+	EMI_TTYPE16_CONA,
+	EMI_TTYPE16_CONB,
+	EMI_TTYPE17_CONA,
+	EMI_TTYPE17_CONB,
+	EMI_TTYPE18_CONA,
+	EMI_TTYPE18_CONB,
+	EMI_TTYPE19_CONA,
+	EMI_TTYPE19_CONB,
+	EMI_TTYPE20_CONA,
+	EMI_TTYPE20_CONB,
+	EMI_TTYPE21_CONA,
+	EMI_TTYPE21_CONB
+};
+#define EMI_CONFIG_MX_NR (sizeof(emi_config)/sizeof(unsigned int))
+static unsigned int emi_config_val[EMI_NUM][EMI_CONFIG_MX_NR];
+
+
+
+static inline void MET_REG_BCLR(unsigned long reg, u32 shift)
+{
+	unsigned int read_val = 0;
+
+	read_val = emi_readl(IOMEM(reg));
+	emi_reg_sync_writel(read_val & (~((1 << shift) & 0xFFFFFFFF)), reg);
+}
+
+
+int MET_BM_Init(void)
+{
+	/*emi*/
+	/*int idx;*/
+	unsigned int emi_no;
+
+	for(emi_no=0; emi_no<EMI_NUM ;emi_no++)
+	{
+		BaseAddrEMI[emi_no] = 0;
+	}
+
+	if( EMI_NUM == 1 )
+	{
+		BaseAddrEMI[0] = ioremap(EMI_REG_BASE, EMI_REG_SIZE);
+	} 
+	else {
+		/*EMI num == 2*/
+		BaseAddrEMI[0] = ioremap(EMI_REG_BASE, EMI_REG_SIZE);
+		BaseAddrEMI[1] = ioremap(SUB_EMI_REG_BASE, EMI_REG_SIZE);
+	}
+
+	/*check the ioremap sucess or not*/
+	for(emi_no=0; emi_no<EMI_NUM ;emi_no++)
+	{
+		if (BaseAddrEMI[emi_no] == 0)
+		{
+			METERROR("BaseAddrEMI[%d] ioremap fail\n",emi_no);
+			PR_BOOTMSG_ONCE("BaseAddrEMI[%d] ioremap fail\n",emi_no);
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+
+void MET_BM_DeInit(void)
+{
+
+	/*iounmap*/
+	if( EMI_NUM == 1 )
+	{
+		if (BaseAddrEMI[0])
+			iounmap(BaseAddrEMI[0]);
+
+		BaseAddrEMI[0] = NULL;
+	} 
+	else {
+		/*EMI num == 2*/
+		if (BaseAddrEMI[0])
+			iounmap(BaseAddrEMI[0]);
+
+		BaseAddrEMI[0] = NULL;
+
+		if (BaseAddrEMI[1])
+			iounmap(BaseAddrEMI[1]);
+
+		BaseAddrEMI[1] = NULL;
+	}
+}
+
+
+void MET_BM_SaveCfg(void)
+{
+	int i,emi_no;
+
+	for(emi_no=0; emi_no<EMI_NUM ;emi_no++){
+		for (i = 0; i < EMI_CONFIG_MX_NR; i++)
+			emi_config_val[emi_no][i] = emi_readl(IOMEM((unsigned long)BaseAddrEMI[emi_no] + emi_config[i]));
+	}
+}
+
+
+void MET_BM_RestoreCfg(void)
+{
+	int i,emi_no;
+	for(emi_no=0; emi_no<EMI_NUM ;emi_no++){
+		for (i = 0; i < EMI_CONFIG_MX_NR; i++)
+			emi_reg_sync_writel(emi_config_val[emi_no][i], (unsigned long)BaseAddrEMI[emi_no] + emi_config[i]);
+	}
+}
+
+
+void MET_BM_SetReadWriteType(const unsigned int ReadWriteType)
+{
+	/*
+	 * ReadWriteType: 00/11 --> both R/W
+	 *                   01 --> only R
+	 *                   10 --> only W
+	 */
+	volatile unsigned int value;
+	unsigned int emi_no;
+	for(emi_no=0; emi_no<EMI_NUM ;emi_no++)
+	{
+		value = emi_readl(IOMEM((unsigned long)BaseAddrEMI[emi_no] + EMI_BMEN));
+		emi_reg_sync_writel((value & 0xFFFFFFCF) | (ReadWriteType << 4), (unsigned long)BaseAddrEMI[emi_no] + EMI_BMEN);
+	}
+}
+
+
+
+
+int MET_BM_SetMonitorCounter(const unsigned int counter_num,
+			     const unsigned int master, const unsigned int trans_type)
+{
+	unsigned int value, addr;
+	const unsigned int iMask = (MASK_TRANS_TYPE << 8) | MASK_MASTER;
+	unsigned int emi_no;
+
+	if (counter_num < 1 || counter_num > BM_COUNTER_MAX)
+		return BM_ERR_WRONG_REQ;
+
+	for(emi_no=0; emi_no<EMI_NUM ;emi_no++)
+	{
+		if (counter_num == 1) {
+			addr = EMI_BMEN;
+			value = (emi_readl(IOMEM((unsigned long)BaseAddrEMI[emi_no] + addr)) & ~(iMask << 16)) |
+			    ((trans_type & MASK_TRANS_TYPE) << 24) | ((master & MASK_MASTER) << 16);
+		} else {
+			addr = (counter_num <= 3) ? EMI_MSEL : (EMI_MSEL2 + (counter_num / 2 - 2) * 8);
+
+			/* clear master and transaction type fields */
+			value = emi_readl(IOMEM((unsigned long)BaseAddrEMI[emi_no] + addr)) & ~(iMask << ((counter_num % 2) * 16));
+
+			/* set master and transaction type fields */
+			value |= (((trans_type & MASK_TRANS_TYPE) << 8) |
+				  (master & MASK_MASTER)) << ((counter_num % 2) * 16);
+		}
+
+		emi_reg_sync_writel(value, (unsigned long)BaseAddrEMI[emi_no] + addr);
+	}
+	return BM_REQ_OK;
+}
+
+
+int MET_BM_SetTtypeCounterRW(unsigned int bmrw0_val, unsigned int bmrw1_val)
+{
+
+	volatile unsigned int value_origin;
+	unsigned int emi_no;
+
+	for(emi_no=0; emi_no<EMI_NUM ;emi_no++)
+	{
+		value_origin = emi_readl(IOMEM((unsigned long)BaseAddrEMI[emi_no] + EMI_BMRW0));
+		MET_TRACE("[MET_EMI_settype1] value_origin: %x\n", value_origin);
+		if (value_origin != bmrw0_val) {
+			emi_reg_sync_writel(bmrw0_val, (unsigned long)BaseAddrEMI[emi_no] + EMI_BMRW0);
+			MET_TRACE("[MET_EMI_settype1] bmrw0_val: %x, value_origin: %x\n", bmrw0_val,
+				   value_origin);
+		}
+
+
+		value_origin = emi_readl(IOMEM((unsigned long)BaseAddrEMI[emi_no] + EMI_BMRW1));
+		MET_TRACE("[MET_EMI_settype2] value_origin: %x\n", value_origin);
+		if (value_origin != bmrw1_val) {
+			emi_reg_sync_writel(bmrw1_val, (unsigned long)BaseAddrEMI[emi_no] + EMI_BMRW1);
+			MET_TRACE("[MET_EMI_settype2] bmrw0_val: %x, value_origin: %x\n", bmrw1_val,
+				   value_origin);
+
+		}
+	}
+	return BM_REQ_OK;
+}
+
+/*not use after SEDA 3.5*/
+int MET_BM_Set_WsctTsct_id_sel(unsigned int counter_num, unsigned int enable)
+{
+	volatile unsigned int value;
+	unsigned int emi_no;
+
+	if (counter_num > 3)
+		return BM_ERR_WRONG_REQ;
+
+	for(emi_no=0; emi_no<EMI_NUM ;emi_no++)
+	{
+		value =
+		    ((emi_readl(IOMEM((unsigned long)BaseAddrEMI[emi_no] + EMI_BMEN2)) & (~(1 << (28 + counter_num)))) |
+		     (enable << (28 + counter_num)));
+		emi_reg_sync_writel(value, (unsigned long)BaseAddrEMI[emi_no] + EMI_BMEN2);
+	}
+	return BM_REQ_OK;
+}
+
+
+int MET_BM_SetMaster(const unsigned int counter_num, const unsigned int master)
+{
+	volatile unsigned int value, addr;
+	const unsigned int iMask = 0x7F;
+	unsigned int emi_no;
+
+	if (counter_num < 1 || counter_num > BM_COUNTER_MAX)
+		return BM_ERR_WRONG_REQ;
+
+	for(emi_no=0; emi_no<EMI_NUM ;emi_no++)
+	{
+		if (counter_num == 1) {
+			addr = EMI_BMEN;
+			value =
+			    (emi_readl(IOMEM((unsigned long)BaseAddrEMI[emi_no] + addr)) & ~(iMask << 16)) | ((master & iMask) << 16);
+		} else {
+			addr = (counter_num <= 3) ? EMI_MSEL : (EMI_MSEL2 + (counter_num / 2 - 2) * 8);
+
+			/* clear master and transaction type fields */
+			value = emi_readl(IOMEM((unsigned long)BaseAddrEMI[emi_no] + addr)) & ~(iMask << ((counter_num % 2) * 16));
+
+			/* set master and transaction type fields */
+			value |= ((master & iMask) << ((counter_num % 2) * 16));
+		}
+
+		emi_reg_sync_writel(value, (unsigned long)BaseAddrEMI[emi_no] + addr);
+	}
+	return BM_REQ_OK;
+}
+
+
+int MET_BM_SetbusID_En(const unsigned int counter_num,
+		       const unsigned int enable)
+{
+	volatile unsigned int value;
+	unsigned int emi_no;
+
+	if ((counter_num < 1 || counter_num > BM_COUNTER_MAX) || (enable > 1))
+		return BM_ERR_WRONG_REQ;
+
+	for(emi_no=0; emi_no<EMI_NUM ;emi_no++)
+	{
+		if (enable == 0) {
+			/* clear  EMI ID selection Enabling   SEL_ID_EN */
+			value = (emi_readl(IOMEM((unsigned long)BaseAddrEMI[emi_no] + EMI_BMEN2))
+				 & ~(1 << (counter_num - 1)));
+		} else {
+			/* enable  EMI ID selection Enabling   SEL_ID_EN */
+			value = (emi_readl(IOMEM((unsigned long)BaseAddrEMI[emi_no] + EMI_BMEN2))
+				 | (1 << (counter_num - 1)));
+		}
+		emi_reg_sync_writel(value, (unsigned long)BaseAddrEMI[emi_no] + EMI_BMEN2);
+	}
+	return BM_REQ_OK;
+}
+
+
+int MET_BM_SetbusID(const unsigned int counter_num,
+		    const unsigned int id)
+{
+	volatile unsigned int value, addr, shift_num;
+	unsigned int emi_no;
+
+	if ((counter_num < 1 || counter_num > BM_COUNTER_MAX))
+		return BM_ERR_WRONG_REQ;
+
+	for(emi_no=0; emi_no<EMI_NUM ;emi_no++)
+	{
+		/* offset of EMI_BMIDx register */
+		addr = EMI_BMID0 + (counter_num - 1) / 2 * 4;
+		shift_num = ((counter_num - 1) % 2) * 16;
+		/* clear SELx_ID field */
+		value = emi_readl(IOMEM((unsigned long)BaseAddrEMI[emi_no] + addr)) & ~(EMI_BMID_MASK << shift_num);
+
+		/* set SELx_ID field */
+		if (id <= 0xffff)       /*bigger then 0xff_ff : no select busid in master, reset busid as 0*/
+			value |= id << shift_num;
+
+		emi_reg_sync_writel(value, (unsigned long)BaseAddrEMI[emi_no] + addr);
+	}
+	return BM_REQ_OK;
+}
+
+
+int MET_BM_SetUltraHighFilter(const unsigned int counter_num, const unsigned int enable)
+{
+	volatile unsigned int value;
+	unsigned int emi_no;
+
+	if ((counter_num < 1 || counter_num > BM_COUNTER_MAX) || (enable > 1))
+		return BM_ERR_WRONG_REQ;
+
+	for(emi_no=0; emi_no<EMI_NUM ;emi_no++)
+	{
+		value = (emi_readl(IOMEM((unsigned long)BaseAddrEMI[emi_no] + EMI_BMEN1))
+			 & ~(1 << (counter_num - 1)))
+			| (enable << (counter_num - 1));
+
+		emi_reg_sync_writel(value, (unsigned long)BaseAddrEMI[emi_no] + EMI_BMEN1);
+	}
+	return BM_REQ_OK;
+}
+
+
+int MET_BM_SetLatencyCounter(unsigned int enable)
+{
+	volatile unsigned int value;
+	unsigned int emi_no;
+
+	for(emi_no=0; emi_no<EMI_NUM ;emi_no++)
+	{
+		value = emi_readl(IOMEM((unsigned long)BaseAddrEMI[emi_no] + EMI_BMEN2)) & ~(0x3 << 24);
+		/*
+		 * emi_ttype1 -- emi_ttype8 change as total latencies
+		 * for m0 -- m7,
+		 * and emi_ttype9 -- emi_ttype16 change as total transaction counts
+		 * for m0 -- m7
+		 */
+		if (enable == 1)
+			value |= (0x2 << 24);
+
+		emi_reg_sync_writel(value, (unsigned long)BaseAddrEMI[emi_no] + EMI_BMEN2);
+	}
+	return BM_REQ_OK;
+}
+
+
+
+
+
+
+
+
+int MET_BM_SetEmiDcm(const unsigned int setting)
+{
+	volatile unsigned int value;
+	unsigned int emi_no;
+
+	for(emi_no=0; emi_no<EMI_NUM ;emi_no++)
+	{
+		value = emi_readl(IOMEM((unsigned long)BaseAddrEMI[emi_no] + EMI_CONM));
+		emi_reg_sync_writel((value & 0x00FFFFFF) | (setting << 24), (unsigned long)BaseAddrEMI[emi_no] + EMI_CONM);
+	}
+	return BM_REQ_OK;
+}
+
+
+
+
+unsigned int MET_EMI_GetDramChannNum(unsigned int emi_no)
+{
+	int num = -1;
+
+	if (BaseAddrEMI[emi_no]) {
+		num = emi_readl(IOMEM((unsigned long)BaseAddrEMI[emi_no] + EMI_CONA));
+		num = ((num >> 8) & 0x0000003);
+	} else {
+		return 1;
+	}
+
+	if (num == M0_DOUBLE_HALF_BW_1CH)
+		return 1;
+	else if (num == M0_DOUBLE_HALF_BW_2CH)
+		return 2;
+	else if (num == M0_DOUBLE_HALF_BW_4CH)
+		return 4;
+	else                    /* default return single channel */
+		return 1;
+}
+
+
+unsigned int MET_EMI_GetDramRankNum(unsigned int emi_no)
+{
+	int dual_rank = 0;
+
+	if (BaseAddrEMI[emi_no]) {
+		dual_rank = emi_readl(IOMEM((unsigned long)BaseAddrEMI[emi_no] + EMI_CONA));
+		dual_rank = ((dual_rank >> 17) & RANK_MASK);
+	} else {
+		return DUAL_RANK;
+	}
+
+	if (dual_rank == DISABLE_DUAL_RANK_MODE)
+		return ONE_RANK;
+	else			/* default return dual rank */
+		return DUAL_RANK;
+}
+
+
+unsigned int MET_EMI_GetDramRankNum_CHN1(unsigned int emi_no)
+{
+	int dual_rank = 0;
+
+	if (BaseAddrEMI[emi_no]) {
+		dual_rank = emi_readl(IOMEM((unsigned long)BaseAddrEMI[emi_no] + EMI_CONA));
+		dual_rank = ((dual_rank >> 16) & RANK_MASK);
+	} else {
+		return DUAL_RANK;
+	}
+
+	if (dual_rank == DISABLE_DUAL_RANK_MODE)
+		return ONE_RANK;
+	else			/* default return dual rank */
+		return DUAL_RANK;
+}
+
+
+
+unsigned int MET_EMI_Get_BaseClock_Rate(void)
+{
+	unsigned int DRAM_TYPE;
+
+	if (get_cur_ddr_ratio_symbol)
+		return get_cur_ddr_ratio_symbol();
+	else {
+
+		if (mtk_dramc_get_ddr_type_symbol) {
+			DRAM_TYPE = mtk_dramc_get_ddr_type_symbol();
+
+			if ((DRAM_TYPE == 2) || (DRAM_TYPE == 3))
+				return DRAM_EMI_BASECLOCK_RATE_LP4;
+			else
+				return DRAM_EMI_BASECLOCK_RATE_LP3;
+
+		} else {
+			return DRAM_EMI_BASECLOCK_RATE_LP4;
+		}
+	}
+}
+
+/* For SEDA3.5 wsct setting*/
+/* EMI_DBWX[15:8],    X=A~F   (SEL_MASTER) */
+/* RW:    EMI_DBWX[1:0],    X=A~F */
+int MET_BM_SetWSCT_master_rw(unsigned int *master , unsigned int *rw)
+{
+	volatile unsigned int value, addr;
+	int i;
+	unsigned int emi_no;
+
+	const unsigned int Mask_master = 0xFF;
+	const unsigned int offset_master = 8;
+
+	const unsigned int Mask_rw = 0x3;
+	const unsigned int offset_rw = 0;
+
+	for(emi_no=0; emi_no<EMI_NUM ;emi_no++)
+	{
+		for (i=0; i<WSCT_AMOUNT; i++) {
+			addr = EMI_DBWA + i*4;
+			value = emi_readl(IOMEM((unsigned long)BaseAddrEMI[emi_no] + addr));
+
+			value = (value & ~(Mask_master << offset_master)) | ((*(master+i) & Mask_master) << offset_master);
+			value = (value & ~(Mask_rw << offset_rw)) | ((*(rw+i) & Mask_rw) << offset_rw);
+
+
+			emi_reg_sync_writel(value, (unsigned long)BaseAddrEMI[emi_no] + addr);
+		}
+	}
+	return BM_REQ_OK;
+}
+
+int MET_BM_SetWSCT_high_priority(unsigned int *disable, unsigned int *select)
+{
+	volatile unsigned int value, addr;
+	int i;
+	unsigned int emi_no;
+
+	const unsigned int Mask_disable = 0x1;
+	const unsigned int offset_disable  = 2;
+
+	const unsigned int Mask_select = 0xF;
+	const unsigned int offset_select  = 28;
+
+	for(emi_no=0; emi_no<EMI_NUM ;emi_no++)
+	{
+		for (i=0;i<WSCT_AMOUNT;i++) {
+			addr = EMI_DBWA + i*4;
+			value = emi_readl(IOMEM((unsigned long)BaseAddrEMI[emi_no] + addr));
+			value = (value & ~(Mask_disable << offset_disable)) | ((*(disable+i) & Mask_disable) << offset_disable);
+			emi_reg_sync_writel(value, (unsigned long)BaseAddrEMI[emi_no] + addr);
+
+			/* ultra level setting */
+			addr = EMI_DBWA_2ND + i*4;
+			value = emi_readl(IOMEM((unsigned long)BaseAddrEMI[emi_no] + addr));
+			value = (value & ~(Mask_select << offset_select)) | ((*(select+i) & Mask_select) << offset_select);
+			emi_reg_sync_writel(value, (unsigned long)BaseAddrEMI[emi_no] + addr);
+		}
+	}
+	return BM_REQ_OK;
+}
+
+/* busid enbale: EMI_DBWX[3],    X=A~F */
+/* busid sel: EMI_DBWX[28:16],    X=A~F  (SEL_ID_TMP) */
+/* busid mask : EMI_DBWY[12:0]  或 EMI_DBWY[28:16],  Y=I~K    (SEL_ID_MSK) */
+int MET_BM_SetWSCT_busid_idmask(unsigned int *busid, unsigned int *idMask)
+{
+	volatile unsigned int value, addr;
+	volatile unsigned int enable_tmp, busid_tmp, idmask_tmp; 
+	int i;
+	unsigned int emi_no;
+
+	const unsigned int Mask_busid = 0x1FFF;
+	const unsigned int offset_busid  = 16;
+
+	const unsigned int Mask_enable = 0x1;
+	const unsigned int offset_enable  = 3;
+
+	const unsigned int Mask_idMask = 0x1FFF;
+	const unsigned int offset_idMask_even  = 0;
+	const unsigned int offset_idMask_odd  = 16;
+
+	for(emi_no=0; emi_no<EMI_NUM ;emi_no++)
+	{
+		for (i=0;i<WSCT_AMOUNT;i++) {
+
+			/*enable, SEL_ID_TMP*/
+			if (*(busid+i)>0xffff) {
+				enable_tmp = 0;
+				busid_tmp = 0x1FFF;
+				idmask_tmp = 0x1FFF;
+			}
+			else {
+				enable_tmp = 1;
+				busid_tmp = *(busid+i) & Mask_busid;
+				idmask_tmp = *(idMask+i) & Mask_idMask;
+			}
+
+			
+			addr = EMI_DBWA + i*4;
+			value = emi_readl(IOMEM((unsigned long)BaseAddrEMI[emi_no] + addr));
+
+			value = (value & ~(Mask_busid << offset_busid)) | (busid_tmp << offset_busid);
+			value = (value & ~(Mask_enable << offset_enable)) | (enable_tmp << offset_enable);
+
+			emi_reg_sync_writel(value, (unsigned long)BaseAddrEMI[emi_no] + addr);
+
+			/*SEL_ID_MSK*/
+			addr = EMI_DBWI + (i/2)*4;
+
+			value = emi_readl(IOMEM((unsigned long)BaseAddrEMI[emi_no] + addr));
+
+			if (i%2==0)
+				value = (value & ~(Mask_idMask << offset_idMask_even)) | (idmask_tmp << offset_idMask_even);
+			else
+				value = (value & ~(Mask_idMask << offset_idMask_odd)) | (idmask_tmp << offset_idMask_odd);
+
+			emi_reg_sync_writel(value, (unsigned long)BaseAddrEMI[emi_no] + addr);
+		}
+	}
+
+	return BM_REQ_OK;
+}
+
+
+int MET_BM_SetWSCT_chn_rank_sel(unsigned int *chn_rank_sel)
+{
+	volatile unsigned int value, addr;
+	int i;
+	unsigned int emi_no;
+
+	const unsigned int Mask = 0xF;
+	const unsigned int offset  = 12;
+
+	for(emi_no=0; emi_no<EMI_NUM ;emi_no++)
+	{
+		for (i=0;i<WSCT_AMOUNT;i++) {
+			addr = EMI_DBWA_2ND + i*4;
+			value = emi_readl(IOMEM((unsigned long)BaseAddrEMI[emi_no] + addr));
+
+			value = (value & ~(Mask << offset)) | ((*(chn_rank_sel+i) & Mask) << offset);
+
+			emi_reg_sync_writel(value, (unsigned long)BaseAddrEMI[emi_no] + addr);
+		}
+	}
+	return BM_REQ_OK;
+}
+
+int MET_BM_SetWSCT_burst_range(unsigned int *bnd_dis, unsigned int *low_bnd, unsigned int *up_bnd)
+{
+	volatile unsigned int value, addr;
+	int i;
+	unsigned int emi_no;
+
+	const unsigned int Mask_dis = 0x1, Mask_low_bnd = 0x1FF, Mask_up_bnd = 0x1FF;
+	const unsigned int offset_dis = 4, offset_low_bnd = 16 , offset_up_bnd = 0 ;
+
+	for(emi_no=0; emi_no<EMI_NUM ;emi_no++)
+	{
+		for (i=0;i<WSCT_AMOUNT;i++) {
+
+			addr = EMI_DBWA + i*4;
+			value = emi_readl(IOMEM((unsigned long)BaseAddrEMI[emi_no] + addr));
+
+			value = (value & ~(Mask_dis << offset_dis)) | ((*(bnd_dis+i) & Mask_dis) << offset_dis);
+
+			emi_reg_sync_writel(value, (unsigned long)BaseAddrEMI[emi_no] + addr);
+
+
+			addr = EMI_DBWA_2ND + i*4;
+			value = emi_readl(IOMEM((unsigned long)BaseAddrEMI[emi_no] + addr));
+
+			value = (value & ~(Mask_low_bnd << offset_low_bnd)) | ((*(low_bnd+i) & Mask_low_bnd) << offset_low_bnd);
+			value = (value & ~(Mask_up_bnd << offset_up_bnd)) | ((*(up_bnd+i) & Mask_up_bnd) << offset_up_bnd);
+
+			emi_reg_sync_writel(value, (unsigned long)BaseAddrEMI[emi_no] + addr);
+		}
+	}
+	return BM_REQ_OK;
+
+}
+
+int MET_BM_SetTSCT_busid_enable(unsigned int *enable)
+{
+	//MET_BM_Set_WsctTsct_id_sel
+
+	int i;
+
+	for (i=0;i<TSCT_AMOUNT;i++) {
+		MET_BM_Set_WsctTsct_id_sel(i, *(enable+i));
+	}
+
+	return BM_REQ_OK;
+}
+
+//use the origin together, MET_BM_SetUltraHighFilter()
+/* EMI_TTYPEN_CONA [23:20],  N=1~21   (HPRI_SEL) */
+int MET_BM_SetTtype_high_priority_sel(unsigned int _high_priority_filter, unsigned int *select)
+{
+	int i;
+	volatile unsigned int enable;
+	volatile unsigned int value, addr;
+	unsigned int emi_no;
+
+	const unsigned int Mask_sel = 0xF;
+	const unsigned int offset_sel = 20;
+
+	for(emi_no=0; emi_no<EMI_NUM ;emi_no++)
+	{
+		for (i = 0; i < BM_COUNTER_MAX; i++) {
+			if ((_high_priority_filter & (1 << i)) == 0){
+				enable = 0;
+			}
+			else {
+				enable = 1;
+			}
+
+			MET_BM_SetUltraHighFilter(i + 1, enable);
+
+			/* ultra level select */
+			addr = EMI_TTYPE1_CONA + i*8;
+			value = emi_readl(IOMEM((unsigned long)BaseAddrEMI[emi_no] + addr));
+
+			value = (value & ~(Mask_sel << offset_sel)) | ((*(select+i) & Mask_sel) << offset_sel);
+
+			emi_reg_sync_writel(value, (unsigned long)BaseAddrEMI[emi_no] + addr);
+
+		}
+	}
+	return BM_REQ_OK;
+}
+
+
+//always call this API to init the reg
+//related API, MET_BM_SetbusID, MET_BM_SetbusID_En
+int MET_BM_SetTtype_busid_idmask(unsigned int *busid, unsigned int *idMask, int _ttype1_16_en, int _ttype17_21_en)
+{
+	int i;
+	volatile unsigned int value, addr;
+	unsigned int emi_no;
+
+	const unsigned int Mask_idMask = 0x1FFF;
+	const unsigned int offset_idMask = 0;
+
+	if (_ttype1_16_en != BM_TTYPE1_16_ENABLE) {
+		/* mask set 0x1FFF , busid set disable*/
+		for (i = 1; i <= 16; i++) {
+			*(busid + i - 1) = 0xfffff;
+			*(idMask + i - 1) = 0x1FFF;
+		}
+	}
+
+	if (_ttype17_21_en != BM_TTYPE17_21_ENABLE) {
+		for (i = 17; i <= 21; i++) {
+			*(busid + i - 1) = 0xfffff;
+			*(idMask + i - 1) = 0x1FFF;
+		}
+	}
+
+	for(emi_no=0; emi_no<EMI_NUM ;emi_no++)
+	{
+		for (i = 1; i <= BM_COUNTER_MAX; i++) {
+			MET_BM_SetbusID(i, *(busid + i - 1));
+			MET_BM_SetbusID_En(i, ( *(busid + i - 1) > 0xffff) ? 0 : 1);
+
+			/* set idMask */
+			addr = EMI_TTYPE1_CONA + (i-1)*8;
+			value = emi_readl(IOMEM((unsigned long)BaseAddrEMI[emi_no] + addr));
+
+			value = (value & ~(Mask_idMask << offset_idMask)) | ((*(idMask+i-1) & Mask_idMask) << offset_idMask);
+
+			emi_reg_sync_writel(value, (unsigned long)BaseAddrEMI[emi_no] + addr);
+
+		}
+	}
+	return BM_REQ_OK;
+}
+
+
+int MET_BM_SetTtype_chn_rank_sel(unsigned int *chn_rank_sel)
+{
+	volatile unsigned int value, addr;
+	int i;
+	unsigned int emi_no;
+	
+	const unsigned int Mask = 0xF;
+	const unsigned int offset  = 16;
+
+	for(emi_no=0; emi_no<EMI_NUM ;emi_no++)
+	{
+		for (i=0;i<BM_COUNTER_MAX;i++) {
+			addr = EMI_TTYPE1_CONA + i*8;
+			value = emi_readl(IOMEM((unsigned long)BaseAddrEMI[emi_no] + addr));
+
+			value = (value & ~(Mask << offset)) | ((*(chn_rank_sel+i) & Mask) << offset);
+
+			emi_reg_sync_writel(value, (unsigned long)BaseAddrEMI[emi_no] + addr);
+		}
+	}
+	return BM_REQ_OK;
+}
+
+int MET_BM_SetTtype_burst_range(unsigned int *bnd_dis, unsigned int *low_bnd, unsigned int *up_bnd)
+{
+	volatile unsigned int value, addr;
+	int i;
+	unsigned int emi_no;
+
+	const unsigned int Mask_dis = 0x1, Mask_low_bnd = 0x1FF, Mask_up_bnd = 0x1FF;
+	const unsigned int offset_dis = 24, offset_low_bnd = 16 , offset_up_bnd = 0 ;
+
+	for(emi_no=0; emi_no<EMI_NUM ;emi_no++)
+	{
+		for (i=0;i<BM_COUNTER_MAX;i++) {
+
+			/* set dis bit */
+			addr = EMI_TTYPE1_CONA + i*8;
+			value = emi_readl(IOMEM((unsigned long)BaseAddrEMI[emi_no] + addr));
+
+			value = (value & ~(Mask_dis << offset_dis)) | ((*(bnd_dis+i) & Mask_dis) << offset_dis);
+
+			emi_reg_sync_writel(value, (unsigned long)BaseAddrEMI[emi_no] + addr);
+
+
+			addr = EMI_TTYPE1_CONB + i*8;
+			value = emi_readl(IOMEM((unsigned long)BaseAddrEMI[emi_no] + addr));
+
+			value = (value & ~(Mask_low_bnd << offset_low_bnd)) | ((*(low_bnd+i) & Mask_low_bnd) << offset_low_bnd);
+			value = (value & ~(Mask_up_bnd << offset_up_bnd)) | ((*(up_bnd+i) & Mask_up_bnd) << offset_up_bnd);
+
+			emi_reg_sync_writel(value, (unsigned long)BaseAddrEMI[emi_no] + addr);
+		}
+	}
+	return BM_REQ_OK;
+}
+
+unsigned int MET_EMI_Get_CONH_2ND(unsigned int emi_no)
+{
+	return readl(IOMEM((unsigned long)BaseAddrEMI[emi_no] + EMI_CONH_2ND));
+}
diff --git a/src/devtools/met-driver/4.19/common/emi/SEDA3_6/mtk_emi_bm.h b/src/devtools/met-driver/4.19/common/emi/SEDA3_6/mtk_emi_bm.h
new file mode 100644
index 0000000..96bd645
--- /dev/null
+++ b/src/devtools/met-driver/4.19/common/emi/SEDA3_6/mtk_emi_bm.h
@@ -0,0 +1,300 @@
+/*  SPDX-License-Identifier: GPL-2.0 */  
+/*
+ * Copyright (C) 2019 MediaTek Inc.
+ *
+ */
+#ifndef __MT_MET_EMI_BM_H__
+#define __MT_MET_EMI_BM_H__
+
+#define EMI_VER_MAJOR  3
+#define EMI_VER_MINOR  6
+
+#define EMI_NUM_MAX     2
+#define EMI_NUM 	2
+
+#define FILE_NODE_DATA_LEN 512 
+#define WSCT_AMOUNT 6
+#define TSCT_AMOUNT 3
+
+
+#define DRAM_EMI_BASECLOCK_RATE_LP4     4
+#define DRAM_EMI_BASECLOCK_RATE_LP3     2
+
+#define DRAM_IO_BUS_WIDTH_LP4           16
+#define DRAM_IO_BUS_WIDTH_LP3           32
+
+#define DRAM_DATARATE   2
+
+#define DRAM_FREQ_DEFAULT  3733
+#define DDR_RATIO_DEFAULT  8
+#define DRAM_TYPE_DEFAULT  3
+
+/*#define ADDR_EMI        ((unsigned long)BaseAddrEMI)*/
+
+#define BM_MASTER_M0            (0x01)
+#define BM_MASTER_M1            (0x02)
+#define BM_MASTER_M2            (0x04)
+#define BM_MASTER_M3            (0x08)
+#define BM_MASTER_M4            (0x10)
+#define BM_MASTER_M5            (0x20)
+#define BM_MASTER_M6            (0x40)
+#define BM_MASTER_M7            (0x80)
+#define BM_MASTER_ALL           (0xFF)
+
+
+enum BM_RW_Type {
+	BM_BOTH_READ_WRITE,
+	BM_READ_ONLY,
+	BM_WRITE_ONLY
+};
+
+enum {
+	BM_TRANS_TYPE_1BEAT = 0x0,
+	BM_TRANS_TYPE_2BEAT,
+	BM_TRANS_TYPE_3BEAT,
+	BM_TRANS_TYPE_4BEAT,
+	BM_TRANS_TYPE_5BEAT,
+	BM_TRANS_TYPE_6BEAT,
+	BM_TRANS_TYPE_7BEAT,
+	BM_TRANS_TYPE_8BEAT,
+	BM_TRANS_TYPE_9BEAT,
+	BM_TRANS_TYPE_10BEAT,
+	BM_TRANS_TYPE_11BEAT,
+	BM_TRANS_TYPE_12BEAT,
+	BM_TRANS_TYPE_13BEAT,
+	BM_TRANS_TYPE_14BEAT,
+	BM_TRANS_TYPE_15BEAT,
+	BM_TRANS_TYPE_16BEAT,
+	BM_TRANS_TYPE_1Byte = 0 << 4,
+	BM_TRANS_TYPE_2Byte = 1 << 4,
+	BM_TRANS_TYPE_4Byte = 2 << 4,
+	BM_TRANS_TYPE_8Byte = 3 << 4,
+	BM_TRANS_TYPE_16Byte = 4 << 4,
+	BM_TRANS_TYPE_32Byte = 5 << 4,
+	BM_TRANS_TYPE_BURST_WRAP = 0 << 7,
+	BM_TRANS_TYPE_BURST_INCR = 1 << 7
+};
+
+enum {
+	BM_TRANS_RW_DEFAULT = 0x0,
+	BM_TRANS_RW_READONLY,
+	BM_TRANS_RW_WRITEONLY,
+	BM_TRANS_RW_RWBOTH
+};
+
+enum {
+	BM_WSCT_RW_DISABLE = 0x0,
+	BM_WSCT_RW_READONLY,
+	BM_WSCT_RW_WRITEONLY,
+	BM_WSCT_RW_RWBOTH
+};
+
+/*coda busid 12bit, but HW support 16 bit*/
+#define EMI_BMID_MASK				(0xFFFF)
+#define BM_COUNTER_MAX				(21)
+
+enum {
+	BUS_MON_EN_SHIFT = 0,
+	BUS_MON_PAUSE_SHIFT = 1,
+	BUS_MON_IDLE_SHIFT = 3,
+	BC_OVERRUN_SHIFT = 8,
+	DRAMC_CG_SHIFT = 9,
+};
+
+#define BM_REQ_OK				(0)
+#define BM_ERR_WRONG_REQ			(-1)
+#define BM_ERR_OVERRUN				(-2)
+
+#define BM_WSCT_TSCT_IDSEL_ENABLE		(0)
+#define BM_WSCT_TSCT_IDSEL_DISABLE		(-1)
+#define BM_TTYPE1_16_ENABLE			(0)
+#define BM_TTYPE1_16_DISABLE			(-1)
+#define BM_TTYPE17_21_ENABLE			(0)
+#define BM_TTYPE17_21_DISABLE			(-1)
+#define BM_BW_LIMITER_ENABLE			(0)
+#define BM_BW_LIMITER_DISABLE			(-1)
+
+#define M0_DOUBLE_HALF_BW_1CH	(0x0)
+#define M0_DOUBLE_HALF_BW_2CH	(0x1)
+#define M0_DOUBLE_HALF_BW_4CH	(0x2)
+
+/* EMI Rank configuration */
+enum {
+	DISABLE_DUAL_RANK_MODE = 0,
+	ENABLE_DUAL_RANK_MODE,
+};
+
+#define RANK_MASK 0x1
+#define ONE_RANK 1
+#define DUAL_RANK 2
+
+
+#if defined(CONFIG_MTK_TINYSYS_SSPM_SUPPORT) && defined(ONDIEMET_SUPPORT)
+enum BM_EMI_IPI_Type {
+	SET_BASE_EMI = 0x0,
+	SET_EBM_CONFIGS1 = 0x7,
+	SET_EBM_CONFIGS2 = 0x8,
+	SET_REGISTER_CB = 0x9,
+};
+#endif
+
+
+#define	EMI_OFF			0x0000
+#define EMI_CONA		(0x000-EMI_OFF)
+#define EMI_CONH		(0x038-EMI_OFF)
+#define EMI_CONH_2ND		(0x03C-EMI_OFF)
+#define EMI_CONM		(0x060-EMI_OFF)
+#define EMI_CONO		(0x070-EMI_OFF)
+
+#define EMI_MDCT		(0x078 - EMI_OFF)
+#define EMI_MDCT_2ND		(0x07C - EMI_OFF)
+
+#define EMI_ARBA		(0x100 - EMI_OFF)
+#define EMI_ARBB		(0x108 - EMI_OFF)
+#define EMI_ARBC		(0x110 - EMI_OFF)
+#define EMI_ARBD		(0x118 - EMI_OFF)
+#define EMI_ARBE		(0x120 - EMI_OFF)
+#define EMI_ARBF		(0x128 - EMI_OFF)
+#define EMI_ARBG		(0x130 - EMI_OFF)
+#define EMI_ARBG_2ND		(0x134 - EMI_OFF)
+#define EMI_ARBH		(0x138 - EMI_OFF)
+
+
+#define EMI_BMEN		(0x400-EMI_OFF)
+#define EMI_MSEL		(0x440 - EMI_OFF)
+#define EMI_MSEL2		(0x468 - EMI_OFF)
+#define EMI_MSEL3		(0x470 - EMI_OFF)
+#define EMI_MSEL4		(0x478 - EMI_OFF)
+#define EMI_MSEL5		(0x480 - EMI_OFF)
+#define EMI_MSEL6		(0x488 - EMI_OFF)
+#define EMI_MSEL7		(0x490 - EMI_OFF)
+#define EMI_MSEL8		(0x498 - EMI_OFF)
+#define EMI_MSEL9		(0x4A0 - EMI_OFF)
+#define EMI_MSEL10		(0x4A8 - EMI_OFF)
+
+#define EMI_BMID0		(0x4B0 - EMI_OFF)
+#define EMI_BMID1		(0x4B4 - EMI_OFF)
+#define EMI_BMID2		(0x4B8 - EMI_OFF)
+#define EMI_BMID3		(0x4BC - EMI_OFF)
+#define EMI_BMID4		(0x4C0 - EMI_OFF)
+#define EMI_BMID5		(0x4C4 - EMI_OFF)
+#define EMI_BMID6		(0x4C8 - EMI_OFF)
+#define EMI_BMID7		(0x4CC - EMI_OFF)
+#define EMI_BMID8		(0x4D0 - EMI_OFF)
+#define EMI_BMID9		(0x4D4 - EMI_OFF)
+#define EMI_BMID10		(0x4D8 - EMI_OFF)
+
+#define EMI_BMEN1		(0x4E0 - EMI_OFF)
+#define EMI_BMEN2		(0x4E8 - EMI_OFF)
+#define EMI_BMRW0		(0x4F8 - EMI_OFF)
+#define EMI_BMRW1		(0x4FC - EMI_OFF)
+
+
+/* SEDA 3.5 New! reg*/
+/* For WSCT setting*/
+#define EMI_DBWA (0xF00 - EMI_OFF)
+#define EMI_DBWB (0xF04 - EMI_OFF)
+#define EMI_DBWC (0xF08 - EMI_OFF)
+#define EMI_DBWD (0xF0C - EMI_OFF)
+#define EMI_DBWE (0xF10 - EMI_OFF)
+#define EMI_DBWF (0xF14 - EMI_OFF)
+
+
+#define EMI_DBWA_2ND (0xF2C - EMI_OFF)
+#define EMI_DBWB_2ND (0xF30 - EMI_OFF)
+#define EMI_DBWC_2ND (0xF34 - EMI_OFF)
+#define EMI_DBWD_2ND (0xF38 - EMI_OFF)
+#define EMI_DBWE_2ND (0xF3C - EMI_OFF)
+#define EMI_DBWF_2ND (0xF40 - EMI_OFF)
+
+#define EMI_DBWI (0xF20 - EMI_OFF) /* SEL_ID_MSK*/
+#define EMI_DBWJ (0xF24 - EMI_OFF)
+#define EMI_DBWK (0xF28 - EMI_OFF)
+
+/* For Ttype setting */
+#define EMI_TTYPE1_CONA (0xF50 - EMI_OFF)
+#define EMI_TTYPE1_CONB (0xF54 - EMI_OFF)
+#define EMI_TTYPE2_CONA (0xF58 - EMI_OFF)
+#define EMI_TTYPE2_CONB (0xF5C - EMI_OFF)
+#define EMI_TTYPE3_CONA (0xF60 - EMI_OFF)
+#define EMI_TTYPE3_CONB (0xF64 - EMI_OFF)
+#define EMI_TTYPE4_CONA (0xF68 - EMI_OFF)
+#define EMI_TTYPE4_CONB (0xF6C - EMI_OFF)
+#define EMI_TTYPE5_CONA (0xF70 - EMI_OFF)
+#define EMI_TTYPE5_CONB (0xF74 - EMI_OFF)
+#define EMI_TTYPE6_CONA (0xF78 - EMI_OFF)
+#define EMI_TTYPE6_CONB (0xF7C - EMI_OFF)
+#define EMI_TTYPE7_CONA (0xF80 - EMI_OFF)
+#define EMI_TTYPE7_CONB (0xF84 - EMI_OFF)
+#define EMI_TTYPE8_CONA (0xF88 - EMI_OFF)
+#define EMI_TTYPE8_CONB (0xF8C - EMI_OFF)
+#define EMI_TTYPE9_CONA (0xF90 - EMI_OFF)
+#define EMI_TTYPE9_CONB (0xF94 - EMI_OFF)
+#define EMI_TTYPE10_CONA (0xF98 - EMI_OFF)
+#define EMI_TTYPE10_CONB (0xF9C - EMI_OFF)
+#define EMI_TTYPE11_CONA (0xFA0 - EMI_OFF)
+#define EMI_TTYPE11_CONB (0xFA4 - EMI_OFF)
+#define EMI_TTYPE12_CONA (0xFA8 - EMI_OFF)
+#define EMI_TTYPE12_CONB (0xFAC - EMI_OFF)
+#define EMI_TTYPE13_CONA (0xFB0 - EMI_OFF)
+#define EMI_TTYPE13_CONB (0xFB4 - EMI_OFF)
+#define EMI_TTYPE14_CONA (0xFB8 - EMI_OFF)
+#define EMI_TTYPE14_CONB (0xFBC - EMI_OFF)
+#define EMI_TTYPE15_CONA (0xFC0 - EMI_OFF)
+#define EMI_TTYPE15_CONB (0xFC4 - EMI_OFF)
+#define EMI_TTYPE16_CONA (0xFC8 - EMI_OFF)
+#define EMI_TTYPE16_CONB (0xFCC - EMI_OFF)
+#define EMI_TTYPE17_CONA (0xFD0 - EMI_OFF)
+#define EMI_TTYPE17_CONB (0xFD4 - EMI_OFF)
+#define EMI_TTYPE18_CONA (0xFD8 - EMI_OFF)
+#define EMI_TTYPE18_CONB (0xFDC - EMI_OFF)
+#define EMI_TTYPE19_CONA (0xFE0 - EMI_OFF)
+#define EMI_TTYPE19_CONB (0xFE4 - EMI_OFF)
+#define EMI_TTYPE20_CONA (0xFE8 - EMI_OFF)
+#define EMI_TTYPE20_CONB (0xFEC - EMI_OFF)
+#define EMI_TTYPE21_CONA (0xFF0 - EMI_OFF)
+#define EMI_TTYPE21_CONB (0xFF4 - EMI_OFF)
+
+
+
+extern int MET_BM_Init(void);
+extern void MET_BM_DeInit(void);
+extern void MET_BM_SaveCfg(void);
+extern void MET_BM_RestoreCfg(void);
+
+
+
+extern int MET_BM_SetMonitorCounter(const unsigned int counter_num,
+				    const unsigned int master, const unsigned int trans_type);
+extern int MET_BM_SetTtypeCounterRW(unsigned int bmrw0_val, unsigned int bmrw1_val);
+extern int MET_BM_Set_WsctTsct_id_sel(unsigned int counter_num, unsigned int enable);
+extern int MET_BM_SetMaster(const unsigned int counter_num, const unsigned int master);
+extern int MET_BM_SetbusID_En(const unsigned int counter_num,
+			      const unsigned int enable);
+extern int MET_BM_SetbusID(const unsigned int counter_num,
+			   const unsigned int id);
+extern int MET_BM_SetUltraHighFilter(const unsigned int counter_num, const unsigned int enable);
+extern int MET_BM_SetLatencyCounter(unsigned int enable);
+extern void MET_BM_SetReadWriteType(const unsigned int ReadWriteType);
+
+extern unsigned int MET_EMI_GetDramRankNum(unsigned int emi_no);
+extern unsigned int MET_EMI_GetDramRankNum_CHN1(unsigned int emi_no);
+
+
+unsigned int MET_EMI_GetDramChannNum(unsigned int emi_no);
+unsigned int MET_EMI_Get_CONH_2ND(unsigned int emi_no);
+
+/* SEDA 3.5 NEW */
+extern int MET_BM_SetWSCT_master_rw(unsigned int *master , unsigned int *rw);
+extern int MET_BM_SetWSCT_high_priority(unsigned int *disable, unsigned int *select);
+extern int MET_BM_SetWSCT_busid_idmask(unsigned int *busid, unsigned int *idMask);
+extern int MET_BM_SetWSCT_chn_rank_sel(unsigned int *chn_rank_sel);
+extern int MET_BM_SetWSCT_burst_range(unsigned int *bnd_dis, unsigned int *low_bnd, unsigned int *up_bnd);
+extern int MET_BM_SetTSCT_busid_enable(unsigned int *enable);
+extern int MET_BM_SetTtype_high_priority_sel(unsigned int _high_priority_filter, unsigned int *select);
+extern int MET_BM_SetTtype_busid_idmask(unsigned int *busid, unsigned int *idMask, int _ttype1_16_en, int _ttype17_21_en);
+extern int MET_BM_SetTtype_chn_rank_sel(unsigned int *chn_rank_sel);
+extern int MET_BM_SetTtype_burst_range(unsigned int *bnd_dis, unsigned int *low_bnd, unsigned int *up_bnd);
+extern unsigned int MET_EMI_Get_BaseClock_Rate(void);
+
+#endif                          /* !__MT_MET_EMI_BM_H__ */
diff --git a/src/devtools/met-driver/4.19/common/interface.c b/src/devtools/met-driver/4.19/common/interface.c
new file mode 100644
index 0000000..a30ba4d
--- /dev/null
+++ b/src/devtools/met-driver/4.19/common/interface.c
@@ -0,0 +1,1484 @@
+/*
+ * Copyright (C) 2019 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/sched/clock.h>
+#include <linux/fs.h>
+#include <linux/sched.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/miscdevice.h>
+#include <linux/kallsyms.h>
+#include <linux/syscore_ops.h>
+#include <linux/dma-mapping.h>
+#include "interface.h"
+#include "sampler.h"
+#include "util.h"
+
+#include "ondiemet.h"
+
+#include "met_drv.h"
+#include "met_tag.h"
+#include "met_kernel_symbol.h"
+#include "met_power.h"
+#include "met_api_tbl.h"
+#include "version.h"
+
+extern int enable_met_backlight_tag(void);
+extern int output_met_backlight_tag(int level);
+
+static int run = -1;
+static int sample_rate = 1000;	/* Default: 1000 Hz */
+static int met_suspend_compensation_mode;
+static int met_suspend_compensation_flag;
+
+/*
+ * met_cpu_pmu_method:
+ *   0: MET pmu driver
+ *   1: perf APIs
+ */
+unsigned int met_cpu_pmu_method = 1;
+/*
+ * controls whether re-configuring pmu events after leaving cpu off state
+ */
+unsigned int met_cpu_pm_pmu_reconfig = 1;
+
+int met_hrtimer_expire;		/* in ns */
+int met_timer_expire;		/* in jiffies */
+unsigned int ctrl_flags;
+int met_mode;
+EXPORT_SYMBOL(met_mode);
+
+DEFINE_PER_CPU(char[MET_STRBUF_SIZE], met_strbuf_nmi);
+EXPORT_SYMBOL(met_strbuf_nmi);
+
+DEFINE_PER_CPU(char[MET_STRBUF_SIZE], met_strbuf_irq);
+EXPORT_SYMBOL(met_strbuf_irq);
+
+DEFINE_PER_CPU(char[MET_STRBUF_SIZE], met_strbuf_sirq);
+EXPORT_SYMBOL(met_strbuf_sirq);
+
+DEFINE_PER_CPU(char[MET_STRBUF_SIZE], met_strbuf);
+EXPORT_SYMBOL(met_strbuf);
+
+static void calc_timer_value(int rate)
+{
+	sample_rate = rate;
+
+	if (rate == 0) {
+		met_hrtimer_expire = 0;
+		met_timer_expire = 0;
+		return;
+	}
+
+	met_hrtimer_expire = 1000000000 / rate;
+
+	/* Case 1: hrtimer < 1 OS tick, met_timer_expire = 1 OS tick */
+	if (rate > HZ)
+		met_timer_expire = 1;
+	/* Case 2: hrtimer > 1 OS tick, met_timer_expire is hrtimer + 1 OS tick */
+	else
+		met_timer_expire = (HZ / rate) + 1;
+
+	/* pr_debug("JBK HZ=%d, met_hrtimer_expire=%d ns, met_timer_expire=%d ticks\n", */
+	/* HZ, met_hrtimer_expire, met_timer_expire); */
+}
+
+int met_parse_num(const char *str, unsigned int *value, int len)
+{
+	int ret;
+
+	if (len <= 0)
+		return -1;
+
+	if ((len > 2) &&
+		((str[0] == '0') &&
+		((str[1] == 'x') || (str[1] == 'X')))) {
+		ret = kstrtoint(str, 16, value);
+	} else {
+		ret = kstrtoint(str, 10, value);
+	}
+
+	if (ret != 0)
+		return -1;
+
+	return 0;
+}
+
+void met_set_suspend_notify(int flag)
+{
+	if (met_suspend_compensation_mode == 1)
+		met_suspend_compensation_flag = flag;
+	else
+		met_suspend_compensation_flag = 0;
+}
+
+LIST_HEAD(met_list);
+static struct kobject *kobj_misc;
+static struct kobject *kobj_pmu;
+static struct kobject *kobj_bus;
+
+static ssize_t ver_show(struct device *dev, struct device_attribute *attr, char *buf);
+static DEVICE_ATTR(ver, 0444, ver_show, NULL);
+
+static ssize_t plf_show(struct device *dev, struct device_attribute *attr, char *buf);
+static DEVICE_ATTR(plf, 0444, plf_show, NULL);
+
+static ssize_t chip_id_show(struct device *dev, struct device_attribute *attr, char *buf);
+static DEVICE_ATTR(chip_id, 0444, chip_id_show, NULL);
+
+static ssize_t core_topology_show(struct device *dev, struct device_attribute *attr, char *buf);
+static DEVICE_ATTR(core_topology, 0444, core_topology_show, NULL);
+
+static ssize_t devices_show(struct device *dev, struct device_attribute *attr, char *buf);
+static DEVICE_ATTR(devices, 0444, devices_show, NULL);
+
+static ssize_t ctrl_show(struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t ctrl_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			  size_t count);
+static DEVICE_ATTR(ctrl, 0664, ctrl_show, ctrl_store);
+
+static ssize_t spr_show(struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t spr_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			 size_t count);
+static DEVICE_ATTR(sample_rate, 0664, spr_show, spr_store);
+
+static ssize_t run_show(struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t run_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			 size_t count);
+static DEVICE_ATTR(run, 0664, run_show, run_store);
+
+static ssize_t ksym_show(struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t ksym_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			 size_t count);
+static DEVICE_ATTR(ksym, 0664, ksym_show, ksym_store);
+
+static ssize_t cpu_pmu_method_show(struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t cpu_pmu_method_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			  size_t count);
+static DEVICE_ATTR(cpu_pmu_method, 0664, cpu_pmu_method_show, cpu_pmu_method_store);
+
+static ssize_t cpu_pm_pmu_reconfig_show(struct device *dev,
+					struct device_attribute *attr,
+					char *buf);
+static ssize_t cpu_pm_pmu_reconfig_store(struct device *dev,
+					 struct device_attribute *attr,
+					 const char *buf,
+					 size_t count);
+static DEVICE_ATTR(cpu_pm_pmu_reconfig,
+		   0664,
+		   cpu_pm_pmu_reconfig_show,
+		   cpu_pm_pmu_reconfig_store);
+
+#ifdef PR_CPU_NOTIFY
+int met_cpu_notify;
+static ssize_t cpu_notify_show(struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t cpu_notify_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			  size_t count);
+static DEVICE_ATTR(cpu_notify, 0664, cpu_notify_show, cpu_notify_store);
+#endif
+
+#ifdef CONFIG_CPU_FREQ
+static ssize_t dvfs_show(struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t dvfs_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			  size_t count);
+static DEVICE_ATTR(dvfs, 0664, dvfs_show, dvfs_store);
+#endif
+
+static ssize_t suspend_compensation_enable_show(struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t suspend_compensation_enable_store(struct device *dev, struct device_attribute *attr,
+							const char *buf, size_t count);
+static DEVICE_ATTR(suspend_compensation_enable, 0664, suspend_compensation_enable_show,
+			suspend_compensation_enable_store);
+
+static ssize_t suspend_compensation_flag_show(struct device *dev, struct device_attribute *attr, char *buf);
+static DEVICE_ATTR(suspend_compensation_flag, 0444, suspend_compensation_flag_show, NULL);
+
+static ssize_t ipi_test_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			 size_t count);
+static DEVICE_ATTR(ipi_test, 0220, NULL, ipi_test_store);
+
+static const struct file_operations met_file_ops = {
+	.owner = THIS_MODULE
+};
+
+struct miscdevice met_device = {
+	.minor = MISC_DYNAMIC_MINOR,
+	.name = "met",
+	.mode = 0664,
+	.fops = &met_file_ops
+};
+EXPORT_SYMBOL(met_device);
+
+static int met_run(void)
+{
+	sampler_start();
+#ifdef MET_USER_EVENT_SUPPORT
+	bltab.flag &= (~MET_CLASS_ALL);
+#endif
+	ondiemet_start();
+	return 0;
+}
+
+static void met_stop(void)
+{
+#ifdef MET_USER_EVENT_SUPPORT
+	bltab.flag |= MET_CLASS_ALL;
+#endif
+	sampler_stop();
+	/* the met.ko will be use by script "cat ...", release it */
+	if ((ondiemet_module[ONDIEMET_SSPM] == 0) || (sspm_buffer_size == -1))
+		ondiemet_log_manager_stop();
+	ondiemet_stop();
+	ondiemet_extract();
+}
+
+static ssize_t ver_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int i;
+
+	mutex_lock(&dev->mutex);
+	i = snprintf(buf, PAGE_SIZE, "%s\n", MET_BACKEND_VERSION);
+	mutex_unlock(&dev->mutex);
+	return i;
+}
+
+static ssize_t devices_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int len, total_len = 0;
+	struct metdevice *c = NULL;
+
+	mutex_lock(&dev->mutex);
+	list_for_each_entry(c, &met_list, list) {
+		len = 0;
+		if (c->type == MET_TYPE_PMU)
+			len = snprintf(buf, PAGE_SIZE - total_len, "pmu/%s:0\n", c->name);
+		else if (c->type == MET_TYPE_BUS)
+			len = snprintf(buf, PAGE_SIZE - total_len, "bus/%s:0\n", c->name);
+		else if (c->type == MET_TYPE_MISC)
+			len = snprintf(buf, PAGE_SIZE - total_len, "misc/%s:0\n", c->name);
+
+		if (c->ondiemet_mode == 0) {
+			if (c->process_argument)
+				buf[len - 2]++;
+		} else if (c->ondiemet_mode == 1) {
+			if (c->ondiemet_process_argument)
+				buf[len - 2]++;
+		} else if (c->ondiemet_mode == 2) {
+			if (c->process_argument)
+				buf[len - 2]++;
+			if (c->ondiemet_process_argument)
+				buf[len - 2]++;
+		}
+
+		buf += len;
+		total_len += len;
+	}
+
+	mutex_unlock(&dev->mutex);
+	return total_len;
+}
+
+static char met_platform[16] = "none";
+static ssize_t plf_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int i;
+
+	mutex_lock(&dev->mutex);
+	i = snprintf(buf, PAGE_SIZE, "%s\n", met_platform);
+	mutex_unlock(&dev->mutex);
+	return i;
+}
+
+static unsigned int met_chip_id = 0;
+static ssize_t chip_id_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int i;
+
+	mutex_lock(&dev->mutex);
+	i = snprintf(buf, PAGE_SIZE, "0x%08X\n", met_chip_id);
+	mutex_unlock(&dev->mutex);
+	return i;
+}
+
+static char met_topology[64] = "none";
+static ssize_t core_topology_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int i;
+
+	mutex_lock(&dev->mutex);
+	i = snprintf(buf, PAGE_SIZE, "%s\n", met_topology);
+	mutex_unlock(&dev->mutex);
+	return i;
+}
+
+static ssize_t ctrl_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%d\n", ctrl_flags);
+}
+
+static ssize_t ctrl_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			  size_t count)
+{
+	unsigned int value = 0;
+
+	if (met_parse_num(buf, &value, count) < 0)
+		return -EINVAL;
+
+	ctrl_flags = value;
+	return count;
+}
+
+static ssize_t cpu_pmu_method_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%d\n", met_cpu_pmu_method);
+}
+
+static ssize_t cpu_pmu_method_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			  size_t count)
+{
+	unsigned int value;
+
+	if (met_parse_num(buf, &value, count) < 0)
+		return -EINVAL;
+
+	met_cpu_pmu_method = value;
+	return count;
+}
+
+static ssize_t cpu_pm_pmu_reconfig_show(struct device *dev,
+					struct device_attribute *attr,
+					char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%d\n", met_cpu_pm_pmu_reconfig);
+}
+
+static ssize_t cpu_pm_pmu_reconfig_store(struct device *dev,
+					 struct device_attribute *attr,
+					 const char *buf,
+					 size_t count)
+{
+	unsigned int value;
+
+	if (met_parse_num(buf, &value, count) < 0)
+		return -EINVAL;
+
+	met_cpu_pm_pmu_reconfig = value;
+	return count;
+}
+
+static void _test_trace_ipi_raise(void *info)
+{
+	unsigned int *cpu = (unsigned int *)info;
+	void (*arch_send_call_function_single_ipi_sym)(int cpu) = NULL;
+
+	arch_send_call_function_single_ipi_sym = (void *)symbol_get(met_arch_send_call_function_single_ipi);
+	if (arch_send_call_function_single_ipi_sym)
+		arch_send_call_function_single_ipi_sym(*cpu);
+}
+
+
+static ssize_t ipi_test_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			 size_t count)
+{
+	int this_cpu = smp_processor_id();
+	unsigned int cpu = 0;
+	unsigned int value;
+
+	if (met_parse_num(buf, &value, count) < 0)
+		return -EINVAL;
+
+	cpu = value;
+	if (cpu == this_cpu)
+		_test_trace_ipi_raise(&cpu);
+	else
+		met_smp_call_function_single_symbol(cpu, _test_trace_ipi_raise, &cpu, 1);
+
+	return count;
+}
+
+
+#if	defined(MET_BOOT_MSG)
+char met_boot_msg_tmp[256];
+char met_boot_msg[PAGE_SIZE];
+int met_boot_msg_idx;
+
+int pr_bootmsg(int str_len, char *str)
+{
+	if (met_boot_msg_idx+str_len+1 > PAGE_SIZE)
+		return -1;
+	memcpy(met_boot_msg+met_boot_msg_idx, str, str_len);
+	met_boot_msg_idx += str_len;
+	return 0;
+}
+
+static ssize_t bootmsg_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int	i;
+
+	mutex_lock(&dev->mutex);
+	i = snprintf(buf, PAGE_SIZE, "%s\n", met_boot_msg);
+	mutex_unlock(&dev->mutex);
+	return i;
+}
+
+static DEVICE_ATTR_RO(bootmsg);
+EXPORT_SYMBOL(met_boot_msg_tmp);
+EXPORT_SYMBOL(pr_bootmsg);
+#endif
+
+static ssize_t spr_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int i;
+
+	mutex_lock(&dev->mutex);
+	i = snprintf(buf, PAGE_SIZE, "%d\n", sample_rate);
+	mutex_unlock(&dev->mutex);
+	return i;
+}
+
+static ssize_t spr_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			 size_t count)
+{
+	int value;
+	struct metdevice *c = NULL;
+
+	mutex_lock(&dev->mutex);
+
+	if ((run == 1) || (count == 0) || (buf == NULL)) {
+		mutex_unlock(&dev->mutex);
+		return -EINVAL;
+	}
+	if (kstrtoint(buf, 0, &value) != 0) {
+		mutex_unlock(&dev->mutex);
+		return -EINVAL;
+	}
+
+	if ((value < 0) || (value > 10000)) {
+		mutex_unlock(&dev->mutex);
+		return -EINVAL;
+	}
+
+	calc_timer_value(value);
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->polling_interval > 0)
+			c->polling_count_reload = ((c->polling_interval * sample_rate) - 1) / 1000;
+		else
+			c->polling_count_reload = 0;
+	}
+
+	mutex_unlock(&dev->mutex);
+
+	return count;
+}
+
+static ssize_t run_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int i;
+
+	mutex_lock(&dev->mutex);
+	i = snprintf(buf, PAGE_SIZE, "%d\n", run);
+	mutex_unlock(&dev->mutex);
+	return i;
+}
+
+static ssize_t run_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			 size_t count)
+{
+	int value;
+
+	mutex_lock(&dev->mutex);
+
+	if ((count == 0) || (buf == NULL)) {
+		mutex_unlock(&dev->mutex);
+		return -EINVAL;
+	}
+	if (kstrtoint(buf, 0, &value) != 0) {
+		mutex_unlock(&dev->mutex);
+		return -EINVAL;
+	}
+
+	switch (value) {
+	case 1:
+		if (run != 1) {
+			run = 1;
+			met_run();
+		}
+		break;
+	case 0:
+		if (run != 0) {
+			if (run == 1) {
+				met_stop();
+#ifdef MET_USER_EVENT_SUPPORT
+#ifdef CONFIG_MET_MODULE
+				met_save_dump_buffer_real("/data/trace.dump");
+#else
+				met_save_dump_buffer("/data/trace.dump");
+#endif
+#endif
+				run = 0;
+			} else
+				/* run == -1 */
+				run = 0;
+		}
+		break;
+	case -1:
+		if (run != -1) {
+			if (run == 1)
+				met_stop();
+
+			run = -1;
+		}
+		break;
+	default:
+		mutex_unlock(&dev->mutex);
+		return -EINVAL;
+	}
+
+	mutex_unlock(&dev->mutex);
+
+	return count;
+}
+
+static unsigned int met_ksym_addr;
+static char met_func_name[512];
+static ssize_t ksym_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int i;
+	int len = 0;
+	int idx = 0;
+
+	mutex_lock(&dev->mutex);
+	if (met_ksym_addr != 0)
+		len = sprint_symbol_no_offset(met_func_name, met_ksym_addr);
+	if (len != 0) {
+		for (idx = 0; idx < 512; idx++)
+			if (met_func_name[idx] == ' ')
+				met_func_name[idx] = '\0';
+		i = snprintf(buf, PAGE_SIZE, "%s\n", met_func_name);
+	} else
+		i = snprintf(buf, PAGE_SIZE, "ksymlookup fail(%x)\n", met_ksym_addr);
+
+	mutex_unlock(&dev->mutex);
+	return i;
+}
+
+static ssize_t ksym_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			 size_t count)
+{
+	mutex_lock(&dev->mutex);
+
+	if ((count == 0) || (buf == NULL)) {
+		mutex_unlock(&dev->mutex);
+		return -EINVAL;
+	}
+	if (kstrtoint(buf, 16, &met_ksym_addr) != 0) {
+		mutex_unlock(&dev->mutex);
+		return -EINVAL;
+	}
+
+	mutex_unlock(&dev->mutex);
+
+	return count;
+}
+
+#if	defined(PR_CPU_NOTIFY)
+static ssize_t cpu_notify_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int i;
+
+	i = snprintf(buf, PAGE_SIZE, "%d\n", met_cpu_notify);
+	return i;
+}
+
+static ssize_t cpu_notify_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			  size_t count)
+{
+	if ((count == 0) || (buf == NULL))
+		return -EINVAL;
+
+	if (kstrtoint(buf, 0, &met_cpu_notify) != 0)
+		return -EINVAL;
+
+	return count;
+}
+#endif
+
+#ifdef CONFIG_CPU_FREQ
+static ssize_t dvfs_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int i;
+
+	i = snprintf(buf, PAGE_SIZE, "%d\n", 0);
+	return i;
+}
+
+static ssize_t dvfs_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			  size_t count)
+{
+	return count;
+}
+#endif
+
+static ssize_t suspend_compensation_enable_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int ret;
+
+	ret = snprintf(buf, PAGE_SIZE, "%d\n", met_suspend_compensation_mode);
+
+	return ret;
+}
+
+static ssize_t suspend_compensation_enable_store(struct device *dev, struct device_attribute *attr,
+			const char *buf, size_t count)
+{
+	int value;
+
+	if ((count == 0) || (buf == NULL))
+		return -EINVAL;
+
+	if (kstrtoint(buf, 0, &value) != 0)
+		return -EINVAL;
+
+	if (value < 0)
+		return -EINVAL;
+
+	met_suspend_compensation_mode = value;
+
+	return count;
+}
+
+static ssize_t suspend_compensation_flag_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int ret;
+
+	ret = snprintf(buf, PAGE_SIZE, "%d\n", met_suspend_compensation_flag);
+
+	return ret;
+}
+
+static ssize_t hash_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	return 0;
+}
+
+static ssize_t hash_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			 size_t count)
+{
+	return 0;
+}
+
+static DEVICE_ATTR(hash, 0664, hash_show, hash_store);
+
+static ssize_t mode_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	struct metdevice *c = NULL;
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->kobj == kobj)
+			break;
+	}
+	if (c == NULL)
+		return -ENOENT;
+
+	return snprintf(buf, PAGE_SIZE, "%d\n", c->mode);
+}
+
+static ssize_t mode_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf,
+			  size_t n)
+{
+	struct metdevice *c = NULL;
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->kobj == kobj)
+			break;
+	}
+	if (c == NULL)
+		return -ENOENT;
+
+	if (kstrtoint(buf, 0, &(c->mode)) != 0)
+		return -EINVAL;
+
+	return n;
+}
+
+static struct kobj_attribute mode_attr = __ATTR(mode, 0664, mode_show, mode_store);
+
+static ssize_t ondiemet_mode_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	struct metdevice *c = NULL;
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->kobj == kobj)
+			break;
+	}
+	if (c == NULL)
+		return -ENOENT;
+
+	return snprintf(buf, PAGE_SIZE, "%d\n", c->ondiemet_mode);
+}
+
+static ssize_t ondiemet_mode_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf,
+			  size_t n)
+{
+	struct metdevice *c = NULL;
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->kobj == kobj)
+			break;
+	}
+	if (c == NULL)
+		return -ENOENT;
+
+	if (kstrtoint(buf, 0, &(c->ondiemet_mode)) != 0)
+		return -EINVAL;
+
+	return n;
+}
+
+static struct kobj_attribute ondiemet_mode_attr = __ATTR(ondiemet_mode, 0664, ondiemet_mode_show, ondiemet_mode_store);
+
+static ssize_t polling_interval_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	int interval = 1;
+	struct metdevice *c = NULL;
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->kobj == kobj)
+			break;
+	}
+	if (c == NULL)
+		return -ENOENT;
+
+	if (c->polling_interval)
+		interval = c->polling_interval;
+
+	return snprintf(buf, PAGE_SIZE, "%d\n", interval);
+}
+
+static ssize_t polling_interval_store(struct kobject *kobj, struct kobj_attribute *attr,
+				      const char *buf, size_t n)
+{
+	struct metdevice *c = NULL;
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->kobj == kobj)
+			break;
+	}
+	if (c == NULL)
+		return -ENOENT;
+
+	if (kstrtoint(buf, 0, &(c->polling_interval)) != 0)
+		return -EINVAL;
+
+	if (c->polling_interval > 0)
+		c->polling_count_reload = ((c->polling_interval * sample_rate) - 1) / 1000;
+	else
+		c->polling_count_reload = 0;
+
+	return n;
+}
+
+static struct kobj_attribute polling_interval_attr =
+__ATTR(polling_ms, 0664, polling_interval_show, polling_interval_store);
+
+static ssize_t header_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	struct metdevice *c = NULL;
+	ssize_t count = 0;
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->kobj == kobj)
+			break;
+	}
+	if (c == NULL)
+		return -ENOENT;
+
+	if (c->ondiemet_mode == 0) {
+		if ((c->mode) && (c->print_header))
+			return c->print_header(buf, PAGE_SIZE);
+	} else if (c->ondiemet_mode == 1) {
+		if ((c->mode) && (c->ondiemet_print_header))
+			return c->ondiemet_print_header(buf, PAGE_SIZE);
+	} else if (c->ondiemet_mode == 2) {
+		if ((c->mode) && (c->print_header))
+			count = c->print_header(buf, PAGE_SIZE);
+		if (count < PAGE_SIZE) {
+			if ((c->mode) && (c->ondiemet_print_header))
+				count += c->ondiemet_print_header(buf+count, PAGE_SIZE - count);
+		}
+		return count;
+	}
+
+	return 0;
+}
+
+static struct kobj_attribute header_attr = __ATTR(header, 0444, header_show, NULL);
+
+static ssize_t help_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	struct metdevice *c = NULL;
+	ssize_t count = 0;
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->kobj == kobj)
+			break;
+	}
+	if (c == NULL)
+		return -ENOENT;
+
+	if (c->ondiemet_mode == 0) {
+		if (c->print_help)
+			return c->print_help(buf, PAGE_SIZE);
+	} else if (c->ondiemet_mode == 1) {
+		if (c->ondiemet_print_help)
+			return c->ondiemet_print_help(buf, PAGE_SIZE);
+	} else if (c->ondiemet_mode == 2) {
+		if (c->print_help)
+			count = c->print_help(buf, PAGE_SIZE);
+		if (count < PAGE_SIZE) {
+			if (c->ondiemet_print_help)
+				count += c->ondiemet_print_help(buf+count, PAGE_SIZE - count);
+		}
+		return count;
+	}
+
+	return 0;
+}
+
+static struct kobj_attribute help_attr = __ATTR(help, 0444, help_show, NULL);
+
+static int argu_status = -1;
+static ssize_t argu_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf,
+			  size_t n)
+{
+	int ret = 0;
+	struct metdevice *c = NULL;
+
+	argu_status = -1;
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->kobj == kobj)
+			break;
+	}
+	if (c == NULL)
+		return -ENOENT;
+
+	if (c->ondiemet_mode == 0) {
+		if (c->process_argument)
+			ret = c->process_argument(buf, (int)n);
+	} else if (c->ondiemet_mode == 1) {
+		if (c->ondiemet_process_argument)
+			ret = c->ondiemet_process_argument(buf, (int)n);
+	} else if (c->ondiemet_mode == 2) {
+		if (c->process_argument)
+			ret = c->process_argument(buf, (int)n);
+		if (c->ondiemet_process_argument)
+			ret = c->ondiemet_process_argument(buf, (int)n);
+	}
+
+	if (ret != 0)
+		return -EINVAL;
+
+	argu_status = 0;
+	return n;
+}
+
+static ssize_t argu_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%d\n", argu_status);
+}
+
+static struct kobj_attribute argu_attr = __ATTR(argu, 0664, argu_show, argu_store);
+
+static ssize_t reset_store(struct kobject *kobj,
+			struct kobj_attribute *attr,
+			const char *buf,
+			size_t n)
+{
+	int ret = 0;
+	struct metdevice *c = NULL;
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->kobj == kobj)
+			break;
+	}
+	if (c == NULL)
+		return -ENOENT;
+
+	if (c->ondiemet_mode == 0) {
+		if (c->reset)
+			ret = c->reset();
+		else
+			c->mode = 0;
+	} else if (c->ondiemet_mode == 1) {
+		if (c->ondiemet_reset)
+			ret = c->ondiemet_reset();
+	} else if (c->ondiemet_mode == 2) {
+		if (c->reset)
+			ret = c->reset();
+		else
+			c->mode = 0;
+		if (c->ondiemet_reset)
+			ret = c->ondiemet_reset();
+	}
+
+	if (ret != 0)
+		return -EINVAL;
+
+	return n;
+}
+
+static struct kobj_attribute reset_attr = __ATTR(reset, 0220, NULL, reset_store);
+
+static ssize_t header_read_again_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	struct metdevice *c = NULL;
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->kobj == kobj)
+			break;
+	}
+	if (c == NULL)
+		return -ENOENT;
+
+	return snprintf(buf, PAGE_SIZE, "%d\n", c->header_read_again);
+}
+
+static struct kobj_attribute header_read_again_attr = __ATTR(header_read_again, 0664, header_read_again_show, NULL);
+
+
+int met_register(struct metdevice *met)
+{
+	int ret, cpu;
+	struct metdevice *c;
+
+	list_for_each_entry(c, &met_list, list) {
+		if (!strcmp(c->name, met->name))
+			return -EEXIST;
+	}
+
+	PR_BOOTMSG("met_register %s ...\n", met->name);
+
+	INIT_LIST_HEAD(&met->list);
+
+	/* Allocate timer count for per CPU */
+	met->polling_count = alloc_percpu(int);
+	if (met->polling_count == NULL)
+		return -EINVAL;
+
+	for_each_possible_cpu(cpu)
+		*(per_cpu_ptr(met->polling_count, cpu)) = 0;
+
+	if (met->polling_interval > 0) {
+		ret = ((met->polling_interval * sample_rate) - 1) / 1000;
+		met->polling_count_reload = ret;
+	} else
+		met->polling_count_reload = 0;
+
+	met->kobj = NULL;
+
+	if (met->type == MET_TYPE_BUS)
+		met->kobj = kobject_create_and_add(met->name, kobj_bus);
+	else if (met->type == MET_TYPE_PMU)
+		met->kobj = kobject_create_and_add(met->name, kobj_pmu);
+	else if (met->type == MET_TYPE_MISC)
+		met->kobj = kobject_create_and_add(met->name, kobj_misc);
+	else {
+		ret = -EINVAL;
+		goto err_out;
+	}
+
+	if (met->kobj == NULL) {
+		ret = -EINVAL;
+		goto err_out;
+	}
+
+	if (met->create_subfs) {
+		ret = met->create_subfs(met->kobj);
+		if (ret)
+			goto err_out;
+	}
+
+	ret = sysfs_create_file(met->kobj, &mode_attr.attr);
+	if (ret)
+		goto err_out;
+
+
+	ret = sysfs_create_file(met->kobj, &ondiemet_mode_attr.attr);
+	if (ret)
+		goto err_out;
+
+	ret = sysfs_create_file(met->kobj, &polling_interval_attr.attr);
+	if (ret)
+		goto err_out;
+
+	ret = sysfs_create_file(met->kobj, &header_read_again_attr.attr);
+	if (ret)
+		goto err_out;
+
+	if (met->print_header || met->ondiemet_print_header) {
+		ret = sysfs_create_file(met->kobj, &header_attr.attr);
+		if (ret)
+			goto err_out;
+	}
+
+	if (met->print_help || met->ondiemet_print_help) {
+		ret = sysfs_create_file(met->kobj, &help_attr.attr);
+		if (ret)
+			goto err_out;
+	}
+
+	if (met->process_argument || met->ondiemet_process_argument) {
+		ret = sysfs_create_file(met->kobj, &argu_attr.attr);
+		if (ret)
+			goto err_out;
+	}
+
+	if (met->reset) {
+		ret = sysfs_create_file(met->kobj, &reset_attr.attr);
+		if (ret)
+			goto err_out;
+	}
+
+	spin_lock_init(&met->my_lock);
+
+	list_add(&met->list, &met_list);
+	return 0;
+
+ err_out:
+
+	if (met->polling_count)
+		free_percpu(met->polling_count);
+
+	if (met->kobj) {
+		kobject_del(met->kobj);
+		kobject_put(met->kobj);
+		met->kobj = NULL;
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL(met_register);
+
+int met_deregister(struct metdevice *met)
+{
+	struct metdevice *c = NULL;
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c == met)
+			break;
+	}
+	if (c != met)
+		return -ENOENT;
+
+	if (met->print_header || met->ondiemet_print_header)
+		sysfs_remove_file(met->kobj, &header_attr.attr);
+
+	if (met->print_help || met->ondiemet_print_help)
+		sysfs_remove_file(met->kobj, &help_attr.attr);
+
+	if (met->process_argument || met->ondiemet_process_argument)
+		sysfs_remove_file(met->kobj, &argu_attr.attr);
+
+	sysfs_remove_file(met->kobj, &reset_attr.attr);
+	sysfs_remove_file(met->kobj, &header_read_again_attr.attr);
+	sysfs_remove_file(met->kobj, &polling_interval_attr.attr);
+	sysfs_remove_file(met->kobj, &mode_attr.attr);
+	sysfs_remove_file(met->kobj, &ondiemet_mode_attr.attr);
+
+	if (met->delete_subfs)
+		met->delete_subfs();
+
+	kobject_del(met->kobj);
+	kobject_put(met->kobj);
+	met->kobj = NULL;
+
+	if (met->polling_count)
+		free_percpu(met->polling_count);
+
+	list_del(&met->list);
+	return 0;
+}
+EXPORT_SYMBOL(met_deregister);
+
+int met_set_platform(const char *plf_name, int flag)
+{
+	strncpy(met_platform, plf_name, sizeof(met_platform) - 1);
+#if 0
+	int ret;
+
+	if (flag) {
+		ret = device_create_file(met_device.this_device, &dev_attr_plf);
+		if (ret != 0) {
+			pr_debug("can not create device file: plf\n");
+			return ret;
+		}
+		strncpy(met_platform, plf_name, sizeof(met_platform) - 1);
+	} else
+		device_remove_file(met_device.this_device, &dev_attr_plf);
+
+#endif
+	return 0;
+}
+EXPORT_SYMBOL(met_set_platform);
+
+char *met_get_platform(void)
+{
+	return met_platform;
+}
+EXPORT_SYMBOL(met_get_platform);
+
+int met_set_chip_id(const unsigned int chip_id)
+{
+	met_chip_id = chip_id;
+
+	return 0;
+}
+EXPORT_SYMBOL(met_set_chip_id);
+
+const unsigned int met_get_chip_id(void)
+{
+	return met_chip_id;
+}
+EXPORT_SYMBOL(met_get_chip_id);
+
+int met_set_topology(const char *topology_name, int flag)
+{
+	strncpy(met_topology, topology_name, sizeof(met_topology) - 1);
+#if 0
+	int ret;
+
+	if (flag) {
+		ret = device_create_file(met_device.this_device, &dev_attr_core_topology);
+		if (ret != 0) {
+			pr_debug("can not create device file: topology\n");
+			return ret;
+		}
+		strncpy(met_topology, topology_name, sizeof(met_topology) - 1);
+	} else {
+		device_remove_file(met_device.this_device, &dev_attr_core_topology);
+	}
+#endif
+	return 0;
+}
+EXPORT_SYMBOL(met_set_topology);
+
+#include "met_struct.h"
+
+void force_sample(void *unused)
+{
+	int cpu;
+	unsigned long long stamp;
+	struct metdevice *c;
+	struct met_cpu_struct *met_cpu_ptr;
+
+	if ((run != 1) || (sample_rate == 0))
+		return;
+
+	/* to avoid met tag is coming after __met_hrtimer_stop and before run=-1 */
+	met_cpu_ptr = this_cpu_ptr(&met_cpu);
+	if (met_cpu_ptr->work_enabled == 0)
+		return;
+
+	cpu = smp_processor_id();
+
+	stamp = cpu_clock(cpu);
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->ondiemet_mode == 0) {
+			if ((c->mode != 0) && (c->tagged_polling != NULL))
+				c->tagged_polling(stamp, 0);
+		} else if (c->ondiemet_mode == 1) {
+			if ((c->mode != 0) && (c->ondiemet_tagged_polling != NULL))
+				c->ondiemet_tagged_polling(stamp, 0);
+		} else if (c->ondiemet_mode == 2) {
+			if ((c->mode != 0) && (c->tagged_polling != NULL))
+				c->tagged_polling(stamp, 0);
+			if ((c->mode != 0) && (c->ondiemet_tagged_polling != NULL))
+				c->ondiemet_tagged_polling(stamp, 0);
+		}
+	}
+}
+
+#define MET_SUSPEND_HAND
+#ifdef MET_SUSPEND_HAND
+static struct syscore_ops met_hrtimer_ops = {
+	.suspend = met_hrtimer_suspend,
+	.resume = met_hrtimer_resume,
+};
+#endif
+
+int fs_reg(int met_minor)
+{
+	int ret = -1;
+
+	ctrl_flags = 0;
+	met_mode = 0;
+
+#ifdef MET_SUSPEND_HAND
+	/* suspend/resume function handle register */
+	register_syscore_ops(&met_hrtimer_ops);
+#endif
+
+	calc_timer_value(sample_rate);
+
+	if ( met_minor != -1)
+		met_device.minor = met_minor;
+	ret = misc_register(&met_device);
+	if (ret != 0) {
+		pr_debug("misc register failed, minor = %d \n", met_device.minor);
+		return ret;
+	}
+
+	/* dma map config */
+	/* arch_setup_dma_ops(met_device.this_device, 0, 0, NULL, false); */
+	met_arch_setup_dma_ops_symbol(met_device.this_device);
+
+	ret = device_create_file(met_device.this_device, &dev_attr_ksym);
+	if (ret != 0) {
+		pr_debug("can not create device file: ksym\n");
+		return ret;
+	}
+
+	ret = device_create_file(met_device.this_device, &dev_attr_run);
+	if (ret != 0) {
+		pr_debug("can not create device file: run\n");
+		return ret;
+	}
+
+#if	defined(PR_CPU_NOTIFY)
+	ret = device_create_file(met_device.this_device, &dev_attr_cpu_notify);
+	if (ret != 0) {
+		pr_debug("can not create device file: cpu_notify\n");
+		return ret;
+	}
+#endif
+
+#ifdef CONFIG_CPU_FREQ
+	ret = device_create_file(met_device.this_device, &dev_attr_dvfs);
+	if (ret != 0) {
+		pr_debug("can not create device file: dvfs\n");
+		return ret;
+	}
+#endif
+
+	ret = device_create_file(met_device.this_device, &dev_attr_suspend_compensation_enable);
+	if (ret != 0) {
+		pr_debug("can not create device file: suspend_compensation_enable\n");
+		return ret;
+	}
+
+	ret = device_create_file(met_device.this_device, &dev_attr_suspend_compensation_flag);
+	if (ret != 0) {
+		pr_debug("can not create device file: suspend_compensation_enable\n");
+		return ret;
+	}
+
+	ret = device_create_file(met_device.this_device, &dev_attr_ver);
+	if (ret != 0) {
+		pr_debug("can not create device file: ver\n");
+		return ret;
+	}
+
+	ret = device_create_file(met_device.this_device, &dev_attr_devices);
+	if (ret != 0) {
+		pr_debug("can not create device file: devices\n");
+		return ret;
+	}
+
+	ret = device_create_file(met_device.this_device, &dev_attr_ctrl);
+	if (ret != 0) {
+		pr_debug("can not create device file: ctrl\n");
+		return ret;
+	}
+
+	ret = device_create_file(met_device.this_device, &dev_attr_cpu_pmu_method);
+	if (ret != 0) {
+		pr_debug("can not create device file: cpu_pmu_method\n");
+		return ret;
+	}
+
+	ret = device_create_file(met_device.this_device, &dev_attr_cpu_pm_pmu_reconfig);
+	if (ret != 0) {
+		pr_debug("can not create device file: cpu_pm_pmu_reconfig\n");
+		return ret;
+	}
+
+#if	defined(MET_BOOT_MSG)
+	ret = device_create_file(met_device.this_device, &dev_attr_bootmsg);
+	if (ret != 0) {
+		pr_debug("can not create device file: bootmsg\n");
+		return ret;
+	}
+#endif
+
+	ret = device_create_file(met_device.this_device, &dev_attr_sample_rate);
+	if (ret != 0) {
+		pr_debug("can not create device file: sample_rate\n");
+		return ret;
+	}
+
+	ret = device_create_file(met_device.this_device, &dev_attr_core_topology);
+	if (ret != 0) {
+		pr_debug("can not create device file: topology\n");
+		return ret;
+	}
+
+	ret = device_create_file(met_device.this_device, &dev_attr_plf);
+	if (ret != 0) {
+		pr_debug("can not create device file: plf\n");
+		return ret;
+	}
+
+	ret = device_create_file(met_device.this_device, &dev_attr_chip_id);
+	if (ret != 0) {
+		pr_debug("can not create device file: chip_id\n");
+		return ret;
+	}
+
+	ret = device_create_file(met_device.this_device, &dev_attr_hash);
+	if (ret != 0) {
+		pr_debug("can not create device file: hash\n");
+		return ret;
+	}
+
+	ret = device_create_file(met_device.this_device, &dev_attr_ipi_test);
+	if (ret != 0) {
+		pr_debug("can not create device file: ipi_test\n");
+		return ret;
+	}
+
+	kobj_misc = kobject_create_and_add("misc", &met_device.this_device->kobj);
+	if (kobj_misc == NULL) {
+		pr_debug("can not create kobject: kobj_misc\n");
+		return -1;
+	}
+
+	kobj_pmu = kobject_create_and_add("pmu", &met_device.this_device->kobj);
+	if (kobj_pmu == NULL) {
+		pr_debug("can not create kobject: kobj_pmu\n");
+		return -1;
+	}
+
+	kobj_bus = kobject_create_and_add("bus", &met_device.this_device->kobj);
+	if (kobj_bus == NULL) {
+		pr_debug("can not create kobject: kobj_bus\n");
+		return -1;
+	}
+
+	met_register(&met_cookie);
+	met_register(&met_cpupmu);
+	met_register(&met_memstat);
+	met_register(&met_switch);
+#ifdef MET_EVENT_POWER_SUPPORT
+	met_register(&met_trace_event);
+	met_ext_api.met_pm_qos_update_request = pm_qos_update_request;
+	met_ext_api.met_pm_qos_update_target = pm_qos_update_target;
+#endif
+
+	met_register(&met_dummy_header);
+
+	met_register(&met_backlight);
+	met_ext_api.enable_met_backlight_tag = enable_met_backlight_tag_real;
+	met_ext_api.output_met_backlight_tag = output_met_backlight_tag_real;
+
+#ifdef MET_USER_EVENT_SUPPORT
+	tag_reg((struct file_operations * const) met_device.fops, &met_device.this_device->kobj);
+#endif
+
+	met_register(&met_stat);
+
+	ondiemet_log_manager_init(met_device.this_device);
+	ondiemet_attr_init(met_device.this_device);
+
+	return 0;
+}
+
+void fs_unreg(void)
+{
+	if (run == 1)
+		met_stop();
+
+	run = -1;
+
+	met_deregister(&met_stat);
+
+#ifdef MET_USER_EVENT_SUPPORT
+	tag_unreg();
+#endif
+
+	met_deregister(&met_dummy_header);
+#ifdef MET_EVENT_POWER_SUPPORT
+	met_deregister(&met_trace_event);
+	met_ext_api.met_pm_qos_update_request = NULL;
+	met_ext_api.met_pm_qos_update_target = NULL;
+#endif
+	met_deregister(&met_switch);
+	met_deregister(&met_memstat);
+	met_deregister(&met_cpupmu);
+	met_deregister(&met_cookie);
+	met_deregister(&met_backlight);
+	met_ext_api.enable_met_backlight_tag = NULL;
+	met_ext_api.output_met_backlight_tag = NULL;
+
+	kobject_del(kobj_misc);
+	kobject_put(kobj_misc);
+	kobj_misc = NULL;
+	kobject_del(kobj_pmu);
+	kobject_put(kobj_pmu);
+	kobj_pmu = NULL;
+	kobject_del(kobj_bus);
+	kobject_put(kobj_bus);
+	kobj_bus = NULL;
+
+	device_remove_file(met_device.this_device, &dev_attr_ksym);
+
+	device_remove_file(met_device.this_device, &dev_attr_run);
+#ifdef PR_CPU_NOTIFY
+	device_remove_file(met_device.this_device, &dev_attr_cpu_notify);
+#endif
+#ifdef CONFIG_CPU_FREQ
+	device_remove_file(met_device.this_device, &dev_attr_dvfs);
+#endif
+	device_remove_file(met_device.this_device, &dev_attr_suspend_compensation_enable);
+	device_remove_file(met_device.this_device, &dev_attr_suspend_compensation_flag);
+
+	device_remove_file(met_device.this_device, &dev_attr_ver);
+	device_remove_file(met_device.this_device, &dev_attr_devices);
+	device_remove_file(met_device.this_device, &dev_attr_sample_rate);
+
+	device_remove_file(met_device.this_device, &dev_attr_ctrl);
+	device_remove_file(met_device.this_device, &dev_attr_cpu_pmu_method);
+	device_remove_file(met_device.this_device, &dev_attr_cpu_pm_pmu_reconfig);
+
+	device_remove_file(met_device.this_device, &dev_attr_core_topology);
+	device_remove_file(met_device.this_device, &dev_attr_plf);
+	device_remove_file(met_device.this_device, &dev_attr_chip_id);
+	device_remove_file(met_device.this_device, &dev_attr_hash);
+	device_remove_file(met_device.this_device, &dev_attr_ipi_test);
+
+	ondiemet_log_manager_uninit(met_device.this_device);
+	ondiemet_attr_uninit(met_device.this_device);
+
+	misc_deregister(&met_device);
+#ifdef MET_SUSPEND_HAND
+	/* suspend/resume function handle register */
+	unregister_syscore_ops(&met_hrtimer_ops);
+#endif
+}
+
+unsigned int get_ctrl_flags(void)
+{
+	return ctrl_flags;
+}
diff --git a/src/devtools/met-driver/4.19/common/interface.h b/src/devtools/met-driver/4.19/common/interface.h
new file mode 100644
index 0000000..216cae8
--- /dev/null
+++ b/src/devtools/met-driver/4.19/common/interface.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2019 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __INTERFACE_H__
+#define __INTERFACE_H__
+
+#include <linux/fs.h>
+
+#ifdef MET_USER_EVENT_SUPPORT
+extern int tag_reg(struct file_operations *const fops, struct kobject *kobj);
+extern int tag_unreg(void);
+#include "met_drv.h"
+#include "met_tag.h"
+extern struct bltable_t bltab;
+#endif
+
+extern struct metdevice met_stat;
+extern struct metdevice met_cpupmu;
+extern struct metdevice met_cookie;
+extern struct metdevice met_memstat;
+extern struct metdevice met_switch;
+extern struct metdevice met_trace_event;
+extern struct metdevice met_dummy_header;
+extern struct metdevice met_backlight;
+
+/* This variable will decide which method to access the CPU PMU counter */
+/*     0: access registers directly */
+/*     others: via Linux perf driver */
+extern unsigned int met_cpu_pmu_method;
+
+/*
+ * controls whether re-configuring pmu events after leaving cpu off state
+ */
+extern unsigned int met_cpu_pm_pmu_reconfig;
+
+extern int met_parse_num(const char *str, unsigned int *value, int len);
+extern void met_set_suspend_notify(int flag);
+
+#define	PR_CPU_NOTIFY
+#if	defined(PR_CPU_NOTIFY)
+extern int met_cpu_notify;
+#endif
+
+//#undef	MET_BOOT_MSG
+#define	MET_BOOT_MSG
+#if	defined(MET_BOOT_MSG)
+extern char met_boot_msg_tmp[256];
+extern int pr_bootmsg(int str_len, char *str);
+#define	PR_BOOTMSG(fmt, args...) { \
+	int str_len = snprintf(met_boot_msg_tmp, sizeof(met_boot_msg_tmp), \
+			       fmt, ##args); \
+	pr_bootmsg(str_len, met_boot_msg_tmp); }
+#define	PR_BOOTMSG_ONCE(fmt, args...) { \
+	static int once; \
+	if (!once) { \
+		int str_len = snprintf(met_boot_msg_tmp, \
+				       sizeof(met_boot_msg_tmp), \
+				       fmt, ##args); \
+		pr_bootmsg(str_len, met_boot_msg_tmp); \
+		once = 1; \
+	} }
+#else
+#define	pr_bootmsg(str_len, str)
+#define PR_BOOTMSG(fmt, args...)
+#define	PR_BOOTMSG_ONCE(fmt, args...)
+#endif
+
+#endif	/* __INTERFACE_H__ */
diff --git a/src/devtools/met-driver/4.19/common/mem_stat.c b/src/devtools/met-driver/4.19/common/mem_stat.c
new file mode 100644
index 0000000..4fed966
--- /dev/null
+++ b/src/devtools/met-driver/4.19/common/mem_stat.c
@@ -0,0 +1,313 @@
+/*
+ * Copyright (C) 2019 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/cpu.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+
+#include "met_drv.h"
+#include "mem_stat.h"
+#include "trace.h"
+
+
+/* define MEMSTAT_DEBUG */
+#ifdef MEMSTAT_DEBUG
+#define debug_memstat(fmt, arg...) pr_debug(fmt, ##arg)
+#else
+#define debug_memstat(fmt, arg...) do {} while (0)
+#endif
+
+struct metdevice met_memstat;
+
+unsigned int phy_memstat_mask;
+unsigned int vir_memstat_mask;
+
+#define MAX_PHY_MEMSTAT_EVENT_AMOUNT 6
+#define MAX_VIR_MEMSTAT_EVENT_AMOUNT 6
+
+struct mem_event phy_memstat_table[] = {
+	{FREE_MEM, "free_mem", "Free Memory"}
+};
+
+#define PHY_MEMSTAT_TABLE_SIZE (sizeof(phy_memstat_table) / sizeof(struct mem_event))
+
+struct mem_event vir_memstat_table[] = {
+	{FILE_PAGES, "file_pages", "File Pages"},
+	{FILE_DIRTY, "file_dirty", "FD APP->FS(KB)"},
+	{NUM_DIRTIED, "num_dirtied", "Num Dirtied"},
+	{WRITE_BACK, "write_back", "WB. FS->Block IO(KB)"},
+	{NUM_WRITTEN, "num_written", "Num Written"},
+	{PG_FAULT_CNT, "pg_fault_cnt", "Page Fault Count"}
+};
+
+#define VIR_MEMSTAT_TABLE_SIZE (sizeof(vir_memstat_table) / sizeof(struct mem_event))
+
+int vm_event_counters_enable;
+unsigned long *vm_status;
+static struct delayed_work dwork;
+
+noinline void memstat(unsigned int cnt, unsigned int *value)
+{
+	MET_GENERAL_PRINT(MET_TRACE, cnt, value);
+}
+
+static int get_phy_memstat(unsigned int *value)
+{
+	int i, cnt = 0;
+	struct sysinfo info;
+
+#define K(x) ((x) << (PAGE_SHIFT - 10))
+
+	si_meminfo(&info);
+
+	for (i = 0; i < MAX_PHY_MEMSTAT_EVENT_AMOUNT; i++) {
+		if (phy_memstat_mask & (1 << i)) {
+			switch (i) {
+			case FREE_MEM:
+				value[cnt] = K(info.freeram);
+				break;
+			}
+
+			cnt++;
+		}
+	}
+
+	return cnt;
+}
+
+static int get_vir_memstat(unsigned int *value)
+{
+	int i, cnt = 0;
+
+	for (i = 0; i < NR_VM_ZONE_STAT_ITEMS; i++)
+		vm_status[i] = global_zone_page_state(i);
+
+	all_vm_events(vm_status + NR_VM_ZONE_STAT_ITEMS);
+
+	for (i = 0; i < MAX_VIR_MEMSTAT_EVENT_AMOUNT; i++) {
+		if (vir_memstat_mask & (1 << i)) {
+			switch (i) {
+			case FILE_PAGES:
+				value[cnt] = vm_status[NR_FILE_PAGES] << (PAGE_SHIFT - 10);
+				break;
+			case FILE_DIRTY:
+				value[cnt] = vm_status[NR_FILE_DIRTY] << (PAGE_SHIFT - 10);
+				break;
+			case NUM_DIRTIED:
+				value[cnt] = vm_status[NR_DIRTIED] << (PAGE_SHIFT - 10);
+				break;
+			case WRITE_BACK:
+				value[cnt] = vm_status[NR_WRITEBACK] << (PAGE_SHIFT - 10);
+				break;
+			case NUM_WRITTEN:
+				value[cnt] = vm_status[NR_WRITTEN] << (PAGE_SHIFT - 10);
+				break;
+			case PG_FAULT_CNT:
+				value[cnt] = vm_status[NR_VM_ZONE_STAT_ITEMS + PGFAULT];
+				break;
+			}
+
+			cnt++;
+		}
+	}
+
+	return cnt;
+}
+
+static void wq_get_memstat(struct work_struct *work)
+{
+	int total_event_amount = 0, phy_event_amount = 0;
+	unsigned int stat_val[MAX_PHY_MEMSTAT_EVENT_AMOUNT + MAX_VIR_MEMSTAT_EVENT_AMOUNT];
+
+	memset(stat_val, 0, sizeof(unsigned int) * (MAX_PHY_MEMSTAT_EVENT_AMOUNT + MAX_VIR_MEMSTAT_EVENT_AMOUNT));
+	total_event_amount = phy_event_amount = get_phy_memstat(stat_val);
+
+	if (vm_event_counters_enable)
+		total_event_amount += get_vir_memstat(&(stat_val[phy_event_amount]));
+
+	if (total_event_amount <= (MAX_PHY_MEMSTAT_EVENT_AMOUNT + MAX_VIR_MEMSTAT_EVENT_AMOUNT))
+		memstat(total_event_amount-1, stat_val);
+}
+
+void met_memstat_polling(unsigned long long stamp, int cpu)
+{
+	schedule_delayed_work(&dwork, 0);
+}
+
+static void met_memstat_start(void)
+{
+	int stat_items_size = 0;
+
+	stat_items_size = NR_VM_ZONE_STAT_ITEMS * sizeof(unsigned long);
+
+#ifdef CONFIG_VM_EVENT_COUNTERS
+	stat_items_size += sizeof(struct vm_event_state);
+#endif
+
+	vm_status = kmalloc(stat_items_size, GFP_KERNEL);
+	if (vm_status == NULL)
+		return;
+	INIT_DELAYED_WORK(&dwork, wq_get_memstat);
+}
+
+static void met_memstat_stop(void)
+{
+	kfree(vm_status);
+	cancel_delayed_work_sync(&dwork);
+}
+
+static const char help[] =
+"  --memstat=[phy_mem_stat|vir_mem_stat]:event_name enable sampling physical & virtual memory status\n";
+
+static int met_memstat_print_help(char *buf, int len)
+{
+	int i, l;
+
+	l = snprintf(buf, PAGE_SIZE, help);
+
+	for (i = 0; i < PHY_MEMSTAT_TABLE_SIZE; i++)
+		l += snprintf(buf + l, PAGE_SIZE - l, "  --memstat=phy_mem_stat:%s\n",
+			      phy_memstat_table[i].name);
+
+#ifdef CONFIG_VM_EVENT_COUNTERS
+	for (i = 0; i < VIR_MEMSTAT_TABLE_SIZE; i++)
+		l += snprintf(buf + l, PAGE_SIZE - l, "  --memstat=vir_mem_stat:%s\n",
+			      vir_memstat_table[i].name);
+#endif
+
+	return l;
+}
+
+static const char header[] = "met-info [000] 0.0: ms_ud_sys_header: memstat,";
+
+
+static int met_memstat_print_header(char *buf, int len)
+{
+	int i, l;
+	int event_amount = 0;
+
+	l = snprintf(buf, PAGE_SIZE, header);
+
+	for (i = 0; i < MAX_PHY_MEMSTAT_EVENT_AMOUNT; i++) {
+		if ((phy_memstat_mask & (1 << i)) && (i < PHY_MEMSTAT_TABLE_SIZE)) {
+			l += snprintf(buf + l, PAGE_SIZE - l, phy_memstat_table[i].header_name);
+			l += snprintf(buf + l, PAGE_SIZE - l, ",");
+			event_amount++;
+		}
+	}
+
+#ifdef CONFIG_VM_EVENT_COUNTERS
+	for (i = 0; i < MAX_VIR_MEMSTAT_EVENT_AMOUNT; i++) {
+		if ((vir_memstat_mask & (1 << i)) && (i < VIR_MEMSTAT_TABLE_SIZE)) {
+			l += snprintf(buf + l, PAGE_SIZE - l, vir_memstat_table[i].header_name);
+			l += snprintf(buf + l, PAGE_SIZE - l, ",");
+			event_amount++;
+		}
+	}
+#endif
+
+	for (i = 0; i < event_amount; i++) {
+		l += snprintf(buf + l, PAGE_SIZE - l, "x");
+		l += snprintf(buf + l, PAGE_SIZE - l, ",");
+	}
+
+	phy_memstat_mask = 0;
+	vir_memstat_mask = 0;
+
+	l += snprintf(buf + l, PAGE_SIZE - l, "\n");
+
+	return l;
+}
+
+static int met_memstat_process_argument(const char *arg, int len)
+{
+	int i, found_event = 0;
+	char choice[16], event[32];
+	char *pch;
+	int str_len;
+
+
+#ifdef CONFIG_VM_EVENT_COUNTERS
+	vm_event_counters_enable = 1;
+#endif
+
+	pch = strchr(arg, ':');
+	if (pch == NULL)
+		goto error;
+
+	memset(choice, 0, sizeof(choice));
+	memset(event, 0, sizeof(event));
+
+	str_len = (int)(pch - arg);
+	memcpy(choice, arg, str_len);
+	memcpy(event, arg + str_len + 1, len - (str_len + 1));
+
+	if (strncmp(choice, "phy_mem_stat", 12) == 0) {
+		for (i = 0; i < PHY_MEMSTAT_TABLE_SIZE; i++) {
+			if (strncmp(event, phy_memstat_table[i].name, MAX_EVENT_NAME_LEN) == 0) {
+				phy_memstat_mask |= (1 << phy_memstat_table[i].id);
+				found_event = 1;
+
+				break;
+			}
+		}
+	} else if (strncmp(choice, "vir_mem_stat", 12) == 0) {
+		if (!vm_event_counters_enable) {
+			pr_debug("[%s] %d: CONFIG_VM_EVENT_COUNTERS is not configured\n", __func__,
+				 __LINE__);
+			goto error;
+		}
+
+		for (i = 0; i < VIR_MEMSTAT_TABLE_SIZE; i++) {
+			if (strncmp(event, vir_memstat_table[i].name, MAX_EVENT_NAME_LEN) == 0) {
+				vir_memstat_mask |= (1 << vir_memstat_table[i].id);
+				found_event = 1;
+
+				break;
+			}
+		}
+	} else {
+		pr_debug("[%s] %d: only support phy_mem_stat & vir_mem_stat keyword\n", __func__,
+			 __LINE__);
+		goto error;
+	}
+
+	if (!found_event) {
+		pr_debug("[%s] %d: input event name error\n", __func__, __LINE__);
+		goto error;
+	}
+
+	met_memstat.mode = 1;
+	return 0;
+
+error:
+	met_memstat.mode = 0;
+	return -EINVAL;
+}
+
+struct metdevice met_memstat = {
+	.name = "memstat",
+	.type = MET_TYPE_PMU,
+	.cpu_related = 0,
+	.start = met_memstat_start,
+	.stop = met_memstat_stop,
+	.polling_interval = 1,
+	.timed_polling = met_memstat_polling,
+	.tagged_polling = met_memstat_polling,
+	.print_help = met_memstat_print_help,
+	.print_header = met_memstat_print_header,
+	.process_argument = met_memstat_process_argument
+};
diff --git a/src/devtools/met-driver/4.19/common/mem_stat.h b/src/devtools/met-driver/4.19/common/mem_stat.h
new file mode 100644
index 0000000..6c88619
--- /dev/null
+++ b/src/devtools/met-driver/4.19/common/mem_stat.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2019 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __MEM_STAT_H__
+#define __MEM_STAT_H__
+
+#define MAX_EVENT_NAME_LEN 32
+
+enum phy_mem_event_id {
+	FREE_MEM = 0
+};
+
+enum vir_mem_event_id {
+	FILE_PAGES = 0,
+	FILE_DIRTY,
+	NUM_DIRTIED,
+	WRITE_BACK,
+	NUM_WRITTEN,
+	PG_FAULT_CNT
+};
+
+struct mem_event {
+	int id;
+	char name[32];
+	char header_name[32];
+};
+
+#endif
diff --git a/src/devtools/met-driver/4.19/common/met_api_tbl.h b/src/devtools/met-driver/4.19/common/met_api_tbl.h
new file mode 100644
index 0000000..432dfaf
--- /dev/null
+++ b/src/devtools/met-driver/4.19/common/met_api_tbl.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2019 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+struct met_api_tbl {
+	int (*met_tag_start)(unsigned int class_id, const char *name);
+	int (*met_tag_end)(unsigned int class_id, const char *name);
+	int (*met_tag_async_start)(unsigned int class_id, const char *name, unsigned int cookie);
+	int (*met_tag_async_end)(unsigned int class_id, const char *name, unsigned int cookie);
+	int (*met_tag_oneshot)(unsigned int class_id, const char *name, unsigned int value);
+	int (*met_tag_userdata)(char *pData);
+	int (*met_tag_dump)(unsigned int class_id, const char *name, void *data, unsigned int length);
+	int (*met_tag_disable)(unsigned int class_id);
+	int (*met_tag_enable)(unsigned int class_id);
+	int (*met_set_dump_buffer)(int size);
+	int (*met_save_dump_buffer)(const char *pathname);
+	int (*met_save_log)(const char *pathname);
+	int (*met_show_bw_limiter)(void);
+	int (*met_reg_bw_limiter)(void *fp);
+	int (*met_show_clk_tree)(const char *name, unsigned int addr, unsigned int status);
+	int (*met_reg_clk_tree)(void *fp);
+	void (*met_sched_switch)(struct task_struct *prev, struct task_struct *next);
+	void (*met_pm_qos_update_request)(int pm_qos_class, s32 value, char *owner);
+	void (*met_pm_qos_update_target)(unsigned int action, int prev_value, int curr_value);
+	int (*enable_met_backlight_tag)(void);
+	int (*output_met_backlight_tag)(int level);
+};
+
+extern struct met_api_tbl met_ext_api;
diff --git a/src/devtools/met-driver/4.19/common/met_backlight.c b/src/devtools/met-driver/4.19/common/met_backlight.c
new file mode 100644
index 0000000..aab5a85
--- /dev/null
+++ b/src/devtools/met-driver/4.19/common/met_backlight.c
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2019 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+#define MET_USER_EVENT_SUPPORT
+#include "met_drv.h"
+#include "trace.h"
+
+static int met_backlight_enable;
+static DEFINE_SPINLOCK(met_backlight_lock);
+static struct kobject *kobj_met_backlight;
+
+static ssize_t bl_tag_enable_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf);
+static ssize_t bl_tag_enable_store(struct kobject *kobj,
+				   struct kobj_attribute *attr, const char *buf, size_t n);
+static struct kobj_attribute bl_tag_enable_attr =
+__ATTR(backlight_tag_enable, 0664, bl_tag_enable_show, bl_tag_enable_store);
+
+int enable_met_backlight_tag_real(void)
+{
+	return met_backlight_enable;
+}
+
+int output_met_backlight_tag_real(int level)
+{
+	int ret;
+	unsigned long flags;
+
+	spin_lock_irqsave(&met_backlight_lock, flags);
+#ifdef CONFIG_MET_MODULE
+	ret = met_tag_oneshot_real(33880, "_MM_BL_", level);
+#else
+	ret = met_tag_oneshot(33880, "_MM_BL_", level);
+#endif
+	spin_unlock_irqrestore(&met_backlight_lock, flags);
+
+	return ret;
+}
+
+static ssize_t bl_tag_enable_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	int ret;
+
+	ret = snprintf(buf, PAGE_SIZE, "%d\n", met_backlight_enable);
+
+	return ret;
+}
+
+static ssize_t bl_tag_enable_store(struct kobject *kobj,
+				   struct kobj_attribute *attr, const char *buf, size_t n)
+{
+	int value;
+
+	if ((n == 0) || (buf == NULL))
+		return -EINVAL;
+
+	if (kstrtoint(buf, 0, &value) != 0)
+		return -EINVAL;
+
+	if (value < 0)
+		return -EINVAL;
+
+	met_backlight_enable = value;
+
+	return n;
+}
+
+static int met_backlight_create(struct kobject *parent)
+{
+	int ret = 0;
+
+	kobj_met_backlight = parent;
+
+	ret = sysfs_create_file(kobj_met_backlight, &bl_tag_enable_attr.attr);
+	if (ret != 0) {
+		pr_debug("Failed to create montype0 in sysfs\n");
+		return ret;
+	}
+
+	return ret;
+}
+
+static void met_backlight_delete(void)
+{
+	sysfs_remove_file(kobj_met_backlight, &bl_tag_enable_attr.attr);
+	kobj_met_backlight = NULL;
+}
+
+struct metdevice met_backlight = {
+	.name = "backlight",
+	.owner = THIS_MODULE,
+	.type = MET_TYPE_BUS,
+	.create_subfs = met_backlight_create,
+	.delete_subfs = met_backlight_delete,
+	.cpu_related = 0,
+};
+EXPORT_SYMBOL(met_backlight);
diff --git a/src/devtools/met-driver/4.19/common/met_drv.h b/src/devtools/met-driver/4.19/common/met_drv.h
new file mode 100644
index 0000000..91e2596
--- /dev/null
+++ b/src/devtools/met-driver/4.19/common/met_drv.h
@@ -0,0 +1,284 @@
+/*
+ * Copyright (C) 2019 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+
+#ifndef MET_DRV
+#define MET_DRV
+
+#include <linux/version.h>
+#include <linux/preempt.h>
+#include <linux/device.h>
+#include <linux/percpu.h>
+#include <linux/hardirq.h>
+#include <linux/clk.h>
+
+extern int met_mode;
+extern int core_plf_init(void);
+extern void core_plf_exit(void);
+
+#ifdef MET_CHIP_USE
+extern int met_chip_init(void);
+extern void met_chip_exit(void);
+#endif
+
+#define MET_MODE_TRACE_CMD_OFFSET	(1)
+#define MET_MODE_TRACE_CMD			(1<<MET_MODE_TRACE_CMD_OFFSET)
+
+#ifdef CONFIG_MET_MODULE
+#define my_preempt_enable() preempt_enable()
+#else
+#define my_preempt_enable() preempt_enable_no_resched()
+#endif
+
+#define MET_STRBUF_SIZE		1024
+DECLARE_PER_CPU(char[MET_STRBUF_SIZE], met_strbuf_nmi);
+DECLARE_PER_CPU(char[MET_STRBUF_SIZE], met_strbuf_irq);
+DECLARE_PER_CPU(char[MET_STRBUF_SIZE], met_strbuf_sirq);
+DECLARE_PER_CPU(char[MET_STRBUF_SIZE], met_strbuf);
+
+#ifdef CONFIG_TRACING
+#define TRACE_PUTS(p) \
+	do { \
+		trace_puts(p);; \
+	} while (0)
+#else
+#define TRACE_PUTS(p) do {} while (0)
+#endif
+
+#define GET_MET_TRACE_BUFFER_ENTER_CRITICAL() \
+	({ \
+		char *pmet_strbuf; \
+		preempt_disable(); \
+		if (in_nmi()) \
+			pmet_strbuf = per_cpu(met_strbuf_nmi, smp_processor_id()); \
+		else if (in_irq()) \
+			pmet_strbuf = per_cpu(met_strbuf_irq, smp_processor_id()); \
+		else if (in_softirq()) \
+			pmet_strbuf = per_cpu(met_strbuf_sirq, smp_processor_id()); \
+		else \
+			pmet_strbuf = per_cpu(met_strbuf, smp_processor_id()); \
+		pmet_strbuf;\
+	})
+
+#define PUT_MET_TRACE_BUFFER_EXIT_CRITICAL(pmet_strbuf) \
+	do {\
+		if (pmet_strbuf)\
+			TRACE_PUTS(pmet_strbuf); \
+		my_preempt_enable(); \
+	} while (0)
+
+#define MET_TRACE(FORMAT, args...) \
+	do { \
+		char *pmet_strbuf; \
+		preempt_disable(); \
+		if (in_nmi()) \
+			pmet_strbuf = per_cpu(met_strbuf_nmi, smp_processor_id()); \
+		else if (in_irq()) \
+			pmet_strbuf = per_cpu(met_strbuf_irq, smp_processor_id()); \
+		else if (in_softirq()) \
+			pmet_strbuf = per_cpu(met_strbuf_sirq, smp_processor_id()); \
+		else \
+			pmet_strbuf = per_cpu(met_strbuf, smp_processor_id()); \
+		if (met_mode & MET_MODE_TRACE_CMD) \
+			snprintf(pmet_strbuf, MET_STRBUF_SIZE, "%s: " FORMAT, __func__, ##args); \
+		else \
+			snprintf(pmet_strbuf, MET_STRBUF_SIZE, FORMAT, ##args); \
+		TRACE_PUTS(pmet_strbuf); \
+		my_preempt_enable(); \
+	} while (0)
+
+/*
+ * SOB: start of buf
+ * EOB: end of buf
+ */
+#define MET_TRACE_GETBUF(pSOB, pEOB) \
+	({ \
+		preempt_disable(); \
+		if (in_nmi()) \
+			*pSOB = per_cpu(met_strbuf_nmi, smp_processor_id()); \
+		else if (in_irq()) \
+			*pSOB = per_cpu(met_strbuf_irq, smp_processor_id()); \
+		else if (in_softirq()) \
+			*pSOB = per_cpu(met_strbuf_sirq, smp_processor_id()); \
+		else \
+			*pSOB = per_cpu(met_strbuf, smp_processor_id()); \
+		*pEOB = *pSOB; \
+		if (met_mode & MET_MODE_TRACE_CMD) \
+			*pEOB += snprintf(*pEOB, MET_STRBUF_SIZE, "%s: ", __func__); \
+	})
+
+#define MET_TRACE_PUTBUF(SOB, EOB) \
+	({ \
+		__trace_puts(_THIS_IP_, (SOB), (uintptr_t)((EOB)-(SOB))); \
+		my_preempt_enable(); \
+	})
+
+#define MET_FTRACE_DUMP(TRACE_NAME, args...)			\
+	do {							\
+		trace_##TRACE_NAME(args);;			\
+	} while (0)
+
+
+#define MET_TYPE_PMU	1
+#define MET_TYPE_BUS	2
+#define MET_TYPE_MISC	3
+
+enum met_action {
+	MET_CPU_ONLINE,
+	MET_CPU_OFFLINE,
+
+	NR_MET_ACTION,
+};
+
+struct metdevice {
+	struct list_head list;
+	int type;
+	const char *name;
+	struct module *owner;
+	struct kobject *kobj;
+
+	int (*create_subfs)(struct kobject *parent);
+	void (*delete_subfs)(void);
+	int mode;
+	int ondiemet_mode;	/* new for ondiemet; 1: call ondiemet functions */
+	int cpu_related;
+	int polling_interval;
+	int polling_count_reload;
+	int __percpu *polling_count;
+	int header_read_again;	/*for header size > 1 page */
+	void (*start)(void);
+	void (*uniq_start)(void);
+	void (*stop)(void);
+	void (*uniq_stop)(void);
+	int (*reset)(void);
+	void (*timed_polling)(unsigned long long stamp, int cpu);
+	void (*tagged_polling)(unsigned long long stamp, int cpu);
+	int (*print_help)(char *buf, int len);
+	int (*print_header)(char *buf, int len);
+	int (*process_argument)(const char *arg, int len);
+	void (*cpu_state_notify)(long cpu, unsigned long action);
+
+	void (*ondiemet_start)(void);
+	void (*uniq_ondiemet_start)(void);
+	void (*ondiemet_stop)(void);
+	void (*uniq_ondiemet_stop)(void);
+	int (*ondiemet_reset)(void);
+	int (*ondiemet_print_help)(char *buf, int len);
+	int (*ondiemet_print_header)(char *buf, int len);
+	int (*ondiemet_process_argument)(const char *arg, int len);
+	void (*ondiemet_timed_polling)(unsigned long long stamp, int cpu);
+	void (*ondiemet_tagged_polling)(unsigned long long stamp, int cpu);
+
+	struct list_head exlist;	/* for linked list before register */
+	void (*suspend)(void);
+	void (*resume)(void);
+
+	unsigned long long prev_stamp;
+	spinlock_t my_lock;
+	void *reversed1;
+};
+
+int met_register(struct metdevice *met);
+int met_deregister(struct metdevice *met);
+int met_set_platform(const char *plf_name, int flag);
+int met_set_chip_id(const unsigned int chip_id);
+int met_set_topology(const char *topology_name, int flag);
+int met_devlink_add(struct metdevice *met);
+int met_devlink_del(struct metdevice *met);
+int met_devlink_register_all(void);
+int met_devlink_deregister_all(void);
+
+int fs_reg(int met_minor);
+void fs_unreg(void);
+
+/******************************************************************************
+ * Tracepoints
+ ******************************************************************************/
+#define MET_DEFINE_PROBE(probe_name, proto) \
+		static void probe_##probe_name(void *data, PARAMS(proto))
+#define MET_REGISTER_TRACE(probe_name) \
+		register_trace_##probe_name(probe_##probe_name, NULL)
+#define MET_UNREGISTER_TRACE(probe_name) \
+		unregister_trace_##probe_name(probe_##probe_name, NULL)
+
+
+/* ====================== Tagging API ================================ */
+
+#define MAX_EVENT_CLASS	31
+#define MAX_TAGNAME_LEN	128
+#define MET_CLASS_ALL	0x80000000
+
+/* IOCTL commands of MET tagging */
+struct mtag_cmd_t {
+	unsigned int class_id;
+	unsigned int value;
+	unsigned int slen;
+	char tname[MAX_TAGNAME_LEN];
+	void *data;
+	unsigned int size;
+};
+
+#define TYPE_START		1
+#define TYPE_END		2
+#define TYPE_ONESHOT		3
+#define TYPE_ENABLE		4
+#define TYPE_DISABLE		5
+#define TYPE_REC_SET		6
+#define TYPE_DUMP		7
+#define TYPE_DUMP_SIZE		8
+#define TYPE_DUMP_SAVE		9
+#define TYPE_USRDATA		10
+#define TYPE_DUMP_AGAIN		11
+#define TYPE_ASYNC_START	12
+#define TYPE_ASYNC_END		13
+#define TYPE_MET_SUSPEND	15
+#define TYPE_MET_RESUME		16
+
+/* Use 'm' as magic number */
+#define MTAG_IOC_MAGIC  'm'
+/* Please use a different 8-bit number in your code */
+#define MTAG_CMD_START		_IOW(MTAG_IOC_MAGIC, TYPE_START, struct mtag_cmd_t)
+#define MTAG_CMD_END		_IOW(MTAG_IOC_MAGIC, TYPE_END, struct mtag_cmd_t)
+#define MTAG_CMD_ONESHOT	_IOW(MTAG_IOC_MAGIC, TYPE_ONESHOT, struct mtag_cmd_t)
+#define MTAG_CMD_ENABLE		_IOW(MTAG_IOC_MAGIC, TYPE_ENABLE, int)
+#define MTAG_CMD_DISABLE	_IOW(MTAG_IOC_MAGIC, TYPE_DISABLE, int)
+#define MTAG_CMD_REC_SET	_IOW(MTAG_IOC_MAGIC, TYPE_REC_SET, int)
+#define MTAG_CMD_DUMP		_IOW(MTAG_IOC_MAGIC, TYPE_DUMP, struct mtag_cmd_t)
+#define MTAG_CMD_DUMP_SIZE	_IOWR(MTAG_IOC_MAGIC, TYPE_DUMP_SIZE, int)
+#define MTAG_CMD_DUMP_SAVE	_IOW(MTAG_IOC_MAGIC, TYPE_DUMP_SAVE, struct mtag_cmd_t)
+#define MTAG_CMD_USRDATA	_IOW(MTAG_IOC_MAGIC, TYPE_USRDATA, struct mtag_cmd_t)
+#define MTAG_CMD_DUMP_AGAIN	_IOW(MTAG_IOC_MAGIC, TYPE_DUMP_AGAIN, void *)
+#define MTAG_CMD_ASYNC_START	_IOW(MTAG_IOC_MAGIC, TYPE_ASYNC_START, struct mtag_cmd_t)
+#define MTAG_CMD_ASYNC_END	_IOW(MTAG_IOC_MAGIC, TYPE_ASYNC_END, struct mtag_cmd_t)
+
+/* include file */
+extern int met_tag_start_real(unsigned int class_id, const char *name);
+extern int met_tag_end_real(unsigned int class_id, const char *name);
+extern int met_tag_async_start_real(unsigned int class_id, const char *name, unsigned int cookie);
+extern int met_tag_async_end_real(unsigned int class_id, const char *name, unsigned int cookie);
+extern int met_tag_oneshot_real(unsigned int class_id, const char *name, unsigned int value);
+extern int met_tag_userdata_real(char *pData);
+extern int met_tag_dump_real(unsigned int class_id, const char *name, void *data, unsigned int length);
+extern int met_tag_disable_real(unsigned int class_id);
+extern int met_tag_enable_real(unsigned int class_id);
+extern int met_set_dump_buffer_real(int size);
+extern int met_save_dump_buffer_real(const char *pathname);
+extern int met_save_log_real(const char *pathname);
+extern int met_show_bw_limiter_real(void);
+extern int met_reg_bw_limiter_real(void *fp);
+extern int met_show_clk_tree_real(const char *name, unsigned int addr, unsigned int status);
+extern int met_reg_clk_tree_real(void *fp);
+extern int enable_met_backlight_tag_real(void);
+extern int output_met_backlight_tag_real(int level);
+
+#endif	/* MET_DRV */
diff --git a/src/devtools/met-driver/4.19/common/met_kernel_symbol.h b/src/devtools/met-driver/4.19/common/met_kernel_symbol.h
new file mode 100644
index 0000000..8289705
--- /dev/null
+++ b/src/devtools/met-driver/4.19/common/met_kernel_symbol.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2019 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef MET_KERNEL_SYMBOL
+#define MET_KERNEL_SYMBOL
+
+/*lookup symbol*/
+#include <asm/cpu.h>
+#include <linux/kallsyms.h>
+#include <linux/perf_event.h>
+#include <linux/kthread.h>
+
+#if	defined(CONFIG_MET_ARM_32BIT)
+extern void met_get_cpuinfo(int cpu, struct cpuinfo_arm **cpuinfo);
+extern void (*met_get_cpuinfo_symbol)(int cpu, struct cpuinfo_arm **cpuinfo);
+#else
+extern void met_get_cpuinfo(int cpu, struct cpuinfo_arm64 **cpuinfo);
+extern void (*met_get_cpuinfo_symbol)(int cpu, struct cpuinfo_arm64 **cpuinfo);
+#endif
+
+extern void (*tracing_record_cmdline_symbol)(struct task_struct *tsk);
+extern void met_cpu_frequency(unsigned int frequency, unsigned int cpu_id);
+extern void (*met_cpu_frequency_symbol)(unsigned int frequency, unsigned int cpu_id);
+extern void (*met_arch_setup_dma_ops_symbol)(struct device *dev);
+extern int (*met_perf_event_read_local_symbol)(struct perf_event *ev, u64 *value);
+extern struct task_struct *(*met_kthread_create_on_cpu_symbol)(int (*threadfn)(void *data),
+				void *data, unsigned int cpu,
+				const char *namefmt);
+extern int (*met_smp_call_function_single_symbol)(int cpu, smp_call_func_t func, void *info, int wait);
+
+extern void met_tracing_record_cmdline(struct task_struct *tsk);
+extern int met_reg_switch(void);
+extern int (*met_reg_switch_symbol)(void);
+extern void met_unreg_switch(void);
+extern void (*met_unreg_switch_symbol)(void);
+#ifdef MET_EVENT_POWER_SUPPORT
+extern int met_reg_event_power(void);
+extern int (*met_reg_event_power_symbol)(void);
+extern void met_unreg_event_power(void);
+extern void (*met_unreg_event_power_symbol)(void);
+#endif
+extern void met_arch_setup_dma_ops(struct device *dev);
+extern int met_perf_event_read_local(struct perf_event *ev, u64 *value);
+extern struct task_struct *met_kthread_create_on_cpu(int (*threadfn)(void *data),
+				void *data, unsigned int cpu,
+				const char *namefmt);
+extern int met_smp_call_function_single(int cpu, smp_call_func_t func, void *info, int wait);
+extern void met_arch_send_call_function_single_ipi(int cpu);
+
+#if defined(CONFIG_MTK_TINYSYS_SSPM_SUPPORT) && defined(ONDIEMET_SUPPORT)
+#ifdef SSPM_VERSION_V2
+#include "sspm/ondiemet_sspm.h"
+extern struct mtk_ipi_device sspm_ipidev;
+extern struct mtk_ipi_device *sspm_ipidev_symbol;
+#endif
+#endif
+
+#endif	/* MET_KERNEL_SYMBOL */
diff --git a/src/devtools/met-driver/4.19/common/met_main.c b/src/devtools/met-driver/4.19/common/met_main.c
new file mode 100644
index 0000000..f0424c6
--- /dev/null
+++ b/src/devtools/met-driver/4.19/common/met_main.c
@@ -0,0 +1,444 @@
+/*
+ * Copyright (C) 2019 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/hrtimer.h>
+#include <linux/cpu.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/profile.h>
+#include <linux/dcache.h>
+#include <linux/types.h>
+#include <linux/dcookies.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <mt-plat/mtk_chip.h>
+
+#include <asm/irq_regs.h>
+
+#include "met_struct.h"
+#include "met_drv.h"
+#include "met_kernel_symbol.h"
+#include "interface.h"
+#include <linux/of.h>
+
+
+extern struct device_node *of_root;
+static const char *platform_name;
+
+struct cpu_type_name {
+	char full_name[32];
+	char abbr_name[8];
+};
+
+static struct cpu_type_name met_known_cpu_type[] = {
+	{"arm,cortex-a53", "CA53"},
+	{"arm,cortex-a55", "CA55"},
+	{"arm,cortex-a72", "CA72"},
+	{"arm,cortex-a73", "CA73"},
+	{"arm,cortex-a75", "CA75"},
+	{"arm,cortex-a76", "CA76"},
+};
+#define MET_KNOWN_CPU_TYPE_COUNT \
+	(sizeof(met_known_cpu_type)/sizeof(struct cpu_type_name))
+
+static char met_cpu_topology[64];
+
+#if	defined(CONFIG_MET_ARM_32BIT)
+void (*met_get_cpuinfo_symbol)(int cpu, struct cpuinfo_arm **cpuinfo);
+#else
+void (*met_get_cpuinfo_symbol)(int cpu, struct cpuinfo_arm64 **cpuinfo);
+#endif
+
+void (*tracing_record_cmdline_symbol)(struct task_struct *tsk);
+void (*met_cpu_frequency_symbol)(unsigned int frequency, unsigned int cpu_id);
+int (*met_reg_switch_symbol)(void);
+void (*met_unreg_switch_symbol)(void);
+
+#ifdef MET_EVENT_POWER_SUPPORT
+int (*met_reg_event_power_symbol)(void);
+void (*met_unreg_event_power_symbol)(void);
+#endif
+
+void (*met_arch_setup_dma_ops_symbol)(struct device *dev);
+int (*met_perf_event_read_local_symbol)(struct perf_event *ev, u64 *value);
+struct task_struct *(*met_kthread_create_on_cpu_symbol)(int (*threadfn)(void *data),
+				void *data, unsigned int cpu,
+				const char *namefmt);
+int (*met_smp_call_function_single_symbol)(int cpu, smp_call_func_t func, void *info, int wait);
+
+#if defined(CONFIG_MTK_TINYSYS_SSPM_SUPPORT) && defined(ONDIEMET_SUPPORT)
+#ifdef SSPM_VERSION_V2
+struct mtk_ipi_device *sspm_ipidev_symbol = NULL;
+#endif
+#endif
+
+static int met_minor = -1;
+module_param(met_minor, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
+
+
+unsigned int (*mt_get_chip_id_symbol)(void);
+
+#ifdef MTK_PLATFORM
+#define _SHOW_MTK_PLATFORM(X) #X
+#define SHOW_MTK_PLATFORM(X) _SHOW_MTK_PLATFORM(X)
+#endif
+
+static int is_platform_name_valid(const char * buf)
+{
+	int len = strlen(buf);
+	int i;
+
+	for (i=0; i<len; i++) {
+		if ((buf[i] == 'm' && buf[i+1] == 't')
+			|| (buf[i] == 'M' && buf[i+1] == 'T')
+			|| (buf[i] == 'M' && buf[i+1] == 't')
+			|| (buf[i] == 'm' && buf[i+1] == 'T')) {
+			return i;
+		}
+	}
+	return -1;
+}
+
+#if 0
+const char *met_get_platform_name(void)
+{
+	const char default_platform_name[7] = "mtxxxx";
+	int found = -1;
+
+	found = is_platform_name_valid(platform_name);
+
+	if(found < 0){
+#ifdef MTK_PLATFORM
+		const char buf[7] = SHOW_MTK_PLATFORM(MTK_PLATFORM);
+		found = is_platform_name_valid(buf);
+		if ( !(found < 0) )
+			platform_name = buf;
+		else
+#endif
+			platform_name = default_platform_name;
+	}
+
+	strncpy(&buf_tmp[2], &platform_name[found+2], 4);
+	return platform_name;
+}
+EXPORT_SYMBOL(met_get_platform_name);
+#endif
+
+static void get_cpu_type_name(const char *compatible, char *cpu_type)
+{
+	int i;
+
+	for (i = 0; i < MET_KNOWN_CPU_TYPE_COUNT; i++) {
+		if (!strncmp(compatible, met_known_cpu_type[i].full_name,
+					strlen(met_known_cpu_type[i].full_name)))
+			strncpy(cpu_type, met_known_cpu_type[i].abbr_name,
+					strlen(met_known_cpu_type[i].abbr_name) + 1);
+	}
+}
+
+static void met_set_cpu_topology(int core_id, int cluster_core_num)
+{
+	int i, buf_len = strlen(met_cpu_topology);
+	struct device_node *node = NULL;
+	const char *prev_cptb = NULL;
+	const char *cptb;
+	char cpu_type[16];
+
+	for (i = 0; i < cluster_core_num; i++) {
+		node = of_get_cpu_node(core_id + i, NULL);
+		if (node) {
+			cptb = of_get_property(node, "compatible", NULL);
+			if (cptb) {
+				get_cpu_type_name(cptb, cpu_type);
+				if (prev_cptb == NULL)
+					/* first write:  write core_type & core_number */
+					buf_len += snprintf(met_cpu_topology + buf_len,
+								sizeof(met_cpu_topology) - buf_len,
+								"%s:%d", cpu_type, core_id + i);
+				else if (!strncmp(prev_cptb, cptb, strlen(prev_cptb)))
+					/* cpu type is the same with before */
+					/* write core_number */
+					buf_len += snprintf(met_cpu_topology + buf_len,
+								sizeof(met_cpu_topology) - buf_len,
+								",%d", core_id + i);
+				else
+					/* cpu type is different with before */
+					/* write core_type & core_number */
+					buf_len += snprintf(met_cpu_topology + buf_len,
+								sizeof(met_cpu_topology) - buf_len,
+								"|%s:%d", cpu_type, core_id + i);
+
+				prev_cptb = cptb;
+			}
+		}
+	}
+}
+
+static int met_create_cpu_topology(void)
+{
+	int i, j, len;
+	struct device_node *node = NULL, *core_node = NULL;
+	int start_core_id = 0;
+	int cluster_num = 0, cluster_core_num = 0;
+	char cluster_name[16], core_name[16];
+
+	node = of_find_node_by_name(NULL, "cpu-map");
+	if (!node)
+		node = of_find_node_by_name(NULL, "virtual-cpu-map");
+
+	if (node) {
+		cluster_num = of_get_child_count(node);
+		of_node_put(node);
+
+		for (i = 0; i < cluster_num; i++) {
+			snprintf(cluster_name, sizeof(cluster_name), "cluster%d", i);
+			node = of_find_node_by_name(NULL, cluster_name);
+			if (node) {
+
+				j = 0;
+				cluster_core_num = 0;
+				do {
+					snprintf(core_name, sizeof(core_name), "core%d", j);
+					core_node = of_get_child_by_name(node, core_name);
+					if (core_node) {
+						cluster_core_num++;
+						of_node_put(core_node);
+					}
+					j++;
+				} while (core_node);
+				of_node_put(node);
+
+				/* "|" use to separate different cluster */
+				if (i > 0) {
+					len = strlen(met_cpu_topology);
+					snprintf(met_cpu_topology + len, sizeof(met_cpu_topology) - len, "|");
+				}
+
+				met_set_cpu_topology(start_core_id, cluster_core_num);
+				start_core_id = cluster_core_num;
+			}
+		}
+	}
+
+	return strlen(met_cpu_topology);
+}
+
+static int met_kernel_symbol_get(void)
+{
+	int ret = 0;
+
+	if (met_get_cpuinfo_symbol == NULL)
+		met_get_cpuinfo_symbol = (void *)symbol_get(met_get_cpuinfo);
+	if (met_get_cpuinfo_symbol == NULL)
+		return -2;
+
+	if (tracing_record_cmdline_symbol == NULL)
+		tracing_record_cmdline_symbol = (void *)symbol_get(met_tracing_record_cmdline);
+	if (tracing_record_cmdline_symbol == NULL)
+		ret = -3;
+
+	if (met_cpu_frequency_symbol == NULL)
+		met_cpu_frequency_symbol = (void *)symbol_get(met_cpu_frequency);
+	if (met_cpu_frequency_symbol == NULL)
+		ret = -4;
+
+	if (met_reg_switch_symbol == NULL)
+		met_reg_switch_symbol = (void *)symbol_get(met_reg_switch);
+	if (met_reg_switch_symbol == NULL)
+		ret = -5;
+
+	if (met_unreg_switch_symbol == NULL)
+		met_unreg_switch_symbol = (void *)symbol_get(met_unreg_switch);
+	if (met_unreg_switch_symbol == NULL)
+		ret = -6;
+
+	if (met_arch_setup_dma_ops_symbol == NULL)
+		met_arch_setup_dma_ops_symbol = (void *)symbol_get(met_arch_setup_dma_ops);
+	if (met_arch_setup_dma_ops_symbol == NULL)
+		ret = -7;
+
+	if (met_perf_event_read_local_symbol == NULL)
+		met_perf_event_read_local_symbol = (void *)symbol_get(met_perf_event_read_local);
+	if (met_perf_event_read_local_symbol == NULL)
+		ret = -8;
+
+	if (met_kthread_create_on_cpu_symbol == NULL)
+		met_kthread_create_on_cpu_symbol = (void *)symbol_get(met_kthread_create_on_cpu);
+	if (met_kthread_create_on_cpu_symbol == NULL)
+		ret = -9;
+
+	if (met_smp_call_function_single_symbol == NULL)
+		met_smp_call_function_single_symbol = (void *)symbol_get(met_smp_call_function_single);
+	if (met_smp_call_function_single_symbol == NULL)
+		ret = -10;
+
+#ifdef MET_EVENT_POWER_SUPPORT
+	if (met_reg_event_power_symbol == NULL)
+		met_reg_event_power_symbol = (void *)symbol_get(met_reg_event_power);
+	if (met_reg_event_power_symbol == NULL)
+		ret = -11;
+
+	if (met_unreg_event_power_symbol == NULL)
+		met_unreg_event_power_symbol = (void *)symbol_get(met_unreg_event_power);
+	if (met_unreg_event_power_symbol == NULL)
+		ret = -12;
+#endif
+
+#if defined(CONFIG_MTK_TINYSYS_SSPM_SUPPORT) && defined(ONDIEMET_SUPPORT)
+#ifdef SSPM_VERSION_V2
+	if (sspm_ipidev_symbol == NULL)
+		sspm_ipidev_symbol = symbol_get(sspm_ipidev);
+#endif
+#endif
+
+	return ret;
+}
+
+DEFINE_PER_CPU(struct met_cpu_struct, met_cpu);
+
+static int __init met_drv_init(void)
+{
+	int cpu;
+	int ret;
+	int cpu_topology_len;
+	struct met_cpu_struct *met_cpu_ptr;
+
+	for_each_possible_cpu(cpu) {
+		met_cpu_ptr = &per_cpu(met_cpu, cpu);
+		/* snprintf(&(met_cpu_ptr->name[0]), sizeof(met_cpu_ptr->name), "met%02d", cpu); */
+		met_cpu_ptr->cpu = cpu;
+	}
+
+	ret = met_kernel_symbol_get();
+	if (ret) {
+		pr_notice("[MET] met_kernel_symbol_get fail, ret = %d\n", ret);
+		return ret;
+	}
+	ret = fs_reg(met_minor);
+	if (ret) {
+		pr_notice("[MET] met fs_reg fail, ret = %d\n", ret);
+		return ret;
+	}
+
+
+	if (of_root){
+		/*
+			mt6765.dts
+			model = "MT6765";
+			compatible = "mediatek,MT6765";
+			interrupt-parent = <&sysirq>;
+		*/
+		if (of_root->properties) {
+			of_property_read_string(of_root, "compatible", &platform_name);
+			PR_BOOTMSG("dts property compatible=%s\n", platform_name);
+		}
+	}
+	if (platform_name) {
+		char buf[7];
+		int found = -1;
+
+		found = is_platform_name_valid(platform_name);
+
+		if ( !(found < 0) ) {
+			memset(buf, 0x0, 7);
+			buf[0] = 'm';
+			buf[1] = 't';
+			strncpy(&buf[2], &platform_name[found+2], 4);
+			met_set_platform(buf, 1);
+			PR_BOOTMSG("Get platform info from dts, platform_name=%s\n", buf);
+		} else {
+#ifdef MTK_PLATFORM
+			memset(buf, 0x0, 7);
+			strcpy(buf, SHOW_MTK_PLATFORM(MTK_PLATFORM));
+			found = is_platform_name_valid((const char *)buf);
+			if( !(found < 0) ){
+				PR_BOOTMSG("Get platform info from met_drv Kbuild, platform_name=%s\n", buf);
+				met_set_platform(buf, 1);
+			}
+			else
+#endif
+			{
+				PR_BOOTMSG("Can not get platform info from dts nor met_drv Kbuild, set platform_name=mtxxxx\n");
+				met_set_platform("mtxxxx", 1);
+			}
+		}
+	}
+	/* get the chip id */
+
+	if (mt_get_chip_id_symbol == NULL)
+		mt_get_chip_id_symbol = (void *)symbol_get(mt_get_chip_id);
+
+	if (mt_get_chip_id_symbol != NULL)
+		met_set_chip_id(mt_get_chip_id_symbol());
+	else
+		PR_BOOTMSG("Can not get chip id info by mt_get_chip_id(), set chip_id=0x0\n");
+
+	cpu_topology_len = met_create_cpu_topology();
+	if (cpu_topology_len)
+		met_set_topology(met_cpu_topology, 1);
+
+#ifdef MET_PLF_USE
+	core_plf_init();
+#endif
+
+#ifdef MET_CHIP_USE
+	met_chip_init();
+#endif
+
+	return 0;
+}
+
+static void __exit met_drv_exit(void)
+{
+	if (met_cpu_frequency_symbol)
+		symbol_put(met_cpu_frequency);
+	if (met_reg_switch_symbol)
+		symbol_put(met_reg_switch);
+	if (met_unreg_switch_symbol)
+		symbol_put(met_unreg_switch);
+
+#ifdef MET_EVENT_POWER_SUPPORT
+	if (met_reg_event_power_symbol)
+		symbol_put(met_reg_event_power);
+	if (met_unreg_event_power_symbol)
+		symbol_put(met_unreg_event_power);
+#endif
+
+	if (tracing_record_cmdline_symbol)
+		symbol_put(met_tracing_record_cmdline);
+	if (met_get_cpuinfo_symbol)
+		symbol_put(met_get_cpuinfo);
+
+	if (mt_get_chip_id_symbol)
+		symbol_put(mt_get_chip_id);
+
+#ifdef MET_PLF_USE
+	core_plf_exit();
+#endif
+
+#ifdef MET_CHIP_USE
+	met_chip_exit();
+#endif
+
+	fs_unreg();
+
+}
+module_init(met_drv_init);
+module_exit(met_drv_exit);
+
+MODULE_AUTHOR("DT_DM5");
+MODULE_DESCRIPTION("MET_CORE");
+MODULE_LICENSE("GPL");
diff --git a/src/devtools/met-driver/4.19/common/met_power.h b/src/devtools/met-driver/4.19/common/met_power.h
new file mode 100644
index 0000000..f4298b4
--- /dev/null
+++ b/src/devtools/met-driver/4.19/common/met_power.h
@@ -0,0 +1,63 @@
+/*
+ * * Copyright (C) 2019 MediaTek Inc.
+ * *
+ * * 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.
+ * *
+ * * This program is distributed in the hope that it will be useful,
+ * * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * * GNU General Public License for more details.
+ * */
+
+
+#ifndef MET_POWER
+#define MET_POWER
+
+enum {
+	_PM_QOS_RESERVED = 0,
+	_PM_QOS_CPU_DMA_LATENCY,
+	_PM_QOS_NETWORK_LATENCY,
+	_PM_QOS_NETWORK_THROUGHPUT,
+	_PM_QOS_MEMORY_BANDWIDTH,
+
+	_PM_QOS_CPU_MEMORY_BANDWIDTH,
+	_PM_QOS_GPU_MEMORY_BANDWIDTH,
+	_PM_QOS_MM_MEMORY_BANDWIDTH,
+	_PM_QOS_OTHER_MEMORY_BANDWIDTH,
+	_PM_QOS_MM0_BANDWIDTH_LIMITER,
+	_PM_QOS_MM1_BANDWIDTH_LIMITER,
+
+	_PM_QOS_DDR_OPP,
+	_PM_QOS_VCORE_OPP,
+	_PM_QOS_SCP_VCORE_REQUEST,
+	_PM_QOS_POWER_MODEL_DDR_REQUEST,
+	_PM_QOS_POWER_MODEL_VCORE_REQUEST,
+	_PM_QOS_VCORE_DVFS_FORCE_OPP,
+
+	_PM_QOS_DISP_FREQ,
+	_PM_QOS_MDP_FREQ,
+	_PM_QOS_VDEC_FREQ,
+	_PM_QOS_VENC_FREQ,
+	_PM_QOS_IMG_FREQ,
+	_PM_QOS_CAM_FREQ,
+	_PM_QOS_VVPU_OPP,
+	_PM_QOS_VMDLA_OPP,
+	_PM_QOS_ISP_HRT_BANDWIDTH,
+	_PM_QOS_APU_MEMORY_BANDWIDTH,
+	/* insert new class ID */
+	_PM_QOS_NUM_CLASSES,
+};
+/* Action requested to pm_qos_update_target */
+enum _pm_qos_req_action {
+	_PM_QOS_ADD_REQ,		/* Add a new request */
+	_PM_QOS_UPDATE_REQ,	/* Update an existing request */
+	_PM_QOS_REMOVE_REQ	/* Remove an existing request */
+};
+
+extern void pm_qos_update_request(int pm_qos_class, s32 value, char *owner);
+extern void pm_qos_update_target(unsigned int action, int prev_value, int curr_value);
+
+#endif	/* MET_DRV */
+
diff --git a/src/devtools/met-driver/4.19/common/met_ptpod.c b/src/devtools/met-driver/4.19/common/met_ptpod.c
new file mode 100644
index 0000000..4ed1494
--- /dev/null
+++ b/src/devtools/met-driver/4.19/common/met_ptpod.c
@@ -0,0 +1,364 @@
+/*
+ * Copyright (C) 2019 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/random.h>
+#include <linux/fs.h>
+
+#include "met_drv.h"
+#include "trace.h"
+#include "core_plf_init.h"
+#include "core_plf_trace.h"
+
+static unsigned int MT_GPU_DVFS_IDX = NR_MT_CPU_DVFS;
+static unsigned int g_u4GPUVolt;
+static unsigned int g_u4Volt[NR_MT_CPU_DVFS + 1];
+
+/* g_ap_ptpod: cpu volt from ap or sspm setting
+ * if 1: cpu volt from ap
+ * if 0: cpu volt from sspm */
+static unsigned int g_ap_ptpod;
+
+/* gpu_volt_enable:
+ * if 1: enable gpu volt to output
+ * if 0: disable gpu volt to output */
+static unsigned int gpu_volt_enable = 1;
+
+/* get_volt_by_wq:
+ * if 1: get cpu/gpu volt by workqueue
+ * if 0: get cpu/gpu volt in irq */
+static unsigned int get_volt_by_wq;
+
+static int ptpod_started;
+static struct kobject *kobj_ptpod;
+static struct delayed_work get_volt_dwork;
+
+noinline void ms_ptpod(void)
+{
+	char *SOB, *EOB;
+
+	if (g_ap_ptpod) {
+		MET_TRACE_GETBUF(&SOB, &EOB);
+
+		if (gpu_volt_enable) {
+			g_u4Volt[MT_GPU_DVFS_IDX] = g_u4GPUVolt;
+			EOB = ms_formatD_EOL(EOB, ARRAY_SIZE(g_u4Volt), g_u4Volt);
+		} else
+			EOB = ms_formatD_EOL(EOB, ARRAY_SIZE(g_u4Volt) - 1, g_u4Volt);
+
+		MET_TRACE_PUTBUF(SOB, EOB);
+	} else {
+		if (gpu_volt_enable) {
+			MET_TRACE_GETBUF(&SOB, &EOB);
+			EOB = ms_formatD_EOL(EOB, 1, &g_u4GPUVolt);
+			MET_TRACE_PUTBUF(SOB, EOB);
+		}
+	}
+}
+
+#if 0
+static void ptpod_cpu_voltSampler(enum mt_cpu_dvfs_id id, unsigned int volt)
+{
+	switch (id) {
+	case MT_CPU_DVFS_LL:
+		g_u4CPUVolt_LL = volt;
+		break;
+	case MT_CPU_DVFS_L:
+		g_u4CPUVolt_L = volt;
+		break;
+	case MT_CPU_DVFS_CCI:
+		g_u4CPUVolt_CCI = volt;
+		break;
+	default:
+		return;
+	}
+	ptpod();
+}
+#endif
+
+#if 0
+static void ptpod_gpu_voltSampler(unsigned int a_u4Volt)
+{
+	g_u4GPUVolt = (a_u4Volt+50)/100;
+
+	if (ptpod_started)
+		ptpod();
+}
+#endif
+
+#define PTPOD_CONF_SHOW_IMPLEMENT(var) \
+	do { \
+		int i; \
+		i = snprintf(buf, PAGE_SIZE, "%d\n", var); \
+		return i; \
+	} while (0)
+
+#define PTPOD_CONF_STORE_IMPLEMENT(var) \
+	do { \
+		int value; \
+		if ((n == 0) || (buf == NULL)) \
+			return -EINVAL; \
+		if (kstrtoint(buf, 0, &value) != 0) \
+			return -EINVAL; \
+		if (value == 1) \
+			var = 1; \
+		else \
+			var = 0; \
+		return n; \
+	} while (0)
+
+
+static ssize_t ap_ptpod_enable_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	PTPOD_CONF_SHOW_IMPLEMENT(g_ap_ptpod);
+}
+
+static ssize_t ap_ptpod_enable_store(struct kobject *kobj,
+			struct kobj_attribute *attr,
+			const char *buf,
+			size_t n)
+{
+	PTPOD_CONF_STORE_IMPLEMENT(g_ap_ptpod);
+}
+
+static ssize_t gpu_volt_enable_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	PTPOD_CONF_SHOW_IMPLEMENT(gpu_volt_enable);
+}
+
+static ssize_t gpu_volt_enable_store(struct kobject *kobj,
+			struct kobj_attribute *attr,
+			const char *buf,
+			size_t n)
+{
+	PTPOD_CONF_STORE_IMPLEMENT(gpu_volt_enable);
+}
+
+static ssize_t get_volt_by_wq_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	PTPOD_CONF_SHOW_IMPLEMENT(get_volt_by_wq);
+}
+
+static ssize_t get_volt_by_wq_store(struct kobject *kobj,
+			struct kobj_attribute *attr,
+			const char *buf,
+			size_t n)
+{
+	PTPOD_CONF_STORE_IMPLEMENT(get_volt_by_wq);
+}
+
+static struct kobj_attribute ap_ptpod_enable_attr = __ATTR(ap_ptpod_enable, 0664, ap_ptpod_enable_show, ap_ptpod_enable_store);
+static struct kobj_attribute gpu_volt_enable_attr = __ATTR(gpu_volt_enable, 0664, gpu_volt_enable_show, gpu_volt_enable_store);
+static struct kobj_attribute get_volt_by_wq_attr = __ATTR(get_volt_by_wq, 0664, get_volt_by_wq_show, get_volt_by_wq_store);
+
+/* create ptpod related kobj node */
+#define KOBJ_ATTR_LIST \
+	do { \
+		KOBJ_ATTR_ITEM(ap_ptpod_enable); \
+		KOBJ_ATTR_ITEM(gpu_volt_enable); \
+		KOBJ_ATTR_ITEM(get_volt_by_wq); \
+	} while (0)
+
+static int ptpod_create(struct kobject *parent)
+{
+	int ret = 0;
+
+	kobj_ptpod = parent;
+
+#define KOBJ_ATTR_ITEM(attr_name) \
+	do { \
+		ret = sysfs_create_file(kobj_ptpod, &attr_name ## _attr.attr); \
+		if (ret != 0) { \
+			pr_notice("Failed to create " #attr_name " in sysfs\n"); \
+			return ret; \
+		} \
+	} while (0)
+	KOBJ_ATTR_LIST;
+#undef  KOBJ_ATTR_ITEM
+
+	return 0;
+}
+
+static void ptpod_delete(void)
+{
+#define KOBJ_ATTR_ITEM(attr_name) \
+	sysfs_remove_file(kobj_ptpod, &attr_name ## _attr.attr)
+
+	if (kobj_ptpod != NULL) {
+		KOBJ_ATTR_LIST;
+		kobj_ptpod = NULL;
+	}
+#undef  KOBJ_ATTR_ITEM
+}
+
+static void update_volt_value(void)
+{
+	int i;
+
+	if (g_ap_ptpod && mt_cpufreq_get_cur_volt_symbol) {
+		/*
+		g_u4CPUVolt_LL = mt_cpufreq_get_cur_volt_symbol(MT_CPU_DVFS_LL)/100;
+		g_u4CPUVolt_L = mt_cpufreq_get_cur_volt_symbol(MT_CPU_DVFS_L)/100;
+		g_u4CPUVolt_CCI = mt_cpufreq_get_cur_volt_symbol(MT_CPU_DVFS_CCI)/100;
+		*/
+		for (i = 0; i < NR_MT_CPU_DVFS; i++)
+			g_u4Volt[i] = mt_cpufreq_get_cur_volt_symbol(i) / 100;
+	}
+
+	if (gpu_volt_enable) {
+		if (mt_gpufreq_get_cur_volt_symbol)
+			g_u4GPUVolt = ((mt_gpufreq_get_cur_volt_symbol() + 50) / 100);
+	}
+}
+
+static void get_volt_notify(unsigned long long stamp, int cpu)
+{
+	schedule_delayed_work(&get_volt_dwork, 0);
+}
+
+static void met_ptpod_polling_by_wq(struct work_struct *work)
+{
+	update_volt_value();
+
+	ms_ptpod();
+}
+
+static void met_ptpod_polling(unsigned long long stamp, int cpu)
+{
+	update_volt_value();
+
+	ms_ptpod();
+}
+
+/*
+ * Called from "met-cmd --start"
+ */
+static void ptpod_start(void)
+{
+#if 0
+	met_gpufreq_setvolt_registerCB(ptpod_gpu_voltSampler, kFOR_MET_PTPOD_USE);
+#endif
+
+	if (get_volt_by_wq) {
+		met_ptpod.timed_polling = get_volt_notify;
+
+		INIT_DELAYED_WORK(&get_volt_dwork, met_ptpod_polling_by_wq);
+	} else
+		met_ptpod.timed_polling = met_ptpod_polling;
+
+	update_volt_value();
+
+	ms_ptpod();
+
+#if 0
+	/* register callback */
+	if (mt_cpufreq_setvolt_registerCB_symbol)
+		mt_cpufreq_setvolt_registerCB_symbol(ptpod_cpu_voltSampler);
+#endif
+
+	ptpod_started = 1;
+}
+
+/*
+ * Called from "met-cmd --stop"
+ */
+static void ptpod_stop(void)
+{
+	ptpod_started = 0;
+
+#if 0
+	/* unregister callback */
+	if (mt_cpufreq_setvolt_registerCB_symbol)
+		mt_cpufreq_setvolt_registerCB_symbol(NULL);
+#endif
+
+	if (get_volt_by_wq)
+		cancel_delayed_work_sync(&get_volt_dwork);
+
+	update_volt_value();
+
+	ms_ptpod();
+
+#if 0
+	met_gpufreq_setvolt_registerCB(NULL, kFOR_MET_PTPOD_USE);
+#endif
+}
+
+static char help[] =
+	"  --ptpod                               Measure CPU/GPU voltage\n";
+static int ptpod_print_help(char *buf, int len)
+{
+	return snprintf(buf, PAGE_SIZE, help);
+}
+
+/*
+ * It will be called back when run "met-cmd --extract" and mode is 1
+ */
+static int ptpod_print_header(char *buf, int len)
+{
+	int str_len = 0;
+
+	if (g_ap_ptpod) {
+		int i;
+
+		str_len += snprintf(buf + str_len, PAGE_SIZE - str_len,
+						"met-info [000] 0.0: met_ptpod_header: ");
+
+		for (i = 0; i < NR_MT_CPU_DVFS; i++)
+			str_len += snprintf(buf + str_len, PAGE_SIZE - str_len, "CPUVolt_%d,", i);
+
+		if (gpu_volt_enable)
+			str_len += snprintf(buf + str_len, PAGE_SIZE - str_len, "GPUVolt,");
+
+		buf[str_len-1] = '\n';
+
+		str_len += snprintf(buf + str_len, PAGE_SIZE - str_len,
+							"met-info [000] 0.0: met_ptpod_version: ap\n");
+	} else {
+		if (gpu_volt_enable) {
+			str_len += snprintf(buf + str_len, PAGE_SIZE - str_len,
+							"met-info [000] 0.0: met_ptpod_header: ");
+
+			str_len += snprintf(buf + str_len, PAGE_SIZE - str_len, "GPUVolt\n");
+		}
+
+		str_len += snprintf(buf + str_len, PAGE_SIZE - str_len,
+							"met-info [000] 0.0: met_ptpod_version: sspm\n");
+	}
+
+	if (gpu_volt_enable)
+		str_len += snprintf(buf + str_len, PAGE_SIZE - str_len,
+						"met-info [000] 0.0: met_ptpod_gpu_volt_enable: YES\n");
+	else
+		str_len += snprintf(buf + str_len, PAGE_SIZE - str_len,
+						"met-info [000] 0.0: met_ptpod_gpu_volt_enable: NO\n");
+
+	return str_len;
+}
+
+struct metdevice met_ptpod = {
+	.name = "ptpod",
+	.owner = THIS_MODULE,
+	.type = MET_TYPE_PMU,
+	.cpu_related = 0,
+	.create_subfs = ptpod_create,
+	.delete_subfs = ptpod_delete,
+	.start = ptpod_start,
+	.stop = ptpod_stop,
+	.timed_polling = met_ptpod_polling,
+	.print_help = ptpod_print_help,
+	.print_header = ptpod_print_header,
+};
+EXPORT_SYMBOL(met_ptpod);
diff --git a/src/devtools/met-driver/4.19/common/met_struct.h b/src/devtools/met-driver/4.19/common/met_struct.h
new file mode 100644
index 0000000..cff68d1
--- /dev/null
+++ b/src/devtools/met-driver/4.19/common/met_struct.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2019 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MET_STRUCT_H_
+#define _MET_STRUCT_H_
+
+#include <linux/hrtimer.h>
+
+struct met_cpu_struct {
+	struct hrtimer hrtimer;
+	struct delayed_work dwork;
+/* struct kmem_cache *cachep; */
+/* struct list_head sample_head; */
+/* spinlock_t list_lock; */
+/* struct mutex list_sync_lock; */
+	int work_enabled;
+	int cpu;
+	int hrtimer_online_check;
+/* char name[16]; */
+};
+
+DECLARE_PER_CPU(struct met_cpu_struct, met_cpu);
+
+#endif				/* _MET_STRUCT_H_ */
diff --git a/src/devtools/met-driver/4.19/common/met_tag.h b/src/devtools/met-driver/4.19/common/met_tag.h
new file mode 100644
index 0000000..a0bdedd
--- /dev/null
+++ b/src/devtools/met-driver/4.19/common/met_tag.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2019 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __MET_TAG_EX_H__
+#define __MET_TAG_EX_H__
+
+#ifdef BUILD_WITH_MET
+void force_sample(void *unused);
+#else
+#include <linux/string.h>
+#endif
+
+/* Black List Table */
+struct bltable_t {
+	struct mutex mlock;
+	/* flag - Bit31: Global ON/OFF; Bit0~30: ON/OF slot map of class_id */
+	unsigned int flag;
+	int class_id[MAX_EVENT_CLASS];
+};
+
+extern void met_sched_switch(struct task_struct *prev, struct task_struct *next);
+
+extern int tracing_mark_write(int type, unsigned int class_id,
+		const char *name, unsigned int value,
+		unsigned int value2, unsigned int value3);
+
+#endif				/* __MET_TAG_EX_H__ */
diff --git a/src/devtools/met-driver/4.19/common/met_tag_ex.c b/src/devtools/met-driver/4.19/common/met_tag_ex.c
new file mode 100644
index 0000000..5a84606
--- /dev/null
+++ b/src/devtools/met-driver/4.19/common/met_tag_ex.c
@@ -0,0 +1,644 @@
+/*
+ * Copyright (C) 2019 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define BUILD_WITH_MET
+
+#ifdef MET_USER_EVENT_SUPPORT
+
+#include <linux/kernel.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/fs.h>
+/* #include <asm/uaccess.h> */
+#include <linux/uaccess.h>
+
+#include "met_drv.h"
+#include "met_tag.h"
+#include "interface.h"
+#include "switch.h"
+#include "met_api_tbl.h"
+
+struct bltable_t bltab;
+
+static int dump_buffer_size;
+static int dump_data_size;
+static int dump_overrun;
+static int dump_overrun_size;
+static int dump_seq_no;
+static void *dump_buffer;
+
+#define OPFLAG_OVERWRITE	0x1
+static unsigned int options_flag;
+
+#define DEVICE_NAME		"met_tag"
+
+/* #define ERRF_ENABLE */
+/* #define DEBF_ENABLE */
+
+#ifdef ERRF_ENABLE
+#define MSG_ERR			"Error:["DEVICE_NAME"]"
+#define ERRF(args...)	pr_debug(MSG_ERR args)
+#else
+#define ERRF(args...)
+#endif
+
+#ifdef DEBF_ENABLE
+#define MSG_IFO			"Info :["DEVICE_NAME"]"
+#define DEBF(args...)	pr_debug(MSG_IFO args)
+#else
+#define DEBF(args...)
+#endif
+
+static int is_enabled(unsigned int class_id)
+{
+	int i;
+
+	if (bltab.flag == 0)
+		return 1;
+	if (bltab.flag & MET_CLASS_ALL)
+		return 0;
+
+	for (i = 0; i < MAX_EVENT_CLASS; i++) {
+		if ((bltab.flag & (1 << i)) && (bltab.class_id[i] == class_id))
+			return 0;
+	}
+
+	return 1;
+}
+
+noinline int tracing_mark_write(int type, unsigned int class_id,
+				       const char *name, unsigned int value,
+				       unsigned int value2, unsigned int value3)
+{
+	if (type == TYPE_MET_SUSPEND) {
+		MET_TRACE("C|0|MET_SUSPEND|1");
+		return 0;
+	}
+	if (type == TYPE_MET_RESUME) {
+		MET_TRACE("C|0|MET_SUSPEND|0");
+		return 0;
+	}
+	if (!is_enabled(class_id))
+		return 0;
+	switch (type) {
+	case TYPE_START:
+		MET_TRACE("B|%d|%s\n", class_id, name);
+		break;
+	case TYPE_END:
+		MET_TRACE("E|%s\n", name);
+		break;
+	case TYPE_ONESHOT:
+		MET_TRACE("C|%d|%s|%d\n", class_id, name, value);
+		break;
+	case TYPE_ASYNC_START:
+		MET_TRACE("S|%d|%s|%d\n", class_id, name, value);
+		break;
+	case TYPE_ASYNC_END:
+		MET_TRACE("F|%d|%s|%d\n", class_id, name, value);
+		break;
+	case TYPE_DUMP:
+		MET_TRACE("D|%d|%s|%d|%d|%d\n", class_id, name, value, value2, value3);
+		break;
+	default:
+		return -1;
+	}
+	return 0;
+}
+
+int met_tag_init(void)
+{
+	memset(&bltab, 0, sizeof(struct bltable_t));
+	bltab.flag = MET_CLASS_ALL;
+	mutex_init(&bltab.mlock);
+	return 0;
+}
+
+int met_tag_uninit(void)
+{
+	met_set_dump_buffer_real(0);
+	return 0;
+}
+
+int met_tag_start_real(unsigned int class_id, const char *name)
+{
+	int ret;
+
+	ret = tracing_mark_write(TYPE_START, class_id, name, 0, 0, 0);
+#if 0
+	if ((met_switch.mode & MT_SWITCH_TAGPOLLING)) {
+		/* tag polling only enable when MT_SWITCH_TAGPOLLING is config */
+		force_sample(NULL);
+	}
+#endif
+	return ret;
+}
+EXPORT_SYMBOL(met_tag_start_real);
+
+int met_tag_end_real(unsigned int class_id, const char *name)
+{
+	int ret;
+#if 0
+	if ((met_switch.mode & MT_SWITCH_TAGPOLLING)) {
+		/* tag polling only enable when MT_SWITCH_TAGPOLLING is config */
+		force_sample(NULL);
+	}
+#endif
+	ret = tracing_mark_write(TYPE_END, class_id, name, 0, 0, 0);
+
+	return ret;
+}
+EXPORT_SYMBOL(met_tag_end_real);
+
+int met_tag_async_start_real(unsigned int class_id, const char *name, unsigned int cookie)
+{
+	int ret;
+
+	ret = tracing_mark_write(TYPE_ASYNC_START, class_id, name, cookie, 0, 0);
+#if 0
+	if ((met_switch.mode & MT_SWITCH_TAGPOLLING)) {
+		/* tag polling only enable when MT_SWITCH_TAGPOLLING is config */
+		force_sample(NULL);
+	}
+#endif
+	return ret;
+}
+
+int met_tag_async_end_real(unsigned int class_id, const char *name, unsigned int cookie)
+{
+	int ret;
+
+#if 0
+	if ((met_switch.mode & MT_SWITCH_TAGPOLLING)) {
+		/* tag polling only enable when MT_SWITCH_TAGPOLLING is config */
+		force_sample(NULL);
+	}
+#endif
+	ret = tracing_mark_write(TYPE_ASYNC_END, class_id, name, cookie, 0, 0);
+	return ret;
+}
+
+int met_tag_oneshot_real(unsigned int class_id, const char *name, unsigned int value)
+{
+	int ret;
+
+	ret = tracing_mark_write(TYPE_ONESHOT, class_id, name, value, 0, 0);
+#if 0
+	if ((met_switch.mode & MT_SWITCH_TAGPOLLING)) {
+		/* tag polling only enable when MT_SWITCH_TAGPOLLING is config */
+		force_sample(NULL);
+	}
+#endif
+	return ret;
+}
+EXPORT_SYMBOL(met_tag_oneshot_real);
+
+int met_tag_userdata_real(char *pData)
+{
+	MET_TRACE("%s\n", pData);
+	return 0;
+}
+
+int met_tag_dump_real(unsigned int class_id, const char *name, void *data, unsigned int length)
+{
+	int ret;
+
+	if ((dump_data_size + length + sizeof(int)) > dump_buffer_size) {
+		if (options_flag & OPFLAG_OVERWRITE) {
+			dump_overrun_size = dump_data_size;
+			dump_overrun++;
+			memcpy(dump_buffer, &dump_seq_no, sizeof(int));
+			memcpy(dump_buffer + sizeof(int), data, length);
+			ret = tracing_mark_write(TYPE_DUMP, class_id, name,
+						 dump_seq_no++, 0, length + sizeof(int));
+			dump_data_size = length + sizeof(int);
+		} else {
+			ret = tracing_mark_write(TYPE_DUMP, class_id, name, dump_seq_no++, 0, 0);
+		}
+	} else {
+		memcpy(dump_buffer + dump_data_size, &dump_seq_no, sizeof(int));
+		memcpy(dump_buffer + dump_data_size + sizeof(int), data, length);
+		ret = tracing_mark_write(TYPE_DUMP, class_id, name,
+					 dump_seq_no++, dump_data_size, length + sizeof(int));
+		dump_data_size += length + sizeof(int);
+	}
+	return ret;
+}
+
+int met_tag_disable_real(unsigned int class_id)
+{
+	int i;
+
+	mutex_lock(&bltab.mlock);
+
+	if (class_id == MET_CLASS_ALL) {
+		bltab.flag |= MET_CLASS_ALL;
+		mutex_unlock(&bltab.mlock);
+		return 0;
+	}
+
+	for (i = 0; i < MAX_EVENT_CLASS; i++) {
+		if ((bltab.flag & (1 << i)) == 0) {
+			bltab.class_id[i] = class_id;
+			bltab.flag |= (1 << i);
+			mutex_unlock(&bltab.mlock);
+			return 0;
+		}
+	}
+
+	mutex_unlock(&bltab.mlock);
+	return -1;
+}
+
+int met_tag_enable_real(unsigned int class_id)
+{
+	int i;
+
+	mutex_lock(&bltab.mlock);
+
+	if (class_id == MET_CLASS_ALL) {
+		bltab.flag &= (~MET_CLASS_ALL);
+		mutex_unlock(&bltab.mlock);
+		return 0;
+	}
+
+	for (i = 0; i < MAX_EVENT_CLASS; i++) {
+		if ((bltab.flag & (1 << i)) && (bltab.class_id[i] == class_id)) {
+			bltab.flag &= (~(1 << i));
+			bltab.class_id[i] = 0;
+			mutex_unlock(&bltab.mlock);
+			return 0;
+		}
+	}
+
+	mutex_unlock(&bltab.mlock);
+	return -1;
+}
+
+int met_set_dump_buffer_real(int size)
+{
+	if (dump_buffer_size && dump_buffer) {
+		free_pages((unsigned long)dump_buffer, get_order(dump_buffer_size));
+		dump_data_size = 0;
+		dump_overrun = 0;
+		dump_overrun_size = 0;
+		dump_seq_no = 0;
+		dump_buffer_size = 0;
+	}
+	/* size is 0 means free dump buffer */
+	if (size == 0)
+		return 0;
+
+	if (size < 0)
+		return -1;
+
+	size = (size + (PAGE_SIZE - 1)) & (~(PAGE_SIZE - 1));
+	dump_buffer = (void *)__get_free_pages(GFP_KERNEL, get_order(size));
+	if (dump_buffer == NULL) {
+		ERRF("can not allocate buffer to copy\n");
+		return -ENOMEM;
+	}
+
+	dump_buffer_size = size;
+	return dump_buffer_size;
+}
+
+int met_save_dump_buffer_real(const char *pathname)
+{
+	int size, ret = 0;
+	struct file *outfp = NULL;
+	mm_segment_t oldfs;
+
+	if (dump_data_size == 0)
+		return 0;
+
+	if (dump_data_size < 0 || dump_overrun_size < 0)
+		return -1;
+
+	if (dump_buffer == NULL || dump_buffer_size <= 0)
+		return -1;
+
+	if (dump_overrun)
+		size = dump_overrun_size;
+	else
+		size = dump_data_size;
+
+	if (size >= dump_buffer_size)
+		return -1;
+
+	oldfs = get_fs();
+	set_fs(KERNEL_DS);
+
+	outfp = filp_open(pathname, O_WRONLY | O_TRUNC | O_CREAT, 0664);
+	if (unlikely(outfp == NULL)) {
+		ERRF("can not open saved file for write\n");
+		return -EIO;
+	}
+
+	ret = vfs_write(outfp, dump_buffer, size, &(outfp->f_pos));
+	if (ret < 0)
+		ERRF("can not write to dump file\n");
+	else {
+		dump_data_size = 0;
+		dump_overrun = 0;
+		dump_overrun_size = 0;
+		dump_seq_no = 0;
+	}
+
+	set_fs(oldfs);
+
+	if (outfp != NULL)
+		filp_close(outfp, NULL);
+
+	return 0;
+}
+
+int met_save_log_real(const char *pathname)
+{
+	int len, ret = 0;
+	struct file *infp = NULL;
+	struct file *outfp = NULL;
+	void *ptr = NULL;
+	mm_segment_t oldfs;
+
+	infp = filp_open("/sys/kernel/debug/tracing/trace", O_RDONLY, 0);
+	if (unlikely(infp == NULL)) {
+		ERRF("can not open trace file for read\n");
+		ret = -1;
+		goto save_out;
+	}
+
+	outfp = filp_open(pathname, O_WRONLY | O_TRUNC | O_CREAT, 0664);
+	if (unlikely(outfp == NULL)) {
+		ERRF("can not open saved file for write\n");
+		ret = -2;
+		goto save_out;
+	}
+
+	ptr = (void *)__get_free_pages(GFP_KERNEL, 2);
+	if (ptr == NULL) {
+		ERRF("can not allocate buffer to copy\n");
+		ret = -3;
+		goto save_out;
+	}
+
+	oldfs = get_fs();
+	set_fs(KERNEL_DS);
+
+	while (1) {
+		len = vfs_read(infp, ptr, PAGE_SIZE << 2, &(infp->f_pos));
+		if (len < 0) {
+			ERRF("can not read from trace file\n");
+			ret = -3;
+			break;
+		} else if (len == 0) {
+			break;
+		}
+
+		ret = vfs_write(outfp, ptr, len, &(outfp->f_pos));
+		if (ret < 0) {
+			ERRF("can not write to saved file\n");
+			break;
+		}
+	}
+
+	set_fs(oldfs);
+
+save_out:
+	if (ptr != NULL)
+		free_pages((unsigned long)ptr, 2);
+	if (infp != NULL)
+		filp_close(infp, NULL);
+	if (outfp != NULL)
+		filp_close(outfp, NULL);
+
+	return ret;
+}
+
+#ifdef BUILD_WITH_MET
+#include <linux/module.h>
+#include <linux/uaccess.h>
+/* =========================================================================== */
+/* misc file nodes */
+/* =========================================================================== */
+static ssize_t enable_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	int i;
+
+	i = snprintf(buf, PAGE_SIZE, "%d\n", (bltab.flag >> 31) ? 0 : 1);
+	return i;
+}
+
+static ssize_t enable_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf,
+			    size_t n)
+{
+	int value;
+
+	if ((n == 0) || (buf == NULL))
+		return -EINVAL;
+	if (kstrtoint(buf, 0, &value) != 0)
+		return -EINVAL;
+
+	mutex_lock(&bltab.mlock);
+
+	if (value == 1)
+		bltab.flag &= (~MET_CLASS_ALL);
+	else
+		bltab.flag |= MET_CLASS_ALL;
+
+	mutex_unlock(&bltab.mlock);
+
+	return n;
+}
+
+static struct kobject *kobj_tag;
+static struct kobj_attribute enable_attr = __ATTR(enable, 0664, enable_show, enable_store);
+
+static ssize_t dump_buffer_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	int i, size;
+
+	if (dump_overrun)
+		size = dump_overrun_size;
+	else
+		size = dump_data_size;
+
+	i = snprintf(buf, PAGE_SIZE, "Buffer Size (KB)=%d\nData Size (KB)=%d\nOverrun=%d\n",
+		     dump_buffer_size >> 10, size >> 10, dump_overrun);
+	return i;
+}
+
+static ssize_t dump_buffer_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf,
+				 size_t n)
+{
+	int ret, value;
+
+	if ((n == 0) || (buf == NULL))
+		return -EINVAL;
+	if (kstrtoint(buf, 0, &value) != 0)
+		return -EINVAL;
+
+	ret = met_set_dump_buffer_real(value << 10);
+
+	if (ret < 0)
+		return ret;
+
+	return n;
+}
+
+static struct kobj_attribute dump_buffer_attr =
+__ATTR(dump_buffer_kb, 0664, dump_buffer_show, dump_buffer_store);
+
+static ssize_t options_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	int i = 0;
+
+	buf[0] = 0;
+
+	if (options_flag == 0) {
+		strncat(buf, "none\n", PAGE_SIZE - 1 - i);
+		i += 5;
+	}
+
+	if (options_flag & OPFLAG_OVERWRITE) {
+		strncat(buf, "overwrite\n", PAGE_SIZE - 1 - i);
+		i += 10;
+	}
+
+	return i;
+}
+
+static ssize_t options_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf,
+			     size_t n)
+{
+	if ((n == 0) || (buf == NULL))
+		return -EINVAL;
+
+	if ((n == 1) && (buf[0] == 0xA)) {
+		options_flag = 0;
+		return n;
+	}
+
+	if (strncmp(buf, "overwrite", 9) == 0)
+		options_flag |= OPFLAG_OVERWRITE;
+	else
+		return -EINVAL;
+
+	return n;
+}
+
+static struct kobj_attribute options_attr = __ATTR(options, 0664, options_show, options_store);
+
+int tag_reg(struct file_operations *const fops, struct kobject *kobj)
+{
+	int ret;
+
+	kobj_tag = kobject_create_and_add("tag", kobj);
+	if (kobj_tag == NULL) {
+		ERRF("can not create kobject: kobj_bus\n");
+		return -1;
+	}
+
+	ret = sysfs_create_file(kobj_tag, &enable_attr.attr);
+	if (ret != 0) {
+		ERRF("Failed to create enable in sysfs\n");
+		kobject_del(kobj_tag);
+		kobject_put(kobj_tag);
+		kobj_tag = NULL;
+		return ret;
+	}
+
+	ret = sysfs_create_file(kobj_tag, &dump_buffer_attr.attr);
+	if (ret != 0) {
+		ERRF("Failed to create dump_buffer in sysfs\n");
+		sysfs_remove_file(kobj_tag, &enable_attr.attr);
+		kobject_del(kobj_tag);
+		kobject_put(kobj_tag);
+		kobj_tag = NULL;
+		return ret;
+	}
+
+	ret = sysfs_create_file(kobj_tag, &options_attr.attr);
+	if (ret != 0) {
+		ERRF("Failed to create options in sysfs\n");
+		sysfs_remove_file(kobj_tag, &enable_attr.attr);
+		sysfs_remove_file(kobj_tag, &dump_buffer_attr.attr);
+		kobject_del(kobj_tag);
+		kobject_put(kobj_tag);
+		kobj_tag = NULL;
+		return ret;
+	}
+
+	met_tag_init();
+	met_ext_api.met_tag_start = met_tag_start_real;
+	met_ext_api.met_tag_end = met_tag_end_real;
+	met_ext_api.met_tag_async_start = met_tag_async_start_real;
+	met_ext_api.met_tag_async_end = met_tag_async_end_real;
+	met_ext_api.met_tag_oneshot = met_tag_oneshot_real;
+	met_ext_api.met_tag_userdata = met_tag_userdata_real;
+	met_ext_api.met_tag_dump = met_tag_dump_real;
+	met_ext_api.met_tag_disable = met_tag_disable_real;
+	met_ext_api.met_tag_enable = met_tag_enable_real;
+	met_ext_api.met_set_dump_buffer = met_set_dump_buffer_real;
+	met_ext_api.met_save_dump_buffer = met_save_dump_buffer_real;
+	met_ext_api.met_save_log = met_save_log_real;
+	met_ext_api.met_sched_switch = met_sched_switch;
+	return 0;
+}
+
+int tag_unreg(void)
+{
+	met_ext_api.met_tag_start = NULL;
+	met_ext_api.met_tag_end = NULL;
+	met_ext_api.met_tag_async_start = NULL;
+	met_ext_api.met_tag_async_end = NULL;
+	met_ext_api.met_tag_oneshot = NULL;
+	met_ext_api.met_tag_userdata = NULL;
+	met_ext_api.met_tag_dump = NULL;
+	met_ext_api.met_tag_disable = NULL;
+	met_ext_api.met_tag_enable = NULL;
+	met_ext_api.met_set_dump_buffer = NULL;
+	met_ext_api.met_save_dump_buffer = NULL;
+	met_ext_api.met_save_log = NULL;
+	met_ext_api.met_show_bw_limiter = NULL;
+	met_ext_api.met_reg_bw_limiter = NULL;
+	met_ext_api.met_show_clk_tree = NULL;
+	met_ext_api.met_reg_clk_tree = NULL;
+	met_ext_api.met_sched_switch = NULL;
+	met_tag_uninit();
+	sysfs_remove_file(kobj_tag, &enable_attr.attr);
+	sysfs_remove_file(kobj_tag, &dump_buffer_attr.attr);
+	sysfs_remove_file(kobj_tag, &options_attr.attr);
+	kobject_del(kobj_tag);
+	kobject_put(kobj_tag);
+	kobj_tag = NULL;
+	return 0;
+}
+
+#endif				/* BUILD_WITH_MET */
+
+#else				/* not MET_USER_EVENT_SUPPORT */
+
+#ifdef BUILD_WITH_MET
+int tag_reg(void *p, void *q)
+{
+	return 0;
+}
+
+int tag_unreg(void)
+{
+	return 0;
+}
+#endif
+
+#endif				/* MET_USER_EVENT_SUPPORT */
diff --git a/src/devtools/met-driver/4.19/common/met_vcoredvfs.c b/src/devtools/met-driver/4.19/common/met_vcoredvfs.c
new file mode 100644
index 0000000..ea7dd70
--- /dev/null
+++ b/src/devtools/met-driver/4.19/common/met_vcoredvfs.c
@@ -0,0 +1,314 @@
+/*
+ * Copyright (C) 2019 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/dma-mapping.h>
+
+#include <helio-dvfsrc.h>
+
+#define MET_USER_EVENT_SUPPORT
+#include "met_drv.h"
+#include "trace.h"
+
+#include "core_plf_init.h"
+#include "core_plf_trace.h"
+
+/*======================================================================*/
+/*	Global variable definitions					*/
+/*======================================================================*/
+
+/* Global variables */
+struct metdevice met_vcoredvfs;
+int polling_mode = 1;
+
+/* external symbols */
+#define DEFAULT_INFO_NUM 3
+#define DEFAULT_SRC_NUM 1
+
+char *default_info_name[DEFAULT_INFO_NUM] = {
+	"OPP",
+	"VOLT",
+	"FREQ"
+};
+
+char *default_src_name[DEFAULT_SRC_NUM] = {
+	"MD2SPM"
+};
+
+unsigned int opp_info[DEFAULT_INFO_NUM];
+unsigned int src_req[DEFAULT_SRC_NUM];
+
+static int met_vcorefs_get_num_opp(void)
+{
+	if (vcorefs_get_num_opp_symbol)
+		return vcorefs_get_num_opp_symbol();
+	else
+		return 0;
+}
+
+static int met_vcorefs_get_opp_info_num(void)
+{
+	if (vcorefs_get_opp_info_num_symbol)
+		return vcorefs_get_opp_info_num_symbol();
+	else
+		return DEFAULT_INFO_NUM;
+}
+
+
+static int met_vcorefs_get_src_req_num(void)
+{
+	if (vcorefs_get_src_req_num_symbol)
+		return vcorefs_get_src_req_num_symbol();
+	else
+		return DEFAULT_SRC_NUM;
+}
+
+
+static char **met_vcorefs_get_opp_info_name(void)
+{
+	if (vcorefs_get_opp_info_name_symbol)
+		return vcorefs_get_opp_info_name_symbol();
+	else
+		return default_info_name;
+}
+
+static char **met_vcorefs_get_src_req_name(void)
+{
+	if (vcorefs_get_src_req_name_symbol)
+		return vcorefs_get_src_req_name_symbol();
+	else
+		return default_src_name;
+}
+
+static unsigned int *met_vcorefs_get_opp_info(void)
+{
+	if (vcorefs_get_opp_info_symbol)
+		return vcorefs_get_opp_info_symbol();
+
+	return opp_info;
+}
+
+static unsigned int *met_vcorefs_get_src_req(void)
+{
+	if (vcorefs_get_src_req_symbol)
+		return vcorefs_get_src_req_symbol();
+
+	return src_req;
+}
+
+
+/*======================================================================*/
+/*	File Node definitions					*/
+/*======================================================================*/
+
+static struct kobject *kobj_met_vcoredvfs;
+
+static ssize_t vcorefs_polling_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf);
+static ssize_t vcorefs_polling_store(struct kobject *kobj,
+				   struct kobj_attribute *attr, const char *buf, size_t n);
+static struct kobj_attribute vcorefs_polling_attr =
+__ATTR(vcorefs_polling, 0664, vcorefs_polling_show, vcorefs_polling_store);
+
+/*======================================================================*/
+/*	Utilities					*/
+/*======================================================================*/
+
+static inline int do_vcoredvfs(void)
+{
+	return met_vcoredvfs.mode;
+}
+
+/*======================================================================*/
+/*	Data Output					*/
+/*======================================================================*/
+
+noinline void vcorefs(unsigned char cnt, unsigned int *value)
+{
+	char *SOB, *EOB;
+
+	MET_TRACE_GETBUF(&SOB, &EOB);
+	EOB = ms_formatH_EOL(EOB, cnt, value);
+	MET_TRACE_PUTBUF(SOB, EOB);
+}
+
+noinline void vcorefs_kicker(unsigned char cnt, int *value)
+{
+	char *SOB, *EOB;
+
+	MET_TRACE_GETBUF(&SOB, &EOB);
+	EOB = ms_formatH_EOL(EOB, cnt, value);
+	MET_TRACE_PUTBUF(SOB, EOB);
+}
+
+noinline void ms_vcorefs(unsigned char cnt, unsigned int *value)
+{
+	char *SOB, *EOB;
+
+	MET_TRACE_GETBUF(&SOB, &EOB);
+	EOB = ms_formatH_EOL(EOB, cnt, value);
+	MET_TRACE_PUTBUF(SOB, EOB);
+}
+
+/*======================================================================*/
+/*	Callback functions						*/
+/*======================================================================*/
+void vcoredvfs_irq(int opp)
+{
+	int num;
+	unsigned int *out;
+
+	num = met_vcorefs_get_opp_info_num();
+	out = met_vcorefs_get_opp_info();
+
+	vcorefs(num, out);
+}
+
+/*======================================================================*/
+/*	File Node Operations						*/
+/*======================================================================*/
+static ssize_t vcorefs_polling_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%d\n", polling_mode);
+}
+
+static ssize_t vcorefs_polling_store(struct kobject *kobj,
+				   struct kobj_attribute *attr, const char *buf, size_t n)
+{
+	int value;
+
+	if ((n == 0) || (buf == NULL))
+		return -EINVAL;
+
+	if (kstrtoint(buf, 0, &value) != 0)
+		return -EINVAL;
+
+	if (value < 0)
+		return -EINVAL;
+
+	polling_mode = value;
+
+	return n;
+}
+
+/*======================================================================*/
+/*	MET Device Operations						*/
+/*======================================================================*/
+static int met_vcoredvfs_create(struct kobject *parent)
+{
+	int ret = 0;
+
+	kobj_met_vcoredvfs = parent;
+
+	ret = sysfs_create_file(kobj_met_vcoredvfs, &vcorefs_polling_attr.attr);
+	if (ret != 0) {
+		pr_debug("Failed to create vcoredvfs in sysfs\n");
+		return ret;
+	}
+
+	return ret;
+}
+
+static void met_vcoredvfs_delete(void)
+{
+	sysfs_remove_file(kobj_met_vcoredvfs, &vcorefs_polling_attr.attr);
+}
+
+static void met_vcoredvfs_start(void)
+{
+	vcoredvfs_irq(-1);
+}
+
+static void met_vcoredvfs_stop(void)
+{
+	vcoredvfs_irq(-1);
+}
+
+static void met_vcoredvfs_polling(unsigned long long stamp, int cpu)
+{
+	int num;
+	unsigned int *out;
+
+	if (!do_vcoredvfs())
+		return;
+
+	/* vcorefs opp information */
+	if (polling_mode)
+		vcoredvfs_irq(-1);
+
+	/* vcorefs source request */
+	num = met_vcorefs_get_src_req_num();
+	out = met_vcorefs_get_src_req();
+
+	ms_vcorefs(num, out);
+}
+
+static const char help[] =
+	"  --vcoredvfs				monitor VCORE DVFS\n";
+static int vcoredvfs_print_help(char *buf, int len)
+{
+	return snprintf(buf, PAGE_SIZE, help);
+}
+
+static const char header_dvfs[] = "met-info [000] 0.0: met_vcorefs_header:";
+
+static const char header_ms_dvfs[] = "met-info [000] 0.0: ms_vcorefs_header:";
+
+static int vcoredvfs_print_header(char *buf, int len)
+{
+	int ret = 0;
+	int idx = 0;
+	int num = 0;
+	char **header;
+
+	ret = snprintf(buf, PAGE_SIZE, "met-info [000] 0.0: met_vcorefs_cfg: NUM_OPP:%d\n", met_vcorefs_get_num_opp());
+
+	/* opp information */
+	num = met_vcorefs_get_opp_info_num();
+	header = met_vcorefs_get_opp_info_name();
+
+	ret += snprintf(buf + ret, PAGE_SIZE, header_dvfs);
+	for (idx = 0; idx < num; idx++)
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "%s,", header[idx]);
+	buf[strlen(buf)-1] = '\n';
+
+	/* source requests */
+	num = met_vcorefs_get_src_req_num();
+	header = met_vcorefs_get_src_req_name();
+
+	ret += snprintf(buf + ret, PAGE_SIZE - ret, header_ms_dvfs);
+	for (idx = 0; idx < num; idx++)
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "%s,", header[idx]);
+	buf[strlen(buf)-1] = '\n';
+
+	met_vcoredvfs.mode = 0;
+
+	return ret;
+}
+
+struct metdevice met_vcoredvfs = {
+	.name = "vcoredvfs",
+	.owner = THIS_MODULE,
+	.type = MET_TYPE_BUS,
+	.create_subfs = met_vcoredvfs_create,
+	.delete_subfs = met_vcoredvfs_delete,
+	.cpu_related = 0,
+	.start = met_vcoredvfs_start,
+	.stop = met_vcoredvfs_stop,
+	.polling_interval = 1,	/* ms */
+	.timed_polling = met_vcoredvfs_polling,
+	.print_help = vcoredvfs_print_help,
+	.print_header = vcoredvfs_print_header,
+};
diff --git a/src/devtools/met-driver/4.19/common/met_wall_time.c b/src/devtools/met-driver/4.19/common/met_wall_time.c
new file mode 100644
index 0000000..4c768fa
--- /dev/null
+++ b/src/devtools/met-driver/4.19/common/met_wall_time.c
@@ -0,0 +1,271 @@
+/*
+ * Copyright (C) 2019 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/sched/clock.h>
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <asm/div64.h>
+#include <linux/types.h>
+#include <linux/kallsyms.h>
+#include <linux/ktime.h>
+
+#define MET_USER_EVENT_SUPPORT
+#include "met_drv.h"
+#include "trace.h"
+#include "interface.h"
+
+#define MODE_CUSOM_CLKSRC       2
+struct metdevice met_wall_time;
+
+/**                                                                        */
+/* How to add a new clocksource:                                           */
+/*                                                                         */
+/* 1. add constant for new clocksource in #define-macro                    */
+/* 2. declare new weakref function                                         */
+/* 3. implement handler functions:                                         */
+/*     (1) clksrc_attr_t::*ready:                                          */
+/*         check if ...                                                    */
+/*         (i) clocksource correctly working                               */
+/*         (ii) weakref function is not null                               */
+/*     (2) clksrc_attr_t::*get_cnt: read clocksource from weakref function */
+/* 4. place attrs of new clocksource into clksrc_attr_tb                   */
+/* 5. update DEFAULT_CLKSRC_STR                                            */
+/* 6. update help message                                                  */
+/**                                                                        */
+
+#define __SYS_TIMER             0x0
+#define __GPT1                  0x1
+#define __GPT2                  0x2
+#define __GPT3                  0x3
+#define __GPT4                  0x4
+#define __GPT5                  0x5
+#define __GPT6                  0x6
+
+#define DEFAULT_CLKSRC_STR      "SYS_TIMER"
+
+extern u64 met_arch_counter_get_cntvct(void);
+extern ktime_t ktime_get(void);
+u64 (*met_arch_counter_get_cntvct_symbol)(void);
+
+int __sys_timer_get_cnt(u8 clksrc, u64 *cycles)
+{
+	if (met_arch_counter_get_cntvct_symbol)
+		*cycles = met_arch_counter_get_cntvct_symbol();
+	return 0;
+}
+
+
+struct clksrc_attr_t {
+	u8 clksrc;
+	const char *clksrc_str;
+	/* checks if clksrc/get_cnt function is working/available */
+	int (*clksrc_ready)(u8 clksrc);
+	int (*clksrc_get_cnt)(u8 clksrc, u64 *cycles);
+};
+
+struct clksrc_attr_t clksrc_attr_tb[] = {
+	{__SYS_TIMER, "SYS_TIMER", NULL, __sys_timer_get_cnt},
+	/* {__GPT1, "GPT1", __gpt_timer_ready, __gpt_timer_get_cnt}, */
+	/* {__GPT2, "GPT2", __gpt_timer_ready, __gpt_timer_get_cnt}, */
+	/* {__GPT3, "GPT3", __gpt_timer_ready, __gpt_timer_get_cnt}, */
+	/* {__GPT4, "GPT4", __gpt_timer_ready, __gpt_timer_get_cnt}, */
+	/* {__GPT5, "GPT5", __gpt_timer_ready, __gpt_timer_get_cnt}, */
+	/* {__GPT6, "GPT6", __gpt_timer_ready, __gpt_timer_get_cnt}, */
+};
+
+static const struct clksrc_attr_t *lookup_clksrc_attr_tb(const char *clksrc_str, int len);
+static const struct clksrc_attr_t *wall_time_attr;
+
+/* definitions for auto-sampling of working freq. of clocksource */
+/* maximum tolerable error percentage(%) of sampled clock freq */
+#define FREQ_ERR_PERCENT        3
+
+/* expected working freq. of clocksources */
+static const u32 freq_level[] = { 32768, 13000000, 26000000 };
+
+static u32 lookup_freq_level(u32 freq);
+
+/* flag indicating whether sampling is on-going */
+static u32 do_sample;
+static u32 freq;
+static u64 start_us_ts;
+static u64 start_wall_time;
+/* end definitions for sampling of freq. */
+
+static void wall_time_start(void)
+{
+	met_arch_counter_get_cntvct_symbol = (void *)symbol_get(met_arch_counter_get_cntvct);
+
+	if (met_wall_time.mode != MODE_CUSOM_CLKSRC) {
+		wall_time_attr = lookup_clksrc_attr_tb(DEFAULT_CLKSRC_STR,
+						       strlen(DEFAULT_CLKSRC_STR));
+	}
+
+	freq = 0;
+	do_sample = 1;
+
+	if (wall_time_attr) {
+
+		/* XXX: always use CPU 0 */
+		start_us_ts = cpu_clock(0);
+		wall_time_attr->clksrc_get_cnt(wall_time_attr->clksrc, &start_wall_time);
+
+		/* us_ts = ap_ts/1000; */
+		do_div(start_us_ts, 1000);
+	}
+}
+
+noinline void met_mono_time(void)
+{
+	/* mono time vs local time */
+	u64 cur_local_ns = sched_clock();
+	ktime_t cur_mono_ts = ktime_get();
+
+	MET_TRACE("TS.APTS=%llu TS.MONO=%llu\n", (unsigned long long)cur_local_ns,
+		(unsigned long long)ktime_to_ns(cur_mono_ts));
+}
+
+noinline void met_ap_wall_time(unsigned long long ts, int cpu)
+{
+	u64 ap_ts;
+	u64 us_ts;
+	u64 sec;
+	u64 usec;
+	u64 cycles;
+	u64 f;
+
+	if (wall_time_attr) {
+
+		wall_time_attr->clksrc_get_cnt(wall_time_attr->clksrc, &cycles);
+		ap_ts = cpu_clock(cpu);
+
+		us_ts = ap_ts;
+		do_div(us_ts, 1000);	/* us_ts = ap_ts/1000; */
+
+		sec = us_ts;
+		usec = do_div(sec, 1000000);	/* sec = us_ts/1000000; usec = us_ts%1000000; */
+		MET_TRACE("TS.APTS=%llu.%06llu WCLK=%llu\n", (unsigned long long)sec,
+			   (unsigned long long)usec, (unsigned long long)cycles);
+
+		// print local time vs mono time for gpu time shift
+		met_mono_time();
+
+		if (do_sample) {
+
+			do_sample = 0;
+
+			f = (cycles - start_wall_time) * 1000000;
+			do_div(f, us_ts - start_us_ts);
+
+			/* don't worry about the u64 -> u32 assignment,           */
+			/* sampled wall-clock freq is expected to be below 2^32-1 */
+			freq = lookup_freq_level(f);
+
+			/* debug message */
+			/* MET_TRACE("wall_time debug: result: %u," */
+			/*         "start cycle: %llu, end cycle: %llu, cycle diff: %llu," */
+			/*         "start us: %llu, end us: %llu, us diff: %llu", */
+			/*         f, */
+			/*         start_wall_time, cycles, cycles - start_wall_time, */
+			/*         start_us_ts, us_ts, us_ts - start_us_ts); */
+
+			if (freq != 0)
+				met_tag_oneshot_real(33880, "_WCLK_FREQ_", freq);
+		}
+	}
+}
+
+static const char help[] =
+"  --wall_time                             output wall-clock syncing info in system timer\n";
+/* "  --wall_time=SYS_TIMER|GPT[1-6]  output wall-clock syncing info in custom clocksource\n"; */
+
+static int wall_time_print_help(char *buf, int len)
+{
+	return snprintf(buf, PAGE_SIZE, help);
+}
+
+static const char *header =
+"met-info [000] 0.0: WCLK: %d\n"
+"met-info [000] 0.0: clocksource: %s\n";
+
+static int wall_time_print_header(char *buf, int len)
+{
+	return snprintf(buf, len, header,
+			freq == 0 ? -1 : freq,
+			wall_time_attr ? wall_time_attr->clksrc_str : "NONE");
+}
+
+static int wall_time_process_argument(const char *arg, int len)
+{
+	/* reset wall-time clocksource */
+	wall_time_attr = lookup_clksrc_attr_tb(arg, len);
+
+	if (!wall_time_attr) {
+		met_wall_time.mode = 0;
+		return -1;
+	}
+
+	met_wall_time.mode = MODE_CUSOM_CLKSRC;
+	return 0;
+}
+
+static const struct clksrc_attr_t *lookup_clksrc_attr_tb(const char *clksrc_str, int len)
+{
+	int i;
+	const struct clksrc_attr_t *attr;
+	int tb_nmemb = sizeof(clksrc_attr_tb) / sizeof(*clksrc_attr_tb);
+
+	for (i = 0; i < tb_nmemb; i++) {
+
+		attr = clksrc_attr_tb + i;
+
+		if (strlen(attr->clksrc_str) == len &&
+		    strncmp(clksrc_str, attr->clksrc_str, len) == 0) {
+			return attr;
+		}
+	}
+
+	return NULL;
+}
+
+static u32 lookup_freq_level(u32 freq)
+{
+
+	int ii;
+	int freq_nmemb = sizeof(freq_level) / sizeof(*freq_level);
+	u32 fdiff;
+
+	for (ii = 0; ii < freq_nmemb; ii++) {
+		fdiff = freq_level[ii] > freq ? freq_level[ii] - freq : freq - freq_level[ii];
+		if (fdiff < freq_level[ii] * FREQ_ERR_PERCENT / 100)
+			return freq_level[ii];
+	}
+
+	return 0;
+}
+
+struct metdevice met_wall_time = {
+	.name = "wall_time",
+	.owner = THIS_MODULE,
+	.type = MET_TYPE_BUS,
+	.cpu_related = 0,
+	.start = wall_time_start,
+	.polling_interval = 1000,
+	.timed_polling = met_ap_wall_time,
+	.print_help = wall_time_print_help,
+	.print_header = wall_time_print_header,
+	.process_argument = wall_time_process_argument,
+};
+EXPORT_SYMBOL(met_wall_time);
diff --git a/src/devtools/met-driver/4.19/common/mips_pmu_hw.c b/src/devtools/met-driver/4.19/common/mips_pmu_hw.c
new file mode 100644
index 0000000..03178f0
--- /dev/null
+++ b/src/devtools/met-driver/4.19/common/mips_pmu_hw.c
@@ -0,0 +1,438 @@
+/*
+ * Copyright (C) 2019 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <asm/system.h>
+#include <linux/smp.h>
+
+#include "cpu_pmu.h"
+#include "mips_pmu_name.h"
+
+struct chip_pmu {
+	enum cpu_type_enum type;
+	struct pmu_desc **desc;
+	void *refptr;
+	const char *cpu_name;
+	unsigned int pmu_desc_size;
+	unsigned int max_hw_events;
+	unsigned int max_reg_count;
+};
+
+struct pmu_desc *mips_pmu_desc[MIPS_MAX_HWEVENTS];
+
+static struct chip_pmu chips[] = {
+	{CPU_1004K, mips_pmu_desc, (void *)mips_1004k_pmu_desc, "MIPS_1004K",
+	 MIPS_1004K_PMU_DESC_SIZE, MIPS_1004K_PMU_DESC_COUNT, PMU_1004K_MAX_HW_REGS},
+};
+
+static struct chip_pmu chip_unknown = { CPU_UNKNOWN, NULL, NULL, "Unknown CPU", 0, 0, 0 };
+
+#define CHIP_PMU_COUNT (sizeof(chips) / sizeof(struct chip_pmu))
+static struct chip_pmu *chip;
+#define M_CONFIG1_PC    (1 << 4)
+
+#define M_PERFCTL_EXL           (1  <<  0)
+#define M_PERFCTL_KERNEL        (1  <<  1)
+#define M_PERFCTL_SUPERVISOR        (1  <<  2)
+#define M_PERFCTL_USER          (1  <<  3)
+#define M_PERFCTL_INTERRUPT_ENABLE  (1  <<  4)
+#define M_PERFCTL_EVENT(event)      (((event) & 0x3ff)  << 5)
+#define M_PERFCTL_VPEID(vpe)        ((vpe)    << 16)
+
+#ifdef CONFIG_CPU_BMIPS5000
+#define M_PERFCTL_MT_EN(filter)     0
+#else				/* !CONFIG_CPU_BMIPS5000 */
+#define M_PERFCTL_MT_EN(filter)     ((filter) << 20)
+#endif				/* CONFIG_CPU_BMIPS5000 */
+
+#define    M_TC_EN_ALL          M_PERFCTL_MT_EN(0)
+#define    M_TC_EN_VPE          M_PERFCTL_MT_EN(1)
+#define    M_TC_EN_TC           M_PERFCTL_MT_EN(2)
+#define M_PERFCTL_TCID(tcid)        ((tcid)   << 22)
+#define M_PERFCTL_WIDE          (1  << 30)
+#define M_PERFCTL_MORE          (1  << 31)
+#define M_PERFCTL_TC            (1  << 30)
+
+#define M_PERFCTL_COUNT_EVENT_WHENEVER  (M_PERFCTL_EXL |        \
+		M_PERFCTL_KERNEL |      \
+		M_PERFCTL_USER |        \
+		M_PERFCTL_SUPERVISOR |      \
+		M_PERFCTL_INTERRUPT_ENABLE)
+
+#ifdef CONFIG_MIPS_MT_SMP
+#define M_PERFCTL_CONFIG_MASK       0x3fff801f
+#else
+#define M_PERFCTL_CONFIG_MASK       0x1f
+#endif
+#define M_PERFCTL_EVENT_MASK        0xfe0
+
+#define vpe_id()    0
+
+/* To get current TCID*/
+#define read_c0_tcbind() __read_32bit_c0_register($2, 2)
+
+struct cpu_hw_events {
+	unsigned int config_base[MIPS_MAX_HWEVENTS];
+	unsigned int saved_ctrl[MIPS_MAX_HWEVENTS];
+};
+
+DEFINE_PER_CPU(struct cpu_hw_events, cpu_hw_events) = {
+	.config_base = {
+	0, 0, 0, 0}, .saved_ctrl = {
+0, 0, 0, 0},};
+
+static enum cpu_type_enum mips_get_ic(void)
+{
+	unsigned int value = current_cpu_type();
+
+	/* pr_debug("ic value: %X\n", value); */
+	return value;
+}
+
+static int __n_counters(void)
+{
+	if (!(read_c0_config1() & M_CONFIG1_PC))
+		return 0;
+	if (!(read_c0_perfctrl0() & M_PERFCTL_MORE))
+		return 1;
+	if (!(read_c0_perfctrl1() & M_PERFCTL_MORE))
+		return 2;
+	if (!(read_c0_perfctrl2() & M_PERFCTL_MORE))
+		return 3;
+
+	return 4;
+}
+
+static int n_counters(void)
+{
+	int counters;
+
+	switch (current_cpu_type()) {
+	case CPU_R10000:
+		counters = 2;
+		break;
+	case CPU_R12000:
+	case CPU_R14000:
+		counters = 4;
+		break;
+	default:
+		counters = __n_counters();
+		break;
+	}
+
+	return counters;
+}
+
+static int mips_pmu_hw_get_counters(void)
+{
+	int count = n_counters();
+
+	/* pr_debug("pmu hw event nr: %d\n", count); */
+	return count;
+}
+
+static unsigned int mipsxx_pmu_swizzle_perf_idx(unsigned int idx)
+{
+	if (vpe_id() == 1)
+		idx = (idx + 2) & 3;
+	return idx;
+}
+
+static void mipsxx_pmu_write_counter(unsigned int idx, u64 val)
+{
+	idx = mipsxx_pmu_swizzle_perf_idx(idx);
+
+	switch (idx) {
+	case 0:
+		write_c0_perfcntr0(val);
+		return;
+	case 1:
+		write_c0_perfcntr1(val);
+		return;
+	case 2:
+		write_c0_perfcntr2(val);
+		return;
+	case 3:
+		write_c0_perfcntr3(val);
+		return;
+	}
+}
+
+static u64 mipsxx_pmu_read_counter(unsigned int idx)
+{
+	idx = mipsxx_pmu_swizzle_perf_idx(idx);
+
+	switch (idx) {
+	case 0:
+		/*
+		 * The counters are unsigned, we must cast to truncate
+		 * off the high bits.
+		 */
+		return (u32) read_c0_perfcntr0();
+	case 1:
+		return (u32) read_c0_perfcntr1();
+	case 2:
+		return (u32) read_c0_perfcntr2();
+	case 3:
+		return (u32) read_c0_perfcntr3();
+	default:
+		WARN_ONCE(1, "Invalid performance counter number (%d)\n", idx);
+		return 0;
+	}
+}
+
+
+static unsigned int mipsxx_pmu_read_control(unsigned int idx)
+{
+	idx = mipsxx_pmu_swizzle_perf_idx(idx);
+
+	switch (idx) {
+	case 0:
+		return read_c0_perfctrl0();
+	case 1:
+		return read_c0_perfctrl1();
+	case 2:
+		return read_c0_perfctrl2();
+	case 3:
+		return read_c0_perfctrl3();
+	default:
+		WARN_ONCE(1, "Invalid performance counter number (%d)\n", idx);
+		return 0;
+	}
+}
+
+static void mipsxx_pmu_write_control(unsigned int idx, unsigned int val)
+{
+	idx = mipsxx_pmu_swizzle_perf_idx(idx);
+
+	switch (idx) {
+	case 0:
+		write_c0_perfctrl0(val);
+		return;
+	case 1:
+		write_c0_perfctrl1(val);
+		return;
+	case 2:
+		write_c0_perfctrl2(val);
+		return;
+	case 3:
+		write_c0_perfctrl3(val);
+		return;
+	}
+}
+
+static int mipsxx_pmu_get_vpeid(void)
+{
+	return read_c0_tcbind() & 0xF;
+}
+
+static void mipsxx_pmu_reset_counters(int idx)
+{
+	switch (idx) {
+	case 3:
+		mipsxx_pmu_write_control(3, 0);
+		mipsxx_pmu_write_counter(3, 0);
+		break;
+	case 2:
+		mipsxx_pmu_write_control(2, 0);
+		mipsxx_pmu_write_counter(2, 0);
+		break;
+	case 1:
+		mipsxx_pmu_write_control(1, 0);
+		mipsxx_pmu_write_counter(1, 0);
+		break;
+	case 0:
+		mipsxx_pmu_write_control(0, 0);
+		mipsxx_pmu_write_counter(0, 0);
+		break;
+	}
+}
+
+static void mipsxx_pmu_enable_event(int idx, int event)
+{
+	struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
+	unsigned long flags;
+
+	WARN_ON(idx < 0 || idx >= chip->max_hw_events);
+	cpuc->saved_ctrl[idx] = M_PERFCTL_EVENT(event & 0xff) |
+	    M_PERFCTL_VPEID(mipsxx_pmu_get_vpeid()) |
+	    (cpuc->config_base[idx] & M_PERFCTL_CONFIG_MASK);
+#ifdef CONFIG_CPU_BMIPS5000
+	/* if (IS_ENABLED(CONFIG_CPU_BMIPS5000)) */
+	/* enable the counter for the calling thread */
+	cpuc->saved_ctrl[idx] |= (1 << (12 + vpe_id())) | M_PERFCTL_TC;
+#endif
+	/*
+	 * To enable pmu count
+	 */
+	local_irq_save(flags);
+	mipsxx_pmu_write_control(idx, cpuc->saved_ctrl[idx]);
+	local_irq_restore(flags);
+}
+
+static void mipsxx_pmu_disable_event(int idx)
+{
+	struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
+	unsigned long flags;
+
+	/* WARN_ON(idx < 0 || idx >= mipspmu.num_counters); */
+	WARN_ON(idx < 0 || idx >= chip->max_hw_events);
+
+	local_irq_save(flags);
+	cpuc->saved_ctrl[idx] = mipsxx_pmu_read_control(idx) & ~M_PERFCTL_COUNT_EVENT_WHENEVER;
+	mipsxx_pmu_write_control(idx, cpuc->saved_ctrl[idx]);
+	local_irq_restore(flags);
+}
+
+static int mips_pmu_hw_get_event_desc(int idx, int event, char *event_desc)
+{
+	int i;
+
+	if (event_desc == NULL) {
+		pr_debug("event_desc is NULL\n");
+		return -1;
+	}
+
+	for (i = 0; i < chip->max_reg_count; i++) {
+		if (chip->desc[idx][i].event == event) {
+			strncpy(event_desc, chip->desc[idx][i].name, MXSIZE_PMU_DESC - 1);
+			break;
+		}
+	}
+	if (i == chip->max_reg_count)
+		return -1;
+
+	return 0;
+}
+
+
+static int mips_pmu_hw_check_event(struct met_pmu *pmu, int idx, int event)
+{
+	int i;
+
+	/* to check index over run */
+	if (!chip)
+		return -1;
+
+	if (idx >= chip->max_hw_events)
+		return -1;
+
+	for (i = 0; i < chip->max_reg_count; i++) {
+		if (chip->desc[idx][i].event == event)
+			break;
+	}
+	if (i == chip->max_reg_count)
+		return -1;
+
+	return 0;
+}
+
+static void mips_pmu_hw_start(struct met_pmu *pmu, int count)
+{
+	int i;
+	int generic = count - 1;
+	struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
+
+	/* pr_debug("hw_start generic: %d\n", generic); */
+	for (i = 0; i < generic; i++) {
+		/* init config */
+		cpuc->config_base[i] = 0;
+		cpuc->config_base[i] |= M_TC_EN_VPE;
+		cpuc->config_base[i] |= M_PERFCTL_USER;
+		cpuc->config_base[i] |= M_PERFCTL_KERNEL;
+		cpuc->config_base[i] |= M_PERFCTL_EXL;
+		cpuc->config_base[i] |= M_PERFCTL_SUPERVISOR;
+		cpuc->config_base[i] &= M_PERFCTL_CONFIG_MASK;
+		 /**/ mipsxx_pmu_reset_counters(i);
+		if (pmu[i].mode == MODE_POLLING)
+			mipsxx_pmu_enable_event(i, pmu[i].event);
+	}
+	if (pmu[count - 1].mode == MODE_POLLING)
+		pr_debug("%s %d BUG!!! index over run!!\n", __func__, __LINE__);
+}
+
+static void mips_pmu_hw_stop(int count)
+{
+	int idx = 0;
+	int generic = count - 1;
+	/* pr_debug("reset %d\n", generic); */
+	for (idx = 0; idx < generic; idx++) {
+		mipsxx_pmu_reset_counters(idx);
+		mipsxx_pmu_disable_event(idx);
+	}
+}
+
+
+static unsigned int mips_pmu_hw_polling(struct met_pmu *pmu, int count, unsigned int *pmu_value)
+{
+	int i, cnt = 0;
+	int generic = count - 1;
+
+	for (i = 0; i < generic; i++) {
+		if (pmu[i].mode == MODE_POLLING) {
+			pmu_value[cnt] = mipsxx_pmu_read_counter(i);
+			cnt++;
+			mipsxx_pmu_reset_counters(i);
+			mipsxx_pmu_enable_event(i, pmu[i].event);
+		}
+	}
+	if (pmu[count - 1].mode == MODE_POLLING) {
+		pr_debug("%s %d BUG!!! index over run!!\n", __func__, __LINE__);
+		pmu_value[cnt] = 0xFFFF;
+		cnt++;
+	}
+
+	return cnt;
+}
+
+
+
+struct cpu_pmu_hw mips_pmu = {
+	.name = "mips_pmu",
+	.get_event_desc = mips_pmu_hw_get_event_desc,
+	.check_event = mips_pmu_hw_check_event,
+	.start = mips_pmu_hw_start,
+	.stop = mips_pmu_hw_stop,
+	.polling = mips_pmu_hw_polling,
+};
+
+struct cpu_pmu_hw *cpu_pmu_hw_init(void)
+{
+	int i = 0;
+	enum cpu_type_enum type;
+	int pmu_hw_count = 0;
+
+	type = mips_get_ic();
+
+	if (CPU_UNKNOWN == type || CPU_LAST == type) {
+		chip = &chip_unknown;
+		return NULL;
+	}
+	for (i = 0; i < CHIP_PMU_COUNT; i++) {
+		if (chips[i].type == type) {
+			chip = &(chips[i]);
+			break;
+		}
+	}
+	if (i == CHIP_PMU_COUNT) {
+		chip = &chip_unknown;
+		return NULL;
+	}
+
+	pmu_hw_count = mips_pmu_hw_get_counters();
+	for (i = 0; i < pmu_hw_count; i++)
+		chip->desc[i] = chip->refptr + (chip->pmu_desc_size * i);
+
+	mips_pmu.nr_cnt = pmu_hw_count + 1;
+	mips_pmu.cpu_name = chip->cpu_name;
+	return &mips_pmu;
+}
diff --git a/src/devtools/met-driver/4.19/common/mips_pmu_name.h b/src/devtools/met-driver/4.19/common/mips_pmu_name.h
new file mode 100644
index 0000000..423dc87
--- /dev/null
+++ b/src/devtools/met-driver/4.19/common/mips_pmu_name.h
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2019 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+
+#ifndef _MIPS_PMU_NAME_H_
+#define _MIPS_PMU_NAME_H_
+
+/* MIPS 1004K */
+#define MIPS_MAX_HWEVENTS		(4)
+
+#define PMU_1004K_MAX_HW_REGS	(128)
+struct pmu_desc mips_1004k_pmu_desc[][PMU_1004K_MAX_HW_REGS] = {
+	/* COUNT 0 */
+	{
+	 {0, "CPU_CYCLES"},
+	 {1, "CPU_INST"},
+	 {2, "BRANCH_INSNS"},
+	 {3, "JR_31_INSNS"},
+	 {4, "JR_NON_31_INSNS"},
+	 {5, "ITLB_ACCESSES"},
+	 {6, "DTLB_ACCESSES"},
+	 {7, "JTLB_INSN_ACCESSES"},
+	 {8, "JTLB_DATA_ACCESSES"},
+	 {9, "ICACHE_ACCESSES"},
+	 {10, "DCACHE_ACCESSES"},
+	 {11, "DCACHE_MISSES"},
+	 {12, "RESERVED"},
+	 {13, "STORE_MISS_INSNS"},
+	 {14, "INTEGER_INSNS"},
+	 {15, "LOAD_INSNS"},
+	 {16, "J_JAL_INSNS"},
+	 {17, "NO_OPS_INSNS"},
+	 {18, "ALL_STALLS"},
+	 {19, "SC_INSNS"},
+	 {20, "PREFETCH_INSNS"},
+	 {21, "L2_CACHE_WRITEBACKS"},
+	 {22, "L2_CACHE_MISSES"},
+	 {23, "EXCEPTIONS_TAKEN"},
+	 {24, "CACHE_FIXUP_CYCLES"},
+	 {25, "IFU_STALLS"},
+	 {26, "DSP_INSNS"},
+	 {27, "RESERVED"},
+	 {28, "POLICY_EVENTS"},
+	 {29, "ISPRAM_EVENTS"},
+	 {30, "COREEXTEND_EVENTS"},
+	 {31, "YIELD_EVENTS"},
+	 {32, "ITC_LOADS"},
+	 {33, "UNCACHED_LOAD_INSNS"},
+	 {34, "FORK_INSNS"},
+	 {35, "CP2_ARITH_INSNS"},
+	 {36, "INTERVENTION_STALLS"},
+	 {37, "ICACHE_MISS_STALLS"},
+	 {38, "RESERVED"},
+	 {39, "DCACHE_MISS_CYCLES"},
+	 {40, "UNCACHED_STALLS"},
+	 {41, "MDU_STALLS"},
+	 {42, "CP2_STALLS"},
+	 {43, "ISPRAM_STALLS"},
+	 {44, "CACHE_INSN_STALLS"},
+	 {45, "LOAD_USE_STALLS"},
+	 {46, "INTERLOCK_STALLS"},
+	 {47, "RELAX_STALLS"},
+	 {48, "IFU_FB_FULL_REFETCHES"},
+	 {49, "EJTAG_INSN_TRIGGERS"},
+	 {50, "FSB_LESS_25_FULL"},
+	 {51, "FSB_OVER_50_FULL"},
+	 {52, "LDQ_LESS_25_FULL"},
+	 {53, "LDQ_OVER_50_FULL"},
+	 {54, "WBB_LESS_25_FULL"},
+	 {55, "WBB_OVER_50_FULL"},
+	 {56, "INTERVENTION_HIT_COUNT"},
+	 {57, "INVALIDATE_INTERVENTION_COUNT"},
+	 {58, "EVICTION_COUNT"},
+	 {59, "MESI_INVAL_COUNT"},
+	 {60, "MESI_MODIFIED_COUNT"},
+	 {61, "SELF_INTERVENTION_LATENCY"},
+	 {62, "READ_RESPONSE_LATENCY"},
+	 {63, "RESERVED"},
+	 {64, "SI_PCEVENT1"},
+	 {65, "SI_PCEVENT3"},
+	 {66, "SI_PCEVENT5"},
+	 {67, "SI_PCEVENT7"},
+	 {-1, "RESERVED"},
+	 /* 68 - 127 for Reserved */
+	 },
+	/* COUNT 1 */
+	{
+	 {0, "CPU_CYCLES"},
+	 {1, "CPU_INST"},
+	 {2, "MISPREDICTED_BRANCH_INSNS"},
+	 {3, "JR_31_MISPREDICTIONS"},
+	 {4, "JR_31_NO_PREDICTIONS"},
+	 {5, "ITLB_MISSES"},
+	 {6, "DTLB_MISSES"},
+	 {7, "JTLB_INSN_MISSES"},
+	 {8, "JTLB_DATA_MISSES"},
+	 {9, "ICACHE_MISSES"},
+	 {10, "DCACHE_WRITEBACKS"},
+	 {11, "DCACHE_MISSES"},
+	 {12, "RESERVED"},
+	 {13, "LOAD_MISS_INSNS"},
+	 {14, "FPU_INSNS"},
+	 {15, "STORE_INSNS"},
+	 {16, "MIPS16_INSNS"},
+	 {17, "INT_MUL_DIV_INSNS"},
+	 {18, "REPLAYED_INSNS"},
+	 {19, "SC_INSNS_FAILED"},
+	 {20, "CACHE_HIT_PREFETCH_INSNS"},
+	 {21, "L2_CACHE_ACCESSES"},
+	 {22, "L2_CACHE_SINGLE_BIT_ERRORS"},
+	 {23, "SINGLE_THREADED_CYCLES"},
+	 {24, "REFETCHED_INSNS"},
+	 {25, "ALU_STALLS"},
+	 {26, "ALU_DSP_SATURATION_INSNS"},
+	 {27, "MDU_DSP_SATURATION_INSNS"},
+	 {28, "CP2_EVENTS"},
+	 {29, "DSPRAM_EVENTS"},
+	 {30, "RESERVED"},
+	 {31, "ITC_EVENT"},
+	 {33, "UNCACHED_STORE_INSNS"},
+	 {34, "YIELD_IN_COMP"},
+	 {35, "CP2_TO_FROM_INSNS"},
+	 {36, "INTERVENTION_MISS_STALLS"},
+	 {37, "DCACHE_MISS_STALLS"},
+	 {38, "RESERVED"},
+	 /* 38 was listed in OPROFILE web page, but not listed 1004k mips spec */
+	 /* {38, "FSB_INDEX_CONFLICT_STALLS"}, */
+	 {39, "L2_CACHE_MISS_CYCLES"},
+	 {40, "ITC_STALLS"},
+	 {41, "FPU_STALLS"},
+	 {42, "COREEXTEND_STALLS"},
+	 {43, "DSPRAM_STALLS"},
+	 {45, "ALU_TO_AGEN_STALLS"},
+	 {46, "MISPREDICTION_STALLS"},
+	 {47, "RESERVED"},
+	 {48, "FB_ENTRY_ALLOCATED_CYCLES"},
+	 {49, "EJTAG_DATA_TRIGGERS"},
+	 {50, "FSB_25_50_FULL"},
+	 {51, "FSB_FULL_STALLS"},
+	 {52, "LDQ_25_50_FULL"},
+	 {53, "LDQ_FULL_STALLS"},
+	 {54, "WBB_25_50_FULL"},
+	 {55, "WBB_FULL_STALLS"},
+	 {56, "INTERVENTION_COUNT"},
+	 {57, "INVALID_INTERVENT_HIT_CNT"},
+	 {58, "WRITEBACK_COUNT"},
+	 {59, "MESI_EXCLUSIVE_COUNT"},
+	 {60, "MESI_SHARED_COUNT"},
+	 {61, "SELF_INTERVENTION_COUNT"},
+	 {62, "READ_RESPONSE_COUNT"},
+	 {63, "RESERVED"},
+	 {64, "SI_PCEVENT0"},
+	 {65, "SI_PCEVENT2"},
+	 {66, "SI_PCEVENT4"},
+	 {67, "SI_PCEVENT6"},
+	 {-1, "RESERVED"},
+	 },
+};
+
+#define MIPS_1004K_PMU_DESC_SIZE (sizeof(mips_1004k_pmu_desc[0]))
+#define MIPS_1004K_PMU_DESC_COUNT (sizeof(mips_1004k_pmu_desc) / MIPS_1004K_PMU_DESC_SIZE)
+
+#endif				/* _V8_PMU_NAME_H_ */
diff --git a/src/devtools/met-driver/4.19/common/mtk_gpu_metmonitor.c b/src/devtools/met-driver/4.19/common/mtk_gpu_metmonitor.c
new file mode 100644
index 0000000..e206753
--- /dev/null
+++ b/src/devtools/met-driver/4.19/common/mtk_gpu_metmonitor.c
@@ -0,0 +1,887 @@
+/*
+ * Copyright (C) 2019 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <asm/page.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/syscalls.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <asm/uaccess.h>
+#include <linux/io.h>
+#include <linux/hrtimer.h>
+
+#include "met_drv.h"
+#include "trace.h"
+#include "interface.h"
+
+#include "mtk_gpu_metmonitor.h"
+#include "core_plf_init.h"
+#include "core_plf_trace.h"
+
+
+/*
+ * define if the hal implementation might re-schedule, cannot run inside softirq
+ * undefine this is better for sampling jitter if HAL support it
+ */
+#undef GPU_HAL_RUN_PREMPTIBLE
+
+#ifdef GPU_HAL_RUN_PREMPTIBLE
+static struct delayed_work gpu_dwork;
+static struct delayed_work gpu_pwr_dwork;
+#endif
+
+/* the mt_gpufreq_get_thermal_limit_freq use mutex_lock to do its job */
+/* so, change the gpu-dvfs implementation to dwork */
+static struct delayed_work gpu_dvfs_dwork;
+
+/*
+ * GPU monitor HAL comes from alps\mediatek\kernel\include\linux\mtk_gpu_utility.h
+ *
+ * mtk_get_gpu_memory_usage(unsigned int* pMemUsage) in unit of bytes
+ *
+ * mtk_get_gpu_xxx_loading are in unit of %
+*/
+
+enum MET_GPU_PROFILE_INDEX {
+	eMET_GPU_LOADING = 0,
+	eMET_GPU_BLOCK_LOADING,	/* 1 */
+	eMET_GPU_IDLE_LOADING,	/* 2 */
+	eMET_GPU_PROFILE_CNT
+};
+
+static unsigned long g_u4AvailableInfo;
+
+static unsigned int output_header_pmu_len;
+static unsigned int output_pmu_str_len;
+
+noinline void GPU_Loading(unsigned char cnt, unsigned int *value)
+{
+	switch (cnt) {
+	case 1:
+		MET_TRACE("%u\n", value[0]);
+		break;
+	case 2:
+		MET_TRACE("%u,%u\n", value[0], value[1]);
+		break;
+	case 3:
+		MET_TRACE("%u,%u,%u\n", value[0], value[1], value[2]);
+		break;
+	case 4:
+		MET_TRACE("%u,%u,%u,%u\n", value[0], value[1], value[2], value[3]);
+		break;
+	default:
+		break;
+	}
+
+}
+
+noinline void GPU_Sub_Loading(unsigned int loading)
+{
+	MET_TRACE("%u\n", loading);
+}
+
+noinline void GPU_3D_Fences_Count(int count)
+{
+	MET_TRACE("%d\n", count);
+}
+
+#ifdef GPU_HAL_RUN_PREMPTIBLE
+static void gpu_GPULoading(struct work_struct *work)
+{
+	unsigned int	pu4Value[eMET_GPU_PROFILE_CNT];
+	unsigned long	u4Index = 0;
+	unsigned int	loading = 0;
+	int		count = 0;
+
+	memset(pu4Value, 0x00, eMET_GPU_PROFILE_CNT);
+	if ((1 << eMET_GPU_LOADING) & g_u4AvailableInfo) {
+		if (mtk_get_gpu_loading_symbol && mtk_get_gpu_loading_symbol(&pu4Value[u4Index]))
+			u4Index += 1;
+	}
+
+	if ((1 << eMET_GPU_BLOCK_LOADING) & g_u4AvailableInfo) {
+		if (mtk_get_gpu_block_symbol && mtk_get_gpu_block_symbol(&pu4Value[u4Index]))
+			u4Index += 1;
+	}
+
+	if ((1 << eMET_GPU_IDLE_LOADING) & g_u4AvailableInfo) {
+		if (mtk_get_gpu_idle_symbol && mtk_get_gpu_idle_symbol(&pu4Value[u4Index]))
+			u4Index += 1;
+	}
+
+	if (g_u4AvailableInfo)
+		GPU_Loading(u4Index, pu4Value);
+
+	if (mtk_get_gpu_sub_loading_symbol && mtk_get_gpu_sub_loading_symbol(&loading))
+		GPU_Sub_Loading(loading);
+
+	if (mtk_get_3D_fences_count_symbol && mtk_get_3D_fences_count_symbol(&count))
+		GPU_3D_Fences_Count(count);
+}
+#else
+static void gpu_GPULoading(unsigned long long stamp, int cpu)
+{
+	unsigned int	pu4Value[eMET_GPU_PROFILE_CNT];
+	unsigned long	u4Index = 0;
+	unsigned int	loading = 0;
+	int		count = 0;
+
+	memset(pu4Value, 0x00, eMET_GPU_PROFILE_CNT);
+	if ((1 << eMET_GPU_LOADING) & g_u4AvailableInfo) {
+		if (mtk_get_gpu_loading_symbol) {
+			mtk_get_gpu_loading_symbol(&pu4Value[u4Index]);
+			u4Index += 1;
+		}
+	}
+
+	if ((1 << eMET_GPU_BLOCK_LOADING) & g_u4AvailableInfo) {
+		if (mtk_get_gpu_block_symbol) {
+			mtk_get_gpu_block_symbol(&pu4Value[u4Index]);
+			u4Index += 1;
+		}
+	}
+
+	if ((1 << eMET_GPU_IDLE_LOADING) & g_u4AvailableInfo) {
+		if (mtk_get_gpu_idle_symbol) {
+			mtk_get_gpu_idle_symbol(&pu4Value[u4Index]);
+			u4Index += 1;
+		}
+	}
+
+	if (g_u4AvailableInfo)
+		GPU_Loading(u4Index, pu4Value);
+
+	if (mtk_get_gpu_sub_loading_symbol) {
+		mtk_get_gpu_sub_loading_symbol(&loading);
+		GPU_Sub_Loading(loading);
+	}
+
+	if (mtk_get_3D_fences_count_symbol) {
+		mtk_get_3D_fences_count_symbol(&count);
+		GPU_3D_Fences_Count(count);
+	}
+}
+#endif
+
+static void gpu_monitor_start(void)
+{
+	if (mtk_get_gpu_loading_symbol)
+		g_u4AvailableInfo |= (1 << eMET_GPU_LOADING);
+	if (mtk_get_gpu_block_symbol)
+		g_u4AvailableInfo |= (1 << eMET_GPU_BLOCK_LOADING);
+	if (mtk_get_gpu_idle_symbol)
+		g_u4AvailableInfo |= (1 << eMET_GPU_IDLE_LOADING);
+
+#ifdef GPU_HAL_RUN_PREMPTIBLE
+	INIT_DELAYED_WORK(&gpu_dwork, gpu_GPULoading);
+#endif
+}
+
+#ifdef GPU_HAL_RUN_PREMPTIBLE
+static void gpu_monitor_stop(void)
+{
+	cancel_delayed_work_sync(&gpu_dwork);
+}
+
+static void GPULoadingNotify(unsigned long long stamp, int cpu)
+{
+	schedule_delayed_work(&gpu_dwork, 0);
+}
+#endif
+
+static char help[] =
+	"  --gpu					monitor gpu status\n";
+static int gpu_status_print_help(char *buf, int len)
+{
+	return snprintf(buf, PAGE_SIZE, help);
+}
+
+static char g_pComGPUStatusHeader[] =
+	"met-info [000] 0.0: met_gpu_loading_header: ";
+static int gpu_status_print_header(char *buf, int len)
+{
+	int ret = 0;
+
+	ret = snprintf(buf, PAGE_SIZE, "%s", g_pComGPUStatusHeader);
+
+	if ((1 << eMET_GPU_LOADING) & g_u4AvailableInfo)
+		ret += snprintf(buf+ret, PAGE_SIZE-ret, "%s", "Loading,");
+
+	if ((1 << eMET_GPU_BLOCK_LOADING) & g_u4AvailableInfo)
+		ret += snprintf(buf+ret, PAGE_SIZE-ret, "%s", "Blcok,");
+
+	if ((1 << eMET_GPU_IDLE_LOADING) & g_u4AvailableInfo)
+		ret += snprintf(buf+ret, PAGE_SIZE-ret, "%s", "Idle");
+
+	ret += snprintf(buf+ret, PAGE_SIZE-ret, "%s", "\n");
+
+	ret += snprintf(buf+ret, PAGE_SIZE-ret,
+			"met-info [000] 0.0: met_gpu_sub_loading_header: Loading\n");
+	ret += snprintf(buf+ret, PAGE_SIZE-ret,
+			"met-info [000] 0.0: met_gpu_3d_fences_count_header: Count\n");
+
+	return ret;
+}
+
+struct metdevice met_gpu = {
+	.name			= "gpu",
+	.owner			= THIS_MODULE,
+	.type			= MET_TYPE_BUS,
+	.cpu_related		= 0,
+	.start			= gpu_monitor_start,
+	.mode			= 0,
+	.polling_interval	= 1,	/* ms */
+#ifdef GPU_HAL_RUN_PREMPTIBLE
+	.timed_polling		= GPULoadingNotify,
+	.stop			= gpu_monitor_stop,
+#else
+	.timed_polling		= gpu_GPULoading,
+#endif
+	.print_help		= gpu_status_print_help,
+	.print_header		= gpu_status_print_header,
+};
+
+/*
+ * GPU DVFS Monitor
+ */
+#define	MTK_GPU_DVFS_TYPE_ITEM(type)	#type,
+static char *gpu_dvfs_type_name[] = MTK_GPU_DVFS_TYPE_LIST;
+#undef	MTK_GPU_DVFS_TYPE_ITEM
+
+static MTK_GPU_DVFS_TYPE gpu_dvfs_type_prev;
+static unsigned long gpu_dvfs_type_freq_prev;
+static unsigned int gpu_dvfs_type_freq[ARRAY_SIZE(gpu_dvfs_type_name)];
+
+noinline void GPU_DVFS(unsigned int Freq, unsigned int ThermalLimit,
+			unsigned long CustomBoost, unsigned long CustomUpbound)
+{
+	MET_TRACE("%u,%u,%lu,%lu\n", Freq, ThermalLimit, CustomBoost, CustomUpbound);
+}
+
+noinline void GPU_DVFS_TYPE(void)
+{
+	char	*SOB, *EOB;
+
+	MET_TRACE_GETBUF(&SOB, &EOB);
+	EOB = ms_formatD_EOL(EOB, ARRAY_SIZE(gpu_dvfs_type_freq), gpu_dvfs_type_freq);
+	MET_TRACE_PUTBUF(SOB, EOB);
+}
+
+noinline void GPU_DVFS_VSYNC(unsigned long freq)
+{
+	MET_TRACE("%lu\n", freq);
+}
+
+noinline void GPU_VSYNC_OFFSET_STATUS(unsigned int event_status, unsigned int debug_status)
+{
+	MET_TRACE("%u,%u\n", event_status, debug_status);
+}
+
+static void gpu_dvfs(void)
+{
+	unsigned int		freq = 0;
+	unsigned int		ThermalLimit = 0;
+	MTK_GPU_DVFS_TYPE	peType;
+	unsigned long		pulFreq = 0;
+	unsigned long		CustomBoost = 0;
+	unsigned long		CustomUpbound = 0;
+	unsigned int		event_status = 0;
+	unsigned int		debug_status = 0;
+
+	freq = mt_gpufreq_get_cur_freq_symbol ? mt_gpufreq_get_cur_freq_symbol() : 0;
+	ThermalLimit = mt_gpufreq_get_thermal_limit_freq_symbol ? mt_gpufreq_get_thermal_limit_freq_symbol() : 0;
+	if (mtk_get_custom_boost_gpu_freq_symbol)
+		mtk_get_custom_boost_gpu_freq_symbol(&CustomBoost);
+	if (mtk_get_custom_upbound_gpu_freq_symbol)
+		mtk_get_custom_upbound_gpu_freq_symbol(&CustomUpbound);
+	GPU_DVFS(freq, ThermalLimit, CustomBoost, CustomUpbound);
+
+	/* gpu dvfs type */
+	if (mtk_get_gpu_dvfs_from_symbol && mtk_get_gpu_dvfs_from_symbol(&peType, &pulFreq)) {
+		if (gpu_dvfs_type_prev != peType || gpu_dvfs_type_freq_prev != pulFreq) {
+			gpu_dvfs_type_freq[gpu_dvfs_type_prev] = 0;
+			gpu_dvfs_type_prev = peType;
+			gpu_dvfs_type_freq_prev = pulFreq;
+			gpu_dvfs_type_freq[gpu_dvfs_type_prev] = gpu_dvfs_type_freq_prev;
+			GPU_DVFS_TYPE();
+		}
+	}
+
+	if (mtk_get_vsync_based_target_freq_symbol && mtk_get_vsync_based_target_freq_symbol(&pulFreq))
+		GPU_DVFS_VSYNC(pulFreq);
+
+	if (mtk_get_vsync_offset_event_status_symbol && mtk_get_vsync_offset_debug_status_symbol) {
+		if (mtk_get_vsync_offset_event_status_symbol(&event_status)
+		    && mtk_get_vsync_offset_debug_status_symbol(&debug_status)) {
+			GPU_VSYNC_OFFSET_STATUS(event_status, debug_status);
+		}
+	}
+}
+
+static void gpu_dvfs_work(struct work_struct *work)
+{
+	gpu_dvfs();
+}
+
+static void gpu_dvfs_monitor_start(void)
+{
+	gpu_dvfs();
+	INIT_DELAYED_WORK(&gpu_dvfs_dwork, gpu_dvfs_work);
+}
+
+static void gpu_dvfs_monitor_stop(void)
+{
+	cancel_delayed_work_sync(&gpu_dvfs_dwork);
+	gpu_dvfs();
+
+	/* reset status */
+	gpu_dvfs_type_prev = 0;
+	gpu_dvfs_type_freq_prev = 0;
+}
+
+static void gpu_dvfs_monitor_polling(unsigned long long stamp, int cpu)
+{
+	schedule_delayed_work(&gpu_dvfs_dwork, 0);
+}
+
+static int gpu_dvfs_print_help(char *buf, int len)
+{
+	return snprintf(buf, PAGE_SIZE,
+			"  --gpu-dvfs				monitor gpu freq\n");
+}
+
+static int gpu_dvfs_print_header(char *buf, int len)
+{
+	int ret = 0;
+	int i = 0;
+
+	ret = snprintf(buf, PAGE_SIZE,
+			"met-info [000] 0.0: met_gpu_dvfs_header: ");
+	ret += snprintf(buf+ret, PAGE_SIZE-ret,
+			"Freq(kHz),ThermalLimit(kHz),CustomBoost,CustomUpbound\n");
+
+	ret += snprintf(buf+ret, PAGE_SIZE-ret,
+			"met-info [000] 0.0: met_gpu_dvfs_type_header: %s", gpu_dvfs_type_name[0]);
+	for (i = 1; i < ARRAY_SIZE(gpu_dvfs_type_name); i++)
+		ret += snprintf(buf+ret, PAGE_SIZE-ret, ",%s", gpu_dvfs_type_name[i]);
+	ret += snprintf(buf+ret, PAGE_SIZE-ret, "%s", "\n");
+
+	ret += snprintf(buf+ret, PAGE_SIZE-ret,
+			"met-info [000] 0.0: met_gpu_dvfs_vsync_header: VSYNC Based Freq\n");
+	ret += snprintf(buf+ret, PAGE_SIZE-ret,
+			"met-info [000] 0.0: met_gpu_vsync_offset_status_header: Event Status,Debug Status\n");
+
+	return ret;
+}
+
+struct metdevice met_gpudvfs = {
+	.name			= "gpu-dvfs",
+	.owner			= THIS_MODULE,
+	.type			= MET_TYPE_BUS,
+	.cpu_related		= 0,
+	.start			= gpu_dvfs_monitor_start,
+	.stop			= gpu_dvfs_monitor_stop,
+	.polling_interval	= 1,	/* ms */
+	.timed_polling		= gpu_dvfs_monitor_polling,
+	.print_help		= gpu_dvfs_print_help,
+	.print_header		= gpu_dvfs_print_header,
+	.ondiemet_mode		= 0,
+};
+
+/*
+ * GPU MEM monitor
+ */
+static unsigned long g_u4MemProfileIsOn;
+
+static void gpu_mem_monitor_start(void)
+{
+	if (!mtk_get_gpu_memory_usage_symbol)
+		return;
+
+#if 0
+	if (mtk_get_gpu_memory_usage_symbol(&u4Value))
+		g_u4MemProfileIsOn = 1;
+#endif
+	g_u4MemProfileIsOn = 1;
+}
+
+noinline void GPU_MEM(unsigned long long stamp, int cpu)
+{
+	unsigned int u4Value = 0;
+
+	if (!mtk_get_gpu_memory_usage_symbol)
+		return;
+
+	if (g_u4MemProfileIsOn == 1) {
+		mtk_get_gpu_memory_usage_symbol(&u4Value);
+		MET_TRACE("%d\n", u4Value);
+	}
+}
+
+static void gpu_mem_monitor_stop(void)
+{
+	g_u4MemProfileIsOn = 0;
+}
+
+static char help_mem[] =
+	"  --gpu-mem				monitor gpu memory status\n";
+static int gpu_mem_status_print_help(char *buf, int len)
+{
+	return snprintf(buf, PAGE_SIZE, help_mem);
+}
+
+static char g_pComGPUMemHeader[] =
+	"met-info [000] 0.0: met_gpu_mem_header: Usage\n";
+static int gpu_mem_status_print_header(char *buf, int len)
+{
+	return snprintf(buf, PAGE_SIZE, g_pComGPUMemHeader);
+}
+
+struct metdevice met_gpumem = {
+	.name			= "gpu-mem",
+	.owner			= THIS_MODULE,
+	.type			= MET_TYPE_BUS,
+	.cpu_related		= 0,
+	.start			= gpu_mem_monitor_start,
+	.stop			= gpu_mem_monitor_stop,
+	.mode			= 0,
+	.polling_interval	= 1,	/* ms */
+	.timed_polling		= GPU_MEM,
+	.print_help		= gpu_mem_status_print_help,
+	.print_header		= gpu_mem_status_print_header,
+};
+
+/*
+ * GPU power monitor
+ */
+static unsigned long g_u4PowerProfileIsOn;
+
+#ifdef GPU_HAL_RUN_PREMPTIBLE
+noinline void GPU_Power(struct work_struct *work)
+{
+	unsigned int u4Value = 0;
+
+	if (!mtk_get_gpu_power_loading_symbol)
+		return;
+
+	mtk_get_gpu_power_loading_symbol(&u4Value);
+	MET_TRACE("%d\n", u4Value);
+}
+
+static void GPU_PowerNotify(unsigned long long stamp, int cpu)
+{
+	if (g_u4PowerProfileIsOn == 1)
+		schedule_delayed_work(&gpu_pwr_dwork, 0);
+}
+#else
+noinline void GPU_Power(unsigned long long stamp, int cpu)
+{
+	unsigned int u4Value = 0;
+
+	if (!mtk_get_gpu_power_loading_symbol)
+		return;
+
+	if (g_u4PowerProfileIsOn == 1) {
+		mtk_get_gpu_power_loading_symbol(&u4Value);
+		MET_TRACE("%d\n", u4Value);
+	}
+}
+#endif
+
+static void gpu_Power_monitor_start(void)
+{
+	if (!mtk_get_gpu_power_loading_symbol)
+		return;
+
+#if 0
+	if (mtk_get_gpu_power_loading_symbol(&u4Value))
+		g_u4PowerProfileIsOn = 1;
+#endif
+	g_u4PowerProfileIsOn = 1;
+#ifdef GPU_HAL_RUN_PREMPTIBLE
+	INIT_DELAYED_WORK(&gpu_pwr_dwork, GPU_Power);
+#endif
+}
+
+static void gpu_Power_monitor_stop(void)
+{
+	g_u4PowerProfileIsOn = 0;
+
+#ifdef GPU_HAL_RUN_PREMPTIBLE
+	cancel_delayed_work_sync(&gpu_pwr_dwork);
+#endif
+}
+
+static char help_pwr[] =
+	"  --gpu-pwr				monitor gpu power status\n";
+static int gpu_Power_status_print_help(char *buf, int len)
+{
+	return snprintf(buf, PAGE_SIZE, help_pwr);
+}
+
+static char g_pComGPUPowerHeader[] =
+	"met-info [000] 0.0: met_gpu_power_header: Loading\n";
+static int gpu_Power_status_print_header(char *buf, int len)
+{
+	return snprintf(buf, PAGE_SIZE, g_pComGPUPowerHeader);
+}
+
+struct metdevice met_gpupwr = {
+	.name			= "gpu-pwr",
+	.owner			= THIS_MODULE,
+	.type			= MET_TYPE_BUS,
+	.cpu_related		= 0,
+	.start			= gpu_Power_monitor_start,
+	.stop			= gpu_Power_monitor_stop,
+	.mode			= 0,
+	.polling_interval	= 1,	/* ms */
+#ifdef GPU_HAL_RUN_PREMPTIBLE
+	.timed_polling		= GPU_PowerNotify,
+#else
+	.timed_polling		= GPU_Power,
+#endif
+	.print_help		= gpu_Power_status_print_help,
+	.print_header		= gpu_Power_status_print_header,
+};
+
+
+/*
+ * GPU PMU
+ */
+#define UNUSE_ARG(arg) ((void)arg)
+
+#ifdef GPU_HAL_RUN_PREMPTIBLE
+static struct delayed_work gpu_pmu_dwork;
+#endif
+
+#define MAX_PMU_STR_LEN (1024 * 5)
+
+static const char help_pmu[] = "  --gpu-pmu				monitor gpu pmu status";
+static const char header_pmu[] = "met-info [000] 0.0: met_gpu_pmu_header: ";
+static char pmu_str[MAX_PMU_STR_LEN];
+static int pmu_cnt;
+static int gpu_pwr_status = 1;
+static GPU_PMU *pmu_list;
+
+
+noinline void GPU_PMU_RAW(
+	unsigned long long stamp,
+	int cpu)
+{
+	bool ret;
+	int i = 0;
+	char *SOB, *EOB;
+	unsigned int value[pmu_cnt];
+
+	if (stamp == 0 && cpu == 0) {
+		for (i = 0; i < pmu_cnt; i++)
+			value[i] = 0;
+
+		MET_TRACE_GETBUF(&SOB, &EOB);
+		EOB = ms_formatH(EOB, pmu_cnt, value);
+		MET_TRACE_PUTBUF(SOB, EOB);
+		return;
+	}
+
+	if (mtk_get_gpu_pmu_swapnreset_symbol) {
+		ret = mtk_get_gpu_pmu_swapnreset_symbol(pmu_list, pmu_cnt);
+		if (ret) {
+			for (i = 0; i < pmu_cnt; i++) {
+				if (pmu_list[i].overflow)
+					pmu_list[i].value = 0xFFFFFFFF;
+				value[i] = pmu_list[i].value;
+			}
+			MET_TRACE_GETBUF(&SOB, &EOB);
+			EOB = ms_formatH(EOB, pmu_cnt, value);
+			MET_TRACE_PUTBUF(SOB, EOB);
+		}
+	}
+}
+
+static int create_gpu_pmu_list(void)
+{
+	int ret = 0;
+	int len = 0;
+	int i = 0;
+
+	if (mtk_get_gpu_pmu_init_symbol) {
+		ret = mtk_get_gpu_pmu_init_symbol(NULL, 0, &pmu_cnt);
+		if (pmu_cnt == 0 || ret == 0)
+			return 0;
+	} else
+		return 0;
+
+	pmu_list = kmalloc_array(pmu_cnt, sizeof(GPU_PMU), GFP_KERNEL);
+	if (pmu_list) {
+		memset(pmu_list, 0x00, sizeof(GPU_PMU)*pmu_cnt);
+		ret = mtk_get_gpu_pmu_init_symbol(pmu_list, pmu_cnt, NULL);
+
+		memset(pmu_str, 0x00, MAX_PMU_STR_LEN);
+		len = snprintf(pmu_str, MAX_PMU_STR_LEN, "%s", pmu_list[0].name);
+		for (i = 1; i < pmu_cnt; i++)
+			len += snprintf(pmu_str + len, MAX_PMU_STR_LEN - len, ",%s", pmu_list[i].name);
+
+		/*
+		* dummy read in order to reset GPU PMU counter
+		*/
+		if (mtk_get_gpu_pmu_swapnreset_symbol)
+			mtk_get_gpu_pmu_swapnreset_symbol(pmu_list, pmu_cnt);
+	}
+
+	/* init state */
+	met_gpu_pmu.header_read_again = 0;
+	output_header_pmu_len = 0;
+	output_pmu_str_len = 0;
+
+	return ret;
+}
+
+static void delete_gpu_pmu_list(void)
+{
+	kfree(pmu_list);
+	pmu_list = NULL;
+	pmu_cnt = 0;
+}
+
+static void gpu_pwr_status_cb(int on)
+{
+	MET_TRACE("on = %d\n", on);
+
+	if (on == 1) {
+		/*
+		* dummy read in order to reset GPU PMU counter
+		*/
+		if (mtk_get_gpu_pmu_swapnreset_symbol)
+			mtk_get_gpu_pmu_swapnreset_symbol(pmu_list, pmu_cnt);
+
+	} else {
+		GPU_PMU_RAW(1, 0);
+		GPU_PMU_RAW(0, 0);
+	}
+
+	gpu_pwr_status = on;
+}
+
+static void gpu_pmu_monitor_start(void)
+{
+	int ret;
+
+	ret = create_gpu_pmu_list();
+	if (ret == 0)
+		return;
+
+	if (mtk_register_gpu_power_change_symbol)
+		mtk_register_gpu_power_change_symbol("met_gpu", gpu_pwr_status_cb);
+
+#ifdef GPU_HAL_RUN_PREMPTIBLE
+	INIT_DELAYED_WORK(&gpu_pmu_dwork, GPU_PMU_RAW);
+#endif
+}
+
+static void gpu_pmu_monitor_stop(void)
+{
+#ifdef GPU_HAL_RUN_PREMPTIBLE
+	cancel_delayed_work_sync(&gpu_pmu_dwork);
+#endif
+
+	if (mtk_unregister_gpu_power_change_symbol)
+		mtk_unregister_gpu_power_change_symbol("met_gpu");
+	delete_gpu_pmu_list();
+
+#if 1
+	/* stop polling counter */
+	if (mtk_get_gpu_pmu_swapnreset_stop_symbol)
+		mtk_get_gpu_pmu_swapnreset_stop_symbol();
+	/* release resource */
+	if (mtk_get_gpu_pmu_deinit_symbol)
+		mtk_get_gpu_pmu_deinit_symbol();
+#endif
+}
+
+#ifdef GPU_HAL_RUN_PREMPTIBLE
+static void gpu_pmu_timed_polling_notify(
+	unsigned long long stamp,
+	int cpu)
+{
+	UNUSE_ARG(stamp);
+	UNUSE_ARG(cpu);
+
+	if (gpu_pwr_status == 1)
+		schedule_delayed_work(&gpu_pmu_dwork, 0);
+}
+#else
+static void gpu_pmu_timed_polling(
+	unsigned long long stamp,
+	int cpu)
+{
+	UNUSE_ARG(stamp);
+	UNUSE_ARG(cpu);
+
+	if (gpu_pwr_status == 1)
+		GPU_PMU_RAW(stamp, cpu);
+}
+#endif
+
+static int gpu_pmu_print_help(
+	char *buf,
+	int len)
+{
+	UNUSE_ARG(len);
+	return snprintf(buf, PAGE_SIZE, "%s\n", help_pmu);
+}
+
+static int gpu_pmu_print_header(
+	char *buf,
+	int len)
+{
+	if(output_header_pmu_len == 0){
+		len = snprintf(buf, PAGE_SIZE, "%s", header_pmu);
+		met_gpu_pmu.header_read_again = 1;
+		output_header_pmu_len = len;
+	}
+	else{
+		if( (strlen(pmu_str) - output_pmu_str_len) > PAGE_SIZE ){
+			char output_buf[PAGE_SIZE/4];
+
+			strncpy(output_buf, pmu_str+output_pmu_str_len, PAGE_SIZE/4);
+			len = snprintf(buf, PAGE_SIZE, "%s", output_buf);
+			output_pmu_str_len += len;
+		}
+		else{
+			len = snprintf(buf, PAGE_SIZE, "%s\n", pmu_str+output_pmu_str_len);
+
+			/* reset state */
+			met_gpu_pmu.header_read_again = 0;
+			output_header_pmu_len = 0;
+			output_pmu_str_len = 0;
+		}
+	}
+
+	return len;
+}
+
+struct metdevice met_gpu_pmu = {
+	.name			= "gpu-pmu",
+	.owner			= THIS_MODULE,
+	.type			= MET_TYPE_PMU,
+	.cpu_related		= 0,
+	.start			= gpu_pmu_monitor_start,
+	.stop			= gpu_pmu_monitor_stop,
+	.mode			= 0,
+	.polling_interval	= 1,	/* ms */
+#ifdef GPU_HAL_RUN_PREMPTIBLE
+	.timed_polling		= gpu_pmu_timed_polling_notify,
+#else
+	.timed_polling		= gpu_pmu_timed_polling,
+#endif
+	.print_help		= gpu_pmu_print_help,
+	.print_header		= gpu_pmu_print_header,
+};
+
+/*
+ * GPU stall counters
+ */
+#ifdef MET_GPU_STALL_MONITOR
+static void __iomem	*io_addr_gpu_stall;
+
+static int gpu_stall_create_subfs(struct kobject *parent)
+{
+	io_addr_gpu_stall = ioremap(IO_ADDR_GPU_STALL, IO_SIZE_GPU_STALL);
+	if (!io_addr_gpu_stall) {
+		PR_BOOTMSG("Failed to init GPU stall counters!!\n");
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+static void gpu_stall_delete_subfs(void)
+{
+	if (io_addr_gpu_stall) {
+		iounmap(io_addr_gpu_stall);
+		io_addr_gpu_stall = NULL;
+	}
+}
+
+static void gpu_stall_start(void)
+{
+#ifdef GPU_STALL_CNT_SINGLE
+	unsigned int value = 0x00000001;
+#else
+	unsigned int value = 0x00010001;
+#endif
+	writel(value, io_addr_gpu_stall+OFFSET_STALL_GPU_M0_CHECK);
+	writel(value, io_addr_gpu_stall+OFFSET_STALL_GPU_M1_CHECK);
+	writel(value, io_addr_gpu_stall+OFFSET_STALL_GPU_M0_EMI_CHECK);
+	writel(value, io_addr_gpu_stall+OFFSET_STALL_GPU_M1_EMI_CHECK);
+}
+
+static void gpu_stall_stop(void)
+{
+	writel(0x00000000, io_addr_gpu_stall+OFFSET_STALL_GPU_M0_CHECK);
+	writel(0x00000000, io_addr_gpu_stall+OFFSET_STALL_GPU_M1_CHECK);
+	writel(0x00000000, io_addr_gpu_stall+OFFSET_STALL_GPU_M0_EMI_CHECK);
+	writel(0x00000000, io_addr_gpu_stall+OFFSET_STALL_GPU_M1_EMI_CHECK);
+}
+
+noinline void GPU_STALL_RAW(void)
+{
+	unsigned int	stall_counters[4];
+	char		*SOB, *EOB;
+
+	stall_counters[0] = (unsigned int)readl(io_addr_gpu_stall+OFFSET_STALL_GPU_M0_CHECK);
+	stall_counters[1] = (unsigned int)readl(io_addr_gpu_stall+OFFSET_STALL_GPU_M1_CHECK);
+	stall_counters[2] = (unsigned int)readl(io_addr_gpu_stall+OFFSET_STALL_GPU_M0_EMI_CHECK);
+	stall_counters[3] = (unsigned int)readl(io_addr_gpu_stall+OFFSET_STALL_GPU_M1_EMI_CHECK);
+
+	MET_TRACE_GETBUF(&SOB, &EOB);
+	EOB = ms_formatH(EOB, ARRAY_SIZE(stall_counters), stall_counters);
+	MET_TRACE_PUTBUF(SOB, EOB);
+}
+
+static void gpu_stall_timed_polling(unsigned long long stamp, int cpu)
+{
+	GPU_STALL_RAW();
+}
+
+#ifdef GPU_STALL_CNT_SINGLE
+static char g_pComGPUStallHeader[] =
+	"met-info [000] 0.0: met_gpu_stall_header: M0_WR,M0_RD,M1_WR,M1_RD\n";
+#else
+static char g_pComGPUStallHeader[] =
+	"met-info [000] 0.0: met_gpu_stall_header: M0_STATUS_1,M1_STATUS_1,M0_STATUS_2,M1_STATUS_2\n";
+#endif
+static int gpu_stall_print_header(char *buf, int len)
+{
+	return snprintf(buf, PAGE_SIZE, g_pComGPUStallHeader);
+}
+
+struct metdevice met_gpu_stall = {
+	.name			= "gpu-stall",
+	.owner			= THIS_MODULE,
+	.type			= MET_TYPE_BUS,
+	.cpu_related		= 0,
+	.create_subfs		= gpu_stall_create_subfs,
+	.delete_subfs		= gpu_stall_delete_subfs,
+	.start			= gpu_stall_start,
+	.stop			= gpu_stall_stop,
+	.mode			= 0,
+	.polling_interval	= 1,	/* ms */
+	.timed_polling		= gpu_stall_timed_polling,
+	.print_header		= gpu_stall_print_header,
+};
+#endif	/* MET_GPU_STALL_MONITOR */
diff --git a/src/devtools/met-driver/4.19/common/mtk_gpu_metmonitor.h b/src/devtools/met-driver/4.19/common/mtk_gpu_metmonitor.h
new file mode 100644
index 0000000..41c5a25
--- /dev/null
+++ b/src/devtools/met-driver/4.19/common/mtk_gpu_metmonitor.h
@@ -0,0 +1,18 @@
+/*
+ * Copyright (C) 2019 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MT_GPU_METMONITOR_H_
+
+#define _MT_GPU_METMONITOR_H_
+
+#endif				/* _MT_GPU_METMONITOR_H_ */
diff --git a/src/devtools/met-driver/4.19/common/mtk_typedefs.h b/src/devtools/met-driver/4.19/common/mtk_typedefs.h
new file mode 100644
index 0000000..fd516e3
--- /dev/null
+++ b/src/devtools/met-driver/4.19/common/mtk_typedefs.h
@@ -0,0 +1,318 @@
+/*
+ * Copyright (C) 2019 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MT_TYPEDEFS_H__
+
+/*
+ *  KOBJ ATTR Manipulations Macros
+ */
+
+#define KOBJ_ITEM_LIST(args...)		args
+
+/*
+ * Declaring KOBJ attributes
+ */
+#define DECLARE_KOBJ_ATTR(attr_name) \
+	static struct kobj_attribute attr_name##_attr = \
+		__ATTR(attr_name, 0664, attr_name##_show, attr_name##_store)
+
+#define DECLARE_KOBJ_ATTR_RO(attr_name) \
+	static struct kobj_attribute attr_name##_attr = \
+		__ATTR_RO(attr_name)
+
+/*
+ * Declaring KOBJ attributes with integer variable
+ */
+/* normal version */
+#define DECLARE_KOBJ_ATTR_SHOW_INT(attr_name, var_name) \
+	static ssize_t attr_name##_show( \
+		struct kobject *kobj, \
+		struct kobj_attribute *attr, \
+		char *buf) \
+	{ \
+		return snprintf(buf, PAGE_SIZE, "%d\n", var_name); \
+	}
+#define DECLARE_KOBJ_ATTR_STORE_INT(attr_name, var_name) \
+	static ssize_t attr_name##_store( \
+		struct kobject *kobj, \
+		struct kobj_attribute *attr, \
+		const char *buf, \
+		size_t n) \
+	{ \
+		int	val = 0; \
+		if (kstrtoint(buf, 0, &val) != 0) { \
+			return -EINVAL; \
+		} \
+		var_name = val; \
+		return n; \
+	}
+#define DECLARE_KOBJ_ATTR_INT(attr_name, var_name) \
+	DECLARE_KOBJ_ATTR_SHOW_INT(attr_name, var_name) \
+	DECLARE_KOBJ_ATTR_STORE_INT(attr_name, var_name) \
+	DECLARE_KOBJ_ATTR(attr_name)
+#define DECLARE_KOBJ_ATTR_RO_INT(attr_name, var_name) \
+	DECLARE_KOBJ_ATTR_SHOW_INT(attr_name, var_name) \
+	DECLARE_KOBJ_ATTR_RO(attr_name)
+
+/* cond-check version */
+#define DECLARE_KOBJ_ATTR_STORE_INT_CHECK(attr_name, var_name, cond) \
+	static ssize_t attr_name##_store( \
+		struct kobject *kobj, \
+		struct kobj_attribute *attr, \
+		const char *buf, \
+		size_t n) \
+	{ \
+		int	var_name##temp = var_name; \
+		if (kstrtoint(buf, 0, &var_name) != 0) { \
+			var_name = var_name##temp; \
+			return -EINVAL; \
+		} \
+		if (cond) { \
+			return n; \
+		} else { \
+			var_name = var_name##temp; \
+			return -EINVAL; \
+		} \
+	}
+/* Note: the name of val in cond can NOT be the same as var_name */
+#define DECLARE_KOBJ_ATTR_INT_CHECK(attr_name, var_name, cond) \
+	DECLARE_KOBJ_ATTR_SHOW_INT(attr_name, var_name) \
+	DECLARE_KOBJ_ATTR_STORE_INT_CHECK(attr_name, var_name, cond) \
+	DECLARE_KOBJ_ATTR(attr_name)
+
+/* helper procedure version */
+#define DECLARE_KOBJ_ATTR_SHOW_INT_PROC(attr_name, var_name, func) \
+	static ssize_t attr_name##_show( \
+		struct kobject *kobj, \
+		struct kobj_attribute *attr, \
+		char *buf) \
+	{ \
+		return func(kobj, attr, buf, var_name); \
+	}
+#define DECLARE_KOBJ_ATTR_STORE_INT_PROC(attr_name, var_name, func) \
+	static ssize_t attr_name##_store( \
+		struct kobject *kobj, \
+		struct kobj_attribute *attr, \
+		const char *buf, \
+		size_t n) \
+	{ \
+		return func(kobj, attr, buf, n, &(var_name)); \
+	}
+#define DECLARE_KOBJ_ATTR_INT_PROC(attr_name, var_name, show, store) \
+	DECLARE_KOBJ_ATTR_SHOW_INT_PROC(attr_name, var_name, show) \
+	DECLARE_KOBJ_ATTR_STORE_INT_PROC(attr_name, var_name, store) \
+	DECLARE_KOBJ_ATTR(attr_name)
+
+/*
+ * Declaring KOBJ attributes with integer(hex) variable
+ */
+/* normal version */
+#define DECLARE_KOBJ_ATTR_SHOW_HEX(attr_name, var_name) \
+	static ssize_t attr_name##_show( \
+		struct kobject *kobj, \
+		struct kobj_attribute *attr, \
+		char *buf) \
+	{ \
+		return snprintf(buf, PAGE_SIZE, "%x\n", var_name); \
+	}
+#define DECLARE_KOBJ_ATTR_STORE_HEX(attr_name, var_name) \
+	static ssize_t attr_name##_store( \
+		struct kobject *kobj, \
+		struct kobj_attribute *attr, \
+		const char *buf, \
+		size_t n) \
+	{ \
+		unsigned int	val = 0; \
+		if (kstrtouint(buf, 0, &val) != 0) { \
+			return -EINVAL; \
+		} \
+		var_name = val; \
+		return n; \
+	}
+#define DECLARE_KOBJ_ATTR_HEX(attr_name, var_name) \
+	DECLARE_KOBJ_ATTR_SHOW_HEX(attr_name, var_name) \
+	DECLARE_KOBJ_ATTR_STORE_HEX(attr_name, var_name) \
+	DECLARE_KOBJ_ATTR(attr_name)
+#define DECLARE_KOBJ_ATTR_RO_HEX(attr_name, var_name) \
+	DECLARE_KOBJ_ATTR_SHOW_HEX(attr_name, var_name) \
+	DECLARE_KOBJ_ATTR_RO(attr_name)
+
+/* cond-check version */
+#define DECLARE_KOBJ_ATTR_STORE_HEX_CHECK(attr_name, var_name, cond) \
+	static ssize_t attr_name##_store( \
+		struct kobject *kobj, \
+		struct kobj_attribute *attr, \
+		const char *buf, \
+		size_t n) \
+	{ \
+		unsigned int	var_name##temp = var_name; \
+		if (kstrtouint(buf, 0, &var_name) != 0) { \
+			var_name = var_name##temp; \
+			return -EINVAL; \
+		} \
+		if (cond) { \
+			return n; \
+		} else { \
+			var_name = var_name##temp; \
+			return -EINVAL; \
+		} \
+	}
+/* Note: the name of val in cond can NOT be the same as var_name */
+#define DECLARE_KOBJ_ATTR_HEX_CHECK(attr_name, var_name, cond) \
+	DECLARE_KOBJ_ATTR_SHOW_HEX(attr_name, var_name) \
+	DECLARE_KOBJ_ATTR_STORE_HEX_CHECK(attr_name, var_name, cond) \
+	DECLARE_KOBJ_ATTR(attr_name)
+
+/* helper procedure version */
+#define DECLARE_KOBJ_ATTR_SHOW_HEX_PROC(attr_name, var_name, func) \
+	static ssize_t attr_name##_show( \
+		struct kobject *kobj, \
+		struct kobj_attribute *attr, \
+		char *buf) \
+	{ \
+		return func(kobj, attr, buf, var_name); \
+	}
+#define DECLARE_KOBJ_ATTR_STORE_HEX_PROC(attr_name, var_name, func) \
+	static ssize_t attr_name##_store( \
+		struct kobject *kobj, \
+		struct kobj_attribute *attr, \
+		const char *buf, \
+		size_t n) \
+	{ \
+		return func(kobj, attr, buf, n, &(var_name)); \
+	}
+#define DECLARE_KOBJ_ATTR_HEX_PROC(attr_name, var_name, show, store) \
+	DECLARE_KOBJ_ATTR_SHOW_HEX_PROC(attr_name, var_name, show) \
+	DECLARE_KOBJ_ATTR_STORE_HEX_PROC(attr_name, var_name, store) \
+	DECLARE_KOBJ_ATTR(attr_name)
+
+/*
+ * Declaring KOBJ attributes with string variable
+ */
+#define DECLARE_KOBJ_ATTR_SHOW_STR(attr_name, var_name) \
+	static ssize_t attr_name##_show( \
+		struct kobject *kobj, \
+		struct kobj_attribute *attr, \
+		char *buf) \
+	{ \
+		return snprintf(buf, PAGE_SIZE, "%s", var_name); \
+	}
+
+#define DECLARE_KOBJ_ATTR_RO_STR(attr_name, var_name) \
+	DECLARE_KOBJ_ATTR_SHOW_STR(attr_name, var_name) \
+	DECLARE_KOBJ_ATTR_RO(attr_name)
+
+/*
+ * Declaring KOBJ attributes with integer list variable
+ */
+#define DECLARE_KOBJ_ATTR_INT_LIST_ITEM(list_name, list) \
+	static struct list_name##_list_item_t { \
+		int	key; \
+		int	val; \
+	} const list_name##_list_item[] = { list }
+#define DECLARE_KOBJ_ATTR_SHOW_INT_LIST(attr_name, var_name, list_name) \
+	static ssize_t attr_name##_show( \
+		struct kobject *kobj, \
+		struct kobj_attribute *attr, \
+		char *buf) \
+	{ \
+		int	i; \
+		for (i = 0; i < ARRAY_SIZE(list_name##_list_item); i++) { \
+			if (var_name == list_name##_list_item[i].key) { \
+				return snprintf(buf, \
+						PAGE_SIZE, \
+						"%d\n", \
+						list_name##_list_item[i].val); \
+			} \
+		} \
+		return snprintf(buf, PAGE_SIZE, "%d\n", -1); \
+	}
+#define DECLARE_KOBJ_ATTR_STORE_INT_LIST(attr_name, var_name, list_name) \
+	static ssize_t attr_name##_store( \
+		struct kobject *kobj, \
+		struct kobj_attribute *attr, \
+		const char *buf, \
+		size_t n) \
+	{ \
+		int	value; \
+		int	i; \
+		if (kstrtoint(buf, 10, &value) != 0) \
+			return -EINVAL; \
+		for (i = 0; i < ARRAY_SIZE(list_name##_list_item); i++) { \
+			if (value == list_name##_list_item[i].val) { \
+				var_name = list_name##_list_item[i].key; \
+				return n; \
+			} \
+		} \
+		return -EINVAL; \
+	}
+#define DECLARE_KOBJ_ATTR_INT_LIST(attr_name, var_name, list_name) \
+	DECLARE_KOBJ_ATTR_SHOW_INT_LIST(attr_name, var_name, list_name) \
+	DECLARE_KOBJ_ATTR_STORE_INT_LIST(attr_name, var_name, list_name) \
+	DECLARE_KOBJ_ATTR(attr_name)
+
+/*
+ * Declaring KOBJ attributes with string list variable
+ */
+#define DECLARE_KOBJ_ATTR_STR_LIST_ITEM(list_name, list) \
+	static struct list_name##_list_item_t { \
+		int	key; \
+		char	*val; \
+	} const list_name##_list_item[] = { list }
+#define DECLARE_KOBJ_ATTR_SHOW_STR_LIST(attr_name, var_name, list_name) \
+	static ssize_t attr_name##_show( \
+		struct kobject *kobj, \
+		struct kobj_attribute *attr, \
+		char *buf) \
+	{ \
+		int	i; \
+		for (i = 0; i < ARRAY_SIZE(list_name##_list_item); i++) { \
+			if (var_name == list_name##_list_item[i].key) { \
+				return snprintf(buf, \
+						PAGE_SIZE, \
+						"%s\n", \
+						list_name##_list_item[i].val); \
+			} \
+		} \
+		return snprintf(buf, PAGE_SIZE, "%s\n", "ERR"); \
+	}
+#define DECLARE_KOBJ_ATTR_STORE_STR_LIST(attr_name, var_name, list_name) \
+	static ssize_t attr_name##_store( \
+		struct kobject *kobj, \
+		struct kobj_attribute *attr, \
+		const char *buf, \
+		size_t n) \
+	{ \
+		int	i; \
+		for (i = 0; i < ARRAY_SIZE(list_name##_list_item); i++) { \
+			if (strncasecmp(buf, \
+					list_name##_list_item[i].val, \
+					strlen(list_name##_list_item[i].val)) == 0) { \
+				var_name = list_name##_list_item[i].key; \
+				return n; \
+			} \
+		} \
+		return -EINVAL; \
+	}
+#define DECLARE_KOBJ_ATTR_STR_LIST(attr_name, var_name, list_name) \
+	DECLARE_KOBJ_ATTR_SHOW_STR_LIST(attr_name, var_name, list_name) \
+	DECLARE_KOBJ_ATTR_STORE_STR_LIST(attr_name, var_name, list_name) \
+	DECLARE_KOBJ_ATTR(attr_name)
+
+/*
+ *  MET Debug Message
+ */
+#define METINFO(format, ...)	pr_debug("[MET]%s: "format, __func__, ##__VA_ARGS__)
+#define METERROR(format, ...)	pr_debug("[MET][ERR]%s: "format, __func__, ##__VA_ARGS__)
+
+#endif	/* _MT_TYPEDEFS_H__ */
diff --git a/src/devtools/met-driver/4.19/common/ondiemet.c b/src/devtools/met-driver/4.19/common/ondiemet.c
new file mode 100644
index 0000000..37fae92
--- /dev/null
+++ b/src/devtools/met-driver/4.19/common/ondiemet.c
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2019 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "ondiemet.h"
+
+/* record enabled modules */
+unsigned int ondiemet_module[ONDIEMET_NUM];
+EXPORT_SYMBOL(ondiemet_module);
+
+void (*scp_start[ONDIEMET_NUM]) (void) = {
+sspm_start, NULL, NULL};
+
+void (*scp_stop[ONDIEMET_NUM]) (void) = {
+sspm_stop, NULL, NULL};
+
+void (*scp_extract[ONDIEMET_NUM]) (void) = {
+sspm_extract, NULL, NULL};
+
+/* record which MCU is started to generate data */
+int ondiemet_module_started[ONDIEMET_NUM];
+
+int ondiemet_attr_init(struct device *dev)
+{
+	int ret;
+
+	ret = sspm_attr_init(dev);
+	if (ret != 0) {
+		pr_debug("can not create device file: sspm related\n");
+		return ret;
+	}
+
+	return 0;
+
+}
+
+int ondiemet_attr_uninit(struct device *dev)
+{
+	int ret;
+
+	ret = sspm_attr_uninit(dev);
+	if (ret != 0) {
+		pr_debug("can not delete device file: sspm related\n");
+		return ret;
+	}
+
+	return 0;
+
+}
+
+void ondiemet_start(void)
+{
+	int i;
+
+	for (i = 0; i < ONDIEMET_NUM; i++) {
+		if (ondiemet_module[i] != 0) {
+			ondiemet_module_started[i] = 1;
+			(*scp_start[i]) ();
+		}
+	}
+}
+
+void ondiemet_stop(void)
+{
+	int i;
+
+	for (i = 0; i < ONDIEMET_NUM; i++) {
+		if (ondiemet_module[i] != 0) {
+			(*scp_stop[i]) ();
+			ondiemet_module_started[i] = 0;
+		}
+	}
+}
+
+void ondiemet_extract(void)
+{
+	int i;
+
+	for (i = 0; i < ONDIEMET_NUM; i++) {
+		if (ondiemet_module[i] != 0)
+			(*scp_extract[i]) ();
+	}
+}
diff --git a/src/devtools/met-driver/4.19/common/ondiemet.h b/src/devtools/met-driver/4.19/common/ondiemet.h
new file mode 100644
index 0000000..46769ab
--- /dev/null
+++ b/src/devtools/met-driver/4.19/common/ondiemet.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2019 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __ONDIEMET_H
+#define __ONDIEMET_H
+
+#include "ondiemet_log.h"
+
+extern void ondiemet_extract(void);
+extern void ondiemet_stop(void);
+extern void ondiemet_start(void);
+
+#define ONDIEMET_SSPM  0
+#define ONDIEMET_NUM  3		/* total number of supported */
+extern unsigned int ondiemet_module[];
+extern void sspm_start(void);
+extern void sspm_stop(void);
+extern void sspm_extract(void);
+extern int sspm_attr_init(struct device *dev);
+extern int sspm_attr_uninit(struct device *dev);
+
+extern int ondiemet_attr_init(struct device *dev);
+extern int ondiemet_attr_uninit(struct device *dev);
+
+extern int sspm_buffer_size;
+
+#endif				/* __ONDIEMET_H */
diff --git a/src/devtools/met-driver/4.19/common/ondiemet_log.c b/src/devtools/met-driver/4.19/common/ondiemet_log.c
new file mode 100644
index 0000000..c47c9d9
--- /dev/null
+++ b/src/devtools/met-driver/4.19/common/ondiemet_log.c
@@ -0,0 +1,517 @@
+/*
+ * Copyright (C) 2019 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/debugfs.h>
+#include <linux/mutex.h>
+#include <linux/semaphore.h>
+#include <linux/freezer.h>
+#include <linux/uaccess.h>
+#include <linux/completion.h>
+
+#include "ondiemet_log.h"
+
+#define ONDIEMET_LOG_REQ 1
+/* TODO: abandon this constatnt */
+#define ONDIEMET_LOG_STOP 2
+
+#define PID_NONE (-1)
+
+#define ONDIEMET_LOG_STOP_MODE 0
+#define ONDIEMET_LOG_RUN_MODE 1
+#define ONDIEMET_LOG_DEBUG_MODE 2
+
+static int ondiemet_trace_run;
+static struct dentry *dbgfs_met_dir;
+
+struct mutex lock_tracef;
+struct ondiemet_log_req_q_t {
+	struct list_head listq;
+	struct mutex lockq;
+	/* struct semaphore new_evt_sema; */
+	struct completion new_evt_comp;
+	int closeq_flag;
+} ondiemet_log_req_q;
+
+struct ondiemet_log_req {
+	struct list_head list;
+	int cmd_type;
+	const char *src;
+	size_t num;
+
+	void (*on_fini_cb)(const void *p);
+	const void *param;
+};
+
+#define __ondiemet_log_req_init(req, cmd, s, n, pf, p)	\
+	do {						\
+		INIT_LIST_HEAD(&req->list);		\
+		req->cmd_type = cmd;			\
+		req->src = s;				\
+		req->num = n;				\
+		req->on_fini_cb = pf;			\
+		req->param = p;				\
+	} while (0)
+
+#define __ondiemet_log_req_fini(req)		        \
+	do {					        \
+		if (req->on_fini_cb)			\
+			req->on_fini_cb(req->param);	\
+		kfree(req);				\
+	} while (0)
+
+static void __ondiemet_log_req_q_init(struct ondiemet_log_req_q_t *q)
+{
+	INIT_LIST_HEAD(&q->listq);
+	mutex_init(&q->lockq);
+	/* sema_init(&q->new_evt_sema, 0); */
+	init_completion(&q->new_evt_comp);
+	q->closeq_flag = 1;
+}
+
+/* undequeue is seen as a roll-back operation, so it can be done even when the queue is closed */
+static void __ondiemet_log_req_undeq(struct ondiemet_log_req *req)
+{
+	mutex_lock(&ondiemet_log_req_q.lockq);
+	list_add(&req->list, &ondiemet_log_req_q.listq);
+	mutex_unlock(&ondiemet_log_req_q.lockq);
+
+	/* up(&ondiemet_log_req_q.new_evt_sema); */
+	complete(&ondiemet_log_req_q.new_evt_comp);
+}
+
+static int __ondiemet_log_req_enq(struct ondiemet_log_req *req)
+{
+	mutex_lock(&ondiemet_log_req_q.lockq);
+	if (ondiemet_log_req_q.closeq_flag) {
+		mutex_unlock(&ondiemet_log_req_q.lockq);
+		return -EBUSY;
+	}
+
+	list_add_tail(&req->list, &ondiemet_log_req_q.listq);
+	if (req->cmd_type == ONDIEMET_LOG_STOP)
+		ondiemet_log_req_q.closeq_flag = 1;
+	mutex_unlock(&ondiemet_log_req_q.lockq);
+
+	/* up(&ondiemet_log_req_q.new_evt_sema); */
+	complete(&ondiemet_log_req_q.new_evt_comp);
+
+	return 0;
+}
+
+int ondiemet_log_req_enq(const char *src, size_t num, void (*on_fini_cb)(const void *p), const void *param)
+{
+	struct ondiemet_log_req *req = kmalloc(sizeof(*req), GFP_KERNEL);
+
+	__ondiemet_log_req_init(req, ONDIEMET_LOG_REQ, src, num, on_fini_cb, param);
+	return __ondiemet_log_req_enq(req);
+}
+
+/*int down_freezable_interruptible(struct semaphore *sem) */
+int down_freezable_interruptible(struct completion *comp)
+{
+
+	int ret;
+
+	freezer_do_not_count();
+	/* ret = down_interruptible(sem); */
+	ret = wait_for_completion_interruptible(comp);
+	freezer_count();
+
+	return ret;
+}
+
+struct ondiemet_log_req *__ondiemet_log_req_deq(void)
+{
+	struct ondiemet_log_req *ret_req;
+
+	/*if (down_freezable_interruptible(&ondiemet_log_req_q.new_evt_sema))*/
+	if (down_freezable_interruptible(&ondiemet_log_req_q.new_evt_comp))
+		return NULL;
+
+	mutex_lock(&ondiemet_log_req_q.lockq);
+	ret_req = list_entry(ondiemet_log_req_q.listq.next, struct ondiemet_log_req, list);
+	list_del_init(&ret_req->list);
+	mutex_unlock(&ondiemet_log_req_q.lockq);
+
+	return ret_req;
+}
+
+void __ondiemet_log_req_open(void)
+{
+	mutex_lock(&ondiemet_log_req_q.lockq);
+	ondiemet_log_req_q.closeq_flag = 0;
+	mutex_unlock(&ondiemet_log_req_q.lockq);
+}
+
+int __ondiemet_log_req_closed(void)
+{
+	int ret;
+
+	mutex_lock(&ondiemet_log_req_q.lockq);
+	ret = ondiemet_log_req_q.closeq_flag && list_empty(&ondiemet_log_req_q.listq);
+	mutex_unlock(&ondiemet_log_req_q.lockq);
+
+	return ret;
+}
+
+int __ondiemet_log_req_working(void)
+{
+	int ret;
+
+	mutex_lock(&ondiemet_log_req_q.lockq);
+	ret = !ondiemet_log_req_q.closeq_flag;
+	mutex_unlock(&ondiemet_log_req_q.lockq);
+
+	return ret;
+}
+
+static void *__ondiemet_trace_seq_next(struct seq_file *seqf, loff_t *offset)
+{
+	struct ondiemet_log_req *next_req;
+
+	if (ondiemet_trace_run == ONDIEMET_LOG_DEBUG_MODE)
+		pr_debug("[met] __ondiemet_trace_seq_next: pid: %d\n", current->pid);
+
+	if (__ondiemet_log_req_closed())
+		return NULL;
+
+	next_req = __ondiemet_log_req_deq();
+
+	if (next_req == NULL)
+		return NULL;
+
+	if (next_req->cmd_type == ONDIEMET_LOG_STOP) {
+		__ondiemet_log_req_fini(next_req);
+		return NULL;
+	}
+
+	return (void *) next_req;
+}
+
+struct mutex lock_trace_owner_pid;
+pid_t trace_owner_pid = PID_NONE;
+static void *ondiemet_trace_seq_start(struct seq_file *seqf, loff_t *offset)
+{
+	void *ret;
+
+	if (ondiemet_trace_run == ONDIEMET_LOG_DEBUG_MODE) {
+		pr_debug("[met] ondiemet_trace_seq_start: locked_pid: %d, pid: %d, offset: %llu\n",
+			 trace_owner_pid, current->pid, *offset);
+	}
+
+	if (!mutex_trylock(&lock_tracef))
+		return NULL;
+
+	mutex_lock(&lock_trace_owner_pid);
+	trace_owner_pid = current->pid;
+	current->flags |= PF_NOFREEZE;
+	mutex_unlock(&lock_trace_owner_pid);
+
+	ret = __ondiemet_trace_seq_next(seqf, offset);
+
+	return ret;
+}
+
+static void *ondiemet_trace_seq_next(struct seq_file *seqf, void *p, loff_t *offset)
+{
+	if (ondiemet_trace_run == ONDIEMET_LOG_DEBUG_MODE)
+		pr_debug("[met] ondiemet_trace_seq_next: pid: %d\n", current->pid);
+
+	(*offset)++;
+	return __ondiemet_trace_seq_next(seqf, offset);
+}
+
+static int ondiemet_trace_seq_show(struct seq_file *seqf, void *p)
+{
+	struct ondiemet_log_req *req = (struct ondiemet_log_req *) p;
+	size_t l_sz;
+	size_t r_sz;
+	struct ondiemet_log_req *l_req;
+	struct ondiemet_log_req *r_req;
+	int ret;
+
+	if (ondiemet_trace_run == ONDIEMET_LOG_DEBUG_MODE)
+		pr_debug("[met] ondiemet_trace_seq_show: pid: %d\n", current->pid);
+
+	if (req->num >= seqf->size) {
+		l_req = kmalloc(sizeof(*req), GFP_KERNEL);
+		r_req = req;
+
+		l_sz = seqf->size >> 1;
+		r_sz = req->num - l_sz;
+		__ondiemet_log_req_init(l_req, ONDIEMET_LOG_REQ, req->src, l_sz, NULL, NULL);
+		__ondiemet_log_req_init(r_req, ONDIEMET_LOG_REQ, req->src + l_sz,
+					r_sz, req->on_fini_cb, req->param);
+
+		__ondiemet_log_req_undeq(r_req);
+		req = l_req;
+
+		if (ondiemet_trace_run == ONDIEMET_LOG_DEBUG_MODE)
+			pr_debug("[met] ondiemet_trace_seq_show: split request\n");
+	}
+
+	ret = seq_write(seqf, req->src, req->num);
+
+	if (ret) {
+		/* check if seq_file buffer overflows */
+		if (seqf->count == seqf->size) {
+			__ondiemet_log_req_undeq(req);
+		} else {
+			if (ondiemet_trace_run == ONDIEMET_LOG_DEBUG_MODE)
+				pr_debug("[met] ondiemet_trace_seq_show: reading trace record failed, some data may be lost or corrupted\n");
+			__ondiemet_log_req_fini(req);
+		}
+		return 0;
+	}
+
+	__ondiemet_log_req_fini(req);
+	return 0;
+}
+
+static void ondiemet_trace_seq_stop(struct seq_file *seqf, void *p)
+{
+	if (ondiemet_trace_run == ONDIEMET_LOG_DEBUG_MODE)
+		pr_debug("[met] ondiemet_trace_seq_stop: pid: %d\n", current->pid);
+
+	mutex_lock(&lock_trace_owner_pid);
+	if (current->pid == trace_owner_pid) {
+		trace_owner_pid = PID_NONE;
+		mutex_unlock(&lock_tracef);
+	}
+	mutex_unlock(&lock_trace_owner_pid);
+}
+
+static const struct seq_operations ondiemet_trace_seq_ops = {
+	.start = ondiemet_trace_seq_start,
+	.next = ondiemet_trace_seq_next,
+	.stop = ondiemet_trace_seq_stop,
+	.show = ondiemet_trace_seq_show
+};
+
+static int ondiemet_trace_open(struct inode *inode, struct file *fp)
+{
+	return seq_open(fp, &ondiemet_trace_seq_ops);
+}
+
+static const struct file_operations ondiemet_trace_fops = {
+	.owner = THIS_MODULE,
+	.open = ondiemet_trace_open,
+	.read = seq_read,
+	.llseek = seq_lseek,
+	.release = seq_release
+};
+
+/*struct semaphore log_start_sema;*/
+struct completion log_start_comp;
+int ondiemet_log_manager_start(void)
+{
+	int ret;
+
+	/* TODO: choose a better return value */
+	if (__ondiemet_log_req_working())
+		return -EINVAL;
+
+	if (!__ondiemet_log_req_closed()) {
+		/*ret = down_killable(&log_start_sema);*/
+		ret = wait_for_completion_killable(&log_start_comp);
+		if (ret)
+			return ret;
+	}
+
+	__ondiemet_log_req_open();
+
+	return 0;
+}
+
+/*struct semaphore log_stop_sema;*/
+struct completion log_stop_comp;
+static void __log_stop_cb(const void *p)
+{
+	/* up(&log_start_sema); */
+	/* up(&log_stop_sema); */
+	complete(&log_start_comp);
+	complete(&log_stop_comp);
+}
+
+int ondiemet_log_manager_stop(void)
+{
+	int ret;
+	struct ondiemet_log_req *req;
+
+	/* TODO: choose a better return value */
+	if (__ondiemet_log_req_closed())
+		return -EINVAL;
+
+	req = kmalloc(sizeof(*req), GFP_KERNEL);
+
+	__ondiemet_log_req_init(req, ONDIEMET_LOG_STOP, NULL, 0, __log_stop_cb, NULL);
+	/*sema_init(&log_start_sema, 0); */
+	/*sema_init(&log_stop_sema, 0); */
+	init_completion(&log_start_comp);
+	init_completion(&log_stop_comp);
+
+	ret = __ondiemet_log_req_enq(req);
+	if (ret)
+		return ret;
+
+	/* XXX: blocking may be break by SIGKILL */
+	/*return down_killable(&log_stop_sema);*/
+	return wait_for_completion_killable(&log_stop_comp);
+}
+
+int ondiemet_parse_num(const char *str, unsigned int *value, int len)
+{
+	int ret;
+
+	if (len <= 0)
+		return -1;
+
+	if ((len > 2) &&
+	    ((str[0] == '0') &&
+	     ((str[1] == 'x') || (str[1] == 'X')))) {
+		ret = kstrtouint(str, 16, value);
+	} else {
+		ret = kstrtouint(str, 10, value);
+	}
+
+	if (ret != 0)
+		return -1;
+
+	return 0;
+}
+
+/* XXX: seq_file will output only when a page is filled */
+static ssize_t ondiemet_log_write_store(struct device *dev,
+					struct device_attribute *attr,
+					const char *buf,
+					size_t count)
+{
+	char *plog = NULL;
+
+	plog = kmalloc_array(count, sizeof(*plog), GFP_KERNEL);
+	if (!plog) {
+		/* TODO: use a better error code */
+		return -EINVAL;
+	}
+
+	memcpy(plog, buf, count);
+
+	mutex_lock(&dev->mutex);
+	ondiemet_log_req_enq(plog, strnlen(plog, count), kfree, plog);
+	mutex_unlock(&dev->mutex);
+
+	return count;
+}
+
+static DEVICE_ATTR(ondiemet_log_write, 0664, NULL, ondiemet_log_write_store);
+
+static ssize_t ondiemet_log_run_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int sz;
+
+	mutex_lock(&dev->mutex);
+	sz = snprintf(buf, PAGE_SIZE, "%d\n", ondiemet_trace_run);
+	mutex_unlock(&dev->mutex);
+	return sz;
+}
+
+static ssize_t ondiemet_log_run_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+	int ret;
+	int prev_run_state;
+
+	mutex_lock(&dev->mutex);
+
+	prev_run_state = ondiemet_trace_run;
+
+	if (kstrtoint(buf, 10, &ondiemet_trace_run) != 0)
+		return -EINVAL;
+
+	if (ondiemet_trace_run <= ONDIEMET_LOG_STOP_MODE) {
+		ondiemet_trace_run = ONDIEMET_LOG_STOP_MODE;
+		ondiemet_log_manager_stop();
+
+		if (prev_run_state == ONDIEMET_LOG_DEBUG_MODE)
+			device_remove_file(dev, &dev_attr_ondiemet_log_write);
+	} else if (ondiemet_trace_run == ONDIEMET_LOG_RUN_MODE) {
+		ondiemet_trace_run = ONDIEMET_LOG_RUN_MODE;
+		ondiemet_log_manager_start();
+
+		if (prev_run_state == ONDIEMET_LOG_DEBUG_MODE)
+			device_remove_file(dev, &dev_attr_ondiemet_log_write);
+	} else {
+		ondiemet_trace_run = ONDIEMET_LOG_DEBUG_MODE;
+		ondiemet_log_manager_start();
+
+		if (prev_run_state != ONDIEMET_LOG_DEBUG_MODE) {
+			ret = device_create_file(dev, &dev_attr_ondiemet_log_write);
+			if (ret != 0)
+				pr_debug("[met] can not create device node: ondiemet_log_write\n");
+		}
+	}
+
+	mutex_unlock(&dev->mutex);
+
+	return count;
+}
+
+static DEVICE_ATTR(ondiemet_log_run, 0660, ondiemet_log_run_show, ondiemet_log_run_store);
+
+int ondiemet_log_manager_init(struct device *dev)
+{
+	int ret;
+	struct dentry *d;
+
+	mutex_init(&lock_tracef);
+
+	__ondiemet_log_req_q_init(&ondiemet_log_req_q);
+
+	/*sema_init(&log_start_sema, 0);*/
+	/*sema_init(&log_stop_sema, 0);*/
+	init_completion(&log_start_comp);
+	init_completion(&log_stop_comp);
+
+	dbgfs_met_dir = debugfs_create_dir("ondiemet", NULL);
+	if (!dbgfs_met_dir) {
+		pr_debug("[met] can not create debugfs directory: met\n");
+		return -ENOMEM;
+	}
+
+	mutex_init(&lock_trace_owner_pid);
+
+	d = debugfs_create_file("trace", 0644, dbgfs_met_dir, NULL, &ondiemet_trace_fops);
+	if (!d) {
+		pr_debug("[met] can not create devide node in debugfs: ondiemet_trace\n");
+		return -ENOMEM;
+	}
+
+	ondiemet_trace_run = __ondiemet_log_req_working();
+	ret = device_create_file(dev, &dev_attr_ondiemet_log_run);
+	if (ret != 0) {
+		pr_debug("[met] can not create device node: ondiemet_log_run\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+int ondiemet_log_manager_uninit(struct device *dev)
+{
+	device_remove_file(dev, &dev_attr_ondiemet_log_run);
+	debugfs_remove_recursive(dbgfs_met_dir);
+	return 0;
+}
diff --git a/src/devtools/met-driver/4.19/common/ondiemet_log.h b/src/devtools/met-driver/4.19/common/ondiemet_log.h
new file mode 100644
index 0000000..a9ccc40
--- /dev/null
+++ b/src/devtools/met-driver/4.19/common/ondiemet_log.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2019 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _ONDIEMET_LOG_H_
+#define _ONDIEMET_LOG_H_
+
+#include <linux/device.h>
+
+int ondiemet_log_manager_init(struct device *dev);
+int ondiemet_log_manager_uninit(struct device *dev);
+int ondiemet_log_manager_start(void);
+/* Log manager can be reactivated by inserting new requests, i.e., calling ondiemet_log_req_enq() */
+int ondiemet_log_manager_stop(void);
+int ondiemet_log_req_enq(const char *src, size_t num, void (*on_fini_cb) (const void *p),
+			 const void *param);
+
+#endif				/* _ONDIEMET_LOG_H_ */
diff --git a/src/devtools/met-driver/4.19/common/power.c b/src/devtools/met-driver/4.19/common/power.c
new file mode 100644
index 0000000..d3cf723
--- /dev/null
+++ b/src/devtools/met-driver/4.19/common/power.c
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2019 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/cpufreq.h>
+#include <trace/events/power.h>
+
+#include "power.h"
+#include "met_drv.h"
+#include "met_kernel_symbol.h"
+
+noinline void cpu_frequency(unsigned int frequency, unsigned int cpu_id)
+{
+	/* suppose this symbol is available, otherwise, the met.ko will fail */
+	met_cpu_frequency_symbol(frequency, cpu_id);
+}
+
+void force_power_log(int cpu)
+{
+	struct cpufreq_policy *p;
+
+	if (cpu == POWER_LOG_ALL) {
+		for_each_possible_cpu(cpu) {
+			p = cpufreq_cpu_get(cpu);
+			if (p != NULL) {
+				cpu_frequency(p->cur, cpu);
+				cpufreq_cpu_put(p);
+			} else {
+				cpu_frequency(0, cpu);
+			}
+		}
+	} else {
+		p = cpufreq_cpu_get(cpu);
+		if (p != NULL) {
+			cpu_frequency(p->cur, cpu);
+			cpufreq_cpu_put(p);
+		} else {
+			cpu_frequency(0, cpu);
+		}
+	}
+}
+
+void force_power_log_val(unsigned int frequency, int cpu)
+{
+	cpu_frequency(frequency, cpu);
+}
diff --git a/src/devtools/met-driver/4.19/common/power.h b/src/devtools/met-driver/4.19/common/power.h
new file mode 100644
index 0000000..2c8634f
--- /dev/null
+++ b/src/devtools/met-driver/4.19/common/power.h
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2019 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _POWER_H_
+#define _POWER_H_
+
+#define POWER_LOG_ALL	-1
+void force_power_log(int cpu);
+void force_power_log_val(unsigned int frequency, int cpu);
+
+#endif				/* _POWER_H_ */
diff --git a/src/devtools/met-driver/4.19/common/sampler.c b/src/devtools/met-driver/4.19/common/sampler.c
new file mode 100644
index 0000000..23c2cc8
--- /dev/null
+++ b/src/devtools/met-driver/4.19/common/sampler.c
@@ -0,0 +1,702 @@
+/*
+ * Copyright (C) 2019 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/sched/clock.h>
+#include <linux/kernel.h>
+#include <linux/cpuhotplug.h>
+#include <linux/cpu.h>
+#include <linux/sched.h>
+#include <linux/notifier.h>
+#include <linux/module.h>
+#include <linux/irq.h>
+#if 0				/* fix me later, no such file on current tree */
+#include <mach/mt_cpuxgpt.h>
+#endif
+#include <asm/arch_timer.h>
+
+#define	MET_USER_EVENT_SUPPORT
+#include "interface.h"
+#include "sampler.h"
+#include "met_struct.h"
+#include "util.h"
+#include "switch.h"
+#include "trace.h"
+#include "met_drv.h"
+#include "met_tag.h" /* for tracing_mark_write */
+
+#include "cpu_pmu.h"	/* for using kernel perf PMU driver */
+
+#include "met_kernel_symbol.h"
+
+#undef	DEBUG_CPU_NOTIFY
+/* #define DEBUG_CPU_NOTIFY */
+#if	defined(DEBUG_CPU_NOTIFY)
+#ifdef CONFIG_MET_MODULE
+#define	dbg_met_tag_oneshot	met_tag_oneshot_real
+#else
+#define	dbg_met_tag_oneshot	met_tag_oneshot
+#endif /* CONFIG_MET_MODULE */
+#else
+#define	dbg_met_tag_oneshot(class_id, name, value)	({ 0; })
+#endif
+
+static int start;
+static unsigned int online_cpu_map;
+static int curr_polling_cpu;
+static int cpu_related_cnt;
+static int cpu_related_polling_hdlr_cnt;
+
+static int preferred_cpu_list[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
+
+static int calc_preferred_polling_cpu(unsigned int cpu_map)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(preferred_cpu_list); i++) {
+		if (cpu_map & (1 << preferred_cpu_list[i]))
+			return preferred_cpu_list[i];
+	}
+
+	return -1;
+}
+
+static void wq_sync_buffer(struct work_struct *work)
+{
+	int cpu;
+	struct delayed_work *dw = container_of(work, struct delayed_work, work);
+	struct met_cpu_struct *met_cpu_ptr = container_of(dw, struct met_cpu_struct, dwork);
+
+	cpu = smp_processor_id();
+	if (met_cpu_ptr->cpu != cpu) {
+		/* panic("ERROR"); */
+		return;
+	}
+
+	/* sync_samples(cpu); */
+	/* don't re-add the work if we're shutting down */
+	if (met_cpu_ptr->work_enabled)
+		schedule_delayed_work(dw, DEFAULT_TIMER_EXPIRE);
+}
+
+static enum hrtimer_restart met_hrtimer_notify(struct hrtimer *hrtimer)
+{
+	int cpu;
+	int *count;
+	unsigned long long stamp;
+	struct met_cpu_struct *met_cpu_ptr = container_of(hrtimer, struct met_cpu_struct, hrtimer);
+	struct metdevice *c;
+#if	defined(DEBUG_CPU_NOTIFY)
+	char msg[32];
+#endif
+
+	cpu = smp_processor_id();
+#if	defined(DEBUG_CPU_NOTIFY)
+	{
+		char msg[32];
+
+		snprintf(msg, sizeof(msg), "met_hrtimer notify_%d", cpu);
+		dbg_met_tag_oneshot(0, msg, 1);
+	}
+#endif
+
+	if (met_cpu_ptr->cpu != cpu) {
+		/* panic("ERROR2"); */
+		dbg_met_tag_oneshot(0, msg, -3);
+		return HRTIMER_NORESTART;
+	}
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->ondiemet_mode == 0) {
+			if ((c->mode == 0) || (c->timed_polling == NULL))
+				continue;
+		} else if (c->ondiemet_mode == 1) {
+			if ((c->mode == 0) || (c->ondiemet_timed_polling == NULL))
+				continue;
+		} else if (c->ondiemet_mode == 2) {
+			if ((c->mode == 0) || ((c->timed_polling == NULL)
+					       && (c->ondiemet_timed_polling == NULL)))
+				continue;
+		}
+
+		count = per_cpu_ptr(c->polling_count, cpu);
+		if ((*count) > 0) {
+			(*count)--;
+			continue;
+		}
+
+		*(count) = c->polling_count_reload;
+
+		stamp = cpu_clock(cpu);
+
+		if (c->cpu_related == 0) {
+			if (cpu == curr_polling_cpu) {
+				if (c->ondiemet_mode == 0) {
+					c->timed_polling(stamp, 0);
+				} else if (c->ondiemet_mode == 1) {
+					c->ondiemet_timed_polling(stamp, 0);
+				} else if (c->ondiemet_mode == 2) {
+					if (c->timed_polling)
+						c->timed_polling(stamp, 0);
+					if (c->ondiemet_timed_polling)
+						c->ondiemet_timed_polling(stamp, 0);
+				}
+			}
+		} else {
+			if (c->ondiemet_mode == 0) {
+				c->timed_polling(stamp, cpu);
+			} else if (c->ondiemet_mode == 1) {
+				c->ondiemet_timed_polling(stamp, cpu);
+			} else if (c->ondiemet_mode == 2) {
+				if (c->timed_polling)
+					c->timed_polling(stamp, 0);
+				if (c->ondiemet_timed_polling)
+					c->ondiemet_timed_polling(stamp, 0);
+			}
+		}
+	}
+
+	if (met_cpu_ptr->hrtimer_online_check) {
+		online_cpu_map |= (1 << cpu);
+		met_cpu_ptr->hrtimer_online_check = 0;
+		dbg_met_tag_oneshot(0, "met_online check done", cpu);
+		if (calc_preferred_polling_cpu(online_cpu_map) == cpu) {
+			curr_polling_cpu = cpu;
+			dbg_met_tag_oneshot(0, "met_curr polling cpu", cpu);
+		}
+	}
+
+	if (met_cpu_ptr->work_enabled) {
+		hrtimer_forward_now(hrtimer, ns_to_ktime(DEFAULT_HRTIMER_EXPIRE));
+		dbg_met_tag_oneshot(0, msg, 0);
+		return HRTIMER_RESTART;
+	}
+	dbg_met_tag_oneshot(0, msg, 0);
+	return HRTIMER_NORESTART;
+}
+
+static void __met_init_cpu_related_device(void *unused)
+{
+	struct metdevice *c;
+
+	list_for_each_entry(c, &met_list, list) {
+		*(this_cpu_ptr(c->polling_count)) = 0;
+		if (c->ondiemet_mode == 0) {
+			if ((c->cpu_related) && (c->mode) && (c->start))
+				c->start();
+		} else if (c->ondiemet_mode == 1) {
+			if (((c->cpu_related)) && (c->mode) && (c->ondiemet_start))
+				c->ondiemet_start();
+		} else if (c->ondiemet_mode == 2) {
+			if ((c->cpu_related) && (c->mode) && (c->start))
+				c->start();
+			if (((c->cpu_related)) && (c->mode) && (c->ondiemet_start))
+				c->ondiemet_start();
+		}
+	}
+}
+
+static void __met_hrtimer_register(void *unused)
+{
+	struct met_cpu_struct *met_cpu_ptr = NULL;
+	struct hrtimer *hrtimer = NULL;
+	/* struct delayed_work *dw; */
+	/*struct metdevice *c;*/
+
+	met_cpu_ptr = this_cpu_ptr(&met_cpu);
+#if	defined(DEBUG_CPU_NOTIFY)
+	{
+		char msg[32];
+
+		snprintf(msg, sizeof(msg), "met_hrtimer status_%d", met_cpu_ptr->cpu);
+		dbg_met_tag_oneshot(0, msg, 1);
+	}
+#endif
+	/*
+	 * do not open HRtimer when EVENT timer enable
+	 */
+	if (!(met_switch.mode & MT_SWITCH_EVENT_TIMER)) {
+
+		hrtimer = &met_cpu_ptr->hrtimer;
+		hrtimer_init(hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+		hrtimer->function = met_hrtimer_notify;
+
+		if (DEFAULT_HRTIMER_EXPIRE) {
+			met_cpu_ptr->work_enabled = 1;
+			/* schedule_delayed_work_on(smp_processor_id(), dw, DEFAULT_TIMER_EXPIRE); */
+			hrtimer_start(hrtimer, ns_to_ktime(DEFAULT_HRTIMER_EXPIRE),
+				      HRTIMER_MODE_REL_PINNED);
+		}
+	}
+}
+
+static void __met_hrtimer_stop(void *unused)
+{
+	struct met_cpu_struct *met_cpu_ptr;
+	struct hrtimer *hrtimer;
+	/* struct delayed_work *dw; */
+	struct metdevice *c;
+
+	met_cpu_ptr = this_cpu_ptr(&met_cpu);
+#if	defined(DEBUG_CPU_NOTIFY)
+	{
+		char msg[32];
+
+		snprintf(msg, sizeof(msg), "met_hrtimer status_%d", met_cpu_ptr->cpu);
+		dbg_met_tag_oneshot(0, msg, 0);
+	}
+#endif
+	/*
+	 * do not open HRtimer when EVENT timer enable
+	 */
+	if (!(met_switch.mode & MT_SWITCH_EVENT_TIMER)) {
+		hrtimer = &met_cpu_ptr->hrtimer;
+		/* dw = &met_cpu_ptr->dwork; */
+
+		met_cpu_ptr->work_enabled = 0;
+		hrtimer_cancel(hrtimer);
+
+		/* cancel_delayed_work_sync(dw); */
+	}
+	list_for_each_entry(c, &met_list, list) {
+		if (c->ondiemet_mode == 0) {
+			if ((c->cpu_related) && (c->mode) && (c->stop))
+				c->stop();
+		} else if (c->ondiemet_mode == 1) {
+			if ((c->cpu_related) && (c->mode) && (c->ondiemet_stop))
+				c->ondiemet_stop();
+		} else if (c->ondiemet_mode == 2) {
+			if ((c->cpu_related) && (c->mode) && (c->stop))
+				c->stop();
+			if ((c->cpu_related) && (c->mode) && (c->ondiemet_stop))
+				c->ondiemet_stop();
+		}
+		*(this_cpu_ptr(c->polling_count)) = 0;
+	}
+}
+
+static int met_pmu_cpu_notify(enum met_action action, unsigned int cpu)
+{
+	struct met_cpu_struct *met_cpu_ptr;
+	struct delayed_work *dw;
+	int preferred_polling_cpu;
+	struct metdevice *c;
+
+	if (start == 0)
+		return NOTIFY_OK;
+
+#if	defined(DEBUG_CPU_NOTIFY)
+	{
+		char msg[32];
+
+		snprintf(msg, sizeof(msg), "met_cpu notify_%d", cpu);
+		dbg_met_tag_oneshot(0, msg, action);
+	}
+#elif	defined(PR_CPU_NOTIFY)
+	{
+		char msg[32];
+
+		if (met_cpu_notify) {
+			snprintf(msg, sizeof(msg), "met_cpu notify_%d", cpu);
+			dbg_met_tag_oneshot(0, msg, action);
+		}
+	}
+#endif
+
+	if (cpu < 0 || cpu >= NR_CPUS)
+		return NOTIFY_OK;
+
+	switch (action) {
+	case MET_CPU_ONLINE:
+		met_cpu_ptr = &per_cpu(met_cpu, cpu);
+		met_cpu_ptr->hrtimer_online_check = 1;
+		dbg_met_tag_oneshot(0, "met_online check", cpu);
+
+		if (cpu_related_cnt == 0) {
+			/*pr_info("%s, %d: curr_polling_cpu is alive = %d\n",
+			 *		__func__, __LINE__, online_cpu_map & (1 << curr_polling_cpu));
+			 */
+
+			online_cpu_map |= (1 << cpu);
+
+			/* check curr_polling_cpu is alive, if it is down,
+			 * start current cpu hrtimer, and change it to be currr_pollling_cpu
+			 */
+			if ((online_cpu_map & (1 << curr_polling_cpu)) == 0) {
+				met_smp_call_function_single_symbol(cpu, __met_hrtimer_register, NULL, 1);
+				curr_polling_cpu = cpu;
+			}
+		} else {
+			if (cpu_related_polling_hdlr_cnt) {
+				met_smp_call_function_single_symbol(cpu, __met_init_cpu_related_device, NULL, 1);
+				met_smp_call_function_single_symbol(cpu, __met_hrtimer_register, NULL, 1);
+			} else {
+
+				/*pr_info("%s, %d: curr_polling_cpu is alive = %d\n",
+				 *		__func__, __LINE__, online_cpu_map & (1 << curr_polling_cpu));
+				 */
+
+				online_cpu_map |= (1 << cpu);
+
+				/* check curr_polling_cpu is alive, if it is down,
+				 * start current cpu hrtimer, and change it to be currr_pollling_cpu
+				 */
+				if ((online_cpu_map & (1 << curr_polling_cpu)) == 0) {
+					met_smp_call_function_single_symbol(cpu, __met_init_cpu_related_device, NULL, 1);
+					met_smp_call_function_single_symbol(cpu, __met_hrtimer_register, NULL, 1);
+					curr_polling_cpu = cpu;
+				}
+			}
+		}
+
+#ifdef CONFIG_CPU_FREQ
+		force_power_log(cpu);
+#endif
+		list_for_each_entry(c, &met_list, list) {
+			if (c->cpu_state_notify)
+				c->cpu_state_notify(cpu, action);
+		}
+		break;
+
+	case MET_CPU_OFFLINE:
+		list_for_each_entry(c, &met_list, list) {
+			if (c->cpu_state_notify)
+				c->cpu_state_notify(cpu, action);
+		}
+
+		online_cpu_map &= ~(1 << cpu);
+		dbg_met_tag_oneshot(0, "met_offline cpu", cpu);
+		if (cpu == curr_polling_cpu) {
+			/* pr_info("%s, %d: curr_polling_cpu %d is down\n",
+			 *		__func__, __LINE__, curr_polling_cpu);
+			 */
+			preferred_polling_cpu = calc_preferred_polling_cpu(online_cpu_map);
+			/* pr_info("%s, %d: preferred_polling_cpu = %d\n",
+			 *		__func__, __LINE__, preferred_polling_cpu);
+			 */
+			if (preferred_polling_cpu != -1) {
+				curr_polling_cpu = preferred_polling_cpu;
+				dbg_met_tag_oneshot(0, "met_curr polling cpu", curr_polling_cpu);
+
+				if (cpu_related_cnt == 0) {
+					/* pr_info("%s, %d: start cpu %d hrtimer start\n",
+					 *		__func__, __LINE__, curr_polling_cpu);
+					 */
+					met_smp_call_function_single_symbol(curr_polling_cpu, __met_hrtimer_register, NULL, 1);
+				} else if (cpu_related_polling_hdlr_cnt == 0) {
+					met_smp_call_function_single_symbol(curr_polling_cpu, __met_hrtimer_register, NULL, 1);
+				}
+			}
+		}
+
+		met_smp_call_function_single_symbol(cpu, __met_hrtimer_stop, NULL, 1);
+
+		met_cpu_ptr = &per_cpu(met_cpu, cpu);
+		dw = &met_cpu_ptr->dwork;
+		cancel_delayed_work_sync(dw);
+
+		/* sync_samples(cpu); */
+		break;
+	default:
+		list_for_each_entry(c, &met_list, list) {
+			if (c->cpu_state_notify)
+				c->cpu_state_notify(cpu, action);
+		}
+	}
+
+	return NOTIFY_OK;
+}
+
+static int _met_pmu_cpu_notify_online(unsigned int cpu)
+{
+	met_pmu_cpu_notify(MET_CPU_ONLINE, cpu);
+
+	return 0;
+}
+
+static int _met_pmu_cpu_notify_offline(unsigned int cpu)
+{
+	met_pmu_cpu_notify(MET_CPU_OFFLINE, cpu);
+
+	return 0;
+}
+
+int sampler_start(void)
+{
+	int ret, cpu;
+	struct met_cpu_struct *met_cpu_ptr;
+	struct metdevice *c;
+	int preferred_polling_cpu;
+
+	met_set_suspend_notify(0);
+
+#ifdef	CONFIG_CPU_FREQ
+	force_power_log(POWER_LOG_ALL);
+#endif
+
+	for_each_possible_cpu(cpu) {
+		met_cpu_ptr = &per_cpu(met_cpu, cpu);
+		met_cpu_ptr->work_enabled = 0;
+		met_cpu_ptr->hrtimer_online_check = 0;
+		hrtimer_init(&met_cpu_ptr->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+		met_cpu_ptr->hrtimer.function = met_hrtimer_notify;
+		INIT_DELAYED_WORK(&met_cpu_ptr->dwork, wq_sync_buffer);
+	}
+
+	start = 0;
+	ret = cpuhp_setup_state_nocalls(CPUHP_AP_ONLINE_DYN,
+						   "met:online",
+						   _met_pmu_cpu_notify_online,
+						   _met_pmu_cpu_notify_offline);	
+
+	list_for_each_entry(c, &met_list, list) {
+
+		if (try_module_get(c->owner) == 0)
+			continue;
+
+		if ((c->mode) && (c->cpu_related == 1)) {
+			cpu_related_cnt = 1;
+
+			if (c->ondiemet_mode == 0) {
+				if (c->timed_polling)
+					cpu_related_polling_hdlr_cnt = 1;
+			} else if (c->ondiemet_mode == 1) {
+				if (c->ondiemet_timed_polling)
+					cpu_related_polling_hdlr_cnt = 1;
+			} else if (c->ondiemet_mode == 2) {
+				if (c->timed_polling || c->ondiemet_timed_polling)
+					cpu_related_polling_hdlr_cnt = 1;
+			}
+		}
+
+		if (c->ondiemet_mode == 0) {
+			if ((!(c->cpu_related)) && (c->mode) && (c->start))
+				c->start();
+			else if ((c->cpu_related) && (c->mode) && (c->uniq_start))
+				c->uniq_start();
+		} else if (c->ondiemet_mode == 1) {
+			if ((!(c->cpu_related)) && (c->mode) && (c->ondiemet_start))
+				c->ondiemet_start();
+			if ((c->cpu_related) && (c->mode) && (c->uniq_ondiemet_start))
+				c->uniq_ondiemet_start();
+		} else if (c->ondiemet_mode == 2) {
+			if ((!(c->cpu_related)) && (c->mode) && (c->start))
+				c->start();
+			else if ((c->cpu_related) && (c->mode) && (c->uniq_start))
+				c->uniq_start();
+
+			if ((!(c->cpu_related)) && (c->mode) && (c->ondiemet_start))
+				c->ondiemet_start();
+			else if ((c->cpu_related) && (c->mode) && (c->uniq_ondiemet_start))
+				c->uniq_ondiemet_start();
+		}
+	}
+
+	get_online_cpus();
+	online_cpu_map = 0;
+	for_each_online_cpu(cpu) {
+		online_cpu_map |= (1 << cpu);
+	}
+	dbg_met_tag_oneshot(0, "met_online cpu map", online_cpu_map);
+	preferred_polling_cpu = calc_preferred_polling_cpu(online_cpu_map);
+	if (preferred_polling_cpu != -1)
+		curr_polling_cpu = preferred_polling_cpu;
+	dbg_met_tag_oneshot(0, "met_curr polling cpu", curr_polling_cpu);
+	start = 1;
+
+	if (cpu_related_cnt == 0) {
+		met_smp_call_function_single_symbol(curr_polling_cpu, __met_hrtimer_register, NULL, 1);
+	}
+	else {
+		//on_each_cpu(__met_hrtimer_start, NULL, 1);
+		for_each_online_cpu(cpu) {
+			met_smp_call_function_single_symbol(cpu, __met_init_cpu_related_device, NULL, 1);
+		}
+
+		if (cpu_related_polling_hdlr_cnt) {
+			for_each_online_cpu(cpu) {
+				met_smp_call_function_single_symbol(cpu, __met_hrtimer_register, NULL, 1);
+			}
+		} else {
+			met_smp_call_function_single_symbol(curr_polling_cpu, __met_hrtimer_register, NULL, 1);
+		}
+	}
+	put_online_cpus();
+
+	return ret;
+}
+
+void sampler_stop(void)
+{
+	int cpu;
+	struct met_cpu_struct *met_cpu_ptr;
+	struct metdevice *c;
+	struct delayed_work *dw;
+
+
+	get_online_cpus();
+	//on_each_cpu(__met_hrtimer_stop, NULL, 1);
+	online_cpu_map = 0;
+	for_each_online_cpu(cpu) {
+		online_cpu_map |= (1 << cpu);
+	}
+
+	for_each_online_cpu(cpu) {
+		met_smp_call_function_single_symbol(cpu, __met_hrtimer_stop, NULL, 1);
+	}
+
+	/* for_each_online_cpu(cpu) { */
+	for_each_possible_cpu(cpu) {	/* Just for case */
+		met_cpu_ptr = &per_cpu(met_cpu, cpu);
+		dw = &met_cpu_ptr->dwork;
+		cancel_delayed_work_sync(dw);
+		/* sync_samples(cpu); */
+	}
+	start = 0;
+	put_online_cpus();
+
+	cpuhp_remove_state_nocalls(CPUHP_AP_ONLINE_DYN);
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->ondiemet_mode == 0) {
+			if ((!(c->cpu_related)) && (c->mode) && (c->stop))
+				c->stop();
+			else if ((c->cpu_related) && (c->mode) && (c->uniq_stop))
+				c->uniq_stop();
+		} else if (c->ondiemet_mode == 1) {
+			if ((!(c->cpu_related)) && (c->mode) && (c->ondiemet_stop))
+				c->ondiemet_stop();
+			else if ((c->cpu_related) && (c->mode) && (c->uniq_ondiemet_stop))
+				c->uniq_ondiemet_stop();
+		} else if (c->ondiemet_mode == 2) {
+			if ((!(c->cpu_related)) && (c->mode) && (c->stop))
+				c->stop();
+			else if ((c->cpu_related) && (c->mode) && (c->uniq_stop))
+				c->uniq_stop();
+
+			if ((!(c->cpu_related)) && (c->mode) && (c->ondiemet_stop))
+				c->ondiemet_stop();
+			else if ((c->cpu_related) && (c->mode) && (c->uniq_ondiemet_stop))
+				c->uniq_ondiemet_stop();
+		}
+		module_put(c->owner);
+	}
+
+	cpu_related_cnt = 0;
+	cpu_related_polling_hdlr_cnt = 0;
+}
+
+#if 0 /* cann't use static now */
+enum {
+	MET_SUSPEND = 1,
+	MET_RESUME = 2,
+};
+
+static noinline void tracing_mark_write(int op)
+{
+	switch (op) {
+	case MET_SUSPEND:
+		MET_TRACE("C|0|MET_SUSPEND|1");
+		break;
+	case MET_RESUME:
+		MET_TRACE("C|0|MET_SUSPEND|0");
+		break;
+	}
+}
+#endif
+
+int met_hrtimer_suspend(void)
+{
+	struct metdevice *c;
+
+	met_set_suspend_notify(1);
+	/* tracing_mark_write(MET_SUSPEND); */
+	tracing_mark_write(TYPE_MET_SUSPEND, 0, 0, 0, 0, 0);
+	if (start == 0)
+		return 0;
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->suspend)
+			c->suspend();
+	}
+
+	/* get current COUNT */
+	MET_TRACE("TS: %llu GPT: %llX", sched_clock(), arch_counter_get_cntvct());
+	return 0;
+}
+
+void met_hrtimer_resume(void)
+{
+	struct metdevice *c;
+
+	/* get current COUNT */
+	MET_TRACE("TS: %llu GPT: %llX", sched_clock(), arch_counter_get_cntvct());
+
+	/* tracing_mark_write(MET_RESUME); */
+	tracing_mark_write(TYPE_MET_RESUME, 0, 0, 0, 0, 0);
+	if (start == 0)
+		return;
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->resume)
+			c->resume();
+	}
+}
+
+/*
+ * event timer:
+ * register IRQ, sched_switch event to monitor Polling count
+ * count can be printed at any live cpu.
+ */
+void met_event_timer_notify(void)
+{
+	unsigned long long stamp;
+	struct metdevice *c;
+	int cpu = -1;
+
+	if (start == 0)
+		return;
+
+	cpu = smp_processor_id();
+	list_for_each_entry(c, &met_list, list) {
+		stamp = local_clock();
+
+		if (c->prev_stamp == 0)
+			c->prev_stamp = stamp;
+
+		/* Critical Section Start */
+		/* try spinlock to prevent a event print twice between config time interval */
+		if (!spin_trylock(&(c->my_lock)))
+			continue;
+
+		/*
+		 * DEFAULT_HRTIMER_EXPIRE (met_hrtimer_expire):
+		 * sample_rate == 0 --> always print
+		 * sample_rate == 1000 --> print interval larger than 1 ms
+		 */
+		if (DEFAULT_HRTIMER_EXPIRE == 0 || (stamp - c->prev_stamp) < DEFAULT_HRTIMER_EXPIRE) {
+			spin_unlock(&(c->my_lock));
+			continue;
+		}
+
+		c->prev_stamp = stamp;
+		spin_unlock(&(c->my_lock));
+		/* Critical Section End */
+
+		if ((c->mode == 0) || (c->timed_polling == NULL))
+			continue;
+
+		stamp = local_clock();
+		c->timed_polling(stamp, cpu);
+	}
+}
+
diff --git a/src/devtools/met-driver/4.19/common/sampler.h b/src/devtools/met-driver/4.19/common/sampler.h
new file mode 100644
index 0000000..488dec4
--- /dev/null
+++ b/src/devtools/met-driver/4.19/common/sampler.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2019 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _SAMPLER_H_
+#define _SAMPLER_H_
+
+/*
+ * sampling rate: 1ms
+ * log generating rate: 10ms
+ */
+#if 0
+#define DEFAULT_TIMER_EXPIRE (HZ / 100)
+#define DEFAULT_HRTIMER_EXPIRE (TICK_NSEC / 10)
+#else
+extern int met_timer_expire;	/* in jiffies */
+extern int met_hrtimer_expire;	/* in us */
+#define DEFAULT_TIMER_EXPIRE (met_timer_expire)
+#define DEFAULT_HRTIMER_EXPIRE (met_hrtimer_expire)
+#endif
+/*
+ * sampling rate: 10ms
+ * log generating rate: 100ms
+ */
+/* #define DEFAULT_TIMER_EXPIRE (HZ / 10) */
+/* #define DEFAULT_HRTIMER_EXPIRE (TICK_NSEC / 1) */
+
+int met_hrtimer_start(void);
+void met_hrtimer_stop(void);
+int sampler_start(void);
+void sampler_stop(void);
+
+extern struct list_head met_list;
+extern void add_cookie(struct pt_regs *regs, int cpu);
+extern int met_hrtimer_suspend(void);
+extern void met_hrtimer_resume(void);
+extern void met_event_timer_notify(void);
+
+#ifdef CONFIG_CPU_FREQ
+#include "power.h"
+#endif
+
+#endif				/* _SAMPLER_H_ */
diff --git a/src/devtools/met-driver/4.19/common/spmtwam/ap/met_spmtwam.c b/src/devtools/met-driver/4.19/common/spmtwam/ap/met_spmtwam.c
new file mode 100644
index 0000000..e8c1f79
--- /dev/null
+++ b/src/devtools/met-driver/4.19/common/spmtwam/ap/met_spmtwam.c
@@ -0,0 +1,690 @@
+/*
+ * Copyright (C) 2019 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/random.h>
+#include <linux/fs.h>
+#include <linux/ctype.h>
+
+#include <mtk_spm.h>
+#include "met_drv.h"
+#include "trace.h"
+#include "core_plf_init.h"
+#include "interface.h"
+#include "met_spmtwam.h"
+
+/* #define SPM_TWAM_DEBUG */
+
+#define INFRA_FMEM_DCM_BASE 0x1020E000
+#define INFRA_FMEM_DCM_SIZE 0x1000
+#define FMEM_MUX_ADDR_OFFSET 0x200
+#define FMEM_MUX_VALUE 0x780
+#define TWAM_DBG_SIG_BASE 0x0D0A0000
+#define TWAM_DBG_SIG_SIZE 0x100
+#define TWAM_DBG_SIG_OFFSET 0x94
+
+
+struct metdevice met_spmtwam;
+static struct kobject *kobj_spmtwam;
+/* static void __iomem *fmem_dcm_base; */
+static void __iomem *twam_dbg_signal_base;
+static struct met_spmtwam_para spmtwam_para[MAX_EVENT_COUNT];
+
+#ifdef SPM_TWAM_DEBUG
+static unsigned int debug_signal_val;
+#endif
+
+static struct twam_sig twamsig;
+static struct twam_sig montype;			/* b'00: rising, b'01: falling, b'10: high, b'11: low */
+static struct twam_sig dbgout;
+static int used_count;
+static int start;
+static bool twam_clock_mode = TWAM_SPEED_MODE;		/* true:speed mode, false:normal mode */
+static unsigned int window_len = 1300000;	/* 50 ms in 26 MHz */
+static unsigned int idle_sel;
+
+#define MONTYPE_SHOW_IMPLEMENT(num) \
+	do { \
+		int i; \
+		i = snprintf(buf, PAGE_SIZE, "%d\n", montype.sig ## num); \
+		return i; \
+	} while (0)
+
+#define MONTYPE_STORE_IMPLEMENT(num) \
+	do { \
+		int value; \
+		if ((n == 0) || (buf == NULL)) \
+			return -EINVAL; \
+		if (kstrtoint(buf, 10, &value) != 0) \
+			return -EINVAL; \
+		if (value < 0 || value > 3) \
+			return -EINVAL; \
+		montype.sig ## num= value; \
+		return n; \
+	} while (0)
+
+#define DBGOUT_SHOW_IMPLEMENT(num) \
+	do { \
+		int i; \
+		i = snprintf(buf, PAGE_SIZE, "%d\n", dbgout.sig ## num); \
+		return i; \
+	} while (0)
+
+#define DBGOUT_STORE_IMPLEMENT(num) \
+	do { \
+		int value; \
+		if ((n == 0) || (buf == NULL)) \
+			return -EINVAL; \
+		if (kstrtoint(buf, 10, &value) != 0) \
+			return -EINVAL; \
+		if (value < 0 || value > 127) \
+			return -EINVAL; \
+		dbgout.sig ## num = value; \
+		return n; \
+	} while (0)
+
+
+static ssize_t montype0_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	MONTYPE_SHOW_IMPLEMENT(0);
+}
+
+static ssize_t montype0_store(struct kobject *kobj,
+			struct kobj_attribute *attr,
+			const char *buf,
+			size_t n)
+{
+	MONTYPE_STORE_IMPLEMENT(0);
+}
+
+static ssize_t montype1_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	MONTYPE_SHOW_IMPLEMENT(1);
+}
+
+static ssize_t montype1_store(struct kobject *kobj,
+			struct kobj_attribute *attr,
+			const char *buf,
+			size_t n)
+{
+	MONTYPE_STORE_IMPLEMENT(1);
+}
+
+static ssize_t montype2_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	MONTYPE_SHOW_IMPLEMENT(2);
+}
+
+static ssize_t montype2_store(struct kobject *kobj,
+			struct kobj_attribute *attr,
+			const char *buf,
+			size_t n)
+{
+	MONTYPE_STORE_IMPLEMENT(2);
+}
+
+static ssize_t montype3_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	MONTYPE_SHOW_IMPLEMENT(3);
+}
+
+static ssize_t montype3_store(struct kobject *kobj,
+			struct kobj_attribute *attr,
+			const char *buf,
+			size_t n)
+{
+	MONTYPE_STORE_IMPLEMENT(3);
+}
+
+static ssize_t window_len_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	int i;
+
+	i = snprintf(buf, PAGE_SIZE, "%d\n", window_len);
+
+	return i;
+}
+
+static ssize_t window_len_store(struct kobject *kobj,
+			struct kobj_attribute *attr,
+			const char *buf,
+			size_t n)
+{
+	int value;
+
+	if ((n == 0) || (buf == NULL))
+		return -EINVAL;
+
+	if (kstrtoint(buf, 10, &value) != 0)
+		return -EINVAL;
+
+	if (value < 0)
+		return -EINVAL;
+
+	window_len = value;
+
+	return n;
+}
+
+static ssize_t dbgout0_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	DBGOUT_SHOW_IMPLEMENT(0);
+}
+
+static ssize_t dbgout0_store(struct kobject *kobj,
+			struct kobj_attribute *attr,
+			const char *buf,
+			size_t n)
+{
+	DBGOUT_STORE_IMPLEMENT(0);
+}
+
+static ssize_t dbgout1_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	DBGOUT_SHOW_IMPLEMENT(1);
+}
+
+static ssize_t dbgout1_store(struct kobject *kobj,
+			struct kobj_attribute *attr,
+			const char *buf,
+			size_t n)
+{
+	DBGOUT_STORE_IMPLEMENT(1);
+}
+
+static ssize_t dbgout2_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	DBGOUT_SHOW_IMPLEMENT(2);
+}
+
+static ssize_t dbgout2_store(struct kobject *kobj,
+			struct kobj_attribute *attr,
+			const char *buf,
+			size_t n)
+{
+	DBGOUT_STORE_IMPLEMENT(2);
+}
+
+static ssize_t dbgout3_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	DBGOUT_SHOW_IMPLEMENT(3);
+}
+
+static ssize_t dbgout3_store(struct kobject *kobj,
+			struct kobj_attribute *attr,
+			const char *buf,
+			size_t n)
+{
+	DBGOUT_STORE_IMPLEMENT(3);
+}
+
+#ifdef SPM_TWAM_DEBUG
+/* extern void *mt_spm_base_get(void); */
+static ssize_t debug_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	int ret;
+
+	ret = snprintf(buf, PAGE_SIZE, "0x%x\n", debug_signal_val);
+	ret += snprintf(buf+ret, PAGE_SIZE-ret, "%d, %d, %d, %d\n",
+			twam_sig_size[0], twam_sig_size[1], twam_sig_size[2], twam_sig_size[3]);
+/*	ret += snprintf(buf+ret, PAGE_SIZE-ret, "spm_base_addr: %p\n", mt_spm_base_get()); */
+
+	return ret;
+}
+static struct kobj_attribute debug_attr = __ATTR_RO(debug);
+#endif
+
+static struct kobj_attribute montype0_attr = __ATTR(montype0, 0664, montype0_show, montype0_store);
+static struct kobj_attribute montype1_attr = __ATTR(montype1, 0664, montype1_show, montype1_store);
+static struct kobj_attribute montype2_attr = __ATTR(montype2, 0664, montype2_show, montype2_store);
+static struct kobj_attribute montype3_attr = __ATTR(montype3, 0664, montype3_show, montype3_store);
+static struct kobj_attribute window_len_attr = __ATTR(window_len, 0664, window_len_show, window_len_store);
+static struct kobj_attribute dbgout0_attr = __ATTR(dbgout0, 0664, dbgout0_show, dbgout0_store);
+static struct kobj_attribute dbgout1_attr = __ATTR(dbgout1, 0664, dbgout1_show, dbgout1_store);
+static struct kobj_attribute dbgout2_attr = __ATTR(dbgout2, 0664, dbgout2_show, dbgout2_store);
+static struct kobj_attribute dbgout3_attr = __ATTR(dbgout3, 0664, dbgout3_show, dbgout3_store);
+
+
+/* create spmtwam related kobj node */
+#define KOBJ_ATTR_LIST \
+	do { \
+		KOBJ_ATTR_ITEM(montype0); \
+		KOBJ_ATTR_ITEM(montype1); \
+		KOBJ_ATTR_ITEM(montype2); \
+		KOBJ_ATTR_ITEM(montype3); \
+		KOBJ_ATTR_ITEM(window_len); \
+		KOBJ_ATTR_ITEM(dbgout0); \
+		KOBJ_ATTR_ITEM(dbgout1); \
+		KOBJ_ATTR_ITEM(dbgout2); \
+		KOBJ_ATTR_ITEM(dbgout3); \
+	} while (0)
+
+static int met_spmtwam_create(struct kobject *parent)
+{
+	int ret = 0;
+
+	kobj_spmtwam = parent;
+
+#define KOBJ_ATTR_ITEM(attr_name) \
+	do { \
+		ret = sysfs_create_file(kobj_spmtwam, &attr_name ## _attr.attr); \
+		if (ret != 0) { \
+			pr_notice("Failed to create " #attr_name " in sysfs\n"); \
+			return ret; \
+		} \
+	} while (0)
+	KOBJ_ATTR_LIST;
+#undef  KOBJ_ATTR_ITEM
+
+#ifdef SPM_TWAM_DEBUG
+	ret = sysfs_create_file(kobj_spmtwam, &debug_attr.attr);
+	if (ret != 0) {
+		pr_debug("Failed to create debug in sysfs\n");
+		return ret;
+	}
+#endif
+
+	/* init. */
+	montype.sig0 = 0x2;
+	montype.sig1 = 0x2;
+	montype.sig2 = 0x2;
+	montype.sig3 = 0x2;
+
+#if 0
+	dbgout.sig0 = 87;
+	dbgout.sig1 = 89;
+	dbgout.sig2 = 91;
+	dbgout.sig3 = 106;
+#endif
+
+	return ret;
+}
+
+static void met_spmtwam_delete(void)
+{
+#define KOBJ_ATTR_ITEM(attr_name) \
+	sysfs_remove_file(kobj_spmtwam, &attr_name ## _attr.attr)
+
+	if (kobj_spmtwam != NULL) {
+		KOBJ_ATTR_LIST;
+		kobj_spmtwam = NULL;
+	}
+#undef  KOBJ_ATTR_ITEM
+
+#ifdef SPM_TWAM_DEBUG
+	sysfs_remove_file(kobj_spmtwam, &debug_attr.attr);
+#endif
+}
+
+void ms_spmtwam(struct twam_sig *ts)
+{
+	switch (used_count) {
+	case 1:
+		MET_TRACE(MP_FMT1,
+			(ts->sig0));
+		break;
+	case 2:
+		MET_TRACE(MP_FMT2,
+			(ts->sig0),
+			(ts->sig1));
+		break;
+	case 3:
+		MET_TRACE(MP_FMT3,
+			(ts->sig0),
+			(ts->sig1),
+			(ts->sig2));
+		break;
+	case 4:
+		MET_TRACE(MP_FMT4,
+			(ts->sig0),
+			(ts->sig1),
+			(ts->sig2),
+			(ts->sig3));
+		break;
+	default:
+		MET_SPMTWAM_ERR("No assign profile event\n");
+		break;
+	}
+}
+
+static int reset_driver_stat(void)
+{
+	met_spmtwam.mode = 0;
+	used_count = 0;
+	start = 0;
+	return 0;
+}
+
+void spm_twam_enable_debug_out(struct twam_sig *sig, void *addr)
+{
+	int value = 0;
+
+	value |= ((1 << 31) | (sig->sig3 << 24) | (sig->sig2 << 16) | (sig->sig1 << 8) | (sig->sig0 << 0));
+
+#ifdef SPM_TWAM_DEBUG
+	debug_signal_val = value;
+#endif
+
+	writel(value, addr);
+}
+
+void spm_twam_disable_debug_out(void *addr)
+{
+	unsigned int value = 0;
+
+	value = readl(addr);
+	value &= (((unsigned int)(1 << 31)) - 1);
+
+#ifdef SPM_TWAM_DEBUG
+	debug_signal_val = value;
+#endif
+
+	writel(value, addr);
+}
+
+/*
+ * Called from "met-cmd --start"
+ */
+static void spmtwam_start(void)
+{
+	if (idle_sel == 3) {
+#if 0
+		/* swithc idle signal D id0 pimux to fmem */
+		if (fmem_dcm_base == NULL) {
+			fmem_dcm_base = ioremap_nocache(INFRA_FMEM_DCM_BASE, INFRA_FMEM_DCM_SIZE);
+			if (!fmem_dcm_base) {
+				pr_debug("fmem_dcm_base ioremap fail...");
+				return;
+			}
+
+			writel(FMEM_MUX_VALUE, (fmem_dcm_base + FMEM_MUX_ADDR_OFFSET));
+		}
+#endif
+	} else if (idle_sel == 2) {
+		/* debug signal mapping */
+		if (twam_dbg_signal_base == NULL) {
+			twam_dbg_signal_base = ioremap_nocache(TWAM_DBG_SIG_BASE, TWAM_DBG_SIG_SIZE);
+			if (!twam_dbg_signal_base) {
+				pr_debug("twam_dbg_signal_base ioremap fail...");
+				return;
+			}
+		}
+
+		spm_twam_enable_debug_out(&dbgout, (twam_dbg_signal_base + TWAM_DBG_SIG_OFFSET));
+	}
+
+	if (spm_twam_set_mon_type_symbol)
+		spm_twam_set_mon_type_symbol(&montype);
+	else {
+		MET_SPMTWAM_ERR("spm_twam_set_mon_type_symbol is NULL\n");
+		return;
+	}
+
+	if (spm_twam_set_window_length_symbol)
+		spm_twam_set_window_length_symbol(window_len);
+	else {
+		MET_SPMTWAM_ERR("spm_twam_set_window_length_symbol is NULL\n");
+		return;
+	}
+
+	if (spm_twam_set_idle_select_symbol)
+		spm_twam_set_idle_select_symbol(idle_sel);
+	else {
+		MET_SPMTWAM_ERR("spm_twam_set_idle_select_symbol is NULL\n");
+		return;
+	}
+
+	if (spm_twam_register_handler_symbol)
+		spm_twam_register_handler_symbol(ms_spmtwam);
+	else {
+		MET_SPMTWAM_ERR("spm_twam_register_handler_symbol is NULL\n");
+		return;
+	}
+
+	if (spm_twam_enable_monitor_symbol)
+		spm_twam_enable_monitor_symbol(&twamsig, twam_clock_mode);
+	else {
+		MET_SPMTWAM_ERR("spm_twam_enable_monitor_symbol is NULL\n");
+		return;
+	}
+
+	start = 1;
+}
+
+/*
+ * Called from "met-cmd --stop"
+ */
+static void spmtwam_stop(void)
+{
+	if (idle_sel == 3) {
+#if 0
+		if (fmem_dcm_base)
+			iounmap(fmem_dcm_base);
+#endif
+	} else if (idle_sel == 2) {
+		spm_twam_disable_debug_out(twam_dbg_signal_base + TWAM_DBG_SIG_OFFSET);
+
+		if (twam_dbg_signal_base) {
+			iounmap(twam_dbg_signal_base);
+			twam_dbg_signal_base = NULL;
+		}
+	}
+
+	if (spm_twam_register_handler_symbol)
+		spm_twam_register_handler_symbol(NULL);
+	else {
+		MET_SPMTWAM_ERR("spm_twam_register_handler_symbol is NULL\n");
+		return;
+	}
+
+	if (spm_twam_disable_monitor_symbol)
+		spm_twam_disable_monitor_symbol();
+	else {
+		MET_SPMTWAM_ERR("spm_twam_disable_monitor_symbol is NULL\n");
+		return;
+	}
+}
+
+static const char header[] = "met-info [000] 0.0: ms_spmtwam_header: ";
+
+/*
+ * It will be called back when run "met-cmd --extract" and mode is 1
+ */
+static int spmtwam_print_header(char *buf, int len)
+{
+    int i, total_size;
+	char idle_sig;
+    unsigned int event;
+
+    total_size = snprintf(buf, PAGE_SIZE, header);
+
+    for (i = 0; i < used_count; i++) {
+		idle_sig = spmtwam_para[i].idle_sig;
+		event = spmtwam_para[i].event;
+
+		total_size += snprintf(buf + total_size, PAGE_SIZE - total_size,
+                        "signal_%c_%02u,", idle_sig, event);
+    }
+
+	/* cut the last comma */
+	buf[total_size - 1] = '\n';
+
+    total_size += snprintf(buf + total_size, PAGE_SIZE - total_size, "met-info [000] 0.0: spmtwam_clock_mode: %s\n",
+            twam_clock_mode == TWAM_SPEED_MODE ? "speed" : "normal");
+
+#ifdef SPMTWAM_SINGLE_IDLE_SIGNAL
+    total_size += snprintf(buf + total_size, PAGE_SIZE - total_size, "met-info [000] 0.0: spmtwam_idle_signal_support: %s\n",
+					TWAM_SINGLE_IDLE_SIGNAL);
+#endif
+
+#ifdef SPMTWAM_MULTIPLE_IDLE_SIGNAL
+    total_size += snprintf(buf + total_size, PAGE_SIZE - total_size, "met-info [000] 0.0: spmtwam_idle_signal_support: %s\n",
+					TWAM_MULTIPLE_IDLE_SIGNAL);
+#endif
+
+    reset_driver_stat();
+    return total_size;
+}
+
+static int assign_slot(char idle_sig, unsigned int event)
+{
+	int i;
+	int sig2int;
+
+	if (used_count == MAX_EVENT_COUNT) {
+		PR_BOOTMSG("%s exceed max used event count\n", MET_SPMTWAM_TAG);
+		return -1;
+	}
+
+	/* check duplicated */
+	for (i = 0; i < used_count; i++) {
+		if ((spmtwam_para[i].idle_sig == idle_sig) &&
+			(spmtwam_para[i].event == event)) {
+			PR_BOOTMSG("%s input duplicated event %u\n", MET_SPMTWAM_TAG, event);
+			return -2;
+		}
+	}
+
+	/* check idle_sig range in a~d or A~D */
+	if (tolower(idle_sig) < 'a' || tolower(idle_sig) > 'd') {
+		PR_BOOTMSG("%s input idle signal %c is not in a~d range\n",
+					MET_SPMTWAM_TAG, idle_sig);
+		return -3;
+	}
+
+	/* check event no */
+	if (event > MAX_TWAM_EVENT_COUNT) {
+		PR_BOOTMSG("%s input event %u exceed max twam event %u\n",
+					MET_SPMTWAM_TAG, event, MAX_TWAM_EVENT_COUNT);
+		return -4;
+	}
+
+#ifdef SPMTWAM_SINGLE_IDLE_SIGNAL
+	if (used_count > 0) {
+		for (i = 0; i < used_count; i++) {
+			if (idle_sig != spmtwam_para[i].idle_sig) {
+				PR_BOOTMSG("%s %c idle signal is defferent previous, only support one idle signal\n",
+							MET_SPMTWAM_TAG, idle_sig);
+				return -5;
+			}
+		}
+	}
+#endif
+
+	spmtwam_para[used_count].idle_sig = idle_sig;
+	spmtwam_para[used_count].event = event;
+
+	sig2int = (int) (tolower(idle_sig) - 'a');
+#ifdef SPMTWAM_SINGLE_IDLE_SIGNAL
+	idle_sel = sig2int;
+#endif
+
+	switch (used_count) {
+	case 0:
+		twamsig.sig0 = event;
+		break;
+	case 1:
+		twamsig.sig1 = event;
+		break;
+	case 2:
+		twamsig.sig2 = event;
+		break;
+	case 3:
+		twamsig.sig3 = event;
+		break;
+	}
+
+	used_count++;
+
+	return 0;
+}
+
+static char help[] =
+	"  --spmtwam=clock:[speed|normal]	default is normal\n"
+	"					normal mode monitors 4 channels\n"
+	"					speed mode monitors 4 channels\n"
+	"  --spmtwam=signal:selx\n"
+	"					signal= a ~ d for idle signal A~D select\n"
+	"					selx= 0 ~ 31 for for channel event\n";
+
+/*
+ * Called from "met-cmd --help"
+ */
+static int spmtwam_print_help(char *buf, int len)
+{
+	return snprintf(buf, PAGE_SIZE, help);
+}
+
+static int spmtwam_process_argument(const char *arg, int len)
+{
+	if (start == 1)
+		reset_driver_stat();
+
+	if (strncmp(arg, "clock:", 6) == 0) {
+		if (strncmp(&(arg[6]), "speed", 5) == 0) {
+			twam_clock_mode = TWAM_SPEED_MODE;
+		} else if (strncmp(&(arg[6]), "normal", 6) == 0) {
+			twam_clock_mode = TWAM_NORMAL_MODE;
+		} else {
+			PR_BOOTMSG("%s unknown clock mode\n", MET_SPMTWAM_TAG);
+
+			goto error;
+		}
+	} else {
+		char signal;
+		int event;
+		int ret;
+
+		if (len < 3) {
+			PR_BOOTMSG("%s input parameter is too short !!!\n", MET_SPMTWAM_TAG);
+			goto error;
+		}
+
+		/*
+		 * parse arguments
+		 */
+		ret = sscanf(arg, "%c:%u", &signal, &event);
+		if (ret < 2) {
+			PR_BOOTMSG("%s input parameter is wrong format !!!\n", MET_SPMTWAM_TAG);
+			goto error;
+		}
+
+		if (assign_slot(signal, event) < 0) {
+			goto error;
+		}
+	}
+
+	met_spmtwam.mode = 1;
+	return 0;
+
+error:
+	reset_driver_stat();
+	return -1;
+}
+
+struct metdevice met_spmtwam = {
+	.name = "spmtwam",
+	.owner = THIS_MODULE,
+	.type = MET_TYPE_BUS,
+	.create_subfs = met_spmtwam_create,
+	.delete_subfs = met_spmtwam_delete,
+	.cpu_related = 0,
+	.start = spmtwam_start,
+	.stop = spmtwam_stop,
+	.reset = reset_driver_stat,
+	.print_help = spmtwam_print_help,
+	.print_header = spmtwam_print_header,
+	.process_argument = spmtwam_process_argument
+};
+EXPORT_SYMBOL(met_spmtwam);
diff --git a/src/devtools/met-driver/4.19/common/spmtwam/include/met_spmtwam.h b/src/devtools/met-driver/4.19/common/spmtwam/include/met_spmtwam.h
new file mode 100644
index 0000000..28e7636
--- /dev/null
+++ b/src/devtools/met-driver/4.19/common/spmtwam/include/met_spmtwam.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2019 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __MET_SPMTWAM_H__
+#define __MET_SPMTWAM_H__
+
+#define MET_SPMTWAM_TAG "[met_spmtwam]"
+#define MET_SPMTWAM_ERR(format, ...) \
+	do { \
+		MET_TRACE(MET_SPMTWAM_TAG format, ##__VA_ARGS__); \
+		PR_BOOTMSG(MET_SPMTWAM_TAG format, ##__VA_ARGS__); \
+	} while (0)
+
+#define TWAM_ENABLE true
+#define TWAM_DISABLE false
+#define	TWAM_SPEED_MODE true
+#define	TWAM_NORMAL_MODE false
+#define TWAM_DEBUG_SIG_ENABLE 1
+#define TWAM_DEBUG_SIG_DISABLE 0
+#define TWAM_SINGLE_IDLE_SIGNAL "single"
+#define TWAM_MULTIPLE_IDLE_SIGNAL "multiple"
+
+struct met_spmtwam_para {
+	char idle_sig;
+	int event;
+};
+
+
+/* event counters by HW spec */
+#define MAX_EVENT_COUNT 4
+#define MAX_TWAM_EVENT_COUNT 32
+
+#endif
diff --git a/src/devtools/met-driver/4.19/common/spmtwam/sspm/met_spmtwam.c b/src/devtools/met-driver/4.19/common/spmtwam/sspm/met_spmtwam.c
new file mode 100644
index 0000000..c75775a
--- /dev/null
+++ b/src/devtools/met-driver/4.19/common/spmtwam/sspm/met_spmtwam.c
@@ -0,0 +1,477 @@
+/*
+ * Copyright (C) 2019 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/random.h>
+#include <linux/fs.h>
+#include <linux/ctype.h>
+
+#include <mtk_spm.h>
+#include "met_drv.h"
+#include "trace.h"
+#include "core_plf_init.h"
+#include "interface.h"
+#include "met_spmtwam.h"
+
+
+struct metdevice met_spmtwam;
+static struct kobject *kobj_spmtwam;
+
+static struct twam_cfg twam_config;
+static struct met_spmtwam_para spmtwam_para[MAX_EVENT_COUNT];
+
+static unsigned int twam_dbg_enable = TWAM_DEBUG_SIG_DISABLE;
+static bool twam_clock_mode = TWAM_SPEED_MODE;		/* true:speed mode, false:normal mode */
+static unsigned int window_len = 1300000;	/* 50 ms in 26 MHz */
+
+static int used_count;
+static int start;
+
+
+#define MONTYPE_SHOW_IMPLEMENT(cfg) \
+	do { \
+		int i; \
+		i = snprintf(buf, PAGE_SIZE, "%d\n", cfg.monitor_type); \
+		return i; \
+	} while (0)
+
+#define MONTYPE_STORE_IMPLEMENT(cfg) \
+	do { \
+		int value; \
+		if ((n == 0) || (buf == NULL)) \
+			return -EINVAL; \
+		if (kstrtoint(buf, 10, &value) != 0) \
+			return -EINVAL; \
+		if (value < 0 || value > 3) \
+			return -EINVAL; \
+		cfg.monitor_type = value; \
+		return n; \
+	} while (0)
+
+static ssize_t montype0_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	MONTYPE_SHOW_IMPLEMENT(twam_config.byte[0]);
+}
+
+static ssize_t montype0_store(struct kobject *kobj,
+			struct kobj_attribute *attr,
+			const char *buf,
+			size_t n)
+{
+	MONTYPE_STORE_IMPLEMENT(twam_config.byte[0]);
+}
+
+static ssize_t montype1_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	MONTYPE_SHOW_IMPLEMENT(twam_config.byte[1]);
+}
+
+static ssize_t montype1_store(struct kobject *kobj,
+			struct kobj_attribute *attr,
+			const char *buf,
+			size_t n)
+{
+	MONTYPE_STORE_IMPLEMENT(twam_config.byte[1]);
+}
+
+static ssize_t montype2_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	MONTYPE_SHOW_IMPLEMENT(twam_config.byte[2]);
+}
+
+static ssize_t montype2_store(struct kobject *kobj,
+			struct kobj_attribute *attr,
+			const char *buf,
+			size_t n)
+{
+	MONTYPE_STORE_IMPLEMENT(twam_config.byte[2]);
+}
+
+static ssize_t montype3_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	MONTYPE_SHOW_IMPLEMENT(twam_config.byte[3]);
+}
+
+static ssize_t montype3_store(struct kobject *kobj,
+			struct kobj_attribute *attr,
+			const char *buf,
+			size_t n)
+{
+	MONTYPE_STORE_IMPLEMENT(twam_config.byte[3]);
+}
+
+static ssize_t window_len_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	int i;
+
+	i = snprintf(buf, PAGE_SIZE, "%d\n", window_len);
+
+	return i;
+}
+
+static ssize_t window_len_store(struct kobject *kobj,
+			struct kobj_attribute *attr,
+			const char *buf,
+			size_t n)
+{
+	int value;
+
+	if ((n == 0) || (buf == NULL))
+		return -EINVAL;
+
+	if (kstrtoint(buf, 10, &value) != 0)
+		return -EINVAL;
+
+	if (value < 0)
+		return -EINVAL;
+
+	window_len = value;
+
+	return n;
+}
+
+static struct kobj_attribute montype0_attr = __ATTR(montype0, 0664, montype0_show, montype0_store);
+static struct kobj_attribute montype1_attr = __ATTR(montype1, 0664, montype1_show, montype1_store);
+static struct kobj_attribute montype2_attr = __ATTR(montype2, 0664, montype2_show, montype2_store);
+static struct kobj_attribute montype3_attr = __ATTR(montype3, 0664, montype3_show, montype3_store);
+static struct kobj_attribute window_len_attr = __ATTR(window_len, 0664, window_len_show, window_len_store);
+
+
+/* create spmtwam related kobj node */
+#define KOBJ_ATTR_LIST \
+	do { \
+		KOBJ_ATTR_ITEM(montype0); \
+		KOBJ_ATTR_ITEM(montype1); \
+		KOBJ_ATTR_ITEM(montype2); \
+		KOBJ_ATTR_ITEM(montype3); \
+		KOBJ_ATTR_ITEM(window_len); \
+	} while (0)
+
+static int met_spmtwam_create(struct kobject *parent)
+{
+	int i;
+	int ret = 0;
+
+	kobj_spmtwam = parent;
+
+#define KOBJ_ATTR_ITEM(attr_name) \
+	do { \
+		ret = sysfs_create_file(kobj_spmtwam, &attr_name ## _attr.attr); \
+		if (ret != 0) { \
+			pr_notice("Failed to create " #attr_name " in sysfs\n"); \
+			return ret; \
+		} \
+	} while (0)
+	KOBJ_ATTR_LIST;
+#undef  KOBJ_ATTR_ITEM
+
+	/* init. */
+	for (i = 0; i < MAX_EVENT_COUNT; i++)
+		twam_config.byte[i].monitor_type = 0x2;
+
+	return ret;
+}
+
+static void met_spmtwam_delete(void)
+{
+#define KOBJ_ATTR_ITEM(attr_name) \
+	sysfs_remove_file(kobj_spmtwam, &attr_name ## _attr.attr)
+
+	if (kobj_spmtwam != NULL) {
+		KOBJ_ATTR_LIST;
+		kobj_spmtwam = NULL;
+	}
+#undef  KOBJ_ATTR_ITEM
+}
+
+void ms_spmtwam(struct twam_cfg *cfg, struct twam_select *twam_sel)
+{
+	switch (used_count) {
+	case 1:
+		MET_TRACE(MP_FMT1,
+			(cfg->byte[0].id));
+		break;
+	case 2:
+		MET_TRACE(MP_FMT2,
+			(cfg->byte[0].id),
+			(cfg->byte[1].id));
+		break;
+	case 3:
+		MET_TRACE(MP_FMT3,
+			(cfg->byte[0].id),
+			(cfg->byte[1].id),
+			(cfg->byte[2].id));
+		break;
+	case 4:
+		MET_TRACE(MP_FMT4,
+			(cfg->byte[0].id),
+			(cfg->byte[1].id),
+			(cfg->byte[2].id),
+			(cfg->byte[3].id));
+		break;
+	default:
+		MET_SPMTWAM_ERR("No assign profile event\n");
+		break;
+	}
+}
+
+static int reset_driver_stat(void)
+{
+	met_spmtwam.mode = 0;
+	used_count = 0;
+	start = 0;
+
+	return 0;
+}
+
+#if 0
+void sspm_twam_debug(void)
+{
+/*
+	PR_BOOTMSG("[MET_TWAM] byte0 idle=%d, event=%d, type=%d \n",twam_config.byte[0].signal,twam_config.byte[0].id,twam_config.byte[0].monitor_type);
+	PR_BOOTMSG("[MET_TWAM] byte1 idle=%d, event=%d, type=%d \n",twam_config.byte[1].signal,twam_config.byte[1].id,twam_config.byte[1].monitor_type);
+	PR_BOOTMSG("[MET_TWAM] byte2 idle=%d, event=%d, type=%d \n",twam_config.byte[2].signal,twam_config.byte[2].id,twam_config.byte[2].monitor_type);
+	PR_BOOTMSG("[MET_TWAM] byte3 idle=%d, event=%d, type=%d \n",twam_config.byte[3].signal,twam_config.byte[3].id,twam_config.byte[3].monitor_type);
+	PR_BOOTMSG("[MET_TWAM] twam_clock_mode=%d, window_len=%d \n",twam_clock_mode, window_len);
+*/
+}
+#endif
+
+/*
+ * Called from "met-cmd --start"
+ */
+static void spmtwam_start(void)
+{
+	if (spm_twam_met_enable_symbol) {
+		if (true == spm_twam_met_enable_symbol()) {
+			if (spm_twam_enable_monitor_symbol)
+				spm_twam_enable_monitor_symbol(TWAM_DISABLE, TWAM_DEBUG_SIG_DISABLE, NULL);
+			else {
+				MET_SPMTWAM_ERR("spm_twam_enable_monitor_symbol is NULL\n");
+				return;
+			}
+		}
+	} else {
+		MET_SPMTWAM_ERR("spm_twam_met_enable_symbol is NULL\n");
+		return;
+	}
+
+	if (spm_twam_config_channel_symbol)
+		spm_twam_config_channel_symbol(&twam_config, twam_clock_mode, window_len);
+	else {
+		MET_SPMTWAM_ERR("spm_twam_config_channel_symbol is NULL\n");
+		return;
+	}
+
+	if (spm_twam_enable_monitor_symbol) {
+		spm_twam_enable_monitor_symbol(TWAM_ENABLE, twam_dbg_enable, ms_spmtwam);
+		/* sspm_twam_debug(); */
+	} else {
+		MET_SPMTWAM_ERR("spm_twam_enable_monitor_symbol is NULL\n");
+		return;
+	}
+
+	start = 1;
+}
+
+/*
+ * Called from "met-cmd --stop"
+ */
+static void spmtwam_stop(void)
+{
+	if (spm_twam_enable_monitor_symbol)
+		spm_twam_enable_monitor_symbol(TWAM_DISABLE, TWAM_DEBUG_SIG_DISABLE, NULL);
+	else {
+		MET_SPMTWAM_ERR("spm_twam_enable_monitor_symbol is NULL\n");
+		return;
+	}
+}
+
+static const char header[] = "met-info [000] 0.0: ms_spmtwam_header: ";
+
+/*
+ * It will be called back when run "met-cmd --extract" and mode is 1
+ */
+static int spmtwam_print_header(char *buf, int len)
+{
+    int i, total_size;
+	char idle_sig;
+    unsigned int event;
+
+    total_size = snprintf(buf, PAGE_SIZE, header);
+
+    for (i = 0; i < used_count; i++) {
+		idle_sig = spmtwam_para[i].idle_sig;
+		event = spmtwam_para[i].event;
+
+		total_size += snprintf(buf + total_size, PAGE_SIZE - total_size,
+                        "signal_%c_%02u,", idle_sig, event);
+    }
+
+	/* cut the last comma */
+	buf[total_size - 1] = '\n';
+
+    total_size += snprintf(buf + total_size, PAGE_SIZE - total_size, "met-info [000] 0.0: spmtwam_clock_mode: %s\n",
+            twam_clock_mode == TWAM_SPEED_MODE ? "speed" : "normal");
+
+#ifdef SPMTWAM_SINGLE_IDLE_SIGNAL
+    total_size += snprintf(buf + total_size, PAGE_SIZE - total_size, "met-info [000] 0.0: spmtwam_idle_signal_support: %s\n",
+					TWAM_SINGLE_IDLE_SIGNAL);
+#endif
+
+#ifdef SPMTWAM_MULTIPLE_IDLE_SIGNAL
+    total_size += snprintf(buf + total_size, PAGE_SIZE - total_size, "met-info [000] 0.0: spmtwam_idle_signal_support: %s\n",
+					TWAM_MULTIPLE_IDLE_SIGNAL);
+#endif
+
+    reset_driver_stat();
+    return total_size;
+}
+
+static int assign_slot_sspm_twam(char idle_sig, unsigned int event)
+{
+	int i;
+	int sig2int;
+
+	if (used_count == MAX_EVENT_COUNT) {
+		PR_BOOTMSG("%s exceed max used event count\n", MET_SPMTWAM_TAG);
+		return -1;
+	}
+
+	/* check duplicated */
+	for (i = 0; i < used_count; i++) {
+		if ((spmtwam_para[i].idle_sig == idle_sig) &&
+			(spmtwam_para[i].event == event)) {
+			PR_BOOTMSG("%s input duplicated event %u\n", MET_SPMTWAM_TAG, event);
+			return -2;
+		}
+	}
+
+	/* check idle_sig range in a~d or A~D */
+	if (tolower(idle_sig) < 'a' || tolower(idle_sig) > 'd') {
+		PR_BOOTMSG("%s input idle signal %c is not in a~d range\n",
+					MET_SPMTWAM_TAG, idle_sig);
+		return -3;
+	}
+
+	/* check event no */
+	if (event > MAX_TWAM_EVENT_COUNT) {
+		PR_BOOTMSG("%s input event %u exceed max twam event %u\n",
+					MET_SPMTWAM_TAG, event, MAX_TWAM_EVENT_COUNT);
+		return -4;
+	}
+
+#ifdef SPMTWAM_SINGLE_IDLE_SIGNAL
+	if (used_count > 0) {
+		for (i = 0; i < used_count; i++) {
+			if (idle_sig != spmtwam_para[i].idle_sig) {
+				PR_BOOTMSG("%s %c idle signal is defferent previous, only support one idle signal\n",
+							MET_SPMTWAM_TAG, idle_sig);
+				return -5;
+			}
+		}
+	}
+#endif
+
+	spmtwam_para[used_count].idle_sig = idle_sig;
+	spmtwam_para[used_count].event = event;
+
+	sig2int = (int) (tolower(idle_sig) - 'a');
+
+	twam_config.byte[used_count].id = event;
+	twam_config.byte[used_count].signal = sig2int;
+
+	used_count++;
+
+	return 0;
+}
+
+static char help[] =
+	"  --spmtwam=clock:[speed|normal]	default is normal\n"
+	"					normal mode monitors 4 channels\n"
+	"					speed mode monitors 4 channels\n"
+	"  --spmtwam=signal:selx\n"
+	"					signal= a ~ d for idle signal A~D select\n"
+	"					selx= 0 ~ 31 for for channel event\n";
+
+/*
+ * Called from "met-cmd --help"
+ */
+static int spmtwam_print_help(char *buf, int len)
+{
+	return snprintf(buf, PAGE_SIZE, help);
+}
+
+static int spmtwam_process_argument(const char *arg, int len)
+{
+	if (start == 1)
+		reset_driver_stat();
+
+	if (strncmp(arg, "clock:", 6) == 0) {
+		if (strncmp(&(arg[6]), "speed", 5) == 0) {
+			twam_clock_mode = TWAM_SPEED_MODE;
+		} else if (strncmp(&(arg[6]), "normal", 6) == 0) {
+			twam_clock_mode = TWAM_NORMAL_MODE;
+		} else {
+			PR_BOOTMSG("%s unknown clock mode\n", MET_SPMTWAM_TAG);
+
+			goto error;
+		}
+	} else {
+		char signal;
+		int event;
+		int ret;
+
+		if (len < 3) {
+			PR_BOOTMSG("%s input parameter is too short !!!\n", MET_SPMTWAM_TAG);
+			goto error;
+		}
+
+		/*
+		 * parse arguments
+		 */
+		ret = sscanf(arg, "%c:%u", &signal, &event);
+		if (ret < 2) {
+			PR_BOOTMSG("%s input parameter is wrong format !!!\n", MET_SPMTWAM_TAG);
+			goto error;
+		}
+
+		if (assign_slot_sspm_twam(signal, event) < 0) {
+			goto error;
+		}
+	}
+
+	met_spmtwam.mode = 1;
+	return 0;
+
+error:
+	reset_driver_stat();
+	return -1;
+}
+
+struct metdevice met_spmtwam = {
+	.name = "spmtwam",
+	.owner = THIS_MODULE,
+	.type = MET_TYPE_BUS,
+	.create_subfs = met_spmtwam_create,
+	.delete_subfs = met_spmtwam_delete,
+	.cpu_related = 0,
+	.start = spmtwam_start,
+	.stop = spmtwam_stop,
+	.reset = reset_driver_stat,
+	.print_help = spmtwam_print_help,
+	.print_header = spmtwam_print_header,
+	.process_argument = spmtwam_process_argument
+};
+EXPORT_SYMBOL(met_spmtwam);
diff --git a/src/devtools/met-driver/4.19/common/sspm/ondiemet_sspm.c b/src/devtools/met-driver/4.19/common/sspm/ondiemet_sspm.c
new file mode 100644
index 0000000..0dc3df0
--- /dev/null
+++ b/src/devtools/met-driver/4.19/common/sspm/ondiemet_sspm.c
@@ -0,0 +1,533 @@
+/*
+ * Copyright (C) 2019 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/delay.h>
+#include <linux/module.h> /* symbol_get */
+
+#define MET_USER_EVENT_SUPPORT
+#include "met_drv.h"
+
+#if defined(CONFIG_MTK_TINYSYS_SSPM_SUPPORT) && defined(ONDIEMET_SUPPORT)
+#include "ondiemet_sspm.h"
+#include "met_kernel_symbol.h"
+
+#if defined(CONFIG_MTK_GMO_RAM_OPTIMIZE) || defined(CONFIG_MTK_MET_MEM_ALLOC)
+#ifdef CONFIG_MET_ARM_32BIT
+#include <asm/dma-mapping.h> /* arm_coherent_dma_ops */
+#else /* CONFIG_MET_ARM_32BIT */
+#include <linux/dma-mapping.h>
+#endif /* CONFIG_MET_ARM_32BIT */
+#else /* CONFIG_MTK_GMO_RAM_OPTIMIZE */
+#include "sspm_reservedmem.h"
+#include "sspm_reservedmem_define.h"
+#endif /* CONFIG_MTK_GMO_RAM_OPTIMIZE */
+
+dma_addr_t ondiemet_sspm_log_phy_addr;
+void *ondiemet_sspm_log_virt_addr;
+uint32_t ondiemet_sspm_log_size = 0x400000;
+
+/* SSPM_LOG_FILE 0 */
+/* SSPM_LOG_SRAM 1 */
+/* SSPM_LOG_DRAM 2 */
+int sspm_log_mode;
+/* SSPM_RUN_NORMAL mode 0 */
+/* SSPM_RUN_CONTINUOUS mode 1 */
+int sspm_run_mode;
+int met_sspm_log_discard = -1;
+int sspm_log_size = 500;
+
+int sspm_buffer_size;
+int sspm_buf_available;
+EXPORT_SYMBOL(sspm_buf_available);
+int sspm_buf_mapped = -1; /* get buffer by MET itself */
+
+
+static ssize_t sspm_ipi_supported_show(struct device *dev, struct device_attribute *attr, char *buf);
+static DEVICE_ATTR(sspm_ipi_supported, 0444, sspm_ipi_supported_show, NULL);
+static ssize_t sspm_buffer_size_show(struct device *dev, struct device_attribute *attr, char *buf);
+static DEVICE_ATTR(sspm_buffer_size, 0444, sspm_buffer_size_show, NULL);
+
+static ssize_t sspm_available_show(struct device *dev, struct device_attribute *attr, char *buf);
+static DEVICE_ATTR(sspm_available, 0444, sspm_available_show, NULL);
+
+static ssize_t sspm_log_discard_show(struct device *dev, struct device_attribute *attr, char *buf);
+static DEVICE_ATTR(sspm_log_discard, 0444, sspm_log_discard_show, NULL);
+
+static ssize_t sspm_log_mode_show(struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t sspm_log_mode_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
+static DEVICE_ATTR(sspm_log_mode, 0664, sspm_log_mode_show, sspm_log_mode_store);
+
+static ssize_t sspm_log_size_show(struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t sspm_log_size_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
+static DEVICE_ATTR(sspm_log_size, 0664, sspm_log_size_show, sspm_log_size_store);
+
+
+static ssize_t sspm_run_mode_show(struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t sspm_run_mode_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
+static DEVICE_ATTR(sspm_run_mode, 0664, sspm_run_mode_show, sspm_run_mode_store);
+
+static ssize_t sspm_modules_show(struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t sspm_modules_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
+static DEVICE_ATTR(sspm_modules, 0664, sspm_modules_show, sspm_modules_store);
+
+static ssize_t sspm_op_ctrl_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
+static DEVICE_ATTR(sspm_op_ctrl, 0220, NULL, sspm_op_ctrl_store);
+
+
+static ssize_t sspm_ipi_supported_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int ipi_supported;
+	int i;
+
+	ipi_supported = 0;
+#if defined(CONFIG_MTK_TINYSYS_SSPM_SUPPORT) && defined(ONDIEMET_SUPPORT)
+	#ifndef SSPM_VERSION_V2
+	ipi_supported = 1;
+	#else
+	if(sspm_ipidev_symbol)
+		ipi_supported = 1;
+	#endif
+#endif
+	i = snprintf(buf, PAGE_SIZE, "%d\n", ipi_supported);
+
+	return i;
+}
+
+
+static ssize_t sspm_buffer_size_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int i;
+
+	mutex_lock(&dev->mutex);
+	i = snprintf(buf, PAGE_SIZE, "%d\n", sspm_buffer_size);
+	mutex_unlock(&dev->mutex);
+	return i;
+}
+
+static ssize_t sspm_available_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int i;
+
+	mutex_lock(&dev->mutex);
+	i = snprintf(buf, PAGE_SIZE, "%d\n", 1);
+	mutex_unlock(&dev->mutex);
+	return i;
+}
+
+static ssize_t sspm_log_discard_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int i;
+
+	mutex_lock(&dev->mutex);
+	i = snprintf(buf, PAGE_SIZE, "%d\n", met_sspm_log_discard);
+	mutex_unlock(&dev->mutex);
+	return i;
+}
+
+static ssize_t sspm_log_mode_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int i;
+
+	mutex_lock(&dev->mutex);
+	i = snprintf(buf, PAGE_SIZE, "%d\n", sspm_log_mode);
+	mutex_unlock(&dev->mutex);
+	return i;
+}
+
+static ssize_t sspm_log_mode_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+	int value;
+
+	if (kstrtoint(buf, 0, &value) != 0)
+		return -EINVAL;
+	mutex_lock(&dev->mutex);
+	sspm_log_mode = value;
+	mutex_unlock(&dev->mutex);
+	return count;
+}
+
+
+static ssize_t sspm_log_size_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int i;
+
+	mutex_lock(&dev->mutex);
+	i = snprintf(buf, PAGE_SIZE, "%d\n", sspm_log_size);
+	mutex_unlock(&dev->mutex);
+	return i;
+}
+
+static ssize_t sspm_log_size_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+	int value;
+
+	if (kstrtoint(buf, 0, &value) != 0)
+		return -EINVAL;
+	mutex_lock(&dev->mutex);
+	sspm_log_size = value;
+	mutex_unlock(&dev->mutex);
+	return count;
+}
+
+
+static ssize_t sspm_run_mode_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int i;
+
+	mutex_lock(&dev->mutex);
+	i = snprintf(buf, PAGE_SIZE, "%d\n", sspm_run_mode);
+	mutex_unlock(&dev->mutex);
+	return i;
+}
+
+static ssize_t sspm_run_mode_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+	int value;
+
+	if (kstrtoint(buf, 0, &value) != 0)
+		return -EINVAL;
+	mutex_lock(&dev->mutex);
+	sspm_run_mode = value;
+	mutex_unlock(&dev->mutex);
+	return count;
+}
+
+static ssize_t sspm_op_ctrl_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+	int value;
+
+	if (kstrtoint(buf, 0, &value) != 0)
+		return -EINVAL;
+	mutex_lock(&dev->mutex);
+	if (value == 1)
+		sspm_start();
+	else if (value == 2)
+		sspm_stop();
+	else if (value == 3)
+		sspm_extract();
+	else if (value == 4)
+		sspm_flush();
+	mutex_unlock(&dev->mutex);
+	return count;
+}
+
+static ssize_t sspm_modules_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int i;
+
+	mutex_lock(&dev->mutex);
+	i = snprintf(buf, PAGE_SIZE, "%x\n", ondiemet_module[ONDIEMET_SSPM]);
+	mutex_unlock(&dev->mutex);
+	return i;
+}
+
+static ssize_t sspm_modules_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+	uint32_t value;
+
+	if (kstrtouint(buf, 0, &value) != 0)
+		return -EINVAL;
+	mutex_lock(&dev->mutex);
+	ondiemet_module[ONDIEMET_SSPM] = value;
+	mutex_unlock(&dev->mutex);
+	return count;
+}
+
+int sspm_attr_init(struct device *dev)
+{
+	int ret;
+
+#if defined(CONFIG_MTK_GMO_RAM_OPTIMIZE) || defined(CONFIG_MTK_MET_MEM_ALLOC)
+#ifdef CONFIG_MET_ARM_32BIT
+	struct dma_map_ops *ops = (struct dma_map_ops *)symbol_get(arm_coherent_dma_ops);
+
+	if (ops && ops->alloc) {
+		dev->coherent_dma_mask = DMA_BIT_MASK(32);
+		ondiemet_sspm_log_virt_addr = ops->alloc(dev,
+						ondiemet_sspm_log_size,
+						&ondiemet_sspm_log_phy_addr,
+						GFP_KERNEL,
+						0);
+	}
+#else /* CONFIG_MET_ARM_32BIT */
+	/* dma_alloc */
+	ondiemet_sspm_log_virt_addr = dma_alloc_coherent(dev,
+			ondiemet_sspm_log_size,
+			&ondiemet_sspm_log_phy_addr,
+			GFP_KERNEL);
+#endif /* CONFIG_MET_ARM_32BIT */
+#else /* CONFIG_MTK_GMO_RAM_OPTIMIZE */
+
+	phys_addr_t (*sspm_reserve_mem_get_phys_sym)(unsigned int id) = NULL;
+	phys_addr_t (*sspm_reserve_mem_get_virt_sym)(unsigned int id) = NULL;
+	phys_addr_t (*sspm_reserve_mem_get_size_sym)(unsigned int id) = NULL;
+
+	sspm_reserve_mem_get_phys_sym = (phys_addr_t (*)(unsigned int id))symbol_get(sspm_reserve_mem_get_virt);
+	sspm_reserve_mem_get_virt_sym = (phys_addr_t (*)(unsigned int id))symbol_get(sspm_reserve_mem_get_phys);
+	sspm_reserve_mem_get_size_sym = (phys_addr_t (*)(unsigned int id))symbol_get(sspm_reserve_mem_get_size);
+	if (sspm_reserve_mem_get_phys_sym)
+		ondiemet_sspm_log_virt_addr = (void*)sspm_reserve_mem_get_virt(MET_MEM_ID);
+	if (sspm_reserve_mem_get_virt_sym)
+		ondiemet_sspm_log_phy_addr = sspm_reserve_mem_get_phys(MET_MEM_ID);
+	if (sspm_reserve_mem_get_size_sym)
+		ondiemet_sspm_log_size = sspm_reserve_mem_get_size(MET_MEM_ID);
+#endif /* CONFIG_MTK_GMO_RAM_OPTIMIZE */
+
+	ret = device_create_file(dev, &dev_attr_sspm_buffer_size);
+	if (ret != 0) {
+		pr_debug("can not create device file: sspm_buffer_size\n");
+		return ret;
+	}
+
+	ret = device_create_file(dev, &dev_attr_sspm_available);
+	if (ret != 0) {
+		pr_debug("can not create device file: sspm_available\n");
+		return ret;
+	}
+
+	ret = device_create_file(dev, &dev_attr_sspm_log_discard);
+	if (ret != 0) {
+		pr_debug("can not create device file: sspm_log_discard\n");
+		return ret;
+	}
+	ret = device_create_file(dev, &dev_attr_sspm_log_mode);
+	if (ret != 0) {
+		pr_debug("can not create device file: sspm_log_mode\n");
+		return ret;
+	}
+	ret = device_create_file(dev, &dev_attr_sspm_log_size);
+	if (ret != 0) {
+		pr_debug("can not create device file: sspm_log_size\n");
+		return ret;
+	}
+	ret = device_create_file(dev, &dev_attr_sspm_run_mode);
+	if (ret != 0) {
+		pr_debug("can not create device file: sspm_run_mode\n");
+		return ret;
+	}
+	ret = device_create_file(dev, &dev_attr_sspm_op_ctrl);
+	if (ret != 0) {
+		pr_debug("can not create device file: sspm_op_ctrl\n");
+		return ret;
+	}
+	ret = device_create_file(dev, &dev_attr_sspm_modules);
+	if (ret != 0) {
+		pr_debug("can not create device file: sspm_modules\n");
+		return ret;
+	}
+	ret = device_create_file(dev, &dev_attr_sspm_ipi_supported);
+	if (ret != 0) {
+		pr_debug("can not create device file: sspm_ipi_supported\n");
+		return ret;
+	}
+
+	if (ondiemet_sspm_log_virt_addr != NULL) {
+		start_sspm_ipi_recv_thread();
+		sspm_buf_available = 1;
+		sspm_buffer_size = ondiemet_sspm_log_size;
+	} else {
+		sspm_buf_available = 0;
+		sspm_buffer_size = -1;
+	}
+
+	return 0;
+}
+
+int sspm_attr_uninit(struct device *dev)
+{
+	/* dma_free */
+	if (ondiemet_sspm_log_virt_addr != NULL) {
+#if defined(CONFIG_MTK_GMO_RAM_OPTIMIZE) || defined(CONFIG_MTK_MET_MEM_ALLOC)
+#ifdef CONFIG_MET_ARM_32BIT
+		struct dma_map_ops *ops = (struct dma_map_ops *)symbol_get(arm_coherent_dma_ops);
+
+		if (ops && ops->free) {
+			ops->free(dev,
+				ondiemet_sspm_log_size,
+				ondiemet_sspm_log_virt_addr,
+				ondiemet_sspm_log_phy_addr,
+				0);
+		}
+#else /* CONFIG_MET_ARM_32BIT */
+		dma_free_coherent(dev,
+			ondiemet_sspm_log_size,
+			ondiemet_sspm_log_virt_addr,
+			ondiemet_sspm_log_phy_addr);
+#endif /* CONFIG_MET_ARM_32BIT */
+#endif /* CONFIG_MTK_GMO_RAM_OPTIMIZE */
+		ondiemet_sspm_log_virt_addr = NULL;
+		stop_sspm_ipi_recv_thread();
+	}
+
+	device_remove_file(dev, &dev_attr_sspm_buffer_size);
+	device_remove_file(dev, &dev_attr_sspm_available);
+	device_remove_file(dev, &dev_attr_sspm_log_discard);
+	device_remove_file(dev, &dev_attr_sspm_log_mode);
+	device_remove_file(dev, &dev_attr_sspm_log_size);
+	device_remove_file(dev, &dev_attr_sspm_run_mode);
+	device_remove_file(dev, &dev_attr_sspm_op_ctrl);
+	device_remove_file(dev, &dev_attr_sspm_modules);
+	device_remove_file(dev, &dev_attr_sspm_ipi_supported);
+
+	return 0;
+}
+
+#if 0 /* move to sspm_attr_init() */
+void sspm_get_buffer_info(void)
+{
+	if (ondiemet_sspm_log_virt_addr != NULL) {
+		sspm_buf_available = 1;
+		sspm_buffer_size = ondiemet_sspm_log_size;
+	} else {
+		sspm_buf_available = 0;
+		sspm_buffer_size = -1;
+	}
+}
+#endif
+
+/*extern const char *met_get_platform_name(void);*/
+extern char *met_get_platform(void);
+void sspm_start(void)
+{
+	int32_t ret = 0;
+	uint32_t rdata;
+	uint32_t ipi_buf[4];
+	const char* platform_name = NULL;
+	unsigned int platform_id = 0;
+	met_sspm_log_discard = -1;
+
+	/* clear DRAM buffer */
+	if (ondiemet_sspm_log_virt_addr != NULL)
+		memset_io((void *)ondiemet_sspm_log_virt_addr, 0, ondiemet_sspm_log_size);
+	else
+		return;
+
+	/*platform_name = met_get_platform_name();*/
+	platform_name = met_get_platform();
+	if (platform_name) {
+		char buf[5];
+
+		memset(buf, 0x0, 5);
+		memcpy(buf, &platform_name[2], 4);
+
+		ret = kstrtouint(buf, 10, &platform_id);
+	}
+
+	/* send DRAM physical address */
+	ipi_buf[0] = MET_MAIN_ID | MET_BUFFER_INFO;
+	ipi_buf[1] = (unsigned int)ondiemet_sspm_log_phy_addr; /* address */
+	if (ret == 0)
+		ipi_buf[2] = platform_id;
+	else
+		ipi_buf[2] = 0;
+
+	ipi_buf[3] = 0;
+	ret = met_ipi_to_sspm_command((void *)ipi_buf, 0, &rdata, 1);
+
+	/* start ondiemet now */
+	ipi_buf[0] = MET_MAIN_ID | MET_OP | MET_OP_START;
+	ipi_buf[1] = ondiemet_module[ONDIEMET_SSPM];
+	ipi_buf[2] = sspm_log_mode;
+	ipi_buf[3] = sspm_run_mode;
+	ret = met_ipi_to_sspm_command((void *)ipi_buf, 0, &rdata, 1);
+}
+
+extern unsigned int met_get_chip_id(void);
+void sspm_stop(void)
+{
+	int32_t ret;
+	uint32_t rdata;
+	uint32_t ipi_buf[4];
+
+	unsigned int chip_id = 0;
+	chip_id = met_get_chip_id();
+
+	if (sspm_buf_available == 1) {
+		ipi_buf[0] = MET_MAIN_ID|MET_OP|MET_OP_STOP;
+		ipi_buf[1] = chip_id;
+		ipi_buf[2] = 0;
+		ipi_buf[3] = 0;
+		ret = met_ipi_to_sspm_command((void *)ipi_buf, 0, &rdata, 1);
+	}
+}
+
+void sspm_extract(void)
+{
+	int32_t ret;
+	uint32_t rdata;
+	uint32_t ipi_buf[4];
+	int32_t count;
+
+	count = 20;
+	if (sspm_buf_available == 1) {
+		while ((sspm_buffer_dumping == 1) && (count != 0)) {
+			msleep(50);
+			count--;
+		}
+		ipi_buf[0] = MET_MAIN_ID|MET_OP|MET_OP_EXTRACT;
+		ipi_buf[1] = 0;
+		ipi_buf[2] = 0;
+		ipi_buf[3] = 0;
+		ret = met_ipi_to_sspm_command((void *)ipi_buf, 0, &rdata, 1);
+	}
+
+	if (sspm_run_mode == SSPM_RUN_NORMAL)
+		ondiemet_module[ONDIEMET_SSPM] = 0;
+}
+
+void sspm_flush(void)
+{
+	int32_t ret;
+	uint32_t rdata;
+	uint32_t ipi_buf[4];
+
+	if (sspm_buf_available == 1) {
+		ipi_buf[0] = MET_MAIN_ID|MET_OP|MET_OP_FLUSH;
+		ipi_buf[1] = 0;
+		ipi_buf[2] = 0;
+		ipi_buf[3] = 0;
+		ret = met_ipi_to_sspm_command((void *)ipi_buf, 0, &rdata, 1);
+	}
+
+	if (sspm_run_mode == SSPM_RUN_NORMAL)
+		ondiemet_module[ONDIEMET_SSPM] = 0;
+}
+#else /* CONFIG_MTK_TINYSYS_SSPM_SUPPORT && ONDIEMET_SUPPORT */
+int sspm_buffer_size = -1;
+
+int sspm_attr_init(struct device *dev)
+{
+	return 0;
+}
+
+int sspm_attr_uninit(struct device *dev)
+{
+	return 0;
+}
+
+void sspm_start(void)
+{
+}
+
+void sspm_stop(void)
+{
+}
+
+void sspm_extract(void)
+{
+}
+
+void sspm_flush(void)
+{
+}
+
+#endif /* CONFIG_MTK_TINYSYS_SSPM_SUPPORT && ONDIEMET_SUPPORT */
diff --git a/src/devtools/met-driver/4.19/common/sspm/ondiemet_sspm.h b/src/devtools/met-driver/4.19/common/sspm/ondiemet_sspm.h
new file mode 100644
index 0000000..6ce7fb6
--- /dev/null
+++ b/src/devtools/met-driver/4.19/common/sspm/ondiemet_sspm.h
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2019 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __ONDIEMET_SSPM_H
+#define __ONDIEMET_SSPM_H
+
+#if defined(CONFIG_MTK_TINYSYS_SSPM_SUPPORT) && defined(ONDIEMET_SUPPORT)
+#include "ondiemet.h"
+
+#ifndef SSPM_VERSION_V2
+#include "sspm_ipi.h"
+#else
+#include <sspm_ipi_id.h>
+#endif /* SSPM_VERSION_V2 */
+#include <linux/dma-mapping.h>
+
+/* we may use IPI_ID_PLATFORM for mt6759 to reduce SRAM */
+#ifndef SSPM_VERSION_V2
+#ifndef IPI_ID_MET
+/* #define IPI_ID_MET IPI_ID_TST1 */
+#define IPI_ID_MET IPI_ID_PLATFORM
+#endif
+#endif /* SSPM_VERSION_V2 */
+/* MET IPI command definition: mbox 0 */
+/* main func ID: bit[31-24]; sub func ID: bit[23-18]; argu 0: bit[17-0] */
+#define MET_MAIN_ID_MASK        0xff000000 /* bit 31 - 24 */
+#define MET_SUB_ID_MASK         0x00fc0000 /* bit 23 - 18 */
+#define MET_ARGU0_MASK          0x0003ffff /* bit 17 - 0 */
+#define FUNC_BIT_SHIFT          18
+#define MID_BIT_SHIFT           9
+#define MET_MAIN_ID             0x06000000
+/* handle argument and attribute */
+#define PROCESS_ARGU            0x01
+#define PROCESS_ATTR            0x02
+#define MODULE_ID_MASK          0x3fe00 /* bit 9 - 17 */
+#define ARGUMENT_MASK           0x01ff  /* bit 0 - 8 */
+
+/* the following command is used for AP to MD32 */
+#define MET_OP            (1 << FUNC_BIT_SHIFT)
+/* argu 0: start: 0x01; stop: 0x02; extract: 0x03 */
+#define MET_OP_START        0x00000001
+#define MET_OP_STOP         0x00000002
+#define MET_OP_EXTRACT      0x00000003
+#define MET_OP_FLUSH        0x00000004
+#define MET_SR            (2 << FUNC_BIT_SHIFT) /* sample rate */
+#define MET_MODULE        (3 << FUNC_BIT_SHIFT) /* module enable/disable */
+#define MET_ARGU          (4 << FUNC_BIT_SHIFT) /* argument passing */
+#define MET_ATTR          (5 << FUNC_BIT_SHIFT) /* attribute passing */
+/* system memory information for on-die-met log data */
+#define MET_BUFFER_INFO   (6 << FUNC_BIT_SHIFT)
+#define MET_TIMESTAMP     (7 << FUNC_BIT_SHIFT) /* timestamp info */
+#define MET_GPT           (8 << FUNC_BIT_SHIFT) /* GPT counter reading */
+#define MET_REQ_AP2MD     (9 << FUNC_BIT_SHIFT) /* user defined command */
+#define MET_RESP_AP2MD    (10 << FUNC_BIT_SHIFT) /* may no need */
+/* mode: bit 15 - 0: */
+/*  Bit 0: MD32 SRAM mode; Bit 1: System DRAM mode */
+/*  value: 0: output to next level of storage; 1: loop in its own storage */
+#define MET_DATA_MODE     (11 << FUNC_BIT_SHIFT) /* log output mode */
+/* start/stop read data into MD32 SRAM buffer; both DMA and met_printf() */
+#define MET_DATA_OP       (12 << FUNC_BIT_SHIFT) /* data read operation */
+/* the following command is used for MD32 to AP */
+#define MET_DUMP_BUFFER   (13 << FUNC_BIT_SHIFT)
+#define MET_REQ_MD2AP     (14 << FUNC_BIT_SHIFT) /* user defined command */
+#define MET_CLOSE_FILE    (15 << FUNC_BIT_SHIFT) /* Inform to close the SD file */
+#define MET_RESP_MD2AP    (16 << FUNC_BIT_SHIFT)
+#define MET_RUN_MODE      (17 << FUNC_BIT_SHIFT)
+
+/* Note: the module ID and its bit pattern should be fixed as below */
+/* DMA based module first */
+enum {
+	MID_PMQOS = 0,
+	MID_VCORE_DVFS,
+	MID_EMI,
+	MID_THERMAL_CPU,
+	MID_WALL_TIME,
+	MID_CPU_DVFS,
+	MID_GPU_DVFS,
+	MID_PTPOD,
+	MID_SPM,
+	MID_PROFILE,
+	MID_MET_TAG,
+	MID_TS,
+	MID_TS_ISR,
+	MID_TS_LAST,
+	MID_TS_ISR_LAST,
+	MID_SRAM_INFO,
+	MID_MET_STOP,
+	MID_IOP_MON,
+	MID_CPU_INFO_MAPPING,
+	MID_SMI,
+	MID_PMU,
+
+	MID_COMMON = 0x1F
+};
+
+#define ID_PMQOS       (1 << MID_PMQOS)
+#define ID_SMI         (1 << MID_SMI)
+#define ID_EMI         (1 << MID_EMI)
+#define ID_THERMAL_CPU (1 << MID_THERMAL_CPU)
+#define ID_WALL_TIME   (1 << MID_WALL_TIME)
+#define ID_CPU_DVFS    (1 << MID_CPU_DVFS)
+#define ID_GPU_DVFS    (1 << MID_GPU_DVFS)
+#define ID_VCORE_DVFS  (1 << MID_VCORE_DVFS)
+#define ID_PTPOD       (1 << MID_PTPOD)
+#define ID_SPM         (1 << MID_SPM)
+#define ID_PROFILE     (1 << MID_PROFILE)
+#define ID_COMMON      (1 << MID_COMMON)
+#define ID_CPU_INFO_MAPPING      (1 << MID_CPU_INFO_MAPPING)
+#define ID_SMI      (1 << MID_SMI)
+#define ID_PMU      (1 << MID_PMU)
+
+extern void ondiemet_extract(void);
+extern void ondiemet_stop(void);
+extern void ondiemet_start(void);
+
+extern void start_sspm_ipi_recv_thread(void);
+extern void stop_sspm_ipi_recv_thread(void);
+extern int met_ipi_to_sspm_command(void *buffer, int slot,
+						   unsigned int *retbuf, int retslot);
+extern int met_ipi_to_sspm_command_async(void *buffer, int slot,
+						   unsigned int *retbuf, int retslot);
+
+
+extern unsigned int ondiemet_ipi_buf[];
+
+/* extern phys_addr_t ondiemet_sspm_log_phy_addr, ondiemet_sspm_log_virt_addr; */
+extern dma_addr_t ondiemet_sspm_log_phy_addr;
+
+extern void *ondiemet_sspm_log_virt_addr;
+extern uint32_t ondiemet_sspm_log_size;
+
+extern int ondiemet_attr_init(struct device *dev);
+extern int ondiemet_attr_uninit(struct device *dev);
+extern int met_sspm_log_discard;
+
+#define SSPM_LOG_FILE 0
+#define SSPM_LOG_SRAM 1
+#define SSPM_LOG_DRAM 2
+extern int sspm_log_mode;
+#define SSPM_RUN_NORMAL 0
+#define SSPM_RUN_CONTINUOUS 1
+extern int sspm_run_mode;
+
+/* extern void sspm_get_buffer_info(void); */
+extern int sspm_buf_available;
+extern int sspm_buffer_dumping;
+
+void sspm_flush(void);
+
+#endif /* CONFIG_MTK_TINYSYS_SSPM_SUPPORT && ONDIEMET_SUPPORT */
+#endif /* __ONDIEMET_SSPM_H */
diff --git a/src/devtools/met-driver/4.19/common/sspm/sspm_common.c b/src/devtools/met-driver/4.19/common/sspm/sspm_common.c
new file mode 100644
index 0000000..3b1aca7
--- /dev/null
+++ b/src/devtools/met-driver/4.19/common/sspm/sspm_common.c
@@ -0,0 +1,279 @@
+/*

+ * Copyright (C) 2019 MediaTek Inc.

+ *

+ * 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.

+ *

+ * This program is distributed in the hope that it will be useful,

+ * but WITHOUT ANY WARRANTY; without even the implied warranty of

+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the

+ * GNU General Public License for more details.

+ */

+

+#include <linux/string.h>

+#include <linux/slab.h>

+#include <linux/kernel.h>

+

+#include "met_drv.h"

+#include "ondiemet_sspm.h"

+

+

+struct sspm_met_evnet_header {

+	unsigned int rts_event_id;

+	char *rts_event_name;

+	char *chart_line_name;

+	char *key_list;

+};

+

+

+enum {

+	#ifdef MET_SSPM_RTS_EVNET

+	#undef MET_SSPM_RTS_EVNET

+	#endif

+	#define MET_SSPM_RTS_EVNET(rts_event_id, key_list) rts_event_id,

+	#include "met_sspm_rts_event.h"

+

+	/**********************/

+	CUR_MET_RTS_EVENT_NUM,

+	MAX_MET_RTS_EVENT_NUM = 128

+};

+

+

+struct sspm_met_evnet_header met_evnet_header[MAX_MET_RTS_EVENT_NUM] = {

+	#ifdef MET_SSPM_RTS_EVNET

+	#undef MET_SSPM_RTS_EVNET

+	#endif

+	#define MET_SSPM_RTS_EVNET(rts_event_id, key_list) {rts_event_id, #rts_event_id, #rts_event_id, key_list},

+	#include "met_sspm_rts_event.h"

+};

+

+

+static void ondiemet_sspm_start(void);

+static void ondiemet_sspm_stop(void);

+static int ondiemet_sspm_print_help(char *buf, int len);

+static int ondiemet_sspm_process_argument(const char *arg, int len);

+static int ondiemet_sspm_print_header(char *buf, int len);

+

+

+static unsigned int event_id_flag0;

+static unsigned int event_id_flag1;

+static unsigned int event_id_flag2;

+static unsigned int event_id_flag3;

+static char *update_rts_event_tbl[MAX_MET_RTS_EVENT_NUM];

+static char sspm_help[] = "  --sspm_common=rts_event_name\n";

+static char header[] = 	"met-info [000] 0.0: sspm_common_header: ";

+

+struct metdevice met_sspm_common = {

+	.name = "sspm_common",

+	.owner = THIS_MODULE,

+	.type = MET_TYPE_BUS,

+	.cpu_related = 0,

+	.ondiemet_mode = 1,

+	.ondiemet_start = ondiemet_sspm_start,

+	.ondiemet_stop = ondiemet_sspm_stop,

+	.ondiemet_process_argument = ondiemet_sspm_process_argument,

+	.ondiemet_print_help = ondiemet_sspm_print_help,

+	.ondiemet_print_header = ondiemet_sspm_print_header,

+};

+

+

+static int ondiemet_sspm_print_help(char *buf, int len)

+{

+	return snprintf(buf, PAGE_SIZE, sspm_help);

+}

+

+

+static int ondiemet_sspm_print_header(char *buf, int len)

+{

+	int i;

+	int write_len;

+	int flag = 0;

+	static int is_dump_header = 0;

+	static int read_idx = 0;

+

+	len = 0;

+	met_sspm_common.header_read_again = 0;

+	if (is_dump_header == 0) {

+		len = snprintf(buf, PAGE_SIZE, "%s", header);

+		is_dump_header = 1;

+	}

+

+	for (i=read_idx; i<MAX_MET_RTS_EVENT_NUM; i++) {

+		if (met_evnet_header[i].chart_line_name) {

+			if (i <32) {

+				flag = 1<<i;

+				flag = event_id_flag0 & flag;

+			} else if (i >=32 && i < 64) {

+				flag = 1<<(i-32);

+				flag = event_id_flag1 & flag;

+			} else if (i >=64 && i < 96) {

+				flag = 1<<(i-64);

+				flag = event_id_flag2 & flag;

+			} else if (i >=96 && i < 128) {

+				flag = 1<<(i-96);

+				flag = event_id_flag3 & flag;

+			}

+			if (flag == 0)

+				continue;

+

+			write_len = strlen(met_evnet_header[i].chart_line_name) + strlen(met_evnet_header[i].key_list) + 3;

+			if ((len+write_len) < PAGE_SIZE) {

+				len += snprintf(buf+len, PAGE_SIZE-len, "%u,%s,%s;",

+					met_evnet_header[i].rts_event_id,

+					met_evnet_header[i].chart_line_name,

+					met_evnet_header[i].key_list);

+			} else {

+				met_sspm_common.header_read_again = 1;

+				read_idx = i;

+				return len;

+			}

+		}

+	}

+

+	if (i == MAX_MET_RTS_EVENT_NUM) {

+		is_dump_header = 0;

+		read_idx = 0;

+		buf[len-1] = '\n';

+		for (i=0; i<MAX_MET_RTS_EVENT_NUM; i++) {

+			if (update_rts_event_tbl[i]) {

+				kfree(update_rts_event_tbl[i]);

+				update_rts_event_tbl[i] = NULL;

+			}

+		}

+		met_sspm_common.mode = 0;

+		event_id_flag0 = 0;

+		event_id_flag1 = 0;

+		event_id_flag2 = 0;

+		event_id_flag3 = 0;

+	}

+

+	return len;

+}

+

+

+static void ondiemet_sspm_start(void)

+{

+	if (sspm_buf_available == 0)

+		return ;

+

+	/* ID_COMMON = 1<<MID_COMMON */

+	ondiemet_module[ONDIEMET_SSPM] |= ID_COMMON;

+}

+

+

+static void ondiemet_sspm_stop(void)

+{

+	if (sspm_buf_available == 0)

+		return ;

+}

+

+

+static void update_event_id_flag(int event_id)

+{

+	unsigned int ipi_buf[4];

+	unsigned int rdata;

+	unsigned int res;

+

+	if (sspm_buf_available == 0)

+		return ;

+

+	/* main func ID: bit[31-24]; sub func ID: bit[23-18]; argu 0: bit[17-0]

+	   #define MET_MAIN_ID_MASK		0xff000000

+	   #define FUNC_BIT_SHIFT		  18

+	   #define MET_ARGU				(4 << FUNC_BIT_SHIFT)

+	   #define MID_BIT_SHIFT		   9

+	*/

+	if (event_id <32) {

+		event_id_flag0 |= 1<<event_id;

+		ipi_buf[0] = MET_MAIN_ID | MET_ARGU | MID_COMMON<<MID_BIT_SHIFT | 1;

+		ipi_buf[1] = 0;

+		ipi_buf[2] = event_id_flag0;

+		ipi_buf[3] = 0;

+		res = met_ipi_to_sspm_command((void *)ipi_buf, 0, &rdata, 1);

+	} else if (event_id >=32 && event_id < 64) {

+		event_id_flag1 |= 1<<(event_id-32);

+		ipi_buf[0] = MET_MAIN_ID | MET_ARGU | MID_COMMON<<MID_BIT_SHIFT | 1;

+		ipi_buf[1] = 1;

+		ipi_buf[2] = event_id_flag1;

+		ipi_buf[3] = 0;

+		res = met_ipi_to_sspm_command((void *)ipi_buf, 0, &rdata, 1);

+	} else if (event_id >=64 && event_id < 96) {

+		event_id_flag2 |= 1<<(event_id-64);

+		ipi_buf[0] = MET_MAIN_ID | MET_ARGU | MID_COMMON<<MID_BIT_SHIFT | 1;

+		ipi_buf[1] = 2;

+		ipi_buf[2] = event_id_flag2;

+		ipi_buf[3] = 0;

+		res = met_ipi_to_sspm_command((void *)ipi_buf, 0, &rdata, 1);

+	} else if (event_id >=96 && event_id < 128) {

+		event_id_flag3 = 1<<(event_id-96);

+		ipi_buf[0] |= MET_MAIN_ID | MET_ARGU | MID_COMMON<<MID_BIT_SHIFT | 1;

+		ipi_buf[1] = 3;

+		ipi_buf[2] = event_id_flag3;

+		ipi_buf[3] = 0;

+		res = met_ipi_to_sspm_command((void *)ipi_buf, 0, &rdata, 1);

+	}

+	met_sspm_common.mode = 1;

+}

+

+

+static char *strdup(const char *s)

+{

+	char *p = kmalloc(strlen(s) + 1, GFP_KERNEL);

+

+	if (p)

+		strcpy(p, s);

+	return p;

+}

+

+

+static int ondiemet_sspm_process_argument(const char *arg, int len)

+{

+	int i;

+	int rts_event_id = -1;

+	int res;

+	char *line;

+	char *token;

+

+	for (i=0; met_evnet_header[i].rts_event_name && i<MAX_MET_RTS_EVENT_NUM; i++) {

+		if (strcmp(met_evnet_header[i].rts_event_name, arg) == 0) {

+			rts_event_id = i;

+			break;

+		}

+	}

+	if (strstarts(arg, "update_rts_event_tbl")) {

+		char *ptr;

+

+		/* update_rts_event_tbl=rts_event_id;rts_event_name;chart_line_name;key_list*/

+		line = strdup(arg);

+		if (line == NULL)

+			return -1;

+		ptr = line;

+		token = strsep(&line, "=");

+

+		/* rts_event_id, */

+		token = strsep(&line, ";");

+		res = kstrtoint(token, 10, &rts_event_id);

+		met_evnet_header[rts_event_id].rts_event_id = rts_event_id;

+

+		/* rts_event_name */

+		token = strsep(&line, ";");

+		met_evnet_header[rts_event_id].rts_event_name = token;

+

+		/* chart_line_name */

+		token = strsep(&line, ";");

+		met_evnet_header[rts_event_id].chart_line_name = token;

+

+		/* key_list */

+		token = strsep(&line, ";\n");

+		met_evnet_header[rts_event_id].key_list = token;

+

+		update_rts_event_tbl[rts_event_id] = ptr;

+	}

+

+	if (rts_event_id >=0)

+		update_event_id_flag(rts_event_id);

+

+	return 0;

+}

+EXPORT_SYMBOL(met_sspm_common);

diff --git a/src/devtools/met-driver/4.19/common/sspm/sspm_ipi_handle.c b/src/devtools/met-driver/4.19/common/sspm/sspm_ipi_handle.c
new file mode 100644
index 0000000..dc6c3b8
--- /dev/null
+++ b/src/devtools/met-driver/4.19/common/sspm/sspm_ipi_handle.c
@@ -0,0 +1,328 @@
+/*
+ * Copyright (C) 2019 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kthread.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/signal.h>
+#include <linux/semaphore.h>
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/syscalls.h>
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/fcntl.h>
+/* #include <asm/uaccess.h> */
+#include <linux/uaccess.h>
+#include <linux/delay.h>
+
+#include "ondiemet_sspm.h"
+#include "ondiemet_log.h"
+#include "interface.h"
+#include "met_kernel_symbol.h"
+
+
+#ifndef SSPM_VERSION_V2
+static struct ipi_action ondiemet_sspm_isr;
+#else
+#include "core_plf_init.h"
+uint32_t rdata = 0;
+uint32_t ridx, widx, wlen;
+uint32_t ackdata = 0;
+#endif
+static uint32_t log_size;
+static uint32_t recv_buf[4];
+static struct task_struct *ondiemet_sspm_recv_task;
+static int sspm_ipi_thread_started;
+int sspm_buffer_dumping;
+int sspm_recv_thread_comp;
+
+void log_done_cb(const void *p)
+{
+	uint32_t ret;
+	uint32_t rdata = 0;
+	uint32_t ipi_buf[4];
+	uint32_t opt = (p != NULL);
+
+	if (opt == 0) {
+		ipi_buf[0] = MET_MAIN_ID | MET_RESP_AP2MD;
+		ipi_buf[1] = MET_DUMP_BUFFER;
+		ipi_buf[2] = 0;
+		ipi_buf[3] = 0;
+		ret = met_ipi_to_sspm_command((void *)ipi_buf, 0, &rdata, 1);
+	}
+}
+#ifndef SSPM_VERSION_V2
+int ondiemet_sspm_recv_thread(void *data)
+{
+	uint32_t rdata = 0, cmd, ret;
+	uint32_t ridx, widx, wlen;
+
+	ondiemet_sspm_isr.data = (void *)recv_buf;
+	ret = sspm_ipi_recv_registration(IPI_ID_TST1, &ondiemet_sspm_isr);
+	do {
+		sspm_ipi_recv_wait(IPI_ID_TST1);
+		if (sspm_recv_thread_comp == 1) {
+			while (!kthread_should_stop())
+				;
+			return 0;
+		}
+		cmd = recv_buf[0] & MET_SUB_ID_MASK;
+		switch (cmd) {
+		case MET_DUMP_BUFFER:	/* mbox 1: start index; 2: size */
+			sspm_buffer_dumping = 1;
+			ridx = recv_buf[1];
+			widx = recv_buf[2];
+			log_size = recv_buf[3];
+			sspm_ipi_send_ack(IPI_ID_TST1, &rdata);
+			if (widx < ridx) {	/* wrapping occurs */
+				wlen = log_size - ridx;
+				ondiemet_log_req_enq((char *)(ondiemet_sspm_log_virt_addr) + (ridx << 2),
+				wlen * 4, log_done_cb, (void *)1);
+				ondiemet_log_req_enq((char *)(ondiemet_sspm_log_virt_addr),
+						     widx * 4, log_done_cb, (void *)0);
+			} else {
+				wlen = widx - ridx;
+				ondiemet_log_req_enq((char *)(ondiemet_sspm_log_virt_addr) + (ridx << 2),
+				wlen * 4, log_done_cb, (void *)0);
+			}
+			break;
+		case MET_CLOSE_FILE:	/* no argument */
+			/* do close file */
+			ridx = recv_buf[1];
+			widx = recv_buf[2];
+			met_sspm_log_discard = recv_buf[3];
+			if (widx < ridx) {	/* wrapping occurs */
+				wlen = log_size - ridx;
+				ondiemet_log_req_enq((char *)(ondiemet_sspm_log_virt_addr) + (ridx << 2),
+				wlen * 4, log_done_cb, (void *)1);
+				ondiemet_log_req_enq((char *)(ondiemet_sspm_log_virt_addr),
+						     widx * 4, log_done_cb, (void *)1);
+			} else {
+				wlen = widx - ridx;
+				ondiemet_log_req_enq((char *)(ondiemet_sspm_log_virt_addr) + (ridx << 2),
+				wlen * 4, log_done_cb, (void *)1);
+			}
+			ret = ondiemet_log_manager_stop();
+			/* pr_debug("MET_CLOSE_FILE: ret=%d log_discard=%d\n", ret, met_sspm_log_discard); */
+			sspm_ipi_send_ack(IPI_ID_TST1, &rdata);
+			if (sspm_run_mode == SSPM_RUN_CONTINUOUS) {
+				/* clear the memory */
+				memset_io((void *)ondiemet_sspm_log_virt_addr, 0,
+					  ondiemet_sspm_log_size);
+				/* re-start ondiemet again */
+				sspm_start();
+			}
+			break;
+		case MET_RESP_MD2AP:
+			sspm_ipi_send_ack(IPI_ID_TST1, &rdata);
+			sspm_buffer_dumping = 0;
+			break;
+		default:
+			sspm_ipi_send_ack(IPI_ID_TST1, &rdata);
+			break;
+		}
+	} while (!kthread_should_stop());
+	return 0;
+}
+
+#else /* SSPM_VERSION_V2 */
+
+int met_ipi_cb(unsigned int ipi_id, void *prdata, void *data, unsigned int len)
+{
+	uint32_t *cmd_buf = (uint32_t *)data;
+	uint32_t cmd;
+	int ret;
+
+	if (sspm_recv_thread_comp == 1)
+		return 0;
+
+	cmd = cmd_buf[0] & MET_SUB_ID_MASK;
+	switch (cmd) {
+	case MET_DUMP_BUFFER:	/* mbox 1: start index; 2: size */
+		sspm_buffer_dumping = 1;
+		ridx = cmd_buf[1];
+		widx = cmd_buf[2];
+		log_size = cmd_buf[3];
+		break;
+	case MET_CLOSE_FILE:	/* no argument */
+		/* do close file */
+		ridx = cmd_buf[1];
+		widx = cmd_buf[2];
+		met_sspm_log_discard = cmd_buf[3];
+		if (widx < ridx) {	/* wrapping occurs */
+			wlen = log_size - ridx;
+			ondiemet_log_req_enq((char *)(ondiemet_sspm_log_virt_addr) + (ridx << 2),
+			wlen * 4, log_done_cb, (void *)1);
+			ondiemet_log_req_enq((char *)(ondiemet_sspm_log_virt_addr),
+						 widx * 4, log_done_cb, (void *)1);
+		} else {
+			wlen = widx - ridx;
+			ondiemet_log_req_enq((char *)(ondiemet_sspm_log_virt_addr) + (ridx << 2),
+			wlen * 4, log_done_cb, (void *)1);
+		}
+		ret = ondiemet_log_manager_stop();
+		/* pr_debug("MET_CLOSE_FILE: ret=%d log_discard=%d\n", ret, met_sspm_log_discard); */
+		break;
+	case MET_RESP_MD2AP:
+		break;
+	default:
+		break;
+	}
+	return 0;
+}
+
+int ondiemet_sspm_recv_thread(void *data)
+{
+	int ret;
+	uint32_t cmd;
+
+	ret = mtk_ipi_register(sspm_ipidev_symbol, IPIR_C_MET, met_ipi_cb, NULL, (void *)recv_buf);
+	if (ret)
+		pr_debug("[MET] ipi_register:%d failed:%d\n", IPIR_C_MET, ret);
+
+	do {
+		mtk_ipi_recv_reply(sspm_ipidev_symbol,IPIR_C_MET, (void *)&rdata, 1);
+		if (sspm_recv_thread_comp == 1) {
+			while (!kthread_should_stop())
+				;
+			return 0;
+		}
+		cmd = recv_buf[0] & MET_SUB_ID_MASK;
+		switch (cmd) {
+		case MET_DUMP_BUFFER:	/* mbox 1: start index; 2: size */
+			if (widx < ridx) {	/* wrapping occurs */
+				wlen = log_size - ridx;
+				ondiemet_log_req_enq((char *)(ondiemet_sspm_log_virt_addr) + (ridx << 2),
+				wlen * 4, log_done_cb, (void *)1);
+				ondiemet_log_req_enq((char *)(ondiemet_sspm_log_virt_addr),
+							 widx * 4, log_done_cb, (void *)0);
+			} else {
+				wlen = widx - ridx;
+				ondiemet_log_req_enq((char *)(ondiemet_sspm_log_virt_addr) + (ridx << 2),
+				wlen * 4, log_done_cb, (void *)0);
+			}
+			break;
+		case MET_CLOSE_FILE:	/* no argument */
+			if (sspm_run_mode == SSPM_RUN_CONTINUOUS) {
+				/* clear the memory */
+				memset_io((void *)ondiemet_sspm_log_virt_addr, 0,
+					  ondiemet_sspm_log_size);
+				/* re-start ondiemet again */
+				sspm_start();
+			}
+			break;
+		case MET_RESP_MD2AP:
+			sspm_buffer_dumping = 0;
+			break;
+		default:
+			break;
+		}
+	} while (!kthread_should_stop());
+	return 0;
+}
+
+#endif /* SSPM_VERSION_V2 */
+
+
+
+void start_sspm_ipi_recv_thread(void)
+{
+#ifdef SSPM_VERSION_V2
+	int ret;
+
+	if(!sspm_ipidev_symbol)
+		return;
+
+	ret = mtk_ipi_register(sspm_ipidev_symbol, IPIS_C_MET, NULL, NULL, (void *) &ackdata);
+	if (ret)
+		pr_debug("[MET] ipi_register:%d failed:%d\n", IPIS_C_MET, ret);
+#endif
+
+	if (sspm_ipi_thread_started != 1) {
+		sspm_recv_thread_comp = 0;
+		ondiemet_sspm_recv_task =
+		    kthread_run(ondiemet_sspm_recv_thread, NULL, "ondiemet_sspm_recv");
+		if (IS_ERR(ondiemet_sspm_recv_task))
+			pr_debug("MET: Can not create ondiemet_sspm_recv\n");
+		else
+			sspm_ipi_thread_started = 1;
+	}
+}
+
+void stop_sspm_ipi_recv_thread(void)
+{
+	if (ondiemet_sspm_recv_task) {
+		sspm_recv_thread_comp = 1;
+#ifndef SSPM_VERSION_V2
+		sspm_ipi_recv_complete(IPI_ID_TST1);
+#endif
+		kthread_stop(ondiemet_sspm_recv_task);
+		ondiemet_sspm_recv_task = NULL;
+		sspm_ipi_thread_started = 0;
+#ifndef SSPM_VERSION_V2
+		sspm_ipi_recv_unregistration(IPI_ID_TST1);
+#else
+		mtk_ipi_unregister(sspm_ipidev_symbol, IPIR_C_MET);
+		mtk_ipi_unregister(sspm_ipidev_symbol, IPIS_C_MET);
+#endif
+	}
+}
+
+int met_ipi_to_sspm_command(void *buffer, int slot, unsigned int *retbuf, int retslot)
+{
+	int ret;
+
+#ifndef SSPM_VERSION_V2
+	ret = sspm_ipi_send_sync(IPI_ID_MET, IPI_OPT_WAIT, buffer, slot, retbuf, retslot);
+#else
+	if(!sspm_ipidev_symbol)
+		ret = -1;
+	else {
+		ret = mtk_ipi_send_compl(sspm_ipidev_symbol, IPIS_C_MET, IPI_SEND_WAIT, buffer, slot, 2000);
+		*retbuf = ackdata;
+	}
+#endif
+	if (ret != 0)
+		pr_debug("met_ipi_to_sspm_command error(%d)\n", ret);
+
+	return ret;
+}
+EXPORT_SYMBOL(met_ipi_to_sspm_command);
+
+
+int met_ipi_to_sspm_command_async(void *buffer, int slot, unsigned int *retbuf, int retslot)
+{
+	int ret;
+
+#ifndef SSPM_VERSION_V2
+	ret = sspm_ipi_send_sync(IPI_ID_MET, IPI_OPT_WAIT, buffer, slot, retbuf, retslot);
+#else
+	if(!sspm_ipidev_symbol)
+		ret = -1;
+	else {
+		ret = mtk_ipi_send(sspm_ipidev_symbol, IPIS_C_MET, IPI_SEND_WAIT, buffer, slot, 2000);
+		*retbuf = ackdata;
+	}
+#endif
+	if (ret != 0)
+		pr_debug("met_ipi_to_sspm_command error(%d)\n", ret);
+
+	return ret;
+}
+EXPORT_SYMBOL(met_ipi_to_sspm_command_async);
+
+
+
diff --git a/src/devtools/met-driver/4.19/common/sspm/sspm_met_smi.c b/src/devtools/met-driver/4.19/common/sspm/sspm_met_smi.c
new file mode 100644
index 0000000..9e4cf96
--- /dev/null
+++ b/src/devtools/met-driver/4.19/common/sspm/sspm_met_smi.c
@@ -0,0 +1,544 @@
+/*
+ * Copyright (C) 2019 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/ctype.h>
+#include <mt-plat/sync_write.h>
+#include <mt-plat/mtk_io.h>
+
+#include "met_drv.h"
+#include "trace.h"
+
+#include "mtk_typedefs.h"
+#include "sspm_mtk_smi.h"
+#include "sspm_met_smi.h"
+#include "sspm_met_smi_name.h"
+#include "core_plf_trace.h"
+#include "core_plf_init.h"
+#include "interface.h"
+#include "sspm/ondiemet_sspm.h"
+
+#define MET_SMI_DEBUG		1
+#define MET_SMI_BUF_SIZE	128
+#define MET_SMI_DEBUGBUF_SIZE	512
+#define NPORT_IN_PM		4
+
+// SMI Encode -- Master
+#ifdef SMI_MASTER_8BIT
+// bit15~bit16
+#define MET_SMI_BIT_REQ_LARB	15
+// bit13~bit14
+#define MET_SMI_BIT_REQ_COMM	13
+// bit12:Parallel Mode */
+#define MET_SMI_BIT_PM		12
+// bit9~bit8:Destination */
+#define MET_SMI_BIT_DST		10
+/* bit5~bit4:Request Type */
+#define MET_SMI_BIT_REQ		8
+/* bit3~bit0:Master */
+#define MET_SMI_BIT_MASTER	0
+#else
+// bit15~bit16
+#define MET_SMI_BIT_REQ_LARB	15
+// bit13~bit14
+#define MET_SMI_BIT_REQ_COMM	13
+// bit12:Parallel Mode */
+#define MET_SMI_BIT_PM		12
+// bit9~bit8:Destination */
+#define MET_SMI_BIT_DST		8
+/* bit5~bit4:Request Type */
+#define MET_SMI_BIT_REQ		4
+/* bit3~bit0:Master */
+#define MET_SMI_BIT_MASTER	0
+#endif
+
+// SMI Encode -- Metadata
+/* bit6~bit5:RW */
+#define MET_SMI_BIT_RW		5
+/* bit4~bit0:Port */
+#define MET_SMI_BIT_PORT	0
+
+/*
+*declare smi: larb0-larbn:
+*real define table in met_smi_name.h
+*/
+/*MET_SMI_DESC_DEFINE();*/
+/**/
+
+/*======================================================================*/
+/*	Global variable definitions					*/
+/*======================================================================*/
+#define MAX_CONFIG_ARRAY_SIZE 20
+struct metdevice met_sspm_smi;
+struct met_smi_conf smi_conf_array[MAX_CONFIG_ARRAY_SIZE];
+int smi_array_index;
+
+//static unsigned int smi_met_larb_number = SMI_LARB_NUMBER;
+static int count = SMI_LARB_NUMBER + SMI_COMM_NUMBER;
+static struct kobject *kobj_smi;
+
+/* Request type */
+static unsigned int larb_req_type = SMI_REQ_ALL;
+static unsigned int comm_req_type = SMI_REQ_ALL;
+
+/* Parallel mode */
+static unsigned int parallel_mode;
+
+/* Read/Write type in parallel mode */
+static int comm_pm_rw_type[SMI_COMM_NUMBER][NPORT_IN_PM];
+/* Error message */
+static char err_msg[MET_SMI_BUF_SIZE];
+static char debug_msg[MET_SMI_DEBUGBUF_SIZE];
+
+/*======================================================================*/
+/*	KOBJ Declarations						*/
+/*======================================================================*/
+/* KOBJ: larb_req_type */
+DECLARE_KOBJ_ATTR_INT(larb_req_type, larb_req_type);
+
+/* KOBJ : comm_req_type */
+DECLARE_KOBJ_ATTR_INT(comm_req_type, comm_req_type);
+
+/* KOBJ : enable_parallel_mode */
+DECLARE_KOBJ_ATTR_INT(enable_parallel_mode, parallel_mode);
+
+/* KOBJ : pm_rwtypeX */
+DECLARE_KOBJ_ATTR_STR_LIST_ITEM(
+	pm_rwtype,
+	KOBJ_ITEM_LIST(
+		{SMI_READ_ONLY,		"READ"},
+		{SMI_WRITE_ONLY,	"WRITE"}
+	)
+);
+DECLARE_KOBJ_ATTR_STR_LIST(pm_rwtype1, comm_pm_rw_type[0][0], pm_rwtype);
+DECLARE_KOBJ_ATTR_STR_LIST(pm_rwtype2, comm_pm_rw_type[0][1], pm_rwtype);
+DECLARE_KOBJ_ATTR_STR_LIST(pm_rwtype3, comm_pm_rw_type[0][2], pm_rwtype);
+DECLARE_KOBJ_ATTR_STR_LIST(pm_rwtype4, comm_pm_rw_type[0][3], pm_rwtype);
+
+/* KOBJ : count */
+DECLARE_KOBJ_ATTR_RO_INT(count, count);
+
+/* KOBJ : err_msg */
+DECLARE_KOBJ_ATTR_RO_STR(err_msg, err_msg);
+
+#define KOBJ_ATTR_LIST \
+do { \
+	KOBJ_ATTR_ITEM(larb_req_type); \
+	KOBJ_ATTR_ITEM(comm_req_type); \
+	KOBJ_ATTR_ITEM(enable_parallel_mode); \
+	KOBJ_ATTR_ITEM(pm_rwtype1); \
+	KOBJ_ATTR_ITEM(pm_rwtype2); \
+	KOBJ_ATTR_ITEM(pm_rwtype3); \
+	KOBJ_ATTR_ITEM(pm_rwtype4); \
+	KOBJ_ATTR_ITEM(count); \
+	KOBJ_ATTR_ITEM(err_msg); \
+} while (0)
+
+/*======================================================================*/
+/*	SMI Operations							*/
+/*======================================================================*/
+static void met_smi_debug(char *debug_log)
+{
+	MET_TRACE("%s", debug_log);
+}
+
+static int do_smi(void)
+{
+	return met_sspm_smi.mode;
+}
+
+static void smi_init_value(void)
+{
+	int i;
+
+	smi_array_index = 0;
+	for (i = 0; i < MAX_CONFIG_ARRAY_SIZE; i++) {
+		smi_conf_array[i].master = 0;
+		smi_conf_array[i].port[0] = -1;
+		smi_conf_array[i].port[1] = -1;
+		smi_conf_array[i].port[2] = -1;
+		smi_conf_array[i].port[3] = -1;
+		smi_conf_array[i].rwtype[0] = SMI_RW_ALL;
+		smi_conf_array[i].rwtype[1] = SMI_RW_ALL;
+		smi_conf_array[i].rwtype[2] = SMI_RW_ALL;
+		smi_conf_array[i].rwtype[3] = SMI_RW_ALL;
+		smi_conf_array[i].desttype = SMI_DEST_EMI;
+		smi_conf_array[i].reqtype = SMI_REQ_ALL;
+	}
+}
+
+static int smi_init(void)
+{
+	int i;
+
+	if (smi_array_index < MAX_CONFIG_ARRAY_SIZE) {
+		for (i = 0; i < (smi_array_index + 1); i++) {
+			snprintf(debug_msg, MET_SMI_DEBUGBUF_SIZE,
+				"===SMI config: parallel_mode = %d, master = %d, port0 = %d, port1 = %d, port2 = %d, port3 = %d, rwtype0 = %d, rwtype1 = %d, rwtype2 = %d, rwtype3 = %d, desttype = %d, reqtype(larb) = %d, reqtype(comm) = %d\n",
+				parallel_mode,
+				smi_conf_array[i].master,
+				smi_conf_array[i].port[0],
+				smi_conf_array[i].port[1],
+				smi_conf_array[i].port[2],
+				smi_conf_array[i].port[3],
+				smi_conf_array[i].rwtype[0],
+				smi_conf_array[i].rwtype[1],
+				smi_conf_array[i].rwtype[2],
+				smi_conf_array[i].rwtype[3],
+				smi_conf_array[i].desttype,
+				smi_conf_array[i].reqtype >> MET_SMI_BIT_REQ_LARB,
+				smi_conf_array[i].reqtype >> MET_SMI_BIT_REQ_COMM);
+			met_smi_debug(debug_msg);
+		}
+	} else {
+		met_smi_debug("smi_init() FAIL : smi_array_index overflow\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+static int met_smi_create(struct kobject *parent)
+{
+#define	KOBJ_ATTR_ITEM(attr_name) \
+do { \
+	ret = sysfs_create_file(kobj_smi, &attr_name##_attr.attr); \
+	if (ret != 0) { \
+		pr_debug("Failed to create " #attr_name " in sysfs\n"); \
+		return ret; \
+	} \
+} while (0)
+
+	int	j;
+	int	ret = 0;
+
+	pr_debug(" met_smi_create\n  met_smi_create\n  met_smi_create\n  met_smi_create\n  met_smi_create\n  met_smi_create\n  met_smi_create\n  met_smi_create\n");
+
+	/* Init. */
+
+	smi_init_value();
+
+	larb_req_type = SMI_REQ_ALL;
+	comm_req_type = SMI_REQ_ALL;
+	parallel_mode = 0;
+
+	for (j = 0; j < NPORT_IN_PM; j++)
+		comm_pm_rw_type[0][j] = SMI_READ_ONLY;
+
+	kobj_smi = parent;
+
+	KOBJ_ATTR_LIST;
+
+	return ret;
+
+#undef	KOBJ_ATTR_ITEM
+}
+
+void met_smi_delete(void)
+{
+#define	KOBJ_ATTR_ITEM(attr_name) \
+	sysfs_remove_file(kobj_smi, &attr_name##_attr.attr)
+
+
+	if (kobj_smi != NULL) {
+		KOBJ_ATTR_LIST;
+		kobj_smi = NULL;
+	}
+
+#undef	KOBJ_ATTR_ITEM
+}
+
+static void met_smi_start(void)
+{
+	if (do_smi()) {
+		if (smi_init() != 0) {
+			met_sspm_smi.mode = 0;
+			smi_init_value();
+			return;
+		}
+	}
+}
+
+static void met_smi_stop(void)
+{
+	int j;
+
+	if (do_smi()) {
+
+		/* Reset */
+		smi_init_value();
+		
+		larb_req_type = SMI_REQ_ALL;
+		comm_req_type = SMI_REQ_ALL;
+		parallel_mode = 0;
+		
+		for (j = 0; j < NPORT_IN_PM; j++)
+			comm_pm_rw_type[0][j] = SMI_READ_ONLY;
+		
+		
+		met_sspm_smi.mode = 0;
+	}
+	return ;
+}
+
+static char help[] =
+"  --smi=master:port:rw:dest:bus         monitor specified SMI banwidth\n"
+"  --smi=master:p1[:p2][:p3][:p4]        monitor parallel mode\n";
+
+static int smi_print_help(char *buf, int len)
+{
+	return snprintf(buf, PAGE_SIZE, "%s", help);
+}
+
+static int get_num(const char *__restrict__ dc, int *pValue)
+{
+	int	value = 0, digit;
+	int	i = 0;
+
+	while (((*dc) >= '0') && ((*dc) <= '9')) {
+		digit = (int)(*dc - '0');
+		value = value * 10 + digit;
+		dc++;
+		i++;
+	}
+
+	if (i == 0)
+		return 0;
+	*pValue = value;
+
+	return i;
+}
+
+/*
+ * There are serveal cases as follows:
+ *
+ * 1. "met-cmd --start --smi=master:port:rwtype:desttype:bustype" => Can assign multiple master
+ * 2. "met-cmd --start --smi=master:port[:port1][:port2][:port3]" ==> parael mode
+ *
+ */
+static int smi_process_argument(const char *__restrict__ arg, int len)
+{
+	int	args[5];
+	int	i, array_index;
+	int	idx;
+	unsigned int smi_conf_index = 0;
+	struct met_smi_conf smi_conf;
+
+	uint32_t ipi_buf[4];
+	uint32_t ret;
+	uint32_t rdata;
+	uint16_t sspm_master = 0;
+	uint32_t sspm_meta = 0;
+
+	if (len < 3)
+		return -1;
+
+	/*reset local config structure*/
+	memset(err_msg, 0, MET_SMI_BUF_SIZE);
+	for (i = 0; i < 4; i++) {
+		smi_conf.port[i] = -1;
+		smi_conf.rwtype[i] = SMI_RW_ALL;
+	}
+	smi_conf.master = 0;
+	smi_conf.reqtype = SMI_REQ_ALL;
+	smi_conf.desttype = SMI_DEST_EMI;
+
+	if (met_sspm_smi.mode != 0 && met_sspm_smi.mode != 1)
+		return -1;
+
+	/*
+	 * parse arguments
+	 * arg[0] = master
+	 * arg[1] = port or port1
+	 * arg[2] = rwtype or port2
+	 * arg[3] = desttype or port3
+	 * arg[4] = bustype or port4
+	 */
+	for (i = 0; i < ARRAY_SIZE(args); i++)
+		args[i] = -1;
+	idx = 0;
+	for (i = 0; i < ARRAY_SIZE(args); i++) {
+		ret = get_num(&(arg[idx]), &(args[i]));
+		if (ret == 0)
+			break;
+		idx += ret;
+		if (arg[idx] != ':')
+			break;
+		idx++;
+	}
+
+	pr_debug("===SMI process argu: args[0](%d), args[1](%d), args[2](%d), args[3](%d), args[4](%d)\n",
+		args[0],
+		args[1],
+		args[2],
+		args[3],
+		args[4]);
+
+	/*fill local config structure*/
+	smi_conf.master = args[0];
+	smi_conf.reqtype = (larb_req_type << MET_SMI_BIT_REQ_LARB) | (comm_req_type << MET_SMI_BIT_REQ_COMM);
+	if (parallel_mode == 0) {			/*legacy mode*/
+		smi_conf.rwtype[0] = args[2];
+		smi_conf.desttype = args[3];
+		smi_conf.port[0] = args[1];
+	} else {							/*parallel mode*/
+		smi_conf.desttype = SMI_DEST_EMI;
+		for (i = 0; i < 4; i++) {
+			if (args[i+1] < 0)
+				break;
+			smi_conf.port[i] = args[i+1];
+			smi_conf.rwtype[i] = comm_pm_rw_type[0][i];
+		}
+	}
+
+/*debug log to ftrace*/
+#ifdef MET_SMI_DEBUG
+	snprintf(debug_msg, MET_SMI_DEBUGBUF_SIZE,
+		"(argu)===SMI process argu Master[%d]: parallel_mode = %d, master = %d, port0 = %d, port1 = %d, port2 = %d, port3 = %d, rwtype0 = %d, rwtype1 = %d, rwtype2 = %d, rwtype3 = %d, desttype = %d, reqtype(larb) = %d, reqtype(comm) = %d\n",
+		args[0],
+		parallel_mode,
+		smi_conf.master,
+		smi_conf.port[0],
+		smi_conf.port[1],
+		smi_conf.port[2],
+		smi_conf.port[3],
+		smi_conf.rwtype[0],
+		smi_conf.rwtype[1],
+		smi_conf.rwtype[2],
+		smi_conf.rwtype[3],
+		smi_conf.desttype,
+		(smi_conf.reqtype >> MET_SMI_BIT_REQ_LARB) & 0x3,
+		(smi_conf.reqtype >> MET_SMI_BIT_REQ_COMM) & 0x3);	
+		met_smi_debug(debug_msg);
+#endif
+
+	/*find the empty conf_array*/
+	for (i = 0; i < MAX_CONFIG_ARRAY_SIZE; i++) {
+		if ((smi_conf_array[i].master == smi_conf.master) && (smi_conf_array[i].port[0] != -1))
+			break;
+	}
+	if (i >= MAX_CONFIG_ARRAY_SIZE) {
+		if (smi_conf_array[0].port[0] == -1) {
+			smi_array_index = 0;
+			array_index = 0;
+		} else {
+			smi_array_index = smi_array_index + 1;
+			array_index = smi_array_index;
+		}
+	} else {
+		array_index = i;
+	}
+
+	if ((smi_array_index >= MAX_CONFIG_ARRAY_SIZE) || (array_index >= MAX_CONFIG_ARRAY_SIZE)) {
+		snprintf(err_msg, MET_SMI_BUF_SIZE,
+			"===Setting Master[%d]: check smi_array_index=%d, array_index=%d overflow (> %d)\n",
+			args[0], smi_array_index, array_index, MAX_CONFIG_ARRAY_SIZE);
+		return -1;
+	}
+
+	smi_conf_array[array_index].master = smi_conf.master;
+
+
+	if (parallel_mode == 0) {	/* Legacy mode */
+		smi_conf_array[array_index].port[0] = smi_conf.port[0];
+	} else {					/* Parallel mode */
+		for (i = 0; i < NPORT_IN_PM; i++) {
+			if (smi_conf_array[array_index].port[i] == -1)
+				smi_conf_array[array_index].port[i] = smi_conf.port[smi_conf_index++];
+		}
+	}
+	smi_conf_array[array_index].rwtype[0] = smi_conf.rwtype[0];
+	smi_conf_array[array_index].rwtype[1] = smi_conf.rwtype[1];
+	smi_conf_array[array_index].rwtype[2] = smi_conf.rwtype[2];
+	smi_conf_array[array_index].rwtype[3] = smi_conf.rwtype[3];
+	smi_conf_array[array_index].desttype = smi_conf.desttype;
+	smi_conf_array[array_index].reqtype = smi_conf.reqtype;
+
+	pr_debug("===SMI process argu Master[%d]: parallel_mode = %d, master = %d, port0 = %d, port1 = %d, port2 = %d, port3 = %d, rwtype0 = %d, rwtype1 = %d, rwtype2 = %d, rwtype3 = %d, desttype = %d, reqtype(larb) = %d, reqtype(comm) = %d\n",
+		args[0],
+		parallel_mode,
+		smi_conf.master,
+		smi_conf.port[0],
+		smi_conf.port[1],
+		smi_conf.port[2],
+		smi_conf.port[3],
+		smi_conf.rwtype[0],
+		smi_conf.rwtype[1],
+		smi_conf.rwtype[2],
+		smi_conf.rwtype[3],
+		smi_conf.desttype,
+		(smi_conf.reqtype >> MET_SMI_BIT_REQ_LARB) & 0x3,
+		(smi_conf.reqtype >> MET_SMI_BIT_REQ_COMM) & 0x3);
+
+	// Encode SMI config: Master (request format from SMI driver)
+	sspm_master = sspm_master | (smi_conf_array[array_index].master << MET_SMI_BIT_MASTER);
+	sspm_master = sspm_master | 0 << MET_SMI_BIT_REQ; //reqtype value will be update in sspm side
+	sspm_master = sspm_master | (smi_conf_array[array_index].desttype << MET_SMI_BIT_DST);
+	sspm_master = sspm_master | (parallel_mode << MET_SMI_BIT_PM);
+	// Extrace info for larb and comm reqestType since of unable to recognize master belong to larb or comm.
+	// BIT13~BIT14: comm reqtype
+	// BIT15~BIT16: larb reqtype
+	sspm_master = sspm_master | smi_conf_array[array_index].reqtype;
+
+
+	// Encode SMI config: Meta (request format from SMI driver)
+	// Encode 4 Metadata into 1 unsigned int
+	// BIT0~BIT4: port
+	// BIT5~BIT6: rwtype
+	if(parallel_mode == 0){
+		sspm_meta = sspm_meta | (smi_conf_array[array_index].port[0] << MET_SMI_BIT_PORT);
+		sspm_meta = sspm_meta | (smi_conf_array[array_index].rwtype[0] << MET_SMI_BIT_RW);
+	}
+	else{
+		for(i = 0; i < 4; i++){
+			if(smi_conf_array[array_index].port[i] == 0xFFFFFFFF){
+				smi_conf_array[array_index].port[i] = USHRT_MAX;
+			}
+			sspm_meta = sspm_meta | (smi_conf_array[array_index].port[i] << (MET_SMI_BIT_PORT+8*i));
+			sspm_meta = sspm_meta | (smi_conf_array[array_index].rwtype[i] << (MET_SMI_BIT_RW+8*i));
+		}
+	}
+
+	// Transfer to SSPM side
+	if (sspm_buf_available == 1)
+	{
+		ipi_buf[0] = MET_MAIN_ID | MET_ARGU | MID_SMI << MID_BIT_SHIFT | 1;
+		ipi_buf[1] = sspm_master;
+		ipi_buf[2] = sspm_meta;
+		ipi_buf[3] = 0;
+		ret = met_ipi_to_sspm_command((void *)ipi_buf, 0, &rdata, 1);
+
+		/* Set mode */
+		met_sspm_smi.mode = 1;
+		ondiemet_module[ONDIEMET_SSPM] |= ID_CPU_INFO_MAPPING;
+	}
+
+	return 0;
+}
+
+struct metdevice met_sspm_smi = {
+	.name						= "smi",
+	.owner						= THIS_MODULE,
+	.type						= MET_TYPE_BUS,
+	.create_subfs				= met_smi_create,
+	.delete_subfs				= met_smi_delete,
+	.cpu_related				= 0,
+	.ondiemet_mode 				= 1,	
+	.ondiemet_start				= met_smi_start,
+	.ondiemet_stop				= met_smi_stop,
+	.ondiemet_process_argument 	= smi_process_argument,
+	.ondiemet_print_help		= smi_print_help,	
+};
+
diff --git a/src/devtools/met-driver/4.19/common/sspm/sspm_met_smi.h b/src/devtools/met-driver/4.19/common/sspm/sspm_met_smi.h
new file mode 100644
index 0000000..d05a8f3
--- /dev/null
+++ b/src/devtools/met-driver/4.19/common/sspm/sspm_met_smi.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2019 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _SMI_H_
+#define _SMI_H_
+
+#include <linux/device.h>
+
+struct met_smi {
+	int mode;
+	int master;
+	unsigned int port;
+	unsigned int rwtype;	/* 0 for R+W, 1 for read, 2 for write */
+	unsigned int desttype;	/* 0 for EMI+internal mem, 1 for EMI, 3 for internal mem */
+	unsigned int bustype;	/* 0 for GMC, 1 for AXI */
+	/* unsigned long requesttype;// 0:All, 1:ultra high, 2:pre-ultrahigh, 3:normal. */
+	struct kobject *kobj_bus_smi;
+};
+
+struct smi_cfg {
+	int master;
+	unsigned int port;
+	unsigned int rwtype;	/* 0 for R+W, 1 for read, 2 for write */
+	unsigned int desttype;	/* 0 for EMI+internal mem, 1 for EMI, 3 for internal mem */
+	unsigned int bustype;	/*0 for GMC, 1 for AXI */
+	/*unsigned long requesttype;// 0:All, 1:ultra high, 2:pre-ultrahigh, 3:normal. */
+};
+
+struct smi_mon_con {
+	unsigned int requesttype;	/* 0:All, 1:ultra high, 2:pre-ultrahigh, 3:normal. */
+};
+
+/* ====================== SMI/EMI Interface ================================ */
+
+struct met_smi_conf {
+	unsigned int master;	/*Ex : Whitney: 0~8 for larb0~larb8,  9 for common larb*/
+	int	port[4];	/* port select : [0] only for legacy mode, [0~3] ports for parallel mode, -1 no select*/
+	unsigned int reqtype; /* Selects request type : 0 for all,1 for ultra,2 for preultra,3 for normal*/
+	unsigned int rwtype[4]; /* Selects read/write:  0 for R+W,  1 for read,  2 for write;*/
+				/* [0] for legacy and parallel larb0~8, [0~3] for parallel mode common*/
+	unsigned int desttype; /* Selects destination: 0 and 3 for all memory, 1 for External,2 for internal*/
+};
+
+#endif				/* _SMI_H_ */
diff --git a/src/devtools/met-driver/4.19/common/sspm/sspm_met_smi_name.h b/src/devtools/met-driver/4.19/common/sspm/sspm_met_smi_name.h
new file mode 100644
index 0000000..62bfd62
--- /dev/null
+++ b/src/devtools/met-driver/4.19/common/sspm/sspm_met_smi_name.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2019 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _SMI_NAME_H_
+#define _SMI_NAME_H_
+#include "mtk_smi.h"
+
+enum SMI_DEST {
+	SMI_DEST_ALL		= 0,
+	SMI_DEST_EMI		= 1,
+	SMI_DEST_INTERNAL	= 2,
+	SMI_DEST_NONE		= 9
+};
+
+enum SMI_RW {
+	SMI_RW_ALL		= 0,
+	SMI_READ_ONLY		= 1,
+	SMI_WRITE_ONLY		= 2,
+	SMI_RW_RESPECTIVE	= 3,
+	SMI_RW_NONE		= 9
+};
+
+enum SMI_BUS {
+	SMI_BUS_GMC		= 0,
+	SMI_BUS_AXI		= 1,
+	SMI_BUS_NONE		= 9
+};
+
+enum SMI_REQUEST {
+	SMI_REQ_ALL		= 0,
+	SMI_REQ_ULTRA		= 1,
+	SMI_REQ_PREULTRA	= 2,
+	SMI_NORMAL_ULTRA	= 3,
+	SMI_REQ_NONE		= 9
+};
+
+struct smi_desc {
+	unsigned long port;
+	enum SMI_DEST desttype;
+	enum SMI_RW rwtype;
+	enum SMI_BUS bustype;
+};
+#endif	/* _SMI_NAME_H_ */
diff --git a/src/devtools/met-driver/4.19/common/sspm/sspm_mtk_smi.h b/src/devtools/met-driver/4.19/common/sspm/sspm_mtk_smi.h
new file mode 100644
index 0000000..692c1d8
--- /dev/null
+++ b/src/devtools/met-driver/4.19/common/sspm/sspm_mtk_smi.h
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2019 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __MT_SMI_H__
+#define __MT_SMI_H__
+
+/* the default value, but the real number will get from symbol function*/
+#define SMI_LARB_NUMBER		9
+#define SMI_COMM_NUMBER		1
+#define SMI_LARB_MONITOR_NUMBER    1
+#define SMI_COMM_MONITOR_NUMBER    1
+
+#define SMI_REQ_OK		    (0)
+#define SMI_ERR_WRONG_REQ	(-1)
+#define SMI_ERR_OVERRUN		(-2)
+
+#define SMI_IOMEM_ADDR(b, off)	((void __iomem *)(((unsigned long)b)+off))
+
+#define SMI_LARB_MON_ENA(b)		    SMI_IOMEM_ADDR((b), 0x400)
+#define SMI_LARB_MON_CLR(b)		    SMI_IOMEM_ADDR((b), 0x404)
+#define SMI_LARB_MON_PORT(b)		SMI_IOMEM_ADDR((b), 0x408)
+#define SMI_LARB_MON_CON(b)		    SMI_IOMEM_ADDR((b), 0x40C)
+
+#define SMI_LARB_MON_ACT_CNT(b)		SMI_IOMEM_ADDR((b), 0x410)
+#define SMI_LARB_MON_REQ_CNT(b)		SMI_IOMEM_ADDR((b), 0x414)
+#define SMI_LARB_MON_BEA_CNT(b)		SMI_IOMEM_ADDR((b), 0x418)
+#define SMI_LARB_MON_BYT_CNT(b)		SMI_IOMEM_ADDR((b), 0x41C)
+#define SMI_LARB_MON_CP_CNT(b)		SMI_IOMEM_ADDR((b), 0x420)
+#define SMI_LARB_MON_DP_CNT(b)		SMI_IOMEM_ADDR((b), 0x424)
+#define SMI_LARB_MON_OSTD_CNT(b)	SMI_IOMEM_ADDR((b), 0x428)
+#define SMI_LARB_MON_CP_MAX(b)		SMI_IOMEM_ADDR((b), 0x430)
+#define SMI_LARB_MON_OSTD_MAX(b)	SMI_IOMEM_ADDR((b), 0x434)
+
+#define SMI_COMM_MON_ENA(b)		    SMI_IOMEM_ADDR((b), 0x1A0)
+#define SMI_COMM_MON_CLR(b)		    SMI_IOMEM_ADDR((b), 0x1A4)
+#define SMI_COMM_MON_TYPE(b)		SMI_IOMEM_ADDR((b), 0x1AC)
+#define SMI_COMM_MON_CON(b)		    SMI_IOMEM_ADDR((b), 0x1B0)
+#define SMI_COMM_MON_ACT_CNT(b)		SMI_IOMEM_ADDR((b), 0x1C0)
+#define SMI_COMM_MON_REQ_CNT(b)		SMI_IOMEM_ADDR((b), 0x1C4)
+#define SMI_COMM_MON_OSTD_CNT(b)	SMI_IOMEM_ADDR((b), 0x1C8)
+#define SMI_COMM_MON_BEA_CNT(b)		SMI_IOMEM_ADDR((b), 0x1CC)
+#define SMI_COMM_MON_BYT_CNT(b)		SMI_IOMEM_ADDR((b), 0x1D0)
+#define SMI_COMM_MON_CP_CNT(b)		SMI_IOMEM_ADDR((b), 0x1D4)
+#define SMI_COMM_MON_DP_CNT(b)		SMI_IOMEM_ADDR((b), 0x1D8)
+#define SMI_COMM_MON_CP_MAX(b)		SMI_IOMEM_ADDR((b), 0x1DC)
+#define SMI_COMM_MON_OSTD_MAX(b)	SMI_IOMEM_ADDR((b), 0x1E0)
+
+
+/*ondiemet smi ipi command*/
+enum MET_SMI_IPI_Type {
+	SMI_DRIVER_INITIAL_VALUE = 0x0,
+	SMI_DRIVER_RESET_VALUE,
+	SET_BASE_SMI,
+	SMI_ASSIGN_PORT_START,
+	SMI_ASSIGN_PORT_I,
+	SMI_ASSIGN_PORT_II,
+	SMI_ASSIGN_PORT_III,
+	SMI_ASSIGN_PORT_END,
+};
+
+
+
+
+void MET_SMI_IPI_baseaddr(void);
+int MET_SMI_Init(void);
+void MET_SMI_Fini(void);
+void MET_SMI_Enable(int larbno);
+void MET_SMI_Disable(int larbno);
+void MET_SMI_Pause(int larbno);
+void MET_SMI_Clear(int larbno);
+int MET_SMI_PowerOn(unsigned int master);
+void MET_SMI_PowerOff(unsigned int master);
+int MET_SMI_LARB_SetCfg(int larbno,
+			unsigned int pm,
+			unsigned int reqtype, unsigned int rwtype, unsigned int dsttype);
+int MET_SMI_LARB_SetPortNo(int larbno, unsigned int idx, unsigned int port);
+int MET_SMI_COMM_SetCfg(int commonno, unsigned int pm, unsigned int reqtype);
+int MET_SMI_COMM_SetPortNo(int commonno, unsigned int idx, unsigned int port);
+int MET_SMI_COMM_SetRWType(int commonno, unsigned int idx, unsigned int rw);
+
+/* config */
+int MET_SMI_GetEna(int larbno);
+int MET_SMI_GetClr(int larbno);
+int MET_SMI_GetPortNo(int larbno);
+int MET_SMI_GetCon(int larbno);
+
+/* cnt */
+int MET_SMI_GetActiveCnt(int larbno);
+int MET_SMI_GetRequestCnt(int larbno);
+int MET_SMI_GetBeatCnt(int larbno);
+int MET_SMI_GetByteCnt(int larbno);
+int MET_SMI_GetCPCnt(int larbno);
+int MET_SMI_GetDPCnt(int larbno);
+int MET_SMI_GetOSTDCnt(int larbno);
+int MET_SMI_GetCP_MAX(int larbno);
+int MET_SMI_GetOSTD_MAX(int larbno);
+
+/* common */
+void MET_SMI_Comm_Init(void);
+void MET_SMI_Comm_Enable(int commonno);
+void MET_SMI_Comm_Disable(int commonno);
+void MET_SMI_Pause(int commonno);
+void MET_SMI_Comm_Clear(int commonno);
+
+/* common config */
+int MET_SMI_Comm_GetEna(int commonno);
+int MET_SMI_Comm_GetClr(int commonno);
+int MET_SMI_Comm_GetType(int commonno);
+int MET_SMI_Comm_GetCon(int commonno);
+
+/* cnt */
+int MET_SMI_Comm_GetPortNo(int commonno);
+int MET_SMI_Comm_GetActiveCnt(int commonno);
+int MET_SMI_Comm_GetRequestCnt(int commonno);
+int MET_SMI_Comm_GetBeatCnt(int commonno);
+int MET_SMI_Comm_GetByteCnt(int commonno);
+int MET_SMI_Comm_GetCPCnt(int commonno);
+int MET_SMI_Comm_GetDPCnt(int commonno);
+int MET_SMI_Comm_GetOSTDCnt(int commonno);
+int MET_SMI_Comm_GetCP_MAX(int commonno);
+int MET_SMI_Comm_GetOSTD_MAX(int commonno);
+
+#endif				/* __MT_SMI_H__ */
diff --git a/src/devtools/met-driver/4.19/common/sspm/sspm_walltime.c b/src/devtools/met-driver/4.19/common/sspm/sspm_walltime.c
new file mode 100644
index 0000000..82cbaf2
--- /dev/null
+++ b/src/devtools/met-driver/4.19/common/sspm/sspm_walltime.c
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2019 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/random.h>
+#include <linux/fs.h>
+
+#include "met_drv.h"
+#include "trace.h"
+/* #include "plf_init.h" */
+#include "sspm/ondiemet_sspm.h"
+
+static void sspm_walltime_start(void)
+{
+	ondiemet_module[ONDIEMET_SSPM] |= ID_WALL_TIME;
+}
+
+static void sspm_walltime_stop(void)
+{
+}
+
+static const char sspm_walltime_header[] = "met-info [000] 0.0: sspm WCLK: freq\n";
+
+static int sspm_walltime_print_header(char *buf, int len)
+{
+	return snprintf(buf, PAGE_SIZE, sspm_walltime_header);
+}
+
+struct metdevice met_sspm_walltime = {
+	.name = "sspm_wall_time",
+	.owner = THIS_MODULE,
+	.type = MET_TYPE_BUS,
+	.cpu_related = 0,
+	.ondiemet_mode = 0,
+	.print_header = sspm_walltime_print_header,
+	.ondiemet_start = sspm_walltime_start,
+	.ondiemet_stop = sspm_walltime_stop,
+	.ondiemet_print_header = sspm_walltime_print_header,
+};
+EXPORT_SYMBOL(met_sspm_walltime);
diff --git a/src/devtools/met-driver/4.19/common/stat.c b/src/devtools/met-driver/4.19/common/stat.c
new file mode 100644
index 0000000..494d210
--- /dev/null
+++ b/src/devtools/met-driver/4.19/common/stat.c
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2019 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/cpu.h>
+#include <linux/cpumask.h>
+/* #include <linux/fs.h> */
+#include <linux/init.h>
+#include <linux/version.h>
+#include <linux/interrupt.h>
+#include <linux/kernel_stat.h>
+/* #include <linux/proc_fs.h> */
+#include <linux/sched.h>
+/* #include <linux/seq_file.h> */
+/* #include <linux/slab.h> */
+#include <linux/time.h>
+#include <linux/irqnr.h>
+#include <linux/vmalloc.h>
+/* #include <asm/cputime.h> */
+/* #include <asm-generic/cputime.h> */
+#include <linux/tick.h>
+#include <linux/jiffies.h>
+
+#include <asm/page.h>
+#include <linux/slab.h>
+
+#include "stat.h"
+#include "met_drv.h"
+#include "trace.h"
+
+#define MS_STAT_FMT	"%5lu.%06lu"
+#define FMTLX7		",%llx,%llx,%llx,%llx,%llx,%llx,%llx\n"
+#define FMTLX10		",%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx\n"
+
+
+static DEFINE_PER_CPU(int, cpu_status);
+
+
+/* void ms_st(unsigned long long timestamp, unsigned char cnt, unsigned int *value) */
+noinline void ms_st(unsigned long long timestamp, unsigned char cnt, u64 *value)
+{
+	unsigned long nano_rem = do_div(timestamp, 1000000000);
+
+	switch (cnt) {
+	case 10:
+		MET_TRACE(MS_STAT_FMT FMTLX10, (unsigned long)(timestamp), (nano_rem/1000),
+			value[0], value[1], value[2], value[3], value[4],
+			value[5], value[6], value[7], value[8], value[9]);
+		break;
+	case 7:
+		MET_TRACE(MS_STAT_FMT FMTLX7, (unsigned long)(timestamp), (nano_rem/1000),
+			value[0], value[1], value[2], value[3], value[4],
+			value[5], value[6]);
+		break;
+	}
+}
+
+static void met_stat_start(void)
+{
+	int	cpu = raw_smp_processor_id();
+
+	if (get_ctrl_flags() & 1)
+		met_stat.mode = 0;
+
+	per_cpu(cpu_status, cpu) = MET_CPU_ONLINE;
+}
+
+static void met_stat_stop(void)
+{
+}
+
+static int do_stat(void)
+{
+	return met_stat.mode;
+}
+
+u64 met_usecs_to_cputime64(u64 n)
+{
+#if (NSEC_PER_SEC % HZ) == 0
+	/* Common case, HZ = 100, 128, 200, 250, 256, 500, 512, 1000 etc. */
+	return div_u64(n, NSEC_PER_SEC / HZ);
+#elif (HZ % 512) == 0
+	/* overflow after 292 years if HZ = 1024 */
+	return div_u64(n * HZ / 512, NSEC_PER_SEC / 512);
+#else
+	/*
+	 * Generic case - optimized for cases where HZ is a multiple of 3.
+	 * overflow after 64.99 years, exact for HZ = 60, 72, 90, 120 etc.
+	 */
+	return div_u64(n * 9, (9ull * NSEC_PER_SEC + HZ / 2) / HZ);
+#endif
+}
+
+static u64 get_idle_time(int cpu)
+{
+	u64 idle, idle_time = get_cpu_idle_time_us(cpu, NULL);
+
+	if (idle_time == -1ULL) {
+		/* !NO_HZ so we can rely on cpustat.idle */
+		idle = kcpustat_cpu(cpu).cpustat[CPUTIME_IDLE];
+	} else
+		idle = met_usecs_to_cputime64(idle_time);
+
+	return idle;
+}
+
+static u64 get_iowait_time(int cpu)
+{
+	u64 iowait, iowait_time = get_cpu_iowait_time_us(cpu, NULL);
+
+	if (iowait_time == -1ULL) {
+		/* !NO_HZ so we can rely on cpustat.iowait */
+		iowait = kcpustat_cpu(cpu).cpustat[CPUTIME_IOWAIT];
+	} else
+		iowait = met_usecs_to_cputime64(iowait_time);
+
+	return iowait;
+}
+
+
+static unsigned int stat_os_polling(u64 *value, int i)
+{
+	int j = -1;
+
+	/* Copy values here to work around gcc-2.95.3, gcc-2.96 */
+	value[++j] = jiffies_64_to_clock_t(kcpustat_cpu(i).cpustat[CPUTIME_USER]);	/* user */
+	value[++j] = jiffies_64_to_clock_t(kcpustat_cpu(i).cpustat[CPUTIME_NICE]);	/* nice */
+	value[++j] = jiffies_64_to_clock_t(kcpustat_cpu(i).cpustat[CPUTIME_SYSTEM]);	/* system */
+	value[++j] = jiffies_64_to_clock_t(get_idle_time(i));	/* idle */
+	value[++j] = jiffies_64_to_clock_t(get_iowait_time(i));	/* iowait */
+	value[++j] = jiffies_64_to_clock_t(kcpustat_cpu(i).cpustat[CPUTIME_IRQ]);	/* irq */
+	value[++j] = jiffies_64_to_clock_t(kcpustat_cpu(i).cpustat[CPUTIME_SOFTIRQ]);	/* softirq */
+	value[++j] = jiffies_64_to_clock_t(kcpustat_cpu(i).cpustat[CPUTIME_STEAL]);	/* steal */
+	value[++j] = jiffies_64_to_clock_t(kcpustat_cpu(i).cpustat[CPUTIME_GUEST]);	/* guest */
+	value[++j] = jiffies_64_to_clock_t(kcpustat_cpu(i).cpustat[CPUTIME_GUEST_NICE]);	/* guest_nice */
+
+	return j + 1;
+}
+
+static void met_stat_polling(unsigned long long stamp, int cpu)
+{
+	unsigned char count;
+	u64 value[10] = {0};
+
+	if (per_cpu(cpu_status, cpu) != MET_CPU_ONLINE)
+		return;
+
+	/* return; */
+	if (do_stat()) {
+		count = stat_os_polling(value, cpu);
+		if (count)
+			ms_st(stamp, count, value);
+	}
+}
+
+static const char header[] =
+	"met-info [000] 0.0: met_st_header: user,nice,system,idle,iowait,irq,softirq,steal,guest,guest_nice\n";
+
+static const char help[] = "  --stat                                monitor stat\n";
+
+
+static int met_stat_print_help(char *buf, int len)
+{
+	return snprintf(buf, PAGE_SIZE, help);
+}
+
+static int met_stat_print_header(char *buf, int len)
+{
+	return snprintf(buf, PAGE_SIZE, header);
+}
+
+static void met_stat_cpu_state_notify(long cpu, unsigned long action)
+{
+	per_cpu(cpu_status, cpu) = action;
+}
+
+
+struct metdevice met_stat = {
+	.name = "stat",
+	.type = MET_TYPE_PMU,
+	.cpu_related = 1,
+	.start = met_stat_start,
+	.stop = met_stat_stop,
+	.polling_interval = 30,
+	.timed_polling = met_stat_polling,
+	.print_help = met_stat_print_help,
+	.print_header = met_stat_print_header,
+	.cpu_state_notify = met_stat_cpu_state_notify,
+};
diff --git a/src/devtools/met-driver/4.19/common/stat.h b/src/devtools/met-driver/4.19/common/stat.h
new file mode 100644
index 0000000..752456c
--- /dev/null
+++ b/src/devtools/met-driver/4.19/common/stat.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2019 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _STAT_H_
+#define _STAT_H_
+
+#include <linux/device.h>
+
+extern struct metdevice met_stat;
+
+int stat_reg(struct kobject *parent);
+void stat_unreg(void);
+
+void stat_start(void);
+void stat_stop(void);
+void stat_polling(unsigned long long stamp, int cpu);
+
+unsigned int get_ctrl_flags(void);
+
+#endif				/* _STAT_H_ */
diff --git a/src/devtools/met-driver/4.19/common/switch.c b/src/devtools/met-driver/4.19/common/switch.c
new file mode 100644
index 0000000..b2588da
--- /dev/null
+++ b/src/devtools/met-driver/4.19/common/switch.c
@@ -0,0 +1,302 @@
+/*
+ * Copyright (C) 2019 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/* include <asm/percpu.h> */
+#include <trace/events/sched.h>
+#include <linux/module.h>
+#include <trace/events/irq.h>
+#include <trace/events/power.h>
+
+#include "interface.h"
+#include "met_drv.h"
+#include "cpu_pmu.h"
+#include "switch.h"
+#include "sampler.h"
+#include "met_kernel_symbol.h"
+/* #include "trace.h" */
+
+/*
+ * IRQ_TIRGGER and CPU_IDLE_TRIGGER
+ */
+/* #define IRQ_TRIGGER */
+/* #define CPU_IDLE_TRIGGER */
+
+static DEFINE_PER_CPU(unsigned int, first_log);
+
+#ifdef __aarch64__
+/* #include <asm/compat.h> */
+#include <linux/compat.h>
+#endif
+
+noinline void mt_switch(struct task_struct *prev, struct task_struct *next)
+{
+	int cpu;
+	int prev_state = 0, next_state = 0;
+
+#ifdef __aarch64__
+	prev_state = !(is_compat_thread(task_thread_info(prev)));
+	next_state = !(is_compat_thread(task_thread_info(next)));
+#endif
+
+	cpu = smp_processor_id();
+	if (per_cpu(first_log, cpu)) {
+		MET_TRACE("%d, %d, %d, %d\n", prev->pid, prev_state, next->pid, next_state);
+		per_cpu(first_log, cpu) = 0;
+	}
+	else if (prev_state != next_state)
+		MET_TRACE("%d, %d, %d, %d\n", prev->pid, prev_state, next->pid, next_state);
+}
+
+
+#if 0 /* move to kernel space */
+MET_DEFINE_PROBE(sched_switch,
+		 TP_PROTO(bool preempt, struct task_struct *prev, struct task_struct *next))
+{
+	/* speedup sched_switch callback handle */
+	if (met_switch.mode == 0)
+		return;
+
+	if (met_switch.mode & MT_SWITCH_EVENT_TIMER)
+		met_event_timer_notify();
+
+	if (met_switch.mode & MT_SWITCH_64_32BIT)
+		mt_switch(prev, next);
+
+	if (met_switch.mode & MT_SWITCH_SCHEDSWITCH) {
+		if (get_pmu_profiling_version() == 1)
+			cpupmu_polling(0, smp_processor_id());
+#ifdef MET_SUPPORT_CPUPMU_V2
+		else if (get_pmu_profiling_version() == 2)
+			cpupmu_polling_v2(0, smp_processor_id());
+#endif
+	}
+}
+#endif
+
+void met_sched_switch(struct task_struct *prev, struct task_struct *next)
+{
+	/* speedup sched_switch callback handle */
+	if (met_switch.mode == 0)
+		return;
+
+	if (met_switch.mode & MT_SWITCH_EVENT_TIMER)
+		met_event_timer_notify();
+
+	if (met_switch.mode & MT_SWITCH_64_32BIT)
+		mt_switch(prev, next);
+
+	/* met_perf_cpupmu_status: 0: stop, others: polling */
+	if ((met_switch.mode & MT_SWITCH_SCHEDSWITCH) && met_perf_cpupmu_status)
+		met_perf_cpupmu_polling(0, smp_processor_id());
+}
+
+#ifdef IRQ_TRIGGER
+MET_DEFINE_PROBE(irq_handler_entry, TP_PROTO(int irq, struct irqaction *action))
+{
+	if (met_switch.mode & MT_SWITCH_EVENT_TIMER) {
+		met_event_timer_notify();
+		return;
+	}
+}
+#endif
+
+#ifdef CPU_IDLE_TRIGGER
+MET_DEFINE_PROBE(cpu_idle, TP_PROTO(unsigned int state, unsigned int cpu_id))
+{
+	if (met_switch.mode & MT_SWITCH_EVENT_TIMER) {
+		met_event_timer_notify();
+		return;
+	}
+}
+#endif
+
+#ifdef MET_ANYTIME
+/*
+ * create related subfs file node
+ */
+
+static ssize_t default_on_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "1\n");
+}
+
+static struct kobj_attribute default_on_attr = __ATTR(default_on, 0664, default_on_show, NULL);
+static struct kobject *kobj_cpu;
+#endif
+
+static int met_switch_create_subfs(struct kobject *parent)
+{
+	int ret = 0;
+
+	/* register tracepoints */
+#if 0
+	if (MET_REGISTER_TRACE(sched_switch)) {
+		pr_debug("can not register callback of sched_switch\n");
+		return -ENODEV;
+	}
+#else
+	if (met_reg_switch_symbol)
+		ret = met_reg_switch_symbol();
+#endif
+#ifdef CPU_IDLE_TRIGGER
+	if (MET_REGISTER_TRACE(cpu_idle)) {
+		pr_debug("can not register callback of irq_handler_entry\n");
+		return -ENODEV;
+	}
+#endif
+#ifdef IRQ_TRIGGER
+	if (MET_REGISTER_TRACE(irq_handler_entry)) {
+		pr_debug("can not register callback of irq_handler_entry\n");
+		return -ENODEV;
+	}
+#endif
+
+#ifdef MET_ANYTIME
+	/*
+	 * to create default_on file node
+	 * let user space can know we can support MET default on
+	 */
+	kobj_cpu = parent;
+	ret = sysfs_create_file(kobj_cpu, &default_on_attr.attr);
+	if (ret != 0) {
+		pr_debug("Failed to create default_on in sysfs\n");
+		return -1;
+	}
+#endif
+
+	return ret;
+}
+
+
+static void met_switch_delete_subfs(void)
+{
+#ifdef MET_ANYTIME
+	if (kobj_cpu != NULL) {
+		sysfs_remove_file(kobj_cpu, &default_on_attr.attr);
+		kobj_cpu = NULL;
+	}
+#endif
+#ifdef IRQ_TRIGGER
+	MET_UNREGISTER_TRACE(irq_handler_entry);
+#endif
+#ifdef CPU_IDLE_TRIGGER
+	MET_UNREGISTER_TRACE(cpu_idle);
+#endif
+#if 0
+	MET_UNREGISTER_TRACE(sched_switch);
+#else
+	if (met_unreg_switch_symbol)
+		met_unreg_switch_symbol();
+#endif
+
+}
+
+
+static void (*cpu_timed_polling)(unsigned long long stamp, int cpu);
+/* static void (*cpu_tagged_polling)(unsigned long long stamp, int cpu); */
+
+static void met_switch_start(void)
+{
+	int cpu;
+
+	if (met_switch.mode & MT_SWITCH_SCHEDSWITCH) {
+		cpu_timed_polling = met_cpupmu.timed_polling;
+		/* cpu_tagged_polling = met_cpupmu.tagged_polling; */
+		met_cpupmu.timed_polling = NULL;
+		/* met_cpupmu.tagged_polling = NULL; */
+	}
+
+	for_each_possible_cpu(cpu) {
+		per_cpu(first_log, cpu) = 1;
+	}
+
+}
+
+
+static void met_switch_stop(void)
+{
+	int cpu;
+
+	if (met_switch.mode & MT_SWITCH_SCHEDSWITCH) {
+		met_cpupmu.timed_polling = cpu_timed_polling;
+		/* met_cpupmu.tagged_polling = cpu_tagged_polling; */
+	}
+
+	for_each_possible_cpu(cpu) {
+		per_cpu(first_log, cpu) = 0;
+	}
+
+}
+
+
+static int met_switch_process_argument(const char *arg, int len)
+{
+	unsigned int value;
+	/*ex: mxitem is 0x0005, max value should be (5-1) + (5-2) = 0x100 + 0x11 = 7 */
+	unsigned int max_value = ((MT_SWITCH_MX_ITEM * 2) - 3);
+
+
+	if (met_parse_num(arg, &value, len) < 0)
+		goto arg_switch_exit;
+
+	if ((value < 1) || (value > max_value))
+		goto arg_switch_exit;
+
+	met_switch.mode = value;
+	return 0;
+
+arg_switch_exit:
+	met_switch.mode = 0;
+	return -EINVAL;
+}
+
+static const char header[] =
+	"met-info [000] 0.0: met_switch_header: prev_pid,prev_state,next_pid,next_state\n";
+
+static const char help[] =
+"  --switch=mode                         mode:0x1 - output CPUPMU whenever sched_switch\n"
+"                                        mode:0x2 - output Aarch 32/64 state whenever state changed (no CPUPMU)\n"
+"                                        mode:0x4 - force output count at tag_start/tag_end\n"
+"                                        mode:0x8 - task switch timer\n"
+"                                        mode:0xF - mode 0x1 + 0x2 + 04 + 08\n";
+
+static int met_switch_print_help(char *buf, int len)
+{
+	return snprintf(buf, PAGE_SIZE, help);
+}
+
+static int met_switch_print_header(char *buf, int len)
+{
+	int ret = 0;
+
+	ret =
+	    snprintf(buf, PAGE_SIZE, "met-info [000] 0.0: mp_cpu_switch_base: %d\n",
+		     met_switch.mode);
+	if (met_switch.mode & MT_SWITCH_64_32BIT)
+		ret += snprintf(buf + ret, PAGE_SIZE, header);
+
+	return ret;
+}
+
+
+struct metdevice met_switch = {
+	.name = "switch",
+	.type = MET_TYPE_PMU,
+	.create_subfs = met_switch_create_subfs,
+	.delete_subfs = met_switch_delete_subfs,
+	.start = met_switch_start,
+	.stop = met_switch_stop,
+	.process_argument = met_switch_process_argument,
+	.print_help = met_switch_print_help,
+	.print_header = met_switch_print_header,
+};
diff --git a/src/devtools/met-driver/4.19/common/switch.h b/src/devtools/met-driver/4.19/common/switch.h
new file mode 100644
index 0000000..0ee39b2
--- /dev/null
+++ b/src/devtools/met-driver/4.19/common/switch.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2019 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __MT_SWITCH__
+#define __MT_SWITCH__
+/*
+ * =========================
+ * !!!!!!!!!!!NOTICE!!!!!!!!
+ * =========================
+ * MT_SWITCH OPTION must delcare as Mask value
+ * And sort them from smallest to largest
+ * MT_SWITCH_MX_ITEM was used to determine argument range
+*/
+enum {
+	/* =================== */
+	/* user define mt switch event */
+	/* =================== */
+	MT_SWITCH_SCHEDSWITCH = 0x0001,
+	MT_SWITCH_64_32BIT = 0x0002,
+	MT_SWITCH_TAGPOLLING = 0x0004,
+	MT_SWITCH_EVENT_TIMER = 0x0008,
+	/* =================== */
+	MT_SWITCH_MX_ITEM
+};
+
+extern struct metdevice met_switch;
+#endif
diff --git a/src/devtools/met-driver/4.19/common/trace.h b/src/devtools/met-driver/4.19/common/trace.h
new file mode 100644
index 0000000..f0abcf1
--- /dev/null
+++ b/src/devtools/met-driver/4.19/common/trace.h
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2019 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _TRACE_H_
+#define _TRACE_H_
+
+
+extern void (*mp_cp_ptr)(unsigned long long timestamp,
+	       struct task_struct *task,
+	       unsigned long program_counter,
+	       unsigned long dcookie,
+	       unsigned long offset,
+	       unsigned char cnt, unsigned int *value);
+
+#define MP_FMT1	"%x\n"
+#define MP_FMT2	"%x,%x\n"
+#define MP_FMT3	"%x,%x,%x\n"
+#define MP_FMT4	"%x,%x,%x,%x\n"
+#define MP_FMT5	"%x,%x,%x,%x,%x\n"
+#define MP_FMT6	"%x,%x,%x,%x,%x,%x\n"
+#define MP_FMT7	"%x,%x,%x,%x,%x,%x,%x\n"
+#define MP_FMT8	"%x,%x,%x,%x,%x,%x,%x,%x\n"
+#define MP_FMT9	"%x,%x,%x,%x,%x,%x,%x,%x,%x\n"
+#define MP_FMT10 "%x,%x,%x,%x,%x,%x,%x,%x,%x,%x\n"
+#define MP_FMT11 "%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x\n"
+#define MP_FMT12 "%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x\n"
+#define MP_FMT13 "%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x\n"
+#define MP_FMT14 "%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x\n"
+#define MP_FMT15 "%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x\n"
+
+#define MET_GENERAL_PRINT(FUNC, count, value) \
+do { \
+	switch (count) { \
+	case 1: { \
+		FUNC(MP_FMT1, value[0]); \
+		} \
+		break; \
+	case 2: { \
+		FUNC(MP_FMT2, value[0], value[1]); \
+		} \
+		break; \
+	case 3: { \
+		FUNC(MP_FMT3, value[0], value[1], value[2]); \
+		} \
+		break; \
+	case 4: { \
+		FUNC(MP_FMT4, value[0], value[1], value[2], value[3]); \
+		} \
+		break; \
+	case 5: { \
+		FUNC(MP_FMT5, value[0], value[1], value[2], value[3], value[4]); \
+		} \
+		break; \
+	case 6: { \
+		FUNC(MP_FMT6, value[0], value[1], value[2], value[3], value[4], value[5]); \
+		} \
+		break; \
+	case 7: { \
+		FUNC(MP_FMT7, value[0], value[1], value[2], value[3], value[4], value[5], value[6]); \
+		} \
+		break; \
+	case 8: { \
+		FUNC(MP_FMT8, value[0], value[1], value[2], value[3], value[4], value[5], value[6], value[7]); \
+		} \
+		break; \
+	case 9: { \
+		FUNC(MP_FMT9, value[0], value[1], value[2], value[3], value[4], value[5], value[6], value[7], \
+				value[8]); \
+		} \
+		break; \
+	case 10: { \
+		FUNC(MP_FMT10, value[0], value[1], value[2], value[3], value[4], value[5], value[6], value[7], \
+				value[8], value[9]); \
+		} \
+		break; \
+	case 11: { \
+		FUNC(MP_FMT11, value[0], value[1], value[2], value[3], value[4], value[5], value[6], value[7], \
+				value[8], value[9], value[10]); \
+		} \
+		break; \
+	case 12: { \
+		FUNC(MP_FMT12, value[0], value[1], value[2], value[3], value[4], value[5], value[6], value[7], \
+				value[8], value[9], value[10], value[11]); \
+		} \
+		break; \
+	case 13: { \
+		FUNC(MP_FMT13, value[0], value[1], value[2], value[3], value[4], value[5], value[6], value[7], \
+				value[8], value[9], value[10], value[11], value[12]); \
+		} \
+		break; \
+	case 14: { \
+		FUNC(MP_FMT14, value[0], value[1], value[2], value[3], value[4], value[5], value[6], value[7], \
+				value[8], value[9], value[10], value[11], value[12], value[13]); \
+		} \
+		break; \
+	case 15: { \
+		FUNC(MP_FMT15, value[0], value[1], value[2], value[3], value[4], value[5], value[6], value[7], \
+				value[8], value[9], value[10], value[11], value[12], value[13], value[14]); \
+		} \
+		break; \
+	} \
+} while (0)
+#endif /* _TRACE_H_ */
diff --git a/src/devtools/met-driver/4.19/common/trace_event.c b/src/devtools/met-driver/4.19/common/trace_event.c
new file mode 100644
index 0000000..499a626
--- /dev/null
+++ b/src/devtools/met-driver/4.19/common/trace_event.c
@@ -0,0 +1,274 @@
+/*
+ * Copyright (C) 2019 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <asm/page.h>
+#include "interface.h"
+#include "met_drv.h"
+
+#ifdef	CONFIG_GPU_TRACEPOINTS
+#include <trace/events/gpu.h>
+
+#define show_secs_from_ns(ns) \
+	({ \
+		u64 t = ns + (NSEC_PER_USEC / 2); \
+		do_div(t, NSEC_PER_SEC); \
+		t; \
+	})
+
+#define show_usecs_from_ns(ns) \
+	({ \
+		u64 t = ns + (NSEC_PER_USEC / 2) ; \
+		u32 rem; \
+		do_div(t, NSEC_PER_USEC); \
+		rem = do_div(t, USEC_PER_SEC); \
+	})
+
+static int event_gpu_registered;
+static int event_gpu_enabled;
+
+noinline void gpu_sched_switch(const char *gpu_name, u64 timestamp,
+				      u32 next_ctx_id, s32 next_prio, u32 next_job_id)
+{
+	MET_TRACE("gpu_name=%s ts=%llu.%06lu next_ctx_id=%lu next_prio=%ld next_job_id=%lu\n",
+		   gpu_name,
+		   (unsigned long long)show_secs_from_ns(timestamp),
+		   (unsigned long)show_usecs_from_ns(timestamp),
+		   (unsigned long)next_ctx_id, (long)next_prio, (unsigned long)next_job_id);
+}
+
+MET_DEFINE_PROBE(gpu_sched_switch, TP_PROTO(const char *gpu_name, u64 timestamp,
+		u32 next_ctx_id, s32 next_prio, u32 next_job_id))
+{
+	gpu_sched_switch(gpu_name, timestamp, next_ctx_id, next_prio, next_job_id);
+}
+
+noinline void gpu_job_enqueue(u32 ctx_id, u32 job_id, const char *type)
+{
+	MET_TRACE("ctx_id=%lu job_id=%lu type=%s",
+		   (unsigned long)ctx_id, (unsigned long)job_id, type);
+}
+
+MET_DEFINE_PROBE(gpu_job_enqueue, TP_PROTO(u32 ctx_id, u32 job_id, const char *type))
+{
+	gpu_job_enqueue(ctx_id, job_id, type);
+}
+#endif
+
+
+#ifdef  MET_EVENT_POWER_SUPPORT
+#include "met_power.h"
+#include "met_kernel_symbol.h"
+
+static int event_power_registered;
+static int event_power_enabled;
+
+const char *
+met_trace_print_symbols_seq(char* pclass_name, unsigned long val,
+			const struct trace_print_flags *symbol_array)
+{
+	int i;
+    size_t new_fsize=0;
+    char _buf[32];
+	const char *ret = pclass_name;
+
+	for (i = 0;  symbol_array[i].name; i++) {
+
+		if (val != symbol_array[i].mask)
+			continue;
+
+		new_fsize = sprintf(pclass_name, symbol_array[i].name, strlen(symbol_array[i].name));
+		break;
+	}
+
+	if (new_fsize == 0) {
+		snprintf(_buf, 32, "0x%lx", val);
+		new_fsize = sprintf(pclass_name, _buf, strlen(_buf));
+	}
+
+	return ret;
+}
+
+#define __print_symbolic(pclass_name, value, symbol_array...)			\
+	({								\
+		static const struct trace_print_flags symbols[] =	\
+			{ symbol_array, { -1, NULL }};			\
+		met_trace_print_symbols_seq(pclass_name, value, symbols);		\
+	})
+
+#ifdef pm_qos_update_request
+#undef pm_qos_update_request
+#endif
+void pm_qos_update_request(int pm_qos_class, s32 value, char *owner)
+{
+	char class_name[64];
+	MET_TRACE("pm_qos_class=%s value=%d owner=%s\n",
+	  __print_symbolic(class_name, pm_qos_class,
+		{ _PM_QOS_CPU_DMA_LATENCY,	"CPU_DMA_LATENCY" },
+		{ _PM_QOS_NETWORK_LATENCY,	"NETWORK_LATENCY" },
+		{ _PM_QOS_NETWORK_THROUGHPUT,	"NETWORK_THROUGHPUT" }),
+	  value, owner);
+}
+//#endif
+
+#ifdef pm_qos_update_target
+#undef pm_qos_update_target
+#endif
+void pm_qos_update_target(unsigned int action, int prev_value, int curr_value)
+{
+	char class_name[64];
+
+	MET_TRACE("action=%s prev_value=%d curr_value=%d\n",
+	  __print_symbolic(class_name, action,
+		{ _PM_QOS_ADD_REQ,	"ADD_REQ" },
+		{ _PM_QOS_UPDATE_REQ,	"UPDATE_REQ" },
+		{ _PM_QOS_REMOVE_REQ,	"REMOVE_REQ" }),
+	  prev_value, curr_value);
+}
+#endif
+//#endif
+
+static int reset_driver_stat(void)
+{
+#ifdef	CONFIG_GPU_TRACEPOINTS
+	event_gpu_enabled = 0;
+#endif
+#ifdef MET_EVENT_POWER_SUPPORT
+	event_power_enabled = 0;
+#endif
+
+	met_trace_event.mode = 0;
+	return 0;
+}
+
+
+
+static void met_event_start(void)
+{
+#ifdef	CONFIG_GPU_TRACEPOINTS
+	/* register trace event for gpu */
+	do {
+		if (!event_gpu_enabled)
+			break;
+		if (MET_REGISTER_TRACE(gpu_sched_switch)) {
+			pr_debug("can not register callback of gpu_sched_switch\n");
+			break;
+		}
+		if (MET_REGISTER_TRACE(gpu_job_enqueue)) {
+			pr_debug("can not register callback of gpu_job_enqueue\n");
+			MET_UNREGISTER_TRACE(gpu_sched_switch);
+			break;
+		}
+		event_gpu_registered = 1;
+	} while (0);
+#endif
+
+#ifdef  MET_EVENT_POWER_SUPPORT
+	/* register trace event for power */
+	do {
+		if (!event_power_enabled)
+			break;
+		if (met_reg_event_power_symbol)
+			if (met_reg_event_power_symbol()) {
+				pr_debug("can not register callback of met_reg_event_power\n");
+				break;
+			}
+		event_power_registered = 1;
+	} while (0);
+#endif
+
+}
+
+static void met_event_stop(void)
+{
+#ifdef	CONFIG_GPU_TRACEPOINTS
+	/* unregister trace event for gpu */
+	if (event_gpu_registered) {
+		MET_UNREGISTER_TRACE(gpu_job_enqueue);
+		MET_UNREGISTER_TRACE(gpu_sched_switch);
+		event_gpu_registered = 0;
+	}
+#endif
+
+#ifdef  MET_EVENT_POWER_SUPPORT
+	/* unregister trace event for power */
+	if (event_power_registered) {
+		if (met_unreg_event_power_symbol)
+			met_unreg_event_power_symbol();
+		event_power_registered = 0;
+	}
+#endif
+}
+
+static int met_event_process_argument(const char *arg, int len)
+{
+	int	ret = -1;
+
+#ifdef	CONFIG_GPU_TRACEPOINTS
+	if (strcasecmp(arg, "gpu") == 0) {
+		event_gpu_enabled = 1;
+		met_trace_event.mode = 1;
+		ret = 0;
+	}
+#endif
+#ifdef  MET_EVENT_POWER_SUPPORT
+	if (strcasecmp(arg, "power") == 0) {
+		event_power_enabled = 1;
+		met_trace_event.mode = 1;
+		ret = 0;
+	}
+#endif
+	return ret;
+}
+
+static const char help[] = "\b"
+#ifdef	CONFIG_GPU_TRACEPOINTS
+	"  --event=gpu                           output gpu trace events\n"
+#endif
+#ifdef  MET_EVENT_POWER_SUPPORT
+	"  --event=power						 output pmqos trace events\n"
+#endif
+	;
+
+static int met_event_print_help(char *buf, int len)
+{
+	return snprintf(buf, PAGE_SIZE, help);
+}
+
+static const char header[] =
+	"met-info [000] 0.0: met_ftrace_event:"
+#ifdef	CONFIG_GPU_TRACEPOINTS
+	" gpu:gpu_sched_switch gpu:gpu_job_enqueue"
+#endif
+#ifdef  MET_EVENT_POWER_SUPPORT
+	" power:pm_qos_update_request power:pm_qos_update_target"
+#endif
+	"\n";
+
+static int met_event_print_header(char *buf, int len)
+{
+	int	ret;
+
+	ret = snprintf(buf, PAGE_SIZE, header);
+	return ret;
+}
+
+struct metdevice met_trace_event = {
+	.name			= "event",
+	.type			= MET_TYPE_PMU,
+	.start			= met_event_start,
+	.stop			= met_event_stop,
+	.reset			= reset_driver_stat,
+	.process_argument	= met_event_process_argument,
+	.print_help		= met_event_print_help,
+	.print_header		= met_event_print_header,
+};
diff --git a/src/devtools/met-driver/4.19/common/util.c b/src/devtools/met-driver/4.19/common/util.c
new file mode 100644
index 0000000..a02444d
--- /dev/null
+++ b/src/devtools/met-driver/4.19/common/util.c
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2019 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "util.h"
+#include <linux/fs.h>
+#include <linux/kernel.h>
+/* #include <asm/uaccess.h> */
+#include <linux/uaccess.h>
+
+#ifdef FILELOG
+
+static char tmp[1000] = { 0 };
+
+ /*TODO*/
+/**
+ * open file
+ * @param name path to open
+ * @return file pointer
+ */
+struct file *open_file(const char *name)
+{
+	struct file *fp = NULL;
+
+	fp = filp_open(name, O_WRONLY | O_APPEND /*| O_TRUNC */  | O_CREAT, 0664);
+	if (unlikely(fp == NULL)) {
+		pr_debug(KERNEL_INFO "can not open result file");
+		return NULL;
+	}
+	return fp;
+}
+
+/**
+ * write to file
+ * @param fp file pointer
+ * @param format format string
+ * @param ... variable-length subsequent arguments
+ */
+void write_file(struct file *fp, const char *format, ...)
+{
+	va_list va;
+	mm_segment_t fs = get_fs();
+
+	va_start(va, format);
+	vsnprintf(tmp, sizeof(tmp), format, va);
+	set_fs(KERNEL_DS);
+	vfs_write(fp, tmp, strlen(tmp), &(fp->f_pos));
+	set_fs(fs);
+	va_end(va);
+}
+
+/**
+ * close file
+ * @param fp file pointer
+ * @return exit code
+ */
+int close_file(struct file *fp)
+{
+	if (likely(fp != NULL)) {
+		filp_close(fp, NULL);
+		fp = NULL;
+		return 0;
+	}
+	pr_debug("cannot close file pointer:%p\n", fp);
+	return -1;
+}
+
+void filelog(char *str)
+{
+	struct file *fp;
+
+	fp = open_file("/data/met.log");
+	if (fp != NULL) {
+		write_file(fp, "%s", str);
+		close_file(fp);
+	}
+}
+
+#endif				/* FILELOG */
diff --git a/src/devtools/met-driver/4.19/common/util.h b/src/devtools/met-driver/4.19/common/util.h
new file mode 100644
index 0000000..45bc6e6
--- /dev/null
+++ b/src/devtools/met-driver/4.19/common/util.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2019 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _SRC_UTIL_H_
+#define _SRC_UTIL_H_
+
+/* #define FILELOG 1 */
+
+#ifdef FILELOG
+void filelog(char *str);
+#else
+#define filelog(str)
+#endif
+
+#endif				/* _SRC_UTIL_H_ */
diff --git a/src/devtools/met-driver/4.19/common/v6_pmu_hw.c b/src/devtools/met-driver/4.19/common/v6_pmu_hw.c
new file mode 100644
index 0000000..28727cd
--- /dev/null
+++ b/src/devtools/met-driver/4.19/common/v6_pmu_hw.c
@@ -0,0 +1,242 @@
+/*
+ * Copyright (C) 2019 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "cpu_pmu.h"
+
+/*******************************
+ *      ARM v6 operations      *
+ *******************************/
+#define ARMV6_PMCR_ENABLE               (1 << 0)
+#define ARMV6_PMCR_CTR01_RESET          (1 << 1)
+#define ARMV6_PMCR_CCOUNT_RESET         (1 << 2)
+#define ARMV6_PMCR_CCOUNT_DIV           (1 << 3)
+#define ARMV6_PMCR_COUNT0_IEN           (1 << 4)
+#define ARMV6_PMCR_COUNT1_IEN           (1 << 5)
+#define ARMV6_PMCR_CCOUNT_IEN           (1 << 6)
+#define ARMV6_PMCR_COUNT0_OVERFLOW      (1 << 8)
+#define ARMV6_PMCR_COUNT1_OVERFLOW      (1 << 9)
+#define ARMV6_PMCR_CCOUNT_OVERFLOW      (1 << 10)
+#define ARMV6_PMCR_EVT_COUNT0_SHIFT     20
+#define ARMV6_PMCR_EVT_COUNT0_MASK      (0xFF << ARMV6_PMCR_EVT_COUNT0_SHIFT)
+#define ARMV6_PMCR_EVT_COUNT1_SHIFT     12
+#define ARMV6_PMCR_EVT_COUNT1_MASK      (0xFF << ARMV6_PMCR_EVT_COUNT1_SHIFT)
+
+#define ARMV6_PMCR_OVERFLOWED_MASK \
+	(ARMV6_PMCR_COUNT0_OVERFLOW | ARMV6_PMCR_COUNT1_OVERFLOW | \
+	ARMV6_PMCR_CCOUNT_OVERFLOW)
+
+enum armv6_counters {
+	ARMV6_COUNTER0 = 0,
+	ARMV6_COUNTER1,
+	ARMV6_CYCLE_COUNTER,
+};
+
+static inline unsigned long armv6_pmcr_read(void)
+{
+	u32 val;
+
+	asm volatile ("mrc   p15, 0, %0, c15, c12, 0":"=r" (val));
+	return val;
+}
+
+static inline void armv6_pmcr_write(unsigned long val)
+{
+	asm volatile ("mcr   p15, 0, %0, c15, c12, 0"::"r" (val));
+}
+
+static inline unsigned int armv6_pmu_read_count(unsigned int idx)
+{
+	unsigned long value = 0;
+
+	if (idx == ARMV6_CYCLE_COUNTER)
+		asm volatile ("mrc   p15, 0, %0, c15, c12, 1":"=r" (value));
+	else if (idx == ARMV6_COUNTER0)
+		asm volatile ("mrc   p15, 0, %0, c15, c12, 2":"=r" (value));
+	else if (idx == ARMV6_COUNTER1)
+		asm volatile ("mrc   p15, 0, %0, c15, c12, 3":"=r" (value));
+
+	return value;
+}
+
+static inline void armv6_pmu_overflow(void)
+{
+	unsigned int val;
+
+	val = armv6_pmcr_read();
+	val |= ARMV6_PMCR_OVERFLOWED_MASK;
+	armv6_pmcr_write(val);
+}
+
+static inline unsigned int armv6_pmu_control_read(void)
+{
+	u32 val;
+
+	asm volatile ("mrc   p15, 0, %0, c15, c12, 0":"=r" (val));
+	return val;
+}
+
+static inline void armv6_pmu_control_write(unsigned int setting)
+{
+	unsigned long val;
+
+	val = armv6_pmcr_read();
+	val |= setting;
+	armv6_pmcr_write(val);
+}
+
+static void armv6_pmu_hw_reset_all(void)
+{
+	unsigned long val;
+
+	val = armv6_pmcr_read();
+	val &= ~ARMV6_PMCR_ENABLE;	/* disable all counters */
+	val |= (ARMV6_PMCR_CTR01_RESET | ARMV6_PMCR_CCOUNT_RESET);	/* reset CCNT, PMNC1/2 counter to zero */
+	armv6_pmcr_write(val);
+
+	armv6_pmu_overflow();
+}
+
+static void armv6pmu_enable_event(int idx, unsigned short config)
+{
+	unsigned long val, mask, evt;
+
+	if (idx == ARMV6_CYCLE_COUNTER) {
+		mask = 0;
+		evt = ARMV6_PMCR_CCOUNT_IEN;
+	} else if (idx == ARMV6_COUNTER0) {
+		mask = ARMV6_PMCR_EVT_COUNT0_MASK;
+		evt = (config << ARMV6_PMCR_EVT_COUNT0_SHIFT) | ARMV6_PMCR_COUNT0_IEN;
+	} else if (idx == ARMV6_COUNTER1) {
+		mask = ARMV6_PMCR_EVT_COUNT1_MASK;
+		evt = (config << ARMV6_PMCR_EVT_COUNT1_SHIFT) | ARMV6_PMCR_COUNT1_IEN;
+	} else {
+		pr_debug("invalid counter number (%d)\n", idx);
+		return;
+	}
+
+	/*
+	 * Mask out the current event and set the counter to count the event
+	 * that we're interested in.
+	 */
+	val = armv6_pmcr_read();
+	val &= ~mask;
+	val |= evt;
+	armv6_pmcr_write(val);
+}
+
+/***********************************
+ *      MET ARM v6 operations      *
+ ***********************************/
+enum ARM_TYPE {
+	ARM1136 = 0xB36,
+	ARM1156 = 0xB56,
+	ARM1176 = 0xB76,
+	CHIP_UNKNOWN = 0xFFF
+};
+
+struct chip_pmu {
+	enum ARM_TYPE type;
+};
+
+static struct chip_pmu chips[] = {
+	{ARM1136},
+	{ARM1156},
+	{ARM1176},
+};
+
+static int armv6_pmu_hw_check_event(struct met_pmu *pmu, int idx, int event)
+{
+	int i;
+
+	/* Check if event is duplicate */
+	for (i = 0; i < idx; i++) {
+		if (pmu[i].event == event)
+			break;
+	}
+	if (i < idx) {
+		/* pr_debug("++++++ found duplicate event 0x%02x i=%d\n", event, i); */
+		return -1;
+	}
+
+	return 0;
+}
+
+static void armv6_pmu_hw_start(struct met_pmu *pmu, int count)
+{
+	int i;
+	int generic = count - 1;
+
+	armv6_pmu_hw_reset_all();
+
+	for (i = 0; i < generic; i++) {
+		if (pmu[i].mode == MODE_POLLING)
+			armv6pmu_enable_event(i, pmu[i].event);
+	}
+
+	if (pmu[count - 1].mode == MODE_POLLING)
+		armv6pmu_enable_event(2, pmu[2].event);
+
+	armv6_pmu_control_write(ARMV6_PMCR_ENABLE);
+}
+
+static void armv6_pmu_hw_stop(int count)
+{
+	armv6_pmu_hw_reset_all();
+}
+
+static unsigned int armv6_pmu_hw_polling(struct met_pmu *pmu, int count, unsigned int *pmu_value)
+{
+	int i, cnt = 0;
+	int generic = count - 1;
+
+	for (i = 0; i < generic; i++) {
+		if (pmu[i].mode == MODE_POLLING) {
+			pmu_value[cnt] = armv6_pmu_read_count(i);
+			cnt++;
+		}
+	}
+
+	if (pmu[count - 1].mode == MODE_POLLING) {
+		pmu_value[cnt] = armv6_pmu_read_count(2);
+		cnt++;
+	}
+
+	armv6_pmu_control_write(ARMV6_PMCR_ENABLE | ARMV6_PMCR_CTR01_RESET |
+				ARMV6_PMCR_CCOUNT_RESET);
+
+	return cnt;
+}
+
+struct cpu_pmu_hw armv6_pmu = {
+	.name = "armv6_pmu",
+	.check_event = armv6_pmu_hw_check_event,
+	.start = armv6_pmu_hw_start,
+	.stop = armv6_pmu_hw_stop,
+	.polling = armv6_pmu_hw_polling,
+};
+
+struct cpu_pmu_hw *v6_cpu_pmu_hw_init(int typeid)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(chips); i++)
+		if (chips[i].type == typeid)
+			break;
+
+	if (i == ARRAY_SIZE(chips))
+		return NULL;
+
+	armv6_pmu.nr_cnt = 3;
+
+	return &armv6_pmu;
+}
diff --git a/src/devtools/met-driver/4.19/common/v6_pmu_hw.h b/src/devtools/met-driver/4.19/common/v6_pmu_hw.h
new file mode 100644
index 0000000..e6de522
--- /dev/null
+++ b/src/devtools/met-driver/4.19/common/v6_pmu_hw.h
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2019 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+
+#ifndef __V6_PMU_HW_H__
+#define __V6_PMU_HW_H__
+
+extern struct cpu_pmu_hw armv6_pmu;
+extern struct cpu_pmu_hw *v6_cpu_pmu_hw_init(int typeid);
+
+#endif
diff --git a/src/devtools/met-driver/4.19/common/v7_pmu_hw.c b/src/devtools/met-driver/4.19/common/v7_pmu_hw.c
new file mode 100644
index 0000000..0f1b05f
--- /dev/null
+++ b/src/devtools/met-driver/4.19/common/v7_pmu_hw.c
@@ -0,0 +1,296 @@
+/*
+ * Copyright (C) 2019 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "cpu_pmu.h"
+#include "v6_pmu_hw.h"
+
+/*******************************
+ *      ARM v7 operations      *
+ *******************************/
+#define ARMV7_PMCR_E		(1 << 0)	/* enable all counters */
+#define ARMV7_PMCR_P		(1 << 1)
+#define ARMV7_PMCR_C		(1 << 2)
+#define ARMV7_PMCR_D		(1 << 3)
+#define ARMV7_PMCR_X		(1 << 4)
+#define ARMV7_PMCR_DP		(1 << 5)
+#define ARMV7_PMCR_N_SHIFT	11		/* Number of counters supported */
+#define ARMV7_PMCR_N_MASK	0x1f
+#define ARMV7_PMCR_MASK		0x3f		/* mask for writable bits */
+
+static unsigned int armv7_get_ic(void)
+{
+	unsigned int value;
+	/* Read Main ID Register */
+	asm volatile ("mrc p15, 0, %0, c0, c0, 0":"=r" (value));
+
+	value = (value & 0xffff) >> 4;	/* primary part number */
+	return value;
+}
+
+static inline void armv7_pmu_counter_select(unsigned int idx)
+{
+	asm volatile ("mcr p15, 0, %0, c9, c12, 5"::"r" (idx));
+	isb();
+}
+
+static inline void armv7_pmu_type_select(unsigned int idx, unsigned int type)
+{
+	armv7_pmu_counter_select(idx);
+	asm volatile ("mcr p15, 0, %0, c9, c13, 1"::"r" (type));
+}
+
+static inline unsigned int armv7_pmu_read_count(unsigned int idx)
+{
+	unsigned int value;
+
+	if (idx == 31) {
+		asm volatile ("mrc p15, 0, %0, c9, c13, 0":"=r" (value));
+	} else {
+		armv7_pmu_counter_select(idx);
+		asm volatile ("mrc p15, 0, %0, c9, c13, 2":"=r" (value));
+	}
+	return value;
+}
+
+static inline void armv7_pmu_write_count(int idx, u32 value)
+{
+	if (idx == 31) {
+		asm volatile ("mcr p15, 0, %0, c9, c13, 0"::"r" (value));
+	} else {
+		armv7_pmu_counter_select(idx);
+		asm volatile ("mcr p15, 0, %0, c9, c13, 2"::"r" (value));
+	}
+}
+
+static inline void armv7_pmu_enable_count(unsigned int idx)
+{
+	asm volatile ("mcr p15, 0, %0, c9, c12, 1"::"r" (1 << idx));
+}
+
+static inline void armv7_pmu_disable_count(unsigned int idx)
+{
+	asm volatile ("mcr p15, 0, %0, c9, c12, 2"::"r" (1 << idx));
+}
+
+static inline void armv7_pmu_enable_intr(unsigned int idx)
+{
+	asm volatile ("mcr p15, 0, %0, c9, c14, 1"::"r" (1 << idx));
+}
+
+static inline void armv7_pmu_disable_intr(unsigned int idx)
+{
+	asm volatile ("mcr p15, 0, %0, c9, c14, 2"::"r" (1 << idx));
+}
+
+static inline unsigned int armv7_pmu_overflow(void)
+{
+	unsigned int val;
+
+	asm volatile ("mrc p15, 0, %0, c9, c12, 3":"=r" (val));	/* read */
+	asm volatile ("mcr p15, 0, %0, c9, c12, 3"::"r" (val));
+	return val;
+}
+
+static inline unsigned int armv7_pmu_control_read(void)
+{
+	u32 val;
+
+	asm volatile ("mrc p15, 0, %0, c9, c12, 0":"=r" (val));
+	return val;
+}
+
+static inline void armv7_pmu_control_write(unsigned int val)
+{
+	val &= ARMV7_PMCR_MASK;
+	isb();
+	asm volatile ("mcr p15, 0, %0, c9, c12, 0"::"r" (val));
+}
+
+static int armv7_pmu_hw_get_counters(void)
+{
+	int count = armv7_pmu_control_read();
+	/* N, bits[15:11] */
+	count = ((count >> ARMV7_PMCR_N_SHIFT) & ARMV7_PMCR_N_MASK);
+	return count;
+}
+
+static void armv7_pmu_hw_reset_all(int generic_counters)
+{
+	int i;
+
+	armv7_pmu_control_write(ARMV7_PMCR_C | ARMV7_PMCR_P);
+	/* generic counter */
+	for (i = 0; i < generic_counters; i++) {
+		armv7_pmu_disable_intr(i);
+		armv7_pmu_disable_count(i);
+	}
+	/* cycle counter */
+	armv7_pmu_disable_intr(31);
+	armv7_pmu_disable_count(31);
+	armv7_pmu_overflow();	/* clear overflow */
+}
+
+/***********************************
+ *      MET ARM v7 operations      *
+ ***********************************/
+enum ARM_TYPE {
+	CORTEX_A7 = 0xC07,
+	CORTEX_A9 = 0xC09,
+	CORTEX_A12 = 0xC0D,
+	CORTEX_A15 = 0xC0F,
+	CORTEX_A17 = 0xC0E,
+	CORTEX_A53 = 0xD03,
+	CORTEX_A57 = 0xD07,
+	CHIP_UNKNOWN = 0xFFF
+};
+
+struct chip_pmu {
+	enum ARM_TYPE type;
+};
+
+static struct chip_pmu chips[] = {
+	{CORTEX_A7},
+	{CORTEX_A9},
+	{CORTEX_A12},
+	{CORTEX_A15},
+	{CORTEX_A17},
+	{CORTEX_A53},
+	{CORTEX_A57},
+};
+
+static int armv7_pmu_hw_check_event(struct met_pmu *pmu, int idx, int event)
+{
+	int i;
+
+	/* Check if event is duplicate */
+	for (i = 0; i < idx; i++) {
+		if (pmu[i].event == event)
+			break;
+	}
+	if (i < idx) {
+		/* pr_debug("++++++ found duplicate event 0x%02x i=%d\n", event, i); */
+		return -1;
+	}
+
+	return 0;
+}
+
+static void armv7_pmu_hw_start(struct met_pmu *pmu, int count)
+{
+	int i;
+	int generic = count - 1;
+
+	armv7_pmu_hw_reset_all(generic);
+	for (i = 0; i < generic; i++) {
+		if (pmu[i].mode == MODE_POLLING) {
+			armv7_pmu_type_select(i, pmu[i].event);
+			armv7_pmu_enable_count(i);
+		}
+	}
+	if (pmu[count - 1].mode == MODE_POLLING) {	/* cycle counter */
+		armv7_pmu_enable_count(31);
+	}
+	armv7_pmu_control_write(ARMV7_PMCR_E);
+}
+
+static void armv7_pmu_hw_stop(int count)
+{
+	int generic = count - 1;
+
+	armv7_pmu_hw_reset_all(generic);
+}
+
+static unsigned int armv7_pmu_hw_polling(struct met_pmu *pmu, int count, unsigned int *pmu_value)
+{
+	int i, cnt = 0;
+	int generic = count - 1;
+
+	for (i = 0; i < generic; i++) {
+		if (pmu[i].mode == MODE_POLLING) {
+			pmu_value[cnt] = armv7_pmu_read_count(i);
+			cnt++;
+		}
+	}
+	if (pmu[count - 1].mode == MODE_POLLING) {
+		pmu_value[cnt] = armv7_pmu_read_count(31);
+		cnt++;
+	}
+	armv7_pmu_control_write(ARMV7_PMCR_C | ARMV7_PMCR_P | ARMV7_PMCR_E);
+
+	return cnt;
+}
+
+#define ARMV7_PMU_EVTYPE_EVENT 0xff
+
+static unsigned long armv7_perf_event_get_evttype(struct perf_event *ev) {
+
+	struct hw_perf_event *hwc;
+
+	hwc = &ev->hw;
+	return hwc->config_base & ARMV7_PMU_EVTYPE_EVENT;
+}
+
+#define	PMU_OVSR_MASK		0xffffffff     /* Mask for writable bits */
+
+static u32 armv7_pmu_read_clear_overflow_flag(void)
+{
+	u32 value;
+
+	asm volatile ("mrc p3, 3, %0, c9, c12, 3":"=r" (value));
+
+	/* Write to clear flags */
+	value &= PMU_OVSR_MASK;
+	asm volatile ("mcr p3, 3, %0, c9, c12, 3"::"r" (value));
+
+	return value;
+}
+
+static struct met_pmu	pmus[MXNR_CPU][MXNR_PMU_EVENTS];
+
+struct cpu_pmu_hw armv7_pmu = {
+	.name = "armv7_pmu",
+	.check_event = armv7_pmu_hw_check_event,
+	.start = armv7_pmu_hw_start,
+	.stop = armv7_pmu_hw_stop,
+	.polling = armv7_pmu_hw_polling,
+	.perf_event_get_evttype = armv7_perf_event_get_evttype,
+	.pmu_read_clear_overflow_flag = armv7_pmu_read_clear_overflow_flag,
+};
+
+struct cpu_pmu_hw *cpu_pmu_hw_init(void)
+{
+	int i;
+	enum ARM_TYPE type;
+	struct cpu_pmu_hw *pmu;
+
+	type = (enum ARM_TYPE)armv7_get_ic();
+	for (i = 0; i < ARRAY_SIZE(chips); i++)
+		if (chips[i].type == type)
+			break;
+
+	if (i < ARRAY_SIZE(chips)) {
+		armv7_pmu.nr_cnt = armv7_pmu_hw_get_counters() + 1;
+		pmu = &armv7_pmu;
+	} else
+		pmu = v6_cpu_pmu_hw_init(type);
+
+	if (pmu == NULL)
+		return NULL;
+
+	for (i = 0; i < MXNR_CPU; i++) {
+		pmu->event_count[i] = pmu->nr_cnt;
+		pmu->pmu[i] = pmus[i];
+	}
+
+	return pmu;
+}
diff --git a/src/devtools/met-driver/4.19/common/v8_dsu_hw.c b/src/devtools/met-driver/4.19/common/v8_dsu_hw.c
new file mode 100644
index 0000000..0c214eb
--- /dev/null
+++ b/src/devtools/met-driver/4.19/common/v8_dsu_hw.c
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2019 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <asm/cpu.h>
+#include "met_kernel_symbol.h"
+#include "cpu_dsu.h"
+
+//dsu support 6 event
+#define DSU_EVENT_MAX_CNT 6
+
+static int armv8_dsu_hw_check_event(struct met_dsu *pmu, int idx, int event)
+{
+	int i;
+
+	/* Check if event is duplicate */
+	for (i = 0; i < idx; i++) {
+		if (pmu[i].event == event)
+			break;
+	}
+	if (i < idx) {
+		/* pr_debug("++++++ found duplicate event 0x%02x i=%d\n", event, i); */
+		return -1;
+	}
+	return 0;
+}
+
+
+static struct met_dsu	pmus[MXNR_DSU_EVENTS];
+
+struct cpu_dsu_hw armv8_dsu = {
+	.name = "armv8_dsu",
+	.check_event = armv8_dsu_hw_check_event,
+};
+
+static void init_dsu(void)
+{
+	armv8_dsu.event_count = DSU_EVENT_MAX_CNT;
+}
+
+struct cpu_dsu_hw *cpu_dsu_hw_init(void)
+{
+
+	init_dsu();
+	armv8_dsu.pmu = pmus;
+	return &armv8_dsu;
+}
diff --git a/src/devtools/met-driver/4.19/common/v8_pmu_hw.c b/src/devtools/met-driver/4.19/common/v8_pmu_hw.c
new file mode 100644
index 0000000..41ef080
--- /dev/null
+++ b/src/devtools/met-driver/4.19/common/v8_pmu_hw.c
@@ -0,0 +1,289 @@
+/*
+ * Copyright (C) 2019 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/perf_event.h>
+#include <asm/cpu.h>
+#include "met_kernel_symbol.h"
+#include "cpu_pmu.h"
+
+/*******************************
+ *      ARM v8 operations      *
+ *******************************/
+/*
+ * Per-CPU PMCR: config reg
+ */
+#define ARMV8_PMCR_E		(1 << 0)	/* Enable all counters */
+#define ARMV8_PMCR_P		(1 << 1)	/* Reset all counters */
+#define ARMV8_PMCR_C		(1 << 2)	/* Cycle counter reset */
+#define ARMV8_PMCR_D		(1 << 3)	/* CCNT counts every 64th cpu cycle */
+#define ARMV8_PMCR_X		(1 << 4)	/* Export to ETM */
+#define ARMV8_PMCR_DP		(1 << 5)	/* Disable CCNT if non-invasive debug */
+#define	ARMV8_PMCR_N_SHIFT	11		/* Number of counters supported */
+#define	ARMV8_PMCR_N_MASK	0x1f
+#define	ARMV8_PMCR_MASK		0x3f		/* Mask for writable bits */
+
+/*
+ * PMOVSR: counters overflow flag status reg
+ */
+#define	ARMV8_OVSR_MASK		0xffffffff	/* Mask for writable bits */
+#define	ARMV8_OVERFLOWED_MASK	ARMV8_OVSR_MASK
+
+static inline void armv8_pmu_counter_select(unsigned int idx)
+{
+	asm volatile ("msr pmselr_el0, %0"::"r" (idx));
+	isb();
+}
+
+static inline void armv8_pmu_type_select(unsigned int idx, unsigned int type)
+{
+	armv8_pmu_counter_select(idx);
+	asm volatile ("msr pmxevtyper_el0, %0"::"r" (type));
+}
+
+static inline unsigned int armv8_pmu_read_count(unsigned int idx)
+{
+	unsigned int value;
+
+	if (idx == 31) {
+		asm volatile ("mrs %0, pmccntr_el0":"=r" (value));
+	} else {
+		armv8_pmu_counter_select(idx);
+		asm volatile ("mrs %0, pmxevcntr_el0":"=r" (value));
+	}
+	return value;
+}
+
+static inline void armv8_pmu_enable_count(unsigned int idx)
+{
+	asm volatile ("msr pmcntenset_el0, %0"::"r" (1 << idx));
+}
+
+static inline void armv8_pmu_disable_count(unsigned int idx)
+{
+	asm volatile ("msr pmcntenclr_el0, %0"::"r" (1 << idx));
+}
+
+static inline void armv8_pmu_enable_intr(unsigned int idx)
+{
+	asm volatile ("msr pmintenset_el1, %0"::"r" (1 << idx));
+}
+
+static inline void armv8_pmu_disable_intr(unsigned int idx)
+{
+	asm volatile ("msr pmintenclr_el1, %0"::"r" (1 << idx));
+	isb();
+	asm volatile ("msr pmovsclr_el0, %0"::"r" (1 << idx));
+	isb();
+}
+
+static inline unsigned int armv8_pmu_overflow(void)
+{
+	unsigned int val;
+
+	asm volatile ("mrs %0, pmovsclr_el0":"=r" (val));	/* read */
+	val &= ARMV8_OVSR_MASK;
+	asm volatile ("mrs %0, pmovsclr_el0"::"r" (val));
+	return val;
+}
+
+static inline unsigned int armv8_pmu_control_read(void)
+{
+	unsigned int val;
+
+	asm volatile ("mrs %0, pmcr_el0":"=r" (val));
+	return val;
+}
+
+static inline void armv8_pmu_control_write(u32 val)
+{
+	val &= ARMV8_PMCR_MASK;
+	isb();
+	asm volatile ("msr pmcr_el0, %0"::"r" (val));
+}
+
+static void armv8_pmu_hw_reset_all(int generic_counters)
+{
+	int i;
+
+	armv8_pmu_control_write(ARMV8_PMCR_C | ARMV8_PMCR_P);
+	/* generic counter */
+	for (i = 0; i < generic_counters; i++) {
+		armv8_pmu_disable_intr(i);
+		armv8_pmu_disable_count(i);
+	}
+	/* cycle counter */
+	armv8_pmu_disable_intr(31);
+	armv8_pmu_disable_count(31);
+	armv8_pmu_overflow();	/* clear overflow */
+}
+
+/***********************************
+ *      MET ARM v8 operations      *
+ ***********************************/
+enum ARM_TYPE {
+	CORTEX_A53 = 0xD03,
+	CORTEX_A35 = 0xD04,
+	CORTEX_A55 = 0xD05,
+	CORTEX_A57 = 0xD07,
+	CORTEX_A72 = 0xD08,
+	CORTEX_A73 = 0xD09,
+	CORTEX_A75 = 0xD0A,
+	CORTEX_A76 = 0xD0B,
+	CORTEX_A77 = 0xD0D,
+	CHIP_UNKNOWN = 0xFFF
+};
+
+struct chip_pmu {
+	enum ARM_TYPE	type;
+	unsigned int	event_count;
+};
+
+static struct chip_pmu	chips[] = {
+	{CORTEX_A35, 6+1},
+	{CORTEX_A53, 6+1},
+	{CORTEX_A55, 6+1},
+	{CORTEX_A57, 6+1},
+	{CORTEX_A72, 6+1},
+	{CORTEX_A73, 6+1},
+	{CORTEX_A75, 6+1},
+	{CORTEX_A76, 6+1},
+	{CORTEX_A77, 6+1},
+};
+
+static int armv8_pmu_hw_check_event(struct met_pmu *pmu, int idx, int event)
+{
+	int i;
+
+	/* Check if event is duplicate */
+	for (i = 0; i < idx; i++) {
+		if (pmu[i].event == event)
+			break;
+	}
+	if (i < idx) {
+		/* pr_debug("++++++ found duplicate event 0x%02x i=%d\n", event, i); */
+		return -1;
+	}
+
+	return 0;
+}
+
+static void armv8_pmu_hw_start(struct met_pmu *pmu, int count)
+{
+	int i;
+	int generic = count - 1;
+
+	armv8_pmu_hw_reset_all(generic);
+	for (i = 0; i < generic; i++) {
+		if (pmu[i].mode == MODE_POLLING) {
+			armv8_pmu_type_select(i, pmu[i].event);
+			armv8_pmu_enable_count(i);
+		}
+	}
+	if (pmu[count - 1].mode == MODE_POLLING) {	/* cycle counter */
+		armv8_pmu_enable_count(31);
+	}
+	armv8_pmu_control_write(ARMV8_PMCR_E);
+}
+
+static void armv8_pmu_hw_stop(int count)
+{
+	int generic = count - 1;
+
+	armv8_pmu_hw_reset_all(generic);
+}
+
+static unsigned int armv8_pmu_hw_polling(struct met_pmu *pmu, int count, unsigned int *pmu_value)
+{
+	int i, cnt = 0;
+	int generic = count - 1;
+
+	for (i = 0; i < generic; i++) {
+		if (pmu[i].mode == MODE_POLLING) {
+			pmu_value[cnt] = armv8_pmu_read_count(i);
+			cnt++;
+		}
+	}
+	if (pmu[count - 1].mode == MODE_POLLING) {
+		pmu_value[cnt] = armv8_pmu_read_count(31);
+		cnt++;
+	}
+	armv8_pmu_control_write(ARMV8_PMCR_C | ARMV8_PMCR_P | ARMV8_PMCR_E);
+
+	return cnt;
+}
+
+static unsigned long armv8_perf_event_get_evttype(struct perf_event *ev) {
+
+	struct hw_perf_event *hwc;
+
+	hwc = &ev->hw;
+	return hwc->config_base & ARMV8_PMU_EVTYPE_EVENT;
+}
+
+#define	PMU_OVSR_MASK		0xffffffff     /* Mask for writable bits */
+
+static u32 armv8_pmu_read_clear_overflow_flag(void)
+{
+	u32 value;
+
+	asm volatile ("mrs %0, pmovsclr_el0":"=r" (value));
+
+	/* Write to clear flags */
+	value &= PMU_OVSR_MASK;
+	asm volatile ("msr pmovsclr_el0, %0"::"r" (value));
+
+	return value;
+}
+
+static struct met_pmu	pmus[MXNR_CPU][MXNR_PMU_EVENTS];
+
+struct cpu_pmu_hw armv8_pmu = {
+	.name = "armv8_pmu",
+	.check_event = armv8_pmu_hw_check_event,
+	.start = armv8_pmu_hw_start,
+	.stop = armv8_pmu_hw_stop,
+	.polling = armv8_pmu_hw_polling,
+	.perf_event_get_evttype = armv8_perf_event_get_evttype,
+	.pmu_read_clear_overflow_flag = armv8_pmu_read_clear_overflow_flag,
+};
+
+static void init_pmus(void)
+{
+	int	cpu;
+	int	i;
+
+	for_each_possible_cpu(cpu) {
+		struct cpuinfo_arm64 *cpuinfo;
+		if (cpu >= MXNR_CPU)
+			continue;
+		met_get_cpuinfo_symbol(cpu, &cpuinfo);
+		/* PR_BOOTMSG("CPU[%d]: reg_midr = %x\n", cpu, cpuinfo->reg_midr); */
+		for (i = 0; i < ARRAY_SIZE(chips); i++) {
+			if (chips[i].type == (cpuinfo->reg_midr & 0xffff) >> 4) {
+				armv8_pmu.event_count[cpu] = chips[i].event_count;
+				break;
+			}
+		}
+	}
+}
+
+struct cpu_pmu_hw *cpu_pmu_hw_init(void)
+{
+	int	cpu;
+
+	init_pmus();
+	for (cpu = 0; cpu < MXNR_CPU; cpu++)
+		armv8_pmu.pmu[cpu] = pmus[cpu];
+
+	return &armv8_pmu;
+}
diff --git a/src/devtools/met-driver/4.19/common/version.h b/src/devtools/met-driver/4.19/common/version.h
new file mode 100644
index 0000000..0395991
--- /dev/null
+++ b/src/devtools/met-driver/4.19/common/version.h
@@ -0,0 +1,14 @@
+/*
+ * Copyright (C) 2019 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define MET_BACKEND_VERSION "6.2.0"
diff --git a/src/devtools/met-driver/4.19/default/Kbuild b/src/devtools/met-driver/4.19/default/Kbuild
new file mode 100644
index 0000000..9b2bb87
--- /dev/null
+++ b/src/devtools/met-driver/4.19/default/Kbuild
@@ -0,0 +1,6 @@
+obj-m := met.o
+
+ccflags-y += -I$(srctree)/include/
+
+met-y := default/met_main.o
+
diff --git a/src/devtools/met-driver/4.19/default/met_main.c b/src/devtools/met-driver/4.19/default/met_main.c
new file mode 100644
index 0000000..6537e38
--- /dev/null
+++ b/src/devtools/met-driver/4.19/default/met_main.c
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/hrtimer.h>
+#include <linux/cpu.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/profile.h>
+#include <linux/dcache.h>
+#include <linux/types.h>
+#include <linux/dcookies.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+
+static int __init met_drv_init(void)
+{
+	pr_info("Hello MET default module\n");
+	return 0;
+}
+
+static void __exit met_drv_exit(void)
+{
+}
+module_init(met_drv_init);
+module_exit(met_drv_exit);
+
+MODULE_AUTHOR("DT_DM5");
+MODULE_DESCRIPTION("MET_DEFAULT");
+MODULE_LICENSE("GPL");
diff --git a/src/devtools/met-driver/4.19/mt2712/Kbuild b/src/devtools/met-driver/4.19/mt2712/Kbuild
new file mode 100644
index 0000000..be6faad
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/Kbuild
@@ -0,0 +1,64 @@
+MET_PLF := $(MTK_PLATFORM)
+
+#ccflags-y += -DMET_CHIP_USE
+
+met-y := $(MET_PLF)/met_main.o \
+         $(MET_PLF)/interface.o \
+         $(MET_PLF)/sampler.o \
+         $(MET_PLF)/util.o \
+         $(MET_PLF)/core_plf_init.o \
+         $(MET_PLF)/core_plf_trace.o \
+         $(MET_PLF)/cookie.o \
+         $(MET_PLF)/ondiemet.o \
+         $(MET_PLF)/ondiemet_log.o \
+         $(MET_PLF)/sspm/ondiemet_sspm.o
+
+CFLAGS_interface.o :=
+#CFLAGS_interface.o += -DMET_USER_EVENT_SUPPORT
+#CFLAGS_met_tag_ex.o += -DMET_USER_EVENT_SUPPORT
+
+$(info ARCH = $(ARCH))
+ifeq ($(ARCH), mips)
+met-y += $(MET_PLF)/mips_pmu_hw.o
+endif #ifeq ($(ARCH), mips)
+
+ifeq ($(ARCH), arm)
+ccflags-y += -DCONFIG_MET_ARM_32BIT
+met-y += $(MET_PLF)/cpu_pmu.o
+met-y += $(MET_PLF)/v7_pmu_hw.o
+met-y += $(MET_PLF)/v6_pmu_hw.o
+endif #ifeq ($(ARCH), arm)
+
+ifeq ($(ARCH), arm64)
+ccflags-y += -DMET_SUPPORT_CPUPMU_V2
+met-y += $(MET_PLF)/cpu_pmu.o
+met-y += $(MET_PLF)/v8_pmu_hw.o
+met-y += $(MET_PLF)/cpu_pmu_v2.o
+met-y += $(MET_PLF)/v8_pmu_hw_v2.o
+endif
+
+$(info CONFIG_CPU_FREQ = $(CONFIG_CPU_FREQ))
+ifeq ($(CONFIG_CPU_FREQ),y)
+    met-y += $(MET_PLF)/power.o
+endif
+
+
+################################################################################
+# MET_AP_EMI
+################################################################################
+FEATURE_AP_EMI := $(if $(FEATURE_AP_EMI),$(FEATURE_AP_EMI),y)
+$(info FEATURE_AP_EMI = $(FEATURE_AP_EMI))
+ifneq ($(FEATURE_AP_EMI), n)
+    MET_AP_EMI := y
+else
+    MET_AP_EMI := n
+endif
+
+met-$(MET_AP_EMI) += $(MET_PLF)/met_emi.o $(MET_PLF)/mtk_emi_bm.o
+
+################################################################################
+# MET_GPU
+################################################################################
+MET_GPU := y
+met-y += $(MET_PLF)/mtk_gpu_metmonitor.o
+
diff --git a/src/devtools/met-driver/4.19/mt2712/Kbuild.platform.h b/src/devtools/met-driver/4.19/mt2712/Kbuild.platform.h
new file mode 100644
index 0000000..73845f1
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/Kbuild.platform.h
@@ -0,0 +1,34 @@
+################################################################################
+# Include Path
+################################################################################
+MET_VCOREDVFS_INC := $(KERNEL_SRC)/drivers/misc/mediatek/base/power/include/vcorefs_v3
+MET_PTPOD_INC := $(KERNEL_SRC)/drivers/misc/mediatek/base/power/cpufreq_v1/src/mach/$(MTK_PLATFORM)/
+
+
+################################################################################
+# Feature Spec
+# CPUPMU_VERSION: V8_0/V8_2
+# EMI_SEDA_VERSION: SEDA2/SEDA3/SEDA3_5
+# EMI_DRAMC_VERSION: V1/V2
+################################################################################
+CPUPMU_LEGACY := y
+CPUPMU_VERSION := V8_0
+EMI_SEDA_VERSION := SEDA3_5
+EMI_DRAMC_VERSION := V2
+
+
+################################################################################
+# Feature On/Off
+################################################################################
+FEATURE_SPMTWAM := n
+FEATURE_CPUDSU := n
+FEATURE_SSPM_EMI := n
+FEATURE_AP_EMI := y
+FEATURE_GPU := n
+FEATURE_VCOREDVFS := n
+FEATURE_PTPOD := n
+FEATURE_WALLTIME := n
+FEATURE_SSPM_SMI := n
+FEATURE_EVENT_POWER := n
+FEATURE_ONDIEMET := n
+
diff --git a/src/devtools/met-driver/4.19/mt2712/Kbuild.yocto b/src/devtools/met-driver/4.19/mt2712/Kbuild.yocto
new file mode 100644
index 0000000..be6faad
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/Kbuild.yocto
@@ -0,0 +1,64 @@
+MET_PLF := $(MTK_PLATFORM)
+
+#ccflags-y += -DMET_CHIP_USE
+
+met-y := $(MET_PLF)/met_main.o \
+         $(MET_PLF)/interface.o \
+         $(MET_PLF)/sampler.o \
+         $(MET_PLF)/util.o \
+         $(MET_PLF)/core_plf_init.o \
+         $(MET_PLF)/core_plf_trace.o \
+         $(MET_PLF)/cookie.o \
+         $(MET_PLF)/ondiemet.o \
+         $(MET_PLF)/ondiemet_log.o \
+         $(MET_PLF)/sspm/ondiemet_sspm.o
+
+CFLAGS_interface.o :=
+#CFLAGS_interface.o += -DMET_USER_EVENT_SUPPORT
+#CFLAGS_met_tag_ex.o += -DMET_USER_EVENT_SUPPORT
+
+$(info ARCH = $(ARCH))
+ifeq ($(ARCH), mips)
+met-y += $(MET_PLF)/mips_pmu_hw.o
+endif #ifeq ($(ARCH), mips)
+
+ifeq ($(ARCH), arm)
+ccflags-y += -DCONFIG_MET_ARM_32BIT
+met-y += $(MET_PLF)/cpu_pmu.o
+met-y += $(MET_PLF)/v7_pmu_hw.o
+met-y += $(MET_PLF)/v6_pmu_hw.o
+endif #ifeq ($(ARCH), arm)
+
+ifeq ($(ARCH), arm64)
+ccflags-y += -DMET_SUPPORT_CPUPMU_V2
+met-y += $(MET_PLF)/cpu_pmu.o
+met-y += $(MET_PLF)/v8_pmu_hw.o
+met-y += $(MET_PLF)/cpu_pmu_v2.o
+met-y += $(MET_PLF)/v8_pmu_hw_v2.o
+endif
+
+$(info CONFIG_CPU_FREQ = $(CONFIG_CPU_FREQ))
+ifeq ($(CONFIG_CPU_FREQ),y)
+    met-y += $(MET_PLF)/power.o
+endif
+
+
+################################################################################
+# MET_AP_EMI
+################################################################################
+FEATURE_AP_EMI := $(if $(FEATURE_AP_EMI),$(FEATURE_AP_EMI),y)
+$(info FEATURE_AP_EMI = $(FEATURE_AP_EMI))
+ifneq ($(FEATURE_AP_EMI), n)
+    MET_AP_EMI := y
+else
+    MET_AP_EMI := n
+endif
+
+met-$(MET_AP_EMI) += $(MET_PLF)/met_emi.o $(MET_PLF)/mtk_emi_bm.o
+
+################################################################################
+# MET_GPU
+################################################################################
+MET_GPU := y
+met-y += $(MET_PLF)/mtk_gpu_metmonitor.o
+
diff --git a/src/devtools/met-driver/4.19/mt2712/cookie.c b/src/devtools/met-driver/4.19/mt2712/cookie.c
new file mode 100644
index 0000000..2bdb711
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/cookie.c
@@ -0,0 +1,259 @@
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/cpu.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <asm/irq_regs.h>
+#include <asm/stacktrace.h>
+#include <linux/stacktrace.h>
+#include "interface.h"
+#include "met_drv.h"
+
+#define LINE_SIZE	256
+
+struct cookie_info {
+	int depth;
+	int strlen;
+	char strbuf[LINE_SIZE];
+};
+
+static unsigned int back_trace_depth;
+static DEFINE_PER_CPU(struct cookie_info, info);
+
+static int reset_driver_stat(void)
+{
+	back_trace_depth = 0;
+	met_cookie.mode = 0;
+	return 0;
+}
+
+
+noinline void cookie(char *strbuf)
+{
+	MET_TRACE("%s\n", strbuf);
+}
+
+noinline void cookie2(char *strbuf)
+{
+	MET_TRACE("%s\n", strbuf);
+}
+
+static void get_kernel_cookie(unsigned long pc, struct cookie_info *pinfo)
+{
+	int ret;
+
+#ifdef CONFIG_MODULES
+	off_t off;
+	struct module *mod = __module_address(pc);
+
+	if (mod) {
+		if (within_module_core(pc, mod)) {
+			off = pc - (unsigned long)mod->core_layout.base;
+			ret = snprintf(pinfo->strbuf + pinfo->strlen, LINE_SIZE - pinfo->strlen,
+					",%s,%lx", mod->name, off);
+			pinfo->strlen += ret;
+			/* cookie(current->comm, pc, mod->name, off, 1); */
+		} else
+			MET_TRACE("Error: pc (0x%lx) is not in module range\n", pc);
+	} else
+#endif
+	{
+		ret =
+		    snprintf(pinfo->strbuf + pinfo->strlen, LINE_SIZE - pinfo->strlen,
+			     ",vmlinux,%lx", pc);
+		pinfo->strlen += ret;
+		/* cookie(current->comm, pc, "vmlinux", pc, 0); */
+	}
+}
+
+#if defined(__arm__)
+static int report_trace(struct stackframe *frame, void *d)
+{
+	struct cookie_info *pinfo = d;
+	unsigned long pc = frame->pc;
+
+	if (pinfo->depth > 0) {
+		get_kernel_cookie(pc, pinfo);
+		pinfo->depth--;
+		return 0;
+	}
+	return 1;
+}
+#endif
+
+static void kernel_backtrace(struct pt_regs *const regs, struct cookie_info *pinfo)
+{
+#if defined(__arm__)
+	struct stackframe frame;
+
+	frame.fp = regs->ARM_fp;
+	frame.sp = regs->ARM_sp;
+	frame.lr = regs->ARM_lr;
+	frame.pc = regs->ARM_pc;
+	walk_stackframe(&frame, report_trace, pinfo);
+#else
+	return;
+#endif
+}
+
+
+void met_cookie_polling(unsigned long long stamp, int cpu)
+{
+	struct pt_regs *regs;
+	struct cookie_info *pinfo;
+	unsigned long pc;
+	int ret, outflag = 0;
+	off_t off;
+
+	regs = get_irq_regs();
+
+	if (regs == 0)
+		return;
+
+	pc = profile_pc(regs);
+
+	pinfo = &(per_cpu(info, cpu));
+	pinfo->strlen = snprintf(pinfo->strbuf, LINE_SIZE, "%s,%lx", current->comm, pc);
+
+	if (user_mode(regs)) {
+		struct mm_struct *mm;
+		struct vm_area_struct *vma;
+		struct path *ppath;
+
+		mm = current->mm;
+		for (vma = find_vma(mm, pc); vma; vma = vma->vm_next) {
+
+			if (pc < vma->vm_start || pc >= vma->vm_end)
+				continue;
+
+			if (vma->vm_file) {
+				ppath = &(vma->vm_file->f_path);
+
+				if (vma->vm_flags & VM_DENYWRITE)
+					off = pc;
+				else
+					off = (vma->vm_pgoff << PAGE_SHIFT) + pc - vma->vm_start;
+
+				ret =
+				    snprintf(pinfo->strbuf + pinfo->strlen,
+					     LINE_SIZE - pinfo->strlen, ",%s,%lx",
+					     (char *)(ppath->dentry->d_name.name), off);
+				pinfo->strlen += ret;
+				outflag = 1;
+			} else {
+				/* must be an anonymous map */
+				ret =
+				    snprintf(pinfo->strbuf + pinfo->strlen,
+					     LINE_SIZE - pinfo->strlen, ",nofile,%lx", pc);
+				pinfo->strlen += ret;
+				outflag = 1;
+			}
+			break;
+		}
+	} else {
+		/* kernel mode code */
+		if (back_trace_depth > 0) {
+			pinfo->depth = back_trace_depth + 1;
+			kernel_backtrace(regs, pinfo);
+		} else
+			get_kernel_cookie(pc, pinfo);
+		outflag = 1;
+	}
+
+	/* check task is resolvable */
+	if (outflag == 0)
+		return;
+
+	if (back_trace_depth == 0)
+		cookie(pinfo->strbuf);
+	else
+		cookie2(pinfo->strbuf);
+}
+
+
+static void met_cookie_start(void)
+{
+	/* return; */
+}
+
+static void met_cookie_stop(void)
+{
+	/* return; */
+}
+
+
+static int met_cookie_process_argument(const char *arg, int len)
+{
+	unsigned int value;
+
+	if (met_parse_num(arg, &value, len) < 0) {
+		met_cookie.mode = 0;
+		return -EINVAL;
+	}
+
+	back_trace_depth = value;
+	met_cookie.mode = 1;
+
+	return 0;
+}
+
+static const char help[] =
+"  --cookie                              enable sampling task and PC\n"
+"  --cookie=N                            enable back trace (depth is N)\n";
+
+static int met_cookie_print_help(char *buf, int len)
+{
+	len = snprintf(buf, PAGE_SIZE, help);
+	return len;
+}
+
+
+static const char header[] =
+"# cookie: task_name,PC,cookie_name,offset\n"
+"met-info [000] 0.0: cookie_header: task_name,PC,cookie_name,offset\n";
+
+static const char header2_1[] = "# cookie2: task_name,PC,cookie,offset";
+static const char header2_2[] = "met-info [000] 0.0: cookie2_header: task_name,PC,cookie,offset";
+
+static int met_cookie_print_header(char *buf, int len)
+{
+	int i, ret;
+
+	if (back_trace_depth == 0) {
+		len = snprintf(buf, PAGE_SIZE, header);
+	} else {
+		len = snprintf(buf, PAGE_SIZE, header2_1);
+		for (i = 0; i < back_trace_depth; i++) {
+			ret = snprintf(buf + len, PAGE_SIZE, ",cookie%d,offset%d", i + 1, i + 1);
+			len += ret;
+		}
+		ret = snprintf(buf + len, PAGE_SIZE, "\n");
+		len += ret;
+
+		ret = snprintf(buf + len, PAGE_SIZE, header2_2);
+		len += ret;
+		for (i = 0; i < back_trace_depth; i++) {
+			ret = snprintf(buf + len, PAGE_SIZE, ",cookie%d,offset%d", i + 1, i + 1);
+			len += ret;
+		}
+		ret = snprintf(buf + len, PAGE_SIZE, "\n");
+		len += ret;
+	}
+
+	return len;
+}
+
+struct metdevice met_cookie = {
+	.name = "cookie",
+	.type = MET_TYPE_PMU,
+	.cpu_related = 1,
+	.start = met_cookie_start,
+	.stop = met_cookie_stop,
+	.reset = reset_driver_stat,
+	.polling_interval = 1,
+	.timed_polling = met_cookie_polling,
+	.process_argument = met_cookie_process_argument,
+	.print_help = met_cookie_print_help,
+	.print_header = met_cookie_print_header,
+};
diff --git a/src/devtools/met-driver/4.19/mt2712/core_plf_init.c b/src/devtools/met-driver/4.19/mt2712/core_plf_init.c
new file mode 100644
index 0000000..a6f86ca
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/core_plf_init.c
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/kallsyms.h>
+#include "met_drv.h"
+#include "met_api_tbl.h"
+#include "interface.h"
+#include "core_plf_init.h"
+
+#undef	DEBUG
+
+#ifdef MET_AP_EMI
+/*
+ *   EMI Monitor
+ */
+void *(*mt_cen_emi_base_get_symbol)(void);
+void *(*mt_chn_emi_base_get_symbol)(int chn);
+unsigned int (*get_dram_data_rate_symbol)(void);
+#if 0 /* New APIs for mt_dramc_nao base get */
+void *(*mt_dramc_nao_cha_base_get_symbol)(void);
+void *(*mt_dramc_nao_chb_base_get_symbol)(void);
+void *(*mt_dramc_nao_chc_base_get_symbol)(void);
+void *(*mt_dramc_nao_chd_base_get_symbol)(void);
+void *(*mt_ddrphy_cha_base_get_symbol)(void);
+void *(*mt_dramc_cha_base_get_symbol)(void);
+#else
+void *(*mt_dramc_nao_chn_base_get_symbol)(int channel);
+void *(*mt_ddrphy_chn_base_get_symbol)(int channel);
+void *(*mt_dramc_chn_base_get_symbol)(int channel);
+#endif
+
+/*
+ *   DRAM
+ */
+void (*mt_dramfreq_setfreq_registerCB_symbol)(dram_sampler_func pCB);
+#endif /* MET_AP_EMI */
+
+#ifdef MET_GPU
+/*
+ *   GPU
+ */
+int (*mtk_get_gpu_loading_symbol)(unsigned int *pLoading);
+int (*mtk_get_gpu_freq_symbol)(unsigned int *pFreq);
+unsigned int (*mtk_get_gpu_memory_usage_symbol)(unsigned int *pMemUsage);
+#endif /* MET_GPU */
+
+static int met_symbol_get(void)
+{
+#define _MET_SYMBOL_GET(_func_name_) \
+	do { \
+		_func_name_##_symbol = (void *)symbol_get(_func_name_); \
+		if (_func_name_##_symbol == NULL) { \
+			pr_debug("MET ext. symbol : %s is not found!\n", #_func_name_); \
+			PR_BOOTMSG_ONCE("MET ext. symbol : %s is not found!\n", #_func_name_); \
+		} \
+	} while (0)
+
+#ifdef MET_AP_EMI
+    _MET_SYMBOL_GET(mt_cen_emi_base_get);
+    _MET_SYMBOL_GET(mt_chn_emi_base_get);
+    _MET_SYMBOL_GET(get_dram_data_rate);
+#if 0 /* New APIs for mt_dramc_nao base get */
+    _MET_SYMBOL_GET(mt_dramc_nao_cha_base_get);
+    _MET_SYMBOL_GET(mt_dramc_nao_chb_base_get);
+    _MET_SYMBOL_GET(mt_dramc_nao_chc_base_get);
+    _MET_SYMBOL_GET(mt_dramc_nao_chd_base_get);
+    _MET_SYMBOL_GET(mt_ddrphy_cha_base_get);
+    _MET_SYMBOL_GET(mt_dramc_cha_base_get);
+#else
+    _MET_SYMBOL_GET(mt_dramc_nao_chn_base_get);
+    _MET_SYMBOL_GET(mt_ddrphy_chn_base_get);
+    _MET_SYMBOL_GET(mt_dramc_chn_base_get);
+#endif
+    _MET_SYMBOL_GET(mt_dramfreq_setfreq_registerCB);
+#endif /* MET_AP_EMI */
+
+#ifdef MET_GPU
+	_MET_SYMBOL_GET(mtk_get_gpu_loading);
+	_MET_SYMBOL_GET(mtk_get_gpu_freq);
+	_MET_SYMBOL_GET(mtk_get_gpu_memory_usage);
+#endif /* MET_GPU */
+
+	return 0;
+}
+
+static int met_symbol_put(void)
+{
+#define _MET_SYMBOL_PUT(_func_name_) { \
+		if (_func_name_##_symbol) { \
+			symbol_put(_func_name_); \
+			_func_name_##_symbol = NULL; \
+		} \
+	}
+
+#ifdef MET_AP_EMI
+    _MET_SYMBOL_PUT(mt_cen_emi_base_get);
+    _MET_SYMBOL_PUT(mt_chn_emi_base_get);
+    _MET_SYMBOL_PUT(get_dram_data_rate);
+#if 0 /* New APIs for mt_dramc_nao base get */
+    _MET_SYMBOL_PUT(mt_dramc_nao_cha_base_get);
+    _MET_SYMBOL_PUT(mt_dramc_nao_chb_base_get);
+    _MET_SYMBOL_PUT(mt_dramc_nao_chc_base_get);
+    _MET_SYMBOL_PUT(mt_dramc_nao_chd_base_get);
+    _MET_SYMBOL_PUT(mt_ddrphy_cha_base_get);
+    _MET_SYMBOL_PUT(mt_dramc_cha_base_get);
+#else
+    _MET_SYMBOL_PUT(mt_dramc_nao_chn_base_get);
+    _MET_SYMBOL_PUT(mt_ddrphy_chn_base_get);
+    _MET_SYMBOL_PUT(mt_dramc_chn_base_get);
+#endif
+    _MET_SYMBOL_PUT(mt_dramfreq_setfreq_registerCB);
+#endif /* MET_AP_EMI */
+
+#ifdef MET_GPU
+	_MET_SYMBOL_PUT(mtk_get_gpu_loading);
+	_MET_SYMBOL_PUT(mtk_get_gpu_freq);
+	_MET_SYMBOL_PUT(mtk_get_gpu_memory_usage);
+#endif /* MET_GPU */
+
+	return 0;
+}
+
+int core_plf_init(void)
+{
+	/*initial met external symbol*/
+	met_symbol_get();
+
+#ifdef MET_AP_EMI
+    met_register(&met_emi);
+#endif
+
+#ifdef MET_GPU
+	met_register(&met_gpu);
+	met_register(&met_gpudvfs);
+	met_register(&met_gpumem);
+#endif
+
+	return 0;
+}
+
+void core_plf_exit(void)
+{
+	/*release met external symbol*/
+	met_symbol_put();
+
+#ifdef MET_AP_EMI
+    met_deregister(&met_emi);
+#endif
+
+#ifdef MET_GPU
+	met_deregister(&met_gpu);
+	met_deregister(&met_gpudvfs);
+	met_deregister(&met_gpumem);
+#endif
+}
diff --git a/src/devtools/met-driver/4.19/mt2712/core_plf_init.h b/src/devtools/met-driver/4.19/mt2712/core_plf_init.h
new file mode 100644
index 0000000..678ce75
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/core_plf_init.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __CORE_PLF_INIT_H__
+#define __CORE_PLF_INIT_H__
+
+extern struct miscdevice met_device;
+
+/*
+ *   MET External Symbol
+ */
+
+#ifdef MET_AP_EMI
+/*
+ *   EMI Monitor
+ */
+extern unsigned int get_dram_data_rate(void);       /* in Mhz */
+extern void *mt_cen_emi_base_get(void);
+extern void *mt_chn_emi_base_get(void);
+
+#if 0 /* New APIs for mt_dramc_nao base get */
+extern void *mt_dramc_nao_cha_base_get(void);
+extern void *mt_dramc_nao_chb_base_get(void);
+extern void *mt_dramc_nao_chc_base_get(void);
+extern void *mt_dramc_nao_chd_base_get(void);
+extern void *mt_ddrphy_cha_base_get(void);
+extern void *mt_dramc_cha_base_get(void);
+#else
+extern void *mt_dramc_nao_chn_base_get(int channel);
+extern void *mt_ddrphy_chn_base_get(int channel);
+extern void *mt_dramc_chn_base_get(int channel);
+#endif
+
+extern unsigned int (*get_dram_data_rate_symbol)(void); /* in Mhz */
+extern void *(*mt_cen_emi_base_get_symbol)(void);
+extern void *(*mt_chn_emi_base_get_symbol)(int chn);
+#if 0 /* New APIs for mt_dramc_nao base get */
+extern void *(*mt_dramc_nao_cha_base_get_symbol)(void);
+extern void *(*mt_dramc_nao_chb_base_get_symbol)(void);
+extern void *(*mt_dramc_nao_chc_base_get_symbol)(void);
+extern void *(*mt_dramc_nao_chd_base_get_symbol)(void);
+extern void *(*mt_ddrphy_cha_base_get_symbol)(void);
+extern void *(*mt_dramc_cha_base_get_symbol)(void);
+#else
+extern void *(*mt_dramc_nao_chn_base_get_symbol)(int channel);
+extern void *(*mt_ddrphy_chn_base_get_symbol)(int channel);
+extern void *(*mt_dramc_chn_base_get_symbol)(int channel);
+#endif
+
+/*
+ *   DRAM
+ */
+typedef void (*dram_sampler_func) (unsigned int);
+extern void (*mt_dramfreq_setfreq_registerCB_symbol)(dram_sampler_func pCB);
+
+/*mt_dramc.c do not have header file, declare here */
+extern void mt_dramfreq_setfreq_registerCB(dram_sampler_func pCB);
+
+extern struct metdevice met_emi;
+#endif
+
+#ifdef MET_GPU
+/*
+ *   GPU
+ */
+//#include <mtk_gpu_utility.h>
+//#include <mtk_gpufreq.h>
+
+extern int mtk_get_gpu_loading(unsigned int *pLoading);
+extern int mtk_get_gpu_freq(unsigned int *pFreq);
+extern unsigned int mtk_get_gpu_memory_usage(unsigned int *pMemUsage);
+
+extern int (*mtk_get_gpu_loading_symbol)(unsigned int *pLoading);
+extern int (*mtk_get_gpu_freq_symbol)(unsigned int *pFreq);
+extern unsigned int (*mtk_get_gpu_memory_usage_symbol)(unsigned int *pMemUsage);
+
+extern struct metdevice met_gpu;
+extern struct metdevice met_gpudvfs;
+extern struct metdevice met_gpumem;
+#endif /* MET_GPU */
+
+#endif /*__CORE_PLF_INIT_H__*/
diff --git a/src/devtools/met-driver/4.19/mt2712/core_plf_trace.c b/src/devtools/met-driver/4.19/mt2712/core_plf_trace.c
new file mode 100644
index 0000000..937f63b
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/core_plf_trace.c
@@ -0,0 +1,376 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+#include "met_drv.h"
+#include "interface.h"
+#include "trace.h"
+
+char *ms_formatH(char *__restrict__ buf, unsigned char cnt, unsigned int *__restrict__ value)
+{
+	char *s = buf;
+	int len;
+
+	if (cnt == 0) {
+		buf[0] = '\0';
+		return buf;
+	}
+
+	switch (cnt % 4) {
+	case 1:
+		len = sprintf(s, "%x", value[0]);
+		s += len;
+		value += 1;
+		cnt -= 1;
+		break;
+	case 2:
+		len = sprintf(s, "%x,%x", value[0], value[1]);
+		s += len;
+		value += 2;
+		cnt -= 2;
+		break;
+	case 3:
+		len = sprintf(s, "%x,%x,%x", value[0], value[1], value[2]);
+		s += len;
+		value += 3;
+		cnt -= 3;
+		break;
+	case 0:
+		len = sprintf(s, "%x,%x,%x,%x", value[0], value[1], value[2], value[3]);
+		s += len;
+		value += 4;
+		cnt -= 4;
+		break;
+	}
+
+	while (cnt) {
+		len = sprintf(s, ",%x,%x,%x,%x", value[0], value[1], value[2], value[3]);
+		s += len;
+		value += 4;
+		cnt -= 4;
+	}
+
+	s[0] = '\0';
+
+	return s;
+}
+EXPORT_SYMBOL(ms_formatH);
+
+char *ms_formatD(char *__restrict__ buf, unsigned char cnt, unsigned int *__restrict__ value)
+{
+	char *s = buf;
+	int len;
+
+	if (cnt == 0) {
+		buf[0] = '\0';
+		return buf;
+	}
+
+	switch (cnt % 4) {
+	case 1:
+		len = sprintf(s, "%u", value[0]);
+		s += len;
+		value += 1;
+		cnt -= 1;
+		break;
+	case 2:
+		len = sprintf(s, "%u,%u", value[0], value[1]);
+		s += len;
+		value += 2;
+		cnt -= 2;
+		break;
+	case 3:
+		len = sprintf(s, "%u,%u,%u", value[0], value[1], value[2]);
+		s += len;
+		value += 3;
+		cnt -= 3;
+		break;
+	case 0:
+		len = sprintf(s, "%u,%u,%u,%u", value[0], value[1], value[2], value[3]);
+		s += len;
+		value += 4;
+		cnt -= 4;
+		break;
+	}
+
+	while (cnt) {
+		len = sprintf(s, ",%u,%u,%u,%u", value[0], value[1], value[2], value[3]);
+		s += len;
+		value += 4;
+		cnt -= 4;
+	}
+
+	s[0] = '\0';
+
+	return s;
+}
+EXPORT_SYMBOL(ms_formatD);
+
+char *ms_formatH_ulong(char *__restrict__ buf, unsigned char cnt, unsigned long *__restrict__ value)
+{
+	char *s = buf;
+	int len;
+
+	if (cnt == 0) {
+		buf[0] = '\0';
+		return buf;
+	}
+
+	switch (cnt % 4) {
+	case 1:
+		len = sprintf(s, "%lx", value[0]);
+		s += len;
+		value += 1;
+		cnt -= 1;
+		break;
+	case 2:
+		len = sprintf(s, "%lx,%lx", value[0], value[1]);
+		s += len;
+		value += 2;
+		cnt -= 2;
+		break;
+	case 3:
+		len = sprintf(s, "%lx,%lx,%lx", value[0], value[1], value[2]);
+		s += len;
+		value += 3;
+		cnt -= 3;
+		break;
+	case 0:
+		len = sprintf(s, "%lx,%lx,%lx,%lx", value[0], value[1], value[2], value[3]);
+		s += len;
+		value += 4;
+		cnt -= 4;
+		break;
+	}
+
+	while (cnt) {
+		len = sprintf(s, ",%lx,%lx,%lx,%lx", value[0], value[1], value[2], value[3]);
+		s += len;
+		value += 4;
+		cnt -= 4;
+	}
+
+	s[0] = '\0';
+
+	return buf;
+}
+EXPORT_SYMBOL(ms_formatH_ulong);
+
+char *ms_formatD_ulong(char *__restrict__ buf, unsigned char cnt, unsigned long *__restrict__ value)
+{
+	char *s = buf;
+	int len;
+
+	if (cnt == 0) {
+		buf[0] = '\0';
+		return buf;
+	}
+
+	switch (cnt % 4) {
+	case 1:
+		len = sprintf(s, "%lu", value[0]);
+		s += len;
+		value += 1;
+		cnt -= 1;
+		break;
+	case 2:
+		len = sprintf(s, "%lu,%lu", value[0], value[1]);
+		s += len;
+		value += 2;
+		cnt -= 2;
+		break;
+	case 3:
+		len = sprintf(s, "%lu,%lu,%lu", value[0], value[1], value[2]);
+		s += len;
+		value += 3;
+		cnt -= 3;
+		break;
+	case 0:
+		len = sprintf(s, "%lu,%lu,%lu,%lu", value[0], value[1], value[2], value[3]);
+		s += len;
+		value += 4;
+		cnt -= 4;
+		break;
+	}
+
+	while (cnt) {
+		len = sprintf(s, ",%lu,%lu,%lu,%lu", value[0], value[1], value[2], value[3]);
+		s += len;
+		value += 4;
+		cnt -= 4;
+	}
+
+	s[0] = '\0';
+
+	return buf;
+}
+EXPORT_SYMBOL(ms_formatD_ulong);
+
+char *ms_formatH_EOL(char *__restrict__ buf, unsigned char cnt, unsigned int *__restrict__ value)
+{
+	char *s = buf;
+	int len;
+
+	if (cnt == 0) {
+		buf[0] = '\0';
+		return buf;
+	}
+
+	switch (cnt % 4) {
+	case 1:
+		len = sprintf(s, "%x", value[0]);
+		s += len;
+		value += 1;
+		cnt -= 1;
+		break;
+	case 2:
+		len = sprintf(s, "%x,%x", value[0], value[1]);
+		s += len;
+		value += 2;
+		cnt -= 2;
+		break;
+	case 3:
+		len = sprintf(s, "%x,%x,%x", value[0], value[1], value[2]);
+		s += len;
+		value += 3;
+		cnt -= 3;
+		break;
+	case 0:
+		len = sprintf(s, "%x,%x,%x,%x", value[0], value[1], value[2], value[3]);
+		s += len;
+		value += 4;
+		cnt -= 4;
+		break;
+	}
+
+	while (cnt) {
+		len = sprintf(s, ",%x,%x,%x,%x", value[0], value[1], value[2], value[3]);
+		s += len;
+		value += 4;
+		cnt -= 4;
+	}
+
+	s[0] = '\n';
+	s[1] = '\0';
+
+	return s + 1;
+}
+EXPORT_SYMBOL(ms_formatH_EOL);
+
+char *ms_formatD_EOL(char *__restrict__ buf, unsigned char cnt, unsigned int *__restrict__ value)
+{
+	char *s = buf;
+	int len;
+
+	if (cnt == 0) {
+		buf[0] = '\0';
+		return buf;
+	}
+
+	switch (cnt % 4) {
+	case 1:
+		len = sprintf(s, "%u", value[0]);
+		s += len;
+		value += 1;
+		cnt -= 1;
+		break;
+	case 2:
+		len = sprintf(s, "%u,%u", value[0], value[1]);
+		s += len;
+		value += 2;
+		cnt -= 2;
+		break;
+	case 3:
+		len = sprintf(s, "%u,%u,%u", value[0], value[1], value[2]);
+		s += len;
+		value += 3;
+		cnt -= 3;
+		break;
+	case 0:
+		len = sprintf(s, "%u,%u,%u,%u", value[0], value[1], value[2], value[3]);
+		s += len;
+		value += 4;
+		cnt -= 4;
+		break;
+	}
+
+	while (cnt) {
+		len = sprintf(s, ",%u,%u,%u,%u", value[0], value[1], value[2], value[3]);
+		s += len;
+		value += 4;
+		cnt -= 4;
+	}
+
+	s[0] = '\n';
+	s[1] = '\0';
+
+	return s + 1;
+}
+EXPORT_SYMBOL(ms_formatD_EOL);
+
+noinline void ms_bw_limiter(unsigned char cnt, unsigned int *value)
+{
+    char *SOB, *EOB;
+
+    MET_TRACE_GETBUF(&SOB, &EOB);
+    EOB = ms_formatH_EOL(EOB, cnt, value);
+    MET_TRACE_PUTBUF(SOB, EOB);
+}
+
+noinline void ms_emi(unsigned char cnt, unsigned int *value)
+{
+    char *SOB, *EOB;
+
+    MET_TRACE_GETBUF(&SOB, &EOB);
+    EOB = ms_formatH_EOL(EOB, cnt, value);
+    MET_TRACE_PUTBUF(SOB, EOB);
+}
+
+noinline void ms_emi_tsct(unsigned char cnt, unsigned int *value)
+{
+    char *SOB, *EOB;
+
+    MET_TRACE_GETBUF(&SOB, &EOB);
+    EOB = ms_formatH_EOL(EOB, cnt, value);
+    MET_TRACE_PUTBUF(SOB, EOB);
+}
+
+noinline void ms_emi_mdct(unsigned char cnt, unsigned int *value)
+{
+    char *SOB, *EOB;
+
+    MET_TRACE_GETBUF(&SOB, &EOB);
+    EOB = ms_formatH_EOL(EOB, cnt, value);
+    MET_TRACE_PUTBUF(SOB, EOB);
+}
+
+noinline void ms_ttype(unsigned char cnt, unsigned int *value)
+{
+    char *SOB, *EOB;
+
+    MET_TRACE_GETBUF(&SOB, &EOB);
+    EOB = ms_formatH_EOL(EOB, cnt, value);
+    MET_TRACE_PUTBUF(SOB, EOB);
+}
+
+noinline void ms_dramc(unsigned char cnt, unsigned int *value)
+{
+    char *SOB, *EOB;
+
+    MET_TRACE_GETBUF(&SOB, &EOB);
+    EOB = ms_formatH_EOL(EOB, cnt, value);
+    MET_TRACE_PUTBUF(SOB, EOB);
+}
+
diff --git a/src/devtools/met-driver/4.19/mt2712/core_plf_trace.h b/src/devtools/met-driver/4.19/mt2712/core_plf_trace.h
new file mode 100644
index 0000000..8dc1e6c
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/core_plf_trace.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _CORE_PLF_TRACE_H_
+#define _CORE_PLF_TRACE_H_
+
+#define	HVALUE_SIZE	9	/* 8 chars (max value ffffffff) + 1 char (',' or NULL) */
+#define	DVALUE_SIZE	12	/* 10 chars (max value 4,294,967,295) + 1 char (',' or NULL) */
+
+char *ms_formatH(char *__restrict__ buf, unsigned char cnt, unsigned int *__restrict__ value);
+char *core_ms_formatD(char *__restrict__ buf, unsigned char cnt, unsigned int *__restrict__ value);
+char *ms_formatH_ulong(char *__restrict__ buf, unsigned char cnt,
+		       unsigned long *__restrict__ value);
+char *ms_formatD_ulong(char *__restrict__ buf, unsigned char cnt,
+		       unsigned long *__restrict__ value);
+char *ms_formatH_EOL(char *__restrict__ buf, unsigned char cnt, unsigned int *__restrict__ value);
+char *ms_formatD_EOL(char *__restrict__ buf, unsigned char cnt, unsigned int *__restrict__ value);
+
+void ms_emi(unsigned char cnt, unsigned int *value);
+void ms_emi_tsct(unsigned char cnt, unsigned int *value);
+void ms_emi_mdct(unsigned char cnt, unsigned int *value);
+
+void ms_ttype(unsigned char cnt, unsigned int *value);
+void ms_bw_limiter(unsigned char cnt, unsigned int *value);
+
+void ms_dramc(unsigned char cnt, unsigned int *value);
+
+#endif	/* _CORE_PLF_TRACE_H_ */
diff --git a/src/devtools/met-driver/4.19/mt2712/cpu_pmu.c b/src/devtools/met-driver/4.19/mt2712/cpu_pmu.c
new file mode 100644
index 0000000..f0ec38d
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/cpu_pmu.c
@@ -0,0 +1,620 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/* include <asm/page.h> */
+#include <linux/slab.h>
+#include <linux/version.h>
+
+#include "interface.h"
+#include "trace.h"
+#include "cpu_pmu.h"
+#include "met_drv.h"
+
+#define MET_USER_EVENT_SUPPORT
+
+#include <linux/kthread.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/signal.h>
+#include <linux/workqueue.h>
+#include <linux/perf_event.h>
+
+#include "met_kernel_symbol.h"
+#include "interface.h"
+
+static int counter_cnt;
+
+struct metdevice met_cpupmu;
+struct cpu_pmu_hw *cpu_pmu;
+static int nr_counters;
+
+static struct kobject *kobj_cpu;
+static struct met_pmu *pmu;
+static int nr_arg;
+
+
+#define CNTMAX 8
+static DEFINE_PER_CPU(unsigned long long[CNTMAX], perfCurr);
+static DEFINE_PER_CPU(unsigned long long[CNTMAX], perfPrev);
+static DEFINE_PER_CPU(int[CNTMAX], perfCntFirst);
+static DEFINE_PER_CPU(struct perf_event * [CNTMAX], pevent);
+static DEFINE_PER_CPU(struct perf_event_attr [CNTMAX], pevent_attr);
+static DEFINE_PER_CPU(int, perfSet);
+static DEFINE_PER_CPU(unsigned int, perf_task_init_done);
+static DEFINE_PER_CPU(unsigned int, perf_cpuid);
+
+static DEFINE_PER_CPU(struct delayed_work, cpu_pmu_dwork);
+static DEFINE_PER_CPU(struct delayed_work *, perf_delayed_work_setup);
+
+static inline int reset_driver_stat(int counters)
+{
+	int i;
+
+	nr_arg = 0;
+	counter_cnt = 0;
+	met_cpupmu.mode = 0;
+	for (i = 0; i < counters; i++) {
+		pmu[i].mode = MODE_DISABLED;
+		pmu[i].event = 0;
+		pmu[i].freq = 0;
+	}
+
+	return 0;
+}
+
+static inline struct met_pmu *lookup_pmu(struct kobject *kobj)
+{
+	int i;
+
+	for (i = 0; i < nr_counters; i++) {
+		if (pmu[i].kobj_cpu_pmu == kobj)
+			return &pmu[i];
+	}
+	return NULL;
+}
+
+static ssize_t count_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%d\n", nr_counters - 1);
+}
+
+static ssize_t count_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t n)
+{
+	return -EINVAL;
+}
+
+static ssize_t event_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	struct met_pmu *p = lookup_pmu(kobj);
+
+	if (p != NULL)
+		return snprintf(buf, PAGE_SIZE, "0x%hx\n", p->event);
+
+	return -EINVAL;
+}
+
+static ssize_t event_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t n)
+{
+	struct met_pmu *p = lookup_pmu(kobj);
+	unsigned short event;
+
+	if (p != NULL) {
+		if (sscanf(buf, "0x%hx", &event) != 1)
+			return -EINVAL;
+
+		if (p == &(pmu[nr_counters - 1])) {	/* cycle counter */
+			if (event != 0xff)
+				return -EINVAL;
+		} else {
+			if (cpu_pmu->check_event(pmu, nr_arg, event) < 0)
+				return -EINVAL;
+		}
+
+		p->event = event;
+		return n;
+	}
+	return -EINVAL;
+}
+
+static ssize_t mode_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	struct met_pmu *p = lookup_pmu(kobj);
+
+	if (p != NULL) {
+		switch (p->mode) {
+		case 0:
+			return snprintf(buf, PAGE_SIZE, "%hhd (disabled)\n", p->mode);
+		case 1:
+			return snprintf(buf, PAGE_SIZE, "%hhd (interrupt)\n", p->mode);
+		case 2:
+			return snprintf(buf, PAGE_SIZE, "%hhd (polling)\n", p->mode);
+		}
+	}
+	return -EINVAL;
+}
+
+static ssize_t mode_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t n)
+{
+	unsigned int mode;
+	struct met_pmu *p = lookup_pmu(kobj);
+
+	if (p != NULL) {
+		if (kstrtouint(buf, 0, &mode) != 0)
+			return -EINVAL;
+
+		if (mode <= 2) {
+			p->mode = (unsigned char)mode;
+			if (mode > 0)
+				met_cpupmu.mode = 1;
+			return n;
+		}
+	}
+	return -EINVAL;
+}
+
+static ssize_t freq_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	struct met_pmu *p = lookup_pmu(kobj);
+
+	if (p != NULL)
+		return snprintf(buf, PAGE_SIZE, "%ld\n", p->freq);
+
+	return -EINVAL;
+}
+
+static ssize_t freq_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t n)
+{
+	struct met_pmu *p = lookup_pmu(kobj);
+
+	if (p != NULL) {
+		if (kstrtoul(buf, 0, &(p->freq)) != 0)
+			return -EINVAL;
+
+		return n;
+	}
+	return -EINVAL;
+}
+
+static struct kobj_attribute count_attr = __ATTR(count, 0664, count_show, count_store);
+static struct kobj_attribute event_attr = __ATTR(event, 0664, event_show, event_store);
+static struct kobj_attribute mode_attr = __ATTR(mode, 0664, mode_show, mode_store);
+static struct kobj_attribute freq_attr = __ATTR(freq, 0664, freq_show, freq_store);
+
+static int cpupmu_create_subfs(struct kobject *parent)
+{
+	int ret = 0;
+	int i;
+	char buf[16];
+
+	cpu_pmu = cpu_pmu_hw_init();
+	if (cpu_pmu == NULL) {
+		PR_BOOTMSG("Failed to init CPU PMU HW!!\n");
+		return -ENODEV;
+	}
+	nr_counters = cpu_pmu->nr_cnt;
+
+	pmu = kmalloc_array(nr_counters, sizeof(struct met_pmu), GFP_KERNEL);
+	if (pmu == NULL)
+		return -ENOMEM;
+
+	memset(pmu, 0, sizeof(struct met_pmu) * nr_counters);
+	cpu_pmu->pmu = pmu;
+	kobj_cpu = parent;
+
+	ret = sysfs_create_file(kobj_cpu, &count_attr.attr);
+	if (ret != 0) {
+		PR_BOOTMSG("Failed to create count in sysfs\n");
+		goto out;
+	}
+
+	for (i = 0; i < nr_counters; i++) {
+		snprintf(buf, sizeof(buf), "%d", i);
+		pmu[i].kobj_cpu_pmu = kobject_create_and_add(buf, kobj_cpu);
+
+		ret = sysfs_create_file(pmu[i].kobj_cpu_pmu, &event_attr.attr);
+		if (ret != 0) {
+			PR_BOOTMSG("Failed to create event in sysfs\n");
+			goto out;
+		}
+
+		ret = sysfs_create_file(pmu[i].kobj_cpu_pmu, &mode_attr.attr);
+		if (ret != 0) {
+			PR_BOOTMSG("Failed to create mode in sysfs\n");
+			goto out;
+		}
+
+		ret = sysfs_create_file(pmu[i].kobj_cpu_pmu, &freq_attr.attr);
+		if (ret != 0) {
+			PR_BOOTMSG("Failed to create freq in sysfs\n");
+			goto out;
+		}
+	}
+
+ out:
+	if (ret != 0) {
+		if (pmu != NULL) {
+			kfree(pmu);
+			pmu = NULL;
+		}
+	}
+	return ret;
+}
+
+static void cpupmu_delete_subfs(void)
+{
+	int i;
+
+	if (kobj_cpu != NULL) {
+		for (i = 0; i < nr_counters; i++) {
+			sysfs_remove_file(pmu[i].kobj_cpu_pmu, &event_attr.attr);
+			sysfs_remove_file(pmu[i].kobj_cpu_pmu, &mode_attr.attr);
+			sysfs_remove_file(pmu[i].kobj_cpu_pmu, &freq_attr.attr);
+			kobject_del(pmu[i].kobj_cpu_pmu);
+			kobject_put(pmu[i].kobj_cpu_pmu);
+			pmu[i].kobj_cpu_pmu = NULL;
+		}
+		sysfs_remove_file(kobj_cpu, &count_attr.attr);
+		kobj_cpu = NULL;
+	}
+
+	if (pmu != NULL) {
+		kfree(pmu);
+		pmu = NULL;
+	}
+
+	cpu_pmu  = NULL;
+}
+
+noinline void mp_cpu(unsigned char cnt, unsigned int *value)
+{
+	MET_GENERAL_PRINT(MET_TRACE, cnt, value);
+}
+
+static void dummy_handler(struct perf_event *event, struct perf_sample_data *data,
+			  struct pt_regs *regs)
+{
+/* Required as perf_event_create_kernel_counter() requires an overflow handler, even though all we do is poll */
+}
+
+void perf_cpupmu_polling(unsigned long long stamp, int cpu)
+{
+	int i, count;
+	long long int delta;
+	struct perf_event *ev;
+	unsigned int pmu_value[MXNR_CPU];
+	u64 value;
+
+	MET_TRACE("counter_cnt = %d\n", counter_cnt);
+	if (per_cpu(perfSet, cpu) == 0)
+		return;
+
+	memset(pmu_value, 0, sizeof(pmu_value));
+	count = 0;
+	for (i = 0; i < nr_counters; i++) {
+		if (pmu[i].mode == 0)
+			continue;
+
+		ev = per_cpu(pevent, cpu)[i];
+		if ((ev != NULL) && (ev->state == PERF_EVENT_STATE_ACTIVE)) {
+			met_perf_event_read_local_symbol(ev, &value, NULL, NULL);
+			per_cpu(perfCurr, cpu)[i] = value;
+			delta = (per_cpu(perfCurr, cpu)[i] - per_cpu(perfPrev, cpu)[i]);
+			per_cpu(perfPrev, cpu)[i] = per_cpu(perfCurr, cpu)[i];
+			if (per_cpu(perfCntFirst, cpu)[i] == 1) {
+				/* we shall omit delta counter when we get first counter */
+				per_cpu(perfCntFirst, cpu)[i] = 0;
+				continue;
+			}
+			pmu_value[i] = (unsigned int) delta;
+			count++;
+		}
+	}
+
+	MET_TRACE("count = %d, counter_cnt = %d\n", count, counter_cnt);
+
+	if (count == counter_cnt)
+		mp_cpu(count, pmu_value);
+}
+
+static int perf_thread_set_perf_events(unsigned int cpu)
+{
+	int i, size;
+	struct perf_event *ev;
+
+	size = sizeof(struct perf_event_attr);
+	if (per_cpu(perfSet, cpu) == 0) {
+		for (i = 0; i < nr_counters; i++) {
+			per_cpu(pevent, cpu)[i] = NULL;
+			if (!pmu[i].mode)	/* Skip disabled counters */
+				continue;
+
+			per_cpu(perfPrev, cpu)[i] = 0;
+			per_cpu(perfCurr, cpu)[i] = 0;
+			memset(&per_cpu(pevent_attr, cpu)[i], 0, size);
+			per_cpu(pevent_attr, cpu)[i].config = pmu[i].event;
+			per_cpu(pevent_attr, cpu)[i].type = PERF_TYPE_RAW;
+			per_cpu(pevent_attr, cpu)[i].size = size;
+			per_cpu(pevent_attr, cpu)[i].sample_period = 0;
+			per_cpu(pevent_attr, cpu)[i].pinned = 1;
+			if (pmu[i].event == 0xff) {
+				per_cpu(pevent_attr, cpu)[i].type = PERF_TYPE_HARDWARE;
+				per_cpu(pevent_attr, cpu)[i].config = PERF_COUNT_HW_CPU_CYCLES;
+			}
+
+			per_cpu(pevent, cpu)[i] =
+			    perf_event_create_kernel_counter(&per_cpu(pevent_attr, cpu)[i], cpu, NULL,
+				     dummy_handler, NULL);
+			if (IS_ERR(per_cpu(pevent, cpu)[i])) {
+				per_cpu(pevent, cpu)[i] = NULL;
+				PR_BOOTMSG("CPU=%d, %s:%d\n", cpu, __FUNCTION__, __LINE__);
+				continue;
+			}
+
+			if (per_cpu(pevent, cpu)[i]->state != PERF_EVENT_STATE_ACTIVE) {
+				perf_event_release_kernel(per_cpu(pevent, cpu)[i]);
+				per_cpu(pevent, cpu)[i] = NULL;
+				PR_BOOTMSG("CPU=%d, %s:%d\n", cpu, __FUNCTION__, __LINE__);
+				continue;
+			}
+
+			ev = per_cpu(pevent, cpu)[i];
+			if (ev != NULL)
+				perf_event_enable(ev);
+			per_cpu(perfCntFirst, cpu)[i] = 1;
+		} /* for all PMU counter */
+		per_cpu(perfSet, cpu) = 1;
+	} /* for perfSet */
+	return 0;
+}
+
+
+static void perf_thread_setup(struct work_struct *work)
+{
+	unsigned int cpu;
+	struct delayed_work *dwork = to_delayed_work(work);
+
+	cpu = dwork->cpu;
+	if (per_cpu(perf_task_init_done, cpu) == 0) {
+		per_cpu(perf_task_init_done, cpu) = 1;
+		perf_thread_set_perf_events(cpu);
+	}
+
+	return;
+}
+
+void met_perf_cpupmu_online(unsigned int cpu)
+{
+	if (met_cpupmu.mode == 0)
+		return;
+
+	per_cpu(perf_cpuid, cpu) = cpu;
+	if (per_cpu(perf_delayed_work_setup, cpu) == NULL) {
+		struct delayed_work *dwork;
+
+		dwork = &per_cpu(cpu_pmu_dwork, cpu);
+		dwork->cpu = cpu;
+		INIT_DELAYED_WORK(dwork, perf_thread_setup);
+		schedule_delayed_work(dwork, 0);
+		per_cpu(perf_delayed_work_setup, cpu) = dwork;
+	}
+}
+
+
+void met_perf_cpupmu_down(void *data)
+{
+	unsigned int cpu;
+	unsigned int i;
+	struct perf_event *ev;
+
+	cpu = *((unsigned int *)data);
+	if (met_cpupmu.mode == 0)
+		return;
+	if (per_cpu(perfSet, cpu) == 0)
+		return;
+	per_cpu(perfSet, cpu) = 0;
+	for (i = 0; i < nr_counters; i++) {
+		if (!pmu[i].mode)
+			continue;
+		ev = per_cpu(pevent, cpu)[i];
+		if ((ev != NULL) && (ev->state == PERF_EVENT_STATE_ACTIVE)) {
+			perf_event_disable(ev);
+			perf_event_release_kernel(ev);
+		}
+	}
+	per_cpu(perf_task_init_done, cpu) = 0;
+	per_cpu(perf_delayed_work_setup, cpu) = NULL;
+}
+
+void met_perf_cpupmu_stop(void)
+{
+	unsigned int cpu;
+
+	for_each_online_cpu(cpu) {
+		per_cpu(perf_cpuid, cpu) = cpu;
+		met_perf_cpupmu_down((void *)&per_cpu(perf_cpuid, cpu));
+	}
+}
+
+void met_perf_cpupmu_start(void)
+{
+	unsigned int cpu;
+
+	for_each_online_cpu(cpu) {
+		met_perf_cpupmu_online(cpu);
+	}
+}
+
+void cpupmu_polling(unsigned long long stamp, int cpu)
+{
+	int count;
+	unsigned int pmu_value[MXNR_CPU];
+
+	if (met_cpu_pmu_method == 0) {
+		count = cpu_pmu->polling(pmu, nr_counters, pmu_value);
+		mp_cpu(count, pmu_value);
+	} else {
+		perf_cpupmu_polling(stamp, cpu);
+	}
+}
+
+static void cpupmu_start(void)
+{
+	if (met_cpu_pmu_method == 0) {
+		nr_arg = 0;
+		cpu_pmu->start(pmu, nr_counters);
+	}
+}
+
+static void cpupmu_stop(void)
+{
+	if (met_cpu_pmu_method == 0)
+		cpu_pmu->stop(nr_counters);
+}
+
+static const char cache_line_header[] =
+	"met-info [000] 0.0: met_cpu_cache_line_size: %d\n";
+static const char header_n[] =
+	"# mp_cpu: pmu_value1, ...\n"
+	"met-info [000] 0.0: met_cpu_header: 0x%x:%s";
+static const char header[] =
+	"# mp_cpu: pmu_value1, ...\n"
+	"met-info [000] 0.0: met_cpu_header: 0x%x";
+
+static const char help[] =
+	"  --pmu-cpu-evt=EVENT                   select CPU-PMU events. in %s,\n"
+	"                                        you can enable at most \"%d general purpose events\"\n"
+	"                                        plus \"one special 0xff (CPU_CYCLE) event\"\n";
+
+static int cpupmu_print_help(char *buf, int len)
+{
+	return snprintf(buf, PAGE_SIZE, help, cpu_pmu->cpu_name, nr_counters - 1);
+}
+
+static int cpupmu_print_header(char *buf, int len)
+{
+	int i, ret, first;
+	char name[32];
+
+	first = 1;
+	ret = 0;
+
+	/*append CPU PMU access method*/
+	if (met_cpu_pmu_method == 0)
+		ret += snprintf(buf + ret, PAGE_SIZE,
+			"met-info [000] 0.0: CPU_PMU_method: PMU registers\n");
+	else
+		ret += snprintf(buf + ret, PAGE_SIZE,
+			"met-info [000] 0.0: CPU_PMU_method: perf APIs\n");
+
+	/*append cache line size*/
+	ret += snprintf(buf + ret, PAGE_SIZE - ret, cache_line_header, cache_line_size());
+
+	for (i = 0; i < nr_counters; i++) {
+		if (pmu[i].mode == 0)
+			continue;
+		if (cpu_pmu->get_event_desc && 0 == cpu_pmu->get_event_desc(i, pmu[i].event, name)) {
+			if (first) {
+				ret += snprintf(buf + ret, PAGE_SIZE - ret, header_n, pmu[i].event, name);
+				first = 0;
+			} else {
+				ret += snprintf(buf + ret, PAGE_SIZE - ret, ",0x%x:%s", pmu[i].event, name);
+			}
+		} else {
+			if (first) {
+				ret += snprintf(buf + ret, PAGE_SIZE - ret, header, pmu[i].event);
+				first = 0;
+			} else {
+				ret += snprintf(buf + ret, PAGE_SIZE - ret, ",0x%x", pmu[i].event);
+			}
+		}
+		pmu[i].mode = 0;
+
+	}
+
+	ret += snprintf(buf + ret, PAGE_SIZE - ret, "\n");
+	met_cpupmu.mode = 0;
+	reset_driver_stat(nr_counters);
+	nr_arg = 0;
+	return ret;
+}
+
+/*
+ * "met-cmd --start --pmu_cpu_evt=0x3"
+ */
+static int cpupmu_process_argument(const char *arg, int len)
+{
+	unsigned int value;
+
+	if (met_cpu_pmu_method == 0)
+		nr_counters = cpu_pmu->nr_cnt;
+	else
+		nr_counters = perf_num_counters();
+
+	if (nr_counters == 0)
+		goto arg_out;
+
+	if (met_parse_num(arg, &value, len) < 0)
+		goto arg_out;
+
+	if (cpu_pmu->check_event(pmu, nr_arg, value) < 0)
+		goto arg_out;
+
+	if (value == 0xff) {
+		if (met_cpu_pmu_method == 0) {
+			pmu[nr_counters - 1].mode = MODE_POLLING;
+			pmu[nr_counters - 1].event = 0xff;
+			pmu[nr_counters - 1].freq = 0;
+		} else {
+			if (nr_arg > (nr_counters - 1))
+				goto arg_out;
+
+			pmu[nr_arg].mode = MODE_POLLING;
+			pmu[nr_arg].event = value;
+			pmu[nr_arg].freq = 0;
+			nr_arg++;
+		}
+	} else {
+
+		if (nr_arg >= (nr_counters - 1))
+			goto arg_out;
+
+		pmu[nr_arg].mode = MODE_POLLING;
+		pmu[nr_arg].event = value;
+		pmu[nr_arg].freq = 0;
+		nr_arg++;
+	}
+	counter_cnt++;
+
+	met_cpupmu.mode = 1;
+	return 0;
+
+arg_out:
+	reset_driver_stat(nr_counters);
+	return -EINVAL;
+}
+
+struct metdevice met_cpupmu = {
+	.name = "cpu",
+	.type = MET_TYPE_PMU,
+	.cpu_related = 1,
+	.create_subfs = cpupmu_create_subfs,
+	.delete_subfs = cpupmu_delete_subfs,
+	.start = cpupmu_start,
+	.stop = cpupmu_stop,
+	.polling_interval = 1,
+	.timed_polling = cpupmu_polling,
+	.print_help = cpupmu_print_help,
+	.print_header = cpupmu_print_header,
+	.process_argument = cpupmu_process_argument
+};
diff --git a/src/devtools/met-driver/4.19/mt2712/cpu_pmu.h b/src/devtools/met-driver/4.19/mt2712/cpu_pmu.h
new file mode 100644
index 0000000..013a063
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/cpu_pmu.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _PMU_H_
+#define _PMU_H_
+
+#include <linux/device.h>
+
+#define MODE_DISABLED	0
+#define MODE_INTERRUPT	1
+#define MODE_POLLING	2
+
+#define MXSIZE_PMU_DESC 32
+#define MXNR_CPU 16
+
+struct met_pmu {
+	unsigned char mode;
+	unsigned short event;
+	unsigned long freq;
+	struct kobject *kobj_cpu_pmu;
+};
+
+struct cpu_pmu_hw {
+	const char *name;
+	const char *cpu_name;
+	int nr_cnt;
+	int (*get_event_desc)(int idx, int event, char *event_desc);
+	int (*check_event)(struct met_pmu *pmu, int idx, int event);
+	void (*start)(struct met_pmu *pmu, int count);
+	void (*stop)(int count);
+	unsigned int (*polling)(struct met_pmu *pmu, int count, unsigned int *pmu_value);
+	struct met_pmu *pmu;
+};
+
+struct pmu_desc {
+	unsigned int event;
+	char name[MXSIZE_PMU_DESC];
+};
+
+struct cpu_pmu_hw *cpu_pmu_hw_init(void);
+
+extern struct cpu_pmu_hw *cpu_pmu;
+extern noinline void mp_cpu(unsigned char cnt, unsigned int *value);
+
+extern void met_perf_cpupmu_start(void);
+extern void met_perf_cpupmu_stop(void);
+extern void met_perf_cpupmu_online(unsigned int cpu);
+extern void met_perf_cpupmu_down(void *cpu);
+extern void cpupmu_polling(unsigned long long stamp, int cpu);
+
+#endif				/* _PMU_H_ */
diff --git a/src/devtools/met-driver/4.19/mt2712/cpu_pmu_v2.c b/src/devtools/met-driver/4.19/mt2712/cpu_pmu_v2.c
new file mode 100644
index 0000000..26dcbfa
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/cpu_pmu_v2.c
@@ -0,0 +1,675 @@
+/*

+ * Copyright (C) 2018 MediaTek Inc.

+ *

+ * 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.

+ *

+ * This program is distributed in the hope that it will be useful,

+ * but WITHOUT ANY WARRANTY; without even the implied warranty of

+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the

+ * GNU General Public License for more details.

+ */

+

+#include <linux/slab.h>

+#include <linux/version.h>

+

+#include "interface.h"

+#include "trace.h"

+#include "cpu_pmu_v2.h"

+#include "v8_pmu_hw_v2.h"

+#include "met_drv.h"

+

+

+#define MET_USER_EVENT_SUPPORT

+

+#include <linux/kthread.h>

+#include <linux/kernel.h>

+#include <linux/sched.h>

+#include <linux/wait.h>

+#include <linux/signal.h>

+#include <linux/workqueue.h>

+#include <linux/perf_event.h>

+#include "met_kernel_symbol.h"

+

+

+/*******************************************************************************

+*				Type Define

+*******************************************************************************/

+#define CNTMAX 8

+

+

+/*******************************************************************************

+*				Fuction Pototypes

+*******************************************************************************/

+static inline struct met_pmu_v2 *get_met_pmu_by_cpu_id(const unsigned int cpu);

+static inline void set_met_pmu_by_cpu_id(const unsigned int cpu, struct met_pmu_v2 *met_pmu);

+

+static int reset_driver_stat(void);

+static struct met_pmu_v2 *lookup_pmu(struct kobject *kobj);

+

+static ssize_t mode_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf);

+

+static int cpupmu_create_subfs(struct kobject *parent);

+static void cpupmu_delete_subfs(void);

+static void _cpupmu_start(void *info);

+static void cpupmu_start(void);

+static void _cpupmu_stop(void *info);

+static void cpupmu_stop(void);

+static void cpupmu_polling(unsigned long long stamp, int cpu);

+extern void cpupmu_polling_v2(unsigned long long stamp, int cpu);

+static int cpupmu_print_help(char *buf, int len);

+static int cpupmu_print_header(char *buf, int len);

+static int cpupmu_process_argument(const char *arg, int len);

+

+

+/*******************************************************************************

+*				Globe Variables

+*******************************************************************************/

+static int module_status;

+

+struct cpu_pmu_hw_v2 *met_pmu_hw_v2;

+

+static unsigned int gPMU_CNT[2*MXNR_CPU_V2];

+static unsigned int gMAX_PMU_HW_CNT;

+

+static struct kobject *gKOBJ_CPU;

+static struct met_pmu_v2 *gMET_PMU[2*MXNR_CPU_V2];

+

+static struct kobj_attribute mode_attr = __ATTR(mode, 0444, mode_show, NULL);

+

+static const char cache_line_header[] =

+	"met-info [000] 0.0: met_cpu_cache_line_size: %d\n";

+static const char header[] =

+	"met-info [000] 0.0: met_cpu_header_v2: ";

+static const char help[] =

+	"  --cpu-pmu=CORE_ID:EVENT	     select CPU-PMU events. in %s,\n"

+	"				      you can enable at most \"%d general purpose events\"\n"

+	"				      plus \"one special 0xff (CPU_CYCLE) event\"\n";

+

+static DEFINE_PER_CPU(int[CNTMAX], perfCurr);

+static DEFINE_PER_CPU(int[CNTMAX], perfPrev);

+static DEFINE_PER_CPU(struct perf_event * [CNTMAX], pevent);

+static DEFINE_PER_CPU(struct perf_event_attr [CNTMAX], pevent_attr);

+static DEFINE_PER_CPU(int, perfSet);

+static DEFINE_PER_CPU(unsigned int, perf_task_init_done);

+static DEFINE_PER_CPU(unsigned int, perf_cpuid);

+

+static DEFINE_PER_CPU(struct delayed_work, cpu_pmu_dwork);

+static DEFINE_PER_CPU(struct delayed_work *, perf_delayed_work_setup);

+

+struct metdevice met_cpupmu_v2 = {

+	.name = "cpu-pmu",

+	.type = MET_TYPE_PMU,

+	.cpu_related = 1,

+	.create_subfs = cpupmu_create_subfs,

+	.delete_subfs = cpupmu_delete_subfs,

+	.start = cpupmu_start,

+	.stop = cpupmu_stop,

+	.polling_interval = 1,

+	.timed_polling = cpupmu_polling,

+	.print_help = cpupmu_print_help,

+	.print_header = cpupmu_print_header,

+	.process_argument = cpupmu_process_argument

+};

+

+

+/*******************************************************************************

+*				Iplement Start

+*******************************************************************************/

+static inline struct met_pmu_v2 *get_met_pmu_by_cpu_id(const unsigned int cpu)

+{

+	if (cpu < MXNR_CPU_V2)

+		return gMET_PMU[cpu];

+	else

+		return NULL;

+}

+

+

+static inline void set_met_pmu_by_cpu_id(const unsigned int cpu, struct met_pmu_v2 *met_pmu)

+{

+	if (cpu < MXNR_CPU_V2)

+		gMET_PMU[cpu] = met_pmu;

+}

+

+

+static int reset_driver_stat()

+{

+	int i;

+	int cpu;

+	struct met_pmu_v2 *met_pmu;

+

+	met_cpupmu_v2.mode = 0;

+	for_each_possible_cpu(cpu) {

+		met_pmu = get_met_pmu_by_cpu_id(cpu);

+		for (i = 0; i < gMAX_PMU_HW_CNT; i++) {

+			met_pmu[i].mode = MODE_DISABLED;

+			met_pmu[i].event = 0;

+		}

+		gPMU_CNT[cpu] = 0;

+	}

+	module_status = 0;

+	return 0;

+}

+

+

+static struct met_pmu_v2 *lookup_pmu(struct kobject *kobj)

+{

+	int i;

+	int cpu;

+	struct met_pmu_v2 *met_pmu;

+

+	for_each_possible_cpu(cpu) {

+		met_pmu = get_met_pmu_by_cpu_id(cpu);

+		for (i = 0; i < gMAX_PMU_HW_CNT; i++) {

+			if (met_pmu[i].kobj_cpu_pmu == kobj)

+				return &met_pmu[i];

+		}

+	}

+	return NULL;

+}

+

+

+static ssize_t mode_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)

+{

+	struct met_pmu_v2 *p = lookup_pmu(kobj);

+

+	if (p != NULL) {

+		switch (p->mode) {

+		case 0:

+			return snprintf(buf, PAGE_SIZE, "%hhd (disabled)\n", p->mode);

+		case 1:

+			return snprintf(buf, PAGE_SIZE, "%hhd (interrupt)\n", p->mode);

+		case 2:

+			return snprintf(buf, PAGE_SIZE, "%hhd (polling)\n", p->mode);

+		}

+	}

+	return -EINVAL;

+}

+

+

+static int cpupmu_create_subfs(struct kobject *parent)

+{

+	int ret = 0;

+	unsigned int i;

+	unsigned int cpu;

+	char buf[16];

+	struct met_pmu_v2 *met_pmu;

+

+	met_pmu_hw_v2 = cpu_pmu_hw_init_v2();

+	if (met_pmu_hw_v2 == NULL) {

+		PR_BOOTMSG("Failed to init CPU PMU HW!!\n");

+		return -ENODEV;

+	}

+	gMAX_PMU_HW_CNT = met_pmu_hw_v2->max_hw_count;

+

+	gKOBJ_CPU = parent;

+	for_each_possible_cpu(cpu) {

+		met_pmu = kmalloc_array(gMAX_PMU_HW_CNT, sizeof(struct met_pmu_v2), GFP_KERNEL);

+		if (met_pmu != NULL) {

+			memset(met_pmu, 0x0, gMAX_PMU_HW_CNT * sizeof(struct met_pmu_v2));

+			met_pmu_hw_v2->met_pmu[cpu] = met_pmu;

+			set_met_pmu_by_cpu_id(cpu, met_pmu);

+		} else

+			ret = -ENOMEM;

+

+		for (i = 0; i < gMAX_PMU_HW_CNT; i++) {

+			snprintf(buf, sizeof(buf), "CPU-%d-%d", cpu, i);

+			met_pmu[i].kobj_cpu_pmu = kobject_create_and_add(buf, gKOBJ_CPU);

+			if (met_pmu[i].kobj_cpu_pmu) {

+				ret = sysfs_create_file(met_pmu[i].kobj_cpu_pmu, &mode_attr.attr);

+				if (ret != 0) {

+					PR_BOOTMSG("Failed to create mode in sysfs\n");

+					goto out;

+				}

+			}

+		}

+	}

+ out:

+	if (ret != 0) {

+		for_each_possible_cpu(cpu) {

+			met_pmu = get_met_pmu_by_cpu_id(cpu);

+			if (met_pmu != NULL) {

+				kfree(met_pmu);

+				set_met_pmu_by_cpu_id(cpu, NULL);

+			}

+		}

+	}

+	return ret;

+}

+

+

+static void cpupmu_delete_subfs(void)

+{

+	unsigned int i;

+	unsigned int cpu;

+	struct met_pmu_v2 *met_pmu;

+

+	for_each_possible_cpu(cpu) {

+		met_pmu = get_met_pmu_by_cpu_id(cpu);

+		if (met_pmu != NULL) {

+			for (i = 0; i < gMAX_PMU_HW_CNT; i++) {

+				sysfs_remove_file(met_pmu[i].kobj_cpu_pmu, &mode_attr.attr);

+				kobject_del(met_pmu[i].kobj_cpu_pmu);

+				kobject_put(met_pmu[i].kobj_cpu_pmu);

+				met_pmu[i].kobj_cpu_pmu = NULL;

+			}

+			kfree(met_pmu);

+		}

+		set_met_pmu_by_cpu_id(cpu, NULL);

+	}

+

+	if (gKOBJ_CPU != NULL) {

+		gKOBJ_CPU = NULL;

+	}

+

+	met_pmu_hw_v2  = NULL;

+}

+

+

+noinline void mp_cpu_v2(unsigned char cnt, unsigned int *value)

+{

+        if (cnt < MXNR_CPU_V2)

+	        MET_GENERAL_PRINT(MET_TRACE, cnt, value);

+}

+

+

+static void dummy_handler(struct perf_event *event, struct perf_sample_data *data,

+			  struct pt_regs *regs)

+{

+/* Required as perf_event_create_kernel_counter() requires an overflow handler, even though all we do is poll */

+}

+

+

+void perf_cpupmu_polling_v2(unsigned long long stamp, int cpu)

+{

+	int i, count, delta;

+	struct perf_event *ev;

+	unsigned int pmu_value[MXNR_CPU_V2];

+	struct met_pmu_v2 *met_pmu;

+	u64 value;

+

+	if (per_cpu(perfSet, cpu) == 0)

+		return;

+

+	memset(pmu_value, 0, sizeof(pmu_value));

+	count = 0;

+	met_pmu = get_met_pmu_by_cpu_id(cpu);

+	for (i = 0; i < gMAX_PMU_HW_CNT; i++) {

+		if (met_pmu[i].mode == 0)

+			continue;

+

+		ev = per_cpu(pevent, cpu)[i];

+		if ((ev != NULL) && (ev->state == PERF_EVENT_STATE_ACTIVE)) {

+			if (per_cpu(perfPrev, cpu)[i] == 0) {

+				met_perf_event_read_local_symbol(ev, &value, NULL, NULL);

+				per_cpu(perfPrev, cpu)[i] = value;

+				continue;

+			}

+			met_perf_event_read_local_symbol(ev, &value, NULL, NULL);

+			per_cpu(perfCurr, cpu)[i] = value;

+			delta = per_cpu(perfCurr, cpu)[i] - per_cpu(perfPrev, cpu)[i];

+			per_cpu(perfPrev, cpu)[i] = per_cpu(perfCurr, cpu)[i];

+			if (delta < 0)

+				delta *= -1;

+			pmu_value[i] = delta;

+			count++;

+		}

+	}

+

+	if (count == gPMU_CNT[cpu])

+		mp_cpu_v2(count, pmu_value);

+}

+

+

+static int perf_thread_set_perf_events_v2(unsigned int cpu)

+{

+	int i, size;

+	struct perf_event *ev;

+	struct met_pmu_v2 *met_pmu;

+

+	size = sizeof(struct perf_event_attr);

+	if (per_cpu(perfSet, cpu) == 0) {

+		met_pmu = get_met_pmu_by_cpu_id(cpu);

+		for (i = 0; i < gMAX_PMU_HW_CNT; i++) {

+			per_cpu(pevent, cpu)[i] = NULL;

+			if (!met_pmu[i].mode) {/* Skip disabled counters */

+				continue;

+			}

+			per_cpu(perfPrev, cpu)[i] = 0;

+			per_cpu(perfCurr, cpu)[i] = 0;

+			memset(&per_cpu(pevent_attr, cpu)[i], 0, size);

+			per_cpu(pevent_attr, cpu)[i].config = met_pmu[i].event;

+			per_cpu(pevent_attr, cpu)[i].type = PERF_TYPE_RAW;

+			per_cpu(pevent_attr, cpu)[i].size = size;

+			per_cpu(pevent_attr, cpu)[i].sample_period = 0;

+			per_cpu(pevent_attr, cpu)[i].pinned = 1;

+			if (met_pmu[i].event == 0xff) {

+				per_cpu(pevent_attr, cpu)[i].type = PERF_TYPE_HARDWARE;

+				per_cpu(pevent_attr, cpu)[i].config = PERF_COUNT_HW_CPU_CYCLES;

+			}

+

+			per_cpu(pevent, cpu)[i] =

+			    perf_event_create_kernel_counter(&per_cpu(pevent_attr, cpu)[i], cpu, NULL,

+				     dummy_handler, NULL);

+			if (IS_ERR(per_cpu(pevent, cpu)[i])) {

+				per_cpu(pevent, cpu)[i] = NULL;

+				PR_BOOTMSG("CPU=%d, %s:%d\n", cpu, __FUNCTION__, __LINE__);

+				continue;

+			}

+

+			if (per_cpu(pevent, cpu)[i]->state != PERF_EVENT_STATE_ACTIVE) {

+				perf_event_release_kernel(per_cpu(pevent, cpu)[i]);

+				per_cpu(pevent, cpu)[i] = NULL;

+				PR_BOOTMSG("CPU=%d, %s:%d\n", cpu, __FUNCTION__, __LINE__);

+				continue;

+			}

+

+			ev = per_cpu(pevent, cpu)[i];

+			if (ev != NULL) {

+				perf_event_enable(ev);

+			}

+		} /* for all PMU counter */

+		per_cpu(perfSet, cpu) = 1;

+	} /* for perfSet */

+	return 0;

+}

+

+

+static void perf_thread_setup_v2(struct work_struct *work)

+{

+	unsigned int cpu;

+	struct delayed_work *dwork = to_delayed_work(work);

+

+	cpu = dwork->cpu;

+	if (per_cpu(perf_task_init_done, cpu) == 0) {

+		per_cpu(perf_task_init_done, cpu) = 1;

+		perf_thread_set_perf_events_v2(cpu);

+	}

+

+	return ;

+}

+

+

+void met_perf_cpupmu_online_v2(unsigned int cpu)

+{

+	if (met_cpupmu_v2.mode == 0) {

+		PR_BOOTMSG("CPU=%d, %s:%d\n", cpu, __FUNCTION__, __LINE__);

+		return;

+	}

+

+	per_cpu(perf_cpuid, cpu) = cpu;

+	if (per_cpu(perf_delayed_work_setup, cpu) == NULL) {

+		struct delayed_work *dwork;

+

+		dwork = &per_cpu(cpu_pmu_dwork, cpu);

+		dwork->cpu = cpu;

+		INIT_DELAYED_WORK(dwork, perf_thread_setup_v2);

+		schedule_delayed_work(dwork, 0);

+		per_cpu(perf_delayed_work_setup, cpu) = dwork;

+	}

+}

+

+

+void met_perf_cpupmu_down_v2(void *data)

+{

+	unsigned int cpu;

+	unsigned int i;

+	struct perf_event *ev;

+	struct met_pmu_v2 *met_pmu;

+

+	cpu = *((unsigned int *)data);

+	if (met_cpupmu_v2.mode == 0)

+		return;

+	if (per_cpu(perfSet, cpu) == 0)

+		return;

+

+	met_pmu = get_met_pmu_by_cpu_id(cpu);

+	per_cpu(perfSet, cpu) = 0;

+	for (i = 0; i < gMAX_PMU_HW_CNT; i++) {

+		if (!met_pmu[i].mode)

+			continue;

+		ev = per_cpu(pevent, cpu)[i];

+		if ((ev != NULL) && (ev->state == PERF_EVENT_STATE_ACTIVE)) {

+			perf_event_disable(ev);

+			perf_event_release_kernel(ev);

+		}

+	}

+	per_cpu(perf_task_init_done, cpu) = 0;

+	per_cpu(perf_delayed_work_setup, cpu) = NULL;

+}

+

+

+void met_perf_cpupmu_start_v2(void)

+{

+	unsigned int cpu;

+

+	for_each_online_cpu(cpu) {

+		met_perf_cpupmu_online_v2(cpu);

+	}

+}

+

+

+void met_perf_cpupmu_stop_v2(void)

+{

+	unsigned int cpu;

+

+	for_each_online_cpu(cpu) {

+		per_cpu(perf_cpuid, cpu) = cpu;

+		met_perf_cpupmu_down_v2((void *)&per_cpu(perf_cpuid, cpu));

+	}

+}

+

+

+static void cpupmu_polling(unsigned long long stamp, int cpu)

+{

+	int count;

+	struct met_pmu_v2 *met_pmu;

+	unsigned int pmu_value[MXNR_CPU_V2];

+

+	met_pmu = get_met_pmu_by_cpu_id(cpu);

+	if (met_cpu_pmu_method == 0) {

+		count = met_pmu_hw_v2->polling(met_pmu, gMAX_PMU_HW_CNT, pmu_value);

+		mp_cpu_v2(count, pmu_value);

+	} else

+		perf_cpupmu_polling_v2(stamp, cpu);

+}

+

+

+void cpupmu_polling_v2(unsigned long long stamp, int cpu)

+{

+	cpupmu_polling(stamp, cpu);

+}

+

+

+static void _cpupmu_start(void *info)

+{

+	unsigned int *cpu = (unsigned int *)info;

+	struct met_pmu_v2 *met_pmu;

+

+	met_pmu = get_met_pmu_by_cpu_id(*cpu);

+	met_pmu_hw_v2->start(met_pmu, gMAX_PMU_HW_CNT);

+}

+

+static void cpupmu_start(void)

+{

+	if (module_status == 1) {

+		PR_BOOTMSG("%s:%d\n", __FUNCTION__, __LINE__);

+		return;

+	}

+

+	if (met_cpu_pmu_method == 0) {

+		int this_cpu = smp_processor_id();

+		int cpu;

+

+		for_each_possible_cpu(cpu) {

+			if (cpu == this_cpu)

+				_cpupmu_start(&cpu);

+			else

+				met_smp_call_function_single_symbol(cpu, _cpupmu_start, &cpu, 1);

+		}

+	}

+	module_status = 1;

+}

+

+static void _cpupmu_stop(void *info)

+{

+	(void)info;

+

+	met_pmu_hw_v2->stop(gMAX_PMU_HW_CNT);

+}

+

+static void cpupmu_stop(void)

+{

+	if (module_status == 0) {

+		PR_BOOTMSG("%s:%d\n", __FUNCTION__, __LINE__);

+		return;

+	}

+

+	if (met_cpu_pmu_method == 0) {

+		int this_cpu = smp_processor_id();

+		int cpu;

+

+		for_each_possible_cpu(cpu) {

+			if (cpu == this_cpu)

+				_cpupmu_stop(&cpu);

+			else

+				met_smp_call_function_single_symbol(cpu, _cpupmu_stop, &cpu, 1);

+		}

+	}

+	module_status = 0;

+}

+

+static int cpupmu_print_help(char *buf, int len)

+{

+	return snprintf(buf, PAGE_SIZE, help, met_pmu_hw_v2->name, gMAX_PMU_HW_CNT - 1);

+}

+

+static int cpupmu_print_header(char *buf, int len)

+{

+	int i;

+	int ret = 0;

+	int pmu_cnt = 0;

+	char name[32];

+	unsigned int cpu;

+	struct met_pmu_v2 *met_pmu;

+

+	/*append CPU PMU access method*/

+	if (met_cpu_pmu_method == 0)

+		ret += snprintf(buf + ret, PAGE_SIZE,

+			"met-info [000] 0.0: CPU_PMU_method: PMU registers\n");

+	else

+		ret += snprintf(buf + ret, PAGE_SIZE,

+			"met-info [000] 0.0: CPU_PMU_method: perf APIs\n");

+

+	/*append cache line size*/

+	ret += snprintf(buf + ret, PAGE_SIZE - ret, cache_line_header, cache_line_size());

+	ret += snprintf(buf + ret, PAGE_SIZE - ret, header);

+	for_each_online_cpu(cpu) {

+		int cnt = 0;

+

+		pmu_cnt = gPMU_CNT[cpu];

+		met_pmu = get_met_pmu_by_cpu_id(cpu);

+		for (i = 0; i < pmu_cnt; i++) {

+			if (met_pmu[i].mode == 0)

+				continue;

+

+			if (met_pmu_hw_v2->get_event_desc && 0 == met_pmu_hw_v2->get_event_desc(met_pmu[i].event, name)) {

+				if (cnt == 0) {

+					ret += snprintf(buf + ret, PAGE_SIZE - ret, "CPU-%d=0x%x:%s", cpu, met_pmu[i].event, name);

+					cnt++;

+				} else

+					ret += snprintf(buf + ret, PAGE_SIZE - ret, ",0x%x:%s", met_pmu[i].event, name);

+			}

+			met_pmu[i].mode = 0;

+		}

+		if (cnt > 0 && cpu < MXNR_CPU_V2 - 1)

+			ret += snprintf(buf + ret, PAGE_SIZE - ret, ";");

+	}

+	ret += snprintf(buf + ret, PAGE_SIZE - ret, "\n");

+	met_cpupmu_v2.mode = 0;

+	reset_driver_stat();

+	return ret;

+}

+

+/*

+ * "met-cmd --start --pmu_core_evt=0:0x3,0x16,0x17"

+ */

+static int cpupmu_process_argument(const char *arg, int len)

+{

+	int ret;

+	unsigned int cpu;

+	unsigned int value;

+	unsigned int idx = 0;

+	char *str = NULL;

+	char *token = NULL;

+	struct met_pmu_v2 *met_pmu = NULL;

+

+	if (met_cpu_pmu_method == 0)

+		gMAX_PMU_HW_CNT = met_pmu_hw_v2->max_hw_count;

+	else

+		gMAX_PMU_HW_CNT = perf_num_counters();

+

+	if (gMAX_PMU_HW_CNT == 0) {

+		PR_BOOTMSG("%s:%d\n", __FUNCTION__, __LINE__);

+		goto arg_out;

+	}

+

+	str = kstrdup(arg, GFP_KERNEL);

+	token = strsep(&str, ":");

+	ret = met_parse_num(token, &cpu, strlen(token));

+	if (ret != 0) {

+		PR_BOOTMSG("%s:%d\n", __FUNCTION__, __LINE__);

+		goto arg_out;

+	}

+

+	met_pmu = get_met_pmu_by_cpu_id(cpu);

+	while (token && met_pmu && idx < gMAX_PMU_HW_CNT) {

+		token = strsep(&str, ",\r\n");

+		if (token) {

+			ret = met_parse_num(token, &value, strlen(token));

+			if (ret != 0) {

+				PR_BOOTMSG("%s:%d\n", __FUNCTION__, __LINE__);

+				goto arg_out;

+			}

+			if (value != 0xff) {

+				if (idx >= (gMAX_PMU_HW_CNT - 1)) {

+					PR_BOOTMSG("%s:%d\n", __FUNCTION__, __LINE__);

+					goto arg_out;

+				}

+				met_pmu[idx].mode = MODE_POLLING;

+				met_pmu[idx].event = value;

+				idx++;

+				gPMU_CNT[cpu]++;

+			} else {

+				if (met_cpu_pmu_method == 0) {

+					met_pmu[gMAX_PMU_HW_CNT - 1].mode = MODE_POLLING;

+					met_pmu[gMAX_PMU_HW_CNT - 1].event = 0xff;

+					gPMU_CNT[cpu]++;

+				} else {

+					if (idx > (gMAX_PMU_HW_CNT - 1)) {

+						PR_BOOTMSG("%s:%d\n", __FUNCTION__, __LINE__);

+						goto arg_out;

+					}

+					met_pmu[idx].mode = MODE_POLLING;

+					met_pmu[idx].event = 0xff;

+					idx++;

+					gPMU_CNT[cpu]++;

+				}

+			}

+			if (met_pmu_hw_v2->check_event(met_pmu, gPMU_CNT[cpu], value) < 0) {

+				PR_BOOTMSG("%s:%d\n", __FUNCTION__, __LINE__);

+				goto arg_out;

+			}

+		}

+	}

+	met_cpupmu_v2.mode = 1;

+	module_status = 0;

+	return 0;

+

+arg_out:

+	if (str)

+		kfree(str);

+	reset_driver_stat();

+	return -EINVAL;

+}

diff --git a/src/devtools/met-driver/4.19/mt2712/cpu_pmu_v2.h b/src/devtools/met-driver/4.19/mt2712/cpu_pmu_v2.h
new file mode 100644
index 0000000..6af56bb
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/cpu_pmu_v2.h
@@ -0,0 +1,85 @@
+/*

+ * Copyright (C) 2018 MediaTek Inc.

+ *

+ * 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.

+ *

+ * This program is distributed in the hope that it will be useful,

+ * but WITHOUT ANY WARRANTY; without even the implied warranty of

+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the

+ * GNU General Public License for more details.

+ */

+

+#ifndef _PMU_V2_H_

+#define _PMU_V2_H_

+

+#include <linux/device.h>

+#include <linux/threads.h> /* NR_CPUS */

+

+#define MODE_DISABLED	0

+#define MODE_INTERRUPT	1

+#define MODE_POLLING	2

+#define MXSIZE_PMU_DESC 32

+#define MXNR_CPU_V2     NR_CPUS

+

+

+/*******************************************************************************

+*                                Type Define

+*******************************************************************************/

+struct met_pmu_v2 {

+	unsigned char mode;

+	unsigned short event;

+	struct kobject *kobj_cpu_pmu;

+        const char *cpu_name;

+};

+

+enum ARM_TYPE_v2 {

+	CORTEX_A53 = 0xD03,

+	CORTEX_A35 = 0xD04,

+	CORTEX_A57 = 0xD07,

+	CORTEX_A72 = 0xD08,

+	CORTEX_A73 = 0xD09,

+	CHIP_UNKNOWN = 0xFFF

+};

+

+struct pmu_desc_v2 {

+	unsigned int event;

+	char name[MXSIZE_PMU_DESC];

+};

+

+struct chip_pmu_v2 {

+	enum ARM_TYPE_v2 type;

+	struct pmu_desc_v2 *desc;

+        unsigned int pmu_count;

+	unsigned int hw_count;

+	const char *cpu_name;

+};

+

+struct cpu_pmu_hw_v2 {

+	const char *name;

+        int max_hw_count;

+	int (*get_event_desc)(int event, char *event_desc);

+	int (*check_event)(struct met_pmu_v2 *pmu, int idx, int event);

+	void (*start)(struct met_pmu_v2 *pmu, int count);

+	void (*stop)(int count);

+	unsigned int (*polling)(struct met_pmu_v2 *pmu, int count, unsigned int *pmu_value);

+	struct met_pmu_v2 *met_pmu[MXNR_CPU_V2];

+        struct chip_pmu_v2 *chip_pmu[MXNR_CPU_V2];

+};

+

+

+extern struct cpu_pmu_hw_v2 *cpu_pmu_hw_v2;

+

+

+/*******************************************************************************

+*                                Fuction Pototypes

+*******************************************************************************/

+extern noinline void mp_cpu_v2(unsigned char cnt, unsigned int *value);

+extern void met_perf_cpupmu_online_v2(unsigned int cpu);

+extern void met_perf_cpupmu_down_v2(void *cpu);

+extern void met_perf_cpupmu_start_v2(void);

+extern void met_perf_cpupmu_stop_v2(void);

+extern void cpupmu_polling_v2(unsigned long long stamp, int cpu);

+

+#endif /* _PMU_V2_H_ */

diff --git a/src/devtools/met-driver/4.19/mt2712/interface.c b/src/devtools/met-driver/4.19/mt2712/interface.c
new file mode 100644
index 0000000..6417fdd
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/interface.c
@@ -0,0 +1,1372 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/sched/clock.h>
+#include <linux/fs.h>
+#include <linux/sched.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/miscdevice.h>
+#include <linux/kallsyms.h>
+#include <linux/syscore_ops.h>
+#include <linux/dma-mapping.h>
+#include "interface.h"
+#include "sampler.h"
+#include "util.h"
+
+#include "ondiemet.h"
+
+#include "met_drv.h"
+#include "met_tag.h"
+#include "met_kernel_symbol.h"
+#include "met_api_tbl.h"
+#include "version.h"
+
+extern int enable_met_backlight_tag(void);
+extern int output_met_backlight_tag(int level);
+
+static int run = -1;
+static int sample_rate = 1000;	/* Default: 1000 Hz */
+static int met_suspend_compensation_mode;
+static int met_suspend_compensation_flag;
+
+/*
+ * met_cpu_pmu_method:
+ *   0: MET pmu driver
+ *   1: perf APIs
+ */
+unsigned int met_cpu_pmu_method = 1;
+
+int met_hrtimer_expire;		/* in ns */
+int met_timer_expire;		/* in jiffies */
+unsigned int ctrl_flags;
+int met_mode;
+EXPORT_SYMBOL(met_mode);
+
+DEFINE_PER_CPU(char[MET_STRBUF_SIZE], met_strbuf_nmi);
+EXPORT_SYMBOL(met_strbuf_nmi);
+
+DEFINE_PER_CPU(char[MET_STRBUF_SIZE], met_strbuf_irq);
+EXPORT_SYMBOL(met_strbuf_irq);
+
+DEFINE_PER_CPU(char[MET_STRBUF_SIZE], met_strbuf_sirq);
+EXPORT_SYMBOL(met_strbuf_sirq);
+
+DEFINE_PER_CPU(char[MET_STRBUF_SIZE], met_strbuf);
+EXPORT_SYMBOL(met_strbuf);
+
+static void calc_timer_value(int rate)
+{
+	sample_rate = rate;
+
+	if (rate == 0) {
+		met_hrtimer_expire = 0;
+		met_timer_expire = 0;
+		return;
+	}
+
+	met_hrtimer_expire = 1000000000 / rate;
+
+	/* Case 1: hrtimer < 1 OS tick, met_timer_expire = 1 OS tick */
+	if (rate > HZ)
+		met_timer_expire = 1;
+	/* Case 2: hrtimer > 1 OS tick, met_timer_expire is hrtimer + 1 OS tick */
+	else
+		met_timer_expire = (HZ / rate) + 1;
+
+	/* pr_debug("JBK HZ=%d, met_hrtimer_expire=%d ns, met_timer_expire=%d ticks\n", */
+	/* HZ, met_hrtimer_expire, met_timer_expire); */
+}
+
+int met_parse_num(const char *str, unsigned int *value, int len)
+{
+	int ret;
+
+	if (len <= 0)
+		return -1;
+
+	if ((len > 2) &&
+		((str[0] == '0') &&
+		((str[1] == 'x') || (str[1] == 'X')))) {
+		ret = kstrtoint(str, 16, value);
+	} else {
+		ret = kstrtoint(str, 10, value);
+	}
+
+	if (ret != 0)
+		return -1;
+
+	return 0;
+}
+
+void met_set_suspend_notify(int flag)
+{
+	if (met_suspend_compensation_mode == 1)
+		met_suspend_compensation_flag = flag;
+	else
+		met_suspend_compensation_flag = 0;
+}
+
+LIST_HEAD(met_list);
+static struct kobject *kobj_misc;
+static struct kobject *kobj_pmu;
+static struct kobject *kobj_bus;
+
+static ssize_t ver_show(struct device *dev, struct device_attribute *attr, char *buf);
+static DEVICE_ATTR(ver, 0444, ver_show, NULL);
+
+static ssize_t plf_show(struct device *dev, struct device_attribute *attr, char *buf);
+static DEVICE_ATTR(plf, 0444, plf_show, NULL);
+
+static ssize_t core_topology_show(struct device *dev, struct device_attribute *attr, char *buf);
+static DEVICE_ATTR(core_topology, 0444, core_topology_show, NULL);
+
+static ssize_t devices_show(struct device *dev, struct device_attribute *attr, char *buf);
+static DEVICE_ATTR(devices, 0444, devices_show, NULL);
+
+static ssize_t ctrl_show(struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t ctrl_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			  size_t count);
+static DEVICE_ATTR(ctrl, 0664, ctrl_show, ctrl_store);
+
+static ssize_t spr_show(struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t spr_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			 size_t count);
+static DEVICE_ATTR(sample_rate, 0664, spr_show, spr_store);
+
+static ssize_t run_show(struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t run_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			 size_t count);
+static DEVICE_ATTR(run, 0664, run_show, run_store);
+
+static ssize_t ksym_show(struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t ksym_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			 size_t count);
+static DEVICE_ATTR(ksym, 0664, ksym_show, ksym_store);
+
+static ssize_t cpu_pmu_method_show(struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t cpu_pmu_method_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			  size_t count);
+static DEVICE_ATTR(cpu_pmu_method, 0664, cpu_pmu_method_show, cpu_pmu_method_store);
+
+#ifdef PR_CPU_NOTIFY
+int met_cpu_notify;
+static ssize_t cpu_notify_show(struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t cpu_notify_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			  size_t count);
+static DEVICE_ATTR(cpu_notify, 0664, cpu_notify_show, cpu_notify_store);
+#endif
+
+#ifdef CONFIG_CPU_FREQ
+static ssize_t dvfs_show(struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t dvfs_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			  size_t count);
+static DEVICE_ATTR(dvfs, 0664, dvfs_show, dvfs_store);
+#endif
+
+static ssize_t suspend_compensation_enable_show(struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t suspend_compensation_enable_store(struct device *dev, struct device_attribute *attr,
+							const char *buf, size_t count);
+static DEVICE_ATTR(suspend_compensation_enable, 0664, suspend_compensation_enable_show,
+			suspend_compensation_enable_store);
+
+static ssize_t suspend_compensation_flag_show(struct device *dev, struct device_attribute *attr, char *buf);
+static DEVICE_ATTR(suspend_compensation_flag, 0444, suspend_compensation_flag_show, NULL);
+
+static ssize_t ipi_test_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			 size_t count);
+static DEVICE_ATTR(ipi_test, 0220, NULL, ipi_test_store);
+
+static const struct file_operations met_file_ops = {
+	.owner = THIS_MODULE
+};
+
+struct miscdevice met_device = {
+	.minor = MISC_DYNAMIC_MINOR,
+	.name = "met",
+	.mode = 0664,
+	.fops = &met_file_ops
+};
+EXPORT_SYMBOL(met_device);
+
+static int met_run(void)
+{
+	sampler_start();
+#ifdef MET_USER_EVENT_SUPPORT
+	bltab.flag &= (~MET_CLASS_ALL);
+#endif
+	ondiemet_start();
+	return 0;
+}
+
+static void met_stop(void)
+{
+#ifdef MET_USER_EVENT_SUPPORT
+	bltab.flag |= MET_CLASS_ALL;
+#endif
+	sampler_stop();
+	/* the met.ko will be use by script "cat ...", release it */
+	if ((ondiemet_module[ONDIEMET_SSPM] == 0) || (sspm_buffer_size == -1))
+		ondiemet_log_manager_stop();
+	ondiemet_stop();
+	ondiemet_extract();
+}
+
+static ssize_t ver_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int i;
+
+	mutex_lock(&dev->mutex);
+	i = snprintf(buf, PAGE_SIZE, "%s\n", MET_BACKEND_VERSION);
+	mutex_unlock(&dev->mutex);
+	return i;
+}
+
+static ssize_t devices_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int len, total_len = 0;
+	struct metdevice *c = NULL;
+
+	mutex_lock(&dev->mutex);
+	list_for_each_entry(c, &met_list, list) {
+		len = 0;
+		if (c->type == MET_TYPE_PMU)
+			len = snprintf(buf, PAGE_SIZE - total_len, "pmu/%s:0\n", c->name);
+		else if (c->type == MET_TYPE_BUS)
+			len = snprintf(buf, PAGE_SIZE - total_len, "bus/%s:0\n", c->name);
+		else if (c->type == MET_TYPE_MISC)
+			len = snprintf(buf, PAGE_SIZE - total_len, "misc/%s:0\n", c->name);
+
+		if (c->ondiemet_mode == 0) {
+			if (c->process_argument)
+				buf[len - 2]++;
+		} else if (c->ondiemet_mode == 1) {
+			if (c->ondiemet_process_argument)
+				buf[len - 2]++;
+		} else if (c->ondiemet_mode == 2) {
+			if (c->process_argument)
+				buf[len - 2]++;
+			if (c->ondiemet_process_argument)
+				buf[len - 2]++;
+		}
+
+		buf += len;
+		total_len += len;
+	}
+
+	mutex_unlock(&dev->mutex);
+	return total_len;
+}
+
+static char met_platform[16] = "none";
+static ssize_t plf_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int i;
+
+	mutex_lock(&dev->mutex);
+	i = snprintf(buf, PAGE_SIZE, "%s\n", met_platform);
+	mutex_unlock(&dev->mutex);
+	return i;
+}
+
+static char met_topology[64] = "none";
+static ssize_t core_topology_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int i;
+
+	mutex_lock(&dev->mutex);
+	i = snprintf(buf, PAGE_SIZE, "%s\n", met_topology);
+	mutex_unlock(&dev->mutex);
+	return i;
+}
+
+static ssize_t ctrl_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%d\n", ctrl_flags);
+}
+
+static ssize_t ctrl_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			  size_t count)
+{
+	unsigned int value = 0;
+
+	if (met_parse_num(buf, &value, count) < 0)
+		return -EINVAL;
+
+	ctrl_flags = value;
+	return count;
+}
+
+static ssize_t cpu_pmu_method_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%d\n", met_cpu_pmu_method);
+}
+
+static ssize_t cpu_pmu_method_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			  size_t count)
+{
+	unsigned int value;
+
+	if (met_parse_num(buf, &value, count) < 0)
+		return -EINVAL;
+
+	met_cpu_pmu_method = value;
+	return count;
+}
+
+
+static void _test_trace_ipi_raise(void *info)
+{
+	unsigned int *cpu = (unsigned int *)info;
+	void (*arch_send_call_function_single_ipi_sym)(int cpu) = NULL;
+
+	arch_send_call_function_single_ipi_sym = (void *)symbol_get(met_arch_send_call_function_single_ipi);
+	if (arch_send_call_function_single_ipi_sym)
+		arch_send_call_function_single_ipi_sym(*cpu);
+}
+
+
+static ssize_t ipi_test_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			 size_t count)
+{
+	int this_cpu = smp_processor_id();
+	unsigned int cpu = 0;
+	unsigned int value;
+
+	if (met_parse_num(buf, &value, count) < 0)
+		return -EINVAL;
+
+	cpu = value;
+	if (cpu == this_cpu)
+		_test_trace_ipi_raise(&cpu);
+	else
+		met_smp_call_function_single_symbol(cpu, _test_trace_ipi_raise, &cpu, 1);
+
+	return count;
+}
+
+
+#if	defined(MET_BOOT_MSG)
+char met_boot_msg_tmp[256];
+char met_boot_msg[PAGE_SIZE];
+int met_boot_msg_idx;
+
+int pr_bootmsg(int str_len, char *str)
+{
+	if (met_boot_msg_idx+str_len+1 > PAGE_SIZE)
+		return -1;
+	memcpy(met_boot_msg+met_boot_msg_idx, str, str_len);
+	met_boot_msg_idx += str_len;
+	return 0;
+}
+
+static ssize_t bootmsg_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int	i;
+
+	mutex_lock(&dev->mutex);
+	i = snprintf(buf, PAGE_SIZE, "%s\n", met_boot_msg);
+	mutex_unlock(&dev->mutex);
+	return i;
+}
+
+static DEVICE_ATTR_RO(bootmsg);
+EXPORT_SYMBOL(met_boot_msg_tmp);
+EXPORT_SYMBOL(pr_bootmsg);
+#endif
+
+static ssize_t spr_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int i;
+
+	mutex_lock(&dev->mutex);
+	i = snprintf(buf, PAGE_SIZE, "%d\n", sample_rate);
+	mutex_unlock(&dev->mutex);
+	return i;
+}
+
+static ssize_t spr_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			 size_t count)
+{
+	int value;
+	struct metdevice *c = NULL;
+
+	mutex_lock(&dev->mutex);
+
+	if ((run == 1) || (count == 0) || (buf == NULL)) {
+		mutex_unlock(&dev->mutex);
+		return -EINVAL;
+	}
+	if (kstrtoint(buf, 0, &value) != 0) {
+		mutex_unlock(&dev->mutex);
+		return -EINVAL;
+	}
+
+	if ((value < 0) || (value > 10000)) {
+		mutex_unlock(&dev->mutex);
+		return -EINVAL;
+	}
+
+	calc_timer_value(value);
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->polling_interval > 0)
+			c->polling_count_reload = ((c->polling_interval * sample_rate) - 1) / 1000;
+		else
+			c->polling_count_reload = 0;
+	}
+
+	mutex_unlock(&dev->mutex);
+
+	return count;
+}
+
+static ssize_t run_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int i;
+
+	mutex_lock(&dev->mutex);
+	i = snprintf(buf, PAGE_SIZE, "%d\n", run);
+	mutex_unlock(&dev->mutex);
+	return i;
+}
+
+static ssize_t run_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			 size_t count)
+{
+	int value;
+
+	mutex_lock(&dev->mutex);
+
+	if ((count == 0) || (buf == NULL)) {
+		mutex_unlock(&dev->mutex);
+		return -EINVAL;
+	}
+	if (kstrtoint(buf, 0, &value) != 0) {
+		mutex_unlock(&dev->mutex);
+		return -EINVAL;
+	}
+
+	switch (value) {
+	case 1:
+		if (run != 1) {
+			run = 1;
+			met_run();
+		}
+		break;
+	case 0:
+		if (run != 0) {
+			if (run == 1) {
+				met_stop();
+#ifdef MET_USER_EVENT_SUPPORT
+#ifdef CONFIG_MET_MODULE
+//				met_save_dump_buffer_real("/data/trace.dump");
+#else
+				met_save_dump_buffer("/data/trace.dump");
+#endif
+#endif
+				run = 0;
+			} else
+				/* run == -1 */
+				run = 0;
+		}
+		break;
+	case -1:
+		if (run != -1) {
+			if (run == 1)
+				met_stop();
+
+			run = -1;
+		}
+		break;
+	default:
+		mutex_unlock(&dev->mutex);
+		return -EINVAL;
+	}
+
+	mutex_unlock(&dev->mutex);
+
+	return count;
+}
+
+static unsigned int met_ksym_addr;
+static char met_func_name[512];
+static ssize_t ksym_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int i;
+	int len = 0;
+	int idx = 0;
+
+	mutex_lock(&dev->mutex);
+	if (met_ksym_addr != 0)
+		len = sprint_symbol_no_offset(met_func_name, met_ksym_addr);
+	if (len != 0) {
+		for (idx = 0; idx < 512; idx++)
+			if (met_func_name[idx] == ' ')
+				met_func_name[idx] = '\0';
+		i = snprintf(buf, PAGE_SIZE, "%s\n", met_func_name);
+	} else
+		i = snprintf(buf, PAGE_SIZE, "ksymlookup fail(%x)\n", met_ksym_addr);
+
+	mutex_unlock(&dev->mutex);
+	return i;
+}
+
+static ssize_t ksym_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			 size_t count)
+{
+	mutex_lock(&dev->mutex);
+
+	if ((count == 0) || (buf == NULL)) {
+		mutex_unlock(&dev->mutex);
+		return -EINVAL;
+	}
+	if (kstrtoint(buf, 16, &met_ksym_addr) != 0) {
+		mutex_unlock(&dev->mutex);
+		return -EINVAL;
+	}
+
+	mutex_unlock(&dev->mutex);
+
+	return count;
+}
+
+#if	defined(PR_CPU_NOTIFY)
+static ssize_t cpu_notify_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int i;
+
+	i = snprintf(buf, PAGE_SIZE, "%d\n", met_cpu_notify);
+	return i;
+}
+
+static ssize_t cpu_notify_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			  size_t count)
+{
+	if ((count == 0) || (buf == NULL))
+		return -EINVAL;
+
+	if (kstrtoint(buf, 0, &met_cpu_notify) != 0)
+		return -EINVAL;
+
+	return count;
+}
+#endif
+
+#ifdef CONFIG_CPU_FREQ
+static ssize_t dvfs_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int i;
+
+	i = snprintf(buf, PAGE_SIZE, "%d\n", 0);
+	return i;
+}
+
+static ssize_t dvfs_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			  size_t count)
+{
+	return count;
+}
+#endif
+
+static ssize_t suspend_compensation_enable_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int ret;
+
+	ret = snprintf(buf, PAGE_SIZE, "%d\n", met_suspend_compensation_mode);
+
+	return ret;
+}
+
+static ssize_t suspend_compensation_enable_store(struct device *dev, struct device_attribute *attr,
+			const char *buf, size_t count)
+{
+	int value;
+
+	if ((count == 0) || (buf == NULL))
+		return -EINVAL;
+
+	if (kstrtoint(buf, 0, &value) != 0)
+		return -EINVAL;
+
+	if (value < 0)
+		return -EINVAL;
+
+	met_suspend_compensation_mode = value;
+
+	return count;
+}
+
+static ssize_t suspend_compensation_flag_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int ret;
+
+	ret = snprintf(buf, PAGE_SIZE, "%d\n", met_suspend_compensation_flag);
+
+	return ret;
+}
+
+static ssize_t hash_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	return 0;
+}
+
+static ssize_t hash_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			 size_t count)
+{
+	return 0;
+}
+
+static DEVICE_ATTR(hash, 0664, hash_show, hash_store);
+
+static ssize_t mode_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	struct metdevice *c = NULL;
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->kobj == kobj)
+			break;
+	}
+	if (c == NULL)
+		return -ENOENT;
+
+	return snprintf(buf, PAGE_SIZE, "%d\n", c->mode);
+}
+
+static ssize_t mode_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf,
+			  size_t n)
+{
+	struct metdevice *c = NULL;
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->kobj == kobj)
+			break;
+	}
+	if (c == NULL)
+		return -ENOENT;
+
+	if (kstrtoint(buf, 0, &(c->mode)) != 0)
+		return -EINVAL;
+
+	return n;
+}
+
+static struct kobj_attribute mode_attr = __ATTR(mode, 0664, mode_show, mode_store);
+
+static ssize_t ondiemet_mode_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	struct metdevice *c = NULL;
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->kobj == kobj)
+			break;
+	}
+	if (c == NULL)
+		return -ENOENT;
+
+	return snprintf(buf, PAGE_SIZE, "%d\n", c->ondiemet_mode);
+}
+
+static ssize_t ondiemet_mode_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf,
+			  size_t n)
+{
+	struct metdevice *c = NULL;
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->kobj == kobj)
+			break;
+	}
+	if (c == NULL)
+		return -ENOENT;
+
+	if (kstrtoint(buf, 0, &(c->ondiemet_mode)) != 0)
+		return -EINVAL;
+
+	return n;
+}
+
+static struct kobj_attribute ondiemet_mode_attr = __ATTR(ondiemet_mode, 0664, ondiemet_mode_show, ondiemet_mode_store);
+
+static ssize_t polling_interval_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	int interval = 1;
+	struct metdevice *c = NULL;
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->kobj == kobj)
+			break;
+	}
+	if (c == NULL)
+		return -ENOENT;
+
+	if (c->polling_interval)
+		interval = c->polling_interval;
+
+	return snprintf(buf, PAGE_SIZE, "%d\n", interval);
+}
+
+static ssize_t polling_interval_store(struct kobject *kobj, struct kobj_attribute *attr,
+				      const char *buf, size_t n)
+{
+	struct metdevice *c = NULL;
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->kobj == kobj)
+			break;
+	}
+	if (c == NULL)
+		return -ENOENT;
+
+	if (kstrtoint(buf, 0, &(c->polling_interval)) != 0)
+		return -EINVAL;
+
+	if (c->polling_interval > 0)
+		c->polling_count_reload = ((c->polling_interval * sample_rate) - 1) / 1000;
+	else
+		c->polling_count_reload = 0;
+
+	return n;
+}
+
+static struct kobj_attribute polling_interval_attr =
+__ATTR(polling_ms, 0664, polling_interval_show, polling_interval_store);
+
+static ssize_t header_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	struct metdevice *c = NULL;
+	ssize_t count = 0;
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->kobj == kobj)
+			break;
+	}
+	if (c == NULL)
+		return -ENOENT;
+
+	if (c->ondiemet_mode == 0) {
+		if ((c->mode) && (c->print_header))
+			return c->print_header(buf, PAGE_SIZE);
+	} else if (c->ondiemet_mode == 1) {
+		if ((c->mode) && (c->ondiemet_print_header))
+			return c->ondiemet_print_header(buf, PAGE_SIZE);
+	} else if (c->ondiemet_mode == 2) {
+		if ((c->mode) && (c->print_header))
+			count = c->print_header(buf, PAGE_SIZE);
+		if (count < PAGE_SIZE) {
+			if ((c->mode) && (c->ondiemet_print_header))
+				count += c->ondiemet_print_header(buf+count, PAGE_SIZE - count);
+		}
+		return count;
+	}
+
+	return 0;
+}
+
+static struct kobj_attribute header_attr = __ATTR(header, 0444, header_show, NULL);
+
+static ssize_t help_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	struct metdevice *c = NULL;
+	ssize_t count = 0;
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->kobj == kobj)
+			break;
+	}
+	if (c == NULL)
+		return -ENOENT;
+
+	if (c->ondiemet_mode == 0) {
+		if (c->print_help)
+			return c->print_help(buf, PAGE_SIZE);
+	} else if (c->ondiemet_mode == 1) {
+		if (c->ondiemet_print_help)
+			return c->ondiemet_print_help(buf, PAGE_SIZE);
+	} else if (c->ondiemet_mode == 2) {
+		if (c->print_help)
+			count = c->print_help(buf, PAGE_SIZE);
+		if (count < PAGE_SIZE) {
+			if (c->ondiemet_print_help)
+				count += c->ondiemet_print_help(buf+count, PAGE_SIZE - count);
+		}
+		return count;
+	}
+
+	return 0;
+}
+
+static struct kobj_attribute help_attr = __ATTR(help, 0444, help_show, NULL);
+
+static int argu_status = -1;
+static ssize_t argu_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf,
+			  size_t n)
+{
+	int ret = 0;
+	struct metdevice *c = NULL;
+
+	argu_status = -1;
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->kobj == kobj)
+			break;
+	}
+	if (c == NULL)
+		return -ENOENT;
+
+	if (c->ondiemet_mode == 0) {
+		if (c->process_argument)
+			ret = c->process_argument(buf, (int)n);
+	} else if (c->ondiemet_mode == 1) {
+		if (c->ondiemet_process_argument)
+			ret = c->ondiemet_process_argument(buf, (int)n);
+	} else if (c->ondiemet_mode == 2) {
+		if (c->process_argument)
+			ret = c->process_argument(buf, (int)n);
+		if (c->ondiemet_process_argument)
+			ret = c->ondiemet_process_argument(buf, (int)n);
+	}
+
+	if (ret != 0)
+		return -EINVAL;
+
+	argu_status = 0;
+	return n;
+}
+
+static ssize_t argu_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%d\n", argu_status);
+}
+
+static struct kobj_attribute argu_attr = __ATTR(argu, 0664, argu_show, argu_store);
+
+static ssize_t reset_store(struct kobject *kobj,
+			struct kobj_attribute *attr,
+			const char *buf,
+			size_t n)
+{
+	int ret = 0;
+	struct metdevice *c = NULL;
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->kobj == kobj)
+			break;
+	}
+	if (c == NULL)
+		return -ENOENT;
+
+	if (c->ondiemet_mode == 0) {
+		if (c->reset)
+			ret = c->reset();
+		else
+			c->mode = 0;
+	} else if (c->ondiemet_mode == 1) {
+		if (c->ondiemet_reset)
+			ret = c->ondiemet_reset();
+	} else if (c->ondiemet_mode == 2) {
+		if (c->reset)
+			ret = c->reset();
+		else
+			c->mode = 0;
+		if (c->ondiemet_reset)
+			ret = c->ondiemet_reset();
+	}
+
+	if (ret != 0)
+		return -EINVAL;
+
+	return n;
+}
+
+static struct kobj_attribute reset_attr = __ATTR(reset, 0220, NULL, reset_store);
+
+static ssize_t header_read_again_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	struct metdevice *c = NULL;
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->kobj == kobj)
+			break;
+	}
+	if (c == NULL)
+		return -ENOENT;
+
+	return snprintf(buf, PAGE_SIZE, "%d\n", c->header_read_again);
+}
+
+static struct kobj_attribute header_read_again_attr = __ATTR(header_read_again, 0664, header_read_again_show, NULL);
+
+
+int met_register(struct metdevice *met)
+{
+	int ret, cpu;
+	struct metdevice *c;
+
+	list_for_each_entry(c, &met_list, list) {
+		if (!strcmp(c->name, met->name))
+			return -EEXIST;
+	}
+
+	PR_BOOTMSG("met_register %s ...\n", met->name);
+
+	INIT_LIST_HEAD(&met->list);
+
+	/* Allocate timer count for per CPU */
+	met->polling_count = alloc_percpu(int);
+	if (met->polling_count == NULL)
+		return -EINVAL;
+
+	for_each_possible_cpu(cpu)
+		*(per_cpu_ptr(met->polling_count, cpu)) = 0;
+
+	if (met->polling_interval > 0) {
+		ret = ((met->polling_interval * sample_rate) - 1) / 1000;
+		met->polling_count_reload = ret;
+	} else
+		met->polling_count_reload = 0;
+
+	met->kobj = NULL;
+
+	if (met->type == MET_TYPE_BUS)
+		met->kobj = kobject_create_and_add(met->name, kobj_bus);
+	else if (met->type == MET_TYPE_PMU)
+		met->kobj = kobject_create_and_add(met->name, kobj_pmu);
+	else if (met->type == MET_TYPE_MISC)
+		met->kobj = kobject_create_and_add(met->name, kobj_misc);
+	else {
+		ret = -EINVAL;
+		goto err_out;
+	}
+
+	if (met->kobj == NULL) {
+		ret = -EINVAL;
+		goto err_out;
+	}
+
+	if (met->create_subfs) {
+		ret = met->create_subfs(met->kobj);
+		if (ret)
+			goto err_out;
+	}
+
+	ret = sysfs_create_file(met->kobj, &mode_attr.attr);
+	if (ret)
+		goto err_out;
+
+
+	ret = sysfs_create_file(met->kobj, &ondiemet_mode_attr.attr);
+	if (ret)
+		goto err_out;
+
+	ret = sysfs_create_file(met->kobj, &polling_interval_attr.attr);
+	if (ret)
+		goto err_out;
+
+	ret = sysfs_create_file(met->kobj, &header_read_again_attr.attr);
+	if (ret)
+		goto err_out;
+
+	if (met->print_header || met->ondiemet_print_header) {
+		ret = sysfs_create_file(met->kobj, &header_attr.attr);
+		if (ret)
+			goto err_out;
+	}
+
+	if (met->print_help || met->ondiemet_print_help) {
+		ret = sysfs_create_file(met->kobj, &help_attr.attr);
+		if (ret)
+			goto err_out;
+	}
+
+	if (met->process_argument || met->ondiemet_process_argument) {
+		ret = sysfs_create_file(met->kobj, &argu_attr.attr);
+		if (ret)
+			goto err_out;
+	}
+
+	if (met->reset) {
+		ret = sysfs_create_file(met->kobj, &reset_attr.attr);
+		if (ret)
+			goto err_out;
+	}
+
+	spin_lock_init(&met->my_lock);
+
+	list_add(&met->list, &met_list);
+	return 0;
+
+ err_out:
+
+	if (met->polling_count)
+		free_percpu(met->polling_count);
+
+	if (met->kobj) {
+		kobject_del(met->kobj);
+		kobject_put(met->kobj);
+		met->kobj = NULL;
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL(met_register);
+
+int met_deregister(struct metdevice *met)
+{
+	struct metdevice *c = NULL;
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c == met)
+			break;
+	}
+	if (c != met)
+		return -ENOENT;
+
+	if (met->print_header || met->ondiemet_print_header)
+		sysfs_remove_file(met->kobj, &header_attr.attr);
+
+	if (met->print_help || met->ondiemet_print_help)
+		sysfs_remove_file(met->kobj, &help_attr.attr);
+
+	if (met->process_argument || met->ondiemet_process_argument)
+		sysfs_remove_file(met->kobj, &argu_attr.attr);
+
+	sysfs_remove_file(met->kobj, &reset_attr.attr);
+	sysfs_remove_file(met->kobj, &header_read_again_attr.attr);
+	sysfs_remove_file(met->kobj, &polling_interval_attr.attr);
+	sysfs_remove_file(met->kobj, &mode_attr.attr);
+	sysfs_remove_file(met->kobj, &ondiemet_mode_attr.attr);
+
+	if (met->delete_subfs)
+		met->delete_subfs();
+
+	kobject_del(met->kobj);
+	kobject_put(met->kobj);
+	met->kobj = NULL;
+
+	if (met->polling_count)
+		free_percpu(met->polling_count);
+
+	list_del(&met->list);
+	return 0;
+}
+EXPORT_SYMBOL(met_deregister);
+
+int met_set_platform(const char *plf_name, int flag)
+{
+	strncpy(met_platform, plf_name, sizeof(met_platform) - 1);
+#if 0
+	int ret;
+
+	if (flag) {
+		ret = device_create_file(met_device.this_device, &dev_attr_plf);
+		if (ret != 0) {
+			pr_debug("can not create device file: plf\n");
+			return ret;
+		}
+		strncpy(met_platform, plf_name, sizeof(met_platform) - 1);
+	} else
+		device_remove_file(met_device.this_device, &dev_attr_plf);
+
+#endif
+	return 0;
+}
+EXPORT_SYMBOL(met_set_platform);
+
+int met_set_topology(const char *topology_name, int flag)
+{
+	strncpy(met_topology, topology_name, sizeof(met_topology) - 1);
+#if 0
+	int ret;
+
+	if (flag) {
+		ret = device_create_file(met_device.this_device, &dev_attr_core_topology);
+		if (ret != 0) {
+			pr_debug("can not create device file: topology\n");
+			return ret;
+		}
+		strncpy(met_topology, topology_name, sizeof(met_topology) - 1);
+	} else {
+		device_remove_file(met_device.this_device, &dev_attr_core_topology);
+	}
+#endif
+	return 0;
+}
+EXPORT_SYMBOL(met_set_topology);
+
+#include "met_struct.h"
+
+void force_sample(void *unused)
+{
+	int cpu;
+	unsigned long long stamp;
+	struct metdevice *c;
+	struct met_cpu_struct *met_cpu_ptr;
+
+	if ((run != 1) || (sample_rate == 0))
+		return;
+
+	/* to avoid met tag is coming after __met_hrtimer_stop and before run=-1 */
+	met_cpu_ptr = this_cpu_ptr(&met_cpu);
+	if (met_cpu_ptr->work_enabled == 0)
+		return;
+
+	cpu = smp_processor_id();
+
+	stamp = cpu_clock(cpu);
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->ondiemet_mode == 0) {
+			if ((c->mode != 0) && (c->tagged_polling != NULL))
+				c->tagged_polling(stamp, 0);
+		} else if (c->ondiemet_mode == 1) {
+			if ((c->mode != 0) && (c->ondiemet_tagged_polling != NULL))
+				c->ondiemet_tagged_polling(stamp, 0);
+		} else if (c->ondiemet_mode == 2) {
+			if ((c->mode != 0) && (c->tagged_polling != NULL))
+				c->tagged_polling(stamp, 0);
+			if ((c->mode != 0) && (c->ondiemet_tagged_polling != NULL))
+				c->ondiemet_tagged_polling(stamp, 0);
+		}
+	}
+}
+
+#define MET_SUSPEND_HAND
+#ifdef MET_SUSPEND_HAND
+static struct syscore_ops met_hrtimer_ops = {
+	.suspend = met_hrtimer_suspend,
+	.resume = met_hrtimer_resume,
+};
+#endif
+
+int fs_reg(void)
+{
+	int ret = 0;
+
+	ctrl_flags = 0;
+	met_mode = 0;
+
+#ifdef MET_SUSPEND_HAND
+	/* suspend/resume function handle register */
+	register_syscore_ops(&met_hrtimer_ops);
+#endif
+
+	calc_timer_value(sample_rate);
+
+	ret = misc_register(&met_device);
+	if (ret != 0) {
+		pr_debug("misc register failed\n");
+		return ret;
+	}
+
+	/* dma map config */
+	/* arch_setup_dma_ops(met_device.this_device, 0, 0, NULL, false); */
+	met_arch_setup_dma_ops_symbol(met_device.this_device);
+
+	ret = device_create_file(met_device.this_device, &dev_attr_ksym);
+	if (ret != 0) {
+		pr_debug("can not create device file: ksym\n");
+		return ret;
+	}
+
+	ret = device_create_file(met_device.this_device, &dev_attr_run);
+	if (ret != 0) {
+		pr_debug("can not create device file: run\n");
+		return ret;
+	}
+
+#if	defined(PR_CPU_NOTIFY)
+	ret = device_create_file(met_device.this_device, &dev_attr_cpu_notify);
+	if (ret != 0) {
+		pr_debug("can not create device file: cpu_notify\n");
+		return ret;
+	}
+#endif
+
+#ifdef CONFIG_CPU_FREQ
+	ret = device_create_file(met_device.this_device, &dev_attr_dvfs);
+	if (ret != 0) {
+		pr_debug("can not create device file: dvfs\n");
+		return ret;
+	}
+#endif
+
+	ret = device_create_file(met_device.this_device, &dev_attr_suspend_compensation_enable);
+	if (ret != 0) {
+		pr_debug("can not create device file: suspend_compensation_enable\n");
+		return ret;
+	}
+
+	ret = device_create_file(met_device.this_device, &dev_attr_suspend_compensation_flag);
+	if (ret != 0) {
+		pr_debug("can not create device file: suspend_compensation_enable\n");
+		return ret;
+	}
+
+	ret = device_create_file(met_device.this_device, &dev_attr_ver);
+	if (ret != 0) {
+		pr_debug("can not create device file: ver\n");
+		return ret;
+	}
+
+	ret = device_create_file(met_device.this_device, &dev_attr_devices);
+	if (ret != 0) {
+		pr_debug("can not create device file: devices\n");
+		return ret;
+	}
+
+	ret = device_create_file(met_device.this_device, &dev_attr_ctrl);
+	if (ret != 0) {
+		pr_debug("can not create device file: ctrl\n");
+		return ret;
+	}
+
+	ret = device_create_file(met_device.this_device, &dev_attr_cpu_pmu_method);
+	if (ret != 0) {
+		pr_debug("can not create device file: cpu_pmu_method\n");
+		return ret;
+	}
+
+#if	defined(MET_BOOT_MSG)
+	ret = device_create_file(met_device.this_device, &dev_attr_bootmsg);
+	if (ret != 0) {
+		pr_debug("can not create device file: bootmsg\n");
+		return ret;
+	}
+#endif
+
+	ret = device_create_file(met_device.this_device, &dev_attr_sample_rate);
+	if (ret != 0) {
+		pr_debug("can not create device file: sample_rate\n");
+		return ret;
+	}
+
+	ret = device_create_file(met_device.this_device, &dev_attr_core_topology);
+	if (ret != 0) {
+		pr_debug("can not create device file: topology\n");
+		return ret;
+	}
+
+	ret = device_create_file(met_device.this_device, &dev_attr_plf);
+	if (ret != 0) {
+		pr_debug("can not create device file: plf\n");
+		return ret;
+	}
+
+	ret = device_create_file(met_device.this_device, &dev_attr_hash);
+	if (ret != 0) {
+		pr_debug("can not create device file: hash\n");
+		return ret;
+	}
+
+	ret = device_create_file(met_device.this_device, &dev_attr_ipi_test);
+	if (ret != 0) {
+		pr_debug("can not create device file: ipi_test\n");
+		return ret;
+	}
+
+	kobj_misc = kobject_create_and_add("misc", &met_device.this_device->kobj);
+	if (kobj_misc == NULL) {
+		pr_debug("can not create kobject: kobj_misc\n");
+		return ret;
+	}
+
+	kobj_pmu = kobject_create_and_add("pmu", &met_device.this_device->kobj);
+	if (kobj_pmu == NULL) {
+		pr_debug("can not create kobject: kobj_pmu\n");
+		return ret;
+	}
+
+	kobj_bus = kobject_create_and_add("bus", &met_device.this_device->kobj);
+	if (kobj_bus == NULL) {
+		pr_debug("can not create kobject: kobj_bus\n");
+		return ret;
+	}
+
+	met_register(&met_cookie);
+	met_register(&met_cpupmu);
+#ifdef MET_SUPPORT_CPUPMU_V2
+	met_register(&met_cpupmu_v2);
+#endif
+#ifdef MET_USER_EVENT_SUPPORT
+	tag_reg((struct file_operations * const) met_device.fops, &met_device.this_device->kobj);
+#endif
+
+	ondiemet_log_manager_init(met_device.this_device);
+	ondiemet_attr_init(met_device.this_device);
+
+	return ret;
+}
+
+void fs_unreg(void)
+{
+	if (run == 1)
+		met_stop();
+
+	run = -1;
+
+#ifdef MET_USER_EVENT_SUPPORT
+	tag_unreg();
+#endif
+	met_deregister(&met_cpupmu);
+#ifdef MET_SUPPORT_CPUPMU_V2
+	met_deregister(&met_cpupmu_v2);
+#endif
+
+	kobject_del(kobj_misc);
+	kobject_put(kobj_misc);
+	kobj_misc = NULL;
+	kobject_del(kobj_pmu);
+	kobject_put(kobj_pmu);
+	kobj_pmu = NULL;
+	kobject_del(kobj_bus);
+	kobject_put(kobj_bus);
+	kobj_bus = NULL;
+
+	device_remove_file(met_device.this_device, &dev_attr_ksym);
+
+	device_remove_file(met_device.this_device, &dev_attr_run);
+#ifdef PR_CPU_NOTIFY
+	device_remove_file(met_device.this_device, &dev_attr_cpu_notify);
+#endif
+#ifdef CONFIG_CPU_FREQ
+	device_remove_file(met_device.this_device, &dev_attr_dvfs);
+#endif
+	device_remove_file(met_device.this_device, &dev_attr_suspend_compensation_enable);
+	device_remove_file(met_device.this_device, &dev_attr_suspend_compensation_flag);
+
+	device_remove_file(met_device.this_device, &dev_attr_ver);
+	device_remove_file(met_device.this_device, &dev_attr_devices);
+	device_remove_file(met_device.this_device, &dev_attr_sample_rate);
+
+	device_remove_file(met_device.this_device, &dev_attr_ctrl);
+	device_remove_file(met_device.this_device, &dev_attr_cpu_pmu_method);
+
+	device_remove_file(met_device.this_device, &dev_attr_core_topology);
+	device_remove_file(met_device.this_device, &dev_attr_plf);
+	device_remove_file(met_device.this_device, &dev_attr_hash);
+	device_remove_file(met_device.this_device, &dev_attr_ipi_test);
+
+	ondiemet_log_manager_uninit(met_device.this_device);
+	ondiemet_attr_uninit(met_device.this_device);
+
+	misc_deregister(&met_device);
+#ifdef MET_SUSPEND_HAND
+	/* suspend/resume function handle register */
+	unregister_syscore_ops(&met_hrtimer_ops);
+#endif
+}
+
+unsigned int get_ctrl_flags(void)
+{
+	return ctrl_flags;
+}
diff --git a/src/devtools/met-driver/4.19/mt2712/interface.h b/src/devtools/met-driver/4.19/mt2712/interface.h
new file mode 100644
index 0000000..00c26a6
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/interface.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __INTERFACE_H__
+#define __INTERFACE_H__
+
+#include <linux/fs.h>
+
+#ifdef MET_USER_EVENT_SUPPORT
+extern int tag_reg(struct file_operations *const fops, struct kobject *kobj);
+extern int tag_unreg(void);
+#include "met_drv.h"
+#include "met_tag.h"
+extern struct bltable_t bltab;
+#endif
+
+extern struct metdevice met_stat;
+extern struct metdevice met_cpupmu;
+extern struct metdevice met_cpupmu_v2;
+extern struct metdevice met_cookie;
+extern struct metdevice met_memstat;
+extern struct metdevice met_switch;
+extern struct metdevice met_trace_event;
+extern struct metdevice met_dummy_header;
+extern struct metdevice met_backlight;
+
+/* This variable will decide which method to access the CPU PMU counter */
+/*     0: access registers directly */
+/*     others: via Linux perf driver */
+extern unsigned int met_cpu_pmu_method;
+
+extern int met_parse_num(const char *str, unsigned int *value, int len);
+extern void met_set_suspend_notify(int flag);
+
+#define	PR_CPU_NOTIFY
+#if	defined(PR_CPU_NOTIFY)
+extern int met_cpu_notify;
+#endif
+
+//#undef	MET_BOOT_MSG
+#define	MET_BOOT_MSG
+#if	defined(MET_BOOT_MSG)
+extern char met_boot_msg_tmp[256];
+extern int pr_bootmsg(int str_len, char *str);
+#define	PR_BOOTMSG(fmt, args...) { \
+	int str_len = snprintf(met_boot_msg_tmp, sizeof(met_boot_msg_tmp), \
+			       fmt, ##args); \
+	pr_bootmsg(str_len, met_boot_msg_tmp); }
+#define	PR_BOOTMSG_ONCE(fmt, args...) { \
+	static int once; \
+	if (!once) { \
+		int str_len = snprintf(met_boot_msg_tmp, \
+				       sizeof(met_boot_msg_tmp), \
+				       fmt, ##args); \
+		pr_bootmsg(str_len, met_boot_msg_tmp); \
+		once = 1; \
+	} }
+#else
+#define	pr_bootmsg(str_len, str)
+#define PR_BOOTMSG(fmt, args...)
+#define	PR_BOOTMSG_ONCE(fmt, args...)
+#endif
+
+#endif	/* __INTERFACE_H__ */
diff --git a/src/devtools/met-driver/4.19/mt2712/met_api_tbl.h b/src/devtools/met-driver/4.19/mt2712/met_api_tbl.h
new file mode 100644
index 0000000..f93ae5f
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/met_api_tbl.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+struct met_api_tbl {
+	int (*met_tag_start)(unsigned int class_id, const char *name);
+	int (*met_tag_end)(unsigned int class_id, const char *name);
+	int (*met_tag_async_start)(unsigned int class_id, const char *name, unsigned int cookie);
+	int (*met_tag_async_end)(unsigned int class_id, const char *name, unsigned int cookie);
+	int (*met_tag_oneshot)(unsigned int class_id, const char *name, unsigned int value);
+	int (*met_tag_userdata)(char *pData);
+	int (*met_tag_dump)(unsigned int class_id, const char *name, void *data, unsigned int length);
+	int (*met_tag_disable)(unsigned int class_id);
+	int (*met_tag_enable)(unsigned int class_id);
+	int (*met_set_dump_buffer)(int size);
+	int (*met_save_dump_buffer)(const char *pathname);
+	int (*met_save_log)(const char *pathname);
+	int (*met_show_bw_limiter)(void);
+	int (*met_reg_bw_limiter)(void *fp);
+	int (*met_show_clk_tree)(const char *name, unsigned int addr, unsigned int status);
+	int (*met_reg_clk_tree)(void *fp);
+	void (*met_sched_switch)(struct task_struct *prev, struct task_struct *next);
+	int (*enable_met_backlight_tag)(void);
+	int (*output_met_backlight_tag)(int level);
+};
+
+extern struct met_api_tbl met_ext_api;
diff --git a/src/devtools/met-driver/4.19/mt2712/met_drv.h b/src/devtools/met-driver/4.19/mt2712/met_drv.h
new file mode 100644
index 0000000..99204e0
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/met_drv.h
@@ -0,0 +1,273 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+
+#ifndef MET_DRV
+#define MET_DRV
+
+#include <linux/version.h>
+#include <linux/preempt.h>
+#include <linux/device.h>
+#include <linux/percpu.h>
+#include <linux/hardirq.h>
+#include <linux/clk.h>
+
+extern int met_mode;
+extern int core_plf_init(void);
+extern void core_plf_exit(void);
+
+#define MET_MODE_TRACE_CMD_OFFSET	(1)
+#define MET_MODE_TRACE_CMD			(1<<MET_MODE_TRACE_CMD_OFFSET)
+
+#ifdef CONFIG_MET_MODULE
+#define my_preempt_enable() preempt_enable()
+#else
+#define my_preempt_enable() preempt_enable_no_resched()
+#endif
+
+#define MET_STRBUF_SIZE		1024
+DECLARE_PER_CPU(char[MET_STRBUF_SIZE], met_strbuf_nmi);
+DECLARE_PER_CPU(char[MET_STRBUF_SIZE], met_strbuf_irq);
+DECLARE_PER_CPU(char[MET_STRBUF_SIZE], met_strbuf_sirq);
+DECLARE_PER_CPU(char[MET_STRBUF_SIZE], met_strbuf);
+
+#ifdef CONFIG_TRACING
+#define TRACE_PUTS(p) \
+	do { \
+		trace_puts(p);; \
+	} while (0)
+#else
+#define TRACE_PUTS(p) do {} while (0)
+#endif
+
+#define GET_MET_TRACE_BUFFER_ENTER_CRITICAL() \
+	({ \
+		char *pmet_strbuf; \
+		preempt_disable(); \
+		if (in_nmi()) \
+			pmet_strbuf = per_cpu(met_strbuf_nmi, smp_processor_id()); \
+		else if (in_irq()) \
+			pmet_strbuf = per_cpu(met_strbuf_irq, smp_processor_id()); \
+		else if (in_softirq()) \
+			pmet_strbuf = per_cpu(met_strbuf_sirq, smp_processor_id()); \
+		else \
+			pmet_strbuf = per_cpu(met_strbuf, smp_processor_id()); \
+		pmet_strbuf;\
+	})
+
+#define PUT_MET_TRACE_BUFFER_EXIT_CRITICAL(pmet_strbuf) \
+	do {\
+		if (pmet_strbuf)\
+			TRACE_PUTS(pmet_strbuf); \
+		my_preempt_enable(); \
+	} while (0)
+
+#define MET_TRACE(FORMAT, args...) \
+	do { \
+		char *pmet_strbuf; \
+		preempt_disable(); \
+		if (in_nmi()) \
+			pmet_strbuf = per_cpu(met_strbuf_nmi, smp_processor_id()); \
+		else if (in_irq()) \
+			pmet_strbuf = per_cpu(met_strbuf_irq, smp_processor_id()); \
+		else if (in_softirq()) \
+			pmet_strbuf = per_cpu(met_strbuf_sirq, smp_processor_id()); \
+		else \
+			pmet_strbuf = per_cpu(met_strbuf, smp_processor_id()); \
+		if (met_mode & MET_MODE_TRACE_CMD) \
+			snprintf(pmet_strbuf, MET_STRBUF_SIZE, "%s: " FORMAT, __func__, ##args); \
+		else \
+			snprintf(pmet_strbuf, MET_STRBUF_SIZE, FORMAT, ##args); \
+		TRACE_PUTS(pmet_strbuf); \
+		my_preempt_enable(); \
+	} while (0)
+
+/*
+ * SOB: start of buf
+ * EOB: end of buf
+ */
+#define MET_TRACE_GETBUF(pSOB, pEOB) \
+	({ \
+		preempt_disable(); \
+		if (in_nmi()) \
+			*pSOB = per_cpu(met_strbuf_nmi, smp_processor_id()); \
+		else if (in_irq()) \
+			*pSOB = per_cpu(met_strbuf_irq, smp_processor_id()); \
+		else if (in_softirq()) \
+			*pSOB = per_cpu(met_strbuf_sirq, smp_processor_id()); \
+		else \
+			*pSOB = per_cpu(met_strbuf, smp_processor_id()); \
+		*pEOB = *pSOB; \
+		if (met_mode & MET_MODE_TRACE_CMD) \
+			*pEOB += snprintf(*pEOB, MET_STRBUF_SIZE, "%s: ", __func__); \
+	})
+
+#define MET_TRACE_PUTBUF(SOB, EOB) \
+	({ \
+		__trace_puts(_THIS_IP_, (SOB), (uintptr_t)((EOB)-(SOB))); \
+		my_preempt_enable(); \
+	})
+
+#define MET_FTRACE_DUMP(TRACE_NAME, args...)			\
+	do {							\
+		trace_##TRACE_NAME(args);;			\
+	} while (0)
+
+
+#define MET_TYPE_PMU	1
+#define MET_TYPE_BUS	2
+#define MET_TYPE_MISC	3
+
+enum met_action {
+	MET_CPU_ONLINE,
+	MET_CPU_OFFLINE,
+
+	NR_MET_ACTION,
+};
+
+struct metdevice {
+	struct list_head list;
+	int type;
+	const char *name;
+	struct module *owner;
+	struct kobject *kobj;
+
+	int (*create_subfs)(struct kobject *parent);
+	void (*delete_subfs)(void);
+	int mode;
+	int ondiemet_mode;	/* new for ondiemet; 1: call ondiemet functions */
+	int cpu_related;
+	int polling_interval;
+	int polling_count_reload;
+	int __percpu *polling_count;
+	int header_read_again;	/*for header size > 1 page */
+	void (*start)(void);
+	void (*stop)(void);
+	int (*reset)(void);
+	void (*timed_polling)(unsigned long long stamp, int cpu);
+	void (*tagged_polling)(unsigned long long stamp, int cpu);
+	int (*print_help)(char *buf, int len);
+	int (*print_header)(char *buf, int len);
+	int (*process_argument)(const char *arg, int len);
+
+	void (*ondiemet_start)(void);
+	void (*ondiemet_stop)(void);
+	int (*ondiemet_reset)(void);
+	int (*ondiemet_print_help)(char *buf, int len);
+	int (*ondiemet_print_header)(char *buf, int len);
+	int (*ondiemet_process_argument)(const char *arg, int len);
+	void (*ondiemet_timed_polling)(unsigned long long stamp, int cpu);
+	void (*ondiemet_tagged_polling)(unsigned long long stamp, int cpu);
+
+	struct list_head exlist;	/* for linked list before register */
+	void (*suspend)(void);
+	void (*resume)(void);
+
+	unsigned long long prev_stamp;
+	spinlock_t my_lock;
+	void *reversed1;
+};
+
+int met_register(struct metdevice *met);
+int met_deregister(struct metdevice *met);
+int met_set_platform(const char *plf_name, int flag);
+int met_set_topology(const char *topology_name, int flag);
+int met_devlink_add(struct metdevice *met);
+int met_devlink_del(struct metdevice *met);
+int met_devlink_register_all(void);
+int met_devlink_deregister_all(void);
+
+int fs_reg(void);
+void fs_unreg(void);
+
+/******************************************************************************
+ * Tracepoints
+ ******************************************************************************/
+#define MET_DEFINE_PROBE(probe_name, proto) \
+		static void probe_##probe_name(void *data, PARAMS(proto))
+#define MET_REGISTER_TRACE(probe_name) \
+		register_trace_##probe_name(probe_##probe_name, NULL)
+#define MET_UNREGISTER_TRACE(probe_name) \
+		unregister_trace_##probe_name(probe_##probe_name, NULL)
+
+
+/* ====================== Tagging API ================================ */
+
+#define MAX_EVENT_CLASS	31
+#define MAX_TAGNAME_LEN	128
+#define MET_CLASS_ALL	0x80000000
+
+/* IOCTL commands of MET tagging */
+struct mtag_cmd_t {
+	unsigned int class_id;
+	unsigned int value;
+	unsigned int slen;
+	char tname[MAX_TAGNAME_LEN];
+	void *data;
+	unsigned int size;
+};
+
+#define TYPE_START		1
+#define TYPE_END		2
+#define TYPE_ONESHOT		3
+#define TYPE_ENABLE		4
+#define TYPE_DISABLE		5
+#define TYPE_REC_SET		6
+#define TYPE_DUMP		7
+#define TYPE_DUMP_SIZE		8
+#define TYPE_DUMP_SAVE		9
+#define TYPE_USRDATA		10
+#define TYPE_DUMP_AGAIN		11
+#define TYPE_ASYNC_START	12
+#define TYPE_ASYNC_END		13
+#define TYPE_MET_SUSPEND	15
+#define TYPE_MET_RESUME		16
+
+/* Use 'm' as magic number */
+#define MTAG_IOC_MAGIC  'm'
+/* Please use a different 8-bit number in your code */
+#define MTAG_CMD_START		_IOW(MTAG_IOC_MAGIC, TYPE_START, struct mtag_cmd_t)
+#define MTAG_CMD_END		_IOW(MTAG_IOC_MAGIC, TYPE_END, struct mtag_cmd_t)
+#define MTAG_CMD_ONESHOT	_IOW(MTAG_IOC_MAGIC, TYPE_ONESHOT, struct mtag_cmd_t)
+#define MTAG_CMD_ENABLE		_IOW(MTAG_IOC_MAGIC, TYPE_ENABLE, int)
+#define MTAG_CMD_DISABLE	_IOW(MTAG_IOC_MAGIC, TYPE_DISABLE, int)
+#define MTAG_CMD_REC_SET	_IOW(MTAG_IOC_MAGIC, TYPE_REC_SET, int)
+#define MTAG_CMD_DUMP		_IOW(MTAG_IOC_MAGIC, TYPE_DUMP, struct mtag_cmd_t)
+#define MTAG_CMD_DUMP_SIZE	_IOWR(MTAG_IOC_MAGIC, TYPE_DUMP_SIZE, int)
+#define MTAG_CMD_DUMP_SAVE	_IOW(MTAG_IOC_MAGIC, TYPE_DUMP_SAVE, struct mtag_cmd_t)
+#define MTAG_CMD_USRDATA	_IOW(MTAG_IOC_MAGIC, TYPE_USRDATA, struct mtag_cmd_t)
+#define MTAG_CMD_DUMP_AGAIN	_IOW(MTAG_IOC_MAGIC, TYPE_DUMP_AGAIN, void *)
+#define MTAG_CMD_ASYNC_START	_IOW(MTAG_IOC_MAGIC, TYPE_ASYNC_START, struct mtag_cmd_t)
+#define MTAG_CMD_ASYNC_END	_IOW(MTAG_IOC_MAGIC, TYPE_ASYNC_END, struct mtag_cmd_t)
+
+/* include file */
+extern int met_tag_start_real(unsigned int class_id, const char *name);
+extern int met_tag_end_real(unsigned int class_id, const char *name);
+extern int met_tag_async_start_real(unsigned int class_id, const char *name, unsigned int cookie);
+extern int met_tag_async_end_real(unsigned int class_id, const char *name, unsigned int cookie);
+extern int met_tag_oneshot_real(unsigned int class_id, const char *name, unsigned int value);
+extern int met_tag_userdata_real(char *pData);
+extern int met_tag_dump_real(unsigned int class_id, const char *name, void *data, unsigned int length);
+extern int met_tag_disable_real(unsigned int class_id);
+extern int met_tag_enable_real(unsigned int class_id);
+extern int met_set_dump_buffer_real(int size);
+extern int met_save_dump_buffer_real(const char *pathname);
+extern int met_save_log_real(const char *pathname);
+extern int met_show_bw_limiter_real(void);
+extern int met_reg_bw_limiter_real(void *fp);
+extern int met_show_clk_tree_real(const char *name, unsigned int addr, unsigned int status);
+extern int met_reg_clk_tree_real(void *fp);
+extern int enable_met_backlight_tag(void);
+extern int output_met_backlight_tag(int level);
+
+#endif	/* MET_DRV */
diff --git a/src/devtools/met-driver/4.19/mt2712/met_emi.c b/src/devtools/met-driver/4.19/mt2712/met_emi.c
new file mode 100644
index 0000000..7f18f4a
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/met_emi.c
@@ -0,0 +1,1107 @@
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/device.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/dma-mapping.h>
+#include <linux/string.h>
+
+#define MET_USER_EVENT_SUPPORT
+#include "met_drv.h"
+#include "trace.h"
+
+#include "mtk_typedefs.h"
+#include "core_plf_init.h"
+#include "core_plf_trace.h"
+#include "mtk_emi_bm.h"
+
+extern struct miscdevice met_device;
+
+/*======================================================================*/
+/*	Global variable definitions					*/
+/*======================================================================*/
+/*ondiemet emi sampling interval in us */
+int ondiemet_emi_polling_200us;
+int emi_tsct_enable;
+int emi_mdct_enable;
+
+
+int emi_use_ondiemet;
+int metemi_func_opt;
+
+int met_emi_regdump;
+/*WSCT/TSCT id selection enable*/
+int emi_wsct_tsct_id_selection[4];
+/* Dynamic MonitorCounter selection !!!EXPERIMENT!!! */
+static int msel_enable;
+static unsigned int msel_group1 = BM_Master_GP_1_Default;
+static unsigned int msel_group2 = BM_Master_GP_2_Default;
+static unsigned int msel_group3 = BM_Master_GP_3_Default;
+
+/* CVS Added changeable buffer for testing */
+static int mdmcu_sel_enable;
+static unsigned int rd_mdmcu_rsv_num = 0x5;
+
+/* Global variables */
+static struct kobject *kobj_emi;
+static int rwtype = BM_BOTH_READ_WRITE;
+
+/* BW Limiter */
+/*#define CNT_COUNTDOWN	(1000-1)*/		/* 1000 * 1ms = 1sec */
+#define CNT_COUNTDOWN	(0)			/* 1ms */
+static int countdown;
+static int bw_limiter_enable = BM_BW_LIMITER_ENABLE;
+
+/* TTYPE counter */
+static int ttype1_16_en = BM_TTYPE1_16_DISABLE;
+static int ttype17_21_en = BM_TTYPE17_21_DISABLE;
+
+unsigned int fmem_divider_freq_1;
+unsigned int fmem_divider_freq_2;
+
+static int dramc_pdir_enable;
+static int dram_chann_num = 1;
+
+/*======================================================================*/
+/*	EMI Test Operations						*/
+/*======================================================================*/
+static int times;
+
+static ssize_t test_apmcu_store(struct kobject *kobj,
+		struct kobj_attribute *attr,
+		const char *buf,
+		size_t n)
+{
+	int		i;
+	unsigned int	*src_addr_v;
+	dma_addr_t	src_addr_p;
+
+	if ((n == 0) || (buf == NULL))
+		return -EINVAL;
+	if (sscanf(buf, "%d", &times) != 1)
+		return -EINVAL;
+	if (times < 0)
+		return -EINVAL;
+
+	if (times > 5000)	/* Less than 20MB */
+		return -EINVAL;
+
+	/* dma_alloc */
+	src_addr_v = dma_alloc_coherent(met_device.this_device,
+			PAGE_SIZE,
+			&src_addr_p,
+			GFP_KERNEL);
+	if (src_addr_v == NULL) {
+	/*	met_tag_oneshot(0, "test_apmcu dma alloc fail", PAGE_SIZE); */
+		return -ENOMEM;
+	}
+	/* testing */
+	preempt_disable();
+	/* met_tag_start(0, "TEST_EMI_APMCU"); */
+	for (i = 0; i < times; i++) {
+		memset(src_addr_v, 2*i, PAGE_SIZE);
+		/* met_tag_oneshot(0, "TEST_EMI_APMCU", PAGE_SIZE); */
+	}
+	/* met_tag_end(0, "TEST_EMI_APMCU"); */
+	preempt_enable();
+
+	/* dma_free */
+	if (src_addr_v != NULL)
+		dma_free_coherent(met_device.this_device,
+				PAGE_SIZE,
+				src_addr_v,
+				src_addr_p);
+	return n;
+}
+
+/*======================================================================*/
+/*	KOBJ Declarations						*/
+/*======================================================================*/
+DECLARE_KOBJ_ATTR_INT(ondiemet_emi_polling_200us, ondiemet_emi_polling_200us)
+DECLARE_KOBJ_ATTR_INT(emi_tsct_enable, emi_tsct_enable)
+DECLARE_KOBJ_ATTR_INT(emi_mdct_enable, emi_mdct_enable)
+DECLARE_KOBJ_ATTR_INT(metemi_func_opt, metemi_func_opt)
+DECLARE_KOBJ_ATTR_INT(emi_regdump, met_emi_regdump)
+DECLARE_KOBJ_ATTR_INT(emi_wsct_tsct_id_selection1, emi_wsct_tsct_id_selection[0])
+DECLARE_KOBJ_ATTR_INT(emi_wsct_tsct_id_selection2, emi_wsct_tsct_id_selection[1])
+DECLARE_KOBJ_ATTR_INT(emi_wsct_tsct_id_selection3, emi_wsct_tsct_id_selection[2])
+DECLARE_KOBJ_ATTR_INT(emi_wsct_tsct_id_selection4, emi_wsct_tsct_id_selection[3])
+/* KOBJ: Dynamic MonitorCounter selection !!!EXPERIMENT!!! */
+DECLARE_KOBJ_ATTR_INT(msel_enable, msel_enable)
+DECLARE_KOBJ_ATTR_HEX_CHECK(msel_group1, msel_group1, msel_group1 > 0 && msel_group1 <= BM_MASTER_ALL)
+DECLARE_KOBJ_ATTR_HEX_CHECK(msel_group2, msel_group2, msel_group2 > 0 && msel_group2 <= BM_MASTER_ALL)
+DECLARE_KOBJ_ATTR_HEX_CHECK(msel_group3, msel_group3, msel_group3 > 0 && msel_group3 <= BM_MASTER_ALL)
+DECLARE_KOBJ_ATTR_INT(mdmcu_sel_enable, mdmcu_sel_enable)
+DECLARE_KOBJ_ATTR_INT(rd_mdmcu_rsv_num, rd_mdmcu_rsv_num)
+
+
+/* KOBJ: rwtype */
+DECLARE_KOBJ_ATTR_INT_CHECK(rwtype, rwtype, rwtype >= 0 && rwtype <= BM_WRITE_ONLY)
+
+static unsigned int get_emi_clock_rate(unsigned int dram_data_rate_MHz)
+{
+	return dram_data_rate_MHz/DRAM_EMI_BASECLOCK_RATE/DRAM_DATARATE;
+}
+
+/* KOBJ: emi_clock_rate */
+static ssize_t emi_clock_rate_show(struct kobject *kobj,
+		struct kobj_attribute *attr,
+		char *buf)
+{
+	unsigned int	dram_data_rate_MHz;
+
+	dram_data_rate_MHz = get_dram_data_rate();
+	return snprintf(buf, PAGE_SIZE, "%d\n",
+			get_emi_clock_rate(dram_data_rate_MHz));
+}
+
+DECLARE_KOBJ_ATTR_RO(emi_clock_rate)
+
+static ssize_t dram_data_rate_show(struct kobject *kobj,
+		struct kobj_attribute *attr,
+		char *buf)
+{
+	unsigned int	dram_data_rate_MHz;
+
+	dram_data_rate_MHz = get_dram_data_rate();
+	return snprintf(buf, PAGE_SIZE, "%d\n", dram_data_rate_MHz);
+}
+
+DECLARE_KOBJ_ATTR_RO(dram_data_rate)
+
+/* KOBJ: ttype1_16_en */
+DECLARE_KOBJ_ATTR_STR_LIST_ITEM(
+		ttype1_16_en,
+		KOBJ_ITEM_LIST(
+			{BM_TTYPE1_16_ENABLE,	"ENABLE"},
+			{BM_TTYPE1_16_DISABLE,	"DISABLE"}
+			)
+		)
+DECLARE_KOBJ_ATTR_STR_LIST(ttype1_16_en, ttype1_16_en, ttype1_16_en)
+
+/* KOBJ: ttype17_21_en */
+DECLARE_KOBJ_ATTR_STR_LIST_ITEM(
+		ttype17_21_en,
+		KOBJ_ITEM_LIST(
+			{BM_TTYPE17_21_ENABLE,	"ENABLE"},
+			{BM_TTYPE17_21_DISABLE,	"DISABLE"}
+			)
+		)
+DECLARE_KOBJ_ATTR_STR_LIST(ttype17_21_en, ttype17_21_en, ttype17_21_en)
+
+/* KOBJ: bw_limiter_enable */
+DECLARE_KOBJ_ATTR_STR_LIST_ITEM(
+		bw_limiter_enable,
+		KOBJ_ITEM_LIST(
+			{BM_BW_LIMITER_ENABLE,	"ENABLE"},
+			{BM_BW_LIMITER_DISABLE,	"DISABLE"}
+			)
+		)
+
+DECLARE_KOBJ_ATTR_STR_LIST(bw_limiter_enable, bw_limiter_enable, bw_limiter_enable)
+
+/* KOBJ: ttype_master */
+DECLARE_KOBJ_ATTR_STR_LIST_ITEM(
+		ttype_master,
+		KOBJ_ITEM_LIST(
+			{BM_MASTER_M0,	"M0"},
+			{BM_MASTER_M1,	"M1"},
+			{BM_MASTER_M2,	"M2"},
+			{BM_MASTER_M3,	"M3"},
+			{BM_MASTER_M4,	"M4"},
+			{BM_MASTER_M5,	"M5"},
+			{BM_MASTER_M6,	"M6"},
+			{BM_MASTER_M7,	"M7"}
+			)
+		)
+
+
+/* KOBJ: ttypeX_nbeat, ttypeX_nbyte, ttypeX_burst */
+DECLARE_KOBJ_ATTR_INT_LIST_ITEM(
+		ttype_nbeat,
+		KOBJ_ITEM_LIST(
+			{BM_TRANS_TYPE_1BEAT,	1},
+			{BM_TRANS_TYPE_2BEAT,	2},
+			{BM_TRANS_TYPE_3BEAT,	3},
+			{BM_TRANS_TYPE_4BEAT,	4},
+			{BM_TRANS_TYPE_5BEAT,	5},
+			{BM_TRANS_TYPE_6BEAT,	6},
+			{BM_TRANS_TYPE_7BEAT,	7},
+			{BM_TRANS_TYPE_8BEAT,	8},
+			{BM_TRANS_TYPE_9BEAT,	9},
+			{BM_TRANS_TYPE_10BEAT,	10},
+			{BM_TRANS_TYPE_11BEAT,	11},
+			{BM_TRANS_TYPE_12BEAT,	12},
+			{BM_TRANS_TYPE_13BEAT,	13},
+			{BM_TRANS_TYPE_14BEAT,	14},
+			{BM_TRANS_TYPE_15BEAT,	15},
+			{BM_TRANS_TYPE_16BEAT,	16}
+			)
+		)
+DECLARE_KOBJ_ATTR_INT_LIST_ITEM(
+		ttype_nbyte,
+		KOBJ_ITEM_LIST(
+			{BM_TRANS_TYPE_1Byte,	1},
+			{BM_TRANS_TYPE_2Byte,	2},
+			{BM_TRANS_TYPE_4Byte,	4},
+			{BM_TRANS_TYPE_8Byte,	8},
+			{BM_TRANS_TYPE_16Byte,	16},
+			{BM_TRANS_TYPE_32Byte,	32}
+			)
+		)
+DECLARE_KOBJ_ATTR_STR_LIST_ITEM(
+		ttype_burst,
+		KOBJ_ITEM_LIST(
+			{BM_TRANS_TYPE_BURST_INCR,	"INCR"},
+			{BM_TRANS_TYPE_BURST_WRAP,	"WRAP"}
+			)
+		)
+
+DECLARE_KOBJ_ATTR_STR_LIST_ITEM(
+		ttype_rw,
+		KOBJ_ITEM_LIST(
+			{BM_TRANS_RW_DEFAULT,	"DEFAULT"},
+			{BM_TRANS_RW_READONLY,	"R"},
+			{BM_TRANS_RW_WRITEONLY,	"W"},
+			{BM_TRANS_RW_RWBOTH,	"BOTH"}
+			)
+		)
+
+/* KOBJ: test_apmcu */
+DECLARE_KOBJ_ATTR_SHOW_INT(test_apmcu, times)
+/* please refer to session: "EMI Test Operations" for store operation */
+DECLARE_KOBJ_ATTR(test_apmcu)
+
+DECLARE_KOBJ_ATTR_INT(dramc_pdir_enable, dramc_pdir_enable)
+
+/*enable high priority filter*/
+static int high_priority_filter;
+DECLARE_KOBJ_ATTR_HEX(high_priority_filter, high_priority_filter)
+
+
+/**/
+static int ttype_master_val[21];
+static int ttype_busid_val[21];
+static int ttype_nbeat_val[21];
+static int ttype_nbyte_val[21];
+static int ttype_burst_val[21];
+static int ttype_rw_val[21];
+
+#define DECLARE_KOBJ_SERIAL_FNODE(nr) \
+	DECLARE_KOBJ_ATTR_STR_LIST(ttype##nr##_master, ttype_master_val[nr-1], ttype_master) \
+	DECLARE_KOBJ_ATTR_HEX(ttype##nr##_busid, ttype_busid_val[nr-1]) \
+	DECLARE_KOBJ_ATTR_INT_LIST(ttype##nr##_nbeat, ttype_nbeat_val[nr-1], ttype_nbeat) \
+	DECLARE_KOBJ_ATTR_INT_LIST(ttype##nr##_nbyte, ttype_nbyte_val[nr-1], ttype_nbyte) \
+	DECLARE_KOBJ_ATTR_STR_LIST(ttype##nr##_burst, ttype_burst_val[nr-1], ttype_burst) \
+	DECLARE_KOBJ_ATTR_STR_LIST(ttype##nr##_rw, ttype_rw_val[nr-1], ttype_rw)
+
+DECLARE_KOBJ_SERIAL_FNODE(1)
+DECLARE_KOBJ_SERIAL_FNODE(2)
+DECLARE_KOBJ_SERIAL_FNODE(3)
+DECLARE_KOBJ_SERIAL_FNODE(4)
+DECLARE_KOBJ_SERIAL_FNODE(5)
+DECLARE_KOBJ_SERIAL_FNODE(6)
+DECLARE_KOBJ_SERIAL_FNODE(7)
+DECLARE_KOBJ_SERIAL_FNODE(8)
+DECLARE_KOBJ_SERIAL_FNODE(9)
+DECLARE_KOBJ_SERIAL_FNODE(10)
+DECLARE_KOBJ_SERIAL_FNODE(11)
+DECLARE_KOBJ_SERIAL_FNODE(12)
+DECLARE_KOBJ_SERIAL_FNODE(13)
+DECLARE_KOBJ_SERIAL_FNODE(14)
+DECLARE_KOBJ_SERIAL_FNODE(15)
+DECLARE_KOBJ_SERIAL_FNODE(16)
+DECLARE_KOBJ_SERIAL_FNODE(17)
+DECLARE_KOBJ_SERIAL_FNODE(18)
+DECLARE_KOBJ_SERIAL_FNODE(19)
+DECLARE_KOBJ_SERIAL_FNODE(20)
+DECLARE_KOBJ_SERIAL_FNODE(21)
+
+	/**/
+#define KOBJ_ATTR_ITEM_SERIAL_FNODE(nr) \
+	KOBJ_ATTR_ITEM(ttype##nr##_master) \
+	KOBJ_ATTR_ITEM(ttype##nr##_nbeat) \
+	KOBJ_ATTR_ITEM(ttype##nr##_nbyte) \
+	KOBJ_ATTR_ITEM(ttype##nr##_burst) \
+	KOBJ_ATTR_ITEM(ttype##nr##_busid) \
+	KOBJ_ATTR_ITEM(ttype##nr##_rw) \
+
+#define KOBJ_ATTR_LIST \
+	KOBJ_ATTR_ITEM(high_priority_filter) \
+	KOBJ_ATTR_ITEM(metemi_func_opt) \
+	KOBJ_ATTR_ITEM(emi_tsct_enable) \
+	KOBJ_ATTR_ITEM(emi_mdct_enable) \
+	KOBJ_ATTR_ITEM(ondiemet_emi_polling_200us) \
+	KOBJ_ATTR_ITEM(emi_regdump) \
+	KOBJ_ATTR_ITEM(emi_wsct_tsct_id_selection1) \
+	KOBJ_ATTR_ITEM(emi_wsct_tsct_id_selection2) \
+	KOBJ_ATTR_ITEM(emi_wsct_tsct_id_selection3) \
+	KOBJ_ATTR_ITEM(emi_wsct_tsct_id_selection4) \
+	KOBJ_ATTR_ITEM(msel_enable) \
+	KOBJ_ATTR_ITEM(msel_group1) \
+	KOBJ_ATTR_ITEM(msel_group2) \
+	KOBJ_ATTR_ITEM(msel_group3) \
+	KOBJ_ATTR_ITEM(emi_clock_rate) \
+	KOBJ_ATTR_ITEM(dram_data_rate) \
+	KOBJ_ATTR_ITEM(rwtype) \
+	KOBJ_ATTR_ITEM(ttype17_21_en) \
+	KOBJ_ATTR_ITEM(ttype1_16_en) \
+	KOBJ_ATTR_ITEM_SERIAL_FNODE(1) \
+	KOBJ_ATTR_ITEM_SERIAL_FNODE(2) \
+	KOBJ_ATTR_ITEM_SERIAL_FNODE(3) \
+	KOBJ_ATTR_ITEM_SERIAL_FNODE(4) \
+	KOBJ_ATTR_ITEM_SERIAL_FNODE(5) \
+	KOBJ_ATTR_ITEM_SERIAL_FNODE(6) \
+	KOBJ_ATTR_ITEM_SERIAL_FNODE(7) \
+	KOBJ_ATTR_ITEM_SERIAL_FNODE(8) \
+	KOBJ_ATTR_ITEM_SERIAL_FNODE(9) \
+	KOBJ_ATTR_ITEM_SERIAL_FNODE(10) \
+	KOBJ_ATTR_ITEM_SERIAL_FNODE(11) \
+	KOBJ_ATTR_ITEM_SERIAL_FNODE(12) \
+	KOBJ_ATTR_ITEM_SERIAL_FNODE(13) \
+	KOBJ_ATTR_ITEM_SERIAL_FNODE(14) \
+	KOBJ_ATTR_ITEM_SERIAL_FNODE(15) \
+	KOBJ_ATTR_ITEM_SERIAL_FNODE(16) \
+	KOBJ_ATTR_ITEM_SERIAL_FNODE(17) \
+	KOBJ_ATTR_ITEM_SERIAL_FNODE(18) \
+	KOBJ_ATTR_ITEM_SERIAL_FNODE(19) \
+	KOBJ_ATTR_ITEM_SERIAL_FNODE(20) \
+	KOBJ_ATTR_ITEM_SERIAL_FNODE(21) \
+	KOBJ_ATTR_ITEM(test_apmcu) \
+	KOBJ_ATTR_ITEM(bw_limiter_enable) \
+	KOBJ_ATTR_ITEM(dramc_pdir_enable) \
+	KOBJ_ATTR_ITEM(mdmcu_sel_enable) \
+	KOBJ_ATTR_ITEM(rd_mdmcu_rsv_num) \
+
+/*======================================================================*/
+/*	EMI Operations							*/
+/*======================================================================*/
+static void emi_init(void)
+{
+	unsigned int bmrw0_val, bmrw1_val, i, enable;
+	unsigned int msel_group1_val, msel_group2_val, msel_group3_val;
+
+	/* get dram channel number */
+	dram_chann_num = MET_EMI_GetDramChannNum();
+
+	/* Init. EMI bus monitor */
+	MET_BM_SetReadWriteType(rwtype);
+
+	/* MSEL1: ALL */
+	MET_BM_SetMonitorCounter(1,
+		BM_MASTER_ALL,
+		BM_TRANS_TYPE_4BEAT |
+		BM_TRANS_TYPE_8Byte |
+		BM_TRANS_TYPE_BURST_WRAP);
+	if (msel_enable) {
+		msel_group1_val = msel_group1;
+		msel_group2_val = msel_group2;
+		msel_group3_val = msel_group3;
+	} else {
+		msel_group1_val = BM_Master_GP_1_Default;
+		msel_group2_val = BM_Master_GP_2_Default;
+		msel_group3_val = BM_Master_GP_3_Default;
+	}
+
+	/* MSEL2: msel_group1 */
+	MET_BM_SetMonitorCounter(2,
+		msel_group1_val & BM_MASTER_ALL,
+		BM_TRANS_TYPE_4BEAT |
+		BM_TRANS_TYPE_8Byte |
+		BM_TRANS_TYPE_BURST_WRAP);
+	/* MSEL3: msel_group2 */
+	MET_BM_SetMonitorCounter(3,
+		msel_group2_val & BM_MASTER_ALL,
+		BM_TRANS_TYPE_4BEAT |
+		BM_TRANS_TYPE_8Byte |
+		BM_TRANS_TYPE_BURST_WRAP);
+	/* MSEL4: msel_group3 */
+	MET_BM_SetMonitorCounter(4,
+		msel_group3_val & BM_MASTER_ALL,
+		BM_TRANS_TYPE_4BEAT |
+		BM_TRANS_TYPE_8Byte |
+		BM_TRANS_TYPE_BURST_WRAP);
+
+
+	if (ttype1_16_en == BM_TTYPE1_16_ENABLE)	{
+		MET_BM_SetLatencyCounter(0);
+
+		for (i = 1; i <= 16; i++) {
+			MET_BM_SetMonitorCounter(i,
+					ttype_master_val[i - 1],
+					ttype_nbeat_val[i - 1] |
+					ttype_nbyte_val[i - 1] |
+					ttype_burst_val[i - 1]);
+			MET_BM_SetIDSelect(i, ttype_busid_val[i - 1], (ttype_busid_val[i - 1] >= 0xffff) ? 0 : 1);
+		}
+	} else {
+		MET_BM_SetLatencyCounter(1);
+	}
+
+	if (ttype17_21_en == BM_TTYPE17_21_ENABLE) {
+		for (i = 17; i <= 21; i++) {
+			MET_BM_SetMonitorCounter(i,
+					ttype_master_val[i - 1],
+					ttype_nbeat_val[i - 1] |
+					ttype_nbyte_val[i - 1] |
+					ttype_burst_val[i - 1]);
+			MET_BM_SetIDSelect(i, ttype_busid_val[i - 1], (ttype_busid_val[i - 1] >= 0xffff) ? 0 : 1);
+		}
+	}
+
+	bmrw0_val = (
+			(ttype_rw_val[0] << 0) | (ttype_rw_val[1] << 2) |
+			(ttype_rw_val[2] << 4) | (ttype_rw_val[3] << 6) |
+			(ttype_rw_val[4] << 8) | (ttype_rw_val[5] << 10) |
+			(ttype_rw_val[6] << 12) | (ttype_rw_val[7] << 14) |
+			(ttype_rw_val[8] << 16) | (ttype_rw_val[9] << 18) |
+			(ttype_rw_val[10] << 20) | (ttype_rw_val[11] << 22) |
+			(ttype_rw_val[12] << 24) | (ttype_rw_val[13] << 26) |
+			(ttype_rw_val[14] << 28) | (ttype_rw_val[15] << 30));
+
+	bmrw1_val = (
+			(ttype_rw_val[16] << 0) | (ttype_rw_val[17] << 2) |
+			(ttype_rw_val[18] << 4) | (ttype_rw_val[19] << 6) |
+			(ttype_rw_val[20] << 8));
+
+	MET_BM_SetTtypeCounterRW(bmrw0_val, bmrw1_val);
+
+	for (i = 0; i < 4; i++)
+		MET_BM_Set_WsctTsct_id_sel(i, emi_wsct_tsct_id_selection[i]);
+
+	for (i = 0; i < BM_COUNTER_MAX; i++) {
+		if ((high_priority_filter & (1<<i)) == 0)
+			enable = 0;
+		else
+			enable = 1;
+
+		MET_BM_SetUltraHighFilter(i+1, enable);
+	}
+
+	if (met_emi_regdump == 1)
+		emi_dump_reg();
+}
+
+static void emi_uninit(void)
+{
+}
+
+static inline void emi_start(void)
+{
+	MET_BM_Enable(1);
+}
+
+static inline void emi_stop(void)
+{
+	MET_BM_Enable(0);
+}
+
+static inline int do_emi(void)
+{
+	return met_emi.mode;
+}
+
+noinline void DRAM_DVFS(unsigned int dram_data_rate_MHz)
+{
+	MET_TRACE("%u\n", dram_data_rate_MHz);
+}
+
+static unsigned int emi_bw_limiter(unsigned int *__restrict__ array)
+{
+	int		idx = 0;
+	unsigned int	dram_data_rate_MHz;
+
+	dram_data_rate_MHz = get_dram_data_rate();
+
+	/* print dram data rate */
+	DRAM_DVFS(dram_data_rate_MHz);
+
+	/* get correct dram_clock_rate */
+	array[idx++] = dram_data_rate_MHz;
+
+	/* get correct ARB A->LAST */
+	array[idx++] = MET_EMI_GetARBA();
+	array[idx++] = MET_EMI_GetARBB();
+	array[idx++] = MET_EMI_GetARBC();
+	array[idx++] = MET_EMI_GetARBD();
+	array[idx++] = MET_EMI_GetARBE();
+	array[idx++] = MET_EMI_GetARBF();
+	array[idx++] = MET_EMI_GetARBG();
+	array[idx++] = MET_EMI_GetARBH();
+	/* EMI Total BW Thresholds */
+	array[idx++] = MET_EMI_GetBWCT0();
+	array[idx++] = MET_EMI_GetBWCT1();
+	array[idx++] = MET_EMI_GetBWCT2();
+	array[idx++] = MET_EMI_GetBWCT3();
+	array[idx++] = MET_EMI_GetBWCT4();
+	array[idx++] = MET_EMI_GetBWST0();
+	array[idx++] = MET_EMI_GetBWST1();
+	/* EMI C+G BW Thresholds */
+	array[idx++] = MET_EMI_GetBWCT0_2ND();
+	array[idx++] = MET_EMI_GetBWCT1_2ND();
+	array[idx++] = MET_EMI_GetBWST_2ND();
+
+	return idx;
+}
+
+
+static void _ms_dramc(unsigned int *__restrict__ dramc_pdir_value, int dram_chann_num)
+{
+	MET_DRAMC_GetDebugCounter(dramc_pdir_value, dram_chann_num);
+}
+
+static unsigned int emi_polling(unsigned int *__restrict__ emi_value, unsigned int *__restrict__ emi_tsct,
+	unsigned int *__restrict__ emi_ttype_value, unsigned int *__restrict__ dramc_pdir_value,
+	unsigned int *__restrict__ emi_mdct_value)
+{
+	int	j = 4;		/* skip 4 WSCTs */
+	int	i = 0;		/* ttype start at 0 */
+	int	k = 0;		/* tsct start at 0 */
+	int n;
+
+	MET_BM_Pause();
+
+	if (ttype1_16_en != BM_TTYPE1_16_ENABLE) {	/*1~21 NOT for ttype*/
+
+		/* Get Word Count */
+
+		emi_value[0] = MET_BM_GetWordCount(1);	/* All */
+		emi_value[1] = MET_BM_GetWordCount(2);	/* Group 1 */
+		emi_value[2] = MET_BM_GetWordCount(3);	/* Group 2 */
+		emi_value[3] = MET_BM_GetWordCount(4);	/* Group 3 */
+
+
+		/* Get Latency */
+		emi_value[j++] = MET_BM_GetLatencyCycle(1);
+		emi_value[j++] = MET_BM_GetLatencyCycle(2);
+		emi_value[j++] = MET_BM_GetLatencyCycle(3);
+		emi_value[j++] = MET_BM_GetLatencyCycle(4);
+		emi_value[j++] = MET_BM_GetLatencyCycle(5);
+		emi_value[j++] = MET_BM_GetLatencyCycle(6);
+		emi_value[j++] = MET_BM_GetLatencyCycle(7);
+		emi_value[j++] = MET_BM_GetLatencyCycle(8);
+
+		/* Get Trans. */
+		emi_value[j++] = MET_BM_GetLatencyCycle(9);
+		emi_value[j++] = MET_BM_GetLatencyCycle(10);
+		emi_value[j++] = MET_BM_GetLatencyCycle(11);
+		emi_value[j++] = MET_BM_GetLatencyCycle(12);
+		emi_value[j++] = MET_BM_GetLatencyCycle(13);
+		emi_value[j++] = MET_BM_GetLatencyCycle(14);
+		emi_value[j++] = MET_BM_GetLatencyCycle(15);
+		emi_value[j++] = MET_BM_GetLatencyCycle(16);
+	} else {
+		for (n = 0; n < 20; n++)
+			emi_value[n] = 0;
+		j = 20;
+
+		emi_ttype_value[i++] = MET_BM_GetLatencyCycle(1);
+		emi_ttype_value[i++] = MET_BM_GetLatencyCycle(2);
+		emi_ttype_value[i++] = MET_BM_GetLatencyCycle(3);
+		emi_ttype_value[i++] = MET_BM_GetLatencyCycle(4);
+		emi_ttype_value[i++] = MET_BM_GetLatencyCycle(5);
+		emi_ttype_value[i++] = MET_BM_GetLatencyCycle(6);
+		emi_ttype_value[i++] = MET_BM_GetLatencyCycle(7);
+		emi_ttype_value[i++] = MET_BM_GetLatencyCycle(8);
+
+		/* Get Trans. */
+		emi_ttype_value[i++] = MET_BM_GetLatencyCycle(9);
+		emi_ttype_value[i++] = MET_BM_GetLatencyCycle(10);
+		emi_ttype_value[i++] = MET_BM_GetLatencyCycle(11);
+		emi_ttype_value[i++] = MET_BM_GetLatencyCycle(12);
+		emi_ttype_value[i++] = MET_BM_GetLatencyCycle(13);
+		emi_ttype_value[i++] = MET_BM_GetLatencyCycle(14);
+		emi_ttype_value[i++] = MET_BM_GetLatencyCycle(15);
+		emi_ttype_value[i++] = MET_BM_GetLatencyCycle(16);
+	}
+
+	/* Get BACT/BSCT/BCNT */
+	emi_value[j++] = MET_BM_GetBandwidthWordCount();
+	emi_value[j++] = MET_BM_GetOverheadWordCount();
+	emi_value[j++] = MET_BM_GetBusCycCount();
+	/* Get PageHist/PageMiss/InterBank/Idle */
+	for (n = 0; n < dram_chann_num; n++) {
+#if 1
+		/* TBD */
+		emi_value[j++] = MET_DRAMC_GetPageHitCount(DRAMC_ALL, n);
+		emi_value[j++] = MET_DRAMC_GetPageMissCount(DRAMC_ALL, n);
+		emi_value[j++] = MET_DRAMC_GetInterbankCount(DRAMC_ALL, n);
+		emi_value[j++] = MET_DRAMC_GetIdleCount(n);
+		emi_value[j++] = ((MET_DRAMC_Misc_Status(n) >> 8) & 0x7);
+		emi_value[j++] = MET_DRAMC_RefPop(n);
+		emi_value[j++] = MET_DRAMC_Free26M(n);
+		emi_value[j++] = MET_DRAMC_RByte(n);
+		emi_value[j++] = MET_DRAMC_WByte(n);
+#else
+		emi_value[j++] = 0;
+		emi_value[j++] = 0;
+		emi_value[j++] = 0;
+		emi_value[j++] = 0;
+		emi_value[j++] = 0;
+		emi_value[j++] = 0;
+		emi_value[j++] = 0;
+		emi_value[j++] = 0;
+		emi_value[j++] = 0;
+#endif
+	}
+	/* TTYPE */
+	if (ttype17_21_en == BM_TTYPE17_21_ENABLE) {	/*17~21 for ttype*/
+		emi_ttype_value[16] = MET_BM_GetLatencyCycle(17);
+		emi_ttype_value[17] = MET_BM_GetLatencyCycle(18);
+		emi_ttype_value[18] = MET_BM_GetLatencyCycle(19);
+		emi_ttype_value[19] = MET_BM_GetLatencyCycle(20);
+		emi_ttype_value[20] = MET_BM_GetLatencyCycle(21);
+	}
+
+	/* Get tsct */
+	if (emi_tsct_enable == 1) {
+		emi_tsct[k++] = MET_BM_GetTransCount(1);
+		emi_tsct[k++] = MET_BM_GetTransCount(2);
+		emi_tsct[k++] = MET_BM_GetTransCount(3);
+	}
+	/*get mdct rsv buffer*/
+	if (emi_mdct_enable == 1) {
+		emi_mdct_value[0] = (MET_BM_GetMDCT() >> 16) & 0x7;
+		emi_mdct_value[1] = (MET_BM_GetMDCT_2() & 0x7);
+	}
+
+	if (dramc_pdir_enable == 1)
+		_ms_dramc(dramc_pdir_value, dram_chann_num);
+
+	MET_BM_Continue();
+	MET_BM_Enable(0);
+	MET_BM_Enable(1);
+
+	return j;
+}
+
+/*======================================================================*/
+/*	MET Device Operations						*/
+/*======================================================================*/
+static int emi_inited;
+
+static int met_emi_create(struct kobject *parent)
+{
+	int ret = 0;
+	int i;
+
+	for (i = 0; i < 21; i++) {
+		ttype_master_val[i] = BM_MASTER_M0;
+		ttype_nbeat_val[i] = BM_TRANS_TYPE_1BEAT;
+		ttype_nbyte_val[i] = BM_TRANS_TYPE_8Byte;
+		ttype_burst_val[i] = BM_TRANS_TYPE_BURST_INCR;
+		ttype_busid_val[i] = 0xffff;
+		ttype_rw_val[i] =  BM_TRANS_RW_DEFAULT;
+	}
+
+	ret = MET_BM_Init();
+	if (ret != 0) {
+		pr_err("MET_BM_Init failed!!!\n");
+		ret = 0;	/* will retry later */
+	} else {
+		emi_inited = 1;
+	}
+
+	kobj_emi = parent;
+
+#define	KOBJ_ATTR_ITEM(attr_name) \
+	ret = sysfs_create_file(kobj_emi, &attr_name##_attr.attr); \
+	if (ret != 0) { \
+		pr_err("Failed to create " #attr_name " in sysfs\n"); \
+		return ret; \
+	}
+	KOBJ_ATTR_LIST
+#undef	KOBJ_ATTR_ITEM
+
+	return ret;
+}
+
+static void met_emi_delete(void)
+{
+#define	KOBJ_ATTR_ITEM(attr_name) \
+	sysfs_remove_file(kobj_emi, &attr_name##_attr.attr);
+	if (kobj_emi != NULL) {
+		KOBJ_ATTR_LIST
+			kobj_emi = NULL;
+	}
+#undef	KOBJ_ATTR_ITEM
+
+	if (emi_inited)
+		MET_BM_DeInit();
+}
+
+static void met_emi_start(void)
+{
+	unsigned int bw_limiter[NIDX_BL];
+
+	if (!emi_inited) {
+		if (MET_BM_Init() != 0) {
+			met_emi.mode = 0;
+			pr_err("MET_BM_Init failed!!!\n");
+			return;
+		}
+		emi_inited = 1;
+	}
+
+	if (do_emi()) {
+		emi_init();
+		emi_stop();
+		emi_start();
+
+		/* Draw the first BW Limiter point */
+		if (bw_limiter_enable == BM_BW_LIMITER_ENABLE) {
+			emi_bw_limiter(bw_limiter);
+			ms_bw_limiter(NIDX_BL, bw_limiter);
+			/* init countdown value */
+			countdown = CNT_COUNTDOWN;
+		}
+	}
+}
+
+static void met_emi_stop(void)
+{
+	unsigned int	bw_limiter[NIDX_BL];
+
+	if (!emi_inited)
+		return;
+
+	if (met_emi_regdump == 1)
+		emi_dump_reg();
+
+	if (do_emi()) {
+		/* Draw the last BW Limiter point */
+		if (bw_limiter_enable == BM_BW_LIMITER_ENABLE) {
+			/*
+			 * Skip drawing when we just draw
+			 * the point at last polling.
+			 */
+			if (countdown < CNT_COUNTDOWN) {
+				emi_bw_limiter(bw_limiter);
+				ms_bw_limiter(NIDX_BL, bw_limiter);
+			}
+		}
+
+		emi_stop();
+		emi_uninit();
+	}
+}
+
+static void met_emi_polling(unsigned long long stamp, int cpu)
+{
+	unsigned int	emi_value[NIDX];
+	unsigned int	emi_tsct[3];
+	unsigned int	emi_ttype_value[21];
+	unsigned int	dramc_pdir_value[DRAMC_Debug_MAX_CNT*NCH];
+	unsigned int	emi_mdct_value[2];
+
+	if (!do_emi())
+		return;
+
+	/* get emi & dramc counters */
+	emi_value[0] = 0;	/* 0: pure linux MET , 0xa5: OnDieMET*/
+	emi_value[1] = 0;	/* EBM pause duration (ns)*/
+	emi_polling(emi_value+2, emi_tsct, emi_ttype_value, dramc_pdir_value, emi_mdct_value);
+
+	/* get and output BW Limiter */
+	if (bw_limiter_enable == BM_BW_LIMITER_ENABLE) {
+		unsigned int bw_limiter[NIDX_BL];
+
+		if (countdown > 0) {
+			countdown--;
+		} else {
+			emi_bw_limiter(bw_limiter);
+			ms_bw_limiter(NIDX_BL, bw_limiter);
+			/* reload countdown value */
+			countdown = CNT_COUNTDOWN;
+		}
+	}
+
+	/* output emi */
+	ms_emi(NIDX_EMI-NTTYPE + (NCNT*dram_chann_num), emi_value);
+	/* output tsct*/
+
+
+
+	if (emi_tsct_enable == 1)
+		ms_emi_tsct(3, emi_tsct);
+
+	/* output mdct*/
+	if (emi_mdct_enable == 1)
+		ms_emi_mdct(2, emi_mdct_value);
+
+	/* output dramc*/
+	if (dramc_pdir_enable == 1)
+		ms_dramc(DRAMC_Debug_MAX_CNT*dram_chann_num, dramc_pdir_value);
+
+	/* output ms_ttype */
+	if ((ttype1_16_en == BM_TTYPE1_16_ENABLE) && (ttype17_21_en == BM_TTYPE17_21_ENABLE))
+		ms_ttype(21, emi_ttype_value);
+	else if (ttype17_21_en == BM_TTYPE17_21_ENABLE)
+		ms_ttype(5, (emi_ttype_value + 16));
+
+	/* adjust MDMCU buffer */
+	if (mdmcu_sel_enable == 1)
+		MET_BM_SetMDCT_MDMCU(rd_mdmcu_rsv_num);
+}
+
+static char help[] = "  --emi                                 monitor EMI banwidth\n";
+static int emi_print_help(char *buf, int len)
+{
+	return snprintf(buf, PAGE_SIZE, help);
+}
+
+
+#define TTYPE_NAME_STR_LEN  64
+static char ttype_name[21][TTYPE_NAME_STR_LEN];
+static int emi_print_header(char *buf, int len)
+{
+	int	ret = 0;
+	int	ret_m[21];
+	int	i = 0;
+	unsigned int	dram_data_rate_MHz;
+
+	/*ttype header info*/
+	for (i = 0; i < 21; i++) {
+
+		int k;
+
+		/*busid >= 0xffff    not specific bus id , show all on specificmaster*/
+		if (ttype_busid_val[i] >= 0xffff) {
+
+			int j;
+
+			for (j = 0; j < ARRAY_SIZE(ttype_master_list_item); j++) {
+				if (ttype_master_val[i] == ttype_master_list_item[j].key) {
+					ret_m[i] = snprintf(ttype_name[i], TTYPE_NAME_STR_LEN, "ttyp%d_%s",
+						i+1, ttype_master_list_item[j].val);/*master*/
+					break;
+				}
+			}
+			if (j == ARRAY_SIZE(ttype_master_list_item))
+				ret_m[i] = snprintf(ttype_name[i], TTYPE_NAME_STR_LEN, "ttyp%d_%s",
+					i+1, "unknown");
+		} else {
+			ret_m[i] = snprintf(ttype_name[i], TTYPE_NAME_STR_LEN, "ttyp%d_%x",
+				i+1, ttype_busid_val[i]);/*busID*/
+		}
+
+		/*show beat type*/
+		for (k = 0; k < ARRAY_SIZE(ttype_nbeat_list_item); k++) {
+
+			if (ttype_nbeat_val[i] == ttype_nbeat_list_item[k].key)
+				ret_m[i] += snprintf(ttype_name[i]+ret_m[i], TTYPE_NAME_STR_LEN-ret_m[i], "_%d",
+					ttype_nbeat_list_item[k].val);/*beat*/
+		}
+
+		/*show byte type*/
+		for (k = 0; k < ARRAY_SIZE(ttype_nbyte_list_item); k++) {
+
+			if (ttype_nbyte_val[i] == ttype_nbyte_list_item[k].key)
+				ret_m[i] += snprintf(ttype_name[i]+ret_m[i], TTYPE_NAME_STR_LEN-ret_m[i], "x%d",
+					ttype_nbyte_list_item[k].val);/*byte*/
+		}
+
+		/*show burst type*/
+		for (k = 0; k < ARRAY_SIZE(ttype_burst_list_item); k++) {
+
+			if (ttype_burst_val[i] == ttype_burst_list_item[k].key)
+				ret_m[i] += snprintf(ttype_name[i]+ret_m[i], TTYPE_NAME_STR_LEN-ret_m[i], "_%s",
+					ttype_burst_list_item[k].val);/*burst*/
+		}
+
+		/*show rw type*/
+		for (k = 0; k < ARRAY_SIZE(ttype_rw_list_item); k++) {
+
+			if (ttype_rw_val[i] == ttype_rw_list_item[k].key)
+				ret_m[i] += snprintf(ttype_name[i]+ret_m[i], TTYPE_NAME_STR_LEN-ret_m[i], "_%s",
+					ttype_rw_list_item[k].val);/*rw*/
+		}
+	}
+
+	dram_chann_num = MET_EMI_GetDramChannNum();
+	/* met_dram_chann_num_header */
+	/* TBD: what is VID */
+	ret = snprintf(buf, PAGE_SIZE, "met-info [000] 0.0: met_dram_chann_num_header: %d,%d,%d,%d,%d\n",
+			dram_chann_num, DRAM_EMI_BASECLOCK_RATE, DRAM_IO_BUS_WIDTH, DRAM_DATARATE, 0);
+
+	/* metemi_func_opt for middleware */
+	ret += snprintf(buf+ret, PAGE_SIZE-ret, "met-info [000] 0.0: metemi_func_opt_header: %d\n",
+			metemi_func_opt);
+
+	dram_data_rate_MHz = get_dram_data_rate();
+	ret += snprintf(buf+ret, PAGE_SIZE-ret,
+			"met-info [000] 0.0: met_dram_clockrate: %u\n",
+			dram_data_rate_MHz);
+
+	/*master port mapping*/
+	ret += snprintf(buf+ret, PAGE_SIZE-ret,
+			"met-info [000] 0.0: met_emi_mport_map: %s,%s,%s,%s,%s,%s,%s,%s\n",
+			BM_Master_M0_name, BM_Master_M1_name, BM_Master_M2_name, BM_Master_M3_name,
+			BM_Master_M4_name, BM_Master_M5_name, BM_Master_M6_name, BM_Master_M7_name);
+
+	/* not to change by default master port sequency */
+	ret += snprintf(buf+ret, PAGE_SIZE-ret,
+			"met-info [000] 0.0: met_emi_mgroup_map: %x,%x,%x,%x\n",
+			BM_Master_GP_AP, BM_Master_GP_MM, BM_Master_GP_GPU, BM_Master_GP_PERI);
+
+	/* 1 : by ondiemet, 0: by pure linux */
+	ret += snprintf(buf+ret, PAGE_SIZE-ret,
+		       "met-info [000] 0.0: emi_use_ondiemet: %u\n",
+		       emi_use_ondiemet);
+
+	/* msel header */
+	if (msel_enable) {
+		ret += snprintf(buf+ret, PAGE_SIZE-ret,
+				"met-info [000] 0.0: met_emi_msel: %x,%x,%x\n",
+				msel_group1 & BM_MASTER_ALL,
+				msel_group2 & BM_MASTER_ALL,
+				msel_group3 & BM_MASTER_ALL);
+	} else {
+		ret += snprintf(buf+ret, PAGE_SIZE-ret,
+				"met-info [000] 0.0: met_emi_msel: %x,%x,%x\n",
+				BM_Master_GP_1_Default & BM_MASTER_ALL,
+				BM_Master_GP_2_Default & BM_MASTER_ALL,
+				BM_Master_GP_3_Default & BM_MASTER_ALL);
+	}
+
+	/* ms_emi */
+	ret += snprintf(buf+ret, PAGE_SIZE-ret,
+			"# ms_emi: TS0,TS1,GP0_WSCT,GP1_WSCT,GP2_WSCT,GP3_WSCT,");
+	ret += snprintf(buf+ret, PAGE_SIZE-ret,
+			"M0_LATENCY,M1_LATENCY,M2_LATENCY,M3_LATENCY,M4_LATENCY,M5_LATENCY,M6_LATENCY,M7_LATENCY,");
+	ret += snprintf(buf+ret, PAGE_SIZE-ret,
+			"M0_TRANS,M1_TRANS,M2_TRANS,M3_TRANS,M4_TRANS,M5_TRANS,M6_TRANS,M7_TRANS,");
+	ret += snprintf(buf+ret, PAGE_SIZE-ret,
+			"BACT,BSCT,BCNT,");
+
+	for (i = 0; i < dram_chann_num; i++) {
+		if (i != 0)
+			ret += snprintf(buf+ret, PAGE_SIZE-ret,
+				",");
+		ret += snprintf(buf+ret, PAGE_SIZE-ret,
+				"PageHit_%d,PageMiss_%d,InterBank_%d,Idle_%d,", i, i, i, i);
+		ret += snprintf(buf+ret, PAGE_SIZE-ret,
+				"mr4_%d,refresh_pop_%d,freerun_26m_%d,", i, i, i);
+		ret += snprintf(buf+ret, PAGE_SIZE-ret,
+				"read_bytes_%d,write_bytes_%d", i, i);
+	}
+	ret += snprintf(buf+ret, PAGE_SIZE-ret,
+			"\n");
+
+
+	ret += snprintf(buf+ret, PAGE_SIZE-ret,
+			"met-info [000] 0.0: met_emi_header: TS0,TS1,GP0_WSCT,GP1_WSCT,GP2_WSCT,GP3_WSCT,");
+	ret += snprintf(buf+ret, PAGE_SIZE-ret,
+			"M0_LATENCY,M1_LATENCY,M2_LATENCY,M3_LATENCY,M4_LATENCY,M5_LATENCY,M6_LATENCY,M7_LATENCY,");
+	ret += snprintf(buf+ret, PAGE_SIZE-ret,
+			"M0_TRANS,M1_TRANS,M2_TRANS,M3_TRANS,M4_TRANS,M5_TRANS,M6_TRANS,M7_TRANS,");
+	ret += snprintf(buf+ret, PAGE_SIZE-ret,
+			"BACT,BSCT,BCNT,");
+
+	for (i = 0; i < dram_chann_num; i++) {
+		if (i != 0)
+			ret += snprintf(buf+ret, PAGE_SIZE-ret,
+				",");
+		ret += snprintf(buf+ret, PAGE_SIZE-ret,
+				"PageHit_%d,PageMiss_%d,InterBank_%d,Idle_%d,", i, i, i, i);
+		ret += snprintf(buf+ret, PAGE_SIZE-ret,
+				"mr4_%d,refresh_pop_%d,freerun_26m_%d,", i, i, i);
+		ret += snprintf(buf+ret, PAGE_SIZE-ret,
+				"read_bytes_%d,write_bytes_%d", i, i);
+	}
+	ret += snprintf(buf+ret, PAGE_SIZE-ret,
+			"\n");
+
+
+	/*TSCT header*/
+	if (emi_tsct_enable == 1) {
+		ret += snprintf(buf+ret, PAGE_SIZE-ret,
+				"met-info [000] 0.0: ms_ud_sys_header: ms_emi_tsct,");
+		ret += snprintf(buf+ret, PAGE_SIZE-ret,
+				"tsct1,tsct2,tsct3,x,x,x\n");
+	}
+
+
+	/*MDCT header*/
+	if (emi_mdct_enable == 1) {
+		ret += snprintf(buf+ret, PAGE_SIZE-ret,
+				"met-info [000] 0.0: ms_ud_sys_header: ms_emi_mdct,");
+		ret += snprintf(buf+ret, PAGE_SIZE-ret,
+				"RD_ULTRA,RD_MDMCU,d,d\n");
+		ret += snprintf(buf+ret, PAGE_SIZE-ret,
+				"met-info [000] 0.0: ms_ud_sys_description: ms_emi_mdct:");
+		ret += snprintf(buf+ret, PAGE_SIZE-ret,
+				"CHART_TYPE=histogram_edge;STATISTICS_METHOD=histogram_edge;CHART_RESAMPLE_METHOD=del_dup\n");
+	}
+
+	/*ttype header*/
+	if ((ttype1_16_en == BM_TTYPE1_16_ENABLE) && (ttype17_21_en == BM_TTYPE17_21_ENABLE)) {
+		/*header = ttype1~21t*/
+		int i;
+
+		ret += snprintf(buf+ret, PAGE_SIZE-ret,	"met-info [000] 0.0: ms_ud_sys_header: ms_ttype,");
+		for (i = 0; i < 21; i++)
+			ret += snprintf(buf+ret, PAGE_SIZE-ret, "%s,", ttype_name[i]);
+
+		ret += snprintf(buf+ret, PAGE_SIZE-ret, "x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x\n");
+
+	} else if (ttype17_21_en == BM_TTYPE17_21_ENABLE) {
+		/*header = ttype17~21t*/
+		int i;
+
+		ret += snprintf(buf+ret, PAGE_SIZE-ret, "met-info [000] 0.0: ms_ud_sys_header: ms_ttype,");
+
+		for (i = 16; i < 21; i++)
+			ret += snprintf(buf+ret, PAGE_SIZE-ret, "%s,", ttype_name[i]);
+
+		ret += snprintf(buf+ret, PAGE_SIZE-ret, "x,x,x,x,x\n");
+	}
+
+	/* met_bw_limiter_header */
+	if (bw_limiter_enable == BM_BW_LIMITER_ENABLE) {
+		ret += snprintf(buf+ret, PAGE_SIZE-ret,
+				"met-info [000] 0.0: met_bw_limiter_header: CLK,");
+		ret += snprintf(buf+ret, PAGE_SIZE-ret,
+				"ARBA,ARBB,ARBC,ARBD,ARBE,ARBF,ARBG,ARBH,BWCT0,BWCT1,BWCT2,BWCT3,BWCT4,BWST0,BWST1,BWCT0_2ND,BWCT1_2ND,BWST_2ND\n");
+	}
+	/* DRAM DVFS */
+	ret += snprintf(buf+ret, PAGE_SIZE-ret,
+			"met-info [000] 0.0: ms_ud_sys_header: DRAM_DVFS,datarate(MHz),d\n");
+	ret += snprintf(buf+ret, PAGE_SIZE-ret,
+			"met-info [000] 0.0: ms_ud_sys_description: DRAM_DVFS:");
+	ret += snprintf(buf+ret, PAGE_SIZE-ret,
+			"CHART_TYPE=histogram_edge;STATISTICS_METHOD=histogram_edge;CHART_RESAMPLE_METHOD=del_dup\n");
+
+	/*PDIR met_dramc_header*/
+	if (dramc_pdir_enable == 1) {
+		ret += snprintf(buf+ret, PAGE_SIZE-ret,
+			"met-info [000] 0.0: met_dramc_header: ");
+		for (i = 0; i < dram_chann_num; i++) {
+			if (i != 0)
+				ret += snprintf(buf+ret, PAGE_SIZE-ret,
+					",");
+			ret += snprintf(buf+ret, PAGE_SIZE-ret,
+				"rk0_pre_sb_%d,rk0_pre_pd_%d,rk0_act_sb_%d,rk0_act_pd_%d,", i, i, i, i);
+			ret += snprintf(buf+ret, PAGE_SIZE-ret,
+				"rk1_pre_sb_%d,rk1_pre_pd_%d,rk1_act_sb_%d,rk1_act_pd_%d,", i, i, i, i);
+			ret += snprintf(buf+ret, PAGE_SIZE-ret,
+				"rk2_pre_sb_%d,rk2_pre_pd_%d,rk2_act_sb_%d,rk2_act_pd_%d", i, i, i, i);
+		}
+		ret += snprintf(buf+ret, PAGE_SIZE-ret, "\n");
+	}
+	return ret;
+}
+
+struct metdevice met_emi = {
+	.name			= "emi",
+	.owner			= THIS_MODULE,
+	.type			= MET_TYPE_BUS,
+	.create_subfs	= met_emi_create,
+	.delete_subfs	= met_emi_delete,
+	.cpu_related	= 0,
+	.start			= met_emi_start,
+	.stop			= met_emi_stop,
+	.timed_polling	= met_emi_polling,
+	.print_help		= emi_print_help,
+	.print_header	= emi_print_header,
+};
diff --git a/src/devtools/met-driver/4.19/mt2712/met_kernel_symbol.h b/src/devtools/met-driver/4.19/mt2712/met_kernel_symbol.h
new file mode 100644
index 0000000..10f70e1
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/met_kernel_symbol.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef MET_KERNEL_SYMBOL
+#define MET_KERNEL_SYMBOL
+
+/*lookup symbol*/
+#include <asm/cpu.h>
+#include <linux/kallsyms.h>
+#include <linux/perf_event.h>
+#include <linux/kthread.h>
+
+#if	defined(CONFIG_MET_ARM_32BIT)
+extern void met_get_cpuinfo(int cpu, struct cpuinfo_arm **cpuinfo);
+extern void (*met_get_cpuinfo_symbol)(int cpu, struct cpuinfo_arm **cpuinfo);
+#else
+extern void met_get_cpuinfo(int cpu, struct cpuinfo_arm64 **cpuinfo);
+extern void (*met_get_cpuinfo_symbol)(int cpu, struct cpuinfo_arm64 **cpuinfo);
+#endif
+
+extern void (*tracing_record_cmdline_symbol)(struct task_struct *tsk);
+extern void met_cpu_frequency(unsigned int frequency, unsigned int cpu_id);
+extern void (*met_cpu_frequency_symbol)(unsigned int frequency, unsigned int cpu_id);
+extern void (*met_arch_setup_dma_ops_symbol)(struct device *dev);
+extern int (*met_perf_event_read_local_symbol)(struct perf_event *ev, u64 *value, u64 *enable, u64 *running);
+extern struct task_struct *(*met_kthread_create_on_cpu_symbol)(int (*threadfn)(void *data),
+				void *data, unsigned int cpu,
+				const char *namefmt);
+extern int (*met_smp_call_function_single_symbol)(int cpu, smp_call_func_t func, void *info, int wait);
+
+extern void met_tracing_record_cmdline(struct task_struct *tsk);
+extern int met_reg_switch(void);
+extern int (*met_reg_switch_symbol)(void);
+extern void met_unreg_switch(void);
+extern void (*met_unreg_switch_symbol)(void);
+extern void met_arch_setup_dma_ops(struct device *dev);
+extern int met_perf_event_read_local(struct perf_event *ev, u64 *value, u64 *enable, u64 *running);
+extern struct task_struct *met_kthread_create_on_cpu(int (*threadfn)(void *data),
+				void *data, unsigned int cpu,
+				const char *namefmt);
+extern int met_smp_call_function_single(int cpu, smp_call_func_t func, void *info, int wait);
+extern void met_arch_send_call_function_single_ipi(int cpu);
+#endif	/* MET_KERNEL_SYMBOL */
diff --git a/src/devtools/met-driver/4.19/mt2712/met_main.c b/src/devtools/met-driver/4.19/mt2712/met_main.c
new file mode 100644
index 0000000..883a918
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/met_main.c
@@ -0,0 +1,293 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/hrtimer.h>
+#include <linux/cpu.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/profile.h>
+#include <linux/dcache.h>
+#include <linux/types.h>
+#include <linux/dcookies.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+
+#include <asm/irq_regs.h>
+
+#include "met_struct.h"
+#include "met_drv.h"
+#include "met_kernel_symbol.h"
+#include "interface.h"
+#include <linux/of.h>
+
+
+extern struct device_node *of_root;
+static const char *platform_name;
+
+struct cpu_type_name {
+	char full_name[32];
+	char abbr_name[8];
+};
+
+static struct cpu_type_name met_known_cpu_type[] = {
+	{"arm,cortex-a35", "CA35"},
+	{"arm,cortex-a53", "CA53"},
+	{"arm,cortex-a72", "CA72"},
+	{"arm,cortex-a73", "CA73"},
+};
+#define MET_KNOWN_CPU_TYPE_COUNT \
+	(sizeof(met_known_cpu_type)/sizeof(struct cpu_type_name))
+
+static char met_cpu_topology[64];
+
+#if	defined(CONFIG_MET_ARM_32BIT)
+void (*met_get_cpuinfo_symbol)(int cpu, struct cpuinfo_arm **cpuinfo);
+#else
+void (*met_get_cpuinfo_symbol)(int cpu, struct cpuinfo_arm64 **cpuinfo);
+#endif
+
+void (*tracing_record_cmdline_symbol)(struct task_struct *tsk);
+void (*met_cpu_frequency_symbol)(unsigned int frequency, unsigned int cpu_id);
+int (*met_reg_switch_symbol)(void);
+void (*met_unreg_switch_symbol)(void);
+
+void (*met_arch_setup_dma_ops_symbol)(struct device *dev);
+int (*met_perf_event_read_local_symbol)(struct perf_event *ev, u64 *value, u64 *enable, u64 *running);
+struct task_struct *(*met_kthread_create_on_cpu_symbol)(int (*threadfn)(void *data),
+				void *data, unsigned int cpu,
+				const char *namefmt);
+int (*met_smp_call_function_single_symbol)(int cpu, smp_call_func_t func, void *info, int wait);
+
+
+const char *met_get_platform_name(void)
+{
+	return platform_name;
+}
+EXPORT_SYMBOL(met_get_platform_name);
+
+static void get_cpu_type_name(const char *compatible, char *cpu_type)
+{
+	int i;
+
+	for (i = 0; i < MET_KNOWN_CPU_TYPE_COUNT; i++) {
+		if (!strncmp(compatible, met_known_cpu_type[i].full_name,
+					strlen(met_known_cpu_type[i].full_name)))
+			strncpy(cpu_type, met_known_cpu_type[i].abbr_name,
+					strlen(met_known_cpu_type[i].abbr_name) + 1);
+	}
+}
+
+static void met_set_cpu_topology(int core_id, int cluster_core_num)
+{
+	int i, buf_len = strlen(met_cpu_topology);
+	struct device_node *node = NULL;
+	const char *prev_cptb = NULL;
+	const char *cptb;
+	char cpu_type[16];
+
+	for (i = 0; i < cluster_core_num; i++) {
+		node = of_get_cpu_node(core_id + i, NULL);
+		if (node) {
+			cptb = of_get_property(node, "compatible", NULL);
+			if (cptb) {
+				get_cpu_type_name(cptb, cpu_type);
+				if (prev_cptb == NULL)
+					/* first write:  write core_type & core_number */
+					buf_len += snprintf(met_cpu_topology + buf_len,
+								sizeof(met_cpu_topology) - buf_len,
+								"%s:%d", cpu_type, core_id + i);
+				else if (!strncmp(prev_cptb, cptb, strlen(prev_cptb)))
+					/* cpu type is the same with before */
+					/* write core_number */
+					buf_len += snprintf(met_cpu_topology + buf_len,
+								sizeof(met_cpu_topology) - buf_len,
+								",%d", core_id + i);
+				else
+					/* cpu type is different with before */
+					/* write core_type & core_number */
+					buf_len += snprintf(met_cpu_topology + buf_len,
+								sizeof(met_cpu_topology) - buf_len,
+								"|%s:%d", cpu_type, core_id + i);
+
+				prev_cptb = cptb;
+			}
+		}
+	}
+}
+
+static int met_create_cpu_topology(void)
+{
+	int i, len;
+	struct device_node *node = NULL;
+	int start_core_id = 0;
+	int cluster_num = 0, cluster_core_num = 0;
+	char cluster_name[16];
+
+	node = of_find_node_by_name(NULL, "cpu-map");
+	if (node) {
+		cluster_num = of_get_child_count(node);
+
+		for (i = 0; i < cluster_num; i++) {
+			snprintf(cluster_name, sizeof(cluster_name), "cluster%d", i);
+			node = of_find_node_by_name(NULL, cluster_name);
+			if (node) {
+				cluster_core_num = of_get_child_count(node);
+
+				/* "|" use to separate different cluster */
+				if (i > 0) {
+					len = strlen(met_cpu_topology);
+					snprintf(met_cpu_topology + len, sizeof(met_cpu_topology) - len, "|");
+				}
+
+				met_set_cpu_topology(start_core_id, cluster_core_num);
+				start_core_id = cluster_core_num;
+			}
+		}
+	}
+
+	return strlen(met_cpu_topology);
+}
+
+static int met_kernel_symbol_get(void)
+{
+	int ret = 0;
+
+	if (met_get_cpuinfo_symbol == NULL)
+		met_get_cpuinfo_symbol = (void *)symbol_get(met_get_cpuinfo);
+	if (met_get_cpuinfo_symbol == NULL)
+		return -2;
+
+	if (tracing_record_cmdline_symbol == NULL)
+		tracing_record_cmdline_symbol = (void *)symbol_get(met_tracing_record_cmdline);
+	if (tracing_record_cmdline_symbol == NULL)
+		ret = -3;
+
+	if (met_cpu_frequency_symbol == NULL)
+		met_cpu_frequency_symbol = (void *)symbol_get(met_cpu_frequency);
+	if (met_cpu_frequency_symbol == NULL)
+		ret = -4;
+
+	if (met_reg_switch_symbol == NULL)
+		met_reg_switch_symbol = (void *)symbol_get(met_reg_switch);
+	if (met_reg_switch_symbol == NULL)
+		ret = -5;
+
+	if (met_unreg_switch_symbol == NULL)
+		met_unreg_switch_symbol = (void *)symbol_get(met_unreg_switch);
+	if (met_unreg_switch_symbol == NULL)
+		ret = -6;
+
+	if (met_arch_setup_dma_ops_symbol == NULL)
+		met_arch_setup_dma_ops_symbol = (void *)symbol_get(met_arch_setup_dma_ops);
+	if (met_arch_setup_dma_ops_symbol == NULL)
+		ret = -7;
+
+	if (met_perf_event_read_local_symbol == NULL)
+		met_perf_event_read_local_symbol = (void *)symbol_get(met_perf_event_read_local);
+	if (met_perf_event_read_local_symbol == NULL)
+		ret = -8;
+
+	if (met_kthread_create_on_cpu_symbol == NULL)
+		met_kthread_create_on_cpu_symbol = (void *)symbol_get(met_kthread_create_on_cpu);
+	if (met_kthread_create_on_cpu_symbol == NULL)
+		ret = -9;
+
+	if (met_smp_call_function_single_symbol == NULL)
+		met_smp_call_function_single_symbol = (void *)symbol_get(met_smp_call_function_single);
+	if (met_smp_call_function_single_symbol == NULL)
+		ret = -10;
+	return ret;
+}
+
+DEFINE_PER_CPU(struct met_cpu_struct, met_cpu);
+
+static int __init met_drv_init(void)
+{
+	int cpu;
+	int ret;
+	int cpu_topology_len;
+	struct met_cpu_struct *met_cpu_ptr;
+
+	for_each_possible_cpu(cpu) {
+		met_cpu_ptr = &per_cpu(met_cpu, cpu);
+		/* snprintf(&(met_cpu_ptr->name[0]), sizeof(met_cpu_ptr->name), "met%02d", cpu); */
+		met_cpu_ptr->cpu = cpu;
+	}
+
+	ret = met_kernel_symbol_get();
+	if (ret) {
+		pr_notice("[MET] met_kernel_symbol_get fail, ret = %d\n", ret);
+		return ret;
+	}
+	fs_reg();
+
+	if (of_root){
+		/*
+			mt6765.dts
+			model = "MT6765";
+			compatible = "mediatek,MT6765";
+			interrupt-parent = <&sysirq>;
+		*/
+		if (of_root->properties) {
+			of_property_read_string(of_root, of_root->properties->name, &platform_name);
+			PR_BOOTMSG("platform_name=%s\n", platform_name);
+		}
+	}
+	if (platform_name) {
+		char buf[7];
+
+		memset(buf, 0x0, 7);
+		buf[0] = 'm';
+		buf[1] = 't';
+		strncpy(&buf[2], &platform_name[11], 4);
+		met_set_platform(buf, 1);
+		PR_BOOTMSG("buf=%s\n", buf);
+	}
+
+	cpu_topology_len = met_create_cpu_topology();
+	if (cpu_topology_len)
+		met_set_topology(met_cpu_topology, 1);
+
+#ifdef MET_PLF_USE
+	core_plf_init();
+#endif
+	return 0;
+}
+
+static void __exit met_drv_exit(void)
+{
+	if (met_cpu_frequency_symbol)
+		symbol_put(met_cpu_frequency);
+	if (met_reg_switch_symbol)
+		symbol_put(met_reg_switch);
+	if (met_unreg_switch_symbol)
+		symbol_put(met_unreg_switch);
+	if (tracing_record_cmdline_symbol)
+		symbol_put(met_tracing_record_cmdline);
+	if (met_get_cpuinfo_symbol)
+		symbol_put(met_get_cpuinfo);
+
+#ifdef MET_PLF_USE
+	core_plf_exit();
+#endif
+	fs_unreg();
+
+}
+module_init(met_drv_init);
+module_exit(met_drv_exit);
+
+MODULE_AUTHOR("DT_DM5");
+MODULE_DESCRIPTION("MET_CORE");
+MODULE_LICENSE("GPL");
diff --git a/src/devtools/met-driver/4.19/mt2712/met_struct.h b/src/devtools/met-driver/4.19/mt2712/met_struct.h
new file mode 100644
index 0000000..a74ff78
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/met_struct.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MET_STRUCT_H_
+#define _MET_STRUCT_H_
+
+#include <linux/hrtimer.h>
+
+struct met_cpu_struct {
+	struct hrtimer hrtimer;
+	struct delayed_work dwork;
+/* struct kmem_cache *cachep; */
+/* struct list_head sample_head; */
+/* spinlock_t list_lock; */
+/* struct mutex list_sync_lock; */
+	int work_enabled;
+	int cpu;
+	int hrtimer_online_check;
+/* char name[16]; */
+};
+
+DECLARE_PER_CPU(struct met_cpu_struct, met_cpu);
+
+#endif				/* _MET_STRUCT_H_ */
diff --git a/src/devtools/met-driver/4.19/mt2712/met_tag.h b/src/devtools/met-driver/4.19/mt2712/met_tag.h
new file mode 100644
index 0000000..04b5e09
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/met_tag.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __MET_TAG_EX_H__
+#define __MET_TAG_EX_H__
+
+#ifdef BUILD_WITH_MET
+void force_sample(void *unused);
+#else
+#include <linux/string.h>
+#endif
+
+/* Black List Table */
+struct bltable_t {
+	struct mutex mlock;
+	/* flag - Bit31: Global ON/OFF; Bit0~30: ON/OF slot map of class_id */
+	unsigned int flag;
+	int class_id[MAX_EVENT_CLASS];
+};
+
+extern void met_sched_switch(struct task_struct *prev, struct task_struct *next);
+
+extern int tracing_mark_write(int type, unsigned int class_id,
+		const char *name, unsigned int value,
+		unsigned int value2, unsigned int value3);
+
+#endif				/* __MET_TAG_EX_H__ */
diff --git a/src/devtools/met-driver/4.19/mt2712/mips_pmu_hw.c b/src/devtools/met-driver/4.19/mt2712/mips_pmu_hw.c
new file mode 100644
index 0000000..19e836b
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/mips_pmu_hw.c
@@ -0,0 +1,438 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <asm/system.h>
+#include <linux/smp.h>
+
+#include "cpu_pmu.h"
+#include "mips_pmu_name.h"
+
+struct chip_pmu {
+	enum cpu_type_enum type;
+	struct pmu_desc **desc;
+	void *refptr;
+	const char *cpu_name;
+	unsigned int pmu_desc_size;
+	unsigned int max_hw_events;
+	unsigned int max_reg_count;
+};
+
+struct pmu_desc *mips_pmu_desc[MIPS_MAX_HWEVENTS];
+
+static struct chip_pmu chips[] = {
+	{CPU_1004K, mips_pmu_desc, (void *)mips_1004k_pmu_desc, "MIPS_1004K",
+	 MIPS_1004K_PMU_DESC_SIZE, MIPS_1004K_PMU_DESC_COUNT, PMU_1004K_MAX_HW_REGS},
+};
+
+static struct chip_pmu chip_unknown = { CPU_UNKNOWN, NULL, NULL, "Unknown CPU", 0, 0, 0 };
+
+#define CHIP_PMU_COUNT (sizeof(chips) / sizeof(struct chip_pmu))
+static struct chip_pmu *chip;
+#define M_CONFIG1_PC    (1 << 4)
+
+#define M_PERFCTL_EXL           (1  <<  0)
+#define M_PERFCTL_KERNEL        (1  <<  1)
+#define M_PERFCTL_SUPERVISOR        (1  <<  2)
+#define M_PERFCTL_USER          (1  <<  3)
+#define M_PERFCTL_INTERRUPT_ENABLE  (1  <<  4)
+#define M_PERFCTL_EVENT(event)      (((event) & 0x3ff)  << 5)
+#define M_PERFCTL_VPEID(vpe)        ((vpe)    << 16)
+
+#ifdef CONFIG_CPU_BMIPS5000
+#define M_PERFCTL_MT_EN(filter)     0
+#else				/* !CONFIG_CPU_BMIPS5000 */
+#define M_PERFCTL_MT_EN(filter)     ((filter) << 20)
+#endif				/* CONFIG_CPU_BMIPS5000 */
+
+#define    M_TC_EN_ALL          M_PERFCTL_MT_EN(0)
+#define    M_TC_EN_VPE          M_PERFCTL_MT_EN(1)
+#define    M_TC_EN_TC           M_PERFCTL_MT_EN(2)
+#define M_PERFCTL_TCID(tcid)        ((tcid)   << 22)
+#define M_PERFCTL_WIDE          (1  << 30)
+#define M_PERFCTL_MORE          (1  << 31)
+#define M_PERFCTL_TC            (1  << 30)
+
+#define M_PERFCTL_COUNT_EVENT_WHENEVER  (M_PERFCTL_EXL |        \
+		M_PERFCTL_KERNEL |      \
+		M_PERFCTL_USER |        \
+		M_PERFCTL_SUPERVISOR |      \
+		M_PERFCTL_INTERRUPT_ENABLE)
+
+#ifdef CONFIG_MIPS_MT_SMP
+#define M_PERFCTL_CONFIG_MASK       0x3fff801f
+#else
+#define M_PERFCTL_CONFIG_MASK       0x1f
+#endif
+#define M_PERFCTL_EVENT_MASK        0xfe0
+
+#define vpe_id()    0
+
+/* To get current TCID*/
+#define read_c0_tcbind() __read_32bit_c0_register($2, 2)
+
+struct cpu_hw_events {
+	unsigned int config_base[MIPS_MAX_HWEVENTS];
+	unsigned int saved_ctrl[MIPS_MAX_HWEVENTS];
+};
+
+DEFINE_PER_CPU(struct cpu_hw_events, cpu_hw_events) = {
+	.config_base = {
+	0, 0, 0, 0}, .saved_ctrl = {
+0, 0, 0, 0},};
+
+static enum cpu_type_enum mips_get_ic(void)
+{
+	unsigned int value = current_cpu_type();
+
+	/* pr_debug("ic value: %X\n", value); */
+	return value;
+}
+
+static int __n_counters(void)
+{
+	if (!(read_c0_config1() & M_CONFIG1_PC))
+		return 0;
+	if (!(read_c0_perfctrl0() & M_PERFCTL_MORE))
+		return 1;
+	if (!(read_c0_perfctrl1() & M_PERFCTL_MORE))
+		return 2;
+	if (!(read_c0_perfctrl2() & M_PERFCTL_MORE))
+		return 3;
+
+	return 4;
+}
+
+static int n_counters(void)
+{
+	int counters;
+
+	switch (current_cpu_type()) {
+	case CPU_R10000:
+		counters = 2;
+		break;
+	case CPU_R12000:
+	case CPU_R14000:
+		counters = 4;
+		break;
+	default:
+		counters = __n_counters();
+		break;
+	}
+
+	return counters;
+}
+
+static int mips_pmu_hw_get_counters(void)
+{
+	int count = n_counters();
+
+	/* pr_debug("pmu hw event nr: %d\n", count); */
+	return count;
+}
+
+static unsigned int mipsxx_pmu_swizzle_perf_idx(unsigned int idx)
+{
+	if (vpe_id() == 1)
+		idx = (idx + 2) & 3;
+	return idx;
+}
+
+static void mipsxx_pmu_write_counter(unsigned int idx, u64 val)
+{
+	idx = mipsxx_pmu_swizzle_perf_idx(idx);
+
+	switch (idx) {
+	case 0:
+		write_c0_perfcntr0(val);
+		return;
+	case 1:
+		write_c0_perfcntr1(val);
+		return;
+	case 2:
+		write_c0_perfcntr2(val);
+		return;
+	case 3:
+		write_c0_perfcntr3(val);
+		return;
+	}
+}
+
+static u64 mipsxx_pmu_read_counter(unsigned int idx)
+{
+	idx = mipsxx_pmu_swizzle_perf_idx(idx);
+
+	switch (idx) {
+	case 0:
+		/*
+		 * The counters are unsigned, we must cast to truncate
+		 * off the high bits.
+		 */
+		return (u32) read_c0_perfcntr0();
+	case 1:
+		return (u32) read_c0_perfcntr1();
+	case 2:
+		return (u32) read_c0_perfcntr2();
+	case 3:
+		return (u32) read_c0_perfcntr3();
+	default:
+		WARN_ONCE(1, "Invalid performance counter number (%d)\n", idx);
+		return 0;
+	}
+}
+
+
+static unsigned int mipsxx_pmu_read_control(unsigned int idx)
+{
+	idx = mipsxx_pmu_swizzle_perf_idx(idx);
+
+	switch (idx) {
+	case 0:
+		return read_c0_perfctrl0();
+	case 1:
+		return read_c0_perfctrl1();
+	case 2:
+		return read_c0_perfctrl2();
+	case 3:
+		return read_c0_perfctrl3();
+	default:
+		WARN_ONCE(1, "Invalid performance counter number (%d)\n", idx);
+		return 0;
+	}
+}
+
+static void mipsxx_pmu_write_control(unsigned int idx, unsigned int val)
+{
+	idx = mipsxx_pmu_swizzle_perf_idx(idx);
+
+	switch (idx) {
+	case 0:
+		write_c0_perfctrl0(val);
+		return;
+	case 1:
+		write_c0_perfctrl1(val);
+		return;
+	case 2:
+		write_c0_perfctrl2(val);
+		return;
+	case 3:
+		write_c0_perfctrl3(val);
+		return;
+	}
+}
+
+static int mipsxx_pmu_get_vpeid(void)
+{
+	return read_c0_tcbind() & 0xF;
+}
+
+static void mipsxx_pmu_reset_counters(int idx)
+{
+	switch (idx) {
+	case 3:
+		mipsxx_pmu_write_control(3, 0);
+		mipsxx_pmu_write_counter(3, 0);
+		break;
+	case 2:
+		mipsxx_pmu_write_control(2, 0);
+		mipsxx_pmu_write_counter(2, 0);
+		break;
+	case 1:
+		mipsxx_pmu_write_control(1, 0);
+		mipsxx_pmu_write_counter(1, 0);
+		break;
+	case 0:
+		mipsxx_pmu_write_control(0, 0);
+		mipsxx_pmu_write_counter(0, 0);
+		break;
+	}
+}
+
+static void mipsxx_pmu_enable_event(int idx, int event)
+{
+	struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
+	unsigned long flags;
+
+	WARN_ON(idx < 0 || idx >= chip->max_hw_events);
+	cpuc->saved_ctrl[idx] = M_PERFCTL_EVENT(event & 0xff) |
+	    M_PERFCTL_VPEID(mipsxx_pmu_get_vpeid()) |
+	    (cpuc->config_base[idx] & M_PERFCTL_CONFIG_MASK);
+#ifdef CONFIG_CPU_BMIPS5000
+	/* if (IS_ENABLED(CONFIG_CPU_BMIPS5000)) */
+	/* enable the counter for the calling thread */
+	cpuc->saved_ctrl[idx] |= (1 << (12 + vpe_id())) | M_PERFCTL_TC;
+#endif
+	/*
+	 * To enable pmu count
+	 */
+	local_irq_save(flags);
+	mipsxx_pmu_write_control(idx, cpuc->saved_ctrl[idx]);
+	local_irq_restore(flags);
+}
+
+static void mipsxx_pmu_disable_event(int idx)
+{
+	struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
+	unsigned long flags;
+
+	/* WARN_ON(idx < 0 || idx >= mipspmu.num_counters); */
+	WARN_ON(idx < 0 || idx >= chip->max_hw_events);
+
+	local_irq_save(flags);
+	cpuc->saved_ctrl[idx] = mipsxx_pmu_read_control(idx) & ~M_PERFCTL_COUNT_EVENT_WHENEVER;
+	mipsxx_pmu_write_control(idx, cpuc->saved_ctrl[idx]);
+	local_irq_restore(flags);
+}
+
+static int mips_pmu_hw_get_event_desc(int idx, int event, char *event_desc)
+{
+	int i;
+
+	if (event_desc == NULL) {
+		pr_debug("event_desc is NULL\n");
+		return -1;
+	}
+
+	for (i = 0; i < chip->max_reg_count; i++) {
+		if (chip->desc[idx][i].event == event) {
+			strncpy(event_desc, chip->desc[idx][i].name, MXSIZE_PMU_DESC - 1);
+			break;
+		}
+	}
+	if (i == chip->max_reg_count)
+		return -1;
+
+	return 0;
+}
+
+
+static int mips_pmu_hw_check_event(struct met_pmu *pmu, int idx, int event)
+{
+	int i;
+
+	/* to check index over run */
+	if (!chip)
+		return -1;
+
+	if (idx >= chip->max_hw_events)
+		return -1;
+
+	for (i = 0; i < chip->max_reg_count; i++) {
+		if (chip->desc[idx][i].event == event)
+			break;
+	}
+	if (i == chip->max_reg_count)
+		return -1;
+
+	return 0;
+}
+
+static void mips_pmu_hw_start(struct met_pmu *pmu, int count)
+{
+	int i;
+	int generic = count - 1;
+	struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
+
+	/* pr_debug("hw_start generic: %d\n", generic); */
+	for (i = 0; i < generic; i++) {
+		/* init config */
+		cpuc->config_base[i] = 0;
+		cpuc->config_base[i] |= M_TC_EN_VPE;
+		cpuc->config_base[i] |= M_PERFCTL_USER;
+		cpuc->config_base[i] |= M_PERFCTL_KERNEL;
+		cpuc->config_base[i] |= M_PERFCTL_EXL;
+		cpuc->config_base[i] |= M_PERFCTL_SUPERVISOR;
+		cpuc->config_base[i] &= M_PERFCTL_CONFIG_MASK;
+		 /**/ mipsxx_pmu_reset_counters(i);
+		if (pmu[i].mode == MODE_POLLING)
+			mipsxx_pmu_enable_event(i, pmu[i].event);
+	}
+	if (pmu[count - 1].mode == MODE_POLLING)
+		pr_debug("%s %d BUG!!! index over run!!\n", __func__, __LINE__);
+}
+
+static void mips_pmu_hw_stop(int count)
+{
+	int idx = 0;
+	int generic = count - 1;
+	/* pr_debug("reset %d\n", generic); */
+	for (idx = 0; idx < generic; idx++) {
+		mipsxx_pmu_reset_counters(idx);
+		mipsxx_pmu_disable_event(idx);
+	}
+}
+
+
+static unsigned int mips_pmu_hw_polling(struct met_pmu *pmu, int count, unsigned int *pmu_value)
+{
+	int i, cnt = 0;
+	int generic = count - 1;
+
+	for (i = 0; i < generic; i++) {
+		if (pmu[i].mode == MODE_POLLING) {
+			pmu_value[cnt] = mipsxx_pmu_read_counter(i);
+			cnt++;
+			mipsxx_pmu_reset_counters(i);
+			mipsxx_pmu_enable_event(i, pmu[i].event);
+		}
+	}
+	if (pmu[count - 1].mode == MODE_POLLING) {
+		pr_debug("%s %d BUG!!! index over run!!\n", __func__, __LINE__);
+		pmu_value[cnt] = 0xFFFF;
+		cnt++;
+	}
+
+	return cnt;
+}
+
+
+
+struct cpu_pmu_hw mips_pmu = {
+	.name = "mips_pmu",
+	.get_event_desc = mips_pmu_hw_get_event_desc,
+	.check_event = mips_pmu_hw_check_event,
+	.start = mips_pmu_hw_start,
+	.stop = mips_pmu_hw_stop,
+	.polling = mips_pmu_hw_polling,
+};
+
+struct cpu_pmu_hw *cpu_pmu_hw_init(void)
+{
+	int i = 0;
+	enum cpu_type_enum type;
+	int pmu_hw_count = 0;
+
+	type = mips_get_ic();
+
+	if (CPU_UNKNOWN == type || CPU_LAST == type) {
+		chip = &chip_unknown;
+		return NULL;
+	}
+	for (i = 0; i < CHIP_PMU_COUNT; i++) {
+		if (chips[i].type == type) {
+			chip = &(chips[i]);
+			break;
+		}
+	}
+	if (i == CHIP_PMU_COUNT) {
+		chip = &chip_unknown;
+		return NULL;
+	}
+
+	pmu_hw_count = mips_pmu_hw_get_counters();
+	for (i = 0; i < pmu_hw_count; i++)
+		chip->desc[i] = chip->refptr + (chip->pmu_desc_size * i);
+
+	mips_pmu.nr_cnt = pmu_hw_count + 1;
+	mips_pmu.cpu_name = chip->cpu_name;
+	return &mips_pmu;
+}
diff --git a/src/devtools/met-driver/4.19/mt2712/mips_pmu_name.h b/src/devtools/met-driver/4.19/mt2712/mips_pmu_name.h
new file mode 100644
index 0000000..d7ea26a
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/mips_pmu_name.h
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+
+#ifndef _MIPS_PMU_NAME_H_
+#define _MIPS_PMU_NAME_H_
+
+/* MIPS 1004K */
+#define MIPS_MAX_HWEVENTS		(4)
+
+#define PMU_1004K_MAX_HW_REGS	(128)
+struct pmu_desc mips_1004k_pmu_desc[][PMU_1004K_MAX_HW_REGS] = {
+	/* COUNT 0 */
+	{
+	 {0, "CPU_CYCLES"},
+	 {1, "CPU_INST"},
+	 {2, "BRANCH_INSNS"},
+	 {3, "JR_31_INSNS"},
+	 {4, "JR_NON_31_INSNS"},
+	 {5, "ITLB_ACCESSES"},
+	 {6, "DTLB_ACCESSES"},
+	 {7, "JTLB_INSN_ACCESSES"},
+	 {8, "JTLB_DATA_ACCESSES"},
+	 {9, "ICACHE_ACCESSES"},
+	 {10, "DCACHE_ACCESSES"},
+	 {11, "DCACHE_MISSES"},
+	 {12, "RESERVED"},
+	 {13, "STORE_MISS_INSNS"},
+	 {14, "INTEGER_INSNS"},
+	 {15, "LOAD_INSNS"},
+	 {16, "J_JAL_INSNS"},
+	 {17, "NO_OPS_INSNS"},
+	 {18, "ALL_STALLS"},
+	 {19, "SC_INSNS"},
+	 {20, "PREFETCH_INSNS"},
+	 {21, "L2_CACHE_WRITEBACKS"},
+	 {22, "L2_CACHE_MISSES"},
+	 {23, "EXCEPTIONS_TAKEN"},
+	 {24, "CACHE_FIXUP_CYCLES"},
+	 {25, "IFU_STALLS"},
+	 {26, "DSP_INSNS"},
+	 {27, "RESERVED"},
+	 {28, "POLICY_EVENTS"},
+	 {29, "ISPRAM_EVENTS"},
+	 {30, "COREEXTEND_EVENTS"},
+	 {31, "YIELD_EVENTS"},
+	 {32, "ITC_LOADS"},
+	 {33, "UNCACHED_LOAD_INSNS"},
+	 {34, "FORK_INSNS"},
+	 {35, "CP2_ARITH_INSNS"},
+	 {36, "INTERVENTION_STALLS"},
+	 {37, "ICACHE_MISS_STALLS"},
+	 {38, "RESERVED"},
+	 {39, "DCACHE_MISS_CYCLES"},
+	 {40, "UNCACHED_STALLS"},
+	 {41, "MDU_STALLS"},
+	 {42, "CP2_STALLS"},
+	 {43, "ISPRAM_STALLS"},
+	 {44, "CACHE_INSN_STALLS"},
+	 {45, "LOAD_USE_STALLS"},
+	 {46, "INTERLOCK_STALLS"},
+	 {47, "RELAX_STALLS"},
+	 {48, "IFU_FB_FULL_REFETCHES"},
+	 {49, "EJTAG_INSN_TRIGGERS"},
+	 {50, "FSB_LESS_25_FULL"},
+	 {51, "FSB_OVER_50_FULL"},
+	 {52, "LDQ_LESS_25_FULL"},
+	 {53, "LDQ_OVER_50_FULL"},
+	 {54, "WBB_LESS_25_FULL"},
+	 {55, "WBB_OVER_50_FULL"},
+	 {56, "INTERVENTION_HIT_COUNT"},
+	 {57, "INVALIDATE_INTERVENTION_COUNT"},
+	 {58, "EVICTION_COUNT"},
+	 {59, "MESI_INVAL_COUNT"},
+	 {60, "MESI_MODIFIED_COUNT"},
+	 {61, "SELF_INTERVENTION_LATENCY"},
+	 {62, "READ_RESPONSE_LATENCY"},
+	 {63, "RESERVED"},
+	 {64, "SI_PCEVENT1"},
+	 {65, "SI_PCEVENT3"},
+	 {66, "SI_PCEVENT5"},
+	 {67, "SI_PCEVENT7"},
+	 {-1, "RESERVED"},
+	 /* 68 - 127 for Reserved */
+	 },
+	/* COUNT 1 */
+	{
+	 {0, "CPU_CYCLES"},
+	 {1, "CPU_INST"},
+	 {2, "MISPREDICTED_BRANCH_INSNS"},
+	 {3, "JR_31_MISPREDICTIONS"},
+	 {4, "JR_31_NO_PREDICTIONS"},
+	 {5, "ITLB_MISSES"},
+	 {6, "DTLB_MISSES"},
+	 {7, "JTLB_INSN_MISSES"},
+	 {8, "JTLB_DATA_MISSES"},
+	 {9, "ICACHE_MISSES"},
+	 {10, "DCACHE_WRITEBACKS"},
+	 {11, "DCACHE_MISSES"},
+	 {12, "RESERVED"},
+	 {13, "LOAD_MISS_INSNS"},
+	 {14, "FPU_INSNS"},
+	 {15, "STORE_INSNS"},
+	 {16, "MIPS16_INSNS"},
+	 {17, "INT_MUL_DIV_INSNS"},
+	 {18, "REPLAYED_INSNS"},
+	 {19, "SC_INSNS_FAILED"},
+	 {20, "CACHE_HIT_PREFETCH_INSNS"},
+	 {21, "L2_CACHE_ACCESSES"},
+	 {22, "L2_CACHE_SINGLE_BIT_ERRORS"},
+	 {23, "SINGLE_THREADED_CYCLES"},
+	 {24, "REFETCHED_INSNS"},
+	 {25, "ALU_STALLS"},
+	 {26, "ALU_DSP_SATURATION_INSNS"},
+	 {27, "MDU_DSP_SATURATION_INSNS"},
+	 {28, "CP2_EVENTS"},
+	 {29, "DSPRAM_EVENTS"},
+	 {30, "RESERVED"},
+	 {31, "ITC_EVENT"},
+	 {33, "UNCACHED_STORE_INSNS"},
+	 {34, "YIELD_IN_COMP"},
+	 {35, "CP2_TO_FROM_INSNS"},
+	 {36, "INTERVENTION_MISS_STALLS"},
+	 {37, "DCACHE_MISS_STALLS"},
+	 {38, "RESERVED"},
+	 /* 38 was listed in OPROFILE web page, but not listed 1004k mips spec */
+	 /* {38, "FSB_INDEX_CONFLICT_STALLS"}, */
+	 {39, "L2_CACHE_MISS_CYCLES"},
+	 {40, "ITC_STALLS"},
+	 {41, "FPU_STALLS"},
+	 {42, "COREEXTEND_STALLS"},
+	 {43, "DSPRAM_STALLS"},
+	 {45, "ALU_TO_AGEN_STALLS"},
+	 {46, "MISPREDICTION_STALLS"},
+	 {47, "RESERVED"},
+	 {48, "FB_ENTRY_ALLOCATED_CYCLES"},
+	 {49, "EJTAG_DATA_TRIGGERS"},
+	 {50, "FSB_25_50_FULL"},
+	 {51, "FSB_FULL_STALLS"},
+	 {52, "LDQ_25_50_FULL"},
+	 {53, "LDQ_FULL_STALLS"},
+	 {54, "WBB_25_50_FULL"},
+	 {55, "WBB_FULL_STALLS"},
+	 {56, "INTERVENTION_COUNT"},
+	 {57, "INVALID_INTERVENT_HIT_CNT"},
+	 {58, "WRITEBACK_COUNT"},
+	 {59, "MESI_EXCLUSIVE_COUNT"},
+	 {60, "MESI_SHARED_COUNT"},
+	 {61, "SELF_INTERVENTION_COUNT"},
+	 {62, "READ_RESPONSE_COUNT"},
+	 {63, "RESERVED"},
+	 {64, "SI_PCEVENT0"},
+	 {65, "SI_PCEVENT2"},
+	 {66, "SI_PCEVENT4"},
+	 {67, "SI_PCEVENT6"},
+	 {-1, "RESERVED"},
+	 },
+};
+
+#define MIPS_1004K_PMU_DESC_SIZE (sizeof(mips_1004k_pmu_desc[0]))
+#define MIPS_1004K_PMU_DESC_COUNT (sizeof(mips_1004k_pmu_desc) / MIPS_1004K_PMU_DESC_SIZE)
+
+#endif				/* _V8_PMU_NAME_H_ */
diff --git a/src/devtools/met-driver/4.19/mt2712/mtk_dramc.c b/src/devtools/met-driver/4.19/mt2712/mtk_dramc.c
new file mode 100644
index 0000000..22de7af
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/mtk_dramc.c
@@ -0,0 +1,511 @@
+/* Copyright Statement:
+ *
+ * This software/firmware and related documentation ("MediaTek Software") are
+ * protected under relevant copyright laws. The information contained herein
+ * is confidential and proprietary to MediaTek Inc. and/or its licensors.
+ * Without the prior written permission of MediaTek inc. and/or its licensors,
+ * any reproduction, modification, use or disclosure of MediaTek Software,
+ * and information contained herein, in whole or in part, shall be strictly prohibited.
+ */
+/* MediaTek Inc. (C) 2012. All rights reserved.
+ *
+ * BY OPENING THIS FILE, RECEIVER HEREBY UNEQUIVOCALLY ACKNOWLEDGES AND AGREES
+ * THAT THE SOFTWARE/FIRMWARE AND ITS DOCUMENTATIONS ("MEDIATEK SOFTWARE")
+ * RECEIVED FROM MEDIATEK AND/OR ITS REPRESENTATIVES ARE PROVIDED TO RECEIVER ON
+ * AN "AS-IS" BASIS ONLY. MEDIATEK EXPRESSLY DISCLAIMS ANY AND ALL WARRANTIES,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NONINFRINGEMENT.
+ * NEITHER DOES MEDIATEK PROVIDE ANY WARRANTY WHATSOEVER WITH RESPECT TO THE
+ * SOFTWARE OF ANY THIRD PARTY WHICH MAY BE USED BY, INCORPORATED IN, OR
+ * SUPPLIED WITH THE MEDIATEK SOFTWARE, AND RECEIVER AGREES TO LOOK ONLY TO SUCH
+ * THIRD PARTY FOR ANY WARRANTY CLAIM RELATING THERETO. RECEIVER EXPRESSLY ACKNOWLEDGES
+ * THAT IT IS RECEIVER'S SOLE RESPONSIBILITY TO OBTAIN FROM ANY THIRD PARTY ALL PROPER LICENSES
+ * CONTAINED IN MEDIATEK SOFTWARE. MEDIATEK SHALL ALSO NOT BE RESPONSIBLE FOR ANY MEDIATEK
+ * SOFTWARE RELEASES MADE TO RECEIVER'S SPECIFICATION OR TO CONFORM TO A PARTICULAR
+ * STANDARD OR OPEN FORUM. RECEIVER'S SOLE AND EXCLUSIVE REMEDY AND MEDIATEK'S ENTIRE AND
+ * CUMULATIVE LIABILITY WITH RESPECT TO THE MEDIATEK SOFTWARE RELEASED HEREUNDER WILL BE,
+ * AT MEDIATEK'S OPTION, TO REVISE OR REPLACE THE MEDIATEK SOFTWARE AT ISSUE,
+ * OR REFUND ANY SOFTWARE LICENSE FEES OR SERVICE CHARGE PAID BY RECEIVER TO
+ * MEDIATEK FOR SUCH MEDIATEK SOFTWARE AT ISSUE.
+ *
+ * The following software/firmware and/or related documentation ("MediaTek Software")
+ * have been modified by MediaTek Inc. All revisions are subject to any receiver's
+ * applicable license agreements with MediaTek Inc.
+ */
+
+#include <linux/io.h>
+#include <asm/cacheflush.h>
+#include <asm/uaccess.h>
+/* #include <asm/system.h> */
+#include "mtk_dramc.h"
+//#include "x_hal_io.h"
+
+/* #define DRAMC_DEBUG */
+#ifndef DRAMC_DEBUG
+#define DRAMC_LOG(fmt, ...)
+#else
+#define DRAMC_LOG(fmt, arg...) printk("%s:%d:"fmt, __FILE__, __LINE__,##arg);
+#endif
+
+/* #define TEST_AGENT */
+
+
+#if 1
+struct dramc_desc_t dramc_desc[DRAMC_MX_CHANNUM][DRAMC_MX_GRPNUM][DRAMC_MX_NUM_IN_AGRP] = DRAMC_AGENT_TABLE;
+#else
+struct dramc_desc_t dramc_desc[DRAMC_MX_CHANNUM][DRAMC_MX_GRPNUM][DRAMC_MX_NUM_IN_AGRP] =
+{
+/* 	/////////////////////////////////////////////////////////
+	define channel A releationship between group and name
+	///////////////////////////////////////////////////////// */
+	{
+		{
+			/* GRP 1 */
+			{0, 	"00_audio"},
+			{1, 	"01_demux/gcpu/ddi"},
+			{2, 	"02_vbi/3d/tve/dolby"},
+			{3, 	"03_xpscaler_ip/tddc"},
+			{4, 	"04_mib"},
+			{5, 	"05_b2r"},
+			{6, 	"06_cpu"},
+			{7, 	"07_scpos"},
+			{8,	"08_vdec_vld"},
+			{9, 	"09_audio_dsp1"},
+			{10, 	"10_3d_gpu"},
+			{11,	"11_2d_graph/jpgdec/osd/gfx_imgrsz/2d_graph_cmd/irt_dma/png"},
+			{12,	"12_ethernet/demod_isdbt/ci_spi/rs232/usb2/usb3/usb3_d"},
+			{13,	"13_osd"},
+			{14,	"14_venc/vp8_encoder"},
+			{15,	"15_test0/audio_dsp0"},
+			{-1,	""}
+		},
+		{
+			/* GRP 2 */
+			{16,	"16_vdec_lat"},
+			{17,	"17_mmu"},
+			{18,	"18_memc"},
+			{19,	"19_video_imgrsz0/video_imgrsz2"},
+			{20,	"20_arm11"},
+			{21,	"21_msdc/emmc/gdma"},
+			{22,	"22_vdec_mc"},
+			{30,	"30_agent_30"},
+			{-1,	""}
+		},
+		{
+			/* GRP 3 */
+			{23,	"23_cpu_bim_read"},
+			{24,	"24_demux/gcpu/ddi "},
+			{25,	"25_nfi_dma/sfalsh_dma/lzhs/ci_spi"},
+			{26,	"26_rs232"},
+			{27,	"27_agent_27"},
+			{28,	"28_agent_28"},
+			{29,	"29_agent_29"},
+			{31,	"31_agent_31"},
+			{-1,	""}
+		}
+	},
+
+/* 	///////////////////////////////////////////////////////////
+	define channel B releationship between group and name
+	///////////////////////////////////////////////////////// */
+	{
+		{
+			/* GRP 1 */
+			{0, 	"00_audio"},
+			{1, 	"01_demux/gcpu/ddi"},
+			{2, 	"02_vbi/3d/tve/dolby"},
+			{3, 	"03_xpscaler_ip/tddc"},
+			{4, 	"04_mib"},
+			{5, 	"05_b2r"},
+			{6, 	"06_cpu"},
+			{7, 	"07_scpos"},
+			{8,	"08_vdec_vld"},
+			{9, 	"09_audio_dsp1"},
+			{10, 	"10_3d_gpu"},
+			{11,	"11_2d_graph/jpgdec/osd/gfx_imgrsz/2d_graph_cmd/irt_dma/png"},
+			{12,	"12_ethernet/demod_isdbt/ci_spi/rs232/usb2/usb3/usb3_d"},
+			{13,	"13_osd"},
+			{14,	"14_venc/vp8_encoder"},
+			{15,	"15_test0/audio_dsp0"},
+			{-1,	""}
+		},
+		{
+			/* GRP 2 */
+			{16,	"16_vdec_lat"},
+			{17,	"17_mmu"},
+			{18,	"18_memc"},
+			{19,	"19_video_imgrsz0/video_imgrsz2"},
+			{20,	"20_arm11"},
+			{21,	"21_msdc/emmc/gdma"},
+			{22,	"22_vdec_mc"},
+			{30,	"30_agent_30"},
+			{-1,	""}
+		},
+		{
+			/* GRP 3 */
+			{23,	"23_cpu_bim_read"},
+			{24,	"24_demux/gcpu/ddi "},
+			{25,	"25_nfi_dma/sfalsh_dma/lzhs/ci_spi"},
+			{26,	"26_rs232"},
+			{27,	"27_agent_27"},
+			{28,	"28_agent_28"},
+			{29,	"29_agent_29"},
+			{31,	"31_agent_31"},
+			{-1,	""}
+		}
+	},
+
+/* 	///////////////////////////////////////////////////////////
+	define channel C releationship between group and name
+	/////////////////////////////////////////////////////////// */
+	{
+		{
+			/* GRP 1 */
+			{0, 	"00_audio"},
+			{1, 	"01_demux/gcpu/ddi"},
+			{2, 	"02_vbi/3d/tve/dolby"},
+			{3, 	"03_xpscaler_ip/tddc"},
+			{4, 	"04_mib"},
+			{5, 	"05_b2r"},
+			{6, 	"06_cpu"},
+			{7, 	"07_scpos"},
+			{8,	"08_vdec_vld"},
+			{9, 	"09_audio_dsp1"},
+			{10, 	"10_3d_gpu"},
+			{11,	"11_2d_graph/jpgdec/osd/gfx_imgrsz/2d_graph_cmd/irt_dma/png"},
+			{12,	"12_ethernet/demod_isdbt/ci_spi/rs232/usb2/usb3/usb3_d"},
+			{13,	"13_osd"},
+			{14,	"14_venc/vp8_encoder"},
+			{15,	"15_test0/audio_dsp0"},
+			{-1,	""}
+		},
+		{
+			/* GRP 2 */
+			{16,	"16_vdec_lat"},
+			{17,	"17_mmu"},
+			{18,	"18_memc"},
+			{19,	"19_video_imgrsz0/video_imgrsz2"},
+			{20,	"20_arm11"},
+			{21,	"21_msdc/emmc/gdma"},
+			{22,	"22_vdec_mc"},
+			{30,	"30_agent_30"},
+			{-1,	""}
+		},
+		{
+			/* GRP 3 */
+			{23,	"23_cpu_bim_read"},
+			{24,	"24_demux/gcpu/ddi "},
+			{25,	"25_nfi_dma/sfalsh_dma/lzhs/ci_spi"},
+			{26,	"26_rs232"},
+			{27,	"27_agent_27"},
+			{28,	"28_agent_28"},
+			{29,	"29_agent_29"},
+			{31,	"31_agent_31"},
+			{-1,	""}
+		}
+	}
+
+};
+#endif
+
+static unsigned long dramc_base[DRAMC_MX_CHANNUM] =
+{
+	MET_DRAMC0_BASE,
+};
+
+/*
+force to 32 bit means current is 16 bit
+not force to 32 bit means current is 32 bit
+*/
+static unsigned long dramc_ext32_off[DRAMC_MX_CHANNUM] =
+{
+	DRAM_CHA_FORCE32,
+};
+
+
+static inline unsigned int dramc_reg_read(unsigned long addr)
+{
+#if 0
+#ifdef CONFIG_64BIT
+	unsigned int value = 0;
+	value = HAL_READ32((void*)addr);
+#else
+	unsigned int value = readl((void*)addr);
+#endif
+#endif
+
+	unsigned int value = readl((void*)addr);
+
+	mb();
+	return value;
+}
+
+static inline void dramc_reg_write(unsigned long addr, unsigned int value)
+{
+#if 0
+	/* make sure writel() be completed before outer_sync() */
+#ifdef CONFIG_64BIT
+	HAL_WRITE32((void*)addr, value);
+#else
+	writel(value, (void*)addr);
+#endif
+#endif
+
+	writel(value, (void*)addr);
+	mb();
+}
+
+#define DRAMC_SET_VALUE(target, value, shift, bit) \
+do { \
+	volatile u32 temp = dramc_reg_read(target); \
+	u32 mask1 = (~(((0xFFFFFFFF >> (32 - bit))<< shift))); \
+	u32 mask2 = ((0xFFFFFFFF >> (32 - bit))<< shift); \
+	dramc_reg_write(target,(temp & mask1) | ((value << shift) & mask2)); \
+} while (0)
+
+void DRAMC_Init(int chan)
+{
+	unsigned long base = dramc_base[chan];
+	volatile u32 temp_reg = 0;
+
+	DRAMC_LOG("%s chann: %d ext_32_value: %x ext_32bit: %d\n", __FUNCTION__, chan, dramc_ext32_off[chan], DRMAC_IS_FORCE32(dramc_ext32_off[chan]));
+	/* determine the number of dram for the corresponding dramc */
+	DRAMC_SET_VALUE(DRAMC_MON_BM(base),  DRMAC_IS_FORCE32(dramc_ext32_off[chan]), DRAMC_BM_DMBW32B_SHIFT, 1);
+
+	/* disable all group */
+	DRAMC_SET_VALUE(DRAMC_MON_BM(base), 0, DRAMC_BM_GROUP1_ENABLE_SHIFT, 1);
+	DRAMC_SET_VALUE(DRAMC_MON_BM(base), 0, DRAMC_BM_GROUP2_ENABLE_SHIFT, 1);
+	DRAMC_SET_VALUE(DRAMC_MON_BM(base), 0, DRAMC_BM_GROUP3_ENABLE_SHIFT, 1);
+	DRAMC_SET_VALUE(DRAMC_MON_BM(base), 0, DRAMC_BM_FREEZE_SHIFT, 1);
+	temp_reg = dramc_reg_read(DRAMC_MON_BM(base));
+	DRAMC_LOG("%s MON_BM: %X\n", __FUNCTION__, temp_reg);
+
+#ifdef TEST_AGENT
+	u32 reg;
+	reg = base + 0;
+	dramc_reg_write(reg, 0xC050EF00);
+
+	reg = base + 0x100;
+	if (0 == chan)
+		dramc_reg_write(reg, 0x3C553000);
+	else if (1 == chan)
+		dramc_reg_write(reg, 0x5FFD0000);
+	else if (2 == chan)
+		dramc_reg_write(reg, 0x85D20000);
+
+	reg = base + 0x104;
+	dramc_reg_write(reg, 0x8000);
+
+	reg = base + 0x118;
+	dramc_reg_write(reg, 0x0600110D);
+	dramc_reg_write(reg, 0x1600110D);
+
+	reg = base + 0x160;
+	temp_reg = dramc_reg_read(reg);
+#endif
+}
+
+void DRAMC_MaxLtcyMode(int chan, int enable)
+{
+	unsigned long base = dramc_base[chan];
+
+	if (1 == enable) {
+		DRAMC_SET_VALUE(DRAMC_MON_BM(base), 0, DRAMC_BM_PACNTEN, 1);
+		DRAMC_SET_VALUE(DRAMC_MON_BM(base), 1, DRAMC_BM_REQCNTEN, 1);
+	} else {
+		DRAMC_SET_VALUE(DRAMC_MON_BM(base), 0, DRAMC_BM_PACNTEN, 1);
+		DRAMC_SET_VALUE(DRAMC_MON_BM(base), 0, DRAMC_BM_REQCNTEN, 1);
+	}
+}
+
+void DRAMC_Enable(int chan, int group, int agent)
+{
+	unsigned long base = dramc_base[chan];
+	volatile u32 temp_reg = 0;
+
+	/* disable all group */
+	switch (group) {
+	case 1:
+		DRAMC_SET_VALUE(DRAMC_MON_BM(base), agent, DRAMC_BM_GROUP1_AGENT_ID_SHIFT, DRAMC_BM_GROUP1_AGENT_ID_LEN);
+		DRAMC_SET_VALUE(DRAMC_MON_BM(base), 1, DRAMC_BM_GROUP1_ENABLE_SHIFT, 1);
+		break;
+
+	case 2:
+		DRAMC_SET_VALUE(DRAMC_MON_BM(base), agent, DRAMC_BM_GROUP2_AGENT_ID_SHIFT, DRAMC_BM_GROUP2_AGENT_ID_LEN);
+		DRAMC_SET_VALUE(DRAMC_MON_BM(base), 1, DRAMC_BM_GROUP2_ENABLE_SHIFT, 1);
+		break;
+
+	case 3:
+		DRAMC_SET_VALUE(DRAMC_MON_BM(base), agent, DRAMC_BM_GROUP3_AGENT_ID_SHIFT, DRAMC_BM_GROUP3_AGENT_ID_LEN);
+		DRAMC_SET_VALUE(DRAMC_MON_BM(base), 1, DRAMC_BM_GROUP3_ENABLE_SHIFT, 1);
+		break;
+	}
+	temp_reg = dramc_reg_read(DRAMC_MON_BM(base));
+	DRAMC_LOG("%s MON_BM: %X\n", __FUNCTION__, temp_reg);
+}
+
+
+void DRAMC_Disable(int chan)
+{
+	unsigned long base = dramc_base[chan];
+	volatile u32 temp_reg = 0;
+
+	/* disable all group */
+	DRAMC_SET_VALUE(DRAMC_MON_BM(base), 0, DRAMC_BM_GROUP1_ENABLE_SHIFT, 1);
+	DRAMC_SET_VALUE(DRAMC_MON_BM(base), 0, DRAMC_BM_GROUP2_ENABLE_SHIFT, 1);
+	DRAMC_SET_VALUE(DRAMC_MON_BM(base), 0, DRAMC_BM_GROUP3_ENABLE_SHIFT, 1);
+
+	/* fire the BM function */
+	DRAMC_SET_VALUE(DRAMC_MON_BM(base), 0, DRAMC_BM_FREEZE_SHIFT, 1);
+	temp_reg = dramc_reg_read(DRAMC_MON_BM(base));
+	DRAMC_LOG("%s MON_BM: %X\n", __FUNCTION__, temp_reg);
+}
+
+void DRAMC_Freeze(int chan)
+{
+	unsigned long base = dramc_base[chan];
+	volatile u32 temp_reg = dramc_reg_read(DRAMC_MON_BM(base));
+
+	/* Freeze the BM function */
+	DRAMC_SET_VALUE(DRAMC_MON_BM(base), 1, DRAMC_BM_FREEZE_SHIFT, 1);
+	temp_reg = dramc_reg_read(DRAMC_MON_BM(base));
+	DRAMC_LOG("%s MON_BM: %X\n", __FUNCTION__, temp_reg);
+}
+
+void DRAMC_ConfigTargetCount(int chan, u32 count)
+{
+	unsigned long base = dramc_base[chan];
+	volatile u32 temp_reg = 0;
+
+	dramc_reg_write(DRAMC_MON_BMCYC(base), 0xFFFFFFFF);
+	temp_reg = dramc_reg_read(DRAMC_MON_BMCYC(base));
+	DRAMC_LOG("%s MON_BMCYC: %X\n", __FUNCTION__, temp_reg);
+	return;
+}
+
+u32 DRAMC_GetMaxLtcy(int chan)
+{
+	unsigned long base = dramc_base[chan];
+
+	return dramc_reg_read(DRAMC_MON_ROBM4(base));
+}
+
+u32 DRAMC_GetDramcFreq(void)
+{
+	unsigned long tcm_dramc_flags = 0;
+	tcm_dramc_flags = dramc_reg_read(TCM_DRAM_FLAGS_ADDR);
+	return TCMGET_DDR_CLK(tcm_dramc_flags);
+}
+
+
+u32 DRAMC_GetCycleCount(int chan, int group)
+{
+	unsigned long base = dramc_base[chan];
+
+	switch (group) {
+	case 1:
+		return dramc_reg_read(DRAMC_MON_ROBM0(base));
+
+	case 2:
+		return dramc_reg_read(DRAMC_MON_ROBM1(base));
+
+	case 3:
+		return dramc_reg_read(DRAMC_MON_ROBM2(base));
+	}
+	return 0;
+}
+
+u32 DRAMC_GetTotalCycleCount(int chan)
+{
+	unsigned long base = dramc_base[chan];
+
+	return dramc_reg_read(DRAMC_MON_ROBM3(base));
+}
+
+int DRAMC_CheckCntIsOverFlow(u32 count)
+{
+	if (0xFFFFFFFF == count) {
+		return 1;
+	}
+	return 0;
+}
+
+void DRAMC_WriteMode(int chan, int enable)
+{
+	unsigned long base = dramc_base[chan];
+
+	if (1 == enable) {
+		DRAMC_SET_VALUE(DRAMC_MON_BM(base), 1, DRAMC_BM_WREN_SHIFT, 1);
+	} else {
+		DRAMC_SET_VALUE(DRAMC_MON_BM(base), 0, DRAMC_BM_WREN_SHIFT, 1);
+	}
+}
+
+void DRAMC_ReadMode(int chan, int enable)
+{
+	unsigned long base = dramc_base[chan];
+
+	if (1 == enable) {
+		DRAMC_SET_VALUE(DRAMC_MON_BM(base), 1, DRAMC_BM_RDEN_SHIFT, 1);
+	} else {
+		DRAMC_SET_VALUE(DRAMC_MON_BM(base), 0, DRAMC_BM_RDEN_SHIFT, 1);
+	}
+}
+
+unsigned int DRAMC_GetEfuseValue(void)
+{
+	volatile u32 efuse_reg = 0;
+
+	efuse_reg = dramc_reg_read(DRAMC_EFUSE_BIT_ADDRESS);
+
+	return (efuse_reg & DRAMC_EFUSE_BIT_MASK);
+}
+
+void DRAMC_GetGroup1AgentCounter(int channel, unsigned int* counter)
+{
+	unsigned long base = dramc_base[channel];
+	unsigned long agent_base = 0;
+	int		   i = 0;
+
+	agent_base = DRAMC_MON_AGENT_BASE(base);
+	for (i=0; i<DRAMC_MON_AGENT_NUM; i++) {
+		counter[i] = dramc_reg_read(agent_base + i*sizeof(int));
+	}
+	smp_rmb();
+
+	return ;
+}
+
+void DRAMC_GetGroup1Latency(int channel, unsigned int* latency)
+{
+	unsigned long base = dramc_base[channel];
+	unsigned long lty_base = 0;
+	int		   i = 0;
+
+	lty_base = DRAMC_MON_LTY_AGENT_BASE(base);
+	for (i=0; i<DRAMC_MON_LTY_AGENT_NUM; i++) {
+		latency[i] = dramc_reg_read(lty_base + i*sizeof(int));
+	}
+	smp_rmb();
+
+	return ;
+}
+
+void DRAMC_GetGroup1MaxLatency(int channel, unsigned int* max_ltcy)
+{
+	unsigned long base = dramc_base[channel];
+	unsigned long max_lty_base = 0;
+	unsigned int  value = 0;
+	int		   i = 0;
+
+	/* max latency counter is 16 bit*/
+	max_lty_base = DRAMC_MON_MAX_LTY_AGENT_BASE(base);
+	for (i=0; i<DRAMC_MON_LTY_AGENT_NUM/sizeof(short); i++) {
+		value = dramc_reg_read(max_lty_base + i*sizeof(int));
+		max_ltcy[2*i] = value & 0x0000FFFF;
+		max_ltcy[2*i+1] = (value & 0xFFFF0000) >> 16;
+	}
+	smp_rmb();
+
+	return ;
+}
diff --git a/src/devtools/met-driver/4.19/mt2712/mtk_dramc.h b/src/devtools/met-driver/4.19/mt2712/mtk_dramc.h
new file mode 100644
index 0000000..1f2e8a1
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/mtk_dramc.h
@@ -0,0 +1,300 @@
+/* Copyright Statement:
+ *
+ * This software/firmware and related documentation ("MediaTek Software") are
+ * protected under relevant copyright laws. The information contained herein
+ * is confidential and proprietary to MediaTek Inc. and/or its licensors.
+ * Without the prior written permission of MediaTek inc. and/or its licensors,
+ * any reproduction, modification, use or disclosure of MediaTek Software,
+ * and information contained herein, in whole or in part, shall be strictly prohibited.
+ */
+/* MediaTek Inc. (C) 2012. All rights reserved.
+ *
+ * BY OPENING THIS FILE, RECEIVER HEREBY UNEQUIVOCALLY ACKNOWLEDGES AND AGREES
+ * THAT THE SOFTWARE/FIRMWARE AND ITS DOCUMENTATIONS ("MEDIATEK SOFTWARE")
+ * RECEIVED FROM MEDIATEK AND/OR ITS REPRESENTATIVES ARE PROVIDED TO RECEIVER ON
+ * AN "AS-IS" BASIS ONLY. MEDIATEK EXPRESSLY DISCLAIMS ANY AND ALL WARRANTIES,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NONINFRINGEMENT.
+ * NEITHER DOES MEDIATEK PROVIDE ANY WARRANTY WHATSOEVER WITH RESPECT TO THE
+ * SOFTWARE OF ANY THIRD PARTY WHICH MAY BE USED BY, INCORPORATED IN, OR
+ * SUPPLIED WITH THE MEDIATEK SOFTWARE, AND RECEIVER AGREES TO LOOK ONLY TO SUCH
+ * THIRD PARTY FOR ANY WARRANTY CLAIM RELATING THERETO. RECEIVER EXPRESSLY ACKNOWLEDGES
+ * THAT IT IS RECEIVER'S SOLE RESPONSIBILITY TO OBTAIN FROM ANY THIRD PARTY ALL PROPER LICENSES
+ * CONTAINED IN MEDIATEK SOFTWARE. MEDIATEK SHALL ALSO NOT BE RESPONSIBLE FOR ANY MEDIATEK
+ * SOFTWARE RELEASES MADE TO RECEIVER'S SPECIFICATION OR TO CONFORM TO A PARTICULAR
+ * STANDARD OR OPEN FORUM. RECEIVER'S SOLE AND EXCLUSIVE REMEDY AND MEDIATEK'S ENTIRE AND
+ * CUMULATIVE LIABILITY WITH RESPECT TO THE MEDIATEK SOFTWARE RELEASED HEREUNDER WILL BE,
+ * AT MEDIATEK'S OPTION, TO REVISE OR REPLACE THE MEDIATEK SOFTWARE AT ISSUE,
+ * OR REFUND ANY SOFTWARE LICENSE FEES OR SERVICE CHARGE PAID BY RECEIVER TO
+ * MEDIATEK FOR SUCH MEDIATEK SOFTWARE AT ISSUE.
+ *
+ * The following software/firmware and/or related documentation ("MediaTek Software")
+ * have been modified by MediaTek Inc. All revisions are subject to any receiver's
+ * applicable license agreements with MediaTek Inc.
+ */
+
+#ifndef __MT_DRAMC_H__
+#define __MT_DRAMC_H__
+#include <linux/types.h>
+//#include "x_hal_io.h"
+
+/* define BM function base address */
+#define MET_DRAMC0_BASE                         (0xF0006000)
+#define MET_DRAMC1_BASE                         (0xF0010000)
+#define MET_DRAMC2_BASE                         (0xF0011000)
+#define TCM_DRAM_FLAGS_ADDR                     (0xF00080F8)
+#define DRAM_CLOCK_MASK                         (0x00000FFF)
+
+#define BASE_DDR_CLK                            1000000
+#define TCMGET_DDR_CLK(p)                       ((p & DRAM_CLOCK_MASK) * BASE_DDR_CLK)
+
+/* define BM function offset */
+#define DRAMC_MON_BM(i)                         (i + 0x80)
+#define DRAMC_MON_BMCYC(i)                      (i + 0x8C) /* Bus monitor cycle number, BMCYC */
+#define DRAMC_MON_ROBM0(i)                      (i + 0x90) /* Bus monitor counter for group 1,BMLENGP1_CNT */
+#define DRAMC_MON_ROBM1(i)                      (i + 0x94) /* Bus monitor counter for group 2,BMLENGP2_CNT */
+#define DRAMC_MON_ROBM2(i)                      (i + 0x98) /* Bus monitor counter for group 3,BMLENGP3_CNT */
+#define DRAMC_MON_ROBM3(i)                      (i + 0x9C) /* bus monitor cycle counter,BM_CYC_CNT */
+#define DRAMC_MON_ROBM4(i)                      (i + 0xA0) /* Maximum burst length, 16'h0000, MAX_BSTCNT */
+
+/* define BM function shift */
+#define DRAMC_BM_PACNTEN                        (31) /* Bus monitor function select for number of precharge and active */
+#define DRAMC_BM_REQCNTEN                       (30) /* Bus monitor function select for number of REQ='1'&ALE='1' */
+#define DRAMC_BM_CHGPRIEN                       (29) /* On-line change agent priority enabling */
+#define DRAMC_BM_FREEZE_SHIFT                   (28) /* Bus monitor freeze */
+#define DRAMC_BM_DMBW32B_SHIFT                  (27) /* 1:External 32Bit data bus, 0:External 16Bit data bus */
+#define DRAMC_BM_WREN_SHIFT                     (25) /* Bus monitor only monitor write cycle enabling */
+#define DRAMC_BM_RDEN_SHIFT                     (24) /* Bus monitor only monitor read cycle enabling */
+#define DRAMC_BM_GROUP3_ENABLE_SHIFT            (23) /* Bus monitor for Group 3 agents enabling */
+#define DRAMC_BM_GROUP3_AGENT_ID_SHIFT          (20) /* Bus monitor for Group 3 agent-ID */
+#define DRAMC_BM_GROUP3_AGENT_ID_LEN            (3)
+#define DRAMC_BM_GROUP2_ENABLE_SHIFT            (19) /* Bus monitor for Group 2 agents enabling */
+#define DRAMC_BM_GROUP2_AGENT_ID_SHIFT          (16) /* Bus monitor for Group 2 agent-ID */
+#define DRAMC_BM_GROUP2_AGENT_ID_LEN            (3)
+#define DRAMC_BM_GROUP1_ENABLE_SHIFT            (15) /* Bus monitor for Group 1 agents enabling */
+#define DRAMC_BM_GROUP1_AGENT_ID_SHIFT          (8) /* Bus monitor for Group 1 agent-ID or */
+#define DRAMC_BM_GROUP1_AGENT_ID_LEN            (5)
+#define DRAMC_BM_BSTCNTEN                       (7) /* Monitor burst length enable */
+#define DRAMC_BM_R_DMBSTCNT_SEL                 (6) /* Latency mode select */
+#define DRAMC_BM_R_DMLATRECEN                   (5) /* Agent latency record enable */
+#define DRAMC_BM_BSTCNT                         (0) /* Agent-ID of the burst length monitored agent */
+#define DRAMC_BM_BSTCNT_LEN                     (4)
+
+/* define TCM function base address */
+#ifndef IO_VIRT
+#define IO_VIRT                                 (0xF0000000)
+#endif
+
+#define REG_RW_GPRB6                            (0x00f8) /* RISC Byte General Purpose Register 6 */
+#define TCM_SRAM_ADDR                           (IO_VIRT + 0x8000)
+
+#ifdef CONFIG_64BIT
+#define TCM_DRAM_FLAGS                          (*((volatile unsigned long*)(OFFSET_64BIT + TCM_SRAM_ADDR + REG_RW_GPRB6)))
+#else
+#define TCM_DRAM_FLAGS                          (*((volatile unsigned long*)(TCM_SRAM_ADDR + REG_RW_GPRB6)))
+#endif
+
+#define CHA_FORCE32_SHIFT                       (22)
+#define DRAM_CHA_FORCE32                        (1U << CHA_FORCE32_SHIFT)
+#define CHB_FORCE32_SHIFT                       (23)
+#define DRAM_CHB_FORCE32                        (1U << CHB_FORCE32_SHIFT)
+#define CHC_FORCE32_SHIFT                       (31)
+#define DRAM_CHC_FORCE32                        (1U << CHC_FORCE32_SHIFT)
+
+
+/* to check dramc is ext 32 bit or not*/
+#define DRMAC_IS_FORCE32(p)                     ((TCM_DRAM_FLAGS & p) ? 0 : 1)
+
+/* generate random cycle to return fake report */
+/* #define DRAMC_FAKE_REPORT */
+
+/* define dramc spec */
+#define DRAMC_MX_CHANNUM                        (1)
+#define DRAMC_MX_GRPNUM                         (3)
+#define DRAMC_MX_NUM_IN_AGRP                    (32)
+#define DRAMC_MX_AGENT_NAMEBUF                  (96)
+
+/* define all group id */
+#define DRAMC_ALL_GROUP_AGENT_ID                (0x20)
+#define DRAMC_MX_LATENCY                        (0x21)
+
+/* efuse bit[15:14]
+00:4G
+01:3G
+10:2G
+11:1G */
+#define DRAMC_EFUSE_BIT_ADDRESS                 (0xF0008664)
+#define DRAMC_EFUSE_BIT_MASK                    (0x0000C000)
+#define DRAMC_EFUSE_BIT_1G                      (0x0000C000)
+#define DRAMC_EFUSE_BIT_2G                      (0x00008000)
+#define DRAMC_EFUSE_BIT_3G                      (0x00004000)
+#define DRAMC_EFUSE_BIT_4G                      (0x00000000)
+
+#define DRAM_DDRPHY_CHA_BANK                    (IO_VIRT + 0x7A000)
+#define DRAM_DDRPHY_CHB_BANK                    (IO_VIRT + 0x7B000)
+#define DRAM_DDRPHY_CHC_BANK                    (IO_VIRT + 0x7C000)
+
+#define DRAM_BASE                               (IO_VIRT + 0x07000)
+#define DRAM_CHB_BASE                           (IO_VIRT + 0x0F000)
+#define DRAM_CHC_BASE                           (IO_VIRT + 0x15000)
+
+/* Bandwidth counter for agent0-15 */
+#define DRAMC_MON_AGENT_NUM                     (16)
+#define DRAMC_MON_AGENT_BASE(i)                 (i + 0x2C0)
+#define DRAMC_MON_AGENT0(i)                     (i + 0x2C0)
+#define DRAMC_MON_AGENT1(i)                     (i + 0x2C4)
+#define DRAMC_MON_AGENT2(i)                     (i + 0x2C8)
+#define DRAMC_MON_AGENT3(i)                     (i + 0x2CC)
+#define DRAMC_MON_AGENT4(i)                     (i + 0x2D0)
+#define DRAMC_MON_AGENT5(i)                     (i + 0x2D4)
+#define DRAMC_MON_AGENT6(i)                     (i + 0x2D8)
+#define DRAMC_MON_AGENT7(i)                     (i + 0x2DC)
+#define DRAMC_MON_AGENT8(i)                     (i + 0x2E0)
+#define DRAMC_MON_AGENT9(i)                     (i + 0x2E4)
+#define DRAMC_MON_AGENT10(i)                    (i + 0x2E8)
+#define DRAMC_MON_AGENT11(i)                    (i + 0x2EC)
+#define DRAMC_MON_AGENT12(i)                    (i + 0x2F0)
+#define DRAMC_MON_AGENT13(i)                    (i + 0x2F4)
+#define DRAMC_MON_AGENT14(i)                    (i + 0x2F8)
+#define DRAMC_MON_AGENT15(i)                    (i + 0x2FC)
+
+/* total latency counter for agent0-15 */
+#define DRAMC_MON_LTY_AGENT_NUM                 (16)
+#define DRAMC_MON_LTY_AGENT_BASE(i)             (i + 0x300)
+#define DRAMC_MON_LTY_AGENT0(i)                 (i + 0x300)
+#define DRAMC_MON_LTY_AGENT1(i)                 (i + 0x304)
+#define DRAMC_MON_LTY_AGENT2(i)                 (i + 0x308)
+#define DRAMC_MON_LTY_AGENT3(i)                 (i + 0x30C)
+#define DRAMC_MON_LTY_AGENT4(i)                 (i + 0x310)
+#define DRAMC_MON_LTY_AGENT5(i)                 (i + 0x314)
+#define DRAMC_MON_LTY_AGENT6(i)                 (i + 0x318)
+#define DRAMC_MON_LTY_AGENT7(i)                 (i + 0x31C)
+#define DRAMC_MON_LTY_AGENT8(i)                 (i + 0x320)
+#define DRAMC_MON_LTY_AGENT9(i)                 (i + 0x324)
+#define DRAMC_MON_LTY_AGENT10(i)                (i + 0x328)
+#define DRAMC_MON_LTY_AGENT11(i)                (i + 0x32C)
+#define DRAMC_MON_LTY_AGENT12(i)                (i + 0x330)
+#define DRAMC_MON_LTY_AGENT13(i)                (i + 0x334)
+#define DRAMC_MON_LTY_AGENT14(i)                (i + 0x338)
+#define DRAMC_MON_LTY_AGENT15(i)                (i + 0x33C)
+
+/* max latency counter for agent0-15 */
+#define DRAMC_MON_MAX_LTY_AGENT_NUM             (16)
+#define DRAMC_MON_MAX_LTY_AGENT_BASE(i)         (i + 0x340)
+#define DRAMC_MON_MAX_LTY_AGENT0(i)             (i + 0x340)
+#define DRAMC_MON_MAX_LTY_AGENT1(i)             (i + 0x340)
+#define DRAMC_MON_MAX_LTY_AGENT2(i)             (i + 0x344)
+#define DRAMC_MON_MAX_LTY_AGENT3(i)             (i + 0x344)
+#define DRAMC_MON_MAX_LTY_AGENT4(i)             (i + 0x348)
+#define DRAMC_MON_MAX_LTY_AGENT5(i)             (i + 0x348)
+#define DRAMC_MON_MAX_LTY_AGENT6(i)             (i + 0x34C)
+#define DRAMC_MON_MAX_LTY_AGENT7(i)             (i + 0x34C)
+#define DRAMC_MON_MAX_LTY_AGENT8(i)             (i + 0x350)
+#define DRAMC_MON_MAX_LTY_AGENT9(i)             (i + 0x350)
+#define DRAMC_MON_MAX_LTY_AGENT10(i)            (i + 0x354)
+#define DRAMC_MON_MAX_LTY_AGENT11(i)            (i + 0x354)
+#define DRAMC_MON_MAX_LTY_AGENT12(i)            (i + 0x358)
+#define DRAMC_MON_MAX_LTY_AGENT13(i)            (i + 0x358)
+#define DRAMC_MON_MAX_LTY_AGENT14(i)            (i + 0x35C)
+#define DRAMC_MON_MAX_LTY_AGENT15(i)            (i + 0x35C)
+
+struct dramc_desc_t {
+    int agent_id;
+    char name[DRAMC_MX_AGENT_NAMEBUF];
+};
+
+#define DRAMC_AGENT_TABLE \
+{ \
+	/* define channel A releationship between group and name */ \
+	{ \
+		{ \
+			/* GRP 1 */ \
+			{0, 	"00_audio"}, \
+			{1, 	"01_demux/gcpu/ddi"}, \
+			{2, 	"02_vbi/3d/tve"}, \
+			{3, 	"03_xpscaler_ip/tddc"}, \
+			{4, 	"04_none"}, \
+			{5, 	"05_audio_dsp_low1"}, \
+			{6, 	"06_2d_graph/2dgraph_cmd/irt_dma"}, \
+			{7, 	"07_ether/ci-spi/rs232"}, \
+			{8,	"08_demod_isdbt"}, \
+			{9, 	"09_usb2"}, \
+			{10, 	"10_usb3_d"}, \
+			{11,	"11_mmu"}, \
+			{12,	"12_arm11"}, \
+			{13,	"13_msdc/emmc"}, \
+			{14,	"14_gdma"}, \
+			{15,	"15_test0/audio_dsp0"}, \
+			{-1,	""} \
+		}, \
+		{ \
+			/* GRP 2 */ \
+			{16,	"16_nfi_dma/sfalsh_dma/lzhs/ci_spi"}, \
+			{17,	"17_ufozip"}, \
+			{18,	"18_usb3_c"}, \
+			{19,	"19_none"}, \
+			{20,	"20_none"}, \
+			{21,	"21_none"}, \
+			{22,	"22_none"}, \
+			{30,	"30_none"}, \
+			{-1,	""} \
+		}, \
+		{ \
+			/* GRP 3 */ \
+			{23,	"23_none"}, \
+			{24,	"24_none"}, \
+			{25,	"25_none"}, \
+			{26,	"26_none"}, \
+			{27,	"27_none"}, \
+			{28,	"28_none"}, \
+			{29,	"29_none"}, \
+			{31,	"31_none"}, \
+			{-1,	""} \
+		} \
+	} \
+}
+
+/* some marco and static inline function define */
+static inline int FIND_NEXT_AGENT(u64 mask, int agent)
+{
+    int idx = 0;
+    int mx_cnt = sizeof(u64)*8;
+    int temp_agent = agent + 1;
+
+    for (idx = 0 ; idx < mx_cnt; idx++,temp_agent++) {
+        if (mx_cnt == temp_agent)
+            temp_agent = 0;
+        if ((1ULL<<temp_agent) & mask)
+            break;
+    }
+    if (idx == mx_cnt)
+        return -1;
+    return temp_agent;
+}
+
+#define GROUP_ID(agent) ((agent <= 0xF)?1:((agent<=0x17)?2:3))
+#define GROUP1_MASK     (0xFFFF)
+
+
+/* function prototype */
+extern void DRAMC_Init(int chan);
+extern void DRAMC_MaxLtcyMode(int chan, int enable);
+extern void DRAMC_Enable(int chan, int group, int agent);
+extern void DRAMC_Disable(int chan);
+extern void DRAMC_Freeze(int chan);
+extern void DRAMC_ConfigTargetCount(int chan, u32 count);
+extern u32 DRAMC_GetMaxLtcy(int chan);
+extern u32 DRAMC_GetCycleCount(int chan, int group);
+extern u32 DRAMC_GetTotalCycleCount(int chan);
+extern u32 DRAMC_GetDramcFreq(void);
+extern int DRAMC_CheckCntIsOverFlow(u32 count);
+
+extern void DRAMC_WriteMode(int chan, int enable);
+extern void DRAMC_ReadMode(int chan, int enable);
+extern unsigned int DRAMC_GetEfuseValue(void);
+
+extern void DRAMC_GetGroup1AgentCounter(int chan, unsigned int* counter);
+extern void DRAMC_GetGroup1Latency(int chan, unsigned int* latency);
+extern void DRAMC_GetGroup1MaxLatency(int chan, unsigned int* max_ltcy);
+
+#endif
diff --git a/src/devtools/met-driver/4.19/mt2712/mtk_emi_bm.c b/src/devtools/met-driver/4.19/mt2712/mtk_emi_bm.c
new file mode 100644
index 0000000..d7ecb7c
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/mtk_emi_bm.c
@@ -0,0 +1,1362 @@
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/io.h>
+#include <asm/cacheflush.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+//#include <mt-plat/sync_write.h>
+//#include <mt-plat/mtk_io.h>
+//#include "x_hal_io.h"
+//#include "x_hal_5381.h"
+#include "mtk_typedefs.h"
+#include "core_plf_init.h"
+#include "mtk_emi_bm.h"
+#include "mtk_dramc.h"
+#include "met_drv.h"
+#include "interface.h"
+
+#define IOMEM(x)	((void __force __iomem *)(x))
+
+#define mt_reg_sync_writel(v, a) \
+	do {    \
+		__raw_writel((v), (void __force __iomem *)((a)));   \
+		mb();  \
+	} while (0)
+
+#if 0
+#undef  MET_BOOT_MSG
+#if     defined(MET_BOOT_MSG)
+extern char met_boot_msg_tmp[256];
+extern int pr_bootmsg(int str_len, char *str);
+#define PR_BOOTMSG(fmt, args...) { \
+        int str_len = snprintf(met_boot_msg_tmp, sizeof(met_boot_msg_tmp), \
+                               fmt, ##args); \
+        pr_bootmsg(str_len, met_boot_msg_tmp); }
+#define PR_BOOTMSG_ONCE(fmt, args...) { \
+        static int once; \
+        if (!once) { \
+                int str_len = snprintf(met_boot_msg_tmp, \
+                                       sizeof(met_boot_msg_tmp), \
+                                       fmt, ##args); \
+                pr_bootmsg(str_len, met_boot_msg_tmp); \
+                once = 1; \
+        } }
+#else
+#define pr_bootmsg(str_len, str)
+#define PR_BOOTMSG(fmt, args...)
+#define PR_BOOTMSG_ONCE(fmt, args...)
+#endif
+#endif
+
+//static unsigned long gEMIBaseAddr = EMI_REG_BASE;
+
+
+/* MET internal define */
+#ifndef DRAMC0_NAO_BASE
+#define DRAMC0_NAO_BASE             (IO_VIRT + 0x204000)
+#endif
+#ifndef DRAMC1_NAO_BASE
+#define DRAMC1_NAO_BASE             (IO_VIRT + 0x20C000)
+#endif
+#ifndef DRAMC2_NAO_BASE
+#define DRAMC2_NAO_BASE             (IO_VIRT + 0x214000)
+#endif
+#ifndef DRAMC3_NAO_BASE
+#define DRAMC3_NAO_BASE             (IO_VIRT + 0x21C000)
+#endif
+static unsigned long gDRAMCBaseAddr[] =
+{
+	DRAMC0_NAO_BASE,
+	DRAMC1_NAO_BASE,
+	DRAMC2_NAO_BASE,
+	DRAMC3_NAO_BASE,
+};
+
+#define MX_DRAMC_CHAN_NR (sizeof(gDRAMCBaseAddr)/sizeof(gDRAMCBaseAddr[0]))
+
+/* GET EMI Base Address */
+#if 0
+unsigned long get_emi_base_addr(void)
+{
+	return gEMIBaseAddr;
+}
+#endif
+
+unsigned long get_dram_nao_base_addr(int idx)
+{
+	if (idx >= MX_DRAMC_CHAN_NR) {
+		METERROR("Invalid DRAMC idx: %d !\n", idx);
+		PR_BOOTMSG_ONCE("Invalid DRAMC idx: %d !\n", idx);
+		return 0;
+	}
+	return gDRAMCBaseAddr[idx];
+}
+
+#undef	DEBUG
+#undef	debug_reg
+
+#ifdef	debug_reg
+static inline unsigned int emi_readl(void __iomem *padr)
+{
+	unsigned int tmp;
+
+	tmp = readl(padr);
+	MET_TRACE("[MET_EMI] RD_Reg: %p: %08x\n", padr, tmp);
+	return tmp;
+}
+
+static inline void __emi_reg_sync_writel(unsigned int data, void __iomem *padr)
+{
+	unsigned int tmp;
+
+	mt_reg_sync_writel(data, padr);
+	tmp = readl(padr);
+	MET_TRACE("[MET_EMI] WR_Reg: %p: %08x, %08x\n", padr, data, tmp);
+}
+
+#define emi_reg_sync_writel(data, adr)	__emi_reg_sync_writel(data, IOMEM(adr))
+
+#else
+#define	emi_readl		readl
+#define	emi_reg_sync_writel	mt_reg_sync_writel
+#endif
+
+#define MASK_MASTER	0xFF
+#define MASK_TRANS_TYPE	0xFF
+
+static void __iomem *BaseAddrEMI;
+static void __iomem *BaseAddrDRAMC0;
+static void __iomem *BaseAddrDRAMC1;
+static void __iomem *BaseAddrDRAMC2;
+static void __iomem *BaseAddrDRAMC3;
+static void __iomem *AddrDramDatarate;
+
+/*
+*   MET_REG_BSET/MET_REG_BCLR:
+*   reading value before set and clear
+*/
+static inline void MET_REG_BSET(unsigned long reg, u32 shift)
+{
+	volatile unsigned int read_val = 0;
+
+	read_val = emi_readl(IOMEM(reg));
+	emi_reg_sync_writel(read_val | (1<<shift), reg);
+}
+
+static inline void MET_REG_BCLR(unsigned long reg, u32 shift)
+{
+	volatile unsigned int read_val = 0;
+
+	read_val = emi_readl(IOMEM(reg));
+	emi_reg_sync_writel(read_val & (~((1<<shift) & 0xFFFFFFFF)), reg);
+}
+
+#define GET_EMI_FROM_DTS
+//#undef GET_EMI_FROM_DTS
+
+int MET_BM_Init(void)
+{
+#ifdef GET_EMI_FROM_DTS
+	struct device_node *node;
+
+	/* Device Tree Path */
+	node = of_find_compatible_node(NULL, NULL, of_emi_desc);
+	if (!node) {
+		METINFO("node of_emi_desc not found\n");
+		PR_BOOTMSG_ONCE("node of_emi_desc not found\n");
+		return -1;
+	}
+
+	BaseAddrEMI = (void *)of_iomap(node, 0);
+#else
+	BaseAddrEMI = (void*)get_emi_base_addr();
+#endif
+	if (BaseAddrEMI == 0) {
+		METERROR("BaseAddrEMI = 0\n");
+		PR_BOOTMSG_ONCE("BaseAddrEMI = 0\n");
+		return -1;
+	}
+	METINFO("MET EMI: map emi to %p\n", BaseAddrEMI);
+	PR_BOOTMSG("MET EMI: map emi to %p\n", BaseAddrEMI);
+
+#ifdef GET_EMI_FROM_DTS
+	/* DRAMC TBD */
+	/* MAP NAO (NON-AO) DRAMC address */
+	node = of_find_compatible_node(NULL, NULL, of_dramc_desc);
+	if (!node) {
+		METINFO("node of_dramc_desc not found\n");
+		PR_BOOTMSG_ONCE("node of_dramc_desc not found\n");
+		return -1;
+	}
+	BaseAddrDRAMC0 = (void *)of_iomap(node, 0);
+	BaseAddrDRAMC1 = (void *)of_iomap(node, 1);
+	BaseAddrDRAMC2 = (void *)of_iomap(node, 2);
+	BaseAddrDRAMC3 = (void *)of_iomap(node, 3);
+#else
+	BaseAddrDRAMC0 = (void *) get_dram_nao_base_addr(0);
+	BaseAddrDRAMC1 = (void *) get_dram_nao_base_addr(1);
+	BaseAddrDRAMC2 = (void *) get_dram_nao_base_addr(2);
+	BaseAddrDRAMC3 = (void *) get_dram_nao_base_addr(3);
+#endif
+
+	AddrDramDatarate = ioremap_nocache(0x10100000, 0x1000);
+	if (AddrDramDatarate == NULL) {
+		pr_err("ioremap AddrDramDatarate fail ...\n");
+		return -1;
+	}
+
+	if (BaseAddrDRAMC0 == 0
+		|| BaseAddrDRAMC1 == 0
+		|| BaseAddrDRAMC2 == 0
+		|| BaseAddrDRAMC3 == 0
+	) {
+		METERROR("BaseAddrDRAMC0 = %p\n", BaseAddrDRAMC0);
+		METERROR("BaseAddrDRAMC1 = %p\n", BaseAddrDRAMC1);
+		METERROR("BaseAddrDRAMC2 = %p\n", BaseAddrDRAMC2);
+		METERROR("BaseAddrDRAMC3 = %p\n", BaseAddrDRAMC3);
+		PR_BOOTMSG_ONCE("BaseAddrDRAMC0 = %p\n", BaseAddrDRAMC0);
+		PR_BOOTMSG_ONCE("BaseAddrDRAMC1 = %p\n", BaseAddrDRAMC1);
+		PR_BOOTMSG_ONCE("BaseAddrDRAMC2 = %p\n", BaseAddrDRAMC2);
+		PR_BOOTMSG_ONCE("BaseAddrDRAMC3 = %p\n", BaseAddrDRAMC3);
+		return -1;
+	}
+	METINFO("MET EMI: map nao dramcA to %p\n", BaseAddrDRAMC0);
+	METINFO("MET EMI: map nao dramcB to %p\n", BaseAddrDRAMC1);
+	METINFO("MET EMI: map nao dramcC to %p\n", BaseAddrDRAMC2);
+	METINFO("MET EMI: map nao dramcD to %p\n", BaseAddrDRAMC3);
+	PR_BOOTMSG("MET EMI: map nao dramcA to %p\n", BaseAddrDRAMC0);
+	PR_BOOTMSG("MET EMI: map nao dramcB to %p\n", BaseAddrDRAMC1);
+	PR_BOOTMSG("MET EMI: map nao dramcC to %p\n", BaseAddrDRAMC2);
+	PR_BOOTMSG("MET EMI: map nao dramcD to %p\n", BaseAddrDRAMC3);
+	return 0;
+}
+
+void MET_BM_DeInit(void)
+{
+}
+
+void MET_BM_Enable(const unsigned int enable)
+{
+	volatile unsigned long int value_check;
+	int i = 0;
+
+	while (i < 100) {
+
+		if (enable == 0) {
+			/* SET BIT IDLE */
+			MET_REG_BSET(ADDR_EMI+EMI_BMEN, BUS_MON_IDLE_SHIFT);
+
+			/* CLR BIT EN */
+			MET_REG_BCLR(ADDR_EMI+EMI_BMEN, BUS_MON_EN_SHIFT);
+
+			/* CLR BIT IDLE */
+			MET_REG_BCLR(ADDR_EMI+EMI_BMEN, BUS_MON_IDLE_SHIFT);
+		} else {
+			/* CLR BIT IDLE */
+			MET_REG_BCLR(ADDR_EMI+EMI_BMEN, BUS_MON_IDLE_SHIFT);
+
+			/* SET BIT EN */
+			MET_REG_BSET(ADDR_EMI+EMI_BMEN, BUS_MON_EN_SHIFT);
+		}
+
+		value_check = emi_readl(IOMEM(ADDR_EMI+EMI_BMEN));
+
+		if (enable == 0) {
+			/* EN == 0, IDLE == 0 when EMI RESET*/
+			if (!test_bit(BUS_MON_EN_SHIFT, &value_check)
+					&& !test_bit(BUS_MON_IDLE_SHIFT, &value_check)) {
+				break;
+			}
+		} else {
+			/* EN == 1, IDLE == 0 when EMI START*/
+			if (test_bit(BUS_MON_EN_SHIFT, &value_check)
+					&& !test_bit(BUS_MON_IDLE_SHIFT, &value_check)) {
+				break;
+			}
+		}
+		i++;
+	}
+	for (i = 0; i < MX_DRAMC_CHAN_NR; i++) {
+		if (enable == 0)
+			/* Disable Dramc Bus Monitor */
+			MET_DRAMC_BMEnable(i, 0);
+		else
+			/* Enable Dramc Bus Monitor */
+			MET_DRAMC_BMEnable(i, 1);
+	}
+	/*MET_TRACE("[MET_BM_ENABLE] value_check: %lx, enable = %d\n", value_check, enable);*/
+
+}
+
+void MET_BM_Pause(void)
+{
+	int i = 0;
+	const unsigned int value = emi_readl(IOMEM(ADDR_EMI+EMI_BMEN));
+
+	emi_reg_sync_writel(value | (1<<BUS_MON_PAUSE_SHIFT), ADDR_EMI+EMI_BMEN);
+	for (i = 0; i < MX_DRAMC_CHAN_NR; i++) {
+		/* Pause Dramc Bus Monitor */
+		MET_DRAMC_BMPause(i, 1);
+	}
+}
+
+void MET_BM_Continue(void)
+{
+	int i = 0;
+	const unsigned int value = emi_readl(IOMEM(ADDR_EMI+EMI_BMEN));
+
+	emi_reg_sync_writel(value & (~(1<<BUS_MON_PAUSE_SHIFT)), ADDR_EMI+EMI_BMEN);
+	for (i = 0; i < MX_DRAMC_CHAN_NR; i++) {
+		/* Enable Dramc Bus Monitor */
+		MET_DRAMC_BMPause(i, 0);
+	}
+}
+
+unsigned int MET_BM_IsOverrun(void)
+{
+	/*
+	 * return 0 if EMI_BCNT(bus cycle counts) or
+	 * EMI_WACT(total word counts) is overrun,
+	 * otherwise return an !0 value
+	 */
+	const unsigned int value = emi_readl(IOMEM(ADDR_EMI+EMI_BMEN));
+
+	return (value & (1<<BC_OVERRUN_SHIFT));
+}
+
+unsigned int MET_BM_GetReadWriteType(void)
+{
+	const unsigned int value = emi_readl(IOMEM(ADDR_EMI+EMI_BMEN));
+
+	return ((value & 0xFFFFFFCF) >> 4);
+}
+
+void MET_BM_SetReadWriteType(const unsigned int ReadWriteType)
+{
+	const unsigned int value = emi_readl(IOMEM(ADDR_EMI+EMI_BMEN));
+
+	/*
+	 * ReadWriteType: 00/11 --> both R/W
+	 *                   01 --> only R
+	 *                   10 --> only W
+	 */
+	emi_reg_sync_writel((value & 0xFFFFFFCF) | (ReadWriteType << 4), ADDR_EMI+EMI_BMEN);
+}
+
+int MET_BM_GetBusCycCount(void)
+{
+	return MET_BM_IsOverrun() ? BM_ERR_OVERRUN : emi_readl(IOMEM(ADDR_EMI+EMI_BCNT));/*Bus cycle counter*/
+}
+
+unsigned int MET_BM_GetTransAllCount(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI+EMI_TACT));
+}
+
+int MET_BM_GetTransCount(const unsigned int counter_num)
+{
+	unsigned int iCount;
+
+	switch (counter_num) {
+	case 1:
+		iCount = emi_readl(IOMEM(ADDR_EMI+EMI_TSCT));
+		break;
+
+	case 2:
+		iCount = emi_readl(IOMEM(ADDR_EMI+EMI_TSCT2));
+		break;
+
+	case 3:
+		iCount = emi_readl(IOMEM(ADDR_EMI+EMI_TSCT3));
+		break;
+
+	default:
+		return BM_ERR_WRONG_REQ;
+	}
+
+	return iCount;
+}
+
+int MET_BM_GetWordAllCount(void)
+{
+	return MET_BM_IsOverrun() ? BM_ERR_OVERRUN : emi_readl(IOMEM(ADDR_EMI+EMI_WACT));
+}
+
+int MET_BM_GetWordCount(const unsigned int counter_num)
+{
+	unsigned int iCount;
+
+	switch (counter_num) {
+	case 1:
+		iCount = emi_readl(IOMEM(ADDR_EMI+EMI_WSCT));
+		break;
+
+	case 2:
+		iCount = emi_readl(IOMEM(ADDR_EMI+EMI_WSCT2));
+		break;
+
+	case 3:
+		iCount = emi_readl(IOMEM(ADDR_EMI+EMI_WSCT3));
+		break;
+
+	case 4:
+		iCount = emi_readl(IOMEM(ADDR_EMI+EMI_WSCT4));
+		break;
+
+	default:
+		return BM_ERR_WRONG_REQ;
+	}
+
+	return iCount;
+}
+
+unsigned int MET_BM_GetBandwidthWordCount(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI+EMI_BACT));/*Bandwidth counter for access*/
+}
+
+unsigned int MET_BM_GetOverheadWordCount(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI+EMI_BSCT));/*Overhead counter*/
+}
+
+int MET_BM_GetTransTypeCount(const unsigned int counter_num)
+{
+	return (counter_num < 1 || counter_num > BM_COUNTER_MAX)
+		? BM_ERR_WRONG_REQ : emi_readl(IOMEM(ADDR_EMI+EMI_TTYPE1 + (counter_num - 1) * 8));
+}
+
+int MET_BM_GetMDCT(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI+EMI_MDCT));
+}
+
+int MET_BM_GetMDCT_2(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI+EMI_MDCT_2ND));
+}
+
+int MET_BM_SetMDCT_MDMCU(unsigned int mdmcu_rd_buf)
+{
+	volatile unsigned int value_origin;
+
+	value_origin = emi_readl(IOMEM(ADDR_EMI+EMI_MDCT_2ND));
+	MET_TRACE("[MET_BM_SetMDCT_MDMCU] value_origin: %x\n", value_origin);
+
+	value_origin = value_origin & ~(0x7);
+	value_origin = value_origin | ((mdmcu_rd_buf)&0x7);
+
+	emi_reg_sync_writel(value_origin, ADDR_EMI+EMI_MDCT_2ND);
+
+	return BM_REQ_OK;
+}
+
+
+int MET_BM_GetMonitorCounter(const unsigned int counter_num,
+		unsigned int *master,
+		unsigned int *trans_type)
+{
+	unsigned int value, addr;
+
+	if (counter_num < 1 || counter_num > BM_COUNTER_MAX)
+		return BM_ERR_WRONG_REQ;
+
+
+	if (counter_num == 1) {
+		addr = EMI_BMEN;
+		value = emi_readl(IOMEM(ADDR_EMI+addr));
+		*master = (value>>16) & MASK_MASTER;
+		*trans_type = (value>>24) & MASK_TRANS_TYPE;
+	} else {
+		addr = (counter_num <= 3) ?
+			EMI_MSEL : (EMI_MSEL2 + (counter_num / 2 - 2) * 8);
+		value = emi_readl(IOMEM(ADDR_EMI+addr)) >> ((counter_num % 2) * 16);
+		*master = value & MASK_MASTER;
+		*trans_type = (value>>8) & MASK_TRANS_TYPE;
+	}
+
+	return BM_REQ_OK;
+}
+
+int MET_BM_SetMonitorCounter(const unsigned int counter_num,
+		const unsigned int master,
+		const unsigned int trans_type)
+{
+	unsigned int value, addr;
+	const unsigned int iMask = (MASK_TRANS_TYPE << 8) | MASK_MASTER;
+
+	if (counter_num < 1 || counter_num > BM_COUNTER_MAX)
+		return BM_ERR_WRONG_REQ;
+
+
+	if (counter_num == 1) {
+		addr = EMI_BMEN;
+		value = (emi_readl(IOMEM(ADDR_EMI+addr)) & ~(iMask << 16)) |
+			((trans_type & MASK_TRANS_TYPE) << 24) |
+			((master & MASK_MASTER) << 16);
+	} else {
+		addr = (counter_num <= 3) ?
+			EMI_MSEL : (EMI_MSEL2 + (counter_num / 2 - 2) * 8);
+
+		/* clear master and transaction type fields */
+		value = emi_readl(IOMEM(ADDR_EMI+addr)) & ~(iMask << ((counter_num % 2) * 16));
+
+		/* set master and transaction type fields */
+		value |= (((trans_type & MASK_TRANS_TYPE) << 8) |
+				(master & MASK_MASTER)) <<
+			((counter_num % 2) * 16);
+	}
+
+	emi_reg_sync_writel(value, ADDR_EMI+addr);
+
+	return BM_REQ_OK;
+}
+
+int MET_BM_SetTtypeCounterRW(unsigned int bmrw0_val, unsigned int bmrw1_val)
+{
+
+	volatile unsigned int value_origin;
+
+	value_origin = emi_readl(IOMEM(ADDR_EMI+EMI_BMRW0));
+	MET_TRACE("[MET_EMI_settype1] value_origin: %x\n", value_origin);
+	if (value_origin != bmrw0_val) {
+		emi_reg_sync_writel(bmrw0_val, ADDR_EMI+EMI_BMRW0);
+		MET_TRACE("[MET_EMI_settype1] bmrw0_val: %x, value_origin: %x\n", bmrw0_val, value_origin);
+	}
+
+
+	value_origin = emi_readl(IOMEM(ADDR_EMI+EMI_BMRW1));
+	MET_TRACE("[MET_EMI_settype2] value_origin: %x\n", value_origin);
+	if (value_origin != bmrw1_val) {
+		emi_reg_sync_writel(bmrw1_val, ADDR_EMI+EMI_BMRW1);
+		MET_TRACE("[MET_EMI_settype2] bmrw0_val: %x, value_origin: %x\n", bmrw1_val, value_origin);
+
+	}
+	return BM_REQ_OK;
+}
+
+int MET_BM_Set_WsctTsct_id_sel(unsigned int counter_num, unsigned int enable)
+{
+	unsigned int value;
+
+	if (counter_num > 3)
+		return BM_ERR_WRONG_REQ;
+
+	value = ((emi_readl(IOMEM(ADDR_EMI+EMI_BMEN2)) & (~(1 << (28+counter_num)))) | (enable << (28+counter_num)));
+	emi_reg_sync_writel(value, ADDR_EMI+EMI_BMEN2);
+
+	return BM_REQ_OK;
+}
+
+int MET_BM_SetMaster(const unsigned int counter_num, const unsigned int master)
+{
+	unsigned int value, addr;
+	const unsigned int iMask = 0x7F;
+
+	if (counter_num < 1 || counter_num > BM_COUNTER_MAX)
+		return BM_ERR_WRONG_REQ;
+
+
+	if (counter_num == 1) {
+		addr = EMI_BMEN;
+		value = (emi_readl(IOMEM(ADDR_EMI+addr)) & ~(iMask << 16)) | ((master & iMask) << 16);
+	} else {
+		addr = (counter_num <= 3) ? EMI_MSEL : (EMI_MSEL2 + (counter_num / 2 - 2) * 8);
+
+		/* clear master and transaction type fields */
+		value = emi_readl(IOMEM(ADDR_EMI+addr)) & ~(iMask << ((counter_num % 2) * 16));
+
+		/* set master and transaction type fields */
+		value |= ((master & iMask) << ((counter_num % 2) * 16));
+	}
+
+	emi_reg_sync_writel(value, ADDR_EMI+addr);
+
+	return BM_REQ_OK;
+}
+
+int MET_BM_SetIDSelect(const unsigned int counter_num,
+		const unsigned int id,
+		const unsigned int enable)
+{
+	unsigned int value, addr, shift_num;
+
+	if (enable == 0) {
+
+		value = (emi_readl(IOMEM(ADDR_EMI+EMI_BMEN2))
+				& ~(1 << (counter_num - 1)));
+
+
+		emi_reg_sync_writel(value, ADDR_EMI+EMI_BMEN2);
+		return BM_REQ_OK;
+	}
+
+	if ((counter_num < 1 || counter_num > BM_COUNTER_MAX) || (id > EMI_BMID_MASK) || (enable > 1))
+		return BM_ERR_WRONG_REQ;
+
+
+	addr = EMI_BMID0 + (counter_num - 1) / 2 * 4;
+
+	/* field's offset in the target EMI_BMIDx register */
+	shift_num = ((counter_num - 1) % 2) * 16;
+
+	/* clear SELx_ID field */
+	value = emi_readl(IOMEM(ADDR_EMI+addr)) & ~(EMI_BMID_MASK << shift_num);
+
+	/* set SELx_ID field */
+	value |= id << shift_num;
+
+	emi_reg_sync_writel(value, ADDR_EMI+addr);
+
+	value = (emi_readl(IOMEM(ADDR_EMI+EMI_BMEN2))
+		& ~(1 << (counter_num - 1)))
+		| (enable << (counter_num - 1));
+
+	emi_reg_sync_writel(value, ADDR_EMI+EMI_BMEN2);
+
+	return BM_REQ_OK;
+}
+
+int MET_BM_SetUltraHighFilter(const unsigned int counter_num, const unsigned int enable)
+{
+	unsigned int value;
+
+	if ((counter_num < 1 || counter_num > BM_COUNTER_MAX) || (enable > 1))
+		return BM_ERR_WRONG_REQ;
+
+
+	value = (emi_readl(IOMEM(ADDR_EMI+EMI_BMEN1))
+		& ~(1 << (counter_num - 1)))
+		| (enable << (counter_num - 1));
+
+	emi_reg_sync_writel(value, ADDR_EMI+EMI_BMEN1);
+
+	return BM_REQ_OK;
+}
+
+int MET_BM_SetLatencyCounter(unsigned int enable)
+{
+	unsigned int value;
+
+	value = emi_readl(IOMEM(ADDR_EMI+EMI_BMEN2)) & ~(0x3 << 24);
+	/*
+	 * emi_ttype1 -- emi_ttype8 change as total latencies
+	 * for m0 -- m7,
+	 * and emi_ttype9 -- emi_ttype16 change as total transaction counts
+	 * for m0 -- m7
+	 */
+	if (enable == 1)
+		value |= (0x2 << 24);
+
+	emi_reg_sync_writel(value, ADDR_EMI+EMI_BMEN2);
+
+	return BM_REQ_OK;
+}
+
+int MET_BM_GetLatencyCycle(const unsigned int counter_num)
+{
+	unsigned int cycle_count;
+
+	switch (counter_num) {
+	case 1:
+		cycle_count = emi_readl(IOMEM(ADDR_EMI+EMI_TTYPE1));
+		break;
+	case 2:
+		cycle_count = emi_readl(IOMEM(ADDR_EMI+EMI_TTYPE2));
+		break;
+	case 3:
+		cycle_count = emi_readl(IOMEM(ADDR_EMI+EMI_TTYPE3));
+		break;
+	case 4:
+		cycle_count = emi_readl(IOMEM(ADDR_EMI+EMI_TTYPE4));
+		break;
+	case 5:
+		cycle_count = emi_readl(IOMEM(ADDR_EMI+EMI_TTYPE5));
+		break;
+	case 6:
+		cycle_count = emi_readl(IOMEM(ADDR_EMI+EMI_TTYPE6));
+		break;
+	case 7:
+		cycle_count = emi_readl(IOMEM(ADDR_EMI+EMI_TTYPE7));
+		break;
+	case 8:
+		cycle_count = emi_readl(IOMEM(ADDR_EMI+EMI_TTYPE8));
+		break;
+	case 9:
+		cycle_count = emi_readl(IOMEM(ADDR_EMI+EMI_TTYPE9));
+		break;
+	case 10:
+		cycle_count = emi_readl(IOMEM(ADDR_EMI+EMI_TTYPE10));
+		break;
+	case 11:
+		cycle_count = emi_readl(IOMEM(ADDR_EMI+EMI_TTYPE11));
+		break;
+	case 12:
+		cycle_count = emi_readl(IOMEM(ADDR_EMI+EMI_TTYPE12));
+		break;
+	case 13:
+		cycle_count = emi_readl(IOMEM(ADDR_EMI+EMI_TTYPE13));
+		break;
+	case 14:
+		cycle_count = emi_readl(IOMEM(ADDR_EMI+EMI_TTYPE14));
+		break;
+	case 15:
+		cycle_count = emi_readl(IOMEM(ADDR_EMI+EMI_TTYPE15));
+		break;
+	case 16:
+		cycle_count = emi_readl(IOMEM(ADDR_EMI+EMI_TTYPE16));
+		break;
+	case 17:
+		cycle_count = emi_readl(IOMEM(ADDR_EMI+EMI_TTYPE17));
+		break;
+	case 18:
+		cycle_count = emi_readl(IOMEM(ADDR_EMI+EMI_TTYPE18));
+		break;
+	case 19:
+		cycle_count = emi_readl(IOMEM(ADDR_EMI+EMI_TTYPE19));
+		break;
+	case 20:
+		cycle_count = emi_readl(IOMEM(ADDR_EMI+EMI_TTYPE20));
+		break;
+	case 21:
+		cycle_count = emi_readl(IOMEM(ADDR_EMI+EMI_TTYPE21));
+		break;
+	default:
+		return BM_ERR_WRONG_REQ;
+	}
+
+	return cycle_count;
+}
+
+unsigned int MET_BM_GetEmiDcm(void)
+{
+	return (emi_readl(IOMEM(ADDR_EMI+EMI_CONM))>>24);
+}
+
+int MET_BM_SetEmiDcm(const unsigned int setting)
+{
+	unsigned int value;
+
+	value = emi_readl(IOMEM(ADDR_EMI+EMI_CONM));
+	emi_reg_sync_writel((value & 0x00FFFFFF) | (setting << 24), ADDR_EMI+EMI_CONM);
+
+	return BM_REQ_OK;
+}
+
+/* DRAMC */
+unsigned int MET_DRAMC_GetPageHitCount(DRAMC_Cnt_Type CountType, int chann)
+{
+	unsigned int iCount;
+	unsigned long addr_base = ADDR_DRAMC0;
+
+	if (chann == 0)
+		addr_base = ADDR_DRAMC0;
+	else if (chann == 1)
+		addr_base = ADDR_DRAMC1;
+	else if (chann == 2)
+		addr_base = ADDR_DRAMC2;
+	else if (chann == 3)
+		addr_base = ADDR_DRAMC3;
+	else
+		return BM_ERR_WRONG_REQ;
+
+
+	switch (CountType) {
+	case DRAMC_R2R:
+		iCount = emi_readl(IOMEM(addr_base+DRAMC_R2R_PAGE_HIT));
+		break;
+	case DRAMC_R2W:
+		iCount = emi_readl(IOMEM(addr_base+DRAMC_R2W_PAGE_HIT));
+		break;
+	case DRAMC_W2R:
+		iCount = emi_readl(IOMEM(addr_base+DRAMC_W2R_PAGE_HIT));
+		break;
+	case DRAMC_W2W:
+		iCount = emi_readl(IOMEM(addr_base+DRAMC_W2W_PAGE_HIT));
+		break;
+	case DRAMC_ALL:
+		iCount = emi_readl(IOMEM(addr_base+DRAMC_R2R_PAGE_HIT)) +
+			 emi_readl(IOMEM(addr_base+DRAMC_R2W_PAGE_HIT)) +
+			 emi_readl(IOMEM(addr_base+DRAMC_W2R_PAGE_HIT)) +
+			 emi_readl(IOMEM(addr_base+DRAMC_W2W_PAGE_HIT));
+		break;
+	default:
+		return BM_ERR_WRONG_REQ;
+	}
+
+	return iCount;
+}
+
+unsigned int MET_DRAMC_GetPageMissCount(DRAMC_Cnt_Type CountType, int chann)
+{
+	unsigned int iCount;
+	unsigned long addr_base = ADDR_DRAMC0;
+
+	if (chann == 0)
+		addr_base = ADDR_DRAMC0;
+	else if (chann == 1)
+		addr_base = ADDR_DRAMC1;
+	else if (chann == 2)
+		addr_base = ADDR_DRAMC2;
+	else if (chann == 3)
+		addr_base = ADDR_DRAMC3;
+	else
+		return BM_ERR_WRONG_REQ;
+
+
+	switch (CountType) {
+	case DRAMC_R2R:
+		iCount = emi_readl(IOMEM(addr_base+DRAMC_R2R_PAGE_MISS));
+		break;
+	case DRAMC_R2W:
+		iCount = emi_readl(IOMEM(addr_base+DRAMC_R2W_PAGE_MISS));
+		break;
+	case DRAMC_W2R:
+		iCount = emi_readl(IOMEM(addr_base+DRAMC_W2R_PAGE_MISS));
+		break;
+	case DRAMC_W2W:
+		iCount = emi_readl(IOMEM(addr_base+DRAMC_W2W_PAGE_MISS));
+		break;
+	case DRAMC_ALL:
+		iCount = emi_readl(IOMEM(addr_base+DRAMC_R2R_PAGE_MISS)) +
+			 emi_readl(IOMEM(addr_base+DRAMC_R2W_PAGE_MISS)) +
+			 emi_readl(IOMEM(addr_base+DRAMC_W2R_PAGE_MISS)) +
+			 emi_readl(IOMEM(addr_base+DRAMC_W2W_PAGE_MISS));
+		break;
+	default:
+		return BM_ERR_WRONG_REQ;
+	}
+
+	return iCount;
+}
+
+unsigned int MET_DRAMC_GetInterbankCount(DRAMC_Cnt_Type CountType, int chann)
+{
+	unsigned int iCount;
+	unsigned long addr_base = ADDR_DRAMC0;
+
+	if (chann == 0)
+		addr_base = ADDR_DRAMC0;
+	else if (chann == 1)
+		addr_base = ADDR_DRAMC1;
+	else if (chann == 2)
+		addr_base = ADDR_DRAMC2;
+	else if (chann == 3)
+		addr_base = ADDR_DRAMC3;
+	else
+		return BM_ERR_WRONG_REQ;
+
+
+	switch (CountType) {
+	case DRAMC_R2R:
+		iCount = emi_readl(IOMEM(addr_base+DRAMC_R2R_INTERBANK));
+		break;
+	case DRAMC_R2W:
+		iCount = emi_readl(IOMEM(addr_base+DRAMC_R2W_INTERBANK));
+		break;
+	case DRAMC_W2R:
+		iCount = emi_readl(IOMEM(addr_base+DRAMC_W2R_INTERBANK));
+		break;
+	case DRAMC_W2W:
+		iCount = emi_readl(IOMEM(addr_base+DRAMC_W2W_INTERBANK));
+		break;
+	case DRAMC_ALL:
+		iCount = emi_readl(IOMEM(addr_base+DRAMC_R2R_INTERBANK)) +
+			 emi_readl(IOMEM(addr_base+DRAMC_R2W_INTERBANK)) +
+			 emi_readl(IOMEM(addr_base+DRAMC_W2R_INTERBANK)) +
+			 emi_readl(IOMEM(addr_base+DRAMC_W2W_INTERBANK));
+		break;
+	default:
+		return BM_ERR_WRONG_REQ;
+	}
+
+	return iCount;
+}
+
+unsigned int MET_DRAMC_GetIdleCount(int chann)
+{
+	unsigned long addr_base = ADDR_DRAMC0;
+
+	if (chann == 0)
+		addr_base = ADDR_DRAMC0;
+	else if (chann == 1)
+		addr_base = ADDR_DRAMC1;
+	else if (chann == 2)
+		addr_base = ADDR_DRAMC2;
+	else if (chann == 3)
+		addr_base = ADDR_DRAMC3;
+	else
+		return BM_ERR_WRONG_REQ;
+
+	return emi_readl(IOMEM(addr_base+DRAMC_IDLE_COUNT));
+}
+
+unsigned int MET_DRAMC_Misc_Status(int chann)
+{
+	unsigned long addr_base = ADDR_DRAMC0;
+
+	if (chann == 0)
+		addr_base = ADDR_DRAMC0;
+	else if (chann == 1)
+		addr_base = ADDR_DRAMC1;
+	else if (chann == 2)
+		addr_base = ADDR_DRAMC2;
+	else if (chann == 3)
+		addr_base = ADDR_DRAMC3;
+	else
+		return BM_ERR_WRONG_REQ;
+
+	return emi_readl(IOMEM(addr_base+DRAMC_MISC_STATUSA));
+}
+
+int MET_DRAMC_BMPause(int chann, int set)
+{
+	unsigned long addr_base = ADDR_DRAMC0;
+	unsigned int value;
+
+	if (chann == 0)
+		addr_base = ADDR_DRAMC0;
+	else if (chann == 1)
+		addr_base = ADDR_DRAMC1;
+	else if (chann == 2)
+		addr_base = ADDR_DRAMC2;
+	else if (chann == 3)
+		addr_base = ADDR_DRAMC3;
+	else
+		return BM_ERR_WRONG_REQ;
+
+	value = emi_readl(IOMEM(addr_base+DRAMC_DMMONITOR));
+	if (set == 0) {
+		/* Continue DRAMC Monitor*/
+		value = (value & 0xFFFFFFFB);
+	}
+	else if (set == 1) {
+		/* Pause DRAMC Monitor*/
+		value = (value | 0x00000004);
+	}
+	else
+		return BM_ERR_WRONG_REQ;
+	emi_reg_sync_writel(value, addr_base+DRAMC_DMMONITOR);
+
+	return BM_REQ_OK;
+}
+
+
+int MET_DRAMC_BMEnable(int chann, int set)
+{
+	unsigned long addr_base = ADDR_DRAMC0;
+	unsigned int value;
+
+	if (chann == 0)
+		addr_base = ADDR_DRAMC0;
+	else if (chann == 1)
+		addr_base = ADDR_DRAMC1;
+	else if (chann == 2)
+		addr_base = ADDR_DRAMC2;
+	else if (chann == 3)
+		addr_base = ADDR_DRAMC3;
+	else
+		return BM_ERR_WRONG_REQ;
+
+	value = emi_readl(IOMEM(addr_base+DRAMC_DMMONITOR));
+	if (set == 0) {
+		/* Disable DRAMC Monitor*/
+		value = (value & 0xFFFFFFF7);
+	}
+	else if (set == 1) {
+		/* Enable DRAMC Monitor*/
+		value = (value | 0x00000008);
+	}
+	else
+		return BM_ERR_WRONG_REQ;
+	emi_reg_sync_writel(value, addr_base+DRAMC_DMMONITOR);
+
+	return BM_REQ_OK;
+}
+
+unsigned int MET_DRAMC_RefPop(int chann)
+{
+	unsigned long addr_base = ADDR_DRAMC0;
+
+	if (chann == 0)
+		addr_base = ADDR_DRAMC0;
+	else if (chann == 1)
+		addr_base = ADDR_DRAMC1;
+	else if (chann == 2)
+		addr_base = ADDR_DRAMC2;
+	else if (chann == 3)
+		addr_base = ADDR_DRAMC3;
+	else
+		return BM_ERR_WRONG_REQ;
+
+	return emi_readl(IOMEM(addr_base+DRAMC_REFRESH_POP));
+}
+
+
+
+unsigned int MET_DRAMC_Free26M(int chann)
+{
+	unsigned long addr_base = ADDR_DRAMC0;
+
+	if (chann == 0)
+		addr_base = ADDR_DRAMC0;
+	else if (chann == 1)
+		addr_base = ADDR_DRAMC1;
+	else if (chann == 2)
+		addr_base = ADDR_DRAMC2;
+	else if (chann == 3)
+		addr_base = ADDR_DRAMC3;
+	else
+		return BM_ERR_WRONG_REQ;
+
+	return emi_readl(IOMEM(addr_base+DRAMC_FREERUN_26M));
+}
+
+unsigned int MET_DRAMC_RByte(int chann)
+{
+	unsigned long addr_base = ADDR_DRAMC0;
+
+	if (chann == 0)
+		addr_base = ADDR_DRAMC0;
+	else if (chann == 1)
+		addr_base = ADDR_DRAMC1;
+	else if (chann == 2)
+		addr_base = ADDR_DRAMC2;
+	else if (chann == 3)
+		addr_base = ADDR_DRAMC3;
+	else
+		return BM_ERR_WRONG_REQ;
+
+	return emi_readl(IOMEM(addr_base+DRAMC_READ_BYTES));
+}
+
+unsigned int MET_DRAMC_WByte(int chann)
+{
+	unsigned long addr_base = ADDR_DRAMC0;
+
+	if (chann == 0)
+		addr_base = ADDR_DRAMC0;
+	else if (chann == 1)
+		addr_base = ADDR_DRAMC1;
+	else if (chann == 2)
+		addr_base = ADDR_DRAMC2;
+	else if (chann == 3)
+		addr_base = ADDR_DRAMC3;
+	else
+		return BM_ERR_WRONG_REQ;
+
+	return emi_readl(IOMEM(addr_base+DRAMC_WRITE_BYTES));
+}
+
+unsigned int MET_EMI_GetMDCT(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI+EMI_MDCT));
+}
+
+unsigned int MET_EMI_GetMDCT_2ND(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI+EMI_MDCT_2ND));
+}
+
+
+unsigned int MET_EMI_GetARBA(void)
+{
+	/* EMI_ARBA EMI Bandwidth Filter Control M0/1 */
+	return emi_readl(IOMEM(ADDR_EMI+EMI_ARBA));
+}
+
+unsigned int MET_EMI_GetARBB(void)
+{
+	/* return emi_readl(IOMEM(ADDR_EMI+EMI_ARBB)); */
+	/* Note: Olympus Coda doesn't has this regiester, So we return 0 */
+	return 0;
+}
+
+unsigned int MET_EMI_GetARBC(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI+EMI_ARBC));
+}
+
+unsigned int MET_EMI_GetARBD(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI+EMI_ARBD));
+}
+
+unsigned int MET_EMI_GetARBE(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI+EMI_ARBE));
+}
+
+unsigned int MET_EMI_GetARBF(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI+EMI_ARBF));
+}
+
+unsigned int MET_EMI_GetARBG(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI+EMI_ARBG));
+}
+
+unsigned int MET_EMI_GetARBH(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI+EMI_ARBH));
+}
+
+/* Total BW status*/
+unsigned int MET_EMI_GetBWCT0(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI+EMI_BWCT0));
+}
+
+/* Total BW status*/
+unsigned int MET_EMI_GetBWCT1(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI+EMI_BWCT1));
+}
+
+/* Total BW status*/
+unsigned int MET_EMI_GetBWCT2(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI+EMI_BWCT2));
+}
+
+/* Total BW status*/
+unsigned int MET_EMI_GetBWCT3(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI+EMI_BWCT3));
+}
+
+/* Total BW status*/
+unsigned int MET_EMI_GetBWCT4(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI+EMI_BWCT4));
+}
+
+/* Total BW status*/
+unsigned int MET_EMI_GetBWST0(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI+EMI_BWST0));
+}
+
+/* Total BW status*/
+unsigned int MET_EMI_GetBWST1(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI+EMI_BWST1));
+}
+
+
+/* C+G BW status*/
+unsigned int MET_EMI_GetBWCT0_2ND(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI+EMI_BWCT0_2ND));
+}
+
+/* C+G BW status*/
+unsigned int MET_EMI_GetBWCT1_2ND(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI+EMI_BWCT1_2ND));
+}
+
+/* C+G BW status*/
+unsigned int MET_EMI_GetBWST_2ND(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI+EMI_BWST_2ND));
+}
+
+unsigned int MET_EMI_GetBMRW0(void)
+{
+	return readl(IOMEM(ADDR_EMI+EMI_BMRW0));
+}
+
+void emi_dump_reg(void)
+{
+	int i;
+
+	MET_TRACE("[emi_regdump]\n");
+	for (i = 0x400; i < 0x500; i = i+16)
+		MET_TRACE("%4x__ %8x %8x %8x %8x\n", i,  readl(IOMEM(ADDR_EMI+i)),
+			readl(IOMEM(ADDR_EMI+i+4)), readl(IOMEM(ADDR_EMI+i+8)), readl(IOMEM(ADDR_EMI+i+12)));
+}
+
+unsigned int MET_EMI_GetDramChannNum(void)
+{
+	int num = -1;
+
+	if (BaseAddrEMI) {
+		num = emi_readl(IOMEM(ADDR_EMI+EMI_CONA));
+		num = ((num >> 8) & 0x0000003);
+	} else {
+		return 1;
+	}
+
+	if (num == M0_DOUBLE_HALF_BW_1CH)
+		return 1;
+	else if (num == M0_DOUBLE_HALF_BW_2CH)
+		return 2;
+	else if (num == M0_DOUBLE_HALF_BW_4CH)
+		return 4;
+
+	else		/* default return single channel */
+		return 1;
+
+}
+
+void MET_DRAMC_GetDebugCounter(int *value, int chann)
+{
+	int i;
+	unsigned long addr_base = ADDR_DRAMC0;
+
+	for (i = 0; i < chann; i++)	{
+
+		if (i == 0)
+			addr_base = ADDR_DRAMC0;
+		else if (i == 1)
+			addr_base = ADDR_DRAMC1;
+		else if (i == 2)
+			addr_base = ADDR_DRAMC2;
+		else if (i == 3)
+			addr_base = ADDR_DRAMC3;
+		else
+			return;
+
+
+		value[DRAMC_Debug_MAX_CNT*i + BM_RK0_PRE_STANDBY] =
+			emi_readl(IOMEM(addr_base + DRAMC_RK0_PRE_STANDBY));
+		value[DRAMC_Debug_MAX_CNT*i + BM_RK0_PRE_POWERDOWN] =
+			emi_readl(IOMEM(addr_base + DRAMC_RK0_PRE_POWERDOWN));
+		value[DRAMC_Debug_MAX_CNT*i + BM_RK0_ACT_STANDBY] =
+			emi_readl(IOMEM(addr_base + DRAMC_RK0_ACT_STANDBY));
+		value[DRAMC_Debug_MAX_CNT*i + BM_RK0_ACT_POWERDOWN] =
+			emi_readl(IOMEM(addr_base + DRAMC_RK0_ACT_POWERDOWN));
+
+		value[DRAMC_Debug_MAX_CNT*i + BM_RK1_PRE_STANDBY] =
+			emi_readl(IOMEM(addr_base + DRAMC_RK1_PRE_STANDBY));
+		value[DRAMC_Debug_MAX_CNT*i + BM_RK1_PRE_POWERDOWN] =
+			emi_readl(IOMEM(addr_base + DRAMC_RK1_PRE_POWERDOWN));
+		value[DRAMC_Debug_MAX_CNT*i + BM_RK1_ACT_STANDBY] =
+			emi_readl(IOMEM(addr_base + DRAMC_RK1_ACT_STANDBY));
+		value[DRAMC_Debug_MAX_CNT*i + BM_RK1_ACT_POWERDOWN] =
+			emi_readl(IOMEM(addr_base + DRAMC_RK1_ACT_POWERDOWN));
+
+		value[DRAMC_Debug_MAX_CNT*i + BM_RK2_PRE_STANDBY] =
+			emi_readl(IOMEM(addr_base + DRAMC_RK2_PRE_STANDBY));
+		value[DRAMC_Debug_MAX_CNT*i + BM_RK2_PRE_POWERDOWN] =
+			emi_readl(IOMEM(addr_base + DRAMC_RK2_PRE_POWERDOWN));
+		value[DRAMC_Debug_MAX_CNT*i + BM_RK2_ACT_STANDBY] =
+			emi_readl(IOMEM(addr_base + DRAMC_RK2_ACT_STANDBY));
+		value[DRAMC_Debug_MAX_CNT*i + BM_RK2_ACT_POWERDOWN] =
+			emi_readl(IOMEM(addr_base + DRAMC_RK2_ACT_POWERDOWN));
+	}
+}
+#if 1
+static inline unsigned int dramc_reg_read(unsigned long addr)
+{
+#if 0
+#ifdef CONFIG_64BIT
+	unsigned int value = 0;
+	value = HAL_READ32((void*)addr);
+#else
+	unsigned int value = readl((void*)addr);
+#endif
+#endif
+
+	unsigned int value = readl((void*)addr);
+
+	mb();
+	return value;
+}
+
+#if 0
+u32 DRAMC_GetDramcFreq(void)
+{
+	unsigned long tcm_dramc_flags = 0;
+	tcm_dramc_flags = dramc_reg_read(TCM_DRAM_FLAGS_ADDR);
+	return TCMGET_DDR_CLK(tcm_dramc_flags);
+}
+
+unsigned int get_dram_data_rate(void)
+{
+	return DRAMC_GetDramcFreq()/1000000;
+}
+#else
+
+#define DRAM_DATARATE_REG	0x10100D9C
+
+u32 DRAMC_GetDramcFreq(void)
+{
+	unsigned long reg_val;
+	unsigned long dram_datarate = 0;
+
+	reg_val = dramc_reg_read((unsigned long)(AddrDramDatarate + 0xD9C));
+//	pr_err("dram datarate reg val = 0x%lx, reg_val[9-16] = %lu\n", reg_val, (reg_val >> 9) & 0xff);
+	dram_datarate = (((dramc_reg_read((unsigned long)(AddrDramDatarate + 0xD9C))) >> 16) & 0x1ffff) * 26 / 256;
+
+	return dram_datarate;
+}
+
+unsigned int get_dram_data_rate(void)
+{
+	return DRAMC_GetDramcFreq();
+}
+#endif
+
+
+#else
+#define DRAMC_AO_CHA_BASE_ADDR (DRAM_BASE)
+#define PDEF_DRAMC0_CHA_REG_0E4 IOMEM((DRAMC_AO_CHA_BASE_ADDR + 0x00e4))
+#define PDEF_DRAMC0_CHA_REG_010 IOMEM((DRAMC_AO_CHA_BASE_ADDR + 0x0010))
+#define DDRPHY_CHA_BASE_ADDR (DRAM_DDRPHY_CHA_BANK)
+unsigned int DRAM_TYPE = 0;
+enum DDRTYPE {
+        TYPE_LPDDR3 = 1,
+        TYPE_LPDDR4,
+        TYPE_LPDDR4X
+};
+
+static unsigned int get_shuffle_status(void)
+{
+	/* HPM = 0, LPM = 1, ULPM = 2; */
+	return (emi_readl(PDEF_DRAMC0_CHA_REG_0E4) & 0x6) >> 1;
+}
+
+unsigned int get_dram_data_rate(void)
+{
+	return DRAMC_GetDramcFreq();
+
+	unsigned long addr_base = DDRPHY_CHA_BASE_ADDR;
+	unsigned int u4ShuLevel, u4SDM_PCW, u4PREDIV, u4POSDIV, u4CKDIV4, u4VCOFreq, u4DataRate = 0;
+	if (DRAM_TYPE == 0)
+		DRAM_TYPE = (emi_readl(PDEF_DRAMC0_CHA_REG_010) & 0x1C00) >> 10;
+	u4ShuLevel = get_shuffle_status();
+
+	u4SDM_PCW = emi_readl(IOMEM(addr_base + 0xd94 + 0x500 * u4ShuLevel)) >> 16;
+	u4PREDIV = (emi_readl(IOMEM(addr_base + 0xda0 + 0x500 * u4ShuLevel)) & 0x000c0000) >> 18;
+	u4POSDIV = emi_readl(IOMEM(addr_base + 0xda0 + 0x500 * u4ShuLevel)) & 0x00000007;
+	u4CKDIV4 = (emi_readl(IOMEM(addr_base + 0xd18 + 0x500 * u4ShuLevel)) & 0x08000000) >> 27;
+
+	u4VCOFreq = ((52>>u4PREDIV)*(u4SDM_PCW>>8))>>u4POSDIV;
+
+	u4DataRate = u4VCOFreq>>u4CKDIV4;
+
+	/* pr_err("[DRAMC Driver] u4ShuLevel=%d, PCW=0x%X, u4PREDIV=%d, u4POSDIV=%d, CKDIV4=%d, DataRate=%d\n",
+	 * u4ShuLevel, u4SDM_PCW, u4PREDIV, u4POSDIV, u4CKDIV4, u4DataRate);
+	 */
+
+//	if (DRAM_TYPE == TYPE_LPDDR4X) {
+		if (u4DataRate == 3198)
+			u4DataRate = 3200;
+		else if (u4DataRate == 2652)
+			u4DataRate = 2667;
+		else if (u4DataRate == 1599)
+			u4DataRate = 1600;
+		else if (u4DataRate == 799)
+			u4DataRate = 800;
+		else
+			u4DataRate = 0;
+//	} else
+//		u4DataRate = 0;
+
+	return u4DataRate;
+}
+#endif
diff --git a/src/devtools/met-driver/4.19/mt2712/mtk_emi_bm.h b/src/devtools/met-driver/4.19/mt2712/mtk_emi_bm.h
new file mode 100644
index 0000000..921d80d
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/mtk_emi_bm.h
@@ -0,0 +1,414 @@
+#ifndef __MT_MET_EMI_BM_H__
+#define __MT_MET_EMI_BM_H__
+
+
+
+
+#define DEF_BM_RW_TYPE		(BM_BOTH_READ_WRITE)
+#define NTS					2
+#define NWSCT				4
+#define NLATENCY			8
+#define NTRANS				8
+#define NALL					3
+#define NTTYPE				5
+#define NIDX_EMI			(NTS + NWSCT + NLATENCY + NTRANS + NALL + NTTYPE)
+
+#define NCNT					9
+#define NCH					4
+#define NIDX_DRAMC			(NCNT * NCH)
+#define NIDX					(NIDX_EMI + NIDX_DRAMC)
+
+#define NCLK					1
+#define NARB					8
+#define NBW					10
+#define NIDX_BL				(NCLK + NARB + NBW)
+
+/* 1000 To Khz and 4x freq & 2x data rate for LPDDR4 */
+/* 1000 To Khz and 2x freq & 2x data rate for LPDDR3*/
+/* TBD: calculate emi clock rate from DRAM DATA RATE */
+
+/*dram baseclock/EMI clock  :	LP4=4	LP3=2	*/
+#define DRAM_EMI_BASECLOCK_RATE	4
+/*dram io width  :	LP4=x16		LP3=x32 or x16	*/
+#define DRAM_IO_BUS_WIDTH		16
+/*dram datarate  :	DDR=double */
+#define DRAM_DATARATE	2
+
+#define	ADDR_EMI		((unsigned long) BaseAddrEMI)
+#define	ADDR_DRAMC0	((unsigned long) BaseAddrDRAMC0)
+#define	ADDR_DRAMC1	((unsigned long) BaseAddrDRAMC1)
+#define	ADDR_DRAMC2	((unsigned long) BaseAddrDRAMC2)
+#define	ADDR_DRAMC3	((unsigned long) BaseAddrDRAMC3)
+
+static const char of_emi_desc[] = "mediatek,mt2712-emi";
+static const char of_dramc_desc[] = "mediatek,mt2712-dramc";
+
+typedef enum {
+	DRAMC_DTS_DRAMC0_AO = 0x0,
+	DRAMC_DTS_DRAMC0_NAO = 0x4,
+	DRAMC_DTS_DRAMC1_NAO = 0x5,
+	DRAMC_DTS_DRAMC2_NAO = 0x6,
+	DRAMC_DTS_DRAMC3_NAO = 0x7,
+	DRAMC_DTS_DDRPHY0_AO = 0x8,
+}	BM_DRAMC_DTS_INDEX;
+
+#define BM_Master_M0_name	"m0_APMCU0"
+#define BM_Master_M1_name	"m1_APMCU1"
+#define BM_Master_M2_name	"m2_MM1_M1"
+#define BM_Master_M3_name	"m3_MM2_M1"
+#define BM_Master_M4_name	"m4_MM2_M0"
+#define BM_Master_M5_name	"m5_MM1_M0"
+#define BM_Master_M6_name	"m6_PERI"
+#define BM_Master_M7_name	"m7_GPU"
+
+#define BM_Master_GP_AP	(BM_MASTER_M0 | BM_MASTER_M1)
+#define BM_Master_GP_MM	(BM_MASTER_M2 | BM_MASTER_M3 | BM_MASTER_M4 | BM_MASTER_M5)
+#define BM_Master_GP_GPU	(BM_MASTER_M7)
+#define BM_Master_GP_PERI	(BM_MASTER_M6)
+
+
+/*Need no change by project*/
+#define BM_Master_GP_1_Default	BM_Master_GP_AP
+#define BM_Master_GP_2_Default	BM_Master_GP_MM
+#define BM_Master_GP_3_Default	BM_Master_GP_GPU
+
+#define BM_MASTER_M0		(0x01)
+#define BM_MASTER_M1		(0x02)
+#define BM_MASTER_M2		(0x04)
+#define BM_MASTER_M3		(0x08)
+#define BM_MASTER_M4		(0x10)
+#define BM_MASTER_M5		(0x20)
+#define BM_MASTER_M6		(0x40)
+#define BM_MASTER_M7		(0x80)
+#define BM_MASTER_ALL		(0xFF)
+
+typedef enum {
+	BM_RK0_PRE_STANDBY = 0x0,
+	BM_RK0_PRE_POWERDOWN,
+	BM_RK0_ACT_STANDBY,
+	BM_RK0_ACT_POWERDOWN,
+	BM_RK1_PRE_STANDBY,
+	BM_RK1_PRE_POWERDOWN,
+	BM_RK1_ACT_STANDBY,
+	BM_RK1_ACT_POWERDOWN,
+	BM_RK2_PRE_STANDBY,
+	BM_RK2_PRE_POWERDOWN,
+	BM_RK2_ACT_STANDBY,
+	BM_RK2_ACT_POWERDOWN,
+	DRAMC_Debug_MAX_CNT
+} DRAMC_Debug_Type;
+
+typedef enum {
+	DRAMC_R2R,
+	DRAMC_R2W,
+	DRAMC_W2R,
+	DRAMC_W2W,
+	DRAMC_ALL
+} DRAMC_Cnt_Type;
+
+typedef enum {
+	BM_BOTH_READ_WRITE,
+	BM_READ_ONLY,
+	BM_WRITE_ONLY
+} BM_RW_Type;
+
+enum {
+	BM_TRANS_TYPE_1BEAT = 0x0,
+	BM_TRANS_TYPE_2BEAT,
+	BM_TRANS_TYPE_3BEAT,
+	BM_TRANS_TYPE_4BEAT,
+	BM_TRANS_TYPE_5BEAT,
+	BM_TRANS_TYPE_6BEAT,
+	BM_TRANS_TYPE_7BEAT,
+	BM_TRANS_TYPE_8BEAT,
+	BM_TRANS_TYPE_9BEAT,
+	BM_TRANS_TYPE_10BEAT,
+	BM_TRANS_TYPE_11BEAT,
+	BM_TRANS_TYPE_12BEAT,
+	BM_TRANS_TYPE_13BEAT,
+	BM_TRANS_TYPE_14BEAT,
+	BM_TRANS_TYPE_15BEAT,
+	BM_TRANS_TYPE_16BEAT,
+	BM_TRANS_TYPE_1Byte = 0 << 4,
+	BM_TRANS_TYPE_2Byte = 1 << 4,
+	BM_TRANS_TYPE_4Byte = 2 << 4,
+	BM_TRANS_TYPE_8Byte = 3 << 4,
+	BM_TRANS_TYPE_16Byte = 4 << 4,
+	BM_TRANS_TYPE_32Byte = 5 << 4,
+	BM_TRANS_TYPE_BURST_WRAP = 0 << 7,
+	BM_TRANS_TYPE_BURST_INCR = 1 << 7
+};
+
+enum {
+	BM_TRANS_RW_DEFAULT = 0x0,
+	BM_TRANS_RW_READONLY,
+	BM_TRANS_RW_WRITEONLY,
+	BM_TRANS_RW_RWBOTH
+};
+
+
+/*coda busid 12bit, but HW support 16 bit*/
+#define EMI_BMID_MASK					(0xFFFF)
+#define BM_COUNTER_MAX				(21)
+
+/*
+*#define BUS_MON_EN		    (0x00000001)
+*#define BUS_MON_PAUSE		(0x00000002)
+*#define BUS_MON_IDLE		(0x00000008)
+*#define BC_OVERRUN		    (0x00000100)
+*/
+enum {
+	BUS_MON_EN_SHIFT = 0,
+	BUS_MON_PAUSE_SHIFT = 1,
+	BUS_MON_IDLE_SHIFT = 3,
+	BC_OVERRUN_SHIFT = 8,
+};
+
+#define BM_REQ_OK						(0)
+#define BM_ERR_WRONG_REQ				(-1)
+#define BM_ERR_OVERRUN					(-2)
+
+#define BM_WSCT_TSCT_IDSEL_ENABLE	(0)
+#define BM_WSCT_TSCT_IDSEL_DISABLE	(-1)
+#define BM_TTYPE1_16_ENABLE			(0)
+#define BM_TTYPE1_16_DISABLE			(-1)
+#define BM_TTYPE17_21_ENABLE			(0)
+#define BM_TTYPE17_21_DISABLE			(-1)
+#define BM_BW_LIMITER_ENABLE			(0)
+#define BM_BW_LIMITER_DISABLE			(-1)
+
+#define M0_DOUBLE_HALF_BW_1CH	(0x0)
+#define M0_DOUBLE_HALF_BW_2CH	(0x1)
+#define M0_DOUBLE_HALF_BW_4CH	(0x2)
+
+#ifdef CONFIG_MTK_TINYSYS_SSPM_SUPPORT
+/*ondiemet emi ipi command*/
+typedef enum {
+	SET_BASE_EMI = 0x0,
+	SET_BASE_DRAMC0,
+	SET_BASE_DRAMC1,
+	SET_BASE_DRAMC2,
+	SET_BASE_DRAMC3,
+	SET_BASE_DDRPHY0AO,
+	SET_BASE_DRAMC0_AO,
+	SET_EBM_CONFIGS,
+}	BM_EMI_IPI_Type;
+#endif
+
+#define	EMI_OFF			0x0000
+#define EMI_CONA		(0x000-EMI_OFF)
+#define EMI_CONH		(0x038-EMI_OFF)
+#define EMI_CONM		(0x060-EMI_OFF)
+#define EMI_CONO		(0x070-EMI_OFF)
+
+#define EMI_MDCT		(0x078-EMI_OFF)
+#define EMI_MDCT_2ND    (0x07C-EMI_OFF)
+
+#define EMI_ARBA		(0x100-EMI_OFF)
+#define EMI_ARBB		(0x108-EMI_OFF)
+#define EMI_ARBC		(0x110-EMI_OFF)
+#define EMI_ARBD		(0x118-EMI_OFF)
+#define EMI_ARBE		(0x120-EMI_OFF)
+#define EMI_ARBF		(0x128-EMI_OFF)
+#define EMI_ARBG		(0x130-EMI_OFF)
+#define EMI_ARBG_2ND    (0x134-EMI_OFF)
+#define EMI_ARBH		(0x138-EMI_OFF)
+
+#define EMI_BMEN		(0x400-EMI_OFF)
+#define EMI_BCNT		(0x408-EMI_OFF)
+#define EMI_TACT		(0x410-EMI_OFF)
+#define EMI_TSCT		(0x418-EMI_OFF)
+#define EMI_WACT		(0x420-EMI_OFF)
+#define EMI_WSCT		(0x428-EMI_OFF)
+#define EMI_BACT		(0x430-EMI_OFF)
+#define EMI_BSCT		(0x438-EMI_OFF)
+
+#define EMI_MSEL		(0x440-EMI_OFF)
+#define EMI_TSCT2		(0x448-EMI_OFF)
+#define EMI_TSCT3		(0x450-EMI_OFF)
+#define EMI_WSCT2		(0x458-EMI_OFF)
+#define EMI_WSCT3		(0x460-EMI_OFF)
+#define EMI_WSCT4		(0x464-EMI_OFF)
+#define EMI_MSEL2		(0x468-EMI_OFF)
+#define EMI_MSEL3		(0x470-EMI_OFF)
+#define EMI_MSEL4		(0x478-EMI_OFF)
+#define EMI_MSEL5		(0x480-EMI_OFF)
+#define EMI_MSEL6		(0x488-EMI_OFF)
+#define EMI_MSEL7		(0x490-EMI_OFF)
+#define EMI_MSEL8		(0x498-EMI_OFF)
+#define EMI_MSEL9		(0x4A0-EMI_OFF)
+#define EMI_MSEL10		(0x4A8-EMI_OFF)
+
+#define EMI_BMID0		(0x4B0-EMI_OFF)
+#define EMI_BMID1		(0x4B4-EMI_OFF)
+#define EMI_BMID2		(0x4B8-EMI_OFF)
+#define EMI_BMID3		(0x4BC-EMI_OFF)
+#define EMI_BMID4		(0x4C0-EMI_OFF)
+#define EMI_BMID5		(0x4C4-EMI_OFF)
+#define EMI_BMID6		(0x4C8-EMI_OFF)
+#define EMI_BMID7		(0x4CC-EMI_OFF)
+#define EMI_BMID8		(0x4D0-EMI_OFF)
+#define EMI_BMID9		(0x4D4-EMI_OFF)
+#define EMI_BMID10		(0x4D8-EMI_OFF)
+
+#define EMI_BMEN1		(0x4E0-EMI_OFF)
+#define EMI_BMEN2		(0x4E8-EMI_OFF)
+#define EMI_BMRW0		(0x4F8-EMI_OFF)
+#define EMI_BMRW1		(0x4FC-EMI_OFF)
+#define EMI_TTYPE1		(0x500-EMI_OFF)
+#define EMI_TTYPE2		(0x508-EMI_OFF)
+#define EMI_TTYPE3		(0x510-EMI_OFF)
+#define EMI_TTYPE4		(0x518-EMI_OFF)
+#define EMI_TTYPE5		(0x520-EMI_OFF)
+#define EMI_TTYPE6		(0x528-EMI_OFF)
+#define EMI_TTYPE7		(0x530-EMI_OFF)
+#define EMI_TTYPE8		(0x538-EMI_OFF)
+#define EMI_TTYPE9		(0x540-EMI_OFF)
+#define EMI_TTYPE10		(0x548-EMI_OFF)
+#define EMI_TTYPE11		(0x550-EMI_OFF)
+#define EMI_TTYPE12		(0x558-EMI_OFF)
+#define EMI_TTYPE13		(0x560-EMI_OFF)
+#define EMI_TTYPE14		(0x568-EMI_OFF)
+#define EMI_TTYPE15		(0x570-EMI_OFF)
+#define EMI_TTYPE16		(0x578-EMI_OFF)
+#define EMI_TTYPE17		(0x580-EMI_OFF)
+#define EMI_TTYPE18		(0x588-EMI_OFF)
+#define EMI_TTYPE19		(0x590-EMI_OFF)
+#define EMI_TTYPE20		(0x598-EMI_OFF)
+#define EMI_TTYPE21		(0x5A0-EMI_OFF)
+
+#define EMI_BWCT0		(0x5B0-EMI_OFF)
+#define EMI_BWCT1		(0x5B4-EMI_OFF)
+#define EMI_BWCT2		(0x5B8-EMI_OFF)
+#define EMI_BWCT3		(0x5BC-EMI_OFF)
+#define EMI_BWCT4		(0x5C0-EMI_OFF)
+#define EMI_BWST0		(0x5C4-EMI_OFF)
+#define EMI_BWST1		(0x5C8-EMI_OFF)
+
+#define EMI_BWCT0_2ND	(0x6A0-EMI_OFF)
+#define EMI_BWCT1_2ND	(0x6A4-EMI_OFF)
+#define EMI_BWST_2ND	(0x6A8-EMI_OFF)
+
+#define DRAMC_DMMONITOR		0x24
+#define DRAMC_MISC_STATUSA	0x80
+#define DRAMC_REFRESH_POP       0x300
+#define DRAMC_FREERUN_26M       0x304
+#define DRAMC_R2R_PAGE_HIT	0x30C
+#define DRAMC_R2R_PAGE_MISS	0x310
+#define DRAMC_R2R_INTERBANK	0x314
+#define DRAMC_R2W_PAGE_HIT	0x318
+#define DRAMC_R2W_PAGE_MISS	0x31C
+#define DRAMC_R2W_INTERBANK	0x320
+#define DRAMC_W2R_PAGE_HIT	0x324
+#define DRAMC_W2R_PAGE_MISS	0x328
+#define DRAMC_W2R_INTERBANK	0x32C
+#define DRAMC_W2W_PAGE_HIT	0x330
+#define DRAMC_W2W_PAGE_MISS	0x334
+#define DRAMC_W2W_INTERBANK	0x338
+#define DRAMC_IDLE_COUNT	0x308
+#define DRAMC_RK0_PRE_STANDBY   0x33c
+#define DRAMC_RK0_PRE_POWERDOWN 0x340
+#define DRAMC_RK0_ACT_STANDBY   0x344
+#define DRAMC_RK0_ACT_POWERDOWN 0x348
+#define DRAMC_RK1_PRE_STANDBY   0x34c
+#define DRAMC_RK1_PRE_POWERDOWN 0x350
+#define DRAMC_RK1_ACT_STANDBY   0x354
+#define DRAMC_RK1_ACT_POWERDOWN 0x358
+#define DRAMC_RK2_PRE_STANDBY   0x35c
+#define DRAMC_RK2_PRE_POWERDOWN 0x360
+#define DRAMC_RK2_ACT_STANDBY   0x364
+#define DRAMC_RK2_ACT_POWERDOWN 0x368
+#define DRAMC_READ_BYTES	0x38c
+#define DRAMC_WRITE_BYTES	0x390
+
+
+extern void emi_dump_reg(void);
+extern int MET_BM_Init(void);
+extern void MET_BM_DeInit(void);
+extern void MET_BM_Enable(const unsigned int enable);
+extern void MET_BM_Pause(void);
+extern void MET_BM_Continue(void);
+extern unsigned int MET_BM_IsOverrun(void);
+extern unsigned int MET_BM_GetReadWriteType(void);
+extern void MET_BM_SetReadWriteType(const unsigned int ReadWriteType);
+extern int MET_BM_GetBusCycCount(void);
+extern unsigned int MET_BM_GetTransAllCount(void);
+extern int MET_BM_GetTransCount(const unsigned int counter_num);
+extern int MET_BM_GetWordAllCount(void);
+extern int MET_BM_GetWordCount(const unsigned int counter_num);
+extern unsigned int MET_BM_GetBandwidthWordCount(void);
+extern unsigned int MET_BM_GetOverheadWordCount(void);
+extern int MET_BM_GetTransTypeCount(const unsigned int counter_num);
+extern int MET_BM_GetMDCT(void);
+extern int MET_BM_GetMDCT_2(void);
+extern int MET_BM_GetMonitorCounter(const unsigned int counter_num,
+		unsigned int *master,
+		unsigned int *trans_type);
+extern int MET_BM_SetMDCT_MDMCU(unsigned int mdmcu_rd_buf);
+extern int MET_BM_SetMonitorCounter(const unsigned int counter_num,
+		const unsigned int master,
+		const unsigned int trans_type);
+extern int MET_BM_SetTtypeCounterRW(unsigned int bmrw0_val, unsigned int bmrw1_val);
+extern int MET_BM_Set_WsctTsct_id_sel(unsigned int counter_num, unsigned int enable);
+extern int MET_BM_SetMaster(const unsigned int counter_num,
+		const unsigned int master);
+extern int MET_BM_SetIDSelect(const unsigned int counter_num,
+		const unsigned int id,
+		const unsigned int enable);
+extern int MET_BM_SetUltraHighFilter(const unsigned int counter_num,
+		const unsigned int enable);
+extern int MET_BM_SetLatencyCounter(unsigned int enable);
+extern int MET_BM_GetLatencyCycle(const unsigned int counter_num);
+extern unsigned int MET_BM_GetEmiDcm(void);
+extern int MET_BM_SetEmiDcm(const unsigned int setting);
+
+/* DRAMC */
+extern unsigned int MET_DRAMC_GetPageHitCount(DRAMC_Cnt_Type CountType, int chann);
+extern unsigned int MET_DRAMC_GetPageMissCount(DRAMC_Cnt_Type CountType, int chann);
+extern unsigned int MET_DRAMC_GetInterbankCount(DRAMC_Cnt_Type CountType, int chann);
+extern unsigned int MET_DRAMC_GetIdleCount(int chann);
+extern unsigned int MET_DRAMC_Misc_Status(int chann);
+extern unsigned int MET_DRAMC_RefPop(int chann);
+extern unsigned int MET_DRAMC_Free26M(int chann);
+extern unsigned int MET_DRAMC_RByte(int chann);
+extern unsigned int MET_DRAMC_WByte(int chann);
+extern int MET_DRAMC_BMEnable(int chann, int set);
+extern int MET_DRAMC_BMPause(int chann, int set);
+extern unsigned int get_dram_data_rate(void);
+
+/* Config */
+unsigned int MET_EMI_GetARBA(void);
+unsigned int MET_EMI_GetARBB(void);
+unsigned int MET_EMI_GetARBC(void);
+unsigned int MET_EMI_GetARBD(void);
+unsigned int MET_EMI_GetARBE(void);
+unsigned int MET_EMI_GetARBF(void);
+unsigned int MET_EMI_GetARBG(void);
+unsigned int MET_EMI_GetARBH(void);
+
+/* Total BW status */
+extern unsigned int MET_EMI_GetBWCT0(void);
+extern unsigned int MET_EMI_GetBWCT1(void);
+extern unsigned int MET_EMI_GetBWCT2(void);
+extern unsigned int MET_EMI_GetBWCT3(void);
+extern unsigned int MET_EMI_GetBWCT4(void);
+extern unsigned int MET_EMI_GetBWST0(void);
+extern unsigned int MET_EMI_GetBWST1(void);
+/* C+G BW */
+extern unsigned int MET_EMI_GetBWCT0_2ND(void);
+extern unsigned int MET_EMI_GetBWCT1_2ND(void);
+extern unsigned int MET_EMI_GetBWST_2ND(void);
+
+unsigned int MET_EMI_GetBMRW0(void);
+unsigned int MET_EMI_GetDramChannNum(void);
+
+/* Debug Counter status */
+void MET_DRAMC_GetDebugCounter(int *value, int chann);
+
+/* ondiemet*/
+void MET_BM_IPI_baseaddr(void);
+void met_emi_phyaddr_debug(void);
+
+
+
+#endif  /* !__MT_MET_EMI_BM_H__ */
diff --git a/src/devtools/met-driver/4.19/mt2712/mtk_gpu_metmonitor.c b/src/devtools/met-driver/4.19/mt2712/mtk_gpu_metmonitor.c
new file mode 100644
index 0000000..842825f
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/mtk_gpu_metmonitor.c
@@ -0,0 +1,802 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <asm/page.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/syscalls.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <asm/uaccess.h>
+#include <linux/hrtimer.h>
+
+#include "met_drv.h"
+#include "trace.h"
+
+#include "mtk_gpu_metmonitor.h"
+#include "core_plf_init.h"
+#include "core_plf_trace.h"
+
+
+/*
+ * define if the hal implementation might re-schedule, cannot run inside softirq
+ * undefine this is better for sampling jitter if HAL support it
+ */
+#undef GPU_HAL_RUN_PREMPTIBLE
+
+#ifdef GPU_HAL_RUN_PREMPTIBLE
+static struct delayed_work gpu_dwork;
+//static struct delayed_work gpu_pwr_dwork;
+#endif
+
+/* the mt_gpufreq_get_thermal_limit_freq use mutex_lock to do its job */
+/* so, change the gpu-dvfs implementation to dwork */
+static struct delayed_work gpu_dvfs_dwork;
+
+/*
+ * GPU monitor HAL comes from alps\mediatek\kernel\include\linux\mtk_gpu_utility.h
+ *
+ * mtk_get_gpu_memory_usage(unsigned int* pMemUsage) in unit of bytes
+ *
+ * mtk_get_gpu_xxx_loading are in unit of %
+*/
+
+enum MET_GPU_PROFILE_INDEX {
+	eMET_GPU_LOADING = 0,
+	eMET_GPU_BLOCK_LOADING,	/* 1 */
+	eMET_GPU_IDLE_LOADING,	/* 2 */
+	eMET_GPU_PROFILE_CNT
+};
+
+static unsigned long g_u4AvailableInfo;
+
+noinline void GPU_Loading(unsigned char cnt, int *value)
+{
+	switch (cnt) {
+	case 1:
+		MET_TRACE("%d\n", value[0]);
+		break;
+	case 2:
+		MET_TRACE("%d,%d\n", value[0], value[1]);
+		break;
+	case 3:
+		MET_TRACE("%d,%d,%d\n", value[0], value[1], value[2]);
+		break;
+	case 4:
+		MET_TRACE("%d,%d,%d,%d\n", value[0], value[1], value[2], value[3]);
+		break;
+	default:
+		break;
+	}
+
+}
+
+noinline void GPU_Sub_Loading(unsigned int loading)
+{
+	MET_TRACE("%u\n", loading);
+}
+
+noinline void GPU_3D_Fences_Count(int count)
+{
+	MET_TRACE("%d\n", count);
+}
+
+#ifdef GPU_HAL_RUN_PREMPTIBLE
+static void gpu_GPULoading(struct work_struct *work)
+{
+	unsigned int	pu4Value[eMET_GPU_PROFILE_CNT];
+	unsigned long	u4Index = 0;
+	unsigned int	loading = 0;
+	int		count = 0;
+
+	memset(pu4Value, 0x00, sizeof(unsigned int)*eMET_GPU_PROFILE_CNT);
+	if ((1 << eMET_GPU_LOADING) & g_u4AvailableInfo) {
+		if (mtk_get_gpu_loading_symbol && mtk_get_gpu_loading_symbol(&pu4Value[u4Index]))
+			u4Index += 1;
+	}
+
+	if ((1 << eMET_GPU_BLOCK_LOADING) & g_u4AvailableInfo) {
+		if (mtk_get_gpu_block_symbol && mtk_get_gpu_block_symbol(&pu4Value[u4Index]))
+			u4Index += 1;
+	}
+
+	if ((1 << eMET_GPU_IDLE_LOADING) & g_u4AvailableInfo) {
+		if (mtk_get_gpu_idle_symbol && mtk_get_gpu_idle_symbol(&pu4Value[u4Index]))
+			u4Index += 1;
+	}
+
+	if (g_u4AvailableInfo)
+		GPU_Loading(u4Index, pu4Value);
+
+	if (mtk_get_gpu_sub_loading_symbol && mtk_get_gpu_sub_loading_symbol(&loading))
+		GPU_Sub_Loading(loading);
+
+	if (mtk_get_3D_fences_count_symbol && mtk_get_3D_fences_count_symbol(&count))
+		GPU_3D_Fences_Count(count);
+}
+#else
+static void gpu_GPULoading(unsigned long long stamp, int cpu)
+{
+	int	pu4Value[eMET_GPU_PROFILE_CNT];
+	unsigned long	u4Index = 0;
+	unsigned int	loading = 0;
+//	int		count = 0;
+
+	memset(pu4Value, 0x00, sizeof(int)*eMET_GPU_PROFILE_CNT);
+	if ((1 << eMET_GPU_LOADING) & g_u4AvailableInfo) {
+		if (mtk_get_gpu_loading_symbol) {
+			mtk_get_gpu_loading_symbol(&loading);
+			pu4Value[u4Index] = loading;
+		}
+		else
+			pu4Value[u4Index] = -1;
+
+		u4Index += 1;
+	}
+
+#if 0
+	if ((1 << eMET_GPU_BLOCK_LOADING) & g_u4AvailableInfo) {
+		if (mtk_get_gpu_block_symbol) {
+			mtk_get_gpu_block_symbol(&pu4Value[u4Index]);
+			u4Index += 1;
+		}
+	}
+
+	if ((1 << eMET_GPU_IDLE_LOADING) & g_u4AvailableInfo) {
+		if (mtk_get_gpu_idle_symbol) {
+			mtk_get_gpu_idle_symbol(&pu4Value[u4Index]);
+			u4Index += 1;
+		}
+	}
+#endif
+
+	if (g_u4AvailableInfo)
+		GPU_Loading(u4Index, pu4Value);
+
+#if 0
+	if (mtk_get_gpu_sub_loading_symbol) {
+		mtk_get_gpu_sub_loading_symbol(&loading);
+		GPU_Sub_Loading(loading);
+	}
+
+	if (mtk_get_3D_fences_count_symbol) {
+		mtk_get_3D_fences_count_symbol(&count);
+		GPU_3D_Fences_Count(count);
+	}
+#endif
+}
+#endif
+
+static void gpu_monitor_start(void)
+{
+	if (mtk_get_gpu_loading_symbol) {
+		g_u4AvailableInfo |= (1 << eMET_GPU_LOADING);
+	}
+#if 0
+	if (mtk_get_gpu_block_symbol)
+		g_u4AvailableInfo |= (1 << eMET_GPU_BLOCK_LOADING);
+	if (mtk_get_gpu_idle_symbol)
+		g_u4AvailableInfo |= (1 << eMET_GPU_IDLE_LOADING);
+#endif
+
+#ifdef GPU_HAL_RUN_PREMPTIBLE
+	INIT_DELAYED_WORK(&gpu_dwork, gpu_GPULoading);
+#endif
+}
+
+#ifdef GPU_HAL_RUN_PREMPTIBLE
+static void gpu_monitor_stop(void)
+{
+	cancel_delayed_work_sync(&gpu_dwork);
+}
+
+static void GPULoadingNotify(unsigned long long stamp, int cpu)
+{
+	schedule_delayed_work(&gpu_dwork, 0);
+}
+#endif
+
+static char help[] =
+	"  --gpu				monitor gpu status\n";
+static int gpu_status_print_help(char *buf, int len)
+{
+	return snprintf(buf, PAGE_SIZE, help);
+}
+
+static char g_pComGPUStatusHeader[] =
+	"met-info [000] 0.0: met_gpu_loading_header: ";
+static int gpu_status_print_header(char *buf, int len)
+{
+	int ret = 0;
+
+	ret = snprintf(buf, PAGE_SIZE, "%s", g_pComGPUStatusHeader);
+
+	if ((1 << eMET_GPU_LOADING) & g_u4AvailableInfo)
+		ret += snprintf(buf+ret, PAGE_SIZE-ret, "%s", "Loading\n");
+
+#if 0
+	if ((1 << eMET_GPU_BLOCK_LOADING) & g_u4AvailableInfo)
+		ret += snprintf(buf+ret, PAGE_SIZE-ret, "%s", "Blcok,");
+
+	if ((1 << eMET_GPU_IDLE_LOADING) & g_u4AvailableInfo)
+		ret += snprintf(buf+ret, PAGE_SIZE-ret, "%s", "Idle");
+
+	ret += snprintf(buf+ret, PAGE_SIZE-ret, "%s", "\n");
+
+	ret += snprintf(buf+ret, PAGE_SIZE-ret,
+			"met-info [000] 0.0: met_gpu_sub_loading_header: Loading\n");
+	ret += snprintf(buf+ret, PAGE_SIZE-ret,
+			"met-info [000] 0.0: met_gpu_3d_fences_count_header: Count\n");
+#endif
+
+	return ret;
+}
+
+struct metdevice met_gpu = {
+	.name			= "gpu",
+	.owner			= THIS_MODULE,
+	.type			= MET_TYPE_BUS,
+	.cpu_related		= 0,
+	.start			= gpu_monitor_start,
+	.mode			= 0,
+	.polling_interval	= 1,	/* ms */
+#ifdef GPU_HAL_RUN_PREMPTIBLE
+	.timed_polling		= GPULoadingNotify,
+	.stop			= gpu_monitor_stop,
+#else
+	.timed_polling		= gpu_GPULoading,
+#endif
+	.print_help		= gpu_status_print_help,
+	.print_header		= gpu_status_print_header,
+};
+
+/*
+ * GPU DVFS Monitor
+ */
+#if 0
+#define	MTK_GPU_DVFS_TYPE_ITEM(type)	#type,
+static char *gpu_dvfs_type_name[] = MTK_GPU_DVFS_TYPE_LIST;
+#undef	MTK_GPU_DVFS_TYPE_ITEM
+
+static MTK_GPU_DVFS_TYPE gpu_dvfs_type_prev;
+static unsigned long gpu_dvfs_type_freq_prev;
+static unsigned int gpu_dvfs_type_freq[ARRAY_SIZE(gpu_dvfs_type_name)];
+#endif
+
+#if 0
+noinline void GPU_DVFS(unsigned int Freq, unsigned int ThermalLimit,
+			unsigned long CustomBoost, unsigned long CustomUpbound)
+{
+	MET_TRACE("%u,%u,%lu,%lu\n", Freq, ThermalLimit, CustomBoost, CustomUpbound);
+}
+#else
+noinline void GPU_DVFS(int Freq)
+{
+	MET_TRACE("%d\n", Freq);
+}
+#endif
+
+#if 0
+noinline void GPU_DVFS_TYPE(void)
+{
+	char	*SOB, *EOB;
+
+	MET_TRACE_GETBUF(&SOB, &EOB);
+	EOB = ms_formatD_EOL(EOB, ARRAY_SIZE(gpu_dvfs_type_freq), gpu_dvfs_type_freq);
+	MET_TRACE_PUTBUF(SOB, EOB);
+}
+
+noinline void GPU_DVFS_VSYNC(unsigned long freq)
+{
+	MET_TRACE("%lu\n", freq);
+}
+
+noinline void GPU_VSYNC_OFFSET_STATUS(unsigned int event_status, unsigned int debug_status)
+{
+	MET_TRACE("%u,%u\n", event_status, debug_status);
+}
+#endif
+
+static void gpu_dvfs(void)
+{
+	unsigned int freq = 0;
+#if 0
+	unsigned int		ThermalLimit = 0;
+	MTK_GPU_DVFS_TYPE	peType;
+	unsigned long		pulFreq = 0;
+	unsigned long		CustomBoost = 0;
+	unsigned long		CustomUpbound = 0;
+	unsigned int		event_status = 0;
+	unsigned int		debug_status = 0;
+#endif
+
+	if (mtk_get_gpu_freq_symbol)
+		mtk_get_gpu_freq_symbol(&freq);
+	else
+		freq = 0;
+#if 0
+	ThermalLimit = mt_gpufreq_get_thermal_limit_freq_symbol ? mt_gpufreq_get_thermal_limit_freq_symbol() : 0;
+	if (mtk_get_custom_boost_gpu_freq_symbol)
+		mtk_get_custom_boost_gpu_freq_symbol(&CustomBoost);
+	if (mtk_get_custom_upbound_gpu_freq_symbol)
+		mtk_get_custom_upbound_gpu_freq_symbol(&CustomUpbound);
+#endif
+//	GPU_DVFS(freq, ThermalLimit, CustomBoost, CustomUpbound);
+	GPU_DVFS(freq);
+
+#if 0
+	/* gpu dvfs type */
+	if (mtk_get_gpu_dvfs_from_symbol && mtk_get_gpu_dvfs_from_symbol(&peType, &pulFreq)) {
+		if (gpu_dvfs_type_prev != peType || gpu_dvfs_type_freq_prev != pulFreq) {
+			gpu_dvfs_type_freq[gpu_dvfs_type_prev] = 0;
+			gpu_dvfs_type_prev = peType;
+			gpu_dvfs_type_freq_prev = pulFreq;
+			gpu_dvfs_type_freq[gpu_dvfs_type_prev] = gpu_dvfs_type_freq_prev;
+			GPU_DVFS_TYPE();
+		}
+	}
+
+	if (mtk_get_vsync_based_target_freq_symbol && mtk_get_vsync_based_target_freq_symbol(&pulFreq))
+		GPU_DVFS_VSYNC(pulFreq);
+
+	if (mtk_get_vsync_offset_event_status_symbol && mtk_get_vsync_offset_debug_status_symbol) {
+		if (mtk_get_vsync_offset_event_status_symbol(&event_status)
+		    && mtk_get_vsync_offset_debug_status_symbol(&debug_status)) {
+			GPU_VSYNC_OFFSET_STATUS(event_status, debug_status);
+		}
+	}
+#endif
+}
+
+static void gpu_dvfs_work(struct work_struct *work)
+{
+	gpu_dvfs();
+}
+
+static void gpu_dvfs_monitor_start(void)
+{
+	gpu_dvfs();
+	INIT_DELAYED_WORK(&gpu_dvfs_dwork, gpu_dvfs_work);
+}
+
+static void gpu_dvfs_monitor_stop(void)
+{
+	cancel_delayed_work_sync(&gpu_dvfs_dwork);
+	gpu_dvfs();
+}
+
+static void gpu_dvfs_monitor_polling(unsigned long long stamp, int cpu)
+{
+	schedule_delayed_work(&gpu_dvfs_dwork, 0);
+}
+
+static int gpu_dvfs_print_help(char *buf, int len)
+{
+	return snprintf(buf, PAGE_SIZE,
+			"  --gpu-dvfs				monitor gpu freq\n");
+}
+
+static int gpu_dvfs_print_header(char *buf, int len)
+{
+	int ret = 0;
+//	int i = 0;
+
+	ret = snprintf(buf, PAGE_SIZE,
+			"met-info [000] 0.0: met_gpu_dvfs_header: ");
+#if 0
+	ret += snprintf(buf+ret, PAGE_SIZE-ret,
+			"Freq(kHz),ThermalLimit(kHz),CustomBoost,CustomUpbound\n");
+#else
+	ret += snprintf(buf+ret, PAGE_SIZE-ret,
+			"Freq(kHz)\n");
+#endif
+
+#if 0
+	ret += snprintf(buf+ret, PAGE_SIZE-ret,
+			"met-info [000] 0.0: met_gpu_dvfs_type_header: %s", gpu_dvfs_type_name[0]);
+	for (i = 1; i < ARRAY_SIZE(gpu_dvfs_type_name); i++)
+		ret += snprintf(buf+ret, PAGE_SIZE-ret, ",%s", gpu_dvfs_type_name[i]);
+	ret += snprintf(buf+ret, PAGE_SIZE-ret, "%s", "\n");
+
+	ret += snprintf(buf+ret, PAGE_SIZE-ret,
+			"met-info [000] 0.0: met_gpu_dvfs_vsync_header: VSYNC Based Freq\n");
+	ret += snprintf(buf+ret, PAGE_SIZE-ret,
+			"met-info [000] 0.0: met_gpu_vsync_offset_status_header: Event Status,Debug Status\n");
+#endif
+
+	return ret;
+}
+
+struct metdevice met_gpudvfs = {
+	.name			= "gpu-dvfs",
+	.owner			= THIS_MODULE,
+	.type			= MET_TYPE_BUS,
+	.cpu_related		= 0,
+	.start			= gpu_dvfs_monitor_start,
+	.stop			= gpu_dvfs_monitor_stop,
+	.polling_interval	= 1,	/* ms */
+	.timed_polling		= gpu_dvfs_monitor_polling,
+	.print_help		= gpu_dvfs_print_help,
+	.print_header		= gpu_dvfs_print_header,
+	.ondiemet_mode		= 0,
+};
+
+/*
+ * GPU MEM monitor
+ */
+static unsigned long g_u4MemProfileIsOn;
+
+static void gpu_mem_monitor_start(void)
+{
+	if (!mtk_get_gpu_memory_usage_symbol)
+		return;
+
+	g_u4MemProfileIsOn = 1;
+}
+
+noinline void GPU_MEM(unsigned long long stamp, int cpu)
+{
+	unsigned int u4Value = 0;
+
+	if (!mtk_get_gpu_memory_usage_symbol)
+		return;
+
+	if (g_u4MemProfileIsOn == 1) {
+		mtk_get_gpu_memory_usage_symbol(&u4Value);
+		MET_TRACE("%d\n", u4Value);
+	}
+}
+
+static void gpu_mem_monitor_stop(void)
+{
+	g_u4MemProfileIsOn = 0;
+}
+
+static char help_mem[] =
+	"  --gpu-mem				monitor gpu memory status\n";
+static int gpu_mem_status_print_help(char *buf, int len)
+{
+	return snprintf(buf, PAGE_SIZE, help_mem);
+}
+
+static char g_pComGPUMemHeader[] =
+	"met-info [000] 0.0: met_gpu_mem_header: Usage(Byte)\n";
+static int gpu_mem_status_print_header(char *buf, int len)
+{
+	return snprintf(buf, PAGE_SIZE, g_pComGPUMemHeader);
+}
+
+struct metdevice met_gpumem = {
+	.name			= "gpu-mem",
+	.owner			= THIS_MODULE,
+	.type			= MET_TYPE_BUS,
+	.cpu_related		= 0,
+	.start			= gpu_mem_monitor_start,
+	.stop			= gpu_mem_monitor_stop,
+	.mode			= 0,
+	.polling_interval	= 1,	/* ms */
+	.timed_polling		= GPU_MEM,
+	.print_help		= gpu_mem_status_print_help,
+	.print_header		= gpu_mem_status_print_header,
+};
+
+#if 0
+
+/*
+ * GPU power monitor
+ */
+static unsigned long g_u4PowerProfileIsOn;
+
+#ifdef GPU_HAL_RUN_PREMPTIBLE
+noinline void GPU_Power(struct work_struct *work)
+{
+	unsigned int u4Value = 0;
+
+	if (!mtk_get_gpu_power_loading_symbol)
+		return;
+
+	mtk_get_gpu_power_loading_symbol(&u4Value);
+	MET_TRACE("%d\n", u4Value);
+}
+
+static void GPU_PowerNotify(unsigned long long stamp, int cpu)
+{
+	if (g_u4PowerProfileIsOn == 1)
+		schedule_delayed_work(&gpu_pwr_dwork, 0);
+}
+#else
+noinline void GPU_Power(unsigned long long stamp, int cpu)
+{
+	unsigned int u4Value = 0;
+
+	if (!mtk_get_gpu_power_loading_symbol)
+		return;
+
+	if (g_u4PowerProfileIsOn == 1) {
+		mtk_get_gpu_power_loading_symbol(&u4Value);
+		MET_TRACE("%d\n", u4Value);
+	}
+}
+#endif
+
+static void gpu_Power_monitor_start(void)
+{
+	if (!mtk_get_gpu_power_loading_symbol)
+		return;
+
+#if 0
+	if (mtk_get_gpu_power_loading_symbol(&u4Value))
+		g_u4PowerProfileIsOn = 1;
+#endif
+	g_u4PowerProfileIsOn = 1;
+#ifdef GPU_HAL_RUN_PREMPTIBLE
+	INIT_DELAYED_WORK(&gpu_pwr_dwork, GPU_Power);
+#endif
+}
+
+static void gpu_Power_monitor_stop(void)
+{
+	g_u4PowerProfileIsOn = 0;
+
+#ifdef GPU_HAL_RUN_PREMPTIBLE
+	cancel_delayed_work_sync(&gpu_pwr_dwork);
+#endif
+}
+
+static char help_pwr[] =
+	"  --gpu-pwr				monitor gpu power status\n";
+static int gpu_Power_status_print_help(char *buf, int len)
+{
+	return snprintf(buf, PAGE_SIZE, help_pwr);
+}
+
+static char g_pComGPUPowerHeader[] =
+	"met-info [000] 0.0: met_gpu_power_header: Loading\n";
+static int gpu_Power_status_print_header(char *buf, int len)
+{
+	return snprintf(buf, PAGE_SIZE, g_pComGPUPowerHeader);
+}
+
+struct metdevice met_gpupwr = {
+	.name			= "gpu-pwr",
+	.owner			= THIS_MODULE,
+	.type			= MET_TYPE_BUS,
+	.cpu_related		= 0,
+	.start			= gpu_Power_monitor_start,
+	.stop			= gpu_Power_monitor_stop,
+	.mode			= 0,
+	.polling_interval	= 1,	/* ms */
+#ifdef GPU_HAL_RUN_PREMPTIBLE
+	.timed_polling		= GPU_PowerNotify,
+#else
+	.timed_polling		= GPU_Power,
+#endif
+	.print_help		= gpu_Power_status_print_help,
+	.print_header		= gpu_Power_status_print_header,
+};
+
+
+/*
+ * GPU PMU
+ */
+#define UNUSE_ARG(arg) ((void)arg)
+
+#ifdef GPU_HAL_RUN_PREMPTIBLE
+static struct delayed_work gpu_pmu_dwork;
+#endif
+
+#define MAX_PMU_STR_LEN (1024 * 5)
+
+static const char help_pmu[] = "  --gpu-pmu				monitor gpu pmu status";
+static const char header_pmu[] = "met-info [000] 0.0: met_gpu_pmu_header: ";
+static char pmu_str[MAX_PMU_STR_LEN];
+static int pmu_cnt;
+static int gpu_pwr_status = 1;
+static GPU_PMU *pmu_list;
+
+
+noinline void GPU_PMU_RAW(
+	unsigned long long stamp,
+	int cpu)
+{
+	bool ret;
+	int i = 0;
+	char *SOB, *EOB;
+	unsigned int value[pmu_cnt];
+
+	if (stamp == 0 && cpu == 0) {
+		for (i = 0; i < pmu_cnt; i++)
+			value[i] = 0;
+
+		MET_TRACE_GETBUF(&SOB, &EOB);
+		EOB = ms_formatH(EOB, pmu_cnt, value);
+		MET_TRACE_PUTBUF(SOB, EOB);
+		return;
+	}
+
+	if (mtk_get_gpu_pmu_swapnreset_symbol) {
+		ret = mtk_get_gpu_pmu_swapnreset_symbol(pmu_list, pmu_cnt);
+		if (ret) {
+			for (i = 0; i < pmu_cnt; i++) {
+				if (pmu_list[i].overflow)
+					pmu_list[i].value = 0xFFFFFFFF;
+				value[i] = pmu_list[i].value;
+			}
+			MET_TRACE_GETBUF(&SOB, &EOB);
+			EOB = ms_formatH(EOB, pmu_cnt, value);
+			MET_TRACE_PUTBUF(SOB, EOB);
+		}
+	}
+}
+
+static int create_gpu_pmu_list(void)
+{
+	int ret = 0;
+	int len = 0;
+	int i = 0;
+
+	if (mtk_get_gpu_pmu_init_symbol) {
+		ret = mtk_get_gpu_pmu_init(NULL, 0, &pmu_cnt);
+		if (pmu_cnt == 0 || ret == 0)
+			return 0;
+	} else
+		return 0;
+
+	pmu_list = kmalloc_array(pmu_cnt, sizeof(GPU_PMU), GFP_KERNEL);
+	if (pmu_list) {
+		memset(pmu_list, 0x00, sizeof(GPU_PMU)*pmu_cnt);
+		ret = mtk_get_gpu_pmu_init(pmu_list, pmu_cnt, NULL);
+
+		memset(pmu_str, 0x00, MAX_PMU_STR_LEN);
+		len = snprintf(pmu_str, MAX_PMU_STR_LEN, "%s", pmu_list[0].name);
+		for (i = 1; i < pmu_cnt; i++)
+			len += snprintf(pmu_str + len, MAX_PMU_STR_LEN - len, ",%s", pmu_list[i].name);
+
+		/*
+		* dummy read in order to reset GPU PMU counter
+		*/
+		if (mtk_get_gpu_pmu_swapnreset_symbol)
+			mtk_get_gpu_pmu_swapnreset_symbol(pmu_list, pmu_cnt);
+	}
+
+	return ret;
+}
+
+static void delete_gpu_pmu_list(void)
+{
+	kfree(pmu_list);
+	pmu_list = NULL;
+	pmu_cnt = 0;
+}
+
+static void gpu_pwr_status_cb(int on)
+{
+	MET_TRACE("on = %d\n", on);
+
+	if (on == 1) {
+		/*
+		* dummy read in order to reset GPU PMU counter
+		*/
+		if (mtk_get_gpu_pmu_swapnreset_symbol)
+			mtk_get_gpu_pmu_swapnreset_symbol(pmu_list, pmu_cnt);
+
+	} else {
+		GPU_PMU_RAW(1, 0);
+		GPU_PMU_RAW(0, 0);
+	}
+
+	gpu_pwr_status = on;
+}
+
+static void gpu_pmu_monitor_start(void)
+{
+	int ret;
+
+	ret = create_gpu_pmu_list();
+	if (ret == 0)
+		return;
+
+	if (mtk_register_gpu_power_change_symbol)
+		mtk_register_gpu_power_change_symbol("met_gpu", gpu_pwr_status_cb);
+
+#ifdef GPU_HAL_RUN_PREMPTIBLE
+	INIT_DELAYED_WORK(&gpu_pmu_dwork, GPU_PMU_RAW);
+#endif
+}
+
+static void gpu_pmu_monitor_stop(void)
+{
+#ifdef GPU_HAL_RUN_PREMPTIBLE
+	cancel_delayed_work_sync(&gpu_pmu_dwork);
+#endif
+
+	if (mtk_unregister_gpu_power_change_symbol)
+		mtk_unregister_gpu_power_change_symbol("met_gpu");
+	delete_gpu_pmu_list();
+
+#if 0
+	/* stop polling counter */
+	if (mtk_get_gpu_pmu_swapnreset_stop_symbol)
+		mtk_get_gpu_pmu_swapnreset_stop_symbol();
+	/* release resource */
+	if (mtk_get_gpu_pmu_deinit_symbol)
+		mtk_get_gpu_pmu_deinit_symbol();
+#endif
+}
+
+#ifdef GPU_HAL_RUN_PREMPTIBLE
+static void gpu_pmu_timed_polling_notify(
+	unsigned long long stamp,
+	int cpu)
+{
+	UNUSE_ARG(stamp);
+	UNUSE_ARG(cpu);
+
+	if (gpu_pwr_status == 1)
+		schedule_delayed_work(&gpu_pmu_dwork, 0);
+}
+#else
+static void gpu_pmu_timed_polling(
+	unsigned long long stamp,
+	int cpu)
+{
+	UNUSE_ARG(stamp);
+	UNUSE_ARG(cpu);
+
+	if (gpu_pwr_status == 1)
+		GPU_PMU_RAW(stamp, cpu);
+}
+#endif
+
+static int gpu_pmu_print_help(
+	char *buf,
+	int len)
+{
+	UNUSE_ARG(len);
+	return snprintf(buf, PAGE_SIZE, "%s\n", help_pmu);
+}
+
+static int gpu_pmu_print_header(
+	char *buf,
+	int len)
+{
+	len = 0;
+
+	len = snprintf(buf, PAGE_SIZE, "%s", header_pmu);
+	len += snprintf(buf + len, PAGE_SIZE - len, "%s\n", pmu_str);
+
+	return len;
+}
+
+struct metdevice met_gpu_pmu = {
+	.name			= "gpu-pmu",
+	.owner			= THIS_MODULE,
+	.type			= MET_TYPE_PMU,
+	.cpu_related		= 0,
+	.start			= gpu_pmu_monitor_start,
+	.stop			= gpu_pmu_monitor_stop,
+	.mode			= 0,
+	.polling_interval	= 1,	/* ms */
+#ifdef GPU_HAL_RUN_PREMPTIBLE
+	.timed_polling		= gpu_pmu_timed_polling_notify,
+#else
+	.timed_polling		= gpu_pmu_timed_polling,
+#endif
+	.print_help		= gpu_pmu_print_help,
+	.print_header		= gpu_pmu_print_header,
+};
+
+#endif
diff --git a/src/devtools/met-driver/4.19/mt2712/mtk_gpu_metmonitor.h b/src/devtools/met-driver/4.19/mt2712/mtk_gpu_metmonitor.h
new file mode 100644
index 0000000..069c534
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/mtk_gpu_metmonitor.h
@@ -0,0 +1,18 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MT_GPU_METMONITOR_H_
+
+#define _MT_GPU_METMONITOR_H_
+
+#endif				/* _MT_GPU_METMONITOR_H_ */
diff --git a/src/devtools/met-driver/4.19/mt2712/mtk_typedefs.h b/src/devtools/met-driver/4.19/mt2712/mtk_typedefs.h
new file mode 100644
index 0000000..2b7af2d
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/mtk_typedefs.h
@@ -0,0 +1,318 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MT_TYPEDEFS_H__
+
+/*
+ *  KOBJ ATTR Manipulations Macros
+ */
+
+#define KOBJ_ITEM_LIST(args...)		args
+
+/*
+ * Declaring KOBJ attributes
+ */
+#define DECLARE_KOBJ_ATTR(attr_name) \
+	static struct kobj_attribute attr_name##_attr = \
+		__ATTR(attr_name, 0664, attr_name##_show, attr_name##_store);
+
+#define DECLARE_KOBJ_ATTR_RO(attr_name) \
+	static struct kobj_attribute attr_name##_attr = \
+		__ATTR_RO(attr_name);
+
+/*
+ * Declaring KOBJ attributes with integer variable
+ */
+/* normal version */
+#define DECLARE_KOBJ_ATTR_SHOW_INT(attr_name, var_name) \
+	static ssize_t attr_name##_show( \
+		struct kobject *kobj, \
+		struct kobj_attribute *attr, \
+		char *buf) \
+	{ \
+		return snprintf(buf, PAGE_SIZE, "%d\n", var_name); \
+	}
+#define DECLARE_KOBJ_ATTR_STORE_INT(attr_name, var_name) \
+	static ssize_t attr_name##_store( \
+		struct kobject *kobj, \
+		struct kobj_attribute *attr, \
+		const char *buf, \
+		size_t n) \
+	{ \
+		int	val; \
+		if (kstrtoint(buf, 0, &val) != 0) { \
+			return -EINVAL; \
+		} \
+		var_name = val; \
+		return n; \
+	}
+#define DECLARE_KOBJ_ATTR_INT(attr_name, var_name) \
+	DECLARE_KOBJ_ATTR_SHOW_INT(attr_name, var_name) \
+	DECLARE_KOBJ_ATTR_STORE_INT(attr_name, var_name) \
+	DECLARE_KOBJ_ATTR(attr_name)
+#define DECLARE_KOBJ_ATTR_RO_INT(attr_name, var_name) \
+	DECLARE_KOBJ_ATTR_SHOW_INT(attr_name, var_name) \
+	DECLARE_KOBJ_ATTR_RO(attr_name)
+
+/* cond-check version */
+#define DECLARE_KOBJ_ATTR_STORE_INT_CHECK(attr_name, var_name, cond) \
+	static ssize_t attr_name##_store( \
+		struct kobject *kobj, \
+		struct kobj_attribute *attr, \
+		const char *buf, \
+		size_t n) \
+	{ \
+		int	var_name##temp = var_name; \
+		if (kstrtoint(buf, 0, &var_name) != 0) { \
+			var_name = var_name##temp; \
+			return -EINVAL; \
+		} \
+		if (cond) { \
+			return n; \
+		} else { \
+			var_name = var_name##temp; \
+			return -EINVAL; \
+		} \
+	}
+/* Note: the name of val in cond can NOT be the same as var_name */
+#define DECLARE_KOBJ_ATTR_INT_CHECK(attr_name, var_name, cond) \
+	DECLARE_KOBJ_ATTR_SHOW_INT(attr_name, var_name) \
+	DECLARE_KOBJ_ATTR_STORE_INT_CHECK(attr_name, var_name, cond) \
+	DECLARE_KOBJ_ATTR(attr_name)
+
+/* helper procedure version */
+#define DECLARE_KOBJ_ATTR_SHOW_INT_PROC(attr_name, var_name, func) \
+	static ssize_t attr_name##_show( \
+		struct kobject *kobj, \
+		struct kobj_attribute *attr, \
+		char *buf) \
+	{ \
+		return func(kobj, attr, buf, var_name); \
+	}
+#define DECLARE_KOBJ_ATTR_STORE_INT_PROC(attr_name, var_name, func) \
+	static ssize_t attr_name##_store( \
+		struct kobject *kobj, \
+		struct kobj_attribute *attr, \
+		const char *buf, \
+		size_t n) \
+	{ \
+		return func(kobj, attr, buf, n, &(var_name)); \
+	}
+#define DECLARE_KOBJ_ATTR_INT_PROC(attr_name, var_name, show, store) \
+	DECLARE_KOBJ_ATTR_SHOW_INT_PROC(attr_name, var_name, show) \
+	DECLARE_KOBJ_ATTR_STORE_INT_PROC(attr_name, var_name, store) \
+	DECLARE_KOBJ_ATTR(attr_name)
+
+/*
+ * Declaring KOBJ attributes with integer(hex) variable
+ */
+/* normal version */
+#define DECLARE_KOBJ_ATTR_SHOW_HEX(attr_name, var_name) \
+	static ssize_t attr_name##_show( \
+		struct kobject *kobj, \
+		struct kobj_attribute *attr, \
+		char *buf) \
+	{ \
+		return snprintf(buf, PAGE_SIZE, "%x\n", var_name); \
+	}
+#define DECLARE_KOBJ_ATTR_STORE_HEX(attr_name, var_name) \
+	static ssize_t attr_name##_store( \
+		struct kobject *kobj, \
+		struct kobj_attribute *attr, \
+		const char *buf, \
+		size_t n) \
+	{ \
+		unsigned int	val; \
+		if (kstrtouint(buf, 0, &val) != 0) { \
+			return -EINVAL; \
+		} \
+		var_name = val; \
+		return n; \
+	}
+#define DECLARE_KOBJ_ATTR_HEX(attr_name, var_name) \
+	DECLARE_KOBJ_ATTR_SHOW_HEX(attr_name, var_name) \
+	DECLARE_KOBJ_ATTR_STORE_HEX(attr_name, var_name) \
+	DECLARE_KOBJ_ATTR(attr_name)
+#define DECLARE_KOBJ_ATTR_RO_HEX(attr_name, var_name) \
+	DECLARE_KOBJ_ATTR_SHOW_HEX(attr_name, var_name) \
+	DECLARE_KOBJ_ATTR_RO(attr_name)
+
+/* cond-check version */
+#define DECLARE_KOBJ_ATTR_STORE_HEX_CHECK(attr_name, var_name, cond) \
+	static ssize_t attr_name##_store( \
+		struct kobject *kobj, \
+		struct kobj_attribute *attr, \
+		const char *buf, \
+		size_t n) \
+	{ \
+		unsigned int	var_name##temp = var_name; \
+		if (kstrtouint(buf, 0, &var_name) != 0) { \
+			var_name = var_name##temp; \
+			return -EINVAL; \
+		} \
+		if (cond) { \
+			return n; \
+		} else { \
+			var_name = var_name##temp; \
+			return -EINVAL; \
+		} \
+	}
+/* Note: the name of val in cond can NOT be the same as var_name */
+#define DECLARE_KOBJ_ATTR_HEX_CHECK(attr_name, var_name, cond) \
+	DECLARE_KOBJ_ATTR_SHOW_HEX(attr_name, var_name) \
+	DECLARE_KOBJ_ATTR_STORE_HEX_CHECK(attr_name, var_name, cond) \
+	DECLARE_KOBJ_ATTR(attr_name)
+
+/* helper procedure version */
+#define DECLARE_KOBJ_ATTR_SHOW_HEX_PROC(attr_name, var_name, func) \
+	static ssize_t attr_name##_show( \
+		struct kobject *kobj, \
+		struct kobj_attribute *attr, \
+		char *buf) \
+	{ \
+		return func(kobj, attr, buf, var_name); \
+	}
+#define DECLARE_KOBJ_ATTR_STORE_HEX_PROC(attr_name, var_name, func) \
+	static ssize_t attr_name##_store( \
+		struct kobject *kobj, \
+		struct kobj_attribute *attr, \
+		const char *buf, \
+		size_t n) \
+	{ \
+		return func(kobj, attr, buf, n, &(var_name)); \
+	}
+#define DECLARE_KOBJ_ATTR_HEX_PROC(attr_name, var_name, show, store) \
+	DECLARE_KOBJ_ATTR_SHOW_HEX_PROC(attr_name, var_name, show) \
+	DECLARE_KOBJ_ATTR_STORE_HEX_PROC(attr_name, var_name, store) \
+	DECLARE_KOBJ_ATTR(attr_name)
+
+/*
+ * Declaring KOBJ attributes with string variable
+ */
+#define DECLARE_KOBJ_ATTR_SHOW_STR(attr_name, var_name) \
+	static ssize_t attr_name##_show( \
+		struct kobject *kobj, \
+		struct kobj_attribute *attr, \
+		char *buf) \
+	{ \
+		return snprintf(buf, PAGE_SIZE, "%s", var_name); \
+	}
+
+#define DECLARE_KOBJ_ATTR_RO_STR(attr_name, var_name) \
+	DECLARE_KOBJ_ATTR_SHOW_STR(attr_name, var_name) \
+	DECLARE_KOBJ_ATTR_RO(attr_name)
+
+/*
+ * Declaring KOBJ attributes with integer list variable
+ */
+#define DECLARE_KOBJ_ATTR_INT_LIST_ITEM(list_name, list) \
+	static struct list_name##_list_item_t { \
+		int	key; \
+		int	val; \
+	} const list_name##_list_item[] = { list };
+#define DECLARE_KOBJ_ATTR_SHOW_INT_LIST(attr_name, var_name, list_name) \
+	static ssize_t attr_name##_show( \
+		struct kobject *kobj, \
+		struct kobj_attribute *attr, \
+		char *buf) \
+	{ \
+		int	i; \
+		for (i = 0; i < ARRAY_SIZE(list_name##_list_item); i++) { \
+			if (var_name == list_name##_list_item[i].key) { \
+				return snprintf(buf, \
+						PAGE_SIZE, \
+						"%d\n", \
+						list_name##_list_item[i].val); \
+			} \
+		} \
+		return snprintf(buf, PAGE_SIZE, "%d\n", -1); \
+	}
+#define DECLARE_KOBJ_ATTR_STORE_INT_LIST(attr_name, var_name, list_name) \
+	static ssize_t attr_name##_store( \
+		struct kobject *kobj, \
+		struct kobj_attribute *attr, \
+		const char *buf, \
+		size_t n) \
+	{ \
+		int	value; \
+		int	i; \
+		if (kstrtoint(buf, 10, &value) != 0) \
+			return -EINVAL; \
+		for (i = 0; i < ARRAY_SIZE(list_name##_list_item); i++) { \
+			if (value == list_name##_list_item[i].val) { \
+				var_name = list_name##_list_item[i].key; \
+				return n; \
+			} \
+		} \
+		return -EINVAL; \
+	}
+#define DECLARE_KOBJ_ATTR_INT_LIST(attr_name, var_name, list_name) \
+	DECLARE_KOBJ_ATTR_SHOW_INT_LIST(attr_name, var_name, list_name) \
+	DECLARE_KOBJ_ATTR_STORE_INT_LIST(attr_name, var_name, list_name) \
+	DECLARE_KOBJ_ATTR(attr_name)
+
+/*
+ * Declaring KOBJ attributes with string list variable
+ */
+#define DECLARE_KOBJ_ATTR_STR_LIST_ITEM(list_name, list) \
+	static struct list_name##_list_item_t { \
+		int	key; \
+		char	*val; \
+	} const list_name##_list_item[] = { list };
+#define DECLARE_KOBJ_ATTR_SHOW_STR_LIST(attr_name, var_name, list_name) \
+	static ssize_t attr_name##_show( \
+		struct kobject *kobj, \
+		struct kobj_attribute *attr, \
+		char *buf) \
+	{ \
+		int	i; \
+		for (i = 0; i < ARRAY_SIZE(list_name##_list_item); i++) { \
+			if (var_name == list_name##_list_item[i].key) { \
+				return snprintf(buf, \
+						PAGE_SIZE, \
+						"%s\n", \
+						list_name##_list_item[i].val); \
+			} \
+		} \
+		return snprintf(buf, PAGE_SIZE, "%s\n", "ERR"); \
+	}
+#define DECLARE_KOBJ_ATTR_STORE_STR_LIST(attr_name, var_name, list_name) \
+	static ssize_t attr_name##_store( \
+		struct kobject *kobj, \
+		struct kobj_attribute *attr, \
+		const char *buf, \
+		size_t n) \
+	{ \
+		int	i; \
+		for (i = 0; i < ARRAY_SIZE(list_name##_list_item); i++) { \
+			if (strncasecmp(buf, \
+					list_name##_list_item[i].val, \
+					strlen(list_name##_list_item[i].val)) == 0) { \
+				var_name = list_name##_list_item[i].key; \
+				return n; \
+			} \
+		} \
+		return -EINVAL; \
+	}
+#define DECLARE_KOBJ_ATTR_STR_LIST(attr_name, var_name, list_name) \
+	DECLARE_KOBJ_ATTR_SHOW_STR_LIST(attr_name, var_name, list_name) \
+	DECLARE_KOBJ_ATTR_STORE_STR_LIST(attr_name, var_name, list_name) \
+	DECLARE_KOBJ_ATTR(attr_name)
+
+/*
+ *  MET Debug Message
+ */
+#define METINFO(format, ...)	pr_debug("[MET]%s: "format, __func__, ##__VA_ARGS__)
+#define METERROR(format, ...)	pr_debug("[MET][ERR]%s: "format, __func__, ##__VA_ARGS__)
+
+#endif	/* _MT_TYPEDEFS_H__ */
diff --git a/src/devtools/met-driver/4.19/mt2712/ondiemet.c b/src/devtools/met-driver/4.19/mt2712/ondiemet.c
new file mode 100644
index 0000000..5247fa7
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/ondiemet.c
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "ondiemet.h"
+
+/* record enabled modules */
+unsigned int ondiemet_module[ONDIEMET_NUM];
+EXPORT_SYMBOL(ondiemet_module);
+
+void (*scp_start[ONDIEMET_NUM]) (void) = {
+sspm_start, NULL, NULL};
+
+void (*scp_stop[ONDIEMET_NUM]) (void) = {
+sspm_stop, NULL, NULL};
+
+void (*scp_extract[ONDIEMET_NUM]) (void) = {
+sspm_extract, NULL, NULL};
+
+/* record which MCU is started to generate data */
+int ondiemet_module_started[ONDIEMET_NUM];
+
+int ondiemet_attr_init(struct device *dev)
+{
+	int ret;
+
+	ret = sspm_attr_init(dev);
+	if (ret != 0) {
+		pr_debug("can not create device file: sspm related\n");
+		return ret;
+	}
+
+	return 0;
+
+}
+
+int ondiemet_attr_uninit(struct device *dev)
+{
+	int ret;
+
+	ret = sspm_attr_uninit(dev);
+	if (ret != 0) {
+		pr_debug("can not delete device file: sspm related\n");
+		return ret;
+	}
+
+	return 0;
+
+}
+
+void ondiemet_start(void)
+{
+	int i;
+
+	for (i = 0; i < ONDIEMET_NUM; i++) {
+		if (ondiemet_module[i] != 0) {
+			ondiemet_module_started[i] = 1;
+			(*scp_start[i]) ();
+		}
+	}
+}
+
+void ondiemet_stop(void)
+{
+	int i;
+
+	for (i = 0; i < ONDIEMET_NUM; i++) {
+		if (ondiemet_module[i] != 0) {
+			(*scp_stop[i]) ();
+			ondiemet_module_started[i] = 0;
+		}
+	}
+}
+
+void ondiemet_extract(void)
+{
+	int i;
+
+	for (i = 0; i < ONDIEMET_NUM; i++) {
+		if (ondiemet_module[i] != 0)
+			(*scp_extract[i]) ();
+	}
+}
diff --git a/src/devtools/met-driver/4.19/mt2712/ondiemet.h b/src/devtools/met-driver/4.19/mt2712/ondiemet.h
new file mode 100644
index 0000000..3fff604
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/ondiemet.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __ONDIEMET_H
+#define __ONDIEMET_H
+
+#include "ondiemet_log.h"
+
+extern void ondiemet_extract(void);
+extern void ondiemet_stop(void);
+extern void ondiemet_start(void);
+
+#define ONDIEMET_SSPM  0
+#define ONDIEMET_NUM  3		/* total number of supported */
+extern unsigned int ondiemet_module[];
+extern void sspm_start(void);
+extern void sspm_stop(void);
+extern void sspm_extract(void);
+extern int sspm_attr_init(struct device *dev);
+extern int sspm_attr_uninit(struct device *dev);
+
+extern int ondiemet_attr_init(struct device *dev);
+extern int ondiemet_attr_uninit(struct device *dev);
+
+extern int sspm_buffer_size;
+
+#endif				/* __ONDIEMET_H */
diff --git a/src/devtools/met-driver/4.19/mt2712/ondiemet_log.c b/src/devtools/met-driver/4.19/mt2712/ondiemet_log.c
new file mode 100644
index 0000000..4f3ad69
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/ondiemet_log.c
@@ -0,0 +1,516 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/debugfs.h>
+#include <linux/mutex.h>
+#include <linux/semaphore.h>
+#include <linux/freezer.h>
+#include <linux/uaccess.h>
+#include <linux/completion.h>
+
+#include "ondiemet_log.h"
+
+#define ONDIEMET_LOG_REQ 1
+/* TODO: abandon this constatnt */
+#define ONDIEMET_LOG_STOP 2
+
+#define PID_NONE (-1)
+
+#define ONDIEMET_LOG_STOP_MODE 0
+#define ONDIEMET_LOG_RUN_MODE 1
+#define ONDIEMET_LOG_DEBUG_MODE 2
+
+static int ondiemet_trace_run;
+static struct dentry *dbgfs_met_dir;
+
+struct mutex lock_tracef;
+struct ondiemet_log_req_q_t {
+	struct list_head listq;
+	struct mutex lockq;
+	/* struct semaphore new_evt_sema; */
+	struct completion new_evt_comp;
+	int closeq_flag;
+} ondiemet_log_req_q;
+
+struct ondiemet_log_req {
+	struct list_head list;
+	int cmd_type;
+	const char *src;
+	size_t num;
+
+	void (*on_fini_cb)(const void *p);
+	const void *param;
+};
+
+#define __ondiemet_log_req_init(req, cmd, s, n, pf, p)	\
+	do {						\
+		INIT_LIST_HEAD(&req->list);		\
+		req->cmd_type = cmd;			\
+		req->src = s;				\
+		req->num = n;				\
+		req->on_fini_cb = pf;			\
+		req->param = p;				\
+	} while (0)
+
+#define __ondiemet_log_req_fini(req)		        \
+	do {					        \
+		if (req->on_fini_cb)			\
+			req->on_fini_cb(req->param);	\
+		kfree(req);				\
+	} while (0)
+
+static void __ondiemet_log_req_q_init(struct ondiemet_log_req_q_t *q)
+{
+	INIT_LIST_HEAD(&q->listq);
+	mutex_init(&q->lockq);
+	/* sema_init(&q->new_evt_sema, 0); */
+	init_completion(&q->new_evt_comp);
+	q->closeq_flag = 1;
+}
+
+/* undequeue is seen as a roll-back operation, so it can be done even when the queue is closed */
+static void __ondiemet_log_req_undeq(struct ondiemet_log_req *req)
+{
+	mutex_lock(&ondiemet_log_req_q.lockq);
+	list_add(&req->list, &ondiemet_log_req_q.listq);
+	mutex_unlock(&ondiemet_log_req_q.lockq);
+
+	/* up(&ondiemet_log_req_q.new_evt_sema); */
+	complete(&ondiemet_log_req_q.new_evt_comp);
+}
+
+static int __ondiemet_log_req_enq(struct ondiemet_log_req *req)
+{
+	mutex_lock(&ondiemet_log_req_q.lockq);
+	if (ondiemet_log_req_q.closeq_flag) {
+		mutex_unlock(&ondiemet_log_req_q.lockq);
+		return -EBUSY;
+	}
+
+	list_add_tail(&req->list, &ondiemet_log_req_q.listq);
+	if (req->cmd_type == ONDIEMET_LOG_STOP)
+		ondiemet_log_req_q.closeq_flag = 1;
+	mutex_unlock(&ondiemet_log_req_q.lockq);
+
+	/* up(&ondiemet_log_req_q.new_evt_sema); */
+	complete(&ondiemet_log_req_q.new_evt_comp);
+
+	return 0;
+}
+
+int ondiemet_log_req_enq(const char *src, size_t num, void (*on_fini_cb)(const void *p), const void *param)
+{
+	struct ondiemet_log_req *req = kmalloc(sizeof(*req), GFP_KERNEL);
+
+	__ondiemet_log_req_init(req, ONDIEMET_LOG_REQ, src, num, on_fini_cb, param);
+	return __ondiemet_log_req_enq(req);
+}
+
+/*int down_freezable_interruptible(struct semaphore *sem) */
+int down_freezable_interruptible(struct completion *comp)
+{
+
+	int ret;
+
+	freezer_do_not_count();
+	/* ret = down_interruptible(sem); */
+	ret = wait_for_completion_interruptible(comp);
+	freezer_count();
+
+	return ret;
+}
+
+struct ondiemet_log_req *__ondiemet_log_req_deq(void)
+{
+	struct ondiemet_log_req *ret_req;
+
+	/*if (down_freezable_interruptible(&ondiemet_log_req_q.new_evt_sema))*/
+	if (down_freezable_interruptible(&ondiemet_log_req_q.new_evt_comp))
+		return NULL;
+
+	mutex_lock(&ondiemet_log_req_q.lockq);
+	ret_req = list_entry(ondiemet_log_req_q.listq.next, struct ondiemet_log_req, list);
+	list_del_init(&ret_req->list);
+	mutex_unlock(&ondiemet_log_req_q.lockq);
+
+	return ret_req;
+}
+
+void __ondiemet_log_req_open(void)
+{
+	mutex_lock(&ondiemet_log_req_q.lockq);
+	ondiemet_log_req_q.closeq_flag = 0;
+	mutex_unlock(&ondiemet_log_req_q.lockq);
+}
+
+int __ondiemet_log_req_closed(void)
+{
+	int ret;
+
+	mutex_lock(&ondiemet_log_req_q.lockq);
+	ret = ondiemet_log_req_q.closeq_flag && list_empty(&ondiemet_log_req_q.listq);
+	mutex_unlock(&ondiemet_log_req_q.lockq);
+
+	return ret;
+}
+
+int __ondiemet_log_req_working(void)
+{
+	int ret;
+
+	mutex_lock(&ondiemet_log_req_q.lockq);
+	ret = !ondiemet_log_req_q.closeq_flag;
+	mutex_unlock(&ondiemet_log_req_q.lockq);
+
+	return ret;
+}
+
+static void *__ondiemet_trace_seq_next(struct seq_file *seqf, loff_t *offset)
+{
+	struct ondiemet_log_req *next_req;
+
+	if (ondiemet_trace_run == ONDIEMET_LOG_DEBUG_MODE)
+		pr_debug("[met] __ondiemet_trace_seq_next: pid: %d\n", current->pid);
+
+	if (__ondiemet_log_req_closed())
+		return NULL;
+
+	next_req = __ondiemet_log_req_deq();
+
+	if (next_req == NULL)
+		return NULL;
+
+	if (next_req->cmd_type == ONDIEMET_LOG_STOP) {
+		__ondiemet_log_req_fini(next_req);
+		return NULL;
+	}
+
+	return (void *) next_req;
+}
+
+struct mutex lock_trace_owner_pid;
+pid_t trace_owner_pid = PID_NONE;
+static void *ondiemet_trace_seq_start(struct seq_file *seqf, loff_t *offset)
+{
+	void *ret;
+
+	if (ondiemet_trace_run == ONDIEMET_LOG_DEBUG_MODE) {
+		pr_debug("[met] ondiemet_trace_seq_start: locked_pid: %d, pid: %d, offset: %llu\n",
+			 trace_owner_pid, current->pid, *offset);
+	}
+
+	if (!mutex_trylock(&lock_tracef))
+		return NULL;
+
+	mutex_lock(&lock_trace_owner_pid);
+	trace_owner_pid = current->pid;
+	mutex_unlock(&lock_trace_owner_pid);
+
+	ret = __ondiemet_trace_seq_next(seqf, offset);
+
+	return ret;
+}
+
+static void *ondiemet_trace_seq_next(struct seq_file *seqf, void *p, loff_t *offset)
+{
+	if (ondiemet_trace_run == ONDIEMET_LOG_DEBUG_MODE)
+		pr_debug("[met] ondiemet_trace_seq_next: pid: %d\n", current->pid);
+
+	(*offset)++;
+	return __ondiemet_trace_seq_next(seqf, offset);
+}
+
+static int ondiemet_trace_seq_show(struct seq_file *seqf, void *p)
+{
+	struct ondiemet_log_req *req = (struct ondiemet_log_req *) p;
+	size_t l_sz;
+	size_t r_sz;
+	struct ondiemet_log_req *l_req;
+	struct ondiemet_log_req *r_req;
+	int ret;
+
+	if (ondiemet_trace_run == ONDIEMET_LOG_DEBUG_MODE)
+		pr_debug("[met] ondiemet_trace_seq_show: pid: %d\n", current->pid);
+
+	if (req->num >= seqf->size) {
+		l_req = kmalloc(sizeof(*req), GFP_KERNEL);
+		r_req = req;
+
+		l_sz = seqf->size >> 1;
+		r_sz = req->num - l_sz;
+		__ondiemet_log_req_init(l_req, ONDIEMET_LOG_REQ, req->src, l_sz, NULL, NULL);
+		__ondiemet_log_req_init(r_req, ONDIEMET_LOG_REQ, req->src + l_sz,
+					r_sz, req->on_fini_cb, req->param);
+
+		__ondiemet_log_req_undeq(r_req);
+		req = l_req;
+
+		if (ondiemet_trace_run == ONDIEMET_LOG_DEBUG_MODE)
+			pr_debug("[met] ondiemet_trace_seq_show: split request\n");
+	}
+
+	ret = seq_write(seqf, req->src, req->num);
+
+	if (ret) {
+		/* check if seq_file buffer overflows */
+		if (seqf->count == seqf->size) {
+			__ondiemet_log_req_undeq(req);
+		} else {
+			if (ondiemet_trace_run == ONDIEMET_LOG_DEBUG_MODE)
+				pr_debug("[met] ondiemet_trace_seq_show: reading trace record failed, some data may be lost or corrupted\n");
+			__ondiemet_log_req_fini(req);
+		}
+		return 0;
+	}
+
+	__ondiemet_log_req_fini(req);
+	return 0;
+}
+
+static void ondiemet_trace_seq_stop(struct seq_file *seqf, void *p)
+{
+	if (ondiemet_trace_run == ONDIEMET_LOG_DEBUG_MODE)
+		pr_debug("[met] ondiemet_trace_seq_stop: pid: %d\n", current->pid);
+
+	mutex_lock(&lock_trace_owner_pid);
+	if (current->pid == trace_owner_pid) {
+		trace_owner_pid = PID_NONE;
+		mutex_unlock(&lock_tracef);
+	}
+	mutex_unlock(&lock_trace_owner_pid);
+}
+
+static const struct seq_operations ondiemet_trace_seq_ops = {
+	.start = ondiemet_trace_seq_start,
+	.next = ondiemet_trace_seq_next,
+	.stop = ondiemet_trace_seq_stop,
+	.show = ondiemet_trace_seq_show
+};
+
+static int ondiemet_trace_open(struct inode *inode, struct file *fp)
+{
+	return seq_open(fp, &ondiemet_trace_seq_ops);
+}
+
+static const struct file_operations ondiemet_trace_fops = {
+	.owner = THIS_MODULE,
+	.open = ondiemet_trace_open,
+	.read = seq_read,
+	.llseek = seq_lseek,
+	.release = seq_release
+};
+
+/*struct semaphore log_start_sema;*/
+struct completion log_start_comp;
+int ondiemet_log_manager_start(void)
+{
+	int ret;
+
+	/* TODO: choose a better return value */
+	if (__ondiemet_log_req_working())
+		return -EINVAL;
+
+	if (!__ondiemet_log_req_closed()) {
+		/*ret = down_killable(&log_start_sema);*/
+		ret = wait_for_completion_killable(&log_start_comp);
+		if (ret)
+			return ret;
+	}
+
+	__ondiemet_log_req_open();
+
+	return 0;
+}
+
+/*struct semaphore log_stop_sema;*/
+struct completion log_stop_comp;
+static void __log_stop_cb(const void *p)
+{
+	/* up(&log_start_sema); */
+	/* up(&log_stop_sema); */
+	complete(&log_start_comp);
+	complete(&log_stop_comp);
+}
+
+int ondiemet_log_manager_stop(void)
+{
+	int ret;
+	struct ondiemet_log_req *req;
+
+	/* TODO: choose a better return value */
+	if (__ondiemet_log_req_closed())
+		return -EINVAL;
+
+	req = kmalloc(sizeof(*req), GFP_KERNEL);
+
+	__ondiemet_log_req_init(req, ONDIEMET_LOG_STOP, NULL, 0, __log_stop_cb, NULL);
+	/*sema_init(&log_start_sema, 0); */
+	/*sema_init(&log_stop_sema, 0); */
+	init_completion(&log_start_comp);
+	init_completion(&log_stop_comp);
+
+	ret = __ondiemet_log_req_enq(req);
+	if (ret)
+		return ret;
+
+	/* XXX: blocking may be break by SIGKILL */
+	/*return down_killable(&log_stop_sema);*/
+	return wait_for_completion_killable(&log_stop_comp);
+}
+
+int ondiemet_parse_num(const char *str, unsigned int *value, int len)
+{
+	int ret;
+
+	if (len <= 0)
+		return -1;
+
+	if ((len > 2) &&
+	    ((str[0] == '0') &&
+	     ((str[1] == 'x') || (str[1] == 'X')))) {
+		ret = kstrtouint(str, 16, value);
+	} else {
+		ret = kstrtouint(str, 10, value);
+	}
+
+	if (ret != 0)
+		return -1;
+
+	return 0;
+}
+
+/* XXX: seq_file will output only when a page is filled */
+static ssize_t ondiemet_log_write_store(struct device *dev,
+					struct device_attribute *attr,
+					const char *buf,
+					size_t count)
+{
+	char *plog = NULL;
+
+	plog = kmalloc_array(count, sizeof(*plog), GFP_KERNEL);
+	if (!plog) {
+		/* TODO: use a better error code */
+		return -EINVAL;
+	}
+
+	memcpy(plog, buf, count);
+
+	mutex_lock(&dev->mutex);
+	ondiemet_log_req_enq(plog, strnlen(plog, count), kfree, plog);
+	mutex_unlock(&dev->mutex);
+
+	return count;
+}
+
+static DEVICE_ATTR(ondiemet_log_write, 0664, NULL, ondiemet_log_write_store);
+
+static ssize_t ondiemet_log_run_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int sz;
+
+	mutex_lock(&dev->mutex);
+	sz = snprintf(buf, PAGE_SIZE, "%d\n", ondiemet_trace_run);
+	mutex_unlock(&dev->mutex);
+	return sz;
+}
+
+static ssize_t ondiemet_log_run_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+	int ret;
+	int prev_run_state;
+
+	mutex_lock(&dev->mutex);
+
+	prev_run_state = ondiemet_trace_run;
+
+	if (kstrtoint(buf, 10, &ondiemet_trace_run) != 0)
+		return -EINVAL;
+
+	if (ondiemet_trace_run <= ONDIEMET_LOG_STOP_MODE) {
+		ondiemet_trace_run = ONDIEMET_LOG_STOP_MODE;
+		ondiemet_log_manager_stop();
+
+		if (prev_run_state == ONDIEMET_LOG_DEBUG_MODE)
+			device_remove_file(dev, &dev_attr_ondiemet_log_write);
+	} else if (ondiemet_trace_run == ONDIEMET_LOG_RUN_MODE) {
+		ondiemet_trace_run = ONDIEMET_LOG_RUN_MODE;
+		ondiemet_log_manager_start();
+
+		if (prev_run_state == ONDIEMET_LOG_DEBUG_MODE)
+			device_remove_file(dev, &dev_attr_ondiemet_log_write);
+	} else {
+		ondiemet_trace_run = ONDIEMET_LOG_DEBUG_MODE;
+		ondiemet_log_manager_start();
+
+		if (prev_run_state != ONDIEMET_LOG_DEBUG_MODE) {
+			ret = device_create_file(dev, &dev_attr_ondiemet_log_write);
+			if (ret != 0)
+				pr_debug("[met] can not create device node: ondiemet_log_write\n");
+		}
+	}
+
+	mutex_unlock(&dev->mutex);
+
+	return count;
+}
+
+static DEVICE_ATTR(ondiemet_log_run, 0660, ondiemet_log_run_show, ondiemet_log_run_store);
+
+int ondiemet_log_manager_init(struct device *dev)
+{
+	int ret;
+	struct dentry *d;
+
+	mutex_init(&lock_tracef);
+
+	__ondiemet_log_req_q_init(&ondiemet_log_req_q);
+
+	/*sema_init(&log_start_sema, 0);*/
+	/*sema_init(&log_stop_sema, 0);*/
+	init_completion(&log_start_comp);
+	init_completion(&log_stop_comp);
+
+	dbgfs_met_dir = debugfs_create_dir("ondiemet", NULL);
+	if (!dbgfs_met_dir) {
+		pr_debug("[met] can not create debugfs directory: met\n");
+		return -ENOMEM;
+	}
+
+	mutex_init(&lock_trace_owner_pid);
+
+	d = debugfs_create_file("trace", 0644, dbgfs_met_dir, NULL, &ondiemet_trace_fops);
+	if (!d) {
+		pr_debug("[met] can not create devide node in debugfs: ondiemet_trace\n");
+		return -ENOMEM;
+	}
+
+	ondiemet_trace_run = __ondiemet_log_req_working();
+	ret = device_create_file(dev, &dev_attr_ondiemet_log_run);
+	if (ret != 0) {
+		pr_debug("[met] can not create device node: ondiemet_log_run\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+int ondiemet_log_manager_uninit(struct device *dev)
+{
+	device_remove_file(dev, &dev_attr_ondiemet_log_run);
+	debugfs_remove_recursive(dbgfs_met_dir);
+	return 0;
+}
diff --git a/src/devtools/met-driver/4.19/mt2712/ondiemet_log.h b/src/devtools/met-driver/4.19/mt2712/ondiemet_log.h
new file mode 100644
index 0000000..cfe8be9
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/ondiemet_log.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _ONDIEMET_LOG_H_
+#define _ONDIEMET_LOG_H_
+
+#include <linux/device.h>
+
+int ondiemet_log_manager_init(struct device *dev);
+int ondiemet_log_manager_uninit(struct device *dev);
+int ondiemet_log_manager_start(void);
+/* Log manager can be reactivated by inserting new requests, i.e., calling ondiemet_log_req_enq() */
+int ondiemet_log_manager_stop(void);
+int ondiemet_log_req_enq(const char *src, size_t num, void (*on_fini_cb) (const void *p),
+			 const void *param);
+
+#endif				/* _ONDIEMET_LOG_H_ */
diff --git a/src/devtools/met-driver/4.19/mt2712/power.c b/src/devtools/met-driver/4.19/mt2712/power.c
new file mode 100644
index 0000000..c57d907
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/power.c
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/cpufreq.h>
+#include <trace/events/power.h>
+
+#include "power.h"
+#include "met_drv.h"
+#include "met_kernel_symbol.h"
+
+noinline void cpu_frequency(unsigned int frequency, unsigned int cpu_id)
+{
+	/* suppose this symbol is available, otherwise, the met.ko will fail */
+	met_cpu_frequency_symbol(frequency, cpu_id);
+}
+
+void force_power_log(int cpu)
+{
+	struct cpufreq_policy *p;
+
+	if (cpu == POWER_LOG_ALL) {
+		for_each_possible_cpu(cpu) {
+			p = cpufreq_cpu_get(cpu);
+			if (p != NULL) {
+				cpu_frequency(p->cur, cpu);
+				cpufreq_cpu_put(p);
+			} else {
+				cpu_frequency(0, cpu);
+			}
+		}
+	} else {
+		p = cpufreq_cpu_get(cpu);
+		if (p != NULL) {
+			cpu_frequency(p->cur, cpu);
+			cpufreq_cpu_put(p);
+		} else {
+			cpu_frequency(0, cpu);
+		}
+	}
+}
+
+void force_power_log_val(unsigned int frequency, int cpu)
+{
+	cpu_frequency(frequency, cpu);
+}
diff --git a/src/devtools/met-driver/4.19/mt2712/power.h b/src/devtools/met-driver/4.19/mt2712/power.h
new file mode 100644
index 0000000..8a0e8f0
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/power.h
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _POWER_H_
+#define _POWER_H_
+
+#define POWER_LOG_ALL	-1
+void force_power_log(int cpu);
+void force_power_log_val(unsigned int frequency, int cpu);
+
+#endif				/* _POWER_H_ */
diff --git a/src/devtools/met-driver/4.19/mt2712/sampler.c b/src/devtools/met-driver/4.19/mt2712/sampler.c
new file mode 100644
index 0000000..dca6883
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/sampler.c
@@ -0,0 +1,714 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/sched/clock.h>
+#include <linux/kernel.h>
+#include <linux/cpuhotplug.h>
+#include <linux/cpu.h>
+#include <linux/sched.h>
+#include <linux/notifier.h>
+#include <linux/module.h>
+#include <linux/irq.h>
+#if 0				/* fix me later, no such file on current tree */
+#include <mach/mt_cpuxgpt.h>
+#endif
+#include <asm/arch_timer.h>
+
+#define	MET_USER_EVENT_SUPPORT
+#include "interface.h"
+#include "sampler.h"
+#include "met_struct.h"
+#include "util.h"
+#include "switch.h"
+#include "trace.h"
+#include "met_drv.h"
+#include "met_tag.h" /* for tracing_mark_write */
+
+#include "cpu_pmu.h" /* for using kernel perf PMU driver */
+#include "cpu_pmu_v2.h" /* for using kernel perf PMU v2 driver */
+#include "met_kernel_symbol.h"
+
+#undef	DEBUG_CPU_NOTIFY
+/* #define DEBUG_CPU_NOTIFY */
+#if	defined(DEBUG_CPU_NOTIFY)
+#ifdef CONFIG_MET_MODULE
+#define	dbg_met_tag_oneshot	met_tag_oneshot_real
+#else
+#define	dbg_met_tag_oneshot	met_tag_oneshot
+#endif /* CONFIG_MET_MODULE */
+#else
+#define	dbg_met_tag_oneshot(class_id, name, value)	({ 0; })
+#endif
+
+static int start;
+static unsigned int online_cpu_map;
+static int curr_polling_cpu;
+static int cpu_related_cnt;
+
+static int pmu_profiling_version = 0;
+
+static DEFINE_PER_CPU(unsigned int, perf_cpuid);
+
+static int preferred_cpu_list[] = { 0, 4, 1, 2, 3, 5, 6, 7 };
+
+int get_pmu_profiling_version()
+{
+	return pmu_profiling_version;
+}
+
+static int calc_preferred_polling_cpu(unsigned int cpu_map)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(preferred_cpu_list); i++) {
+		if (cpu_map & (1 << preferred_cpu_list[i]))
+			return preferred_cpu_list[i];
+	}
+
+	return -1;
+}
+
+static void wq_sync_buffer(struct work_struct *work)
+{
+	int cpu;
+	struct delayed_work *dw = container_of(work, struct delayed_work, work);
+	struct met_cpu_struct *met_cpu_ptr = container_of(dw, struct met_cpu_struct, dwork);
+
+	cpu = smp_processor_id();
+	if (met_cpu_ptr->cpu != cpu) {
+		/* panic("ERROR"); */
+		return;
+	}
+
+	/* sync_samples(cpu); */
+	/* don't re-add the work if we're shutting down */
+	if (met_cpu_ptr->work_enabled)
+		schedule_delayed_work(dw, DEFAULT_TIMER_EXPIRE);
+}
+
+static enum hrtimer_restart met_hrtimer_notify(struct hrtimer *hrtimer)
+{
+	int cpu;
+	int *count;
+	unsigned long long stamp;
+	struct met_cpu_struct *met_cpu_ptr = container_of(hrtimer, struct met_cpu_struct, hrtimer);
+	struct metdevice *c;
+#if	defined(DEBUG_CPU_NOTIFY)
+	char msg[32];
+#endif
+
+	cpu = smp_processor_id();
+#if	defined(DEBUG_CPU_NOTIFY)
+	{
+		char msg[32];
+
+		snprintf(msg, sizeof(msg), "met_hrtimer notify_%d", cpu);
+		dbg_met_tag_oneshot(0, msg, 1);
+	}
+#endif
+
+	if (met_cpu_ptr->cpu != cpu) {
+		/* panic("ERROR2"); */
+		dbg_met_tag_oneshot(0, msg, -3);
+		return HRTIMER_NORESTART;
+	}
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->ondiemet_mode == 0) {
+			if ((c->mode == 0) || (c->timed_polling == NULL))
+				continue;
+		} else if (c->ondiemet_mode == 1) {
+			if ((c->mode == 0) || (c->ondiemet_timed_polling == NULL))
+				continue;
+		} else if (c->ondiemet_mode == 2) {
+			if ((c->mode == 0) || ((c->timed_polling == NULL)
+					       && (c->ondiemet_timed_polling == NULL)))
+				continue;
+		}
+
+		count = per_cpu_ptr(c->polling_count, cpu);
+		if ((*count) > 0) {
+			(*count)--;
+			continue;
+		}
+
+		*(count) = c->polling_count_reload;
+
+		stamp = cpu_clock(cpu);
+
+		if (c->cpu_related == 0) {
+			if (cpu == curr_polling_cpu) {
+				if (c->ondiemet_mode == 0) {
+					c->timed_polling(stamp, 0);
+				} else if (c->ondiemet_mode == 1) {
+					c->ondiemet_timed_polling(stamp, 0);
+				} else if (c->ondiemet_mode == 2) {
+					if (c->timed_polling)
+						c->timed_polling(stamp, 0);
+					if (c->ondiemet_timed_polling)
+						c->ondiemet_timed_polling(stamp, 0);
+				}
+			}
+		} else {
+			if (c->ondiemet_mode == 0) {
+				c->timed_polling(stamp, cpu);
+			} else if (c->ondiemet_mode == 1) {
+				c->ondiemet_timed_polling(stamp, cpu);
+			} else if (c->ondiemet_mode == 2) {
+				if (c->timed_polling)
+					c->timed_polling(stamp, 0);
+				if (c->ondiemet_timed_polling)
+					c->ondiemet_timed_polling(stamp, 0);
+			}
+		}
+	}
+
+	if (met_cpu_ptr->hrtimer_online_check) {
+		online_cpu_map |= (1 << cpu);
+		met_cpu_ptr->hrtimer_online_check = 0;
+		dbg_met_tag_oneshot(0, "met_online check done", cpu);
+		if (calc_preferred_polling_cpu(online_cpu_map) == cpu) {
+			curr_polling_cpu = cpu;
+			dbg_met_tag_oneshot(0, "met_curr polling cpu", cpu);
+		}
+	}
+
+	if (met_cpu_ptr->work_enabled) {
+		hrtimer_forward_now(hrtimer, ns_to_ktime(DEFAULT_HRTIMER_EXPIRE));
+		dbg_met_tag_oneshot(0, msg, 0);
+		return HRTIMER_RESTART;
+	}
+	dbg_met_tag_oneshot(0, msg, 0);
+	return HRTIMER_NORESTART;
+}
+
+static void __met_hrtimer_start(void *unused)
+{
+	struct met_cpu_struct *met_cpu_ptr = NULL;
+	struct hrtimer *hrtimer = NULL;
+	/* struct delayed_work *dw; */
+	struct metdevice *c;
+
+	met_cpu_ptr = this_cpu_ptr(&met_cpu);
+#if	defined(DEBUG_CPU_NOTIFY)
+	{
+		char msg[32];
+
+		snprintf(msg, sizeof(msg), "met_hrtimer status_%d", met_cpu_ptr->cpu);
+		dbg_met_tag_oneshot(0, msg, 1);
+	}
+#endif
+	/*
+	 * do not open HRtimer when EVENT timer enable
+	 */
+//	if (!(met_switch.mode & MT_SWITCH_EVENT_TIMER)) {
+		hrtimer = &met_cpu_ptr->hrtimer;
+		/* dw = &met_cpu_ptr->dwork; */
+
+		hrtimer_init(hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+		hrtimer->function = met_hrtimer_notify;
+//	}
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->ondiemet_mode == 0) {
+			if ((c->cpu_related) && (c->mode) && (c->start))
+				c->start();
+		} else if (c->ondiemet_mode == 1) {
+			if (((c->cpu_related)) && (c->mode) && (c->ondiemet_start))
+				c->ondiemet_start();
+		} else if (c->ondiemet_mode == 2) {
+			if ((c->cpu_related) && (c->mode) && (c->start))
+				c->start();
+			if (((c->cpu_related)) && (c->mode) && (c->ondiemet_start))
+				c->ondiemet_start();
+		}
+	}
+	/*
+	 * do not open HRtimer when EVENT timer enable
+	 */
+//	if (!(met_switch.mode & MT_SWITCH_EVENT_TIMER)) {
+		if (DEFAULT_HRTIMER_EXPIRE) {
+			met_cpu_ptr->work_enabled = 1;
+			/* schedule_delayed_work_on(smp_processor_id(), dw, DEFAULT_TIMER_EXPIRE); */
+			hrtimer_start(hrtimer, ns_to_ktime(DEFAULT_HRTIMER_EXPIRE),
+				      HRTIMER_MODE_REL_PINNED);
+		}
+//	}
+}
+
+static void __met_hrtimer_stop(void *unused)
+{
+	struct met_cpu_struct *met_cpu_ptr;
+	struct hrtimer *hrtimer;
+	/* struct delayed_work *dw; */
+	struct metdevice *c;
+
+	met_cpu_ptr = this_cpu_ptr(&met_cpu);
+#if	defined(DEBUG_CPU_NOTIFY)
+	{
+		char msg[32];
+
+		snprintf(msg, sizeof(msg), "met_hrtimer status_%d", met_cpu_ptr->cpu);
+		dbg_met_tag_oneshot(0, msg, 0);
+	}
+#endif
+	/*
+	 * do not open HRtimer when EVENT timer enable
+	 */
+//	if (!(met_switch.mode & MT_SWITCH_EVENT_TIMER)) {
+		hrtimer = &met_cpu_ptr->hrtimer;
+		/* dw = &met_cpu_ptr->dwork; */
+
+		met_cpu_ptr->work_enabled = 0;
+		hrtimer_cancel(hrtimer);
+		/* cancel_delayed_work_sync(dw); */
+//	}
+	list_for_each_entry(c, &met_list, list) {
+		if (c->ondiemet_mode == 0) {
+			if ((c->cpu_related) && (c->mode) && (c->stop))
+				c->stop();
+		} else if (c->ondiemet_mode == 1) {
+			if ((c->cpu_related) && (c->mode) && (c->ondiemet_stop))
+				c->ondiemet_stop();
+		} else if (c->ondiemet_mode == 2) {
+			if ((c->cpu_related) && (c->mode) && (c->stop))
+				c->stop();
+			if ((c->cpu_related) && (c->mode) && (c->ondiemet_stop))
+				c->ondiemet_stop();
+		}
+	}
+}
+
+static int met_pmu_cpu_notify(enum met_action action, unsigned int cpu)
+{
+	struct met_cpu_struct *met_cpu_ptr;
+	struct delayed_work *dw;
+	int preferred_polling_cpu;
+
+	if (start == 0)
+		return NOTIFY_OK;
+
+#if	defined(DEBUG_CPU_NOTIFY)
+	{
+		char msg[32];
+
+		snprintf(msg, sizeof(msg), "met_cpu notify_%d", cpu);
+		dbg_met_tag_oneshot(0, msg, action);
+	}
+#elif	defined(PR_CPU_NOTIFY)
+	{
+		char msg[32];
+
+		if (met_cpu_notify) {
+			snprintf(msg, sizeof(msg), "met_cpu notify_%d", cpu);
+			dbg_met_tag_oneshot(0, msg, action);
+		}
+	}
+#endif
+
+	if (cpu < 0 || cpu >= NR_CPUS)
+		return NOTIFY_OK;
+
+	switch (action) {
+	case MET_CPU_ONLINE:
+		met_cpu_ptr = &per_cpu(met_cpu, cpu);
+		met_cpu_ptr->hrtimer_online_check = 1;
+		dbg_met_tag_oneshot(0, "met_online check", cpu);
+
+		if (cpu_related_cnt == 0) {
+			/*printk("%s, %d: curr_polling_cpu is alive = %d\n",
+			 *		__func__, __LINE__, online_cpu_map & (1 << curr_polling_cpu));
+			 */
+
+			online_cpu_map |= (1 << cpu);
+
+			/* check curr_polling_cpu is alive, if it is down,
+			 * start current cpu hrtimer, and change it to be currr_pollling_cpu
+			 */
+			if ((online_cpu_map & (1 << curr_polling_cpu)) == 0) {
+				met_smp_call_function_single_symbol(cpu, __met_hrtimer_start, NULL, 1);
+				curr_polling_cpu = cpu;
+			}
+		} else
+			met_smp_call_function_single_symbol(cpu, __met_hrtimer_start, NULL, 1);
+
+		if (met_cpu_pmu_method != 0) {
+			if (pmu_profiling_version == 1)
+				met_perf_cpupmu_online(cpu);
+#ifdef MET_SUPPORT_CPUPMU_V2
+			else if (pmu_profiling_version == 2)
+				met_perf_cpupmu_online_v2(cpu);
+#endif
+		}
+
+#ifdef CONFIG_CPU_FREQ
+		force_power_log(cpu);
+#endif
+		break;
+
+	case MET_CPU_OFFLINE:
+		online_cpu_map &= ~(1 << cpu);
+		dbg_met_tag_oneshot(0, "met_offline cpu", cpu);
+		if (cpu == curr_polling_cpu) {
+			/* printk("%s, %d: curr_polling_cpu %d is down\n",
+			 *		__func__, __LINE__, curr_polling_cpu);
+			 */
+			preferred_polling_cpu = calc_preferred_polling_cpu(online_cpu_map);
+			/* printk("%s, %d: preferred_polling_cpu = %d\n",
+			 *		__func__, __LINE__, preferred_polling_cpu);
+			 */
+			if (preferred_polling_cpu != -1) {
+				curr_polling_cpu = preferred_polling_cpu;
+				dbg_met_tag_oneshot(0, "met_curr polling cpu", curr_polling_cpu);
+
+				if (cpu_related_cnt == 0)
+					/* printk("%s, %d: start cpu %d hrtimer start\n",
+					 *		__func__, __LINE__, curr_polling_cpu);
+					 */
+					met_smp_call_function_single_symbol(curr_polling_cpu, __met_hrtimer_start, NULL, 1);
+			}
+		}
+
+		met_smp_call_function_single_symbol(cpu, __met_hrtimer_stop, NULL, 1);
+		if (met_cpu_pmu_method != 0) {
+			if (pmu_profiling_version == 1) {
+				per_cpu(perf_cpuid, cpu) = cpu;
+				met_smp_call_function_single_symbol(cpu, met_perf_cpupmu_down, (void *)&per_cpu(perf_cpuid, cpu), 1);
+			}
+#ifdef MET_SUPPORT_CPUPMU_V2
+			else if (pmu_profiling_version == 2) {
+				per_cpu(perf_cpuid, cpu) = cpu;
+				met_smp_call_function_single_symbol(cpu, met_perf_cpupmu_down_v2, (void *)&per_cpu(perf_cpuid, cpu), 1);
+			}
+#endif
+		}
+
+		met_cpu_ptr = &per_cpu(met_cpu, cpu);
+		dw = &met_cpu_ptr->dwork;
+		cancel_delayed_work_sync(dw);
+
+		/* sync_samples(cpu); */
+		break;
+	default:
+		break;
+	}
+
+	return NOTIFY_OK;
+}
+
+static int _met_pmu_cpu_notify_online(unsigned int cpu)
+{
+	met_pmu_cpu_notify(MET_CPU_ONLINE, cpu);
+
+	return 0;
+}
+
+static int _met_pmu_cpu_notify_offline(unsigned int cpu)
+{
+	met_pmu_cpu_notify(MET_CPU_OFFLINE, cpu);
+
+	return 0;
+}
+
+int sampler_start(void)
+{
+	int ret, cpu;
+	struct met_cpu_struct *met_cpu_ptr;
+	struct metdevice *c;
+	int preferred_polling_cpu;
+
+	met_set_suspend_notify(0);
+
+#ifdef	CONFIG_CPU_FREQ
+	force_power_log(POWER_LOG_ALL);
+#endif
+
+	for_each_possible_cpu(cpu) {
+		met_cpu_ptr = &per_cpu(met_cpu, cpu);
+		met_cpu_ptr->work_enabled = 0;
+		met_cpu_ptr->hrtimer_online_check = 0;
+		hrtimer_init(&met_cpu_ptr->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+		met_cpu_ptr->hrtimer.function = met_hrtimer_notify;
+		INIT_DELAYED_WORK(&met_cpu_ptr->dwork, wq_sync_buffer);
+	}
+
+	start = 0;
+	ret = cpuhp_setup_state_nocalls(CPUHP_AP_ONLINE_DYN,
+						   "met:online",
+						   _met_pmu_cpu_notify_online,
+						   _met_pmu_cpu_notify_offline);
+
+	list_for_each_entry(c, &met_list, list) {
+
+		if (try_module_get(c->owner) == 0)
+			continue;
+#ifdef CONFIG_MET_ARM_32BIT
+		if (strcmp(c->name, "cpu") == 0) {
+			if ((c->mode) && (c->start)) {
+				pmu_profiling_version = 1;
+				cpu_related_cnt = 1;
+				if (met_cpu_pmu_method != 0)
+					met_perf_cpupmu_start();
+				else
+					c->start();
+			}
+			continue;
+		}
+#endif
+
+#ifdef MET_SUPPORT_CPUPMU_V2
+		if (strcmp(c->name, "cpu-pmu") == 0) {
+			if ((c->mode) && (c->start)) {
+				pmu_profiling_version = 2;
+				cpu_related_cnt = 1;
+				if (met_cpu_pmu_method != 0)
+					met_perf_cpupmu_start_v2();
+				else
+					c->start();
+			}
+			continue;
+		} else if (strcmp(c->name, "cpu") == 0) {
+			if ((c->mode) && (c->start)) {
+				pmu_profiling_version = 1;
+				cpu_related_cnt = 1;
+				if (met_cpu_pmu_method != 0)
+					met_perf_cpupmu_start();
+				else
+					c->start();
+			}
+			continue;
+		}
+#endif
+		if ((c->mode) && (c->cpu_related == 1))
+			cpu_related_cnt = 1;
+
+		if (c->ondiemet_mode == 0) {
+			if ((!(c->cpu_related)) && (c->mode) && (c->start))
+				c->start();
+		} else if (c->ondiemet_mode == 1) {
+			if ((!(c->cpu_related)) && (c->mode) && (c->ondiemet_start))
+				c->ondiemet_start();
+		} else if (c->ondiemet_mode == 2) {
+			if ((!(c->cpu_related)) && (c->mode) && (c->start))
+				c->start();
+			if ((!(c->cpu_related)) && (c->mode) && (c->ondiemet_start))
+				c->ondiemet_start();
+		}
+	}
+
+	get_online_cpus();
+	online_cpu_map = 0;
+	for_each_online_cpu(cpu) {
+		online_cpu_map |= (1 << cpu);
+	}
+	dbg_met_tag_oneshot(0, "met_online cpu map", online_cpu_map);
+	preferred_polling_cpu = calc_preferred_polling_cpu(online_cpu_map);
+	if (preferred_polling_cpu != -1)
+		curr_polling_cpu = preferred_polling_cpu;
+	dbg_met_tag_oneshot(0, "met_curr polling cpu", curr_polling_cpu);
+	start = 1;
+
+	if (cpu_related_cnt == 0)
+		met_smp_call_function_single_symbol(curr_polling_cpu, __met_hrtimer_start, NULL, 1);
+	else
+		on_each_cpu(__met_hrtimer_start, NULL, 1);
+	put_online_cpus();
+
+	return ret;
+}
+
+void sampler_stop(void)
+{
+	int cpu;
+	struct met_cpu_struct *met_cpu_ptr;
+	struct metdevice *c;
+	struct delayed_work *dw;
+
+	get_online_cpus();
+
+	on_each_cpu(__met_hrtimer_stop, NULL, 1);
+/* for_each_online_cpu(cpu) { */
+	for_each_possible_cpu(cpu) {	/* Just for case */
+		met_cpu_ptr = &per_cpu(met_cpu, cpu);
+		dw = &met_cpu_ptr->dwork;
+		cancel_delayed_work_sync(dw);
+		/* sync_samples(cpu); */
+	}
+
+	start = 0;
+	put_online_cpus();
+
+	cpuhp_remove_state_nocalls(CPUHP_AP_ONLINE_DYN);
+
+	list_for_each_entry(c, &met_list, list) {
+#ifdef CONFIG_MET_ARM_32BIT
+		if (strcmp(c->name, "cpu") == 0) {
+			pmu_profiling_version = 0;
+			if ((c->mode) && (c->stop)) {
+				if (met_cpu_pmu_method != 0)
+					met_perf_cpupmu_stop();
+				else
+					c->stop();
+			}
+			module_put(c->owner);
+			continue;
+		}
+#endif
+
+#ifdef MET_SUPPORT_CPUPMU_V2
+		if (strcmp(c->name, "cpu-pmu") == 0) {
+			pmu_profiling_version = 0;
+			if ((c->mode) && (c->stop)) {
+				if (met_cpu_pmu_method != 0)
+					met_perf_cpupmu_stop_v2();
+				else
+					c->stop();
+			}
+			module_put(c->owner);
+			continue;
+		}
+		else if (strcmp(c->name, "cpu") == 0) {
+			pmu_profiling_version = 0;
+			if ((c->mode) && (c->stop)) {
+				if (met_cpu_pmu_method != 0)
+					met_perf_cpupmu_stop();
+				else
+					c->stop();
+			}
+			module_put(c->owner);
+			continue;
+		}
+#endif
+		if (c->ondiemet_mode == 0) {
+			if ((!(c->cpu_related)) && (c->mode) && (c->stop))
+				c->stop();
+		} else if (c->ondiemet_mode == 1) {
+			if ((!(c->cpu_related)) && (c->mode) && (c->ondiemet_stop))
+				c->ondiemet_stop();
+		} else if (c->ondiemet_mode == 2) {
+			if ((!(c->cpu_related)) && (c->mode) && (c->stop))
+				c->stop();
+			if ((!(c->cpu_related)) && (c->mode) && (c->ondiemet_stop))
+				c->ondiemet_stop();
+		}
+		module_put(c->owner);
+	}
+
+	cpu_related_cnt = 0;
+}
+
+#if 0 /* cann't use static now */
+enum {
+	MET_SUSPEND = 1,
+	MET_RESUME = 2,
+};
+
+static noinline void tracing_mark_write(int op)
+{
+	switch (op) {
+	case MET_SUSPEND:
+		MET_TRACE("C|0|MET_SUSPEND|1");
+		break;
+	case MET_RESUME:
+		MET_TRACE("C|0|MET_SUSPEND|0");
+		break;
+	}
+}
+#endif
+
+int met_hrtimer_suspend(void)
+{
+	struct metdevice *c;
+
+	met_set_suspend_notify(1);
+	/* tracing_mark_write(MET_SUSPEND); */
+//	tracing_mark_write(TYPE_MET_SUSPEND, 0, 0, 0, 0, 0);
+	if (start == 0)
+		return 0;
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->suspend)
+			c->suspend();
+	}
+
+	/* get current COUNT */
+	MET_TRACE("TS: %llu GPT: %llX", sched_clock(), arch_counter_get_cntvct());
+	return 0;
+}
+
+void met_hrtimer_resume(void)
+{
+	struct metdevice *c;
+
+	/* get current COUNT */
+	MET_TRACE("TS: %llu GPT: %llX", sched_clock(), arch_counter_get_cntvct());
+
+	/* tracing_mark_write(MET_RESUME); */
+//	tracing_mark_write(TYPE_MET_RESUME, 0, 0, 0, 0, 0);
+	if (start == 0)
+		return;
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->resume)
+			c->resume();
+	}
+}
+
+/*
+ * event timer:
+ * register IRQ, sched_switch event to monitor Polling count
+ * count can be printed at any live cpu.
+ */
+void met_event_timer_notify(void)
+{
+	unsigned long long stamp;
+	struct metdevice *c;
+	int cpu = -1;
+
+	if (start == 0)
+		return;
+
+	cpu = smp_processor_id();
+	list_for_each_entry(c, &met_list, list) {
+		stamp = local_clock();
+
+		if (c->prev_stamp == 0)
+			c->prev_stamp = stamp;
+
+		/* Critical Section Start */
+		/* try spinlock to prevent a event print twice between config time interval */
+		if (!spin_trylock(&(c->my_lock)))
+			continue;
+
+		/*
+		 * DEFAULT_HRTIMER_EXPIRE (met_hrtimer_expire):
+		 * sample_rate == 0 --> always print
+		 * sample_rate == 1000 --> print interval larger than 1 ms
+		 */
+		if (DEFAULT_HRTIMER_EXPIRE == 0 || (stamp - c->prev_stamp) < DEFAULT_HRTIMER_EXPIRE) {
+			spin_unlock(&(c->my_lock));
+			continue;
+		}
+
+		c->prev_stamp = stamp;
+		spin_unlock(&(c->my_lock));
+		/* Critical Section End */
+
+		if ((c->mode == 0) || (c->timed_polling == NULL))
+			continue;
+
+		stamp = local_clock();
+		c->timed_polling(stamp, cpu);
+	}
+}
+
diff --git a/src/devtools/met-driver/4.19/mt2712/sampler.h b/src/devtools/met-driver/4.19/mt2712/sampler.h
new file mode 100644
index 0000000..ae780c0
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/sampler.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _SAMPLER_H_
+#define _SAMPLER_H_
+
+/*
+ * sampling rate: 1ms
+ * log generating rate: 10ms
+ */
+#if 0
+#define DEFAULT_TIMER_EXPIRE (HZ / 100)
+#define DEFAULT_HRTIMER_EXPIRE (TICK_NSEC / 10)
+#else
+extern int met_timer_expire;	/* in jiffies */
+extern int met_hrtimer_expire;	/* in us */
+#define DEFAULT_TIMER_EXPIRE (met_timer_expire)
+#define DEFAULT_HRTIMER_EXPIRE (met_hrtimer_expire)
+#endif
+/*
+ * sampling rate: 10ms
+ * log generating rate: 100ms
+ */
+/* #define DEFAULT_TIMER_EXPIRE (HZ / 10) */
+/* #define DEFAULT_HRTIMER_EXPIRE (TICK_NSEC / 1) */
+
+int met_hrtimer_start(void);
+void met_hrtimer_stop(void);
+int sampler_start(void);
+void sampler_stop(void);
+
+extern struct list_head met_list;
+extern void add_cookie(struct pt_regs *regs, int cpu);
+extern int met_hrtimer_suspend(void);
+extern void met_hrtimer_resume(void);
+extern void met_event_timer_notify(void);
+
+extern int get_pmu_profiling_version(void);
+
+#ifdef CONFIG_CPU_FREQ
+#include "power.h"
+#endif
+
+#endif				/* _SAMPLER_H_ */
diff --git a/src/devtools/met-driver/4.19/mt2712/sspm/ondiemet_sspm.c b/src/devtools/met-driver/4.19/mt2712/sspm/ondiemet_sspm.c
new file mode 100644
index 0000000..08db3fa
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/sspm/ondiemet_sspm.c
@@ -0,0 +1,493 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/delay.h>
+#include <linux/module.h> /* symbol_get */
+
+#include "ondiemet_sspm.h"
+#define MET_USER_EVENT_SUPPORT
+#include "met_drv.h"
+
+#ifdef CONFIG_MTK_TINYSYS_SSPM_SUPPORT
+#ifdef CONFIG_MTK_GMO_RAM_OPTIMIZE
+#ifdef CONFIG_MET_ARM_32BIT
+#include <asm/dma-mapping.h> /* arm_coherent_dma_ops */
+#else /* CONFIG_MET_ARM_32BIT */
+#include <linux/dma-mapping.h>
+#endif /* CONFIG_MET_ARM_32BIT */
+#else /* CONFIG_MTK_GMO_RAM_OPTIMIZE */
+#include "sspm_reservedmem.h"
+#include "sspm_reservedmem_define.h"
+#endif /* CONFIG_MTK_GMO_RAM_OPTIMIZE */
+
+dma_addr_t ondiemet_sspm_log_phy_addr;
+void *ondiemet_sspm_log_virt_addr;
+uint32_t ondiemet_sspm_log_size = 0x400000;
+
+/* SSPM_LOG_FILE 0 */
+/* SSPM_LOG_SRAM 1 */
+/* SSPM_LOG_DRAM 2 */
+int sspm_log_mode;
+/* SSPM_RUN_NORMAL mode 0 */
+/* SSPM_RUN_CONTINUOUS mode 1 */
+int sspm_run_mode;
+int met_sspm_log_discard = -1;
+int sspm_log_size = 100;
+
+int sspm_buffer_size;
+int sspm_buf_available;
+EXPORT_SYMBOL(sspm_buf_available);
+int sspm_buf_mapped = -1; /* get buffer by MET itself */
+
+static ssize_t sspm_buffer_size_show(struct device *dev, struct device_attribute *attr, char *buf);
+static DEVICE_ATTR(sspm_buffer_size, 0444, sspm_buffer_size_show, NULL);
+
+static ssize_t sspm_available_show(struct device *dev, struct device_attribute *attr, char *buf);
+static DEVICE_ATTR(sspm_available, 0444, sspm_available_show, NULL);
+
+static ssize_t sspm_log_discard_show(struct device *dev, struct device_attribute *attr, char *buf);
+static DEVICE_ATTR(sspm_log_discard, 0444, sspm_log_discard_show, NULL);
+
+static ssize_t sspm_log_mode_show(struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t sspm_log_mode_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
+static DEVICE_ATTR(sspm_log_mode, 0664, sspm_log_mode_show, sspm_log_mode_store);
+
+static ssize_t sspm_log_size_show(struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t sspm_log_size_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
+static DEVICE_ATTR(sspm_log_size, 0664, sspm_log_size_show, sspm_log_size_store);
+
+
+static ssize_t sspm_run_mode_show(struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t sspm_run_mode_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
+static DEVICE_ATTR(sspm_run_mode, 0664, sspm_run_mode_show, sspm_run_mode_store);
+
+static ssize_t sspm_modules_show(struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t sspm_modules_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
+static DEVICE_ATTR(sspm_modules, 0664, sspm_modules_show, sspm_modules_store);
+
+static ssize_t sspm_op_ctrl_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
+static DEVICE_ATTR(sspm_op_ctrl, 0220, NULL, sspm_op_ctrl_store);
+
+static ssize_t sspm_buffer_size_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int i;
+
+	mutex_lock(&dev->mutex);
+	i = snprintf(buf, PAGE_SIZE, "%d\n", sspm_buffer_size);
+	mutex_unlock(&dev->mutex);
+	return i;
+}
+
+static ssize_t sspm_available_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int i;
+
+	mutex_lock(&dev->mutex);
+	i = snprintf(buf, PAGE_SIZE, "%d\n", 1);
+	mutex_unlock(&dev->mutex);
+	return i;
+}
+
+static ssize_t sspm_log_discard_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int i;
+
+	mutex_lock(&dev->mutex);
+	i = snprintf(buf, PAGE_SIZE, "%d\n", met_sspm_log_discard);
+	mutex_unlock(&dev->mutex);
+	return i;
+}
+
+static ssize_t sspm_log_mode_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int i;
+
+	mutex_lock(&dev->mutex);
+	i = snprintf(buf, PAGE_SIZE, "%d\n", sspm_log_mode);
+	mutex_unlock(&dev->mutex);
+	return i;
+}
+
+static ssize_t sspm_log_mode_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+	int value;
+
+	if (kstrtoint(buf, 0, &value) != 0)
+		return -EINVAL;
+	mutex_lock(&dev->mutex);
+	sspm_log_mode = value;
+	mutex_unlock(&dev->mutex);
+	return count;
+}
+
+
+static ssize_t sspm_log_size_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int i;
+
+	mutex_lock(&dev->mutex);
+	i = snprintf(buf, PAGE_SIZE, "%d\n", sspm_log_size);
+	mutex_unlock(&dev->mutex);
+	return i;
+}
+
+static ssize_t sspm_log_size_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+	int value;
+
+	if (kstrtoint(buf, 0, &value) != 0)
+		return -EINVAL;
+	mutex_lock(&dev->mutex);
+	sspm_log_size = value;
+	mutex_unlock(&dev->mutex);
+	return count;
+}
+
+
+static ssize_t sspm_run_mode_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int i;
+
+	mutex_lock(&dev->mutex);
+	i = snprintf(buf, PAGE_SIZE, "%d\n", sspm_run_mode);
+	mutex_unlock(&dev->mutex);
+	return i;
+}
+
+static ssize_t sspm_run_mode_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+	int value;
+
+	if (kstrtoint(buf, 0, &value) != 0)
+		return -EINVAL;
+	mutex_lock(&dev->mutex);
+	sspm_run_mode = value;
+	mutex_unlock(&dev->mutex);
+	return count;
+}
+
+static ssize_t sspm_op_ctrl_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+	int value;
+
+	if (kstrtoint(buf, 0, &value) != 0)
+		return -EINVAL;
+	mutex_lock(&dev->mutex);
+	if (value == 1)
+		sspm_start();
+	else if (value == 2)
+		sspm_stop();
+	else if (value == 3)
+		sspm_extract();
+	else if (value == 4)
+		sspm_flush();
+	mutex_unlock(&dev->mutex);
+	return count;
+}
+
+static ssize_t sspm_modules_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int i;
+
+	mutex_lock(&dev->mutex);
+	i = snprintf(buf, PAGE_SIZE, "%x\n", ondiemet_module[ONDIEMET_SSPM]);
+	mutex_unlock(&dev->mutex);
+	return i;
+}
+
+static ssize_t sspm_modules_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+	uint32_t value;
+
+	if (kstrtouint(buf, 0, &value) != 0)
+		return -EINVAL;
+	mutex_lock(&dev->mutex);
+	ondiemet_module[ONDIEMET_SSPM] = value;
+	mutex_unlock(&dev->mutex);
+	return count;
+}
+
+int sspm_attr_init(struct device *dev)
+{
+	int ret;
+
+#ifdef CONFIG_MTK_GMO_RAM_OPTIMIZE
+#ifdef CONFIG_MET_ARM_32BIT
+	struct dma_map_ops *ops = (struct dma_map_ops *)symbol_get(arm_coherent_dma_ops);
+
+	if (ops && ops->alloc) {
+		dev->coherent_dma_mask = DMA_BIT_MASK(32);
+		ondiemet_sspm_log_virt_addr = ops->alloc(dev,
+						ondiemet_sspm_log_size,
+						&ondiemet_sspm_log_phy_addr,
+						GFP_KERNEL,
+						0);
+	}
+#else /* CONFIG_MET_ARM_32BIT */
+	/* dma_alloc */
+	ondiemet_sspm_log_virt_addr = dma_alloc_coherent(dev,
+			ondiemet_sspm_log_size,
+			&ondiemet_sspm_log_phy_addr,
+			GFP_KERNEL);
+#endif /* CONFIG_MET_ARM_32BIT */
+#else /* CONFIG_MTK_GMO_RAM_OPTIMIZE */
+
+	phys_addr_t (*sspm_reserve_mem_get_phys_sym)(unsigned int id) = NULL;
+	phys_addr_t (*sspm_reserve_mem_get_virt_sym)(unsigned int id) = NULL;
+	phys_addr_t (*sspm_reserve_mem_get_size_sym)(unsigned int id) = NULL;
+
+	sspm_reserve_mem_get_phys_sym = (phys_addr_t (*)(unsigned int id))symbol_get(sspm_reserve_mem_get_virt);
+	sspm_reserve_mem_get_virt_sym = (phys_addr_t (*)(unsigned int id))symbol_get(sspm_reserve_mem_get_phys);
+	sspm_reserve_mem_get_size_sym = (phys_addr_t (*)(unsigned int id))symbol_get(sspm_reserve_mem_get_size);
+	if (sspm_reserve_mem_get_phys_sym)
+		ondiemet_sspm_log_virt_addr = (void*)sspm_reserve_mem_get_virt(MET_MEM_ID);
+	if (sspm_reserve_mem_get_virt_sym)
+		ondiemet_sspm_log_phy_addr = sspm_reserve_mem_get_phys(MET_MEM_ID);
+	if (sspm_reserve_mem_get_size_sym)
+		ondiemet_sspm_log_size = sspm_reserve_mem_get_size(MET_MEM_ID);
+#endif /* CONFIG_MTK_GMO_RAM_OPTIMIZE */
+
+	ret = device_create_file(dev, &dev_attr_sspm_buffer_size);
+	if (ret != 0) {
+		pr_debug("can not create device file: sspm_buffer_size\n");
+		return ret;
+	}
+
+	ret = device_create_file(dev, &dev_attr_sspm_available);
+	if (ret != 0) {
+		pr_debug("can not create device file: sspm_available\n");
+		return ret;
+	}
+
+	ret = device_create_file(dev, &dev_attr_sspm_log_discard);
+	if (ret != 0) {
+		pr_debug("can not create device file: sspm_log_discard\n");
+		return ret;
+	}
+	ret = device_create_file(dev, &dev_attr_sspm_log_mode);
+	if (ret != 0) {
+		pr_debug("can not create device file: sspm_log_mode\n");
+		return ret;
+	}
+	ret = device_create_file(dev, &dev_attr_sspm_log_size);
+	if (ret != 0) {
+		pr_debug("can not create device file: sspm_log_size\n");
+		return ret;
+	}
+	ret = device_create_file(dev, &dev_attr_sspm_run_mode);
+	if (ret != 0) {
+		pr_debug("can not create device file: sspm_run_mode\n");
+		return ret;
+	}
+	ret = device_create_file(dev, &dev_attr_sspm_op_ctrl);
+	if (ret != 0) {
+		pr_debug("can not create device file: sspm_op_ctrl\n");
+		return ret;
+	}
+	ret = device_create_file(dev, &dev_attr_sspm_modules);
+	if (ret != 0) {
+		pr_debug("can not create device file: sspm_modules\n");
+		return ret;
+	}
+
+	if (ondiemet_sspm_log_virt_addr != NULL) {
+		start_sspm_ipi_recv_thread();
+		sspm_buf_available = 1;
+		sspm_buffer_size = ondiemet_sspm_log_size;
+	} else {
+		sspm_buf_available = 0;
+		sspm_buffer_size = -1;
+	}
+
+	return 0;
+}
+
+int sspm_attr_uninit(struct device *dev)
+{
+	/* dma_free */
+	if (ondiemet_sspm_log_virt_addr != NULL) {
+#ifdef CONFIG_MTK_GMO_RAM_OPTIMIZE
+#ifdef CONFIG_MET_ARM_32BIT
+		struct dma_map_ops *ops = (struct dma_map_ops *)symbol_get(arm_coherent_dma_ops);
+
+		if (ops && ops->free) {
+			ops->free(dev,
+				ondiemet_sspm_log_size,
+				ondiemet_sspm_log_virt_addr,
+				ondiemet_sspm_log_phy_addr,
+				0);
+		}
+#else /* CONFIG_MET_ARM_32BIT */
+		dma_free_coherent(dev,
+			ondiemet_sspm_log_size,
+			ondiemet_sspm_log_virt_addr,
+			ondiemet_sspm_log_phy_addr);
+#endif /* CONFIG_MET_ARM_32BIT */
+#endif /* CONFIG_MTK_GMO_RAM_OPTIMIZE */
+		ondiemet_sspm_log_virt_addr = NULL;
+		stop_sspm_ipi_recv_thread();
+	}
+
+	device_remove_file(dev, &dev_attr_sspm_buffer_size);
+	device_remove_file(dev, &dev_attr_sspm_available);
+	device_remove_file(dev, &dev_attr_sspm_log_discard);
+	device_remove_file(dev, &dev_attr_sspm_log_mode);
+	device_remove_file(dev, &dev_attr_sspm_log_size);
+	device_remove_file(dev, &dev_attr_sspm_run_mode);
+	device_remove_file(dev, &dev_attr_sspm_op_ctrl);
+	device_remove_file(dev, &dev_attr_sspm_modules);
+
+	return 0;
+}
+
+#if 0 /* move to sspm_attr_init() */
+void sspm_get_buffer_info(void)
+{
+	if (ondiemet_sspm_log_virt_addr != NULL) {
+		sspm_buf_available = 1;
+		sspm_buffer_size = ondiemet_sspm_log_size;
+	} else {
+		sspm_buf_available = 0;
+		sspm_buffer_size = -1;
+	}
+}
+#endif
+
+extern const char *met_get_platform_name(void);
+void sspm_start(void)
+{
+	int32_t ret = 0;
+	uint32_t rdata;
+	uint32_t ipi_buf[4];
+	const char* platform_name = NULL;
+	unsigned int platform_id = 0;
+	met_sspm_log_discard = -1;
+
+	/* clear DRAM buffer */
+	if (ondiemet_sspm_log_virt_addr != NULL)
+		memset_io((void *)ondiemet_sspm_log_virt_addr, 0, ondiemet_sspm_log_size);
+	else
+		return;
+
+	platform_name = met_get_platform_name();
+	if (platform_name) {
+		char buf[5];
+
+		memset(buf, 0x0, 5);
+		memcpy(buf, &platform_name[2], 4);
+		ret = kstrtouint(buf, 10, &platform_id);
+	}
+
+	/* send DRAM physical address */
+	ipi_buf[0] = MET_MAIN_ID | MET_BUFFER_INFO;
+	ipi_buf[1] = (unsigned int)ondiemet_sspm_log_phy_addr; /* address */
+	if (ret == 0)
+		ipi_buf[2] = platform_id;
+	else
+		ipi_buf[2] = 0;
+	ipi_buf[3] = 0;
+	ret = sspm_ipi_send_sync(IPI_ID_MET, IPI_OPT_WAIT, (void *)ipi_buf, 0, &rdata, 1);
+
+	/* start ondiemet now */
+	ipi_buf[0] = MET_MAIN_ID | MET_OP | MET_OP_START;
+	ipi_buf[1] = ondiemet_module[ONDIEMET_SSPM];
+	ipi_buf[2] = sspm_log_mode;
+	ipi_buf[3] = sspm_run_mode;
+	ret = sspm_ipi_send_sync(IPI_ID_MET, IPI_OPT_WAIT, (void *)ipi_buf, 0, &rdata, 1);
+}
+
+void sspm_stop(void)
+{
+	int32_t ret;
+	uint32_t rdata;
+	uint32_t ipi_buf[4];
+
+	if (sspm_buf_available == 1) {
+		ipi_buf[0] = MET_MAIN_ID|MET_OP|MET_OP_STOP;
+		ipi_buf[1] = 0;
+		ipi_buf[2] = 0;
+		ipi_buf[3] = 0;
+		ret = sspm_ipi_send_sync(IPI_ID_MET, IPI_OPT_WAIT, (void *)ipi_buf, 0, &rdata, 1);
+	}
+}
+
+void sspm_extract(void)
+{
+	int32_t ret;
+	uint32_t rdata;
+	uint32_t ipi_buf[4];
+	int32_t count;
+
+	count = 20;
+	if (sspm_buf_available == 1) {
+		while ((sspm_buffer_dumping == 1) && (count != 0)) {
+			msleep(50);
+			count--;
+		}
+		ipi_buf[0] = MET_MAIN_ID|MET_OP|MET_OP_EXTRACT;
+		ipi_buf[1] = 0;
+		ipi_buf[2] = 0;
+		ipi_buf[3] = 0;
+		ret = sspm_ipi_send_sync(IPI_ID_MET, IPI_OPT_WAIT, (void *)ipi_buf, 0, &rdata, 1);
+	}
+
+	if (sspm_run_mode == SSPM_RUN_NORMAL)
+		ondiemet_module[ONDIEMET_SSPM] = 0;
+}
+
+void sspm_flush(void)
+{
+	int32_t ret;
+	uint32_t rdata;
+	uint32_t ipi_buf[4];
+
+	if (sspm_buf_available == 1) {
+		ipi_buf[0] = MET_MAIN_ID|MET_OP|MET_OP_FLUSH;
+		ipi_buf[1] = 0;
+		ipi_buf[2] = 0;
+		ipi_buf[3] = 0;
+		ret = sspm_ipi_send_sync(IPI_ID_MET, IPI_OPT_WAIT, (void *)ipi_buf, 0, &rdata, 1);
+	}
+
+	if (sspm_run_mode == SSPM_RUN_NORMAL)
+		ondiemet_module[ONDIEMET_SSPM] = 0;
+}
+#else /* CONFIG_MTK_TINYSYS_SSPM_SUPPORT */
+int sspm_buffer_size = -1;
+
+int sspm_attr_init(struct device *dev)
+{
+	return 0;
+}
+
+int sspm_attr_uninit(struct device *dev)
+{
+	return 0;
+}
+
+void sspm_start(void)
+{
+}
+
+void sspm_stop(void)
+{
+}
+
+void sspm_extract(void)
+{
+}
+
+void sspm_flush(void)
+{
+}
+
+#endif /* CONFIG_MTK_TINYSYS_SSPM_SUPPORT */
diff --git a/src/devtools/met-driver/4.19/mt2712/sspm/ondiemet_sspm.h b/src/devtools/met-driver/4.19/mt2712/sspm/ondiemet_sspm.h
new file mode 100644
index 0000000..6fa37c9
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/sspm/ondiemet_sspm.h
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __ONDIEMET_SSPM_H
+#define __ONDIEMET_SSPM_H
+
+#ifdef CONFIG_MTK_TINYSYS_SSPM_SUPPORT
+#include "ondiemet.h"
+#include "sspm_ipi.h"
+#include <linux/dma-mapping.h>
+
+/* we may use IPI_ID_PLATFORM for mt6759 to reduce SRAM */
+#ifndef IPI_ID_MET
+/* #define IPI_ID_MET IPI_ID_TST1 */
+#define IPI_ID_MET IPI_ID_PLATFORM
+#endif
+
+/* MET IPI command definition: mbox 0 */
+/* main func ID: bit[31-24]; sub func ID: bit[23-18]; argu 0: bit[17-0] */
+#define MET_MAIN_ID_MASK        0xff000000 /* bit 31 - 24 */
+#define MET_SUB_ID_MASK         0x00fc0000 /* bit 23 - 18 */
+#define MET_ARGU0_MASK          0x0003ffff /* bit 17 - 0 */
+#define FUNC_BIT_SHIFT          18
+#define MID_BIT_SHIFT           9
+#define MET_MAIN_ID             0x06000000
+/* handle argument and attribute */
+#define PROCESS_ARGU            0x01
+#define PROCESS_ATTR            0x02
+#define MODULE_ID_MASK          0x3fe00 /* bit 9 - 17 */
+#define ARGUMENT_MASK           0x01ff  /* bit 0 - 8 */
+
+/* the following command is used for AP to MD32 */
+#define MET_OP            (1 << FUNC_BIT_SHIFT)
+/* argu 0: start: 0x01; stop: 0x02; extract: 0x03 */
+#define MET_OP_START        0x00000001
+#define MET_OP_STOP         0x00000002
+#define MET_OP_EXTRACT      0x00000003
+#define MET_OP_FLUSH        0x00000004
+#define MET_SR            (2 << FUNC_BIT_SHIFT) /* sample rate */
+#define MET_MODULE        (3 << FUNC_BIT_SHIFT) /* module enable/disable */
+#define MET_ARGU          (4 << FUNC_BIT_SHIFT) /* argument passing */
+#define MET_ATTR          (5 << FUNC_BIT_SHIFT) /* attribute passing */
+/* system memory information for on-die-met log data */
+#define MET_BUFFER_INFO   (6 << FUNC_BIT_SHIFT)
+#define MET_TIMESTAMP     (7 << FUNC_BIT_SHIFT) /* timestamp info */
+#define MET_GPT           (8 << FUNC_BIT_SHIFT) /* GPT counter reading */
+#define MET_REQ_AP2MD     (9 << FUNC_BIT_SHIFT) /* user defined command */
+#define MET_RESP_AP2MD    (10 << FUNC_BIT_SHIFT) /* may no need */
+/* mode: bit 15 - 0: */
+/*  Bit 0: MD32 SRAM mode; Bit 1: System DRAM mode */
+/*  value: 0: output to next level of storage; 1: loop in its own storage */
+#define MET_DATA_MODE     (11 << FUNC_BIT_SHIFT) /* log output mode */
+/* start/stop read data into MD32 SRAM buffer; both DMA and met_printf() */
+#define MET_DATA_OP       (12 << FUNC_BIT_SHIFT) /* data read operation */
+/* the following command is used for MD32 to AP */
+#define MET_DUMP_BUFFER   (13 << FUNC_BIT_SHIFT)
+#define MET_REQ_MD2AP     (14 << FUNC_BIT_SHIFT) /* user defined command */
+#define MET_CLOSE_FILE    (15 << FUNC_BIT_SHIFT) /* Inform to close the SD file */
+#define MET_RESP_MD2AP    (16 << FUNC_BIT_SHIFT)
+#define MET_RUN_MODE      (17 << FUNC_BIT_SHIFT)
+
+/* Note: the module ID and its bit pattern should be fixed as below */
+/* DMA based module first */
+enum {
+	MID_PMQOS = 0,
+	MID_VCORE_DVFS,
+	MID_EMI,
+	MID_THERMAL_CPU,
+	MID_WALL_TIME,
+	MID_CPU_DVFS,
+	MID_GPU_DVFS,
+	MID_PTPOD,
+	MID_SPM,
+	MID_PROFILE,
+
+	MID_COMMON = 0x1F
+};
+
+#define ID_PMQOS       (1 << MID_PMQOS)
+#define ID_SMI         (1 << MID_SMI)
+#define ID_EMI         (1 << MID_EMI)
+#define ID_THERMAL_CPU (1 << MID_THERMAL_CPU)
+#define ID_WALL_TIME   (1 << MID_WALL_TIME)
+#define ID_CPU_DVFS    (1 << MID_CPU_DVFS)
+#define ID_GPU_DVFS    (1 << MID_GPU_DVFS)
+#define ID_VCORE_DVFS  (1 << MID_VCORE_DVFS)
+#define ID_PTPOD       (1 << MID_PTPOD)
+#define ID_SPM         (1 << MID_SPM)
+#define ID_PROFILE     (1 << MID_PROFILE)
+#define ID_COMMON      (1 << MID_COMMON)
+
+
+extern void ondiemet_extract(void);
+extern void ondiemet_stop(void);
+extern void ondiemet_start(void);
+
+extern void start_sspm_ipi_recv_thread(void);
+extern void stop_sspm_ipi_recv_thread(void);
+
+extern unsigned int ondiemet_ipi_buf[];
+
+/* extern phys_addr_t ondiemet_sspm_log_phy_addr, ondiemet_sspm_log_virt_addr; */
+extern dma_addr_t ondiemet_sspm_log_phy_addr;
+
+extern void *ondiemet_sspm_log_virt_addr;
+extern uint32_t ondiemet_sspm_log_size;
+
+extern int ondiemet_attr_init(struct device *dev);
+extern int ondiemet_attr_uninit(struct device *dev);
+extern int met_sspm_log_discard;
+
+#define SSPM_LOG_FILE 0
+#define SSPM_LOG_SRAM 1
+#define SSPM_LOG_DRAM 2
+extern int sspm_log_mode;
+#define SSPM_RUN_NORMAL 0
+#define SSPM_RUN_CONTINUOUS 1
+extern int sspm_run_mode;
+
+/* extern void sspm_get_buffer_info(void); */
+extern int sspm_buf_available;
+extern int sspm_buffer_dumping;
+
+void sspm_flush(void);
+
+#endif /* CONFIG_MTK_TINYSYS_SSPM_SUPPORT */
+#endif /* __ONDIEMET_SSPM_H */
diff --git a/src/devtools/met-driver/4.19/mt2712/switch.h b/src/devtools/met-driver/4.19/mt2712/switch.h
new file mode 100644
index 0000000..14397d7
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/switch.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __MT_SWITCH__
+#define __MT_SWITCH__
+/*
+ * =========================
+ * !!!!!!!!!!!NOTICE!!!!!!!!
+ * =========================
+ * MT_SWITCH OPTION must delcare as Mask value
+ * And sort them from smallest to largest
+ * MT_SWITCH_MX_ITEM was used to determine argument range
+*/
+enum {
+	/* =================== */
+	/* user define mt switch event */
+	/* =================== */
+	MT_SWITCH_SCHEDSWITCH = 0x0001,
+	MT_SWITCH_64_32BIT = 0x0002,
+	MT_SWITCH_TAGPOLLING = 0x0004,
+	MT_SWITCH_EVENT_TIMER = 0x0008,
+	/* =================== */
+	MT_SWITCH_MX_ITEM
+};
+
+extern struct metdevice met_switch;
+#endif
diff --git a/src/devtools/met-driver/4.19/mt2712/trace.h b/src/devtools/met-driver/4.19/mt2712/trace.h
new file mode 100644
index 0000000..f259b7a
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/trace.h
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _TRACE_H_
+#define _TRACE_H_
+
+
+extern void (*mp_cp_ptr)(unsigned long long timestamp,
+	       struct task_struct *task,
+	       unsigned long program_counter,
+	       unsigned long dcookie,
+	       unsigned long offset,
+	       unsigned char cnt, unsigned int *value);
+
+#define MP_FMT1	"%x\n"
+#define MP_FMT2	"%x,%x\n"
+#define MP_FMT3	"%x,%x,%x\n"
+#define MP_FMT4	"%x,%x,%x,%x\n"
+#define MP_FMT5	"%x,%x,%x,%x,%x\n"
+#define MP_FMT6	"%x,%x,%x,%x,%x,%x\n"
+#define MP_FMT7	"%x,%x,%x,%x,%x,%x,%x\n"
+#define MP_FMT8	"%x,%x,%x,%x,%x,%x,%x,%x\n"
+#define MP_FMT9	"%x,%x,%x,%x,%x,%x,%x,%x,%x\n"
+#define MP_FMT10 "%x,%x,%x,%x,%x,%x,%x,%x,%x,%x\n"
+#define MP_FMT11 "%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x\n"
+#define MP_FMT12 "%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x\n"
+#define MP_FMT13 "%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x\n"
+#define MP_FMT14 "%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x\n"
+#define MP_FMT15 "%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x\n"
+
+#define MET_GENERAL_PRINT(FUNC, count, value) \
+do { \
+	switch (count) { \
+	case 1: { \
+		FUNC(MP_FMT1, value[0]); \
+		} \
+		break; \
+	case 2: { \
+		FUNC(MP_FMT2, value[0], value[1]); \
+		} \
+		break; \
+	case 3: { \
+		FUNC(MP_FMT3, value[0], value[1], value[2]); \
+		} \
+		break; \
+	case 4: { \
+		FUNC(MP_FMT4, value[0], value[1], value[2], value[3]); \
+		} \
+		break; \
+	case 5: { \
+		FUNC(MP_FMT5, value[0], value[1], value[2], value[3], value[4]); \
+		} \
+		break; \
+	case 6: { \
+		FUNC(MP_FMT6, value[0], value[1], value[2], value[3], value[4], value[5]); \
+		} \
+		break; \
+	case 7: { \
+		FUNC(MP_FMT7, value[0], value[1], value[2], value[3], value[4], value[5], value[6]); \
+		} \
+		break; \
+	case 8: { \
+		FUNC(MP_FMT8, value[0], value[1], value[2], value[3], value[4], value[5], value[6], value[7]); \
+		} \
+		break; \
+	case 9: { \
+		FUNC(MP_FMT9, value[0], value[1], value[2], value[3], value[4], value[5], value[6], value[7], \
+				value[8]); \
+		} \
+		break; \
+	case 10: { \
+		FUNC(MP_FMT10, value[0], value[1], value[2], value[3], value[4], value[5], value[6], value[7], \
+				value[8], value[9]); \
+		} \
+		break; \
+	case 11: { \
+		FUNC(MP_FMT11, value[0], value[1], value[2], value[3], value[4], value[5], value[6], value[7], \
+				value[8], value[9], value[10]); \
+		} \
+		break; \
+	case 12: { \
+		FUNC(MP_FMT12, value[0], value[1], value[2], value[3], value[4], value[5], value[6], value[7], \
+				value[8], value[9], value[10], value[11]); \
+		} \
+		break; \
+	case 13: { \
+		FUNC(MP_FMT13, value[0], value[1], value[2], value[3], value[4], value[5], value[6], value[7], \
+				value[8], value[9], value[10], value[11], value[12]); \
+		} \
+		break; \
+	case 14: { \
+		FUNC(MP_FMT14, value[0], value[1], value[2], value[3], value[4], value[5], value[6], value[7], \
+				value[8], value[9], value[10], value[11], value[12], value[13]); \
+		} \
+		break; \
+	case 15: { \
+		FUNC(MP_FMT15, value[0], value[1], value[2], value[3], value[4], value[5], value[6], value[7], \
+				value[8], value[9], value[10], value[11], value[12], value[13], value[14]); \
+		} \
+		break; \
+	} \
+} while (0)
+#endif /* _TRACE_H_ */
diff --git a/src/devtools/met-driver/4.19/mt2712/util.c b/src/devtools/met-driver/4.19/mt2712/util.c
new file mode 100644
index 0000000..051a3bd
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/util.c
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "util.h"
+#include <linux/fs.h>
+#include <linux/kernel.h>
+/* #include <asm/uaccess.h> */
+#include <linux/uaccess.h>
+
+#ifdef FILELOG
+
+static char tmp[1000] = { 0 };
+
+ /*TODO*/
+/**
+ * open file
+ * @param name path to open
+ * @return file pointer
+ */
+struct file *open_file(const char *name)
+{
+	struct file *fp = NULL;
+
+	fp = filp_open(name, O_WRONLY | O_APPEND /*| O_TRUNC */  | O_CREAT, 0664);
+	if (unlikely(fp == NULL)) {
+		pr_debug(KERNEL_INFO "can not open result file");
+		return NULL;
+	}
+	return fp;
+}
+
+/**
+ * write to file
+ * @param fp file pointer
+ * @param format format string
+ * @param ... variable-length subsequent arguments
+ */
+void write_file(struct file *fp, const char *format, ...)
+{
+	va_list va;
+	mm_segment_t fs = get_fs();
+
+	va_start(va, format);
+	vsnprintf(tmp, sizeof(tmp), format, va);
+	set_fs(KERNEL_DS);
+	vfs_write(fp, tmp, strlen(tmp), &(fp->f_pos));
+	set_fs(fs);
+	va_end(va);
+}
+
+/**
+ * close file
+ * @param fp file pointer
+ * @return exit code
+ */
+int close_file(struct file *fp)
+{
+	if (likely(fp != NULL)) {
+		filp_close(fp, NULL);
+		fp = NULL;
+		return 0;
+	}
+	pr_debug("cannot close file pointer:%p\n", fp);
+	return -1;
+}
+
+void filelog(char *str)
+{
+	struct file *fp;
+
+	fp = open_file("/data/met.log");
+	if (fp != NULL) {
+		write_file(fp, "%s", str);
+		close_file(fp);
+	}
+}
+
+#endif				/* FILELOG */
diff --git a/src/devtools/met-driver/4.19/mt2712/util.h b/src/devtools/met-driver/4.19/mt2712/util.h
new file mode 100644
index 0000000..5730376
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/util.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _SRC_UTIL_H_
+#define _SRC_UTIL_H_
+
+/* #define FILELOG 1 */
+
+#ifdef FILELOG
+void filelog(char *str);
+#else
+#define filelog(str)
+#endif
+
+#endif				/* _SRC_UTIL_H_ */
diff --git a/src/devtools/met-driver/4.19/mt2712/v6_pmu_hw.c b/src/devtools/met-driver/4.19/mt2712/v6_pmu_hw.c
new file mode 100644
index 0000000..a1c5635
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/v6_pmu_hw.c
@@ -0,0 +1,290 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+
+/* include <asm/system.h> */
+#include <linux/smp.h>
+#include "cpu_pmu.h"
+#include "v6_pmu_name.h"
+
+enum ARM_TYPE {
+	ARM1136 = 0xB36,
+	ARM1156 = 0xB56,
+	ARM1176 = 0xB76,
+	CHIP_UNKNOWN = 0xFFF
+};
+
+struct chip_pmu {
+	enum ARM_TYPE type;
+	struct pmu_desc *desc;
+	unsigned int count;
+	const char *cpu_name;
+};
+
+static struct chip_pmu chips[] = {
+	{ARM1136, arm11_pmu_desc, ARM11_PMU_DESC_COUNT, "arm1136"},
+	{ARM1156, arm11_pmu_desc, ARM11_PMU_DESC_COUNT, "arm1156"},
+	{ARM1176, arm11_pmu_desc, ARM11_PMU_DESC_COUNT, "arm1176"},
+};
+static struct chip_pmu chip_unknown = { CHIP_UNKNOWN, NULL, 0, "Unknown CPU" };
+
+#define CHIP_PMU_COUNT (sizeof(chips) / sizeof(struct chip_pmu))
+
+static struct chip_pmu *chip;
+
+/* define V6_PMU_HW_DEBUG */
+#ifdef V6_PMU_HW_DEBUG
+#define v6pmu_hw_debug(fmt, arg...)     pr_debug(fmt, ##arg)
+#else
+#define v6pmu_hw_debug(fmt, arg...)     do {} while (0)
+#endif
+
+#define ARMV6_PMCR_ENABLE               (1 << 0)
+#define ARMV6_PMCR_CTR01_RESET          (1 << 1)
+#define ARMV6_PMCR_CCOUNT_RESET         (1 << 2)
+#define ARMV6_PMCR_CCOUNT_DIV           (1 << 3)
+#define ARMV6_PMCR_COUNT0_IEN           (1 << 4)
+#define ARMV6_PMCR_COUNT1_IEN           (1 << 5)
+#define ARMV6_PMCR_CCOUNT_IEN           (1 << 6)
+#define ARMV6_PMCR_COUNT0_OVERFLOW      (1 << 8)
+#define ARMV6_PMCR_COUNT1_OVERFLOW      (1 << 9)
+#define ARMV6_PMCR_CCOUNT_OVERFLOW      (1 << 10)
+#define ARMV6_PMCR_EVT_COUNT0_SHIFT     20
+#define ARMV6_PMCR_EVT_COUNT0_MASK      (0xFF << ARMV6_PMCR_EVT_COUNT0_SHIFT)
+#define ARMV6_PMCR_EVT_COUNT1_SHIFT     12
+#define ARMV6_PMCR_EVT_COUNT1_MASK      (0xFF << ARMV6_PMCR_EVT_COUNT1_SHIFT)
+
+#define ARMV6_PMCR_OVERFLOWED_MASK \
+	(ARMV6_PMCR_COUNT0_OVERFLOW | ARMV6_PMCR_COUNT1_OVERFLOW | \
+	ARMV6_PMCR_CCOUNT_OVERFLOW)
+
+enum armv6_counters {
+	ARMV6_COUNTER0 = 0,
+	ARMV6_COUNTER1,
+	ARMV6_CYCLE_COUNTER,
+};
+
+static inline unsigned long armv6_pmcr_read(void)
+{
+	u32 val;
+
+	asm volatile ("mrc   p15, 0, %0, c15, c12, 0":"=r" (val));
+	return val;
+}
+
+static inline void armv6_pmcr_write(unsigned long val)
+{
+	asm volatile ("mcr   p15, 0, %0, c15, c12, 0"::"r" (val));
+}
+
+static inline unsigned int armv6_pmu_read_count(unsigned int idx)
+{
+	unsigned long value = 0;
+
+	if (idx == ARMV6_CYCLE_COUNTER)
+		asm volatile ("mrc   p15, 0, %0, c15, c12, 1":"=r" (value));
+	else if (idx == ARMV6_COUNTER0)
+		asm volatile ("mrc   p15, 0, %0, c15, c12, 2":"=r" (value));
+	else if (idx == ARMV6_COUNTER1)
+		asm volatile ("mrc   p15, 0, %0, c15, c12, 3":"=r" (value));
+
+	return value;
+}
+
+static inline void armv6_pmu_overflow(void)
+{
+	unsigned int val;
+
+	val = armv6_pmcr_read();
+	val |= ARMV6_PMCR_OVERFLOWED_MASK;
+	armv6_pmcr_write(val);
+}
+
+static inline unsigned int armv6_pmu_control_read(void)
+{
+	u32 val;
+
+	asm volatile ("mrc   p15, 0, %0, c15, c12, 0":"=r" (val));
+	return val;
+}
+
+static inline void armv6_pmu_control_write(unsigned int setting)
+{
+	unsigned long val;
+
+	val = armv6_pmcr_read();
+	val |= setting;
+	armv6_pmcr_write(val);
+}
+
+static void armv6_pmu_hw_reset_all(void)
+{
+	unsigned long val;
+
+	val = armv6_pmcr_read();
+	val &= ~ARMV6_PMCR_ENABLE;	/* disable all counters */
+	val |= (ARMV6_PMCR_CTR01_RESET | ARMV6_PMCR_CCOUNT_RESET);	/* reset CCNT, PMNC1/2 counter to zero */
+	armv6_pmcr_write(val);
+
+	armv6_pmu_overflow();
+}
+
+static void armv6pmu_enable_event(int idx, unsigned short config)
+{
+	unsigned long val, mask, evt;
+
+	if (idx == ARMV6_CYCLE_COUNTER) {
+		mask = 0;
+		evt = ARMV6_PMCR_CCOUNT_IEN;
+	} else if (idx == ARMV6_COUNTER0) {
+		mask = ARMV6_PMCR_EVT_COUNT0_MASK;
+		evt = (config << ARMV6_PMCR_EVT_COUNT0_SHIFT) | ARMV6_PMCR_COUNT0_IEN;
+	} else if (idx == ARMV6_COUNTER1) {
+		mask = ARMV6_PMCR_EVT_COUNT1_MASK;
+		evt = (config << ARMV6_PMCR_EVT_COUNT1_SHIFT) | ARMV6_PMCR_COUNT1_IEN;
+	} else {
+		pr_debug("invalid counter number (%d)\n", idx);
+		return;
+	}
+
+	/*
+	 * Mask out the current event and set the counter to count the event
+	 * that we're interested in.
+	 */
+	val = armv6_pmcr_read();
+	val &= ~mask;
+	val |= evt;
+	armv6_pmcr_write(val);
+}
+
+static int armv6_pmu_hw_get_event_desc(int i, int event, char *event_desc)
+{
+	if (event_desc == NULL)
+		return -1;
+
+	for (i = 0; i < chip->count; i++) {
+		if (chip->desc[i].event == event) {
+			strncpy(event_desc, chip->desc[i].name, MXSIZE_PMU_DESC - 1);
+			break;
+		}
+	}
+
+	if (i == chip->count)
+		return -1;
+
+	return 0;
+}
+
+static int armv6_pmu_hw_check_event(struct met_pmu *pmu, int idx, int event)
+{
+	int i;
+
+	/* Check if event is duplicate */
+	for (i = 0; i < idx; i++) {
+		if (pmu[i].event == event)
+			break;
+	}
+	if (i < idx) {
+		/* pr_debug("++++++ found duplicate event 0x%02x i=%d\n", event, i); */
+		return -1;
+	}
+
+	for (i = 0; i < chip->count; i++) {
+		if (chip->desc[i].event == event)
+			break;
+	}
+
+	if (i == chip->count)
+		return -1;
+
+	return 0;
+}
+
+static void armv6_pmu_hw_start(struct met_pmu *pmu, int count)
+{
+	int i;
+	int generic = count - 1;
+
+	armv6_pmu_hw_reset_all();
+
+	for (i = 0; i < generic; i++) {
+		if (pmu[i].mode == MODE_POLLING)
+			armv6pmu_enable_event(i, pmu[i].event);
+	}
+
+	if (pmu[count - 1].mode == MODE_POLLING)
+		armv6pmu_enable_event(2, pmu[2].event);
+
+	armv6_pmu_control_write(ARMV6_PMCR_ENABLE);
+}
+
+static void armv6_pmu_hw_stop(int count)
+{
+	armv6_pmu_hw_reset_all();
+}
+
+static unsigned int armv6_pmu_hw_polling(struct met_pmu *pmu, int count, unsigned int *pmu_value)
+{
+	int i, cnt = 0;
+	int generic = count - 1;
+
+	for (i = 0; i < generic; i++) {
+		if (pmu[i].mode == MODE_POLLING) {
+			pmu_value[cnt] = armv6_pmu_read_count(i);
+			cnt++;
+		}
+	}
+
+	if (pmu[count - 1].mode == MODE_POLLING) {
+		pmu_value[cnt] = armv6_pmu_read_count(2);
+		cnt++;
+	}
+
+	armv6_pmu_control_write(ARMV6_PMCR_ENABLE | ARMV6_PMCR_CTR01_RESET |
+				ARMV6_PMCR_CCOUNT_RESET);
+
+	return cnt;
+}
+
+struct cpu_pmu_hw armv6_pmu = {
+	.name = "armv6_pmu",
+	.get_event_desc = armv6_pmu_hw_get_event_desc,
+	.check_event = armv6_pmu_hw_check_event,
+	.start = armv6_pmu_hw_start,
+	.stop = armv6_pmu_hw_stop,
+	.polling = armv6_pmu_hw_polling,
+};
+
+struct cpu_pmu_hw *v6_cpu_pmu_hw_init(int typeid)
+{
+	int i;
+
+	for (i = 0; i < CHIP_PMU_COUNT; i++) {
+		if (chips[i].type == typeid) {
+			chip = &(chips[i]);
+
+			break;
+		}
+	}
+
+	if (chip == NULL) {
+		chip = &chip_unknown;
+
+		return NULL;
+	}
+
+	armv6_pmu.nr_cnt = 3;
+	armv6_pmu.cpu_name = chip->cpu_name;
+
+	return &armv6_pmu;
+}
diff --git a/src/devtools/met-driver/4.19/mt2712/v6_pmu_hw.h b/src/devtools/met-driver/4.19/mt2712/v6_pmu_hw.h
new file mode 100644
index 0000000..a532f13
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/v6_pmu_hw.h
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+
+#ifndef __V6_PMU_HW_H__
+#define __V6_PMU_HW_H__
+
+extern struct cpu_pmu_hw armv6_pmu;
+extern struct cpu_pmu_hw *v6_cpu_pmu_hw_init(int typeid);
+
+#endif
diff --git a/src/devtools/met-driver/4.19/mt2712/v6_pmu_name.h b/src/devtools/met-driver/4.19/mt2712/v6_pmu_name.h
new file mode 100644
index 0000000..c1cdd91
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/v6_pmu_name.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+
+#ifndef _V6_PMU_NAME_H_
+#define _V6_PMU_NAME_H_
+
+/* ARM11 */
+struct pmu_desc arm11_pmu_desc[] = {
+	{0x00, "ICACHE_MISS"},
+	{0x01, "IBUF_STALL"},
+	{0x02, "DDEP_STALL"},
+	{0x03, "ITLB_MISS"},
+	{0x04, "DTLB_MISS"},
+	{0x05, "BR_EXEC"},
+	{0x06, "BR_MISPREDICT"},
+	{0x07, "CPU_INST"},
+	{0x09, "DCACHE_HIT"},
+	{0x0A, "L1D_CACHE"},
+	{0x0B, "L1D_CACHE_REFILL"},
+	{0x0C, "DCACHE_WBACK"},
+	{0x0D, "SW_PC_CHANGE"},
+	{0x0F, "MAIN_TLB_MISS"},
+	{0x10, "EXPL_D_ACCESS"},
+	{0x11, "LSU_FULL_STALL"},
+	{0x12, "WBUF_DRAINED"},
+	{0xFF, "CPU_CYCLES"},
+	{0x20, "NOP"},
+};
+
+#define ARM11_PMU_DESC_COUNT (sizeof(arm11_pmu_desc) / sizeof(struct pmu_desc))
+
+#endif				/* _V6_PMU_NAME_H_ */
diff --git a/src/devtools/met-driver/4.19/mt2712/v7_pmu_hw.c b/src/devtools/met-driver/4.19/mt2712/v7_pmu_hw.c
new file mode 100644
index 0000000..161bf22
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/v7_pmu_hw.c
@@ -0,0 +1,303 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/* include <asm/system.h> */
+#include <linux/smp.h>
+#include "cpu_pmu.h"
+#include "v6_pmu_hw.h"
+#include "v7_pmu_name.h"
+#include "v8_pmu_name.h"	/* for 32-bit build of arm64 cpu */
+
+#define ARMV7_PMCR_E		(1 << 0)	/* enable all counters */
+#define ARMV7_PMCR_P		(1 << 1)
+#define ARMV7_PMCR_C		(1 << 2)
+#define ARMV7_PMCR_D		(1 << 3)
+#define ARMV7_PMCR_X		(1 << 4)
+#define ARMV7_PMCR_DP		(1 << 5)
+#define ARMV7_PMCR_N_SHIFT		11	/* Number of counters supported */
+#define ARMV7_PMCR_N_MASK		0x1f
+#define ARMV7_PMCR_MASK			0x3f	/* mask for writable bits */
+
+
+enum ARM_TYPE {
+	CORTEX_A7 = 0xC07,
+	CORTEX_A9 = 0xC09,
+	CORTEX_A12 = 0xC0D,
+	CORTEX_A15 = 0xC0F,
+	CORTEX_A17 = 0xC0E,
+	CORTEX_A53 = 0xD03,
+	CORTEX_A57 = 0xD07,
+	CHIP_UNKNOWN = 0xFFF
+};
+
+struct chip_pmu {
+	enum ARM_TYPE type;
+	struct pmu_desc *desc;
+	unsigned int count;
+	const char *cpu_name;
+};
+
+static struct chip_pmu chips[] = {
+	{CORTEX_A7, a7_pmu_desc, A7_PMU_DESC_COUNT, "Cortex-A7"},
+	{CORTEX_A9, a9_pmu_desc, A9_PMU_DESC_COUNT, "Cortex-A9"},
+	{CORTEX_A12, a7_pmu_desc, A7_PMU_DESC_COUNT, "Cortex-A12"},
+	{CORTEX_A15, a7_pmu_desc, A7_PMU_DESC_COUNT, "Cortex-A15"},
+	{CORTEX_A17, a7_pmu_desc, A7_PMU_DESC_COUNT, "Cortex-A17"},
+	{CORTEX_A53, a53_pmu_desc, A53_PMU_DESC_COUNT, "Cortex-A53"},
+	{CORTEX_A57, a7_pmu_desc, A7_PMU_DESC_COUNT, "Cortex-A57"},
+};
+static struct chip_pmu chip_unknown = { CHIP_UNKNOWN, NULL, 0, "Unknown CPU" };
+
+#define CHIP_PMU_COUNT (sizeof(chips) / sizeof(struct chip_pmu))
+
+static struct chip_pmu *chip;
+
+static enum ARM_TYPE armv7_get_ic(void)
+{
+	unsigned int value;
+	/* Read Main ID Register */
+	asm volatile ("mrc p15, 0, %0, c0, c0, 0":"=r" (value));
+
+	value = (value & 0xffff) >> 4;	/* primary part number */
+	return value;
+}
+
+static inline void armv7_pmu_counter_select(unsigned int idx)
+{
+	asm volatile ("mcr p15, 0, %0, c9, c12, 5"::"r" (idx));
+	isb();
+}
+
+static inline void armv7_pmu_type_select(unsigned int idx, unsigned int type)
+{
+	armv7_pmu_counter_select(idx);
+	asm volatile ("mcr p15, 0, %0, c9, c13, 1"::"r" (type));
+}
+
+static inline unsigned int armv7_pmu_read_count(unsigned int idx)
+{
+	unsigned int value;
+
+	if (idx == 31) {
+		asm volatile ("mrc p15, 0, %0, c9, c13, 0":"=r" (value));
+	} else {
+		armv7_pmu_counter_select(idx);
+		asm volatile ("mrc p15, 0, %0, c9, c13, 2":"=r" (value));
+	}
+	return value;
+}
+
+static inline void armv7_pmu_write_count(int idx, u32 value)
+{
+	if (idx == 31) {
+		asm volatile ("mcr p15, 0, %0, c9, c13, 0"::"r" (value));
+	} else {
+		armv7_pmu_counter_select(idx);
+		asm volatile ("mcr p15, 0, %0, c9, c13, 2"::"r" (value));
+	}
+}
+
+static inline void armv7_pmu_enable_count(unsigned int idx)
+{
+	asm volatile ("mcr p15, 0, %0, c9, c12, 1"::"r" (1 << idx));
+}
+
+static inline void armv7_pmu_disable_count(unsigned int idx)
+{
+	asm volatile ("mcr p15, 0, %0, c9, c12, 2"::"r" (1 << idx));
+}
+
+static inline void armv7_pmu_enable_intr(unsigned int idx)
+{
+	asm volatile ("mcr p15, 0, %0, c9, c14, 1"::"r" (1 << idx));
+}
+
+static inline void armv7_pmu_disable_intr(unsigned int idx)
+{
+	asm volatile ("mcr p15, 0, %0, c9, c14, 2"::"r" (1 << idx));
+}
+
+static inline unsigned int armv7_pmu_overflow(void)
+{
+	unsigned int val;
+
+	asm volatile ("mrc p15, 0, %0, c9, c12, 3":"=r" (val));	/* read */
+	asm volatile ("mcr p15, 0, %0, c9, c12, 3"::"r" (val));
+	return val;
+}
+
+static inline unsigned int armv7_pmu_control_read(void)
+{
+	u32 val;
+
+	asm volatile ("mrc p15, 0, %0, c9, c12, 0":"=r" (val));
+	return val;
+}
+
+static inline void armv7_pmu_control_write(unsigned int val)
+{
+	val &= ARMV7_PMCR_MASK;
+	isb();
+	asm volatile ("mcr p15, 0, %0, c9, c12, 0"::"r" (val));
+}
+
+static int armv7_pmu_hw_get_counters(void)
+{
+	int count = armv7_pmu_control_read();
+	/* N, bits[15:11] */
+	count = ((count >> ARMV7_PMCR_N_SHIFT) & ARMV7_PMCR_N_MASK);
+	return count;
+}
+
+static void armv7_pmu_hw_reset_all(int generic_counters)
+{
+	int i;
+
+	armv7_pmu_control_write(ARMV7_PMCR_C | ARMV7_PMCR_P);
+	/* generic counter */
+	for (i = 0; i < generic_counters; i++) {
+		armv7_pmu_disable_intr(i);
+		armv7_pmu_disable_count(i);
+	}
+	/* cycle counter */
+	armv7_pmu_disable_intr(31);
+	armv7_pmu_disable_count(31);
+	armv7_pmu_overflow();	/* clear overflow */
+}
+
+static int armv7_pmu_hw_get_event_desc(int i, int event, char *event_desc)
+{
+	if (event_desc == NULL)
+		return -1;
+
+	for (i = 0; i < chip->count; i++) {
+		if (chip->desc[i].event == event) {
+			strncpy(event_desc, chip->desc[i].name, MXSIZE_PMU_DESC - 1);
+			break;
+		}
+	}
+	if (i == chip->count)
+		return -1;
+
+	return 0;
+}
+
+static int armv7_pmu_hw_check_event(struct met_pmu *pmu, int idx, int event)
+{
+	int i;
+
+	/* Check if event is duplicate */
+	for (i = 0; i < idx; i++) {
+		if (pmu[i].event == event)
+			break;
+	}
+	if (i < idx) {
+		/* pr_debug("++++++ found duplicate event 0x%02x i=%d\n", event, i); */
+		return -1;
+	}
+
+	for (i = 0; i < chip->count; i++) {
+		if (chip->desc[i].event == event)
+			break;
+	}
+
+	if (i == chip->count)
+		return -1;
+
+	return 0;
+}
+
+static void armv7_pmu_hw_start(struct met_pmu *pmu, int count)
+{
+	int i;
+	int generic = count - 1;
+
+	armv7_pmu_hw_reset_all(generic);
+	for (i = 0; i < generic; i++) {
+		if (pmu[i].mode == MODE_POLLING) {
+			armv7_pmu_type_select(i, pmu[i].event);
+			armv7_pmu_enable_count(i);
+		}
+	}
+	if (pmu[count - 1].mode == MODE_POLLING) {	/* cycle counter */
+		armv7_pmu_enable_count(31);
+	}
+	armv7_pmu_control_write(ARMV7_PMCR_E);
+}
+
+static void armv7_pmu_hw_stop(int count)
+{
+	int generic = count - 1;
+
+	armv7_pmu_hw_reset_all(generic);
+}
+
+static unsigned int armv7_pmu_hw_polling(struct met_pmu *pmu, int count, unsigned int *pmu_value)
+{
+	int i, cnt = 0;
+	int generic = count - 1;
+
+	for (i = 0; i < generic; i++) {
+		if (pmu[i].mode == MODE_POLLING) {
+			pmu_value[cnt] = armv7_pmu_read_count(i);
+			cnt++;
+		}
+	}
+	if (pmu[count - 1].mode == MODE_POLLING) {
+		pmu_value[cnt] = armv7_pmu_read_count(31);
+		cnt++;
+	}
+	armv7_pmu_control_write(ARMV7_PMCR_C | ARMV7_PMCR_P | ARMV7_PMCR_E);
+
+	return cnt;
+}
+
+
+struct cpu_pmu_hw armv7_pmu = {
+	.name = "armv7_pmu",
+	.get_event_desc = armv7_pmu_hw_get_event_desc,
+	.check_event = armv7_pmu_hw_check_event,
+	.start = armv7_pmu_hw_start,
+	.stop = armv7_pmu_hw_stop,
+	.polling = armv7_pmu_hw_polling,
+};
+
+struct cpu_pmu_hw *cpu_pmu_hw_init(void)
+{
+	int i;
+	enum ARM_TYPE type;
+	struct cpu_pmu_hw *pmu = NULL;
+
+	type = armv7_get_ic();
+	for (i = 0; i < CHIP_PMU_COUNT; i++) {
+		if (chips[i].type == type) {
+			chip = &(chips[i]);
+			break;
+		}
+	}
+
+	if (chip != NULL) {
+		armv7_pmu.nr_cnt = armv7_pmu_hw_get_counters() + 1;
+		armv7_pmu.cpu_name = chip->cpu_name;
+		pmu = &armv7_pmu;
+	} else {
+		pmu = v6_cpu_pmu_hw_init(type);
+	}
+
+	if ((chip == NULL) && (pmu == NULL)) {
+		chip = &chip_unknown;
+		return NULL;
+	}
+
+	return pmu;
+}
diff --git a/src/devtools/met-driver/4.19/mt2712/v7_pmu_name.h b/src/devtools/met-driver/4.19/mt2712/v7_pmu_name.h
new file mode 100644
index 0000000..3219cd9
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/v7_pmu_name.h
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _V7_PMU_NAME_H_
+#define _V7_PMU_NAME_H_
+
+/* Cortex-A7 */
+struct pmu_desc a7_pmu_desc[] = {
+	{0x00, "SW_INCR"},
+	{0x01, "L1I_CACHE_REFILL"},
+	{0x02, "L1I_TLB_REFILL"},
+	{0x03, "L1D_CACHE_REFILL"},
+	{0x04, "L1D_CACHE"},
+	{0x05, "L1D_TLB_REFILL"},
+	{0x06, "LD_RETIRED"},
+	{0x07, "ST_RETIRED"},
+	/* {0x08, "INST_RETIRED"}, */
+	{0x08, "CPU_INST"},
+	{0x09, "EXC_TAKEN"},
+	{0x0A, "EXC_RETURN"},
+	{0x0B, "CID_WRITE_RETIRED"},
+	{0x0C, "PC_WRITE_RETIRED"},
+	{0x0D, "BR_IMMED_RETIRED"},
+	{0x0E, "BR_RETURN_RETIRED"},
+	{0x0F, "UNALIGNED_LDST_RETIRED"},
+	{0x10, "BR_MIS_PRED"},
+	{0x12, "BR_PRED"},
+	{0x13, "MEM_ACCESS"},
+	{0x14, "L1I_CACHE"},
+	{0x15, "L1D_CACHE_WB"},
+	{0x16, "L2D_CACHE"},
+	{0x17, "L2D_CACHE_REFILL"},
+	{0x18, "L2D_CACHE_WB"},
+	{0x19, "BUS_ACCESS"},
+	{0x1D, "BUS_CYCLES"},
+	{0x60, "BUS_READ_ACCESS"},
+	{0x61, "BUS_WRITE_ACCESS"},
+	{0x86, "IRQ_EXC_TAKEN"},
+	{0x87, "FIQ_EXC_TAKEN"},
+	{0xC0, "EXT_MEM_REQ"},
+	{0xC1, "NO_CACHE_EXT_MEM_REQ"},
+	{0xC2, "PREFETCH_LINEFILL"},
+	{0xC3, "PREFETCH_LINEFILL_DROPPED"},
+	{0xC4, "ENT_READ_ALLOC_MODE"},
+	{0xC5, "READ_ALLOC_MODE"},
+	{0xC7, "ETM_EXT_OUT0"},
+	{0xC8, "ETM_EXT_OUT1"},
+	{0xC9, "DATA_WRITE_STALL"},
+	{0xCA, "DATA_READ_SNOOP_CLUSTER"},
+	{0xFF, "CPU_CYCLES"}
+};
+
+/* Cortex-A9 */
+struct pmu_desc a9_pmu_desc[] = {
+	{0x00, "SW_INCR"},
+	{0x01, "L1I_CACHE_REFILL"},
+	{0x02, "L1I_TLB_REFILL"},
+	{0x03, "L1D_CACHE_REFILL"},
+	{0x04, "L1D_CACHE"},
+	{0x05, "L1D_TLB_REFILL"},
+	{0x06, "LD_RETIRED"},
+	{0x07, "ST_RETIRED"},
+	{0x09, "EXC_TAKEN"},
+	{0x0A, "EXC_RETURN"},
+	{0x0B, "CID_WRITE_RETIRED"},
+	{0x0C, "PC_WRITE_RETIRED"},
+	{0x0D, "BR_IMMED_RETIRED"},
+	{0x0F, "UNALIGNED_LDST_RETIRED"},
+	{0x10, "BR_MIS_PRED"},
+	{0x12, "BR_PRED"},
+	{0x40, "JAVA_BC_EXEC"},
+	{0x41, "SW_JAVA_BC_EXEC"},
+	{0x42, "JAZELLE_BB_EXEC"},
+	{0x50, "CO_LF_MISS"},
+	{0x51, "CO_LF_HIT"},
+	{0x60, "ICACHE_DEP_STALL"},
+	{0x61, "DCACHE_DEP_STALL"},
+	{0x62, "M_TLB_STALL"},
+	{0x63, "STREX_PASSED"},
+	{0x64, "STREX_FAILED"},
+	{0x65, "DATA_EVICT"},
+	{0x66, "ISSUE_NO_DISP"},
+	{0x67, "ISSUE_EMPTY"},
+	/* {0x68, "INS_RENAME"}, */
+	{0x68, "CPU_INST"},
+	{0x6E, "PRED_FN_RET"},
+	{0x70, "MAIN_EXEC_INST"},
+	{0x71, "SEC_EXEC_INST"},
+	{0x72, "LOAD_STORE_INST"},
+	{0x73, "FLOAT_INST_RR"},
+	{0x74, "NEON_INST_RR"},
+	{0x80, "STALL_PLD"},
+	{0x81, "STALL_WRITE"},
+	{0x82, "STALL_INST_M_TLB_MISS"},
+	{0x83, "STALL_DATA_M_TLB_MISS"},
+	{0x84, "STALL_INST_U_TLB"},
+	{0x85, "STALL_DATA_U_TLB"},
+	{0x86, "STALL_DMB"},
+	{0x8A, "INT_CLK_EN"},
+	{0x8B, "DATA_E_CLK_EN"},
+	{0x90, "ISB_INST"},
+	{0x91, "DSB_INST"},
+	{0x92, "INS_DMB"},
+	{0x93, "EXT_IRQ"},
+	{0xA0, "PLE_CACHE_REQ_COMP"},
+	{0xA1, "PLE_CACHE_REQ_SKP"},
+	{0xA2, "PLE_FIFO_FLUSH"},
+	{0xA3, "PLE_REQ_COMP"},
+	{0xA4, "PLE_FIFO_OF"},
+	{0xA5, "PLE_REQ_PRG"},
+	{0xFF, "CPU_CYCLES"}
+};
+
+#define A7_PMU_DESC_COUNT (sizeof(a7_pmu_desc) / sizeof(struct pmu_desc))
+#define A9_PMU_DESC_COUNT (sizeof(a9_pmu_desc) / sizeof(struct pmu_desc))
+
+#endif				/* _V7_PMU_NAME_H_ */
diff --git a/src/devtools/met-driver/4.19/mt2712/v8_pmu_hw.c b/src/devtools/met-driver/4.19/mt2712/v8_pmu_hw.c
new file mode 100644
index 0000000..6a286af
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/v8_pmu_hw.c
@@ -0,0 +1,304 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/smp.h>
+#include "interface.h"
+#include "cpu_pmu.h"
+#include "v8_pmu_name.h"
+
+/*
+ * Per-CPU PMCR: config reg
+ */
+#define ARMV8_PMCR_E		(1 << 0)	/* Enable all counters */
+#define ARMV8_PMCR_P		(1 << 1)	/* Reset all counters */
+#define ARMV8_PMCR_C		(1 << 2)	/* Cycle counter reset */
+#define ARMV8_PMCR_D		(1 << 3)	/* CCNT counts every 64th cpu cycle */
+#define ARMV8_PMCR_X		(1 << 4)	/* Export to ETM */
+#define ARMV8_PMCR_DP		(1 << 5)	/* Disable CCNT if non-invasive debug */
+#define	ARMV8_PMCR_N_SHIFT	11	/* Number of counters supported */
+#define	ARMV8_PMCR_N_MASK	0x1f
+#define	ARMV8_PMCR_MASK		0x3f	/* Mask for writable bits */
+
+/*
+ * PMOVSR: counters overflow flag status reg
+ */
+#define	ARMV8_OVSR_MASK		0xffffffff	/* Mask for writable bits */
+#define	ARMV8_OVERFLOWED_MASK	ARMV8_OVSR_MASK
+
+
+enum ARM_TYPE {
+	CORTEX_A53 = 0xD03,
+	CORTEX_A35 = 0xD04,
+	CORTEX_A57 = 0xD07,
+	CORTEX_A72 = 0xD08,
+	CORTEX_A73 = 0xD09,
+	CHIP_UNKNOWN = 0xFFF
+};
+
+struct chip_pmu {
+	enum ARM_TYPE type;
+	struct pmu_desc *desc;
+	unsigned int count;
+	const char *cpu_name;
+};
+
+static struct chip_pmu chips[] = {
+	{CORTEX_A53, a53_pmu_desc, A53_PMU_DESC_COUNT, "Cortex-A53"},
+	{CORTEX_A35, a53_pmu_desc, A53_PMU_DESC_COUNT, "Cortex-A35"},
+	{CORTEX_A57, a53_pmu_desc, A53_PMU_DESC_COUNT, "Cortex-A57"},
+	{CORTEX_A72, a53_pmu_desc, A53_PMU_DESC_COUNT, "Cortex-A72"},
+	{CORTEX_A73, a53_pmu_desc, A53_PMU_DESC_COUNT, "Cortex-A73"},
+};
+static struct chip_pmu chip_unknown = { CHIP_UNKNOWN, NULL, 0, "Unknown CPU" };
+
+#define CHIP_PMU_COUNT (sizeof(chips) / sizeof(struct chip_pmu))
+
+static struct chip_pmu *chip;
+
+static enum ARM_TYPE armv8_get_ic(void)
+{
+	unsigned int value;
+	/* Read Main ID Register */
+	asm volatile ("mrs %x0, midr_el1":"=r"(value));
+
+	value = (value & 0xffff) >> 4;	/* primary part number */
+	return value;
+}
+
+static inline void armv8_pmu_counter_select(unsigned int idx)
+{
+	asm volatile ("msr pmselr_el0, %x0"::"r" (idx));
+	isb();
+}
+
+static inline void armv8_pmu_type_select(unsigned int idx, unsigned int type)
+{
+	armv8_pmu_counter_select(idx);
+	asm volatile ("msr pmxevtyper_el0, %x0"::"r" (type));
+}
+
+static inline unsigned int armv8_pmu_read_count(unsigned int idx)
+{
+	unsigned int value;
+
+	if (idx == 31) {
+		asm volatile ("mrs %x0, pmccntr_el0":"=r" (value));
+	} else {
+		armv8_pmu_counter_select(idx);
+		asm volatile ("mrs %x0, pmxevcntr_el0":"=r" (value));
+	}
+	return value;
+}
+
+static inline void armv8_pmu_write_count(int idx, u32 value)
+{
+	if (idx == 31) {
+		asm volatile ("msr pmccntr_el0, %x0"::"r" (value));
+	} else {
+		armv8_pmu_counter_select(idx);
+		asm volatile ("msr pmxevcntr_el0, %x0"::"r" (value));
+	}
+}
+
+static inline void armv8_pmu_enable_count(unsigned int idx)
+{
+	asm volatile ("msr pmcntenset_el0, %x0"::"r" (1 << idx));
+}
+
+static inline void armv8_pmu_disable_count(unsigned int idx)
+{
+	asm volatile ("msr pmcntenclr_el0, %x0"::"r" (1 << idx));
+}
+
+static inline void armv8_pmu_enable_intr(unsigned int idx)
+{
+	asm volatile ("msr pmintenset_el1, %x0"::"r" (1 << idx));
+}
+
+static inline void armv8_pmu_disable_intr(unsigned int idx)
+{
+	asm volatile ("msr pmintenclr_el1, %x0"::"r" (1 << idx));
+	isb();
+	asm volatile ("msr pmovsclr_el0, %x0"::"r" (1 << idx));
+	isb();
+}
+
+static inline unsigned int armv8_pmu_overflow(void)
+{
+	unsigned int val;
+
+	asm volatile ("mrs %x0, pmovsclr_el0":"=r" (val));	/* read */
+	val &= ARMV8_OVSR_MASK;
+	asm volatile ("mrs %x0, pmovsclr_el0"::"r" (val));
+	return val;
+}
+
+static inline unsigned int armv8_pmu_control_read(void)
+{
+	unsigned int val;
+
+	asm volatile ("mrs %x0, pmcr_el0":"=r" (val));
+	return val;
+}
+
+static inline void armv8_pmu_control_write(u32 val)
+{
+	val &= ARMV8_PMCR_MASK;
+	isb();
+	asm volatile ("msr pmcr_el0, %x0"::"r" (val));
+}
+
+static int armv8_pmu_hw_get_counters(void)
+{
+	int count = armv8_pmu_control_read();
+	/* N, bits[15:11] */
+	count = ((count >> ARMV8_PMCR_N_SHIFT) & ARMV8_PMCR_N_MASK);
+	return count;
+}
+
+static void armv8_pmu_hw_reset_all(int generic_counters)
+{
+	int i;
+
+	armv8_pmu_control_write(ARMV8_PMCR_C | ARMV8_PMCR_P);
+	/* generic counter */
+	for (i = 0; i < generic_counters; i++) {
+		armv8_pmu_disable_intr(i);
+		armv8_pmu_disable_count(i);
+	}
+	/* cycle counter */
+	armv8_pmu_disable_intr(31);
+	armv8_pmu_disable_count(31);
+	armv8_pmu_overflow();	/* clear overflow */
+}
+
+static int armv8_pmu_hw_get_event_desc(int i, int event, char *event_desc)
+{
+	if (event_desc == NULL)
+		return -1;
+
+	for (i = 0; i < chip->count; i++) {
+		if (chip->desc[i].event == event) {
+			strncpy(event_desc, chip->desc[i].name, MXSIZE_PMU_DESC - 1);
+			break;
+		}
+	}
+	if (i == chip->count)
+		return -1;
+
+	return 0;
+}
+
+static int armv8_pmu_hw_check_event(struct met_pmu *pmu, int idx, int event)
+{
+	int i;
+
+	/* Check if event is duplicate */
+	for (i = 0; i < idx; i++) {
+		if (pmu[i].event == event)
+			break;
+	}
+	if (i < idx) {
+		/* pr_debug("++++++ found duplicate event 0x%02x i=%d\n", event, i); */
+		return -1;
+	}
+
+	for (i = 0; i < chip->count; i++) {
+		if (chip->desc[i].event == event)
+			break;
+	}
+
+	if (i == chip->count)
+		return -1;
+
+	return 0;
+}
+
+static void armv8_pmu_hw_start(struct met_pmu *pmu, int count)
+{
+	int i;
+	int generic = count - 1;
+
+	armv8_pmu_hw_reset_all(generic);
+	for (i = 0; i < generic; i++) {
+		if (pmu[i].mode == MODE_POLLING) {
+			armv8_pmu_type_select(i, pmu[i].event);
+			armv8_pmu_enable_count(i);
+		}
+	}
+	if (pmu[count - 1].mode == MODE_POLLING) {	/* cycle counter */
+		armv8_pmu_enable_count(31);
+	}
+	armv8_pmu_control_write(ARMV8_PMCR_E);
+}
+
+static void armv8_pmu_hw_stop(int count)
+{
+	int generic = count - 1;
+
+	armv8_pmu_hw_reset_all(generic);
+}
+
+static unsigned int armv8_pmu_hw_polling(struct met_pmu *pmu, int count, unsigned int *pmu_value)
+{
+	int i, cnt = 0;
+	int generic = count - 1;
+
+	for (i = 0; i < generic; i++) {
+		if (pmu[i].mode == MODE_POLLING) {
+			pmu_value[cnt] = armv8_pmu_read_count(i);
+			cnt++;
+		}
+	}
+	if (pmu[count - 1].mode == MODE_POLLING) {
+		pmu_value[cnt] = armv8_pmu_read_count(31);
+		cnt++;
+	}
+	armv8_pmu_control_write(ARMV8_PMCR_C | ARMV8_PMCR_P | ARMV8_PMCR_E);
+
+	return cnt;
+}
+
+
+struct cpu_pmu_hw armv8_pmu = {
+	.name = "armv8_pmu",
+	.get_event_desc = armv8_pmu_hw_get_event_desc,
+	.check_event = armv8_pmu_hw_check_event,
+	.start = armv8_pmu_hw_start,
+	.stop = armv8_pmu_hw_stop,
+	.polling = armv8_pmu_hw_polling,
+};
+
+struct cpu_pmu_hw *cpu_pmu_hw_init(void)
+{
+	int i;
+	enum ARM_TYPE type;
+
+	type = armv8_get_ic();
+	PR_BOOTMSG("CPU TYPE - v8: %x\n", (unsigned int)type);
+	for (i = 0; i < CHIP_PMU_COUNT; i++) {
+		if (chips[i].type == type) {
+			chip = &(chips[i]);
+			break;
+		}
+	}
+	if (i == CHIP_PMU_COUNT) {
+		chip = &chip_unknown;
+		return NULL;
+	}
+
+	armv8_pmu.nr_cnt = armv8_pmu_hw_get_counters() + 1;
+	armv8_pmu.cpu_name = chip->cpu_name;
+
+	return &armv8_pmu;
+}
diff --git a/src/devtools/met-driver/4.19/mt2712/v8_pmu_hw_v2.c b/src/devtools/met-driver/4.19/mt2712/v8_pmu_hw_v2.c
new file mode 100644
index 0000000..c6d4b21
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/v8_pmu_hw_v2.c
@@ -0,0 +1,497 @@
+/*

+ * Copyright (C) 2018 MediaTek Inc.

+ *

+ * 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.

+ *

+ * This program is distributed in the hope that it will be useful,

+ * but WITHOUT ANY WARRANTY; without even the implied warranty of

+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the

+ * GNU General Public License for more details.

+ */

+

+#include <linux/smp.h> /* on_each_cpu */

+#include <linux/cpumask.h> /* for_each_possible_cpu(cpu) */

+

+#include "interface.h"

+#include "cpu_pmu_v2.h"

+#include "v8_pmu_hw_v2.h"

+#include "met_kernel_symbol.h"

+

+

+/*******************************************************************************

+*				Type Define

+*******************************************************************************/

+/*

+ * Per-CPU PMCR: config reg

+ */

+#define ARMV8_PMCR_E		(1 << 0)	/* Enable all counters */

+#define ARMV8_PMCR_P		(1 << 1)	/* Reset all counters */

+#define ARMV8_PMCR_C		(1 << 2)	/* Cycle counter reset */

+#define ARMV8_PMCR_D		(1 << 3)	/* CCNT counts every 64th cpu cycle */

+#define ARMV8_PMCR_X		(1 << 4)	/* Export to ETM */

+#define ARMV8_PMCR_DP		(1 << 5)	/* Disable CCNT if non-invasive debug */

+#define	ARMV8_PMCR_N_SHIFT	11		/* Number of counters supported */

+#define	ARMV8_PMCR_N_MASK	0x1f

+#define	ARMV8_PMCR_MASK		0x3f		/* Mask for writable bits */

+

+/*

+ * PMOVSR: counters overflow flag status reg

+ */

+#define	ARMV8_OVSR_MASK		0xffffffff	/* Mask for writable bits */

+#define	ARMV8_OVERFLOWED_MASK	ARMV8_OVSR_MASK

+

+

+/*******************************************************************************

+*				Fuction Pototypes

+*******************************************************************************/

+static int armv8_pmu_hw_get_event_desc(int event, char *event_desc);

+static int armv8_pmu_hw_check_event(struct met_pmu_v2 *pmu, int idx, int event);

+static void armv8_pmu_hw_start(struct met_pmu_v2 *pmu, int count);

+static void armv8_pmu_hw_stop(int count);

+static unsigned int armv8_pmu_hw_polling(struct met_pmu_v2 *pmu, int count, unsigned int *pmu_value);

+

+

+/*******************************************************************************

+*				Globe Variables

+*******************************************************************************/

+struct pmu_desc_v2 a53_pmu_desc_v2[] = {

+	{0x00, "SW_INCR"},

+	{0x01, "L1I_CACHE_REFILL"},

+	{0x02, "L1I_TLB_REFILL"},

+	{0x03, "L1D_CACHE_REFILL"},

+	{0x04, "L1D_CACHE"},

+	{0x05, "L1D_TLB_REFILL"},

+	{0x06, "LD_RETIRED"},

+	{0x07, "ST_RETIRED"},

+	{0x08, "INST_RETIRED"},

+	{0x09, "EXC_TAKEN"},

+	{0x0A, "EXC_RETURN"},

+	{0x0B, "CID_WRITE_RETIRED"},

+	{0x0C, "PC_WRITE_RETIRED"},

+	{0x0D, "BR_IMMED_RETIRED"},

+	{0x0E, "BR_RETURN_RETIRED"},

+	{0x0F, "UNALIGNED_LDST_RETIRED"},

+

+	{0x10, "BR_MIS_PRED"},

+	{0x11, "CPU_CYCLES"},

+	{0x12, "BR_PRED"},

+	{0x13, "MEM_ACCESS"},

+	{0x14, "L1I_CACHE"},

+	{0x15, "L1D_CACHE_WB"},

+	{0x16, "L2D_CACHE"},

+	{0x17, "L2D_CACHE_REFILL"},

+	{0x18, "L2D_CACHE_WB"},

+	{0x19, "BUS_ACCESS"},

+	{0x1A, "MEMORY_ERROR"},

+	{0x1D, "BUS_CYCLES"},

+	{0x1E, "CHAIN"},

+

+	{0x60, "BUS_READ_ACCESS"},

+	{0x61, "BUS_WRITE_ACCESS"},

+

+	{0x7A, "BR_INDIRECT_SPEC"},

+

+	{0x86, "IRQ_EXC_TAKEN"},

+	{0x87, "FIQ_EXC_TAKEN"},

+

+	{0xC0, "EXT_MEM_REQ"},

+	{0xC1, "NO_CACHE_EXT_MEM_REQ"},

+	{0xC2, "PREFETCH_LINEFILL"},

+	{0xC4, "ENT_READ_ALLOC_MODE"},

+	{0xC5, "READ_ALLOC_MODE"},

+	{0xC6, "PRE_DECODE_ERROR"},

+	{0xC7, "WRITE_STALL"},

+	{0xC8, "SCU_SNOOP_DATA_FROM_ANOTHER_CPU"},

+	{0xC9, "CONDITIONAL_BRANCH_EXE"},

+	{0xCA, "INDIRECT_BRANCH_MISPREDICT"},

+	{0xCB, "INDIRECT_BRANCH_MISPREDICT_ADDR"},/*"INDIRECT_BRANCH_MISPREDICT_ADDR_MISSCOMPARE" */

+	{0xCC, "COND_BRANCH_MISPREDICT"},

+

+	{0xD0, "L1_INST_CACHE_MEM_ERR"},

+

+	{0xE1, "ICACHE_MISS_STALL"},

+	{0xE2, "DPU_IQ_EMPTY"},

+	{0xE4, "NOT_FPU_NEON_INTERLOCK"},

+	{0xE5, "LOAD_STORE_INTERLOCK"},

+	{0xE6, "FPU_NEON_INTERLOCK"},

+	{0xE7, "LOAD_MISS_STALL"},

+	{0xE8, "STORE_STALL"},

+	{0xFF, "CPU_CYCLES"}

+};

+#define A53_PMU_DESC_COUNT (sizeof(a53_pmu_desc_v2) / sizeof(struct pmu_desc_v2))

+

+/* Cortex-A73 */

+struct pmu_desc_v2 a73_pmu_desc_v2[] = {

+	{0x00, "SW_INCR"},

+	{0x01, "L1I_CACHE_REFILL"},

+	{0x02, "L1I_TLB_REFILL"},

+	{0x03, "L1D_CACHE_REFILL"},

+	{0x04, "L1D_CACHE"},

+	{0x05, "L1D_TLB_REFILL"},

+	{0x08, "INST_RETIRED"},

+	{0x09, "EXC_TAKEN"},

+	{0x0A, "EXC_RETURN"},

+	{0x0B, "CID_WRITE_RETIRED"},

+	{0x0C, "PC_WRITE_RETIRED"},

+	{0x0D, "BR_IMMED_RETIRED"},

+	{0x0E, "BR_RETURN_RETIRED"},

+

+	{0x10, "BR_MIS_PRED"},

+	{0x11, "CPU_CYCLES"},

+	{0x12, "BR_PRED"},

+	{0x13, "MEM_ACCESS"},

+	{0x14, "L1I_CACHE"},

+	{0x15, "L1D_CACHE_WB"},

+	{0x16, "L2D_CACHE"},

+	{0x17, "L2D_CACHE_REFILL"},

+	{0x18, "L2D_CACHE_WB"},

+	{0x19, "BUS_ACCESS"},

+	{0x1B, "INT_SPEC"},

+	{0x1C, "TTBR_WRITE_RETIRED"},

+	{0x1D, "BUS_CYCLES"},

+	{0x1E, "CHAIN"},

+

+	{0x40, "L1D_CACHE_RD"},

+	{0x41, "L1D_CACHE_WR"},

+

+	{0x50, "L2D_CACHE_RD"},

+	{0x51, "L2D_CACHE_WR"},

+	{0x56, "L2D_CACHE_WB_VICTIM"},

+	{0x57, "L2D_CACHE_WB_CLEAN"},

+	{0x58, "L2D_CACHE_INVAL"},

+

+	{0x62, "BUS_ACCESS_SHARED"},

+	{0x63, "BUS_ACCESS_NOT_SHARED"},

+	{0x64, "BUS_ACCESS_NORMAL"},

+	{0x65, "BUS_ACCESS_SO_DIV"},

+	{0x66, "MEM_ACCESS_RD"},

+	{0x67, "MEM_ACCESS_WR"},

+	{0x6A, "UNALIGNED_LDST_SPEC"},

+	{0x6C, "LDREX_SPEC"},

+	{0x6E, "STREC_FAIL_SPEC"},

+

+	{0x70, "LD_SPEC"},

+	{0x71, "ST_SPEC"},

+	{0x72, "LDST_SPEC"},

+	{0x73, "DP_SPEC"},

+	{0x74, "ASE_SPEC"},

+	{0x75, "VFP_SPEC"},

+	{0x77, "CRYPTO_SPEC"},

+	{0x7A, "BR_INDIRECT_SPEC"},

+	{0x7C, "ISB_SPEC"},

+	{0x7D, "DSB_SPEC"},

+	{0x7E, "DMB_SPEC"},

+

+	{0x8A, "EXC_HVC"},

+

+	{0xC0, "LF_STALL"},

+	{0xC1, "PTW_STALL"},

+	{0xC2, "I_TAG_RAM_RD"},

+	{0xC3, "I_DATA_RAM_RD"},

+	{0xC4, "I_BTAC_RAM_RD"},

+

+	{0xD3, "D_LSU_SLOT_FULL"},

+	{0xD8, "LS_IQ_FULL"},

+	{0xD9, "DP_IQ_FULL"},

+	{0xDA, "DE_IQ_FULL"},

+	{0xDC, "EXC_TRAP_HYP"},

+	{0xDE, "ETM_EXT_OUT0"},

+	{0xDF, "ETM_EXT_OUT1"},

+

+	{0xE0, "MMU_PTW"},

+	{0xE1, "MMU_PTW_ST1"},

+	{0xE2, "MMU_PTW_ST2"},

+	{0xE3, "MMU_PTW_LSU"},

+	{0xE4, "MMU_PTW_ISIDE"},

+	{0xE5, "MMU_PTW_PLD"},

+	{0xE6, "MMU_PTW_CP15"},

+	{0xE7, "PLD_UTLB_REFILL"},

+	{0xE8, "CP15_UTLB_REFILL"},

+	{0xE9, "UTLB_FLUSH"},

+	{0xEA, "TLB_ACESS"},

+	{0xEB, "TLB_MISS"},

+	{0xEC, "DCACHE_SELF_HIT_VIPT"},

+        {0xEE, "CYCLES_L2_IDLE"},

+        {0xEF, "CPU_DECODE_UNIT_STALLED"},

+	{0xFF, "CPU_CYCLES"}

+};

+#define A73_PMU_DESC_COUNT (sizeof(a73_pmu_desc_v2) / sizeof(struct pmu_desc_v2))

+

+

+static struct chip_pmu_v2 *gChip[MXNR_CPU_V2];

+static struct chip_pmu_v2 chips[] = {

+	{CORTEX_A53, a53_pmu_desc_v2, A53_PMU_DESC_COUNT, 0, "Cortex-A53"},

+	{CORTEX_A35, a53_pmu_desc_v2, A53_PMU_DESC_COUNT, 0, "Cortex-A35"},

+	{CORTEX_A57, a53_pmu_desc_v2, A53_PMU_DESC_COUNT, 0, "Cortex-A57"},

+	{CORTEX_A72, a53_pmu_desc_v2, A53_PMU_DESC_COUNT, 0, "Cortex-A72"},

+	{CORTEX_A73, a73_pmu_desc_v2, A73_PMU_DESC_COUNT, 0, "Cortex-A73"},

+};

+static struct chip_pmu_v2 chip_unknown = { CHIP_UNKNOWN, NULL, 0, 0, "Unknown CPU" };

+#define CHIP_PMU_COUNT (sizeof(chips) / sizeof(struct chip_pmu_v2))

+

+struct cpu_pmu_hw_v2 armv8_pmu_v2 = {

+	.name = "armv8_pmu",

+	.get_event_desc = armv8_pmu_hw_get_event_desc,

+	.check_event = armv8_pmu_hw_check_event,

+	.start = armv8_pmu_hw_start,

+	.stop = armv8_pmu_hw_stop,

+	.polling = armv8_pmu_hw_polling,

+};

+

+

+/*******************************************************************************

+*				Iplement Start

+*******************************************************************************/

+static struct chip_pmu_v2 *get_chip_pmu_by_cpu_id(int cpu)

+{

+	return gChip[cpu];

+}

+

+static void set_chip_pmu_by_cpu_id(int cpu, struct chip_pmu_v2 *chip)

+{

+	gChip[cpu] = chip;

+}

+

+static inline void armv8_pmu_counter_select(unsigned int idx)

+{

+	asm volatile ("msr pmselr_el0, %x0"::"r" (idx));

+	isb();

+}

+

+static inline void armv8_pmu_type_select(unsigned int idx, unsigned int type)

+{

+	armv8_pmu_counter_select(idx);

+	asm volatile ("msr pmxevtyper_el0, %x0"::"r" (type));

+}

+

+static inline unsigned int armv8_pmu_read_count(unsigned int idx)

+{

+	unsigned int value;

+

+	if (idx == 31) {

+		asm volatile ("mrs %x0, pmccntr_el0":"=r" (value));

+	} else {

+		armv8_pmu_counter_select(idx);

+		asm volatile ("mrs %x0, pmxevcntr_el0":"=r" (value));

+	}

+	return value;

+}

+

+static inline void armv8_pmu_write_count(int idx, u32 value)

+{

+	if (idx == 31) {

+		asm volatile ("msr pmccntr_el0, %x0"::"r" (value));

+	} else {

+		armv8_pmu_counter_select(idx);

+		asm volatile ("msr pmxevcntr_el0, %x0"::"r" (value));

+	}

+}

+

+static inline void armv8_pmu_enable_count(unsigned int idx)

+{

+	asm volatile ("msr pmcntenset_el0, %x0"::"r" (1 << idx));

+}

+

+static inline void armv8_pmu_disable_count(unsigned int idx)

+{

+	asm volatile ("msr pmcntenclr_el0, %x0"::"r" (1 << idx));

+}

+

+static inline void armv8_pmu_enable_intr(unsigned int idx)

+{

+	asm volatile ("msr pmintenset_el1, %x0"::"r" (1 << idx));

+}

+

+static inline void armv8_pmu_disable_intr(unsigned int idx)

+{

+	asm volatile ("msr pmintenclr_el1, %x0"::"r" (1 << idx));

+	isb();

+	asm volatile ("msr pmovsclr_el0, %x0"::"r" (1 << idx));

+	isb();

+}

+

+static inline unsigned int armv8_pmu_overflow(void)

+{

+	unsigned int val;

+

+	asm volatile ("mrs %x0, pmovsclr_el0":"=r" (val));	/* read */

+	val &= ARMV8_OVSR_MASK;

+	asm volatile ("mrs %x0, pmovsclr_el0"::"r" (val));

+	return val;

+}

+

+static inline unsigned int armv8_pmu_control_read(void)

+{

+	unsigned int val;

+

+	asm volatile ("mrs %x0, pmcr_el0":"=r" (val));

+	return val;

+}

+

+static inline void armv8_pmu_control_write(u32 val)

+{

+	val &= ARMV8_PMCR_MASK;

+	isb();

+	asm volatile ("msr pmcr_el0, %x0"::"r" (val));

+}

+

+static int armv8_pmu_hw_get_counters(void)

+{

+	int count = armv8_pmu_control_read();

+	/* N, bits[15:11] */

+	count = ((count >> ARMV8_PMCR_N_SHIFT) & ARMV8_PMCR_N_MASK);

+	return count;

+}

+

+static void armv8_pmu_hw_reset_all(int generic_counters)

+{

+	int i;

+

+	armv8_pmu_control_write(ARMV8_PMCR_C | ARMV8_PMCR_P);

+	/* generic counter */

+	for (i = 0; i < generic_counters; i++) {

+		armv8_pmu_disable_intr(i);

+		armv8_pmu_disable_count(i);

+	}

+	/* cycle counter */

+	armv8_pmu_disable_intr(31);

+	armv8_pmu_disable_count(31);

+	armv8_pmu_overflow();	/* clear overflow */

+}

+

+static int armv8_pmu_hw_get_event_desc(int event, char *event_desc)

+{

+	int i;

+	int this_cpu;

+	struct chip_pmu_v2 *chip_pmu;

+

+	this_cpu = smp_processor_id();

+	chip_pmu = get_chip_pmu_by_cpu_id(this_cpu);

+	if (event_desc == NULL)

+		return -1;

+

+	for (i = 0; i < chip_pmu->pmu_count; i++) {

+		if (chip_pmu->desc[i].event == event) {

+			strncpy(event_desc, chip_pmu->desc[i].name, MXSIZE_PMU_DESC - 1);

+			break;

+		}

+	}

+	if (i == chip_pmu->pmu_count)

+		return -1;

+

+	return 0;

+}

+

+static int armv8_pmu_hw_check_event(struct met_pmu_v2 *pmu, int idx, int event)

+{

+	int this_cpu;

+	struct chip_pmu_v2 *chip_pmu;

+	int i;

+

+	this_cpu = smp_processor_id();

+	chip_pmu = get_chip_pmu_by_cpu_id(this_cpu);

+	for (i = 0; i < chip_pmu->pmu_count; i++) {

+		if (chip_pmu->desc[i].event == event)

+			break;

+	}

+

+	if (i == chip_pmu->pmu_count) {

+                PR_BOOTMSG("%s:%d => i=%d, pmu_count=%d\n", __FUNCTION__, __LINE__, i, chip_pmu->pmu_count);

+                return -1;

+        }

+

+	return 0;

+}

+

+static void armv8_pmu_hw_start(struct met_pmu_v2 *pmu, int count)

+{

+	int i;

+	int generic = count - 1;

+

+	armv8_pmu_hw_reset_all(generic);

+	for (i = 0; i < generic; i++) {

+		if (pmu[i].mode == MODE_POLLING) {

+			armv8_pmu_type_select(i, pmu[i].event);

+			armv8_pmu_enable_count(i);

+		}

+	}

+	if (pmu[count - 1].mode == MODE_POLLING)

+		armv8_pmu_enable_count(31);

+

+	armv8_pmu_control_write(ARMV8_PMCR_E);

+}

+

+static void armv8_pmu_hw_stop(int count)

+{

+	int generic = count - 1;

+

+	armv8_pmu_hw_reset_all(generic);

+}

+

+static unsigned int armv8_pmu_hw_polling(struct met_pmu_v2 *pmu, int count, unsigned int *pmu_value)

+{

+	int i, cnt = 0;

+	int generic = count - 1;

+

+	for (i = 0; i < generic; i++) {

+		if (pmu[i].mode == MODE_POLLING) {

+			pmu_value[cnt] = armv8_pmu_read_count(i);

+			cnt++;

+		}

+	}

+	if (pmu[count - 1].mode == MODE_POLLING) {

+		pmu_value[cnt] = armv8_pmu_read_count(31);

+		cnt++;

+	}

+	armv8_pmu_control_write(ARMV8_PMCR_C | ARMV8_PMCR_P | ARMV8_PMCR_E);

+

+	return cnt;

+}

+

+static void armv8_get_ic(void *info)

+{

+	unsigned int value;

+	unsigned int *type = (unsigned int *)info;

+

+	/* Read Main ID Register */

+	asm("mrs %x0, midr_el1":"=r"(value));

+	*type = (value & 0xffff) >> 4;	/* primary part number */

+}

+

+struct cpu_pmu_hw_v2 *cpu_pmu_hw_init_v2(void)

+{

+	enum ARM_TYPE_v2 type;

+	struct chip_pmu_v2 *chip;

+	int this_cpu = smp_processor_id();

+	int cpu;

+	int i;

+

+	for_each_possible_cpu(cpu) {

+		if (cpu == this_cpu)

+			armv8_get_ic(&type);

+		else

+			met_smp_call_function_single_symbol(cpu, armv8_get_ic, &type, 1);

+

+		PR_BOOTMSG("CPU TYPE - v8: %x\n", (unsigned int)type);

+		for (i = 0; i < CHIP_PMU_COUNT; i++) {

+			if (chips[i].type == type) {

+				chip = &(chips[i]);

+				chip->hw_count = armv8_pmu_hw_get_counters() + 1;

+				set_chip_pmu_by_cpu_id(cpu, chip);

+				armv8_pmu_v2.chip_pmu[cpu] = chip;

+				if (chip->hw_count >= armv8_pmu_v2.max_hw_count)

+					armv8_pmu_v2.max_hw_count = chip->hw_count;

+				break;

+			}

+		}

+		if (i == CHIP_PMU_COUNT) {

+			set_chip_pmu_by_cpu_id(cpu, &chip_unknown);

+			return NULL;

+		}

+	}

+

+	return &armv8_pmu_v2;

+}

+

diff --git a/src/devtools/met-driver/4.19/mt2712/v8_pmu_hw_v2.h b/src/devtools/met-driver/4.19/mt2712/v8_pmu_hw_v2.h
new file mode 100644
index 0000000..4666d0c
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/v8_pmu_hw_v2.h
@@ -0,0 +1,24 @@
+/*

+ * Copyright (C) 2018 MediaTek Inc.

+ *

+ * 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.

+ *

+ * This program is distributed in the hope that it will be useful,

+ * but WITHOUT ANY WARRANTY; without even the implied warranty of

+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the

+ * GNU General Public License for more details.

+ */

+

+#ifndef _V8_PMU_V2_NAME_H_

+#define _V8_PMU_V2_NAME_H_

+

+#include "cpu_pmu_v2.h"

+

+

+/*******************************************************************************

+*                                Fuction Pototypes

+*******************************************************************************/

+struct cpu_pmu_hw_v2 *cpu_pmu_hw_init_v2(void);

+#endif				/* _V8_PMU_V2_NAME_H_ */

diff --git a/src/devtools/met-driver/4.19/mt2712/v8_pmu_name.h b/src/devtools/met-driver/4.19/mt2712/v8_pmu_name.h
new file mode 100644
index 0000000..d3bf3b1
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/v8_pmu_name.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+
+#ifndef _V8_PMU_NAME_H_
+#define _V8_PMU_NAME_H_
+
+/* Cortex-A53 */
+/* CA53 & CA73 intersection event list */
+struct pmu_desc a53_pmu_desc[] = {
+	{0x00, "SW_INCR"},
+	{0x01, "L1I_CACHE_REFILL"},
+	{0x02, "L1I_TLB_REFILL"},
+	{0x03, "L1D_CACHE_REFILL"},
+	{0x04, "L1D_CACHE"},
+	{0x05, "L1D_TLB_REFILL"},
+/*	{0x06, "LD_RETIRED"}, */
+/*	{0x07, "ST_RETIRED"}, */
+	{0x08, "INST_RETIRED"},
+	{0x09, "EXC_TAKEN"},
+	{0x0A, "EXC_RETURN"},
+	{0x0B, "CID_WRITE_RETIRED"},
+/*	{0x0C, "PC_WRITE_RETIRED"}, */
+/*	{0x0D, "BR_IMMED_RETIRED"}, */
+/*	{0x0E, "BR_RETURN_RETIRED"}, */
+/*	{0x0F, "UNALIGNED_LDST_RETIRED"}, */
+	{0x10, "BR_MIS_PRED"},
+	{0x11, "CPU_CYCLES"},
+	{0x12, "BR_PRED"},
+	{0x13, "MEM_ACCESS"},
+	{0x14, "L1I_CACHE"},
+	{0x15, "L1D_CACHE_WB"},
+	{0x16, "L2D_CACHE"},
+	{0x17, "L2D_CACHE_REFILL"},
+	{0x18, "L2D_CACHE_WB"},
+	{0x19, "BUS_ACCESS"},
+	{0x1A, "MEMORY_ERROR"},
+	{0x1D, "BUS_CYCLES"},
+	{0x60, "BUS_READ_ACCESS"},
+	{0x61, "BUS_WRITE_ACCESS"},
+	{0x86, "IRQ_EXC_TAKEN"},
+	{0x87, "FIQ_EXC_TAKEN"},
+/*	{0xC0, "EXT_MEM_REQ"}, */
+/*	{0xC1, "NO_CACHE_EXT_MEM_REQ"}, */
+/*	{0xC2, "PREFETCH_LINEFILL"}, */
+/*	{0xC4, "ENT_READ_ALLOC_MODE"}, */
+/*	{0xC5, "READ_ALLOC_MODE"}, */
+/*	{0xC6, "PRE_DECODE_ERROR"}, */
+/*	{0xC7, "WRITE_STALL"}, */
+/*	{0xC8, "SCU_SNOOP_DATA_FROM_ANOTHER_CPU"}, */
+/*	{0xC9, "CONDITIONAL_BRANCH_EXE"}, */
+/*	{0xCA, "INDIRECT_BRANCH_MISPREDICT"}, */
+/*	{0xCB, "INDIRECT_BRANCH_MISPREDICT_ADDR"},"INDIRECT_BRANCH_MISPREDICT_ADDR_MISSCOMPARE" */
+/*	{0xCC, "COND_BRANCH_MISPREDICT"}, */
+/*	{0xD0, "L1_INST_CACHE_MEM_ERR"}, */
+/*	{0xE1, "ICACHE_MISS_STALL"}, */
+/*	{0xE2, "DPU_IQ_EMPTY"}, */
+/*	{0xE4, "NOT_FPU_NEON_INTERLOCK"}, */
+/*	{0xE5, "LOAD_STORE_INTERLOCK"}, */
+/*	{0xE6, "FPU_NEON_INTERLOCK"}, */
+/*	{0xE7, "LOAD_MISS_STALL"}, */
+/*	{0xE8, "STORE_STALL"}, */
+	{0xFF, "CPU_CYCLES"}
+};
+
+#define A53_PMU_DESC_COUNT (sizeof(a53_pmu_desc) / sizeof(struct pmu_desc))
+
+#endif				/* _V8_PMU_NAME_H_ */
diff --git a/src/devtools/met-driver/4.19/mt2712/version.h b/src/devtools/met-driver/4.19/mt2712/version.h
new file mode 100644
index 0000000..7bb6c77
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/version.h
@@ -0,0 +1,14 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define MET_BACKEND_VERSION "6.1.0"
diff --git a/src/devtools/met-driver/4.4/Android.mk b/src/devtools/met-driver/4.4/Android.mk
new file mode 100644
index 0000000..da8f898
--- /dev/null
+++ b/src/devtools/met-driver/4.4/Android.mk
@@ -0,0 +1,32 @@
+LOCAL_PATH := $(call my-dir)
+
+ifneq (,$(filter $(word 2,$(subst -, ,$(LINUX_KERNEL_VERSION))),$(subst /, ,$(LOCAL_PATH))))
+
+MY_KERNEL_ROOT_DIR := $(PWD)
+MY_KERNEL_CONFIG_FILE := $(MY_KERNEL_ROOT_DIR)/kernel-4.4/arch/$(KERNEL_TARGET_ARCH)/configs/$(KERNEL_DEFCONFIG)
+MY_KERNEL_CONFIG_MODULES := $(shell grep ^CONFIG_MODULES=y $(MY_KERNEL_CONFIG_FILE))
+
+MY_KERNEL_DEFAULT_CONFIG_FILE := $(MY_KERNEL_ROOT_DIR)/kernel-4.4/drivers/misc/mediatek/Kconfig.default
+MY_KERNEL_DEFAULT_CONFIG_MODULES := $(shell grep "select MODULES" $(MY_KERNEL_DEFAULT_CONFIG_FILE))
+
+CONFIG_MODULES_EXIST = n
+ifneq ($(MY_KERNEL_CONFIG_MODULES), "")
+	CONFIG_MODULES_EXIST := y
+else ifneq ($(MY_KERNEL_DEFAULT_CONFIG_MODULES), "")
+	CONFIG_MODULES_EXIST := y
+endif
+
+# we should not build ko for some project without define CONFIG_MODULES
+ifeq ($(CONFIG_MODULES_EXIST), y)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := met.ko
+
+include $(MTK_KERNEL_MODULE)
+else
+$(warning Not building met.ko due to CONFIG_MODULES is not set)
+$(warning Please check following config files whether CONFIG_MODULES is set)
+$(warning 1. $(MY_KERNEL_CONFIG_FILE))
+$(warning 2. $(MY_KERNEL_DEFAULT_CONFIG_FILE))
+endif # $(CONFIG_MODULES_EXIST == y)
+endif # Kernel version matches current path
diff --git a/src/devtools/met-driver/4.4/Makefile b/src/devtools/met-driver/4.4/Makefile
new file mode 100644
index 0000000..7b66e72
--- /dev/null
+++ b/src/devtools/met-driver/4.4/Makefile
@@ -0,0 +1,50 @@
+ifneq ($(MET_DRIVER_DIR),)
+	MTK_PLATFORM := $(subst ",,$(CONFIG_MTK_PLATFORM))
+	MET_COMMON_DIR := $(MET_DRIVER_DIR)/common
+	MET_PLF_DIR := $(MET_DRIVER_DIR)/$(MTK_PLATFORM)
+	MET_BUILD_DEFAULT := n
+
+	ifneq ($(KERNEL_OUT),)
+		include $(KERNEL_OUT)/.config
+		ccflags-y += -imacros $(KERNEL_OUT)/include/generated/autoconf.h
+	endif
+
+	ifeq ($(CONFIG_MODULES),y)
+		ifeq ($(CONFIG_FTRACE),y)
+			ifeq ($(CONFIG_TRACING),y)
+				FTRACE_READY := y
+			endif
+		endif
+
+        $(info ******** Start to build met_drv for $(MTK_PLATFORM) ********)
+		ifneq ($(MET_PLF_DIR),)
+			ifeq ($(FTRACE_READY),y)
+				ifeq ($(CONFIG_BUILD_YOCTO),y)
+					include $(MET_COMMON_DIR)/Kbuild.yocto
+				else
+					include $(MET_COMMON_DIR)/Kbuild
+				endif
+			else
+				$(warning Not building met.ko due to CONFIG_FTRACE/CONFIG_TRACING is not set, build met default)
+				MET_BUILD_DEFAULT = y
+			endif
+		else
+			$(warning not support $(MTK_PLATFORM), build met default)
+			MET_BUILD_DEFAULT = y
+		endif
+	else
+		$(warning Not building met.ko due to CONFIG_MODULES is not set, build met default)
+		MET_BUILD_DEFAULT := y
+	endif
+
+	ifeq ($(MET_BUILD_DEFAULT),y)
+		MET_DEF_DIR := $(MET_DRIVER_DIR)/default
+		ifeq ($(CONFIG_BUILD_YOCTO),y)
+			include $(MET_DEF_DIR)/Kbuild.yocto
+		else
+			include $(MET_DEF_DIR)/Kbuild
+		endif
+	endif
+else
+$(info ******** MET_DRIVER_DIR is empty ********)
+endif
diff --git a/src/devtools/met-driver/4.4/Makefile.yocto b/src/devtools/met-driver/4.4/Makefile.yocto
new file mode 100644
index 0000000..12b3158
--- /dev/null
+++ b/src/devtools/met-driver/4.4/Makefile.yocto
@@ -0,0 +1,15 @@
+MODULE_NAME := met
+
+##############################################################
+# Compile settings
+##############################################################
+all: 
+	make -C $(KERNEL_OUT) M=$(MET_DRIVER_DIR) modules
+
+clean: 
+	make -C $(KERNEL_OUT) M=$(MET_DRIVER_DIR) clean
+	if [ -e $(MET_DRIVER_DIR)/$(MODULE_NAME).ko ]; then rm $(MET_DRIVER_DIR)/$(MODULE_NAME).ko; fi;
+
+.PHONY: all clean
+
+
diff --git a/src/devtools/met-driver/4.4/common/Kbuild b/src/devtools/met-driver/4.4/common/Kbuild
new file mode 100644
index 0000000..eb26672
--- /dev/null
+++ b/src/devtools/met-driver/4.4/common/Kbuild
@@ -0,0 +1,146 @@
+$(info ======== Build met.ko ... ========)
+MET_CORE := common
+
+obj-m := met.o
+
+ccflags-y += -I$(srctree)/include/
+ccflags-y += -I$(srctree)/drivers/misc/mediatek/include/
+ccflags-y += -I$(srctree)/drivers/misc/mediatek/include/mt-plat/$(MTK_PLATFORM)/include/
+ccflags-y += -I$(srctree)/../vendor/mediatek/kernel_modules/met_drv/4.4/$(MTK_PLATFORM)/
+
+
+ccflags-y += $(EXTRA_ARGS) $(EXTRA_CFLAGS)
+
+met-y := $(MET_CORE)/met_main.o \
+         $(MET_CORE)/met_backlight.o \
+         $(MET_CORE)/met_tag_ex.o \
+         $(MET_CORE)/interface.o \
+         $(MET_CORE)/sampler.o \
+         $(MET_CORE)/dummy_header.o \
+         $(MET_CORE)/util.o \
+         $(MET_CORE)/stat.o \
+         $(MET_CORE)/cookie.o \
+         $(MET_CORE)/cpu_pmu.o \
+         $(MET_CORE)/mem_stat.o \
+         $(MET_CORE)/switch.o \
+         $(MET_CORE)/trace_event.o \
+         $(MET_CORE)/core_plf_init.o \
+         $(MET_CORE)/core_plf_trace.o \
+         $(MET_CORE)/ondiemet.o \
+         $(MET_CORE)/ondiemet_log.o \
+         $(MET_CORE)/sspm/ondiemet_sspm.o
+
+CFLAGS_interface.o += -DMET_USER_EVENT_SUPPORT
+CFLAGS_met_tag_ex.o += -DMET_USER_EVENT_SUPPORT
+
+ifeq ($(ARCH), mips)
+    met-y += $(MET_CORE)/mips_pmu_hw.o
+endif #ifeq ($(ARCH), mips)
+
+ifeq ($(ARCH), arm)
+    ccflags-y += -DCONFIG_MET_ARM_32BIT
+    met-y += $(MET_CORE)/v7_pmu_hw.o
+    met-y += $(MET_CORE)/v6_pmu_hw.o
+endif #ifeq ($(ARCH), arm)
+
+ifeq ($(ARCH), arm64)
+    ccflags-y += -DMET_SUPPORT_CPUPMU_V2
+    met-y += $(MET_CORE)/v8_pmu_hw.o
+    met-y += $(MET_CORE)/cpu_pmu_v2.o
+    met-y += $(MET_CORE)/v8_pmu_hw_v2.o
+endif
+
+met-$(CONFIG_CPU_FREQ) += $(MET_CORE)/power.o
+
+
+################################################################################
+# MET_EMI
+################################################################################
+MET_EMI := y
+
+met-$(MET_EMI) += $(MET_CORE)/met_emi.o \
+				  $(MET_CORE)/mtk_emi_bm.o
+
+
+################################################################################
+# MET_GPU
+################################################################################
+MET_GPU := y
+# for mtk_gpufreq.h
+ifneq ("$(wildcard $(srctree)/drivers/misc/mediatek/base/power/$(MTK_PLATFORM)/mtk_gpufreq.h)","")
+    ccflags-y += -I$(srctree)/drivers/misc/mediatek/base/power/$(MTK_PLATFORM)/
+else
+    MET_GPU = n
+endif
+
+# for mtk_gpu_utility.h
+ifneq ("$(wildcard $(srctree)/drivers/misc/mediatek/include/mt-plat/mtk_gpu_utility.h)","")
+    ccflags-y += -I$(srctree)/drivers/misc/mediatek/include/mt-plat/
+else
+    MET_GPU = n
+endif
+
+met-$(MET_GPU) += $(MET_CORE)/mtk_gpu_metmonitor.o
+
+
+################################################################################
+# MET_VCOREDVFS
+################################################################################
+MET_VCOREDVFS := y
+
+# for mtk_vcorefs_manager.h
+ifneq ("$(wildcard $(srctree)/drivers/misc/mediatek/base/power/include/mtk_vcorefs_manager.h)","")
+    ccflags-y += -I$(srctree)/drivers/misc/mediatek/base/power/include/
+
+	# for mtk_vcorefs_governor.h
+	ccflags-y += -I$(srctree)/drivers/misc/mediatek/base/power/include/vcorefs_v3/
+else
+    MET_VCOREDVFS = n
+endif
+
+met-$(MET_VCOREDVFS) += $(MET_CORE)/met_vcoredvfs.o
+
+
+################################################################################
+# MET_PTPOD
+################################################################################
+MET_PTPOD := y
+# for mtk_gpufreq.h
+ifneq ("$(wildcard $(srctree)/drivers/misc/mediatek/base/power/$(MTK_PLATFORM)/mtk_gpufreq.h)","")
+	ccflags-y += -I$(srctree)/drivers/misc/mediatek/base/power/$(MTK_PLATFORM)/
+else
+    MET_PTPOD = n
+endif
+
+# for mtk_cpufreq_api.h
+ifneq ("$(wildcard $(srctree)/drivers/misc/mediatek/include/mt-plat/$(MTK_PLATFORM)/include/mach/mtk_cpufreq_api.h)","")
+	ccflags-y += -I$(srctree)/drivers/misc/mediatek/include/mt-plat/$(MTK_PLATFORM)/include/
+else
+    MET_PTPOD = n
+endif
+
+# for mtk_cpufreq_config.h
+ifneq ("$(wildcard $(srctree)/drivers/misc/mediatek/base/power/cpufreq_v1/src/mach/$(MTK_PLATFORM)/mtk_cpufreq_config.h)","")
+	ccflags-y += -I$(srctree)/drivers/misc/mediatek/base/power/cpufreq_v1/src/mach/$(MTK_PLATFORM)/
+else ifneq ("$(wildcard $(srctree)/drivers/misc/mediatek/base/power/cpufreq_v2/src/mach/$(MTK_PLATFORM)/mtk_cpufreq_config.h)","")
+	ccflags-y += -I$(srctree)/drivers/misc/mediatek/base/power/cpufreq_v2/src/mach/$(MTK_PLATFORM)/
+else
+    MET_PTPOD = n
+endif
+
+met-$(MET_PTPOD) += $(MET_CORE)/met_ptpod.o
+
+
+################################################################################
+# On-die-met SSPM only module
+################################################################################
+ifeq ($(CONFIG_MTK_TINYSYS_SSPM_SUPPORT),y)
+    # for sspm_ipi.h
+    subdir-ccflags-y += -I$(srctree)/drivers/misc/mediatek/sspm
+    subdir-ccflags-y += -I$(srctree)/drivers/misc/mediatek/sspm/$(CONFIG_MTK_PLATFORM)
+    met-y += $(MET_CORE)/sspm/sspm_ipi_handle.o
+endif
+
+
+ccflags-y += $(foreach v, $(filter MET_%,$(.VARIABLES)), $(if $(filter $($(v)),y),-D$(v)))
+
diff --git a/src/devtools/met-driver/4.4/common/Kbuild.yocto b/src/devtools/met-driver/4.4/common/Kbuild.yocto
new file mode 100644
index 0000000..09a8dba
--- /dev/null
+++ b/src/devtools/met-driver/4.4/common/Kbuild.yocto
@@ -0,0 +1,176 @@
+$(info ======== Build met.ko ... ========)
+MET_CORE := common
+obj-m := met.o
+
+ccflags-y += -DCONFIG_MET_MODULE
+ccflags-y += -DMET_PLF_USE
+ccflags-y += -I$(MET_COMMON_DIR)
+ccflags-y += -I$(MET_PLF_DIR)
+
+ccflags-y += -I$(KERNEL_SRC)/include/
+ccflags-y += -I$(KERNEL_SRC)/drivers/misc/mediatek/include/
+ccflags-y += -I$(KERNEL_SRC)/drivers/misc/mediatek/include/mt-plat/
+ccflags-y += -I$(KERNEL_SRC)/drivers/misc/mediatek/include/mt-plat/$(MTK_PLATFORM)/include/
+
+ccflags-y += $(EXTRA_ARGS) $(EXTRA_CFLAGS)
+
+met-y := $(MET_CORE)/met_main.o \
+    $(MET_CORE)/met_backlight.o \
+    $(MET_CORE)/met_tag_ex.o \
+    $(MET_CORE)/interface.o \
+    $(MET_CORE)/sampler.o \
+    $(MET_CORE)/dummy_header.o \
+    $(MET_CORE)/util.o \
+    $(MET_CORE)/stat.o \
+    $(MET_CORE)/cookie.o \
+	$(MET_CORE)/cpu_pmu.o \
+    $(MET_CORE)/mem_stat.o \
+    $(MET_CORE)/switch.o \
+    $(MET_CORE)/trace_event.o \
+    $(MET_CORE)/core_plf_init.o \
+    $(MET_CORE)/core_plf_trace.o \
+    $(MET_CORE)/ondiemet.o \
+    $(MET_CORE)/ondiemet_log.o \
+    $(MET_CORE)/sspm/ondiemet_sspm.o
+
+CFLAGS_interface.o += -DMET_USER_EVENT_SUPPORT
+CFLAGS_met_tag_ex.o += -DMET_USER_EVENT_SUPPORT
+
+$(info ARCH = $(ARCH))
+ifeq ($(ARCH), mips)
+    met-y += $(MET_CORE)/mips_pmu_hw.o
+endif #ifeq ($(ARCH), mips)
+
+ifeq ($(ARCH), arm)
+    ccflags-y += -DCONFIG_MET_ARM_32BIT
+    met-y += $(MET_CORE)/v7_pmu_hw.o
+    met-y += $(MET_CORE)/v6_pmu_hw.o
+endif #ifeq ($(ARCH), arm)
+
+ifeq ($(ARCH), arm64)
+    ccflags-y += -DMET_SUPPORT_CPUPMU_V2
+    met-y += $(MET_CORE)/v8_pmu_hw.o
+    met-y += $(MET_CORE)/cpu_pmu_v2.o
+    met-y += $(MET_CORE)/v8_pmu_hw_v2.o
+
+endif
+
+$(info CONFIG_CPU_FREQ = $(CONFIG_CPU_FREQ))
+ifeq ($(CONFIG_CPU_FREQ),y)
+    met-y += $(MET_CORE)/power.o
+endif
+
+################################################################################
+# MET_EMI
+################################################################################
+FEATURE_SSPM_EMI := $(if $(FEATURE_SSPM_EMI),$(FEATURE_SSPM_EMI),y)
+$(info FEATURE_SSPM_EMI = $(FEATURE_SSPM_EMI))
+
+MET_EMI := $(if $(filter n,$(FEATURE_SSPM_EMI)),n,y)
+
+met-$(MET_EMI) += $(MET_CORE)/met_emi.o \
+     $(MET_CORE)/mtk_emi_bm.o
+
+
+################################################################################
+# MET_GPU
+################################################################################
+MET_GPU := y
+
+    # for mtk_gpufreq.h
+    ifneq ("$(wildcard $(KERNEL_SRC)/drivers/misc/mediatek/base/power/$(MTK_PLATFORM)/mtk_gpufreq.h)","")
+        ccflags-y += -I$(KERNEL_SRC)/drivers/misc/mediatek/base/power/$(MTK_PLATFORM)/
+    else
+        MET_GPU = n
+    endif
+
+    # for mtk_gpu_utility.h
+    ifneq ("$(wildcard $(KERNEL_SRC)/drivers/misc/mediatek/include/mt-plat/mtk_gpu_utility.h)","")
+        ccflags-y += -I$(KERNEL_SRC)/drivers/misc/mediatek/include/mt-plat/
+else
+    MET_GPU := n
+endif
+
+met-$(MET_GPU) += $(MET_CORE)/mtk_gpu_metmonitor.o
+
+
+################################################################################
+# MET_VCOREDVFS
+################################################################################
+    MET_VCOREDVFS := y
+
+    # for mtk_vcorefs_manager.h
+    ifneq ("$(wildcard $(MET_VCOREDVFS_INC)/mtk_vcorefs_manager.h)","")
+        ccflags-y += -I$(MET_VCOREDVFS_INC)/
+
+    # for mtk_vcorefs_governor.h
+    ifneq ("$(wildcard $(MET_VCOREDVFS_INC)/mtk_vcorefs_governor.h)","")
+        ccflags-y += -I$(MET_VCOREDVFS_INC)
+    else
+        MET_VCOREDVFS = n
+        $(info ======== Missing $(MET_VCOREDVFS_INC)/mtk_vcorefs_governor.h ========)
+        $(info ======== disable MET_VCOREDVFS ========)
+    endif
+
+    # for helio-dvfsrc.h
+    ifneq ("$(wildcard $(KERNEL_SRC)/drivers/devfreq/helio-dvfsrc.h)","")
+        ccflags-y += -I$(KERNEL_SRC)/drivers/devfreq/
+    else
+        MET_VCOREDVFS = n
+        $(info ======== Missing $(KERNEL_SRC)/drivers/devfreq/helio-dvfsrc.h ========)
+        $(info ======== disable MET_VCOREDVFS ========)
+    endif
+else
+    MET_VCOREDVFS := n
+endif
+
+met-$(MET_VCOREDVFS) += $(MET_CORE)/met_vcoredvfs.o
+
+
+################################################################################
+# MET_PTPOD
+################################################################################
+    MET_PTPOD := y
+
+    # for mtk_gpufreq.h
+    ifneq ("$(wildcard $(KERNEL_SRC)/drivers/misc/mediatek/base/power/$(MTK_PLATFORM)/mtk_gpufreq.h)","")
+        ccflags-y += -I$(KERNEL_SRC)/drivers/misc/mediatek/base/power/$(MTK_PLATFORM)/
+    else
+        MET_PTPOD = n
+    endif
+
+    # for mtk_cpufreq_api.h
+    ifneq ("$(wildcard $(KERNEL_SRC)/drivers/misc/mediatek/include/mt-plat/$(MTK_PLATFORM)/include/mach/mtk_cpufreq_api.h)","")
+        ccflags-y += -I$(KERNEL_SRC)/drivers/misc/mediatek/include/mt-plat/$(MTK_PLATFORM)/include/
+    else
+        MET_PTPOD = n
+    endif
+
+    # for mtk_cpufreq_config.h
+    ifneq ("$(wildcard $(MET_PTPOD_INC)/mtk_cpufreq_config.h)","")
+        ccflags-y += -I$(MET_PTPOD_INC)
+    else
+        MET_PTPOD = n
+    endif
+#else
+  #  MET_PTPOD := n
+#endif
+
+met-$(MET_PTPOD) += $(MET_CORE)/met_ptpod.o
+
+# On-die-met SSPM only module
+################################################################################
+
+ ifeq ($(CONFIG_MTK_TINYSYS_SSPM_SUPPORT),y)
+        # for sspm_ipi.h
+        subdir-ccflags-y += -I$(KERNEL_SRC)/drivers/misc/mediatek/sspm
+       subdir-ccflags-y += -I$(KERNEL_SRC)/drivers/misc/mediatek/sspm/$(MTK_PLATFORM)
+       met-y += $(MET_CORE)/sspm/sspm_ipi_handle.o
+        
+endif
+
+
+#################################################################################
+# add met_device flags
+#################################################################################
+ccflags-y += $(foreach v, $(filter MET_%,$(.VARIABLES)), $(if $(filter $($(v)),y),-D$(v)))
diff --git a/src/devtools/met-driver/4.4/common/cookie.c b/src/devtools/met-driver/4.4/common/cookie.c
new file mode 100644
index 0000000..f11aa8b
--- /dev/null
+++ b/src/devtools/met-driver/4.4/common/cookie.c
@@ -0,0 +1,268 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/cpu.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <asm/irq_regs.h>
+#include <asm/stacktrace.h>
+#include <linux/stacktrace.h>
+#include "interface.h"
+#include "met_drv.h"
+
+#define LINE_SIZE	256
+
+struct cookie_info {
+	int depth;
+	int strlen;
+	char strbuf[LINE_SIZE];
+};
+
+static unsigned int back_trace_depth;
+static DEFINE_PER_CPU(struct cookie_info, info);
+
+static int reset_driver_stat(void)
+{
+	back_trace_depth = 0;
+	met_cookie.mode = 0;
+	return 0;
+}
+
+
+noinline void cookie(char *strbuf)
+{
+	MET_TRACE("%s\n", strbuf);
+}
+
+noinline void cookie2(char *strbuf)
+{
+	MET_TRACE("%s\n", strbuf);
+}
+
+static void get_kernel_cookie(unsigned long pc, struct cookie_info *pinfo)
+{
+	int ret;
+#ifdef CONFIG_MODULES
+	off_t off;
+	struct module *mod = __module_address(pc);
+
+	if (mod) {
+		off = pc - (unsigned long)mod->module_core;
+		ret = snprintf(pinfo->strbuf + pinfo->strlen, LINE_SIZE - pinfo->strlen,
+			       ",%s,%lx", mod->name, off);
+		pinfo->strlen += ret;
+		/* cookie(current->comm, pc, mod->name, off, 1); */
+	} else
+#endif
+	{
+		ret =
+		    snprintf(pinfo->strbuf + pinfo->strlen, LINE_SIZE - pinfo->strlen,
+			     ",vmlinux,%lx", pc);
+		pinfo->strlen += ret;
+		/* cookie(current->comm, pc, "vmlinux", pc, 0); */
+	}
+}
+
+#if defined(__arm__)
+static int report_trace(struct stackframe *frame, void *d)
+{
+	struct cookie_info *pinfo = d;
+	unsigned long pc = frame->pc;
+
+	if (pinfo->depth > 0) {
+		get_kernel_cookie(pc, pinfo);
+		pinfo->depth--;
+		return 0;
+	}
+	return 1;
+}
+#endif
+
+static void kernel_backtrace(struct pt_regs *const regs, struct cookie_info *pinfo)
+{
+#if defined(__arm__)
+	struct stackframe frame;
+
+	frame.fp = regs->ARM_fp;
+	frame.sp = regs->ARM_sp;
+	frame.lr = regs->ARM_lr;
+	frame.pc = regs->ARM_pc;
+	walk_stackframe(&frame, report_trace, pinfo);
+#else
+	return;
+#endif
+}
+
+
+void met_cookie_polling(unsigned long long stamp, int cpu)
+{
+	struct pt_regs *regs;
+	struct cookie_info *pinfo;
+	unsigned long pc;
+	int ret, outflag = 0;
+	off_t off;
+
+	regs = get_irq_regs();
+
+	if (regs == 0)
+		return;
+
+	pc = profile_pc(regs);
+
+	pinfo = &(per_cpu(info, cpu));
+	pinfo->strlen = snprintf(pinfo->strbuf, LINE_SIZE, "%s,%lx", current->comm, pc);
+
+	if (user_mode(regs)) {
+		struct mm_struct *mm;
+		struct vm_area_struct *vma;
+		struct path *ppath;
+
+		mm = current->mm;
+		for (vma = find_vma(mm, pc); vma; vma = vma->vm_next) {
+
+			if (pc < vma->vm_start || pc >= vma->vm_end)
+				continue;
+
+			if (vma->vm_file) {
+				ppath = &(vma->vm_file->f_path);
+
+				if (vma->vm_flags & VM_DENYWRITE)
+					off = pc;
+				else
+					off = (vma->vm_pgoff << PAGE_SHIFT) + pc - vma->vm_start;
+
+				ret =
+				    snprintf(pinfo->strbuf + pinfo->strlen,
+					     LINE_SIZE - pinfo->strlen, ",%s,%lx",
+					     (char *)(ppath->dentry->d_name.name), off);
+				pinfo->strlen += ret;
+				outflag = 1;
+			} else {
+				/* must be an anonymous map */
+				ret =
+				    snprintf(pinfo->strbuf + pinfo->strlen,
+					     LINE_SIZE - pinfo->strlen, ",nofile,%lx", pc);
+				pinfo->strlen += ret;
+				outflag = 1;
+			}
+			break;
+		}
+	} else {
+		/* kernel mode code */
+		if (back_trace_depth > 0) {
+			pinfo->depth = back_trace_depth + 1;
+			kernel_backtrace(regs, pinfo);
+		} else
+			get_kernel_cookie(pc, pinfo);
+		outflag = 1;
+	}
+
+	/* check task is resolvable */
+	if (outflag == 0)
+		return;
+
+	if (back_trace_depth == 0)
+		cookie(pinfo->strbuf);
+	else
+		cookie2(pinfo->strbuf);
+}
+
+
+static void met_cookie_start(void)
+{
+	/* return; */
+}
+
+static void met_cookie_stop(void)
+{
+	/* return; */
+}
+
+
+static int met_cookie_process_argument(const char *arg, int len)
+{
+	unsigned int value;
+
+	if (met_parse_num(arg, &value, len) < 0) {
+		met_cookie.mode = 0;
+		return -EINVAL;
+	}
+
+	back_trace_depth = value;
+	met_cookie.mode = 1;
+
+	return 0;
+}
+
+static const char help[] =
+"  --cookie                              enable sampling task and PC\n"
+"  --cookie=N                            enable back trace (depth is N)\n";
+
+static int met_cookie_print_help(char *buf, int len)
+{
+	len = snprintf(buf, PAGE_SIZE, help);
+	return len;
+}
+
+
+static const char header[] =
+"# cookie: task_name,PC,cookie_name,offset\n"
+"met-info [000] 0.0: cookie_header: task_name,PC,cookie_name,offset\n";
+
+static const char header2_1[] = "# cookie2: task_name,PC,cookie,offset";
+static const char header2_2[] = "met-info [000] 0.0: cookie2_header: task_name,PC,cookie,offset";
+
+static int met_cookie_print_header(char *buf, int len)
+{
+	int i, ret;
+
+	if (back_trace_depth == 0) {
+		len = snprintf(buf, PAGE_SIZE, header);
+	} else {
+		len = snprintf(buf, PAGE_SIZE, header2_1);
+		for (i = 0; i < back_trace_depth; i++) {
+			ret = snprintf(buf + len, PAGE_SIZE, ",cookie%d,offset%d", i + 1, i + 1);
+			len += ret;
+		}
+		ret = snprintf(buf + len, PAGE_SIZE, "\n");
+		len += ret;
+
+		ret = snprintf(buf + len, PAGE_SIZE, header2_2);
+		len += ret;
+		for (i = 0; i < back_trace_depth; i++) {
+			ret = snprintf(buf + len, PAGE_SIZE, ",cookie%d,offset%d", i + 1, i + 1);
+			len += ret;
+		}
+		ret = snprintf(buf + len, PAGE_SIZE, "\n");
+		len += ret;
+	}
+
+	return len;
+}
+
+struct metdevice met_cookie = {
+	.name = "cookie",
+	.type = MET_TYPE_PMU,
+	.cpu_related = 1,
+	.start = met_cookie_start,
+	.stop = met_cookie_stop,
+	.reset = reset_driver_stat,
+	.polling_interval = 1,
+	.timed_polling = met_cookie_polling,
+	.process_argument = met_cookie_process_argument,
+	.print_help = met_cookie_print_help,
+	.print_header = met_cookie_print_header,
+};
diff --git a/src/devtools/met-driver/4.4/common/core_plf_init.c b/src/devtools/met-driver/4.4/common/core_plf_init.c
new file mode 100644
index 0000000..46c2a50
--- /dev/null
+++ b/src/devtools/met-driver/4.4/common/core_plf_init.c
@@ -0,0 +1,290 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/kallsyms.h>
+#include "met_drv.h"
+#include "met_api_tbl.h"
+#include "interface.h"
+#include "core_plf_init.h"
+
+#undef	DEBUG
+
+#ifdef MET_GPU
+/*
+ *   GPU
+ */
+bool (*mtk_get_gpu_loading_symbol)(unsigned int *pLoading);
+bool (*mtk_get_gpu_block_symbol)(unsigned int *pBlock);
+bool (*mtk_get_gpu_idle_symbol)(unsigned int *pIdle);
+bool (*mtk_get_gpu_dvfs_from_symbol)(MTK_GPU_DVFS_TYPE *peType, unsigned long *pulFreq);
+bool (*mtk_get_gpu_sub_loading_symbol)(unsigned int *pLoading);
+bool (*mtk_get_3D_fences_count_symbol)(int *pi32Count);
+bool (*mtk_get_gpu_memory_usage_symbol)(unsigned int *pMemUsage);
+bool (*mtk_get_gpu_power_loading_symbol)(unsigned int *pLoading);
+bool (*mtk_get_custom_boost_gpu_freq_symbol)(unsigned long *pulFreq);
+bool (*mtk_get_custom_upbound_gpu_freq_symbol)(unsigned long *pulFreq);
+bool (*mtk_get_vsync_based_target_freq_symbol)(unsigned long *pulFreq);
+bool (*mtk_get_vsync_offset_event_status_symbol)(unsigned int *pui32EventStatus);
+bool (*mtk_get_vsync_offset_debug_status_symbol)(unsigned int *pui32EventStatus);
+bool (*mtk_enable_gpu_perf_monitor_symbol)(bool enable);
+bool (*mtk_get_gpu_pmu_init_symbol)(GPU_PMU *pmus, int pmu_size, int *ret_size);
+bool (*mtk_get_gpu_pmu_swapnreset_symbol)(GPU_PMU *pmus, int pmu_size);
+#if 0
+bool (*mtk_get_gpu_pmu_deinit_symbol)(void);
+bool (*mtk_get_gpu_pmu_swapnreset_stop_symbol)(void);
+#endif
+
+unsigned int (*mt_gpufreq_get_cur_freq_symbol)(void);
+unsigned int (*mt_gpufreq_get_thermal_limit_freq_symbol)(void);
+
+bool (*mtk_register_gpu_power_change_symbol)(const char *name, gpu_power_change_notify_fp callback);
+bool (*mtk_unregister_gpu_power_change_symbol)(const char *name);
+#endif /* MET_GPU */
+
+#ifdef MET_VCOREDVFS
+/*
+ *   VCORE DVFS
+ */
+#include <mtk_vcorefs_governor.h>
+#include <mtk_spm_vcore_dvfs.h>
+
+int  (*vcorefs_get_opp_info_num_symbol)(void);
+char ** (*vcorefs_get_opp_info_name_symbol)(void);
+unsigned int * (*vcorefs_get_opp_info_symbol)(void);
+int  (*vcorefs_get_src_req_num_symbol)(void);
+char ** (*vcorefs_get_src_req_name_symbol)(void);
+unsigned int * (*vcorefs_get_src_req_symbol)(void);
+u32 (*spm_vcorefs_get_MD_status_symbol)(void);
+
+void (*spm_vcorefs_register_handler_symbol)(vcorefs_handler_t handler);
+void (*vcorefs_register_req_notify_symbol)(vcorefs_req_handler_t handler);
+
+char *(*governor_get_kicker_name_symbol)(int id);
+int (*vcorefs_enable_debug_isr_symbol)(bool);
+
+int (*vcorefs_get_hw_opp_symbol)(void);
+int (*vcorefs_get_curr_vcore_symbol)(void);
+int (*vcorefs_get_curr_ddr_symbol)(void);
+int (*vcorefs_get_num_opp_symbol)(void);
+int *kicker_table_symbol;
+
+#endif /* MET_VCOREDVFS */
+
+#ifdef MET_EMI
+void *(*mt_cen_emi_base_get_symbol)(void);
+#endif /* MET_EMI */
+
+#ifdef MET_PTPOD
+unsigned int (*mt_gpufreq_get_cur_volt_symbol)(void);
+unsigned int (*mt_cpufreq_get_cur_volt_symbol)(unsigned int cluster_id);
+#endif /* MET_PTPOD */
+
+static int met_symbol_get(void)
+{
+#define _MET_SYMBOL_GET(_func_name_) \
+	do { \
+		_func_name_##_symbol = (void *)symbol_get(_func_name_); \
+		if (_func_name_##_symbol == NULL) { \
+			pr_debug("MET ext. symbol : %s is not found!\n", #_func_name_); \
+			PR_BOOTMSG_ONCE("MET ext. symbol : %s is not found!\n", #_func_name_); \
+		} \
+	} while (0)
+
+
+#ifdef MET_GPU
+	_MET_SYMBOL_GET(mtk_get_gpu_loading);
+	_MET_SYMBOL_GET(mtk_get_gpu_block);
+	_MET_SYMBOL_GET(mtk_get_gpu_idle);
+	_MET_SYMBOL_GET(mtk_get_gpu_dvfs_from);
+	_MET_SYMBOL_GET(mtk_get_gpu_sub_loading);
+	_MET_SYMBOL_GET(mtk_get_3D_fences_count);
+	_MET_SYMBOL_GET(mtk_get_gpu_memory_usage);
+	_MET_SYMBOL_GET(mtk_get_gpu_power_loading);
+	_MET_SYMBOL_GET(mtk_get_custom_boost_gpu_freq);
+	_MET_SYMBOL_GET(mtk_get_custom_upbound_gpu_freq);
+	_MET_SYMBOL_GET(mtk_get_vsync_based_target_freq);
+	_MET_SYMBOL_GET(mtk_get_vsync_offset_event_status);
+	_MET_SYMBOL_GET(mtk_get_vsync_offset_debug_status);
+	_MET_SYMBOL_GET(mtk_enable_gpu_perf_monitor);
+	_MET_SYMBOL_GET(mtk_get_gpu_pmu_init);
+	_MET_SYMBOL_GET(mtk_get_gpu_pmu_swapnreset);
+	_MET_SYMBOL_GET(mt_gpufreq_get_cur_freq);
+	_MET_SYMBOL_GET(mt_gpufreq_get_thermal_limit_freq);
+	_MET_SYMBOL_GET(mtk_register_gpu_power_change);
+	_MET_SYMBOL_GET(mtk_unregister_gpu_power_change);
+#if 0
+	_MET_SYMBOL_GET(mtk_get_gpu_pmu_swapnreset_stop);
+	_MET_SYMBOL_GET(mtk_get_gpu_pmu_deinit);
+#endif
+#endif /* MET_GPU */
+
+#ifdef MET_VCOREDVFS
+	_MET_SYMBOL_GET(vcorefs_get_opp_info_num);
+	_MET_SYMBOL_GET(vcorefs_get_opp_info_name);
+	_MET_SYMBOL_GET(vcorefs_get_opp_info);
+	_MET_SYMBOL_GET(vcorefs_get_src_req_num);
+	_MET_SYMBOL_GET(vcorefs_get_src_req_name);
+	_MET_SYMBOL_GET(vcorefs_get_src_req);
+
+	_MET_SYMBOL_GET(spm_vcorefs_register_handler);
+	_MET_SYMBOL_GET(vcorefs_register_req_notify);
+
+	_MET_SYMBOL_GET(governor_get_kicker_name);
+	_MET_SYMBOL_GET(vcorefs_enable_debug_isr);
+
+	_MET_SYMBOL_GET(vcorefs_get_hw_opp);
+	_MET_SYMBOL_GET(vcorefs_get_curr_vcore);
+	_MET_SYMBOL_GET(vcorefs_get_curr_ddr);
+	_MET_SYMBOL_GET(spm_vcorefs_get_MD_status);
+	_MET_SYMBOL_GET(vcorefs_get_num_opp);
+	_MET_SYMBOL_GET(kicker_table);
+#endif
+
+#ifdef MET_EMI
+	_MET_SYMBOL_GET(mt_cen_emi_base_get);
+#endif
+
+#ifdef MET_PTPOD
+	_MET_SYMBOL_GET(mt_gpufreq_get_cur_volt);
+	_MET_SYMBOL_GET(mt_cpufreq_get_cur_volt);
+#endif
+
+	return 0;
+}
+
+static int met_symbol_put(void)
+{
+#define _MET_SYMBOL_PUT(_func_name_) { \
+		if (_func_name_##_symbol) { \
+			symbol_put(_func_name_); \
+			_func_name_##_symbol = NULL; \
+		} \
+	}
+
+#ifdef MET_GPU
+	_MET_SYMBOL_PUT(mtk_get_gpu_loading);
+	_MET_SYMBOL_PUT(mtk_get_gpu_block);
+	_MET_SYMBOL_PUT(mtk_get_gpu_idle);
+	_MET_SYMBOL_PUT(mtk_get_gpu_dvfs_from);
+	_MET_SYMBOL_PUT(mtk_get_gpu_sub_loading);
+	_MET_SYMBOL_PUT(mtk_get_3D_fences_count);
+	_MET_SYMBOL_PUT(mtk_get_gpu_memory_usage);
+	_MET_SYMBOL_PUT(mtk_get_gpu_power_loading);
+	_MET_SYMBOL_PUT(mtk_get_custom_boost_gpu_freq);
+	_MET_SYMBOL_PUT(mtk_get_custom_upbound_gpu_freq);
+	_MET_SYMBOL_PUT(mtk_get_vsync_based_target_freq);
+	_MET_SYMBOL_PUT(mtk_get_vsync_offset_event_status);
+	_MET_SYMBOL_PUT(mtk_get_vsync_offset_debug_status);
+	_MET_SYMBOL_PUT(mtk_enable_gpu_perf_monitor);
+	_MET_SYMBOL_PUT(mtk_get_gpu_pmu_init);
+	_MET_SYMBOL_PUT(mtk_get_gpu_pmu_swapnreset);
+	_MET_SYMBOL_PUT(mt_gpufreq_get_cur_freq);
+	_MET_SYMBOL_PUT(mt_gpufreq_get_thermal_limit_freq);
+	_MET_SYMBOL_PUT(mtk_register_gpu_power_change);
+	_MET_SYMBOL_PUT(mtk_unregister_gpu_power_change);
+#if 0
+	_MET_SYMBOL_PUT(mtk_get_gpu_pmu_swapnreset_stop);
+	_MET_SYMBOL_PUT(mtk_get_gpu_pmu_deinit);
+#endif
+#endif /* MET_GPU */
+
+#ifdef MET_VCOREDVFS
+	_MET_SYMBOL_PUT(vcorefs_get_opp_info_num);
+	_MET_SYMBOL_PUT(vcorefs_get_opp_info_name);
+	_MET_SYMBOL_PUT(vcorefs_get_opp_info);
+	_MET_SYMBOL_PUT(vcorefs_get_src_req_num);
+	_MET_SYMBOL_PUT(vcorefs_get_src_req_name);
+	_MET_SYMBOL_PUT(vcorefs_get_src_req);
+
+	_MET_SYMBOL_PUT(spm_vcorefs_register_handler);
+	_MET_SYMBOL_PUT(vcorefs_register_req_notify);
+
+	_MET_SYMBOL_PUT(governor_get_kicker_name);
+	_MET_SYMBOL_PUT(vcorefs_enable_debug_isr);
+
+	_MET_SYMBOL_PUT(vcorefs_get_hw_opp);
+	_MET_SYMBOL_PUT(vcorefs_get_curr_vcore);
+	_MET_SYMBOL_PUT(vcorefs_get_curr_ddr);
+	_MET_SYMBOL_PUT(spm_vcorefs_get_MD_status);
+	_MET_SYMBOL_PUT(vcorefs_get_num_opp);
+	_MET_SYMBOL_PUT(kicker_table);
+#endif
+
+#ifdef MET_EMI
+	_MET_SYMBOL_PUT(mt_cen_emi_base_get);
+#endif
+
+#ifdef MET_PTPOD
+	_MET_SYMBOL_PUT(mt_gpufreq_get_cur_volt);
+	_MET_SYMBOL_PUT(mt_cpufreq_get_cur_volt);
+#endif
+
+	return 0;
+}
+
+int core_plf_init(void)
+{
+	/*initial met external symbol*/
+	met_symbol_get();
+
+#ifdef MET_GPU
+	met_register(&met_gpu);
+	met_register(&met_gpudvfs);
+	met_register(&met_gpumem);
+	met_register(&met_gpupwr);
+	met_register(&met_gpu_pmu);
+#endif
+
+#ifdef MET_VCOREDVFS
+	met_register(&met_vcoredvfs);
+#endif
+
+#ifdef MET_EMI
+	met_register(&met_sspm_emi);
+#endif
+
+#ifdef MET_PTPOD
+	met_register(&met_ptpod);
+#endif
+
+	return 0;
+}
+
+void core_plf_exit(void)
+{
+	/*release met external symbol*/
+	met_symbol_put();
+
+#ifdef MET_GPU
+	met_deregister(&met_gpu);
+	met_deregister(&met_gpudvfs);
+	met_deregister(&met_gpumem);
+	met_deregister(&met_gpupwr);
+	met_deregister(&met_gpu_pmu);
+#endif
+
+#ifdef MET_VCOREDVFS
+	met_deregister(&met_vcoredvfs);
+#endif
+
+#ifdef MET_EMI
+	met_deregister(&met_sspm_emi);
+#endif
+
+#ifdef MET_PTPOD
+	met_deregister(&met_ptpod);
+#endif
+}
diff --git a/src/devtools/met-driver/4.4/common/core_plf_init.h b/src/devtools/met-driver/4.4/common/core_plf_init.h
new file mode 100644
index 0000000..bdeb584
--- /dev/null
+++ b/src/devtools/met-driver/4.4/common/core_plf_init.h
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __CORE_PLF_INIT_H__
+#define __CORE_PLF_INIT_H__
+
+extern struct miscdevice met_device;
+
+/*
+ *   MET External Symbol
+ */
+
+#ifdef MET_GPU
+/*
+ *   GPU
+ */
+#include <mtk_gpu_utility.h>
+#include <mtk_gpufreq.h>
+
+extern bool mtk_get_gpu_loading(unsigned int *pLoading);
+extern bool mtk_get_gpu_block(unsigned int *pBlock);
+extern bool mtk_get_gpu_idle(unsigned int *pIdle);
+extern bool mtk_get_gpu_dvfs_from(MTK_GPU_DVFS_TYPE *peType, unsigned long *pulFreq);
+extern bool mtk_get_gpu_sub_loading(unsigned int *pLoading);
+extern bool mtk_get_3D_fences_count(int *pi32Count);
+extern bool mtk_get_gpu_memory_usage(unsigned int *pMemUsage);
+extern bool mtk_get_gpu_power_loading(unsigned int *pLoading);
+extern bool mtk_get_custom_boost_gpu_freq(unsigned int *pui32FreqLevel);
+extern bool mtk_get_custom_upbound_gpu_freq(unsigned int *pui32FreqLevel);
+extern bool mtk_get_vsync_based_target_freq(unsigned long *pulFreq);
+extern bool mtk_get_vsync_offset_event_status(unsigned int *pui32EventStatus);
+extern bool mtk_get_vsync_offset_debug_status(unsigned int *pui32EventStatus);
+extern bool mtk_enable_gpu_perf_monitor(bool enable);
+extern bool mtk_get_gpu_pmu_init(GPU_PMU *pmus, int pmu_size, int *ret_size);
+extern bool mtk_get_gpu_pmu_swapnreset(GPU_PMU *pmus, int pmu_size);
+
+extern bool (*mtk_get_gpu_loading_symbol)(unsigned int *pLoading);
+extern bool (*mtk_get_gpu_block_symbol)(unsigned int *pBlock);
+extern bool (*mtk_get_gpu_idle_symbol)(unsigned int *pIdle);
+extern bool (*mtk_get_gpu_dvfs_from_symbol)(MTK_GPU_DVFS_TYPE *peType, unsigned long *pulFreq);
+extern bool (*mtk_get_gpu_sub_loading_symbol)(unsigned int *pLoading);
+extern bool (*mtk_get_3D_fences_count_symbol)(int *pi32Count);
+extern bool (*mtk_get_gpu_memory_usage_symbol)(unsigned int *pMemUsage);
+extern bool (*mtk_get_gpu_power_loading_symbol)(unsigned int *pLoading);
+extern bool (*mtk_get_custom_boost_gpu_freq_symbol)(unsigned long *pulFreq);
+extern bool (*mtk_get_custom_upbound_gpu_freq_symbol)(unsigned long *pulFreq);
+extern bool (*mtk_get_vsync_based_target_freq_symbol)(unsigned long *pulFreq);
+extern bool (*mtk_get_vsync_offset_event_status_symbol)(unsigned int *pui32EventStatus);
+extern bool (*mtk_get_vsync_offset_debug_status_symbol)(unsigned int *pui32EventStatus);
+extern bool (*mtk_enable_gpu_perf_monitor_symbol)(bool enable);
+extern bool (*mtk_get_gpu_pmu_init_symbol)(GPU_PMU *pmus, int pmu_size, int *ret_size);
+extern bool (*mtk_get_gpu_pmu_swapnreset_symbol)(GPU_PMU *pmus, int pmu_size);
+extern bool (*mtk_get_gpu_pmu_deinit_symbol)(void);
+extern bool (*mtk_get_gpu_pmu_swapnreset_stop_symbol)(void);
+
+extern bool mtk_register_gpu_power_change(const char *name, gpu_power_change_notify_fp callback);
+extern bool mtk_unregister_gpu_power_change(const char *name);
+extern bool (*mtk_register_gpu_power_change_symbol)(const char *name,
+					gpu_power_change_notify_fp callback);
+extern bool (*mtk_unregister_gpu_power_change_symbol)(const char *name);
+
+
+extern unsigned int (*mt_gpufreq_get_cur_freq_symbol)(void);
+extern unsigned int (*mt_gpufreq_get_thermal_limit_freq_symbol)(void);
+
+extern struct metdevice met_gpu;
+extern struct metdevice met_gpudvfs;
+extern struct metdevice met_gpumem;
+extern struct metdevice met_gpupwr;
+extern struct metdevice met_gpu_pmu;
+#endif /* MET_GPU */
+
+
+#ifdef MET_VCOREDVFS
+/*
+ *   VCORE DVFS
+ */
+#include <mtk_spm.h>
+#include <mtk_vcorefs_manager.h>
+extern void (*spm_vcorefs_register_handler_symbol)(vcorefs_handler_t handler);
+extern void (*vcorefs_register_req_notify_symbol)(vcorefs_req_handler_t handler);
+
+extern char *(*governor_get_kicker_name_symbol)(int id);
+extern int (*vcorefs_get_opp_info_num_symbol)(void);
+extern char **(*vcorefs_get_opp_info_name_symbol)(void);
+extern unsigned int *(*vcorefs_get_opp_info_symbol)(void);
+extern int (*vcorefs_get_src_req_num_symbol)(void);
+extern char **(*vcorefs_get_src_req_name_symbol)(void);
+extern unsigned int *(*vcorefs_get_src_req_symbol)(void);
+extern int (*vcorefs_enable_debug_isr_symbol)(bool);
+extern int (*vcorefs_get_num_opp_symbol)(void);
+extern int *kicker_table_symbol;
+
+extern char *governor_get_kicker_name(int id);
+extern int vcorefs_get_opp_info_num(void);
+extern char **vcorefs_get_opp_info_name(void);
+extern unsigned int *vcorefs_get_opp_info(void);
+extern int vcorefs_get_src_req_num(void);
+extern char **vcorefs_get_src_req_name(void);
+extern unsigned int *vcorefs_get_src_req(void);
+extern int vcorefs_enable_debug_isr(bool);
+extern int vcorefs_get_num_opp(void);
+
+extern int (*vcorefs_get_hw_opp_symbol)(void);
+extern int (*vcorefs_get_curr_vcore_symbol)(void);
+extern int (*vcorefs_get_curr_ddr_symbol)(void);
+extern u32 (*spm_vcorefs_get_MD_status_symbol)(void);
+
+extern struct metdevice met_vcoredvfs;
+#endif /* MET_VCOREDVFS */
+
+#ifdef MET_EMI
+extern void *mt_cen_emi_base_get(void);
+
+extern void *(*mt_cen_emi_base_get_symbol)(void);
+
+extern struct metdevice met_sspm_emi;
+#endif /* MET_EMI */
+
+
+#ifdef MET_PTPOD
+#include <mtk_gpufreq.h>
+#include <mach/mtk_cpufreq_api.h>
+#include <mtk_cpufreq_config.h>
+
+extern unsigned int (*mt_gpufreq_get_cur_volt_symbol)(void);
+extern unsigned int (*mt_cpufreq_get_cur_volt_symbol)(unsigned int cluster_id);
+
+extern struct metdevice met_ptpod;
+#endif /* MET_PTPOD */
+
+#endif /*__CORE_PLF_INIT_H__*/
diff --git a/src/devtools/met-driver/4.4/common/core_plf_trace.c b/src/devtools/met-driver/4.4/common/core_plf_trace.c
new file mode 100644
index 0000000..0dc7090
--- /dev/null
+++ b/src/devtools/met-driver/4.4/common/core_plf_trace.c
@@ -0,0 +1,322 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+#include "met_drv.h"
+#include "interface.h"
+#include "trace.h"
+
+char *ms_formatH(char *__restrict__ buf, unsigned char cnt, unsigned int *__restrict__ value)
+{
+	char *s = buf;
+	int len;
+
+	if (cnt == 0) {
+		buf[0] = '\0';
+		return buf;
+	}
+
+	switch (cnt % 4) {
+	case 1:
+		len = sprintf(s, "%x", value[0]);
+		s += len;
+		value += 1;
+		cnt -= 1;
+		break;
+	case 2:
+		len = sprintf(s, "%x,%x", value[0], value[1]);
+		s += len;
+		value += 2;
+		cnt -= 2;
+		break;
+	case 3:
+		len = sprintf(s, "%x,%x,%x", value[0], value[1], value[2]);
+		s += len;
+		value += 3;
+		cnt -= 3;
+		break;
+	case 0:
+		len = sprintf(s, "%x,%x,%x,%x", value[0], value[1], value[2], value[3]);
+		s += len;
+		value += 4;
+		cnt -= 4;
+		break;
+	}
+
+	while (cnt) {
+		len = sprintf(s, ",%x,%x,%x,%x", value[0], value[1], value[2], value[3]);
+		s += len;
+		value += 4;
+		cnt -= 4;
+	}
+
+	s[0] = '\0';
+
+	return s;
+}
+EXPORT_SYMBOL(ms_formatH);
+
+char *ms_formatD(char *__restrict__ buf, unsigned char cnt, unsigned int *__restrict__ value)
+{
+	char *s = buf;
+	int len;
+
+	if (cnt == 0) {
+		buf[0] = '\0';
+		return buf;
+	}
+
+	switch (cnt % 4) {
+	case 1:
+		len = sprintf(s, "%u", value[0]);
+		s += len;
+		value += 1;
+		cnt -= 1;
+		break;
+	case 2:
+		len = sprintf(s, "%u,%u", value[0], value[1]);
+		s += len;
+		value += 2;
+		cnt -= 2;
+		break;
+	case 3:
+		len = sprintf(s, "%u,%u,%u", value[0], value[1], value[2]);
+		s += len;
+		value += 3;
+		cnt -= 3;
+		break;
+	case 0:
+		len = sprintf(s, "%u,%u,%u,%u", value[0], value[1], value[2], value[3]);
+		s += len;
+		value += 4;
+		cnt -= 4;
+		break;
+	}
+
+	while (cnt) {
+		len = sprintf(s, ",%u,%u,%u,%u", value[0], value[1], value[2], value[3]);
+		s += len;
+		value += 4;
+		cnt -= 4;
+	}
+
+	s[0] = '\0';
+
+	return s;
+}
+EXPORT_SYMBOL(ms_formatD);
+
+char *ms_formatH_ulong(char *__restrict__ buf, unsigned char cnt, unsigned long *__restrict__ value)
+{
+	char *s = buf;
+	int len;
+
+	if (cnt == 0) {
+		buf[0] = '\0';
+		return buf;
+	}
+
+	switch (cnt % 4) {
+	case 1:
+		len = sprintf(s, "%lx", value[0]);
+		s += len;
+		value += 1;
+		cnt -= 1;
+		break;
+	case 2:
+		len = sprintf(s, "%lx,%lx", value[0], value[1]);
+		s += len;
+		value += 2;
+		cnt -= 2;
+		break;
+	case 3:
+		len = sprintf(s, "%lx,%lx,%lx", value[0], value[1], value[2]);
+		s += len;
+		value += 3;
+		cnt -= 3;
+		break;
+	case 0:
+		len = sprintf(s, "%lx,%lx,%lx,%lx", value[0], value[1], value[2], value[3]);
+		s += len;
+		value += 4;
+		cnt -= 4;
+		break;
+	}
+
+	while (cnt) {
+		len = sprintf(s, ",%lx,%lx,%lx,%lx", value[0], value[1], value[2], value[3]);
+		s += len;
+		value += 4;
+		cnt -= 4;
+	}
+
+	s[0] = '\0';
+
+	return buf;
+}
+EXPORT_SYMBOL(ms_formatH_ulong);
+
+char *ms_formatD_ulong(char *__restrict__ buf, unsigned char cnt, unsigned long *__restrict__ value)
+{
+	char *s = buf;
+	int len;
+
+	if (cnt == 0) {
+		buf[0] = '\0';
+		return buf;
+	}
+
+	switch (cnt % 4) {
+	case 1:
+		len = sprintf(s, "%lu", value[0]);
+		s += len;
+		value += 1;
+		cnt -= 1;
+		break;
+	case 2:
+		len = sprintf(s, "%lu,%lu", value[0], value[1]);
+		s += len;
+		value += 2;
+		cnt -= 2;
+		break;
+	case 3:
+		len = sprintf(s, "%lu,%lu,%lu", value[0], value[1], value[2]);
+		s += len;
+		value += 3;
+		cnt -= 3;
+		break;
+	case 0:
+		len = sprintf(s, "%lu,%lu,%lu,%lu", value[0], value[1], value[2], value[3]);
+		s += len;
+		value += 4;
+		cnt -= 4;
+		break;
+	}
+
+	while (cnt) {
+		len = sprintf(s, ",%lu,%lu,%lu,%lu", value[0], value[1], value[2], value[3]);
+		s += len;
+		value += 4;
+		cnt -= 4;
+	}
+
+	s[0] = '\0';
+
+	return buf;
+}
+EXPORT_SYMBOL(ms_formatD_ulong);
+
+char *ms_formatH_EOL(char *__restrict__ buf, unsigned char cnt, unsigned int *__restrict__ value)
+{
+	char *s = buf;
+	int len;
+
+	if (cnt == 0) {
+		buf[0] = '\0';
+		return buf;
+	}
+
+	switch (cnt % 4) {
+	case 1:
+		len = sprintf(s, "%x", value[0]);
+		s += len;
+		value += 1;
+		cnt -= 1;
+		break;
+	case 2:
+		len = sprintf(s, "%x,%x", value[0], value[1]);
+		s += len;
+		value += 2;
+		cnt -= 2;
+		break;
+	case 3:
+		len = sprintf(s, "%x,%x,%x", value[0], value[1], value[2]);
+		s += len;
+		value += 3;
+		cnt -= 3;
+		break;
+	case 0:
+		len = sprintf(s, "%x,%x,%x,%x", value[0], value[1], value[2], value[3]);
+		s += len;
+		value += 4;
+		cnt -= 4;
+		break;
+	}
+
+	while (cnt) {
+		len = sprintf(s, ",%x,%x,%x,%x", value[0], value[1], value[2], value[3]);
+		s += len;
+		value += 4;
+		cnt -= 4;
+	}
+
+	s[0] = '\n';
+	s[1] = '\0';
+
+	return s + 1;
+}
+EXPORT_SYMBOL(ms_formatH_EOL);
+
+char *ms_formatD_EOL(char *__restrict__ buf, unsigned char cnt, unsigned int *__restrict__ value)
+{
+	char *s = buf;
+	int len;
+
+	if (cnt == 0) {
+		buf[0] = '\0';
+		return buf;
+	}
+
+	switch (cnt % 4) {
+	case 1:
+		len = sprintf(s, "%u", value[0]);
+		s += len;
+		value += 1;
+		cnt -= 1;
+		break;
+	case 2:
+		len = sprintf(s, "%u,%u", value[0], value[1]);
+		s += len;
+		value += 2;
+		cnt -= 2;
+		break;
+	case 3:
+		len = sprintf(s, "%u,%u,%u", value[0], value[1], value[2]);
+		s += len;
+		value += 3;
+		cnt -= 3;
+		break;
+	case 0:
+		len = sprintf(s, "%u,%u,%u,%u", value[0], value[1], value[2], value[3]);
+		s += len;
+		value += 4;
+		cnt -= 4;
+		break;
+	}
+
+	while (cnt) {
+		len = sprintf(s, ",%u,%u,%u,%u", value[0], value[1], value[2], value[3]);
+		s += len;
+		value += 4;
+		cnt -= 4;
+	}
+
+	s[0] = '\n';
+	s[1] = '\0';
+
+	return s + 1;
+}
+EXPORT_SYMBOL(ms_formatD_EOL);
+
diff --git a/src/devtools/met-driver/4.4/common/core_plf_trace.h b/src/devtools/met-driver/4.4/common/core_plf_trace.h
new file mode 100644
index 0000000..5c89efd
--- /dev/null
+++ b/src/devtools/met-driver/4.4/common/core_plf_trace.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _CORE_PLF_TRACE_H_
+#define _CORE_PLF_TRACE_H_
+
+#define	HVALUE_SIZE	9	/* 8 chars (max value ffffffff) + 1 char (',' or NULL) */
+#define	DVALUE_SIZE	12	/* 10 chars (max value 4,294,967,295) + 1 char (',' or NULL) */
+
+char *ms_formatH(char *__restrict__ buf, unsigned char cnt, unsigned int *__restrict__ value);
+char *core_ms_formatD(char *__restrict__ buf, unsigned char cnt, unsigned int *__restrict__ value);
+char *ms_formatH_ulong(char *__restrict__ buf, unsigned char cnt,
+		       unsigned long *__restrict__ value);
+char *ms_formatD_ulong(char *__restrict__ buf, unsigned char cnt,
+		       unsigned long *__restrict__ value);
+char *ms_formatH_EOL(char *__restrict__ buf, unsigned char cnt, unsigned int *__restrict__ value);
+char *ms_formatD_EOL(char *__restrict__ buf, unsigned char cnt, unsigned int *__restrict__ value);
+
+#endif	/* _CORE_PLF_TRACE_H_ */
diff --git a/src/devtools/met-driver/4.4/common/cpu_pmu.c b/src/devtools/met-driver/4.4/common/cpu_pmu.c
new file mode 100644
index 0000000..ba9cfab
--- /dev/null
+++ b/src/devtools/met-driver/4.4/common/cpu_pmu.c
@@ -0,0 +1,606 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/* include <asm/page.h> */
+#include <linux/slab.h>
+#include <linux/version.h>
+
+#include "interface.h"
+#include "trace.h"
+#include "cpu_pmu.h"
+#include "met_drv.h"
+
+#define MET_USER_EVENT_SUPPORT
+
+#include <linux/kthread.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/signal.h>
+#include <linux/perf_event.h>
+#include "met_kernel_symbol.h"
+#include "interface.h"
+
+static int counter_cnt;
+
+struct metdevice met_cpupmu;
+struct cpu_pmu_hw *cpu_pmu;
+static int nr_counters;
+
+static struct kobject *kobj_cpu;
+static struct met_pmu *pmu;
+static int nr_arg;
+
+#define CNTMAX NR_CPUS
+static DEFINE_PER_CPU(unsigned long long[CNTMAX], perfCurr);
+static DEFINE_PER_CPU(unsigned long long[CNTMAX], perfPrev);
+static DEFINE_PER_CPU(int[CNTMAX], perfCntFirst);
+static DEFINE_PER_CPU(struct perf_event * [CNTMAX], pevent);
+static DEFINE_PER_CPU(struct perf_event_attr [CNTMAX], pevent_attr);
+static DEFINE_PER_CPU(int, perfSet);
+static DEFINE_PER_CPU(struct task_struct *, perf_task_setup);
+static DEFINE_PER_CPU(unsigned int, perf_task_init_done);
+static DEFINE_PER_CPU(unsigned int, perf_cpuid);
+
+static inline int reset_driver_stat(int counters)
+{
+	int i;
+
+	nr_arg = 0;
+	counter_cnt = 0;
+	met_cpupmu.mode = 0;
+	for (i = 0; i < counters; i++) {
+		pmu[i].mode = MODE_DISABLED;
+		pmu[i].event = 0;
+		pmu[i].freq = 0;
+	}
+
+	return 0;
+}
+
+static inline struct met_pmu *lookup_pmu(struct kobject *kobj)
+{
+	int i;
+
+	for (i = 0; i < nr_counters; i++) {
+		if (pmu[i].kobj_cpu_pmu == kobj)
+			return &pmu[i];
+	}
+	return NULL;
+}
+
+static ssize_t count_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%d\n", nr_counters - 1);
+}
+
+static ssize_t count_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t n)
+{
+	return -EINVAL;
+}
+
+static ssize_t event_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	struct met_pmu *p = lookup_pmu(kobj);
+
+	if (p != NULL)
+		return snprintf(buf, PAGE_SIZE, "0x%hx\n", p->event);
+
+	return -EINVAL;
+}
+
+static ssize_t event_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t n)
+{
+	struct met_pmu *p = lookup_pmu(kobj);
+	unsigned short event;
+
+	if (p != NULL) {
+		if (sscanf(buf, "0x%hx", &event) != 1)
+			return -EINVAL;
+
+		if (p == &(pmu[nr_counters - 1])) {	/* cycle counter */
+			if (event != 0xff)
+				return -EINVAL;
+		} else {
+			if (cpu_pmu->check_event(pmu, nr_arg, event) < 0)
+				return -EINVAL;
+		}
+
+		p->event = event;
+		return n;
+	}
+	return -EINVAL;
+}
+
+static ssize_t mode_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	struct met_pmu *p = lookup_pmu(kobj);
+
+	if (p != NULL) {
+		switch (p->mode) {
+		case 0:
+			return snprintf(buf, PAGE_SIZE, "%hhd (disabled)\n", p->mode);
+		case 1:
+			return snprintf(buf, PAGE_SIZE, "%hhd (interrupt)\n", p->mode);
+		case 2:
+			return snprintf(buf, PAGE_SIZE, "%hhd (polling)\n", p->mode);
+		}
+	}
+	return -EINVAL;
+}
+
+static ssize_t mode_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t n)
+{
+	unsigned int mode;
+	struct met_pmu *p = lookup_pmu(kobj);
+
+	if (p != NULL) {
+		if (kstrtouint(buf, 0, &mode) != 0)
+			return -EINVAL;
+
+		if (mode <= 2) {
+			p->mode = (unsigned char)mode;
+			if (mode > 0)
+				met_cpupmu.mode = 1;
+			return n;
+		}
+	}
+	return -EINVAL;
+}
+
+static ssize_t freq_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	struct met_pmu *p = lookup_pmu(kobj);
+
+	if (p != NULL)
+		return snprintf(buf, PAGE_SIZE, "%ld\n", p->freq);
+
+	return -EINVAL;
+}
+
+static ssize_t freq_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t n)
+{
+	struct met_pmu *p = lookup_pmu(kobj);
+
+	if (p != NULL) {
+		if (kstrtoul(buf, 0, &(p->freq)) != 0)
+			return -EINVAL;
+
+		return n;
+	}
+	return -EINVAL;
+}
+
+static struct kobj_attribute count_attr = __ATTR(count, 0664, count_show, count_store);
+static struct kobj_attribute event_attr = __ATTR(event, 0664, event_show, event_store);
+static struct kobj_attribute mode_attr = __ATTR(mode, 0664, mode_show, mode_store);
+static struct kobj_attribute freq_attr = __ATTR(freq, 0664, freq_show, freq_store);
+
+static int cpupmu_create_subfs(struct kobject *parent)
+{
+	int ret = 0;
+	int i;
+	char buf[16];
+
+	cpu_pmu = cpu_pmu_hw_init();
+	if (cpu_pmu == NULL) {
+		PR_BOOTMSG("Failed to init CPU PMU HW!!\n");
+		return -ENODEV;
+	}
+	nr_counters = cpu_pmu->nr_cnt;
+
+	pmu = kmalloc_array(nr_counters, sizeof(struct met_pmu), GFP_KERNEL);
+	if (pmu == NULL)
+		return -ENOMEM;
+
+	memset(pmu, 0, sizeof(struct met_pmu) * nr_counters);
+	cpu_pmu->pmu = pmu;
+	kobj_cpu = parent;
+
+	ret = sysfs_create_file(kobj_cpu, &count_attr.attr);
+	if (ret != 0) {
+		PR_BOOTMSG("Failed to create count in sysfs\n");
+		goto out;
+	}
+
+	for (i = 0; i < nr_counters; i++) {
+		snprintf(buf, sizeof(buf), "%d", i);
+		pmu[i].kobj_cpu_pmu = kobject_create_and_add(buf, kobj_cpu);
+
+		ret = sysfs_create_file(pmu[i].kobj_cpu_pmu, &event_attr.attr);
+		if (ret != 0) {
+			PR_BOOTMSG("Failed to create event in sysfs\n");
+			goto out;
+		}
+
+		ret = sysfs_create_file(pmu[i].kobj_cpu_pmu, &mode_attr.attr);
+		if (ret != 0) {
+			PR_BOOTMSG("Failed to create mode in sysfs\n");
+			goto out;
+		}
+
+		ret = sysfs_create_file(pmu[i].kobj_cpu_pmu, &freq_attr.attr);
+		if (ret != 0) {
+			PR_BOOTMSG("Failed to create freq in sysfs\n");
+			goto out;
+		}
+	}
+
+ out:
+	if (ret != 0) {
+		if (pmu != NULL) {
+			kfree(pmu);
+			pmu = NULL;
+		}
+	}
+	return ret;
+}
+
+static void cpupmu_delete_subfs(void)
+{
+	int i;
+
+	if (kobj_cpu != NULL) {
+		for (i = 0; i < nr_counters; i++) {
+			sysfs_remove_file(pmu[i].kobj_cpu_pmu, &event_attr.attr);
+			sysfs_remove_file(pmu[i].kobj_cpu_pmu, &mode_attr.attr);
+			sysfs_remove_file(pmu[i].kobj_cpu_pmu, &freq_attr.attr);
+			kobject_del(pmu[i].kobj_cpu_pmu);
+			kobject_put(pmu[i].kobj_cpu_pmu);
+			pmu[i].kobj_cpu_pmu = NULL;
+		}
+		sysfs_remove_file(kobj_cpu, &count_attr.attr);
+		kobj_cpu = NULL;
+	}
+
+	if (pmu != NULL) {
+		kfree(pmu);
+		pmu = NULL;
+	}
+
+	cpu_pmu  = NULL;
+}
+
+noinline void mp_cpu(unsigned char cnt, unsigned int *value)
+{
+	MET_GENERAL_PRINT(MET_TRACE, cnt, value);
+}
+
+static void dummy_handler(struct perf_event *event, struct perf_sample_data *data,
+			  struct pt_regs *regs)
+{
+/* Required as perf_event_create_kernel_counter() requires an overflow handler, even though all we do is poll */
+}
+
+void perf_cpupmu_polling(unsigned long long stamp, int cpu)
+{
+	int i, count;
+	long long int delta;
+	struct perf_event *ev;
+	unsigned int pmu_value[MXNR_CPU];
+
+	if (per_cpu(perfSet, cpu) == 0)
+		return;
+
+	memset(pmu_value, 0, sizeof(pmu_value));
+	count = 0;
+	for (i = 0; i < nr_counters; i++) {
+		if (pmu[i].mode == 0)
+			continue;
+
+		ev = per_cpu(pevent, cpu)[i];
+		if ((ev != NULL) && (ev->state == PERF_EVENT_STATE_ACTIVE)) {
+			per_cpu(perfCurr, cpu)[i] = met_perf_event_read_local_symbol(ev);
+			delta = (long long int)(per_cpu(perfCurr, cpu)[i] - per_cpu(perfPrev, cpu)[i]);
+			if (delta < 0)
+				delta *= (-1);
+			per_cpu(perfPrev, cpu)[i] = per_cpu(perfCurr, cpu)[i];
+			if (per_cpu(perfCntFirst, cpu)[i] == 1) {
+				/* we shall omit delta counter when we get first counter */
+				per_cpu(perfCntFirst, cpu)[i] = 0;
+				continue;
+			}
+			pmu_value[i] = (unsigned int) delta;
+			count++;
+		}
+	}
+
+	if (count == counter_cnt)
+		mp_cpu(count, pmu_value);
+}
+
+static int perf_thread_set_perf_events(unsigned int cpu)
+{
+	int i, size;
+	struct perf_event *ev;
+
+	size = sizeof(struct perf_event_attr);
+	if (per_cpu(perfSet, cpu) == 0) {
+		for (i = 0; i < nr_counters; i++) {
+			per_cpu(pevent, cpu)[i] = NULL;
+			if (!pmu[i].mode) {
+				/* Skip disabled counters */
+				continue;
+			}
+			per_cpu(perfPrev, cpu)[i] = 0;
+			per_cpu(perfCurr, cpu)[i] = 0;
+			memset(&per_cpu(pevent_attr, cpu)[i], 0, size);
+			per_cpu(pevent_attr, cpu)[i].config = pmu[i].event;
+			per_cpu(pevent_attr, cpu)[i].type = PERF_TYPE_RAW;
+			per_cpu(pevent_attr, cpu)[i].size = size;
+			per_cpu(pevent_attr, cpu)[i].sample_period = 0;
+			per_cpu(pevent_attr, cpu)[i].pinned = 1;
+			if (pmu[i].event == 0xff) {
+				per_cpu(pevent_attr, cpu)[i].type = PERF_TYPE_HARDWARE;
+				per_cpu(pevent_attr, cpu)[i].config = PERF_COUNT_HW_CPU_CYCLES;
+			}
+
+			per_cpu(pevent, cpu)[i] =
+			    perf_event_create_kernel_counter(&per_cpu(pevent_attr, cpu)[i], cpu, NULL,
+				     dummy_handler, NULL);
+			if (IS_ERR(per_cpu(pevent, cpu)[i])) {
+				per_cpu(pevent, cpu)[i] = NULL;
+				PR_BOOTMSG("CPU=%d, %s:%d\n", cpu, __FUNCTION__, __LINE__);
+				continue;
+			}
+
+			if (per_cpu(pevent, cpu)[i]->state != PERF_EVENT_STATE_ACTIVE) {
+				perf_event_release_kernel(per_cpu(pevent, cpu)[i]);
+				per_cpu(pevent, cpu)[i] = NULL;
+				PR_BOOTMSG("CPU=%d, %s:%d\n", cpu, __FUNCTION__, __LINE__);
+				continue;
+			}
+
+			ev = per_cpu(pevent, cpu)[i];
+			if (ev != NULL)
+				perf_event_enable(ev);
+			per_cpu(perfCntFirst, cpu)[i] = 1;
+		} /* for all PMU counter */
+		per_cpu(perfSet, cpu) = 1;
+	} /* for perfSet */
+	return 0;
+}
+
+static int perf_thread_setup(void *data)
+{
+	unsigned int cpu;
+
+	cpu = *((unsigned int *)data);
+	if (per_cpu(perf_task_init_done, cpu) == 0) {
+		per_cpu(perf_task_init_done, cpu) = 1;
+		perf_thread_set_perf_events(cpu);
+	}
+
+	return 0;
+}
+
+void met_perf_cpupmu_online(unsigned int cpu)
+{
+	if (met_cpupmu.mode == 0)
+		return;
+
+	per_cpu(perf_cpuid, cpu) = cpu;
+	if (per_cpu(perf_task_setup, cpu) == NULL) {
+		per_cpu(perf_task_setup, cpu) = met_kthread_create_on_cpu_symbol(perf_thread_setup,
+			(void *)&per_cpu(perf_cpuid, cpu), cpu, "met_perf");
+		kthread_unpark(per_cpu(perf_task_setup, cpu));
+	}
+}
+
+void met_perf_cpupmu_down(void *data)
+{
+	unsigned int cpu;
+	unsigned int i;
+	struct perf_event *ev;
+
+	cpu = *((unsigned int *)data);
+	if (met_cpupmu.mode == 0)
+		return;
+	if (per_cpu(perfSet, cpu) == 0)
+		return;
+	per_cpu(perfSet, cpu) = 0;
+	for (i = 0; i < nr_counters; i++) {
+		if (!pmu[i].mode)
+			continue;
+		ev = per_cpu(pevent, cpu)[i];
+		if ((ev != NULL) && (ev->state == PERF_EVENT_STATE_ACTIVE)) {
+			perf_event_disable(ev);
+			perf_event_release_kernel(ev);
+		}
+	}
+	per_cpu(perf_task_init_done, cpu) = 0;
+	per_cpu(perf_task_setup, cpu) = NULL;
+}
+
+void met_perf_cpupmu_stop(void)
+{
+	unsigned int cpu;
+
+	for_each_online_cpu(cpu) {
+		per_cpu(perf_cpuid, cpu) = cpu;
+		met_perf_cpupmu_down((void *)&per_cpu(perf_cpuid, cpu));
+	}
+}
+
+void met_perf_cpupmu_start(void)
+{
+	unsigned int cpu;
+
+	for_each_online_cpu(cpu) {
+		met_perf_cpupmu_online(cpu);
+	}
+}
+
+void cpupmu_polling(unsigned long long stamp, int cpu)
+{
+	int count;
+	unsigned int pmu_value[MXNR_CPU];
+
+	if (met_cpu_pmu_method == 0) {
+		count = cpu_pmu->polling(pmu, nr_counters, pmu_value);
+		mp_cpu(count, pmu_value);
+	} else {
+		perf_cpupmu_polling(stamp, cpu);
+	}
+}
+
+static void cpupmu_start(void)
+{
+	if (met_cpu_pmu_method == 0) {
+		nr_arg = 0;
+		cpu_pmu->start(pmu, nr_counters);
+	}
+}
+
+static void cpupmu_stop(void)
+{
+	if (met_cpu_pmu_method == 0)
+		cpu_pmu->stop(nr_counters);
+}
+
+static const char cache_line_header[] =
+	"met-info [000] 0.0: met_cpu_cache_line_size: %d\n";
+static const char header_n[] =
+	"# mp_cpu: pmu_value1, ...\n"
+	"met-info [000] 0.0: met_cpu_header: 0x%x:%s";
+static const char header[] =
+	"# mp_cpu: pmu_value1, ...\n"
+	"met-info [000] 0.0: met_cpu_header: 0x%x";
+
+static const char help[] =
+	"  --pmu-cpu-evt=EVENT                   select CPU-PMU events. in %s,\n"
+	"                                        you can enable at most \"%d general purpose events\"\n"
+	"                                        plus \"one special 0xff (CPU_CYCLE) event\"\n";
+
+static int cpupmu_print_help(char *buf, int len)
+{
+	return snprintf(buf, PAGE_SIZE, help, cpu_pmu->cpu_name, nr_counters - 1);
+}
+
+static int cpupmu_print_header(char *buf, int len)
+{
+	int i, ret, first;
+	char name[32];
+
+	first = 1;
+	ret = 0;
+
+	/*append CPU PMU access method*/
+	if (met_cpu_pmu_method == 0)
+		ret += snprintf(buf + ret, PAGE_SIZE,
+			"met-info [000] 0.0: CPU_PMU_method: PMU registers\n");
+	else
+		ret += snprintf(buf + ret, PAGE_SIZE,
+			"met-info [000] 0.0: CPU_PMU_method: perf APIs\n");
+
+	/*append cache line size*/
+	ret += snprintf(buf + ret, PAGE_SIZE - ret, cache_line_header, cache_line_size());
+
+	for (i = 0; i < nr_counters; i++) {
+		if (pmu[i].mode == 0)
+			continue;
+		if (cpu_pmu->get_event_desc && 0 == cpu_pmu->get_event_desc(i, pmu[i].event, name)) {
+			if (first) {
+				ret += snprintf(buf + ret, PAGE_SIZE - ret, header_n, pmu[i].event, name);
+				first = 0;
+			} else {
+				ret += snprintf(buf + ret, PAGE_SIZE - ret, ",0x%x:%s", pmu[i].event, name);
+			}
+		} else {
+			if (first) {
+				ret += snprintf(buf + ret, PAGE_SIZE - ret, header, pmu[i].event);
+				first = 0;
+			} else {
+				ret += snprintf(buf + ret, PAGE_SIZE - ret, ",0x%x", pmu[i].event);
+			}
+		}
+		pmu[i].mode = 0;
+
+	}
+
+	ret += snprintf(buf + ret, PAGE_SIZE - ret, "\n");
+	met_cpupmu.mode = 0;
+	reset_driver_stat(nr_counters);
+	nr_arg = 0;
+	return ret;
+}
+
+/*
+ * "met-cmd --start --pmu_cpu_evt=0x3"
+ */
+static int cpupmu_process_argument(const char *arg, int len)
+{
+	unsigned int value;
+
+	if (met_cpu_pmu_method == 0)
+		nr_counters = cpu_pmu->nr_cnt;
+	else
+		nr_counters = perf_num_counters();
+
+	if (nr_counters == 0)
+		goto arg_out;
+
+	if (met_parse_num(arg, &value, len) < 0)
+		goto arg_out;
+
+	if (cpu_pmu->check_event(pmu, nr_arg, value) < 0)
+		goto arg_out;
+
+	if (value == 0xff) {
+		if (met_cpu_pmu_method == 0) {
+			pmu[nr_counters - 1].mode = MODE_POLLING;
+			pmu[nr_counters - 1].event = 0xff;
+			pmu[nr_counters - 1].freq = 0;
+		} else {
+			if (nr_arg > (nr_counters - 1))
+				goto arg_out;
+
+			pmu[nr_arg].mode = MODE_POLLING;
+			pmu[nr_arg].event = value;
+			pmu[nr_arg].freq = 0;
+			nr_arg++;
+		}
+	} else {
+
+		if (nr_arg >= (nr_counters - 1))
+			goto arg_out;
+
+		pmu[nr_arg].mode = MODE_POLLING;
+		pmu[nr_arg].event = value;
+		pmu[nr_arg].freq = 0;
+		nr_arg++;
+	}
+	counter_cnt++;
+
+	met_cpupmu.mode = 1;
+	return 0;
+
+arg_out:
+	reset_driver_stat(nr_counters);
+	return -EINVAL;
+}
+
+struct metdevice met_cpupmu = {
+	.name = "cpu",
+	.type = MET_TYPE_PMU,
+	.cpu_related = 1,
+	.create_subfs = cpupmu_create_subfs,
+	.delete_subfs = cpupmu_delete_subfs,
+	.start = cpupmu_start,
+	.stop = cpupmu_stop,
+	.polling_interval = 1,
+	.timed_polling = cpupmu_polling,
+	.print_help = cpupmu_print_help,
+	.print_header = cpupmu_print_header,
+	.process_argument = cpupmu_process_argument
+};
diff --git a/src/devtools/met-driver/4.4/common/cpu_pmu.h b/src/devtools/met-driver/4.4/common/cpu_pmu.h
new file mode 100644
index 0000000..013a063
--- /dev/null
+++ b/src/devtools/met-driver/4.4/common/cpu_pmu.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _PMU_H_
+#define _PMU_H_
+
+#include <linux/device.h>
+
+#define MODE_DISABLED	0
+#define MODE_INTERRUPT	1
+#define MODE_POLLING	2
+
+#define MXSIZE_PMU_DESC 32
+#define MXNR_CPU 16
+
+struct met_pmu {
+	unsigned char mode;
+	unsigned short event;
+	unsigned long freq;
+	struct kobject *kobj_cpu_pmu;
+};
+
+struct cpu_pmu_hw {
+	const char *name;
+	const char *cpu_name;
+	int nr_cnt;
+	int (*get_event_desc)(int idx, int event, char *event_desc);
+	int (*check_event)(struct met_pmu *pmu, int idx, int event);
+	void (*start)(struct met_pmu *pmu, int count);
+	void (*stop)(int count);
+	unsigned int (*polling)(struct met_pmu *pmu, int count, unsigned int *pmu_value);
+	struct met_pmu *pmu;
+};
+
+struct pmu_desc {
+	unsigned int event;
+	char name[MXSIZE_PMU_DESC];
+};
+
+struct cpu_pmu_hw *cpu_pmu_hw_init(void);
+
+extern struct cpu_pmu_hw *cpu_pmu;
+extern noinline void mp_cpu(unsigned char cnt, unsigned int *value);
+
+extern void met_perf_cpupmu_start(void);
+extern void met_perf_cpupmu_stop(void);
+extern void met_perf_cpupmu_online(unsigned int cpu);
+extern void met_perf_cpupmu_down(void *cpu);
+extern void cpupmu_polling(unsigned long long stamp, int cpu);
+
+#endif				/* _PMU_H_ */
diff --git a/src/devtools/met-driver/4.4/common/cpu_pmu_v2.c b/src/devtools/met-driver/4.4/common/cpu_pmu_v2.c
new file mode 100644
index 0000000..8471dbc
--- /dev/null
+++ b/src/devtools/met-driver/4.4/common/cpu_pmu_v2.c
@@ -0,0 +1,672 @@
+/*

+ * Copyright (C) 2018 MediaTek Inc.

+ *

+ * 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.

+ *

+ * This program is distributed in the hope that it will be useful,

+ * but WITHOUT ANY WARRANTY; without even the implied warranty of

+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the

+ * GNU General Public License for more details.

+ */

+

+#include <linux/slab.h>

+#include <linux/version.h>

+

+#include "interface.h"

+#include "trace.h"

+#include "cpu_pmu_v2.h"

+#include "v8_pmu_hw_v2.h"

+#include "met_drv.h"

+

+

+#define MET_USER_EVENT_SUPPORT

+

+#include <linux/kthread.h>

+#include <linux/kernel.h>

+#include <linux/sched.h>

+#include <linux/wait.h>

+#include <linux/signal.h>

+#include <linux/workqueue.h>

+#include <linux/perf_event.h>

+#include "met_kernel_symbol.h"

+

+

+/*******************************************************************************

+*				Type Define

+*******************************************************************************/

+#define CNTMAX NR_CPUS

+

+

+/*******************************************************************************

+*				Fuction Pototypes

+*******************************************************************************/

+static inline struct met_pmu_v2 *get_met_pmu_by_cpu_id(const unsigned int cpu);

+static inline void set_met_pmu_by_cpu_id(const unsigned int cpu, struct met_pmu_v2 *met_pmu);

+

+static int reset_driver_stat(void);

+static struct met_pmu_v2 *lookup_pmu(struct kobject *kobj);

+

+static ssize_t mode_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf);

+

+static int cpupmu_create_subfs(struct kobject *parent);

+static void cpupmu_delete_subfs(void);

+static void _cpupmu_start(void *info);

+static void cpupmu_start(void);

+static void _cpupmu_stop(void *info);

+static void cpupmu_stop(void);

+static void cpupmu_polling(unsigned long long stamp, int cpu);

+extern void cpupmu_polling_v2(unsigned long long stamp, int cpu);

+static int cpupmu_print_help(char *buf, int len);

+static int cpupmu_print_header(char *buf, int len);

+static int cpupmu_process_argument(const char *arg, int len);

+

+

+/*******************************************************************************

+*				Globe Variables

+*******************************************************************************/

+static int module_status;

+

+struct cpu_pmu_hw_v2 *met_pmu_hw_v2;

+

+static unsigned int gPMU_CNT[2*MXNR_CPU_V2];

+static unsigned int gMAX_PMU_HW_CNT;

+

+static struct kobject *gKOBJ_CPU;

+static struct met_pmu_v2 *gMET_PMU[2*MXNR_CPU_V2];

+

+static struct kobj_attribute mode_attr = __ATTR(mode, 0444, mode_show, NULL);

+

+static const char cache_line_header[] =

+	"met-info [000] 0.0: met_cpu_cache_line_size: %d\n";

+static const char header[] =

+	"met-info [000] 0.0: met_cpu_header_v2: ";

+static const char help[] =

+	"  --cpu-pmu=CORE_ID:EVENT	     select CPU-PMU events. in %s,\n"

+	"				      you can enable at most \"%d general purpose events\"\n"

+	"				      plus \"one special 0xff (CPU_CYCLE) event\"\n";

+

+static DEFINE_PER_CPU(int[CNTMAX], perfCurr);

+static DEFINE_PER_CPU(int[CNTMAX], perfPrev);

+static DEFINE_PER_CPU(struct perf_event * [CNTMAX], pevent);

+static DEFINE_PER_CPU(struct perf_event_attr [CNTMAX], pevent_attr);

+static DEFINE_PER_CPU(int, perfSet);

+static DEFINE_PER_CPU(unsigned int, perf_task_init_done);

+static DEFINE_PER_CPU(unsigned int, perf_cpuid);

+

+static DEFINE_PER_CPU(struct delayed_work, cpu_pmu_dwork);

+static DEFINE_PER_CPU(struct delayed_work *, perf_delayed_work_setup);

+

+struct metdevice met_cpupmu_v2 = {

+	.name = "cpu-pmu",

+	.type = MET_TYPE_PMU,

+	.cpu_related = 1,

+	.create_subfs = cpupmu_create_subfs,

+	.delete_subfs = cpupmu_delete_subfs,

+	.start = cpupmu_start,

+	.stop = cpupmu_stop,

+	.polling_interval = 1,

+	.timed_polling = cpupmu_polling,

+	.print_help = cpupmu_print_help,

+	.print_header = cpupmu_print_header,

+	.process_argument = cpupmu_process_argument

+};

+

+

+/*******************************************************************************

+*				Iplement Start

+*******************************************************************************/

+static inline struct met_pmu_v2 *get_met_pmu_by_cpu_id(const unsigned int cpu)

+{

+	if (cpu < MXNR_CPU_V2)

+		return gMET_PMU[cpu];

+	else

+		return NULL;

+}

+

+

+static inline void set_met_pmu_by_cpu_id(const unsigned int cpu, struct met_pmu_v2 *met_pmu)

+{

+	if (cpu < MXNR_CPU_V2)

+		gMET_PMU[cpu] = met_pmu;

+}

+

+

+static int reset_driver_stat()

+{

+	int i;

+	int cpu;

+	struct met_pmu_v2 *met_pmu;

+

+	met_cpupmu_v2.mode = 0;

+	for_each_possible_cpu(cpu) {

+		met_pmu = get_met_pmu_by_cpu_id(cpu);

+		for (i = 0; i < gMAX_PMU_HW_CNT; i++) {

+			met_pmu[i].mode = MODE_DISABLED;

+			met_pmu[i].event = 0;

+		}

+		gPMU_CNT[cpu] = 0;

+	}

+	module_status = 0;

+	return 0;

+}

+

+

+static struct met_pmu_v2 *lookup_pmu(struct kobject *kobj)

+{

+	int i;

+	int cpu;

+	struct met_pmu_v2 *met_pmu;

+

+	for_each_possible_cpu(cpu) {

+		met_pmu = get_met_pmu_by_cpu_id(cpu);

+		for (i = 0; i < gMAX_PMU_HW_CNT; i++) {

+			if (met_pmu[i].kobj_cpu_pmu == kobj)

+				return &met_pmu[i];

+		}

+	}

+	return NULL;

+}

+

+

+static ssize_t mode_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)

+{

+	struct met_pmu_v2 *p = lookup_pmu(kobj);

+

+	if (p != NULL) {

+		switch (p->mode) {

+		case 0:

+			return snprintf(buf, PAGE_SIZE, "%hhd (disabled)\n", p->mode);

+		case 1:

+			return snprintf(buf, PAGE_SIZE, "%hhd (interrupt)\n", p->mode);

+		case 2:

+			return snprintf(buf, PAGE_SIZE, "%hhd (polling)\n", p->mode);

+		}

+	}

+	return -EINVAL;

+}

+

+

+static int cpupmu_create_subfs(struct kobject *parent)

+{

+	int ret = 0;

+	unsigned int i;

+	unsigned int cpu;

+	char buf[16];

+	struct met_pmu_v2 *met_pmu;

+

+	met_pmu_hw_v2 = cpu_pmu_hw_init_v2();

+	if (met_pmu_hw_v2 == NULL) {

+		PR_BOOTMSG("Failed to init CPU PMU HW!!\n");

+		return -ENODEV;

+	}

+	gMAX_PMU_HW_CNT = met_pmu_hw_v2->max_hw_count;

+

+	gKOBJ_CPU = parent;

+	for_each_possible_cpu(cpu) {

+		met_pmu = kmalloc_array(gMAX_PMU_HW_CNT, sizeof(struct met_pmu_v2), GFP_KERNEL);

+		if (met_pmu != NULL) {

+			memset(met_pmu, 0x0, gMAX_PMU_HW_CNT * sizeof(struct met_pmu_v2));

+			met_pmu_hw_v2->met_pmu[cpu] = met_pmu;

+			set_met_pmu_by_cpu_id(cpu, met_pmu);

+		} else

+			ret = -ENOMEM;

+

+		for (i = 0; i < gMAX_PMU_HW_CNT; i++) {

+			snprintf(buf, sizeof(buf), "CPU-%d-%d", cpu, i);

+			met_pmu[i].kobj_cpu_pmu = kobject_create_and_add(buf, gKOBJ_CPU);

+			if (met_pmu[i].kobj_cpu_pmu) {

+				ret = sysfs_create_file(met_pmu[i].kobj_cpu_pmu, &mode_attr.attr);

+				if (ret != 0) {

+					PR_BOOTMSG("Failed to create mode in sysfs\n");

+					goto out;

+				}

+			}

+		}

+	}

+ out:

+	if (ret != 0) {

+		for_each_possible_cpu(cpu) {

+			met_pmu = get_met_pmu_by_cpu_id(cpu);

+			if (met_pmu != NULL) {

+				kfree(met_pmu);

+				set_met_pmu_by_cpu_id(cpu, NULL);

+			}

+		}

+	}

+	return ret;

+}

+

+

+static void cpupmu_delete_subfs(void)

+{

+	unsigned int i;

+	unsigned int cpu;

+	struct met_pmu_v2 *met_pmu;

+

+	for_each_possible_cpu(cpu) {

+		met_pmu = get_met_pmu_by_cpu_id(cpu);

+		if (met_pmu != NULL) {

+			for (i = 0; i < gMAX_PMU_HW_CNT; i++) {

+				sysfs_remove_file(met_pmu[i].kobj_cpu_pmu, &mode_attr.attr);

+				kobject_del(met_pmu[i].kobj_cpu_pmu);

+				kobject_put(met_pmu[i].kobj_cpu_pmu);

+				met_pmu[i].kobj_cpu_pmu = NULL;

+			}

+			kfree(met_pmu);

+		}

+		set_met_pmu_by_cpu_id(cpu, NULL);

+	}

+

+	if (gKOBJ_CPU != NULL) {

+		gKOBJ_CPU = NULL;

+	}

+

+	met_pmu_hw_v2  = NULL;

+}

+

+

+noinline void mp_cpu_v2(unsigned char cnt, unsigned int *value)

+{

+        if (cnt < MXNR_CPU_V2)

+	        MET_GENERAL_PRINT(MET_TRACE, cnt, value);

+}

+

+

+static void dummy_handler(struct perf_event *event, struct perf_sample_data *data,

+			  struct pt_regs *regs)

+{

+/* Required as perf_event_create_kernel_counter() requires an overflow handler, even though all we do is poll */

+}

+

+

+void perf_cpupmu_polling_v2(unsigned long long stamp, int cpu)

+{

+	int i, count, delta;

+	struct perf_event *ev;

+	unsigned int pmu_value[MXNR_CPU_V2];

+	struct met_pmu_v2 *met_pmu;

+

+	if (per_cpu(perfSet, cpu) == 0)

+		return;

+

+	memset(pmu_value, 0, sizeof(pmu_value));

+	count = 0;

+	met_pmu = get_met_pmu_by_cpu_id(cpu);

+	for (i = 0; i < gMAX_PMU_HW_CNT; i++) {

+		if (met_pmu[i].mode == 0)

+			continue;

+

+		ev = per_cpu(pevent, cpu)[i];

+		if ((ev != NULL) && (ev->state == PERF_EVENT_STATE_ACTIVE)) {

+			if (per_cpu(perfPrev, cpu)[i] == 0) {

+				per_cpu(perfPrev, cpu)[i] = met_perf_event_read_local(ev);

+				continue;

+			}

+			per_cpu(perfCurr, cpu)[i] = met_perf_event_read_local(ev);

+			delta = per_cpu(perfCurr, cpu)[i] - per_cpu(perfPrev, cpu)[i];

+			per_cpu(perfPrev, cpu)[i] = per_cpu(perfCurr, cpu)[i];

+			if (delta < 0)

+				delta *= -1;

+			pmu_value[i] = delta;

+			count++;

+		}

+	}

+

+	if (count == gPMU_CNT[cpu])

+		mp_cpu_v2(count, pmu_value);

+}

+

+

+static int perf_thread_set_perf_events_v2(unsigned int cpu)

+{

+	int i, size;

+	struct perf_event *ev;

+	struct met_pmu_v2 *met_pmu;

+

+	size = sizeof(struct perf_event_attr);

+	if (per_cpu(perfSet, cpu) == 0) {

+		met_pmu = get_met_pmu_by_cpu_id(cpu);

+		for (i = 0; i < gMAX_PMU_HW_CNT; i++) {

+			per_cpu(pevent, cpu)[i] = NULL;

+			if (!met_pmu[i].mode) {/* Skip disabled counters */

+				continue;

+			}

+			per_cpu(perfPrev, cpu)[i] = 0;

+			per_cpu(perfCurr, cpu)[i] = 0;

+			memset(&per_cpu(pevent_attr, cpu)[i], 0, size);

+			per_cpu(pevent_attr, cpu)[i].config = met_pmu[i].event;

+			per_cpu(pevent_attr, cpu)[i].type = PERF_TYPE_RAW;

+			per_cpu(pevent_attr, cpu)[i].size = size;

+			per_cpu(pevent_attr, cpu)[i].sample_period = 0;

+			per_cpu(pevent_attr, cpu)[i].pinned = 1;

+			if (met_pmu[i].event == 0xff) {

+				per_cpu(pevent_attr, cpu)[i].type = PERF_TYPE_HARDWARE;

+				per_cpu(pevent_attr, cpu)[i].config = PERF_COUNT_HW_CPU_CYCLES;

+			}

+

+			per_cpu(pevent, cpu)[i] =

+			    perf_event_create_kernel_counter(&per_cpu(pevent_attr, cpu)[i], cpu, NULL,

+				     dummy_handler, NULL);

+			if (IS_ERR(per_cpu(pevent, cpu)[i])) {

+				per_cpu(pevent, cpu)[i] = NULL;

+				PR_BOOTMSG("CPU=%d, %s:%d\n", cpu, __FUNCTION__, __LINE__);

+				continue;

+			}

+

+			if (per_cpu(pevent, cpu)[i]->state != PERF_EVENT_STATE_ACTIVE) {

+				perf_event_release_kernel(per_cpu(pevent, cpu)[i]);

+				per_cpu(pevent, cpu)[i] = NULL;

+				PR_BOOTMSG("CPU=%d, %s:%d\n", cpu, __FUNCTION__, __LINE__);

+				continue;

+			}

+

+			ev = per_cpu(pevent, cpu)[i];

+			if (ev != NULL) {

+				perf_event_enable(ev);

+			}

+		} /* for all PMU counter */

+		per_cpu(perfSet, cpu) = 1;

+	} /* for perfSet */

+	return 0;

+}

+

+

+static void perf_thread_setup_v2(struct work_struct *work)

+{

+	unsigned int cpu;

+	struct delayed_work *dwork = to_delayed_work(work);

+

+	cpu = dwork->cpu;

+	if (per_cpu(perf_task_init_done, cpu) == 0) {

+		per_cpu(perf_task_init_done, cpu) = 1;

+		perf_thread_set_perf_events_v2(cpu);

+	}

+

+	return ;

+}

+

+

+void met_perf_cpupmu_online_v2(unsigned int cpu)

+{

+	if (met_cpupmu_v2.mode == 0) {

+		PR_BOOTMSG("CPU=%d, %s:%d\n", cpu, __FUNCTION__, __LINE__);

+		return;

+	}

+

+	per_cpu(perf_cpuid, cpu) = cpu;

+	if (per_cpu(perf_delayed_work_setup, cpu) == NULL) {

+		struct delayed_work *dwork;

+

+		dwork = &per_cpu(cpu_pmu_dwork, cpu);

+		dwork->cpu = cpu;

+		INIT_DELAYED_WORK(dwork, perf_thread_setup_v2);

+		schedule_delayed_work(dwork, 0);

+		per_cpu(perf_delayed_work_setup, cpu) = dwork;

+	}

+}

+

+

+void met_perf_cpupmu_down_v2(void *data)

+{

+	unsigned int cpu;

+	unsigned int i;

+	struct perf_event *ev;

+	struct met_pmu_v2 *met_pmu;

+

+	cpu = *((unsigned int *)data);

+	if (met_cpupmu_v2.mode == 0)

+		return;

+	if (per_cpu(perfSet, cpu) == 0)

+		return;

+

+	met_pmu = get_met_pmu_by_cpu_id(cpu);

+	per_cpu(perfSet, cpu) = 0;

+	for (i = 0; i < gMAX_PMU_HW_CNT; i++) {

+		if (!met_pmu[i].mode)

+			continue;

+		ev = per_cpu(pevent, cpu)[i];

+		if ((ev != NULL) && (ev->state == PERF_EVENT_STATE_ACTIVE)) {

+			perf_event_disable(ev);

+			perf_event_release_kernel(ev);

+		}

+	}

+	per_cpu(perf_task_init_done, cpu) = 0;

+	per_cpu(perf_delayed_work_setup, cpu) = NULL;

+}

+

+

+void met_perf_cpupmu_start_v2(void)

+{

+	unsigned int cpu;

+

+	for_each_online_cpu(cpu) {

+		met_perf_cpupmu_online_v2(cpu);

+	}

+}

+

+

+void met_perf_cpupmu_stop_v2(void)

+{

+	unsigned int cpu;

+

+	for_each_online_cpu(cpu) {

+		per_cpu(perf_cpuid, cpu) = cpu;

+		met_perf_cpupmu_down_v2((void *)&per_cpu(perf_cpuid, cpu));

+	}

+}

+

+

+static void cpupmu_polling(unsigned long long stamp, int cpu)

+{

+	int count;

+	struct met_pmu_v2 *met_pmu;

+	unsigned int pmu_value[MXNR_CPU_V2];

+

+	met_pmu = get_met_pmu_by_cpu_id(cpu);

+	if (met_cpu_pmu_method == 0) {

+		count = met_pmu_hw_v2->polling(met_pmu, gMAX_PMU_HW_CNT, pmu_value);

+		mp_cpu_v2(count, pmu_value);

+	} else

+		perf_cpupmu_polling_v2(stamp, cpu);

+}

+

+

+void cpupmu_polling_v2(unsigned long long stamp, int cpu)

+{

+	cpupmu_polling(stamp, cpu);

+}

+

+

+static void _cpupmu_start(void *info)

+{

+	unsigned int *cpu = (unsigned int *)info;

+	struct met_pmu_v2 *met_pmu;

+

+	met_pmu = get_met_pmu_by_cpu_id(*cpu);

+	met_pmu_hw_v2->start(met_pmu, gMAX_PMU_HW_CNT);

+}

+

+static void cpupmu_start(void)

+{

+	if (module_status == 1) {

+		PR_BOOTMSG("%s:%d\n", __FUNCTION__, __LINE__);

+		return;

+	}

+

+	if (met_cpu_pmu_method == 0) {

+		int this_cpu = smp_processor_id();

+		int cpu;

+

+		for_each_possible_cpu(cpu) {

+			if (cpu == this_cpu)

+				_cpupmu_start(&cpu);

+			else

+				met_smp_call_function_single_symbol(cpu, _cpupmu_start, &cpu, 1);

+		}

+	}

+	module_status = 1;

+}

+

+static void _cpupmu_stop(void *info)

+{

+	(void)info;

+

+	met_pmu_hw_v2->stop(gMAX_PMU_HW_CNT);

+}

+

+static void cpupmu_stop(void)

+{

+	if (module_status == 0) {

+		PR_BOOTMSG("%s:%d\n", __FUNCTION__, __LINE__);

+		return;

+	}

+

+	if (met_cpu_pmu_method == 0) {

+		int this_cpu = smp_processor_id();

+		int cpu;

+

+		for_each_possible_cpu(cpu) {

+			if (cpu == this_cpu)

+				_cpupmu_stop(&cpu);

+			else

+				met_smp_call_function_single_symbol(cpu, _cpupmu_stop, &cpu, 1);

+		}

+	}

+	module_status = 0;

+}

+

+static int cpupmu_print_help(char *buf, int len)

+{

+	return snprintf(buf, PAGE_SIZE, help, met_pmu_hw_v2->name, gMAX_PMU_HW_CNT - 1);

+}

+

+static int cpupmu_print_header(char *buf, int len)

+{

+	int i;

+	int ret = 0;

+	int pmu_cnt = 0;

+	char name[32];

+	unsigned int cpu;

+	struct met_pmu_v2 *met_pmu;

+

+	/*append CPU PMU access method*/

+	if (met_cpu_pmu_method == 0)

+		ret += snprintf(buf + ret, PAGE_SIZE,

+			"met-info [000] 0.0: CPU_PMU_method: PMU registers\n");

+	else

+		ret += snprintf(buf + ret, PAGE_SIZE,

+			"met-info [000] 0.0: CPU_PMU_method: perf APIs\n");

+

+	/*append cache line size*/

+	ret += snprintf(buf + ret, PAGE_SIZE - ret, cache_line_header, cache_line_size());

+	ret += snprintf(buf + ret, PAGE_SIZE - ret, header);

+	for_each_online_cpu(cpu) {

+		int cnt = 0;

+

+		pmu_cnt = gPMU_CNT[cpu];

+		met_pmu = get_met_pmu_by_cpu_id(cpu);

+		for (i = 0; i < pmu_cnt; i++) {

+			if (met_pmu[i].mode == 0)

+				continue;

+

+			if (met_pmu_hw_v2->get_event_desc && 0 == met_pmu_hw_v2->get_event_desc(met_pmu[i].event, name)) {

+				if (cnt == 0) {

+					ret += snprintf(buf + ret, PAGE_SIZE - ret, "CPU-%d=0x%x:%s", cpu, met_pmu[i].event, name);

+					cnt++;

+				} else

+					ret += snprintf(buf + ret, PAGE_SIZE - ret, ",0x%x:%s", met_pmu[i].event, name);

+			}

+			met_pmu[i].mode = 0;

+		}

+		if (cnt > 0 && cpu < MXNR_CPU_V2 - 1)

+			ret += snprintf(buf + ret, PAGE_SIZE - ret, ";");

+	}

+	ret += snprintf(buf + ret, PAGE_SIZE - ret, "\n");

+	met_cpupmu_v2.mode = 0;

+	reset_driver_stat();

+	return ret;

+}

+

+/*

+ * "met-cmd --start --pmu_core_evt=0:0x3,0x16,0x17"

+ */

+static int cpupmu_process_argument(const char *arg, int len)

+{

+	int ret;

+	unsigned int cpu;

+	unsigned int value;

+	unsigned int idx = 0;

+	char *str = NULL;

+	char *token = NULL;

+	struct met_pmu_v2 *met_pmu = NULL;

+

+	if (met_cpu_pmu_method == 0)

+		gMAX_PMU_HW_CNT = met_pmu_hw_v2->max_hw_count;

+	else

+		gMAX_PMU_HW_CNT = perf_num_counters();

+

+	if (gMAX_PMU_HW_CNT == 0) {

+		PR_BOOTMSG("%s:%d\n", __FUNCTION__, __LINE__);

+		goto arg_out;

+	}

+

+	str = kstrdup(arg, GFP_KERNEL);

+	token = strsep(&str, ":");

+	ret = met_parse_num(token, &cpu, strlen(token));

+	if (ret != 0) {

+		PR_BOOTMSG("%s:%d\n", __FUNCTION__, __LINE__);

+		goto arg_out;

+	}

+

+	met_pmu = get_met_pmu_by_cpu_id(cpu);

+	while (token && met_pmu && idx < gMAX_PMU_HW_CNT) {

+		token = strsep(&str, ",\r\n");

+		if (token) {

+			ret = met_parse_num(token, &value, strlen(token));

+			if (ret != 0) {

+				PR_BOOTMSG("%s:%d\n", __FUNCTION__, __LINE__);

+				goto arg_out;

+			}

+			if (value != 0xff) {

+				if (idx >= (gMAX_PMU_HW_CNT - 1)) {

+					PR_BOOTMSG("%s:%d\n", __FUNCTION__, __LINE__);

+					goto arg_out;

+				}

+				met_pmu[idx].mode = MODE_POLLING;

+				met_pmu[idx].event = value;

+				idx++;

+				gPMU_CNT[cpu]++;

+			} else {

+				if (met_cpu_pmu_method == 0) {

+					met_pmu[gMAX_PMU_HW_CNT - 1].mode = MODE_POLLING;

+					met_pmu[gMAX_PMU_HW_CNT - 1].event = 0xff;

+					gPMU_CNT[cpu]++;

+				} else {

+					if (idx > (gMAX_PMU_HW_CNT - 1)) {

+						PR_BOOTMSG("%s:%d\n", __FUNCTION__, __LINE__);

+						goto arg_out;

+					}

+					met_pmu[idx].mode = MODE_POLLING;

+					met_pmu[idx].event = 0xff;

+					idx++;

+					gPMU_CNT[cpu]++;

+				}

+			}

+			if (met_pmu_hw_v2->check_event(met_pmu, gPMU_CNT[cpu], value) < 0) {

+				PR_BOOTMSG("%s:%d\n", __FUNCTION__, __LINE__);

+				goto arg_out;

+			}

+		}

+	}

+	met_cpupmu_v2.mode = 1;

+	module_status = 0;

+	return 0;

+

+arg_out:

+	if (str)

+		kfree(str);

+	reset_driver_stat();

+	return -EINVAL;

+}

diff --git a/src/devtools/met-driver/4.4/common/cpu_pmu_v2.h b/src/devtools/met-driver/4.4/common/cpu_pmu_v2.h
new file mode 100644
index 0000000..6af56bb
--- /dev/null
+++ b/src/devtools/met-driver/4.4/common/cpu_pmu_v2.h
@@ -0,0 +1,85 @@
+/*

+ * Copyright (C) 2018 MediaTek Inc.

+ *

+ * 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.

+ *

+ * This program is distributed in the hope that it will be useful,

+ * but WITHOUT ANY WARRANTY; without even the implied warranty of

+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the

+ * GNU General Public License for more details.

+ */

+

+#ifndef _PMU_V2_H_

+#define _PMU_V2_H_

+

+#include <linux/device.h>

+#include <linux/threads.h> /* NR_CPUS */

+

+#define MODE_DISABLED	0

+#define MODE_INTERRUPT	1

+#define MODE_POLLING	2

+#define MXSIZE_PMU_DESC 32

+#define MXNR_CPU_V2     NR_CPUS

+

+

+/*******************************************************************************

+*                                Type Define

+*******************************************************************************/

+struct met_pmu_v2 {

+	unsigned char mode;

+	unsigned short event;

+	struct kobject *kobj_cpu_pmu;

+        const char *cpu_name;

+};

+

+enum ARM_TYPE_v2 {

+	CORTEX_A53 = 0xD03,

+	CORTEX_A35 = 0xD04,

+	CORTEX_A57 = 0xD07,

+	CORTEX_A72 = 0xD08,

+	CORTEX_A73 = 0xD09,

+	CHIP_UNKNOWN = 0xFFF

+};

+

+struct pmu_desc_v2 {

+	unsigned int event;

+	char name[MXSIZE_PMU_DESC];

+};

+

+struct chip_pmu_v2 {

+	enum ARM_TYPE_v2 type;

+	struct pmu_desc_v2 *desc;

+        unsigned int pmu_count;

+	unsigned int hw_count;

+	const char *cpu_name;

+};

+

+struct cpu_pmu_hw_v2 {

+	const char *name;

+        int max_hw_count;

+	int (*get_event_desc)(int event, char *event_desc);

+	int (*check_event)(struct met_pmu_v2 *pmu, int idx, int event);

+	void (*start)(struct met_pmu_v2 *pmu, int count);

+	void (*stop)(int count);

+	unsigned int (*polling)(struct met_pmu_v2 *pmu, int count, unsigned int *pmu_value);

+	struct met_pmu_v2 *met_pmu[MXNR_CPU_V2];

+        struct chip_pmu_v2 *chip_pmu[MXNR_CPU_V2];

+};

+

+

+extern struct cpu_pmu_hw_v2 *cpu_pmu_hw_v2;

+

+

+/*******************************************************************************

+*                                Fuction Pototypes

+*******************************************************************************/

+extern noinline void mp_cpu_v2(unsigned char cnt, unsigned int *value);

+extern void met_perf_cpupmu_online_v2(unsigned int cpu);

+extern void met_perf_cpupmu_down_v2(void *cpu);

+extern void met_perf_cpupmu_start_v2(void);

+extern void met_perf_cpupmu_stop_v2(void);

+extern void cpupmu_polling_v2(unsigned long long stamp, int cpu);

+

+#endif /* _PMU_V2_H_ */

diff --git a/src/devtools/met-driver/4.4/common/dummy_header.c b/src/devtools/met-driver/4.4/common/dummy_header.c
new file mode 100644
index 0000000..f8d1187
--- /dev/null
+++ b/src/devtools/met-driver/4.4/common/dummy_header.c
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+
+#include "interface.h"
+#include "met_drv.h"
+
+static struct kobject *kobj_met_dummy;
+static char header_str[PAGE_SIZE];
+static int header_str_len;
+struct metdevice met_dummy_header;
+
+static ssize_t dummy_str_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf);
+static ssize_t dummy_str_store(struct kobject *kobj,
+			       struct kobj_attribute *attr, const char *buf, size_t n);
+static struct kobj_attribute dummy_attr = __ATTR(dummy_str, 0664, dummy_str_show, dummy_str_store);
+
+
+static ssize_t dummy_str_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	int ret;
+
+	ret = snprintf(buf, PAGE_SIZE, "%s", header_str);
+
+	return ret;
+}
+
+static ssize_t dummy_str_store(struct kobject *kobj,
+			       struct kobj_attribute *attr, const char *buf, size_t n)
+{
+	int ret = 0;
+	char *ptr = header_str;
+
+	if ((header_str_len + strlen(buf)) < PAGE_SIZE) {
+		ret = snprintf(ptr + header_str_len, PAGE_SIZE - header_str_len, "%s\n", buf);
+		header_str_len += ret;
+	}
+	met_dummy_header.mode = 1;
+
+	return n;
+}
+
+static int dummy_reset(void)
+{
+	met_dummy_header.mode = 0;
+	memset(header_str, 0x00, PAGE_SIZE);
+	header_str_len = 0;
+
+	return 0;
+}
+
+static int dummy_print_header(char *buf, int len)
+{
+	if (header_str_len > 0)
+		len = snprintf(buf, PAGE_SIZE, "%s", header_str);
+	else
+		len = snprintf(buf, PAGE_SIZE, "# dummy header is empty\n");
+
+	return len;
+}
+
+static int dummy_create(struct kobject *parent)
+{
+	int ret = 0;
+
+	kobj_met_dummy = parent;
+	ret = sysfs_create_file(kobj_met_dummy, &dummy_attr.attr);
+	if (ret != 0) {
+		pr_debug("Failed to create montype0 in sysfs\n");
+		return ret;
+	}
+
+	return ret;
+}
+
+static void dummy_delete(void)
+{
+	sysfs_remove_file(kobj_met_dummy, &dummy_attr.attr);
+	kobj_met_dummy = NULL;
+}
+
+struct metdevice met_dummy_header = {
+	.name = "dummy_header",
+	.type = MET_TYPE_MISC,
+	.cpu_related = 0,
+	.start = NULL,
+	.stop = NULL,
+	.reset = dummy_reset,
+	.polling_interval = 0,
+	.timed_polling = NULL,
+	.print_help = NULL,
+	.print_header = dummy_print_header,
+	.create_subfs = dummy_create,
+	.delete_subfs = dummy_delete,
+};
diff --git a/src/devtools/met-driver/4.4/common/interface.c b/src/devtools/met-driver/4.4/common/interface.c
new file mode 100644
index 0000000..54b165f
--- /dev/null
+++ b/src/devtools/met-driver/4.4/common/interface.c
@@ -0,0 +1,1390 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/fs.h>
+#include <linux/sched.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/miscdevice.h>
+#include <linux/kallsyms.h>
+#include <linux/syscore_ops.h>
+#include <linux/dma-mapping.h>
+
+#include "interface.h"
+#include "sampler.h"
+#include "util.h"
+
+#include "ondiemet.h"
+
+#include "met_drv.h"
+#include "met_tag.h"
+#include "met_kernel_symbol.h"
+#include "met_api_tbl.h"
+#include "version.h"
+
+extern int enable_met_backlight_tag(void);
+extern int output_met_backlight_tag(int level);
+
+static int run = -1;
+static int sample_rate = 1000;	/* Default: 1000 Hz */
+static int met_suspend_compensation_mode;
+static int met_suspend_compensation_flag;
+
+/* defalut method is perf driver */
+unsigned int met_cpu_pmu_method = 1;
+
+int met_hrtimer_expire;		/* in ns */
+int met_timer_expire;		/* in jiffies */
+unsigned int ctrl_flags;
+int met_mode;
+EXPORT_SYMBOL(met_mode);
+
+DEFINE_PER_CPU(char[MET_STRBUF_SIZE], met_strbuf_nmi);
+EXPORT_SYMBOL(met_strbuf_nmi);
+
+DEFINE_PER_CPU(char[MET_STRBUF_SIZE], met_strbuf_irq);
+EXPORT_SYMBOL(met_strbuf_irq);
+
+DEFINE_PER_CPU(char[MET_STRBUF_SIZE], met_strbuf_sirq);
+EXPORT_SYMBOL(met_strbuf_sirq);
+
+DEFINE_PER_CPU(char[MET_STRBUF_SIZE], met_strbuf);
+EXPORT_SYMBOL(met_strbuf);
+
+static void calc_timer_value(int rate)
+{
+	sample_rate = rate;
+
+	if (rate == 0) {
+		met_hrtimer_expire = 0;
+		met_timer_expire = 0;
+		return;
+	}
+
+	met_hrtimer_expire = 1000000000 / rate;
+
+	/* Case 1: hrtimer < 1 OS tick, met_timer_expire = 1 OS tick */
+	if (rate > HZ)
+		met_timer_expire = 1;
+	/* Case 2: hrtimer > 1 OS tick, met_timer_expire is hrtimer + 1 OS tick */
+	else
+		met_timer_expire = (HZ / rate) + 1;
+
+	/* pr_debug("JBK HZ=%d, met_hrtimer_expire=%d ns, met_timer_expire=%d ticks\n", */
+	/* HZ, met_hrtimer_expire, met_timer_expire); */
+}
+
+int met_parse_num(const char *str, unsigned int *value, int len)
+{
+	int ret;
+
+	if (len <= 0)
+		return -1;
+
+	if ((len > 2) &&
+		((str[0] == '0') &&
+		((str[1] == 'x') || (str[1] == 'X')))) {
+		ret = kstrtoint(str, 16, value);
+	} else {
+		ret = kstrtoint(str, 10, value);
+	}
+
+	if (ret != 0)
+		return -1;
+
+	return 0;
+}
+
+void met_set_suspend_notify(int flag)
+{
+	if (met_suspend_compensation_mode == 1)
+		met_suspend_compensation_flag = flag;
+	else
+		met_suspend_compensation_flag = 0;
+}
+
+LIST_HEAD(met_list);
+static struct kobject *kobj_misc;
+static struct kobject *kobj_pmu;
+static struct kobject *kobj_bus;
+
+static ssize_t ver_show(struct device *dev, struct device_attribute *attr, char *buf);
+static DEVICE_ATTR(ver, 0444, ver_show, NULL);
+
+static ssize_t plf_show(struct device *dev, struct device_attribute *attr, char *buf);
+static DEVICE_ATTR(plf, 0444, plf_show, NULL);
+
+static ssize_t core_topology_show(struct device *dev, struct device_attribute *attr, char *buf);
+static DEVICE_ATTR(core_topology, 0444, core_topology_show, NULL);
+
+static ssize_t devices_show(struct device *dev, struct device_attribute *attr, char *buf);
+static DEVICE_ATTR(devices, 0444, devices_show, NULL);
+
+static ssize_t ctrl_show(struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t ctrl_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			  size_t count);
+static DEVICE_ATTR(ctrl, 0664, ctrl_show, ctrl_store);
+
+static ssize_t spr_show(struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t spr_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			 size_t count);
+static DEVICE_ATTR(sample_rate, 0664, spr_show, spr_store);
+
+static ssize_t run_show(struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t run_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			 size_t count);
+static DEVICE_ATTR(run, 0664, run_show, run_store);
+
+static ssize_t ksym_show(struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t ksym_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			 size_t count);
+static DEVICE_ATTR(ksym, 0664, ksym_show, ksym_store);
+
+static ssize_t cpu_pmu_method_show(struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t cpu_pmu_method_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			  size_t count);
+static DEVICE_ATTR(cpu_pmu_method, 0664, cpu_pmu_method_show, cpu_pmu_method_store);
+
+#ifdef PR_CPU_NOTIFY
+int met_cpu_notify;
+static ssize_t cpu_notify_show(struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t cpu_notify_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			  size_t count);
+static DEVICE_ATTR(cpu_notify, 0664, cpu_notify_show, cpu_notify_store);
+#endif
+
+#ifdef CONFIG_CPU_FREQ
+static ssize_t dvfs_show(struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t dvfs_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			  size_t count);
+static DEVICE_ATTR(dvfs, 0664, dvfs_show, dvfs_store);
+#endif
+
+static ssize_t suspend_compensation_enable_show(struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t suspend_compensation_enable_store(struct device *dev, struct device_attribute *attr,
+							const char *buf, size_t count);
+static DEVICE_ATTR(suspend_compensation_enable, 0664, suspend_compensation_enable_show,
+			suspend_compensation_enable_store);
+
+static ssize_t suspend_compensation_flag_show(struct device *dev, struct device_attribute *attr, char *buf);
+static DEVICE_ATTR(suspend_compensation_flag, 0444, suspend_compensation_flag_show, NULL);
+
+
+static ssize_t ipi_test_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			 size_t count);
+static DEVICE_ATTR(ipi_test, 0664, NULL, ipi_test_store);
+
+static const struct file_operations met_file_ops = {
+	.owner = THIS_MODULE
+};
+
+struct miscdevice met_device = {
+	.minor = MISC_DYNAMIC_MINOR,
+	.name = "met",
+	.mode = 0664,
+	.fops = &met_file_ops
+};
+EXPORT_SYMBOL(met_device);
+
+static int met_run(void)
+{
+	sampler_start();
+#ifdef MET_USER_EVENT_SUPPORT
+	bltab.flag &= (~MET_CLASS_ALL);
+#endif
+	ondiemet_start();
+	return 0;
+}
+
+static void met_stop(void)
+{
+#ifdef MET_USER_EVENT_SUPPORT
+	bltab.flag |= MET_CLASS_ALL;
+#endif
+	sampler_stop();
+	/* the met.ko will be use by script "cat ...", release it */
+	if ((ondiemet_module[ONDIEMET_SSPM] == 0) || (sspm_buffer_size == -1))
+		ondiemet_log_manager_stop();
+	ondiemet_stop();
+	ondiemet_extract();
+}
+
+static ssize_t ver_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int i;
+
+	mutex_lock(&dev->mutex);
+	i = snprintf(buf, PAGE_SIZE, "%s\n", MET_BACKEND_VERSION);
+	mutex_unlock(&dev->mutex);
+	return i;
+}
+
+static ssize_t devices_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int len, total_len = 0;
+	struct metdevice *c = NULL;
+
+	mutex_lock(&dev->mutex);
+	list_for_each_entry(c, &met_list, list) {
+		len = 0;
+		if (c->type == MET_TYPE_PMU)
+			len = snprintf(buf, PAGE_SIZE - total_len, "pmu/%s:0\n", c->name);
+		else if (c->type == MET_TYPE_BUS)
+			len = snprintf(buf, PAGE_SIZE - total_len, "bus/%s:0\n", c->name);
+		else if (c->type == MET_TYPE_MISC)
+			len = snprintf(buf, PAGE_SIZE - total_len, "misc/%s:0\n", c->name);
+
+		if (c->ondiemet_mode == 0) {
+			if (c->process_argument)
+				buf[len - 2]++;
+		} else if (c->ondiemet_mode == 1) {
+			if (c->ondiemet_process_argument)
+				buf[len - 2]++;
+		} else if (c->ondiemet_mode == 2) {
+			if (c->process_argument)
+				buf[len - 2]++;
+			if (c->ondiemet_process_argument)
+				buf[len - 2]++;
+		}
+
+		buf += len;
+		total_len += len;
+	}
+
+	mutex_unlock(&dev->mutex);
+	return total_len;
+}
+
+static char met_platform[16] = "none";
+static ssize_t plf_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int i;
+
+	mutex_lock(&dev->mutex);
+	i = snprintf(buf, PAGE_SIZE, "%s\n", met_platform);
+	mutex_unlock(&dev->mutex);
+	return i;
+}
+
+static char met_topology[64] = "none";
+static ssize_t core_topology_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int i;
+
+	mutex_lock(&dev->mutex);
+	i = snprintf(buf, PAGE_SIZE, "%s\n", met_topology);
+	mutex_unlock(&dev->mutex);
+	return i;
+}
+
+static ssize_t ctrl_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%d\n", ctrl_flags);
+}
+
+static ssize_t ctrl_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			  size_t count)
+{
+	unsigned int value = 0;
+
+	if (met_parse_num(buf, &value, count) < 0)
+		return -EINVAL;
+
+	ctrl_flags = value;
+	return count;
+}
+
+static ssize_t cpu_pmu_method_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%d\n", met_cpu_pmu_method);
+}
+
+static ssize_t cpu_pmu_method_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			  size_t count)
+{
+	unsigned int value;
+
+	if (met_parse_num(buf, &value, count) < 0)
+		return -EINVAL;
+
+	met_cpu_pmu_method = value;
+	return count;
+}
+
+
+static void _test_trace_ipi_raise(void *info)
+{
+	unsigned int *cpu = (unsigned int *)info;
+	void (*arch_send_call_function_single_ipi_sym)(int cpu) = NULL;
+
+	arch_send_call_function_single_ipi_sym = (void *)symbol_get(met_arch_send_call_function_single_ipi);
+	if (arch_send_call_function_single_ipi_sym)
+		arch_send_call_function_single_ipi_sym(*cpu);
+}
+
+static ssize_t ipi_test_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			 size_t count)
+{
+	int this_cpu = smp_processor_id();
+	unsigned int cpu = 0;
+	unsigned int value;
+
+	if (met_parse_num(buf, &value, count) < 0)
+		return -EINVAL;
+
+	cpu = value;
+	if (cpu == this_cpu)
+		_test_trace_ipi_raise(&cpu);
+	else
+		met_smp_call_function_single_symbol(cpu, _test_trace_ipi_raise, &cpu, 1);
+
+	return count;
+}
+
+
+#if	defined(MET_BOOT_MSG)
+char met_boot_msg_tmp[256];
+char met_boot_msg[PAGE_SIZE];
+int met_boot_msg_idx;
+
+int pr_bootmsg(int str_len, char *str)
+{
+	if (met_boot_msg_idx+str_len+1 > PAGE_SIZE)
+		return -1;
+	memcpy(met_boot_msg+met_boot_msg_idx, str, str_len);
+	met_boot_msg_idx += str_len;
+	return 0;
+}
+
+static ssize_t bootmsg_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int	i;
+
+	mutex_lock(&dev->mutex);
+	i = snprintf(buf, PAGE_SIZE, "%s\n", met_boot_msg);
+	mutex_unlock(&dev->mutex);
+	return i;
+}
+
+static DEVICE_ATTR_RO(bootmsg);
+EXPORT_SYMBOL(met_boot_msg_tmp);
+EXPORT_SYMBOL(pr_bootmsg);
+#endif
+
+static ssize_t spr_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int i;
+
+	mutex_lock(&dev->mutex);
+	i = snprintf(buf, PAGE_SIZE, "%d\n", sample_rate);
+	mutex_unlock(&dev->mutex);
+	return i;
+}
+
+static ssize_t spr_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			 size_t count)
+{
+	int value;
+	struct metdevice *c = NULL;
+
+	mutex_lock(&dev->mutex);
+
+	if ((run == 1) || (count == 0) || (buf == NULL)) {
+		mutex_unlock(&dev->mutex);
+		return -EINVAL;
+	}
+	if (kstrtoint(buf, 0, &value) != 0) {
+		mutex_unlock(&dev->mutex);
+		return -EINVAL;
+	}
+
+	if ((value < 0) || (value > 10000)) {
+		mutex_unlock(&dev->mutex);
+		return -EINVAL;
+	}
+
+	calc_timer_value(value);
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->polling_interval > 0)
+			c->polling_count_reload = ((c->polling_interval * sample_rate) - 1) / 1000;
+		else
+			c->polling_count_reload = 0;
+	}
+
+	mutex_unlock(&dev->mutex);
+
+	return count;
+}
+
+static ssize_t run_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int i;
+
+	mutex_lock(&dev->mutex);
+	i = snprintf(buf, PAGE_SIZE, "%d\n", run);
+	mutex_unlock(&dev->mutex);
+	return i;
+}
+
+static ssize_t run_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			 size_t count)
+{
+	int value;
+
+	mutex_lock(&dev->mutex);
+
+	if ((count == 0) || (buf == NULL)) {
+		mutex_unlock(&dev->mutex);
+		return -EINVAL;
+	}
+	if (kstrtoint(buf, 0, &value) != 0) {
+		mutex_unlock(&dev->mutex);
+		return -EINVAL;
+	}
+
+	switch (value) {
+	case 1:
+		if (run != 1) {
+			run = 1;
+			met_run();
+		}
+		break;
+	case 0:
+		if (run != 0) {
+			if (run == 1) {
+				met_stop();
+#ifdef MET_USER_EVENT_SUPPORT
+#ifdef CONFIG_MET_MODULE
+				met_save_dump_buffer_real("/data/trace.dump");
+#else
+				met_save_dump_buffer("/data/trace.dump");
+#endif
+#endif
+				run = 0;
+			} else
+				/* run == -1 */
+				run = 0;
+		}
+		break;
+	case -1:
+		if (run != -1) {
+			if (run == 1)
+				met_stop();
+
+			run = -1;
+		}
+		break;
+	default:
+		mutex_unlock(&dev->mutex);
+		return -EINVAL;
+	}
+
+	mutex_unlock(&dev->mutex);
+
+	return count;
+}
+
+static unsigned int met_ksym_addr;
+static char met_func_name[512];
+static ssize_t ksym_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int i;
+	int len = 0;
+	int idx = 0;
+
+	mutex_lock(&dev->mutex);
+	if (met_ksym_addr != 0)
+		len = sprint_symbol_no_offset(met_func_name, met_ksym_addr);
+	if (len != 0) {
+		for (idx = 0; idx < 512; idx++)
+			if (met_func_name[idx] == ' ')
+				met_func_name[idx] = '\0';
+		i = snprintf(buf, PAGE_SIZE, "%s\n", met_func_name);
+	} else
+		i = snprintf(buf, PAGE_SIZE, "ksymlookup fail(%x)\n", met_ksym_addr);
+
+	mutex_unlock(&dev->mutex);
+	return i;
+}
+
+static ssize_t ksym_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			 size_t count)
+{
+	mutex_lock(&dev->mutex);
+
+	if ((count == 0) || (buf == NULL)) {
+		mutex_unlock(&dev->mutex);
+		return -EINVAL;
+	}
+	if (kstrtoint(buf, 16, &met_ksym_addr) != 0) {
+		mutex_unlock(&dev->mutex);
+		return -EINVAL;
+	}
+
+	mutex_unlock(&dev->mutex);
+
+	return count;
+}
+
+#if	defined(PR_CPU_NOTIFY)
+static ssize_t cpu_notify_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int i;
+
+	i = snprintf(buf, PAGE_SIZE, "%d\n", met_cpu_notify);
+	return i;
+}
+
+static ssize_t cpu_notify_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			  size_t count)
+{
+	if ((count == 0) || (buf == NULL))
+		return -EINVAL;
+
+	if (kstrtoint(buf, 0, &met_cpu_notify) != 0)
+		return -EINVAL;
+
+	return count;
+}
+#endif
+
+#ifdef CONFIG_CPU_FREQ
+static ssize_t dvfs_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int i;
+
+	i = snprintf(buf, PAGE_SIZE, "%d\n", 0);
+	return i;
+}
+
+static ssize_t dvfs_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			  size_t count)
+{
+	return count;
+}
+#endif
+
+static ssize_t suspend_compensation_enable_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int ret;
+
+	ret = snprintf(buf, PAGE_SIZE, "%d\n", met_suspend_compensation_mode);
+
+	return ret;
+}
+
+static ssize_t suspend_compensation_enable_store(struct device *dev, struct device_attribute *attr,
+			const char *buf, size_t count)
+{
+	int value;
+
+	if ((count == 0) || (buf == NULL))
+		return -EINVAL;
+
+	if (kstrtoint(buf, 0, &value) != 0)
+		return -EINVAL;
+
+	if (value < 0)
+		return -EINVAL;
+
+	met_suspend_compensation_mode = value;
+
+	return count;
+}
+
+static ssize_t suspend_compensation_flag_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int ret;
+
+	ret = snprintf(buf, PAGE_SIZE, "%d\n", met_suspend_compensation_flag);
+
+	return ret;
+}
+
+static ssize_t hash_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	return 0;
+}
+
+static ssize_t hash_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			 size_t count)
+{
+	return 0;
+}
+
+static DEVICE_ATTR(hash, 0664, hash_show, hash_store);
+
+static ssize_t mode_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	struct metdevice *c = NULL;
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->kobj == kobj)
+			break;
+	}
+	if (c == NULL)
+		return -ENOENT;
+
+	return snprintf(buf, PAGE_SIZE, "%d\n", c->mode);
+}
+
+static ssize_t mode_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf,
+			  size_t n)
+{
+	struct metdevice *c = NULL;
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->kobj == kobj)
+			break;
+	}
+	if (c == NULL)
+		return -ENOENT;
+
+	if (kstrtoint(buf, 0, &(c->mode)) != 0)
+		return -EINVAL;
+
+	return n;
+}
+
+static struct kobj_attribute mode_attr = __ATTR(mode, 0664, mode_show, mode_store);
+
+static ssize_t ondiemet_mode_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	struct metdevice *c = NULL;
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->kobj == kobj)
+			break;
+	}
+	if (c == NULL)
+		return -ENOENT;
+
+	return snprintf(buf, PAGE_SIZE, "%d\n", c->ondiemet_mode);
+}
+
+static ssize_t ondiemet_mode_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf,
+			  size_t n)
+{
+	struct metdevice *c = NULL;
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->kobj == kobj)
+			break;
+	}
+	if (c == NULL)
+		return -ENOENT;
+
+	if (kstrtoint(buf, 0, &(c->ondiemet_mode)) != 0)
+		return -EINVAL;
+
+	return n;
+}
+
+static struct kobj_attribute ondiemet_mode_attr = __ATTR(ondiemet_mode, 0664, ondiemet_mode_show, ondiemet_mode_store);
+
+static ssize_t polling_interval_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	int interval = 1;
+	struct metdevice *c = NULL;
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->kobj == kobj)
+			break;
+	}
+	if (c == NULL)
+		return -ENOENT;
+
+	if (c->polling_interval)
+		interval = c->polling_interval;
+
+	return snprintf(buf, PAGE_SIZE, "%d\n", interval);
+}
+
+static ssize_t polling_interval_store(struct kobject *kobj, struct kobj_attribute *attr,
+				      const char *buf, size_t n)
+{
+	struct metdevice *c = NULL;
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->kobj == kobj)
+			break;
+	}
+	if (c == NULL)
+		return -ENOENT;
+
+	if (kstrtoint(buf, 0, &(c->polling_interval)) != 0)
+		return -EINVAL;
+
+	if (c->polling_interval > 0)
+		c->polling_count_reload = ((c->polling_interval * sample_rate) - 1) / 1000;
+	else
+		c->polling_count_reload = 0;
+
+	return n;
+}
+
+static struct kobj_attribute polling_interval_attr =
+__ATTR(polling_ms, 0664, polling_interval_show, polling_interval_store);
+
+static ssize_t header_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	struct metdevice *c = NULL;
+	ssize_t count = 0;
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->kobj == kobj)
+			break;
+	}
+	if (c == NULL)
+		return -ENOENT;
+
+	if (c->ondiemet_mode == 0) {
+		if ((c->mode) && (c->print_header))
+			return c->print_header(buf, PAGE_SIZE);
+	} else if (c->ondiemet_mode == 1) {
+		if ((c->mode) && (c->ondiemet_print_header))
+			return c->ondiemet_print_header(buf, PAGE_SIZE);
+	} else if (c->ondiemet_mode == 2) {
+		if ((c->mode) && (c->print_header))
+			count = c->print_header(buf, PAGE_SIZE);
+		if (count < PAGE_SIZE) {
+			if ((c->mode) && (c->ondiemet_print_header))
+				count += c->ondiemet_print_header(buf+count, PAGE_SIZE - count);
+		}
+		return count;
+	}
+
+	return 0;
+}
+
+static struct kobj_attribute header_attr = __ATTR(header, 0444, header_show, NULL);
+
+static ssize_t help_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	struct metdevice *c = NULL;
+	ssize_t count = 0;
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->kobj == kobj)
+			break;
+	}
+	if (c == NULL)
+		return -ENOENT;
+
+	if (c->ondiemet_mode == 0) {
+		if (c->print_help)
+			return c->print_help(buf, PAGE_SIZE);
+	} else if (c->ondiemet_mode == 1) {
+		if (c->ondiemet_print_help)
+			return c->ondiemet_print_help(buf, PAGE_SIZE);
+	} else if (c->ondiemet_mode == 2) {
+		if (c->print_help)
+			count = c->print_help(buf, PAGE_SIZE);
+		if (count < PAGE_SIZE) {
+			if (c->ondiemet_print_help)
+				count += c->ondiemet_print_help(buf+count, PAGE_SIZE - count);
+		}
+		return count;
+	}
+
+	return 0;
+}
+
+static struct kobj_attribute help_attr = __ATTR(help, 0444, help_show, NULL);
+
+static int argu_status = -1;
+static ssize_t argu_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf,
+			  size_t n)
+{
+	int ret = 0;
+	struct metdevice *c = NULL;
+
+	argu_status = -1;
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->kobj == kobj)
+			break;
+	}
+	if (c == NULL)
+		return -ENOENT;
+
+	if (c->ondiemet_mode == 0) {
+		if (c->process_argument)
+			ret = c->process_argument(buf, (int)n);
+	} else if (c->ondiemet_mode == 1) {
+		if (c->ondiemet_process_argument)
+			ret = c->ondiemet_process_argument(buf, (int)n);
+	} else if (c->ondiemet_mode == 2) {
+		if (c->process_argument)
+			ret = c->process_argument(buf, (int)n);
+		if (c->ondiemet_process_argument)
+			ret = c->ondiemet_process_argument(buf, (int)n);
+	}
+
+	if (ret != 0)
+		return -EINVAL;
+
+	argu_status = 0;
+	return n;
+}
+
+static ssize_t argu_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%d\n", argu_status);
+}
+
+static struct kobj_attribute argu_attr = __ATTR(argu, 0664, argu_show, argu_store);
+
+static ssize_t reset_store(struct kobject *kobj,
+			struct kobj_attribute *attr,
+			const char *buf,
+			size_t n)
+{
+	int ret = 0;
+	struct metdevice *c = NULL;
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->kobj == kobj)
+			break;
+	}
+	if (c == NULL)
+		return -ENOENT;
+
+	if (c->ondiemet_mode == 0) {
+		if (c->reset)
+			ret = c->reset();
+		else
+			c->mode = 0;
+	} else if (c->ondiemet_mode == 1) {
+		if (c->ondiemet_reset)
+			ret = c->ondiemet_reset();
+	} else if (c->ondiemet_mode == 2) {
+		if (c->reset)
+			ret = c->reset();
+		else
+			c->mode = 0;
+		if (c->ondiemet_reset)
+			ret = c->ondiemet_reset();
+	}
+
+	if (ret != 0)
+		return -EINVAL;
+
+	return n;
+}
+
+static struct kobj_attribute reset_attr = __ATTR(reset, 0220, NULL, reset_store);
+
+static ssize_t header_read_again_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	struct metdevice *c = NULL;
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->kobj == kobj)
+			break;
+	}
+	if (c == NULL)
+		return -ENOENT;
+
+	return snprintf(buf, PAGE_SIZE, "%d\n", c->header_read_again);
+}
+
+static struct kobj_attribute header_read_again_attr = __ATTR(header_read_again, 0664, header_read_again_show, NULL);
+
+
+int met_register(struct metdevice *met)
+{
+	int ret, cpu;
+	struct metdevice *c;
+
+	list_for_each_entry(c, &met_list, list) {
+		if (!strcmp(c->name, met->name))
+			return -EEXIST;
+	}
+
+	PR_BOOTMSG("met_register %s ...\n", met->name);
+
+	INIT_LIST_HEAD(&met->list);
+
+	/* Allocate timer count for per CPU */
+	met->polling_count = alloc_percpu(int);
+	if (met->polling_count == NULL)
+		return -EINVAL;
+
+	for_each_possible_cpu(cpu)
+		*(per_cpu_ptr(met->polling_count, cpu)) = 0;
+
+	if (met->polling_interval > 0) {
+		ret = ((met->polling_interval * sample_rate) - 1) / 1000;
+		met->polling_count_reload = ret;
+	} else
+		met->polling_count_reload = 0;
+
+	met->kobj = NULL;
+
+	if (met->type == MET_TYPE_BUS)
+		met->kobj = kobject_create_and_add(met->name, kobj_bus);
+	else if (met->type == MET_TYPE_PMU)
+		met->kobj = kobject_create_and_add(met->name, kobj_pmu);
+	else if (met->type == MET_TYPE_MISC)
+		met->kobj = kobject_create_and_add(met->name, kobj_misc);
+	else {
+		ret = -EINVAL;
+		goto err_out;
+	}
+
+	if (met->kobj == NULL) {
+		ret = -EINVAL;
+		goto err_out;
+	}
+
+	if (met->create_subfs) {
+		ret = met->create_subfs(met->kobj);
+		if (ret)
+			goto err_out;
+	}
+
+	ret = sysfs_create_file(met->kobj, &mode_attr.attr);
+	if (ret)
+		goto err_out;
+
+
+	ret = sysfs_create_file(met->kobj, &ondiemet_mode_attr.attr);
+	if (ret)
+		goto err_out;
+
+	ret = sysfs_create_file(met->kobj, &polling_interval_attr.attr);
+	if (ret)
+		goto err_out;
+
+	ret = sysfs_create_file(met->kobj, &header_read_again_attr.attr);
+	if (ret)
+		goto err_out;
+
+	if (met->print_header || met->ondiemet_print_header) {
+		ret = sysfs_create_file(met->kobj, &header_attr.attr);
+		if (ret)
+			goto err_out;
+	}
+
+	if (met->print_help || met->ondiemet_print_help) {
+		ret = sysfs_create_file(met->kobj, &help_attr.attr);
+		if (ret)
+			goto err_out;
+	}
+
+	if (met->process_argument || met->ondiemet_process_argument) {
+		ret = sysfs_create_file(met->kobj, &argu_attr.attr);
+		if (ret)
+			goto err_out;
+	}
+
+	if (met->reset) {
+		ret = sysfs_create_file(met->kobj, &reset_attr.attr);
+		if (ret)
+			goto err_out;
+	}
+
+	spin_lock_init(&met->my_lock);
+
+	list_add(&met->list, &met_list);
+	return 0;
+
+ err_out:
+
+	if (met->polling_count)
+		free_percpu(met->polling_count);
+
+	if (met->kobj) {
+		kobject_del(met->kobj);
+		kobject_put(met->kobj);
+		met->kobj = NULL;
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL(met_register);
+
+int met_deregister(struct metdevice *met)
+{
+	struct metdevice *c = NULL;
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c == met)
+			break;
+	}
+	if (c != met)
+		return -ENOENT;
+
+	if (met->print_header || met->ondiemet_print_header)
+		sysfs_remove_file(met->kobj, &header_attr.attr);
+
+	if (met->print_help || met->ondiemet_print_help)
+		sysfs_remove_file(met->kobj, &help_attr.attr);
+
+	if (met->process_argument || met->ondiemet_process_argument)
+		sysfs_remove_file(met->kobj, &argu_attr.attr);
+
+	sysfs_remove_file(met->kobj, &reset_attr.attr);
+	sysfs_remove_file(met->kobj, &header_read_again_attr.attr);
+	sysfs_remove_file(met->kobj, &polling_interval_attr.attr);
+	sysfs_remove_file(met->kobj, &mode_attr.attr);
+	sysfs_remove_file(met->kobj, &ondiemet_mode_attr.attr);
+
+	if (met->delete_subfs)
+		met->delete_subfs();
+
+	kobject_del(met->kobj);
+	kobject_put(met->kobj);
+	met->kobj = NULL;
+
+	if (met->polling_count)
+		free_percpu(met->polling_count);
+
+	list_del(&met->list);
+	return 0;
+}
+EXPORT_SYMBOL(met_deregister);
+
+int met_set_platform(const char *plf_name, int flag)
+{
+	strncpy(met_platform, plf_name, sizeof(met_platform) - 1);
+#if 0
+	int ret;
+
+	if (flag) {
+		ret = device_create_file(met_device.this_device, &dev_attr_plf);
+		if (ret != 0) {
+			pr_debug("can not create device file: plf\n");
+			return ret;
+		}
+		strncpy(met_platform, plf_name, sizeof(met_platform) - 1);
+	} else
+		device_remove_file(met_device.this_device, &dev_attr_plf);
+
+#endif
+	return 0;
+}
+EXPORT_SYMBOL(met_set_platform);
+
+int met_set_topology(const char *topology_name, int flag)
+{
+	strncpy(met_topology, topology_name, sizeof(met_topology) - 1);
+#if 0
+	int ret;
+
+	if (flag) {
+		ret = device_create_file(met_device.this_device, &dev_attr_core_topology);
+		if (ret != 0) {
+			pr_debug("can not create device file: topology\n");
+			return ret;
+		}
+		strncpy(met_topology, topology_name, sizeof(met_topology) - 1);
+	} else {
+		device_remove_file(met_device.this_device, &dev_attr_core_topology);
+	}
+#endif
+	return 0;
+}
+EXPORT_SYMBOL(met_set_topology);
+
+#include "met_struct.h"
+
+void force_sample(void *unused)
+{
+	int cpu;
+	unsigned long long stamp;
+	struct metdevice *c;
+	struct met_cpu_struct *met_cpu_ptr;
+
+	if ((run != 1) || (sample_rate == 0))
+		return;
+
+	/* to avoid met tag is coming after __met_hrtimer_stop and before run=-1 */
+	met_cpu_ptr = this_cpu_ptr(&met_cpu);
+	if (met_cpu_ptr->work_enabled == 0)
+		return;
+
+	cpu = smp_processor_id();
+
+	stamp = cpu_clock(cpu);
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->ondiemet_mode == 0) {
+			if ((c->mode != 0) && (c->tagged_polling != NULL))
+				c->tagged_polling(stamp, 0);
+		} else if (c->ondiemet_mode == 1) {
+			if ((c->mode != 0) && (c->ondiemet_tagged_polling != NULL))
+				c->ondiemet_tagged_polling(stamp, 0);
+		} else if (c->ondiemet_mode == 2) {
+			if ((c->mode != 0) && (c->tagged_polling != NULL))
+				c->tagged_polling(stamp, 0);
+			if ((c->mode != 0) && (c->ondiemet_tagged_polling != NULL))
+				c->ondiemet_tagged_polling(stamp, 0);
+		}
+	}
+}
+
+#define MET_SUSPEND_HAND
+#ifdef MET_SUSPEND_HAND
+static struct syscore_ops met_hrtimer_ops = {
+	.suspend = met_hrtimer_suspend,
+	.resume = met_hrtimer_resume,
+};
+#endif
+
+int fs_reg(void)
+{
+	int ret = 0;
+
+	ctrl_flags = 0;
+	met_mode = 0;
+
+#ifdef MET_SUSPEND_HAND
+	/* suspend/resume function handle register */
+	register_syscore_ops(&met_hrtimer_ops);
+#endif
+
+	calc_timer_value(sample_rate);
+
+	ret = misc_register(&met_device);
+	if (ret != 0) {
+		pr_debug("misc register failed\n");
+		return ret;
+	}
+
+	/* dma map config */
+	/* arch_setup_dma_ops(met_device.this_device, 0, 0, NULL, false); */
+	met_arch_setup_dma_ops_symbol(met_device.this_device);
+
+	ret = device_create_file(met_device.this_device, &dev_attr_ksym);
+	if (ret != 0) {
+		pr_debug("can not create device file: ksym\n");
+		return ret;
+	}
+
+	ret = device_create_file(met_device.this_device, &dev_attr_run);
+	if (ret != 0) {
+		pr_debug("can not create device file: run\n");
+		return ret;
+	}
+
+#if	defined(PR_CPU_NOTIFY)
+	ret = device_create_file(met_device.this_device, &dev_attr_cpu_notify);
+	if (ret != 0) {
+		pr_debug("can not create device file: cpu_notify\n");
+		return ret;
+	}
+#endif
+
+#ifdef CONFIG_CPU_FREQ
+	ret = device_create_file(met_device.this_device, &dev_attr_dvfs);
+	if (ret != 0) {
+		pr_debug("can not create device file: dvfs\n");
+		return ret;
+	}
+#endif
+
+	ret = device_create_file(met_device.this_device, &dev_attr_suspend_compensation_enable);
+	if (ret != 0) {
+		pr_debug("can not create device file: suspend_compensation_enable\n");
+		return ret;
+	}
+
+	ret = device_create_file(met_device.this_device, &dev_attr_suspend_compensation_flag);
+	if (ret != 0) {
+		pr_debug("can not create device file: suspend_compensation_enable\n");
+		return ret;
+	}
+
+	ret = device_create_file(met_device.this_device, &dev_attr_ver);
+	if (ret != 0) {
+		pr_debug("can not create device file: ver\n");
+		return ret;
+	}
+
+	ret = device_create_file(met_device.this_device, &dev_attr_devices);
+	if (ret != 0) {
+		pr_debug("can not create device file: devices\n");
+		return ret;
+	}
+
+	ret = device_create_file(met_device.this_device, &dev_attr_ctrl);
+	if (ret != 0) {
+		pr_debug("can not create device file: ctrl\n");
+		return ret;
+	}
+
+	ret = device_create_file(met_device.this_device, &dev_attr_cpu_pmu_method);
+	if (ret != 0) {
+		pr_debug("can not create device file: cpu_pmu_method\n");
+		return ret;
+	}
+
+#if	defined(MET_BOOT_MSG)
+	ret = device_create_file(met_device.this_device, &dev_attr_bootmsg);
+	if (ret != 0) {
+		pr_debug("can not create device file: bootmsg\n");
+		return ret;
+	}
+#endif
+
+	ret = device_create_file(met_device.this_device, &dev_attr_sample_rate);
+	if (ret != 0) {
+		pr_debug("can not create device file: sample_rate\n");
+		return ret;
+	}
+
+	ret = device_create_file(met_device.this_device, &dev_attr_core_topology);
+	if (ret != 0) {
+		pr_debug("can not create device file: topology\n");
+		return ret;
+	}
+
+	ret = device_create_file(met_device.this_device, &dev_attr_plf);
+	if (ret != 0) {
+		pr_debug("can not create device file: plf\n");
+		return ret;
+	}
+
+	ret = device_create_file(met_device.this_device, &dev_attr_hash);
+	if (ret != 0) {
+		pr_debug("can not create device file: hash\n");
+		return ret;
+	}
+
+	ret = device_create_file(met_device.this_device, &dev_attr_ipi_test);
+	if (ret != 0) {
+		pr_debug("can not create device file: ipi_test\n");
+		return ret;
+	}
+
+	kobj_misc = kobject_create_and_add("misc", &met_device.this_device->kobj);
+	if (kobj_misc == NULL) {
+		pr_debug("can not create kobject: kobj_misc\n");
+		return ret;
+	}
+
+	kobj_pmu = kobject_create_and_add("pmu", &met_device.this_device->kobj);
+	if (kobj_pmu == NULL) {
+		pr_debug("can not create kobject: kobj_pmu\n");
+		return ret;
+	}
+
+	kobj_bus = kobject_create_and_add("bus", &met_device.this_device->kobj);
+	if (kobj_bus == NULL) {
+		pr_debug("can not create kobject: kobj_bus\n");
+		return ret;
+	}
+
+	met_register(&met_cookie);
+	met_register(&met_cpupmu);
+#ifdef MET_SUPPORT_CPUPMU_V2
+	met_register(&met_cpupmu_v2);
+#endif
+	met_register(&met_memstat);
+	met_register(&met_switch);
+	/* met_register(&met_trace_event); */
+	met_register(&met_dummy_header);
+
+	met_register(&met_backlight);
+	met_ext_api.enable_met_backlight_tag = enable_met_backlight_tag;
+	met_ext_api.output_met_backlight_tag = output_met_backlight_tag;
+
+#ifdef MET_USER_EVENT_SUPPORT
+	tag_reg((struct file_operations * const) met_device.fops, &met_device.this_device->kobj);
+#endif
+
+	met_register(&met_stat);
+
+	ondiemet_log_manager_init(met_device.this_device);
+	ondiemet_attr_init(met_device.this_device);
+
+	return ret;
+}
+
+void fs_unreg(void)
+{
+	if (run == 1)
+		met_stop();
+
+	run = -1;
+
+	met_deregister(&met_stat);
+
+#ifdef MET_USER_EVENT_SUPPORT
+	tag_unreg();
+#endif
+
+	met_deregister(&met_dummy_header);
+	/* met_deregister(&met_trace_event); */
+	met_deregister(&met_switch);
+	met_deregister(&met_memstat);
+	met_deregister(&met_cpupmu);
+#ifdef MET_SUPPORT_CPUPMU_V2
+	met_deregister(&met_cpupmu_v2);
+#endif
+	met_deregister(&met_cookie);
+	met_deregister(&met_backlight);
+	met_ext_api.enable_met_backlight_tag = NULL;
+	met_ext_api.output_met_backlight_tag = NULL;
+
+	kobject_del(kobj_misc);
+	kobject_put(kobj_misc);
+	kobj_misc = NULL;
+	kobject_del(kobj_pmu);
+	kobject_put(kobj_pmu);
+	kobj_pmu = NULL;
+	kobject_del(kobj_bus);
+	kobject_put(kobj_bus);
+	kobj_bus = NULL;
+
+	device_remove_file(met_device.this_device, &dev_attr_ksym);
+
+	device_remove_file(met_device.this_device, &dev_attr_run);
+#ifdef PR_CPU_NOTIFY
+	device_remove_file(met_device.this_device, &dev_attr_cpu_notify);
+#endif
+#ifdef CONFIG_CPU_FREQ
+	device_remove_file(met_device.this_device, &dev_attr_dvfs);
+#endif
+	device_remove_file(met_device.this_device, &dev_attr_suspend_compensation_enable);
+	device_remove_file(met_device.this_device, &dev_attr_suspend_compensation_flag);
+
+	device_remove_file(met_device.this_device, &dev_attr_ver);
+	device_remove_file(met_device.this_device, &dev_attr_devices);
+	device_remove_file(met_device.this_device, &dev_attr_sample_rate);
+
+	device_remove_file(met_device.this_device, &dev_attr_ctrl);
+	device_remove_file(met_device.this_device, &dev_attr_cpu_pmu_method);
+
+	device_remove_file(met_device.this_device, &dev_attr_core_topology);
+	device_remove_file(met_device.this_device, &dev_attr_plf);
+	device_remove_file(met_device.this_device, &dev_attr_hash);
+	device_remove_file(met_device.this_device, &dev_attr_ipi_test);
+
+	ondiemet_log_manager_uninit(met_device.this_device);
+	ondiemet_attr_uninit(met_device.this_device);
+
+	misc_deregister(&met_device);
+#ifdef MET_SUSPEND_HAND
+	/* suspend/resume function handle register */
+	unregister_syscore_ops(&met_hrtimer_ops);
+#endif
+}
+
+unsigned int get_ctrl_flags(void)
+{
+	return ctrl_flags;
+}
diff --git a/src/devtools/met-driver/4.4/common/interface.h b/src/devtools/met-driver/4.4/common/interface.h
new file mode 100644
index 0000000..00c26a6
--- /dev/null
+++ b/src/devtools/met-driver/4.4/common/interface.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __INTERFACE_H__
+#define __INTERFACE_H__
+
+#include <linux/fs.h>
+
+#ifdef MET_USER_EVENT_SUPPORT
+extern int tag_reg(struct file_operations *const fops, struct kobject *kobj);
+extern int tag_unreg(void);
+#include "met_drv.h"
+#include "met_tag.h"
+extern struct bltable_t bltab;
+#endif
+
+extern struct metdevice met_stat;
+extern struct metdevice met_cpupmu;
+extern struct metdevice met_cpupmu_v2;
+extern struct metdevice met_cookie;
+extern struct metdevice met_memstat;
+extern struct metdevice met_switch;
+extern struct metdevice met_trace_event;
+extern struct metdevice met_dummy_header;
+extern struct metdevice met_backlight;
+
+/* This variable will decide which method to access the CPU PMU counter */
+/*     0: access registers directly */
+/*     others: via Linux perf driver */
+extern unsigned int met_cpu_pmu_method;
+
+extern int met_parse_num(const char *str, unsigned int *value, int len);
+extern void met_set_suspend_notify(int flag);
+
+#define	PR_CPU_NOTIFY
+#if	defined(PR_CPU_NOTIFY)
+extern int met_cpu_notify;
+#endif
+
+//#undef	MET_BOOT_MSG
+#define	MET_BOOT_MSG
+#if	defined(MET_BOOT_MSG)
+extern char met_boot_msg_tmp[256];
+extern int pr_bootmsg(int str_len, char *str);
+#define	PR_BOOTMSG(fmt, args...) { \
+	int str_len = snprintf(met_boot_msg_tmp, sizeof(met_boot_msg_tmp), \
+			       fmt, ##args); \
+	pr_bootmsg(str_len, met_boot_msg_tmp); }
+#define	PR_BOOTMSG_ONCE(fmt, args...) { \
+	static int once; \
+	if (!once) { \
+		int str_len = snprintf(met_boot_msg_tmp, \
+				       sizeof(met_boot_msg_tmp), \
+				       fmt, ##args); \
+		pr_bootmsg(str_len, met_boot_msg_tmp); \
+		once = 1; \
+	} }
+#else
+#define	pr_bootmsg(str_len, str)
+#define PR_BOOTMSG(fmt, args...)
+#define	PR_BOOTMSG_ONCE(fmt, args...)
+#endif
+
+#endif	/* __INTERFACE_H__ */
diff --git a/src/devtools/met-driver/4.4/common/mem_stat.c b/src/devtools/met-driver/4.4/common/mem_stat.c
new file mode 100644
index 0000000..055824e
--- /dev/null
+++ b/src/devtools/met-driver/4.4/common/mem_stat.c
@@ -0,0 +1,313 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/cpu.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+
+#include "met_drv.h"
+#include "mem_stat.h"
+#include "trace.h"
+
+
+/* define MEMSTAT_DEBUG */
+#ifdef MEMSTAT_DEBUG
+#define debug_memstat(fmt, arg...) pr_debug(fmt, ##arg)
+#else
+#define debug_memstat(fmt, arg...) do {} while (0)
+#endif
+
+struct metdevice met_memstat;
+
+unsigned int phy_memstat_mask;
+unsigned int vir_memstat_mask;
+
+#define MAX_PHY_MEMSTAT_EVENT_AMOUNT 6
+#define MAX_VIR_MEMSTAT_EVENT_AMOUNT 6
+
+struct mem_event phy_memstat_table[] = {
+	{FREE_MEM, "free_mem", "Free Memory"}
+};
+
+#define PHY_MEMSTAT_TABLE_SIZE (sizeof(phy_memstat_table) / sizeof(struct mem_event))
+
+struct mem_event vir_memstat_table[] = {
+	{FILE_PAGES, "file_pages", "File Pages"},
+	{FILE_DIRTY, "file_dirty", "FD APP->FS(KB)"},
+	{NUM_DIRTIED, "num_dirtied", "Num Dirtied"},
+	{WRITE_BACK, "write_back", "WB. FS->Block IO(KB)"},
+	{NUM_WRITTEN, "num_written", "Num Written"},
+	{PG_FAULT_CNT, "pg_fault_cnt", "Page Fault Count"}
+};
+
+#define VIR_MEMSTAT_TABLE_SIZE (sizeof(vir_memstat_table) / sizeof(struct mem_event))
+
+int vm_event_counters_enable;
+unsigned long *vm_status;
+static struct delayed_work dwork;
+
+noinline void memstat(unsigned int cnt, unsigned int *value)
+{
+	MET_GENERAL_PRINT(MET_TRACE, cnt, value);
+}
+
+static int get_phy_memstat(unsigned int *value)
+{
+	int i, cnt = 0;
+	struct sysinfo info;
+
+#define K(x) ((x) << (PAGE_SHIFT - 10))
+
+	si_meminfo(&info);
+
+	for (i = 0; i < MAX_PHY_MEMSTAT_EVENT_AMOUNT; i++) {
+		if (phy_memstat_mask & (1 << i)) {
+			switch (i) {
+			case FREE_MEM:
+				value[cnt] = K(info.freeram);
+				break;
+			}
+
+			cnt++;
+		}
+	}
+
+	return cnt;
+}
+
+static int get_vir_memstat(unsigned int *value)
+{
+	int i, cnt = 0;
+
+	for (i = 0; i < NR_VM_ZONE_STAT_ITEMS; i++)
+		vm_status[i] = global_page_state(i);
+
+	all_vm_events(vm_status + NR_VM_ZONE_STAT_ITEMS);
+
+	for (i = 0; i < MAX_VIR_MEMSTAT_EVENT_AMOUNT; i++) {
+		if (vir_memstat_mask & (1 << i)) {
+			switch (i) {
+			case FILE_PAGES:
+				value[cnt] = vm_status[NR_FILE_PAGES] << (PAGE_SHIFT - 10);
+				break;
+			case FILE_DIRTY:
+				value[cnt] = vm_status[NR_FILE_DIRTY] << (PAGE_SHIFT - 10);
+				break;
+			case NUM_DIRTIED:
+				value[cnt] = vm_status[NR_DIRTIED] << (PAGE_SHIFT - 10);
+				break;
+			case WRITE_BACK:
+				value[cnt] = vm_status[NR_WRITEBACK] << (PAGE_SHIFT - 10);
+				break;
+			case NUM_WRITTEN:
+				value[cnt] = vm_status[NR_WRITTEN] << (PAGE_SHIFT - 10);
+				break;
+			case PG_FAULT_CNT:
+				value[cnt] = vm_status[NR_VM_ZONE_STAT_ITEMS + PGFAULT];
+				break;
+			}
+
+			cnt++;
+		}
+	}
+
+	return cnt;
+}
+
+static void wq_get_memstat(struct work_struct *work)
+{
+	int total_event_amount = 0, phy_event_amount = 0;
+	unsigned int stat_val[MAX_PHY_MEMSTAT_EVENT_AMOUNT + MAX_VIR_MEMSTAT_EVENT_AMOUNT];
+
+	memset(stat_val, 0, sizeof(unsigned int) * (MAX_PHY_MEMSTAT_EVENT_AMOUNT + MAX_VIR_MEMSTAT_EVENT_AMOUNT));
+	total_event_amount = phy_event_amount = get_phy_memstat(stat_val);
+
+	if (vm_event_counters_enable)
+		total_event_amount += get_vir_memstat(&(stat_val[phy_event_amount]));
+
+	if (total_event_amount <= (MAX_PHY_MEMSTAT_EVENT_AMOUNT + MAX_VIR_MEMSTAT_EVENT_AMOUNT))
+		memstat(total_event_amount-1, stat_val);
+}
+
+void met_memstat_polling(unsigned long long stamp, int cpu)
+{
+	schedule_delayed_work(&dwork, 0);
+}
+
+static void met_memstat_start(void)
+{
+	int stat_items_size = 0;
+
+	stat_items_size = NR_VM_ZONE_STAT_ITEMS * sizeof(unsigned long);
+
+#ifdef CONFIG_VM_EVENT_COUNTERS
+	stat_items_size += sizeof(struct vm_event_state);
+#endif
+
+	vm_status = kmalloc(stat_items_size, GFP_KERNEL);
+	if (vm_status == NULL)
+		return;
+	INIT_DELAYED_WORK(&dwork, wq_get_memstat);
+}
+
+static void met_memstat_stop(void)
+{
+	kfree(vm_status);
+	cancel_delayed_work_sync(&dwork);
+}
+
+static const char help[] =
+"  --memstat=[phy_mem_stat|vir_mem_stat]:event_name enable sampling physical & virtual memory status\n";
+
+static int met_memstat_print_help(char *buf, int len)
+{
+	int i, l;
+
+	l = snprintf(buf, PAGE_SIZE, help);
+
+	for (i = 0; i < PHY_MEMSTAT_TABLE_SIZE; i++)
+		l += snprintf(buf + l, PAGE_SIZE - l, "  --memstat=phy_mem_stat:%s\n",
+			      phy_memstat_table[i].name);
+
+#ifdef CONFIG_VM_EVENT_COUNTERS
+	for (i = 0; i < VIR_MEMSTAT_TABLE_SIZE; i++)
+		l += snprintf(buf + l, PAGE_SIZE - l, "  --memstat=vir_mem_stat:%s\n",
+			      vir_memstat_table[i].name);
+#endif
+
+	return l;
+}
+
+static const char header[] = "met-info [000] 0.0: ms_ud_sys_header: memstat,";
+
+
+static int met_memstat_print_header(char *buf, int len)
+{
+	int i, l;
+	int event_amount = 0;
+
+	l = snprintf(buf, PAGE_SIZE, header);
+
+	for (i = 0; i < MAX_PHY_MEMSTAT_EVENT_AMOUNT; i++) {
+		if ((phy_memstat_mask & (1 << i)) && (i < PHY_MEMSTAT_TABLE_SIZE)) {
+			l += snprintf(buf + l, PAGE_SIZE - l, phy_memstat_table[i].header_name);
+			l += snprintf(buf + l, PAGE_SIZE - l, ",");
+			event_amount++;
+		}
+	}
+
+#ifdef CONFIG_VM_EVENT_COUNTERS
+	for (i = 0; i < MAX_VIR_MEMSTAT_EVENT_AMOUNT; i++) {
+		if ((vir_memstat_mask & (1 << i)) && (i < VIR_MEMSTAT_TABLE_SIZE)) {
+			l += snprintf(buf + l, PAGE_SIZE - l, vir_memstat_table[i].header_name);
+			l += snprintf(buf + l, PAGE_SIZE - l, ",");
+			event_amount++;
+		}
+	}
+#endif
+
+	for (i = 0; i < event_amount; i++) {
+		l += snprintf(buf + l, PAGE_SIZE - l, "x");
+		l += snprintf(buf + l, PAGE_SIZE - l, ",");
+	}
+
+	phy_memstat_mask = 0;
+	vir_memstat_mask = 0;
+
+	l += snprintf(buf + l, PAGE_SIZE - l, "\n");
+
+	return l;
+}
+
+static int met_memstat_process_argument(const char *arg, int len)
+{
+	int i, found_event = 0;
+	char choice[16], event[32];
+	char *pch;
+	int str_len;
+
+
+#ifdef CONFIG_VM_EVENT_COUNTERS
+	vm_event_counters_enable = 1;
+#endif
+
+	pch = strchr(arg, ':');
+	if (pch == NULL)
+		goto error;
+
+	memset(choice, 0, sizeof(choice));
+	memset(event, 0, sizeof(event));
+
+	str_len = (int)(pch - arg);
+	memcpy(choice, arg, str_len);
+	memcpy(event, arg + str_len + 1, len - (str_len + 1));
+
+	if (strncmp(choice, "phy_mem_stat", 12) == 0) {
+		for (i = 0; i < PHY_MEMSTAT_TABLE_SIZE; i++) {
+			if (strncmp(event, phy_memstat_table[i].name, MAX_EVENT_NAME_LEN) == 0) {
+				phy_memstat_mask |= (1 << phy_memstat_table[i].id);
+				found_event = 1;
+
+				break;
+			}
+		}
+	} else if (strncmp(choice, "vir_mem_stat", 12) == 0) {
+		if (!vm_event_counters_enable) {
+			pr_debug("[%s] %d: CONFIG_VM_EVENT_COUNTERS is not configured\n", __func__,
+				 __LINE__);
+			goto error;
+		}
+
+		for (i = 0; i < VIR_MEMSTAT_TABLE_SIZE; i++) {
+			if (strncmp(event, vir_memstat_table[i].name, MAX_EVENT_NAME_LEN) == 0) {
+				vir_memstat_mask |= (1 << vir_memstat_table[i].id);
+				found_event = 1;
+
+				break;
+			}
+		}
+	} else {
+		pr_debug("[%s] %d: only support phy_mem_stat & vir_mem_stat keyword\n", __func__,
+			 __LINE__);
+		goto error;
+	}
+
+	if (!found_event) {
+		pr_debug("[%s] %d: input event name error\n", __func__, __LINE__);
+		goto error;
+	}
+
+	met_memstat.mode = 1;
+	return 0;
+
+error:
+	met_memstat.mode = 0;
+	return -EINVAL;
+}
+
+struct metdevice met_memstat = {
+	.name = "memstat",
+	.type = MET_TYPE_PMU,
+	.cpu_related = 0,
+	.start = met_memstat_start,
+	.stop = met_memstat_stop,
+	.polling_interval = 1,
+	.timed_polling = met_memstat_polling,
+	.tagged_polling = met_memstat_polling,
+	.print_help = met_memstat_print_help,
+	.print_header = met_memstat_print_header,
+	.process_argument = met_memstat_process_argument
+};
diff --git a/src/devtools/met-driver/4.4/common/mem_stat.h b/src/devtools/met-driver/4.4/common/mem_stat.h
new file mode 100644
index 0000000..d716075
--- /dev/null
+++ b/src/devtools/met-driver/4.4/common/mem_stat.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __MEM_STAT_H__
+#define __MEM_STAT_H__
+
+#define MAX_EVENT_NAME_LEN 32
+
+enum phy_mem_event_id {
+	FREE_MEM = 0
+};
+
+enum vir_mem_event_id {
+	FILE_PAGES = 0,
+	FILE_DIRTY,
+	NUM_DIRTIED,
+	WRITE_BACK,
+	NUM_WRITTEN,
+	PG_FAULT_CNT
+};
+
+struct mem_event {
+	int id;
+	char name[32];
+	char header_name[32];
+};
+
+#endif
diff --git a/src/devtools/met-driver/4.4/common/met_api_tbl.h b/src/devtools/met-driver/4.4/common/met_api_tbl.h
new file mode 100644
index 0000000..f93ae5f
--- /dev/null
+++ b/src/devtools/met-driver/4.4/common/met_api_tbl.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+struct met_api_tbl {
+	int (*met_tag_start)(unsigned int class_id, const char *name);
+	int (*met_tag_end)(unsigned int class_id, const char *name);
+	int (*met_tag_async_start)(unsigned int class_id, const char *name, unsigned int cookie);
+	int (*met_tag_async_end)(unsigned int class_id, const char *name, unsigned int cookie);
+	int (*met_tag_oneshot)(unsigned int class_id, const char *name, unsigned int value);
+	int (*met_tag_userdata)(char *pData);
+	int (*met_tag_dump)(unsigned int class_id, const char *name, void *data, unsigned int length);
+	int (*met_tag_disable)(unsigned int class_id);
+	int (*met_tag_enable)(unsigned int class_id);
+	int (*met_set_dump_buffer)(int size);
+	int (*met_save_dump_buffer)(const char *pathname);
+	int (*met_save_log)(const char *pathname);
+	int (*met_show_bw_limiter)(void);
+	int (*met_reg_bw_limiter)(void *fp);
+	int (*met_show_clk_tree)(const char *name, unsigned int addr, unsigned int status);
+	int (*met_reg_clk_tree)(void *fp);
+	void (*met_sched_switch)(struct task_struct *prev, struct task_struct *next);
+	int (*enable_met_backlight_tag)(void);
+	int (*output_met_backlight_tag)(int level);
+};
+
+extern struct met_api_tbl met_ext_api;
diff --git a/src/devtools/met-driver/4.4/common/met_backlight.c b/src/devtools/met-driver/4.4/common/met_backlight.c
new file mode 100644
index 0000000..37f1ae9
--- /dev/null
+++ b/src/devtools/met-driver/4.4/common/met_backlight.c
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+#define MET_USER_EVENT_SUPPORT
+#include "met_drv.h"
+#include "trace.h"
+
+static int met_backlight_enable;
+static DEFINE_SPINLOCK(met_backlight_lock);
+static struct kobject *kobj_met_backlight;
+
+static ssize_t bl_tag_enable_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf);
+static ssize_t bl_tag_enable_store(struct kobject *kobj,
+				   struct kobj_attribute *attr, const char *buf, size_t n);
+static struct kobj_attribute bl_tag_enable_attr =
+__ATTR(backlight_tag_enable, 0664, bl_tag_enable_show, bl_tag_enable_store);
+
+int enable_met_backlight_tag(void)
+{
+	return met_backlight_enable;
+}
+
+int output_met_backlight_tag(int level)
+{
+	int ret;
+	unsigned long flags;
+
+	spin_lock_irqsave(&met_backlight_lock, flags);
+#ifdef CONFIG_MET_MODULE
+	ret = met_tag_oneshot_real(33880, "_MM_BL_", level);
+#else
+	ret = met_tag_oneshot(33880, "_MM_BL_", level);
+#endif
+	spin_unlock_irqrestore(&met_backlight_lock, flags);
+
+	return ret;
+}
+
+static ssize_t bl_tag_enable_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	int ret;
+
+	ret = snprintf(buf, PAGE_SIZE, "%d\n", met_backlight_enable);
+
+	return ret;
+}
+
+static ssize_t bl_tag_enable_store(struct kobject *kobj,
+				   struct kobj_attribute *attr, const char *buf, size_t n)
+{
+	int value;
+
+	if ((n == 0) || (buf == NULL))
+		return -EINVAL;
+
+	if (kstrtoint(buf, 0, &value) != 0)
+		return -EINVAL;
+
+	if (value < 0)
+		return -EINVAL;
+
+	met_backlight_enable = value;
+
+	return n;
+}
+
+static int met_backlight_create(struct kobject *parent)
+{
+	int ret = 0;
+
+	kobj_met_backlight = parent;
+
+	ret = sysfs_create_file(kobj_met_backlight, &bl_tag_enable_attr.attr);
+	if (ret != 0) {
+		pr_debug("Failed to create montype0 in sysfs\n");
+		return ret;
+	}
+
+	return ret;
+}
+
+static void met_backlight_delete(void)
+{
+	sysfs_remove_file(kobj_met_backlight, &bl_tag_enable_attr.attr);
+	kobj_met_backlight = NULL;
+}
+
+struct metdevice met_backlight = {
+	.name = "backlight",
+	.owner = THIS_MODULE,
+	.type = MET_TYPE_BUS,
+	.create_subfs = met_backlight_create,
+	.delete_subfs = met_backlight_delete,
+	.cpu_related = 0,
+};
+EXPORT_SYMBOL(met_backlight);
diff --git a/src/devtools/met-driver/4.4/common/met_drv.h b/src/devtools/met-driver/4.4/common/met_drv.h
new file mode 100644
index 0000000..2ecbb38
--- /dev/null
+++ b/src/devtools/met-driver/4.4/common/met_drv.h
@@ -0,0 +1,265 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef MET_DRV
+#define MET_DRV
+
+#include <linux/version.h>
+#include <linux/preempt.h>
+#include <linux/device.h>
+#include <linux/percpu.h>
+#include <linux/hardirq.h>
+#include <linux/clk.h>
+
+extern int met_mode;
+extern int core_plf_init(void);
+extern void core_plf_exit(void);
+
+#define MET_MODE_TRACE_CMD_OFFSET	(1)
+#define MET_MODE_TRACE_CMD			(1<<MET_MODE_TRACE_CMD_OFFSET)
+
+#ifdef CONFIG_MET_MODULE
+#define my_preempt_enable() preempt_enable()
+#else
+#define my_preempt_enable() preempt_enable_no_resched()
+#endif
+
+#define MET_STRBUF_SIZE		1024
+DECLARE_PER_CPU(char[MET_STRBUF_SIZE], met_strbuf_nmi);
+DECLARE_PER_CPU(char[MET_STRBUF_SIZE], met_strbuf_irq);
+DECLARE_PER_CPU(char[MET_STRBUF_SIZE], met_strbuf_sirq);
+DECLARE_PER_CPU(char[MET_STRBUF_SIZE], met_strbuf);
+
+#ifdef CONFIG_TRACING
+#define TRACE_PUTS(p) \
+	do { \
+		trace_puts(p);; \
+	} while (0)
+#else
+#define TRACE_PUTS(p) do {} while (0)
+#endif
+
+#define GET_MET_TRACE_BUFFER_ENTER_CRITICAL() \
+	({ \
+		char *pmet_strbuf; \
+		preempt_disable(); \
+		if (in_nmi()) \
+			pmet_strbuf = per_cpu(met_strbuf_nmi, smp_processor_id()); \
+		else if (in_irq()) \
+			pmet_strbuf = per_cpu(met_strbuf_irq, smp_processor_id()); \
+		else if (in_softirq()) \
+			pmet_strbuf = per_cpu(met_strbuf_sirq, smp_processor_id()); \
+		else \
+			pmet_strbuf = per_cpu(met_strbuf, smp_processor_id()); \
+		pmet_strbuf;\
+	})
+
+#define PUT_MET_TRACE_BUFFER_EXIT_CRITICAL(pmet_strbuf) \
+	do {\
+		if (pmet_strbuf)\
+			TRACE_PUTS(pmet_strbuf); \
+		my_preempt_enable(); \
+	} while (0)
+
+#define MET_TRACE(FORMAT, args...) \
+	do { \
+		char *pmet_strbuf; \
+		preempt_disable(); \
+		if (in_nmi()) \
+			pmet_strbuf = per_cpu(met_strbuf_nmi, smp_processor_id()); \
+		else if (in_irq()) \
+			pmet_strbuf = per_cpu(met_strbuf_irq, smp_processor_id()); \
+		else if (in_softirq()) \
+			pmet_strbuf = per_cpu(met_strbuf_sirq, smp_processor_id()); \
+		else \
+			pmet_strbuf = per_cpu(met_strbuf, smp_processor_id()); \
+		if (met_mode & MET_MODE_TRACE_CMD) \
+			snprintf(pmet_strbuf, MET_STRBUF_SIZE, "%s: " FORMAT, __func__, ##args); \
+		else \
+			snprintf(pmet_strbuf, MET_STRBUF_SIZE, FORMAT, ##args); \
+		TRACE_PUTS(pmet_strbuf); \
+		my_preempt_enable(); \
+	} while (0)
+
+/*
+ * SOB: start of buf
+ * EOB: end of buf
+ */
+#define MET_TRACE_GETBUF(pSOB, pEOB) \
+	({ \
+		preempt_disable(); \
+		if (in_nmi()) \
+			*pSOB = per_cpu(met_strbuf_nmi, smp_processor_id()); \
+		else if (in_irq()) \
+			*pSOB = per_cpu(met_strbuf_irq, smp_processor_id()); \
+		else if (in_softirq()) \
+			*pSOB = per_cpu(met_strbuf_sirq, smp_processor_id()); \
+		else \
+			*pSOB = per_cpu(met_strbuf, smp_processor_id()); \
+		*pEOB = *pSOB; \
+		if (met_mode & MET_MODE_TRACE_CMD) \
+			*pEOB += snprintf(*pEOB, MET_STRBUF_SIZE, "%s: ", __func__); \
+	})
+
+#define MET_TRACE_PUTBUF(SOB, EOB) \
+	({ \
+		__trace_puts(_THIS_IP_, (SOB), (uintptr_t)((EOB)-(SOB))); \
+		my_preempt_enable(); \
+	})
+
+#define MET_FTRACE_DUMP(TRACE_NAME, args...)			\
+	do {							\
+		trace_##TRACE_NAME(args);;			\
+	} while (0)
+
+
+#define MET_TYPE_PMU	1
+#define MET_TYPE_BUS	2
+#define MET_TYPE_MISC	3
+
+struct metdevice {
+	struct list_head list;
+	int type;
+	const char *name;
+	struct module *owner;
+	struct kobject *kobj;
+
+	int (*create_subfs)(struct kobject *parent);
+	void (*delete_subfs)(void);
+	int mode;
+	int ondiemet_mode;	/* new for ondiemet; 1: call ondiemet functions */
+	int cpu_related;
+	int polling_interval;
+	int polling_count_reload;
+	int __percpu *polling_count;
+	int header_read_again;	/*for header size > 1 page */
+	void (*start)(void);
+	void (*stop)(void);
+	int (*reset)(void);
+	void (*timed_polling)(unsigned long long stamp, int cpu);
+	void (*tagged_polling)(unsigned long long stamp, int cpu);
+	int (*print_help)(char *buf, int len);
+	int (*print_header)(char *buf, int len);
+	int (*process_argument)(const char *arg, int len);
+
+	void (*ondiemet_start)(void);
+	void (*ondiemet_stop)(void);
+	int (*ondiemet_reset)(void);
+	int (*ondiemet_print_help)(char *buf, int len);
+	int (*ondiemet_print_header)(char *buf, int len);
+	int (*ondiemet_process_argument)(const char *arg, int len);
+	void (*ondiemet_timed_polling)(unsigned long long stamp, int cpu);
+	void (*ondiemet_tagged_polling)(unsigned long long stamp, int cpu);
+
+	struct list_head exlist;	/* for linked list before register */
+	void (*suspend)(void);
+	void (*resume)(void);
+
+	unsigned long long prev_stamp;
+	spinlock_t my_lock;
+	void *reversed1;
+};
+
+int met_register(struct metdevice *met);
+int met_deregister(struct metdevice *met);
+int met_set_platform(const char *plf_name, int flag);
+int met_set_topology(const char *topology_name, int flag);
+int met_devlink_add(struct metdevice *met);
+int met_devlink_del(struct metdevice *met);
+int met_devlink_register_all(void);
+int met_devlink_deregister_all(void);
+
+int fs_reg(void);
+void fs_unreg(void);
+
+/******************************************************************************
+ * Tracepoints
+ ******************************************************************************/
+#define MET_DEFINE_PROBE(probe_name, proto) \
+		static void probe_##probe_name(void *data, PARAMS(proto))
+#define MET_REGISTER_TRACE(probe_name) \
+		register_trace_##probe_name(probe_##probe_name, NULL)
+#define MET_UNREGISTER_TRACE(probe_name) \
+		unregister_trace_##probe_name(probe_##probe_name, NULL)
+
+
+/* ====================== Tagging API ================================ */
+
+#define MAX_EVENT_CLASS	31
+#define MAX_TAGNAME_LEN	128
+#define MET_CLASS_ALL	0x80000000
+
+/* IOCTL commands of MET tagging */
+struct mtag_cmd_t {
+	unsigned int class_id;
+	unsigned int value;
+	unsigned int slen;
+	char tname[MAX_TAGNAME_LEN];
+	void *data;
+	unsigned int size;
+};
+
+#define TYPE_START		1
+#define TYPE_END		2
+#define TYPE_ONESHOT		3
+#define TYPE_ENABLE		4
+#define TYPE_DISABLE		5
+#define TYPE_REC_SET		6
+#define TYPE_DUMP		7
+#define TYPE_DUMP_SIZE		8
+#define TYPE_DUMP_SAVE		9
+#define TYPE_USRDATA		10
+#define TYPE_DUMP_AGAIN		11
+#define TYPE_ASYNC_START	12
+#define TYPE_ASYNC_END		13
+#define TYPE_MET_SUSPEND	15
+#define TYPE_MET_RESUME		16
+
+/* Use 'm' as magic number */
+#define MTAG_IOC_MAGIC  'm'
+/* Please use a different 8-bit number in your code */
+#define MTAG_CMD_START		_IOW(MTAG_IOC_MAGIC, TYPE_START, struct mtag_cmd_t)
+#define MTAG_CMD_END		_IOW(MTAG_IOC_MAGIC, TYPE_END, struct mtag_cmd_t)
+#define MTAG_CMD_ONESHOT	_IOW(MTAG_IOC_MAGIC, TYPE_ONESHOT, struct mtag_cmd_t)
+#define MTAG_CMD_ENABLE		_IOW(MTAG_IOC_MAGIC, TYPE_ENABLE, int)
+#define MTAG_CMD_DISABLE	_IOW(MTAG_IOC_MAGIC, TYPE_DISABLE, int)
+#define MTAG_CMD_REC_SET	_IOW(MTAG_IOC_MAGIC, TYPE_REC_SET, int)
+#define MTAG_CMD_DUMP		_IOW(MTAG_IOC_MAGIC, TYPE_DUMP, struct mtag_cmd_t)
+#define MTAG_CMD_DUMP_SIZE	_IOWR(MTAG_IOC_MAGIC, TYPE_DUMP_SIZE, int)
+#define MTAG_CMD_DUMP_SAVE	_IOW(MTAG_IOC_MAGIC, TYPE_DUMP_SAVE, struct mtag_cmd_t)
+#define MTAG_CMD_USRDATA	_IOW(MTAG_IOC_MAGIC, TYPE_USRDATA, struct mtag_cmd_t)
+#define MTAG_CMD_DUMP_AGAIN	_IOW(MTAG_IOC_MAGIC, TYPE_DUMP_AGAIN, void *)
+#define MTAG_CMD_ASYNC_START	_IOW(MTAG_IOC_MAGIC, TYPE_ASYNC_START, struct mtag_cmd_t)
+#define MTAG_CMD_ASYNC_END	_IOW(MTAG_IOC_MAGIC, TYPE_ASYNC_END, struct mtag_cmd_t)
+
+/* include file */
+extern int met_tag_start_real(unsigned int class_id, const char *name);
+extern int met_tag_end_real(unsigned int class_id, const char *name);
+extern int met_tag_async_start_real(unsigned int class_id, const char *name, unsigned int cookie);
+extern int met_tag_async_end_real(unsigned int class_id, const char *name, unsigned int cookie);
+extern int met_tag_oneshot_real(unsigned int class_id, const char *name, unsigned int value);
+extern int met_tag_userdata_real(char *pData);
+extern int met_tag_dump_real(unsigned int class_id, const char *name, void *data, unsigned int length);
+extern int met_tag_disable_real(unsigned int class_id);
+extern int met_tag_enable_real(unsigned int class_id);
+extern int met_set_dump_buffer_real(int size);
+extern int met_save_dump_buffer_real(const char *pathname);
+extern int met_save_log_real(const char *pathname);
+extern int met_show_bw_limiter_real(void);
+extern int met_reg_bw_limiter_real(void *fp);
+extern int met_show_clk_tree_real(const char *name, unsigned int addr, unsigned int status);
+extern int met_reg_clk_tree_real(void *fp);
+extern int enable_met_backlight_tag(void);
+extern int output_met_backlight_tag(int level);
+
+#endif	/* MET_DRV */
diff --git a/src/devtools/met-driver/4.4/common/met_emi.c b/src/devtools/met-driver/4.4/common/met_emi.c
new file mode 100644
index 0000000..14879ff
--- /dev/null
+++ b/src/devtools/met-driver/4.4/common/met_emi.c
@@ -0,0 +1,849 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/device.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/dma-mapping.h>
+#include <linux/string.h>
+
+#define MET_USER_EVENT_SUPPORT
+#include "met_drv.h"
+#include "trace.h"
+#include "interface.h"
+
+#include "mtk_typedefs.h"
+#include "core_plf_init.h"
+/* #include "plf_trace.h" */
+#include "mtk_emi_bm.h"
+
+#ifdef CONFIG_MTK_TINYSYS_SSPM_SUPPORT
+#include "sspm/ondiemet_sspm.h"
+#endif
+
+/*======================================================================*/
+/*	Global variable definitions					*/
+/*======================================================================*/
+int emi_TP_busfiltr_enable;
+static int msel_enable;
+static unsigned int msel_group1 = _GP_1_Default;
+static unsigned int msel_group2 = _GP_2_Default;
+static unsigned int msel_group3 = _GP_3_Default;
+
+/* Global variables */
+static struct kobject *kobj_emi;
+
+static int ttype1_16_en = BM_TTYPE1_16_DISABLE;
+static int ttype17_21_en = BM_TTYPE17_21_DISABLE;
+
+
+
+static int times;
+static ssize_t test_store(struct kobject *kobj,
+				struct kobj_attribute *attr,
+				const char *buf,
+				size_t n)
+{
+	int i;
+	unsigned int    *src_addr_v;
+	dma_addr_t src_addr_p;
+
+	if ((n == 0) || (buf == NULL))
+		return -EINVAL;
+	if (kstrtoint(buf, 10, &times) != 0)
+		return -EINVAL;
+	if (times < 0)
+		return -EINVAL;
+
+	if (times > 5000)
+		return -EINVAL;
+
+
+	src_addr_v = dma_alloc_coherent(met_device.this_device,
+					PAGE_SIZE,
+					&src_addr_p,
+					GFP_KERNEL);
+	if (src_addr_v == NULL) {
+#ifdef CONFIG_MET_MODULE
+		met_tag_oneshot_real(0, "test dma alloc fail", PAGE_SIZE);
+#else
+		met_tag_oneshot(0, "test dma alloc fail", PAGE_SIZE);
+#endif
+		return -ENOMEM;
+	}
+
+	preempt_disable();
+#ifdef CONFIG_MET_MODULE
+	met_tag_start_real(0, "TEST_EMI");
+#else
+	met_tag_start(0, "TEST_EMI");
+#endif
+	for (i = 0; i < times; i++) {
+		memset(src_addr_v, 2*i, PAGE_SIZE);
+#ifdef CONFIG_MET_MODULE
+		met_tag_oneshot_real(0, "TEST_EMI", PAGE_SIZE);
+#else
+		met_tag_oneshot(0, "TEST_EMI", PAGE_SIZE);
+#endif
+	}
+#ifdef CONFIG_MET_MODULE
+	met_tag_end_real(0, "TEST_EMI");
+#else
+	met_tag_end(0, "TEST_EMI");
+#endif
+
+	my_preempt_enable();
+
+	if (src_addr_v != NULL)
+		dma_free_coherent(met_device.this_device,
+				  PAGE_SIZE,
+				  src_addr_v,
+				  src_addr_p);
+	return n;
+}
+
+/*======================================================================*/
+/*	KOBJ Declarations						*/
+/*======================================================================*/
+DECLARE_KOBJ_ATTR_INT(emi_TP_busfiltr_enable, emi_TP_busfiltr_enable);
+DECLARE_KOBJ_ATTR_INT(msel_enable, msel_enable);
+DECLARE_KOBJ_ATTR_HEX_CHECK(msel_group1, msel_group1, msel_group1 > 0 && msel_group1 <= _ALL);
+DECLARE_KOBJ_ATTR_HEX_CHECK(msel_group2, msel_group2, msel_group2 > 0 && msel_group2 <= _ALL);
+DECLARE_KOBJ_ATTR_HEX_CHECK(msel_group3, msel_group3, msel_group3 > 0 && msel_group3 <= _ALL);
+
+
+DECLARE_KOBJ_ATTR_STR_LIST_ITEM(
+	ttype1_16_en,
+	KOBJ_ITEM_LIST(
+		{ BM_TTYPE1_16_ENABLE,   "ENABLE" },
+		{ BM_TTYPE1_16_DISABLE,  "DISABLE" }
+		)
+	);
+DECLARE_KOBJ_ATTR_STR_LIST(ttype1_16_en, ttype1_16_en, ttype1_16_en);
+
+
+DECLARE_KOBJ_ATTR_STR_LIST_ITEM(
+	ttype17_21_en,
+	KOBJ_ITEM_LIST(
+		{ BM_TTYPE17_21_ENABLE,  "ENABLE" },
+		{ BM_TTYPE17_21_DISABLE, "DISABLE" }
+		)
+	);
+DECLARE_KOBJ_ATTR_STR_LIST(ttype17_21_en, ttype17_21_en, ttype17_21_en);
+
+DECLARE_KOBJ_ATTR_STR_LIST_ITEM(
+	ttype_master,
+	KOBJ_ITEM_LIST(
+		{ _M0,  "M0" },
+		{ _M1,  "M1" },
+		{ _M2,  "M2" },
+		{ _M3,  "M3" },
+		{ _M4,  "M4" },
+		{ _M5,  "M5" },
+		{ _M6,  "M6" },
+		{ _M7,  "M7" }
+		)
+	);
+
+
+DECLARE_KOBJ_ATTR_INT_LIST_ITEM(
+	ttype_nbeat,
+	KOBJ_ITEM_LIST(
+		{ BM_TRANS_TYPE_1BEAT,   1 },
+		{ BM_TRANS_TYPE_2BEAT,   2 },
+		{ BM_TRANS_TYPE_3BEAT,   3 },
+		{ BM_TRANS_TYPE_4BEAT,   4 },
+		{ BM_TRANS_TYPE_5BEAT,   5 },
+		{ BM_TRANS_TYPE_6BEAT,   6 },
+		{ BM_TRANS_TYPE_7BEAT,   7 },
+		{ BM_TRANS_TYPE_8BEAT,   8 },
+		{ BM_TRANS_TYPE_9BEAT,   9 },
+		{ BM_TRANS_TYPE_10BEAT,  10 },
+		{ BM_TRANS_TYPE_11BEAT,  11 },
+		{ BM_TRANS_TYPE_12BEAT,  12 },
+		{ BM_TRANS_TYPE_13BEAT,  13 },
+		{ BM_TRANS_TYPE_14BEAT,  14 },
+		{ BM_TRANS_TYPE_15BEAT,  15 },
+		{ BM_TRANS_TYPE_16BEAT,  16 }
+		)
+	);
+DECLARE_KOBJ_ATTR_INT_LIST_ITEM(
+	ttype_nbyte,
+	KOBJ_ITEM_LIST(
+		{ BM_TRANS_TYPE_1Byte,   1 },
+		{ BM_TRANS_TYPE_2Byte,   2 },
+		{ BM_TRANS_TYPE_4Byte,   4 },
+		{ BM_TRANS_TYPE_8Byte,   8 },
+		{ BM_TRANS_TYPE_16Byte,  16 },
+		{ BM_TRANS_TYPE_32Byte,  32 }
+		)
+	);
+DECLARE_KOBJ_ATTR_STR_LIST_ITEM(
+	ttype_burst,
+	KOBJ_ITEM_LIST(
+		{ BM_TRANS_TYPE_BURST_INCR,      "INCR" },
+		{ BM_TRANS_TYPE_BURST_WRAP,      "WRAP" }
+		)
+	);
+
+DECLARE_KOBJ_ATTR_STR_LIST_ITEM(
+	ttype_rw,
+	KOBJ_ITEM_LIST(
+		{ BM_TRANS_RW_DEFAULT,   "DEFAULT" },
+		{ BM_TRANS_RW_READONLY,  "R" },
+		{ BM_TRANS_RW_WRITEONLY, "W" },
+		{ BM_TRANS_RW_RWBOTH,    "BOTH" }
+		)
+	);
+
+
+DECLARE_KOBJ_ATTR_SHOW_INT(test, times);
+
+DECLARE_KOBJ_ATTR(test);
+
+
+static int high_priority_filter;
+DECLARE_KOBJ_ATTR_HEX(high_priority_filter, high_priority_filter);
+
+
+static int ttype_master_val[21];
+static int ttype_busid_val[21];
+static int ttype_nbeat_val[21];
+static int ttype_nbyte_val[21];
+static int ttype_burst_val[21];
+static int ttype_rw_val[21];
+
+#define DECLARE_KOBJ_TTYPE_MASTER(nr) \
+	DECLARE_KOBJ_ATTR_STR_LIST(ttype ## nr ## _master, ttype_master_val[nr - 1], ttype_master)
+
+#define DECLARE_KOBJ_TTYPE_NBEAT(nr) \
+	DECLARE_KOBJ_ATTR_INT_LIST(ttype ## nr ## _nbeat, ttype_nbeat_val[nr - 1], ttype_nbeat)
+
+#define DECLARE_KOBJ_TTYPE_NBYTE(nr) \
+	DECLARE_KOBJ_ATTR_INT_LIST(ttype ## nr ## _nbyte, ttype_nbyte_val[nr - 1], ttype_nbyte)
+
+#define DECLARE_KOBJ_TTYPE_BURST(nr) \
+	DECLARE_KOBJ_ATTR_STR_LIST(ttype ## nr ## _burst, ttype_burst_val[nr - 1], ttype_burst)
+
+#define DECLARE_KOBJ_TTYPE_RW(nr) \
+	DECLARE_KOBJ_ATTR_STR_LIST(ttype ## nr ## _rw, ttype_rw_val[nr - 1], ttype_rw)
+
+#define DECLARE_KOBJ_TTYPE_BUSID_VAL(nr) \
+	DECLARE_KOBJ_ATTR_HEX(ttype ## nr ## _busid, ttype_busid_val[nr - 1])
+
+DECLARE_KOBJ_TTYPE_MASTER(1);
+DECLARE_KOBJ_TTYPE_NBEAT(1);
+DECLARE_KOBJ_TTYPE_NBYTE(1);
+DECLARE_KOBJ_TTYPE_BURST(1);
+DECLARE_KOBJ_TTYPE_RW(1);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(1);
+
+DECLARE_KOBJ_TTYPE_MASTER(2);
+DECLARE_KOBJ_TTYPE_NBEAT(2);
+DECLARE_KOBJ_TTYPE_NBYTE(2);
+DECLARE_KOBJ_TTYPE_BURST(2);
+DECLARE_KOBJ_TTYPE_RW(2);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(2);
+
+DECLARE_KOBJ_TTYPE_MASTER(3);
+DECLARE_KOBJ_TTYPE_NBEAT(3);
+DECLARE_KOBJ_TTYPE_NBYTE(3);
+DECLARE_KOBJ_TTYPE_BURST(3);
+DECLARE_KOBJ_TTYPE_RW(3);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(3);
+
+DECLARE_KOBJ_TTYPE_MASTER(4);
+DECLARE_KOBJ_TTYPE_NBEAT(4);
+DECLARE_KOBJ_TTYPE_NBYTE(4);
+DECLARE_KOBJ_TTYPE_BURST(4);
+DECLARE_KOBJ_TTYPE_RW(4);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(4);
+
+DECLARE_KOBJ_TTYPE_MASTER(5);
+DECLARE_KOBJ_TTYPE_NBEAT(5);
+DECLARE_KOBJ_TTYPE_NBYTE(5);
+DECLARE_KOBJ_TTYPE_BURST(5);
+DECLARE_KOBJ_TTYPE_RW(5);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(5);
+
+DECLARE_KOBJ_TTYPE_MASTER(6);
+DECLARE_KOBJ_TTYPE_NBEAT(6);
+DECLARE_KOBJ_TTYPE_NBYTE(6);
+DECLARE_KOBJ_TTYPE_BURST(6);
+DECLARE_KOBJ_TTYPE_RW(6);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(6);
+
+DECLARE_KOBJ_TTYPE_MASTER(7);
+DECLARE_KOBJ_TTYPE_NBEAT(7);
+DECLARE_KOBJ_TTYPE_NBYTE(7);
+DECLARE_KOBJ_TTYPE_BURST(7);
+DECLARE_KOBJ_TTYPE_RW(7);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(7);
+
+DECLARE_KOBJ_TTYPE_MASTER(8);
+DECLARE_KOBJ_TTYPE_NBEAT(8);
+DECLARE_KOBJ_TTYPE_NBYTE(8);
+DECLARE_KOBJ_TTYPE_BURST(8);
+DECLARE_KOBJ_TTYPE_RW(8);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(8);
+
+DECLARE_KOBJ_TTYPE_MASTER(9);
+DECLARE_KOBJ_TTYPE_NBEAT(9);
+DECLARE_KOBJ_TTYPE_NBYTE(9);
+DECLARE_KOBJ_TTYPE_BURST(9);
+DECLARE_KOBJ_TTYPE_RW(9);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(9);
+
+DECLARE_KOBJ_TTYPE_MASTER(10);
+DECLARE_KOBJ_TTYPE_NBEAT(10);
+DECLARE_KOBJ_TTYPE_NBYTE(10);
+DECLARE_KOBJ_TTYPE_BURST(10);
+DECLARE_KOBJ_TTYPE_RW(10);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(10);
+
+DECLARE_KOBJ_TTYPE_MASTER(11);
+DECLARE_KOBJ_TTYPE_NBEAT(11);
+DECLARE_KOBJ_TTYPE_NBYTE(11);
+DECLARE_KOBJ_TTYPE_BURST(11);
+DECLARE_KOBJ_TTYPE_RW(11);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(11);
+
+DECLARE_KOBJ_TTYPE_MASTER(12);
+DECLARE_KOBJ_TTYPE_NBEAT(12);
+DECLARE_KOBJ_TTYPE_NBYTE(12);
+DECLARE_KOBJ_TTYPE_BURST(12);
+DECLARE_KOBJ_TTYPE_RW(12);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(12);
+
+DECLARE_KOBJ_TTYPE_MASTER(13);
+DECLARE_KOBJ_TTYPE_NBEAT(13);
+DECLARE_KOBJ_TTYPE_NBYTE(13);
+DECLARE_KOBJ_TTYPE_BURST(13);
+DECLARE_KOBJ_TTYPE_RW(13);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(13);
+
+DECLARE_KOBJ_TTYPE_MASTER(14);
+DECLARE_KOBJ_TTYPE_NBEAT(14);
+DECLARE_KOBJ_TTYPE_NBYTE(14);
+DECLARE_KOBJ_TTYPE_BURST(14);
+DECLARE_KOBJ_TTYPE_RW(14);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(14);
+
+DECLARE_KOBJ_TTYPE_MASTER(15);
+DECLARE_KOBJ_TTYPE_NBEAT(15);
+DECLARE_KOBJ_TTYPE_NBYTE(15);
+DECLARE_KOBJ_TTYPE_BURST(15);
+DECLARE_KOBJ_TTYPE_RW(15);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(15);
+
+DECLARE_KOBJ_TTYPE_MASTER(16);
+DECLARE_KOBJ_TTYPE_NBEAT(16);
+DECLARE_KOBJ_TTYPE_NBYTE(16);
+DECLARE_KOBJ_TTYPE_BURST(16);
+DECLARE_KOBJ_TTYPE_RW(16);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(16);
+
+DECLARE_KOBJ_TTYPE_MASTER(17);
+DECLARE_KOBJ_TTYPE_NBEAT(17);
+DECLARE_KOBJ_TTYPE_NBYTE(17);
+DECLARE_KOBJ_TTYPE_BURST(17);
+DECLARE_KOBJ_TTYPE_RW(17);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(17);
+
+DECLARE_KOBJ_TTYPE_MASTER(18);
+DECLARE_KOBJ_TTYPE_NBEAT(18);
+DECLARE_KOBJ_TTYPE_NBYTE(18);
+DECLARE_KOBJ_TTYPE_BURST(18);
+DECLARE_KOBJ_TTYPE_RW(18);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(18);
+
+DECLARE_KOBJ_TTYPE_MASTER(19);
+DECLARE_KOBJ_TTYPE_NBEAT(19);
+DECLARE_KOBJ_TTYPE_NBYTE(19);
+DECLARE_KOBJ_TTYPE_BURST(19);
+DECLARE_KOBJ_TTYPE_RW(19);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(19);
+
+DECLARE_KOBJ_TTYPE_MASTER(20);
+DECLARE_KOBJ_TTYPE_NBEAT(20);
+DECLARE_KOBJ_TTYPE_NBYTE(20);
+DECLARE_KOBJ_TTYPE_BURST(20);
+DECLARE_KOBJ_TTYPE_RW(20);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(20);
+
+DECLARE_KOBJ_TTYPE_MASTER(21);
+DECLARE_KOBJ_TTYPE_NBEAT(21);
+DECLARE_KOBJ_TTYPE_NBYTE(21);
+DECLARE_KOBJ_TTYPE_BURST(21);
+DECLARE_KOBJ_TTYPE_RW(21);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(21);
+
+#define KOBJ_ATTR_ITEM_SERIAL_FNODE(nr) \
+	do { \
+		KOBJ_ATTR_ITEM(ttype ## nr ## _master); \
+		KOBJ_ATTR_ITEM(ttype ## nr ## _nbeat); \
+		KOBJ_ATTR_ITEM(ttype ## nr ## _nbyte); \
+		KOBJ_ATTR_ITEM(ttype ## nr ## _burst); \
+		KOBJ_ATTR_ITEM(ttype ## nr ## _busid); \
+		KOBJ_ATTR_ITEM(ttype ## nr ## _rw); \
+	} while (0)
+
+#define KOBJ_ATTR_LIST \
+	do { \
+		KOBJ_ATTR_ITEM(high_priority_filter); \
+		KOBJ_ATTR_ITEM(emi_TP_busfiltr_enable); \
+		KOBJ_ATTR_ITEM(msel_enable); \
+		KOBJ_ATTR_ITEM(msel_group1); \
+		KOBJ_ATTR_ITEM(msel_group2); \
+		KOBJ_ATTR_ITEM(msel_group3); \
+		KOBJ_ATTR_ITEM(ttype17_21_en); \
+		KOBJ_ATTR_ITEM(ttype1_16_en); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(1); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(2); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(3); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(4); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(5); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(6); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(7); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(8); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(9); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(10); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(11); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(12); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(13); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(14); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(15); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(16); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(17); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(18); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(19); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(20); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(21); \
+		KOBJ_ATTR_ITEM(test); \
+	} while (0)
+
+/*======================================================================*/
+/*	EMI Operations							*/
+/*======================================================================*/
+static void emi_init(void)
+{
+	unsigned int bmrw0_val = 0, bmrw1_val = 0, i, enable;
+	unsigned int msel_group_val[4];
+
+	MET_BM_SaveCfg();
+
+	if ((ttype1_16_en != BM_TTYPE1_16_ENABLE) && (emi_TP_busfiltr_enable != 1)) {
+		if (msel_enable) {
+			msel_group_val[0] = _ALL;
+			msel_group_val[1] = msel_group1;
+			msel_group_val[2] = msel_group2;
+			msel_group_val[3] = msel_group3;
+		} else {
+			msel_group_val[0] = _ALL;
+			msel_group_val[1] = _GP_1_Default;
+			msel_group_val[2] = _GP_2_Default;
+			msel_group_val[3] = _GP_3_Default;
+		}
+
+		MET_BM_SetLatencyCounter(1);
+
+		for (i = 1; i <= 4; i++) {
+			MET_BM_SetMonitorCounter(i,
+						 msel_group_val[i - 1] & _ALL,
+						 BM_TRANS_TYPE_4BEAT |
+						 BM_TRANS_TYPE_8Byte |
+						 BM_TRANS_TYPE_BURST_WRAP);
+			MET_BM_SetbusID(i, 0);
+			MET_BM_SetbusID_En(i, 0);
+		}
+		for (i = 0; i < 4; i++)
+			MET_BM_Set_WsctTsct_id_sel(i, 0);
+
+	} else if ((ttype1_16_en != BM_TTYPE1_16_ENABLE) && (emi_TP_busfiltr_enable == 1)) {
+		MET_BM_SetLatencyCounter(1);
+
+		for (i = 1; i <= 4; i++) {
+			MET_BM_SetMonitorCounter(i,
+						 ttype_master_val[i - 1],
+						 ttype_nbeat_val[i - 1] |
+						 ttype_nbyte_val[i - 1] |
+						 ttype_burst_val[i - 1]);
+			MET_BM_SetbusID(i, ttype_busid_val[i - 1]);
+			MET_BM_SetbusID_En(i, 0);
+		}
+		for (i = 0; i < 4; i++)
+			MET_BM_Set_WsctTsct_id_sel(i, 1);
+
+	} else if ((ttype1_16_en == BM_TTYPE1_16_ENABLE) && (emi_TP_busfiltr_enable != 1)) {
+		MET_BM_SetLatencyCounter(0);
+
+		for (i = 1; i <= 16; i++) {
+			MET_BM_SetMonitorCounter(i,
+						 ttype_master_val[i - 1],
+						 ttype_nbeat_val[i - 1] |
+						 ttype_nbyte_val[i - 1] |
+						 ttype_burst_val[i - 1]);
+
+			MET_BM_SetbusID(i, ttype_busid_val[i - 1]);
+
+			MET_BM_SetbusID_En(i, (ttype_busid_val[i - 1] > 0xffff) ? 0 : 1);
+		}
+		for (i = 0; i < 4; i++)
+			MET_BM_Set_WsctTsct_id_sel(i, 0);
+	} else {	/* (ttype1_16_en == BM_TTYPE1_16_ENABLE)  &&  (emi_TP_busfiltr_enable == 1) */
+
+		MET_BM_SetLatencyCounter(0);
+
+		for (i = 1; i <= 16; i++) {
+			MET_BM_SetMonitorCounter(i,
+						 ttype_master_val[i - 1],
+						 ttype_nbeat_val[i - 1] |
+						 ttype_nbyte_val[i - 1] |
+						 ttype_burst_val[i - 1]);
+
+			MET_BM_SetbusID(i, ttype_busid_val[i - 1]);
+
+			MET_BM_SetbusID_En(i, (ttype_busid_val[i - 1] > 0xffff) ? 0 : 1);
+		}
+		for (i = 0; i < 4; i++)
+			MET_BM_Set_WsctTsct_id_sel(i, 1);
+	}
+
+	if (ttype17_21_en == BM_TTYPE17_21_ENABLE) {
+		for (i = 17; i <= 21; i++) {
+			MET_BM_SetMonitorCounter(i,
+						 ttype_master_val[i - 1],
+						 ttype_nbeat_val[i - 1] |
+						 ttype_nbyte_val[i - 1] |
+						 ttype_burst_val[i - 1]);
+			MET_BM_SetbusID(i, ttype_busid_val[i - 1]);
+
+			MET_BM_SetbusID_En(i, (ttype_busid_val[i - 1] > 0xffff) ? 0 : 1);
+		}
+	}
+
+	bmrw0_val = (
+		(ttype_rw_val[0] << 0) | (ttype_rw_val[1] << 2) |
+		(ttype_rw_val[2] << 4) | (ttype_rw_val[3] << 6) |
+		(ttype_rw_val[4] << 8) | (ttype_rw_val[5] << 10) |
+		(ttype_rw_val[6] << 12) | (ttype_rw_val[7] << 14) |
+		(ttype_rw_val[8] << 16) | (ttype_rw_val[9] << 18) |
+		(ttype_rw_val[10] << 20) | (ttype_rw_val[11] << 22) |
+		(ttype_rw_val[12] << 24) | (ttype_rw_val[13] << 26) |
+		(ttype_rw_val[14] << 28) | (ttype_rw_val[15] << 30));
+
+	bmrw1_val = (
+		(ttype_rw_val[16] << 0) | (ttype_rw_val[17] << 2) |
+		(ttype_rw_val[18] << 4) | (ttype_rw_val[19] << 6) |
+		(ttype_rw_val[20] << 8));
+
+	MET_BM_SetTtypeCounterRW(bmrw0_val, bmrw1_val);
+
+	for (i = 0; i < BM_COUNTER_MAX; i++) {
+		if ((high_priority_filter & (1 << i)) == 0)
+			enable = 0;
+		else
+			enable = 1;
+
+		MET_BM_SetUltraHighFilter(i + 1, enable);
+	}
+
+}
+
+static inline int do_emi(void)
+{
+	return met_sspm_emi.mode;
+}
+
+/*======================================================================*/
+/*	MET Device Operations						*/
+/*======================================================================*/
+static int emi_inited;
+static int met_emi_create(struct kobject *parent)
+{
+	int ret = 0;
+	int i;
+
+	for (i = 0; i < 21; i++) {
+		ttype_master_val[i] = _M0;
+		ttype_nbeat_val[i] = BM_TRANS_TYPE_1BEAT;
+		ttype_nbyte_val[i] = BM_TRANS_TYPE_8Byte;
+		ttype_burst_val[i] = BM_TRANS_TYPE_BURST_INCR;
+		ttype_busid_val[i] = 0xfffff;
+		ttype_rw_val[i] =  BM_TRANS_RW_DEFAULT;
+	}
+	ret = MET_BM_Init();
+	if (ret != 0) {
+		pr_debug("MET_BM_Init failed!!!\n");
+		ret = 0;
+	} else
+		emi_inited = 1;
+
+	kobj_emi = parent;
+
+#define	KOBJ_ATTR_ITEM(attr_name) \
+do { \
+	ret = sysfs_create_file(kobj_emi, &attr_name##_attr.attr); \
+	if (ret != 0) { \
+		pr_debug("Failed to create " #attr_name " in sysfs\n"); \
+		return ret; \
+	} \
+} while (0)
+	KOBJ_ATTR_LIST;
+#undef	KOBJ_ATTR_ITEM
+
+	return ret;
+}
+
+static void met_emi_delete(void)
+{
+#define	KOBJ_ATTR_ITEM(attr_name) \
+	sysfs_remove_file(kobj_emi, &attr_name##_attr.attr)
+	if (kobj_emi != NULL) {
+		KOBJ_ATTR_LIST;
+		kobj_emi = NULL;
+	}
+#undef	KOBJ_ATTR_ITEM
+
+	if (emi_inited)
+		MET_BM_DeInit();
+}
+
+
+static void met_emi_resume(void)
+{
+	if (!do_emi())
+		return;
+
+	emi_init();
+}
+
+
+#ifdef CONFIG_MTK_TINYSYS_SSPM_SUPPORT
+static const char help[] = "  --emi                                 monitor EMI banwidth\n";
+
+#define TTYPE_NAME_STR_LEN  64
+static char ttype_name[21][TTYPE_NAME_STR_LEN];
+
+static int emi_print_help(char *buf, int len)
+{
+	return snprintf(buf, PAGE_SIZE, help);
+}
+
+static int emi_print_header(char *buf, int len)
+{
+	int ret = 0;
+	int ret_m[21];
+	int i = 0;
+
+
+	for (i = 0; i < 21; i++) {
+		int k;
+
+		if (ttype_busid_val[i] > 0xffff) {
+			int j;
+
+			for (j = 0; j < ARRAY_SIZE(ttype_master_list_item); j++) {
+				if (ttype_master_val[i] == ttype_master_list_item[j].key) {
+					ret_m[i] = snprintf(ttype_name[i], TTYPE_NAME_STR_LEN, "ttyp%d_%s",
+							    i + 1, ttype_master_list_item[j].val);
+					break;
+				}
+			}
+			if (j == ARRAY_SIZE(ttype_master_list_item))
+				ret_m[i] = snprintf(ttype_name[i], TTYPE_NAME_STR_LEN, "ttyp%d_%s",
+						    i + 1, "unknown");
+		} else {
+			ret_m[i] = snprintf(ttype_name[i], TTYPE_NAME_STR_LEN, "ttyp%d_%x",
+					    i + 1, ttype_busid_val[i]);
+		}
+
+		for (k = 0; k < ARRAY_SIZE(ttype_nbeat_list_item); k++) {
+			if (ttype_nbeat_val[i] == ttype_nbeat_list_item[k].key)
+				ret_m[i] += snprintf(ttype_name[i] + ret_m[i], TTYPE_NAME_STR_LEN - ret_m[i], "_%d",
+						     ttype_nbeat_list_item[k].val);
+		}
+
+		for (k = 0; k < ARRAY_SIZE(ttype_nbyte_list_item); k++) {
+			if (ttype_nbyte_val[i] == ttype_nbyte_list_item[k].key)
+				ret_m[i] += snprintf(ttype_name[i] + ret_m[i], TTYPE_NAME_STR_LEN - ret_m[i], "x%d",
+						     ttype_nbyte_list_item[k].val);
+		}
+
+		for (k = 0; k < ARRAY_SIZE(ttype_burst_list_item); k++) {
+			if (ttype_burst_val[i] == ttype_burst_list_item[k].key)
+				ret_m[i] += snprintf(ttype_name[i] + ret_m[i], TTYPE_NAME_STR_LEN - ret_m[i], "_%s",
+						     ttype_burst_list_item[k].val);
+		}
+
+		for (k = 0; k < ARRAY_SIZE(ttype_rw_list_item); k++) {
+			if (ttype_rw_val[i] == ttype_rw_list_item[k].key)
+				ret_m[i] += snprintf(ttype_name[i] + ret_m[i], TTYPE_NAME_STR_LEN - ret_m[i], "_%s",
+						     ttype_rw_list_item[k].val);
+		}
+	}
+	if ((ttype1_16_en != BM_TTYPE1_16_ENABLE) && (emi_TP_busfiltr_enable != 1)) {
+		if (msel_enable) {
+			ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"met-info [000] 0.0: met_emi_msel: %x,%x,%x\n",
+				msel_group1 & _ALL,
+				msel_group2 & _ALL,
+				msel_group3 & _ALL);
+		} else {
+			ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"met-info [000] 0.0: met_emi_msel: %x,%x,%x\n",
+				_GP_1_Default & _ALL,
+				_GP_2_Default & _ALL,
+				_GP_3_Default & _ALL);
+		}
+	} else {
+		ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"met-info [000] 0.0: met_emi_ttype_master: %x,%x,%x,%x\n",
+				ttype_master_val[0], ttype_master_val[1], ttype_master_val[2], ttype_master_val[3]);
+
+		if (emi_TP_busfiltr_enable == 1) {
+
+			ret += snprintf(buf + ret, PAGE_SIZE - ret,
+					"met-info [000] 0.0: met_emi_ttype_busid: %x,%x,%x,%x\n",
+					ttype_busid_val[0], ttype_busid_val[1], ttype_busid_val[2], ttype_busid_val[3]);
+		}
+	}
+
+	ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_emi_rw_cfg: ");
+	ret += snprintf(buf + ret, PAGE_SIZE - ret, "BOTH");
+
+	for (i = 0; i < 21; i++) {
+		if (ttype_rw_val[i] == BM_TRANS_RW_DEFAULT)
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, ",DEFAULT");
+		else if (ttype_rw_val[i] == BM_TRANS_RW_READONLY)
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, ",R");
+		else if (ttype_rw_val[i] == BM_TRANS_RW_WRITEONLY)
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, ",W");
+		else
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, ",BOTH");
+	}
+	ret += snprintf(buf + ret, PAGE_SIZE - ret, "\n");
+
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"met-info [000] 0.0: met_emi_ultra_filter: %x\n", high_priority_filter);
+	if ((ttype1_16_en == BM_TTYPE1_16_ENABLE) && (ttype17_21_en == BM_TTYPE17_21_ENABLE)) {
+		int i;
+
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: ms_ud_sys_header: ms_ttype,");
+		for (i = 0; i < 21; i++)
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, "%s,", ttype_name[i]);
+
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x\n");
+
+	} else if (ttype17_21_en == BM_TTYPE17_21_ENABLE) {
+		int i;
+
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: ms_ud_sys_header: ms_ttype,");
+
+		for (i = 16; i < 21; i++)
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, "%s,", ttype_name[i]);
+
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "x,x,x,x,x\n");
+	}
+
+	return ret;
+}
+
+static int ondiemet_emi_print_header(char *buf, int len)
+{
+	return emi_print_header(buf, len);
+}
+
+static void MET_BM_IPI_REGISTER_CB(void)
+{
+	int ret, i;
+	unsigned int rdata;
+	unsigned int ipi_buf[4];
+
+	for (i = 0; i < 4; i++)
+		ipi_buf[i] = 0;
+
+	if (sspm_buf_available == 1) {
+		ipi_buf[0] = MET_MAIN_ID | (MID_EMI << MID_BIT_SHIFT) | MET_ARGU | SET_REGISTER_CB;
+		ret = sspm_ipi_send_sync(IPI_ID_MET, IPI_OPT_WAIT, (void *)ipi_buf, 0, &rdata, 1);
+	}
+}
+
+static void MET_BM_IPI_configs(void)
+{
+	int ret, i;
+	unsigned int rdata;
+	unsigned int ipi_buf[4];
+
+	for (i = 0; i < 4; i++)
+		ipi_buf[i] = 0;
+
+	if (sspm_buf_available == 1) {
+		ipi_buf[0] = MET_MAIN_ID | (MID_EMI << MID_BIT_SHIFT) | MET_ARGU | SET_EBM_CONFIGS1;
+		ret = sspm_ipi_send_sync(IPI_ID_MET, IPI_OPT_WAIT, (void *)ipi_buf, 0, &rdata, 1);
+	}
+}
+
+static void ondiemet_emi_start(void)
+{
+	MET_BM_IPI_REGISTER_CB();
+	if (!emi_inited) {
+		if (MET_BM_Init() != 0) {
+			met_sspm_emi.mode = 0;
+			pr_notice("MET_BM_Init failed!!!\n");
+			PR_BOOTMSG("MET_BM_Init failed!!!\n");
+			return;
+		}
+		emi_inited = 1;
+	}
+	MET_BM_IPI_configs();
+
+	if (do_emi())
+		emi_init();
+
+	ondiemet_module[ONDIEMET_SSPM] |= ID_EMI;
+}
+
+static void emi_uninit(void)
+{
+	MET_BM_RestoreCfg();
+}
+
+static void ondiemet_emi_stop(void)
+{
+	if (!emi_inited)
+		return;
+
+	if (do_emi())
+		emi_uninit();
+}
+#endif
+
+struct metdevice met_sspm_emi = {
+	.name			= "emi",
+	.owner			= THIS_MODULE,
+	.type			= MET_TYPE_BUS,
+	.create_subfs		= met_emi_create,
+	.delete_subfs		= met_emi_delete,
+	.resume			= met_emi_resume,
+#ifdef CONFIG_MTK_TINYSYS_SSPM_SUPPORT
+	.ondiemet_start		= ondiemet_emi_start,
+	.ondiemet_stop		= ondiemet_emi_stop,
+	.ondiemet_print_help	= emi_print_help,
+	.ondiemet_print_header	= ondiemet_emi_print_header,
+#endif
+	.ondiemet_mode		= 1,
+};
+EXPORT_SYMBOL(met_sspm_emi);
diff --git a/src/devtools/met-driver/4.4/common/met_kernel_symbol.h b/src/devtools/met-driver/4.4/common/met_kernel_symbol.h
new file mode 100644
index 0000000..2ef14b8
--- /dev/null
+++ b/src/devtools/met-driver/4.4/common/met_kernel_symbol.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef MET_KERNEL_SYMBOL
+#define MET_KERNEL_SYMBOL
+
+/*lookup symbol*/
+#include <linux/kallsyms.h>
+#include <linux/perf_event.h>
+#include <linux/kthread.h>
+
+extern void (*tracing_record_cmdline_symbol)(struct task_struct *tsk);
+extern void met_cpu_frequency(unsigned int frequency, unsigned int cpu_id);
+extern void (*met_cpu_frequency_symbol)(unsigned int frequency, unsigned int cpu_id);
+extern void (*met_arch_setup_dma_ops_symbol)(struct device *dev);
+extern u64 (*met_perf_event_read_local_symbol)(struct perf_event *ev);
+extern struct task_struct *(*met_kthread_create_on_cpu_symbol)(int (*threadfn)(void *data),
+				void *data, unsigned int cpu,
+				const char *namefmt);
+extern int (*met_smp_call_function_single_symbol)(int cpu, smp_call_func_t func, void *info, int wait);
+
+extern void met_tracing_record_cmdline(struct task_struct *tsk);
+extern int met_reg_switch(void);
+extern int (*met_reg_switch_symbol)(void);
+extern void met_unreg_switch(void);
+extern void (*met_unreg_switch_symbol)(void);
+extern void met_arch_setup_dma_ops(struct device *dev);
+extern u64 met_perf_event_read_local(struct perf_event *ev);
+extern struct task_struct *met_kthread_create_on_cpu(int (*threadfn)(void *data),
+				void *data, unsigned int cpu,
+				const char *namefmt);
+extern int met_smp_call_function_single(int cpu, smp_call_func_t func, void *info, int wait);
+extern void met_arch_send_call_function_single_ipi(int cpu);
+#endif	/* MET_KERNEL_SYMBOL */
diff --git a/src/devtools/met-driver/4.4/common/met_main.c b/src/devtools/met-driver/4.4/common/met_main.c
new file mode 100644
index 0000000..0a52243
--- /dev/null
+++ b/src/devtools/met-driver/4.4/common/met_main.c
@@ -0,0 +1,278 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/hrtimer.h>
+#include <linux/cpu.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/profile.h>
+#include <linux/dcache.h>
+#include <linux/types.h>
+#include <linux/dcookies.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+
+#include <asm/irq_regs.h>
+
+#include "met_struct.h"
+#include "met_drv.h"
+#include "met_kernel_symbol.h"
+#include "interface.h"
+#include <linux/of.h>
+
+
+extern struct device_node *of_root;
+static const char *platform_name;
+
+struct cpu_type_name {
+	char full_name[32];
+	char abbr_name[8];
+};
+
+static struct cpu_type_name met_known_cpu_type[] = {
+	{"arm,cortex-a53", "CA53"},
+	{"arm,cortex-a73", "CA73"},
+};
+#define MET_KNOWN_CPU_TYPE_COUNT \
+	(sizeof(met_known_cpu_type)/sizeof(struct cpu_type_name))
+
+static char met_cpu_topology[64];
+
+void (*tracing_record_cmdline_symbol)(struct task_struct *tsk);
+void (*met_cpu_frequency_symbol)(unsigned int frequency, unsigned int cpu_id);
+int (*met_reg_switch_symbol)(void);
+void (*met_unreg_switch_symbol)(void);
+
+void (*met_arch_setup_dma_ops_symbol)(struct device *dev);
+u64 (*met_perf_event_read_local_symbol)(struct perf_event *ev);
+struct task_struct *(*met_kthread_create_on_cpu_symbol)(int (*threadfn)(void *data),
+				void *data, unsigned int cpu,
+				const char *namefmt);
+int (*met_smp_call_function_single_symbol)(int cpu, smp_call_func_t func, void *info, int wait);
+
+
+const char *met_get_platform_name(void)
+{
+	return platform_name;
+}
+EXPORT_SYMBOL(met_get_platform_name);
+
+static void get_cpu_type_name(const char *compatible, char *cpu_type)
+{
+	int i;
+
+	for (i = 0; i < MET_KNOWN_CPU_TYPE_COUNT; i++) {
+		if (!strncmp(compatible, met_known_cpu_type[i].full_name,
+					strlen(met_known_cpu_type[i].full_name)))
+			strncpy(cpu_type, met_known_cpu_type[i].abbr_name,
+					strlen(met_known_cpu_type[i].abbr_name) + 1);
+	}
+}
+
+static void met_set_cpu_topology(int core_id, int cluster_core_num)
+{
+	int i, buf_len = strlen(met_cpu_topology);
+	struct device_node *node = NULL;
+	const char *prev_cptb = NULL;
+	const char *cptb;
+	char cpu_type[16];
+
+	for (i = 0; i < cluster_core_num; i++) {
+		node = of_get_cpu_node(core_id + i, NULL);
+		if (node) {
+			cptb = of_get_property(node, "compatible", NULL);
+			if (cptb) {
+				get_cpu_type_name(cptb, cpu_type);
+				if (prev_cptb == NULL)
+					/* first write:  write core_type & core_number */
+					buf_len += snprintf(met_cpu_topology + buf_len,
+								sizeof(met_cpu_topology) - buf_len,
+								"%s:%d", cpu_type, core_id + i);
+				else if (!strncmp(prev_cptb, cptb, strlen(prev_cptb)))
+					/* cpu type is the same with before */
+					/* write core_number */
+					buf_len += snprintf(met_cpu_topology + buf_len,
+								sizeof(met_cpu_topology) - buf_len,
+								",%d", core_id + i);
+				else
+					/* cpu type is different with before */
+					/* write core_type & core_number */
+					buf_len += snprintf(met_cpu_topology + buf_len,
+								sizeof(met_cpu_topology) - buf_len,
+								"|%s:%d", cpu_type, core_id + i);
+
+				prev_cptb = cptb;
+			}
+		}
+	}
+}
+
+static int met_create_cpu_topology(void)
+{
+	int i, len;
+	struct device_node *node = NULL;
+	int start_core_id = 0;
+	int cluster_num = 0, cluster_core_num = 0;
+	char cluster_name[16];
+
+	node = of_find_node_by_name(NULL, "cpu-map");
+	if (node) {
+		cluster_num = of_get_child_count(node);
+
+		for (i = 0; i < cluster_num; i++) {
+			snprintf(cluster_name, sizeof(cluster_name), "cluster%d", i);
+			node = of_find_node_by_name(NULL, cluster_name);
+			if (node) {
+				cluster_core_num = of_get_child_count(node);
+
+				/* "|" use to separate different cluster */
+				if (i > 0) {
+					len = strlen(met_cpu_topology);
+					snprintf(met_cpu_topology + len, sizeof(met_cpu_topology) - len, "|");
+				}
+
+				met_set_cpu_topology(start_core_id, cluster_core_num);
+				start_core_id = cluster_core_num;
+			}
+		}
+	}
+
+	return strlen(met_cpu_topology);
+}
+
+static int met_kernel_symbol_get(void)
+{
+	int ret = 0;
+
+	if (tracing_record_cmdline_symbol == NULL)
+		tracing_record_cmdline_symbol = (void *)symbol_get(met_tracing_record_cmdline);
+	if (tracing_record_cmdline_symbol == NULL)
+		ret = -3;
+
+	if (met_cpu_frequency_symbol == NULL)
+		met_cpu_frequency_symbol = (void *)symbol_get(met_cpu_frequency);
+	if (met_cpu_frequency_symbol == NULL)
+		ret = -4;
+
+	if (met_reg_switch_symbol == NULL)
+		met_reg_switch_symbol = (void *)symbol_get(met_reg_switch);
+	if (met_reg_switch_symbol == NULL)
+		ret = -5;
+
+	if (met_unreg_switch_symbol == NULL)
+		met_unreg_switch_symbol = (void *)symbol_get(met_unreg_switch);
+	if (met_unreg_switch_symbol == NULL)
+		ret = -6;
+
+	if (met_arch_setup_dma_ops_symbol == NULL)
+		met_arch_setup_dma_ops_symbol = (void *)symbol_get(met_arch_setup_dma_ops);
+	if (met_arch_setup_dma_ops_symbol == NULL)
+		ret = -7;
+
+	if (met_perf_event_read_local_symbol == NULL)
+		met_perf_event_read_local_symbol = (void *)symbol_get(met_perf_event_read_local);
+	if (met_perf_event_read_local_symbol == NULL)
+		ret = -8;
+
+	if (met_kthread_create_on_cpu_symbol == NULL)
+		met_kthread_create_on_cpu_symbol = (void *)symbol_get(met_kthread_create_on_cpu);
+	if (met_kthread_create_on_cpu_symbol == NULL)
+		ret = -9;
+
+	if (met_smp_call_function_single_symbol == NULL)
+		met_smp_call_function_single_symbol = (void *)symbol_get(met_smp_call_function_single);
+	if (met_smp_call_function_single_symbol == NULL)
+		ret = -10;
+	return ret;
+}
+
+DEFINE_PER_CPU(struct met_cpu_struct, met_cpu);
+
+static int __init met_drv_init(void)
+{
+	int cpu;
+	int ret;
+	int cpu_topology_len;
+	struct met_cpu_struct *met_cpu_ptr;
+
+	for_each_possible_cpu(cpu) {
+		met_cpu_ptr = &per_cpu(met_cpu, cpu);
+		/* snprintf(&(met_cpu_ptr->name[0]), sizeof(met_cpu_ptr->name), "met%02d", cpu); */
+		met_cpu_ptr->cpu = cpu;
+	}
+
+	ret = met_kernel_symbol_get();
+	if (ret) {
+		pr_notice("[MET] met_kernel_symbol_get fail, ret = %d\n", ret);
+		return ret;
+	}
+	fs_reg();
+
+	if (of_root){
+		/*
+			mt6765.dts
+			model = "MT6765";
+			compatible = "mediatek,MT6765";
+			interrupt-parent = <&sysirq>;
+		*/
+		if (of_root->properties) {
+			of_property_read_string(of_root, of_root->properties->name, &platform_name);
+			PR_BOOTMSG("platform_name=%s\n", platform_name);
+		}
+	}
+	if (platform_name) {
+		char buf[7];
+
+		memset(buf, 0x0, 7);
+		buf[0] = 'm';
+		buf[1] = 't';
+		strncpy(&buf[2], &platform_name[2], 4);
+		met_set_platform(buf, 1);
+		PR_BOOTMSG("buf=%s\n", buf);
+	}
+
+	cpu_topology_len = met_create_cpu_topology();
+	if (cpu_topology_len)
+		met_set_topology(met_cpu_topology, 1);
+
+#ifdef MET_PLF_USE
+	core_plf_init();
+#endif
+	return 0;
+}
+
+static void __exit met_drv_exit(void)
+{
+	if (met_cpu_frequency_symbol)
+		symbol_put(met_cpu_frequency);
+	if (met_reg_switch_symbol)
+		symbol_put(met_reg_switch);
+	if (met_unreg_switch_symbol)
+		symbol_put(met_unreg_switch);
+	if (tracing_record_cmdline_symbol)
+		symbol_put(met_tracing_record_cmdline);
+
+#ifdef MET_PLF_USE
+	core_plf_exit();
+#endif
+	fs_unreg();
+
+}
+module_init(met_drv_init);
+module_exit(met_drv_exit);
+
+MODULE_AUTHOR("DT_DM5");
+MODULE_DESCRIPTION("MET_CORE");
+MODULE_LICENSE("GPL");
diff --git a/src/devtools/met-driver/4.4/common/met_ptpod.c b/src/devtools/met-driver/4.4/common/met_ptpod.c
new file mode 100644
index 0000000..19cbed5
--- /dev/null
+++ b/src/devtools/met-driver/4.4/common/met_ptpod.c
@@ -0,0 +1,363 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/random.h>
+#include <linux/fs.h>
+
+#include "met_drv.h"
+#include "trace.h"
+#include "core_plf_init.h"
+#include "core_plf_trace.h"
+
+
+static unsigned int MT_GPU_DVFS_IDX = NR_MT_CPU_DVFS;
+static unsigned int g_u4GPUVolt;
+static unsigned int g_u4Volt[NR_MT_CPU_DVFS + 1];
+
+/* g_ap_ptpod: cpu volt from ap or sspm setting
+ * if 1: cpu volt from ap
+ * if 0: cpu volt from sspm */
+static unsigned int g_ap_ptpod = 1;
+
+/* gpu_volt_enable:
+ * if 1: enable gpu volt to output
+ * if 0: disable gpu volt to output */
+static unsigned int gpu_volt_enable = 1;
+
+/* get_volt_by_wq:
+ * if 1: get cpu/gpu volt by workqueue
+ * if 0: get cpu/gpu volt in irq */
+static unsigned int get_volt_by_wq;
+
+static int ptpod_started;
+static struct kobject *kobj_ptpod;
+static struct delayed_work get_volt_dwork;
+
+noinline void ms_ptpod(void)
+{
+	char *SOB, *EOB;
+
+	if (g_ap_ptpod) {
+		MET_TRACE_GETBUF(&SOB, &EOB);
+
+		if (gpu_volt_enable) {
+			g_u4Volt[MT_GPU_DVFS_IDX] = g_u4GPUVolt;
+			EOB = ms_formatD_EOL(EOB, ARRAY_SIZE(g_u4Volt), g_u4Volt);
+		} else
+			EOB = ms_formatD_EOL(EOB, ARRAY_SIZE(g_u4Volt) - 1, g_u4Volt);
+
+		MET_TRACE_PUTBUF(SOB, EOB);
+	} else {
+		if (gpu_volt_enable) {
+			MET_TRACE_GETBUF(&SOB, &EOB);
+			EOB = ms_formatD_EOL(EOB, 1, &g_u4GPUVolt);
+			MET_TRACE_PUTBUF(SOB, EOB);
+		}
+	}
+}
+
+#if 0
+static void ptpod_cpu_voltSampler(enum mt_cpu_dvfs_id id, unsigned int volt)
+{
+	switch (id) {
+	case MT_CPU_DVFS_LL:
+		g_u4CPUVolt_LL = volt;
+		break;
+	case MT_CPU_DVFS_L:
+		g_u4CPUVolt_L = volt;
+		break;
+	case MT_CPU_DVFS_CCI:
+		g_u4CPUVolt_CCI = volt;
+		break;
+	default:
+		return;
+	}
+	ptpod();
+}
+#endif
+
+#if 0
+static void ptpod_gpu_voltSampler(unsigned int a_u4Volt)
+{
+	g_u4GPUVolt = (a_u4Volt+50)/100;
+
+	if (ptpod_started)
+		ptpod();
+}
+#endif
+
+#define PTPOD_CONF_SHOW_IMPLEMENT(var) \
+	do { \
+		int i; \
+		i = snprintf(buf, PAGE_SIZE, "%d\n", var); \
+		return i; \
+	} while (0)
+
+#define PTPOD_CONF_STORE_IMPLEMENT(var) \
+	do { \
+		int value; \
+		if ((n == 0) || (buf == NULL)) \
+			return -EINVAL; \
+		if (kstrtoint(buf, 0, &value) != 0) \
+			return -EINVAL; \
+		if (value == 1) \
+			var = 1; \
+		else \
+			var = 0; \
+		return n; \
+	} while (0)
+
+
+static ssize_t ap_ptpod_enable_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	PTPOD_CONF_SHOW_IMPLEMENT(g_ap_ptpod);
+}
+
+static ssize_t ap_ptpod_enable_store(struct kobject *kobj,
+			struct kobj_attribute *attr,
+			const char *buf,
+			size_t n)
+{
+	PTPOD_CONF_STORE_IMPLEMENT(g_ap_ptpod);
+}
+
+static ssize_t gpu_volt_enable_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	PTPOD_CONF_SHOW_IMPLEMENT(gpu_volt_enable);
+}
+
+static ssize_t gpu_volt_enable_store(struct kobject *kobj,
+			struct kobj_attribute *attr,
+			const char *buf,
+			size_t n)
+{
+	PTPOD_CONF_STORE_IMPLEMENT(gpu_volt_enable);
+}
+
+static ssize_t get_volt_by_wq_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	PTPOD_CONF_SHOW_IMPLEMENT(get_volt_by_wq);
+}
+
+static ssize_t get_volt_by_wq_store(struct kobject *kobj,
+			struct kobj_attribute *attr,
+			const char *buf,
+			size_t n)
+{
+	PTPOD_CONF_STORE_IMPLEMENT(get_volt_by_wq);
+}
+
+static struct kobj_attribute ap_ptpod_enable_attr = __ATTR(ap_ptpod_enable, 0664, ap_ptpod_enable_show, ap_ptpod_enable_store);
+static struct kobj_attribute gpu_volt_enable_attr = __ATTR(gpu_volt_enable, 0664, gpu_volt_enable_show, gpu_volt_enable_store);
+static struct kobj_attribute get_volt_by_wq_attr = __ATTR(get_volt_by_wq, 0664, get_volt_by_wq_show, get_volt_by_wq_store);
+
+/* create ptpod related kobj node */
+#define KOBJ_ATTR_LIST \
+	do { \
+		KOBJ_ATTR_ITEM(ap_ptpod_enable); \
+		KOBJ_ATTR_ITEM(gpu_volt_enable); \
+		KOBJ_ATTR_ITEM(get_volt_by_wq); \
+	} while (0)
+
+static int ptpod_create(struct kobject *parent)
+{
+	int ret = 0;
+
+	kobj_ptpod = parent;
+
+#define KOBJ_ATTR_ITEM(attr_name) \
+	do { \
+		ret = sysfs_create_file(kobj_ptpod, &attr_name ## _attr.attr); \
+		if (ret != 0) { \
+			pr_notice("Failed to create " #attr_name " in sysfs\n"); \
+			return ret; \
+		} \
+	} while (0)
+	KOBJ_ATTR_LIST;
+#undef  KOBJ_ATTR_ITEM
+
+	return 0;
+}
+
+static void ptpod_delete(void)
+{
+#define KOBJ_ATTR_ITEM(attr_name) \
+	sysfs_remove_file(kobj_ptpod, &attr_name ## _attr.attr)
+
+	if (kobj_ptpod != NULL) {
+		KOBJ_ATTR_LIST;
+		kobj_ptpod = NULL;
+	}
+#undef  KOBJ_ATTR_ITEM
+}
+
+static void update_volt_value(void)
+{
+	int i;
+
+	if (g_ap_ptpod && mt_cpufreq_get_cur_volt_symbol) {
+		/*
+		g_u4CPUVolt_LL = mt_cpufreq_get_cur_volt_symbol(MT_CPU_DVFS_LL)/100;
+		g_u4CPUVolt_L = mt_cpufreq_get_cur_volt_symbol(MT_CPU_DVFS_L)/100;
+		g_u4CPUVolt_CCI = mt_cpufreq_get_cur_volt_symbol(MT_CPU_DVFS_CCI)/100;
+		*/
+		for (i = 0; i < NR_MT_CPU_DVFS; i++)
+			g_u4Volt[i] = mt_cpufreq_get_cur_volt_symbol(i) / 100;
+	}
+
+	if (gpu_volt_enable) {
+		if (mt_gpufreq_get_cur_volt_symbol)
+			g_u4GPUVolt = ((mt_gpufreq_get_cur_volt_symbol() + 50) / 100);
+	}
+}
+
+static void get_volt_notify(unsigned long long stamp, int cpu)
+{
+	schedule_delayed_work(&get_volt_dwork, 0);
+}
+
+static void met_ptpod_polling_by_wq(struct work_struct *work)
+{
+	update_volt_value();
+
+	ms_ptpod();
+}
+
+static void met_ptpod_polling(unsigned long long stamp, int cpu)
+{
+	update_volt_value();
+
+	ms_ptpod();
+}
+
+/*
+ * Called from "met-cmd --start"
+ */
+static void ptpod_start(void)
+{
+#if 0
+	met_gpufreq_setvolt_registerCB(ptpod_gpu_voltSampler, kFOR_MET_PTPOD_USE);
+#endif
+
+	if (get_volt_by_wq) {
+		met_ptpod.timed_polling = get_volt_notify;
+
+		INIT_DELAYED_WORK(&get_volt_dwork, met_ptpod_polling_by_wq);
+	} else
+		met_ptpod.timed_polling = met_ptpod_polling;
+
+	update_volt_value();
+
+	ms_ptpod();
+
+#if 0
+	/* register callback */
+	if (mt_cpufreq_setvolt_registerCB_symbol)
+		mt_cpufreq_setvolt_registerCB_symbol(ptpod_cpu_voltSampler);
+#endif
+
+	ptpod_started = 1;
+}
+
+/*
+ * Called from "met-cmd --stop"
+ */
+static void ptpod_stop(void)
+{
+	ptpod_started = 0;
+
+#if 0
+	/* unregister callback */
+	if (mt_cpufreq_setvolt_registerCB_symbol)
+		mt_cpufreq_setvolt_registerCB_symbol(NULL);
+#endif
+
+	if (get_volt_by_wq)
+		cancel_delayed_work_sync(&get_volt_dwork);
+
+	update_volt_value();
+
+	ms_ptpod();
+
+#if 0
+	met_gpufreq_setvolt_registerCB(NULL, kFOR_MET_PTPOD_USE);
+#endif
+}
+
+static char help[] =
+	"  --ptpod                               Measure CPU/GPU voltage\n";
+static int ptpod_print_help(char *buf, int len)
+{
+	return snprintf(buf, PAGE_SIZE, help);
+}
+
+/*
+ * It will be called back when run "met-cmd --extract" and mode is 1
+ */
+static int ptpod_print_header(char *buf, int len)
+{
+	int str_len = 0;
+
+	if (g_ap_ptpod) {
+		int i;
+
+		str_len += snprintf(buf + str_len, PAGE_SIZE - str_len,
+						"met-info [000] 0.0: met_ptpod_header: ");
+
+		for (i = 0; i < NR_MT_CPU_DVFS; i++)
+			str_len += snprintf(buf + str_len, PAGE_SIZE - str_len, "CPUVolt_%d,", i);
+
+		if (gpu_volt_enable)
+			str_len += snprintf(buf + str_len, PAGE_SIZE - str_len, "GPUVolt\n");
+
+		str_len += snprintf(buf + str_len, PAGE_SIZE - str_len,
+							"met-info [000] 0.0: met_ptpod_version: ap\n");
+	} else {
+		if (gpu_volt_enable) {
+			str_len += snprintf(buf + str_len, PAGE_SIZE - str_len,
+							"met-info [000] 0.0: met_ptpod_header: ");
+
+			str_len += snprintf(buf + str_len, PAGE_SIZE - str_len, "GPUVolt\n");
+		}
+
+		str_len += snprintf(buf + str_len, PAGE_SIZE - str_len,
+							"met-info [000] 0.0: met_ptpod_version: sspm\n");
+	}
+
+	if (gpu_volt_enable)
+		str_len += snprintf(buf + str_len, PAGE_SIZE - str_len,
+						"met-info [000] 0.0: met_ptpod_gpu_volt_enable: YES\n");
+	else
+		str_len += snprintf(buf + str_len, PAGE_SIZE - str_len,
+						"met-info [000] 0.0: met_ptpod_gpu_volt_enable: NO\n");
+
+	return str_len;
+}
+
+struct metdevice met_ptpod = {
+	.name = "ptpod",
+	.owner = THIS_MODULE,
+	.type = MET_TYPE_PMU,
+	.cpu_related = 0,
+	.create_subfs = ptpod_create,
+	.delete_subfs = ptpod_delete,
+	.start = ptpod_start,
+	.stop = ptpod_stop,
+	.timed_polling = met_ptpod_polling,
+	.print_help = ptpod_print_help,
+	.print_header = ptpod_print_header,
+};
+EXPORT_SYMBOL(met_ptpod);
diff --git a/src/devtools/met-driver/4.4/common/met_struct.h b/src/devtools/met-driver/4.4/common/met_struct.h
new file mode 100644
index 0000000..a74ff78
--- /dev/null
+++ b/src/devtools/met-driver/4.4/common/met_struct.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MET_STRUCT_H_
+#define _MET_STRUCT_H_
+
+#include <linux/hrtimer.h>
+
+struct met_cpu_struct {
+	struct hrtimer hrtimer;
+	struct delayed_work dwork;
+/* struct kmem_cache *cachep; */
+/* struct list_head sample_head; */
+/* spinlock_t list_lock; */
+/* struct mutex list_sync_lock; */
+	int work_enabled;
+	int cpu;
+	int hrtimer_online_check;
+/* char name[16]; */
+};
+
+DECLARE_PER_CPU(struct met_cpu_struct, met_cpu);
+
+#endif				/* _MET_STRUCT_H_ */
diff --git a/src/devtools/met-driver/4.4/common/met_tag.h b/src/devtools/met-driver/4.4/common/met_tag.h
new file mode 100644
index 0000000..04b5e09
--- /dev/null
+++ b/src/devtools/met-driver/4.4/common/met_tag.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __MET_TAG_EX_H__
+#define __MET_TAG_EX_H__
+
+#ifdef BUILD_WITH_MET
+void force_sample(void *unused);
+#else
+#include <linux/string.h>
+#endif
+
+/* Black List Table */
+struct bltable_t {
+	struct mutex mlock;
+	/* flag - Bit31: Global ON/OFF; Bit0~30: ON/OF slot map of class_id */
+	unsigned int flag;
+	int class_id[MAX_EVENT_CLASS];
+};
+
+extern void met_sched_switch(struct task_struct *prev, struct task_struct *next);
+
+extern int tracing_mark_write(int type, unsigned int class_id,
+		const char *name, unsigned int value,
+		unsigned int value2, unsigned int value3);
+
+#endif				/* __MET_TAG_EX_H__ */
diff --git a/src/devtools/met-driver/4.4/common/met_tag_ex.c b/src/devtools/met-driver/4.4/common/met_tag_ex.c
new file mode 100644
index 0000000..9e250f1
--- /dev/null
+++ b/src/devtools/met-driver/4.4/common/met_tag_ex.c
@@ -0,0 +1,629 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define BUILD_WITH_MET
+
+#ifdef MET_USER_EVENT_SUPPORT
+
+#include <linux/kernel.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/fs.h>
+/* #include <asm/uaccess.h> */
+#include <linux/uaccess.h>
+
+#include "met_drv.h"
+#include "met_tag.h"
+#include "interface.h"
+#include "switch.h"
+#include "met_api_tbl.h"
+
+struct bltable_t bltab;
+
+static int dump_buffer_size;
+static int dump_data_size;
+static int dump_overrun;
+static int dump_overrun_size;
+static int dump_seq_no;
+static void *dump_buffer;
+
+#define OPFLAG_OVERWRITE	0x1
+static unsigned int options_flag;
+
+#define DEVICE_NAME		"met_tag"
+
+/* #define ERRF_ENABLE */
+/* #define DEBF_ENABLE */
+
+#ifdef ERRF_ENABLE
+#define MSG_ERR			"Error:["DEVICE_NAME"]"
+#define ERRF(args...)	pr_debug(MSG_ERR args)
+#else
+#define ERRF(args...)
+#endif
+
+#ifdef DEBF_ENABLE
+#define MSG_IFO			"Info :["DEVICE_NAME"]"
+#define DEBF(args...)	pr_debug(MSG_IFO args)
+#else
+#define DEBF(args...)
+#endif
+
+static int is_enabled(unsigned int class_id)
+{
+	int i;
+
+	if (bltab.flag == 0)
+		return 1;
+	if (bltab.flag & MET_CLASS_ALL)
+		return 0;
+
+	for (i = 0; i < MAX_EVENT_CLASS; i++) {
+		if ((bltab.flag & (1 << i)) && (bltab.class_id[i] == class_id))
+			return 0;
+	}
+
+	return 1;
+}
+
+noinline int tracing_mark_write(int type, unsigned int class_id,
+				       const char *name, unsigned int value,
+				       unsigned int value2, unsigned int value3)
+{
+	if (type == TYPE_MET_SUSPEND) {
+		MET_TRACE("C|0|MET_SUSPEND|1");
+		return 0;
+	}
+	if (type == TYPE_MET_RESUME) {
+		MET_TRACE("C|0|MET_SUSPEND|0");
+		return 0;
+	}
+	if (!is_enabled(class_id))
+		return 0;
+	switch (type) {
+	case TYPE_START:
+		MET_TRACE("B|%d|%s\n", class_id, name);
+		break;
+	case TYPE_END:
+		MET_TRACE("E|%s\n", name);
+		break;
+	case TYPE_ONESHOT:
+		MET_TRACE("C|%d|%s|%d\n", class_id, name, value);
+		break;
+	case TYPE_ASYNC_START:
+		MET_TRACE("S|%d|%s|%d\n", class_id, name, value);
+		break;
+	case TYPE_ASYNC_END:
+		MET_TRACE("F|%d|%s|%d\n", class_id, name, value);
+		break;
+	case TYPE_DUMP:
+		MET_TRACE("D|%d|%s|%d|%d|%d\n", class_id, name, value, value2, value3);
+		break;
+	default:
+		return -1;
+	}
+	return 0;
+}
+
+int met_tag_init(void)
+{
+	memset(&bltab, 0, sizeof(struct bltable_t));
+	bltab.flag = MET_CLASS_ALL;
+	mutex_init(&bltab.mlock);
+	return 0;
+}
+
+int met_tag_uninit(void)
+{
+	met_set_dump_buffer_real(0);
+	return 0;
+}
+
+int met_tag_start_real(unsigned int class_id, const char *name)
+{
+	int ret;
+
+	ret = tracing_mark_write(TYPE_START, class_id, name, 0, 0, 0);
+#if 0
+	if ((met_switch.mode & MT_SWITCH_TAGPOLLING)) {
+		/* tag polling only enable when MT_SWITCH_TAGPOLLING is config */
+		force_sample(NULL);
+	}
+#endif
+	return ret;
+}
+EXPORT_SYMBOL(met_tag_start_real);
+
+int met_tag_end_real(unsigned int class_id, const char *name)
+{
+	int ret;
+#if 0
+	if ((met_switch.mode & MT_SWITCH_TAGPOLLING)) {
+		/* tag polling only enable when MT_SWITCH_TAGPOLLING is config */
+		force_sample(NULL);
+	}
+#endif
+	ret = tracing_mark_write(TYPE_END, class_id, name, 0, 0, 0);
+
+	return ret;
+}
+EXPORT_SYMBOL(met_tag_end_real);
+
+int met_tag_async_start_real(unsigned int class_id, const char *name, unsigned int cookie)
+{
+	int ret;
+
+	ret = tracing_mark_write(TYPE_ASYNC_START, class_id, name, cookie, 0, 0);
+#if 0
+	if ((met_switch.mode & MT_SWITCH_TAGPOLLING)) {
+		/* tag polling only enable when MT_SWITCH_TAGPOLLING is config */
+		force_sample(NULL);
+	}
+#endif
+	return ret;
+}
+
+int met_tag_async_end_real(unsigned int class_id, const char *name, unsigned int cookie)
+{
+	int ret;
+
+#if 0
+	if ((met_switch.mode & MT_SWITCH_TAGPOLLING)) {
+		/* tag polling only enable when MT_SWITCH_TAGPOLLING is config */
+		force_sample(NULL);
+	}
+#endif
+	ret = tracing_mark_write(TYPE_ASYNC_END, class_id, name, cookie, 0, 0);
+	return ret;
+}
+
+int met_tag_oneshot_real(unsigned int class_id, const char *name, unsigned int value)
+{
+	int ret;
+
+	ret = tracing_mark_write(TYPE_ONESHOT, class_id, name, value, 0, 0);
+#if 0
+	if ((met_switch.mode & MT_SWITCH_TAGPOLLING)) {
+		/* tag polling only enable when MT_SWITCH_TAGPOLLING is config */
+		force_sample(NULL);
+	}
+#endif
+	return ret;
+}
+EXPORT_SYMBOL(met_tag_oneshot_real);
+
+int met_tag_userdata_real(char *pData)
+{
+	MET_TRACE("%s\n", pData);
+	return 0;
+}
+
+int met_tag_dump_real(unsigned int class_id, const char *name, void *data, unsigned int length)
+{
+	int ret;
+
+	if ((dump_data_size + length + sizeof(int)) > dump_buffer_size) {
+		if (options_flag & OPFLAG_OVERWRITE) {
+			dump_overrun_size = dump_data_size;
+			dump_overrun++;
+			memcpy(dump_buffer, &dump_seq_no, sizeof(int));
+			memcpy(dump_buffer + sizeof(int), data, length);
+			ret = tracing_mark_write(TYPE_DUMP, class_id, name,
+						 dump_seq_no++, 0, length + sizeof(int));
+			dump_data_size = length + sizeof(int);
+		} else {
+			ret = tracing_mark_write(TYPE_DUMP, class_id, name, dump_seq_no++, 0, 0);
+		}
+	} else {
+		memcpy(dump_buffer + dump_data_size, &dump_seq_no, sizeof(int));
+		memcpy(dump_buffer + dump_data_size + sizeof(int), data, length);
+		ret = tracing_mark_write(TYPE_DUMP, class_id, name,
+					 dump_seq_no++, dump_data_size, length + sizeof(int));
+		dump_data_size += length + sizeof(int);
+	}
+	return ret;
+}
+
+int met_tag_disable_real(unsigned int class_id)
+{
+	int i;
+
+	mutex_lock(&bltab.mlock);
+
+	if (class_id == MET_CLASS_ALL) {
+		bltab.flag |= MET_CLASS_ALL;
+		mutex_unlock(&bltab.mlock);
+		return 0;
+	}
+
+	for (i = 0; i < MAX_EVENT_CLASS; i++) {
+		if ((bltab.flag & (1 << i)) == 0) {
+			bltab.class_id[i] = class_id;
+			bltab.flag |= (1 << i);
+			mutex_unlock(&bltab.mlock);
+			return 0;
+		}
+	}
+
+	mutex_unlock(&bltab.mlock);
+	return -1;
+}
+
+int met_tag_enable_real(unsigned int class_id)
+{
+	int i;
+
+	mutex_lock(&bltab.mlock);
+
+	if (class_id == MET_CLASS_ALL) {
+		bltab.flag &= (~MET_CLASS_ALL);
+		mutex_unlock(&bltab.mlock);
+		return 0;
+	}
+
+	for (i = 0; i < MAX_EVENT_CLASS; i++) {
+		if ((bltab.flag & (1 << i)) && (bltab.class_id[i] == class_id)) {
+			bltab.flag &= (~(1 << i));
+			bltab.class_id[i] = 0;
+			mutex_unlock(&bltab.mlock);
+			return 0;
+		}
+	}
+
+	mutex_unlock(&bltab.mlock);
+	return -1;
+}
+
+int met_set_dump_buffer_real(int size)
+{
+	if (dump_buffer_size && dump_buffer) {
+		free_pages((unsigned long)dump_buffer, get_order(dump_buffer_size));
+		dump_data_size = 0;
+		dump_overrun = 0;
+		dump_overrun_size = 0;
+		dump_seq_no = 0;
+		dump_buffer_size = 0;
+	}
+	/* size is 0 means free dump buffer */
+	if (size == 0)
+		return 0;
+
+	size = (size + (PAGE_SIZE - 1)) & (~(PAGE_SIZE - 1));
+	dump_buffer = (void *)__get_free_pages(GFP_KERNEL, get_order(size));
+	if (dump_buffer == NULL) {
+		ERRF("can not allocate buffer to copy\n");
+		return -ENOMEM;
+	}
+
+	dump_buffer_size = size;
+	return dump_buffer_size;
+}
+
+int met_save_dump_buffer_real(const char *pathname)
+{
+	int size, ret = 0;
+	struct file *outfp = NULL;
+	mm_segment_t oldfs;
+
+	if (dump_data_size == 0)
+		return 0;
+
+	outfp = filp_open(pathname, O_WRONLY | O_TRUNC | O_CREAT, 0664);
+	if (unlikely(outfp == NULL)) {
+		ERRF("can not open saved file for write\n");
+		return -EIO;
+	}
+
+	oldfs = get_fs();
+	set_fs(KERNEL_DS);
+
+	if (dump_overrun)
+		size = dump_overrun_size;
+	else
+		size = dump_data_size;
+
+	ret = vfs_write(outfp, dump_buffer, size, &(outfp->f_pos));
+	if (ret < 0) {
+		ERRF("can not write to dump file\n");
+	} else {
+		dump_data_size = 0;
+		dump_overrun = 0;
+		dump_overrun_size = 0;
+		dump_seq_no = 0;
+	}
+
+	set_fs(oldfs);
+
+	return 0;
+}
+
+int met_save_log_real(const char *pathname)
+{
+	int len, ret = 0;
+	struct file *infp = NULL;
+	struct file *outfp = NULL;
+	void *ptr = NULL;
+	mm_segment_t oldfs;
+
+	infp = filp_open("/sys/kernel/debug/tracing/trace", O_RDONLY, 0);
+	if (unlikely(infp == NULL)) {
+		ERRF("can not open trace file for read\n");
+		ret = -1;
+		goto save_out;
+	}
+
+	outfp = filp_open(pathname, O_WRONLY | O_TRUNC | O_CREAT, 0664);
+	if (unlikely(outfp == NULL)) {
+		ERRF("can not open saved file for write\n");
+		ret = -2;
+		goto save_out;
+	}
+
+	ptr = (void *)__get_free_pages(GFP_KERNEL, 2);
+	if (ptr == NULL) {
+		ERRF("can not allocate buffer to copy\n");
+		ret = -3;
+		goto save_out;
+	}
+
+	oldfs = get_fs();
+	set_fs(KERNEL_DS);
+
+	while (1) {
+		len = vfs_read(infp, ptr, PAGE_SIZE << 2, &(infp->f_pos));
+		if (len < 0) {
+			ERRF("can not read from trace file\n");
+			ret = -3;
+			break;
+		} else if (len == 0) {
+			break;
+		}
+
+		ret = vfs_write(outfp, ptr, len, &(outfp->f_pos));
+		if (ret < 0) {
+			ERRF("can not write to saved file\n");
+			break;
+		}
+	}
+
+	set_fs(oldfs);
+
+save_out:
+	if (ptr != NULL)
+		free_pages((unsigned long)ptr, 2);
+	if (infp != NULL)
+		filp_close(infp, NULL);
+	if (outfp != NULL)
+		filp_close(outfp, NULL);
+
+	return ret;
+}
+
+#ifdef BUILD_WITH_MET
+#include <linux/module.h>
+#include <linux/uaccess.h>
+/* =========================================================================== */
+/* misc file nodes */
+/* =========================================================================== */
+static ssize_t enable_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	int i;
+
+	i = snprintf(buf, PAGE_SIZE, "%d\n", (bltab.flag >> 31) ? 0 : 1);
+	return i;
+}
+
+static ssize_t enable_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf,
+			    size_t n)
+{
+	int value;
+
+	if ((n == 0) || (buf == NULL))
+		return -EINVAL;
+	if (kstrtoint(buf, 0, &value) != 0)
+		return -EINVAL;
+
+	mutex_lock(&bltab.mlock);
+
+	if (value == 1)
+		bltab.flag &= (~MET_CLASS_ALL);
+	else
+		bltab.flag |= MET_CLASS_ALL;
+
+	mutex_unlock(&bltab.mlock);
+
+	return n;
+}
+
+static struct kobject *kobj_tag;
+static struct kobj_attribute enable_attr = __ATTR(enable, 0664, enable_show, enable_store);
+
+static ssize_t dump_buffer_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	int i, size;
+
+	if (dump_overrun)
+		size = dump_overrun_size;
+	else
+		size = dump_data_size;
+
+	i = snprintf(buf, PAGE_SIZE, "Buffer Size (KB)=%d\nData Size (KB)=%d\nOverrun=%d\n",
+		     dump_buffer_size >> 10, size >> 10, dump_overrun);
+	return i;
+}
+
+static ssize_t dump_buffer_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf,
+				 size_t n)
+{
+	int ret, value;
+
+	if ((n == 0) || (buf == NULL))
+		return -EINVAL;
+	if (kstrtoint(buf, 0, &value) != 0)
+		return -EINVAL;
+
+	ret = met_set_dump_buffer_real(value << 10);
+
+	if (ret < 0)
+		return ret;
+
+	return n;
+}
+
+static struct kobj_attribute dump_buffer_attr =
+__ATTR(dump_buffer_kb, 0664, dump_buffer_show, dump_buffer_store);
+
+static ssize_t options_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	int i = 0;
+
+	buf[0] = 0;
+
+	if (options_flag == 0) {
+		strncat(buf, "none\n", PAGE_SIZE - 1 - i);
+		i += 5;
+	}
+
+	if (options_flag & OPFLAG_OVERWRITE) {
+		strncat(buf, "overwrite\n", PAGE_SIZE - 1 - i);
+		i += 10;
+	}
+
+	return i;
+}
+
+static ssize_t options_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf,
+			     size_t n)
+{
+	if ((n == 0) || (buf == NULL))
+		return -EINVAL;
+
+	if ((n == 1) && (buf[0] == 0xA)) {
+		options_flag = 0;
+		return n;
+	}
+
+	if (strncmp(buf, "overwrite", 9) == 0)
+		options_flag |= OPFLAG_OVERWRITE;
+	else
+		return -EINVAL;
+
+	return n;
+}
+
+static struct kobj_attribute options_attr = __ATTR(options, 0664, options_show, options_store);
+
+int tag_reg(struct file_operations *const fops, struct kobject *kobj)
+{
+	int ret;
+
+	kobj_tag = kobject_create_and_add("tag", kobj);
+	if (kobj_tag == NULL) {
+		ERRF("can not create kobject: kobj_bus\n");
+		return -1;
+	}
+
+	ret = sysfs_create_file(kobj_tag, &enable_attr.attr);
+	if (ret != 0) {
+		ERRF("Failed to create enable in sysfs\n");
+		kobject_del(kobj_tag);
+		kobject_put(kobj_tag);
+		kobj_tag = NULL;
+		return ret;
+	}
+
+	ret = sysfs_create_file(kobj_tag, &dump_buffer_attr.attr);
+	if (ret != 0) {
+		ERRF("Failed to create dump_buffer in sysfs\n");
+		sysfs_remove_file(kobj_tag, &enable_attr.attr);
+		kobject_del(kobj_tag);
+		kobject_put(kobj_tag);
+		kobj_tag = NULL;
+		return ret;
+	}
+
+	ret = sysfs_create_file(kobj_tag, &options_attr.attr);
+	if (ret != 0) {
+		ERRF("Failed to create options in sysfs\n");
+		sysfs_remove_file(kobj_tag, &enable_attr.attr);
+		sysfs_remove_file(kobj_tag, &dump_buffer_attr.attr);
+		kobject_del(kobj_tag);
+		kobject_put(kobj_tag);
+		kobj_tag = NULL;
+		return ret;
+	}
+
+	met_tag_init();
+	met_ext_api.met_tag_start = met_tag_start_real;
+	met_ext_api.met_tag_end = met_tag_end_real;
+	met_ext_api.met_tag_async_start = met_tag_async_start_real;
+	met_ext_api.met_tag_async_end = met_tag_async_end_real;
+	met_ext_api.met_tag_oneshot = met_tag_oneshot_real;
+	met_ext_api.met_tag_userdata = met_tag_userdata_real;
+	met_ext_api.met_tag_dump = met_tag_dump_real;
+	met_ext_api.met_tag_disable = met_tag_disable_real;
+	met_ext_api.met_tag_enable = met_tag_enable_real;
+	met_ext_api.met_set_dump_buffer = met_set_dump_buffer_real;
+	met_ext_api.met_save_dump_buffer = met_save_dump_buffer_real;
+	met_ext_api.met_save_log = met_save_log_real;
+	met_ext_api.met_sched_switch = met_sched_switch;
+	return 0;
+}
+
+int tag_unreg(void)
+{
+	met_ext_api.met_tag_start = NULL;
+	met_ext_api.met_tag_end = NULL;
+	met_ext_api.met_tag_async_start = NULL;
+	met_ext_api.met_tag_async_end = NULL;
+	met_ext_api.met_tag_oneshot = NULL;
+	met_ext_api.met_tag_userdata = NULL;
+	met_ext_api.met_tag_dump = NULL;
+	met_ext_api.met_tag_disable = NULL;
+	met_ext_api.met_tag_enable = NULL;
+	met_ext_api.met_set_dump_buffer = NULL;
+	met_ext_api.met_save_dump_buffer = NULL;
+	met_ext_api.met_save_log = NULL;
+	met_ext_api.met_show_bw_limiter = NULL;
+	met_ext_api.met_reg_bw_limiter = NULL;
+	met_ext_api.met_show_clk_tree = NULL;
+	met_ext_api.met_reg_clk_tree = NULL;
+	met_ext_api.met_sched_switch = NULL;
+	met_tag_uninit();
+	sysfs_remove_file(kobj_tag, &enable_attr.attr);
+	sysfs_remove_file(kobj_tag, &dump_buffer_attr.attr);
+	sysfs_remove_file(kobj_tag, &options_attr.attr);
+	kobject_del(kobj_tag);
+	kobject_put(kobj_tag);
+	kobj_tag = NULL;
+	return 0;
+}
+
+#endif				/* BUILD_WITH_MET */
+
+#else				/* not MET_USER_EVENT_SUPPORT */
+
+#ifdef BUILD_WITH_MET
+int tag_reg(void *p, void *q)
+{
+	return 0;
+}
+
+int tag_unreg(void)
+{
+	return 0;
+}
+#endif
+
+#endif				/* MET_USER_EVENT_SUPPORT */
diff --git a/src/devtools/met-driver/4.4/common/met_vcoredvfs.c b/src/devtools/met-driver/4.4/common/met_vcoredvfs.c
new file mode 100644
index 0000000..090e548
--- /dev/null
+++ b/src/devtools/met-driver/4.4/common/met_vcoredvfs.c
@@ -0,0 +1,373 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/dma-mapping.h>
+
+#include <mtk_vcorefs_governor.h>
+#include <mtk_spm_vcore_dvfs.h>
+
+#define MET_USER_EVENT_SUPPORT
+#include "met_drv.h"
+#include "trace.h"
+
+#include "core_plf_init.h"
+#include "core_plf_trace.h"
+
+/*======================================================================*/
+/*	Global variable definitions					*/
+/*======================================================================*/
+
+/* Global variables */
+struct metdevice met_vcoredvfs;
+int polling_mode = 1;
+
+/* external symbols */
+#define DEFAULT_INFO_NUM 3
+#define DEFAULT_SRC_NUM 1
+
+char *default_info_name[DEFAULT_INFO_NUM] = {
+	"OPP",
+	"VOLT",
+	"FREQ"
+};
+
+char *default_src_name[DEFAULT_SRC_NUM] = {
+	"MD2SPM"
+};
+
+unsigned int opp_info[DEFAULT_INFO_NUM];
+unsigned int src_req[DEFAULT_SRC_NUM];
+
+static char *met_governor_get_kicker_name(int id)
+{
+	if (governor_get_kicker_name_symbol)
+		return governor_get_kicker_name_symbol(id);
+	else
+		return "KIR_UNKNOWN";
+}
+
+static int met_vcorefs_get_num_opp(void)
+{
+	if (vcorefs_get_num_opp_symbol)
+		return vcorefs_get_num_opp_symbol();
+	else
+		return 0;
+}
+
+static int met_vcorefs_get_opp_info_num(void)
+{
+	if (vcorefs_get_opp_info_num_symbol)
+		return vcorefs_get_opp_info_num_symbol();
+	else
+		return DEFAULT_INFO_NUM;
+}
+
+static char **met_vcorefs_get_opp_info_name(void)
+{
+	if (vcorefs_get_opp_info_name_symbol)
+		return vcorefs_get_opp_info_name_symbol();
+	else
+		return default_info_name;
+}
+
+static char **met_vcorefs_get_src_req_name(void)
+{
+	if (vcorefs_get_src_req_name_symbol)
+		return vcorefs_get_src_req_name_symbol();
+	else
+		return default_src_name;
+}
+
+static int met_vcorefs_get_src_req_num(void)
+{
+	if (vcorefs_get_src_req_num_symbol)
+		return vcorefs_get_src_req_num_symbol();
+	else
+		return DEFAULT_SRC_NUM;
+}
+
+static unsigned int *met_vcorefs_get_opp_info(void)
+{
+	int count = 0;
+
+	if (vcorefs_get_opp_info_symbol)
+		return vcorefs_get_opp_info_symbol();
+
+	if (vcorefs_get_hw_opp_symbol)
+		opp_info[count++] = vcorefs_get_hw_opp_symbol();
+	if (vcorefs_get_curr_vcore_symbol)
+		opp_info[count++] = vcorefs_get_curr_vcore_symbol(); /* uV */
+	if (vcorefs_get_curr_ddr_symbol)
+		opp_info[count++] = vcorefs_get_curr_ddr_symbol(); /* kHz */
+
+	return opp_info;
+}
+
+static unsigned int *met_vcorefs_get_src_req(void)
+{
+	if (vcorefs_get_src_req_symbol)
+		return vcorefs_get_src_req_symbol();
+
+	if (spm_vcorefs_get_MD_status_symbol)
+		src_req[0] = spm_vcorefs_get_MD_status_symbol();
+	return src_req;
+}
+
+
+/*======================================================================*/
+/*	File Node definitions					*/
+/*======================================================================*/
+
+static struct kobject *kobj_met_vcoredvfs;
+
+static ssize_t vcorefs_polling_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf);
+static ssize_t vcorefs_polling_store(struct kobject *kobj,
+				   struct kobj_attribute *attr, const char *buf, size_t n);
+static struct kobj_attribute vcorefs_polling_attr =
+__ATTR(vcorefs_polling, 0664, vcorefs_polling_show, vcorefs_polling_store);
+
+/*======================================================================*/
+/*	Utilities					*/
+/*======================================================================*/
+
+static inline int do_vcoredvfs(void)
+{
+	return met_vcoredvfs.mode;
+}
+
+/*======================================================================*/
+/*	Data Output					*/
+/*======================================================================*/
+
+noinline void vcorefs(unsigned char cnt, unsigned int *value)
+{
+	char *SOB, *EOB;
+
+	MET_TRACE_GETBUF(&SOB, &EOB);
+	EOB = ms_formatH_EOL(EOB, cnt, value);
+	MET_TRACE_PUTBUF(SOB, EOB);
+}
+
+noinline void vcorefs_kicker(unsigned char cnt, int *value)
+{
+	char *SOB, *EOB;
+
+	MET_TRACE_GETBUF(&SOB, &EOB);
+	EOB = ms_formatH_EOL(EOB, cnt, value);
+	MET_TRACE_PUTBUF(SOB, EOB);
+}
+
+noinline void ms_vcorefs(unsigned char cnt, unsigned int *value)
+{
+	char *SOB, *EOB;
+
+	MET_TRACE_GETBUF(&SOB, &EOB);
+	EOB = ms_formatH_EOL(EOB, cnt, value);
+	MET_TRACE_PUTBUF(SOB, EOB);
+}
+
+/*======================================================================*/
+/*	Callback functions						*/
+/*======================================================================*/
+void sw_kicker_req(enum dvfs_kicker kicker, enum dvfs_opp opp)
+{
+	if (!do_vcoredvfs())
+		return;
+
+	if (kicker_table_symbol)
+		vcorefs_kicker(NUM_KICKER, kicker_table_symbol);
+}
+
+void vcoredvfs_irq(int opp)
+{
+	int num;
+	unsigned int *out;
+
+	num = met_vcorefs_get_opp_info_num();
+	out = met_vcorefs_get_opp_info();
+
+	vcorefs(num, out);
+}
+
+/*======================================================================*/
+/*	File Node Operations						*/
+/*======================================================================*/
+static ssize_t vcorefs_polling_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%d\n", polling_mode);
+}
+
+static ssize_t vcorefs_polling_store(struct kobject *kobj,
+				   struct kobj_attribute *attr, const char *buf, size_t n)
+{
+	int value;
+
+	if ((n == 0) || (buf == NULL))
+		return -EINVAL;
+
+	if (kstrtoint(buf, 0, &value) != 0)
+		return -EINVAL;
+
+	if (value < 0)
+		return -EINVAL;
+
+	polling_mode = value;
+
+	return n;
+}
+
+/*======================================================================*/
+/*	MET Device Operations						*/
+/*======================================================================*/
+static int met_vcoredvfs_create(struct kobject *parent)
+{
+	int ret = 0;
+
+	kobj_met_vcoredvfs = parent;
+
+	ret = sysfs_create_file(kobj_met_vcoredvfs, &vcorefs_polling_attr.attr);
+	if (ret != 0) {
+		pr_debug("Failed to create vcoredvfs in sysfs\n");
+		return ret;
+	}
+
+	return ret;
+}
+
+static void met_vcoredvfs_delete(void)
+{
+	sysfs_remove_file(kobj_met_vcoredvfs, &vcorefs_polling_attr.attr);
+}
+
+static void met_vcoredvfs_start(void)
+{
+	if (kicker_table_symbol)
+		vcorefs_kicker(NUM_KICKER, kicker_table_symbol);
+
+	vcoredvfs_irq(-1);
+
+	if (!polling_mode && spm_vcorefs_register_handler_symbol)
+		spm_vcorefs_register_handler_symbol(vcoredvfs_irq);
+
+	if (vcorefs_register_req_notify_symbol)
+		vcorefs_register_req_notify_symbol(sw_kicker_req);
+	else
+		MET_TRACE("vcorefs_register_req_notify not exist!\n");
+
+	if (!polling_mode && vcorefs_enable_debug_isr_symbol)
+		vcorefs_enable_debug_isr_symbol(true);
+}
+
+static void met_vcoredvfs_stop(void)
+{
+	if (!polling_mode && vcorefs_enable_debug_isr_symbol)
+		vcorefs_enable_debug_isr_symbol(false);
+
+	if (vcorefs_register_req_notify_symbol)
+		vcorefs_register_req_notify_symbol(NULL);
+
+	if (spm_vcorefs_register_handler_symbol)
+		spm_vcorefs_register_handler_symbol(NULL);
+
+	if (kicker_table_symbol)
+		vcorefs_kicker(NUM_KICKER, kicker_table_symbol);
+	vcoredvfs_irq(-1);
+}
+
+static void met_vcoredvfs_polling(unsigned long long stamp, int cpu)
+{
+	int num;
+	unsigned int *out;
+
+	if (!do_vcoredvfs())
+		return;
+
+	/* vcorefs opp information */
+	if (polling_mode)
+		vcoredvfs_irq(-1);
+
+	/* vcorefs source request */
+	num = met_vcorefs_get_src_req_num();
+	out = met_vcorefs_get_src_req();
+
+	ms_vcorefs(num, out);
+}
+
+static const char help[] =
+	"  --vcoredvfs				monitor VCORE DVFS\n";
+static int vcoredvfs_print_help(char *buf, int len)
+{
+	return snprintf(buf, PAGE_SIZE, help);
+}
+
+static const char header_dvfs[] = "met-info [000] 0.0: met_vcorefs_header:";
+
+static const char header_kicker[] = "met-info [000] 0.0: met_vcorefs_kicker_header:";
+
+static const char header_ms_dvfs[] = "met-info [000] 0.0: ms_vcorefs_header:";
+
+static int vcoredvfs_print_header(char *buf, int len)
+{
+	int ret;
+	int idx, num;
+	char **header;
+
+	ret = snprintf(buf, PAGE_SIZE, "met-info [000] 0.0: met_vcorefs_cfg: NUM_OPP:%d\n", met_vcorefs_get_num_opp());
+
+	/* opp information */
+	num = met_vcorefs_get_opp_info_num();
+	header = met_vcorefs_get_opp_info_name();
+
+	ret += snprintf(buf + ret, PAGE_SIZE, header_dvfs);
+	for (idx = 0; idx < num; idx++)
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "%s,", header[idx]);
+	buf[strlen(buf)-1] = '\n';
+
+	/* sw kickers */
+	ret += snprintf(buf + ret, PAGE_SIZE - ret, "%s", header_kicker);
+	for (idx = 0; idx < NUM_KICKER; idx++)
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "%s,", met_governor_get_kicker_name(idx));
+	buf[strlen(buf)-1] = '\n';
+
+	/* source requests */
+	num = met_vcorefs_get_src_req_num();
+	header = met_vcorefs_get_src_req_name();
+
+	ret += snprintf(buf + ret, PAGE_SIZE - ret, header_ms_dvfs);
+	for (idx = 0; idx < num; idx++)
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "%s,", header[idx]);
+	buf[strlen(buf)-1] = '\n';
+
+	met_vcoredvfs.mode = 0;
+
+	return ret;
+}
+
+struct metdevice met_vcoredvfs = {
+	.name = "vcoredvfs",
+	.owner = THIS_MODULE,
+	.type = MET_TYPE_BUS,
+	.create_subfs = met_vcoredvfs_create,
+	.delete_subfs = met_vcoredvfs_delete,
+	.cpu_related = 0,
+	.start = met_vcoredvfs_start,
+	.stop = met_vcoredvfs_stop,
+	.polling_interval = 1,	/* ms */
+	.timed_polling = met_vcoredvfs_polling,
+	.print_help = vcoredvfs_print_help,
+	.print_header = vcoredvfs_print_header,
+};
diff --git a/src/devtools/met-driver/4.4/common/mips_pmu_hw.c b/src/devtools/met-driver/4.4/common/mips_pmu_hw.c
new file mode 100644
index 0000000..19e836b
--- /dev/null
+++ b/src/devtools/met-driver/4.4/common/mips_pmu_hw.c
@@ -0,0 +1,438 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <asm/system.h>
+#include <linux/smp.h>
+
+#include "cpu_pmu.h"
+#include "mips_pmu_name.h"
+
+struct chip_pmu {
+	enum cpu_type_enum type;
+	struct pmu_desc **desc;
+	void *refptr;
+	const char *cpu_name;
+	unsigned int pmu_desc_size;
+	unsigned int max_hw_events;
+	unsigned int max_reg_count;
+};
+
+struct pmu_desc *mips_pmu_desc[MIPS_MAX_HWEVENTS];
+
+static struct chip_pmu chips[] = {
+	{CPU_1004K, mips_pmu_desc, (void *)mips_1004k_pmu_desc, "MIPS_1004K",
+	 MIPS_1004K_PMU_DESC_SIZE, MIPS_1004K_PMU_DESC_COUNT, PMU_1004K_MAX_HW_REGS},
+};
+
+static struct chip_pmu chip_unknown = { CPU_UNKNOWN, NULL, NULL, "Unknown CPU", 0, 0, 0 };
+
+#define CHIP_PMU_COUNT (sizeof(chips) / sizeof(struct chip_pmu))
+static struct chip_pmu *chip;
+#define M_CONFIG1_PC    (1 << 4)
+
+#define M_PERFCTL_EXL           (1  <<  0)
+#define M_PERFCTL_KERNEL        (1  <<  1)
+#define M_PERFCTL_SUPERVISOR        (1  <<  2)
+#define M_PERFCTL_USER          (1  <<  3)
+#define M_PERFCTL_INTERRUPT_ENABLE  (1  <<  4)
+#define M_PERFCTL_EVENT(event)      (((event) & 0x3ff)  << 5)
+#define M_PERFCTL_VPEID(vpe)        ((vpe)    << 16)
+
+#ifdef CONFIG_CPU_BMIPS5000
+#define M_PERFCTL_MT_EN(filter)     0
+#else				/* !CONFIG_CPU_BMIPS5000 */
+#define M_PERFCTL_MT_EN(filter)     ((filter) << 20)
+#endif				/* CONFIG_CPU_BMIPS5000 */
+
+#define    M_TC_EN_ALL          M_PERFCTL_MT_EN(0)
+#define    M_TC_EN_VPE          M_PERFCTL_MT_EN(1)
+#define    M_TC_EN_TC           M_PERFCTL_MT_EN(2)
+#define M_PERFCTL_TCID(tcid)        ((tcid)   << 22)
+#define M_PERFCTL_WIDE          (1  << 30)
+#define M_PERFCTL_MORE          (1  << 31)
+#define M_PERFCTL_TC            (1  << 30)
+
+#define M_PERFCTL_COUNT_EVENT_WHENEVER  (M_PERFCTL_EXL |        \
+		M_PERFCTL_KERNEL |      \
+		M_PERFCTL_USER |        \
+		M_PERFCTL_SUPERVISOR |      \
+		M_PERFCTL_INTERRUPT_ENABLE)
+
+#ifdef CONFIG_MIPS_MT_SMP
+#define M_PERFCTL_CONFIG_MASK       0x3fff801f
+#else
+#define M_PERFCTL_CONFIG_MASK       0x1f
+#endif
+#define M_PERFCTL_EVENT_MASK        0xfe0
+
+#define vpe_id()    0
+
+/* To get current TCID*/
+#define read_c0_tcbind() __read_32bit_c0_register($2, 2)
+
+struct cpu_hw_events {
+	unsigned int config_base[MIPS_MAX_HWEVENTS];
+	unsigned int saved_ctrl[MIPS_MAX_HWEVENTS];
+};
+
+DEFINE_PER_CPU(struct cpu_hw_events, cpu_hw_events) = {
+	.config_base = {
+	0, 0, 0, 0}, .saved_ctrl = {
+0, 0, 0, 0},};
+
+static enum cpu_type_enum mips_get_ic(void)
+{
+	unsigned int value = current_cpu_type();
+
+	/* pr_debug("ic value: %X\n", value); */
+	return value;
+}
+
+static int __n_counters(void)
+{
+	if (!(read_c0_config1() & M_CONFIG1_PC))
+		return 0;
+	if (!(read_c0_perfctrl0() & M_PERFCTL_MORE))
+		return 1;
+	if (!(read_c0_perfctrl1() & M_PERFCTL_MORE))
+		return 2;
+	if (!(read_c0_perfctrl2() & M_PERFCTL_MORE))
+		return 3;
+
+	return 4;
+}
+
+static int n_counters(void)
+{
+	int counters;
+
+	switch (current_cpu_type()) {
+	case CPU_R10000:
+		counters = 2;
+		break;
+	case CPU_R12000:
+	case CPU_R14000:
+		counters = 4;
+		break;
+	default:
+		counters = __n_counters();
+		break;
+	}
+
+	return counters;
+}
+
+static int mips_pmu_hw_get_counters(void)
+{
+	int count = n_counters();
+
+	/* pr_debug("pmu hw event nr: %d\n", count); */
+	return count;
+}
+
+static unsigned int mipsxx_pmu_swizzle_perf_idx(unsigned int idx)
+{
+	if (vpe_id() == 1)
+		idx = (idx + 2) & 3;
+	return idx;
+}
+
+static void mipsxx_pmu_write_counter(unsigned int idx, u64 val)
+{
+	idx = mipsxx_pmu_swizzle_perf_idx(idx);
+
+	switch (idx) {
+	case 0:
+		write_c0_perfcntr0(val);
+		return;
+	case 1:
+		write_c0_perfcntr1(val);
+		return;
+	case 2:
+		write_c0_perfcntr2(val);
+		return;
+	case 3:
+		write_c0_perfcntr3(val);
+		return;
+	}
+}
+
+static u64 mipsxx_pmu_read_counter(unsigned int idx)
+{
+	idx = mipsxx_pmu_swizzle_perf_idx(idx);
+
+	switch (idx) {
+	case 0:
+		/*
+		 * The counters are unsigned, we must cast to truncate
+		 * off the high bits.
+		 */
+		return (u32) read_c0_perfcntr0();
+	case 1:
+		return (u32) read_c0_perfcntr1();
+	case 2:
+		return (u32) read_c0_perfcntr2();
+	case 3:
+		return (u32) read_c0_perfcntr3();
+	default:
+		WARN_ONCE(1, "Invalid performance counter number (%d)\n", idx);
+		return 0;
+	}
+}
+
+
+static unsigned int mipsxx_pmu_read_control(unsigned int idx)
+{
+	idx = mipsxx_pmu_swizzle_perf_idx(idx);
+
+	switch (idx) {
+	case 0:
+		return read_c0_perfctrl0();
+	case 1:
+		return read_c0_perfctrl1();
+	case 2:
+		return read_c0_perfctrl2();
+	case 3:
+		return read_c0_perfctrl3();
+	default:
+		WARN_ONCE(1, "Invalid performance counter number (%d)\n", idx);
+		return 0;
+	}
+}
+
+static void mipsxx_pmu_write_control(unsigned int idx, unsigned int val)
+{
+	idx = mipsxx_pmu_swizzle_perf_idx(idx);
+
+	switch (idx) {
+	case 0:
+		write_c0_perfctrl0(val);
+		return;
+	case 1:
+		write_c0_perfctrl1(val);
+		return;
+	case 2:
+		write_c0_perfctrl2(val);
+		return;
+	case 3:
+		write_c0_perfctrl3(val);
+		return;
+	}
+}
+
+static int mipsxx_pmu_get_vpeid(void)
+{
+	return read_c0_tcbind() & 0xF;
+}
+
+static void mipsxx_pmu_reset_counters(int idx)
+{
+	switch (idx) {
+	case 3:
+		mipsxx_pmu_write_control(3, 0);
+		mipsxx_pmu_write_counter(3, 0);
+		break;
+	case 2:
+		mipsxx_pmu_write_control(2, 0);
+		mipsxx_pmu_write_counter(2, 0);
+		break;
+	case 1:
+		mipsxx_pmu_write_control(1, 0);
+		mipsxx_pmu_write_counter(1, 0);
+		break;
+	case 0:
+		mipsxx_pmu_write_control(0, 0);
+		mipsxx_pmu_write_counter(0, 0);
+		break;
+	}
+}
+
+static void mipsxx_pmu_enable_event(int idx, int event)
+{
+	struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
+	unsigned long flags;
+
+	WARN_ON(idx < 0 || idx >= chip->max_hw_events);
+	cpuc->saved_ctrl[idx] = M_PERFCTL_EVENT(event & 0xff) |
+	    M_PERFCTL_VPEID(mipsxx_pmu_get_vpeid()) |
+	    (cpuc->config_base[idx] & M_PERFCTL_CONFIG_MASK);
+#ifdef CONFIG_CPU_BMIPS5000
+	/* if (IS_ENABLED(CONFIG_CPU_BMIPS5000)) */
+	/* enable the counter for the calling thread */
+	cpuc->saved_ctrl[idx] |= (1 << (12 + vpe_id())) | M_PERFCTL_TC;
+#endif
+	/*
+	 * To enable pmu count
+	 */
+	local_irq_save(flags);
+	mipsxx_pmu_write_control(idx, cpuc->saved_ctrl[idx]);
+	local_irq_restore(flags);
+}
+
+static void mipsxx_pmu_disable_event(int idx)
+{
+	struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
+	unsigned long flags;
+
+	/* WARN_ON(idx < 0 || idx >= mipspmu.num_counters); */
+	WARN_ON(idx < 0 || idx >= chip->max_hw_events);
+
+	local_irq_save(flags);
+	cpuc->saved_ctrl[idx] = mipsxx_pmu_read_control(idx) & ~M_PERFCTL_COUNT_EVENT_WHENEVER;
+	mipsxx_pmu_write_control(idx, cpuc->saved_ctrl[idx]);
+	local_irq_restore(flags);
+}
+
+static int mips_pmu_hw_get_event_desc(int idx, int event, char *event_desc)
+{
+	int i;
+
+	if (event_desc == NULL) {
+		pr_debug("event_desc is NULL\n");
+		return -1;
+	}
+
+	for (i = 0; i < chip->max_reg_count; i++) {
+		if (chip->desc[idx][i].event == event) {
+			strncpy(event_desc, chip->desc[idx][i].name, MXSIZE_PMU_DESC - 1);
+			break;
+		}
+	}
+	if (i == chip->max_reg_count)
+		return -1;
+
+	return 0;
+}
+
+
+static int mips_pmu_hw_check_event(struct met_pmu *pmu, int idx, int event)
+{
+	int i;
+
+	/* to check index over run */
+	if (!chip)
+		return -1;
+
+	if (idx >= chip->max_hw_events)
+		return -1;
+
+	for (i = 0; i < chip->max_reg_count; i++) {
+		if (chip->desc[idx][i].event == event)
+			break;
+	}
+	if (i == chip->max_reg_count)
+		return -1;
+
+	return 0;
+}
+
+static void mips_pmu_hw_start(struct met_pmu *pmu, int count)
+{
+	int i;
+	int generic = count - 1;
+	struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
+
+	/* pr_debug("hw_start generic: %d\n", generic); */
+	for (i = 0; i < generic; i++) {
+		/* init config */
+		cpuc->config_base[i] = 0;
+		cpuc->config_base[i] |= M_TC_EN_VPE;
+		cpuc->config_base[i] |= M_PERFCTL_USER;
+		cpuc->config_base[i] |= M_PERFCTL_KERNEL;
+		cpuc->config_base[i] |= M_PERFCTL_EXL;
+		cpuc->config_base[i] |= M_PERFCTL_SUPERVISOR;
+		cpuc->config_base[i] &= M_PERFCTL_CONFIG_MASK;
+		 /**/ mipsxx_pmu_reset_counters(i);
+		if (pmu[i].mode == MODE_POLLING)
+			mipsxx_pmu_enable_event(i, pmu[i].event);
+	}
+	if (pmu[count - 1].mode == MODE_POLLING)
+		pr_debug("%s %d BUG!!! index over run!!\n", __func__, __LINE__);
+}
+
+static void mips_pmu_hw_stop(int count)
+{
+	int idx = 0;
+	int generic = count - 1;
+	/* pr_debug("reset %d\n", generic); */
+	for (idx = 0; idx < generic; idx++) {
+		mipsxx_pmu_reset_counters(idx);
+		mipsxx_pmu_disable_event(idx);
+	}
+}
+
+
+static unsigned int mips_pmu_hw_polling(struct met_pmu *pmu, int count, unsigned int *pmu_value)
+{
+	int i, cnt = 0;
+	int generic = count - 1;
+
+	for (i = 0; i < generic; i++) {
+		if (pmu[i].mode == MODE_POLLING) {
+			pmu_value[cnt] = mipsxx_pmu_read_counter(i);
+			cnt++;
+			mipsxx_pmu_reset_counters(i);
+			mipsxx_pmu_enable_event(i, pmu[i].event);
+		}
+	}
+	if (pmu[count - 1].mode == MODE_POLLING) {
+		pr_debug("%s %d BUG!!! index over run!!\n", __func__, __LINE__);
+		pmu_value[cnt] = 0xFFFF;
+		cnt++;
+	}
+
+	return cnt;
+}
+
+
+
+struct cpu_pmu_hw mips_pmu = {
+	.name = "mips_pmu",
+	.get_event_desc = mips_pmu_hw_get_event_desc,
+	.check_event = mips_pmu_hw_check_event,
+	.start = mips_pmu_hw_start,
+	.stop = mips_pmu_hw_stop,
+	.polling = mips_pmu_hw_polling,
+};
+
+struct cpu_pmu_hw *cpu_pmu_hw_init(void)
+{
+	int i = 0;
+	enum cpu_type_enum type;
+	int pmu_hw_count = 0;
+
+	type = mips_get_ic();
+
+	if (CPU_UNKNOWN == type || CPU_LAST == type) {
+		chip = &chip_unknown;
+		return NULL;
+	}
+	for (i = 0; i < CHIP_PMU_COUNT; i++) {
+		if (chips[i].type == type) {
+			chip = &(chips[i]);
+			break;
+		}
+	}
+	if (i == CHIP_PMU_COUNT) {
+		chip = &chip_unknown;
+		return NULL;
+	}
+
+	pmu_hw_count = mips_pmu_hw_get_counters();
+	for (i = 0; i < pmu_hw_count; i++)
+		chip->desc[i] = chip->refptr + (chip->pmu_desc_size * i);
+
+	mips_pmu.nr_cnt = pmu_hw_count + 1;
+	mips_pmu.cpu_name = chip->cpu_name;
+	return &mips_pmu;
+}
diff --git a/src/devtools/met-driver/4.4/common/mips_pmu_name.h b/src/devtools/met-driver/4.4/common/mips_pmu_name.h
new file mode 100644
index 0000000..d7ea26a
--- /dev/null
+++ b/src/devtools/met-driver/4.4/common/mips_pmu_name.h
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+
+#ifndef _MIPS_PMU_NAME_H_
+#define _MIPS_PMU_NAME_H_
+
+/* MIPS 1004K */
+#define MIPS_MAX_HWEVENTS		(4)
+
+#define PMU_1004K_MAX_HW_REGS	(128)
+struct pmu_desc mips_1004k_pmu_desc[][PMU_1004K_MAX_HW_REGS] = {
+	/* COUNT 0 */
+	{
+	 {0, "CPU_CYCLES"},
+	 {1, "CPU_INST"},
+	 {2, "BRANCH_INSNS"},
+	 {3, "JR_31_INSNS"},
+	 {4, "JR_NON_31_INSNS"},
+	 {5, "ITLB_ACCESSES"},
+	 {6, "DTLB_ACCESSES"},
+	 {7, "JTLB_INSN_ACCESSES"},
+	 {8, "JTLB_DATA_ACCESSES"},
+	 {9, "ICACHE_ACCESSES"},
+	 {10, "DCACHE_ACCESSES"},
+	 {11, "DCACHE_MISSES"},
+	 {12, "RESERVED"},
+	 {13, "STORE_MISS_INSNS"},
+	 {14, "INTEGER_INSNS"},
+	 {15, "LOAD_INSNS"},
+	 {16, "J_JAL_INSNS"},
+	 {17, "NO_OPS_INSNS"},
+	 {18, "ALL_STALLS"},
+	 {19, "SC_INSNS"},
+	 {20, "PREFETCH_INSNS"},
+	 {21, "L2_CACHE_WRITEBACKS"},
+	 {22, "L2_CACHE_MISSES"},
+	 {23, "EXCEPTIONS_TAKEN"},
+	 {24, "CACHE_FIXUP_CYCLES"},
+	 {25, "IFU_STALLS"},
+	 {26, "DSP_INSNS"},
+	 {27, "RESERVED"},
+	 {28, "POLICY_EVENTS"},
+	 {29, "ISPRAM_EVENTS"},
+	 {30, "COREEXTEND_EVENTS"},
+	 {31, "YIELD_EVENTS"},
+	 {32, "ITC_LOADS"},
+	 {33, "UNCACHED_LOAD_INSNS"},
+	 {34, "FORK_INSNS"},
+	 {35, "CP2_ARITH_INSNS"},
+	 {36, "INTERVENTION_STALLS"},
+	 {37, "ICACHE_MISS_STALLS"},
+	 {38, "RESERVED"},
+	 {39, "DCACHE_MISS_CYCLES"},
+	 {40, "UNCACHED_STALLS"},
+	 {41, "MDU_STALLS"},
+	 {42, "CP2_STALLS"},
+	 {43, "ISPRAM_STALLS"},
+	 {44, "CACHE_INSN_STALLS"},
+	 {45, "LOAD_USE_STALLS"},
+	 {46, "INTERLOCK_STALLS"},
+	 {47, "RELAX_STALLS"},
+	 {48, "IFU_FB_FULL_REFETCHES"},
+	 {49, "EJTAG_INSN_TRIGGERS"},
+	 {50, "FSB_LESS_25_FULL"},
+	 {51, "FSB_OVER_50_FULL"},
+	 {52, "LDQ_LESS_25_FULL"},
+	 {53, "LDQ_OVER_50_FULL"},
+	 {54, "WBB_LESS_25_FULL"},
+	 {55, "WBB_OVER_50_FULL"},
+	 {56, "INTERVENTION_HIT_COUNT"},
+	 {57, "INVALIDATE_INTERVENTION_COUNT"},
+	 {58, "EVICTION_COUNT"},
+	 {59, "MESI_INVAL_COUNT"},
+	 {60, "MESI_MODIFIED_COUNT"},
+	 {61, "SELF_INTERVENTION_LATENCY"},
+	 {62, "READ_RESPONSE_LATENCY"},
+	 {63, "RESERVED"},
+	 {64, "SI_PCEVENT1"},
+	 {65, "SI_PCEVENT3"},
+	 {66, "SI_PCEVENT5"},
+	 {67, "SI_PCEVENT7"},
+	 {-1, "RESERVED"},
+	 /* 68 - 127 for Reserved */
+	 },
+	/* COUNT 1 */
+	{
+	 {0, "CPU_CYCLES"},
+	 {1, "CPU_INST"},
+	 {2, "MISPREDICTED_BRANCH_INSNS"},
+	 {3, "JR_31_MISPREDICTIONS"},
+	 {4, "JR_31_NO_PREDICTIONS"},
+	 {5, "ITLB_MISSES"},
+	 {6, "DTLB_MISSES"},
+	 {7, "JTLB_INSN_MISSES"},
+	 {8, "JTLB_DATA_MISSES"},
+	 {9, "ICACHE_MISSES"},
+	 {10, "DCACHE_WRITEBACKS"},
+	 {11, "DCACHE_MISSES"},
+	 {12, "RESERVED"},
+	 {13, "LOAD_MISS_INSNS"},
+	 {14, "FPU_INSNS"},
+	 {15, "STORE_INSNS"},
+	 {16, "MIPS16_INSNS"},
+	 {17, "INT_MUL_DIV_INSNS"},
+	 {18, "REPLAYED_INSNS"},
+	 {19, "SC_INSNS_FAILED"},
+	 {20, "CACHE_HIT_PREFETCH_INSNS"},
+	 {21, "L2_CACHE_ACCESSES"},
+	 {22, "L2_CACHE_SINGLE_BIT_ERRORS"},
+	 {23, "SINGLE_THREADED_CYCLES"},
+	 {24, "REFETCHED_INSNS"},
+	 {25, "ALU_STALLS"},
+	 {26, "ALU_DSP_SATURATION_INSNS"},
+	 {27, "MDU_DSP_SATURATION_INSNS"},
+	 {28, "CP2_EVENTS"},
+	 {29, "DSPRAM_EVENTS"},
+	 {30, "RESERVED"},
+	 {31, "ITC_EVENT"},
+	 {33, "UNCACHED_STORE_INSNS"},
+	 {34, "YIELD_IN_COMP"},
+	 {35, "CP2_TO_FROM_INSNS"},
+	 {36, "INTERVENTION_MISS_STALLS"},
+	 {37, "DCACHE_MISS_STALLS"},
+	 {38, "RESERVED"},
+	 /* 38 was listed in OPROFILE web page, but not listed 1004k mips spec */
+	 /* {38, "FSB_INDEX_CONFLICT_STALLS"}, */
+	 {39, "L2_CACHE_MISS_CYCLES"},
+	 {40, "ITC_STALLS"},
+	 {41, "FPU_STALLS"},
+	 {42, "COREEXTEND_STALLS"},
+	 {43, "DSPRAM_STALLS"},
+	 {45, "ALU_TO_AGEN_STALLS"},
+	 {46, "MISPREDICTION_STALLS"},
+	 {47, "RESERVED"},
+	 {48, "FB_ENTRY_ALLOCATED_CYCLES"},
+	 {49, "EJTAG_DATA_TRIGGERS"},
+	 {50, "FSB_25_50_FULL"},
+	 {51, "FSB_FULL_STALLS"},
+	 {52, "LDQ_25_50_FULL"},
+	 {53, "LDQ_FULL_STALLS"},
+	 {54, "WBB_25_50_FULL"},
+	 {55, "WBB_FULL_STALLS"},
+	 {56, "INTERVENTION_COUNT"},
+	 {57, "INVALID_INTERVENT_HIT_CNT"},
+	 {58, "WRITEBACK_COUNT"},
+	 {59, "MESI_EXCLUSIVE_COUNT"},
+	 {60, "MESI_SHARED_COUNT"},
+	 {61, "SELF_INTERVENTION_COUNT"},
+	 {62, "READ_RESPONSE_COUNT"},
+	 {63, "RESERVED"},
+	 {64, "SI_PCEVENT0"},
+	 {65, "SI_PCEVENT2"},
+	 {66, "SI_PCEVENT4"},
+	 {67, "SI_PCEVENT6"},
+	 {-1, "RESERVED"},
+	 },
+};
+
+#define MIPS_1004K_PMU_DESC_SIZE (sizeof(mips_1004k_pmu_desc[0]))
+#define MIPS_1004K_PMU_DESC_COUNT (sizeof(mips_1004k_pmu_desc) / MIPS_1004K_PMU_DESC_SIZE)
+
+#endif				/* _V8_PMU_NAME_H_ */
diff --git a/src/devtools/met-driver/4.4/common/mtk_emi_bm.c b/src/devtools/met-driver/4.4/common/mtk_emi_bm.c
new file mode 100644
index 0000000..bbb0ce3
--- /dev/null
+++ b/src/devtools/met-driver/4.4/common/mtk_emi_bm.c
@@ -0,0 +1,253 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <asm/io.h>
+#include <mt-plat/sync_write.h>
+#include <mt-plat/mtk_io.h>
+/* #include "mtk_typedefs.h" */
+#include "core_plf_init.h"
+#include "mtk_emi_bm.h"
+#include "met_drv.h"
+#include "interface.h"
+#ifdef CONFIG_MTK_TINYSYS_SSPM_SUPPORT
+#include "sspm/ondiemet_sspm.h"
+#endif
+
+#undef	DEBUG
+
+#define	emi_readl		readl
+#define	emi_reg_sync_writel	mt_reg_sync_writel
+
+#define MASK_MASTER	0xFF
+#define MASK_TRANS_TYPE	0xFF
+
+static void __iomem *BaseAddrEMI;
+const unsigned int emi_config[] = {
+	EMI_BMEN,
+	EMI_MSEL,
+	EMI_MSEL2,
+	EMI_MSEL3,
+	EMI_MSEL4,
+	EMI_MSEL5,
+	EMI_MSEL6,
+	EMI_MSEL7,
+	EMI_MSEL8,
+	EMI_MSEL9,
+	EMI_MSEL10,
+	EMI_BMID0,
+	EMI_BMID1,
+	EMI_BMID2,
+	EMI_BMID3,
+	EMI_BMID4,
+	EMI_BMID5,
+	EMI_BMID6,
+	EMI_BMID7,
+	EMI_BMID8,
+	EMI_BMID9,
+	EMI_BMID10,
+	EMI_BMEN1,
+	EMI_BMEN2,
+	EMI_BMRW0,
+	EMI_BMRW1
+};
+#define EMI_CONFIG_MX_NR (sizeof(emi_config)/sizeof(unsigned int))
+static unsigned int emi_config_val[EMI_CONFIG_MX_NR];
+
+int MET_BM_Init(void)
+{
+	/*emi*/
+	if (mt_cen_emi_base_get_symbol) {
+		BaseAddrEMI = mt_cen_emi_base_get_symbol();
+	} else {
+		pr_debug("mt_cen_emi_base_get_symbol = NULL\n");
+		PR_BOOTMSG_ONCE("mt_cen_emi_base_get_symbol = NULL\n");
+		BaseAddrEMI = 0;
+	}
+
+	if (BaseAddrEMI == 0) {
+		pr_debug("BaseAddrEMI = 0\n");
+		PR_BOOTMSG_ONCE("BaseAddrEMI = 0\n");
+		return -1;
+	}
+	pr_debug("MET EMI: map emi to %p\n", BaseAddrEMI);
+	PR_BOOTMSG("MET EMI: map emi to %p\n", BaseAddrEMI);
+
+	return 0;
+}
+
+void MET_BM_DeInit(void)
+{
+}
+
+void MET_BM_SaveCfg(void)
+{
+	int i;
+
+	for (i = 0; i < EMI_CONFIG_MX_NR; i++)
+		emi_config_val[i] = emi_readl(IOMEM(ADDR_EMI + emi_config[i]));
+}
+
+void MET_BM_RestoreCfg(void)
+{
+	int i;
+
+	for (i = 0; i < EMI_CONFIG_MX_NR; i++)
+		emi_reg_sync_writel(emi_config_val[i], ADDR_EMI + emi_config[i]);
+}
+
+int MET_BM_SetMonitorCounter(const unsigned int counter_num,
+			     const unsigned int master, const unsigned int trans_type)
+{
+	unsigned int value, addr;
+	const unsigned int iMask = (MASK_TRANS_TYPE << 8) | MASK_MASTER;
+
+	if (counter_num < 1 || counter_num > BM_COUNTER_MAX)
+		return BM_ERR_WRONG_REQ;
+
+
+	if (counter_num == 1) {
+		addr = EMI_BMEN;
+		value = (emi_readl(IOMEM(ADDR_EMI + addr)) & ~(iMask << 16)) |
+			((trans_type & MASK_TRANS_TYPE) << 24) | ((master & MASK_MASTER) << 16);
+	} else {
+		addr = (counter_num <= 3) ? EMI_MSEL : (EMI_MSEL2 + (counter_num / 2 - 2) * 8);
+
+
+		value = emi_readl(IOMEM(ADDR_EMI + addr)) & ~(iMask << ((counter_num % 2) * 16));
+
+
+		value |= (((trans_type & MASK_TRANS_TYPE) << 8) |
+			  (master & MASK_MASTER)) << ((counter_num % 2) * 16);
+	}
+
+	emi_reg_sync_writel(value, ADDR_EMI + addr);
+
+	return BM_REQ_OK;
+}
+
+int MET_BM_SetTtypeCounterRW(unsigned int bmrw0_val, unsigned int bmrw1_val)
+{
+
+	unsigned int value_origin;
+
+	value_origin = emi_readl(IOMEM(ADDR_EMI + EMI_BMRW0));
+	MET_TRACE("[MET_EMI_settype1] value_origin: %x\n", value_origin);
+	if (value_origin != bmrw0_val) {
+		emi_reg_sync_writel(bmrw0_val, ADDR_EMI + EMI_BMRW0);
+		MET_TRACE("[MET_EMI_settype1] bmrw0_val: %x, value_origin: %x\n", bmrw0_val,
+			   value_origin);
+	}
+
+	value_origin = emi_readl(IOMEM(ADDR_EMI + EMI_BMRW1));
+	MET_TRACE("[MET_EMI_settype2] value_origin: %x\n", value_origin);
+	if (value_origin != bmrw1_val) {
+		emi_reg_sync_writel(bmrw1_val, ADDR_EMI + EMI_BMRW1);
+		MET_TRACE("[MET_EMI_settype2] bmrw0_val: %x, value_origin: %x\n", bmrw1_val,
+			   value_origin);
+
+	}
+	return BM_REQ_OK;
+}
+
+int MET_BM_Set_WsctTsct_id_sel(unsigned int counter_num, unsigned int enable)
+{
+	unsigned int value;
+
+	if (counter_num > 3)
+		return BM_ERR_WRONG_REQ;
+
+	value =
+	    ((emi_readl(IOMEM(ADDR_EMI + EMI_BMEN2)) & (~(1 << (28 + counter_num)))) |
+	     (enable << (28 + counter_num)));
+	emi_reg_sync_writel(value, ADDR_EMI + EMI_BMEN2);
+
+	return BM_REQ_OK;
+}
+
+int MET_BM_SetbusID_En(const unsigned int counter_num,
+		       const unsigned int enable)
+{
+	unsigned int value;
+
+	if ((counter_num < 1 || counter_num > BM_COUNTER_MAX) || (enable > 1))
+		return BM_ERR_WRONG_REQ;
+
+	if (enable == 0) {
+
+		value = (emi_readl(IOMEM(ADDR_EMI + EMI_BMEN2))
+			 & ~(1 << (counter_num - 1)));
+	} else {
+
+		value = (emi_readl(IOMEM(ADDR_EMI + EMI_BMEN2))
+			 | (1 << (counter_num - 1)));
+	}
+	emi_reg_sync_writel(value, ADDR_EMI + EMI_BMEN2);
+
+	return BM_REQ_OK;
+}
+
+int MET_BM_SetbusID(const unsigned int counter_num,
+		    const unsigned int id)
+{
+	unsigned int value, addr, shift_num;
+
+	if ((counter_num < 1 || counter_num > BM_COUNTER_MAX))
+		return BM_ERR_WRONG_REQ;
+
+
+	addr = EMI_BMID0 + (counter_num - 1) / 2 * 4;
+	shift_num = ((counter_num - 1) % 2) * 16;
+
+	value = emi_readl(IOMEM(ADDR_EMI + addr)) & ~(EMI_BMID_MASK << shift_num);
+
+
+	if (id <= 0xffff)
+		value |= id << shift_num;
+
+	emi_reg_sync_writel(value, ADDR_EMI + addr);
+
+	return BM_REQ_OK;
+}
+
+int MET_BM_SetUltraHighFilter(const unsigned int counter_num, const unsigned int enable)
+{
+	unsigned int value;
+
+	if ((counter_num < 1 || counter_num > BM_COUNTER_MAX) || (enable > 1))
+		return BM_ERR_WRONG_REQ;
+
+
+	value = (emi_readl(IOMEM(ADDR_EMI + EMI_BMEN1))
+		 & ~(1 << (counter_num - 1)))
+		| (enable << (counter_num - 1));
+
+	emi_reg_sync_writel(value, ADDR_EMI + EMI_BMEN1);
+
+	return BM_REQ_OK;
+}
+
+int MET_BM_SetLatencyCounter(unsigned int enable)
+{
+	unsigned int value;
+
+	value = emi_readl(IOMEM(ADDR_EMI + EMI_BMEN2)) & ~(0x3 << 24);
+	if (enable == 1)
+		value |= (0x2 << 24);
+
+	emi_reg_sync_writel(value, ADDR_EMI + EMI_BMEN2);
+
+	return BM_REQ_OK;
+}
diff --git a/src/devtools/met-driver/4.4/common/mtk_emi_bm.h b/src/devtools/met-driver/4.4/common/mtk_emi_bm.h
new file mode 100644
index 0000000..0d39760
--- /dev/null
+++ b/src/devtools/met-driver/4.4/common/mtk_emi_bm.h
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __MT_MET_EMI_BM_H__
+#define __MT_MET_EMI_BM_H__
+
+
+#define	ADDR_EMI		((unsigned long) BaseAddrEMI)
+
+/*========================================================*/
+/*EMI configuration by project*/
+/*Change config start*/
+/*========================================================*/
+#define _GP_1_Default	(_M0 | _M1)
+#define _GP_2_Default	(_M2 | _M5)
+#define _GP_3_Default	(_M6 | _M7)
+
+
+/*========================================================*/
+/*Change config end*/
+/*========================================================*/
+
+
+#define _M0		(0x01)
+#define _M1		(0x02)
+#define _M2		(0x04)
+#define _M3		(0x08)
+#define _M4		(0x10)
+#define _M5		(0x20)
+#define _M6		(0x40)
+#define _M7		(0x80)
+#define _ALL	(0xFF)
+
+enum BM_RW_Type {
+	BM_BOTH_READ_WRITE,
+	BM_READ_ONLY,
+	BM_WRITE_ONLY
+};
+
+enum {
+	BM_TRANS_TYPE_1BEAT = 0x0,
+	BM_TRANS_TYPE_2BEAT,
+	BM_TRANS_TYPE_3BEAT,
+	BM_TRANS_TYPE_4BEAT,
+	BM_TRANS_TYPE_5BEAT,
+	BM_TRANS_TYPE_6BEAT,
+	BM_TRANS_TYPE_7BEAT,
+	BM_TRANS_TYPE_8BEAT,
+	BM_TRANS_TYPE_9BEAT,
+	BM_TRANS_TYPE_10BEAT,
+	BM_TRANS_TYPE_11BEAT,
+	BM_TRANS_TYPE_12BEAT,
+	BM_TRANS_TYPE_13BEAT,
+	BM_TRANS_TYPE_14BEAT,
+	BM_TRANS_TYPE_15BEAT,
+	BM_TRANS_TYPE_16BEAT,
+	BM_TRANS_TYPE_1Byte = 0 << 4,
+	BM_TRANS_TYPE_2Byte = 1 << 4,
+	BM_TRANS_TYPE_4Byte = 2 << 4,
+	BM_TRANS_TYPE_8Byte = 3 << 4,
+	BM_TRANS_TYPE_16Byte = 4 << 4,
+	BM_TRANS_TYPE_32Byte = 5 << 4,
+	BM_TRANS_TYPE_BURST_WRAP = 0 << 7,
+	BM_TRANS_TYPE_BURST_INCR = 1 << 7
+};
+
+enum {
+	BM_TRANS_RW_DEFAULT = 0x0,
+	BM_TRANS_RW_READONLY,
+	BM_TRANS_RW_WRITEONLY,
+	BM_TRANS_RW_RWBOTH
+};
+
+
+#define EMI_BMID_MASK				(0xFFFF)
+#define BM_COUNTER_MAX				(21)
+
+#define BM_REQ_OK						(0)
+#define BM_ERR_WRONG_REQ				(-1)
+#define BM_ERR_OVERRUN					(-2)
+
+#define BM_TTYPE1_16_ENABLE			(0)
+#define BM_TTYPE1_16_DISABLE			(-1)
+#define BM_TTYPE17_21_ENABLE			(0)
+#define BM_TTYPE17_21_DISABLE			(-1)
+#ifdef CONFIG_MTK_TINYSYS_SSPM_SUPPORT
+
+enum BM_EMI_IPI_Type {
+	SET_BASE_EMI = 0x0,
+	SET_EBM_CONFIGS1 = 0x7,
+	SET_EBM_CONFIGS2 = 0x8,
+	SET_REGISTER_CB = 0x9,
+};
+#endif
+
+#define	EMI_OFF			0x0000
+#define EMI_BMEN		(0x400-EMI_OFF)
+#define EMI_MSEL		(0x440-EMI_OFF)
+#define EMI_MSEL2		(0x468-EMI_OFF)
+#define EMI_MSEL3		(0x470-EMI_OFF)
+#define EMI_MSEL4		(0x478-EMI_OFF)
+#define EMI_MSEL5		(0x480-EMI_OFF)
+#define EMI_MSEL6		(0x488-EMI_OFF)
+#define EMI_MSEL7		(0x490-EMI_OFF)
+#define EMI_MSEL8		(0x498-EMI_OFF)
+#define EMI_MSEL9		(0x4A0-EMI_OFF)
+#define EMI_MSEL10		(0x4A8-EMI_OFF)
+
+#define EMI_BMID0		(0x4B0-EMI_OFF)
+#define EMI_BMID1		(0x4B4-EMI_OFF)
+#define EMI_BMID2		(0x4B8-EMI_OFF)
+#define EMI_BMID3		(0x4BC-EMI_OFF)
+#define EMI_BMID4		(0x4C0-EMI_OFF)
+#define EMI_BMID5		(0x4C4-EMI_OFF)
+#define EMI_BMID6		(0x4C8-EMI_OFF)
+#define EMI_BMID7		(0x4CC-EMI_OFF)
+#define EMI_BMID8		(0x4D0-EMI_OFF)
+#define EMI_BMID9		(0x4D4-EMI_OFF)
+#define EMI_BMID10		(0x4D8-EMI_OFF)
+
+#define EMI_BMEN1		(0x4E0-EMI_OFF)
+#define EMI_BMEN2		(0x4E8-EMI_OFF)
+#define EMI_BMRW0		(0x4F8-EMI_OFF)
+#define EMI_BMRW1		(0x4FC-EMI_OFF)
+
+
+extern int MET_BM_Init(void);
+extern void MET_BM_DeInit(void);
+extern void MET_BM_SaveCfg(void);
+extern void MET_BM_RestoreCfg(void);
+extern int MET_BM_SetMonitorCounter(const unsigned int counter_num,
+				    const unsigned int master, const unsigned int trans_type);
+extern int MET_BM_SetTtypeCounterRW(unsigned int bmrw0_val, unsigned int bmrw1_val);
+extern int MET_BM_Set_WsctTsct_id_sel(unsigned int counter_num, unsigned int enable);
+extern int MET_BM_SetbusID_En(const unsigned int counter_num,
+			      const unsigned int enable);
+extern int MET_BM_SetbusID(const unsigned int counter_num,
+			   const unsigned int id);
+extern int MET_BM_SetUltraHighFilter(const unsigned int counter_num, const unsigned int enable);
+extern int MET_BM_SetLatencyCounter(unsigned int enable);
+#endif				/* !__MT_MET_EMI_BM_H__ */
diff --git a/src/devtools/met-driver/4.4/common/mtk_gpu_metmonitor.c b/src/devtools/met-driver/4.4/common/mtk_gpu_metmonitor.c
new file mode 100644
index 0000000..fadf640
--- /dev/null
+++ b/src/devtools/met-driver/4.4/common/mtk_gpu_metmonitor.c
@@ -0,0 +1,761 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <asm/page.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/syscalls.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <asm/uaccess.h>
+#include <linux/hrtimer.h>
+
+#include "met_drv.h"
+#include "trace.h"
+
+#include "mtk_gpu_metmonitor.h"
+#include "core_plf_init.h"
+#include "core_plf_trace.h"
+
+
+/*
+ * define if the hal implementation might re-schedule, cannot run inside softirq
+ * undefine this is better for sampling jitter if HAL support it
+ */
+#undef GPU_HAL_RUN_PREMPTIBLE
+
+#ifdef GPU_HAL_RUN_PREMPTIBLE
+static struct delayed_work gpu_dwork;
+static struct delayed_work gpu_pwr_dwork;
+#endif
+
+/* the mt_gpufreq_get_thermal_limit_freq use mutex_lock to do its job */
+/* so, change the gpu-dvfs implementation to dwork */
+static struct delayed_work gpu_dvfs_dwork;
+
+/*
+ * GPU monitor HAL comes from alps\mediatek\kernel\include\linux\mtk_gpu_utility.h
+ *
+ * mtk_get_gpu_memory_usage(unsigned int* pMemUsage) in unit of bytes
+ *
+ * mtk_get_gpu_xxx_loading are in unit of %
+*/
+
+enum MET_GPU_PROFILE_INDEX {
+	eMET_GPU_LOADING = 0,
+	eMET_GPU_BLOCK_LOADING,	/* 1 */
+	eMET_GPU_IDLE_LOADING,	/* 2 */
+	eMET_GPU_PROFILE_CNT
+};
+
+static unsigned long g_u4AvailableInfo;
+
+noinline void GPU_Loading(unsigned char cnt, unsigned int *value)
+{
+	switch (cnt) {
+	case 1:
+		MET_TRACE("%u\n", value[0]);
+		break;
+	case 2:
+		MET_TRACE("%u,%u\n", value[0], value[1]);
+		break;
+	case 3:
+		MET_TRACE("%u,%u,%u\n", value[0], value[1], value[2]);
+		break;
+	case 4:
+		MET_TRACE("%u,%u,%u,%u\n", value[0], value[1], value[2], value[3]);
+		break;
+	default:
+		break;
+	}
+
+}
+
+noinline void GPU_Sub_Loading(unsigned int loading)
+{
+	MET_TRACE("%u\n", loading);
+}
+
+noinline void GPU_3D_Fences_Count(int count)
+{
+	MET_TRACE("%d\n", count);
+}
+
+#ifdef GPU_HAL_RUN_PREMPTIBLE
+static void gpu_GPULoading(struct work_struct *work)
+{
+	unsigned int	pu4Value[eMET_GPU_PROFILE_CNT];
+	unsigned long	u4Index = 0;
+	unsigned int	loading = 0;
+	int		count = 0;
+
+	memset(pu4Value, 0x00, eMET_GPU_PROFILE_CNT);
+	if ((1 << eMET_GPU_LOADING) & g_u4AvailableInfo) {
+		if (mtk_get_gpu_loading_symbol && mtk_get_gpu_loading_symbol(&pu4Value[u4Index]))
+			u4Index += 1;
+	}
+
+	if ((1 << eMET_GPU_BLOCK_LOADING) & g_u4AvailableInfo) {
+		if (mtk_get_gpu_block_symbol && mtk_get_gpu_block_symbol(&pu4Value[u4Index]))
+			u4Index += 1;
+	}
+
+	if ((1 << eMET_GPU_IDLE_LOADING) & g_u4AvailableInfo) {
+		if (mtk_get_gpu_idle_symbol && mtk_get_gpu_idle_symbol(&pu4Value[u4Index]))
+			u4Index += 1;
+	}
+
+	if (g_u4AvailableInfo)
+		GPU_Loading(u4Index, pu4Value);
+
+	if (mtk_get_gpu_sub_loading_symbol && mtk_get_gpu_sub_loading_symbol(&loading))
+		GPU_Sub_Loading(loading);
+
+	if (mtk_get_3D_fences_count_symbol && mtk_get_3D_fences_count_symbol(&count))
+		GPU_3D_Fences_Count(count);
+}
+#else
+static void gpu_GPULoading(unsigned long long stamp, int cpu)
+{
+	unsigned int	pu4Value[eMET_GPU_PROFILE_CNT];
+	unsigned long	u4Index = 0;
+	unsigned int	loading = 0;
+	int		count = 0;
+
+	memset(pu4Value, 0x00, eMET_GPU_PROFILE_CNT);
+	if ((1 << eMET_GPU_LOADING) & g_u4AvailableInfo) {
+		if (mtk_get_gpu_loading_symbol) {
+			mtk_get_gpu_loading_symbol(&pu4Value[u4Index]);
+			u4Index += 1;
+		}
+	}
+
+	if ((1 << eMET_GPU_BLOCK_LOADING) & g_u4AvailableInfo) {
+		if (mtk_get_gpu_block_symbol) {
+			mtk_get_gpu_block_symbol(&pu4Value[u4Index]);
+			u4Index += 1;
+		}
+	}
+
+	if ((1 << eMET_GPU_IDLE_LOADING) & g_u4AvailableInfo) {
+		if (mtk_get_gpu_idle_symbol) {
+			mtk_get_gpu_idle_symbol(&pu4Value[u4Index]);
+			u4Index += 1;
+		}
+	}
+
+	if (g_u4AvailableInfo)
+		GPU_Loading(u4Index, pu4Value);
+
+	if (mtk_get_gpu_sub_loading_symbol) {
+		mtk_get_gpu_sub_loading_symbol(&loading);
+		GPU_Sub_Loading(loading);
+	}
+
+	if (mtk_get_3D_fences_count_symbol) {
+		mtk_get_3D_fences_count_symbol(&count);
+		GPU_3D_Fences_Count(count);
+	}
+}
+#endif
+
+static void gpu_monitor_start(void)
+{
+	if (mtk_get_gpu_loading_symbol)
+		g_u4AvailableInfo |= (1 << eMET_GPU_LOADING);
+	if (mtk_get_gpu_block_symbol)
+		g_u4AvailableInfo |= (1 << eMET_GPU_BLOCK_LOADING);
+	if (mtk_get_gpu_idle_symbol)
+		g_u4AvailableInfo |= (1 << eMET_GPU_IDLE_LOADING);
+
+#ifdef GPU_HAL_RUN_PREMPTIBLE
+	INIT_DELAYED_WORK(&gpu_dwork, gpu_GPULoading);
+#endif
+}
+
+#ifdef GPU_HAL_RUN_PREMPTIBLE
+static void gpu_monitor_stop(void)
+{
+	cancel_delayed_work_sync(&gpu_dwork);
+}
+
+static void GPULoadingNotify(unsigned long long stamp, int cpu)
+{
+	schedule_delayed_work(&gpu_dwork, 0);
+}
+#endif
+
+static char help[] =
+	"  --gpu				monitor gpu status\n";
+static int gpu_status_print_help(char *buf, int len)
+{
+	return snprintf(buf, PAGE_SIZE, help);
+}
+
+static char g_pComGPUStatusHeader[] =
+	"met-info [000] 0.0: met_gpu_loading_header: ";
+static int gpu_status_print_header(char *buf, int len)
+{
+	int ret = 0;
+
+	ret = snprintf(buf, PAGE_SIZE, "%s", g_pComGPUStatusHeader);
+
+	if ((1 << eMET_GPU_LOADING) & g_u4AvailableInfo)
+		ret += snprintf(buf+ret, PAGE_SIZE-ret, "%s", "Loading,");
+
+	if ((1 << eMET_GPU_BLOCK_LOADING) & g_u4AvailableInfo)
+		ret += snprintf(buf+ret, PAGE_SIZE-ret, "%s", "Blcok,");
+
+	if ((1 << eMET_GPU_IDLE_LOADING) & g_u4AvailableInfo)
+		ret += snprintf(buf+ret, PAGE_SIZE-ret, "%s", "Idle");
+
+	ret += snprintf(buf+ret, PAGE_SIZE-ret, "%s", "\n");
+
+	ret += snprintf(buf+ret, PAGE_SIZE-ret,
+			"met-info [000] 0.0: met_gpu_sub_loading_header: Loading\n");
+	ret += snprintf(buf+ret, PAGE_SIZE-ret,
+			"met-info [000] 0.0: met_gpu_3d_fences_count_header: Count\n");
+
+	return ret;
+}
+
+struct metdevice met_gpu = {
+	.name			= "gpu",
+	.owner			= THIS_MODULE,
+	.type			= MET_TYPE_BUS,
+	.cpu_related		= 0,
+	.start			= gpu_monitor_start,
+	.mode			= 0,
+	.polling_interval	= 1,	/* ms */
+#ifdef GPU_HAL_RUN_PREMPTIBLE
+	.timed_polling		= GPULoadingNotify,
+	.stop			= gpu_monitor_stop,
+#else
+	.timed_polling		= gpu_GPULoading,
+#endif
+	.print_help		= gpu_status_print_help,
+	.print_header		= gpu_status_print_header,
+};
+
+/*
+ * GPU DVFS Monitor
+ */
+#define	MTK_GPU_DVFS_TYPE_ITEM(type)	#type,
+static char *gpu_dvfs_type_name[] = MTK_GPU_DVFS_TYPE_LIST;
+#undef	MTK_GPU_DVFS_TYPE_ITEM
+
+static MTK_GPU_DVFS_TYPE gpu_dvfs_type_prev;
+static unsigned long gpu_dvfs_type_freq_prev;
+static unsigned int gpu_dvfs_type_freq[ARRAY_SIZE(gpu_dvfs_type_name)];
+
+noinline void GPU_DVFS(unsigned int Freq, unsigned int ThermalLimit,
+			unsigned long CustomBoost, unsigned long CustomUpbound)
+{
+	MET_TRACE("%u,%u,%lu,%lu\n", Freq, ThermalLimit, CustomBoost, CustomUpbound);
+}
+
+noinline void GPU_DVFS_TYPE(void)
+{
+	char	*SOB, *EOB;
+
+	MET_TRACE_GETBUF(&SOB, &EOB);
+	EOB = ms_formatD_EOL(EOB, ARRAY_SIZE(gpu_dvfs_type_freq), gpu_dvfs_type_freq);
+	MET_TRACE_PUTBUF(SOB, EOB);
+}
+
+noinline void GPU_DVFS_VSYNC(unsigned long freq)
+{
+	MET_TRACE("%lu\n", freq);
+}
+
+noinline void GPU_VSYNC_OFFSET_STATUS(unsigned int event_status, unsigned int debug_status)
+{
+	MET_TRACE("%u,%u\n", event_status, debug_status);
+}
+
+static void gpu_dvfs(void)
+{
+	unsigned int		freq = 0;
+	unsigned int		ThermalLimit = 0;
+	MTK_GPU_DVFS_TYPE	peType;
+	unsigned long		pulFreq = 0;
+	unsigned long		CustomBoost = 0;
+	unsigned long		CustomUpbound = 0;
+	unsigned int		event_status = 0;
+	unsigned int		debug_status = 0;
+
+	freq = mt_gpufreq_get_cur_freq_symbol ? mt_gpufreq_get_cur_freq_symbol() : 0;
+	ThermalLimit = mt_gpufreq_get_thermal_limit_freq_symbol ? mt_gpufreq_get_thermal_limit_freq_symbol() : 0;
+	if (mtk_get_custom_boost_gpu_freq_symbol)
+		mtk_get_custom_boost_gpu_freq_symbol(&CustomBoost);
+	if (mtk_get_custom_upbound_gpu_freq_symbol)
+		mtk_get_custom_upbound_gpu_freq_symbol(&CustomUpbound);
+	GPU_DVFS(freq, ThermalLimit, CustomBoost, CustomUpbound);
+
+	/* gpu dvfs type */
+	if (mtk_get_gpu_dvfs_from_symbol && mtk_get_gpu_dvfs_from_symbol(&peType, &pulFreq)) {
+		if (gpu_dvfs_type_prev != peType || gpu_dvfs_type_freq_prev != pulFreq) {
+			gpu_dvfs_type_freq[gpu_dvfs_type_prev] = 0;
+			gpu_dvfs_type_prev = peType;
+			gpu_dvfs_type_freq_prev = pulFreq;
+			gpu_dvfs_type_freq[gpu_dvfs_type_prev] = gpu_dvfs_type_freq_prev;
+			GPU_DVFS_TYPE();
+		}
+	}
+
+	if (mtk_get_vsync_based_target_freq_symbol && mtk_get_vsync_based_target_freq_symbol(&pulFreq))
+		GPU_DVFS_VSYNC(pulFreq);
+
+	if (mtk_get_vsync_offset_event_status_symbol && mtk_get_vsync_offset_debug_status_symbol) {
+		if (mtk_get_vsync_offset_event_status_symbol(&event_status)
+		    && mtk_get_vsync_offset_debug_status_symbol(&debug_status)) {
+			GPU_VSYNC_OFFSET_STATUS(event_status, debug_status);
+		}
+	}
+}
+
+static void gpu_dvfs_work(struct work_struct *work)
+{
+	gpu_dvfs();
+}
+
+static void gpu_dvfs_monitor_start(void)
+{
+	gpu_dvfs();
+	INIT_DELAYED_WORK(&gpu_dvfs_dwork, gpu_dvfs_work);
+}
+
+static void gpu_dvfs_monitor_stop(void)
+{
+	cancel_delayed_work_sync(&gpu_dvfs_dwork);
+	gpu_dvfs();
+}
+
+static void gpu_dvfs_monitor_polling(unsigned long long stamp, int cpu)
+{
+	schedule_delayed_work(&gpu_dvfs_dwork, 0);
+}
+
+static int gpu_dvfs_print_help(char *buf, int len)
+{
+	return snprintf(buf, PAGE_SIZE,
+			"  --gpu-dvfs				monitor gpu freq\n");
+}
+
+static int gpu_dvfs_print_header(char *buf, int len)
+{
+	int ret = 0;
+	int i = 0;
+
+	ret = snprintf(buf, PAGE_SIZE,
+			"met-info [000] 0.0: met_gpu_dvfs_header: ");
+	ret += snprintf(buf+ret, PAGE_SIZE-ret,
+			"Freq(kHz),ThermalLimit(kHz),CustomBoost,CustomUpbound\n");
+
+	ret += snprintf(buf+ret, PAGE_SIZE-ret,
+			"met-info [000] 0.0: met_gpu_dvfs_type_header: %s", gpu_dvfs_type_name[0]);
+	for (i = 1; i < ARRAY_SIZE(gpu_dvfs_type_name); i++)
+		ret += snprintf(buf+ret, PAGE_SIZE-ret, ",%s", gpu_dvfs_type_name[i]);
+	ret += snprintf(buf+ret, PAGE_SIZE-ret, "%s", "\n");
+
+	ret += snprintf(buf+ret, PAGE_SIZE-ret,
+			"met-info [000] 0.0: met_gpu_dvfs_vsync_header: VSYNC Based Freq\n");
+	ret += snprintf(buf+ret, PAGE_SIZE-ret,
+			"met-info [000] 0.0: met_gpu_vsync_offset_status_header: Event Status,Debug Status\n");
+
+	return ret;
+}
+
+struct metdevice met_gpudvfs = {
+	.name			= "gpu-dvfs",
+	.owner			= THIS_MODULE,
+	.type			= MET_TYPE_BUS,
+	.cpu_related		= 0,
+	.start			= gpu_dvfs_monitor_start,
+	.stop			= gpu_dvfs_monitor_stop,
+	.polling_interval	= 1,	/* ms */
+	.timed_polling		= gpu_dvfs_monitor_polling,
+	.print_help		= gpu_dvfs_print_help,
+	.print_header		= gpu_dvfs_print_header,
+	.ondiemet_mode		= 0,
+};
+
+/*
+ * GPU MEM monitor
+ */
+static unsigned long g_u4MemProfileIsOn;
+
+static void gpu_mem_monitor_start(void)
+{
+	if (!mtk_get_gpu_memory_usage_symbol)
+		return;
+
+#if 0
+	if (mtk_get_gpu_memory_usage_symbol(&u4Value))
+		g_u4MemProfileIsOn = 1;
+#endif
+	g_u4MemProfileIsOn = 1;
+}
+
+noinline void GPU_MEM(unsigned long long stamp, int cpu)
+{
+	unsigned int u4Value = 0;
+
+	if (!mtk_get_gpu_memory_usage_symbol)
+		return;
+
+	if (g_u4MemProfileIsOn == 1) {
+		mtk_get_gpu_memory_usage_symbol(&u4Value);
+		MET_TRACE("%d\n", u4Value);
+	}
+}
+
+static void gpu_mem_monitor_stop(void)
+{
+	g_u4MemProfileIsOn = 0;
+}
+
+static char help_mem[] =
+	"  --gpu-mem				monitor gpu memory status\n";
+static int gpu_mem_status_print_help(char *buf, int len)
+{
+	return snprintf(buf, PAGE_SIZE, help_mem);
+}
+
+static char g_pComGPUMemHeader[] =
+	"met-info [000] 0.0: met_gpu_mem_header: Usage\n";
+static int gpu_mem_status_print_header(char *buf, int len)
+{
+	return snprintf(buf, PAGE_SIZE, g_pComGPUMemHeader);
+}
+
+struct metdevice met_gpumem = {
+	.name			= "gpu-mem",
+	.owner			= THIS_MODULE,
+	.type			= MET_TYPE_BUS,
+	.cpu_related		= 0,
+	.start			= gpu_mem_monitor_start,
+	.stop			= gpu_mem_monitor_stop,
+	.mode			= 0,
+	.polling_interval	= 1,	/* ms */
+	.timed_polling		= GPU_MEM,
+	.print_help		= gpu_mem_status_print_help,
+	.print_header		= gpu_mem_status_print_header,
+};
+
+/*
+ * GPU power monitor
+ */
+static unsigned long g_u4PowerProfileIsOn;
+
+#ifdef GPU_HAL_RUN_PREMPTIBLE
+noinline void GPU_Power(struct work_struct *work)
+{
+	unsigned int u4Value = 0;
+
+	if (!mtk_get_gpu_power_loading_symbol)
+		return;
+
+	mtk_get_gpu_power_loading_symbol(&u4Value);
+	MET_TRACE("%d\n", u4Value);
+}
+
+static void GPU_PowerNotify(unsigned long long stamp, int cpu)
+{
+	if (g_u4PowerProfileIsOn == 1)
+		schedule_delayed_work(&gpu_pwr_dwork, 0);
+}
+#else
+noinline void GPU_Power(unsigned long long stamp, int cpu)
+{
+	unsigned int u4Value = 0;
+
+	if (!mtk_get_gpu_power_loading_symbol)
+		return;
+
+	if (g_u4PowerProfileIsOn == 1) {
+		mtk_get_gpu_power_loading_symbol(&u4Value);
+		MET_TRACE("%d\n", u4Value);
+	}
+}
+#endif
+
+static void gpu_Power_monitor_start(void)
+{
+	if (!mtk_get_gpu_power_loading_symbol)
+		return;
+
+#if 0
+	if (mtk_get_gpu_power_loading_symbol(&u4Value))
+		g_u4PowerProfileIsOn = 1;
+#endif
+	g_u4PowerProfileIsOn = 1;
+#ifdef GPU_HAL_RUN_PREMPTIBLE
+	INIT_DELAYED_WORK(&gpu_pwr_dwork, GPU_Power);
+#endif
+}
+
+static void gpu_Power_monitor_stop(void)
+{
+	g_u4PowerProfileIsOn = 0;
+
+#ifdef GPU_HAL_RUN_PREMPTIBLE
+	cancel_delayed_work_sync(&gpu_pwr_dwork);
+#endif
+}
+
+static char help_pwr[] =
+	"  --gpu-pwr				monitor gpu power status\n";
+static int gpu_Power_status_print_help(char *buf, int len)
+{
+	return snprintf(buf, PAGE_SIZE, help_pwr);
+}
+
+static char g_pComGPUPowerHeader[] =
+	"met-info [000] 0.0: met_gpu_power_header: Loading\n";
+static int gpu_Power_status_print_header(char *buf, int len)
+{
+	return snprintf(buf, PAGE_SIZE, g_pComGPUPowerHeader);
+}
+
+struct metdevice met_gpupwr = {
+	.name			= "gpu-pwr",
+	.owner			= THIS_MODULE,
+	.type			= MET_TYPE_BUS,
+	.cpu_related		= 0,
+	.start			= gpu_Power_monitor_start,
+	.stop			= gpu_Power_monitor_stop,
+	.mode			= 0,
+	.polling_interval	= 1,	/* ms */
+#ifdef GPU_HAL_RUN_PREMPTIBLE
+	.timed_polling		= GPU_PowerNotify,
+#else
+	.timed_polling		= GPU_Power,
+#endif
+	.print_help		= gpu_Power_status_print_help,
+	.print_header		= gpu_Power_status_print_header,
+};
+
+
+/*
+ * GPU PMU
+ */
+#define UNUSE_ARG(arg) ((void)arg)
+
+#ifdef GPU_HAL_RUN_PREMPTIBLE
+static struct delayed_work gpu_pmu_dwork;
+#endif
+
+#define MAX_PMU_STR_LEN (1024 * 5)
+
+static const char help_pmu[] = "  --gpu-pmu				monitor gpu pmu status";
+static const char header_pmu[] = "met-info [000] 0.0: met_gpu_pmu_header: ";
+static char pmu_str[MAX_PMU_STR_LEN];
+static int pmu_cnt;
+static int gpu_pwr_status = 1;
+static GPU_PMU *pmu_list;
+
+
+noinline void GPU_PMU_RAW(
+	unsigned long long stamp,
+	int cpu)
+{
+	bool ret;
+	int i = 0;
+	char *SOB, *EOB;
+	unsigned int value[pmu_cnt];
+
+	if (stamp == 0 && cpu == 0) {
+		for (i = 0; i < pmu_cnt; i++)
+			value[i] = 0;
+
+		MET_TRACE_GETBUF(&SOB, &EOB);
+		EOB = ms_formatH(EOB, pmu_cnt, value);
+		MET_TRACE_PUTBUF(SOB, EOB);
+		return;
+	}
+
+	if (mtk_get_gpu_pmu_swapnreset_symbol) {
+		ret = mtk_get_gpu_pmu_swapnreset_symbol(pmu_list, pmu_cnt);
+		if (ret) {
+			for (i = 0; i < pmu_cnt; i++) {
+				if (pmu_list[i].overflow)
+					pmu_list[i].value = 0xFFFFFFFF;
+				value[i] = pmu_list[i].value;
+			}
+			MET_TRACE_GETBUF(&SOB, &EOB);
+			EOB = ms_formatH(EOB, pmu_cnt, value);
+			MET_TRACE_PUTBUF(SOB, EOB);
+		}
+	}
+}
+
+static int create_gpu_pmu_list(void)
+{
+	int ret = 0;
+	int len = 0;
+	int i = 0;
+
+	if (mtk_get_gpu_pmu_init_symbol) {
+		ret = mtk_get_gpu_pmu_init(NULL, 0, &pmu_cnt);
+		if (pmu_cnt == 0 || ret == 0)
+			return 0;
+	} else
+		return 0;
+
+	pmu_list = kmalloc_array(pmu_cnt, sizeof(GPU_PMU), GFP_KERNEL);
+	if (pmu_list) {
+		memset(pmu_list, 0x00, sizeof(GPU_PMU)*pmu_cnt);
+		ret = mtk_get_gpu_pmu_init(pmu_list, pmu_cnt, NULL);
+
+		memset(pmu_str, 0x00, MAX_PMU_STR_LEN);
+		len = snprintf(pmu_str, MAX_PMU_STR_LEN, "%s", pmu_list[0].name);
+		for (i = 1; i < pmu_cnt; i++)
+			len += snprintf(pmu_str + len, MAX_PMU_STR_LEN - len, ",%s", pmu_list[i].name);
+
+		/*
+		* dummy read in order to reset GPU PMU counter
+		*/
+		if (mtk_get_gpu_pmu_swapnreset_symbol)
+			mtk_get_gpu_pmu_swapnreset_symbol(pmu_list, pmu_cnt);
+	}
+
+	return ret;
+}
+
+static void delete_gpu_pmu_list(void)
+{
+	kfree(pmu_list);
+	pmu_list = NULL;
+	pmu_cnt = 0;
+}
+
+static void gpu_pwr_status_cb(int on)
+{
+	MET_TRACE("on = %d\n", on);
+
+	if (on == 1) {
+		/*
+		* dummy read in order to reset GPU PMU counter
+		*/
+		if (mtk_get_gpu_pmu_swapnreset_symbol)
+			mtk_get_gpu_pmu_swapnreset_symbol(pmu_list, pmu_cnt);
+
+	} else {
+		GPU_PMU_RAW(1, 0);
+		GPU_PMU_RAW(0, 0);
+	}
+
+	gpu_pwr_status = on;
+}
+
+static void gpu_pmu_monitor_start(void)
+{
+	int ret;
+
+	ret = create_gpu_pmu_list();
+	if (ret == 0)
+		return;
+
+	if (mtk_register_gpu_power_change_symbol)
+		mtk_register_gpu_power_change_symbol("met_gpu", gpu_pwr_status_cb);
+
+#ifdef GPU_HAL_RUN_PREMPTIBLE
+	INIT_DELAYED_WORK(&gpu_pmu_dwork, GPU_PMU_RAW);
+#endif
+}
+
+static void gpu_pmu_monitor_stop(void)
+{
+#ifdef GPU_HAL_RUN_PREMPTIBLE
+	cancel_delayed_work_sync(&gpu_pmu_dwork);
+#endif
+
+	if (mtk_unregister_gpu_power_change_symbol)
+		mtk_unregister_gpu_power_change_symbol("met_gpu");
+	delete_gpu_pmu_list();
+
+#if 0
+	/* stop polling counter */
+	if (mtk_get_gpu_pmu_swapnreset_stop_symbol)
+		mtk_get_gpu_pmu_swapnreset_stop_symbol();
+	/* release resource */
+	if (mtk_get_gpu_pmu_deinit_symbol)
+		mtk_get_gpu_pmu_deinit_symbol();
+#endif
+}
+
+#ifdef GPU_HAL_RUN_PREMPTIBLE
+static void gpu_pmu_timed_polling_notify(
+	unsigned long long stamp,
+	int cpu)
+{
+	UNUSE_ARG(stamp);
+	UNUSE_ARG(cpu);
+
+	if (gpu_pwr_status == 1)
+		schedule_delayed_work(&gpu_pmu_dwork, 0);
+}
+#else
+static void gpu_pmu_timed_polling(
+	unsigned long long stamp,
+	int cpu)
+{
+	UNUSE_ARG(stamp);
+	UNUSE_ARG(cpu);
+
+	if (gpu_pwr_status == 1)
+		GPU_PMU_RAW(stamp, cpu);
+}
+#endif
+
+static int gpu_pmu_print_help(
+	char *buf,
+	int len)
+{
+	UNUSE_ARG(len);
+	return snprintf(buf, PAGE_SIZE, "%s\n", help_pmu);
+}
+
+static int gpu_pmu_print_header(
+	char *buf,
+	int len)
+{
+	len = 0;
+
+	len = snprintf(buf, PAGE_SIZE, "%s", header_pmu);
+	len += snprintf(buf + len, PAGE_SIZE - len, "%s\n", pmu_str);
+
+	return len;
+}
+
+struct metdevice met_gpu_pmu = {
+	.name			= "gpu-pmu",
+	.owner			= THIS_MODULE,
+	.type			= MET_TYPE_PMU,
+	.cpu_related		= 0,
+	.start			= gpu_pmu_monitor_start,
+	.stop			= gpu_pmu_monitor_stop,
+	.mode			= 0,
+	.polling_interval	= 1,	/* ms */
+#ifdef GPU_HAL_RUN_PREMPTIBLE
+	.timed_polling		= gpu_pmu_timed_polling_notify,
+#else
+	.timed_polling		= gpu_pmu_timed_polling,
+#endif
+	.print_help		= gpu_pmu_print_help,
+	.print_header		= gpu_pmu_print_header,
+};
diff --git a/src/devtools/met-driver/4.4/common/mtk_gpu_metmonitor.h b/src/devtools/met-driver/4.4/common/mtk_gpu_metmonitor.h
new file mode 100644
index 0000000..069c534
--- /dev/null
+++ b/src/devtools/met-driver/4.4/common/mtk_gpu_metmonitor.h
@@ -0,0 +1,18 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MT_GPU_METMONITOR_H_
+
+#define _MT_GPU_METMONITOR_H_
+
+#endif				/* _MT_GPU_METMONITOR_H_ */
diff --git a/src/devtools/met-driver/4.4/common/mtk_typedefs.h b/src/devtools/met-driver/4.4/common/mtk_typedefs.h
new file mode 100644
index 0000000..4424479
--- /dev/null
+++ b/src/devtools/met-driver/4.4/common/mtk_typedefs.h
@@ -0,0 +1,318 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MT_TYPEDEFS_H__
+
+/*
+ *  KOBJ ATTR Manipulations Macros
+ */
+
+#define KOBJ_ITEM_LIST(args...)		args
+
+/*
+ * Declaring KOBJ attributes
+ */
+#define DECLARE_KOBJ_ATTR(attr_name) \
+	static struct kobj_attribute attr_name##_attr = \
+		__ATTR(attr_name, 0664, attr_name##_show, attr_name##_store)
+
+#define DECLARE_KOBJ_ATTR_RO(attr_name) \
+	static struct kobj_attribute attr_name##_attr = \
+		__ATTR_RO(attr_name)
+
+/*
+ * Declaring KOBJ attributes with integer variable
+ */
+/* normal version */
+#define DECLARE_KOBJ_ATTR_SHOW_INT(attr_name, var_name) \
+	static ssize_t attr_name##_show( \
+		struct kobject *kobj, \
+		struct kobj_attribute *attr, \
+		char *buf) \
+	{ \
+		return snprintf(buf, PAGE_SIZE, "%d\n", var_name); \
+	}
+#define DECLARE_KOBJ_ATTR_STORE_INT(attr_name, var_name) \
+	static ssize_t attr_name##_store( \
+		struct kobject *kobj, \
+		struct kobj_attribute *attr, \
+		const char *buf, \
+		size_t n) \
+	{ \
+		int	val; \
+		if (kstrtoint(buf, 0, &val) != 0) { \
+			return -EINVAL; \
+		} \
+		var_name = val; \
+		return n; \
+	}
+#define DECLARE_KOBJ_ATTR_INT(attr_name, var_name) \
+	DECLARE_KOBJ_ATTR_SHOW_INT(attr_name, var_name) \
+	DECLARE_KOBJ_ATTR_STORE_INT(attr_name, var_name) \
+	DECLARE_KOBJ_ATTR(attr_name)
+#define DECLARE_KOBJ_ATTR_RO_INT(attr_name, var_name) \
+	DECLARE_KOBJ_ATTR_SHOW_INT(attr_name, var_name) \
+	DECLARE_KOBJ_ATTR_RO(attr_name)
+
+/* cond-check version */
+#define DECLARE_KOBJ_ATTR_STORE_INT_CHECK(attr_name, var_name, cond) \
+	static ssize_t attr_name##_store( \
+		struct kobject *kobj, \
+		struct kobj_attribute *attr, \
+		const char *buf, \
+		size_t n) \
+	{ \
+		int	var_name##temp = var_name; \
+		if (kstrtoint(buf, 0, &var_name) != 0) { \
+			var_name = var_name##temp; \
+			return -EINVAL; \
+		} \
+		if (cond) { \
+			return n; \
+		} else { \
+			var_name = var_name##temp; \
+			return -EINVAL; \
+		} \
+	}
+/* Note: the name of val in cond can NOT be the same as var_name */
+#define DECLARE_KOBJ_ATTR_INT_CHECK(attr_name, var_name, cond) \
+	DECLARE_KOBJ_ATTR_SHOW_INT(attr_name, var_name) \
+	DECLARE_KOBJ_ATTR_STORE_INT_CHECK(attr_name, var_name, cond) \
+	DECLARE_KOBJ_ATTR(attr_name)
+
+/* helper procedure version */
+#define DECLARE_KOBJ_ATTR_SHOW_INT_PROC(attr_name, var_name, func) \
+	static ssize_t attr_name##_show( \
+		struct kobject *kobj, \
+		struct kobj_attribute *attr, \
+		char *buf) \
+	{ \
+		return func(kobj, attr, buf, var_name); \
+	}
+#define DECLARE_KOBJ_ATTR_STORE_INT_PROC(attr_name, var_name, func) \
+	static ssize_t attr_name##_store( \
+		struct kobject *kobj, \
+		struct kobj_attribute *attr, \
+		const char *buf, \
+		size_t n) \
+	{ \
+		return func(kobj, attr, buf, n, &(var_name)); \
+	}
+#define DECLARE_KOBJ_ATTR_INT_PROC(attr_name, var_name, show, store) \
+	DECLARE_KOBJ_ATTR_SHOW_INT_PROC(attr_name, var_name, show) \
+	DECLARE_KOBJ_ATTR_STORE_INT_PROC(attr_name, var_name, store) \
+	DECLARE_KOBJ_ATTR(attr_name)
+
+/*
+ * Declaring KOBJ attributes with integer(hex) variable
+ */
+/* normal version */
+#define DECLARE_KOBJ_ATTR_SHOW_HEX(attr_name, var_name) \
+	static ssize_t attr_name##_show( \
+		struct kobject *kobj, \
+		struct kobj_attribute *attr, \
+		char *buf) \
+	{ \
+		return snprintf(buf, PAGE_SIZE, "%x\n", var_name); \
+	}
+#define DECLARE_KOBJ_ATTR_STORE_HEX(attr_name, var_name) \
+	static ssize_t attr_name##_store( \
+		struct kobject *kobj, \
+		struct kobj_attribute *attr, \
+		const char *buf, \
+		size_t n) \
+	{ \
+		unsigned int	val; \
+		if (kstrtouint(buf, 0, &val) != 0) { \
+			return -EINVAL; \
+		} \
+		var_name = val; \
+		return n; \
+	}
+#define DECLARE_KOBJ_ATTR_HEX(attr_name, var_name) \
+	DECLARE_KOBJ_ATTR_SHOW_HEX(attr_name, var_name) \
+	DECLARE_KOBJ_ATTR_STORE_HEX(attr_name, var_name) \
+	DECLARE_KOBJ_ATTR(attr_name)
+#define DECLARE_KOBJ_ATTR_RO_HEX(attr_name, var_name) \
+	DECLARE_KOBJ_ATTR_SHOW_HEX(attr_name, var_name) \
+	DECLARE_KOBJ_ATTR_RO(attr_name)
+
+/* cond-check version */
+#define DECLARE_KOBJ_ATTR_STORE_HEX_CHECK(attr_name, var_name, cond) \
+	static ssize_t attr_name##_store( \
+		struct kobject *kobj, \
+		struct kobj_attribute *attr, \
+		const char *buf, \
+		size_t n) \
+	{ \
+		unsigned int	var_name##temp = var_name; \
+		if (kstrtouint(buf, 0, &var_name) != 0) { \
+			var_name = var_name##temp; \
+			return -EINVAL; \
+		} \
+		if (cond) { \
+			return n; \
+		} else { \
+			var_name = var_name##temp; \
+			return -EINVAL; \
+		} \
+	}
+/* Note: the name of val in cond can NOT be the same as var_name */
+#define DECLARE_KOBJ_ATTR_HEX_CHECK(attr_name, var_name, cond) \
+	DECLARE_KOBJ_ATTR_SHOW_HEX(attr_name, var_name) \
+	DECLARE_KOBJ_ATTR_STORE_HEX_CHECK(attr_name, var_name, cond) \
+	DECLARE_KOBJ_ATTR(attr_name)
+
+/* helper procedure version */
+#define DECLARE_KOBJ_ATTR_SHOW_HEX_PROC(attr_name, var_name, func) \
+	static ssize_t attr_name##_show( \
+		struct kobject *kobj, \
+		struct kobj_attribute *attr, \
+		char *buf) \
+	{ \
+		return func(kobj, attr, buf, var_name); \
+	}
+#define DECLARE_KOBJ_ATTR_STORE_HEX_PROC(attr_name, var_name, func) \
+	static ssize_t attr_name##_store( \
+		struct kobject *kobj, \
+		struct kobj_attribute *attr, \
+		const char *buf, \
+		size_t n) \
+	{ \
+		return func(kobj, attr, buf, n, &(var_name)); \
+	}
+#define DECLARE_KOBJ_ATTR_HEX_PROC(attr_name, var_name, show, store) \
+	DECLARE_KOBJ_ATTR_SHOW_HEX_PROC(attr_name, var_name, show) \
+	DECLARE_KOBJ_ATTR_STORE_HEX_PROC(attr_name, var_name, store) \
+	DECLARE_KOBJ_ATTR(attr_name)
+
+/*
+ * Declaring KOBJ attributes with string variable
+ */
+#define DECLARE_KOBJ_ATTR_SHOW_STR(attr_name, var_name) \
+	static ssize_t attr_name##_show( \
+		struct kobject *kobj, \
+		struct kobj_attribute *attr, \
+		char *buf) \
+	{ \
+		return snprintf(buf, PAGE_SIZE, "%s", var_name); \
+	}
+
+#define DECLARE_KOBJ_ATTR_RO_STR(attr_name, var_name) \
+	DECLARE_KOBJ_ATTR_SHOW_STR(attr_name, var_name) \
+	DECLARE_KOBJ_ATTR_RO(attr_name)
+
+/*
+ * Declaring KOBJ attributes with integer list variable
+ */
+#define DECLARE_KOBJ_ATTR_INT_LIST_ITEM(list_name, list) \
+	static struct list_name##_list_item_t { \
+		int	key; \
+		int	val; \
+	} const list_name##_list_item[] = { list }
+#define DECLARE_KOBJ_ATTR_SHOW_INT_LIST(attr_name, var_name, list_name) \
+	static ssize_t attr_name##_show( \
+		struct kobject *kobj, \
+		struct kobj_attribute *attr, \
+		char *buf) \
+	{ \
+		int	i; \
+		for (i = 0; i < ARRAY_SIZE(list_name##_list_item); i++) { \
+			if (var_name == list_name##_list_item[i].key) { \
+				return snprintf(buf, \
+						PAGE_SIZE, \
+						"%d\n", \
+						list_name##_list_item[i].val); \
+			} \
+		} \
+		return snprintf(buf, PAGE_SIZE, "%d\n", -1); \
+	}
+#define DECLARE_KOBJ_ATTR_STORE_INT_LIST(attr_name, var_name, list_name) \
+	static ssize_t attr_name##_store( \
+		struct kobject *kobj, \
+		struct kobj_attribute *attr, \
+		const char *buf, \
+		size_t n) \
+	{ \
+		int	value; \
+		int	i; \
+		if (kstrtoint(buf, 10, &value) != 0) \
+			return -EINVAL; \
+		for (i = 0; i < ARRAY_SIZE(list_name##_list_item); i++) { \
+			if (value == list_name##_list_item[i].val) { \
+				var_name = list_name##_list_item[i].key; \
+				return n; \
+			} \
+		} \
+		return -EINVAL; \
+	}
+#define DECLARE_KOBJ_ATTR_INT_LIST(attr_name, var_name, list_name) \
+	DECLARE_KOBJ_ATTR_SHOW_INT_LIST(attr_name, var_name, list_name) \
+	DECLARE_KOBJ_ATTR_STORE_INT_LIST(attr_name, var_name, list_name) \
+	DECLARE_KOBJ_ATTR(attr_name)
+
+/*
+ * Declaring KOBJ attributes with string list variable
+ */
+#define DECLARE_KOBJ_ATTR_STR_LIST_ITEM(list_name, list) \
+	static struct list_name##_list_item_t { \
+		int	key; \
+		char	*val; \
+	} const list_name##_list_item[] = { list }
+#define DECLARE_KOBJ_ATTR_SHOW_STR_LIST(attr_name, var_name, list_name) \
+	static ssize_t attr_name##_show( \
+		struct kobject *kobj, \
+		struct kobj_attribute *attr, \
+		char *buf) \
+	{ \
+		int	i; \
+		for (i = 0; i < ARRAY_SIZE(list_name##_list_item); i++) { \
+			if (var_name == list_name##_list_item[i].key) { \
+				return snprintf(buf, \
+						PAGE_SIZE, \
+						"%s\n", \
+						list_name##_list_item[i].val); \
+			} \
+		} \
+		return snprintf(buf, PAGE_SIZE, "%s\n", "ERR"); \
+	}
+#define DECLARE_KOBJ_ATTR_STORE_STR_LIST(attr_name, var_name, list_name) \
+	static ssize_t attr_name##_store( \
+		struct kobject *kobj, \
+		struct kobj_attribute *attr, \
+		const char *buf, \
+		size_t n) \
+	{ \
+		int	i; \
+		for (i = 0; i < ARRAY_SIZE(list_name##_list_item); i++) { \
+			if (strncasecmp(buf, \
+					list_name##_list_item[i].val, \
+					strlen(list_name##_list_item[i].val)) == 0) { \
+				var_name = list_name##_list_item[i].key; \
+				return n; \
+			} \
+		} \
+		return -EINVAL; \
+	}
+#define DECLARE_KOBJ_ATTR_STR_LIST(attr_name, var_name, list_name) \
+	DECLARE_KOBJ_ATTR_SHOW_STR_LIST(attr_name, var_name, list_name) \
+	DECLARE_KOBJ_ATTR_STORE_STR_LIST(attr_name, var_name, list_name) \
+	DECLARE_KOBJ_ATTR(attr_name)
+
+/*
+ *  MET Debug Message
+ */
+#define METINFO(format, ...)	pr_debug("[MET]%s: "format, __func__, ##__VA_ARGS__)
+#define METERROR(format, ...)	pr_debug("[MET][ERR]%s: "format, __func__, ##__VA_ARGS__)
+
+#endif	/* _MT_TYPEDEFS_H__ */
diff --git a/src/devtools/met-driver/4.4/common/ondiemet.c b/src/devtools/met-driver/4.4/common/ondiemet.c
new file mode 100644
index 0000000..5247fa7
--- /dev/null
+++ b/src/devtools/met-driver/4.4/common/ondiemet.c
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "ondiemet.h"
+
+/* record enabled modules */
+unsigned int ondiemet_module[ONDIEMET_NUM];
+EXPORT_SYMBOL(ondiemet_module);
+
+void (*scp_start[ONDIEMET_NUM]) (void) = {
+sspm_start, NULL, NULL};
+
+void (*scp_stop[ONDIEMET_NUM]) (void) = {
+sspm_stop, NULL, NULL};
+
+void (*scp_extract[ONDIEMET_NUM]) (void) = {
+sspm_extract, NULL, NULL};
+
+/* record which MCU is started to generate data */
+int ondiemet_module_started[ONDIEMET_NUM];
+
+int ondiemet_attr_init(struct device *dev)
+{
+	int ret;
+
+	ret = sspm_attr_init(dev);
+	if (ret != 0) {
+		pr_debug("can not create device file: sspm related\n");
+		return ret;
+	}
+
+	return 0;
+
+}
+
+int ondiemet_attr_uninit(struct device *dev)
+{
+	int ret;
+
+	ret = sspm_attr_uninit(dev);
+	if (ret != 0) {
+		pr_debug("can not delete device file: sspm related\n");
+		return ret;
+	}
+
+	return 0;
+
+}
+
+void ondiemet_start(void)
+{
+	int i;
+
+	for (i = 0; i < ONDIEMET_NUM; i++) {
+		if (ondiemet_module[i] != 0) {
+			ondiemet_module_started[i] = 1;
+			(*scp_start[i]) ();
+		}
+	}
+}
+
+void ondiemet_stop(void)
+{
+	int i;
+
+	for (i = 0; i < ONDIEMET_NUM; i++) {
+		if (ondiemet_module[i] != 0) {
+			(*scp_stop[i]) ();
+			ondiemet_module_started[i] = 0;
+		}
+	}
+}
+
+void ondiemet_extract(void)
+{
+	int i;
+
+	for (i = 0; i < ONDIEMET_NUM; i++) {
+		if (ondiemet_module[i] != 0)
+			(*scp_extract[i]) ();
+	}
+}
diff --git a/src/devtools/met-driver/4.4/common/ondiemet.h b/src/devtools/met-driver/4.4/common/ondiemet.h
new file mode 100644
index 0000000..3fff604
--- /dev/null
+++ b/src/devtools/met-driver/4.4/common/ondiemet.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __ONDIEMET_H
+#define __ONDIEMET_H
+
+#include "ondiemet_log.h"
+
+extern void ondiemet_extract(void);
+extern void ondiemet_stop(void);
+extern void ondiemet_start(void);
+
+#define ONDIEMET_SSPM  0
+#define ONDIEMET_NUM  3		/* total number of supported */
+extern unsigned int ondiemet_module[];
+extern void sspm_start(void);
+extern void sspm_stop(void);
+extern void sspm_extract(void);
+extern int sspm_attr_init(struct device *dev);
+extern int sspm_attr_uninit(struct device *dev);
+
+extern int ondiemet_attr_init(struct device *dev);
+extern int ondiemet_attr_uninit(struct device *dev);
+
+extern int sspm_buffer_size;
+
+#endif				/* __ONDIEMET_H */
diff --git a/src/devtools/met-driver/4.4/common/ondiemet_log.c b/src/devtools/met-driver/4.4/common/ondiemet_log.c
new file mode 100644
index 0000000..4f3ad69
--- /dev/null
+++ b/src/devtools/met-driver/4.4/common/ondiemet_log.c
@@ -0,0 +1,516 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/debugfs.h>
+#include <linux/mutex.h>
+#include <linux/semaphore.h>
+#include <linux/freezer.h>
+#include <linux/uaccess.h>
+#include <linux/completion.h>
+
+#include "ondiemet_log.h"
+
+#define ONDIEMET_LOG_REQ 1
+/* TODO: abandon this constatnt */
+#define ONDIEMET_LOG_STOP 2
+
+#define PID_NONE (-1)
+
+#define ONDIEMET_LOG_STOP_MODE 0
+#define ONDIEMET_LOG_RUN_MODE 1
+#define ONDIEMET_LOG_DEBUG_MODE 2
+
+static int ondiemet_trace_run;
+static struct dentry *dbgfs_met_dir;
+
+struct mutex lock_tracef;
+struct ondiemet_log_req_q_t {
+	struct list_head listq;
+	struct mutex lockq;
+	/* struct semaphore new_evt_sema; */
+	struct completion new_evt_comp;
+	int closeq_flag;
+} ondiemet_log_req_q;
+
+struct ondiemet_log_req {
+	struct list_head list;
+	int cmd_type;
+	const char *src;
+	size_t num;
+
+	void (*on_fini_cb)(const void *p);
+	const void *param;
+};
+
+#define __ondiemet_log_req_init(req, cmd, s, n, pf, p)	\
+	do {						\
+		INIT_LIST_HEAD(&req->list);		\
+		req->cmd_type = cmd;			\
+		req->src = s;				\
+		req->num = n;				\
+		req->on_fini_cb = pf;			\
+		req->param = p;				\
+	} while (0)
+
+#define __ondiemet_log_req_fini(req)		        \
+	do {					        \
+		if (req->on_fini_cb)			\
+			req->on_fini_cb(req->param);	\
+		kfree(req);				\
+	} while (0)
+
+static void __ondiemet_log_req_q_init(struct ondiemet_log_req_q_t *q)
+{
+	INIT_LIST_HEAD(&q->listq);
+	mutex_init(&q->lockq);
+	/* sema_init(&q->new_evt_sema, 0); */
+	init_completion(&q->new_evt_comp);
+	q->closeq_flag = 1;
+}
+
+/* undequeue is seen as a roll-back operation, so it can be done even when the queue is closed */
+static void __ondiemet_log_req_undeq(struct ondiemet_log_req *req)
+{
+	mutex_lock(&ondiemet_log_req_q.lockq);
+	list_add(&req->list, &ondiemet_log_req_q.listq);
+	mutex_unlock(&ondiemet_log_req_q.lockq);
+
+	/* up(&ondiemet_log_req_q.new_evt_sema); */
+	complete(&ondiemet_log_req_q.new_evt_comp);
+}
+
+static int __ondiemet_log_req_enq(struct ondiemet_log_req *req)
+{
+	mutex_lock(&ondiemet_log_req_q.lockq);
+	if (ondiemet_log_req_q.closeq_flag) {
+		mutex_unlock(&ondiemet_log_req_q.lockq);
+		return -EBUSY;
+	}
+
+	list_add_tail(&req->list, &ondiemet_log_req_q.listq);
+	if (req->cmd_type == ONDIEMET_LOG_STOP)
+		ondiemet_log_req_q.closeq_flag = 1;
+	mutex_unlock(&ondiemet_log_req_q.lockq);
+
+	/* up(&ondiemet_log_req_q.new_evt_sema); */
+	complete(&ondiemet_log_req_q.new_evt_comp);
+
+	return 0;
+}
+
+int ondiemet_log_req_enq(const char *src, size_t num, void (*on_fini_cb)(const void *p), const void *param)
+{
+	struct ondiemet_log_req *req = kmalloc(sizeof(*req), GFP_KERNEL);
+
+	__ondiemet_log_req_init(req, ONDIEMET_LOG_REQ, src, num, on_fini_cb, param);
+	return __ondiemet_log_req_enq(req);
+}
+
+/*int down_freezable_interruptible(struct semaphore *sem) */
+int down_freezable_interruptible(struct completion *comp)
+{
+
+	int ret;
+
+	freezer_do_not_count();
+	/* ret = down_interruptible(sem); */
+	ret = wait_for_completion_interruptible(comp);
+	freezer_count();
+
+	return ret;
+}
+
+struct ondiemet_log_req *__ondiemet_log_req_deq(void)
+{
+	struct ondiemet_log_req *ret_req;
+
+	/*if (down_freezable_interruptible(&ondiemet_log_req_q.new_evt_sema))*/
+	if (down_freezable_interruptible(&ondiemet_log_req_q.new_evt_comp))
+		return NULL;
+
+	mutex_lock(&ondiemet_log_req_q.lockq);
+	ret_req = list_entry(ondiemet_log_req_q.listq.next, struct ondiemet_log_req, list);
+	list_del_init(&ret_req->list);
+	mutex_unlock(&ondiemet_log_req_q.lockq);
+
+	return ret_req;
+}
+
+void __ondiemet_log_req_open(void)
+{
+	mutex_lock(&ondiemet_log_req_q.lockq);
+	ondiemet_log_req_q.closeq_flag = 0;
+	mutex_unlock(&ondiemet_log_req_q.lockq);
+}
+
+int __ondiemet_log_req_closed(void)
+{
+	int ret;
+
+	mutex_lock(&ondiemet_log_req_q.lockq);
+	ret = ondiemet_log_req_q.closeq_flag && list_empty(&ondiemet_log_req_q.listq);
+	mutex_unlock(&ondiemet_log_req_q.lockq);
+
+	return ret;
+}
+
+int __ondiemet_log_req_working(void)
+{
+	int ret;
+
+	mutex_lock(&ondiemet_log_req_q.lockq);
+	ret = !ondiemet_log_req_q.closeq_flag;
+	mutex_unlock(&ondiemet_log_req_q.lockq);
+
+	return ret;
+}
+
+static void *__ondiemet_trace_seq_next(struct seq_file *seqf, loff_t *offset)
+{
+	struct ondiemet_log_req *next_req;
+
+	if (ondiemet_trace_run == ONDIEMET_LOG_DEBUG_MODE)
+		pr_debug("[met] __ondiemet_trace_seq_next: pid: %d\n", current->pid);
+
+	if (__ondiemet_log_req_closed())
+		return NULL;
+
+	next_req = __ondiemet_log_req_deq();
+
+	if (next_req == NULL)
+		return NULL;
+
+	if (next_req->cmd_type == ONDIEMET_LOG_STOP) {
+		__ondiemet_log_req_fini(next_req);
+		return NULL;
+	}
+
+	return (void *) next_req;
+}
+
+struct mutex lock_trace_owner_pid;
+pid_t trace_owner_pid = PID_NONE;
+static void *ondiemet_trace_seq_start(struct seq_file *seqf, loff_t *offset)
+{
+	void *ret;
+
+	if (ondiemet_trace_run == ONDIEMET_LOG_DEBUG_MODE) {
+		pr_debug("[met] ondiemet_trace_seq_start: locked_pid: %d, pid: %d, offset: %llu\n",
+			 trace_owner_pid, current->pid, *offset);
+	}
+
+	if (!mutex_trylock(&lock_tracef))
+		return NULL;
+
+	mutex_lock(&lock_trace_owner_pid);
+	trace_owner_pid = current->pid;
+	mutex_unlock(&lock_trace_owner_pid);
+
+	ret = __ondiemet_trace_seq_next(seqf, offset);
+
+	return ret;
+}
+
+static void *ondiemet_trace_seq_next(struct seq_file *seqf, void *p, loff_t *offset)
+{
+	if (ondiemet_trace_run == ONDIEMET_LOG_DEBUG_MODE)
+		pr_debug("[met] ondiemet_trace_seq_next: pid: %d\n", current->pid);
+
+	(*offset)++;
+	return __ondiemet_trace_seq_next(seqf, offset);
+}
+
+static int ondiemet_trace_seq_show(struct seq_file *seqf, void *p)
+{
+	struct ondiemet_log_req *req = (struct ondiemet_log_req *) p;
+	size_t l_sz;
+	size_t r_sz;
+	struct ondiemet_log_req *l_req;
+	struct ondiemet_log_req *r_req;
+	int ret;
+
+	if (ondiemet_trace_run == ONDIEMET_LOG_DEBUG_MODE)
+		pr_debug("[met] ondiemet_trace_seq_show: pid: %d\n", current->pid);
+
+	if (req->num >= seqf->size) {
+		l_req = kmalloc(sizeof(*req), GFP_KERNEL);
+		r_req = req;
+
+		l_sz = seqf->size >> 1;
+		r_sz = req->num - l_sz;
+		__ondiemet_log_req_init(l_req, ONDIEMET_LOG_REQ, req->src, l_sz, NULL, NULL);
+		__ondiemet_log_req_init(r_req, ONDIEMET_LOG_REQ, req->src + l_sz,
+					r_sz, req->on_fini_cb, req->param);
+
+		__ondiemet_log_req_undeq(r_req);
+		req = l_req;
+
+		if (ondiemet_trace_run == ONDIEMET_LOG_DEBUG_MODE)
+			pr_debug("[met] ondiemet_trace_seq_show: split request\n");
+	}
+
+	ret = seq_write(seqf, req->src, req->num);
+
+	if (ret) {
+		/* check if seq_file buffer overflows */
+		if (seqf->count == seqf->size) {
+			__ondiemet_log_req_undeq(req);
+		} else {
+			if (ondiemet_trace_run == ONDIEMET_LOG_DEBUG_MODE)
+				pr_debug("[met] ondiemet_trace_seq_show: reading trace record failed, some data may be lost or corrupted\n");
+			__ondiemet_log_req_fini(req);
+		}
+		return 0;
+	}
+
+	__ondiemet_log_req_fini(req);
+	return 0;
+}
+
+static void ondiemet_trace_seq_stop(struct seq_file *seqf, void *p)
+{
+	if (ondiemet_trace_run == ONDIEMET_LOG_DEBUG_MODE)
+		pr_debug("[met] ondiemet_trace_seq_stop: pid: %d\n", current->pid);
+
+	mutex_lock(&lock_trace_owner_pid);
+	if (current->pid == trace_owner_pid) {
+		trace_owner_pid = PID_NONE;
+		mutex_unlock(&lock_tracef);
+	}
+	mutex_unlock(&lock_trace_owner_pid);
+}
+
+static const struct seq_operations ondiemet_trace_seq_ops = {
+	.start = ondiemet_trace_seq_start,
+	.next = ondiemet_trace_seq_next,
+	.stop = ondiemet_trace_seq_stop,
+	.show = ondiemet_trace_seq_show
+};
+
+static int ondiemet_trace_open(struct inode *inode, struct file *fp)
+{
+	return seq_open(fp, &ondiemet_trace_seq_ops);
+}
+
+static const struct file_operations ondiemet_trace_fops = {
+	.owner = THIS_MODULE,
+	.open = ondiemet_trace_open,
+	.read = seq_read,
+	.llseek = seq_lseek,
+	.release = seq_release
+};
+
+/*struct semaphore log_start_sema;*/
+struct completion log_start_comp;
+int ondiemet_log_manager_start(void)
+{
+	int ret;
+
+	/* TODO: choose a better return value */
+	if (__ondiemet_log_req_working())
+		return -EINVAL;
+
+	if (!__ondiemet_log_req_closed()) {
+		/*ret = down_killable(&log_start_sema);*/
+		ret = wait_for_completion_killable(&log_start_comp);
+		if (ret)
+			return ret;
+	}
+
+	__ondiemet_log_req_open();
+
+	return 0;
+}
+
+/*struct semaphore log_stop_sema;*/
+struct completion log_stop_comp;
+static void __log_stop_cb(const void *p)
+{
+	/* up(&log_start_sema); */
+	/* up(&log_stop_sema); */
+	complete(&log_start_comp);
+	complete(&log_stop_comp);
+}
+
+int ondiemet_log_manager_stop(void)
+{
+	int ret;
+	struct ondiemet_log_req *req;
+
+	/* TODO: choose a better return value */
+	if (__ondiemet_log_req_closed())
+		return -EINVAL;
+
+	req = kmalloc(sizeof(*req), GFP_KERNEL);
+
+	__ondiemet_log_req_init(req, ONDIEMET_LOG_STOP, NULL, 0, __log_stop_cb, NULL);
+	/*sema_init(&log_start_sema, 0); */
+	/*sema_init(&log_stop_sema, 0); */
+	init_completion(&log_start_comp);
+	init_completion(&log_stop_comp);
+
+	ret = __ondiemet_log_req_enq(req);
+	if (ret)
+		return ret;
+
+	/* XXX: blocking may be break by SIGKILL */
+	/*return down_killable(&log_stop_sema);*/
+	return wait_for_completion_killable(&log_stop_comp);
+}
+
+int ondiemet_parse_num(const char *str, unsigned int *value, int len)
+{
+	int ret;
+
+	if (len <= 0)
+		return -1;
+
+	if ((len > 2) &&
+	    ((str[0] == '0') &&
+	     ((str[1] == 'x') || (str[1] == 'X')))) {
+		ret = kstrtouint(str, 16, value);
+	} else {
+		ret = kstrtouint(str, 10, value);
+	}
+
+	if (ret != 0)
+		return -1;
+
+	return 0;
+}
+
+/* XXX: seq_file will output only when a page is filled */
+static ssize_t ondiemet_log_write_store(struct device *dev,
+					struct device_attribute *attr,
+					const char *buf,
+					size_t count)
+{
+	char *plog = NULL;
+
+	plog = kmalloc_array(count, sizeof(*plog), GFP_KERNEL);
+	if (!plog) {
+		/* TODO: use a better error code */
+		return -EINVAL;
+	}
+
+	memcpy(plog, buf, count);
+
+	mutex_lock(&dev->mutex);
+	ondiemet_log_req_enq(plog, strnlen(plog, count), kfree, plog);
+	mutex_unlock(&dev->mutex);
+
+	return count;
+}
+
+static DEVICE_ATTR(ondiemet_log_write, 0664, NULL, ondiemet_log_write_store);
+
+static ssize_t ondiemet_log_run_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int sz;
+
+	mutex_lock(&dev->mutex);
+	sz = snprintf(buf, PAGE_SIZE, "%d\n", ondiemet_trace_run);
+	mutex_unlock(&dev->mutex);
+	return sz;
+}
+
+static ssize_t ondiemet_log_run_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+	int ret;
+	int prev_run_state;
+
+	mutex_lock(&dev->mutex);
+
+	prev_run_state = ondiemet_trace_run;
+
+	if (kstrtoint(buf, 10, &ondiemet_trace_run) != 0)
+		return -EINVAL;
+
+	if (ondiemet_trace_run <= ONDIEMET_LOG_STOP_MODE) {
+		ondiemet_trace_run = ONDIEMET_LOG_STOP_MODE;
+		ondiemet_log_manager_stop();
+
+		if (prev_run_state == ONDIEMET_LOG_DEBUG_MODE)
+			device_remove_file(dev, &dev_attr_ondiemet_log_write);
+	} else if (ondiemet_trace_run == ONDIEMET_LOG_RUN_MODE) {
+		ondiemet_trace_run = ONDIEMET_LOG_RUN_MODE;
+		ondiemet_log_manager_start();
+
+		if (prev_run_state == ONDIEMET_LOG_DEBUG_MODE)
+			device_remove_file(dev, &dev_attr_ondiemet_log_write);
+	} else {
+		ondiemet_trace_run = ONDIEMET_LOG_DEBUG_MODE;
+		ondiemet_log_manager_start();
+
+		if (prev_run_state != ONDIEMET_LOG_DEBUG_MODE) {
+			ret = device_create_file(dev, &dev_attr_ondiemet_log_write);
+			if (ret != 0)
+				pr_debug("[met] can not create device node: ondiemet_log_write\n");
+		}
+	}
+
+	mutex_unlock(&dev->mutex);
+
+	return count;
+}
+
+static DEVICE_ATTR(ondiemet_log_run, 0660, ondiemet_log_run_show, ondiemet_log_run_store);
+
+int ondiemet_log_manager_init(struct device *dev)
+{
+	int ret;
+	struct dentry *d;
+
+	mutex_init(&lock_tracef);
+
+	__ondiemet_log_req_q_init(&ondiemet_log_req_q);
+
+	/*sema_init(&log_start_sema, 0);*/
+	/*sema_init(&log_stop_sema, 0);*/
+	init_completion(&log_start_comp);
+	init_completion(&log_stop_comp);
+
+	dbgfs_met_dir = debugfs_create_dir("ondiemet", NULL);
+	if (!dbgfs_met_dir) {
+		pr_debug("[met] can not create debugfs directory: met\n");
+		return -ENOMEM;
+	}
+
+	mutex_init(&lock_trace_owner_pid);
+
+	d = debugfs_create_file("trace", 0644, dbgfs_met_dir, NULL, &ondiemet_trace_fops);
+	if (!d) {
+		pr_debug("[met] can not create devide node in debugfs: ondiemet_trace\n");
+		return -ENOMEM;
+	}
+
+	ondiemet_trace_run = __ondiemet_log_req_working();
+	ret = device_create_file(dev, &dev_attr_ondiemet_log_run);
+	if (ret != 0) {
+		pr_debug("[met] can not create device node: ondiemet_log_run\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+int ondiemet_log_manager_uninit(struct device *dev)
+{
+	device_remove_file(dev, &dev_attr_ondiemet_log_run);
+	debugfs_remove_recursive(dbgfs_met_dir);
+	return 0;
+}
diff --git a/src/devtools/met-driver/4.4/common/ondiemet_log.h b/src/devtools/met-driver/4.4/common/ondiemet_log.h
new file mode 100644
index 0000000..cfe8be9
--- /dev/null
+++ b/src/devtools/met-driver/4.4/common/ondiemet_log.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _ONDIEMET_LOG_H_
+#define _ONDIEMET_LOG_H_
+
+#include <linux/device.h>
+
+int ondiemet_log_manager_init(struct device *dev);
+int ondiemet_log_manager_uninit(struct device *dev);
+int ondiemet_log_manager_start(void);
+/* Log manager can be reactivated by inserting new requests, i.e., calling ondiemet_log_req_enq() */
+int ondiemet_log_manager_stop(void);
+int ondiemet_log_req_enq(const char *src, size_t num, void (*on_fini_cb) (const void *p),
+			 const void *param);
+
+#endif				/* _ONDIEMET_LOG_H_ */
diff --git a/src/devtools/met-driver/4.4/common/power.c b/src/devtools/met-driver/4.4/common/power.c
new file mode 100644
index 0000000..c57d907
--- /dev/null
+++ b/src/devtools/met-driver/4.4/common/power.c
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/cpufreq.h>
+#include <trace/events/power.h>
+
+#include "power.h"
+#include "met_drv.h"
+#include "met_kernel_symbol.h"
+
+noinline void cpu_frequency(unsigned int frequency, unsigned int cpu_id)
+{
+	/* suppose this symbol is available, otherwise, the met.ko will fail */
+	met_cpu_frequency_symbol(frequency, cpu_id);
+}
+
+void force_power_log(int cpu)
+{
+	struct cpufreq_policy *p;
+
+	if (cpu == POWER_LOG_ALL) {
+		for_each_possible_cpu(cpu) {
+			p = cpufreq_cpu_get(cpu);
+			if (p != NULL) {
+				cpu_frequency(p->cur, cpu);
+				cpufreq_cpu_put(p);
+			} else {
+				cpu_frequency(0, cpu);
+			}
+		}
+	} else {
+		p = cpufreq_cpu_get(cpu);
+		if (p != NULL) {
+			cpu_frequency(p->cur, cpu);
+			cpufreq_cpu_put(p);
+		} else {
+			cpu_frequency(0, cpu);
+		}
+	}
+}
+
+void force_power_log_val(unsigned int frequency, int cpu)
+{
+	cpu_frequency(frequency, cpu);
+}
diff --git a/src/devtools/met-driver/4.4/common/power.h b/src/devtools/met-driver/4.4/common/power.h
new file mode 100644
index 0000000..8a0e8f0
--- /dev/null
+++ b/src/devtools/met-driver/4.4/common/power.h
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _POWER_H_
+#define _POWER_H_
+
+#define POWER_LOG_ALL	-1
+void force_power_log(int cpu);
+void force_power_log_val(unsigned int frequency, int cpu);
+
+#endif				/* _POWER_H_ */
diff --git a/src/devtools/met-driver/4.4/common/sampler.c b/src/devtools/met-driver/4.4/common/sampler.c
new file mode 100644
index 0000000..a050acd
--- /dev/null
+++ b/src/devtools/met-driver/4.4/common/sampler.c
@@ -0,0 +1,714 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/cpu.h>
+#include <linux/sched.h>
+#include <linux/notifier.h>
+#include <linux/module.h>
+#include <linux/irq.h>
+#if 0				/* fix me later, no such file on current tree */
+#include <mach/mt_cpuxgpt.h>
+#endif
+#include <asm/arch_timer.h>
+
+#define	MET_USER_EVENT_SUPPORT
+#include "interface.h"
+#include "sampler.h"
+#include "met_struct.h"
+#include "util.h"
+#include "switch.h"
+#include "trace.h"
+#include "met_drv.h"
+#include "met_tag.h" /* for tracing_mark_write */
+
+#include "cpu_pmu.h" /* for using kernel perf PMU driver */
+#include "cpu_pmu_v2.h" /* for using kernel perf PMU v2 driver */
+#include "met_kernel_symbol.h"
+
+#undef	DEBUG_CPU_NOTIFY
+/* #define DEBUG_CPU_NOTIFY */
+#if	defined(DEBUG_CPU_NOTIFY)
+#ifdef CONFIG_MET_MODULE
+#define	dbg_met_tag_oneshot	met_tag_oneshot_real
+#else
+#define	dbg_met_tag_oneshot	met_tag_oneshot
+#endif /* CONFIG_MET_MODULE */
+#else
+#define	dbg_met_tag_oneshot(class_id, name, value)	({ 0; })
+#endif
+
+static int start;
+static unsigned int online_cpu_map;
+static int curr_polling_cpu;
+static int cpu_related_cnt;
+
+static int pmu_profiling_version = 0;
+
+static DEFINE_PER_CPU(unsigned int, perf_cpuid);
+
+static int preferred_cpu_list[] = { 0, 4, 1, 2, 3, 5, 6, 7 };
+
+int get_pmu_profiling_version()
+{
+	return pmu_profiling_version;
+}
+
+static int calc_preferred_polling_cpu(unsigned int cpu_map)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(preferred_cpu_list); i++) {
+		if (cpu_map & (1 << preferred_cpu_list[i]))
+			return preferred_cpu_list[i];
+	}
+
+	return -1;
+}
+
+static void wq_sync_buffer(struct work_struct *work)
+{
+	int cpu;
+	struct delayed_work *dw = container_of(work, struct delayed_work, work);
+	struct met_cpu_struct *met_cpu_ptr = container_of(dw, struct met_cpu_struct, dwork);
+
+	cpu = smp_processor_id();
+	if (met_cpu_ptr->cpu != cpu) {
+		/* panic("ERROR"); */
+		return;
+	}
+
+	/* sync_samples(cpu); */
+	/* don't re-add the work if we're shutting down */
+	if (met_cpu_ptr->work_enabled)
+		schedule_delayed_work(dw, DEFAULT_TIMER_EXPIRE);
+}
+
+static enum hrtimer_restart met_hrtimer_notify(struct hrtimer *hrtimer)
+{
+	int cpu;
+	int *count;
+	unsigned long long stamp;
+	struct met_cpu_struct *met_cpu_ptr = container_of(hrtimer, struct met_cpu_struct, hrtimer);
+	struct metdevice *c;
+#if	defined(DEBUG_CPU_NOTIFY)
+	char msg[32];
+#endif
+
+	cpu = smp_processor_id();
+#if	defined(DEBUG_CPU_NOTIFY)
+	{
+		char msg[32];
+
+		snprintf(msg, sizeof(msg), "met_hrtimer notify_%d", cpu);
+		dbg_met_tag_oneshot(0, msg, 1);
+	}
+#endif
+
+	if (met_cpu_ptr->cpu != cpu) {
+		/* panic("ERROR2"); */
+		dbg_met_tag_oneshot(0, msg, -3);
+		return HRTIMER_NORESTART;
+	}
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->ondiemet_mode == 0) {
+			if ((c->mode == 0) || (c->timed_polling == NULL))
+				continue;
+		} else if (c->ondiemet_mode == 1) {
+			if ((c->mode == 0) || (c->ondiemet_timed_polling == NULL))
+				continue;
+		} else if (c->ondiemet_mode == 2) {
+			if ((c->mode == 0) || ((c->timed_polling == NULL)
+					       && (c->ondiemet_timed_polling == NULL)))
+				continue;
+		}
+
+		count = per_cpu_ptr(c->polling_count, cpu);
+		if ((*count) > 0) {
+			(*count)--;
+			continue;
+		}
+
+		*(count) = c->polling_count_reload;
+
+		stamp = cpu_clock(cpu);
+
+		if (c->cpu_related == 0) {
+			if (cpu == curr_polling_cpu) {
+				if (c->ondiemet_mode == 0) {
+					c->timed_polling(stamp, 0);
+				} else if (c->ondiemet_mode == 1) {
+					c->ondiemet_timed_polling(stamp, 0);
+				} else if (c->ondiemet_mode == 2) {
+					if (c->timed_polling)
+						c->timed_polling(stamp, 0);
+					if (c->ondiemet_timed_polling)
+						c->ondiemet_timed_polling(stamp, 0);
+				}
+			}
+		} else {
+			if (c->ondiemet_mode == 0) {
+				c->timed_polling(stamp, cpu);
+			} else if (c->ondiemet_mode == 1) {
+				c->ondiemet_timed_polling(stamp, cpu);
+			} else if (c->ondiemet_mode == 2) {
+				if (c->timed_polling)
+					c->timed_polling(stamp, 0);
+				if (c->ondiemet_timed_polling)
+					c->ondiemet_timed_polling(stamp, 0);
+			}
+		}
+	}
+
+	if (met_cpu_ptr->hrtimer_online_check) {
+		online_cpu_map |= (1 << cpu);
+		met_cpu_ptr->hrtimer_online_check = 0;
+		dbg_met_tag_oneshot(0, "met_online check done", cpu);
+		if (calc_preferred_polling_cpu(online_cpu_map) == cpu) {
+			curr_polling_cpu = cpu;
+			dbg_met_tag_oneshot(0, "met_curr polling cpu", cpu);
+		}
+	}
+
+	if (met_cpu_ptr->work_enabled) {
+		hrtimer_forward_now(hrtimer, ns_to_ktime(DEFAULT_HRTIMER_EXPIRE));
+		dbg_met_tag_oneshot(0, msg, 0);
+		return HRTIMER_RESTART;
+	}
+	dbg_met_tag_oneshot(0, msg, 0);
+	return HRTIMER_NORESTART;
+}
+
+static void __met_hrtimer_start(void *unused)
+{
+	struct met_cpu_struct *met_cpu_ptr = NULL;
+	struct hrtimer *hrtimer = NULL;
+	/* struct delayed_work *dw; */
+	struct metdevice *c;
+
+	met_cpu_ptr = this_cpu_ptr(&met_cpu);
+#if	defined(DEBUG_CPU_NOTIFY)
+	{
+		char msg[32];
+
+		snprintf(msg, sizeof(msg), "met_hrtimer status_%d", met_cpu_ptr->cpu);
+		dbg_met_tag_oneshot(0, msg, 1);
+	}
+#endif
+	/*
+	 * do not open HRtimer when EVENT timer enable
+	 */
+	if (!(met_switch.mode & MT_SWITCH_EVENT_TIMER)) {
+		hrtimer = &met_cpu_ptr->hrtimer;
+		/* dw = &met_cpu_ptr->dwork; */
+
+		hrtimer_init(hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+		hrtimer->function = met_hrtimer_notify;
+	}
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->ondiemet_mode == 0) {
+			if ((c->cpu_related) && (c->mode) && (c->start))
+				c->start();
+		} else if (c->ondiemet_mode == 1) {
+			if (((c->cpu_related)) && (c->mode) && (c->ondiemet_start))
+				c->ondiemet_start();
+		} else if (c->ondiemet_mode == 2) {
+			if ((c->cpu_related) && (c->mode) && (c->start))
+				c->start();
+			if (((c->cpu_related)) && (c->mode) && (c->ondiemet_start))
+				c->ondiemet_start();
+		}
+	}
+	/*
+	 * do not open HRtimer when EVENT timer enable
+	 */
+	if (!(met_switch.mode & MT_SWITCH_EVENT_TIMER)) {
+		if (DEFAULT_HRTIMER_EXPIRE) {
+			met_cpu_ptr->work_enabled = 1;
+			/* schedule_delayed_work_on(smp_processor_id(), dw, DEFAULT_TIMER_EXPIRE); */
+			hrtimer_start(hrtimer, ns_to_ktime(DEFAULT_HRTIMER_EXPIRE),
+				      HRTIMER_MODE_REL_PINNED);
+		}
+	}
+}
+
+static void __met_hrtimer_stop(void *unused)
+{
+	struct met_cpu_struct *met_cpu_ptr;
+	struct hrtimer *hrtimer;
+	/* struct delayed_work *dw; */
+	struct metdevice *c;
+
+	met_cpu_ptr = this_cpu_ptr(&met_cpu);
+#if	defined(DEBUG_CPU_NOTIFY)
+	{
+		char msg[32];
+
+		snprintf(msg, sizeof(msg), "met_hrtimer status_%d", met_cpu_ptr->cpu);
+		dbg_met_tag_oneshot(0, msg, 0);
+	}
+#endif
+	/*
+	 * do not open HRtimer when EVENT timer enable
+	 */
+	if (!(met_switch.mode & MT_SWITCH_EVENT_TIMER)) {
+		hrtimer = &met_cpu_ptr->hrtimer;
+		/* dw = &met_cpu_ptr->dwork; */
+
+		met_cpu_ptr->work_enabled = 0;
+		hrtimer_cancel(hrtimer);
+		/* cancel_delayed_work_sync(dw); */
+	}
+	list_for_each_entry(c, &met_list, list) {
+		if (c->ondiemet_mode == 0) {
+			if ((c->cpu_related) && (c->mode) && (c->stop))
+				c->stop();
+		} else if (c->ondiemet_mode == 1) {
+			if ((c->cpu_related) && (c->mode) && (c->ondiemet_stop))
+				c->ondiemet_stop();
+		} else if (c->ondiemet_mode == 2) {
+			if ((c->cpu_related) && (c->mode) && (c->stop))
+				c->stop();
+			if ((c->cpu_related) && (c->mode) && (c->ondiemet_stop))
+				c->ondiemet_stop();
+		}
+	}
+}
+
+static int met_pmu_cpu_notify(struct notifier_block *self, unsigned long action, void *hcpu)
+{
+	struct met_cpu_struct *met_cpu_ptr;
+	struct delayed_work *dw;
+	long cpu = (long)hcpu;
+	int preferred_polling_cpu;
+
+	if (start == 0)
+		return NOTIFY_OK;
+
+#if	defined(DEBUG_CPU_NOTIFY)
+	{
+		char msg[32];
+
+		snprintf(msg, sizeof(msg), "met_cpu notify_%ld", cpu);
+		dbg_met_tag_oneshot(0, msg, action);
+	}
+#elif	defined(PR_CPU_NOTIFY)
+	{
+		char msg[32];
+
+		if (met_cpu_notify) {
+			snprintf(msg, sizeof(msg), "met_cpu notify_%ld", cpu);
+			dbg_met_tag_oneshot(0, msg, action);
+		}
+	}
+#endif
+
+	if (cpu < 0 || cpu >= NR_CPUS)
+		return NOTIFY_OK;
+
+	switch (action) {
+	case CPU_ONLINE:
+	case CPU_ONLINE_FROZEN:
+		met_cpu_ptr = &per_cpu(met_cpu, cpu);
+		met_cpu_ptr->hrtimer_online_check = 1;
+		dbg_met_tag_oneshot(0, "met_online check", cpu);
+
+		if (cpu_related_cnt == 0) {
+			/*pr_debug("%s, %d: curr_polling_cpu is alive = %d\n",
+			 *		__func__, __LINE__, online_cpu_map & (1 << curr_polling_cpu));
+			 */
+
+			online_cpu_map |= (1 << cpu);
+
+			/* check curr_polling_cpu is alive, if it is down,
+			 * start current cpu hrtimer, and change it to be currr_pollling_cpu
+			 */
+			if ((online_cpu_map & (1 << curr_polling_cpu)) == 0) {
+				met_smp_call_function_single_symbol(cpu, __met_hrtimer_start, NULL, 1);
+				curr_polling_cpu = cpu;
+			}
+		} else
+			met_smp_call_function_single_symbol(cpu, __met_hrtimer_start, NULL, 1);
+
+		if (met_cpu_pmu_method != 0) {
+			if (pmu_profiling_version == 1)
+				met_perf_cpupmu_online(cpu);
+#ifdef MET_SUPPORT_CPUPMU_V2
+			else if (pmu_profiling_version == 2)
+				met_perf_cpupmu_online_v2(cpu);
+#endif
+		}
+
+#ifdef CONFIG_CPU_FREQ
+		force_power_log(cpu);
+#endif
+		break;
+
+	case CPU_DOWN_PREPARE:
+	case CPU_DOWN_PREPARE_FROZEN:
+		online_cpu_map &= ~(1 << cpu);
+		dbg_met_tag_oneshot(0, "met_offline cpu", cpu);
+		if (cpu == curr_polling_cpu) {
+			/* pr_debug("%s, %d: curr_polling_cpu %d is down\n",
+			 *		__func__, __LINE__, curr_polling_cpu);
+			 */
+			preferred_polling_cpu = calc_preferred_polling_cpu(online_cpu_map);
+			/* pr_debug("%s, %d: preferred_polling_cpu = %d\n",
+			 *		__func__, __LINE__, preferred_polling_cpu);
+			 */
+			if (preferred_polling_cpu != -1) {
+				curr_polling_cpu = preferred_polling_cpu;
+				dbg_met_tag_oneshot(0, "met_curr polling cpu", curr_polling_cpu);
+
+				if (cpu_related_cnt == 0)
+					/* pr_debug("%s, %d: start cpu %d hrtimer start\n",
+					 *		__func__, __LINE__, curr_polling_cpu);
+					 */
+					met_smp_call_function_single_symbol(curr_polling_cpu, __met_hrtimer_start, NULL, 1);
+			}
+		}
+
+		met_smp_call_function_single_symbol(cpu, __met_hrtimer_stop, NULL, 1);
+		if (met_cpu_pmu_method != 0) {
+			if (pmu_profiling_version == 1) {
+				per_cpu(perf_cpuid, cpu) = cpu;
+				met_smp_call_function_single_symbol(cpu, met_perf_cpupmu_down, (void *)&per_cpu(perf_cpuid, cpu), 1);
+			}
+#ifdef MET_SUPPORT_CPUPMU_V2
+			else if (pmu_profiling_version == 2) {
+				per_cpu(perf_cpuid, cpu) = cpu;
+				met_smp_call_function_single_symbol(cpu, met_perf_cpupmu_down_v2, (void *)&per_cpu(perf_cpuid, cpu), 1);
+			}
+#endif
+		}
+
+		met_cpu_ptr = &per_cpu(met_cpu, cpu);
+		dw = &met_cpu_ptr->dwork;
+		cancel_delayed_work_sync(dw);
+
+		/* sync_samples(cpu); */
+		break;
+
+	case CPU_DOWN_FAILED:
+	case CPU_DOWN_FAILED_FROZEN:
+		met_cpu_ptr = &per_cpu(met_cpu, cpu);
+		met_cpu_ptr->hrtimer_online_check = 1;
+		dbg_met_tag_oneshot(0, "met_online check", cpu);
+
+		met_smp_call_function_single_symbol(cpu, __met_hrtimer_start, NULL, 1);
+		break;
+
+	case CPU_DEAD:
+	case CPU_DEAD_FROZEN:
+#ifdef CONFIG_CPU_FREQ
+		force_power_log_val(0, cpu);
+#endif
+		break;
+	}
+	return NOTIFY_OK;
+}
+
+static struct notifier_block __refdata met_pmu_cpu_notifier = {
+	.notifier_call = met_pmu_cpu_notify,
+};
+
+int sampler_start(void)
+{
+	int ret, cpu;
+	struct met_cpu_struct *met_cpu_ptr;
+	struct metdevice *c;
+	int preferred_polling_cpu;
+
+	met_set_suspend_notify(0);
+
+#ifdef	CONFIG_CPU_FREQ
+	force_power_log(POWER_LOG_ALL);
+#endif
+
+	for_each_possible_cpu(cpu) {
+		met_cpu_ptr = &per_cpu(met_cpu, cpu);
+		met_cpu_ptr->work_enabled = 0;
+		met_cpu_ptr->hrtimer_online_check = 0;
+		hrtimer_init(&met_cpu_ptr->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+		met_cpu_ptr->hrtimer.function = met_hrtimer_notify;
+		INIT_DELAYED_WORK(&met_cpu_ptr->dwork, wq_sync_buffer);
+	}
+
+	start = 0;
+	ret = register_hotcpu_notifier(&met_pmu_cpu_notifier);
+
+	list_for_each_entry(c, &met_list, list) {
+
+		if (try_module_get(c->owner) == 0)
+			continue;
+#ifdef CONFIG_MET_ARM_32BIT
+		if (strcmp(c->name, "cpu") == 0) {
+			if ((c->mode) && (c->start)) {
+				pmu_profiling_version = 1;
+				cpu_related_cnt = 1;
+				if (met_cpu_pmu_method != 0)
+					met_perf_cpupmu_start();
+				else
+					c->start();
+			}
+			continue;
+		}
+#endif
+
+#ifdef MET_SUPPORT_CPUPMU_V2
+		if (strcmp(c->name, "cpu-pmu") == 0) {
+			if ((c->mode) && (c->start)) {
+				pmu_profiling_version = 2;
+				cpu_related_cnt = 1;
+				if (met_cpu_pmu_method != 0)
+					met_perf_cpupmu_start_v2();
+				else
+					c->start();
+			}
+			continue;
+		} else if (strcmp(c->name, "cpu") == 0) {
+			if ((c->mode) && (c->start)) {
+				pmu_profiling_version = 1;
+				cpu_related_cnt = 1;
+				if (met_cpu_pmu_method != 0)
+					met_perf_cpupmu_start();
+				else
+					c->start();
+			}
+			continue;
+		}
+#endif
+		if ((c->mode) && (c->cpu_related == 1))
+			cpu_related_cnt = 1;
+
+		if (c->ondiemet_mode == 0) {
+			if ((!(c->cpu_related)) && (c->mode) && (c->start))
+				c->start();
+		} else if (c->ondiemet_mode == 1) {
+			if ((!(c->cpu_related)) && (c->mode) && (c->ondiemet_start))
+				c->ondiemet_start();
+		} else if (c->ondiemet_mode == 2) {
+			if ((!(c->cpu_related)) && (c->mode) && (c->start))
+				c->start();
+			if ((!(c->cpu_related)) && (c->mode) && (c->ondiemet_start))
+				c->ondiemet_start();
+		}
+	}
+
+	get_online_cpus();
+	online_cpu_map = 0;
+	for_each_online_cpu(cpu) {
+		online_cpu_map |= (1 << cpu);
+	}
+	dbg_met_tag_oneshot(0, "met_online cpu map", online_cpu_map);
+	preferred_polling_cpu = calc_preferred_polling_cpu(online_cpu_map);
+	if (preferred_polling_cpu != -1)
+		curr_polling_cpu = preferred_polling_cpu;
+	dbg_met_tag_oneshot(0, "met_curr polling cpu", curr_polling_cpu);
+	start = 1;
+
+	if (cpu_related_cnt == 0)
+		met_smp_call_function_single_symbol(curr_polling_cpu, __met_hrtimer_start, NULL, 1);
+	else
+		on_each_cpu(__met_hrtimer_start, NULL, 1);
+	put_online_cpus();
+
+	return ret;
+}
+
+void sampler_stop(void)
+{
+	int cpu;
+	struct met_cpu_struct *met_cpu_ptr;
+	struct metdevice *c;
+	struct delayed_work *dw;
+
+	get_online_cpus();
+
+	on_each_cpu(__met_hrtimer_stop, NULL, 1);
+/* for_each_online_cpu(cpu) { */
+	for_each_possible_cpu(cpu) {	/* Just for case */
+		met_cpu_ptr = &per_cpu(met_cpu, cpu);
+		dw = &met_cpu_ptr->dwork;
+		cancel_delayed_work_sync(dw);
+		/* sync_samples(cpu); */
+	}
+
+	start = 0;
+	put_online_cpus();
+
+	unregister_hotcpu_notifier(&met_pmu_cpu_notifier);
+
+	list_for_each_entry(c, &met_list, list) {
+#ifdef CONFIG_MET_ARM_32BIT
+		if (strcmp(c->name, "cpu") == 0) {
+			pmu_profiling_version = 0;
+			if ((c->mode) && (c->stop)) {
+				if (met_cpu_pmu_method != 0)
+					met_perf_cpupmu_stop();
+				else
+					c->stop();
+			}
+			module_put(c->owner);
+			continue;
+		}
+#endif
+
+#ifdef MET_SUPPORT_CPUPMU_V2
+		if (strcmp(c->name, "cpu-pmu") == 0) {
+			pmu_profiling_version = 0;
+			if ((c->mode) && (c->stop)) {
+				if (met_cpu_pmu_method != 0)
+					met_perf_cpupmu_stop_v2();
+				else
+					c->stop();
+			}
+			module_put(c->owner);
+			continue;
+		}
+		else if (strcmp(c->name, "cpu") == 0) {
+			pmu_profiling_version = 0;
+			if ((c->mode) && (c->stop)) {
+				if (met_cpu_pmu_method != 0)
+					met_perf_cpupmu_stop();
+				else
+					c->stop();
+			}
+			module_put(c->owner);
+			continue;
+		}
+#endif
+		if (c->ondiemet_mode == 0) {
+			if ((!(c->cpu_related)) && (c->mode) && (c->stop))
+				c->stop();
+		} else if (c->ondiemet_mode == 1) {
+			if ((!(c->cpu_related)) && (c->mode) && (c->ondiemet_stop))
+				c->ondiemet_stop();
+		} else if (c->ondiemet_mode == 2) {
+			if ((!(c->cpu_related)) && (c->mode) && (c->stop))
+				c->stop();
+			if ((!(c->cpu_related)) && (c->mode) && (c->ondiemet_stop))
+				c->ondiemet_stop();
+		}
+		module_put(c->owner);
+	}
+
+	cpu_related_cnt = 0;
+}
+
+#if 0 /* cann't use static now */
+enum {
+	MET_SUSPEND = 1,
+	MET_RESUME = 2,
+};
+
+static noinline void tracing_mark_write(int op)
+{
+	switch (op) {
+	case MET_SUSPEND:
+		MET_TRACE("C|0|MET_SUSPEND|1");
+		break;
+	case MET_RESUME:
+		MET_TRACE("C|0|MET_SUSPEND|0");
+		break;
+	}
+}
+#endif
+
+int met_hrtimer_suspend(void)
+{
+	struct metdevice *c;
+
+	met_set_suspend_notify(1);
+	/* tracing_mark_write(MET_SUSPEND); */
+	tracing_mark_write(TYPE_MET_SUSPEND, 0, 0, 0, 0, 0);
+	if (start == 0)
+		return 0;
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->suspend)
+			c->suspend();
+	}
+
+	/* get current COUNT */
+	MET_TRACE("TS: %llu GPT: %llX", sched_clock(), arch_counter_get_cntvct());
+	return 0;
+}
+
+void met_hrtimer_resume(void)
+{
+	struct metdevice *c;
+
+	/* get current COUNT */
+	MET_TRACE("TS: %llu GPT: %llX", sched_clock(), arch_counter_get_cntvct());
+
+	/* tracing_mark_write(MET_RESUME); */
+	tracing_mark_write(TYPE_MET_RESUME, 0, 0, 0, 0, 0);
+	if (start == 0)
+		return;
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->resume)
+			c->resume();
+	}
+}
+
+/*
+ * event timer:
+ * register IRQ, sched_switch event to monitor Polling count
+ * count can be printed at any live cpu.
+ */
+void met_event_timer_notify(void)
+{
+	unsigned long long stamp;
+	struct metdevice *c;
+	int cpu = -1;
+
+	if (start == 0)
+		return;
+
+	cpu = smp_processor_id();
+	list_for_each_entry(c, &met_list, list) {
+		stamp = local_clock();
+
+		if (c->prev_stamp == 0)
+			c->prev_stamp = stamp;
+
+		/* Critical Section Start */
+		/* try spinlock to prevent a event print twice between config time interval */
+		if (!spin_trylock(&(c->my_lock)))
+			continue;
+
+		/*
+		 * DEFAULT_HRTIMER_EXPIRE (met_hrtimer_expire):
+		 * sample_rate == 0 --> always print
+		 * sample_rate == 1000 --> print interval larger than 1 ms
+		 */
+		if (DEFAULT_HRTIMER_EXPIRE == 0 || (stamp - c->prev_stamp) < DEFAULT_HRTIMER_EXPIRE) {
+			spin_unlock(&(c->my_lock));
+			continue;
+		}
+
+		c->prev_stamp = stamp;
+		spin_unlock(&(c->my_lock));
+		/* Critical Section End */
+
+		if ((c->mode == 0) || (c->timed_polling == NULL))
+			continue;
+
+		stamp = local_clock();
+		c->timed_polling(stamp, cpu);
+	}
+}
+
diff --git a/src/devtools/met-driver/4.4/common/sampler.h b/src/devtools/met-driver/4.4/common/sampler.h
new file mode 100644
index 0000000..ae780c0
--- /dev/null
+++ b/src/devtools/met-driver/4.4/common/sampler.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _SAMPLER_H_
+#define _SAMPLER_H_
+
+/*
+ * sampling rate: 1ms
+ * log generating rate: 10ms
+ */
+#if 0
+#define DEFAULT_TIMER_EXPIRE (HZ / 100)
+#define DEFAULT_HRTIMER_EXPIRE (TICK_NSEC / 10)
+#else
+extern int met_timer_expire;	/* in jiffies */
+extern int met_hrtimer_expire;	/* in us */
+#define DEFAULT_TIMER_EXPIRE (met_timer_expire)
+#define DEFAULT_HRTIMER_EXPIRE (met_hrtimer_expire)
+#endif
+/*
+ * sampling rate: 10ms
+ * log generating rate: 100ms
+ */
+/* #define DEFAULT_TIMER_EXPIRE (HZ / 10) */
+/* #define DEFAULT_HRTIMER_EXPIRE (TICK_NSEC / 1) */
+
+int met_hrtimer_start(void);
+void met_hrtimer_stop(void);
+int sampler_start(void);
+void sampler_stop(void);
+
+extern struct list_head met_list;
+extern void add_cookie(struct pt_regs *regs, int cpu);
+extern int met_hrtimer_suspend(void);
+extern void met_hrtimer_resume(void);
+extern void met_event_timer_notify(void);
+
+extern int get_pmu_profiling_version(void);
+
+#ifdef CONFIG_CPU_FREQ
+#include "power.h"
+#endif
+
+#endif				/* _SAMPLER_H_ */
diff --git a/src/devtools/met-driver/4.4/common/sspm/ondiemet_sspm.c b/src/devtools/met-driver/4.4/common/sspm/ondiemet_sspm.c
new file mode 100644
index 0000000..18eda17
--- /dev/null
+++ b/src/devtools/met-driver/4.4/common/sspm/ondiemet_sspm.c
@@ -0,0 +1,494 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/delay.h>
+#include <linux/module.h> /* symbol_get */
+
+#include "ondiemet_sspm.h"
+#define MET_USER_EVENT_SUPPORT
+#include "met_drv.h"
+#include "interface.h"
+
+#ifdef CONFIG_MTK_TINYSYS_SSPM_SUPPORT
+#if defined(CONFIG_MTK_GMO_RAM_OPTIMIZE) || defined(CONFIG_MTK_MET_MEM_ALLOC)
+#ifdef CONFIG_MET_ARM_32BIT
+#include <asm/dma-mapping.h> /* arm_coherent_dma_ops */
+#else /* CONFIG_MET_ARM_32BIT */
+#include <linux/dma-mapping.h>
+#endif /* CONFIG_MET_ARM_32BIT */
+#else /* CONFIG_MTK_GMO_RAM_OPTIMIZE */
+#include "sspm_reservedmem.h"
+#include "sspm_reservedmem_define.h"
+#endif /* CONFIG_MTK_GMO_RAM_OPTIMIZE */
+
+dma_addr_t ondiemet_sspm_log_phy_addr;
+void *ondiemet_sspm_log_virt_addr;
+uint32_t ondiemet_sspm_log_size = 0x400000;
+
+/* SSPM_LOG_FILE 0 */
+/* SSPM_LOG_SRAM 1 */
+/* SSPM_LOG_DRAM 2 */
+int sspm_log_mode;
+/* SSPM_RUN_NORMAL mode 0 */
+/* SSPM_RUN_CONTINUOUS mode 1 */
+int sspm_run_mode;
+int met_sspm_log_discard = -1;
+int sspm_log_size = 100;
+
+int sspm_buffer_size;
+int sspm_buf_available;
+EXPORT_SYMBOL(sspm_buf_available);
+int sspm_buf_mapped = -1; /* get buffer by MET itself */
+
+static ssize_t sspm_buffer_size_show(struct device *dev, struct device_attribute *attr, char *buf);
+static DEVICE_ATTR(sspm_buffer_size, 0444, sspm_buffer_size_show, NULL);
+
+static ssize_t sspm_available_show(struct device *dev, struct device_attribute *attr, char *buf);
+static DEVICE_ATTR(sspm_available, 0444, sspm_available_show, NULL);
+
+static ssize_t sspm_log_discard_show(struct device *dev, struct device_attribute *attr, char *buf);
+static DEVICE_ATTR(sspm_log_discard, 0444, sspm_log_discard_show, NULL);
+
+static ssize_t sspm_log_mode_show(struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t sspm_log_mode_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
+static DEVICE_ATTR(sspm_log_mode, 0664, sspm_log_mode_show, sspm_log_mode_store);
+
+static ssize_t sspm_log_size_show(struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t sspm_log_size_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
+static DEVICE_ATTR(sspm_log_size, 0664, sspm_log_size_show, sspm_log_size_store);
+
+
+static ssize_t sspm_run_mode_show(struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t sspm_run_mode_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
+static DEVICE_ATTR(sspm_run_mode, 0664, sspm_run_mode_show, sspm_run_mode_store);
+
+static ssize_t sspm_modules_show(struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t sspm_modules_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
+static DEVICE_ATTR(sspm_modules, 0664, sspm_modules_show, sspm_modules_store);
+
+static ssize_t sspm_op_ctrl_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
+static DEVICE_ATTR(sspm_op_ctrl, 0220, NULL, sspm_op_ctrl_store);
+
+static ssize_t sspm_buffer_size_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int i;
+
+	mutex_lock(&dev->mutex);
+	i = snprintf(buf, PAGE_SIZE, "%d\n", sspm_buffer_size);
+	mutex_unlock(&dev->mutex);
+	return i;
+}
+
+static ssize_t sspm_available_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int i;
+
+	mutex_lock(&dev->mutex);
+	i = snprintf(buf, PAGE_SIZE, "%d\n", 1);
+	mutex_unlock(&dev->mutex);
+	return i;
+}
+
+static ssize_t sspm_log_discard_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int i;
+
+	mutex_lock(&dev->mutex);
+	i = snprintf(buf, PAGE_SIZE, "%d\n", met_sspm_log_discard);
+	mutex_unlock(&dev->mutex);
+	return i;
+}
+
+static ssize_t sspm_log_mode_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int i;
+
+	mutex_lock(&dev->mutex);
+	i = snprintf(buf, PAGE_SIZE, "%d\n", sspm_log_mode);
+	mutex_unlock(&dev->mutex);
+	return i;
+}
+
+static ssize_t sspm_log_mode_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+	int value;
+
+	if (kstrtoint(buf, 0, &value) != 0)
+		return -EINVAL;
+	mutex_lock(&dev->mutex);
+	sspm_log_mode = value;
+	mutex_unlock(&dev->mutex);
+	return count;
+}
+
+
+static ssize_t sspm_log_size_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int i;
+
+	mutex_lock(&dev->mutex);
+	i = snprintf(buf, PAGE_SIZE, "%d\n", sspm_log_size);
+	mutex_unlock(&dev->mutex);
+	return i;
+}
+
+static ssize_t sspm_log_size_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+	int value;
+
+	if (kstrtoint(buf, 0, &value) != 0)
+		return -EINVAL;
+	mutex_lock(&dev->mutex);
+	sspm_log_size = value;
+	mutex_unlock(&dev->mutex);
+	return count;
+}
+
+
+static ssize_t sspm_run_mode_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int i;
+
+	mutex_lock(&dev->mutex);
+	i = snprintf(buf, PAGE_SIZE, "%d\n", sspm_run_mode);
+	mutex_unlock(&dev->mutex);
+	return i;
+}
+
+static ssize_t sspm_run_mode_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+	int value;
+
+	if (kstrtoint(buf, 0, &value) != 0)
+		return -EINVAL;
+	mutex_lock(&dev->mutex);
+	sspm_run_mode = value;
+	mutex_unlock(&dev->mutex);
+	return count;
+}
+
+static ssize_t sspm_op_ctrl_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+	int value;
+
+	if (kstrtoint(buf, 0, &value) != 0)
+		return -EINVAL;
+	mutex_lock(&dev->mutex);
+	if (value == 1)
+		sspm_start();
+	else if (value == 2)
+		sspm_stop();
+	else if (value == 3)
+		sspm_extract();
+	else if (value == 4)
+		sspm_flush();
+	mutex_unlock(&dev->mutex);
+	return count;
+}
+
+static ssize_t sspm_modules_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int i;
+
+	mutex_lock(&dev->mutex);
+	i = snprintf(buf, PAGE_SIZE, "%x\n", ondiemet_module[ONDIEMET_SSPM]);
+	mutex_unlock(&dev->mutex);
+	return i;
+}
+
+static ssize_t sspm_modules_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+	uint32_t value;
+
+	if (kstrtouint(buf, 0, &value) != 0)
+		return -EINVAL;
+	mutex_lock(&dev->mutex);
+	ondiemet_module[ONDIEMET_SSPM] = value;
+	mutex_unlock(&dev->mutex);
+	return count;
+}
+
+int sspm_attr_init(struct device *dev)
+{
+	int ret;
+
+#if defined(CONFIG_MTK_GMO_RAM_OPTIMIZE) || defined(CONFIG_MTK_MET_MEM_ALLOC)
+#ifdef CONFIG_MET_ARM_32BIT
+	struct dma_map_ops *ops = (struct dma_map_ops *)symbol_get(arm_coherent_dma_ops);
+
+	if (ops && ops->alloc) {
+		dev->coherent_dma_mask = DMA_BIT_MASK(32);
+		ondiemet_sspm_log_virt_addr = ops->alloc(dev,
+						ondiemet_sspm_log_size,
+						&ondiemet_sspm_log_phy_addr,
+						GFP_KERNEL,
+						0);
+	}
+#else /* CONFIG_MET_ARM_32BIT */
+	/* dma_alloc */
+	ondiemet_sspm_log_virt_addr = dma_alloc_coherent(dev,
+			ondiemet_sspm_log_size,
+			&ondiemet_sspm_log_phy_addr,
+			GFP_KERNEL);
+#endif /* CONFIG_MET_ARM_32BIT */
+#else /* CONFIG_MTK_GMO_RAM_OPTIMIZE */
+
+	phys_addr_t (*sspm_reserve_mem_get_phys_sym)(unsigned int id) = NULL;
+	phys_addr_t (*sspm_reserve_mem_get_virt_sym)(unsigned int id) = NULL;
+	phys_addr_t (*sspm_reserve_mem_get_size_sym)(unsigned int id) = NULL;
+
+	sspm_reserve_mem_get_phys_sym = (phys_addr_t (*)(unsigned int id))symbol_get(sspm_reserve_mem_get_virt);
+	sspm_reserve_mem_get_virt_sym = (phys_addr_t (*)(unsigned int id))symbol_get(sspm_reserve_mem_get_phys);
+	sspm_reserve_mem_get_size_sym = (phys_addr_t (*)(unsigned int id))symbol_get(sspm_reserve_mem_get_size);
+	if (sspm_reserve_mem_get_phys_sym)
+		ondiemet_sspm_log_virt_addr = (void*)sspm_reserve_mem_get_virt(MET_MEM_ID);
+	if (sspm_reserve_mem_get_virt_sym)
+		ondiemet_sspm_log_phy_addr = sspm_reserve_mem_get_phys(MET_MEM_ID);
+	if (sspm_reserve_mem_get_size_sym)
+		ondiemet_sspm_log_size = sspm_reserve_mem_get_size(MET_MEM_ID);
+#endif /* CONFIG_MTK_GMO_RAM_OPTIMIZE */
+
+	ret = device_create_file(dev, &dev_attr_sspm_buffer_size);
+	if (ret != 0) {
+		pr_debug("can not create device file: sspm_buffer_size\n");
+		return ret;
+	}
+
+	ret = device_create_file(dev, &dev_attr_sspm_available);
+	if (ret != 0) {
+		pr_debug("can not create device file: sspm_available\n");
+		return ret;
+	}
+
+	ret = device_create_file(dev, &dev_attr_sspm_log_discard);
+	if (ret != 0) {
+		pr_debug("can not create device file: sspm_log_discard\n");
+		return ret;
+	}
+	ret = device_create_file(dev, &dev_attr_sspm_log_mode);
+	if (ret != 0) {
+		pr_debug("can not create device file: sspm_log_mode\n");
+		return ret;
+	}
+	ret = device_create_file(dev, &dev_attr_sspm_log_size);
+	if (ret != 0) {
+		pr_debug("can not create device file: sspm_log_size\n");
+		return ret;
+	}
+	ret = device_create_file(dev, &dev_attr_sspm_run_mode);
+	if (ret != 0) {
+		pr_debug("can not create device file: sspm_run_mode\n");
+		return ret;
+	}
+	ret = device_create_file(dev, &dev_attr_sspm_op_ctrl);
+	if (ret != 0) {
+		pr_debug("can not create device file: sspm_op_ctrl\n");
+		return ret;
+	}
+	ret = device_create_file(dev, &dev_attr_sspm_modules);
+	if (ret != 0) {
+		pr_debug("can not create device file: sspm_modules\n");
+		return ret;
+	}
+
+	if (ondiemet_sspm_log_virt_addr != NULL) {
+		start_sspm_ipi_recv_thread();
+		sspm_buf_available = 1;
+		sspm_buffer_size = ondiemet_sspm_log_size;
+	} else {
+		sspm_buf_available = 0;
+		sspm_buffer_size = -1;
+	}
+
+	return 0;
+}
+
+int sspm_attr_uninit(struct device *dev)
+{
+	/* dma_free */
+	if (ondiemet_sspm_log_virt_addr != NULL) {
+#if defined(CONFIG_MTK_GMO_RAM_OPTIMIZE) || defined(CONFIG_MTK_MET_MEM_ALLOC)
+#ifdef CONFIG_MET_ARM_32BIT
+		struct dma_map_ops *ops = (struct dma_map_ops *)symbol_get(arm_coherent_dma_ops);
+
+		if (ops && ops->free) {
+			ops->free(dev,
+				ondiemet_sspm_log_size,
+				ondiemet_sspm_log_virt_addr,
+				ondiemet_sspm_log_phy_addr,
+				0);
+		}
+#else /* CONFIG_MET_ARM_32BIT */
+		dma_free_coherent(dev,
+			ondiemet_sspm_log_size,
+			ondiemet_sspm_log_virt_addr,
+			ondiemet_sspm_log_phy_addr);
+#endif /* CONFIG_MET_ARM_32BIT */
+#endif /* CONFIG_MTK_GMO_RAM_OPTIMIZE */
+		ondiemet_sspm_log_virt_addr = NULL;
+		stop_sspm_ipi_recv_thread();
+	}
+
+	device_remove_file(dev, &dev_attr_sspm_buffer_size);
+	device_remove_file(dev, &dev_attr_sspm_available);
+	device_remove_file(dev, &dev_attr_sspm_log_discard);
+	device_remove_file(dev, &dev_attr_sspm_log_mode);
+	device_remove_file(dev, &dev_attr_sspm_log_size);
+	device_remove_file(dev, &dev_attr_sspm_run_mode);
+	device_remove_file(dev, &dev_attr_sspm_op_ctrl);
+	device_remove_file(dev, &dev_attr_sspm_modules);
+
+	return 0;
+}
+
+#if 0 /* move to sspm_attr_init() */
+void sspm_get_buffer_info(void)
+{
+	if (ondiemet_sspm_log_virt_addr != NULL) {
+		sspm_buf_available = 1;
+		sspm_buffer_size = ondiemet_sspm_log_size;
+	} else {
+		sspm_buf_available = 0;
+		sspm_buffer_size = -1;
+	}
+}
+#endif
+
+extern const char *met_get_platform_name(void);
+void sspm_start(void)
+{
+	int32_t ret = 0;
+	uint32_t rdata;
+	uint32_t ipi_buf[4];
+	const char* platform_name = NULL;
+	unsigned int platform_id = 0;
+	met_sspm_log_discard = -1;
+
+	/* clear DRAM buffer */
+	if (ondiemet_sspm_log_virt_addr != NULL)
+		memset_io((void *)ondiemet_sspm_log_virt_addr, 0, ondiemet_sspm_log_size);
+	else
+		return;
+
+	platform_name = met_get_platform_name();
+	if (platform_name) {
+		char buf[5];
+
+		memset(buf, 0x0, 5);
+		memcpy(buf, &platform_name[2], 4);
+		ret = kstrtouint(buf, 10, &platform_id);
+	}
+
+	/* send DRAM physical address */
+	ipi_buf[0] = MET_MAIN_ID | MET_BUFFER_INFO;
+	ipi_buf[1] = (unsigned int)ondiemet_sspm_log_phy_addr; /* address */
+	if (ret == 0)
+		ipi_buf[2] = platform_id;
+	else
+		ipi_buf[2] = 0;
+	ipi_buf[3] = 0;
+	ret = sspm_ipi_send_sync(IPI_ID_MET, IPI_OPT_WAIT, (void *)ipi_buf, 0, &rdata, 1);
+
+	/* start ondiemet now */
+	ipi_buf[0] = MET_MAIN_ID | MET_OP | MET_OP_START;
+	ipi_buf[1] = ondiemet_module[ONDIEMET_SSPM];
+	ipi_buf[2] = sspm_log_mode;
+	ipi_buf[3] = sspm_run_mode;
+	ret = sspm_ipi_send_sync(IPI_ID_MET, IPI_OPT_WAIT, (void *)ipi_buf, 0, &rdata, 1);
+}
+
+void sspm_stop(void)
+{
+	int32_t ret;
+	uint32_t rdata;
+	uint32_t ipi_buf[4];
+
+	if (sspm_buf_available == 1) {
+		ipi_buf[0] = MET_MAIN_ID|MET_OP|MET_OP_STOP;
+		ipi_buf[1] = 0;
+		ipi_buf[2] = 0;
+		ipi_buf[3] = 0;
+		ret = sspm_ipi_send_sync(IPI_ID_MET, IPI_OPT_WAIT, (void *)ipi_buf, 0, &rdata, 1);
+	}
+}
+
+void sspm_extract(void)
+{
+	int32_t ret;
+	uint32_t rdata;
+	uint32_t ipi_buf[4];
+	int32_t count;
+
+	count = 20;
+	if (sspm_buf_available == 1) {
+		while ((sspm_buffer_dumping == 1) && (count != 0)) {
+			msleep(50);
+			count--;
+		}
+		ipi_buf[0] = MET_MAIN_ID|MET_OP|MET_OP_EXTRACT;
+		ipi_buf[1] = 0;
+		ipi_buf[2] = 0;
+		ipi_buf[3] = 0;
+		ret = sspm_ipi_send_sync(IPI_ID_MET, IPI_OPT_WAIT, (void *)ipi_buf, 0, &rdata, 1);
+	}
+
+	if (sspm_run_mode == SSPM_RUN_NORMAL)
+		ondiemet_module[ONDIEMET_SSPM] = 0;
+}
+
+void sspm_flush(void)
+{
+	int32_t ret;
+	uint32_t rdata;
+	uint32_t ipi_buf[4];
+
+	if (sspm_buf_available == 1) {
+		ipi_buf[0] = MET_MAIN_ID|MET_OP|MET_OP_FLUSH;
+		ipi_buf[1] = 0;
+		ipi_buf[2] = 0;
+		ipi_buf[3] = 0;
+		ret = sspm_ipi_send_sync(IPI_ID_MET, IPI_OPT_WAIT, (void *)ipi_buf, 0, &rdata, 1);
+	}
+
+	if (sspm_run_mode == SSPM_RUN_NORMAL)
+		ondiemet_module[ONDIEMET_SSPM] = 0;
+}
+#else /* CONFIG_MTK_TINYSYS_SSPM_SUPPORT */
+int sspm_buffer_size = -1;
+
+int sspm_attr_init(struct device *dev)
+{
+	return 0;
+}
+
+int sspm_attr_uninit(struct device *dev)
+{
+	return 0;
+}
+
+void sspm_start(void)
+{
+}
+
+void sspm_stop(void)
+{
+}
+
+void sspm_extract(void)
+{
+}
+
+void sspm_flush(void)
+{
+}
+
+#endif /* CONFIG_MTK_TINYSYS_SSPM_SUPPORT */
diff --git a/src/devtools/met-driver/4.4/common/sspm/ondiemet_sspm.h b/src/devtools/met-driver/4.4/common/sspm/ondiemet_sspm.h
new file mode 100644
index 0000000..03836dc
--- /dev/null
+++ b/src/devtools/met-driver/4.4/common/sspm/ondiemet_sspm.h
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __ONDIEMET_SSPM_H
+#define __ONDIEMET_SSPM_H
+
+#ifdef CONFIG_MTK_TINYSYS_SSPM_SUPPORT
+#include "ondiemet.h"
+#include "sspm_ipi.h"
+#include <linux/dma-mapping.h>
+
+/* we may use IPI_ID_PLATFORM for mt6759 to reduce SRAM */
+#ifndef IPI_ID_MET
+/* #define IPI_ID_MET IPI_ID_TST1 */
+#define IPI_ID_MET IPI_ID_PLATFORM
+#endif
+
+/* MET IPI command definition: mbox 0 */
+/* main func ID: bit[31-24]; sub func ID: bit[23-18]; argu 0: bit[17-0] */
+#define MET_MAIN_ID_MASK        0xff000000 /* bit 31 - 24 */
+#define MET_SUB_ID_MASK         0x00fc0000 /* bit 23 - 18 */
+#define MET_ARGU0_MASK          0x0003ffff /* bit 17 - 0 */
+#define FUNC_BIT_SHIFT          18
+#define MID_BIT_SHIFT           9
+#define MET_MAIN_ID             0x06000000
+/* handle argument and attribute */
+#define PROCESS_ARGU            0x01
+#define PROCESS_ATTR            0x02
+#define MODULE_ID_MASK          0x3fe00 /* bit 9 - 17 */
+#define ARGUMENT_MASK           0x01ff  /* bit 0 - 9 */
+
+/* the following command is used for AP to MD32 */
+#define MET_OP            (1 << FUNC_BIT_SHIFT)
+/* argu 0: start: 0x01; stop: 0x02; extract: 0x03 */
+#define MET_OP_START        0x00000001
+#define MET_OP_STOP         0x00000002
+#define MET_OP_EXTRACT      0x00000003
+#define MET_OP_FLUSH        0x00000004
+#define MET_SR            (2 << FUNC_BIT_SHIFT) /* sample rate */
+#define MET_MODULE        (3 << FUNC_BIT_SHIFT) /* module enable/disable */
+#define MET_ARGU          (4 << FUNC_BIT_SHIFT) /* argument passing */
+#define MET_ATTR          (5 << FUNC_BIT_SHIFT) /* attribute passing */
+/* system memory information for on-die-met log data */
+#define MET_BUFFER_INFO   (6 << FUNC_BIT_SHIFT)
+#define MET_TIMESTAMP     (7 << FUNC_BIT_SHIFT) /* timestamp info */
+#define MET_GPT           (8 << FUNC_BIT_SHIFT) /* GPT counter reading */
+#define MET_REQ_AP2MD     (9 << FUNC_BIT_SHIFT) /* user defined command */
+#define MET_RESP_AP2MD    (10 << FUNC_BIT_SHIFT) /* may no need */
+/* mode: bit 15 - 0: */
+/*  Bit 0: MD32 SRAM mode; Bit 1: System DRAM mode */
+/*  value: 0: output to next level of storage; 1: loop in its own storage */
+#define MET_DATA_MODE     (11 << FUNC_BIT_SHIFT) /* log output mode */
+/* start/stop read data into MD32 SRAM buffer; both DMA and met_printf() */
+#define MET_DATA_OP       (12 << FUNC_BIT_SHIFT) /* data read operation */
+/* the following command is used for MD32 to AP */
+#define MET_DUMP_BUFFER   (13 << FUNC_BIT_SHIFT)
+#define MET_REQ_MD2AP     (14 << FUNC_BIT_SHIFT) /* user defined command */
+#define MET_CLOSE_FILE    (15 << FUNC_BIT_SHIFT) /* Inform to close the SD file */
+#define MET_RESP_MD2AP    (16 << FUNC_BIT_SHIFT)
+#define MET_RUN_MODE      (17 << FUNC_BIT_SHIFT)
+
+/* Note: the module ID and its bit pattern should be fixed as below */
+/* DMA based module first */
+enum {
+	MID_PMQOS = 0,
+	MID_VCORE_DVFS,
+	MID_EMI,
+	MID_THERMAL_CPU,
+	MID_WALL_TIME,
+	MID_CPU_DVFS,
+	MID_GPU_DVFS,
+	MID_PTPOD,
+	MID_SPM,
+	MID_PROFILE,
+};
+
+#define ID_PMQOS       (1 << MID_PMQOS)
+#define ID_SMI         (1 << MID_SMI)
+#define ID_EMI         (1 << MID_EMI)
+#define ID_THERMAL_CPU (1 << MID_THERMAL_CPU)
+#define ID_WALL_TIME   (1 << MID_WALL_TIME)
+#define ID_CPU_DVFS    (1 << MID_CPU_DVFS)
+#define ID_GPU_DVFS    (1 << MID_GPU_DVFS)
+#define ID_VCORE_DVFS  (1 << MID_VCORE_DVFS)
+#define ID_PTPOD       (1 << MID_PTPOD)
+#define ID_SPM         (1 << MID_SPM)
+#define ID_PROFILE     (1 << MID_PROFILE)
+
+extern void ondiemet_extract(void);
+extern void ondiemet_stop(void);
+extern void ondiemet_start(void);
+
+extern void start_sspm_ipi_recv_thread(void);
+extern void stop_sspm_ipi_recv_thread(void);
+
+extern unsigned int ondiemet_ipi_buf[];
+
+/* extern phys_addr_t ondiemet_sspm_log_phy_addr, ondiemet_sspm_log_virt_addr; */
+extern dma_addr_t ondiemet_sspm_log_phy_addr;
+
+extern void *ondiemet_sspm_log_virt_addr;
+extern uint32_t ondiemet_sspm_log_size;
+
+extern int ondiemet_attr_init(struct device *dev);
+extern int ondiemet_attr_uninit(struct device *dev);
+extern int met_sspm_log_discard;
+
+#define SSPM_LOG_FILE 0
+#define SSPM_LOG_SRAM 1
+#define SSPM_LOG_DRAM 2
+extern int sspm_log_mode;
+#define SSPM_RUN_NORMAL 0
+#define SSPM_RUN_CONTINUOUS 1
+extern int sspm_run_mode;
+
+/* extern void sspm_get_buffer_info(void); */
+extern int sspm_buf_available;
+extern int sspm_buffer_dumping;
+
+void sspm_flush(void);
+
+#endif /* CONFIG_MTK_TINYSYS_SSPM_SUPPORT */
+#endif /* __ONDIEMET_SSPM_H */
diff --git a/src/devtools/met-driver/4.4/common/sspm/sspm_ipi_handle.c b/src/devtools/met-driver/4.4/common/sspm/sspm_ipi_handle.c
new file mode 100644
index 0000000..161d752
--- /dev/null
+++ b/src/devtools/met-driver/4.4/common/sspm/sspm_ipi_handle.c
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kthread.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/signal.h>
+#include <linux/semaphore.h>
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/syscalls.h>
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/fcntl.h>
+/* #include <asm/uaccess.h> */
+#include <linux/uaccess.h>
+#include <linux/delay.h>
+
+#include "ondiemet_sspm.h"
+#include "ondiemet_log.h"
+#include "interface.h"
+
+static struct ipi_action ondiemet_sspm_isr;
+static uint32_t log_size;
+static uint32_t recv_buf[4];
+static struct task_struct *ondiemet_sspm_recv_task;
+static int sspm_ipi_thread_started;
+int sspm_buffer_dumping;
+int sspm_recv_thread_comp;
+
+void log_done_cb(const void *p)
+{
+	uint32_t ret;
+	uint32_t rdata = 0;
+	uint32_t ipi_buf[4];
+	uint32_t opt = (p != NULL);
+
+	if (opt == 0) {
+		ipi_buf[0] = MET_MAIN_ID | MET_RESP_AP2MD;
+		ipi_buf[1] = MET_DUMP_BUFFER;
+		ipi_buf[2] = 0;
+		ipi_buf[3] = 0;
+		ret = sspm_ipi_send_sync(IPI_ID_MET, IPI_OPT_WAIT, (void *)ipi_buf, 0, &rdata, 1);
+	}
+}
+
+int ondiemet_sspm_recv_thread(void *data)
+{
+	uint32_t rdata = 0, cmd, ret;
+	uint32_t ridx, widx, wlen;
+
+	ondiemet_sspm_isr.data = (void *)recv_buf;
+	ret = sspm_ipi_recv_registration(IPI_ID_TST1, &ondiemet_sspm_isr);
+	do {
+		sspm_ipi_recv_wait(IPI_ID_TST1);
+		if (sspm_recv_thread_comp == 1) {
+			while (!kthread_should_stop())
+				;
+			return 0;
+		}
+		cmd = recv_buf[0] & MET_SUB_ID_MASK;
+		switch (cmd) {
+		case MET_DUMP_BUFFER:	/* mbox 1: start index; 2: size */
+			sspm_buffer_dumping = 1;
+			ridx = recv_buf[1];
+			widx = recv_buf[2];
+			log_size = recv_buf[3];
+			sspm_ipi_send_ack(IPI_ID_TST1, &rdata);
+			if (widx < ridx) {	/* wrapping occurs */
+				wlen = log_size - ridx;
+				ondiemet_log_req_enq((char *)(ondiemet_sspm_log_virt_addr) + (ridx << 2),
+				wlen * 4, log_done_cb, (void *)1);
+				ondiemet_log_req_enq((char *)(ondiemet_sspm_log_virt_addr),
+						     widx * 4, log_done_cb, (void *)0);
+			} else {
+				wlen = widx - ridx;
+				ondiemet_log_req_enq((char *)(ondiemet_sspm_log_virt_addr) + (ridx << 2),
+				wlen * 4, log_done_cb, (void *)0);
+			}
+			break;
+		case MET_CLOSE_FILE:	/* no argument */
+			/* do close file */
+			ridx = recv_buf[1];
+			widx = recv_buf[2];
+			met_sspm_log_discard = recv_buf[3];
+			if (widx < ridx) {	/* wrapping occurs */
+				wlen = log_size - ridx;
+				ondiemet_log_req_enq((char *)(ondiemet_sspm_log_virt_addr) + (ridx << 2),
+				wlen * 4, log_done_cb, (void *)1);
+				ondiemet_log_req_enq((char *)(ondiemet_sspm_log_virt_addr),
+						     widx * 4, log_done_cb, (void *)1);
+			} else {
+				wlen = widx - ridx;
+				ondiemet_log_req_enq((char *)(ondiemet_sspm_log_virt_addr) + (ridx << 2),
+				wlen * 4, log_done_cb, (void *)1);
+			}
+			ret = ondiemet_log_manager_stop();
+			/* pr_debug("MET_CLOSE_FILE: ret=%d log_discard=%d\n", ret, met_sspm_log_discard); */
+			sspm_ipi_send_ack(IPI_ID_TST1, &rdata);
+			if (sspm_run_mode == SSPM_RUN_CONTINUOUS) {
+				/* clear the memory */
+				memset_io((void *)ondiemet_sspm_log_virt_addr, 0,
+					  ondiemet_sspm_log_size);
+				/* re-start ondiemet again */
+				sspm_start();
+			}
+			break;
+		case MET_RESP_MD2AP:
+			sspm_ipi_send_ack(IPI_ID_TST1, &rdata);
+			sspm_buffer_dumping = 0;
+			break;
+		default:
+			sspm_ipi_send_ack(IPI_ID_TST1, &rdata);
+			break;
+		}
+	} while (!kthread_should_stop());
+	return 0;
+}
+
+void start_sspm_ipi_recv_thread(void)
+{
+	if (sspm_ipi_thread_started != 1) {
+		sspm_recv_thread_comp = 0;
+		ondiemet_sspm_recv_task =
+		    kthread_run(ondiemet_sspm_recv_thread, NULL, "ondiemet_sspm_recv");
+		if (IS_ERR(ondiemet_sspm_recv_task))
+			pr_debug("MET: Can not create ondiemet_sspm_recv\n");
+		else
+			sspm_ipi_thread_started = 1;
+	}
+}
+
+void stop_sspm_ipi_recv_thread(void)
+{
+	if (ondiemet_sspm_recv_task) {
+		sspm_recv_thread_comp = 1;
+		sspm_ipi_recv_complete(IPI_ID_TST1);
+		kthread_stop(ondiemet_sspm_recv_task);
+		ondiemet_sspm_recv_task = NULL;
+		sspm_ipi_thread_started = 0;
+		sspm_ipi_recv_unregistration(IPI_ID_TST1);
+	}
+}
+
diff --git a/src/devtools/met-driver/4.4/common/stat.c b/src/devtools/met-driver/4.4/common/stat.c
new file mode 100644
index 0000000..4f5a6f6
--- /dev/null
+++ b/src/devtools/met-driver/4.4/common/stat.c
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/cpumask.h>
+/* #include <linux/fs.h> */
+#include <linux/init.h>
+#include <linux/version.h>
+#include <linux/interrupt.h>
+#include <linux/kernel_stat.h>
+/* #include <linux/proc_fs.h> */
+#include <linux/sched.h>
+/* #include <linux/seq_file.h> */
+/* #include <linux/slab.h> */
+#include <linux/time.h>
+#include <linux/irqnr.h>
+#include <linux/vmalloc.h>
+#include <asm/cputime.h>
+/* #include <asm-generic/cputime.h> */
+#include <linux/tick.h>
+/* #include <linux/jiffies.h> */
+
+#include <asm/page.h>
+#include <linux/slab.h>
+
+#include "stat.h"
+#include "met_drv.h"
+#include "trace.h"
+
+#define MS_STAT_FMT	"%5lu.%06lu"
+#define FMTLX7		",%llx,%llx,%llx,%llx,%llx,%llx,%llx\n"
+#define FMTLX10		",%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx\n"
+/* void ms_st(unsigned long long timestamp, unsigned char cnt, unsigned int *value) */
+noinline void ms_st(unsigned long long timestamp, unsigned char cnt, u64 *value)
+{
+	unsigned long nano_rem = do_div(timestamp, 1000000000);
+
+	switch (cnt) {
+	case 10:
+		MET_TRACE(MS_STAT_FMT FMTLX10, (unsigned long)(timestamp), (nano_rem/1000),
+			value[0], value[1], value[2], value[3], value[4],
+			value[5], value[6], value[7], value[8], value[9]);
+		break;
+	case 7:
+		MET_TRACE(MS_STAT_FMT FMTLX7, (unsigned long)(timestamp), (nano_rem/1000),
+			value[0], value[1], value[2], value[3], value[4],
+			value[5], value[6]);
+		break;
+	}
+}
+
+static void met_stat_start(void)
+{
+	if (get_ctrl_flags() & 1)
+		met_stat.mode = 0;
+}
+
+static void met_stat_stop(void)
+{
+}
+
+static int do_stat(void)
+{
+	return met_stat.mode;
+}
+
+u64 met_usecs_to_cputime64(u64 n)
+{
+#if (NSEC_PER_SEC % HZ) == 0
+	/* Common case, HZ = 100, 128, 200, 250, 256, 500, 512, 1000 etc. */
+	return div_u64(n, NSEC_PER_SEC / HZ);
+#elif (HZ % 512) == 0
+	/* overflow after 292 years if HZ = 1024 */
+	return div_u64(n * HZ / 512, NSEC_PER_SEC / 512);
+#else
+	/*
+	 * Generic case - optimized for cases where HZ is a multiple of 3.
+	 * overflow after 64.99 years, exact for HZ = 60, 72, 90, 120 etc.
+	 */
+	return div_u64(n * 9, (9ull * NSEC_PER_SEC + HZ / 2) / HZ);
+#endif
+}
+
+static u64 get_idle_time(int cpu)
+{
+	u64 idle, idle_time = get_cpu_idle_time_us(cpu, NULL);
+
+	if (idle_time == -1ULL) {
+		/* !NO_HZ so we can rely on cpustat.idle */
+		idle = kcpustat_cpu(cpu).cpustat[CPUTIME_IDLE];
+	} else
+		idle = met_usecs_to_cputime64(idle_time);
+
+	return idle;
+}
+
+static u64 get_iowait_time(int cpu)
+{
+	u64 iowait, iowait_time = get_cpu_iowait_time_us(cpu, NULL);
+
+	if (iowait_time == -1ULL) {
+		/* !NO_HZ so we can rely on cpustat.iowait */
+		iowait = kcpustat_cpu(cpu).cpustat[CPUTIME_IOWAIT];
+	} else
+		iowait = met_usecs_to_cputime64(iowait_time);
+
+	return iowait;
+}
+
+
+static unsigned int stat_os_polling(u64 *value, int i)
+{
+	int j = -1;
+
+	/* Copy values here to work around gcc-2.95.3, gcc-2.96 */
+	value[++j] = cputime64_to_clock_t(kcpustat_cpu(i).cpustat[CPUTIME_USER]);	/* user */
+	value[++j] = cputime64_to_clock_t(kcpustat_cpu(i).cpustat[CPUTIME_NICE]);	/* nice */
+	value[++j] = cputime64_to_clock_t(kcpustat_cpu(i).cpustat[CPUTIME_SYSTEM]);	/* system */
+	value[++j] = cputime64_to_clock_t(get_idle_time(i));	/* idle */
+	value[++j] = cputime64_to_clock_t(get_iowait_time(i));	/* iowait */
+	value[++j] = cputime64_to_clock_t(kcpustat_cpu(i).cpustat[CPUTIME_IRQ]);	/* irq */
+	value[++j] = cputime64_to_clock_t(kcpustat_cpu(i).cpustat[CPUTIME_SOFTIRQ]);	/* softirq */
+	value[++j] = cputime64_to_clock_t(kcpustat_cpu(i).cpustat[CPUTIME_STEAL]);	/* steal */
+	value[++j] = cputime64_to_clock_t(kcpustat_cpu(i).cpustat[CPUTIME_GUEST]);	/* guest */
+	value[++j] = cputime64_to_clock_t(kcpustat_cpu(i).cpustat[CPUTIME_GUEST_NICE]);	/* guest_nice */
+
+	return j + 1;
+}
+
+static void met_stat_polling(unsigned long long stamp, int cpu)
+{
+	unsigned char count;
+	u64 value[10] = {0};
+	/* return; */
+	if (do_stat()) {
+		count = stat_os_polling(value, cpu);
+		if (count)
+			ms_st(stamp, count, value);
+	}
+}
+
+static const char header[] =
+	"met-info [000] 0.0: met_st_header: user,nice,system,idle,iowait,irq,softirq,steal,guest,guest_nice\n";
+
+static const char help[] = "  --stat                                monitor stat\n";
+
+
+static int met_stat_print_help(char *buf, int len)
+{
+	return snprintf(buf, PAGE_SIZE, help);
+}
+
+static int met_stat_print_header(char *buf, int len)
+{
+	return snprintf(buf, PAGE_SIZE, header);
+}
+
+struct metdevice met_stat = {
+	.name = "stat",
+	.type = MET_TYPE_PMU,
+	.cpu_related = 1,
+	.start = met_stat_start,
+	.stop = met_stat_stop,
+	.polling_interval = 30,
+	.timed_polling = met_stat_polling,
+	.print_help = met_stat_print_help,
+	.print_header = met_stat_print_header,
+};
diff --git a/src/devtools/met-driver/4.4/common/stat.h b/src/devtools/met-driver/4.4/common/stat.h
new file mode 100644
index 0000000..8aabc5c
--- /dev/null
+++ b/src/devtools/met-driver/4.4/common/stat.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _STAT_H_
+#define _STAT_H_
+
+#include <linux/device.h>
+
+extern struct metdevice met_stat;
+
+int stat_reg(struct kobject *parent);
+void stat_unreg(void);
+
+void stat_start(void);
+void stat_stop(void);
+void stat_polling(unsigned long long stamp, int cpu);
+
+unsigned int get_ctrl_flags(void);
+
+#endif				/* _STAT_H_ */
diff --git a/src/devtools/met-driver/4.4/common/switch.c b/src/devtools/met-driver/4.4/common/switch.c
new file mode 100644
index 0000000..f5b5bc6
--- /dev/null
+++ b/src/devtools/met-driver/4.4/common/switch.c
@@ -0,0 +1,308 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/* include <asm/percpu.h> */
+#include <trace/events/sched.h>
+#include <linux/module.h>
+#include <trace/events/irq.h>
+#include <trace/events/power.h>
+
+#include "interface.h"
+#include "met_drv.h"
+#include "cpu_pmu.h"
+#include "cpu_pmu_v2.h"
+#include "switch.h"
+#include "sampler.h"
+#include "met_kernel_symbol.h"
+/* #include "trace.h" */
+
+/*
+ * IRQ_TIRGGER and CPU_IDLE_TRIGGER
+ */
+/* #define IRQ_TRIGGER */
+/* #define CPU_IDLE_TRIGGER */
+
+static DEFINE_PER_CPU(unsigned int, first_log);
+
+#ifdef __aarch64__
+/* #include <asm/compat.h> */
+#include <linux/compat.h>
+#endif
+
+noinline void mt_switch(struct task_struct *prev, struct task_struct *next)
+{
+	int cpu;
+	int prev_state = 0, next_state = 0;
+
+#ifdef __aarch64__
+	prev_state = !(is_compat_thread(task_thread_info(prev)));
+	next_state = !(is_compat_thread(task_thread_info(next)));
+#endif
+
+	cpu = smp_processor_id();
+	if (per_cpu(first_log, cpu)) {
+		MET_TRACE("%d, %d, %d, %d\n", prev->pid, prev_state, next->pid, next_state);
+		per_cpu(first_log, cpu) = 0;
+	}
+	if (prev_state != next_state)
+		MET_TRACE("%d, %d, %d, %d\n", prev->pid, prev_state, next->pid, next_state);
+}
+
+
+#if 0 /* move to kernel space */
+MET_DEFINE_PROBE(sched_switch,
+		 TP_PROTO(bool preempt, struct task_struct *prev, struct task_struct *next))
+{
+	/* speedup sched_switch callback handle */
+	if (met_switch.mode == 0)
+		return;
+
+	if (met_switch.mode & MT_SWITCH_EVENT_TIMER)
+		met_event_timer_notify();
+
+	if (met_switch.mode & MT_SWITCH_64_32BIT)
+		mt_switch(prev, next);
+
+	if (met_switch.mode & MT_SWITCH_SCHEDSWITCH) {
+		if (get_pmu_profiling_version() == 1)
+			cpupmu_polling(0, smp_processor_id());
+#ifdef MET_SUPPORT_CPUPMU_V2
+		else if (get_pmu_profiling_version() == 2)
+			cpupmu_polling_v2(0, smp_processor_id());
+#endif
+	}
+}
+#endif
+
+void met_sched_switch(struct task_struct *prev, struct task_struct *next)
+{
+	/* speedup sched_switch callback handle */
+	if (met_switch.mode == 0)
+		return;
+
+	if (met_switch.mode & MT_SWITCH_EVENT_TIMER)
+		met_event_timer_notify();
+
+	if (met_switch.mode & MT_SWITCH_64_32BIT)
+		mt_switch(prev, next);
+
+	if (met_switch.mode & MT_SWITCH_SCHEDSWITCH) {
+		if (get_pmu_profiling_version() == 1)
+			cpupmu_polling(0, smp_processor_id());
+#ifdef MET_SUPPORT_CPUPMU_V2
+		else if (get_pmu_profiling_version() == 2)
+			cpupmu_polling_v2(0, smp_processor_id());
+#endif
+	}
+}
+
+#ifdef IRQ_TRIGGER
+MET_DEFINE_PROBE(irq_handler_entry, TP_PROTO(int irq, struct irqaction *action))
+{
+	if (met_switch.mode & MT_SWITCH_EVENT_TIMER) {
+		met_event_timer_notify();
+		return;
+	}
+}
+#endif
+
+#ifdef CPU_IDLE_TRIGGER
+MET_DEFINE_PROBE(cpu_idle, TP_PROTO(unsigned int state, unsigned int cpu_id))
+{
+	if (met_switch.mode & MT_SWITCH_EVENT_TIMER) {
+		met_event_timer_notify();
+		return;
+	}
+}
+#endif
+
+#ifdef MET_ANYTIME
+/*
+ * create related subfs file node
+ */
+
+static ssize_t default_on_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "1\n");
+}
+
+static struct kobj_attribute default_on_attr = __ATTR(default_on, 0664, default_on_show, NULL);
+static struct kobject *kobj_cpu;
+#endif
+
+static int met_switch_create_subfs(struct kobject *parent)
+{
+	int ret = 0;
+
+	/* register tracepoints */
+#if 0
+	if (MET_REGISTER_TRACE(sched_switch)) {
+		pr_debug("can not register callback of sched_switch\n");
+		return -ENODEV;
+	}
+#else
+	if (met_reg_switch_symbol)
+		ret = met_reg_switch_symbol();
+#endif
+#ifdef CPU_IDLE_TRIGGER
+	if (MET_REGISTER_TRACE(cpu_idle)) {
+		pr_debug("can not register callback of irq_handler_entry\n");
+		return -ENODEV;
+	}
+#endif
+#ifdef IRQ_TRIGGER
+	if (MET_REGISTER_TRACE(irq_handler_entry)) {
+		pr_debug("can not register callback of irq_handler_entry\n");
+		return -ENODEV;
+	}
+#endif
+
+#ifdef MET_ANYTIME
+	/*
+	 * to create default_on file node
+	 * let user space can know we can support MET default on
+	 */
+	kobj_cpu = parent;
+	ret = sysfs_create_file(kobj_cpu, &default_on_attr.attr);
+	if (ret != 0) {
+		pr_debug("Failed to create default_on in sysfs\n");
+		return -1;
+	}
+#endif
+
+	return ret;
+}
+
+
+static void met_switch_delete_subfs(void)
+{
+#ifdef MET_ANYTIME
+	if (kobj_cpu != NULL) {
+		sysfs_remove_file(kobj_cpu, &default_on_attr.attr);
+		kobj_cpu = NULL;
+	}
+#endif
+#ifdef IRQ_TRIGGER
+	MET_UNREGISTER_TRACE(irq_handler_entry);
+#endif
+#ifdef CPU_IDLE_TRIGGER
+	MET_UNREGISTER_TRACE(cpu_idle);
+#endif
+#if 0
+	MET_UNREGISTER_TRACE(sched_switch);
+#else
+	if (met_unreg_switch_symbol)
+		met_unreg_switch_symbol();
+#endif
+
+}
+
+
+static void (*cpu_timed_polling)(unsigned long long stamp, int cpu);
+/* static void (*cpu_tagged_polling)(unsigned long long stamp, int cpu); */
+
+static void met_switch_start(void)
+{
+	int cpu;
+
+	if (met_switch.mode & MT_SWITCH_SCHEDSWITCH) {
+		cpu_timed_polling = met_cpupmu.timed_polling;
+		/* cpu_tagged_polling = met_cpupmu.tagged_polling; */
+		met_cpupmu.timed_polling = NULL;
+		/* met_cpupmu.tagged_polling = NULL; */
+	}
+
+	for_each_possible_cpu(cpu) {
+		per_cpu(first_log, cpu) = 1;
+	}
+
+}
+
+
+static void met_switch_stop(void)
+{
+	int cpu;
+
+	if (met_switch.mode & MT_SWITCH_SCHEDSWITCH) {
+		met_cpupmu.timed_polling = cpu_timed_polling;
+		/* met_cpupmu.tagged_polling = cpu_tagged_polling; */
+	}
+
+	for_each_possible_cpu(cpu) {
+		per_cpu(first_log, cpu) = 0;
+	}
+
+}
+
+
+static int met_switch_process_argument(const char *arg, int len)
+{
+	unsigned int value;
+	/*ex: mxitem is 0x0005, max value should be (5-1) + (5-2) = 0x100 + 0x11 = 7 */
+	unsigned int max_value = ((MT_SWITCH_MX_ITEM * 2) - 3);
+
+
+	if (met_parse_num(arg, &value, len) < 0)
+		goto arg_switch_exit;
+
+	if ((value < 1) || (value > max_value))
+		goto arg_switch_exit;
+
+	met_switch.mode = value;
+	return 0;
+
+arg_switch_exit:
+	met_switch.mode = 0;
+	return -EINVAL;
+}
+
+static const char header[] =
+	"met-info [000] 0.0: met_switch_header: prev_pid,prev_state,next_pid,next_state\n";
+
+static const char help[] =
+"  --switch=mode                         mode:0x1 - output CPUPMU whenever sched_switch\n"
+"                                        mode:0x2 - output Aarch 32/64 state whenever state changed (no CPUPMU)\n"
+"                                        mode:0x4 - force output count at tag_start/tag_end\n"
+"                                        mode:0x8 - task switch timer\n"
+"                                        mode:0xF - mode 0x1 + 0x2 + 04 + 08\n";
+
+static int met_switch_print_help(char *buf, int len)
+{
+	return snprintf(buf, PAGE_SIZE, help);
+}
+
+static int met_switch_print_header(char *buf, int len)
+{
+	int ret = 0;
+
+	ret =
+	    snprintf(buf, PAGE_SIZE, "met-info [000] 0.0: mp_cpu_switch_base: %d\n",
+		     met_switch.mode);
+	if (met_switch.mode & MT_SWITCH_64_32BIT)
+		ret += snprintf(buf + ret, PAGE_SIZE, header);
+
+	return ret;
+}
+
+
+struct metdevice met_switch = {
+	.name = "switch",
+	.type = MET_TYPE_PMU,
+	.create_subfs = met_switch_create_subfs,
+	.delete_subfs = met_switch_delete_subfs,
+	.start = met_switch_start,
+	.stop = met_switch_stop,
+	.process_argument = met_switch_process_argument,
+	.print_help = met_switch_print_help,
+	.print_header = met_switch_print_header,
+};
diff --git a/src/devtools/met-driver/4.4/common/switch.h b/src/devtools/met-driver/4.4/common/switch.h
new file mode 100644
index 0000000..14397d7
--- /dev/null
+++ b/src/devtools/met-driver/4.4/common/switch.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __MT_SWITCH__
+#define __MT_SWITCH__
+/*
+ * =========================
+ * !!!!!!!!!!!NOTICE!!!!!!!!
+ * =========================
+ * MT_SWITCH OPTION must delcare as Mask value
+ * And sort them from smallest to largest
+ * MT_SWITCH_MX_ITEM was used to determine argument range
+*/
+enum {
+	/* =================== */
+	/* user define mt switch event */
+	/* =================== */
+	MT_SWITCH_SCHEDSWITCH = 0x0001,
+	MT_SWITCH_64_32BIT = 0x0002,
+	MT_SWITCH_TAGPOLLING = 0x0004,
+	MT_SWITCH_EVENT_TIMER = 0x0008,
+	/* =================== */
+	MT_SWITCH_MX_ITEM
+};
+
+extern struct metdevice met_switch;
+#endif
diff --git a/src/devtools/met-driver/4.4/common/trace.h b/src/devtools/met-driver/4.4/common/trace.h
new file mode 100644
index 0000000..f259b7a
--- /dev/null
+++ b/src/devtools/met-driver/4.4/common/trace.h
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _TRACE_H_
+#define _TRACE_H_
+
+
+extern void (*mp_cp_ptr)(unsigned long long timestamp,
+	       struct task_struct *task,
+	       unsigned long program_counter,
+	       unsigned long dcookie,
+	       unsigned long offset,
+	       unsigned char cnt, unsigned int *value);
+
+#define MP_FMT1	"%x\n"
+#define MP_FMT2	"%x,%x\n"
+#define MP_FMT3	"%x,%x,%x\n"
+#define MP_FMT4	"%x,%x,%x,%x\n"
+#define MP_FMT5	"%x,%x,%x,%x,%x\n"
+#define MP_FMT6	"%x,%x,%x,%x,%x,%x\n"
+#define MP_FMT7	"%x,%x,%x,%x,%x,%x,%x\n"
+#define MP_FMT8	"%x,%x,%x,%x,%x,%x,%x,%x\n"
+#define MP_FMT9	"%x,%x,%x,%x,%x,%x,%x,%x,%x\n"
+#define MP_FMT10 "%x,%x,%x,%x,%x,%x,%x,%x,%x,%x\n"
+#define MP_FMT11 "%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x\n"
+#define MP_FMT12 "%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x\n"
+#define MP_FMT13 "%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x\n"
+#define MP_FMT14 "%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x\n"
+#define MP_FMT15 "%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x\n"
+
+#define MET_GENERAL_PRINT(FUNC, count, value) \
+do { \
+	switch (count) { \
+	case 1: { \
+		FUNC(MP_FMT1, value[0]); \
+		} \
+		break; \
+	case 2: { \
+		FUNC(MP_FMT2, value[0], value[1]); \
+		} \
+		break; \
+	case 3: { \
+		FUNC(MP_FMT3, value[0], value[1], value[2]); \
+		} \
+		break; \
+	case 4: { \
+		FUNC(MP_FMT4, value[0], value[1], value[2], value[3]); \
+		} \
+		break; \
+	case 5: { \
+		FUNC(MP_FMT5, value[0], value[1], value[2], value[3], value[4]); \
+		} \
+		break; \
+	case 6: { \
+		FUNC(MP_FMT6, value[0], value[1], value[2], value[3], value[4], value[5]); \
+		} \
+		break; \
+	case 7: { \
+		FUNC(MP_FMT7, value[0], value[1], value[2], value[3], value[4], value[5], value[6]); \
+		} \
+		break; \
+	case 8: { \
+		FUNC(MP_FMT8, value[0], value[1], value[2], value[3], value[4], value[5], value[6], value[7]); \
+		} \
+		break; \
+	case 9: { \
+		FUNC(MP_FMT9, value[0], value[1], value[2], value[3], value[4], value[5], value[6], value[7], \
+				value[8]); \
+		} \
+		break; \
+	case 10: { \
+		FUNC(MP_FMT10, value[0], value[1], value[2], value[3], value[4], value[5], value[6], value[7], \
+				value[8], value[9]); \
+		} \
+		break; \
+	case 11: { \
+		FUNC(MP_FMT11, value[0], value[1], value[2], value[3], value[4], value[5], value[6], value[7], \
+				value[8], value[9], value[10]); \
+		} \
+		break; \
+	case 12: { \
+		FUNC(MP_FMT12, value[0], value[1], value[2], value[3], value[4], value[5], value[6], value[7], \
+				value[8], value[9], value[10], value[11]); \
+		} \
+		break; \
+	case 13: { \
+		FUNC(MP_FMT13, value[0], value[1], value[2], value[3], value[4], value[5], value[6], value[7], \
+				value[8], value[9], value[10], value[11], value[12]); \
+		} \
+		break; \
+	case 14: { \
+		FUNC(MP_FMT14, value[0], value[1], value[2], value[3], value[4], value[5], value[6], value[7], \
+				value[8], value[9], value[10], value[11], value[12], value[13]); \
+		} \
+		break; \
+	case 15: { \
+		FUNC(MP_FMT15, value[0], value[1], value[2], value[3], value[4], value[5], value[6], value[7], \
+				value[8], value[9], value[10], value[11], value[12], value[13], value[14]); \
+		} \
+		break; \
+	} \
+} while (0)
+#endif /* _TRACE_H_ */
diff --git a/src/devtools/met-driver/4.4/common/trace_event.c b/src/devtools/met-driver/4.4/common/trace_event.c
new file mode 100644
index 0000000..8e8be42
--- /dev/null
+++ b/src/devtools/met-driver/4.4/common/trace_event.c
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <asm/page.h>
+#include "interface.h"
+#include "met_drv.h"
+
+#ifdef	CONFIG_GPU_TRACEPOINTS
+#include <trace/events/gpu.h>
+
+#define show_secs_from_ns(ns) \
+	({ \
+		u64 t = ns + (NSEC_PER_USEC / 2); \
+		do_div(t, NSEC_PER_SEC); \
+		t; \
+	})
+
+#define show_usecs_from_ns(ns) \
+	({ \
+		u64 t = ns + (NSEC_PER_USEC / 2) ; \
+		u32 rem; \
+		do_div(t, NSEC_PER_USEC); \
+		rem = do_div(t, USEC_PER_SEC); \
+	})
+
+static int event_gpu_registered;
+static int event_gpu_enabled;
+
+noinline void gpu_sched_switch(const char *gpu_name, u64 timestamp,
+				      u32 next_ctx_id, s32 next_prio, u32 next_job_id)
+{
+	MET_TRACE("gpu_name=%s ts=%llu.%06lu next_ctx_id=%lu next_prio=%ld next_job_id=%lu\n",
+		   gpu_name,
+		   (unsigned long long)show_secs_from_ns(timestamp),
+		   (unsigned long)show_usecs_from_ns(timestamp),
+		   (unsigned long)next_ctx_id, (long)next_prio, (unsigned long)next_job_id);
+}
+
+MET_DEFINE_PROBE(gpu_sched_switch, TP_PROTO(const char *gpu_name, u64 timestamp,
+		u32 next_ctx_id, s32 next_prio, u32 next_job_id))
+{
+	gpu_sched_switch(gpu_name, timestamp, next_ctx_id, next_prio, next_job_id);
+}
+
+noinline void gpu_job_enqueue(u32 ctx_id, u32 job_id, const char *type)
+{
+	MET_TRACE("ctx_id=%lu job_id=%lu type=%s",
+		   (unsigned long)ctx_id, (unsigned long)job_id, type);
+}
+
+MET_DEFINE_PROBE(gpu_job_enqueue, TP_PROTO(u32 ctx_id, u32 job_id, const char *type))
+{
+	gpu_job_enqueue(ctx_id, job_id, type);
+}
+#endif
+
+static int reset_driver_stat(void)
+{
+#ifdef	CONFIG_GPU_TRACEPOINTS
+	event_gpu_enabled = 0;
+#endif
+	met_trace_event.mode = 0;
+	return 0;
+}
+
+
+
+static void met_event_start(void)
+{
+#ifdef	CONFIG_GPU_TRACEPOINTS
+	/* register trace event for gpu */
+	do {
+		if (!event_gpu_enabled)
+			break;
+		if (MET_REGISTER_TRACE(gpu_sched_switch)) {
+			pr_debug("can not register callback of gpu_sched_switch\n");
+			break;
+		}
+		if (MET_REGISTER_TRACE(gpu_job_enqueue)) {
+			pr_debug("can not register callback of gpu_job_enqueue\n");
+			MET_UNREGISTER_TRACE(gpu_sched_switch);
+			break;
+		}
+		event_gpu_registered = 1;
+	} while (0);
+#endif
+}
+
+static void met_event_stop(void)
+{
+#ifdef	CONFIG_GPU_TRACEPOINTS
+	/* unregister trace event for gpu */
+	if (event_gpu_registered) {
+		MET_UNREGISTER_TRACE(gpu_job_enqueue);
+		MET_UNREGISTER_TRACE(gpu_sched_switch);
+		event_gpu_registered = 0;
+	}
+#endif
+}
+
+static int met_event_process_argument(const char *arg, int len)
+{
+	int	ret = -1;
+
+#ifdef	CONFIG_GPU_TRACEPOINTS
+	if (strcasecmp(arg, "gpu") == 0) {
+		event_gpu_enabled = 1;
+		met_trace_event.mode = 1;
+		ret = 0;
+	}
+#endif
+
+	return ret;
+}
+
+static const char help[] = "\b"
+#ifdef	CONFIG_GPU_TRACEPOINTS
+	"  --event=gpu                           output gpu trace events\n"
+#endif
+	;
+
+static int met_event_print_help(char *buf, int len)
+{
+	return snprintf(buf, PAGE_SIZE, help);
+}
+
+static const char header[] =
+	"met-info [000] 0.0: met_ftrace_event:"
+#ifdef	CONFIG_GPU_TRACEPOINTS
+	" gpu:gpu_sched_switch gpu:gpu_job_enqueue"
+#endif
+	"\n";
+
+static int met_event_print_header(char *buf, int len)
+{
+	int	ret;
+
+	ret = snprintf(buf, PAGE_SIZE, header);
+	return ret;
+}
+
+struct metdevice met_trace_event = {
+	.name			= "event",
+	.type			= MET_TYPE_PMU,
+	.start			= met_event_start,
+	.stop			= met_event_stop,
+	.reset			= reset_driver_stat,
+	.process_argument	= met_event_process_argument,
+	.print_help		= met_event_print_help,
+	.print_header		= met_event_print_header,
+};
diff --git a/src/devtools/met-driver/4.4/common/util.c b/src/devtools/met-driver/4.4/common/util.c
new file mode 100644
index 0000000..051a3bd
--- /dev/null
+++ b/src/devtools/met-driver/4.4/common/util.c
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "util.h"
+#include <linux/fs.h>
+#include <linux/kernel.h>
+/* #include <asm/uaccess.h> */
+#include <linux/uaccess.h>
+
+#ifdef FILELOG
+
+static char tmp[1000] = { 0 };
+
+ /*TODO*/
+/**
+ * open file
+ * @param name path to open
+ * @return file pointer
+ */
+struct file *open_file(const char *name)
+{
+	struct file *fp = NULL;
+
+	fp = filp_open(name, O_WRONLY | O_APPEND /*| O_TRUNC */  | O_CREAT, 0664);
+	if (unlikely(fp == NULL)) {
+		pr_debug(KERNEL_INFO "can not open result file");
+		return NULL;
+	}
+	return fp;
+}
+
+/**
+ * write to file
+ * @param fp file pointer
+ * @param format format string
+ * @param ... variable-length subsequent arguments
+ */
+void write_file(struct file *fp, const char *format, ...)
+{
+	va_list va;
+	mm_segment_t fs = get_fs();
+
+	va_start(va, format);
+	vsnprintf(tmp, sizeof(tmp), format, va);
+	set_fs(KERNEL_DS);
+	vfs_write(fp, tmp, strlen(tmp), &(fp->f_pos));
+	set_fs(fs);
+	va_end(va);
+}
+
+/**
+ * close file
+ * @param fp file pointer
+ * @return exit code
+ */
+int close_file(struct file *fp)
+{
+	if (likely(fp != NULL)) {
+		filp_close(fp, NULL);
+		fp = NULL;
+		return 0;
+	}
+	pr_debug("cannot close file pointer:%p\n", fp);
+	return -1;
+}
+
+void filelog(char *str)
+{
+	struct file *fp;
+
+	fp = open_file("/data/met.log");
+	if (fp != NULL) {
+		write_file(fp, "%s", str);
+		close_file(fp);
+	}
+}
+
+#endif				/* FILELOG */
diff --git a/src/devtools/met-driver/4.4/common/util.h b/src/devtools/met-driver/4.4/common/util.h
new file mode 100644
index 0000000..5730376
--- /dev/null
+++ b/src/devtools/met-driver/4.4/common/util.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _SRC_UTIL_H_
+#define _SRC_UTIL_H_
+
+/* #define FILELOG 1 */
+
+#ifdef FILELOG
+void filelog(char *str);
+#else
+#define filelog(str)
+#endif
+
+#endif				/* _SRC_UTIL_H_ */
diff --git a/src/devtools/met-driver/4.4/common/v6_pmu_hw.c b/src/devtools/met-driver/4.4/common/v6_pmu_hw.c
new file mode 100644
index 0000000..d5758ed
--- /dev/null
+++ b/src/devtools/met-driver/4.4/common/v6_pmu_hw.c
@@ -0,0 +1,292 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+
+/* include <asm/system.h> */
+#include <linux/smp.h>
+#include "cpu_pmu.h"
+#include "v6_pmu_name.h"
+
+enum ARM_TYPE {
+	ARM1136 = 0xB36,
+	ARM1156 = 0xB56,
+	ARM1176 = 0xB76,
+	CHIP_UNKNOWN = 0xFFF
+};
+
+struct chip_pmu {
+	enum ARM_TYPE type;
+	struct pmu_desc *desc;
+	unsigned int count;
+	const char *cpu_name;
+};
+
+static struct chip_pmu chips[] = {
+	{ARM1136, arm11_pmu_desc, ARM11_PMU_DESC_COUNT, "arm1136"},
+	{ARM1156, arm11_pmu_desc, ARM11_PMU_DESC_COUNT, "arm1156"},
+	{ARM1176, arm11_pmu_desc, ARM11_PMU_DESC_COUNT, "arm1176"},
+};
+static struct chip_pmu chip_unknown = { CHIP_UNKNOWN, NULL, 0, "Unknown CPU" };
+
+#define CHIP_PMU_COUNT (sizeof(chips) / sizeof(struct chip_pmu))
+
+static struct chip_pmu *chip;
+
+/* define V6_PMU_HW_DEBUG */
+#ifdef V6_PMU_HW_DEBUG
+#define v6pmu_hw_debug(fmt, arg...)     pr_debug(fmt, ##arg)
+#else
+#define v6pmu_hw_debug(fmt, arg...)     do {} while (0)
+#endif
+
+#define ARMV6_PMCR_ENABLE               (1 << 0)
+#define ARMV6_PMCR_CTR01_RESET          (1 << 1)
+#define ARMV6_PMCR_CCOUNT_RESET         (1 << 2)
+#define ARMV6_PMCR_CCOUNT_DIV           (1 << 3)
+#define ARMV6_PMCR_COUNT0_IEN           (1 << 4)
+#define ARMV6_PMCR_COUNT1_IEN           (1 << 5)
+#define ARMV6_PMCR_CCOUNT_IEN           (1 << 6)
+#define ARMV6_PMCR_COUNT0_OVERFLOW      (1 << 8)
+#define ARMV6_PMCR_COUNT1_OVERFLOW      (1 << 9)
+#define ARMV6_PMCR_CCOUNT_OVERFLOW      (1 << 10)
+#define ARMV6_PMCR_EVT_COUNT0_SHIFT     20
+#define ARMV6_PMCR_EVT_COUNT0_MASK      (0xFF << ARMV6_PMCR_EVT_COUNT0_SHIFT)
+#define ARMV6_PMCR_EVT_COUNT1_SHIFT     12
+#define ARMV6_PMCR_EVT_COUNT1_MASK      (0xFF << ARMV6_PMCR_EVT_COUNT1_SHIFT)
+
+#define ARMV6_PMCR_OVERFLOWED_MASK \
+	(ARMV6_PMCR_COUNT0_OVERFLOW | ARMV6_PMCR_COUNT1_OVERFLOW | \
+	ARMV6_PMCR_CCOUNT_OVERFLOW)
+
+enum armv6_counters {
+	ARMV6_COUNTER0 = 0,
+	ARMV6_COUNTER1,
+	ARMV6_CYCLE_COUNTER,
+};
+
+static inline unsigned long armv6_pmcr_read(void)
+{
+	u32 val;
+
+	asm volatile ("mrc   p15, 0, %0, c15, c12, 0":"=r" (val));
+	return val;
+}
+
+static inline void armv6_pmcr_write(unsigned long val)
+{
+	asm volatile ("mcr   p15, 0, %0, c15, c12, 0"::"r" (val));
+}
+
+static inline unsigned int armv6_pmu_read_count(unsigned int idx)
+{
+	unsigned long value = 0;
+
+	if (idx == ARMV6_CYCLE_COUNTER)
+		asm volatile ("mrc   p15, 0, %0, c15, c12, 1":"=r" (value));
+	else
+if (idx == ARMV6_COUNTER0)
+	asm volatile ("mrc   p15, 0, %0, c15, c12, 2":"=r" (value));
+	else
+if (idx == ARMV6_COUNTER1)
+	asm volatile ("mrc   p15, 0, %0, c15, c12, 3":"=r" (value));
+
+	return value;
+}
+
+static inline void armv6_pmu_overflow(void)
+{
+	unsigned int val;
+
+	val = armv6_pmcr_read();
+	val |= ARMV6_PMCR_OVERFLOWED_MASK;
+	armv6_pmcr_write(val);
+}
+
+static inline unsigned int armv6_pmu_control_read(void)
+{
+	u32 val;
+
+	asm volatile ("mrc   p15, 0, %0, c15, c12, 0":"=r" (val));
+	return val;
+}
+
+static inline void armv6_pmu_control_write(unsigned int setting)
+{
+	unsigned long val;
+
+	val = armv6_pmcr_read();
+	val |= setting;
+	armv6_pmcr_write(val);
+}
+
+static void armv6_pmu_hw_reset_all(void)
+{
+	unsigned long val;
+
+	val = armv6_pmcr_read();
+	val &= ~ARMV6_PMCR_ENABLE;	/* disable all counters */
+	val |= (ARMV6_PMCR_CTR01_RESET | ARMV6_PMCR_CCOUNT_RESET);	/* reset CCNT, PMNC1/2 counter to zero */
+	armv6_pmcr_write(val);
+
+	armv6_pmu_overflow();
+}
+
+static void armv6pmu_enable_event(int idx, unsigned short config)
+{
+	unsigned long val, mask, evt;
+
+	if (idx == ARMV6_CYCLE_COUNTER) {
+		mask = 0;
+		evt = ARMV6_PMCR_CCOUNT_IEN;
+	} else if (idx == ARMV6_COUNTER0) {
+		mask = ARMV6_PMCR_EVT_COUNT0_MASK;
+		evt = (config << ARMV6_PMCR_EVT_COUNT0_SHIFT) | ARMV6_PMCR_COUNT0_IEN;
+	} else if (idx == ARMV6_COUNTER1) {
+		mask = ARMV6_PMCR_EVT_COUNT1_MASK;
+		evt = (config << ARMV6_PMCR_EVT_COUNT1_SHIFT) | ARMV6_PMCR_COUNT1_IEN;
+	} else {
+		pr_debug("invalid counter number (%d)\n", idx);
+		return;
+	}
+
+	/*
+	 * Mask out the current event and set the counter to count the event
+	 * that we're interested in.
+	 */
+	val = armv6_pmcr_read();
+	val &= ~mask;
+	val |= evt;
+	armv6_pmcr_write(val);
+}
+
+static int armv6_pmu_hw_get_event_desc(int i, int event, char *event_desc)
+{
+	if (event_desc == NULL)
+		return -1;
+
+	for (i = 0; i < chip->count; i++) {
+		if (chip->desc[i].event == event) {
+			strncpy(event_desc, chip->desc[i].name, MXSIZE_PMU_DESC - 1);
+			break;
+		}
+	}
+
+	if (i == chip->count)
+		return -1;
+
+	return 0;
+}
+
+static int armv6_pmu_hw_check_event(struct met_pmu *pmu, int idx, int event)
+{
+	int i;
+
+	/* Check if event is duplicate */
+	for (i = 0; i < idx; i++) {
+		if (pmu[i].event == event)
+			break;
+	}
+	if (i < idx) {
+		/* pr_debug("++++++ found duplicate event 0x%02x i=%d\n", event, i); */
+		return -1;
+	}
+
+	for (i = 0; i < chip->count; i++) {
+		if (chip->desc[i].event == event)
+			break;
+	}
+
+	if (i == chip->count)
+		return -1;
+
+	return 0;
+}
+
+static void armv6_pmu_hw_start(struct met_pmu *pmu, int count)
+{
+	int i;
+	int generic = count - 1;
+
+	armv6_pmu_hw_reset_all();
+
+	for (i = 0; i < generic; i++) {
+		if (pmu[i].mode == MODE_POLLING)
+			armv6pmu_enable_event(i, pmu[i].event);
+	}
+
+	if (pmu[count - 1].mode == MODE_POLLING)
+		armv6pmu_enable_event(2, pmu[2].event);
+
+	armv6_pmu_control_write(ARMV6_PMCR_ENABLE);
+}
+
+static void armv6_pmu_hw_stop(int count)
+{
+	armv6_pmu_hw_reset_all();
+}
+
+static unsigned int armv6_pmu_hw_polling(struct met_pmu *pmu, int count, unsigned int *pmu_value)
+{
+	int i, cnt = 0;
+	int generic = count - 1;
+
+	for (i = 0; i < generic; i++) {
+		if (pmu[i].mode == MODE_POLLING) {
+			pmu_value[cnt] = armv6_pmu_read_count(i);
+			cnt++;
+		}
+	}
+
+	if (pmu[count - 1].mode == MODE_POLLING) {
+		pmu_value[cnt] = armv6_pmu_read_count(2);
+		cnt++;
+	}
+
+	armv6_pmu_control_write(ARMV6_PMCR_ENABLE | ARMV6_PMCR_CTR01_RESET |
+				ARMV6_PMCR_CCOUNT_RESET);
+
+	return cnt;
+}
+
+struct cpu_pmu_hw armv6_pmu = {
+	.name = "armv6_pmu",
+	.get_event_desc = armv6_pmu_hw_get_event_desc,
+	.check_event = armv6_pmu_hw_check_event,
+	.start = armv6_pmu_hw_start,
+	.stop = armv6_pmu_hw_stop,
+	.polling = armv6_pmu_hw_polling,
+};
+
+struct cpu_pmu_hw *v6_cpu_pmu_hw_init(int typeid)
+{
+	int i;
+
+	for (i = 0; i < CHIP_PMU_COUNT; i++) {
+		if (chips[i].type == typeid) {
+			chip = &(chips[i]);
+
+			break;
+		}
+	}
+
+	if (chip == NULL) {
+		chip = &chip_unknown;
+
+		return NULL;
+	}
+
+	armv6_pmu.nr_cnt = 3;
+	armv6_pmu.cpu_name = chip->cpu_name;
+
+	return &armv6_pmu;
+}
diff --git a/src/devtools/met-driver/4.4/common/v6_pmu_hw.h b/src/devtools/met-driver/4.4/common/v6_pmu_hw.h
new file mode 100644
index 0000000..a532f13
--- /dev/null
+++ b/src/devtools/met-driver/4.4/common/v6_pmu_hw.h
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+
+#ifndef __V6_PMU_HW_H__
+#define __V6_PMU_HW_H__
+
+extern struct cpu_pmu_hw armv6_pmu;
+extern struct cpu_pmu_hw *v6_cpu_pmu_hw_init(int typeid);
+
+#endif
diff --git a/src/devtools/met-driver/4.4/common/v6_pmu_name.h b/src/devtools/met-driver/4.4/common/v6_pmu_name.h
new file mode 100644
index 0000000..c1cdd91
--- /dev/null
+++ b/src/devtools/met-driver/4.4/common/v6_pmu_name.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+
+#ifndef _V6_PMU_NAME_H_
+#define _V6_PMU_NAME_H_
+
+/* ARM11 */
+struct pmu_desc arm11_pmu_desc[] = {
+	{0x00, "ICACHE_MISS"},
+	{0x01, "IBUF_STALL"},
+	{0x02, "DDEP_STALL"},
+	{0x03, "ITLB_MISS"},
+	{0x04, "DTLB_MISS"},
+	{0x05, "BR_EXEC"},
+	{0x06, "BR_MISPREDICT"},
+	{0x07, "CPU_INST"},
+	{0x09, "DCACHE_HIT"},
+	{0x0A, "L1D_CACHE"},
+	{0x0B, "L1D_CACHE_REFILL"},
+	{0x0C, "DCACHE_WBACK"},
+	{0x0D, "SW_PC_CHANGE"},
+	{0x0F, "MAIN_TLB_MISS"},
+	{0x10, "EXPL_D_ACCESS"},
+	{0x11, "LSU_FULL_STALL"},
+	{0x12, "WBUF_DRAINED"},
+	{0xFF, "CPU_CYCLES"},
+	{0x20, "NOP"},
+};
+
+#define ARM11_PMU_DESC_COUNT (sizeof(arm11_pmu_desc) / sizeof(struct pmu_desc))
+
+#endif				/* _V6_PMU_NAME_H_ */
diff --git a/src/devtools/met-driver/4.4/common/v7_pmu_hw.c b/src/devtools/met-driver/4.4/common/v7_pmu_hw.c
new file mode 100644
index 0000000..161bf22
--- /dev/null
+++ b/src/devtools/met-driver/4.4/common/v7_pmu_hw.c
@@ -0,0 +1,303 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/* include <asm/system.h> */
+#include <linux/smp.h>
+#include "cpu_pmu.h"
+#include "v6_pmu_hw.h"
+#include "v7_pmu_name.h"
+#include "v8_pmu_name.h"	/* for 32-bit build of arm64 cpu */
+
+#define ARMV7_PMCR_E		(1 << 0)	/* enable all counters */
+#define ARMV7_PMCR_P		(1 << 1)
+#define ARMV7_PMCR_C		(1 << 2)
+#define ARMV7_PMCR_D		(1 << 3)
+#define ARMV7_PMCR_X		(1 << 4)
+#define ARMV7_PMCR_DP		(1 << 5)
+#define ARMV7_PMCR_N_SHIFT		11	/* Number of counters supported */
+#define ARMV7_PMCR_N_MASK		0x1f
+#define ARMV7_PMCR_MASK			0x3f	/* mask for writable bits */
+
+
+enum ARM_TYPE {
+	CORTEX_A7 = 0xC07,
+	CORTEX_A9 = 0xC09,
+	CORTEX_A12 = 0xC0D,
+	CORTEX_A15 = 0xC0F,
+	CORTEX_A17 = 0xC0E,
+	CORTEX_A53 = 0xD03,
+	CORTEX_A57 = 0xD07,
+	CHIP_UNKNOWN = 0xFFF
+};
+
+struct chip_pmu {
+	enum ARM_TYPE type;
+	struct pmu_desc *desc;
+	unsigned int count;
+	const char *cpu_name;
+};
+
+static struct chip_pmu chips[] = {
+	{CORTEX_A7, a7_pmu_desc, A7_PMU_DESC_COUNT, "Cortex-A7"},
+	{CORTEX_A9, a9_pmu_desc, A9_PMU_DESC_COUNT, "Cortex-A9"},
+	{CORTEX_A12, a7_pmu_desc, A7_PMU_DESC_COUNT, "Cortex-A12"},
+	{CORTEX_A15, a7_pmu_desc, A7_PMU_DESC_COUNT, "Cortex-A15"},
+	{CORTEX_A17, a7_pmu_desc, A7_PMU_DESC_COUNT, "Cortex-A17"},
+	{CORTEX_A53, a53_pmu_desc, A53_PMU_DESC_COUNT, "Cortex-A53"},
+	{CORTEX_A57, a7_pmu_desc, A7_PMU_DESC_COUNT, "Cortex-A57"},
+};
+static struct chip_pmu chip_unknown = { CHIP_UNKNOWN, NULL, 0, "Unknown CPU" };
+
+#define CHIP_PMU_COUNT (sizeof(chips) / sizeof(struct chip_pmu))
+
+static struct chip_pmu *chip;
+
+static enum ARM_TYPE armv7_get_ic(void)
+{
+	unsigned int value;
+	/* Read Main ID Register */
+	asm volatile ("mrc p15, 0, %0, c0, c0, 0":"=r" (value));
+
+	value = (value & 0xffff) >> 4;	/* primary part number */
+	return value;
+}
+
+static inline void armv7_pmu_counter_select(unsigned int idx)
+{
+	asm volatile ("mcr p15, 0, %0, c9, c12, 5"::"r" (idx));
+	isb();
+}
+
+static inline void armv7_pmu_type_select(unsigned int idx, unsigned int type)
+{
+	armv7_pmu_counter_select(idx);
+	asm volatile ("mcr p15, 0, %0, c9, c13, 1"::"r" (type));
+}
+
+static inline unsigned int armv7_pmu_read_count(unsigned int idx)
+{
+	unsigned int value;
+
+	if (idx == 31) {
+		asm volatile ("mrc p15, 0, %0, c9, c13, 0":"=r" (value));
+	} else {
+		armv7_pmu_counter_select(idx);
+		asm volatile ("mrc p15, 0, %0, c9, c13, 2":"=r" (value));
+	}
+	return value;
+}
+
+static inline void armv7_pmu_write_count(int idx, u32 value)
+{
+	if (idx == 31) {
+		asm volatile ("mcr p15, 0, %0, c9, c13, 0"::"r" (value));
+	} else {
+		armv7_pmu_counter_select(idx);
+		asm volatile ("mcr p15, 0, %0, c9, c13, 2"::"r" (value));
+	}
+}
+
+static inline void armv7_pmu_enable_count(unsigned int idx)
+{
+	asm volatile ("mcr p15, 0, %0, c9, c12, 1"::"r" (1 << idx));
+}
+
+static inline void armv7_pmu_disable_count(unsigned int idx)
+{
+	asm volatile ("mcr p15, 0, %0, c9, c12, 2"::"r" (1 << idx));
+}
+
+static inline void armv7_pmu_enable_intr(unsigned int idx)
+{
+	asm volatile ("mcr p15, 0, %0, c9, c14, 1"::"r" (1 << idx));
+}
+
+static inline void armv7_pmu_disable_intr(unsigned int idx)
+{
+	asm volatile ("mcr p15, 0, %0, c9, c14, 2"::"r" (1 << idx));
+}
+
+static inline unsigned int armv7_pmu_overflow(void)
+{
+	unsigned int val;
+
+	asm volatile ("mrc p15, 0, %0, c9, c12, 3":"=r" (val));	/* read */
+	asm volatile ("mcr p15, 0, %0, c9, c12, 3"::"r" (val));
+	return val;
+}
+
+static inline unsigned int armv7_pmu_control_read(void)
+{
+	u32 val;
+
+	asm volatile ("mrc p15, 0, %0, c9, c12, 0":"=r" (val));
+	return val;
+}
+
+static inline void armv7_pmu_control_write(unsigned int val)
+{
+	val &= ARMV7_PMCR_MASK;
+	isb();
+	asm volatile ("mcr p15, 0, %0, c9, c12, 0"::"r" (val));
+}
+
+static int armv7_pmu_hw_get_counters(void)
+{
+	int count = armv7_pmu_control_read();
+	/* N, bits[15:11] */
+	count = ((count >> ARMV7_PMCR_N_SHIFT) & ARMV7_PMCR_N_MASK);
+	return count;
+}
+
+static void armv7_pmu_hw_reset_all(int generic_counters)
+{
+	int i;
+
+	armv7_pmu_control_write(ARMV7_PMCR_C | ARMV7_PMCR_P);
+	/* generic counter */
+	for (i = 0; i < generic_counters; i++) {
+		armv7_pmu_disable_intr(i);
+		armv7_pmu_disable_count(i);
+	}
+	/* cycle counter */
+	armv7_pmu_disable_intr(31);
+	armv7_pmu_disable_count(31);
+	armv7_pmu_overflow();	/* clear overflow */
+}
+
+static int armv7_pmu_hw_get_event_desc(int i, int event, char *event_desc)
+{
+	if (event_desc == NULL)
+		return -1;
+
+	for (i = 0; i < chip->count; i++) {
+		if (chip->desc[i].event == event) {
+			strncpy(event_desc, chip->desc[i].name, MXSIZE_PMU_DESC - 1);
+			break;
+		}
+	}
+	if (i == chip->count)
+		return -1;
+
+	return 0;
+}
+
+static int armv7_pmu_hw_check_event(struct met_pmu *pmu, int idx, int event)
+{
+	int i;
+
+	/* Check if event is duplicate */
+	for (i = 0; i < idx; i++) {
+		if (pmu[i].event == event)
+			break;
+	}
+	if (i < idx) {
+		/* pr_debug("++++++ found duplicate event 0x%02x i=%d\n", event, i); */
+		return -1;
+	}
+
+	for (i = 0; i < chip->count; i++) {
+		if (chip->desc[i].event == event)
+			break;
+	}
+
+	if (i == chip->count)
+		return -1;
+
+	return 0;
+}
+
+static void armv7_pmu_hw_start(struct met_pmu *pmu, int count)
+{
+	int i;
+	int generic = count - 1;
+
+	armv7_pmu_hw_reset_all(generic);
+	for (i = 0; i < generic; i++) {
+		if (pmu[i].mode == MODE_POLLING) {
+			armv7_pmu_type_select(i, pmu[i].event);
+			armv7_pmu_enable_count(i);
+		}
+	}
+	if (pmu[count - 1].mode == MODE_POLLING) {	/* cycle counter */
+		armv7_pmu_enable_count(31);
+	}
+	armv7_pmu_control_write(ARMV7_PMCR_E);
+}
+
+static void armv7_pmu_hw_stop(int count)
+{
+	int generic = count - 1;
+
+	armv7_pmu_hw_reset_all(generic);
+}
+
+static unsigned int armv7_pmu_hw_polling(struct met_pmu *pmu, int count, unsigned int *pmu_value)
+{
+	int i, cnt = 0;
+	int generic = count - 1;
+
+	for (i = 0; i < generic; i++) {
+		if (pmu[i].mode == MODE_POLLING) {
+			pmu_value[cnt] = armv7_pmu_read_count(i);
+			cnt++;
+		}
+	}
+	if (pmu[count - 1].mode == MODE_POLLING) {
+		pmu_value[cnt] = armv7_pmu_read_count(31);
+		cnt++;
+	}
+	armv7_pmu_control_write(ARMV7_PMCR_C | ARMV7_PMCR_P | ARMV7_PMCR_E);
+
+	return cnt;
+}
+
+
+struct cpu_pmu_hw armv7_pmu = {
+	.name = "armv7_pmu",
+	.get_event_desc = armv7_pmu_hw_get_event_desc,
+	.check_event = armv7_pmu_hw_check_event,
+	.start = armv7_pmu_hw_start,
+	.stop = armv7_pmu_hw_stop,
+	.polling = armv7_pmu_hw_polling,
+};
+
+struct cpu_pmu_hw *cpu_pmu_hw_init(void)
+{
+	int i;
+	enum ARM_TYPE type;
+	struct cpu_pmu_hw *pmu = NULL;
+
+	type = armv7_get_ic();
+	for (i = 0; i < CHIP_PMU_COUNT; i++) {
+		if (chips[i].type == type) {
+			chip = &(chips[i]);
+			break;
+		}
+	}
+
+	if (chip != NULL) {
+		armv7_pmu.nr_cnt = armv7_pmu_hw_get_counters() + 1;
+		armv7_pmu.cpu_name = chip->cpu_name;
+		pmu = &armv7_pmu;
+	} else {
+		pmu = v6_cpu_pmu_hw_init(type);
+	}
+
+	if ((chip == NULL) && (pmu == NULL)) {
+		chip = &chip_unknown;
+		return NULL;
+	}
+
+	return pmu;
+}
diff --git a/src/devtools/met-driver/4.4/common/v7_pmu_name.h b/src/devtools/met-driver/4.4/common/v7_pmu_name.h
new file mode 100644
index 0000000..3219cd9
--- /dev/null
+++ b/src/devtools/met-driver/4.4/common/v7_pmu_name.h
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _V7_PMU_NAME_H_
+#define _V7_PMU_NAME_H_
+
+/* Cortex-A7 */
+struct pmu_desc a7_pmu_desc[] = {
+	{0x00, "SW_INCR"},
+	{0x01, "L1I_CACHE_REFILL"},
+	{0x02, "L1I_TLB_REFILL"},
+	{0x03, "L1D_CACHE_REFILL"},
+	{0x04, "L1D_CACHE"},
+	{0x05, "L1D_TLB_REFILL"},
+	{0x06, "LD_RETIRED"},
+	{0x07, "ST_RETIRED"},
+	/* {0x08, "INST_RETIRED"}, */
+	{0x08, "CPU_INST"},
+	{0x09, "EXC_TAKEN"},
+	{0x0A, "EXC_RETURN"},
+	{0x0B, "CID_WRITE_RETIRED"},
+	{0x0C, "PC_WRITE_RETIRED"},
+	{0x0D, "BR_IMMED_RETIRED"},
+	{0x0E, "BR_RETURN_RETIRED"},
+	{0x0F, "UNALIGNED_LDST_RETIRED"},
+	{0x10, "BR_MIS_PRED"},
+	{0x12, "BR_PRED"},
+	{0x13, "MEM_ACCESS"},
+	{0x14, "L1I_CACHE"},
+	{0x15, "L1D_CACHE_WB"},
+	{0x16, "L2D_CACHE"},
+	{0x17, "L2D_CACHE_REFILL"},
+	{0x18, "L2D_CACHE_WB"},
+	{0x19, "BUS_ACCESS"},
+	{0x1D, "BUS_CYCLES"},
+	{0x60, "BUS_READ_ACCESS"},
+	{0x61, "BUS_WRITE_ACCESS"},
+	{0x86, "IRQ_EXC_TAKEN"},
+	{0x87, "FIQ_EXC_TAKEN"},
+	{0xC0, "EXT_MEM_REQ"},
+	{0xC1, "NO_CACHE_EXT_MEM_REQ"},
+	{0xC2, "PREFETCH_LINEFILL"},
+	{0xC3, "PREFETCH_LINEFILL_DROPPED"},
+	{0xC4, "ENT_READ_ALLOC_MODE"},
+	{0xC5, "READ_ALLOC_MODE"},
+	{0xC7, "ETM_EXT_OUT0"},
+	{0xC8, "ETM_EXT_OUT1"},
+	{0xC9, "DATA_WRITE_STALL"},
+	{0xCA, "DATA_READ_SNOOP_CLUSTER"},
+	{0xFF, "CPU_CYCLES"}
+};
+
+/* Cortex-A9 */
+struct pmu_desc a9_pmu_desc[] = {
+	{0x00, "SW_INCR"},
+	{0x01, "L1I_CACHE_REFILL"},
+	{0x02, "L1I_TLB_REFILL"},
+	{0x03, "L1D_CACHE_REFILL"},
+	{0x04, "L1D_CACHE"},
+	{0x05, "L1D_TLB_REFILL"},
+	{0x06, "LD_RETIRED"},
+	{0x07, "ST_RETIRED"},
+	{0x09, "EXC_TAKEN"},
+	{0x0A, "EXC_RETURN"},
+	{0x0B, "CID_WRITE_RETIRED"},
+	{0x0C, "PC_WRITE_RETIRED"},
+	{0x0D, "BR_IMMED_RETIRED"},
+	{0x0F, "UNALIGNED_LDST_RETIRED"},
+	{0x10, "BR_MIS_PRED"},
+	{0x12, "BR_PRED"},
+	{0x40, "JAVA_BC_EXEC"},
+	{0x41, "SW_JAVA_BC_EXEC"},
+	{0x42, "JAZELLE_BB_EXEC"},
+	{0x50, "CO_LF_MISS"},
+	{0x51, "CO_LF_HIT"},
+	{0x60, "ICACHE_DEP_STALL"},
+	{0x61, "DCACHE_DEP_STALL"},
+	{0x62, "M_TLB_STALL"},
+	{0x63, "STREX_PASSED"},
+	{0x64, "STREX_FAILED"},
+	{0x65, "DATA_EVICT"},
+	{0x66, "ISSUE_NO_DISP"},
+	{0x67, "ISSUE_EMPTY"},
+	/* {0x68, "INS_RENAME"}, */
+	{0x68, "CPU_INST"},
+	{0x6E, "PRED_FN_RET"},
+	{0x70, "MAIN_EXEC_INST"},
+	{0x71, "SEC_EXEC_INST"},
+	{0x72, "LOAD_STORE_INST"},
+	{0x73, "FLOAT_INST_RR"},
+	{0x74, "NEON_INST_RR"},
+	{0x80, "STALL_PLD"},
+	{0x81, "STALL_WRITE"},
+	{0x82, "STALL_INST_M_TLB_MISS"},
+	{0x83, "STALL_DATA_M_TLB_MISS"},
+	{0x84, "STALL_INST_U_TLB"},
+	{0x85, "STALL_DATA_U_TLB"},
+	{0x86, "STALL_DMB"},
+	{0x8A, "INT_CLK_EN"},
+	{0x8B, "DATA_E_CLK_EN"},
+	{0x90, "ISB_INST"},
+	{0x91, "DSB_INST"},
+	{0x92, "INS_DMB"},
+	{0x93, "EXT_IRQ"},
+	{0xA0, "PLE_CACHE_REQ_COMP"},
+	{0xA1, "PLE_CACHE_REQ_SKP"},
+	{0xA2, "PLE_FIFO_FLUSH"},
+	{0xA3, "PLE_REQ_COMP"},
+	{0xA4, "PLE_FIFO_OF"},
+	{0xA5, "PLE_REQ_PRG"},
+	{0xFF, "CPU_CYCLES"}
+};
+
+#define A7_PMU_DESC_COUNT (sizeof(a7_pmu_desc) / sizeof(struct pmu_desc))
+#define A9_PMU_DESC_COUNT (sizeof(a9_pmu_desc) / sizeof(struct pmu_desc))
+
+#endif				/* _V7_PMU_NAME_H_ */
diff --git a/src/devtools/met-driver/4.4/common/v8_pmu_hw.c b/src/devtools/met-driver/4.4/common/v8_pmu_hw.c
new file mode 100644
index 0000000..225e1c6
--- /dev/null
+++ b/src/devtools/met-driver/4.4/common/v8_pmu_hw.c
@@ -0,0 +1,304 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/smp.h>
+#include "interface.h"
+#include "cpu_pmu.h"
+#include "v8_pmu_name.h"
+
+/*
+ * Per-CPU PMCR: config reg
+ */
+#define ARMV8_PMCR_E		(1 << 0)	/* Enable all counters */
+#define ARMV8_PMCR_P		(1 << 1)	/* Reset all counters */
+#define ARMV8_PMCR_C		(1 << 2)	/* Cycle counter reset */
+#define ARMV8_PMCR_D		(1 << 3)	/* CCNT counts every 64th cpu cycle */
+#define ARMV8_PMCR_X		(1 << 4)	/* Export to ETM */
+#define ARMV8_PMCR_DP		(1 << 5)	/* Disable CCNT if non-invasive debug */
+#define	ARMV8_PMCR_N_SHIFT	11	/* Number of counters supported */
+#define	ARMV8_PMCR_N_MASK	0x1f
+#define	ARMV8_PMCR_MASK		0x3f	/* Mask for writable bits */
+
+/*
+ * PMOVSR: counters overflow flag status reg
+ */
+#define	ARMV8_OVSR_MASK		0xffffffff	/* Mask for writable bits */
+#define	ARMV8_OVERFLOWED_MASK	ARMV8_OVSR_MASK
+
+
+enum ARM_TYPE {
+	CORTEX_A53 = 0xD03,
+	CORTEX_A35 = 0xD04,
+	CORTEX_A57 = 0xD07,
+	CORTEX_A72 = 0xD08,
+	CORTEX_A73 = 0xD09,
+	CHIP_UNKNOWN = 0xFFF
+};
+
+struct chip_pmu {
+	enum ARM_TYPE type;
+	struct pmu_desc *desc;
+	unsigned int count;
+	const char *cpu_name;
+};
+
+static struct chip_pmu chips[] = {
+	{CORTEX_A53, a53_pmu_desc, A53_PMU_DESC_COUNT, "Cortex-A7L"},
+	{CORTEX_A35, a53_pmu_desc, A53_PMU_DESC_COUNT, "Cortex-A35"},
+	{CORTEX_A57, a53_pmu_desc, A53_PMU_DESC_COUNT, "Cortex-A57"},
+	{CORTEX_A72, a53_pmu_desc, A53_PMU_DESC_COUNT, "Cortex-A72"},
+	{CORTEX_A73, a53_pmu_desc, A53_PMU_DESC_COUNT, "Cortex-A73"},
+};
+static struct chip_pmu chip_unknown = { CHIP_UNKNOWN, NULL, 0, "Unknown CPU" };
+
+#define CHIP_PMU_COUNT (sizeof(chips) / sizeof(struct chip_pmu))
+
+static struct chip_pmu *chip;
+
+static enum ARM_TYPE armv8_get_ic(void)
+{
+	unsigned int value;
+	/* Read Main ID Register */
+	asm("mrs %0, midr_el1":"=r"(value));
+
+	value = (value & 0xffff) >> 4;	/* primary part number */
+	return value;
+}
+
+static inline void armv8_pmu_counter_select(unsigned int idx)
+{
+	asm volatile ("msr pmselr_el0, %0"::"r" (idx));
+	isb();
+}
+
+static inline void armv8_pmu_type_select(unsigned int idx, unsigned int type)
+{
+	armv8_pmu_counter_select(idx);
+	asm volatile ("msr pmxevtyper_el0, %0"::"r" (type));
+}
+
+static inline unsigned int armv8_pmu_read_count(unsigned int idx)
+{
+	unsigned int value;
+
+	if (idx == 31) {
+		asm volatile ("mrs %0, pmccntr_el0":"=r" (value));
+	} else {
+		armv8_pmu_counter_select(idx);
+		asm volatile ("mrs %0, pmxevcntr_el0":"=r" (value));
+	}
+	return value;
+}
+
+static inline void armv8_pmu_write_count(int idx, u32 value)
+{
+	if (idx == 31) {
+		asm volatile ("msr pmccntr_el0, %0"::"r" (value));
+	} else {
+		armv8_pmu_counter_select(idx);
+		asm volatile ("msr pmxevcntr_el0, %0"::"r" (value));
+	}
+}
+
+static inline void armv8_pmu_enable_count(unsigned int idx)
+{
+	asm volatile ("msr pmcntenset_el0, %0"::"r" (1 << idx));
+}
+
+static inline void armv8_pmu_disable_count(unsigned int idx)
+{
+	asm volatile ("msr pmcntenclr_el0, %0"::"r" (1 << idx));
+}
+
+static inline void armv8_pmu_enable_intr(unsigned int idx)
+{
+	asm volatile ("msr pmintenset_el1, %0"::"r" (1 << idx));
+}
+
+static inline void armv8_pmu_disable_intr(unsigned int idx)
+{
+	asm volatile ("msr pmintenclr_el1, %0"::"r" (1 << idx));
+	isb();
+	asm volatile ("msr pmovsclr_el0, %0"::"r" (1 << idx));
+	isb();
+}
+
+static inline unsigned int armv8_pmu_overflow(void)
+{
+	unsigned int val;
+
+	asm volatile ("mrs %0, pmovsclr_el0":"=r" (val));	/* read */
+	val &= ARMV8_OVSR_MASK;
+	asm volatile ("mrs %0, pmovsclr_el0"::"r" (val));
+	return val;
+}
+
+static inline unsigned int armv8_pmu_control_read(void)
+{
+	unsigned int val;
+
+	asm volatile ("mrs %0, pmcr_el0":"=r" (val));
+	return val;
+}
+
+static inline void armv8_pmu_control_write(u32 val)
+{
+	val &= ARMV8_PMCR_MASK;
+	isb();
+	asm volatile ("msr pmcr_el0, %0"::"r" (val));
+}
+
+static int armv8_pmu_hw_get_counters(void)
+{
+	int count = armv8_pmu_control_read();
+	/* N, bits[15:11] */
+	count = ((count >> ARMV8_PMCR_N_SHIFT) & ARMV8_PMCR_N_MASK);
+	return count;
+}
+
+static void armv8_pmu_hw_reset_all(int generic_counters)
+{
+	int i;
+
+	armv8_pmu_control_write(ARMV8_PMCR_C | ARMV8_PMCR_P);
+	/* generic counter */
+	for (i = 0; i < generic_counters; i++) {
+		armv8_pmu_disable_intr(i);
+		armv8_pmu_disable_count(i);
+	}
+	/* cycle counter */
+	armv8_pmu_disable_intr(31);
+	armv8_pmu_disable_count(31);
+	armv8_pmu_overflow();	/* clear overflow */
+}
+
+static int armv8_pmu_hw_get_event_desc(int i, int event, char *event_desc)
+{
+	if (event_desc == NULL)
+		return -1;
+
+	for (i = 0; i < chip->count; i++) {
+		if (chip->desc[i].event == event) {
+			strncpy(event_desc, chip->desc[i].name, MXSIZE_PMU_DESC - 1);
+			break;
+		}
+	}
+	if (i == chip->count)
+		return -1;
+
+	return 0;
+}
+
+static int armv8_pmu_hw_check_event(struct met_pmu *pmu, int idx, int event)
+{
+	int i;
+
+	/* Check if event is duplicate */
+	for (i = 0; i < idx; i++) {
+		if (pmu[i].event == event)
+			break;
+	}
+	if (i < idx) {
+		/* pr_debug("++++++ found duplicate event 0x%02x i=%d\n", event, i); */
+		return -1;
+	}
+
+	for (i = 0; i < chip->count; i++) {
+		if (chip->desc[i].event == event)
+			break;
+	}
+
+	if (i == chip->count)
+		return -1;
+
+	return 0;
+}
+
+static void armv8_pmu_hw_start(struct met_pmu *pmu, int count)
+{
+	int i;
+	int generic = count - 1;
+
+	armv8_pmu_hw_reset_all(generic);
+	for (i = 0; i < generic; i++) {
+		if (pmu[i].mode == MODE_POLLING) {
+			armv8_pmu_type_select(i, pmu[i].event);
+			armv8_pmu_enable_count(i);
+		}
+	}
+	if (pmu[count - 1].mode == MODE_POLLING) {	/* cycle counter */
+		armv8_pmu_enable_count(31);
+	}
+	armv8_pmu_control_write(ARMV8_PMCR_E);
+}
+
+static void armv8_pmu_hw_stop(int count)
+{
+	int generic = count - 1;
+
+	armv8_pmu_hw_reset_all(generic);
+}
+
+static unsigned int armv8_pmu_hw_polling(struct met_pmu *pmu, int count, unsigned int *pmu_value)
+{
+	int i, cnt = 0;
+	int generic = count - 1;
+
+	for (i = 0; i < generic; i++) {
+		if (pmu[i].mode == MODE_POLLING) {
+			pmu_value[cnt] = armv8_pmu_read_count(i);
+			cnt++;
+		}
+	}
+	if (pmu[count - 1].mode == MODE_POLLING) {
+		pmu_value[cnt] = armv8_pmu_read_count(31);
+		cnt++;
+	}
+	armv8_pmu_control_write(ARMV8_PMCR_C | ARMV8_PMCR_P | ARMV8_PMCR_E);
+
+	return cnt;
+}
+
+
+struct cpu_pmu_hw armv8_pmu = {
+	.name = "armv8_pmu",
+	.get_event_desc = armv8_pmu_hw_get_event_desc,
+	.check_event = armv8_pmu_hw_check_event,
+	.start = armv8_pmu_hw_start,
+	.stop = armv8_pmu_hw_stop,
+	.polling = armv8_pmu_hw_polling,
+};
+
+struct cpu_pmu_hw *cpu_pmu_hw_init(void)
+{
+	int i;
+	enum ARM_TYPE type;
+
+	type = armv8_get_ic();
+	PR_BOOTMSG("CPU TYPE - v8: %x\n", (unsigned int)type);
+	for (i = 0; i < CHIP_PMU_COUNT; i++) {
+		if (chips[i].type == type) {
+			chip = &(chips[i]);
+			break;
+		}
+	}
+	if (i == CHIP_PMU_COUNT) {
+		chip = &chip_unknown;
+		return NULL;
+	}
+
+	armv8_pmu.nr_cnt = armv8_pmu_hw_get_counters() + 1;
+	armv8_pmu.cpu_name = chip->cpu_name;
+
+	return &armv8_pmu;
+}
diff --git a/src/devtools/met-driver/4.4/common/v8_pmu_hw_v2.c b/src/devtools/met-driver/4.4/common/v8_pmu_hw_v2.c
new file mode 100644
index 0000000..25a4ed1
--- /dev/null
+++ b/src/devtools/met-driver/4.4/common/v8_pmu_hw_v2.c
@@ -0,0 +1,497 @@
+/*

+ * Copyright (C) 2018 MediaTek Inc.

+ *

+ * 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.

+ *

+ * This program is distributed in the hope that it will be useful,

+ * but WITHOUT ANY WARRANTY; without even the implied warranty of

+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the

+ * GNU General Public License for more details.

+ */

+

+#include <linux/smp.h> /* on_each_cpu */

+#include <linux/cpumask.h> /* for_each_possible_cpu(cpu) */

+

+#include "interface.h"

+#include "cpu_pmu_v2.h"

+#include "v8_pmu_hw_v2.h"

+#include "met_kernel_symbol.h"

+

+

+/*******************************************************************************

+*				Type Define

+*******************************************************************************/

+/*

+ * Per-CPU PMCR: config reg

+ */

+#define ARMV8_PMCR_E		(1 << 0)	/* Enable all counters */

+#define ARMV8_PMCR_P		(1 << 1)	/* Reset all counters */

+#define ARMV8_PMCR_C		(1 << 2)	/* Cycle counter reset */

+#define ARMV8_PMCR_D		(1 << 3)	/* CCNT counts every 64th cpu cycle */

+#define ARMV8_PMCR_X		(1 << 4)	/* Export to ETM */

+#define ARMV8_PMCR_DP		(1 << 5)	/* Disable CCNT if non-invasive debug */

+#define	ARMV8_PMCR_N_SHIFT	11		/* Number of counters supported */

+#define	ARMV8_PMCR_N_MASK	0x1f

+#define	ARMV8_PMCR_MASK		0x3f		/* Mask for writable bits */

+

+/*

+ * PMOVSR: counters overflow flag status reg

+ */

+#define	ARMV8_OVSR_MASK		0xffffffff	/* Mask for writable bits */

+#define	ARMV8_OVERFLOWED_MASK	ARMV8_OVSR_MASK

+

+

+/*******************************************************************************

+*				Fuction Pototypes

+*******************************************************************************/

+static int armv8_pmu_hw_get_event_desc(int event, char *event_desc);

+static int armv8_pmu_hw_check_event(struct met_pmu_v2 *pmu, int idx, int event);

+static void armv8_pmu_hw_start(struct met_pmu_v2 *pmu, int count);

+static void armv8_pmu_hw_stop(int count);

+static unsigned int armv8_pmu_hw_polling(struct met_pmu_v2 *pmu, int count, unsigned int *pmu_value);

+

+

+/*******************************************************************************

+*				Globe Variables

+*******************************************************************************/

+struct pmu_desc_v2 a53_pmu_desc_v2[] = {

+	{0x00, "SW_INCR"},

+	{0x01, "L1I_CACHE_REFILL"},

+	{0x02, "L1I_TLB_REFILL"},

+	{0x03, "L1D_CACHE_REFILL"},

+	{0x04, "L1D_CACHE"},

+	{0x05, "L1D_TLB_REFILL"},

+	{0x06, "LD_RETIRED"},

+	{0x07, "ST_RETIRED"},

+	{0x08, "INST_RETIRED"},

+	{0x09, "EXC_TAKEN"},

+	{0x0A, "EXC_RETURN"},

+	{0x0B, "CID_WRITE_RETIRED"},

+	{0x0C, "PC_WRITE_RETIRED"},

+	{0x0D, "BR_IMMED_RETIRED"},

+	{0x0E, "BR_RETURN_RETIRED"},

+	{0x0F, "UNALIGNED_LDST_RETIRED"},

+

+	{0x10, "BR_MIS_PRED"},

+	{0x11, "CPU_CYCLES"},

+	{0x12, "BR_PRED"},

+	{0x13, "MEM_ACCESS"},

+	{0x14, "L1I_CACHE"},

+	{0x15, "L1D_CACHE_WB"},

+	{0x16, "L2D_CACHE"},

+	{0x17, "L2D_CACHE_REFILL"},

+	{0x18, "L2D_CACHE_WB"},

+	{0x19, "BUS_ACCESS"},

+	{0x1A, "MEMORY_ERROR"},

+	{0x1D, "BUS_CYCLES"},

+	{0x1E, "CHAIN"},

+

+	{0x60, "BUS_READ_ACCESS"},

+	{0x61, "BUS_WRITE_ACCESS"},

+

+	{0x7A, "BR_INDIRECT_SPEC"},

+

+	{0x86, "IRQ_EXC_TAKEN"},

+	{0x87, "FIQ_EXC_TAKEN"},

+

+	{0xC0, "EXT_MEM_REQ"},

+	{0xC1, "NO_CACHE_EXT_MEM_REQ"},

+	{0xC2, "PREFETCH_LINEFILL"},

+	{0xC4, "ENT_READ_ALLOC_MODE"},

+	{0xC5, "READ_ALLOC_MODE"},

+	{0xC6, "PRE_DECODE_ERROR"},

+	{0xC7, "WRITE_STALL"},

+	{0xC8, "SCU_SNOOP_DATA_FROM_ANOTHER_CPU"},

+	{0xC9, "CONDITIONAL_BRANCH_EXE"},

+	{0xCA, "INDIRECT_BRANCH_MISPREDICT"},

+	{0xCB, "INDIRECT_BRANCH_MISPREDICT_ADDR"},/*"INDIRECT_BRANCH_MISPREDICT_ADDR_MISSCOMPARE" */

+	{0xCC, "COND_BRANCH_MISPREDICT"},

+

+	{0xD0, "L1_INST_CACHE_MEM_ERR"},

+

+	{0xE1, "ICACHE_MISS_STALL"},

+	{0xE2, "DPU_IQ_EMPTY"},

+	{0xE4, "NOT_FPU_NEON_INTERLOCK"},

+	{0xE5, "LOAD_STORE_INTERLOCK"},

+	{0xE6, "FPU_NEON_INTERLOCK"},

+	{0xE7, "LOAD_MISS_STALL"},

+	{0xE8, "STORE_STALL"},

+	{0xFF, "CPU_CYCLES"}

+};

+#define A53_PMU_DESC_COUNT (sizeof(a53_pmu_desc_v2) / sizeof(struct pmu_desc_v2))

+

+/* Cortex-A73 */

+struct pmu_desc_v2 a73_pmu_desc_v2[] = {

+	{0x00, "SW_INCR"},

+	{0x01, "L1I_CACHE_REFILL"},

+	{0x02, "L1I_TLB_REFILL"},

+	{0x03, "L1D_CACHE_REFILL"},

+	{0x04, "L1D_CACHE"},

+	{0x05, "L1D_TLB_REFILL"},

+	{0x08, "INST_RETIRED"},

+	{0x09, "EXC_TAKEN"},

+	{0x0A, "EXC_RETURN"},

+	{0x0B, "CID_WRITE_RETIRED"},

+	{0x0C, "PC_WRITE_RETIRED"},

+	{0x0D, "BR_IMMED_RETIRED"},

+	{0x0E, "BR_RETURN_RETIRED"},

+

+	{0x10, "BR_MIS_PRED"},

+	{0x11, "CPU_CYCLES"},

+	{0x12, "BR_PRED"},

+	{0x13, "MEM_ACCESS"},

+	{0x14, "L1I_CACHE"},

+	{0x15, "L1D_CACHE_WB"},

+	{0x16, "L2D_CACHE"},

+	{0x17, "L2D_CACHE_REFILL"},

+	{0x18, "L2D_CACHE_WB"},

+	{0x19, "BUS_ACCESS"},

+	{0x1B, "INT_SPEC"},

+	{0x1C, "TTBR_WRITE_RETIRED"},

+	{0x1D, "BUS_CYCLES"},

+	{0x1E, "CHAIN"},

+

+	{0x40, "L1D_CACHE_RD"},

+	{0x41, "L1D_CACHE_WR"},

+

+	{0x50, "L2D_CACHE_RD"},

+	{0x51, "L2D_CACHE_WR"},

+	{0x56, "L2D_CACHE_WB_VICTIM"},

+	{0x57, "L2D_CACHE_WB_CLEAN"},

+	{0x58, "L2D_CACHE_INVAL"},

+

+	{0x62, "BUS_ACCESS_SHARED"},

+	{0x63, "BUS_ACCESS_NOT_SHARED"},

+	{0x64, "BUS_ACCESS_NORMAL"},

+	{0x65, "BUS_ACCESS_SO_DIV"},

+	{0x66, "MEM_ACCESS_RD"},

+	{0x67, "MEM_ACCESS_WR"},

+	{0x6A, "UNALIGNED_LDST_SPEC"},

+	{0x6C, "LDREX_SPEC"},

+	{0x6E, "STREC_FAIL_SPEC"},

+

+	{0x70, "LD_SPEC"},

+	{0x71, "ST_SPEC"},

+	{0x72, "LDST_SPEC"},

+	{0x73, "DP_SPEC"},

+	{0x74, "ASE_SPEC"},

+	{0x75, "VFP_SPEC"},

+	{0x77, "CRYPTO_SPEC"},

+	{0x7A, "BR_INDIRECT_SPEC"},

+	{0x7C, "ISB_SPEC"},

+	{0x7D, "DSB_SPEC"},

+	{0x7E, "DMB_SPEC"},

+

+	{0x8A, "EXC_HVC"},

+

+	{0xC0, "LF_STALL"},

+	{0xC1, "PTW_STALL"},

+	{0xC2, "I_TAG_RAM_RD"},

+	{0xC3, "I_DATA_RAM_RD"},

+	{0xC4, "I_BTAC_RAM_RD"},

+

+	{0xD3, "D_LSU_SLOT_FULL"},

+	{0xD8, "LS_IQ_FULL"},

+	{0xD9, "DP_IQ_FULL"},

+	{0xDA, "DE_IQ_FULL"},

+	{0xDC, "EXC_TRAP_HYP"},

+	{0xDE, "ETM_EXT_OUT0"},

+	{0xDF, "ETM_EXT_OUT1"},

+

+	{0xE0, "MMU_PTW"},

+	{0xE1, "MMU_PTW_ST1"},

+	{0xE2, "MMU_PTW_ST2"},

+	{0xE3, "MMU_PTW_LSU"},

+	{0xE4, "MMU_PTW_ISIDE"},

+	{0xE5, "MMU_PTW_PLD"},

+	{0xE6, "MMU_PTW_CP15"},

+	{0xE7, "PLD_UTLB_REFILL"},

+	{0xE8, "CP15_UTLB_REFILL"},

+	{0xE9, "UTLB_FLUSH"},

+	{0xEA, "TLB_ACESS"},

+	{0xEB, "TLB_MISS"},

+	{0xEC, "DCACHE_SELF_HIT_VIPT"},

+        {0xEE, "CYCLES_L2_IDLE"},

+        {0xEF, "CPU_DECODE_UNIT_STALLED"},

+	{0xFF, "CPU_CYCLES"}

+};

+#define A73_PMU_DESC_COUNT (sizeof(a73_pmu_desc_v2) / sizeof(struct pmu_desc_v2))

+

+

+static struct chip_pmu_v2 *gChip[MXNR_CPU_V2];

+static struct chip_pmu_v2 chips[] = {

+	{CORTEX_A53, a53_pmu_desc_v2, A53_PMU_DESC_COUNT, 0, "Cortex-A53"},

+	{CORTEX_A35, a53_pmu_desc_v2, A53_PMU_DESC_COUNT, 0, "Cortex-A35"},

+	{CORTEX_A57, a53_pmu_desc_v2, A53_PMU_DESC_COUNT, 0, "Cortex-A57"},

+	{CORTEX_A72, a53_pmu_desc_v2, A53_PMU_DESC_COUNT, 0, "Cortex-A72"},

+	{CORTEX_A73, a73_pmu_desc_v2, A73_PMU_DESC_COUNT, 0, "Cortex-A73"},

+};

+static struct chip_pmu_v2 chip_unknown = { CHIP_UNKNOWN, NULL, 0, 0, "Unknown CPU" };

+#define CHIP_PMU_COUNT (sizeof(chips) / sizeof(struct chip_pmu_v2))

+

+struct cpu_pmu_hw_v2 armv8_pmu_v2 = {

+	.name = "armv8_pmu",

+	.get_event_desc = armv8_pmu_hw_get_event_desc,

+	.check_event = armv8_pmu_hw_check_event,

+	.start = armv8_pmu_hw_start,

+	.stop = armv8_pmu_hw_stop,

+	.polling = armv8_pmu_hw_polling,

+};

+

+

+/*******************************************************************************

+*				Iplement Start

+*******************************************************************************/

+static struct chip_pmu_v2 *get_chip_pmu_by_cpu_id(int cpu)

+{

+	return gChip[cpu];

+}

+

+static void set_chip_pmu_by_cpu_id(int cpu, struct chip_pmu_v2 *chip)

+{

+	gChip[cpu] = chip;

+}

+

+static inline void armv8_pmu_counter_select(unsigned int idx)

+{

+	asm volatile ("msr pmselr_el0, %0"::"r" (idx));

+	isb();

+}

+

+static inline void armv8_pmu_type_select(unsigned int idx, unsigned int type)

+{

+	armv8_pmu_counter_select(idx);

+	asm volatile ("msr pmxevtyper_el0, %0"::"r" (type));

+}

+

+static inline unsigned int armv8_pmu_read_count(unsigned int idx)

+{

+	unsigned int value;

+

+	if (idx == 31) {

+		asm volatile ("mrs %0, pmccntr_el0":"=r" (value));

+	} else {

+		armv8_pmu_counter_select(idx);

+		asm volatile ("mrs %0, pmxevcntr_el0":"=r" (value));

+	}

+	return value;

+}

+

+static inline void armv8_pmu_write_count(int idx, u32 value)

+{

+	if (idx == 31) {

+		asm volatile ("msr pmccntr_el0, %0"::"r" (value));

+	} else {

+		armv8_pmu_counter_select(idx);

+		asm volatile ("msr pmxevcntr_el0, %0"::"r" (value));

+	}

+}

+

+static inline void armv8_pmu_enable_count(unsigned int idx)

+{

+	asm volatile ("msr pmcntenset_el0, %0"::"r" (1 << idx));

+}

+

+static inline void armv8_pmu_disable_count(unsigned int idx)

+{

+	asm volatile ("msr pmcntenclr_el0, %0"::"r" (1 << idx));

+}

+

+static inline void armv8_pmu_enable_intr(unsigned int idx)

+{

+	asm volatile ("msr pmintenset_el1, %0"::"r" (1 << idx));

+}

+

+static inline void armv8_pmu_disable_intr(unsigned int idx)

+{

+	asm volatile ("msr pmintenclr_el1, %0"::"r" (1 << idx));

+	isb();

+	asm volatile ("msr pmovsclr_el0, %0"::"r" (1 << idx));

+	isb();

+}

+

+static inline unsigned int armv8_pmu_overflow(void)

+{

+	unsigned int val;

+

+	asm volatile ("mrs %0, pmovsclr_el0":"=r" (val));	/* read */

+	val &= ARMV8_OVSR_MASK;

+	asm volatile ("mrs %0, pmovsclr_el0"::"r" (val));

+	return val;

+}

+

+static inline unsigned int armv8_pmu_control_read(void)

+{

+	unsigned int val;

+

+	asm volatile ("mrs %0, pmcr_el0":"=r" (val));

+	return val;

+}

+

+static inline void armv8_pmu_control_write(u32 val)

+{

+	val &= ARMV8_PMCR_MASK;

+	isb();

+	asm volatile ("msr pmcr_el0, %0"::"r" (val));

+}

+

+static int armv8_pmu_hw_get_counters(void)

+{

+	int count = armv8_pmu_control_read();

+	/* N, bits[15:11] */

+	count = ((count >> ARMV8_PMCR_N_SHIFT) & ARMV8_PMCR_N_MASK);

+	return count;

+}

+

+static void armv8_pmu_hw_reset_all(int generic_counters)

+{

+	int i;

+

+	armv8_pmu_control_write(ARMV8_PMCR_C | ARMV8_PMCR_P);

+	/* generic counter */

+	for (i = 0; i < generic_counters; i++) {

+		armv8_pmu_disable_intr(i);

+		armv8_pmu_disable_count(i);

+	}

+	/* cycle counter */

+	armv8_pmu_disable_intr(31);

+	armv8_pmu_disable_count(31);

+	armv8_pmu_overflow();	/* clear overflow */

+}

+

+static int armv8_pmu_hw_get_event_desc(int event, char *event_desc)

+{

+	int i;

+	int this_cpu;

+	struct chip_pmu_v2 *chip_pmu;

+

+	this_cpu = smp_processor_id();

+	chip_pmu = get_chip_pmu_by_cpu_id(this_cpu);

+	if (event_desc == NULL)

+		return -1;

+

+	for (i = 0; i < chip_pmu->pmu_count; i++) {

+		if (chip_pmu->desc[i].event == event) {

+			strncpy(event_desc, chip_pmu->desc[i].name, MXSIZE_PMU_DESC - 1);

+			break;

+		}

+	}

+	if (i == chip_pmu->pmu_count)

+		return -1;

+

+	return 0;

+}

+

+static int armv8_pmu_hw_check_event(struct met_pmu_v2 *pmu, int idx, int event)

+{

+	int this_cpu;

+	struct chip_pmu_v2 *chip_pmu;

+	int i;

+

+	this_cpu = smp_processor_id();

+	chip_pmu = get_chip_pmu_by_cpu_id(this_cpu);

+	for (i = 0; i < chip_pmu->pmu_count; i++) {

+		if (chip_pmu->desc[i].event == event)

+			break;

+	}

+

+	if (i == chip_pmu->pmu_count) {                

+                PR_BOOTMSG("%s:%d => i=%d, pmu_count=%d\n", __FUNCTION__, __LINE__, i, chip_pmu->pmu_count);

+                return -1;

+        }

+

+	return 0;

+}

+

+static void armv8_pmu_hw_start(struct met_pmu_v2 *pmu, int count)

+{

+	int i;

+	int generic = count - 1;

+

+	armv8_pmu_hw_reset_all(generic);

+	for (i = 0; i < generic; i++) {

+		if (pmu[i].mode == MODE_POLLING) {

+			armv8_pmu_type_select(i, pmu[i].event);

+			armv8_pmu_enable_count(i);

+		}

+	}

+	if (pmu[count - 1].mode == MODE_POLLING)

+		armv8_pmu_enable_count(31);

+

+	armv8_pmu_control_write(ARMV8_PMCR_E);

+}

+

+static void armv8_pmu_hw_stop(int count)

+{

+	int generic = count - 1;

+

+	armv8_pmu_hw_reset_all(generic);

+}

+

+static unsigned int armv8_pmu_hw_polling(struct met_pmu_v2 *pmu, int count, unsigned int *pmu_value)

+{

+	int i, cnt = 0;

+	int generic = count - 1;

+

+	for (i = 0; i < generic; i++) {

+		if (pmu[i].mode == MODE_POLLING) {

+			pmu_value[cnt] = armv8_pmu_read_count(i);

+			cnt++;

+		}

+	}

+	if (pmu[count - 1].mode == MODE_POLLING) {

+		pmu_value[cnt] = armv8_pmu_read_count(31);

+		cnt++;

+	}

+	armv8_pmu_control_write(ARMV8_PMCR_C | ARMV8_PMCR_P | ARMV8_PMCR_E);

+

+	return cnt;

+}

+

+static void armv8_get_ic(void *info)

+{

+	unsigned int value;

+	unsigned int *type = (unsigned int *)info;

+

+	/* Read Main ID Register */

+	asm("mrs %0, midr_el1":"=r"(value));

+	*type = (value & 0xffff) >> 4;	/* primary part number */

+}

+

+struct cpu_pmu_hw_v2 *cpu_pmu_hw_init_v2(void)

+{

+	enum ARM_TYPE_v2 type;

+	struct chip_pmu_v2 *chip;

+	int this_cpu = smp_processor_id();

+	int cpu;

+	int i;

+

+	for_each_possible_cpu(cpu) {

+		if (cpu == this_cpu)

+			armv8_get_ic(&type);

+		else

+			met_smp_call_function_single_symbol(cpu, armv8_get_ic, &type, 1);

+

+		PR_BOOTMSG("CPU TYPE - v8: %x\n", (unsigned int)type);

+		for (i = 0; i < CHIP_PMU_COUNT; i++) {

+			if (chips[i].type == type) {

+				chip = &(chips[i]);

+				chip->hw_count = armv8_pmu_hw_get_counters() + 1;

+				set_chip_pmu_by_cpu_id(cpu, chip);

+				armv8_pmu_v2.chip_pmu[cpu] = chip;

+				if (chip->hw_count >= armv8_pmu_v2.max_hw_count)

+					armv8_pmu_v2.max_hw_count = chip->hw_count;

+				break;

+			}

+		}

+		if (i == CHIP_PMU_COUNT) {

+			set_chip_pmu_by_cpu_id(cpu, &chip_unknown);

+			return NULL;

+		}

+	}

+

+	return &armv8_pmu_v2;

+}

+

diff --git a/src/devtools/met-driver/4.4/common/v8_pmu_hw_v2.h b/src/devtools/met-driver/4.4/common/v8_pmu_hw_v2.h
new file mode 100644
index 0000000..4666d0c
--- /dev/null
+++ b/src/devtools/met-driver/4.4/common/v8_pmu_hw_v2.h
@@ -0,0 +1,24 @@
+/*

+ * Copyright (C) 2018 MediaTek Inc.

+ *

+ * 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.

+ *

+ * This program is distributed in the hope that it will be useful,

+ * but WITHOUT ANY WARRANTY; without even the implied warranty of

+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the

+ * GNU General Public License for more details.

+ */

+

+#ifndef _V8_PMU_V2_NAME_H_

+#define _V8_PMU_V2_NAME_H_

+

+#include "cpu_pmu_v2.h"

+

+

+/*******************************************************************************

+*                                Fuction Pototypes

+*******************************************************************************/

+struct cpu_pmu_hw_v2 *cpu_pmu_hw_init_v2(void);

+#endif				/* _V8_PMU_V2_NAME_H_ */

diff --git a/src/devtools/met-driver/4.4/common/v8_pmu_name.h b/src/devtools/met-driver/4.4/common/v8_pmu_name.h
new file mode 100644
index 0000000..4aa9d77
--- /dev/null
+++ b/src/devtools/met-driver/4.4/common/v8_pmu_name.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+
+#ifndef _V8_PMU_NAME_H_
+#define _V8_PMU_NAME_H_
+
+/* Cortex-A53 */
+struct pmu_desc a53_pmu_desc[] = {
+	{0x00, "SW_INCR"},
+	{0x01, "L1I_CACHE_REFILL"},
+	{0x02, "L1I_TLB_REFILL"},
+	{0x03, "L1D_CACHE_REFILL"},
+	{0x04, "L1D_CACHE"},
+	{0x05, "L1D_TLB_REFILL"},
+	{0x06, "LD_RETIRED"},
+	{0x07, "ST_RETIRED"},
+	{0x08, "INST_RETIRED"},
+	{0x09, "EXC_TAKEN"},
+	{0x0A, "EXC_RETURN"},
+	{0x0B, "CID_WRITE_RETIRED"},
+	{0x0C, "PC_WRITE_RETIRED"},
+	{0x0D, "BR_IMMED_RETIRED"},
+	{0x0E, "BR_RETURN_RETIRED"},
+	{0x0F, "UNALIGNED_LDST_RETIRED"},
+	{0x10, "BR_MIS_PRED"},
+	{0x11, "CPU_CYCLES"},
+	{0x12, "BR_PRED"},
+	{0x13, "MEM_ACCESS"},
+	{0x14, "L1I_CACHE"},
+	{0x15, "L1D_CACHE_WB"},
+	{0x16, "L2D_CACHE"},
+	{0x17, "L2D_CACHE_REFILL"},
+	{0x18, "L2D_CACHE_WB"},
+	{0x19, "BUS_ACCESS"},
+	{0x1A, "MEMORY_ERROR"},
+	{0x1D, "BUS_CYCLES"},
+	{0x60, "BUS_READ_ACCESS"},
+	{0x61, "BUS_WRITE_ACCESS"},
+	{0x86, "IRQ_EXC_TAKEN"},
+	{0x87, "FIQ_EXC_TAKEN"},
+	{0xC0, "EXT_MEM_REQ"},
+	{0xC1, "NO_CACHE_EXT_MEM_REQ"},
+	{0xC2, "PREFETCH_LINEFILL"},
+	{0xC4, "ENT_READ_ALLOC_MODE"},
+	{0xC5, "READ_ALLOC_MODE"},
+	{0xC6, "PRE_DECODE_ERROR"},
+	{0xC7, "WRITE_STALL"},
+	{0xC8, "SCU_SNOOP_DATA_FROM_ANOTHER_CPU"},
+	{0xC9, "CONDITIONAL_BRANCH_EXE"},
+	{0xCA, "INDIRECT_BRANCH_MISPREDICT"},
+	{0xCB, "INDIRECT_BRANCH_MISPREDICT_ADDR"},/*"INDIRECT_BRANCH_MISPREDICT_ADDR_MISSCOMPARE" */
+	{0xCC, "COND_BRANCH_MISPREDICT"},
+	{0xD0, "L1_INST_CACHE_MEM_ERR"},
+	{0xE1, "ICACHE_MISS_STALL"},
+	{0xE2, "DPU_IQ_EMPTY"},
+	{0xE4, "NOT_FPU_NEON_INTERLOCK"},
+	{0xE5, "LOAD_STORE_INTERLOCK"},
+	{0xE6, "FPU_NEON_INTERLOCK"},
+	{0xE7, "LOAD_MISS_STALL"},
+	{0xE8, "STORE_STALL"},
+	{0xFF, "CPU_CYCLES"}
+};
+
+#define A53_PMU_DESC_COUNT (sizeof(a53_pmu_desc) / sizeof(struct pmu_desc))
+
+#endif				/* _V8_PMU_NAME_H_ */
diff --git a/src/devtools/met-driver/4.4/common/version.h b/src/devtools/met-driver/4.4/common/version.h
new file mode 100644
index 0000000..7bb6c77
--- /dev/null
+++ b/src/devtools/met-driver/4.4/common/version.h
@@ -0,0 +1,14 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define MET_BACKEND_VERSION "6.1.0"
diff --git a/src/devtools/met-driver/4.4/default/Kbuild b/src/devtools/met-driver/4.4/default/Kbuild
new file mode 100644
index 0000000..c2877bf
--- /dev/null
+++ b/src/devtools/met-driver/4.4/default/Kbuild
@@ -0,0 +1,5 @@
+obj-m := met.o
+
+ccflags-y += -I$(srctree)/include/
+
+met-y := default/met_main.o
diff --git a/src/devtools/met-driver/4.4/default/met_main.c b/src/devtools/met-driver/4.4/default/met_main.c
new file mode 100644
index 0000000..48eaae9
--- /dev/null
+++ b/src/devtools/met-driver/4.4/default/met_main.c
@@ -0,0 +1,28 @@
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/hrtimer.h>
+#include <linux/cpu.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/profile.h>
+#include <linux/dcache.h>
+#include <linux/types.h>
+#include <linux/dcookies.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+
+static int __init met_drv_init(void)
+{
+	printk("Hello MET default module\n");
+	return 0;
+}
+
+static void __exit met_drv_exit(void)
+{
+}
+module_init(met_drv_init);
+module_exit(met_drv_exit);
+
+MODULE_AUTHOR("DT_DM5");
+MODULE_DESCRIPTION("MET_DEFAULT");
+MODULE_LICENSE("GPL");
diff --git a/src/devtools/met-driver/4.9/Android.mk.bak b/src/devtools/met-driver/4.9/Android.mk.bak
new file mode 100644
index 0000000..42d6942
--- /dev/null
+++ b/src/devtools/met-driver/4.9/Android.mk.bak
@@ -0,0 +1,11 @@
+LOCAL_PATH := $(call my-dir)
+
+ifneq (,$(filter $(word 2,$(subst -, ,$(LINUX_KERNEL_VERSION))),$(subst /, ,$(LOCAL_PATH))))
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := met.ko
+LOCAL_STRIP_MODULE := true
+MET_DRIVER_DIR := $(LOCAL_PATH)
+include $(MTK_KERNEL_MODULE)
+
+endif # Kernel version matches current path
diff --git a/src/devtools/met-driver/4.9/Makefile b/src/devtools/met-driver/4.9/Makefile
new file mode 100644
index 0000000..5ea7f33
--- /dev/null
+++ b/src/devtools/met-driver/4.9/Makefile
@@ -0,0 +1,50 @@
+ifneq ($(MET_DRIVER_DIR),)
+    MTK_PLATFORM := $(subst ",,$(CONFIG_MTK_PLATFORM))
+    MET_COMMON_DIR := $(MET_DRIVER_DIR)/common
+    MET_PLF_DIR := $(MET_DRIVER_DIR)/$(MTK_PLATFORM)
+    MET_BUILD_DEFAULT := n
+
+    ifneq ($(KERNEL_OUT),)
+        include $(KERNEL_OUT)/.config
+        ccflags-y += -imacros $(KERNEL_OUT)/include/generated/autoconf.h
+    endif
+
+    ifeq ($(CONFIG_MODULES),y)
+        ifeq ($(CONFIG_FTRACE),y)
+            ifeq ($(CONFIG_TRACING),y)
+                FTRACE_READY := y
+            endif
+        endif
+
+        $(info ******** Start to build met_drv for $(MTK_PLATFORM) ********)
+        ifneq ($(MET_PLF_DIR),)
+            ifeq ($(FTRACE_READY),y)
+                ifeq ($(CONFIG_BUILD_YOCTO),y)
+                    include $(MET_COMMON_DIR)/Kbuild.yocto
+                else
+                    include $(MET_COMMON_DIR)/Kbuild
+                endif
+            else
+                $(warning Not building met.ko due to CONFIG_FTRACE/CONFIG_TRACING is not set, build met default)
+                MET_BUILD_DEFAULT = y
+            endif
+        else
+            $(warning not support $(MTK_PLATFORM), build met default)
+            MET_BUILD_DEFAULT = y
+        endif
+    else
+        $(warning Not building met.ko due to CONFIG_MODULES is not set, build met default)
+        MET_BUILD_DEFAULT := y
+    endif
+
+    ifeq ($(MET_BUILD_DEFAULT),y)
+        MET_DEF_DIR := $(MET_DRIVER_DIR)/default
+        ifeq ($(CONFIG_BUILD_YOCTO),y)
+            include $(MET_DEF_DIR)/Kbuild.yocto
+        else
+            include $(MET_DEF_DIR)/Kbuild
+        endif
+    endif
+else
+$(info ******** MET_DRIVER_DIR is empty ********)
+endif
diff --git a/src/devtools/met-driver/4.9/Makefile.yocto b/src/devtools/met-driver/4.9/Makefile.yocto
new file mode 100644
index 0000000..12b3158
--- /dev/null
+++ b/src/devtools/met-driver/4.9/Makefile.yocto
@@ -0,0 +1,15 @@
+MODULE_NAME := met
+
+##############################################################
+# Compile settings
+##############################################################
+all: 
+	make -C $(KERNEL_OUT) M=$(MET_DRIVER_DIR) modules
+
+clean: 
+	make -C $(KERNEL_OUT) M=$(MET_DRIVER_DIR) clean
+	if [ -e $(MET_DRIVER_DIR)/$(MODULE_NAME).ko ]; then rm $(MET_DRIVER_DIR)/$(MODULE_NAME).ko; fi;
+
+.PHONY: all clean
+
+
diff --git a/src/devtools/met-driver/4.9/common/Kbuild b/src/devtools/met-driver/4.9/common/Kbuild
new file mode 100644
index 0000000..63de3d5
--- /dev/null
+++ b/src/devtools/met-driver/4.9/common/Kbuild
@@ -0,0 +1,294 @@
+$(info ======== Build met.ko ... ========)
+MET_CORE := common
+obj-m := met.o
+
+ifneq ($(wildcard $(MET_PLF_DIR)/Kbuild.platform.include),)
+    include $(MET_PLF_DIR)/Kbuild.platform.include
+else
+    $(info ======= Missing $(MET_PLF_DIR)/Kbuild.platform.include ========)
+endif
+
+ccflags-y += -DCONFIG_MET_MODULE
+ccflags-y += -DMET_PLF_USE
+ccflags-y += -I$(MET_COMMON_DIR)
+ccflags-y += -I$(MET_PLF_DIR)
+ccflags-y += -I$(srctree)/include/
+ccflags-y += -I$(srctree)/drivers/misc/mediatek/include/
+ccflags-y += -I$(srctree)/drivers/misc/mediatek/include/mt-plat/
+ccflags-y += -I$(srctree)/drivers/misc/mediatek/include/mt-plat/$(MTK_PLATFORM)/include/
+
+ccflags-y += $(EXTRA_ARGS) $(EXTRA_CFLAGS)
+
+met-y := $(MET_CORE)/met_main.o \
+    $(MET_CORE)/met_backlight.o \
+    $(MET_CORE)/met_tag_ex.o \
+    $(MET_CORE)/interface.o \
+    $(MET_CORE)/sampler.o \
+    $(MET_CORE)/dummy_header.o \
+    $(MET_CORE)/util.o \
+    $(MET_CORE)/stat.o \
+    $(MET_CORE)/cookie.o \
+    $(MET_CORE)/mem_stat.o \
+    $(MET_CORE)/switch.o \
+    $(MET_CORE)/trace_event.o \
+    $(MET_CORE)/core_plf_init.o \
+    $(MET_CORE)/core_plf_trace.o \
+    $(MET_CORE)/ondiemet.o \
+    $(MET_CORE)/ondiemet_log.o \
+    $(MET_CORE)/sspm/ondiemet_sspm.o
+
+CFLAGS_interface.o += -DMET_USER_EVENT_SUPPORT
+CFLAGS_met_tag_ex.o += -DMET_USER_EVENT_SUPPORT
+
+$(info CPUPMU_VERSION = $(CPUPMU_VERSION))
+ifeq ("$(CPUPMU_VERSION)", "V8_2")
+    ccflags-y += -DCPUPMU_V8_2
+endif
+
+$(info ARCH = $(ARCH))
+ifeq ($(ARCH), mips)
+    met-y += $(MET_CORE)/mips_pmu_hw.o
+endif #ifeq ($(ARCH), mips)
+
+ifeq ($(ARCH), arm)
+    ccflags-y += -DCONFIG_MET_ARM_32BIT
+    met-y += $(MET_CORE)/cpu_pmu.o
+    met-y += $(MET_CORE)/v7_pmu_hw.o
+    met-y += $(MET_CORE)/v6_pmu_hw.o
+endif #ifeq ($(ARCH), arm)
+
+ifeq ($(ARCH), arm64)
+    met-y += $(MET_CORE)/cpu_pmu.o
+    met-y += $(MET_CORE)/v8_pmu_hw.o
+endif
+
+$(info CONFIG_CPU_FREQ = $(CONFIG_CPU_FREQ))
+ifeq ($(CONFIG_CPU_FREQ),y)
+    met-y += $(MET_CORE)/power.o
+endif
+
+
+################################################################################
+# MET_EMI
+################################################################################
+FEATURE_SSPM_EMI := $(if $(FEATURE_SSPM_EMI),$(FEATURE_SSPM_EMI),y)
+$(info FEATURE_SSPM_EMI = $(FEATURE_SSPM_EMI))
+
+MET_EMI := $(if $(filter n,$(FEATURE_SSPM_EMI)),n,y)
+
+met-$(MET_EMI) += $(MET_CORE)/met_emi.o \
+        $(MET_CORE)/mtk_emi_bm.o
+
+
+################################################################################
+# MET_GPU
+################################################################################
+FEATURE_GPU := $(if $(FEATURE_GPU),$(FEATURE_GPU),y)
+$(info FEATURE_GPU = $(FEATURE_GPU))
+
+ifneq ($(FEATURE_GPU), n)
+    MET_GPU := y
+
+    # for mtk_gpufreq.h
+    ifneq ("$(wildcard $(srctree)/drivers/misc/mediatek/base/power/$(MTK_PLATFORM)/mtk_gpufreq.h)","")
+        ccflags-y += -I$(srctree)/drivers/misc/mediatek/base/power/$(MTK_PLATFORM)/
+    else
+        MET_GPU = n
+        $(info ======= Missing $(srctree)/drivers/misc/mediatek/base/power/$(MTK_PLATFORM)/mtk_gpufreq.h ========)
+        $(info ======== disable MET_GPU ========)
+    endif
+
+    # for mtk_gpu_utility.h
+    ifneq ("$(wildcard $(srctree)/drivers/misc/mediatek/include/mt-plat/mtk_gpu_utility.h)","")
+        ccflags-y += -I$(srctree)/drivers/misc/mediatek/include/mt-plat/
+    else
+        MET_GPU = n
+        $(info ======== Missing $(srctree)/drivers/misc/mediatek/include/mt-plat/mtk_gpu_utility.h ========)
+        $(info ======== disable MET_GPU ========)
+    endif
+
+    ifneq ($(CONFIG_MTK_GPU_SUPPORT), y)
+        MET_GPU = n
+        $(info ======== CONFIG_MTK_GPU_SUPPORT = n ========)
+        $(info ======== disable MET_GPU ========)
+    endif
+else
+    MET_GPU := n
+endif
+
+met-$(MET_GPU) += $(MET_CORE)/mtk_gpu_metmonitor.o
+
+
+################################################################################
+# MET_VCOREDVFS
+################################################################################
+FEATURE_VCOREDVFS := $(if $(FEATURE_VCOREDVFS),$(FEATURE_VCOREDVFS),y)
+$(info FEATURE_VCOREDVFS = $(FEATURE_VCOREDVFS))
+
+ifneq ($(FEATURE_VCOREDVFS), n)
+    MET_VCOREDVFS := y
+
+    # for mtk_vcorefs_manager.h
+    ifneq ("$(wildcard $(MET_VCOREDVFS_INC)/mtk_vcorefs_manager.h)","")
+        ccflags-y += -I$(MET_VCOREDVFS_INC)/
+    else
+        MET_VCOREDVFS = n
+        $(info ======== Missing $(MET_VCOREDVFS_INC)/mtk_vcorefs_manager.h ========)
+        $(info ======== disable MET_VCOREDVFS ========)
+    endif
+
+    # for mtk_vcorefs_governor.h
+    ifneq ("$(wildcard $(MET_VCOREDVFS_INC)/mtk_vcorefs_governor.h)","")
+        ccflags-y += -I$(MET_VCOREDVFS_INC)
+    else
+        MET_VCOREDVFS = n
+        $(info ======== Missing $(MET_VCOREDVFS_INC)/mtk_vcorefs_governor.h ========)
+        $(info ======== disable MET_VCOREDVFS ========)
+    endif
+
+    # for helio-dvfsrc.h
+    ifneq ("$(wildcard $(srctree)/drivers/devfreq/helio-dvfsrc.h)","")
+        ccflags-y += -I$(srctree)/drivers/devfreq/
+    else
+        MET_VCOREDVFS = n
+        $(info ======== Missing $(srctree)/drivers/devfreq/helio-dvfsrc.h ========)
+        $(info ======== disable MET_VCOREDVFS ========)
+    endif
+else
+    MET_VCOREDVFS := n
+endif
+
+met-$(MET_VCOREDVFS) += $(MET_CORE)/met_vcoredvfs.o
+
+
+################################################################################
+# MET_PTPOD
+################################################################################
+FEATURE_PTPOD := $(if $(FEATURE_PTPOD),$(FEATURE_PTPOD),y)
+$(info FEATURE_PTPOD = $(FEATURE_PTPOD))
+
+ifneq ($(FEATURE_PTPOD), n)
+    MET_PTPOD := y
+
+    # for mtk_gpufreq.h
+    ifneq ("$(wildcard $(srctree)/drivers/misc/mediatek/base/power/$(MTK_PLATFORM)/mtk_gpufreq.h)","")
+        ccflags-y += -I$(srctree)/drivers/misc/mediatek/base/power/$(MTK_PLATFORM)/
+    else
+        MET_PTPOD = n
+        $(info ======== Missing $(srctree)/drivers/misc/mediatek/base/power/$(MTK_PLATFORM)/mtk_gpufreq.h ========)
+        $(info ======== disable MET_PTPOD ========)
+    endif
+
+    # for mtk_cpufreq_api.h
+    ifneq ("$(wildcard $(srctree)/drivers/misc/mediatek/include/mt-plat/$(MTK_PLATFORM)/include/mach/mtk_cpufreq_api.h)","")
+        ccflags-y += -I$(srctree)/drivers/misc/mediatek/include/mt-plat/$(MTK_PLATFORM)/include/
+    else
+        MET_PTPOD = n
+        $(info ======== Missing $(srctree)/drivers/misc/mediatek/include/mt-plat/$(MTK_PLATFORM)/include/mach/mtk_cpufreq_api.h ========)
+        $(info ======== disable MET_PTPOD ========)
+    endif
+
+    # for mtk_cpufreq_config.h
+    ifneq ("$(wildcard $(MET_PTPOD_INC)/mtk_cpufreq_config.h)","")
+        ccflags-y += -I$(MET_PTPOD_INC)
+    else
+        MET_PTPOD = n
+        $(info ======== Missing $(MET_PTPOD_INC)/mtk_cpufreq_config.h ========)
+        $(info ======== disable MET_PTPOD ========)
+    endif
+else
+    MET_PTPOD := n
+endif
+
+met-$(MET_PTPOD) += $(MET_CORE)/met_ptpod.o
+
+
+################################################################################
+# MET_CPUDSU
+################################################################################
+FEATURE_CPUDSU := $(if $(FEATURE_CPUDSU),$(FEATURE_CPUDSU),y)
+$(info FEATURE_CPUDSU = $(FEATURE_CPUDSU))
+
+MET_CPUDSU := $(if $(filter n,$(FEATURE_CPUDSU)),n,y)
+
+met-$(MET_CPUDSU) += $(MET_CORE)/cpu_dsu.o \
+                     $(MET_CORE)/v8_dsu_hw.o
+
+################################################################################
+# MET_WALLTIME
+################################################################################
+FEATURE_WALLTIME := $(if $(FEATURE_WALLTIME),$(FEATURE_WALLTIME),y)
+$(info FEATURE_WALLTIME = $(FEATURE_WALLTIME))
+
+MET_WALLTIME := $(if $(filter n,$(FEATURE_WALLTIME)),n,y)
+
+met-$(MET_WALLTIME) += $(MET_CORE)/met_wall_time.o
+
+################################################################################
+# On-die-met SSPM only module
+################################################################################
+FEATURE_ONDIEMET := $(if $(FEATURE_ONDIEMET),$(FEATURE_ONDIEMET),y)
+ifeq ($(FEATURE_ONDIEMET), y)
+    FEATURE_ONDIEMET_WALLTIME := $(if $(FEATURE_ONDIEMET_WALLTIME),$(FEATURE_ONDIEMET_WALLTIME),y)
+else
+    FEATURE_ONDIEMET_WALLTIME := n
+endif
+
+$(info FEATURE_ONDIEMET = $(FEATURE_ONDIEMET))
+$(info FEATURE_ONDIEMET_WALLTIME = $(FEATURE_ONDIEMET_WALLTIME))
+
+ifneq ($(FEATURE_ONDIEMET), n)
+    subdir-ccflags-y += -DONDIEMET_SUPPORT
+
+    ifeq ($(CONFIG_MTK_TINYSYS_SSPM_SUPPORT),)
+        $(info CONFIG_MTK_TINYSYS_SSPM_SUPPORT = n)
+    else
+        $(info CONFIG_MTK_TINYSYS_SSPM_SUPPORT = $(CONFIG_MTK_TINYSYS_SSPM_SUPPORT))
+    endif
+
+    ifeq ($(CONFIG_MTK_TINYSYS_SSPM_SUPPORT),y)
+        # for sspm_ipi.h
+        subdir-ccflags-y += -I$(srctree)/drivers/misc/mediatek/sspm
+        subdir-ccflags-y += -I$(srctree)/drivers/misc/mediatek/sspm/$(MTK_PLATFORM)
+        met-y += $(MET_CORE)/sspm/sspm_ipi_handle.o
+        met-y += $(MET_CORE)/sspm/sspm_common.o
+        ccflags-y += -DMTK_TINYSYS_SSPM_SUPPORT
+
+        ifneq ("$(wildcard $(srctree)/drivers/misc/mediatek/sspm/$(MTK_PLATFORM)/sspm_ipi_define.h)","")
+            subdir-ccflags-y += -I$(srctree)/drivers/misc/mediatek/sspm \
+                    -I$(srctree)/drivers/misc/mediatek/sspm/$(MTK_PLATFORM)
+
+            SYS_SSPM_READY := y
+        else
+            $(info ======== Missing $(srctree)/drivers/misc/mediatek/sspm/$(MTK_PLATFORM)/sspm_ipi_define.h========)
+            $(info ======== disable ALL ondiemet feature ========)
+
+            SYS_SSPM_READY := n
+        endif
+    else
+        $(info ======== CONFIG_MTK_TINYSYS_SSPM_SUPPORT = n ========)
+        $(info ======== disable ALL ondiemet feature ========)
+
+        SYS_SSPM_READY := n
+    endif
+
+    ifeq ($(SYS_SSPM_READY), y)
+        MET_SSPM_WALLTIME := $(if $(filter n,$(FEATURE_ONDIEMET_WALLTIME)),n,y)
+
+        met-$(MET_SSPM_WALLTIME) += $(MET_CORE)/sspm/sspm_walltime.o
+    endif
+endif
+
+##############################################################################################
+# include $(MET_PLF_DIR)/Kbuild
+##############################################################################################
+ifneq ($(wildcard $(MET_PLF_DIR)/Kbuild),)
+    include $(MET_PLF_DIR)/Kbuild
+else
+    $(info ======= Missing $(MET_PLF_DIR)/Kbuild ========)
+endif
+
+#################################################################################
+# add met_device flags
+#################################################################################
+ccflags-y += $(foreach v, $(filter MET_%,$(.VARIABLES)), $(if $(filter $($(v)),y),-D$(v)))
diff --git a/src/devtools/met-driver/4.9/common/Kbuild.yocto b/src/devtools/met-driver/4.9/common/Kbuild.yocto
new file mode 100644
index 0000000..cc83c66
--- /dev/null
+++ b/src/devtools/met-driver/4.9/common/Kbuild.yocto
@@ -0,0 +1,294 @@
+$(info ======== Build met.ko ... ========)
+MET_CORE := common
+obj-m := met.o
+
+ccflags-y += -DCONFIG_MET_MODULE
+ccflags-y += -DMET_PLF_USE
+ccflags-y += -I$(MET_COMMON_DIR)
+ccflags-y += -I$(MET_PLF_DIR)
+ccflags-y += -I$(KERNEL_SRC)/include/
+ccflags-y += -I$(KERNEL_SRC)/drivers/misc/mediatek/include/
+ccflags-y += -I$(KERNEL_SRC)/drivers/misc/mediatek/include/mt-plat/
+ccflags-y += -I$(KERNEL_SRC)/drivers/misc/mediatek/include/mt-plat/$(MTK_PLATFORM)/include/
+
+ccflags-y += $(EXTRA_ARGS) $(EXTRA_CFLAGS)
+
+met-y := $(MET_CORE)/met_main.o \
+    $(MET_CORE)/met_backlight.o \
+    $(MET_CORE)/met_tag_ex.o \
+    $(MET_CORE)/interface.o \
+    $(MET_CORE)/sampler.o \
+    $(MET_CORE)/dummy_header.o \
+    $(MET_CORE)/util.o \
+    $(MET_CORE)/stat.o \
+    $(MET_CORE)/cookie.o \
+    $(MET_CORE)/mem_stat.o \
+    $(MET_CORE)/switch.o \
+    $(MET_CORE)/trace_event.o \
+    $(MET_CORE)/core_plf_init.o \
+    $(MET_CORE)/core_plf_trace.o \
+    $(MET_CORE)/ondiemet.o \
+    $(MET_CORE)/ondiemet_log.o \
+    $(MET_CORE)/sspm/ondiemet_sspm.o
+
+CFLAGS_interface.o += -DMET_USER_EVENT_SUPPORT
+CFLAGS_met_tag_ex.o += -DMET_USER_EVENT_SUPPORT
+
+$(info ARCH = $(ARCH))
+ifeq ($(ARCH), mips)
+    met-y += $(MET_CORE)/mips_pmu_hw.o
+endif #ifeq ($(ARCH), mips)
+
+ifeq ($(ARCH), arm)
+    ccflags-y += -DCONFIG_MET_ARM_32BIT
+    met-y += $(MET_CORE)/cpu_pmu.o
+    met-y += $(MET_CORE)/v7_pmu_hw.o
+    met-y += $(MET_CORE)/v6_pmu_hw.o
+endif #ifeq ($(ARCH), arm)
+
+ifeq ($(ARCH), arm64)
+    met-y += $(MET_CORE)/cpu_pmu.o
+    met-y += $(MET_CORE)/v8_pmu_hw.o
+endif
+
+$(info CONFIG_CPU_FREQ = $(CONFIG_CPU_FREQ))
+ifeq ($(CONFIG_CPU_FREQ),y)
+    met-y += $(MET_CORE)/power.o
+endif
+
+
+#################################################################################
+# Kbuild.platform.include
+#################################################################################
+ifneq ($(wildcard $(MET_PLF_DIR)/Kbuild.platform.include),)
+    include $(MET_PLF_DIR)/Kbuild.platform.include
+else
+    $(info ======= Missing $(MET_PLF_DIR)/Kbuild.platform.include ========)
+endif
+
+
+################################################################################
+# CPUPMU_VERSION
+################################################################################
+$(info CPUPMU_VERSION = $(CPUPMU_VERSION))
+ifeq ("$(CPUPMU_VERSION)", "V8_2")
+    ccflags-y += -DCPUPMU_V8_2
+endif
+
+
+################################################################################
+# MET_EMI
+################################################################################
+FEATURE_SSPM_EMI := $(if $(FEATURE_SSPM_EMI),$(FEATURE_SSPM_EMI),y)
+$(info FEATURE_SSPM_EMI = $(FEATURE_SSPM_EMI))
+
+MET_EMI := $(if $(filter n,$(FEATURE_SSPM_EMI)),n,y)
+
+met-$(MET_EMI) += $(MET_CORE)/met_emi.o \
+        $(MET_CORE)/mtk_emi_bm.o
+
+
+################################################################################
+# MET_GPU
+################################################################################
+FEATURE_GPU := $(if $(FEATURE_GPU),$(FEATURE_GPU),y)
+$(info FEATURE_GPU = $(FEATURE_GPU))
+
+ifneq ($(FEATURE_GPU), n)
+    MET_GPU := y
+
+    # for mtk_gpufreq.h
+    ifneq ("$(wildcard $(KERNEL_SRC)/drivers/misc/mediatek/base/power/$(MTK_PLATFORM)/mtk_gpufreq.h)","")
+        ccflags-y += -I$(KERNEL_SRC)/drivers/misc/mediatek/base/power/$(MTK_PLATFORM)/
+    else
+        MET_GPU = n
+        $(info ======= Missing $(KERNEL_SRC)/drivers/misc/mediatek/base/power/$(MTK_PLATFORM)/mtk_gpufreq.h ========)
+        $(info ======== disable MET_GPU ========)
+    endif
+
+    # for mtk_gpu_utility.h
+    ifneq ("$(wildcard $(KERNEL_SRC)/drivers/misc/mediatek/include/mt-plat/mtk_gpu_utility.h)","")
+        ccflags-y += -I$(KERNEL_SRC)/drivers/misc/mediatek/include/mt-plat/
+    else
+        MET_GPU = n
+        $(info ======== Missing $(KERNEL_SRC)/drivers/misc/mediatek/include/mt-plat/mtk_gpu_utility.h ========)
+        $(info ======== disable MET_GPU ========)
+    endif
+
+    ifneq ($(CONFIG_MTK_GPU_SUPPORT), y)
+        MET_GPU = n
+        $(info ======== CONFIG_MTK_GPU_SUPPORT = n ========)
+        $(info ======== disable MET_GPU ========)
+    endif
+else
+    MET_GPU := n
+endif
+
+met-$(MET_GPU) += $(MET_CORE)/mtk_gpu_metmonitor.o
+
+
+################################################################################
+# MET_VCOREDVFS
+################################################################################
+FEATURE_VCOREDVFS := $(if $(FEATURE_VCOREDVFS),$(FEATURE_VCOREDVFS),y)
+$(info FEATURE_VCOREDVFS = $(FEATURE_VCOREDVFS))
+
+ifneq ($(FEATURE_VCOREDVFS), n)
+    MET_VCOREDVFS := y
+
+    # for mtk_vcorefs_manager.h
+    ifneq ("$(wildcard $(MET_VCOREDVFS_INC)/mtk_vcorefs_manager.h)","")
+        ccflags-y += -I$(MET_VCOREDVFS_INC)/
+    else
+        MET_VCOREDVFS = n
+        $(info ======== Missing $(MET_VCOREDVFS_INC)/mtk_vcorefs_manager.h ========)
+        $(info ======== disable MET_VCOREDVFS ========)
+    endif
+
+    # for mtk_vcorefs_governor.h
+    ifneq ("$(wildcard $(MET_VCOREDVFS_INC)/mtk_vcorefs_governor.h)","")
+        ccflags-y += -I$(MET_VCOREDVFS_INC)
+    else
+        MET_VCOREDVFS = n
+        $(info ======== Missing $(MET_VCOREDVFS_INC)/mtk_vcorefs_governor.h ========)
+        $(info ======== disable MET_VCOREDVFS ========)
+    endif
+
+    # for helio-dvfsrc.h
+    ifneq ("$(wildcard $(KERNEL_SRC)/drivers/devfreq/helio-dvfsrc.h)","")
+        ccflags-y += -I$(KERNEL_SRC)/drivers/devfreq/
+    else
+        MET_VCOREDVFS = n
+        $(info ======== Missing $(KERNEL_SRC)/drivers/devfreq/helio-dvfsrc.h ========)
+        $(info ======== disable MET_VCOREDVFS ========)
+    endif
+else
+    MET_VCOREDVFS := n
+endif
+
+met-$(MET_VCOREDVFS) += $(MET_CORE)/met_vcoredvfs.o
+
+
+################################################################################
+# MET_PTPOD
+################################################################################
+FEATURE_PTPOD := $(if $(FEATURE_PTPOD),$(FEATURE_PTPOD),y)
+$(info FEATURE_PTPOD = $(FEATURE_PTPOD))
+
+ifneq ($(FEATURE_PTPOD), n)
+    MET_PTPOD := y
+
+    # for mtk_gpufreq.h
+    ifneq ("$(wildcard $(KERNEL_SRC)/drivers/misc/mediatek/base/power/$(MTK_PLATFORM)/mtk_gpufreq.h)","")
+        ccflags-y += -I$(KERNEL_SRC)/drivers/misc/mediatek/base/power/$(MTK_PLATFORM)/
+    else
+        MET_PTPOD = n
+        $(info ======== Missing $(KERNEL_SRC)/drivers/misc/mediatek/base/power/$(MTK_PLATFORM)/mtk_gpufreq.h ========)
+        $(info ======== disable MET_PTPOD ========)
+    endif
+
+    # for mtk_cpufreq_api.h
+    ifneq ("$(wildcard $(KERNEL_SRC)/drivers/misc/mediatek/include/mt-plat/$(MTK_PLATFORM)/include/mach/mtk_cpufreq_api.h)","")
+        ccflags-y += -I$(KERNEL_SRC)/drivers/misc/mediatek/include/mt-plat/$(MTK_PLATFORM)/include/
+    else
+        MET_PTPOD = n
+        $(info ======== Missing $(KERNEL_SRC)/drivers/misc/mediatek/include/mt-plat/$(MTK_PLATFORM)/include/mach/mtk_cpufreq_api.h ========)
+        $(info ======== disable MET_PTPOD ========)
+    endif
+
+    # for mtk_cpufreq_config.h
+    ifneq ("$(wildcard $(MET_PTPOD_INC)/mtk_cpufreq_config.h)","")
+        ccflags-y += -I$(MET_PTPOD_INC)
+    else
+        MET_PTPOD = n
+        $(info ======== Missing $(MET_PTPOD_INC)/mtk_cpufreq_config.h ========)
+        $(info ======== disable MET_PTPOD ========)
+    endif
+else
+    MET_PTPOD := n
+endif
+
+met-$(MET_PTPOD) += $(MET_CORE)/met_ptpod.o
+
+
+################################################################################
+# MET_CPUDSU
+################################################################################
+FEATURE_CPUDSU := $(if $(FEATURE_CPUDSU),$(FEATURE_CPUDSU),y)
+$(info FEATURE_CPUDSU = $(FEATURE_CPUDSU))
+
+MET_CPUDSU := $(if $(filter n,$(FEATURE_CPUDSU)),n,y)
+
+met-$(MET_CPUDSU) += $(MET_CORE)/cpu_dsu.o \
+                     $(MET_CORE)/v8_dsu_hw.o
+
+################################################################################
+# MET_WALLTIME
+################################################################################
+FEATURE_WALLTIME := $(if $(FEATURE_WALLTIME),$(FEATURE_WALLTIME),y)
+$(info FEATURE_WALLTIME = $(FEATURE_WALLTIME))
+
+MET_WALLTIME := $(if $(filter n,$(FEATURE_WALLTIME)),n,y)
+
+met-$(MET_WALLTIME) += $(MET_CORE)/met_wall_time.o
+
+################################################################################
+# On-die-met SSPM only module
+################################################################################
+FEATURE_ONDIEMET := $(if $(FEATURE_ONDIEMET),$(FEATURE_ONDIEMET),y)
+ifeq ($(FEATURE_ONDIEMET), y)
+    FEATURE_ONDIEMET_WALLTIME := $(if $(FEATURE_ONDIEMET_WALLTIME),$(FEATURE_ONDIEMET_WALLTIME),y)
+else
+    FEATURE_ONDIEMET_WALLTIME := n
+endif
+
+$(info FEATURE_ONDIEMET = $(FEATURE_ONDIEMET))
+$(info FEATURE_ONDIEMET_WALLTIME = $(FEATURE_ONDIEMET_WALLTIME))
+
+ifneq ($(FEATURE_ONDIEMET), n)
+    subdir-ccflags-y += -DONDIEMET_SUPPORT
+
+    ifeq ($(CONFIG_MTK_TINYSYS_SSPM_SUPPORT),)
+        $(info CONFIG_MTK_TINYSYS_SSPM_SUPPORT = n)
+    else
+        $(info CONFIG_MTK_TINYSYS_SSPM_SUPPORT = $(CONFIG_MTK_TINYSYS_SSPM_SUPPORT))
+    endif
+
+    ifeq ($(CONFIG_MTK_TINYSYS_SSPM_SUPPORT),y)
+        # for sspm_ipi.h
+        subdir-ccflags-y += -I$(KERNEL_SRC)/drivers/misc/mediatek/sspm
+        subdir-ccflags-y += -I$(KERNEL_SRC)/drivers/misc/mediatek/sspm/$(MTK_PLATFORM)
+        met-y += $(MET_CORE)/sspm/sspm_ipi_handle.o
+        met-y += $(MET_CORE)/sspm/sspm_common.o
+        ccflags-y += -DMTK_TINYSYS_SSPM_SUPPORT
+
+        ifneq ("$(wildcard $(KERNEL_SRC)/drivers/misc/mediatek/sspm/$(MTK_PLATFORM)/sspm_ipi_define.h)","")
+            subdir-ccflags-y += -I$(KERNEL_SRC)/drivers/misc/mediatek/sspm \
+                    -I$(KERNEL_SRC)/drivers/misc/mediatek/sspm/$(MTK_PLATFORM)
+
+            SYS_SSPM_READY := y
+        else
+            $(info ======== Missing $(KERNEL_SRC)/drivers/misc/mediatek/sspm/$(MTK_PLATFORM)/sspm_ipi_define.h========)
+            $(info ======== disable ALL ondiemet feature ========)
+
+            SYS_SSPM_READY := n
+        endif
+    else
+        $(info ======== CONFIG_MTK_TINYSYS_SSPM_SUPPORT = n ========)
+        $(info ======== disable ALL ondiemet feature ========)
+
+        SYS_SSPM_READY := n
+    endif
+
+    ifeq ($(SYS_SSPM_READY), y)
+        MET_SSPM_WALLTIME := $(if $(filter n,$(FEATURE_ONDIEMET_WALLTIME)),n,y)
+
+        met-$(MET_SSPM_WALLTIME) += $(MET_CORE)/sspm/sspm_walltime.o
+    endif
+endif
+
+
+#################################################################################
+# add met_device flags
+#################################################################################
+ccflags-y += $(foreach v, $(filter MET_%,$(.VARIABLES)), $(if $(filter $($(v)),y),-D$(v)))
diff --git a/src/devtools/met-driver/4.9/common/cookie.c b/src/devtools/met-driver/4.9/common/cookie.c
new file mode 100644
index 0000000..400ba79
--- /dev/null
+++ b/src/devtools/met-driver/4.9/common/cookie.c
@@ -0,0 +1,281 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/cpu.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/cpu.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <asm/irq_regs.h>
+#include <asm/stacktrace.h>
+#include <linux/stacktrace.h>
+#include "interface.h"
+#include "met_drv.h"
+
+#define LINE_SIZE	256
+
+struct cookie_info {
+	int depth;
+	int strlen;
+	char strbuf[LINE_SIZE];
+};
+
+static unsigned int back_trace_depth;
+static DEFINE_PER_CPU(struct cookie_info, info);
+static DEFINE_PER_CPU(int, cpu_status);
+
+static int reset_driver_stat(void)
+{
+	back_trace_depth = 0;
+	met_cookie.mode = 0;
+	return 0;
+}
+
+
+noinline void cookie(char *strbuf)
+{
+	MET_TRACE("%s\n", strbuf);
+}
+
+noinline void cookie2(char *strbuf)
+{
+	MET_TRACE("%s\n", strbuf);
+}
+
+static void get_kernel_cookie(unsigned long pc, struct cookie_info *pinfo)
+{
+	int ret;
+#ifdef CONFIG_MODULES
+	off_t off;
+	struct module *mod = __module_address(pc);
+
+	if (mod) {
+		off = pc - (unsigned long)mod->core_layout.base;
+		ret = snprintf(pinfo->strbuf + pinfo->strlen, LINE_SIZE - pinfo->strlen,
+			       ",%s,%lx", mod->name, off);
+		pinfo->strlen += ret;
+		/* cookie(current->comm, pc, mod->name, off, 1); */
+	} else
+#endif
+	{
+		ret =
+		    snprintf(pinfo->strbuf + pinfo->strlen, LINE_SIZE - pinfo->strlen,
+			     ",vmlinux,%lx", pc);
+		pinfo->strlen += ret;
+		/* cookie(current->comm, pc, "vmlinux", pc, 0); */
+	}
+}
+
+#if defined(__arm__)
+static int report_trace(struct stackframe *frame, void *d)
+{
+	struct cookie_info *pinfo = d;
+	unsigned long pc = frame->pc;
+
+	if (pinfo->depth > 0) {
+		get_kernel_cookie(pc, pinfo);
+		pinfo->depth--;
+		return 0;
+	}
+	return 1;
+}
+#endif
+
+static void kernel_backtrace(struct pt_regs *const regs, struct cookie_info *pinfo)
+{
+#if defined(__arm__)
+	struct stackframe frame;
+
+	frame.fp = regs->ARM_fp;
+	frame.sp = regs->ARM_sp;
+	frame.lr = regs->ARM_lr;
+	frame.pc = regs->ARM_pc;
+	walk_stackframe(&frame, report_trace, pinfo);
+#else
+	return;
+#endif
+}
+
+
+void met_cookie_polling(unsigned long long stamp, int cpu)
+{
+	struct pt_regs *regs;
+	struct cookie_info *pinfo;
+	unsigned long pc;
+	int ret, outflag = 0;
+	off_t off;
+
+	if (per_cpu(cpu_status, cpu) != CPU_ONLINE)
+		return;
+
+	regs = get_irq_regs();
+
+	if (regs == 0)
+		return;
+
+	pc = profile_pc(regs);
+
+	pinfo = &(per_cpu(info, cpu));
+	pinfo->strlen = snprintf(pinfo->strbuf, LINE_SIZE, "%s,%lx", current->comm, pc);
+
+	if (user_mode(regs)) {
+		struct mm_struct *mm;
+		struct vm_area_struct *vma;
+		struct path *ppath;
+
+		mm = current->mm;
+		for (vma = find_vma(mm, pc); vma; vma = vma->vm_next) {
+
+			if (pc < vma->vm_start || pc >= vma->vm_end)
+				continue;
+
+			if (vma->vm_file) {
+				ppath = &(vma->vm_file->f_path);
+
+				if (vma->vm_flags & VM_DENYWRITE)
+					off = pc;
+				else
+					off = (vma->vm_pgoff << PAGE_SHIFT) + pc - vma->vm_start;
+
+				ret =
+				    snprintf(pinfo->strbuf + pinfo->strlen,
+					     LINE_SIZE - pinfo->strlen, ",%s,%lx",
+					     (char *)(ppath->dentry->d_name.name), off);
+				pinfo->strlen += ret;
+				outflag = 1;
+			} else {
+				/* must be an anonymous map */
+				ret =
+				    snprintf(pinfo->strbuf + pinfo->strlen,
+					     LINE_SIZE - pinfo->strlen, ",nofile,%lx", pc);
+				pinfo->strlen += ret;
+				outflag = 1;
+			}
+			break;
+		}
+	} else {
+		/* kernel mode code */
+		if (back_trace_depth > 0) {
+			pinfo->depth = back_trace_depth + 1;
+			kernel_backtrace(regs, pinfo);
+		} else
+			get_kernel_cookie(pc, pinfo);
+		outflag = 1;
+	}
+
+	/* check task is resolvable */
+	if (outflag == 0)
+		return;
+
+	if (back_trace_depth == 0)
+		cookie(pinfo->strbuf);
+	else
+		cookie2(pinfo->strbuf);
+}
+
+
+static void met_cookie_start(void)
+{
+	int	cpu = raw_smp_processor_id();
+	per_cpu(cpu_status, cpu) = CPU_ONLINE;
+	/* return; */
+}
+
+static void met_cookie_stop(void)
+{
+	/* return; */
+}
+
+
+static int met_cookie_process_argument(const char *arg, int len)
+{
+	unsigned int value;
+
+	if (met_parse_num(arg, &value, len) < 0) {
+		met_cookie.mode = 0;
+		return -EINVAL;
+	}
+
+	back_trace_depth = value;
+	met_cookie.mode = 1;
+
+	return 0;
+}
+
+static const char help[] =
+"  --cookie                              enable sampling task and PC\n"
+"  --cookie=N                            enable back trace (depth is N)\n";
+
+static int met_cookie_print_help(char *buf, int len)
+{
+	len = snprintf(buf, PAGE_SIZE, help);
+	return len;
+}
+
+
+static const char header[] =
+"# cookie: task_name,PC,cookie_name,offset\n"
+"met-info [000] 0.0: cookie_header: task_name,PC,cookie_name,offset\n";
+
+static const char header2_1[] = "# cookie2: task_name,PC,cookie,offset";
+static const char header2_2[] = "met-info [000] 0.0: cookie2_header: task_name,PC,cookie,offset";
+
+static int met_cookie_print_header(char *buf, int len)
+{
+	int i, ret;
+
+	if (back_trace_depth == 0) {
+		len = snprintf(buf, PAGE_SIZE, header);
+	} else {
+		len = snprintf(buf, PAGE_SIZE, header2_1);
+		for (i = 0; i < back_trace_depth; i++) {
+			ret = snprintf(buf + len, PAGE_SIZE, ",cookie%d,offset%d", i + 1, i + 1);
+			len += ret;
+		}
+		ret = snprintf(buf + len, PAGE_SIZE, "\n");
+		len += ret;
+
+		ret = snprintf(buf + len, PAGE_SIZE, header2_2);
+		len += ret;
+		for (i = 0; i < back_trace_depth; i++) {
+			ret = snprintf(buf + len, PAGE_SIZE, ",cookie%d,offset%d", i + 1, i + 1);
+			len += ret;
+		}
+		ret = snprintf(buf + len, PAGE_SIZE, "\n");
+		len += ret;
+	}
+
+	return len;
+}
+
+static void met_cookie_cpu_state_notify(long cpu, unsigned long action)
+{
+	per_cpu(cpu_status, cpu) = action;
+}
+
+struct metdevice met_cookie = {
+	.name = "cookie",
+	.type = MET_TYPE_PMU,
+	.cpu_related = 1,
+	.start = met_cookie_start,
+	.stop = met_cookie_stop,
+	.reset = reset_driver_stat,
+	.polling_interval = 1,
+	.timed_polling = met_cookie_polling,
+	.process_argument = met_cookie_process_argument,
+	.print_help = met_cookie_print_help,
+	.print_header = met_cookie_print_header,
+	.cpu_state_notify = met_cookie_cpu_state_notify,
+};
diff --git a/src/devtools/met-driver/4.9/common/core_plf_init.c b/src/devtools/met-driver/4.9/common/core_plf_init.c
new file mode 100644
index 0000000..e0393fe
--- /dev/null
+++ b/src/devtools/met-driver/4.9/common/core_plf_init.c
@@ -0,0 +1,360 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/kallsyms.h>
+#include "met_drv.h"
+#include "met_api_tbl.h"
+#include "interface.h"
+#include "core_plf_init.h"
+
+#undef	DEBUG
+
+#ifdef MET_GPU
+/*
+ *   GPU
+ */
+bool (*mtk_get_gpu_loading_symbol)(unsigned int *pLoading);
+bool (*mtk_get_gpu_block_symbol)(unsigned int *pBlock);
+bool (*mtk_get_gpu_idle_symbol)(unsigned int *pIdle);
+bool (*mtk_get_gpu_dvfs_from_symbol)(MTK_GPU_DVFS_TYPE *peType, unsigned long *pulFreq);
+bool (*mtk_get_gpu_sub_loading_symbol)(unsigned int *pLoading);
+bool (*mtk_get_3D_fences_count_symbol)(int *pi32Count);
+bool (*mtk_get_gpu_memory_usage_symbol)(unsigned int *pMemUsage);
+bool (*mtk_get_gpu_power_loading_symbol)(unsigned int *pLoading);
+bool (*mtk_get_custom_boost_gpu_freq_symbol)(unsigned long *pulFreq);
+bool (*mtk_get_custom_upbound_gpu_freq_symbol)(unsigned long *pulFreq);
+bool (*mtk_get_vsync_based_target_freq_symbol)(unsigned long *pulFreq);
+bool (*mtk_get_vsync_offset_event_status_symbol)(unsigned int *pui32EventStatus);
+bool (*mtk_get_vsync_offset_debug_status_symbol)(unsigned int *pui32EventStatus);
+bool (*mtk_enable_gpu_perf_monitor_symbol)(bool enable);
+bool (*mtk_get_gpu_pmu_init_symbol)(GPU_PMU *pmus, int pmu_size, int *ret_size);
+bool (*mtk_get_gpu_pmu_swapnreset_symbol)(GPU_PMU *pmus, int pmu_size);
+#if 0
+bool (*mtk_get_gpu_pmu_deinit_symbol)(void);
+bool (*mtk_get_gpu_pmu_swapnreset_stop_symbol)(void);
+#endif
+unsigned int (*mt_gpufreq_get_cur_freq_symbol)(void);
+unsigned int (*mt_gpufreq_get_thermal_limit_freq_symbol)(void);
+bool (*mtk_register_gpu_power_change_symbol)(const char *name, gpu_power_change_notify_fp callback);
+bool (*mtk_unregister_gpu_power_change_symbol)(const char *name);
+#endif /* MET_GPU */
+
+#ifdef MET_VCOREDVFS
+/*
+ *   VCORE DVFS
+ */
+#include <helio-dvfsrc.h>
+
+int (*vcorefs_get_num_opp_symbol)(void);
+int  (*vcorefs_get_opp_info_num_symbol)(void);
+char ** (*vcorefs_get_opp_info_name_symbol)(void);
+unsigned int * (*vcorefs_get_opp_info_symbol)(void);
+int  (*vcorefs_get_src_req_num_symbol)(void);
+char ** (*vcorefs_get_src_req_name_symbol)(void);
+unsigned int * (*vcorefs_get_src_req_symbol)(void);
+#endif /* MET_VCOREDVFS */
+
+#ifdef MET_EMI
+void *(*mt_cen_emi_base_get_symbol)(void);
+#endif /* MET_EMI */
+
+#ifdef MET_AP_EMI
+void *(*mt_cen_emi_base_get_symbol)(void);
+void *(*mt_chn_emi_base_get_symbol)(int channel);
+
+unsigned int (*get_dram_data_rate_symbol)(void);
+int (*get_ddr_type_symbol)(void);
+void *(*mt_dramc_chn_base_get_symbol)(int channel);
+void *(*mt_dramc_nao_chn_base_get_symbol)(int channel);
+void *(*mt_ddrphy_chn_base_get_symbol)(int channel);
+#endif /* MET_AP_EMI */
+
+
+#ifdef MET_PTPOD
+unsigned int (*mt_gpufreq_get_cur_volt_symbol)(void);
+unsigned int (*mt_cpufreq_get_cur_volt_symbol)(unsigned int cluster_id);
+#endif /* MET_PTPOD */
+
+#ifdef MET_THERMAL
+/*
+ *   Thermal & Thermal_cpu
+ */
+void (*mt_thermalsampler_registerCB_symbol)(met_thermalsampler_func pCB);
+int (*mtk_thermal_get_temp_symbol)(enum mtk_thermal_sensor_id id);
+int (*tscpu_get_cpu_temp_met_symbol)(enum MTK_THERMAL_SENSOR_CPU_ID_MET id);
+#endif
+
+static int met_symbol_get(void)
+{
+#define _MET_SYMBOL_GET(_func_name_) \
+	do { \
+		_func_name_##_symbol = (void *)symbol_get(_func_name_); \
+		if (_func_name_##_symbol == NULL) { \
+			pr_debug("MET ext. symbol : %s is not found!\n", #_func_name_); \
+			PR_BOOTMSG_ONCE("MET ext. symbol : %s is not found!\n", #_func_name_); \
+		} \
+	} while (0)
+
+
+#ifdef MET_GPU
+	_MET_SYMBOL_GET(mtk_get_gpu_loading);
+	_MET_SYMBOL_GET(mtk_get_gpu_block);
+	_MET_SYMBOL_GET(mtk_get_gpu_idle);
+	_MET_SYMBOL_GET(mtk_get_gpu_dvfs_from);
+	_MET_SYMBOL_GET(mtk_get_gpu_sub_loading);
+	_MET_SYMBOL_GET(mtk_get_3D_fences_count);
+	_MET_SYMBOL_GET(mtk_get_gpu_memory_usage);
+	_MET_SYMBOL_GET(mtk_get_gpu_power_loading);
+	_MET_SYMBOL_GET(mtk_get_custom_boost_gpu_freq);
+	_MET_SYMBOL_GET(mtk_get_custom_upbound_gpu_freq);
+	_MET_SYMBOL_GET(mtk_get_vsync_based_target_freq);
+	_MET_SYMBOL_GET(mtk_get_vsync_offset_event_status);
+	_MET_SYMBOL_GET(mtk_get_vsync_offset_debug_status);
+	_MET_SYMBOL_GET(mtk_enable_gpu_perf_monitor);
+	_MET_SYMBOL_GET(mtk_get_gpu_pmu_init);
+	_MET_SYMBOL_GET(mtk_get_gpu_pmu_swapnreset);
+	_MET_SYMBOL_GET(mt_gpufreq_get_cur_freq);
+	_MET_SYMBOL_GET(mt_gpufreq_get_thermal_limit_freq);
+	_MET_SYMBOL_GET(mtk_register_gpu_power_change);
+	_MET_SYMBOL_GET(mtk_unregister_gpu_power_change);
+#if 0
+	_MET_SYMBOL_GET(mtk_get_gpu_pmu_swapnreset_stop);
+	_MET_SYMBOL_GET(mtk_get_gpu_pmu_deinit);
+#endif
+#endif /* MET_GPU */
+
+#ifdef MET_VCOREDVFS
+	_MET_SYMBOL_GET(vcorefs_get_num_opp);
+	_MET_SYMBOL_GET(vcorefs_get_opp_info_num);
+	_MET_SYMBOL_GET(vcorefs_get_opp_info_name);
+	_MET_SYMBOL_GET(vcorefs_get_opp_info);
+	_MET_SYMBOL_GET(vcorefs_get_src_req_num);
+	_MET_SYMBOL_GET(vcorefs_get_src_req_name);
+	_MET_SYMBOL_GET(vcorefs_get_src_req);
+#endif
+
+#ifdef MET_EMI
+	_MET_SYMBOL_GET(mt_cen_emi_base_get);
+#endif
+
+#ifdef MET_AP_EMI
+	_MET_SYMBOL_GET(mt_cen_emi_base_get);
+	_MET_SYMBOL_GET(mt_chn_emi_base_get);
+
+	_MET_SYMBOL_GET(get_dram_data_rate);
+	_MET_SYMBOL_GET(get_ddr_type);
+	_MET_SYMBOL_GET(mt_dramc_chn_base_get);
+	_MET_SYMBOL_GET(mt_dramc_nao_chn_base_get);
+	_MET_SYMBOL_GET(mt_ddrphy_chn_base_get);
+#endif /* MET_AP_EMI */
+
+#ifdef MET_PTPOD
+	_MET_SYMBOL_GET(mt_gpufreq_get_cur_volt);
+	_MET_SYMBOL_GET(mt_cpufreq_get_cur_volt);
+#endif
+
+#ifdef MET_THERMAL
+	_MET_SYMBOL_GET(mt_thermalsampler_registerCB);
+	_MET_SYMBOL_GET(mtk_thermal_get_temp);
+	_MET_SYMBOL_GET(tscpu_get_cpu_temp_met);
+#endif
+
+	return 0;
+}
+
+static int met_symbol_put(void)
+{
+#define _MET_SYMBOL_PUT(_func_name_) { \
+		if (_func_name_##_symbol) { \
+			symbol_put(_func_name_); \
+			_func_name_##_symbol = NULL; \
+		} \
+	}
+
+#ifdef MET_GPU
+	_MET_SYMBOL_PUT(mtk_get_gpu_loading);
+	_MET_SYMBOL_PUT(mtk_get_gpu_block);
+	_MET_SYMBOL_PUT(mtk_get_gpu_idle);
+	_MET_SYMBOL_PUT(mtk_get_gpu_dvfs_from);
+	_MET_SYMBOL_PUT(mtk_get_gpu_sub_loading);
+	_MET_SYMBOL_PUT(mtk_get_3D_fences_count);
+	_MET_SYMBOL_PUT(mtk_get_gpu_memory_usage);
+	_MET_SYMBOL_PUT(mtk_get_gpu_power_loading);
+	_MET_SYMBOL_PUT(mtk_get_custom_boost_gpu_freq);
+	_MET_SYMBOL_PUT(mtk_get_custom_upbound_gpu_freq);
+	_MET_SYMBOL_PUT(mtk_get_vsync_based_target_freq);
+	_MET_SYMBOL_PUT(mtk_get_vsync_offset_event_status);
+	_MET_SYMBOL_PUT(mtk_get_vsync_offset_debug_status);
+	_MET_SYMBOL_PUT(mtk_enable_gpu_perf_monitor);
+	_MET_SYMBOL_PUT(mtk_get_gpu_pmu_init);
+	_MET_SYMBOL_PUT(mtk_get_gpu_pmu_swapnreset);
+	_MET_SYMBOL_PUT(mt_gpufreq_get_cur_freq);
+	_MET_SYMBOL_PUT(mt_gpufreq_get_thermal_limit_freq);
+	_MET_SYMBOL_PUT(mtk_register_gpu_power_change);
+	_MET_SYMBOL_PUT(mtk_unregister_gpu_power_change);
+#if 0
+	_MET_SYMBOL_PUT(mtk_get_gpu_pmu_swapnreset_stop);
+	_MET_SYMBOL_PUT(mtk_get_gpu_pmu_deinit);
+#endif
+#endif /* MET_GPU */
+
+#ifdef MET_VCOREDVFS
+	_MET_SYMBOL_PUT(vcorefs_get_num_opp);
+	_MET_SYMBOL_PUT(vcorefs_get_opp_info_num);
+	_MET_SYMBOL_PUT(vcorefs_get_opp_info_name);
+	_MET_SYMBOL_PUT(vcorefs_get_opp_info);
+	_MET_SYMBOL_PUT(vcorefs_get_src_req_num);
+	_MET_SYMBOL_PUT(vcorefs_get_src_req_name);
+	_MET_SYMBOL_PUT(vcorefs_get_src_req);
+#endif
+
+#ifdef MET_EMI
+	_MET_SYMBOL_PUT(mt_cen_emi_base_get);
+#endif
+
+#ifdef MET_AP_EMI
+	_MET_SYMBOL_PUT(mt_cen_emi_base_get);
+	_MET_SYMBOL_PUT(mt_chn_emi_base_get);
+
+	_MET_SYMBOL_PUT(get_dram_data_rate);
+	_MET_SYMBOL_PUT(get_ddr_type);
+	_MET_SYMBOL_PUT(mt_dramc_chn_base_get);
+	_MET_SYMBOL_PUT(mt_dramc_nao_chn_base_get);
+	_MET_SYMBOL_PUT(mt_ddrphy_chn_base_get);
+#endif /* MET_AP_EMI */
+
+#ifdef MET_PTPOD
+	_MET_SYMBOL_PUT(mt_gpufreq_get_cur_volt);
+	_MET_SYMBOL_PUT(mt_cpufreq_get_cur_volt);
+#endif
+
+#ifdef MET_THERMAL
+	_MET_SYMBOL_PUT(mt_thermalsampler_registerCB);
+	_MET_SYMBOL_PUT(mtk_thermal_get_temp);
+	_MET_SYMBOL_PUT(tscpu_get_cpu_temp_met);
+#endif
+
+
+	return 0;
+}
+
+int core_plf_init(void)
+{
+	/*initial met external symbol*/
+	met_symbol_get();
+
+#ifdef MET_GPU
+	met_register(&met_gpu);
+	met_register(&met_gpudvfs);
+	met_register(&met_gpumem);
+	met_register(&met_gpupwr);
+	met_register(&met_gpu_pmu);
+#endif
+
+#ifdef MET_VCOREDVFS
+	met_register(&met_vcoredvfs);
+#endif
+
+#ifdef MET_EMI
+	met_register(&met_sspm_emi);
+#endif
+
+#ifdef MET_AP_EMI
+	met_register(&met_ap_emi);
+#endif
+
+#ifdef MET_PTPOD
+	met_register(&met_ptpod);
+#endif
+
+#ifdef MET_WALLTIME
+	met_register(&met_wall_time);
+#endif
+
+#ifdef MTK_TINYSYS_SSPM_SUPPORT
+	met_register(&met_sspm_common);
+#endif
+
+#if defined(CONFIG_MTK_TINYSYS_SSPM_SUPPORT) && defined(ONDIEMET_SUPPORT)
+#ifdef MET_SSPM_WALLTIME
+	met_register(&met_sspm_walltime);
+#endif
+#endif /* CONFIG_MTK_TINYSYS_SSPM_SUPPORT && ONDIEMET_SUPPORT */
+
+#ifdef MET_CPUDSU
+	met_register(&met_cpudsu);
+#endif
+
+#ifdef MET_THERMAL
+	met_register(&met_thermal);
+	met_register(&met_thermal_cpu);
+#endif
+
+	return 0;
+}
+
+void core_plf_exit(void)
+{
+	/*release met external symbol*/
+	met_symbol_put();
+
+#ifdef MET_GPU
+	met_deregister(&met_gpu);
+	met_deregister(&met_gpudvfs);
+	met_deregister(&met_gpumem);
+	met_deregister(&met_gpupwr);
+	met_deregister(&met_gpu_pmu);
+#endif
+
+#ifdef MET_VCOREDVFS
+	met_deregister(&met_vcoredvfs);
+#endif
+
+#ifdef MET_EMI
+	met_deregister(&met_sspm_emi);
+#endif
+
+#ifdef MET_AP_EMI
+	met_deregister(&met_ap_emi);
+#endif
+
+#ifdef MET_PTPOD
+	met_deregister(&met_ptpod);
+#endif
+
+#ifdef MET_WALLTIME
+	met_deregister(&met_wall_time);
+#endif
+
+#ifdef MTK_TINYSYS_SSPM_SUPPORT
+	met_deregister(&met_sspm_common);
+#endif
+
+#if defined(CONFIG_MTK_TINYSYS_SSPM_SUPPORT) && defined(ONDIEMET_SUPPORT)
+#ifdef MET_SSPM_WALLTIME
+	met_deregister(&met_sspm_walltime);
+#endif
+#endif /* CONFIG_MTK_TINYSYS_SSPM_SUPPORT && ONDIEMET_SUPPORT */
+
+#ifdef MET_CPUDSU
+	met_deregister(&met_cpudsu);
+#endif
+
+#ifdef MET_THERMAL
+	met_deregister(&met_thermal);
+	met_deregister(&met_thermal_cpu);
+#endif
+}
diff --git a/src/devtools/met-driver/4.9/common/core_plf_init.h b/src/devtools/met-driver/4.9/common/core_plf_init.h
new file mode 100644
index 0000000..00e21f0
--- /dev/null
+++ b/src/devtools/met-driver/4.9/common/core_plf_init.h
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __CORE_PLF_INIT_H__
+#define __CORE_PLF_INIT_H__
+
+extern struct miscdevice met_device;
+
+/*
+ *   MET External Symbol
+ */
+
+#ifdef MET_GPU
+/*
+ *   GPU
+ */
+#include <mtk_gpu_utility.h>
+#include <mtk_gpufreq.h>
+
+extern bool mtk_get_gpu_loading(unsigned int *pLoading);
+extern bool mtk_get_gpu_block(unsigned int *pBlock);
+extern bool mtk_get_gpu_idle(unsigned int *pIdle);
+extern bool mtk_get_gpu_dvfs_from(MTK_GPU_DVFS_TYPE *peType, unsigned long *pulFreq);
+extern bool mtk_get_gpu_sub_loading(unsigned int *pLoading);
+extern bool mtk_get_3D_fences_count(int *pi32Count);
+extern bool mtk_get_gpu_memory_usage(unsigned int *pMemUsage);
+extern bool mtk_get_gpu_power_loading(unsigned int *pLoading);
+extern bool mtk_get_custom_boost_gpu_freq(unsigned int *pui32FreqLevel);
+extern bool mtk_get_custom_upbound_gpu_freq(unsigned int *pui32FreqLevel);
+extern bool mtk_get_vsync_based_target_freq(unsigned long *pulFreq);
+extern bool mtk_get_vsync_offset_event_status(unsigned int *pui32EventStatus);
+extern bool mtk_get_vsync_offset_debug_status(unsigned int *pui32EventStatus);
+extern bool mtk_enable_gpu_perf_monitor(bool enable);
+extern bool mtk_get_gpu_pmu_init(GPU_PMU *pmus, int pmu_size, int *ret_size);
+extern bool mtk_get_gpu_pmu_swapnreset(GPU_PMU *pmus, int pmu_size);
+
+extern bool (*mtk_get_gpu_loading_symbol)(unsigned int *pLoading);
+extern bool (*mtk_get_gpu_block_symbol)(unsigned int *pBlock);
+extern bool (*mtk_get_gpu_idle_symbol)(unsigned int *pIdle);
+extern bool (*mtk_get_gpu_dvfs_from_symbol)(MTK_GPU_DVFS_TYPE *peType, unsigned long *pulFreq);
+extern bool (*mtk_get_gpu_sub_loading_symbol)(unsigned int *pLoading);
+extern bool (*mtk_get_3D_fences_count_symbol)(int *pi32Count);
+extern bool (*mtk_get_gpu_memory_usage_symbol)(unsigned int *pMemUsage);
+extern bool (*mtk_get_gpu_power_loading_symbol)(unsigned int *pLoading);
+extern bool (*mtk_get_custom_boost_gpu_freq_symbol)(unsigned long *pulFreq);
+extern bool (*mtk_get_custom_upbound_gpu_freq_symbol)(unsigned long *pulFreq);
+extern bool (*mtk_get_vsync_based_target_freq_symbol)(unsigned long *pulFreq);
+extern bool (*mtk_get_vsync_offset_event_status_symbol)(unsigned int *pui32EventStatus);
+extern bool (*mtk_get_vsync_offset_debug_status_symbol)(unsigned int *pui32EventStatus);
+extern bool (*mtk_enable_gpu_perf_monitor_symbol)(bool enable);
+extern bool (*mtk_get_gpu_pmu_init_symbol)(GPU_PMU *pmus, int pmu_size, int *ret_size);
+extern bool (*mtk_get_gpu_pmu_swapnreset_symbol)(GPU_PMU *pmus, int pmu_size);
+extern bool (*mtk_get_gpu_pmu_deinit_symbol)(void);
+extern bool (*mtk_get_gpu_pmu_swapnreset_stop_symbol)(void);
+
+extern bool mtk_register_gpu_power_change(const char *name, gpu_power_change_notify_fp callback);
+extern bool mtk_unregister_gpu_power_change(const char *name);
+extern bool (*mtk_register_gpu_power_change_symbol)(const char *name,
+					gpu_power_change_notify_fp callback);
+extern bool (*mtk_unregister_gpu_power_change_symbol)(const char *name);
+
+
+extern unsigned int (*mt_gpufreq_get_cur_freq_symbol)(void);
+extern unsigned int (*mt_gpufreq_get_thermal_limit_freq_symbol)(void);
+
+extern struct metdevice met_gpu;
+extern struct metdevice met_gpudvfs;
+extern struct metdevice met_gpumem;
+extern struct metdevice met_gpupwr;
+extern struct metdevice met_gpu_pmu;
+#endif /* MET_GPU */
+
+
+#ifdef MET_VCOREDVFS
+/*
+ *   VCORE DVFS
+ */
+extern int vcorefs_get_num_opp(void);
+extern int  vcorefs_get_opp_info_num(void);
+extern char ** vcorefs_get_opp_info_name(void);
+extern unsigned int * vcorefs_get_opp_info(void);
+extern int  vcorefs_get_src_req_num(void);
+extern char ** vcorefs_get_src_req_name(void);
+extern unsigned int * vcorefs_get_src_req(void);
+
+extern int (*vcorefs_get_num_opp_symbol)(void);
+extern int  (*vcorefs_get_opp_info_num_symbol)(void);
+extern char ** (*vcorefs_get_opp_info_name_symbol)(void);
+extern unsigned int * (*vcorefs_get_opp_info_symbol)(void);
+extern int  (*vcorefs_get_src_req_num_symbol)(void);
+extern char ** (*vcorefs_get_src_req_name_symbol)(void);
+extern unsigned int * (*vcorefs_get_src_req_symbol)(void);
+
+extern struct metdevice met_vcoredvfs;
+#endif /* MET_VCOREDVFS */
+
+
+#ifdef MET_EMI
+extern void *mt_cen_emi_base_get(void);
+extern void *(*mt_cen_emi_base_get_symbol)(void);
+
+extern struct metdevice met_sspm_emi;
+#endif /* MET_EMI */
+
+
+#ifdef MET_AP_EMI
+extern void *mt_cen_emi_base_get(void);
+extern void *mt_chn_emi_base_get(int channel);
+
+extern unsigned int get_dram_data_rate(void);
+extern int get_ddr_type(void);
+extern void *mt_dramc_chn_base_get(int channel);
+extern void *mt_dramc_nao_chn_base_get(int channel);
+extern void *mt_ddrphy_chn_base_get(int channel);
+
+extern void *(*mt_cen_emi_base_get_symbol)(void);
+extern void *(*mt_chn_emi_base_get_symbol)(int channel);
+
+extern unsigned int (*get_dram_data_rate_symbol)(void);
+extern int (*get_ddr_type_symbol)(void);
+extern void *(*mt_dramc_chn_base_get_symbol)(int channel);
+extern void *(*mt_dramc_nao_chn_base_get_symbol)(int channel);
+extern void *(*mt_ddrphy_chn_base_get_symbol)(int channel);
+
+extern struct metdevice met_ap_emi;
+#endif /* MET_AP_EMI */
+
+
+#ifdef MET_PTPOD
+#include <mtk_gpufreq.h>
+#include <mach/mtk_cpufreq_api.h>
+#include <mtk_cpufreq_config.h>
+
+extern unsigned int (*mt_gpufreq_get_cur_volt_symbol)(void);
+extern unsigned int (*mt_cpufreq_get_cur_volt_symbol)(unsigned int cluster_id);
+
+extern struct metdevice met_ptpod;
+#endif /* MET_PTPOD */
+
+
+#ifdef MET_THERMAL
+/*
+ *   Thermal & Thermal_CPU
+ */
+#include <mtk_thermal_monitor.h>
+#include <mtk_thermal.h>
+
+typedef void (*met_thermalsampler_func) (void /*struct work_struct * */);
+
+extern void mt_thermalsampler_registerCB(met_thermalsampler_func pCB);
+extern int mtk_thermal_get_temp(enum mtk_thermal_sensor_id id);
+extern int tscpu_get_cpu_temp_met(enum MTK_THERMAL_SENSOR_CPU_ID_MET id);
+
+extern void (*mt_thermalsampler_registerCB_symbol)(met_thermalsampler_func pCB);
+extern int (*mtk_thermal_get_temp_symbol)(enum mtk_thermal_sensor_id id);
+extern int (*tscpu_get_cpu_temp_met_symbol)(enum MTK_THERMAL_SENSOR_CPU_ID_MET id);
+#endif
+
+#ifdef MET_WALLTIME
+extern struct metdevice met_wall_time;
+#endif
+
+#ifdef MTK_TINYSYS_SSPM_SUPPORT
+extern struct metdevice met_sspm_common;
+#endif /* MTK_TINYSYS_SSPM_SUPPORT */
+
+#if defined(CONFIG_MTK_TINYSYS_SSPM_SUPPORT) && defined(ONDIEMET_SUPPORT)
+#ifdef MET_SSPM_WALLTIME
+extern struct metdevice met_sspm_walltime;
+#endif
+#endif /* CONFIG_MTK_TINYSYS_SSPM_SUPPORT */
+
+#ifdef MET_CPUDSU
+extern struct metdevice met_cpudsu;
+#endif
+
+#ifdef MET_THERMAL
+extern struct metdevice met_thermal;
+extern struct metdevice met_thermal_cpu;
+#endif
+
+#endif /*__CORE_PLF_INIT_H__*/
diff --git a/src/devtools/met-driver/4.9/common/core_plf_trace.c b/src/devtools/met-driver/4.9/common/core_plf_trace.c
new file mode 100644
index 0000000..0dc7090
--- /dev/null
+++ b/src/devtools/met-driver/4.9/common/core_plf_trace.c
@@ -0,0 +1,322 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+#include "met_drv.h"
+#include "interface.h"
+#include "trace.h"
+
+char *ms_formatH(char *__restrict__ buf, unsigned char cnt, unsigned int *__restrict__ value)
+{
+	char *s = buf;
+	int len;
+
+	if (cnt == 0) {
+		buf[0] = '\0';
+		return buf;
+	}
+
+	switch (cnt % 4) {
+	case 1:
+		len = sprintf(s, "%x", value[0]);
+		s += len;
+		value += 1;
+		cnt -= 1;
+		break;
+	case 2:
+		len = sprintf(s, "%x,%x", value[0], value[1]);
+		s += len;
+		value += 2;
+		cnt -= 2;
+		break;
+	case 3:
+		len = sprintf(s, "%x,%x,%x", value[0], value[1], value[2]);
+		s += len;
+		value += 3;
+		cnt -= 3;
+		break;
+	case 0:
+		len = sprintf(s, "%x,%x,%x,%x", value[0], value[1], value[2], value[3]);
+		s += len;
+		value += 4;
+		cnt -= 4;
+		break;
+	}
+
+	while (cnt) {
+		len = sprintf(s, ",%x,%x,%x,%x", value[0], value[1], value[2], value[3]);
+		s += len;
+		value += 4;
+		cnt -= 4;
+	}
+
+	s[0] = '\0';
+
+	return s;
+}
+EXPORT_SYMBOL(ms_formatH);
+
+char *ms_formatD(char *__restrict__ buf, unsigned char cnt, unsigned int *__restrict__ value)
+{
+	char *s = buf;
+	int len;
+
+	if (cnt == 0) {
+		buf[0] = '\0';
+		return buf;
+	}
+
+	switch (cnt % 4) {
+	case 1:
+		len = sprintf(s, "%u", value[0]);
+		s += len;
+		value += 1;
+		cnt -= 1;
+		break;
+	case 2:
+		len = sprintf(s, "%u,%u", value[0], value[1]);
+		s += len;
+		value += 2;
+		cnt -= 2;
+		break;
+	case 3:
+		len = sprintf(s, "%u,%u,%u", value[0], value[1], value[2]);
+		s += len;
+		value += 3;
+		cnt -= 3;
+		break;
+	case 0:
+		len = sprintf(s, "%u,%u,%u,%u", value[0], value[1], value[2], value[3]);
+		s += len;
+		value += 4;
+		cnt -= 4;
+		break;
+	}
+
+	while (cnt) {
+		len = sprintf(s, ",%u,%u,%u,%u", value[0], value[1], value[2], value[3]);
+		s += len;
+		value += 4;
+		cnt -= 4;
+	}
+
+	s[0] = '\0';
+
+	return s;
+}
+EXPORT_SYMBOL(ms_formatD);
+
+char *ms_formatH_ulong(char *__restrict__ buf, unsigned char cnt, unsigned long *__restrict__ value)
+{
+	char *s = buf;
+	int len;
+
+	if (cnt == 0) {
+		buf[0] = '\0';
+		return buf;
+	}
+
+	switch (cnt % 4) {
+	case 1:
+		len = sprintf(s, "%lx", value[0]);
+		s += len;
+		value += 1;
+		cnt -= 1;
+		break;
+	case 2:
+		len = sprintf(s, "%lx,%lx", value[0], value[1]);
+		s += len;
+		value += 2;
+		cnt -= 2;
+		break;
+	case 3:
+		len = sprintf(s, "%lx,%lx,%lx", value[0], value[1], value[2]);
+		s += len;
+		value += 3;
+		cnt -= 3;
+		break;
+	case 0:
+		len = sprintf(s, "%lx,%lx,%lx,%lx", value[0], value[1], value[2], value[3]);
+		s += len;
+		value += 4;
+		cnt -= 4;
+		break;
+	}
+
+	while (cnt) {
+		len = sprintf(s, ",%lx,%lx,%lx,%lx", value[0], value[1], value[2], value[3]);
+		s += len;
+		value += 4;
+		cnt -= 4;
+	}
+
+	s[0] = '\0';
+
+	return buf;
+}
+EXPORT_SYMBOL(ms_formatH_ulong);
+
+char *ms_formatD_ulong(char *__restrict__ buf, unsigned char cnt, unsigned long *__restrict__ value)
+{
+	char *s = buf;
+	int len;
+
+	if (cnt == 0) {
+		buf[0] = '\0';
+		return buf;
+	}
+
+	switch (cnt % 4) {
+	case 1:
+		len = sprintf(s, "%lu", value[0]);
+		s += len;
+		value += 1;
+		cnt -= 1;
+		break;
+	case 2:
+		len = sprintf(s, "%lu,%lu", value[0], value[1]);
+		s += len;
+		value += 2;
+		cnt -= 2;
+		break;
+	case 3:
+		len = sprintf(s, "%lu,%lu,%lu", value[0], value[1], value[2]);
+		s += len;
+		value += 3;
+		cnt -= 3;
+		break;
+	case 0:
+		len = sprintf(s, "%lu,%lu,%lu,%lu", value[0], value[1], value[2], value[3]);
+		s += len;
+		value += 4;
+		cnt -= 4;
+		break;
+	}
+
+	while (cnt) {
+		len = sprintf(s, ",%lu,%lu,%lu,%lu", value[0], value[1], value[2], value[3]);
+		s += len;
+		value += 4;
+		cnt -= 4;
+	}
+
+	s[0] = '\0';
+
+	return buf;
+}
+EXPORT_SYMBOL(ms_formatD_ulong);
+
+char *ms_formatH_EOL(char *__restrict__ buf, unsigned char cnt, unsigned int *__restrict__ value)
+{
+	char *s = buf;
+	int len;
+
+	if (cnt == 0) {
+		buf[0] = '\0';
+		return buf;
+	}
+
+	switch (cnt % 4) {
+	case 1:
+		len = sprintf(s, "%x", value[0]);
+		s += len;
+		value += 1;
+		cnt -= 1;
+		break;
+	case 2:
+		len = sprintf(s, "%x,%x", value[0], value[1]);
+		s += len;
+		value += 2;
+		cnt -= 2;
+		break;
+	case 3:
+		len = sprintf(s, "%x,%x,%x", value[0], value[1], value[2]);
+		s += len;
+		value += 3;
+		cnt -= 3;
+		break;
+	case 0:
+		len = sprintf(s, "%x,%x,%x,%x", value[0], value[1], value[2], value[3]);
+		s += len;
+		value += 4;
+		cnt -= 4;
+		break;
+	}
+
+	while (cnt) {
+		len = sprintf(s, ",%x,%x,%x,%x", value[0], value[1], value[2], value[3]);
+		s += len;
+		value += 4;
+		cnt -= 4;
+	}
+
+	s[0] = '\n';
+	s[1] = '\0';
+
+	return s + 1;
+}
+EXPORT_SYMBOL(ms_formatH_EOL);
+
+char *ms_formatD_EOL(char *__restrict__ buf, unsigned char cnt, unsigned int *__restrict__ value)
+{
+	char *s = buf;
+	int len;
+
+	if (cnt == 0) {
+		buf[0] = '\0';
+		return buf;
+	}
+
+	switch (cnt % 4) {
+	case 1:
+		len = sprintf(s, "%u", value[0]);
+		s += len;
+		value += 1;
+		cnt -= 1;
+		break;
+	case 2:
+		len = sprintf(s, "%u,%u", value[0], value[1]);
+		s += len;
+		value += 2;
+		cnt -= 2;
+		break;
+	case 3:
+		len = sprintf(s, "%u,%u,%u", value[0], value[1], value[2]);
+		s += len;
+		value += 3;
+		cnt -= 3;
+		break;
+	case 0:
+		len = sprintf(s, "%u,%u,%u,%u", value[0], value[1], value[2], value[3]);
+		s += len;
+		value += 4;
+		cnt -= 4;
+		break;
+	}
+
+	while (cnt) {
+		len = sprintf(s, ",%u,%u,%u,%u", value[0], value[1], value[2], value[3]);
+		s += len;
+		value += 4;
+		cnt -= 4;
+	}
+
+	s[0] = '\n';
+	s[1] = '\0';
+
+	return s + 1;
+}
+EXPORT_SYMBOL(ms_formatD_EOL);
+
diff --git a/src/devtools/met-driver/4.9/common/core_plf_trace.h b/src/devtools/met-driver/4.9/common/core_plf_trace.h
new file mode 100644
index 0000000..5c89efd
--- /dev/null
+++ b/src/devtools/met-driver/4.9/common/core_plf_trace.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _CORE_PLF_TRACE_H_
+#define _CORE_PLF_TRACE_H_
+
+#define	HVALUE_SIZE	9	/* 8 chars (max value ffffffff) + 1 char (',' or NULL) */
+#define	DVALUE_SIZE	12	/* 10 chars (max value 4,294,967,295) + 1 char (',' or NULL) */
+
+char *ms_formatH(char *__restrict__ buf, unsigned char cnt, unsigned int *__restrict__ value);
+char *core_ms_formatD(char *__restrict__ buf, unsigned char cnt, unsigned int *__restrict__ value);
+char *ms_formatH_ulong(char *__restrict__ buf, unsigned char cnt,
+		       unsigned long *__restrict__ value);
+char *ms_formatD_ulong(char *__restrict__ buf, unsigned char cnt,
+		       unsigned long *__restrict__ value);
+char *ms_formatH_EOL(char *__restrict__ buf, unsigned char cnt, unsigned int *__restrict__ value);
+char *ms_formatD_EOL(char *__restrict__ buf, unsigned char cnt, unsigned int *__restrict__ value);
+
+#endif	/* _CORE_PLF_TRACE_H_ */
diff --git a/src/devtools/met-driver/4.9/common/cpu_dsu.c b/src/devtools/met-driver/4.9/common/cpu_dsu.c
new file mode 100644
index 0000000..b30ab0d
--- /dev/null
+++ b/src/devtools/met-driver/4.9/common/cpu_dsu.c
@@ -0,0 +1,360 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/perf_event.h>
+#include "met_drv.h"
+#include "met_kernel_symbol.h"
+#include "interface.h"
+#include "trace.h"
+#include "cpu_dsu.h"
+#include "core_plf_init.h"
+
+
+struct cpu_dsu_hw *cpu_dsu;
+static int counter_cnt;
+static struct kobject *kobj_dsu;
+static int nr_arg;
+static unsigned long long perfCurr[MXNR_DSU_EVENTS];
+static unsigned long long perfPrev[MXNR_DSU_EVENTS];
+static int perfCntFirst[MXNR_DSU_EVENTS];
+static struct perf_event * pevent[MXNR_DSU_EVENTS];
+static struct perf_event_attr pevent_attr[MXNR_DSU_EVENTS];
+static unsigned int perf_device_type = 7;
+static ssize_t perf_type_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%d\n", perf_device_type);
+}
+
+static ssize_t perf_type_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t n)
+{
+	if (kstrtouint(buf, 0, &perf_device_type) != 0)
+		return -EINVAL;
+
+	return n;
+}
+static struct kobj_attribute perf_type_attr = __ATTR(perf_type, 0664, perf_type_show, perf_type_store);
+
+noinline void mp_dsu(unsigned char cnt, unsigned int *value)
+{
+	MET_GENERAL_PRINT(MET_TRACE, cnt, value);
+}
+
+static void dummy_handler(struct perf_event *event, struct perf_sample_data *data,
+			  struct pt_regs *regs)
+{
+	/*
+	 * Required as perf_event_create_kernel_counter() requires an overflow handler,
+	 * even though all we do is poll.
+	 */
+}
+
+static void perf_cpudsu_polling(unsigned long long stamp, int cpu)
+{
+	int	event_count = cpu_dsu->event_count;
+	struct met_dsu	*pmu = cpu_dsu->pmu;
+	int	i, count;
+	unsigned long long	delta;
+	struct perf_event	*ev;
+	unsigned int pmu_value[MXNR_DSU_EVENTS];
+
+	count = 0;
+	for (i = 0; i < event_count; i++) {
+		if (pmu[i].mode == 0)
+			continue;
+
+		ev = pevent[i];
+		if ((ev != NULL) && (ev->state == PERF_EVENT_STATE_ACTIVE)) {
+			perfCurr[i] = met_perf_event_read_local_symbol(ev);
+			delta = (perfCurr[i] - perfPrev[i]);
+			perfPrev[i] = perfCurr[i];
+			if (perfCntFirst[i] == 1) {
+				/* we shall omit delta counter when we get first counter */
+				perfCntFirst[i] = 0;
+				continue;
+			}
+			pmu_value[count] = (unsigned int)delta;
+			count++;
+		}
+	}
+
+	if (count == counter_cnt)
+		mp_dsu(count, pmu_value);
+}
+
+static int perf_thread_set_perf_events(unsigned int cpu)
+{
+	int			i, size;
+	struct perf_event	*ev;
+	struct perf_event_attr	*ev_attr;
+	int event_count = cpu_dsu->event_count;
+	struct met_dsu *pmu = cpu_dsu->pmu;
+
+	size = sizeof(struct perf_event_attr);
+
+	for (i = 0; i < event_count; i++) {
+		pevent[i] = NULL;
+		if (!pmu[i].mode)
+			continue;	/* Skip disabled counters */
+		perfPrev[i] = 0;
+		perfCurr[i] = 0;
+		ev_attr = pevent_attr+i;
+		memset(ev_attr, 0, size);
+		ev_attr->config = pmu[i].event;
+		ev_attr->type = perf_device_type;
+		ev_attr->size = size;
+		ev_attr->sample_period = 0;
+		ev_attr->pinned = 1;
+
+		ev = perf_event_create_kernel_counter(ev_attr, cpu, NULL, dummy_handler, NULL);
+		if (IS_ERR(ev))
+			continue;
+		if (ev->state != PERF_EVENT_STATE_ACTIVE) {
+			perf_event_release_kernel(ev);
+			continue;
+		}
+		pevent[i] = ev;
+		if (ev != NULL)
+			perf_event_enable(ev);
+	}	/* for all PMU counter */
+	return 0;
+}
+
+void met_perf_cpudsu_down(void)
+{
+	int i;
+	struct perf_event *ev;
+	int event_count;
+	struct met_dsu *pmu;
+
+	if (met_cpudsu.mode == 0)
+		return;
+	event_count = cpu_dsu->event_count;
+	pmu = cpu_dsu->pmu;
+	for (i = 0; i < event_count; i++) {
+		if (!pmu[i].mode)
+			continue;
+		ev = pevent[i];
+		if ((ev != NULL) && (ev->state == PERF_EVENT_STATE_ACTIVE)) {
+			perf_event_disable(ev);
+			perf_event_release_kernel(ev);
+		}
+		pevent[i] = NULL;
+	}
+	//perf_delayed_work_setup = NULL;
+}
+
+inline static void met_perf_cpudsu_start(int cpu)
+{
+	if (met_cpudsu.mode == 0)
+		return;
+	if (cpu != 0)
+		return;
+	perf_thread_set_perf_events(cpu);
+}
+
+static int cpudsu_create_subfs(struct kobject *parent)
+{
+	int ret = 0;
+	cpu_dsu = cpu_dsu_hw_init();
+	if (cpu_dsu == NULL) {
+		PR_BOOTMSG("Failed to init CPU PMU HW!!\n");
+		return -ENODEV;
+	}
+	kobj_dsu = parent;
+	ret = sysfs_create_file(kobj_dsu, &perf_type_attr.attr);
+	if (ret != 0) {
+		PR_BOOTMSG("Failed to create perf_type in sysfs\n");
+		goto out;
+	}
+ out:
+	return ret;
+}
+
+static void cpudsu_delete_subfs(void)
+{
+}
+
+void met_perf_cpudsu_polling(unsigned long long stamp, int cpu)
+{
+	perf_cpudsu_polling(stamp, cpu);
+}
+
+static void cpudsu_start(void)
+{
+	int	cpu = raw_smp_processor_id();
+	for_each_online_cpu(cpu)
+		met_perf_cpudsu_start(cpu);
+}
+
+static void cpudsu_stop(void)
+{
+	met_perf_cpudsu_down();
+}
+
+static const char header[] =
+	"met-info [000] 0.0: met_dsu_pmu_header: DSU";
+
+static const char help[] =
+	"  --dsu=EVENT         select DSU-PMU events.\n"
+	"                      you can enable at most \"%d general purpose events\"\n";
+
+static int cpudsu_print_help(char *buf, int len)
+{
+	return snprintf(buf, PAGE_SIZE, help, cpu_dsu->event_count);
+}
+
+static int reset_driver_stat(void)
+{
+	int		i;
+	int		event_count;
+	struct met_dsu	*pmu;
+
+	met_cpudsu.mode = 0;
+	event_count = cpu_dsu->event_count;
+	pmu = cpu_dsu->pmu;
+	counter_cnt = 0;
+	nr_arg = 0;
+	for (i = 0; i < event_count; i++) {
+		pmu[i].mode = MODE_DISABLED;
+		pmu[i].event = 0;
+		pmu[i].freq = 0;
+	}
+	return 0;
+}
+
+static int cpudsu_print_header(char *buf, int len)
+{
+	int		first;
+	int		i, ret;
+	int		event_count;
+	struct met_dsu	*pmu;
+	ret = 0;
+
+	ret += snprintf(buf + ret, PAGE_SIZE - ret, "# mp_dsu: pmu_value1, ...\n");
+	event_count = cpu_dsu->event_count;
+	pmu = cpu_dsu->pmu;
+	first = 1;
+	for (i = 0; i < event_count; i++) {
+		if (pmu[i].mode == 0)
+			continue;
+		if (first) {
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, header);
+			first = 0;
+		}
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, ",0x%x", pmu[i].event);
+		pmu[i].mode = 0;
+	}
+	ret += snprintf(buf + ret, PAGE_SIZE - ret, "\n");
+	reset_driver_stat();
+	return ret;
+}
+
+static int met_parse_num_list(char *arg, int len, int *list, int list_cnt)
+{
+	int	nr_num = 0;
+	char	*num;
+	int	num_len;
+
+	/* search ',' as the splitter */
+	while (len) {
+		num = arg;
+		num_len = 0;
+		if (list_cnt <= 0)
+			return -1;
+		while (len) {
+			len--;
+			if (*arg == ',') {
+				*(arg++) = '\0';
+				break;
+			}
+			arg++;
+			num_len++;
+		}
+		if (met_parse_num(num, list, num_len) < 0)
+			return -1;
+		list++;
+		list_cnt--;
+		nr_num++;
+	}
+	return nr_num;
+}
+
+static int cpudsu_process_argument(const char *arg, int len)
+{
+	int		nr_events, event_list[MXNR_DSU_EVENTS];
+	int		i;
+	int		nr_counters;
+	struct met_dsu	*pmu;
+	int		arg_nr;
+	int		counters;
+	int		event_no;
+
+	/* get event_list */
+	if ((nr_events = met_parse_num_list((char*)arg, len, event_list, ARRAY_SIZE(event_list))) <= 0)
+		goto arg_out;
+
+	/* for each cpu in cpu_list, add all the events in event_list */
+	nr_counters = cpu_dsu->event_count;
+	pmu = cpu_dsu->pmu;
+	arg_nr = nr_arg;
+
+	/*
+	 * setup nr_counters for linux native perf mode.
+	 * because the selected events are stored in pmu,
+	 * so nr_counters can't large then event count in pmu.
+	 */
+	counters = perf_num_counters();
+	if (counters < nr_counters)
+		nr_counters = counters;
+
+	if (nr_counters == 0)
+		goto arg_out;
+
+	for (i = 0; i < nr_events; i++) {
+		event_no = event_list[i];
+		/*
+		 * check if event is duplicate,
+		 * but may not include 0xff when met_cpu_dsu_method == 0.
+		 */
+		if (cpu_dsu->check_event(pmu, arg_nr, event_no) < 0)
+			goto arg_out;
+		if (arg_nr >= nr_counters)
+			goto arg_out;
+		pmu[arg_nr].mode = MODE_POLLING;
+		pmu[arg_nr].event = event_no;
+		pmu[arg_nr].freq = 0;
+		arg_nr++;
+		counter_cnt++;
+	}
+	nr_arg = arg_nr;
+	met_cpudsu.mode = 1;
+	return 0;
+
+arg_out:
+	reset_driver_stat();
+	return -EINVAL;
+}
+
+struct metdevice met_cpudsu = {
+	.name = "dsu",
+	.type = MET_TYPE_PMU,
+	.cpu_related = 0,
+	.create_subfs = cpudsu_create_subfs,
+	.delete_subfs = cpudsu_delete_subfs,
+	.start = cpudsu_start,
+	.stop = cpudsu_stop,
+	.polling_interval = 1,
+	.timed_polling = met_perf_cpudsu_polling,
+	.print_help = cpudsu_print_help,
+	.print_header = cpudsu_print_header,
+	.process_argument = cpudsu_process_argument
+};
diff --git a/src/devtools/met-driver/4.9/common/cpu_dsu.h b/src/devtools/met-driver/4.9/common/cpu_dsu.h
new file mode 100644
index 0000000..084c81a
--- /dev/null
+++ b/src/devtools/met-driver/4.9/common/cpu_dsu.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _CPU_DSU_H_
+#define _CPU_DSU_H_
+
+#include <linux/device.h>
+
+#define MODE_DISABLED	0
+#define MODE_INTERRUPT	1
+#define MODE_POLLING	2
+
+#define MXNR_CPU	NR_CPUS
+
+#define	MXNR_DSU_EVENTS	8	/* max number of pmu counter for armv8 is 6+1 */
+struct met_dsu {
+	unsigned char mode;
+	unsigned short event;
+	unsigned long freq;
+	struct kobject *kobj_cpu_dsu;
+};
+
+struct cpu_dsu_hw {
+	const char *name;
+	int nr_cnt;
+	int (*check_event)(struct met_dsu *pmu, int idx, int event);
+	void (*start)(struct met_dsu *pmu, int count);
+	void (*stop)(int count);
+	unsigned int (*polling)(struct met_dsu *pmu, int count, unsigned int *pmu_value);
+	struct met_dsu *pmu;
+	int event_count;
+};
+
+
+struct cpu_dsu_hw *cpu_dsu_hw_init(void);
+
+
+
+#endif	/* _CPU_DSU_H_ */
diff --git a/src/devtools/met-driver/4.9/common/cpu_pmu.c b/src/devtools/met-driver/4.9/common/cpu_pmu.c
new file mode 100644
index 0000000..8b16172
--- /dev/null
+++ b/src/devtools/met-driver/4.9/common/cpu_pmu.c
@@ -0,0 +1,810 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/cpu.h>
+#include <linux/cpu_pm.h>
+#include <linux/perf_event.h>
+
+#if (defined(CONFIG_ARM64) || defined(CONFIG_ARM))
+#include <linux/platform_device.h>
+#include <linux/perf/arm_pmu.h>
+#endif
+
+#include "met_drv.h"
+#include "met_kernel_symbol.h"
+#include "interface.h"
+#include "trace.h"
+#include "cpu_pmu.h"
+
+struct cpu_pmu_hw *cpu_pmu;
+static int counter_cnt[MXNR_CPU];
+static int nr_arg[MXNR_CPU];
+
+int met_perf_cpupmu_status;
+
+#ifdef CONFIG_CPU_PM
+static int use_cpu_pm_pmu_notifier = 0;
+
+/* helper notifier for maintaining pmu states before cpu state transition */
+static int cpu_pm_pmu_notify(struct notifier_block *b,
+			     unsigned long cmd,
+			     void *p)
+{
+	int ii;
+	int cpu, count;
+	unsigned int pmu_value[MXNR_PMU_EVENTS];
+
+	if (!met_perf_cpupmu_status)
+		return NOTIFY_OK;
+
+	cpu = raw_smp_processor_id();
+
+	switch (cmd) {
+	case CPU_PM_ENTER:
+		count = cpu_pmu->polling(cpu_pmu->pmu[cpu], cpu_pmu->event_count[cpu], pmu_value);
+		for (ii = 0; ii < count; ii ++)
+			cpu_pmu->cpu_pm_unpolled_loss[cpu][ii] += pmu_value[ii];
+
+		cpu_pmu->stop(cpu_pmu->event_count[cpu]);
+		break;
+	case CPU_PM_ENTER_FAILED:
+	case CPU_PM_EXIT:
+		cpu_pmu->start(cpu_pmu->pmu[cpu], cpu_pmu->event_count[cpu]);
+		break;
+	default:
+		return NOTIFY_DONE;
+	}
+	return NOTIFY_OK;
+}
+
+struct notifier_block cpu_pm_pmu_notifier = {
+	.notifier_call = cpu_pm_pmu_notify,
+};
+#endif
+
+static DEFINE_PER_CPU(unsigned long long[MXNR_PMU_EVENTS], perfCurr);
+static DEFINE_PER_CPU(unsigned long long[MXNR_PMU_EVENTS], perfPrev);
+static DEFINE_PER_CPU(int[MXNR_PMU_EVENTS], perfCntFirst);
+static DEFINE_PER_CPU(struct perf_event * [MXNR_PMU_EVENTS], pevent);
+static DEFINE_PER_CPU(struct perf_event_attr [MXNR_PMU_EVENTS], pevent_attr);
+static DEFINE_PER_CPU(int, perfSet);
+static DEFINE_PER_CPU(unsigned int, perf_task_init_done);
+static DEFINE_PER_CPU(int, perf_cpuid);
+static DEFINE_PER_CPU(struct delayed_work, cpu_pmu_dwork_setup);
+static DEFINE_PER_CPU(struct delayed_work*, perf_delayed_work_setup);
+static DEFINE_PER_CPU(struct delayed_work, cpu_pmu_dwork_down);
+static DEFINE_PER_CPU(int, cpu_status);
+
+#ifdef CPUPMU_V8_2
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <mt-plat/sync_write.h>
+#include <mt-plat/mtk_io.h>
+
+static char mcucfg_desc[] = "mediatek,mcucfg";
+static void __iomem *mcucfg_base = NULL;
+#define DBG_CONTROL_CPU6	((unsigned long)mcucfg_base + 0x3000 + 0x308)  /* DBG_CONTROL */
+#define DBG_CONTROL_CPU7	((unsigned long)mcucfg_base + 0x3800 + 0x308)  /* DBG_CONTROL */
+#define ENABLE_MTK_PMU_EVENTS_OFFSET 1
+static int restore_dbg_ctrl_cpu6;
+static int restore_dbg_ctrl_cpu7;
+
+int cpu_pmu_debug_init(void)
+{
+	struct device_node  *node = NULL;
+	unsigned int value6,value7;
+
+	 /*for A75 MTK internal event*/
+	 if (mcucfg_base == NULL) {
+		node = of_find_compatible_node(NULL, NULL, mcucfg_desc);
+		if (node == NULL) {
+			MET_TRACE("[MET_PMU_DB] of_find node == NULL\n");
+			pr_debug("!!!!!!!! [MET_PMU_DB] of_find node == NULL\n");
+			goto out;
+		}
+		mcucfg_base = of_iomap(node, 0);
+		of_node_put(node);
+		if (mcucfg_base == NULL) {
+			MET_TRACE("[MET_PMU_DB] mcucfg_base == NULL\n");
+			pr_debug("!!!!!!!! [MET_PMU_DB] mcucfg_base == NULL\n");
+			goto out;
+		}
+		MET_TRACE("[MET_PMU_DB] regbase %08lx\n", DBG_CONTROL_CPU7);
+		pr_debug("!!!!!!!! [MET_PMU_DB] regbase %08lx\n", DBG_CONTROL_CPU7);
+	}
+
+	value6 = readl(IOMEM(DBG_CONTROL_CPU6));
+	if (value6 & (1 << ENABLE_MTK_PMU_EVENTS_OFFSET)) {
+		restore_dbg_ctrl_cpu6 = 1;
+	} else {
+		restore_dbg_ctrl_cpu6 = 0;
+		mt_reg_sync_writel(value6 | (1 << ENABLE_MTK_PMU_EVENTS_OFFSET), DBG_CONTROL_CPU6);
+	}
+
+	value7 = readl(IOMEM(DBG_CONTROL_CPU7));
+	if (value7 & (1 << ENABLE_MTK_PMU_EVENTS_OFFSET)) {
+		restore_dbg_ctrl_cpu7 = 1;
+	} else {
+		restore_dbg_ctrl_cpu7 = 0;
+		mt_reg_sync_writel(value7 | (1 << ENABLE_MTK_PMU_EVENTS_OFFSET), DBG_CONTROL_CPU7);
+	}
+
+	value6 = readl(IOMEM(DBG_CONTROL_CPU6));
+	value7 = readl(IOMEM(DBG_CONTROL_CPU7));
+	MET_TRACE("[MET_PMU_DB]DBG_CONTROL_CPU6 = %08x,  DBG_CONTROL_CPU7 = %08x\n", value6, value7);
+	pr_debug("!!!!!!!! [MET_PMU_DB]DBG_CONTROL_CPU6 = %08x,  DBG_CONTROL_CPU7 = %08x\n", value6, value7);
+	return 1;
+
+out:
+	if (mcucfg_base != NULL) {
+		iounmap(mcucfg_base);
+		mcucfg_base = NULL;
+	}
+	MET_TRACE("[MET_PMU_DB]DBG_CONTROL init error");
+	pr_debug("!!!!!!!! [MET_PMU_DB]DBG_CONTROL init error");
+	return 0;
+}
+
+int cpu_pmu_debug_uninit(void)
+{
+	unsigned int value6,value7;
+
+	if (restore_dbg_ctrl_cpu6 == 0) {
+		value6 = readl(IOMEM(DBG_CONTROL_CPU6));
+		mt_reg_sync_writel(value6 & (~(1 << ENABLE_MTK_PMU_EVENTS_OFFSET)), DBG_CONTROL_CPU6);
+	}
+	if (restore_dbg_ctrl_cpu7 == 0) {
+		value7 = readl(IOMEM(DBG_CONTROL_CPU7));
+		mt_reg_sync_writel(value7 & (~(1 << ENABLE_MTK_PMU_EVENTS_OFFSET)), DBG_CONTROL_CPU7);
+	}
+
+	value6 = readl(IOMEM(DBG_CONTROL_CPU6));
+	value7 = readl(IOMEM(DBG_CONTROL_CPU7));
+	MET_TRACE("[MET_PMU_DB]DBG_CONTROL_CPU6 = %08x,  DBG_CONTROL_CPU7 = %08x\n", value6, value7);
+	pr_debug("!!!!!!!! [MET_PMU_DB]DBG_CONTROL_CPU6 = %08x,  DBG_CONTROL_CPU7 = %08x\n", value6, value7);
+
+	if (mcucfg_base != NULL) {
+		iounmap(mcucfg_base);
+		mcucfg_base = NULL;
+	}
+	restore_dbg_ctrl_cpu6 = 0;
+	restore_dbg_ctrl_cpu7 = 0;
+	return 1;
+}
+#endif
+
+
+noinline void mp_cpu(unsigned char cnt, unsigned int *value)
+{
+	MET_GENERAL_PRINT(MET_TRACE, cnt, value);
+}
+
+static void dummy_handler(struct perf_event *event, struct perf_sample_data *data,
+			  struct pt_regs *regs)
+{
+	/*
+	 * Required as perf_event_create_kernel_counter() requires an overflow handler,
+	 * even though all we do is poll.
+	 */
+}
+
+static void perf_cpupmu_polling(unsigned long long stamp, int cpu)
+{
+	int			event_count = cpu_pmu->event_count[cpu];
+	struct met_pmu		*pmu = cpu_pmu->pmu[cpu];
+	int			i, count;
+	unsigned long long	delta;
+	struct perf_event	*ev;
+	unsigned int		pmu_value[MXNR_PMU_EVENTS];
+
+	if (per_cpu(perfSet, cpu) == 0)
+		return;
+
+	count = 0;
+	for (i = 0; i < event_count; i++) {
+		if (pmu[i].mode == 0)
+			continue;
+
+		ev = per_cpu(pevent, cpu)[i];
+		if ((ev != NULL) && (ev->state == PERF_EVENT_STATE_ACTIVE)) {
+			per_cpu(perfCurr, cpu)[i] = met_perf_event_read_local_symbol(ev);
+			delta = (per_cpu(perfCurr, cpu)[i] - per_cpu(perfPrev, cpu)[i]);
+			per_cpu(perfPrev, cpu)[i] = per_cpu(perfCurr, cpu)[i];
+			if (per_cpu(perfCntFirst, cpu)[i] == 1) {
+				/* we shall omit delta counter when we get first counter */
+				per_cpu(perfCntFirst, cpu)[i] = 0;
+				continue;
+			}
+			pmu_value[count] = (unsigned int)delta;
+			count++;
+		}
+	}
+
+	if (count == counter_cnt[cpu])
+		mp_cpu(count, pmu_value);
+}
+
+static struct perf_event* perf_event_create(int cpu, unsigned short event, int count)
+{
+	struct perf_event_attr	*ev_attr;
+	struct perf_event	*ev;
+
+	ev_attr = per_cpu(pevent_attr, cpu)+count;
+	memset(ev_attr, 0, sizeof(*ev_attr));
+	if (event == 0xff) {
+		ev_attr->config = PERF_COUNT_HW_CPU_CYCLES;
+		ev_attr->type = PERF_TYPE_HARDWARE;
+	} else {
+		ev_attr->config = event;
+		ev_attr->type = PERF_TYPE_RAW;
+	}
+	ev_attr->size = sizeof(*ev_attr);
+	ev_attr->sample_period = 0;
+	ev_attr->pinned = 1;
+
+	ev = perf_event_create_kernel_counter(ev_attr, cpu, NULL, dummy_handler, NULL);
+	if (IS_ERR(ev)) {
+		pr_debug("!!!!!!!! cpu-%d: [MET_PMU] failed perf_event_create_kernel_counter ev is NULL\n", cpu);
+		return NULL;
+	}
+	do {
+		if (ev->state == PERF_EVENT_STATE_ACTIVE) {
+			break;
+		}
+		if (ev->state == PERF_EVENT_STATE_ERROR) {
+			pr_debug("!!!!!!!! cpu-%d: [MET_PMU] ev->state == PERF_EVENT_STATE_ERROR\n", cpu);
+			perf_event_enable(ev);
+			if (ev->state == PERF_EVENT_STATE_ACTIVE)
+				break;
+		}
+		perf_event_release_kernel(ev);
+		return NULL;
+	} while (0);
+
+	return ev;
+}
+
+static void perf_event_release(int cpu, struct perf_event *ev)
+{
+	pr_debug("!!!!!!!! %s cpu-%d\n", __FUNCTION__, cpu);
+	if (ev->state == PERF_EVENT_STATE_ACTIVE)
+		perf_event_disable(ev);
+	perf_event_release_kernel(ev);
+}
+
+static int perf_thread_set_perf_events(int cpu)
+{
+	int			i, size;
+	struct perf_event	*ev;
+
+	pr_debug("!!!!!!!! %s cpu-%d\n", __FUNCTION__, cpu);
+	size = sizeof(struct perf_event_attr);
+	if (per_cpu(perfSet, cpu) == 0) {
+		int event_count = cpu_pmu->event_count[cpu];
+		struct met_pmu *pmu = cpu_pmu->pmu[cpu];
+		for (i = 0; i < event_count; i++) {
+			if (!pmu[i].mode)
+				continue;	/* Skip disabled counters */
+			ev = perf_event_create(cpu, pmu[i].event, i);
+			if (ev == NULL) {
+				met_cpupmu.mode = 0;
+				met_perf_cpupmu_status = 0;
+
+				MET_TRACE("[MET_PMU] failed to register pmu event %4x\n", pmu[i].event);
+				pr_debug("!!!!!!!! [MET_PMU] failed to register pmu event %4x\n", pmu[i].event);
+				continue;
+			}
+
+			MET_TRACE("[MET_PMU] registered pmu slot: [%d] evt=%#04x\n", ev->hw.idx, pmu[i].event);
+			pr_debug("!!!!!!!! [MET_PMU] registered pmu slot: [%d] evt=%#04x\n", ev->hw.idx, pmu[i].event);
+
+			per_cpu(pevent, cpu)[i] = ev;
+			per_cpu(perfPrev, cpu)[i] = 0;
+			per_cpu(perfCurr, cpu)[i] = 0;
+			perf_event_enable(ev);
+			per_cpu(perfCntFirst, cpu)[i] = 1;
+		}	/* for all PMU counter */
+		per_cpu(perfSet, cpu) = 1;
+	}	/* for perfSet */
+
+	return 0;
+}
+
+static void perf_thread_setup(struct work_struct *work)
+{
+	int			cpu;
+	struct delayed_work	*dwork = to_delayed_work(work);
+
+	cpu = dwork->cpu;
+	pr_debug("!!!!!!!! %s cpu-%d\n", __FUNCTION__, cpu);
+	if (per_cpu(perf_task_init_done, cpu) == 0) {
+		per_cpu(perf_task_init_done, cpu) = 1;
+		perf_thread_set_perf_events(cpu);
+	}
+}
+
+static void met_perf_cpupmu_start(int cpu)
+{
+	pr_debug("!!!!!!!! %s cpu-%d\n", __FUNCTION__, cpu);
+	if (met_cpupmu.mode == 0)
+		return;
+
+	per_cpu(perf_cpuid, cpu) = cpu;
+	if (per_cpu(perf_delayed_work_setup, cpu) == NULL) {
+		struct delayed_work *dwork = &per_cpu(cpu_pmu_dwork_setup, cpu);
+		INIT_DELAYED_WORK(dwork, perf_thread_setup);
+		dwork->cpu = cpu;
+		schedule_delayed_work_on(cpu, dwork, 0);
+		per_cpu(perf_delayed_work_setup, cpu) = dwork;
+	}
+}
+
+static void perf_thread_down(struct work_struct *work)
+{
+	struct delayed_work	*dwork = to_delayed_work(work);
+	int			cpu, i;
+	struct perf_event	*ev;
+	int			event_count;
+	struct met_pmu		*pmu;
+
+	cpu = dwork->cpu;
+	pr_debug("!!!!!!!! %s cpu-%d\n", __FUNCTION__, cpu);
+	if (per_cpu(perfSet, cpu) == 0)
+		return;
+
+	per_cpu(perfSet, cpu) = 0;
+	event_count = cpu_pmu->event_count[cpu];
+	pmu = cpu_pmu->pmu[cpu];
+	for (i = 0; i < event_count; i++) {
+		ev = per_cpu(pevent, cpu)[i];
+		if (ev != NULL) {
+			perf_event_release(cpu, ev);
+			per_cpu(pevent, cpu)[i] = NULL;
+		}
+	}
+	per_cpu(perf_task_init_done, cpu) = 0;
+	per_cpu(perf_delayed_work_setup, cpu) = NULL;
+}
+
+static void met_perf_cpupmu_stop(int cpu)
+{
+	struct delayed_work	*dwork;
+
+	pr_debug("!!!!!!!! %s cpu-%d\n", __FUNCTION__, cpu);
+	per_cpu(perf_cpuid, cpu) = cpu;
+	dwork = &per_cpu(cpu_pmu_dwork_down, cpu);
+	INIT_DELAYED_WORK(dwork, perf_thread_down);
+	dwork->cpu = cpu;
+	schedule_delayed_work_on(cpu, dwork, 0);
+}
+
+static int cpupmu_create_subfs(struct kobject *parent)
+{
+	cpu_pmu = cpu_pmu_hw_init();
+	if (cpu_pmu == NULL) {
+		PR_BOOTMSG("Failed to init CPU PMU HW!!\n");
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+static void cpupmu_delete_subfs(void)
+{
+}
+
+void met_perf_cpupmu_polling(unsigned long long stamp, int cpu)
+{
+	int count;
+	unsigned int pmu_value[MXNR_PMU_EVENTS];
+
+	if (per_cpu(cpu_status, cpu) != CPU_ONLINE)
+		return;
+
+	if (met_cpu_pmu_method) {
+		perf_cpupmu_polling(stamp, cpu);
+	} else {
+		count = cpu_pmu->polling(cpu_pmu->pmu[cpu], cpu_pmu->event_count[cpu], pmu_value);
+
+#ifdef CONFIG_CPU_PM
+		if (met_cpu_pm_pmu_reconfig) {
+			int ii;
+			for (ii = 0; ii < count; ii ++)
+				pmu_value[ii] += cpu_pmu->cpu_pm_unpolled_loss[cpu][ii];
+		}
+#endif
+
+		mp_cpu(count, pmu_value);
+
+#ifdef CONFIG_CPU_PM
+		if (met_cpu_pm_pmu_reconfig) {
+			memset(cpu_pmu->cpu_pm_unpolled_loss[cpu], 0, sizeof (cpu_pmu->cpu_pm_unpolled_loss[0]));
+		}
+#endif
+	}
+}
+
+static void cpupmu_start(void)
+{
+	int	cpu = raw_smp_processor_id();
+
+	pr_debug("!!!!!!!! %s cpu-%d\n", __FUNCTION__, cpu);
+	if (met_cpu_pmu_method)
+		met_perf_cpupmu_start(cpu);
+	else {
+		nr_arg[cpu] = 0;
+		cpu_pmu->start(cpu_pmu->pmu[cpu], cpu_pmu->event_count[cpu]);
+	}
+	met_perf_cpupmu_status = 1;
+	per_cpu(cpu_status, cpu) = CPU_ONLINE;
+}
+
+
+static void cpupmu_unique_start(void)
+{
+	int	cpu = raw_smp_processor_id();
+
+	pr_debug("!!!!!!!! %s cpu-%d\n", __FUNCTION__, cpu);
+#ifdef CPUPMU_V8_2
+	int ret = 0;
+	ret = cpu_pmu_debug_init();
+	if (ret == 0)
+		PR_BOOTMSG("Failed to init CPU PMU debug!!\n");
+#endif
+
+#ifdef CONFIG_CPU_PM
+	use_cpu_pm_pmu_notifier = 0;
+	if (met_cpu_pm_pmu_reconfig) {
+		if (met_cpu_pmu_method) {
+			met_cpu_pm_pmu_reconfig = 0;
+			MET_TRACE("[MET_PMU] met_cpu_pmu_method=%d, met_cpu_pm_pmu_reconfig forced disabled\n", met_cpu_pmu_method);
+			pr_debug("!!!!!!!! [MET_PMU] met_cpu_pmu_method=%d, met_cpu_pm_pmu_reconfig forced disabled\n", met_cpu_pmu_method);
+		} else {
+			memset(cpu_pmu->cpu_pm_unpolled_loss, 0, sizeof (cpu_pmu->cpu_pm_unpolled_loss));
+			cpu_pm_register_notifier(&cpu_pm_pmu_notifier);
+			use_cpu_pm_pmu_notifier = 1;
+		}
+	}
+#else
+	if (met_cpu_pm_pmu_reconfig) {
+		met_cpu_pm_pmu_reconfig = 0;
+		MET_TRACE("[MET_PMU] CONFIG_CPU_PM=%d, met_cpu_pm_pmu_reconfig forced disabled\n", CONFIG_CPU_PM);
+		pr_debug("!!!!!!!! [MET_PMU] CONFIG_CPU_PM=%d, met_cpu_pm_pmu_reconfig forced disabled\n", CONFIG_CPU_PM);
+	}
+#endif
+	MET_TRACE("[MET_PMU] met_cpu_pm_pmu_reconfig=%u\n", met_cpu_pm_pmu_reconfig);
+	pr_debug("!!!!!!!! [MET_PMU] met_cpu_pm_pmu_reconfig=%u\n", met_cpu_pm_pmu_reconfig);
+
+	return;
+}
+
+static void cpupmu_stop(void)
+{
+	int	cpu = raw_smp_processor_id();
+
+	pr_debug("!!!!!!!! %s cpu-%d\n", __FUNCTION__, cpu);
+	met_perf_cpupmu_status = 0;
+	if (met_cpu_pmu_method)
+		met_perf_cpupmu_stop(cpu);
+	else
+		cpu_pmu->stop(cpu_pmu->event_count[cpu]);
+}
+
+static void cpupmu_unique_stop(void)
+{
+	int	cpu = raw_smp_processor_id();
+
+	pr_debug("!!!!!!!! %s cpu-%d\n", __FUNCTION__, cpu);
+#ifdef CPUPMU_V8_2
+	cpu_pmu_debug_uninit();
+#endif
+
+#ifdef CONFIG_CPU_PM
+	if (use_cpu_pm_pmu_notifier) {
+		cpu_pm_unregister_notifier(&cpu_pm_pmu_notifier);
+	}
+#endif
+	return;
+}
+
+static const char cache_line_header[] =
+	"met-info [000] 0.0: met_cpu_cache_line_size: %d\n";
+static const char header[] =
+	"met-info [000] 0.0: met_cpu_header_v2: %d";
+
+static const char help[] =
+	"  --pmu-cpu-evt=[cpu_list:]event_list   select CPU-PMU events in %s\n"
+	"                                        cpu_list: specify the cpu_id list or apply to all the cores\n"
+	"                                            example: 0,1,2\n"
+	"                                        event_list: specify the event number\n"
+	"                                            example: 0x8,0xff\n";
+
+static int cpupmu_print_help(char *buf, int len)
+{
+	return snprintf(buf, PAGE_SIZE, help, cpu_pmu->cpu_name);
+}
+
+static int reset_driver_stat(void)
+{
+	int		cpu, i;
+	int		event_count;
+	struct met_pmu	*pmu;
+
+	met_cpupmu.mode = 0;
+	for_each_possible_cpu(cpu) {
+		event_count = cpu_pmu->event_count[cpu];
+		pmu = cpu_pmu->pmu[cpu];
+		counter_cnt[cpu] = 0;
+		nr_arg[cpu] = 0;
+		for (i = 0; i < event_count; i++) {
+			pmu[i].mode = MODE_DISABLED;
+			pmu[i].event = 0;
+			pmu[i].freq = 0;
+		}
+	}
+
+	return 0;
+}
+
+static int cpupmu_print_header(char *buf, int len)
+{
+	int		cpu, i, ret, first;
+	int		event_count;
+	struct met_pmu	*pmu;
+
+	ret = 0;
+
+	/*append CPU PMU access method*/
+	if (met_cpu_pmu_method)
+		ret += snprintf(buf + ret, PAGE_SIZE,
+			"met-info [000] 0.0: CPU_PMU_method: perf APIs\n");
+	else
+		ret += snprintf(buf + ret, PAGE_SIZE,
+			"met-info [000] 0.0: CPU_PMU_method: MET pmu driver\n");
+
+	/*append cache line size*/
+	ret += snprintf(buf + ret, PAGE_SIZE - ret, cache_line_header, cache_line_size());
+	ret += snprintf(buf + ret, PAGE_SIZE - ret, "# mp_cpu: pmu_value1, ...\n");
+
+	for_each_possible_cpu(cpu) {
+		event_count = cpu_pmu->event_count[cpu];
+		pmu = cpu_pmu->pmu[cpu];
+		first = 1;
+		for (i = 0; i < event_count; i++) {
+			if (pmu[i].mode == 0)
+				continue;
+			if (first) {
+				ret += snprintf(buf + ret, PAGE_SIZE - ret, header, cpu);
+				first = 0;
+			}
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, ",0x%x", pmu[i].event);
+			pmu[i].mode = 0;
+		}
+		if (!first)
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, "\n");
+	}
+
+	reset_driver_stat();
+
+	return ret;
+}
+
+static int met_parse_num_list(char *arg, int len, int *list, int list_cnt)
+{
+	int	nr_num = 0;
+	char	*num;
+	int	num_len;
+
+	/* search ',' as the splitter */
+	while (len) {
+		num = arg;
+		num_len = 0;
+		if (list_cnt <= 0)
+			return -1;
+		while (len) {
+			len--;
+			if (*arg == ',') {
+				*(arg++) = '\0';
+				break;
+			}
+			arg++;
+			num_len++;
+		}
+		if (met_parse_num(num, list, num_len) < 0)
+			return -1;
+		list++;
+		list_cnt--;
+		nr_num++;
+	}
+
+	return nr_num;
+}
+
+static int cpupmu_process_argument(const char *arg, int len)
+{
+	char		*arg1 = (char*)arg;
+	int		len1 = len;
+	int		cpu, cpu_list[MXNR_CPU];
+	int		nr_events, event_list[MXNR_PMU_EVENTS];
+	int		i;
+	int		nr_counters;
+	struct met_pmu	*pmu;
+	int		arg_nr;
+	int		event_no;
+
+	/*
+	 * split cpu_list and event_list by ':'
+	 *   arg, len: cpu_list when found (i < len)
+	 *   arg1, len1: event_list
+	 */
+	for (i = 0; i < len; i++) {
+		if (arg[i] == ':') {
+			arg1[i] = '\0';
+			arg1 += i+1;
+			len1 = len - i - 1;
+			len = i;
+			break;
+		}
+	}
+
+	/*
+	 * setup cpu_list array
+	 *   1: selected
+	 *   0: unselected
+	 */
+	if (arg1 != arg) {	/* is cpu_id list specified? */
+		int list[MXNR_CPU], cnt;
+		int cpu_id;
+		if ((cnt = met_parse_num_list((char*)arg, len, list, ARRAY_SIZE(list))) <= 0)
+			goto arg_out;
+		memset(cpu_list, 0, sizeof(cpu_list));
+		for (i = 0; i < cnt; i++) {
+			cpu_id = list[i];
+			if (cpu_id < 0 || cpu_id >= ARRAY_SIZE(cpu_list))
+				goto arg_out;
+			cpu_list[cpu_id] = 1;
+		}
+	}
+	else
+		memset(cpu_list, 1, sizeof(cpu_list));
+
+	/* get event_list */
+	if ((nr_events = met_parse_num_list(arg1, len1, event_list, ARRAY_SIZE(event_list))) <= 0)
+		goto arg_out;
+
+	/* for each cpu in cpu_list, add all the events in event_list */
+	for_each_possible_cpu(cpu) {
+		pmu = cpu_pmu->pmu[cpu];
+		arg_nr = nr_arg[cpu];
+
+		if (cpu_list[cpu] == 0)
+			continue;
+
+		if (met_cpu_pmu_method) {
+			nr_counters = perf_num_counters();
+		} else {
+			nr_counters = cpu_pmu->event_count[cpu];
+		}
+
+		pr_debug("!!!!!!!! [MET_PMU] pmu slot count=%d\n", nr_counters);
+
+		if (nr_counters == 0)
+			goto arg_out;
+
+		for (i = 0; i < nr_events; i++) {
+			event_no = event_list[i];
+			/*
+			 * check if event is duplicate,
+			 * but may not include 0xff when met_cpu_pmu_method == 0.
+			 */
+			if (cpu_pmu->check_event(pmu, arg_nr, event_no) < 0)
+				goto arg_out;
+
+			/*
+			 * test if this event is available when in perf_APIs mode
+			 */
+			if (met_cpu_pmu_method) {
+				struct perf_event *ev;
+				ev = perf_event_create(cpu, event_no, arg_nr);
+				if (ev == NULL) {
+					pr_debug("!!!!!!!! [MET_PMU] failed pmu alloction test (event_no=%#04x)\n", event_no);
+				} else {
+					perf_event_release(cpu, ev);
+				}
+			}
+
+			if (met_cpu_pmu_method) {
+				if (arg_nr >= nr_counters)
+					goto arg_out;
+				pmu[arg_nr].mode = MODE_POLLING;
+				pmu[arg_nr].event = event_no;
+				pmu[arg_nr].freq = 0;
+				arg_nr++;
+			} else {
+				if (event_no == 0xff) {
+					if (pmu[nr_counters-1].mode == MODE_POLLING)
+						goto arg_out;
+					pmu[nr_counters-1].mode = MODE_POLLING;
+					pmu[nr_counters-1].event = 0xff;
+					pmu[nr_counters-1].freq = 0;
+				} else {
+					if (arg_nr >= (nr_counters - 1))
+						goto arg_out;
+					pmu[arg_nr].mode = MODE_POLLING;
+					pmu[arg_nr].event = event_no;
+					pmu[arg_nr].freq = 0;
+					arg_nr++;
+				}
+			}
+			counter_cnt[cpu]++;
+		}
+		nr_arg[cpu] = arg_nr;
+	}
+
+	met_cpupmu.mode = 1;
+	return 0;
+
+arg_out:
+	reset_driver_stat();
+	return -EINVAL;
+}
+
+
+static void cpupmu_cpu_state_notify(long cpu, unsigned long action)
+{
+	per_cpu(cpu_status, cpu) = action;
+
+#if (defined(CONFIG_ARM64) || defined(CONFIG_ARM))
+	if (met_cpu_pmu_method && action == CPU_DOWN_PREPARE) {
+		struct perf_event *event = NULL;
+		struct arm_pmu *armpmu = NULL;
+		struct platform_device *pmu_device = NULL;
+		int irq = 0;
+
+		event = per_cpu(pevent, cpu)[0];
+		if (event)
+			armpmu = to_arm_pmu(event->pmu);
+		pr_debug("!!!!!!!! %s_%ld, event=%p\n", __FUNCTION__, cpu, event);
+
+		if (armpmu)
+			pmu_device = armpmu->plat_device;
+		pr_debug("!!!!!!!! %s_%ld, armpmu=%p\n", __FUNCTION__, cpu, armpmu);
+
+		if (pmu_device)
+			irq = platform_get_irq(pmu_device, 0);
+		pr_debug("!!!!!!!! %s_%ld, pmu_device=%p\n", __FUNCTION__, cpu, pmu_device);
+
+		if (irq > 0)
+			disable_percpu_irq(irq);
+		pr_debug("!!!!!!!! %s_%ld, irq=%d\n", __FUNCTION__, cpu, irq);
+	}
+#endif
+}
+
+
+struct metdevice met_cpupmu = {
+	.name = "cpu",
+	.type = MET_TYPE_PMU,
+	.cpu_related = 1,
+	.create_subfs = cpupmu_create_subfs,
+	.delete_subfs = cpupmu_delete_subfs,
+	.start = cpupmu_start,
+	.uniq_start = cpupmu_unique_start,
+	.stop = cpupmu_stop,
+	.uniq_stop = cpupmu_unique_stop,
+	.polling_interval = 1,
+	.timed_polling = met_perf_cpupmu_polling,
+	.print_help = cpupmu_print_help,
+	.print_header = cpupmu_print_header,
+	.process_argument = cpupmu_process_argument,
+	.cpu_state_notify = cpupmu_cpu_state_notify
+};
diff --git a/src/devtools/met-driver/4.9/common/cpu_pmu.h b/src/devtools/met-driver/4.9/common/cpu_pmu.h
new file mode 100644
index 0000000..3d046d5
--- /dev/null
+++ b/src/devtools/met-driver/4.9/common/cpu_pmu.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _CPU_PMU_H_
+#define _CPU_PMU_H_
+
+#include <linux/device.h>
+
+#define MODE_DISABLED	0
+#define MODE_INTERRUPT	1
+#define MODE_POLLING	2
+
+#define MXSIZE_PMU_DESC 32
+#define MXNR_CPU	NR_CPUS
+
+#define	MXNR_PMU_EVENTS	8	/* max number of pmu counter for armv8 is 6+1 */
+struct met_pmu {
+	unsigned char mode;
+	unsigned short event;
+	unsigned long freq;
+	struct kobject *kobj_cpu_pmu;
+};
+
+struct cpu_pmu_hw {
+	const char *name;
+	const char *cpu_name;
+	int nr_cnt;
+	int (*get_event_desc)(int idx, int event, char *event_desc);
+	int (*check_event)(struct met_pmu *pmu, int idx, int event);
+	void (*start)(struct met_pmu *pmu, int count);
+	void (*stop)(int count);
+	unsigned int (*polling)(struct met_pmu *pmu, int count, unsigned int *pmu_value);
+	struct met_pmu *pmu[MXNR_CPU];
+	int event_count[MXNR_CPU];
+	/*
+	 * used for compensation of pmu counter loss
+	 * between end of polling and start of cpu pm
+	 */
+	unsigned int cpu_pm_unpolled_loss[MXNR_CPU][MXNR_PMU_EVENTS];
+};
+
+struct pmu_desc {
+	unsigned int event;
+	char name[MXSIZE_PMU_DESC];
+};
+
+struct cpu_pmu_hw *cpu_pmu_hw_init(void);
+
+extern struct cpu_pmu_hw *cpu_pmu;
+extern noinline void mp_cpu(unsigned char cnt, unsigned int *value);
+
+extern int met_perf_cpupmu_status;
+extern void met_perf_cpupmu_polling(unsigned long long stamp, int cpu);
+
+#endif	/* _CPU_PMU_H_ */
diff --git a/src/devtools/met-driver/4.9/common/dummy_header.c b/src/devtools/met-driver/4.9/common/dummy_header.c
new file mode 100644
index 0000000..f8d1187
--- /dev/null
+++ b/src/devtools/met-driver/4.9/common/dummy_header.c
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+
+#include "interface.h"
+#include "met_drv.h"
+
+static struct kobject *kobj_met_dummy;
+static char header_str[PAGE_SIZE];
+static int header_str_len;
+struct metdevice met_dummy_header;
+
+static ssize_t dummy_str_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf);
+static ssize_t dummy_str_store(struct kobject *kobj,
+			       struct kobj_attribute *attr, const char *buf, size_t n);
+static struct kobj_attribute dummy_attr = __ATTR(dummy_str, 0664, dummy_str_show, dummy_str_store);
+
+
+static ssize_t dummy_str_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	int ret;
+
+	ret = snprintf(buf, PAGE_SIZE, "%s", header_str);
+
+	return ret;
+}
+
+static ssize_t dummy_str_store(struct kobject *kobj,
+			       struct kobj_attribute *attr, const char *buf, size_t n)
+{
+	int ret = 0;
+	char *ptr = header_str;
+
+	if ((header_str_len + strlen(buf)) < PAGE_SIZE) {
+		ret = snprintf(ptr + header_str_len, PAGE_SIZE - header_str_len, "%s\n", buf);
+		header_str_len += ret;
+	}
+	met_dummy_header.mode = 1;
+
+	return n;
+}
+
+static int dummy_reset(void)
+{
+	met_dummy_header.mode = 0;
+	memset(header_str, 0x00, PAGE_SIZE);
+	header_str_len = 0;
+
+	return 0;
+}
+
+static int dummy_print_header(char *buf, int len)
+{
+	if (header_str_len > 0)
+		len = snprintf(buf, PAGE_SIZE, "%s", header_str);
+	else
+		len = snprintf(buf, PAGE_SIZE, "# dummy header is empty\n");
+
+	return len;
+}
+
+static int dummy_create(struct kobject *parent)
+{
+	int ret = 0;
+
+	kobj_met_dummy = parent;
+	ret = sysfs_create_file(kobj_met_dummy, &dummy_attr.attr);
+	if (ret != 0) {
+		pr_debug("Failed to create montype0 in sysfs\n");
+		return ret;
+	}
+
+	return ret;
+}
+
+static void dummy_delete(void)
+{
+	sysfs_remove_file(kobj_met_dummy, &dummy_attr.attr);
+	kobj_met_dummy = NULL;
+}
+
+struct metdevice met_dummy_header = {
+	.name = "dummy_header",
+	.type = MET_TYPE_MISC,
+	.cpu_related = 0,
+	.start = NULL,
+	.stop = NULL,
+	.reset = dummy_reset,
+	.polling_interval = 0,
+	.timed_polling = NULL,
+	.print_help = NULL,
+	.print_header = dummy_print_header,
+	.create_subfs = dummy_create,
+	.delete_subfs = dummy_delete,
+};
diff --git a/src/devtools/met-driver/4.9/common/interface.c b/src/devtools/met-driver/4.9/common/interface.c
new file mode 100644
index 0000000..cfdccba
--- /dev/null
+++ b/src/devtools/met-driver/4.9/common/interface.c
@@ -0,0 +1,1431 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/fs.h>
+#include <linux/sched.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/miscdevice.h>
+#include <linux/kallsyms.h>
+#include <linux/syscore_ops.h>
+#include <linux/dma-mapping.h>
+#include "interface.h"
+#include "sampler.h"
+#include "util.h"
+
+#include "ondiemet.h"
+
+#include "met_drv.h"
+#include "met_tag.h"
+#include "met_kernel_symbol.h"
+#include "met_api_tbl.h"
+#include "version.h"
+
+extern int enable_met_backlight_tag(void);
+extern int output_met_backlight_tag(int level);
+
+static int run = -1;
+static int sample_rate = 1000;	/* Default: 1000 Hz */
+static int met_suspend_compensation_mode;
+static int met_suspend_compensation_flag;
+
+/*
+ * met_cpu_pmu_method:
+ *   0: MET pmu driver
+ *   1: perf APIs
+ */
+unsigned int met_cpu_pmu_method = 1;
+/*
+ * controls whether re-configuring pmu events after leaving cpu off state
+ */
+unsigned int met_cpu_pm_pmu_reconfig = 1;
+
+int met_hrtimer_expire;		/* in ns */
+int met_timer_expire;		/* in jiffies */
+unsigned int ctrl_flags;
+int met_mode;
+EXPORT_SYMBOL(met_mode);
+
+DEFINE_PER_CPU(char[MET_STRBUF_SIZE], met_strbuf_nmi);
+EXPORT_SYMBOL(met_strbuf_nmi);
+
+DEFINE_PER_CPU(char[MET_STRBUF_SIZE], met_strbuf_irq);
+EXPORT_SYMBOL(met_strbuf_irq);
+
+DEFINE_PER_CPU(char[MET_STRBUF_SIZE], met_strbuf_sirq);
+EXPORT_SYMBOL(met_strbuf_sirq);
+
+DEFINE_PER_CPU(char[MET_STRBUF_SIZE], met_strbuf);
+EXPORT_SYMBOL(met_strbuf);
+
+static void calc_timer_value(int rate)
+{
+	sample_rate = rate;
+
+	if (rate == 0) {
+		met_hrtimer_expire = 0;
+		met_timer_expire = 0;
+		return;
+	}
+
+	met_hrtimer_expire = 1000000000 / rate;
+
+	/* Case 1: hrtimer < 1 OS tick, met_timer_expire = 1 OS tick */
+	if (rate > HZ)
+		met_timer_expire = 1;
+	/* Case 2: hrtimer > 1 OS tick, met_timer_expire is hrtimer + 1 OS tick */
+	else
+		met_timer_expire = (HZ / rate) + 1;
+
+	/* pr_debug("JBK HZ=%d, met_hrtimer_expire=%d ns, met_timer_expire=%d ticks\n", */
+	/* HZ, met_hrtimer_expire, met_timer_expire); */
+}
+
+int met_parse_num(const char *str, unsigned int *value, int len)
+{
+	int ret;
+
+	if (len <= 0)
+		return -1;
+
+	if ((len > 2) &&
+		((str[0] == '0') &&
+		((str[1] == 'x') || (str[1] == 'X')))) {
+		ret = kstrtoint(str, 16, value);
+	} else {
+		ret = kstrtoint(str, 10, value);
+	}
+
+	if (ret != 0)
+		return -1;
+
+	return 0;
+}
+
+void met_set_suspend_notify(int flag)
+{
+	if (met_suspend_compensation_mode == 1)
+		met_suspend_compensation_flag = flag;
+	else
+		met_suspend_compensation_flag = 0;
+}
+
+LIST_HEAD(met_list);
+static struct kobject *kobj_misc;
+static struct kobject *kobj_pmu;
+static struct kobject *kobj_bus;
+
+static ssize_t ver_show(struct device *dev, struct device_attribute *attr, char *buf);
+static DEVICE_ATTR(ver, 0444, ver_show, NULL);
+
+static ssize_t plf_show(struct device *dev, struct device_attribute *attr, char *buf);
+static DEVICE_ATTR(plf, 0444, plf_show, NULL);
+
+static ssize_t core_topology_show(struct device *dev, struct device_attribute *attr, char *buf);
+static DEVICE_ATTR(core_topology, 0444, core_topology_show, NULL);
+
+static ssize_t devices_show(struct device *dev, struct device_attribute *attr, char *buf);
+static DEVICE_ATTR(devices, 0444, devices_show, NULL);
+
+static ssize_t ctrl_show(struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t ctrl_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			  size_t count);
+static DEVICE_ATTR(ctrl, 0664, ctrl_show, ctrl_store);
+
+static ssize_t spr_show(struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t spr_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			 size_t count);
+static DEVICE_ATTR(sample_rate, 0664, spr_show, spr_store);
+
+static ssize_t run_show(struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t run_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			 size_t count);
+static DEVICE_ATTR(run, 0664, run_show, run_store);
+
+static ssize_t ksym_show(struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t ksym_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			 size_t count);
+static DEVICE_ATTR(ksym, 0664, ksym_show, ksym_store);
+
+static ssize_t cpu_pmu_method_show(struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t cpu_pmu_method_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			  size_t count);
+static DEVICE_ATTR(cpu_pmu_method, 0664, cpu_pmu_method_show, cpu_pmu_method_store);
+
+static ssize_t cpu_pm_pmu_reconfig_show(struct device *dev,
+					struct device_attribute *attr,
+					char *buf);
+static ssize_t cpu_pm_pmu_reconfig_store(struct device *dev,
+					 struct device_attribute *attr,
+					 const char *buf,
+					 size_t count);
+static DEVICE_ATTR(cpu_pm_pmu_reconfig,
+		   0664,
+		   cpu_pm_pmu_reconfig_show,
+		   cpu_pm_pmu_reconfig_store);
+
+#ifdef PR_CPU_NOTIFY
+int met_cpu_notify;
+static ssize_t cpu_notify_show(struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t cpu_notify_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			  size_t count);
+static DEVICE_ATTR(cpu_notify, 0664, cpu_notify_show, cpu_notify_store);
+#endif
+
+#ifdef CONFIG_CPU_FREQ
+static ssize_t dvfs_show(struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t dvfs_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			  size_t count);
+static DEVICE_ATTR(dvfs, 0664, dvfs_show, dvfs_store);
+#endif
+
+static ssize_t suspend_compensation_enable_show(struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t suspend_compensation_enable_store(struct device *dev, struct device_attribute *attr,
+							const char *buf, size_t count);
+static DEVICE_ATTR(suspend_compensation_enable, 0664, suspend_compensation_enable_show,
+			suspend_compensation_enable_store);
+
+static ssize_t suspend_compensation_flag_show(struct device *dev, struct device_attribute *attr, char *buf);
+static DEVICE_ATTR(suspend_compensation_flag, 0444, suspend_compensation_flag_show, NULL);
+
+static ssize_t ipi_test_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			 size_t count);
+static DEVICE_ATTR(ipi_test, 0220, NULL, ipi_test_store);
+
+static const struct file_operations met_file_ops = {
+	.owner = THIS_MODULE
+};
+
+struct miscdevice met_device = {
+	.minor = MISC_DYNAMIC_MINOR,
+	.name = "met",
+	.mode = 0664,
+	.fops = &met_file_ops
+};
+EXPORT_SYMBOL(met_device);
+
+static int met_run(void)
+{
+	sampler_start();
+#ifdef MET_USER_EVENT_SUPPORT
+	bltab.flag &= (~MET_CLASS_ALL);
+#endif
+	ondiemet_start();
+	return 0;
+}
+
+static void met_stop(void)
+{
+#ifdef MET_USER_EVENT_SUPPORT
+	bltab.flag |= MET_CLASS_ALL;
+#endif
+	sampler_stop();
+	/* the met.ko will be use by script "cat ...", release it */
+	if ((ondiemet_module[ONDIEMET_SSPM] == 0) || (sspm_buffer_size == -1))
+		ondiemet_log_manager_stop();
+	ondiemet_stop();
+	ondiemet_extract();
+}
+
+static ssize_t ver_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int i;
+
+	mutex_lock(&dev->mutex);
+	i = snprintf(buf, PAGE_SIZE, "%s\n", MET_BACKEND_VERSION);
+	mutex_unlock(&dev->mutex);
+	return i;
+}
+
+static ssize_t devices_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int len, total_len = 0;
+	struct metdevice *c = NULL;
+
+	mutex_lock(&dev->mutex);
+	list_for_each_entry(c, &met_list, list) {
+		len = 0;
+		if (c->type == MET_TYPE_PMU)
+			len = snprintf(buf, PAGE_SIZE - total_len, "pmu/%s:0\n", c->name);
+		else if (c->type == MET_TYPE_BUS)
+			len = snprintf(buf, PAGE_SIZE - total_len, "bus/%s:0\n", c->name);
+		else if (c->type == MET_TYPE_MISC)
+			len = snprintf(buf, PAGE_SIZE - total_len, "misc/%s:0\n", c->name);
+
+		if (c->ondiemet_mode == 0) {
+			if (c->process_argument)
+				buf[len - 2]++;
+		} else if (c->ondiemet_mode == 1) {
+			if (c->ondiemet_process_argument)
+				buf[len - 2]++;
+		} else if (c->ondiemet_mode == 2) {
+			if (c->process_argument)
+				buf[len - 2]++;
+			if (c->ondiemet_process_argument)
+				buf[len - 2]++;
+		}
+
+		buf += len;
+		total_len += len;
+	}
+
+	mutex_unlock(&dev->mutex);
+	return total_len;
+}
+
+static char met_platform[16] = "none";
+static ssize_t plf_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int i;
+
+	mutex_lock(&dev->mutex);
+	i = snprintf(buf, PAGE_SIZE, "%s\n", met_platform);
+	mutex_unlock(&dev->mutex);
+	return i;
+}
+
+static char met_topology[64] = "none";
+static ssize_t core_topology_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int i;
+
+	mutex_lock(&dev->mutex);
+	i = snprintf(buf, PAGE_SIZE, "%s\n", met_topology);
+	mutex_unlock(&dev->mutex);
+	return i;
+}
+
+static ssize_t ctrl_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%d\n", ctrl_flags);
+}
+
+static ssize_t ctrl_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			  size_t count)
+{
+	unsigned int value = 0;
+
+	if (met_parse_num(buf, &value, count) < 0)
+		return -EINVAL;
+
+	ctrl_flags = value;
+	return count;
+}
+
+static ssize_t cpu_pmu_method_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%d\n", met_cpu_pmu_method);
+}
+
+static ssize_t cpu_pmu_method_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			  size_t count)
+{
+	unsigned int value;
+
+	if (met_parse_num(buf, &value, count) < 0)
+		return -EINVAL;
+
+	met_cpu_pmu_method = value;
+	return count;
+}
+
+
+static ssize_t cpu_pm_pmu_reconfig_show(struct device *dev,
+					struct device_attribute *attr,
+					char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%d\n", met_cpu_pm_pmu_reconfig);
+}
+
+static ssize_t cpu_pm_pmu_reconfig_store(struct device *dev,
+					 struct device_attribute *attr,
+					 const char *buf,
+					 size_t count)
+{
+	unsigned int value;
+
+	if (met_parse_num(buf, &value, count) < 0)
+		return -EINVAL;
+
+	met_cpu_pm_pmu_reconfig = value;
+	return count;
+}
+
+static void _test_trace_ipi_raise(void *info)
+{
+	unsigned int *cpu = (unsigned int *)info;
+	void (*arch_send_call_function_single_ipi_sym)(int cpu) = NULL;
+
+	arch_send_call_function_single_ipi_sym = (void *)symbol_get(met_arch_send_call_function_single_ipi);
+	if (arch_send_call_function_single_ipi_sym)
+		arch_send_call_function_single_ipi_sym(*cpu);
+}
+
+
+static ssize_t ipi_test_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			 size_t count)
+{
+	int this_cpu = smp_processor_id();
+	unsigned int cpu = 0;
+	unsigned int value;
+
+	if (met_parse_num(buf, &value, count) < 0)
+		return -EINVAL;
+
+	cpu = value;
+	if (cpu == this_cpu)
+		_test_trace_ipi_raise(&cpu);
+	else
+		met_smp_call_function_single_symbol(cpu, _test_trace_ipi_raise, &cpu, 1);
+
+	return count;
+}
+
+
+#if	defined(MET_BOOT_MSG)
+char met_boot_msg_tmp[256];
+char met_boot_msg[PAGE_SIZE];
+int met_boot_msg_idx;
+
+int pr_bootmsg(int str_len, char *str)
+{
+	if (met_boot_msg_idx+str_len+1 > PAGE_SIZE)
+		return -1;
+	memcpy(met_boot_msg+met_boot_msg_idx, str, str_len);
+	met_boot_msg_idx += str_len;
+	return 0;
+}
+
+static ssize_t bootmsg_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int	i;
+
+	mutex_lock(&dev->mutex);
+	i = snprintf(buf, PAGE_SIZE, "%s\n", met_boot_msg);
+	mutex_unlock(&dev->mutex);
+	return i;
+}
+
+static DEVICE_ATTR_RO(bootmsg);
+EXPORT_SYMBOL(met_boot_msg_tmp);
+EXPORT_SYMBOL(pr_bootmsg);
+#endif
+
+static ssize_t spr_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int i;
+
+	mutex_lock(&dev->mutex);
+	i = snprintf(buf, PAGE_SIZE, "%d\n", sample_rate);
+	mutex_unlock(&dev->mutex);
+	return i;
+}
+
+static ssize_t spr_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			 size_t count)
+{
+	int value;
+	struct metdevice *c = NULL;
+
+	mutex_lock(&dev->mutex);
+
+	if ((run == 1) || (count == 0) || (buf == NULL)) {
+		mutex_unlock(&dev->mutex);
+		return -EINVAL;
+	}
+	if (kstrtoint(buf, 0, &value) != 0) {
+		mutex_unlock(&dev->mutex);
+		return -EINVAL;
+	}
+
+	if ((value < 0) || (value > 10000)) {
+		mutex_unlock(&dev->mutex);
+		return -EINVAL;
+	}
+
+	calc_timer_value(value);
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->polling_interval > 0)
+			c->polling_count_reload = ((c->polling_interval * sample_rate) - 1) / 1000;
+		else
+			c->polling_count_reload = 0;
+	}
+
+	mutex_unlock(&dev->mutex);
+
+	return count;
+}
+
+static ssize_t run_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int i;
+
+	mutex_lock(&dev->mutex);
+	i = snprintf(buf, PAGE_SIZE, "%d\n", run);
+	mutex_unlock(&dev->mutex);
+	return i;
+}
+
+static ssize_t run_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			 size_t count)
+{
+	int value;
+
+	mutex_lock(&dev->mutex);
+
+	if ((count == 0) || (buf == NULL)) {
+		mutex_unlock(&dev->mutex);
+		return -EINVAL;
+	}
+	if (kstrtoint(buf, 0, &value) != 0) {
+		mutex_unlock(&dev->mutex);
+		return -EINVAL;
+	}
+
+	switch (value) {
+	case 1:
+		if (run != 1) {
+			run = 1;
+			met_run();
+		}
+		break;
+	case 0:
+		if (run != 0) {
+			if (run == 1) {
+				met_stop();
+#ifdef MET_USER_EVENT_SUPPORT
+#ifdef CONFIG_MET_MODULE
+				met_save_dump_buffer_real("/data/trace.dump");
+#else
+				met_save_dump_buffer("/data/trace.dump");
+#endif
+#endif
+				run = 0;
+			} else
+				/* run == -1 */
+				run = 0;
+		}
+		break;
+	case -1:
+		if (run != -1) {
+			if (run == 1)
+				met_stop();
+
+			run = -1;
+		}
+		break;
+	default:
+		mutex_unlock(&dev->mutex);
+		return -EINVAL;
+	}
+
+	mutex_unlock(&dev->mutex);
+
+	return count;
+}
+
+static unsigned int met_ksym_addr;
+static char met_func_name[512];
+static ssize_t ksym_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int i;
+	int len = 0;
+	int idx = 0;
+
+	mutex_lock(&dev->mutex);
+	if (met_ksym_addr != 0)
+		len = sprint_symbol_no_offset(met_func_name, met_ksym_addr);
+	if (len != 0) {
+		for (idx = 0; idx < 512; idx++)
+			if (met_func_name[idx] == ' ')
+				met_func_name[idx] = '\0';
+		i = snprintf(buf, PAGE_SIZE, "%s\n", met_func_name);
+	} else
+		i = snprintf(buf, PAGE_SIZE, "ksymlookup fail(%x)\n", met_ksym_addr);
+
+	mutex_unlock(&dev->mutex);
+	return i;
+}
+
+static ssize_t ksym_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			 size_t count)
+{
+	mutex_lock(&dev->mutex);
+
+	if ((count == 0) || (buf == NULL)) {
+		mutex_unlock(&dev->mutex);
+		return -EINVAL;
+	}
+	if (kstrtoint(buf, 16, &met_ksym_addr) != 0) {
+		mutex_unlock(&dev->mutex);
+		return -EINVAL;
+	}
+
+	mutex_unlock(&dev->mutex);
+
+	return count;
+}
+
+#if	defined(PR_CPU_NOTIFY)
+static ssize_t cpu_notify_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int i;
+
+	i = snprintf(buf, PAGE_SIZE, "%d\n", met_cpu_notify);
+	return i;
+}
+
+static ssize_t cpu_notify_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			  size_t count)
+{
+	if ((count == 0) || (buf == NULL))
+		return -EINVAL;
+
+	if (kstrtoint(buf, 0, &met_cpu_notify) != 0)
+		return -EINVAL;
+
+	return count;
+}
+#endif
+
+#ifdef CONFIG_CPU_FREQ
+static ssize_t dvfs_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int i;
+
+	i = snprintf(buf, PAGE_SIZE, "%d\n", 0);
+	return i;
+}
+
+static ssize_t dvfs_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			  size_t count)
+{
+	return count;
+}
+#endif
+
+static ssize_t suspend_compensation_enable_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int ret;
+
+	ret = snprintf(buf, PAGE_SIZE, "%d\n", met_suspend_compensation_mode);
+
+	return ret;
+}
+
+static ssize_t suspend_compensation_enable_store(struct device *dev, struct device_attribute *attr,
+			const char *buf, size_t count)
+{
+	int value;
+
+	if ((count == 0) || (buf == NULL))
+		return -EINVAL;
+
+	if (kstrtoint(buf, 0, &value) != 0)
+		return -EINVAL;
+
+	if (value < 0)
+		return -EINVAL;
+
+	met_suspend_compensation_mode = value;
+
+	return count;
+}
+
+static ssize_t suspend_compensation_flag_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int ret;
+
+	ret = snprintf(buf, PAGE_SIZE, "%d\n", met_suspend_compensation_flag);
+
+	return ret;
+}
+
+static ssize_t hash_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	return 0;
+}
+
+static ssize_t hash_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			 size_t count)
+{
+	return 0;
+}
+
+static DEVICE_ATTR(hash, 0664, hash_show, hash_store);
+
+static ssize_t mode_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	struct metdevice *c = NULL;
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->kobj == kobj)
+			break;
+	}
+	if (c == NULL)
+		return -ENOENT;
+
+	return snprintf(buf, PAGE_SIZE, "%d\n", c->mode);
+}
+
+static ssize_t mode_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf,
+			  size_t n)
+{
+	struct metdevice *c = NULL;
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->kobj == kobj)
+			break;
+	}
+	if (c == NULL)
+		return -ENOENT;
+
+	if (kstrtoint(buf, 0, &(c->mode)) != 0)
+		return -EINVAL;
+
+	return n;
+}
+
+static struct kobj_attribute mode_attr = __ATTR(mode, 0664, mode_show, mode_store);
+
+static ssize_t ondiemet_mode_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	struct metdevice *c = NULL;
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->kobj == kobj)
+			break;
+	}
+	if (c == NULL)
+		return -ENOENT;
+
+	return snprintf(buf, PAGE_SIZE, "%d\n", c->ondiemet_mode);
+}
+
+static ssize_t ondiemet_mode_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf,
+			  size_t n)
+{
+	struct metdevice *c = NULL;
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->kobj == kobj)
+			break;
+	}
+	if (c == NULL)
+		return -ENOENT;
+
+	if (kstrtoint(buf, 0, &(c->ondiemet_mode)) != 0)
+		return -EINVAL;
+
+	return n;
+}
+
+static struct kobj_attribute ondiemet_mode_attr = __ATTR(ondiemet_mode, 0664, ondiemet_mode_show, ondiemet_mode_store);
+
+static ssize_t polling_interval_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	int interval = 1;
+	struct metdevice *c = NULL;
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->kobj == kobj)
+			break;
+	}
+	if (c == NULL)
+		return -ENOENT;
+
+	if (c->polling_interval)
+		interval = c->polling_interval;
+
+	return snprintf(buf, PAGE_SIZE, "%d\n", interval);
+}
+
+static ssize_t polling_interval_store(struct kobject *kobj, struct kobj_attribute *attr,
+				      const char *buf, size_t n)
+{
+	struct metdevice *c = NULL;
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->kobj == kobj)
+			break;
+	}
+	if (c == NULL)
+		return -ENOENT;
+
+	if (kstrtoint(buf, 0, &(c->polling_interval)) != 0)
+		return -EINVAL;
+
+	if (c->polling_interval > 0)
+		c->polling_count_reload = ((c->polling_interval * sample_rate) - 1) / 1000;
+	else
+		c->polling_count_reload = 0;
+
+	return n;
+}
+
+static struct kobj_attribute polling_interval_attr =
+__ATTR(polling_ms, 0664, polling_interval_show, polling_interval_store);
+
+static ssize_t header_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	struct metdevice *c = NULL;
+	ssize_t count = 0;
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->kobj == kobj)
+			break;
+	}
+	if (c == NULL)
+		return -ENOENT;
+
+	if (c->ondiemet_mode == 0) {
+		if ((c->mode) && (c->print_header))
+			return c->print_header(buf, PAGE_SIZE);
+	} else if (c->ondiemet_mode == 1) {
+		if ((c->mode) && (c->ondiemet_print_header))
+			return c->ondiemet_print_header(buf, PAGE_SIZE);
+	} else if (c->ondiemet_mode == 2) {
+		if ((c->mode) && (c->print_header))
+			count = c->print_header(buf, PAGE_SIZE);
+		if (count < PAGE_SIZE) {
+			if ((c->mode) && (c->ondiemet_print_header))
+				count += c->ondiemet_print_header(buf+count, PAGE_SIZE - count);
+		}
+		return count;
+	}
+
+	return 0;
+}
+
+static struct kobj_attribute header_attr = __ATTR(header, 0444, header_show, NULL);
+
+static ssize_t help_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	struct metdevice *c = NULL;
+	ssize_t count = 0;
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->kobj == kobj)
+			break;
+	}
+	if (c == NULL)
+		return -ENOENT;
+
+	if (c->ondiemet_mode == 0) {
+		if (c->print_help)
+			return c->print_help(buf, PAGE_SIZE);
+	} else if (c->ondiemet_mode == 1) {
+		if (c->ondiemet_print_help)
+			return c->ondiemet_print_help(buf, PAGE_SIZE);
+	} else if (c->ondiemet_mode == 2) {
+		if (c->print_help)
+			count = c->print_help(buf, PAGE_SIZE);
+		if (count < PAGE_SIZE) {
+			if (c->ondiemet_print_help)
+				count += c->ondiemet_print_help(buf+count, PAGE_SIZE - count);
+		}
+		return count;
+	}
+
+	return 0;
+}
+
+static struct kobj_attribute help_attr = __ATTR(help, 0444, help_show, NULL);
+
+static int argu_status = -1;
+static ssize_t argu_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf,
+			  size_t n)
+{
+	int ret = 0;
+	struct metdevice *c = NULL;
+
+	argu_status = -1;
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->kobj == kobj)
+			break;
+	}
+	if (c == NULL)
+		return -ENOENT;
+
+	if (c->ondiemet_mode == 0) {
+		if (c->process_argument)
+			ret = c->process_argument(buf, (int)n);
+	} else if (c->ondiemet_mode == 1) {
+		if (c->ondiemet_process_argument)
+			ret = c->ondiemet_process_argument(buf, (int)n);
+	} else if (c->ondiemet_mode == 2) {
+		if (c->process_argument)
+			ret = c->process_argument(buf, (int)n);
+		if (c->ondiemet_process_argument)
+			ret = c->ondiemet_process_argument(buf, (int)n);
+	}
+
+	if (ret != 0)
+		return -EINVAL;
+
+	argu_status = 0;
+	return n;
+}
+
+static ssize_t argu_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%d\n", argu_status);
+}
+
+static struct kobj_attribute argu_attr = __ATTR(argu, 0664, argu_show, argu_store);
+
+static ssize_t reset_store(struct kobject *kobj,
+			struct kobj_attribute *attr,
+			const char *buf,
+			size_t n)
+{
+	int ret = 0;
+	struct metdevice *c = NULL;
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->kobj == kobj)
+			break;
+	}
+	if (c == NULL)
+		return -ENOENT;
+
+	if (c->ondiemet_mode == 0) {
+		if (c->reset)
+			ret = c->reset();
+		else
+			c->mode = 0;
+	} else if (c->ondiemet_mode == 1) {
+		if (c->ondiemet_reset)
+			ret = c->ondiemet_reset();
+	} else if (c->ondiemet_mode == 2) {
+		if (c->reset)
+			ret = c->reset();
+		else
+			c->mode = 0;
+		if (c->ondiemet_reset)
+			ret = c->ondiemet_reset();
+	}
+
+	if (ret != 0)
+		return -EINVAL;
+
+	return n;
+}
+
+static struct kobj_attribute reset_attr = __ATTR(reset, 0220, NULL, reset_store);
+
+static ssize_t header_read_again_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	struct metdevice *c = NULL;
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->kobj == kobj)
+			break;
+	}
+	if (c == NULL)
+		return -ENOENT;
+
+	return snprintf(buf, PAGE_SIZE, "%d\n", c->header_read_again);
+}
+
+static struct kobj_attribute header_read_again_attr = __ATTR(header_read_again, 0664, header_read_again_show, NULL);
+
+
+int met_register(struct metdevice *met)
+{
+	int ret, cpu;
+	struct metdevice *c;
+
+	list_for_each_entry(c, &met_list, list) {
+		if (!strcmp(c->name, met->name))
+			return -EEXIST;
+	}
+
+	PR_BOOTMSG("met_register %s\n", met->name);
+
+	INIT_LIST_HEAD(&met->list);
+
+	/* Allocate timer count for per CPU */
+	met->polling_count = alloc_percpu(int);
+	if (met->polling_count == NULL)
+		return -EINVAL;
+
+	for_each_possible_cpu(cpu)
+		*(per_cpu_ptr(met->polling_count, cpu)) = 0;
+
+	if (met->polling_interval > 0) {
+		ret = ((met->polling_interval * sample_rate) - 1) / 1000;
+		met->polling_count_reload = ret;
+	} else
+		met->polling_count_reload = 0;
+
+	met->kobj = NULL;
+
+	if (met->type == MET_TYPE_BUS)
+		met->kobj = kobject_create_and_add(met->name, kobj_bus);
+	else if (met->type == MET_TYPE_PMU)
+		met->kobj = kobject_create_and_add(met->name, kobj_pmu);
+	else if (met->type == MET_TYPE_MISC)
+		met->kobj = kobject_create_and_add(met->name, kobj_misc);
+	else {
+		ret = -EINVAL;
+		goto err_out;
+	}
+
+	if (met->kobj == NULL) {
+		ret = -EINVAL;
+		goto err_out;
+	}
+
+	if (met->create_subfs) {
+		ret = met->create_subfs(met->kobj);
+		if (ret)
+			goto err_out;
+	}
+
+	ret = sysfs_create_file(met->kobj, &mode_attr.attr);
+	if (ret)
+		goto err_out;
+
+
+	ret = sysfs_create_file(met->kobj, &ondiemet_mode_attr.attr);
+	if (ret)
+		goto err_out;
+
+	ret = sysfs_create_file(met->kobj, &polling_interval_attr.attr);
+	if (ret)
+		goto err_out;
+
+	ret = sysfs_create_file(met->kobj, &header_read_again_attr.attr);
+	if (ret)
+		goto err_out;
+
+	if (met->print_header || met->ondiemet_print_header) {
+		ret = sysfs_create_file(met->kobj, &header_attr.attr);
+		if (ret)
+			goto err_out;
+	}
+
+	if (met->print_help || met->ondiemet_print_help) {
+		ret = sysfs_create_file(met->kobj, &help_attr.attr);
+		if (ret)
+			goto err_out;
+	}
+
+	if (met->process_argument || met->ondiemet_process_argument) {
+		ret = sysfs_create_file(met->kobj, &argu_attr.attr);
+		if (ret)
+			goto err_out;
+	}
+
+	if (met->reset) {
+		ret = sysfs_create_file(met->kobj, &reset_attr.attr);
+		if (ret)
+			goto err_out;
+	}
+
+	spin_lock_init(&met->my_lock);
+
+	list_add(&met->list, &met_list);
+	return 0;
+
+ err_out:
+
+	if (met->polling_count)
+		free_percpu(met->polling_count);
+
+	if (met->kobj) {
+		kobject_del(met->kobj);
+		kobject_put(met->kobj);
+		met->kobj = NULL;
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL(met_register);
+
+int met_deregister(struct metdevice *met)
+{
+	struct metdevice *c = NULL;
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c == met)
+			break;
+	}
+	if (c != met)
+		return -ENOENT;
+
+	if (met->print_header || met->ondiemet_print_header)
+		sysfs_remove_file(met->kobj, &header_attr.attr);
+
+	if (met->print_help || met->ondiemet_print_help)
+		sysfs_remove_file(met->kobj, &help_attr.attr);
+
+	if (met->process_argument || met->ondiemet_process_argument)
+		sysfs_remove_file(met->kobj, &argu_attr.attr);
+
+	sysfs_remove_file(met->kobj, &reset_attr.attr);
+	sysfs_remove_file(met->kobj, &header_read_again_attr.attr);
+	sysfs_remove_file(met->kobj, &polling_interval_attr.attr);
+	sysfs_remove_file(met->kobj, &mode_attr.attr);
+	sysfs_remove_file(met->kobj, &ondiemet_mode_attr.attr);
+
+	if (met->delete_subfs)
+		met->delete_subfs();
+
+	kobject_del(met->kobj);
+	kobject_put(met->kobj);
+	met->kobj = NULL;
+
+	if (met->polling_count)
+		free_percpu(met->polling_count);
+
+	list_del(&met->list);
+	return 0;
+}
+EXPORT_SYMBOL(met_deregister);
+
+int met_set_platform(const char *plf_name, int flag)
+{
+	strncpy(met_platform, plf_name, sizeof(met_platform) - 1);
+#if 0
+	int ret;
+
+	if (flag) {
+		ret = device_create_file(met_device.this_device, &dev_attr_plf);
+		if (ret != 0) {
+			pr_debug("can not create device file: plf\n");
+			return ret;
+		}
+		strncpy(met_platform, plf_name, sizeof(met_platform) - 1);
+	} else
+		device_remove_file(met_device.this_device, &dev_attr_plf);
+
+#endif
+	return 0;
+}
+EXPORT_SYMBOL(met_set_platform);
+
+int met_set_topology(const char *topology_name, int flag)
+{
+	strncpy(met_topology, topology_name, sizeof(met_topology) - 1);
+#if 0
+	int ret;
+
+	if (flag) {
+		ret = device_create_file(met_device.this_device, &dev_attr_core_topology);
+		if (ret != 0) {
+			pr_debug("can not create device file: topology\n");
+			return ret;
+		}
+		strncpy(met_topology, topology_name, sizeof(met_topology) - 1);
+	} else {
+		device_remove_file(met_device.this_device, &dev_attr_core_topology);
+	}
+#endif
+	return 0;
+}
+EXPORT_SYMBOL(met_set_topology);
+
+#include "met_struct.h"
+
+void force_sample(void *unused)
+{
+	int cpu;
+	unsigned long long stamp;
+	struct metdevice *c;
+	struct met_cpu_struct *met_cpu_ptr;
+
+	if ((run != 1) || (sample_rate == 0))
+		return;
+
+	/* to avoid met tag is coming after __met_hrtimer_stop and before run=-1 */
+	met_cpu_ptr = this_cpu_ptr(&met_cpu);
+	if (met_cpu_ptr->work_enabled == 0)
+		return;
+
+	cpu = smp_processor_id();
+
+	stamp = cpu_clock(cpu);
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->ondiemet_mode == 0) {
+			if ((c->mode != 0) && (c->tagged_polling != NULL))
+				c->tagged_polling(stamp, 0);
+		} else if (c->ondiemet_mode == 1) {
+			if ((c->mode != 0) && (c->ondiemet_tagged_polling != NULL))
+				c->ondiemet_tagged_polling(stamp, 0);
+		} else if (c->ondiemet_mode == 2) {
+			if ((c->mode != 0) && (c->tagged_polling != NULL))
+				c->tagged_polling(stamp, 0);
+			if ((c->mode != 0) && (c->ondiemet_tagged_polling != NULL))
+				c->ondiemet_tagged_polling(stamp, 0);
+		}
+	}
+}
+
+#define MET_SUSPEND_HAND
+#ifdef MET_SUSPEND_HAND
+static struct syscore_ops met_hrtimer_ops = {
+	.suspend = met_hrtimer_suspend,
+	.resume = met_hrtimer_resume,
+};
+#endif
+
+int fs_reg(void)
+{
+	int ret = 0;
+
+	ctrl_flags = 0;
+	met_mode = 0;
+
+#ifdef MET_SUSPEND_HAND
+	/* suspend/resume function handle register */
+	register_syscore_ops(&met_hrtimer_ops);
+#endif
+
+	calc_timer_value(sample_rate);
+
+	ret = misc_register(&met_device);
+	if (ret != 0) {
+		pr_debug("misc register failed\n");
+		return ret;
+	}
+
+	/* dma map config */
+	/* arch_setup_dma_ops(met_device.this_device, 0, 0, NULL, false); */
+	met_arch_setup_dma_ops_symbol(met_device.this_device);
+
+	ret = device_create_file(met_device.this_device, &dev_attr_ksym);
+	if (ret != 0) {
+		pr_debug("can not create device file: ksym\n");
+		return ret;
+	}
+
+	ret = device_create_file(met_device.this_device, &dev_attr_run);
+	if (ret != 0) {
+		pr_debug("can not create device file: run\n");
+		return ret;
+	}
+
+#if	defined(PR_CPU_NOTIFY)
+	ret = device_create_file(met_device.this_device, &dev_attr_cpu_notify);
+	if (ret != 0) {
+		pr_debug("can not create device file: cpu_notify\n");
+		return ret;
+	}
+#endif
+
+#ifdef CONFIG_CPU_FREQ
+	ret = device_create_file(met_device.this_device, &dev_attr_dvfs);
+	if (ret != 0) {
+		pr_debug("can not create device file: dvfs\n");
+		return ret;
+	}
+#endif
+
+	ret = device_create_file(met_device.this_device, &dev_attr_suspend_compensation_enable);
+	if (ret != 0) {
+		pr_debug("can not create device file: suspend_compensation_enable\n");
+		return ret;
+	}
+
+	ret = device_create_file(met_device.this_device, &dev_attr_suspend_compensation_flag);
+	if (ret != 0) {
+		pr_debug("can not create device file: suspend_compensation_enable\n");
+		return ret;
+	}
+
+	ret = device_create_file(met_device.this_device, &dev_attr_ver);
+	if (ret != 0) {
+		pr_debug("can not create device file: ver\n");
+		return ret;
+	}
+
+	ret = device_create_file(met_device.this_device, &dev_attr_devices);
+	if (ret != 0) {
+		pr_debug("can not create device file: devices\n");
+		return ret;
+	}
+
+	ret = device_create_file(met_device.this_device, &dev_attr_ctrl);
+	if (ret != 0) {
+		pr_debug("can not create device file: ctrl\n");
+		return ret;
+	}
+
+	ret = device_create_file(met_device.this_device, &dev_attr_cpu_pmu_method);
+	if (ret != 0) {
+		pr_debug("can not create device file: cpu_pmu_method\n");
+		return ret;
+	}
+
+	ret = device_create_file(met_device.this_device, &dev_attr_cpu_pm_pmu_reconfig);
+	if (ret != 0) {
+		pr_debug("can not create device file: cpu_pm_pmu_reconfig\n");
+		return ret;
+	}
+
+#if	defined(MET_BOOT_MSG)
+	ret = device_create_file(met_device.this_device, &dev_attr_bootmsg);
+	if (ret != 0) {
+		pr_debug("can not create device file: bootmsg\n");
+		return ret;
+	}
+#endif
+
+	ret = device_create_file(met_device.this_device, &dev_attr_sample_rate);
+	if (ret != 0) {
+		pr_debug("can not create device file: sample_rate\n");
+		return ret;
+	}
+
+	ret = device_create_file(met_device.this_device, &dev_attr_core_topology);
+	if (ret != 0) {
+		pr_debug("can not create device file: topology\n");
+		return ret;
+	}
+
+	ret = device_create_file(met_device.this_device, &dev_attr_plf);
+	if (ret != 0) {
+		pr_debug("can not create device file: plf\n");
+		return ret;
+	}
+
+	ret = device_create_file(met_device.this_device, &dev_attr_hash);
+	if (ret != 0) {
+		pr_debug("can not create device file: hash\n");
+		return ret;
+	}
+
+	ret = device_create_file(met_device.this_device, &dev_attr_ipi_test);
+	if (ret != 0) {
+		pr_debug("can not create device file: ipi_test\n");
+		return ret;
+	}
+
+	kobj_misc = kobject_create_and_add("misc", &met_device.this_device->kobj);
+	if (kobj_misc == NULL) {
+		pr_debug("can not create kobject: kobj_misc\n");
+		return ret;
+	}
+
+	kobj_pmu = kobject_create_and_add("pmu", &met_device.this_device->kobj);
+	if (kobj_pmu == NULL) {
+		pr_debug("can not create kobject: kobj_pmu\n");
+		return ret;
+	}
+
+	kobj_bus = kobject_create_and_add("bus", &met_device.this_device->kobj);
+	if (kobj_bus == NULL) {
+		pr_debug("can not create kobject: kobj_bus\n");
+		return ret;
+	}
+
+	met_register(&met_cookie);
+	met_register(&met_cpupmu);
+	met_register(&met_memstat);
+	met_register(&met_switch);
+	/* met_register(&met_trace_event); */
+	met_register(&met_dummy_header);
+
+	met_register(&met_backlight);
+	met_ext_api.enable_met_backlight_tag = enable_met_backlight_tag;
+	met_ext_api.output_met_backlight_tag = output_met_backlight_tag;
+
+#ifdef MET_USER_EVENT_SUPPORT
+	tag_reg((struct file_operations * const) met_device.fops, &met_device.this_device->kobj);
+#endif
+
+	met_register(&met_stat);
+
+	ondiemet_log_manager_init(met_device.this_device);
+	ondiemet_attr_init(met_device.this_device);
+
+	return ret;
+}
+
+void fs_unreg(void)
+{
+	if (run == 1)
+		met_stop();
+
+	run = -1;
+
+	met_deregister(&met_stat);
+
+#ifdef MET_USER_EVENT_SUPPORT
+	tag_unreg();
+#endif
+
+	met_deregister(&met_dummy_header);
+	/* met_deregister(&met_trace_event); */
+	met_deregister(&met_switch);
+	met_deregister(&met_memstat);
+	met_deregister(&met_cpupmu);
+	met_deregister(&met_cookie);
+	met_deregister(&met_backlight);
+	met_ext_api.enable_met_backlight_tag = NULL;
+	met_ext_api.output_met_backlight_tag = NULL;
+
+	kobject_del(kobj_misc);
+	kobject_put(kobj_misc);
+	kobj_misc = NULL;
+	kobject_del(kobj_pmu);
+	kobject_put(kobj_pmu);
+	kobj_pmu = NULL;
+	kobject_del(kobj_bus);
+	kobject_put(kobj_bus);
+	kobj_bus = NULL;
+
+	device_remove_file(met_device.this_device, &dev_attr_ksym);
+
+	device_remove_file(met_device.this_device, &dev_attr_run);
+#ifdef PR_CPU_NOTIFY
+	device_remove_file(met_device.this_device, &dev_attr_cpu_notify);
+#endif
+#ifdef CONFIG_CPU_FREQ
+	device_remove_file(met_device.this_device, &dev_attr_dvfs);
+#endif
+	device_remove_file(met_device.this_device, &dev_attr_suspend_compensation_enable);
+	device_remove_file(met_device.this_device, &dev_attr_suspend_compensation_flag);
+
+	device_remove_file(met_device.this_device, &dev_attr_ver);
+	device_remove_file(met_device.this_device, &dev_attr_devices);
+	device_remove_file(met_device.this_device, &dev_attr_sample_rate);
+
+	device_remove_file(met_device.this_device, &dev_attr_ctrl);
+	device_remove_file(met_device.this_device, &dev_attr_cpu_pmu_method);
+	device_remove_file(met_device.this_device, &dev_attr_cpu_pm_pmu_reconfig);
+
+	device_remove_file(met_device.this_device, &dev_attr_core_topology);
+	device_remove_file(met_device.this_device, &dev_attr_plf);
+	device_remove_file(met_device.this_device, &dev_attr_hash);
+	device_remove_file(met_device.this_device, &dev_attr_ipi_test);
+
+	ondiemet_log_manager_uninit(met_device.this_device);
+	ondiemet_attr_uninit(met_device.this_device);
+
+	misc_deregister(&met_device);
+#ifdef MET_SUSPEND_HAND
+	/* suspend/resume function handle register */
+	unregister_syscore_ops(&met_hrtimer_ops);
+#endif
+}
+
+unsigned int get_ctrl_flags(void)
+{
+	return ctrl_flags;
+}
diff --git a/src/devtools/met-driver/4.9/common/interface.h b/src/devtools/met-driver/4.9/common/interface.h
new file mode 100644
index 0000000..6cbbe6d
--- /dev/null
+++ b/src/devtools/met-driver/4.9/common/interface.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __INTERFACE_H__
+#define __INTERFACE_H__
+
+#include <linux/fs.h>
+
+#ifdef MET_USER_EVENT_SUPPORT
+extern int tag_reg(struct file_operations *const fops, struct kobject *kobj);
+extern int tag_unreg(void);
+#include "met_drv.h"
+#include "met_tag.h"
+extern struct bltable_t bltab;
+#endif
+
+extern struct metdevice met_stat;
+extern struct metdevice met_cpupmu;
+extern struct metdevice met_cookie;
+extern struct metdevice met_memstat;
+extern struct metdevice met_switch;
+extern struct metdevice met_trace_event;
+extern struct metdevice met_dummy_header;
+extern struct metdevice met_backlight;
+
+/* This variable will decide which method to access the CPU PMU counter */
+/*     0: access registers directly */
+/*     others: via Linux perf driver */
+extern unsigned int met_cpu_pmu_method;
+
+/*
+ * controls whether re-configuring pmu events after leaving cpu off state
+ */
+extern unsigned int met_cpu_pm_pmu_reconfig;
+
+extern int met_parse_num(const char *str, unsigned int *value, int len);
+extern void met_set_suspend_notify(int flag);
+
+#define	PR_CPU_NOTIFY
+#if	defined(PR_CPU_NOTIFY)
+extern int met_cpu_notify;
+#endif
+
+//#undef	MET_BOOT_MSG
+#define	MET_BOOT_MSG
+#if	defined(MET_BOOT_MSG)
+extern char met_boot_msg_tmp[256];
+extern int pr_bootmsg(int str_len, char *str);
+#define	PR_BOOTMSG(fmt, args...) { \
+	int str_len = snprintf(met_boot_msg_tmp, sizeof(met_boot_msg_tmp), \
+			       fmt, ##args); \
+	pr_bootmsg(str_len, met_boot_msg_tmp); }
+#define	PR_BOOTMSG_ONCE(fmt, args...) { \
+	static int once; \
+	if (!once) { \
+		int str_len = snprintf(met_boot_msg_tmp, \
+				       sizeof(met_boot_msg_tmp), \
+				       fmt, ##args); \
+		pr_bootmsg(str_len, met_boot_msg_tmp); \
+		once = 1; \
+	} }
+#else
+#define	pr_bootmsg(str_len, str)
+#define PR_BOOTMSG(fmt, args...)
+#define	PR_BOOTMSG_ONCE(fmt, args...)
+#endif
+
+#endif	/* __INTERFACE_H__ */
diff --git a/src/devtools/met-driver/4.9/common/mem_stat.c b/src/devtools/met-driver/4.9/common/mem_stat.c
new file mode 100644
index 0000000..055824e
--- /dev/null
+++ b/src/devtools/met-driver/4.9/common/mem_stat.c
@@ -0,0 +1,313 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/cpu.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+
+#include "met_drv.h"
+#include "mem_stat.h"
+#include "trace.h"
+
+
+/* define MEMSTAT_DEBUG */
+#ifdef MEMSTAT_DEBUG
+#define debug_memstat(fmt, arg...) pr_debug(fmt, ##arg)
+#else
+#define debug_memstat(fmt, arg...) do {} while (0)
+#endif
+
+struct metdevice met_memstat;
+
+unsigned int phy_memstat_mask;
+unsigned int vir_memstat_mask;
+
+#define MAX_PHY_MEMSTAT_EVENT_AMOUNT 6
+#define MAX_VIR_MEMSTAT_EVENT_AMOUNT 6
+
+struct mem_event phy_memstat_table[] = {
+	{FREE_MEM, "free_mem", "Free Memory"}
+};
+
+#define PHY_MEMSTAT_TABLE_SIZE (sizeof(phy_memstat_table) / sizeof(struct mem_event))
+
+struct mem_event vir_memstat_table[] = {
+	{FILE_PAGES, "file_pages", "File Pages"},
+	{FILE_DIRTY, "file_dirty", "FD APP->FS(KB)"},
+	{NUM_DIRTIED, "num_dirtied", "Num Dirtied"},
+	{WRITE_BACK, "write_back", "WB. FS->Block IO(KB)"},
+	{NUM_WRITTEN, "num_written", "Num Written"},
+	{PG_FAULT_CNT, "pg_fault_cnt", "Page Fault Count"}
+};
+
+#define VIR_MEMSTAT_TABLE_SIZE (sizeof(vir_memstat_table) / sizeof(struct mem_event))
+
+int vm_event_counters_enable;
+unsigned long *vm_status;
+static struct delayed_work dwork;
+
+noinline void memstat(unsigned int cnt, unsigned int *value)
+{
+	MET_GENERAL_PRINT(MET_TRACE, cnt, value);
+}
+
+static int get_phy_memstat(unsigned int *value)
+{
+	int i, cnt = 0;
+	struct sysinfo info;
+
+#define K(x) ((x) << (PAGE_SHIFT - 10))
+
+	si_meminfo(&info);
+
+	for (i = 0; i < MAX_PHY_MEMSTAT_EVENT_AMOUNT; i++) {
+		if (phy_memstat_mask & (1 << i)) {
+			switch (i) {
+			case FREE_MEM:
+				value[cnt] = K(info.freeram);
+				break;
+			}
+
+			cnt++;
+		}
+	}
+
+	return cnt;
+}
+
+static int get_vir_memstat(unsigned int *value)
+{
+	int i, cnt = 0;
+
+	for (i = 0; i < NR_VM_ZONE_STAT_ITEMS; i++)
+		vm_status[i] = global_page_state(i);
+
+	all_vm_events(vm_status + NR_VM_ZONE_STAT_ITEMS);
+
+	for (i = 0; i < MAX_VIR_MEMSTAT_EVENT_AMOUNT; i++) {
+		if (vir_memstat_mask & (1 << i)) {
+			switch (i) {
+			case FILE_PAGES:
+				value[cnt] = vm_status[NR_FILE_PAGES] << (PAGE_SHIFT - 10);
+				break;
+			case FILE_DIRTY:
+				value[cnt] = vm_status[NR_FILE_DIRTY] << (PAGE_SHIFT - 10);
+				break;
+			case NUM_DIRTIED:
+				value[cnt] = vm_status[NR_DIRTIED] << (PAGE_SHIFT - 10);
+				break;
+			case WRITE_BACK:
+				value[cnt] = vm_status[NR_WRITEBACK] << (PAGE_SHIFT - 10);
+				break;
+			case NUM_WRITTEN:
+				value[cnt] = vm_status[NR_WRITTEN] << (PAGE_SHIFT - 10);
+				break;
+			case PG_FAULT_CNT:
+				value[cnt] = vm_status[NR_VM_ZONE_STAT_ITEMS + PGFAULT];
+				break;
+			}
+
+			cnt++;
+		}
+	}
+
+	return cnt;
+}
+
+static void wq_get_memstat(struct work_struct *work)
+{
+	int total_event_amount = 0, phy_event_amount = 0;
+	unsigned int stat_val[MAX_PHY_MEMSTAT_EVENT_AMOUNT + MAX_VIR_MEMSTAT_EVENT_AMOUNT];
+
+	memset(stat_val, 0, sizeof(unsigned int) * (MAX_PHY_MEMSTAT_EVENT_AMOUNT + MAX_VIR_MEMSTAT_EVENT_AMOUNT));
+	total_event_amount = phy_event_amount = get_phy_memstat(stat_val);
+
+	if (vm_event_counters_enable)
+		total_event_amount += get_vir_memstat(&(stat_val[phy_event_amount]));
+
+	if (total_event_amount <= (MAX_PHY_MEMSTAT_EVENT_AMOUNT + MAX_VIR_MEMSTAT_EVENT_AMOUNT))
+		memstat(total_event_amount-1, stat_val);
+}
+
+void met_memstat_polling(unsigned long long stamp, int cpu)
+{
+	schedule_delayed_work(&dwork, 0);
+}
+
+static void met_memstat_start(void)
+{
+	int stat_items_size = 0;
+
+	stat_items_size = NR_VM_ZONE_STAT_ITEMS * sizeof(unsigned long);
+
+#ifdef CONFIG_VM_EVENT_COUNTERS
+	stat_items_size += sizeof(struct vm_event_state);
+#endif
+
+	vm_status = kmalloc(stat_items_size, GFP_KERNEL);
+	if (vm_status == NULL)
+		return;
+	INIT_DELAYED_WORK(&dwork, wq_get_memstat);
+}
+
+static void met_memstat_stop(void)
+{
+	kfree(vm_status);
+	cancel_delayed_work_sync(&dwork);
+}
+
+static const char help[] =
+"  --memstat=[phy_mem_stat|vir_mem_stat]:event_name enable sampling physical & virtual memory status\n";
+
+static int met_memstat_print_help(char *buf, int len)
+{
+	int i, l;
+
+	l = snprintf(buf, PAGE_SIZE, help);
+
+	for (i = 0; i < PHY_MEMSTAT_TABLE_SIZE; i++)
+		l += snprintf(buf + l, PAGE_SIZE - l, "  --memstat=phy_mem_stat:%s\n",
+			      phy_memstat_table[i].name);
+
+#ifdef CONFIG_VM_EVENT_COUNTERS
+	for (i = 0; i < VIR_MEMSTAT_TABLE_SIZE; i++)
+		l += snprintf(buf + l, PAGE_SIZE - l, "  --memstat=vir_mem_stat:%s\n",
+			      vir_memstat_table[i].name);
+#endif
+
+	return l;
+}
+
+static const char header[] = "met-info [000] 0.0: ms_ud_sys_header: memstat,";
+
+
+static int met_memstat_print_header(char *buf, int len)
+{
+	int i, l;
+	int event_amount = 0;
+
+	l = snprintf(buf, PAGE_SIZE, header);
+
+	for (i = 0; i < MAX_PHY_MEMSTAT_EVENT_AMOUNT; i++) {
+		if ((phy_memstat_mask & (1 << i)) && (i < PHY_MEMSTAT_TABLE_SIZE)) {
+			l += snprintf(buf + l, PAGE_SIZE - l, phy_memstat_table[i].header_name);
+			l += snprintf(buf + l, PAGE_SIZE - l, ",");
+			event_amount++;
+		}
+	}
+
+#ifdef CONFIG_VM_EVENT_COUNTERS
+	for (i = 0; i < MAX_VIR_MEMSTAT_EVENT_AMOUNT; i++) {
+		if ((vir_memstat_mask & (1 << i)) && (i < VIR_MEMSTAT_TABLE_SIZE)) {
+			l += snprintf(buf + l, PAGE_SIZE - l, vir_memstat_table[i].header_name);
+			l += snprintf(buf + l, PAGE_SIZE - l, ",");
+			event_amount++;
+		}
+	}
+#endif
+
+	for (i = 0; i < event_amount; i++) {
+		l += snprintf(buf + l, PAGE_SIZE - l, "x");
+		l += snprintf(buf + l, PAGE_SIZE - l, ",");
+	}
+
+	phy_memstat_mask = 0;
+	vir_memstat_mask = 0;
+
+	l += snprintf(buf + l, PAGE_SIZE - l, "\n");
+
+	return l;
+}
+
+static int met_memstat_process_argument(const char *arg, int len)
+{
+	int i, found_event = 0;
+	char choice[16], event[32];
+	char *pch;
+	int str_len;
+
+
+#ifdef CONFIG_VM_EVENT_COUNTERS
+	vm_event_counters_enable = 1;
+#endif
+
+	pch = strchr(arg, ':');
+	if (pch == NULL)
+		goto error;
+
+	memset(choice, 0, sizeof(choice));
+	memset(event, 0, sizeof(event));
+
+	str_len = (int)(pch - arg);
+	memcpy(choice, arg, str_len);
+	memcpy(event, arg + str_len + 1, len - (str_len + 1));
+
+	if (strncmp(choice, "phy_mem_stat", 12) == 0) {
+		for (i = 0; i < PHY_MEMSTAT_TABLE_SIZE; i++) {
+			if (strncmp(event, phy_memstat_table[i].name, MAX_EVENT_NAME_LEN) == 0) {
+				phy_memstat_mask |= (1 << phy_memstat_table[i].id);
+				found_event = 1;
+
+				break;
+			}
+		}
+	} else if (strncmp(choice, "vir_mem_stat", 12) == 0) {
+		if (!vm_event_counters_enable) {
+			pr_debug("[%s] %d: CONFIG_VM_EVENT_COUNTERS is not configured\n", __func__,
+				 __LINE__);
+			goto error;
+		}
+
+		for (i = 0; i < VIR_MEMSTAT_TABLE_SIZE; i++) {
+			if (strncmp(event, vir_memstat_table[i].name, MAX_EVENT_NAME_LEN) == 0) {
+				vir_memstat_mask |= (1 << vir_memstat_table[i].id);
+				found_event = 1;
+
+				break;
+			}
+		}
+	} else {
+		pr_debug("[%s] %d: only support phy_mem_stat & vir_mem_stat keyword\n", __func__,
+			 __LINE__);
+		goto error;
+	}
+
+	if (!found_event) {
+		pr_debug("[%s] %d: input event name error\n", __func__, __LINE__);
+		goto error;
+	}
+
+	met_memstat.mode = 1;
+	return 0;
+
+error:
+	met_memstat.mode = 0;
+	return -EINVAL;
+}
+
+struct metdevice met_memstat = {
+	.name = "memstat",
+	.type = MET_TYPE_PMU,
+	.cpu_related = 0,
+	.start = met_memstat_start,
+	.stop = met_memstat_stop,
+	.polling_interval = 1,
+	.timed_polling = met_memstat_polling,
+	.tagged_polling = met_memstat_polling,
+	.print_help = met_memstat_print_help,
+	.print_header = met_memstat_print_header,
+	.process_argument = met_memstat_process_argument
+};
diff --git a/src/devtools/met-driver/4.9/common/mem_stat.h b/src/devtools/met-driver/4.9/common/mem_stat.h
new file mode 100644
index 0000000..d716075
--- /dev/null
+++ b/src/devtools/met-driver/4.9/common/mem_stat.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __MEM_STAT_H__
+#define __MEM_STAT_H__
+
+#define MAX_EVENT_NAME_LEN 32
+
+enum phy_mem_event_id {
+	FREE_MEM = 0
+};
+
+enum vir_mem_event_id {
+	FILE_PAGES = 0,
+	FILE_DIRTY,
+	NUM_DIRTIED,
+	WRITE_BACK,
+	NUM_WRITTEN,
+	PG_FAULT_CNT
+};
+
+struct mem_event {
+	int id;
+	char name[32];
+	char header_name[32];
+};
+
+#endif
diff --git a/src/devtools/met-driver/4.9/common/met_api_tbl.h b/src/devtools/met-driver/4.9/common/met_api_tbl.h
new file mode 100644
index 0000000..f93ae5f
--- /dev/null
+++ b/src/devtools/met-driver/4.9/common/met_api_tbl.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+struct met_api_tbl {
+	int (*met_tag_start)(unsigned int class_id, const char *name);
+	int (*met_tag_end)(unsigned int class_id, const char *name);
+	int (*met_tag_async_start)(unsigned int class_id, const char *name, unsigned int cookie);
+	int (*met_tag_async_end)(unsigned int class_id, const char *name, unsigned int cookie);
+	int (*met_tag_oneshot)(unsigned int class_id, const char *name, unsigned int value);
+	int (*met_tag_userdata)(char *pData);
+	int (*met_tag_dump)(unsigned int class_id, const char *name, void *data, unsigned int length);
+	int (*met_tag_disable)(unsigned int class_id);
+	int (*met_tag_enable)(unsigned int class_id);
+	int (*met_set_dump_buffer)(int size);
+	int (*met_save_dump_buffer)(const char *pathname);
+	int (*met_save_log)(const char *pathname);
+	int (*met_show_bw_limiter)(void);
+	int (*met_reg_bw_limiter)(void *fp);
+	int (*met_show_clk_tree)(const char *name, unsigned int addr, unsigned int status);
+	int (*met_reg_clk_tree)(void *fp);
+	void (*met_sched_switch)(struct task_struct *prev, struct task_struct *next);
+	int (*enable_met_backlight_tag)(void);
+	int (*output_met_backlight_tag)(int level);
+};
+
+extern struct met_api_tbl met_ext_api;
diff --git a/src/devtools/met-driver/4.9/common/met_backlight.c b/src/devtools/met-driver/4.9/common/met_backlight.c
new file mode 100644
index 0000000..37f1ae9
--- /dev/null
+++ b/src/devtools/met-driver/4.9/common/met_backlight.c
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+#define MET_USER_EVENT_SUPPORT
+#include "met_drv.h"
+#include "trace.h"
+
+static int met_backlight_enable;
+static DEFINE_SPINLOCK(met_backlight_lock);
+static struct kobject *kobj_met_backlight;
+
+static ssize_t bl_tag_enable_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf);
+static ssize_t bl_tag_enable_store(struct kobject *kobj,
+				   struct kobj_attribute *attr, const char *buf, size_t n);
+static struct kobj_attribute bl_tag_enable_attr =
+__ATTR(backlight_tag_enable, 0664, bl_tag_enable_show, bl_tag_enable_store);
+
+int enable_met_backlight_tag(void)
+{
+	return met_backlight_enable;
+}
+
+int output_met_backlight_tag(int level)
+{
+	int ret;
+	unsigned long flags;
+
+	spin_lock_irqsave(&met_backlight_lock, flags);
+#ifdef CONFIG_MET_MODULE
+	ret = met_tag_oneshot_real(33880, "_MM_BL_", level);
+#else
+	ret = met_tag_oneshot(33880, "_MM_BL_", level);
+#endif
+	spin_unlock_irqrestore(&met_backlight_lock, flags);
+
+	return ret;
+}
+
+static ssize_t bl_tag_enable_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	int ret;
+
+	ret = snprintf(buf, PAGE_SIZE, "%d\n", met_backlight_enable);
+
+	return ret;
+}
+
+static ssize_t bl_tag_enable_store(struct kobject *kobj,
+				   struct kobj_attribute *attr, const char *buf, size_t n)
+{
+	int value;
+
+	if ((n == 0) || (buf == NULL))
+		return -EINVAL;
+
+	if (kstrtoint(buf, 0, &value) != 0)
+		return -EINVAL;
+
+	if (value < 0)
+		return -EINVAL;
+
+	met_backlight_enable = value;
+
+	return n;
+}
+
+static int met_backlight_create(struct kobject *parent)
+{
+	int ret = 0;
+
+	kobj_met_backlight = parent;
+
+	ret = sysfs_create_file(kobj_met_backlight, &bl_tag_enable_attr.attr);
+	if (ret != 0) {
+		pr_debug("Failed to create montype0 in sysfs\n");
+		return ret;
+	}
+
+	return ret;
+}
+
+static void met_backlight_delete(void)
+{
+	sysfs_remove_file(kobj_met_backlight, &bl_tag_enable_attr.attr);
+	kobj_met_backlight = NULL;
+}
+
+struct metdevice met_backlight = {
+	.name = "backlight",
+	.owner = THIS_MODULE,
+	.type = MET_TYPE_BUS,
+	.create_subfs = met_backlight_create,
+	.delete_subfs = met_backlight_delete,
+	.cpu_related = 0,
+};
+EXPORT_SYMBOL(met_backlight);
diff --git a/src/devtools/met-driver/4.9/common/met_drv.h b/src/devtools/met-driver/4.9/common/met_drv.h
new file mode 100644
index 0000000..df7ed26
--- /dev/null
+++ b/src/devtools/met-driver/4.9/common/met_drv.h
@@ -0,0 +1,269 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+
+#ifndef MET_DRV
+#define MET_DRV
+
+#include <linux/version.h>
+#include <linux/preempt.h>
+#include <linux/device.h>
+#include <linux/percpu.h>
+#include <linux/hardirq.h>
+#include <linux/clk.h>
+
+extern int met_mode;
+extern int core_plf_init(void);
+extern void core_plf_exit(void);
+
+#define MET_MODE_TRACE_CMD_OFFSET	(1)
+#define MET_MODE_TRACE_CMD			(1<<MET_MODE_TRACE_CMD_OFFSET)
+
+#ifdef CONFIG_MET_MODULE
+#define my_preempt_enable() preempt_enable()
+#else
+#define my_preempt_enable() preempt_enable_no_resched()
+#endif
+
+#define MET_STRBUF_SIZE		1024
+DECLARE_PER_CPU(char[MET_STRBUF_SIZE], met_strbuf_nmi);
+DECLARE_PER_CPU(char[MET_STRBUF_SIZE], met_strbuf_irq);
+DECLARE_PER_CPU(char[MET_STRBUF_SIZE], met_strbuf_sirq);
+DECLARE_PER_CPU(char[MET_STRBUF_SIZE], met_strbuf);
+
+#ifdef CONFIG_TRACING
+#define TRACE_PUTS(p) \
+	do { \
+		trace_puts(p);; \
+	} while (0)
+#else
+#define TRACE_PUTS(p) do {} while (0)
+#endif
+
+#define GET_MET_TRACE_BUFFER_ENTER_CRITICAL() \
+	({ \
+		char *pmet_strbuf; \
+		preempt_disable(); \
+		if (in_nmi()) \
+			pmet_strbuf = per_cpu(met_strbuf_nmi, smp_processor_id()); \
+		else if (in_irq()) \
+			pmet_strbuf = per_cpu(met_strbuf_irq, smp_processor_id()); \
+		else if (in_softirq()) \
+			pmet_strbuf = per_cpu(met_strbuf_sirq, smp_processor_id()); \
+		else \
+			pmet_strbuf = per_cpu(met_strbuf, smp_processor_id()); \
+		pmet_strbuf;\
+	})
+
+#define PUT_MET_TRACE_BUFFER_EXIT_CRITICAL(pmet_strbuf) \
+	do {\
+		if (pmet_strbuf)\
+			TRACE_PUTS(pmet_strbuf); \
+		my_preempt_enable(); \
+	} while (0)
+
+#define MET_TRACE(FORMAT, args...) \
+	do { \
+		char *pmet_strbuf; \
+		preempt_disable(); \
+		if (in_nmi()) \
+			pmet_strbuf = per_cpu(met_strbuf_nmi, smp_processor_id()); \
+		else if (in_irq()) \
+			pmet_strbuf = per_cpu(met_strbuf_irq, smp_processor_id()); \
+		else if (in_softirq()) \
+			pmet_strbuf = per_cpu(met_strbuf_sirq, smp_processor_id()); \
+		else \
+			pmet_strbuf = per_cpu(met_strbuf, smp_processor_id()); \
+		if (met_mode & MET_MODE_TRACE_CMD) \
+			snprintf(pmet_strbuf, MET_STRBUF_SIZE, "%s: " FORMAT, __func__, ##args); \
+		else \
+			snprintf(pmet_strbuf, MET_STRBUF_SIZE, FORMAT, ##args); \
+		TRACE_PUTS(pmet_strbuf); \
+		my_preempt_enable(); \
+	} while (0)
+
+/*
+ * SOB: start of buf
+ * EOB: end of buf
+ */
+#define MET_TRACE_GETBUF(pSOB, pEOB) \
+	({ \
+		preempt_disable(); \
+		if (in_nmi()) \
+			*pSOB = per_cpu(met_strbuf_nmi, smp_processor_id()); \
+		else if (in_irq()) \
+			*pSOB = per_cpu(met_strbuf_irq, smp_processor_id()); \
+		else if (in_softirq()) \
+			*pSOB = per_cpu(met_strbuf_sirq, smp_processor_id()); \
+		else \
+			*pSOB = per_cpu(met_strbuf, smp_processor_id()); \
+		*pEOB = *pSOB; \
+		if (met_mode & MET_MODE_TRACE_CMD) \
+			*pEOB += snprintf(*pEOB, MET_STRBUF_SIZE, "%s: ", __func__); \
+	})
+
+#define MET_TRACE_PUTBUF(SOB, EOB) \
+	({ \
+		__trace_puts(_THIS_IP_, (SOB), (uintptr_t)((EOB)-(SOB))); \
+		my_preempt_enable(); \
+	})
+
+#define MET_FTRACE_DUMP(TRACE_NAME, args...)			\
+	do {							\
+		trace_##TRACE_NAME(args);;			\
+	} while (0)
+
+
+#define MET_TYPE_PMU	1
+#define MET_TYPE_BUS	2
+#define MET_TYPE_MISC	3
+
+struct metdevice {
+	struct list_head list;
+	int type;
+	const char *name;
+	struct module *owner;
+	struct kobject *kobj;
+
+	int (*create_subfs)(struct kobject *parent);
+	void (*delete_subfs)(void);
+	int mode;
+	int ondiemet_mode;	/* new for ondiemet; 1: call ondiemet functions */
+	int cpu_related;
+	int polling_interval;
+	int polling_count_reload;
+	int __percpu *polling_count;
+	int header_read_again;	/*for header size > 1 page */
+	void (*start)(void);
+	void (*uniq_start)(void);
+	void (*stop)(void);
+	void (*uniq_stop)(void);
+	int (*reset)(void);
+	void (*timed_polling)(unsigned long long stamp, int cpu);
+	void (*tagged_polling)(unsigned long long stamp, int cpu);
+	int (*print_help)(char *buf, int len);
+	int (*print_header)(char *buf, int len);
+	int (*process_argument)(const char *arg, int len);
+	void (*cpu_state_notify)(long cpu, unsigned long action);
+
+	void (*ondiemet_start)(void);
+	void (*ondiemet_stop)(void);
+	int (*ondiemet_reset)(void);
+	int (*ondiemet_print_help)(char *buf, int len);
+	int (*ondiemet_print_header)(char *buf, int len);
+	int (*ondiemet_process_argument)(const char *arg, int len);
+	void (*ondiemet_timed_polling)(unsigned long long stamp, int cpu);
+	void (*ondiemet_tagged_polling)(unsigned long long stamp, int cpu);
+
+	struct list_head exlist;	/* for linked list before register */
+	void (*suspend)(void);
+	void (*resume)(void);
+
+	unsigned long long prev_stamp;
+	spinlock_t my_lock;
+	void *reversed1;
+};
+
+int met_register(struct metdevice *met);
+int met_deregister(struct metdevice *met);
+int met_set_platform(const char *plf_name, int flag);
+int met_set_topology(const char *topology_name, int flag);
+int met_devlink_add(struct metdevice *met);
+int met_devlink_del(struct metdevice *met);
+int met_devlink_register_all(void);
+int met_devlink_deregister_all(void);
+
+int fs_reg(void);
+void fs_unreg(void);
+
+/******************************************************************************
+ * Tracepoints
+ ******************************************************************************/
+#define MET_DEFINE_PROBE(probe_name, proto) \
+		static void probe_##probe_name(void *data, PARAMS(proto))
+#define MET_REGISTER_TRACE(probe_name) \
+		register_trace_##probe_name(probe_##probe_name, NULL)
+#define MET_UNREGISTER_TRACE(probe_name) \
+		unregister_trace_##probe_name(probe_##probe_name, NULL)
+
+
+/* ====================== Tagging API ================================ */
+
+#define MAX_EVENT_CLASS	31
+#define MAX_TAGNAME_LEN	128
+#define MET_CLASS_ALL	0x80000000
+
+/* IOCTL commands of MET tagging */
+struct mtag_cmd_t {
+	unsigned int class_id;
+	unsigned int value;
+	unsigned int slen;
+	char tname[MAX_TAGNAME_LEN];
+	void *data;
+	unsigned int size;
+};
+
+#define TYPE_START		1
+#define TYPE_END		2
+#define TYPE_ONESHOT		3
+#define TYPE_ENABLE		4
+#define TYPE_DISABLE		5
+#define TYPE_REC_SET		6
+#define TYPE_DUMP		7
+#define TYPE_DUMP_SIZE		8
+#define TYPE_DUMP_SAVE		9
+#define TYPE_USRDATA		10
+#define TYPE_DUMP_AGAIN		11
+#define TYPE_ASYNC_START	12
+#define TYPE_ASYNC_END		13
+#define TYPE_MET_SUSPEND	15
+#define TYPE_MET_RESUME		16
+
+/* Use 'm' as magic number */
+#define MTAG_IOC_MAGIC  'm'
+/* Please use a different 8-bit number in your code */
+#define MTAG_CMD_START		_IOW(MTAG_IOC_MAGIC, TYPE_START, struct mtag_cmd_t)
+#define MTAG_CMD_END		_IOW(MTAG_IOC_MAGIC, TYPE_END, struct mtag_cmd_t)
+#define MTAG_CMD_ONESHOT	_IOW(MTAG_IOC_MAGIC, TYPE_ONESHOT, struct mtag_cmd_t)
+#define MTAG_CMD_ENABLE		_IOW(MTAG_IOC_MAGIC, TYPE_ENABLE, int)
+#define MTAG_CMD_DISABLE	_IOW(MTAG_IOC_MAGIC, TYPE_DISABLE, int)
+#define MTAG_CMD_REC_SET	_IOW(MTAG_IOC_MAGIC, TYPE_REC_SET, int)
+#define MTAG_CMD_DUMP		_IOW(MTAG_IOC_MAGIC, TYPE_DUMP, struct mtag_cmd_t)
+#define MTAG_CMD_DUMP_SIZE	_IOWR(MTAG_IOC_MAGIC, TYPE_DUMP_SIZE, int)
+#define MTAG_CMD_DUMP_SAVE	_IOW(MTAG_IOC_MAGIC, TYPE_DUMP_SAVE, struct mtag_cmd_t)
+#define MTAG_CMD_USRDATA	_IOW(MTAG_IOC_MAGIC, TYPE_USRDATA, struct mtag_cmd_t)
+#define MTAG_CMD_DUMP_AGAIN	_IOW(MTAG_IOC_MAGIC, TYPE_DUMP_AGAIN, void *)
+#define MTAG_CMD_ASYNC_START	_IOW(MTAG_IOC_MAGIC, TYPE_ASYNC_START, struct mtag_cmd_t)
+#define MTAG_CMD_ASYNC_END	_IOW(MTAG_IOC_MAGIC, TYPE_ASYNC_END, struct mtag_cmd_t)
+
+/* include file */
+extern int met_tag_start_real(unsigned int class_id, const char *name);
+extern int met_tag_end_real(unsigned int class_id, const char *name);
+extern int met_tag_async_start_real(unsigned int class_id, const char *name, unsigned int cookie);
+extern int met_tag_async_end_real(unsigned int class_id, const char *name, unsigned int cookie);
+extern int met_tag_oneshot_real(unsigned int class_id, const char *name, unsigned int value);
+extern int met_tag_userdata_real(char *pData);
+extern int met_tag_dump_real(unsigned int class_id, const char *name, void *data, unsigned int length);
+extern int met_tag_disable_real(unsigned int class_id);
+extern int met_tag_enable_real(unsigned int class_id);
+extern int met_set_dump_buffer_real(int size);
+extern int met_save_dump_buffer_real(const char *pathname);
+extern int met_save_log_real(const char *pathname);
+extern int met_show_bw_limiter_real(void);
+extern int met_reg_bw_limiter_real(void *fp);
+extern int met_show_clk_tree_real(const char *name, unsigned int addr, unsigned int status);
+extern int met_reg_clk_tree_real(void *fp);
+extern int enable_met_backlight_tag(void);
+extern int output_met_backlight_tag(int level);
+
+#endif	/* MET_DRV */
diff --git a/src/devtools/met-driver/4.9/common/met_emi.c b/src/devtools/met-driver/4.9/common/met_emi.c
new file mode 100644
index 0000000..de5ff19
--- /dev/null
+++ b/src/devtools/met-driver/4.9/common/met_emi.c
@@ -0,0 +1,914 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/device.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/dma-mapping.h>
+#include <linux/string.h>
+
+#define MET_USER_EVENT_SUPPORT
+#include "met_drv.h"
+#include "trace.h"
+
+#include "mtk_typedefs.h"
+#include "core_plf_init.h"
+/* #include "plf_trace.h" */
+#include "mtk_emi_bm.h"
+#include "interface.h"
+
+#if defined(CONFIG_MTK_TINYSYS_SSPM_SUPPORT) && defined(ONDIEMET_SUPPORT)
+#include "sspm/ondiemet_sspm.h"
+#endif
+
+/*======================================================================*/
+/*	Global variable definitions					*/
+/*======================================================================*/
+int emi_TP_busfiltr_enable;
+static int msel_enable;
+static unsigned int msel_group1 = _GP_1_Default;
+static unsigned int msel_group2 = _GP_2_Default;
+static unsigned int msel_group3 = _GP_3_Default;
+
+/* Global variables */
+static struct kobject *kobj_emi;
+
+static int ttype1_16_en = BM_TTYPE1_16_DISABLE;
+static int ttype17_21_en = BM_TTYPE17_21_DISABLE;
+
+
+
+static int times;
+static ssize_t test_store(struct kobject *kobj,
+				struct kobj_attribute *attr,
+				const char *buf,
+				size_t n)
+{
+	int i;
+	unsigned int    *src_addr_v;
+	dma_addr_t src_addr_p;
+
+	if ((n == 0) || (buf == NULL))
+		return -EINVAL;
+	if (kstrtoint(buf, 10, &times) != 0)
+		return -EINVAL;
+	if (times < 0)
+		return -EINVAL;
+
+	if (times > 5000)
+		return -EINVAL;
+
+
+	src_addr_v = dma_alloc_coherent(met_device.this_device,
+					PAGE_SIZE,
+					&src_addr_p,
+					GFP_KERNEL);
+	if (src_addr_v == NULL) {
+#ifdef CONFIG_MET_MODULE
+		met_tag_oneshot_real(0, "test dma alloc fail", PAGE_SIZE);
+#else
+		met_tag_oneshot(0, "test dma alloc fail", PAGE_SIZE);
+#endif
+		return -ENOMEM;
+	}
+
+	preempt_disable();
+#ifdef CONFIG_MET_MODULE
+	met_tag_start_real(0, "TEST_EMI");
+#else
+	met_tag_start(0, "TEST_EMI");
+#endif
+	for (i = 0; i < times; i++) {
+		memset(src_addr_v, 2*i, PAGE_SIZE);
+#ifdef CONFIG_MET_MODULE
+		met_tag_oneshot_real(0, "TEST_EMI", PAGE_SIZE);
+#else
+		met_tag_oneshot(0, "TEST_EMI", PAGE_SIZE);
+#endif
+	}
+#ifdef CONFIG_MET_MODULE
+	met_tag_end_real(0, "TEST_EMI");
+#else
+	met_tag_end(0, "TEST_EMI");
+#endif
+
+	my_preempt_enable();
+
+	if (src_addr_v != NULL)
+		dma_free_coherent(met_device.this_device,
+				  PAGE_SIZE,
+				  src_addr_v,
+				  src_addr_p);
+	return n;
+}
+
+/*======================================================================*/
+/*	KOBJ Declarations						*/
+/*======================================================================*/
+DECLARE_KOBJ_ATTR_INT(emi_TP_busfiltr_enable, emi_TP_busfiltr_enable);
+DECLARE_KOBJ_ATTR_INT(msel_enable, msel_enable);
+DECLARE_KOBJ_ATTR_HEX_CHECK(msel_group1, msel_group1, msel_group1 > 0 && msel_group1 <= _ALL);
+DECLARE_KOBJ_ATTR_HEX_CHECK(msel_group2, msel_group2, msel_group2 > 0 && msel_group2 <= _ALL);
+DECLARE_KOBJ_ATTR_HEX_CHECK(msel_group3, msel_group3, msel_group3 > 0 && msel_group3 <= _ALL);
+
+
+DECLARE_KOBJ_ATTR_STR_LIST_ITEM(
+	ttype1_16_en,
+	KOBJ_ITEM_LIST(
+		{ BM_TTYPE1_16_ENABLE,   "ENABLE" },
+		{ BM_TTYPE1_16_DISABLE,  "DISABLE" }
+		)
+	);
+DECLARE_KOBJ_ATTR_STR_LIST(ttype1_16_en, ttype1_16_en, ttype1_16_en);
+
+
+DECLARE_KOBJ_ATTR_STR_LIST_ITEM(
+	ttype17_21_en,
+	KOBJ_ITEM_LIST(
+		{ BM_TTYPE17_21_ENABLE,  "ENABLE" },
+		{ BM_TTYPE17_21_DISABLE, "DISABLE" }
+		)
+	);
+DECLARE_KOBJ_ATTR_STR_LIST(ttype17_21_en, ttype17_21_en, ttype17_21_en);
+
+DECLARE_KOBJ_ATTR_STR_LIST_ITEM(
+	ttype_master,
+	KOBJ_ITEM_LIST(
+		{ _M0,  "M0" },
+		{ _M1,  "M1" },
+		{ _M2,  "M2" },
+		{ _M3,  "M3" },
+		{ _M4,  "M4" },
+		{ _M5,  "M5" },
+		{ _M6,  "M6" },
+		{ _M7,  "M7" }
+		)
+	);
+
+
+DECLARE_KOBJ_ATTR_INT_LIST_ITEM(
+	ttype_nbeat,
+	KOBJ_ITEM_LIST(
+		{ BM_TRANS_TYPE_1BEAT,   1 },
+		{ BM_TRANS_TYPE_2BEAT,   2 },
+		{ BM_TRANS_TYPE_3BEAT,   3 },
+		{ BM_TRANS_TYPE_4BEAT,   4 },
+		{ BM_TRANS_TYPE_5BEAT,   5 },
+		{ BM_TRANS_TYPE_6BEAT,   6 },
+		{ BM_TRANS_TYPE_7BEAT,   7 },
+		{ BM_TRANS_TYPE_8BEAT,   8 },
+		{ BM_TRANS_TYPE_9BEAT,   9 },
+		{ BM_TRANS_TYPE_10BEAT,  10 },
+		{ BM_TRANS_TYPE_11BEAT,  11 },
+		{ BM_TRANS_TYPE_12BEAT,  12 },
+		{ BM_TRANS_TYPE_13BEAT,  13 },
+		{ BM_TRANS_TYPE_14BEAT,  14 },
+		{ BM_TRANS_TYPE_15BEAT,  15 },
+		{ BM_TRANS_TYPE_16BEAT,  16 }
+		)
+	);
+DECLARE_KOBJ_ATTR_INT_LIST_ITEM(
+	ttype_nbyte,
+	KOBJ_ITEM_LIST(
+		{ BM_TRANS_TYPE_1Byte,   1 },
+		{ BM_TRANS_TYPE_2Byte,   2 },
+		{ BM_TRANS_TYPE_4Byte,   4 },
+		{ BM_TRANS_TYPE_8Byte,   8 },
+		{ BM_TRANS_TYPE_16Byte,  16 },
+		{ BM_TRANS_TYPE_32Byte,  32 }
+		)
+	);
+DECLARE_KOBJ_ATTR_STR_LIST_ITEM(
+	ttype_burst,
+	KOBJ_ITEM_LIST(
+		{ BM_TRANS_TYPE_BURST_INCR,      "INCR" },
+		{ BM_TRANS_TYPE_BURST_WRAP,      "WRAP" }
+		)
+	);
+
+DECLARE_KOBJ_ATTR_STR_LIST_ITEM(
+	ttype_rw,
+	KOBJ_ITEM_LIST(
+		{ BM_TRANS_RW_DEFAULT,   "DEFAULT" },
+		{ BM_TRANS_RW_READONLY,  "R" },
+		{ BM_TRANS_RW_WRITEONLY, "W" },
+		{ BM_TRANS_RW_RWBOTH,    "BOTH" }
+		)
+	);
+
+
+DECLARE_KOBJ_ATTR_SHOW_INT(test, times);
+
+DECLARE_KOBJ_ATTR(test);
+
+
+static int high_priority_filter;
+DECLARE_KOBJ_ATTR_HEX(high_priority_filter, high_priority_filter);
+
+
+static int ttype_master_val[21];
+static int ttype_busid_val[21];
+static int ttype_nbeat_val[21];
+static int ttype_nbyte_val[21];
+static int ttype_burst_val[21];
+static int ttype_rw_val[21];
+
+#define DECLARE_KOBJ_TTYPE_MASTER(nr) \
+	DECLARE_KOBJ_ATTR_STR_LIST(ttype ## nr ## _master, ttype_master_val[nr - 1], ttype_master)
+
+#define DECLARE_KOBJ_TTYPE_NBEAT(nr) \
+	DECLARE_KOBJ_ATTR_INT_LIST(ttype ## nr ## _nbeat, ttype_nbeat_val[nr - 1], ttype_nbeat)
+
+#define DECLARE_KOBJ_TTYPE_NBYTE(nr) \
+	DECLARE_KOBJ_ATTR_INT_LIST(ttype ## nr ## _nbyte, ttype_nbyte_val[nr - 1], ttype_nbyte)
+
+#define DECLARE_KOBJ_TTYPE_BURST(nr) \
+	DECLARE_KOBJ_ATTR_STR_LIST(ttype ## nr ## _burst, ttype_burst_val[nr - 1], ttype_burst)
+
+#define DECLARE_KOBJ_TTYPE_RW(nr) \
+	DECLARE_KOBJ_ATTR_STR_LIST(ttype ## nr ## _rw, ttype_rw_val[nr - 1], ttype_rw)
+
+#define DECLARE_KOBJ_TTYPE_BUSID_VAL(nr) \
+	DECLARE_KOBJ_ATTR_HEX(ttype ## nr ## _busid, ttype_busid_val[nr - 1])
+
+DECLARE_KOBJ_TTYPE_MASTER(1);
+DECLARE_KOBJ_TTYPE_NBEAT(1);
+DECLARE_KOBJ_TTYPE_NBYTE(1);
+DECLARE_KOBJ_TTYPE_BURST(1);
+DECLARE_KOBJ_TTYPE_RW(1);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(1);
+
+DECLARE_KOBJ_TTYPE_MASTER(2);
+DECLARE_KOBJ_TTYPE_NBEAT(2);
+DECLARE_KOBJ_TTYPE_NBYTE(2);
+DECLARE_KOBJ_TTYPE_BURST(2);
+DECLARE_KOBJ_TTYPE_RW(2);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(2);
+
+DECLARE_KOBJ_TTYPE_MASTER(3);
+DECLARE_KOBJ_TTYPE_NBEAT(3);
+DECLARE_KOBJ_TTYPE_NBYTE(3);
+DECLARE_KOBJ_TTYPE_BURST(3);
+DECLARE_KOBJ_TTYPE_RW(3);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(3);
+
+DECLARE_KOBJ_TTYPE_MASTER(4);
+DECLARE_KOBJ_TTYPE_NBEAT(4);
+DECLARE_KOBJ_TTYPE_NBYTE(4);
+DECLARE_KOBJ_TTYPE_BURST(4);
+DECLARE_KOBJ_TTYPE_RW(4);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(4);
+
+DECLARE_KOBJ_TTYPE_MASTER(5);
+DECLARE_KOBJ_TTYPE_NBEAT(5);
+DECLARE_KOBJ_TTYPE_NBYTE(5);
+DECLARE_KOBJ_TTYPE_BURST(5);
+DECLARE_KOBJ_TTYPE_RW(5);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(5);
+
+DECLARE_KOBJ_TTYPE_MASTER(6);
+DECLARE_KOBJ_TTYPE_NBEAT(6);
+DECLARE_KOBJ_TTYPE_NBYTE(6);
+DECLARE_KOBJ_TTYPE_BURST(6);
+DECLARE_KOBJ_TTYPE_RW(6);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(6);
+
+DECLARE_KOBJ_TTYPE_MASTER(7);
+DECLARE_KOBJ_TTYPE_NBEAT(7);
+DECLARE_KOBJ_TTYPE_NBYTE(7);
+DECLARE_KOBJ_TTYPE_BURST(7);
+DECLARE_KOBJ_TTYPE_RW(7);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(7);
+
+DECLARE_KOBJ_TTYPE_MASTER(8);
+DECLARE_KOBJ_TTYPE_NBEAT(8);
+DECLARE_KOBJ_TTYPE_NBYTE(8);
+DECLARE_KOBJ_TTYPE_BURST(8);
+DECLARE_KOBJ_TTYPE_RW(8);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(8);
+
+DECLARE_KOBJ_TTYPE_MASTER(9);
+DECLARE_KOBJ_TTYPE_NBEAT(9);
+DECLARE_KOBJ_TTYPE_NBYTE(9);
+DECLARE_KOBJ_TTYPE_BURST(9);
+DECLARE_KOBJ_TTYPE_RW(9);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(9);
+
+DECLARE_KOBJ_TTYPE_MASTER(10);
+DECLARE_KOBJ_TTYPE_NBEAT(10);
+DECLARE_KOBJ_TTYPE_NBYTE(10);
+DECLARE_KOBJ_TTYPE_BURST(10);
+DECLARE_KOBJ_TTYPE_RW(10);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(10);
+
+DECLARE_KOBJ_TTYPE_MASTER(11);
+DECLARE_KOBJ_TTYPE_NBEAT(11);
+DECLARE_KOBJ_TTYPE_NBYTE(11);
+DECLARE_KOBJ_TTYPE_BURST(11);
+DECLARE_KOBJ_TTYPE_RW(11);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(11);
+
+DECLARE_KOBJ_TTYPE_MASTER(12);
+DECLARE_KOBJ_TTYPE_NBEAT(12);
+DECLARE_KOBJ_TTYPE_NBYTE(12);
+DECLARE_KOBJ_TTYPE_BURST(12);
+DECLARE_KOBJ_TTYPE_RW(12);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(12);
+
+DECLARE_KOBJ_TTYPE_MASTER(13);
+DECLARE_KOBJ_TTYPE_NBEAT(13);
+DECLARE_KOBJ_TTYPE_NBYTE(13);
+DECLARE_KOBJ_TTYPE_BURST(13);
+DECLARE_KOBJ_TTYPE_RW(13);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(13);
+
+DECLARE_KOBJ_TTYPE_MASTER(14);
+DECLARE_KOBJ_TTYPE_NBEAT(14);
+DECLARE_KOBJ_TTYPE_NBYTE(14);
+DECLARE_KOBJ_TTYPE_BURST(14);
+DECLARE_KOBJ_TTYPE_RW(14);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(14);
+
+DECLARE_KOBJ_TTYPE_MASTER(15);
+DECLARE_KOBJ_TTYPE_NBEAT(15);
+DECLARE_KOBJ_TTYPE_NBYTE(15);
+DECLARE_KOBJ_TTYPE_BURST(15);
+DECLARE_KOBJ_TTYPE_RW(15);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(15);
+
+DECLARE_KOBJ_TTYPE_MASTER(16);
+DECLARE_KOBJ_TTYPE_NBEAT(16);
+DECLARE_KOBJ_TTYPE_NBYTE(16);
+DECLARE_KOBJ_TTYPE_BURST(16);
+DECLARE_KOBJ_TTYPE_RW(16);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(16);
+
+DECLARE_KOBJ_TTYPE_MASTER(17);
+DECLARE_KOBJ_TTYPE_NBEAT(17);
+DECLARE_KOBJ_TTYPE_NBYTE(17);
+DECLARE_KOBJ_TTYPE_BURST(17);
+DECLARE_KOBJ_TTYPE_RW(17);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(17);
+
+DECLARE_KOBJ_TTYPE_MASTER(18);
+DECLARE_KOBJ_TTYPE_NBEAT(18);
+DECLARE_KOBJ_TTYPE_NBYTE(18);
+DECLARE_KOBJ_TTYPE_BURST(18);
+DECLARE_KOBJ_TTYPE_RW(18);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(18);
+
+DECLARE_KOBJ_TTYPE_MASTER(19);
+DECLARE_KOBJ_TTYPE_NBEAT(19);
+DECLARE_KOBJ_TTYPE_NBYTE(19);
+DECLARE_KOBJ_TTYPE_BURST(19);
+DECLARE_KOBJ_TTYPE_RW(19);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(19);
+
+DECLARE_KOBJ_TTYPE_MASTER(20);
+DECLARE_KOBJ_TTYPE_NBEAT(20);
+DECLARE_KOBJ_TTYPE_NBYTE(20);
+DECLARE_KOBJ_TTYPE_BURST(20);
+DECLARE_KOBJ_TTYPE_RW(20);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(20);
+
+DECLARE_KOBJ_TTYPE_MASTER(21);
+DECLARE_KOBJ_TTYPE_NBEAT(21);
+DECLARE_KOBJ_TTYPE_NBYTE(21);
+DECLARE_KOBJ_TTYPE_BURST(21);
+DECLARE_KOBJ_TTYPE_RW(21);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(21);
+
+#define KOBJ_ATTR_ITEM_SERIAL_FNODE(nr) \
+	do { \
+		KOBJ_ATTR_ITEM(ttype ## nr ## _master); \
+		KOBJ_ATTR_ITEM(ttype ## nr ## _nbeat); \
+		KOBJ_ATTR_ITEM(ttype ## nr ## _nbyte); \
+		KOBJ_ATTR_ITEM(ttype ## nr ## _burst); \
+		KOBJ_ATTR_ITEM(ttype ## nr ## _busid); \
+		KOBJ_ATTR_ITEM(ttype ## nr ## _rw); \
+	} while (0)
+
+#define KOBJ_ATTR_LIST \
+	do { \
+		KOBJ_ATTR_ITEM(high_priority_filter); \
+		KOBJ_ATTR_ITEM(emi_TP_busfiltr_enable); \
+		KOBJ_ATTR_ITEM(msel_enable); \
+		KOBJ_ATTR_ITEM(msel_group1); \
+		KOBJ_ATTR_ITEM(msel_group2); \
+		KOBJ_ATTR_ITEM(msel_group3); \
+		KOBJ_ATTR_ITEM(ttype17_21_en); \
+		KOBJ_ATTR_ITEM(ttype1_16_en); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(1); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(2); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(3); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(4); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(5); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(6); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(7); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(8); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(9); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(10); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(11); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(12); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(13); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(14); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(15); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(16); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(17); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(18); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(19); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(20); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(21); \
+		KOBJ_ATTR_ITEM(test); \
+	} while (0)
+
+/*======================================================================*/
+/*	EMI Operations							*/
+/*======================================================================*/
+static void emi_init(void)
+{
+	unsigned int bmrw0_val = 0, bmrw1_val = 0, i, enable;
+	unsigned int msel_group_val[4];
+
+	MET_BM_SaveCfg();
+
+	if ((ttype1_16_en != BM_TTYPE1_16_ENABLE) && (emi_TP_busfiltr_enable != 1)) {
+		if (msel_enable) {
+			msel_group_val[0] = _ALL;
+			msel_group_val[1] = msel_group1;
+			msel_group_val[2] = msel_group2;
+			msel_group_val[3] = msel_group3;
+		} else {
+			msel_group_val[0] = _ALL;
+			msel_group_val[1] = _ALL;
+			msel_group_val[2] = _ALL;
+			msel_group_val[3] = _ALL;
+		}
+
+		MET_BM_SetLatencyCounter(1);
+
+		for (i = 1; i <= 4; i++) {
+			MET_BM_SetMonitorCounter(i,
+						 msel_group_val[i - 1] & _ALL,
+						 BM_TRANS_TYPE_4BEAT |
+						 BM_TRANS_TYPE_8Byte |
+						 BM_TRANS_TYPE_BURST_WRAP);
+			MET_BM_SetbusID(i, 0);
+			MET_BM_SetbusID_En(i, 0);
+		}
+		for (i = 0; i < 4; i++)
+			MET_BM_Set_WsctTsct_id_sel(i, 0);
+
+	} else if ((ttype1_16_en != BM_TTYPE1_16_ENABLE) && (emi_TP_busfiltr_enable == 1)) {
+		MET_BM_SetLatencyCounter(1);
+
+		for (i = 1; i <= 4; i++) {
+			MET_BM_SetMonitorCounter(i,
+						 ttype_master_val[i - 1],
+						 ttype_nbeat_val[i - 1] |
+						 ttype_nbyte_val[i - 1] |
+						 ttype_burst_val[i - 1]);
+			MET_BM_SetbusID(i, ttype_busid_val[i - 1]);
+			MET_BM_SetbusID_En(i, 0);
+		}
+		for (i = 0; i < 4; i++)
+			MET_BM_Set_WsctTsct_id_sel(i, 1);
+
+	} else if ((ttype1_16_en == BM_TTYPE1_16_ENABLE) && (emi_TP_busfiltr_enable != 1)) {
+		MET_BM_SetLatencyCounter(0);
+
+		for (i = 1; i <= 16; i++) {
+			MET_BM_SetMonitorCounter(i,
+						 ttype_master_val[i - 1],
+						 ttype_nbeat_val[i - 1] |
+						 ttype_nbyte_val[i - 1] |
+						 ttype_burst_val[i - 1]);
+
+			MET_BM_SetbusID(i, ttype_busid_val[i - 1]);
+
+			MET_BM_SetbusID_En(i, (ttype_busid_val[i - 1] > 0xffff) ? 0 : 1);
+		}
+		for (i = 0; i < 4; i++)
+			MET_BM_Set_WsctTsct_id_sel(i, 0);
+	} else {	/* (ttype1_16_en == BM_TTYPE1_16_ENABLE)  &&  (emi_TP_busfiltr_enable == 1) */
+
+		MET_BM_SetLatencyCounter(0);
+
+		for (i = 1; i <= 16; i++) {
+			MET_BM_SetMonitorCounter(i,
+						 ttype_master_val[i - 1],
+						 ttype_nbeat_val[i - 1] |
+						 ttype_nbyte_val[i - 1] |
+						 ttype_burst_val[i - 1]);
+
+			MET_BM_SetbusID(i, ttype_busid_val[i - 1]);
+
+			MET_BM_SetbusID_En(i, (ttype_busid_val[i - 1] > 0xffff) ? 0 : 1);
+		}
+		for (i = 0; i < 4; i++)
+			MET_BM_Set_WsctTsct_id_sel(i, 1);
+	}
+
+	if (ttype17_21_en == BM_TTYPE17_21_ENABLE) {
+		for (i = 17; i <= 21; i++) {
+			MET_BM_SetMonitorCounter(i,
+						 ttype_master_val[i - 1],
+						 ttype_nbeat_val[i - 1] |
+						 ttype_nbyte_val[i - 1] |
+						 ttype_burst_val[i - 1]);
+			MET_BM_SetbusID(i, ttype_busid_val[i - 1]);
+
+			MET_BM_SetbusID_En(i, (ttype_busid_val[i - 1] > 0xffff) ? 0 : 1);
+		}
+	}
+
+	bmrw0_val = (
+		(ttype_rw_val[0] << 0) | (ttype_rw_val[1] << 2) |
+		(ttype_rw_val[2] << 4) | (ttype_rw_val[3] << 6) |
+		(ttype_rw_val[4] << 8) | (ttype_rw_val[5] << 10) |
+		(ttype_rw_val[6] << 12) | (ttype_rw_val[7] << 14) |
+		(ttype_rw_val[8] << 16) | (ttype_rw_val[9] << 18) |
+		(ttype_rw_val[10] << 20) | (ttype_rw_val[11] << 22) |
+		(ttype_rw_val[12] << 24) | (ttype_rw_val[13] << 26) |
+		(ttype_rw_val[14] << 28) | (ttype_rw_val[15] << 30));
+
+	bmrw1_val = (
+		(ttype_rw_val[16] << 0) | (ttype_rw_val[17] << 2) |
+		(ttype_rw_val[18] << 4) | (ttype_rw_val[19] << 6) |
+		(ttype_rw_val[20] << 8));
+
+	MET_BM_SetTtypeCounterRW(bmrw0_val, bmrw1_val);
+
+	for (i = 0; i < BM_COUNTER_MAX; i++) {
+		if ((high_priority_filter & (1 << i)) == 0)
+			enable = 0;
+		else
+			enable = 1;
+
+		MET_BM_SetUltraHighFilter(i + 1, enable);
+	}
+
+}
+
+static inline int do_emi(void)
+{
+	return met_sspm_emi.mode;
+}
+
+/*======================================================================*/
+/*	MET Device Operations						*/
+/*======================================================================*/
+static int emi_inited;
+static int met_emi_create(struct kobject *parent)
+{
+	int ret = 0;
+	int i;
+
+	for (i = 0; i < 21; i++) {
+		ttype_master_val[i] = _M0;
+		ttype_nbeat_val[i] = BM_TRANS_TYPE_1BEAT;
+		ttype_nbyte_val[i] = BM_TRANS_TYPE_8Byte;
+		ttype_burst_val[i] = BM_TRANS_TYPE_BURST_INCR;
+		ttype_busid_val[i] = 0xfffff;
+		ttype_rw_val[i] =  BM_TRANS_RW_DEFAULT;
+	}
+	ret = MET_BM_Init();
+	if (ret != 0) {
+		pr_debug("MET_BM_Init failed!!!\n");
+		ret = 0;
+	} else
+		emi_inited = 1;
+
+	kobj_emi = parent;
+
+#define	KOBJ_ATTR_ITEM(attr_name) \
+do { \
+	ret = sysfs_create_file(kobj_emi, &attr_name##_attr.attr); \
+	if (ret != 0) { \
+		pr_debug("Failed to create " #attr_name " in sysfs\n"); \
+		return ret; \
+	} \
+} while (0)
+	KOBJ_ATTR_LIST;
+#undef	KOBJ_ATTR_ITEM
+
+	return ret;
+}
+
+static void met_emi_delete(void)
+{
+#define	KOBJ_ATTR_ITEM(attr_name) \
+	sysfs_remove_file(kobj_emi, &attr_name##_attr.attr)
+	if (kobj_emi != NULL) {
+		KOBJ_ATTR_LIST;
+		kobj_emi = NULL;
+	}
+#undef	KOBJ_ATTR_ITEM
+
+	if (emi_inited)
+		MET_BM_DeInit();
+}
+
+
+static void met_emi_resume(void)
+{
+	if (!do_emi())
+		return;
+
+	emi_init();
+}
+
+
+#if defined(CONFIG_MTK_TINYSYS_SSPM_SUPPORT) && defined(ONDIEMET_SUPPORT)
+static const char help[] = "  --emi                                 monitor EMI banwidth\n";
+
+#define TTYPE_NAME_STR_LEN  64
+/* static char ttype_name[21][TTYPE_NAME_STR_LEN]; */
+
+static int emi_print_help(char *buf, int len)
+{
+	return snprintf(buf, PAGE_SIZE, help);
+}
+
+static int emi_print_header(char *buf, int len)
+{
+	int ret = 0;
+/*	int ret_m[21]; */
+	int i = 0;
+
+#if 0
+	for (i = 0; i < 21; i++) {
+		int k;
+
+		if (ttype_busid_val[i] > 0xffff) {
+			int j;
+
+			for (j = 0; j < ARRAY_SIZE(ttype_master_list_item); j++) {
+				if (ttype_master_val[i] == ttype_master_list_item[j].key) {
+					ret_m[i] = snprintf(ttype_name[i], TTYPE_NAME_STR_LEN, "ttype%d_%s",
+							    i + 1, ttype_master_list_item[j].val);
+					break;
+				}
+			}
+			if (j == ARRAY_SIZE(ttype_master_list_item))
+				ret_m[i] = snprintf(ttype_name[i], TTYPE_NAME_STR_LEN, "ttype%d_%s",
+						    i + 1, "unknown");
+		} else {
+			ret_m[i] = snprintf(ttype_name[i], TTYPE_NAME_STR_LEN, "ttype%d_%x",
+					    i + 1, ttype_busid_val[i]);
+		}
+
+		for (k = 0; k < ARRAY_SIZE(ttype_nbeat_list_item); k++) {
+			if (ttype_nbeat_val[i] == ttype_nbeat_list_item[k].key)
+				ret_m[i] += snprintf(ttype_name[i] + ret_m[i], TTYPE_NAME_STR_LEN - ret_m[i], "_%d",
+						     ttype_nbeat_list_item[k].val);
+		}
+
+		for (k = 0; k < ARRAY_SIZE(ttype_nbyte_list_item); k++) {
+			if (ttype_nbyte_val[i] == ttype_nbyte_list_item[k].key)
+				ret_m[i] += snprintf(ttype_name[i] + ret_m[i], TTYPE_NAME_STR_LEN - ret_m[i], "x%d",
+						     ttype_nbyte_list_item[k].val);
+		}
+
+		for (k = 0; k < ARRAY_SIZE(ttype_burst_list_item); k++) {
+			if (ttype_burst_val[i] == ttype_burst_list_item[k].key)
+				ret_m[i] += snprintf(ttype_name[i] + ret_m[i], TTYPE_NAME_STR_LEN - ret_m[i], "_%s",
+						     ttype_burst_list_item[k].val);
+		}
+
+		for (k = 0; k < ARRAY_SIZE(ttype_rw_list_item); k++) {
+			if (ttype_rw_val[i] == ttype_rw_list_item[k].key)
+				ret_m[i] += snprintf(ttype_name[i] + ret_m[i], TTYPE_NAME_STR_LEN - ret_m[i], "_%s",
+						     ttype_rw_list_item[k].val);
+		}
+	}
+#endif
+
+	if ((ttype1_16_en != BM_TTYPE1_16_ENABLE) && (emi_TP_busfiltr_enable != 1)) {
+		if (msel_enable) {
+			ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"met-info [000] 0.0: met_emi_msel: %x,%x,%x\n",
+				msel_group1 & _ALL,
+				msel_group2 & _ALL,
+				msel_group3 & _ALL);
+		} else {
+			ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"met-info [000] 0.0: met_emi_msel: %x,%x,%x\n",
+				_ALL & _ALL,
+				_ALL & _ALL,
+				_ALL & _ALL);
+		}
+	} else {
+		ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"met-info [000] 0.0: met_emi_ttype_master: %x,%x,%x,%x\n",
+				ttype_master_val[0], ttype_master_val[1], ttype_master_val[2], ttype_master_val[3]);
+
+		if (emi_TP_busfiltr_enable == 1) {
+
+			ret += snprintf(buf + ret, PAGE_SIZE - ret,
+					"met-info [000] 0.0: met_emi_ttype_busid: %x,%x,%x,%x\n",
+					ttype_busid_val[0], ttype_busid_val[1], ttype_busid_val[2], ttype_busid_val[3]);
+		}
+	}
+
+	ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_emi_rw_cfg: ");
+	ret += snprintf(buf + ret, PAGE_SIZE - ret, "BOTH");
+
+	for (i = 0; i < 21; i++) {
+		if (ttype_rw_val[i] == BM_TRANS_RW_DEFAULT)
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, ",DEFAULT");
+		else if (ttype_rw_val[i] == BM_TRANS_RW_READONLY)
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, ",R");
+		else if (ttype_rw_val[i] == BM_TRANS_RW_WRITEONLY)
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, ",W");
+		else
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, ",BOTH");
+	}
+	ret += snprintf(buf + ret, PAGE_SIZE - ret, "\n");
+
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"met-info [000] 0.0: met_emi_ultra_filter: %x\n", high_priority_filter);
+#if 0
+	if ((ttype1_16_en == BM_TTYPE1_16_ENABLE) && (ttype17_21_en == BM_TTYPE17_21_ENABLE)) {
+		int i;
+
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: ms_ud_sys_header: ms_ttype,");
+		for (i = 0; i < 21; i++)
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, "%s,", ttype_name[i]);
+
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x\n");
+
+	} else if (ttype17_21_en == BM_TTYPE17_21_ENABLE) {
+		int i;
+
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: ms_ud_sys_header: ms_ttype,");
+
+		for (i = 16; i < 21; i++)
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, "%s,", ttype_name[i]);
+
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "x,x,x,x,x\n");
+	}
+#else
+	/* ttype header */
+	if (ttype17_21_en == BM_TTYPE17_21_ENABLE) {
+		int i = 0;
+		int j = 0;
+
+		/* ttype master list */
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_emi_ttype_master_list: ");
+		for (i = 0; i < 21; i++) {
+			for (j = 0; j < ARRAY_SIZE(ttype_master_list_item); j++) {
+				if (ttype_master_val[i] == ttype_master_list_item[j].key) {
+					ret += snprintf(buf + ret, PAGE_SIZE - ret, "%s,", ttype_master_list_item[j].val);
+				}
+			}
+		}
+		/* remove the last comma */
+		snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+		/* ttype busid list */
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_emi_ttype_busid_list: ");
+		for (i = 0; i < 21; i++)
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, "%x,", ttype_busid_val[i]);
+
+		snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+		/* ttype nbeat list */
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_emi_ttype_nbeat_list: ");
+		for (i = 0; i < 21; i++) {
+			for (j = 0; j < ARRAY_SIZE(ttype_nbeat_list_item); j++) {
+				if (ttype_nbeat_val[i] == ttype_nbeat_list_item[j].key) {
+					ret += snprintf(buf + ret, PAGE_SIZE - ret, "%d,", ttype_nbeat_list_item[j].val);
+				}
+			}
+		}
+		snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+		/* ttype nbyte list */
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_emi_ttype_nbyte_list: ");
+		for (i = 0; i < 21; i++) {
+			for (j = 0; j < ARRAY_SIZE(ttype_nbyte_list_item); j++) {
+				if (ttype_nbyte_val[i] == ttype_nbyte_list_item[j].key) {
+					ret += snprintf(buf + ret, PAGE_SIZE - ret, "%d,", ttype_nbyte_list_item[j].val);
+				}
+			}
+		}
+		snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+		/* ttype burst list */
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_emi_ttype_burst_list: ");
+		for (i = 0; i < 21; i++) {
+			for (j = 0; j < ARRAY_SIZE(ttype_burst_list_item); j++) {
+				if (ttype_burst_val[i] == ttype_burst_list_item[j].key) {
+					ret += snprintf(buf + ret, PAGE_SIZE - ret, "%s,", ttype_burst_list_item[j].val);
+				}
+			}
+		}
+		snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+		/* ttype enable */
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_emi_ttype_enable: %d,%d\n",ttype1_16_en, ttype17_21_en);
+	}
+#endif
+
+	return ret;
+}
+
+static int ondiemet_emi_print_header(char *buf, int len)
+{
+	return emi_print_header(buf, len);
+}
+
+static void MET_BM_IPI_REGISTER_CB(void)
+{
+	int ret, i;
+	unsigned int rdata;
+	unsigned int ipi_buf[4];
+
+	for (i = 0; i < 4; i++)
+		ipi_buf[i] = 0;
+
+	if (sspm_buf_available == 1) {
+		ipi_buf[0] = MET_MAIN_ID | (MID_EMI << MID_BIT_SHIFT) | MET_ARGU | SET_REGISTER_CB;
+		ret = sspm_ipi_send_sync(IPI_ID_MET, IPI_OPT_WAIT, (void *)ipi_buf, 0, &rdata, 1);
+	}
+}
+
+static void MET_BM_IPI_configs(void)
+{
+	int ret, i;
+	unsigned int rdata;
+	unsigned int ipi_buf[4];
+
+	for (i = 0; i < 4; i++)
+		ipi_buf[i] = 0;
+
+	if (sspm_buf_available == 1) {
+		ipi_buf[0] = MET_MAIN_ID | (MID_EMI << MID_BIT_SHIFT) | MET_ARGU | SET_EBM_CONFIGS1;
+		ret = sspm_ipi_send_sync(IPI_ID_MET, IPI_OPT_WAIT, (void *)ipi_buf, 0, &rdata, 1);
+	}
+}
+
+static void ondiemet_emi_start(void)
+{
+	MET_BM_IPI_REGISTER_CB();
+	if (!emi_inited) {
+		if (MET_BM_Init() != 0) {
+			met_sspm_emi.mode = 0;
+			pr_notice("MET_BM_Init failed!!!\n");
+			PR_BOOTMSG("MET_BM_Init failed!!!\n");
+			return;
+		}
+		emi_inited = 1;
+	}
+	MET_BM_IPI_configs();
+
+	if (do_emi())
+		emi_init();
+
+	ondiemet_module[ONDIEMET_SSPM] |= ID_EMI;
+}
+
+static void emi_uninit(void)
+{
+	MET_BM_RestoreCfg();
+}
+
+static void ondiemet_emi_stop(void)
+{
+	if (!emi_inited)
+		return;
+
+	if (do_emi())
+		emi_uninit();
+}
+#endif
+
+struct metdevice met_sspm_emi = {
+	.name			= "emi",
+	.owner			= THIS_MODULE,
+	.type			= MET_TYPE_BUS,
+	.create_subfs		= met_emi_create,
+	.delete_subfs		= met_emi_delete,
+	.resume			= met_emi_resume,
+#if defined(CONFIG_MTK_TINYSYS_SSPM_SUPPORT) && defined(ONDIEMET_SUPPORT)
+	.ondiemet_start		= ondiemet_emi_start,
+	.ondiemet_stop		= ondiemet_emi_stop,
+	.ondiemet_print_help	= emi_print_help,
+	.ondiemet_print_header	= ondiemet_emi_print_header,
+#endif
+	.ondiemet_mode		= 1,
+};
+EXPORT_SYMBOL(met_sspm_emi);
diff --git a/src/devtools/met-driver/4.9/common/met_kernel_symbol.h b/src/devtools/met-driver/4.9/common/met_kernel_symbol.h
new file mode 100644
index 0000000..27ff39e
--- /dev/null
+++ b/src/devtools/met-driver/4.9/common/met_kernel_symbol.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef MET_KERNEL_SYMBOL
+#define MET_KERNEL_SYMBOL
+
+/*lookup symbol*/
+#include <asm/cpu.h>
+#include <linux/kallsyms.h>
+#include <linux/perf_event.h>
+#include <linux/kthread.h>
+
+#if	defined(CONFIG_MET_ARM_32BIT)
+extern void met_get_cpuinfo(int cpu, struct cpuinfo_arm **cpuinfo);
+extern void (*met_get_cpuinfo_symbol)(int cpu, struct cpuinfo_arm **cpuinfo);
+#else
+extern void met_get_cpuinfo(int cpu, struct cpuinfo_arm64 **cpuinfo);
+extern void (*met_get_cpuinfo_symbol)(int cpu, struct cpuinfo_arm64 **cpuinfo);
+#endif
+
+extern void (*tracing_record_cmdline_symbol)(struct task_struct *tsk);
+extern void met_cpu_frequency(unsigned int frequency, unsigned int cpu_id);
+extern void (*met_cpu_frequency_symbol)(unsigned int frequency, unsigned int cpu_id);
+extern void (*met_arch_setup_dma_ops_symbol)(struct device *dev);
+extern u64 (*met_perf_event_read_local_symbol)(struct perf_event *ev);
+extern struct task_struct *(*met_kthread_create_on_cpu_symbol)(int (*threadfn)(void *data),
+				void *data, unsigned int cpu,
+				const char *namefmt);
+extern int (*met_smp_call_function_single_symbol)(int cpu, smp_call_func_t func, void *info, int wait);
+
+extern void met_tracing_record_cmdline(struct task_struct *tsk);
+extern int met_reg_switch(void);
+extern int (*met_reg_switch_symbol)(void);
+extern void met_unreg_switch(void);
+extern void (*met_unreg_switch_symbol)(void);
+extern void met_arch_setup_dma_ops(struct device *dev);
+extern u64 met_perf_event_read_local(struct perf_event *ev);
+extern struct task_struct *met_kthread_create_on_cpu(int (*threadfn)(void *data),
+				void *data, unsigned int cpu,
+				const char *namefmt);
+extern int met_smp_call_function_single(int cpu, smp_call_func_t func, void *info, int wait);
+extern void met_arch_send_call_function_single_ipi(int cpu);
+#endif	/* MET_KERNEL_SYMBOL */
diff --git a/src/devtools/met-driver/4.9/common/met_main.c b/src/devtools/met-driver/4.9/common/met_main.c
new file mode 100644
index 0000000..8158ddf
--- /dev/null
+++ b/src/devtools/met-driver/4.9/common/met_main.c
@@ -0,0 +1,315 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/hrtimer.h>
+#include <linux/cpu.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/profile.h>
+#include <linux/dcache.h>
+#include <linux/types.h>
+#include <linux/dcookies.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+
+#include <asm/irq_regs.h>
+
+#include "met_struct.h"
+#include "met_drv.h"
+#include "met_kernel_symbol.h"
+#include "interface.h"
+#include <linux/of.h>
+
+
+extern struct device_node *of_root;
+static const char *platform_name;
+
+struct cpu_type_name {
+	char full_name[32];
+	char abbr_name[8];
+};
+
+static struct cpu_type_name met_known_cpu_type[] = {
+	{"arm,cortex-a53", "CA53"},
+	{"arm,cortex-a55", "CA55"},
+	{"arm,cortex-a73", "CA73"},
+	{"arm,cortex-a75", "CA75"},
+};
+#define MET_KNOWN_CPU_TYPE_COUNT \
+	(sizeof(met_known_cpu_type)/sizeof(struct cpu_type_name))
+
+static char met_cpu_topology[64];
+
+#if	defined(CONFIG_MET_ARM_32BIT)
+void (*met_get_cpuinfo_symbol)(int cpu, struct cpuinfo_arm **cpuinfo);
+#else
+void (*met_get_cpuinfo_symbol)(int cpu, struct cpuinfo_arm64 **cpuinfo);
+#endif
+
+void (*tracing_record_cmdline_symbol)(struct task_struct *tsk);
+void (*met_cpu_frequency_symbol)(unsigned int frequency, unsigned int cpu_id);
+int (*met_reg_switch_symbol)(void);
+void (*met_unreg_switch_symbol)(void);
+
+void (*met_arch_setup_dma_ops_symbol)(struct device *dev);
+u64 (*met_perf_event_read_local_symbol)(struct perf_event *ev);
+struct task_struct *(*met_kthread_create_on_cpu_symbol)(int (*threadfn)(void *data),
+				void *data, unsigned int cpu,
+				const char *namefmt);
+int (*met_smp_call_function_single_symbol)(int cpu, smp_call_func_t func, void *info, int wait);
+
+
+const char *met_get_platform_name(void)
+{
+	return platform_name;
+}
+EXPORT_SYMBOL(met_get_platform_name);
+
+static void get_cpu_type_name(const char *compatible, char *cpu_type)
+{
+	int i;
+
+	for (i = 0; i < MET_KNOWN_CPU_TYPE_COUNT; i++) {
+		if (!strncmp(compatible, met_known_cpu_type[i].full_name,
+					strlen(met_known_cpu_type[i].full_name)))
+			strncpy(cpu_type, met_known_cpu_type[i].abbr_name,
+					strlen(met_known_cpu_type[i].abbr_name) + 1);
+	}
+}
+
+static void met_set_cpu_topology(int core_id, int cluster_core_num)
+{
+	int i, buf_len = strlen(met_cpu_topology);
+	struct device_node *node = NULL;
+	const char *prev_cptb = NULL;
+	const char *cptb;
+	char cpu_type[16];
+
+	for (i = 0; i < cluster_core_num; i++) {
+		node = of_get_cpu_node(core_id + i, NULL);
+		if (node) {
+			cptb = of_get_property(node, "compatible", NULL);
+			if (cptb) {
+				get_cpu_type_name(cptb, cpu_type);
+				if (prev_cptb == NULL)
+					/* first write:  write core_type & core_number */
+					buf_len += snprintf(met_cpu_topology + buf_len,
+								sizeof(met_cpu_topology) - buf_len,
+								"%s:%d", cpu_type, core_id + i);
+				else if (!strncmp(prev_cptb, cptb, strlen(prev_cptb)))
+					/* cpu type is the same with before */
+					/* write core_number */
+					buf_len += snprintf(met_cpu_topology + buf_len,
+								sizeof(met_cpu_topology) - buf_len,
+								",%d", core_id + i);
+				else
+					/* cpu type is different with before */
+					/* write core_type & core_number */
+					buf_len += snprintf(met_cpu_topology + buf_len,
+								sizeof(met_cpu_topology) - buf_len,
+								"|%s:%d", cpu_type, core_id + i);
+
+				prev_cptb = cptb;
+			}
+		}
+	}
+}
+
+static int met_create_cpu_topology(void)
+{
+	int i, len;
+	struct device_node *node = NULL;
+	int start_core_id = 0;
+	int cluster_num = 0, cluster_core_num = 0;
+	char cluster_name[16];
+
+	node = of_find_node_by_name(NULL, "cpu-map");
+	if (!node)
+		node = of_find_node_by_name(NULL, "virtual-cpu-map");
+
+	if (node) {
+		cluster_num = of_get_child_count(node);
+
+		for (i = 0; i < cluster_num; i++) {
+			snprintf(cluster_name, sizeof(cluster_name), "cluster%d", i);
+			node = of_find_node_by_name(NULL, cluster_name);
+			if (node) {
+				cluster_core_num = of_get_child_count(node);
+
+				/* "|" use to separate different cluster */
+				if (i > 0) {
+					len = strlen(met_cpu_topology);
+					snprintf(met_cpu_topology + len, sizeof(met_cpu_topology) - len, "|");
+				}
+
+				met_set_cpu_topology(start_core_id, cluster_core_num);
+				start_core_id = cluster_core_num;
+			}
+		}
+	}
+
+	return strlen(met_cpu_topology);
+}
+
+static int met_kernel_symbol_get(void)
+{
+	int ret = 0;
+
+	if (met_get_cpuinfo_symbol == NULL)
+		met_get_cpuinfo_symbol = (void *)symbol_get(met_get_cpuinfo);
+	if (met_get_cpuinfo_symbol == NULL)
+		return -2;
+
+	if (tracing_record_cmdline_symbol == NULL)
+		tracing_record_cmdline_symbol = (void *)symbol_get(met_tracing_record_cmdline);
+	if (tracing_record_cmdline_symbol == NULL)
+		ret = -3;
+
+	if (met_cpu_frequency_symbol == NULL)
+		met_cpu_frequency_symbol = (void *)symbol_get(met_cpu_frequency);
+	if (met_cpu_frequency_symbol == NULL)
+		ret = -4;
+
+	if (met_reg_switch_symbol == NULL)
+		met_reg_switch_symbol = (void *)symbol_get(met_reg_switch);
+	if (met_reg_switch_symbol == NULL)
+		ret = -5;
+
+	if (met_unreg_switch_symbol == NULL)
+		met_unreg_switch_symbol = (void *)symbol_get(met_unreg_switch);
+	if (met_unreg_switch_symbol == NULL)
+		ret = -6;
+
+	if (met_arch_setup_dma_ops_symbol == NULL)
+		met_arch_setup_dma_ops_symbol = (void *)symbol_get(met_arch_setup_dma_ops);
+	if (met_arch_setup_dma_ops_symbol == NULL)
+		ret = -7;
+
+	if (met_perf_event_read_local_symbol == NULL)
+		met_perf_event_read_local_symbol = (void *)symbol_get(met_perf_event_read_local);
+	if (met_perf_event_read_local_symbol == NULL)
+		ret = -8;
+
+	if (met_kthread_create_on_cpu_symbol == NULL)
+		met_kthread_create_on_cpu_symbol = (void *)symbol_get(met_kthread_create_on_cpu);
+	if (met_kthread_create_on_cpu_symbol == NULL)
+		ret = -9;
+
+	if (met_smp_call_function_single_symbol == NULL)
+		met_smp_call_function_single_symbol = (void *)symbol_get(met_smp_call_function_single);
+	if (met_smp_call_function_single_symbol == NULL)
+		ret = -10;
+	return ret;
+}
+
+DEFINE_PER_CPU(struct met_cpu_struct, met_cpu);
+
+static int __init met_drv_init(void)
+{
+	int cpu;
+	int ret;
+	int cpu_topology_len;
+	struct met_cpu_struct *met_cpu_ptr;
+
+	for_each_possible_cpu(cpu) {
+		met_cpu_ptr = &per_cpu(met_cpu, cpu);
+		/* snprintf(&(met_cpu_ptr->name[0]), sizeof(met_cpu_ptr->name), "met%02d", cpu); */
+		met_cpu_ptr->cpu = cpu;
+	}
+
+	ret = met_kernel_symbol_get();
+	if (ret) {
+		pr_notice("[MET] met_kernel_symbol_get fail, ret = %d\n", ret);
+		return ret;
+	}
+	fs_reg();
+
+	if (of_root){
+		/*
+			mt6765.dts
+			model = "MT6765";
+			compatible = "mediatek,MT6765";
+			interrupt-parent = <&sysirq>;
+		*/
+		if (of_root->properties) {
+			of_property_read_string(of_root, of_root->properties->name, &platform_name);
+			PR_BOOTMSG("platform_name=%s\n", platform_name);
+		}
+	}
+	if (platform_name) {
+		char buf[7];
+		int len = strlen(platform_name);
+		int i;
+		int found = 0;
+
+		if (len >= 7) {
+			for (i=0; i<len-6; i++) {
+				if ((platform_name[i] == 'm' && platform_name[i+1] == 't')
+					|| (platform_name[i] == 'M' && platform_name[i+1] == 'T')
+					|| (platform_name[i] == 'M' && platform_name[i+1] == 't')
+					|| (platform_name[i] == 'm' && platform_name[i+1] == 'T')) {
+					found = 1;
+					break;
+				}
+			}
+		}
+
+		if (found == 1) {
+			memset(buf, 0x0, 7);
+			buf[0] = 'm';
+			buf[1] = 't';
+			strncpy(&buf[2], &platform_name[i+2], 4);
+			met_set_platform(buf, 1);
+			PR_BOOTMSG("set_platform=%s\n", buf);
+		} else {
+			met_set_platform("mtxxxx", 1);
+		}
+	}
+
+	cpu_topology_len = met_create_cpu_topology();
+	if (cpu_topology_len)
+		met_set_topology(met_cpu_topology, 1);
+
+#ifdef MET_PLF_USE
+	core_plf_init();
+#endif
+	return 0;
+}
+
+static void __exit met_drv_exit(void)
+{
+	if (met_cpu_frequency_symbol)
+		symbol_put(met_cpu_frequency);
+	if (met_reg_switch_symbol)
+		symbol_put(met_reg_switch);
+	if (met_unreg_switch_symbol)
+		symbol_put(met_unreg_switch);
+	if (tracing_record_cmdline_symbol)
+		symbol_put(met_tracing_record_cmdline);
+	if (met_get_cpuinfo_symbol)
+		symbol_put(met_get_cpuinfo);
+
+#ifdef MET_PLF_USE
+	core_plf_exit();
+#endif
+	fs_unreg();
+
+}
+module_init(met_drv_init);
+module_exit(met_drv_exit);
+
+MODULE_AUTHOR("DT_DM5");
+MODULE_DESCRIPTION("MET_CORE");
+MODULE_LICENSE("GPL");
diff --git a/src/devtools/met-driver/4.9/common/met_ptpod.c b/src/devtools/met-driver/4.9/common/met_ptpod.c
new file mode 100644
index 0000000..fce982b
--- /dev/null
+++ b/src/devtools/met-driver/4.9/common/met_ptpod.c
@@ -0,0 +1,377 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/random.h>
+#include <linux/fs.h>
+
+#include "met_drv.h"
+#include "trace.h"
+#include "core_plf_init.h"
+#include "core_plf_trace.h"
+
+static unsigned int MT_GPU_DVFS_IDX = NR_MT_CPU_DVFS;
+static unsigned int g_u4GPUVolt;
+static unsigned int g_u4Volt[NR_MT_CPU_DVFS + 1];
+
+/* g_ap_ptpod: cpu volt from ap or sspm setting
+ * if 1: cpu volt from ap
+ * if 0: cpu volt from sspm */
+static unsigned int g_ap_ptpod;
+
+/* gpu_volt_enable:
+ * if 1: enable gpu volt to output
+ * if 0: disable gpu volt to output */
+static unsigned int gpu_volt_enable = 1;
+
+/* get_volt_by_wq:
+ * if 1: get cpu/gpu volt by workqueue
+ * if 0: get cpu/gpu volt in irq */
+static unsigned int get_volt_by_wq;
+
+static int ptpod_started;
+static struct kobject *kobj_ptpod;
+static struct delayed_work get_volt_dwork;
+
+noinline void ms_ptpod(void)
+{
+	char *SOB, *EOB;
+
+	if (g_ap_ptpod) {
+		MET_TRACE_GETBUF(&SOB, &EOB);
+
+		if (gpu_volt_enable) {
+			g_u4Volt[MT_GPU_DVFS_IDX] = g_u4GPUVolt;
+			EOB = ms_formatD_EOL(EOB, ARRAY_SIZE(g_u4Volt), g_u4Volt);
+		} else
+			EOB = ms_formatD_EOL(EOB, ARRAY_SIZE(g_u4Volt) - 1, g_u4Volt);
+
+		MET_TRACE_PUTBUF(SOB, EOB);
+	} else {
+		if (gpu_volt_enable) {
+			MET_TRACE_GETBUF(&SOB, &EOB);
+			EOB = ms_formatD_EOL(EOB, 1, &g_u4GPUVolt);
+			MET_TRACE_PUTBUF(SOB, EOB);
+		}
+	}
+}
+
+#if 0
+static void ptpod_cpu_voltSampler(enum mt_cpu_dvfs_id id, unsigned int volt)
+{
+	switch (id) {
+	case MT_CPU_DVFS_LL:
+		g_u4CPUVolt_LL = volt;
+		break;
+	case MT_CPU_DVFS_L:
+		g_u4CPUVolt_L = volt;
+		break;
+	case MT_CPU_DVFS_CCI:
+		g_u4CPUVolt_CCI = volt;
+		break;
+	default:
+		return;
+	}
+	ptpod();
+}
+#endif
+
+#if 0
+static void ptpod_gpu_voltSampler(unsigned int a_u4Volt)
+{
+	g_u4GPUVolt = (a_u4Volt+50)/100;
+
+	if (ptpod_started)
+		ptpod();
+}
+#endif
+
+#define PTPOD_CONF_SHOW_IMPLEMENT(var) \
+	do { \
+		int i; \
+		i = snprintf(buf, PAGE_SIZE, "%d\n", var); \
+		return i; \
+	} while (0)
+
+#define PTPOD_CONF_STORE_IMPLEMENT(var) \
+	do { \
+		int value; \
+		if ((n == 0) || (buf == NULL)) \
+			return -EINVAL; \
+		if (kstrtoint(buf, 0, &value) != 0) \
+			return -EINVAL; \
+		if (value == 1) \
+			var = 1; \
+		else \
+			var = 0; \
+		return n; \
+	} while (0)
+
+
+static ssize_t ap_ptpod_enable_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	PTPOD_CONF_SHOW_IMPLEMENT(g_ap_ptpod);
+}
+
+static ssize_t ap_ptpod_enable_store(struct kobject *kobj,
+			struct kobj_attribute *attr,
+			const char *buf,
+			size_t n)
+{
+	PTPOD_CONF_STORE_IMPLEMENT(g_ap_ptpod);
+}
+
+static ssize_t gpu_volt_enable_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	PTPOD_CONF_SHOW_IMPLEMENT(gpu_volt_enable);
+}
+
+static ssize_t gpu_volt_enable_store(struct kobject *kobj,
+			struct kobj_attribute *attr,
+			const char *buf,
+			size_t n)
+{
+	PTPOD_CONF_STORE_IMPLEMENT(gpu_volt_enable);
+}
+
+static ssize_t get_volt_by_wq_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	PTPOD_CONF_SHOW_IMPLEMENT(get_volt_by_wq);
+}
+
+static ssize_t get_volt_by_wq_store(struct kobject *kobj,
+			struct kobj_attribute *attr,
+			const char *buf,
+			size_t n)
+{
+	PTPOD_CONF_STORE_IMPLEMENT(get_volt_by_wq);
+}
+
+static struct kobj_attribute ap_ptpod_enable_attr = __ATTR(ap_ptpod_enable, 0664, ap_ptpod_enable_show, ap_ptpod_enable_store);
+static struct kobj_attribute gpu_volt_enable_attr = __ATTR(gpu_volt_enable, 0664, gpu_volt_enable_show, gpu_volt_enable_store);
+static struct kobj_attribute get_volt_by_wq_attr = __ATTR(get_volt_by_wq, 0664, get_volt_by_wq_show, get_volt_by_wq_store);
+
+/* create ptpod related kobj node */
+#define KOBJ_ATTR_LIST \
+	do { \
+		KOBJ_ATTR_ITEM(ap_ptpod_enable); \
+		KOBJ_ATTR_ITEM(gpu_volt_enable); \
+		KOBJ_ATTR_ITEM(get_volt_by_wq); \
+	} while (0)
+
+static int ptpod_create(struct kobject *parent)
+{
+	int ret = 0;
+
+	kobj_ptpod = parent;
+
+#define KOBJ_ATTR_ITEM(attr_name) \
+	do { \
+		ret = sysfs_create_file(kobj_ptpod, &attr_name ## _attr.attr); \
+		if (ret != 0) { \
+			pr_notice("Failed to create " #attr_name " in sysfs\n"); \
+			return ret; \
+		} \
+	} while (0)
+	KOBJ_ATTR_LIST;
+#undef  KOBJ_ATTR_ITEM
+
+	return 0;
+}
+
+static void ptpod_delete(void)
+{
+#define KOBJ_ATTR_ITEM(attr_name) \
+	sysfs_remove_file(kobj_ptpod, &attr_name ## _attr.attr)
+
+	if (kobj_ptpod != NULL) {
+		KOBJ_ATTR_LIST;
+		kobj_ptpod = NULL;
+	}
+#undef  KOBJ_ATTR_ITEM
+}
+
+static void update_volt_value(void)
+{
+	int i;
+
+	if (g_ap_ptpod && mt_cpufreq_get_cur_volt_symbol) {
+		/*
+		g_u4CPUVolt_LL = mt_cpufreq_get_cur_volt_symbol(MT_CPU_DVFS_LL)/100;
+		g_u4CPUVolt_L = mt_cpufreq_get_cur_volt_symbol(MT_CPU_DVFS_L)/100;
+		g_u4CPUVolt_CCI = mt_cpufreq_get_cur_volt_symbol(MT_CPU_DVFS_CCI)/100;
+		*/
+		for (i = 0; i < NR_MT_CPU_DVFS; i++)
+			g_u4Volt[i] = mt_cpufreq_get_cur_volt_symbol(i) / 100;
+	}
+
+	if (gpu_volt_enable) {
+		if (mt_gpufreq_get_cur_volt_symbol)
+			g_u4GPUVolt = ((mt_gpufreq_get_cur_volt_symbol() + 50) / 100);
+	}
+}
+
+static void get_volt_notify(unsigned long long stamp, int cpu)
+{
+	schedule_delayed_work(&get_volt_dwork, 0);
+}
+
+static void met_ptpod_polling_by_wq(struct work_struct *work)
+{
+	update_volt_value();
+
+	ms_ptpod();
+}
+
+static void met_ptpod_polling(unsigned long long stamp, int cpu)
+{
+	update_volt_value();
+
+	ms_ptpod();
+}
+
+/*
+ * Called from "met-cmd --start"
+ */
+static void ptpod_start(void)
+{
+#if 0
+	met_gpufreq_setvolt_registerCB(ptpod_gpu_voltSampler, kFOR_MET_PTPOD_USE);
+#endif
+
+	if (get_volt_by_wq) {
+		met_ptpod.timed_polling = get_volt_notify;
+
+		INIT_DELAYED_WORK(&get_volt_dwork, met_ptpod_polling_by_wq);
+	} else
+		met_ptpod.timed_polling = met_ptpod_polling;
+
+	update_volt_value();
+
+	ms_ptpod();
+
+#if 0
+	/* register callback */
+	if (mt_cpufreq_setvolt_registerCB_symbol)
+		mt_cpufreq_setvolt_registerCB_symbol(ptpod_cpu_voltSampler);
+#endif
+
+	ptpod_started = 1;
+}
+
+/*
+ * Called from "met-cmd --stop"
+ */
+static void ptpod_stop(void)
+{
+	ptpod_started = 0;
+
+#if 0
+	/* unregister callback */
+	if (mt_cpufreq_setvolt_registerCB_symbol)
+		mt_cpufreq_setvolt_registerCB_symbol(NULL);
+#endif
+
+	if (get_volt_by_wq)
+		cancel_delayed_work_sync(&get_volt_dwork);
+
+	update_volt_value();
+
+	ms_ptpod();
+
+#if 0
+	met_gpufreq_setvolt_registerCB(NULL, kFOR_MET_PTPOD_USE);
+#endif
+}
+
+static char help[] =
+	"  --ptpod                               Measure CPU/GPU voltage\n";
+static int ptpod_print_help(char *buf, int len)
+{
+	return snprintf(buf, PAGE_SIZE, help);
+}
+
+/*
+ * It will be called back when run "met-cmd --extract" and mode is 1
+ */
+static int ptpod_print_header(char *buf, int len)
+{
+	int str_len = 0;
+
+	if (g_ap_ptpod) {
+		int i;
+
+		str_len += snprintf(buf + str_len, PAGE_SIZE - str_len,
+						"met-info [000] 0.0: met_ptpod_header: ");
+
+		for (i = 0; i < NR_MT_CPU_DVFS; i++)
+		{
+#ifdef MT_CPU_DVFS_CCI
+			if( i == MT_CPU_DVFS_CCI )
+			{
+				str_len += snprintf(buf + str_len, PAGE_SIZE - str_len, "CPUVolt_CCI,", i);
+			}
+			else
+			{
+				str_len += snprintf(buf + str_len, PAGE_SIZE - str_len, "CPUVolt_%d,", i);
+			}
+#else
+				str_len += snprintf(buf + str_len, PAGE_SIZE - str_len, "CPUVolt_%d,", i);
+#endif
+		}
+
+		if (gpu_volt_enable)
+			str_len += snprintf(buf + str_len, PAGE_SIZE - str_len, "GPUVolt,");
+
+		buf[str_len-1] = '\n';
+
+		str_len += snprintf(buf + str_len, PAGE_SIZE - str_len,
+							"met-info [000] 0.0: met_ptpod_version: ap\n");
+	} else {
+		if (gpu_volt_enable) {
+			str_len += snprintf(buf + str_len, PAGE_SIZE - str_len,
+							"met-info [000] 0.0: met_ptpod_header: ");
+
+			str_len += snprintf(buf + str_len, PAGE_SIZE - str_len, "GPUVolt\n");
+		}
+
+		str_len += snprintf(buf + str_len, PAGE_SIZE - str_len,
+							"met-info [000] 0.0: met_ptpod_version: sspm\n");
+	}
+
+	if (gpu_volt_enable)
+		str_len += snprintf(buf + str_len, PAGE_SIZE - str_len,
+						"met-info [000] 0.0: met_ptpod_gpu_volt_enable: YES\n");
+	else
+		str_len += snprintf(buf + str_len, PAGE_SIZE - str_len,
+						"met-info [000] 0.0: met_ptpod_gpu_volt_enable: NO\n");
+
+	return str_len;
+}
+
+struct metdevice met_ptpod = {
+	.name = "ptpod",
+	.owner = THIS_MODULE,
+	.type = MET_TYPE_PMU,
+	.cpu_related = 0,
+	.create_subfs = ptpod_create,
+	.delete_subfs = ptpod_delete,
+	.start = ptpod_start,
+	.stop = ptpod_stop,
+	.timed_polling = met_ptpod_polling,
+	.print_help = ptpod_print_help,
+	.print_header = ptpod_print_header,
+};
+EXPORT_SYMBOL(met_ptpod);
diff --git a/src/devtools/met-driver/4.9/common/met_struct.h b/src/devtools/met-driver/4.9/common/met_struct.h
new file mode 100644
index 0000000..a74ff78
--- /dev/null
+++ b/src/devtools/met-driver/4.9/common/met_struct.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MET_STRUCT_H_
+#define _MET_STRUCT_H_
+
+#include <linux/hrtimer.h>
+
+struct met_cpu_struct {
+	struct hrtimer hrtimer;
+	struct delayed_work dwork;
+/* struct kmem_cache *cachep; */
+/* struct list_head sample_head; */
+/* spinlock_t list_lock; */
+/* struct mutex list_sync_lock; */
+	int work_enabled;
+	int cpu;
+	int hrtimer_online_check;
+/* char name[16]; */
+};
+
+DECLARE_PER_CPU(struct met_cpu_struct, met_cpu);
+
+#endif				/* _MET_STRUCT_H_ */
diff --git a/src/devtools/met-driver/4.9/common/met_tag.h b/src/devtools/met-driver/4.9/common/met_tag.h
new file mode 100644
index 0000000..04b5e09
--- /dev/null
+++ b/src/devtools/met-driver/4.9/common/met_tag.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __MET_TAG_EX_H__
+#define __MET_TAG_EX_H__
+
+#ifdef BUILD_WITH_MET
+void force_sample(void *unused);
+#else
+#include <linux/string.h>
+#endif
+
+/* Black List Table */
+struct bltable_t {
+	struct mutex mlock;
+	/* flag - Bit31: Global ON/OFF; Bit0~30: ON/OF slot map of class_id */
+	unsigned int flag;
+	int class_id[MAX_EVENT_CLASS];
+};
+
+extern void met_sched_switch(struct task_struct *prev, struct task_struct *next);
+
+extern int tracing_mark_write(int type, unsigned int class_id,
+		const char *name, unsigned int value,
+		unsigned int value2, unsigned int value3);
+
+#endif				/* __MET_TAG_EX_H__ */
diff --git a/src/devtools/met-driver/4.9/common/met_tag_ex.c b/src/devtools/met-driver/4.9/common/met_tag_ex.c
new file mode 100644
index 0000000..4413b4e
--- /dev/null
+++ b/src/devtools/met-driver/4.9/common/met_tag_ex.c
@@ -0,0 +1,644 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define BUILD_WITH_MET
+
+#ifdef MET_USER_EVENT_SUPPORT
+
+#include <linux/kernel.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/fs.h>
+/* #include <asm/uaccess.h> */
+#include <linux/uaccess.h>
+
+#include "met_drv.h"
+#include "met_tag.h"
+#include "interface.h"
+#include "switch.h"
+#include "met_api_tbl.h"
+
+struct bltable_t bltab;
+
+static int dump_buffer_size;
+static int dump_data_size;
+static int dump_overrun;
+static int dump_overrun_size;
+static int dump_seq_no;
+static void *dump_buffer;
+
+#define OPFLAG_OVERWRITE	0x1
+static unsigned int options_flag;
+
+#define DEVICE_NAME		"met_tag"
+
+/* #define ERRF_ENABLE */
+/* #define DEBF_ENABLE */
+
+#ifdef ERRF_ENABLE
+#define MSG_ERR			"Error:["DEVICE_NAME"]"
+#define ERRF(args...)	pr_debug(MSG_ERR args)
+#else
+#define ERRF(args...)
+#endif
+
+#ifdef DEBF_ENABLE
+#define MSG_IFO			"Info :["DEVICE_NAME"]"
+#define DEBF(args...)	pr_debug(MSG_IFO args)
+#else
+#define DEBF(args...)
+#endif
+
+static int is_enabled(unsigned int class_id)
+{
+	int i;
+
+	if (bltab.flag == 0)
+		return 1;
+	if (bltab.flag & MET_CLASS_ALL)
+		return 0;
+
+	for (i = 0; i < MAX_EVENT_CLASS; i++) {
+		if ((bltab.flag & (1 << i)) && (bltab.class_id[i] == class_id))
+			return 0;
+	}
+
+	return 1;
+}
+
+noinline int tracing_mark_write(int type, unsigned int class_id,
+				       const char *name, unsigned int value,
+				       unsigned int value2, unsigned int value3)
+{
+	if (type == TYPE_MET_SUSPEND) {
+		MET_TRACE("C|0|MET_SUSPEND|1");
+		return 0;
+	}
+	if (type == TYPE_MET_RESUME) {
+		MET_TRACE("C|0|MET_SUSPEND|0");
+		return 0;
+	}
+	if (!is_enabled(class_id))
+		return 0;
+	switch (type) {
+	case TYPE_START:
+		MET_TRACE("B|%d|%s\n", class_id, name);
+		break;
+	case TYPE_END:
+		MET_TRACE("E|%s\n", name);
+		break;
+	case TYPE_ONESHOT:
+		MET_TRACE("C|%d|%s|%d\n", class_id, name, value);
+		break;
+	case TYPE_ASYNC_START:
+		MET_TRACE("S|%d|%s|%d\n", class_id, name, value);
+		break;
+	case TYPE_ASYNC_END:
+		MET_TRACE("F|%d|%s|%d\n", class_id, name, value);
+		break;
+	case TYPE_DUMP:
+		MET_TRACE("D|%d|%s|%d|%d|%d\n", class_id, name, value, value2, value3);
+		break;
+	default:
+		return -1;
+	}
+	return 0;
+}
+
+int met_tag_init(void)
+{
+	memset(&bltab, 0, sizeof(struct bltable_t));
+	bltab.flag = MET_CLASS_ALL;
+	mutex_init(&bltab.mlock);
+	return 0;
+}
+
+int met_tag_uninit(void)
+{
+	met_set_dump_buffer_real(0);
+	return 0;
+}
+
+int met_tag_start_real(unsigned int class_id, const char *name)
+{
+	int ret;
+
+	ret = tracing_mark_write(TYPE_START, class_id, name, 0, 0, 0);
+#if 0
+	if ((met_switch.mode & MT_SWITCH_TAGPOLLING)) {
+		/* tag polling only enable when MT_SWITCH_TAGPOLLING is config */
+		force_sample(NULL);
+	}
+#endif
+	return ret;
+}
+EXPORT_SYMBOL(met_tag_start_real);
+
+int met_tag_end_real(unsigned int class_id, const char *name)
+{
+	int ret;
+#if 0
+	if ((met_switch.mode & MT_SWITCH_TAGPOLLING)) {
+		/* tag polling only enable when MT_SWITCH_TAGPOLLING is config */
+		force_sample(NULL);
+	}
+#endif
+	ret = tracing_mark_write(TYPE_END, class_id, name, 0, 0, 0);
+
+	return ret;
+}
+EXPORT_SYMBOL(met_tag_end_real);
+
+int met_tag_async_start_real(unsigned int class_id, const char *name, unsigned int cookie)
+{
+	int ret;
+
+	ret = tracing_mark_write(TYPE_ASYNC_START, class_id, name, cookie, 0, 0);
+#if 0
+	if ((met_switch.mode & MT_SWITCH_TAGPOLLING)) {
+		/* tag polling only enable when MT_SWITCH_TAGPOLLING is config */
+		force_sample(NULL);
+	}
+#endif
+	return ret;
+}
+
+int met_tag_async_end_real(unsigned int class_id, const char *name, unsigned int cookie)
+{
+	int ret;
+
+#if 0
+	if ((met_switch.mode & MT_SWITCH_TAGPOLLING)) {
+		/* tag polling only enable when MT_SWITCH_TAGPOLLING is config */
+		force_sample(NULL);
+	}
+#endif
+	ret = tracing_mark_write(TYPE_ASYNC_END, class_id, name, cookie, 0, 0);
+	return ret;
+}
+
+int met_tag_oneshot_real(unsigned int class_id, const char *name, unsigned int value)
+{
+	int ret;
+
+	ret = tracing_mark_write(TYPE_ONESHOT, class_id, name, value, 0, 0);
+#if 0
+	if ((met_switch.mode & MT_SWITCH_TAGPOLLING)) {
+		/* tag polling only enable when MT_SWITCH_TAGPOLLING is config */
+		force_sample(NULL);
+	}
+#endif
+	return ret;
+}
+EXPORT_SYMBOL(met_tag_oneshot_real);
+
+int met_tag_userdata_real(char *pData)
+{
+	MET_TRACE("%s\n", pData);
+	return 0;
+}
+
+int met_tag_dump_real(unsigned int class_id, const char *name, void *data, unsigned int length)
+{
+	int ret;
+
+	if ((dump_data_size + length + sizeof(int)) > dump_buffer_size) {
+		if (options_flag & OPFLAG_OVERWRITE) {
+			dump_overrun_size = dump_data_size;
+			dump_overrun++;
+			memcpy(dump_buffer, &dump_seq_no, sizeof(int));
+			memcpy(dump_buffer + sizeof(int), data, length);
+			ret = tracing_mark_write(TYPE_DUMP, class_id, name,
+						 dump_seq_no++, 0, length + sizeof(int));
+			dump_data_size = length + sizeof(int);
+		} else {
+			ret = tracing_mark_write(TYPE_DUMP, class_id, name, dump_seq_no++, 0, 0);
+		}
+	} else {
+		memcpy(dump_buffer + dump_data_size, &dump_seq_no, sizeof(int));
+		memcpy(dump_buffer + dump_data_size + sizeof(int), data, length);
+		ret = tracing_mark_write(TYPE_DUMP, class_id, name,
+					 dump_seq_no++, dump_data_size, length + sizeof(int));
+		dump_data_size += length + sizeof(int);
+	}
+	return ret;
+}
+
+int met_tag_disable_real(unsigned int class_id)
+{
+	int i;
+
+	mutex_lock(&bltab.mlock);
+
+	if (class_id == MET_CLASS_ALL) {
+		bltab.flag |= MET_CLASS_ALL;
+		mutex_unlock(&bltab.mlock);
+		return 0;
+	}
+
+	for (i = 0; i < MAX_EVENT_CLASS; i++) {
+		if ((bltab.flag & (1 << i)) == 0) {
+			bltab.class_id[i] = class_id;
+			bltab.flag |= (1 << i);
+			mutex_unlock(&bltab.mlock);
+			return 0;
+		}
+	}
+
+	mutex_unlock(&bltab.mlock);
+	return -1;
+}
+
+int met_tag_enable_real(unsigned int class_id)
+{
+	int i;
+
+	mutex_lock(&bltab.mlock);
+
+	if (class_id == MET_CLASS_ALL) {
+		bltab.flag &= (~MET_CLASS_ALL);
+		mutex_unlock(&bltab.mlock);
+		return 0;
+	}
+
+	for (i = 0; i < MAX_EVENT_CLASS; i++) {
+		if ((bltab.flag & (1 << i)) && (bltab.class_id[i] == class_id)) {
+			bltab.flag &= (~(1 << i));
+			bltab.class_id[i] = 0;
+			mutex_unlock(&bltab.mlock);
+			return 0;
+		}
+	}
+
+	mutex_unlock(&bltab.mlock);
+	return -1;
+}
+
+int met_set_dump_buffer_real(int size)
+{
+	if (dump_buffer_size && dump_buffer) {
+		free_pages((unsigned long)dump_buffer, get_order(dump_buffer_size));
+		dump_data_size = 0;
+		dump_overrun = 0;
+		dump_overrun_size = 0;
+		dump_seq_no = 0;
+		dump_buffer_size = 0;
+	}
+	/* size is 0 means free dump buffer */
+	if (size == 0)
+		return 0;
+
+	if (size < 0)
+		return -1;
+
+	size = (size + (PAGE_SIZE - 1)) & (~(PAGE_SIZE - 1));
+	dump_buffer = (void *)__get_free_pages(GFP_KERNEL, get_order(size));
+	if (dump_buffer == NULL) {
+		ERRF("can not allocate buffer to copy\n");
+		return -ENOMEM;
+	}
+
+	dump_buffer_size = size;
+	return dump_buffer_size;
+}
+
+int met_save_dump_buffer_real(const char *pathname)
+{
+	int size, ret = 0;
+	struct file *outfp = NULL;
+	mm_segment_t oldfs;
+
+	if (dump_data_size == 0)
+		return 0;
+
+	if (dump_data_size < 0 || dump_overrun_size < 0)
+		return -1;
+
+	if (dump_buffer == NULL || dump_buffer_size <= 0)
+		return -1;
+
+	if (dump_overrun)
+		size = dump_overrun_size;
+	else
+		size = dump_data_size;
+
+	if (size >= dump_buffer_size)
+		return -1;
+
+	oldfs = get_fs();
+	set_fs(KERNEL_DS);
+
+	outfp = filp_open(pathname, O_WRONLY | O_TRUNC | O_CREAT, 0664);
+	if (unlikely(outfp == NULL)) {
+		ERRF("can not open saved file for write\n");
+		return -EIO;
+	}
+
+	ret = vfs_write(outfp, dump_buffer, size, &(outfp->f_pos));
+	if (ret < 0)
+		ERRF("can not write to dump file\n");
+	else {
+		dump_data_size = 0;
+		dump_overrun = 0;
+		dump_overrun_size = 0;
+		dump_seq_no = 0;
+	}
+
+	set_fs(oldfs);
+
+	if (outfp != NULL)
+		filp_close(outfp, NULL);
+
+	return 0;
+}
+
+int met_save_log_real(const char *pathname)
+{
+	int len, ret = 0;
+	struct file *infp = NULL;
+	struct file *outfp = NULL;
+	void *ptr = NULL;
+	mm_segment_t oldfs;
+
+	infp = filp_open("/sys/kernel/debug/tracing/trace", O_RDONLY, 0);
+	if (unlikely(infp == NULL)) {
+		ERRF("can not open trace file for read\n");
+		ret = -1;
+		goto save_out;
+	}
+
+	outfp = filp_open(pathname, O_WRONLY | O_TRUNC | O_CREAT, 0664);
+	if (unlikely(outfp == NULL)) {
+		ERRF("can not open saved file for write\n");
+		ret = -2;
+		goto save_out;
+	}
+
+	ptr = (void *)__get_free_pages(GFP_KERNEL, 2);
+	if (ptr == NULL) {
+		ERRF("can not allocate buffer to copy\n");
+		ret = -3;
+		goto save_out;
+	}
+
+	oldfs = get_fs();
+	set_fs(KERNEL_DS);
+
+	while (1) {
+		len = vfs_read(infp, ptr, PAGE_SIZE << 2, &(infp->f_pos));
+		if (len < 0) {
+			ERRF("can not read from trace file\n");
+			ret = -3;
+			break;
+		} else if (len == 0) {
+			break;
+		}
+
+		ret = vfs_write(outfp, ptr, len, &(outfp->f_pos));
+		if (ret < 0) {
+			ERRF("can not write to saved file\n");
+			break;
+		}
+	}
+
+	set_fs(oldfs);
+
+save_out:
+	if (ptr != NULL)
+		free_pages((unsigned long)ptr, 2);
+	if (infp != NULL)
+		filp_close(infp, NULL);
+	if (outfp != NULL)
+		filp_close(outfp, NULL);
+
+	return ret;
+}
+
+#ifdef BUILD_WITH_MET
+#include <linux/module.h>
+#include <linux/uaccess.h>
+/* =========================================================================== */
+/* misc file nodes */
+/* =========================================================================== */
+static ssize_t enable_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	int i;
+
+	i = snprintf(buf, PAGE_SIZE, "%d\n", (bltab.flag >> 31) ? 0 : 1);
+	return i;
+}
+
+static ssize_t enable_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf,
+			    size_t n)
+{
+	int value;
+
+	if ((n == 0) || (buf == NULL))
+		return -EINVAL;
+	if (kstrtoint(buf, 0, &value) != 0)
+		return -EINVAL;
+
+	mutex_lock(&bltab.mlock);
+
+	if (value == 1)
+		bltab.flag &= (~MET_CLASS_ALL);
+	else
+		bltab.flag |= MET_CLASS_ALL;
+
+	mutex_unlock(&bltab.mlock);
+
+	return n;
+}
+
+static struct kobject *kobj_tag;
+static struct kobj_attribute enable_attr = __ATTR(enable, 0664, enable_show, enable_store);
+
+static ssize_t dump_buffer_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	int i, size;
+
+	if (dump_overrun)
+		size = dump_overrun_size;
+	else
+		size = dump_data_size;
+
+	i = snprintf(buf, PAGE_SIZE, "Buffer Size (KB)=%d\nData Size (KB)=%d\nOverrun=%d\n",
+		     dump_buffer_size >> 10, size >> 10, dump_overrun);
+	return i;
+}
+
+static ssize_t dump_buffer_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf,
+				 size_t n)
+{
+	int ret, value;
+
+	if ((n == 0) || (buf == NULL))
+		return -EINVAL;
+	if (kstrtoint(buf, 0, &value) != 0)
+		return -EINVAL;
+
+	ret = met_set_dump_buffer_real(value << 10);
+
+	if (ret < 0)
+		return ret;
+
+	return n;
+}
+
+static struct kobj_attribute dump_buffer_attr =
+__ATTR(dump_buffer_kb, 0664, dump_buffer_show, dump_buffer_store);
+
+static ssize_t options_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	int i = 0;
+
+	buf[0] = 0;
+
+	if (options_flag == 0) {
+		strncat(buf, "none\n", PAGE_SIZE - 1 - i);
+		i += 5;
+	}
+
+	if (options_flag & OPFLAG_OVERWRITE) {
+		strncat(buf, "overwrite\n", PAGE_SIZE - 1 - i);
+		i += 10;
+	}
+
+	return i;
+}
+
+static ssize_t options_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf,
+			     size_t n)
+{
+	if ((n == 0) || (buf == NULL))
+		return -EINVAL;
+
+	if ((n == 1) && (buf[0] == 0xA)) {
+		options_flag = 0;
+		return n;
+	}
+
+	if (strncmp(buf, "overwrite", 9) == 0)
+		options_flag |= OPFLAG_OVERWRITE;
+	else
+		return -EINVAL;
+
+	return n;
+}
+
+static struct kobj_attribute options_attr = __ATTR(options, 0664, options_show, options_store);
+
+int tag_reg(struct file_operations *const fops, struct kobject *kobj)
+{
+	int ret;
+
+	kobj_tag = kobject_create_and_add("tag", kobj);
+	if (kobj_tag == NULL) {
+		ERRF("can not create kobject: kobj_bus\n");
+		return -1;
+	}
+
+	ret = sysfs_create_file(kobj_tag, &enable_attr.attr);
+	if (ret != 0) {
+		ERRF("Failed to create enable in sysfs\n");
+		kobject_del(kobj_tag);
+		kobject_put(kobj_tag);
+		kobj_tag = NULL;
+		return ret;
+	}
+
+	ret = sysfs_create_file(kobj_tag, &dump_buffer_attr.attr);
+	if (ret != 0) {
+		ERRF("Failed to create dump_buffer in sysfs\n");
+		sysfs_remove_file(kobj_tag, &enable_attr.attr);
+		kobject_del(kobj_tag);
+		kobject_put(kobj_tag);
+		kobj_tag = NULL;
+		return ret;
+	}
+
+	ret = sysfs_create_file(kobj_tag, &options_attr.attr);
+	if (ret != 0) {
+		ERRF("Failed to create options in sysfs\n");
+		sysfs_remove_file(kobj_tag, &enable_attr.attr);
+		sysfs_remove_file(kobj_tag, &dump_buffer_attr.attr);
+		kobject_del(kobj_tag);
+		kobject_put(kobj_tag);
+		kobj_tag = NULL;
+		return ret;
+	}
+
+	met_tag_init();
+	met_ext_api.met_tag_start = met_tag_start_real;
+	met_ext_api.met_tag_end = met_tag_end_real;
+	met_ext_api.met_tag_async_start = met_tag_async_start_real;
+	met_ext_api.met_tag_async_end = met_tag_async_end_real;
+	met_ext_api.met_tag_oneshot = met_tag_oneshot_real;
+	met_ext_api.met_tag_userdata = met_tag_userdata_real;
+	met_ext_api.met_tag_dump = met_tag_dump_real;
+	met_ext_api.met_tag_disable = met_tag_disable_real;
+	met_ext_api.met_tag_enable = met_tag_enable_real;
+	met_ext_api.met_set_dump_buffer = met_set_dump_buffer_real;
+	met_ext_api.met_save_dump_buffer = met_save_dump_buffer_real;
+	met_ext_api.met_save_log = met_save_log_real;
+	met_ext_api.met_sched_switch = met_sched_switch;
+	return 0;
+}
+
+int tag_unreg(void)
+{
+	met_ext_api.met_tag_start = NULL;
+	met_ext_api.met_tag_end = NULL;
+	met_ext_api.met_tag_async_start = NULL;
+	met_ext_api.met_tag_async_end = NULL;
+	met_ext_api.met_tag_oneshot = NULL;
+	met_ext_api.met_tag_userdata = NULL;
+	met_ext_api.met_tag_dump = NULL;
+	met_ext_api.met_tag_disable = NULL;
+	met_ext_api.met_tag_enable = NULL;
+	met_ext_api.met_set_dump_buffer = NULL;
+	met_ext_api.met_save_dump_buffer = NULL;
+	met_ext_api.met_save_log = NULL;
+	met_ext_api.met_show_bw_limiter = NULL;
+	met_ext_api.met_reg_bw_limiter = NULL;
+	met_ext_api.met_show_clk_tree = NULL;
+	met_ext_api.met_reg_clk_tree = NULL;
+	met_ext_api.met_sched_switch = NULL;
+	met_tag_uninit();
+	sysfs_remove_file(kobj_tag, &enable_attr.attr);
+	sysfs_remove_file(kobj_tag, &dump_buffer_attr.attr);
+	sysfs_remove_file(kobj_tag, &options_attr.attr);
+	kobject_del(kobj_tag);
+	kobject_put(kobj_tag);
+	kobj_tag = NULL;
+	return 0;
+}
+
+#endif				/* BUILD_WITH_MET */
+
+#else				/* not MET_USER_EVENT_SUPPORT */
+
+#ifdef BUILD_WITH_MET
+int tag_reg(void *p, void *q)
+{
+	return 0;
+}
+
+int tag_unreg(void)
+{
+	return 0;
+}
+#endif
+
+#endif				/* MET_USER_EVENT_SUPPORT */
diff --git a/src/devtools/met-driver/4.9/common/met_vcoredvfs.c b/src/devtools/met-driver/4.9/common/met_vcoredvfs.c
new file mode 100644
index 0000000..560ef67
--- /dev/null
+++ b/src/devtools/met-driver/4.9/common/met_vcoredvfs.c
@@ -0,0 +1,314 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/dma-mapping.h>
+
+#include <helio-dvfsrc.h>
+
+#define MET_USER_EVENT_SUPPORT
+#include "met_drv.h"
+#include "trace.h"
+
+#include "core_plf_init.h"
+#include "core_plf_trace.h"
+
+/*======================================================================*/
+/*	Global variable definitions					*/
+/*======================================================================*/
+
+/* Global variables */
+struct metdevice met_vcoredvfs;
+int polling_mode = 1;
+
+/* external symbols */
+#define DEFAULT_INFO_NUM 3
+#define DEFAULT_SRC_NUM 1
+
+char *default_info_name[DEFAULT_INFO_NUM] = {
+	"OPP",
+	"VOLT",
+	"FREQ"
+};
+
+char *default_src_name[DEFAULT_SRC_NUM] = {
+	"MD2SPM"
+};
+
+unsigned int opp_info[DEFAULT_INFO_NUM];
+unsigned int src_req[DEFAULT_SRC_NUM];
+
+static int met_vcorefs_get_num_opp(void)
+{
+	if (vcorefs_get_num_opp_symbol)
+		return vcorefs_get_num_opp_symbol();
+	else
+		return 0;
+}
+
+static int met_vcorefs_get_opp_info_num(void)
+{
+	if (vcorefs_get_opp_info_num_symbol)
+		return vcorefs_get_opp_info_num_symbol();
+	else
+		return DEFAULT_INFO_NUM;
+}
+
+
+static int met_vcorefs_get_src_req_num(void)
+{
+	if (vcorefs_get_src_req_num_symbol)
+		return vcorefs_get_src_req_num_symbol();
+	else
+		return DEFAULT_SRC_NUM;
+}
+
+
+static char **met_vcorefs_get_opp_info_name(void)
+{
+	if (vcorefs_get_opp_info_name_symbol)
+		return vcorefs_get_opp_info_name_symbol();
+	else
+		return default_info_name;
+}
+
+static char **met_vcorefs_get_src_req_name(void)
+{
+	if (vcorefs_get_src_req_name_symbol)
+		return vcorefs_get_src_req_name_symbol();
+	else
+		return default_src_name;
+}
+
+static unsigned int *met_vcorefs_get_opp_info(void)
+{
+	if (vcorefs_get_opp_info_symbol)
+		return vcorefs_get_opp_info_symbol();
+
+	return opp_info;
+}
+
+static unsigned int *met_vcorefs_get_src_req(void)
+{
+	if (vcorefs_get_src_req_symbol)
+		return vcorefs_get_src_req_symbol();
+
+	return src_req;
+}
+
+
+/*======================================================================*/
+/*	File Node definitions					*/
+/*======================================================================*/
+
+static struct kobject *kobj_met_vcoredvfs;
+
+static ssize_t vcorefs_polling_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf);
+static ssize_t vcorefs_polling_store(struct kobject *kobj,
+				   struct kobj_attribute *attr, const char *buf, size_t n);
+static struct kobj_attribute vcorefs_polling_attr =
+__ATTR(vcorefs_polling, 0664, vcorefs_polling_show, vcorefs_polling_store);
+
+/*======================================================================*/
+/*	Utilities					*/
+/*======================================================================*/
+
+static inline int do_vcoredvfs(void)
+{
+	return met_vcoredvfs.mode;
+}
+
+/*======================================================================*/
+/*	Data Output					*/
+/*======================================================================*/
+
+noinline void vcorefs(unsigned char cnt, unsigned int *value)
+{
+	char *SOB, *EOB;
+
+	MET_TRACE_GETBUF(&SOB, &EOB);
+	EOB = ms_formatH_EOL(EOB, cnt, value);
+	MET_TRACE_PUTBUF(SOB, EOB);
+}
+
+noinline void vcorefs_kicker(unsigned char cnt, int *value)
+{
+	char *SOB, *EOB;
+
+	MET_TRACE_GETBUF(&SOB, &EOB);
+	EOB = ms_formatH_EOL(EOB, cnt, value);
+	MET_TRACE_PUTBUF(SOB, EOB);
+}
+
+noinline void ms_vcorefs(unsigned char cnt, unsigned int *value)
+{
+	char *SOB, *EOB;
+
+	MET_TRACE_GETBUF(&SOB, &EOB);
+	EOB = ms_formatH_EOL(EOB, cnt, value);
+	MET_TRACE_PUTBUF(SOB, EOB);
+}
+
+/*======================================================================*/
+/*	Callback functions						*/
+/*======================================================================*/
+void vcoredvfs_irq(int opp)
+{
+	int num;
+	unsigned int *out;
+
+	num = met_vcorefs_get_opp_info_num();
+	out = met_vcorefs_get_opp_info();
+
+	vcorefs(num, out);
+}
+
+/*======================================================================*/
+/*	File Node Operations						*/
+/*======================================================================*/
+static ssize_t vcorefs_polling_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%d\n", polling_mode);
+}
+
+static ssize_t vcorefs_polling_store(struct kobject *kobj,
+				   struct kobj_attribute *attr, const char *buf, size_t n)
+{
+	int value;
+
+	if ((n == 0) || (buf == NULL))
+		return -EINVAL;
+
+	if (kstrtoint(buf, 0, &value) != 0)
+		return -EINVAL;
+
+	if (value < 0)
+		return -EINVAL;
+
+	polling_mode = value;
+
+	return n;
+}
+
+/*======================================================================*/
+/*	MET Device Operations						*/
+/*======================================================================*/
+static int met_vcoredvfs_create(struct kobject *parent)
+{
+	int ret = 0;
+
+	kobj_met_vcoredvfs = parent;
+
+	ret = sysfs_create_file(kobj_met_vcoredvfs, &vcorefs_polling_attr.attr);
+	if (ret != 0) {
+		pr_debug("Failed to create vcoredvfs in sysfs\n");
+		return ret;
+	}
+
+	return ret;
+}
+
+static void met_vcoredvfs_delete(void)
+{
+	sysfs_remove_file(kobj_met_vcoredvfs, &vcorefs_polling_attr.attr);
+}
+
+static void met_vcoredvfs_start(void)
+{
+	vcoredvfs_irq(-1);
+}
+
+static void met_vcoredvfs_stop(void)
+{
+	vcoredvfs_irq(-1);
+}
+
+static void met_vcoredvfs_polling(unsigned long long stamp, int cpu)
+{
+	int num;
+	unsigned int *out;
+
+	if (!do_vcoredvfs())
+		return;
+
+	/* vcorefs opp information */
+	if (polling_mode)
+		vcoredvfs_irq(-1);
+
+	/* vcorefs source request */
+	num = met_vcorefs_get_src_req_num();
+	out = met_vcorefs_get_src_req();
+
+	ms_vcorefs(num, out);
+}
+
+static const char help[] =
+	"  --vcoredvfs				monitor VCORE DVFS\n";
+static int vcoredvfs_print_help(char *buf, int len)
+{
+	return snprintf(buf, PAGE_SIZE, help);
+}
+
+static const char header_dvfs[] = "met-info [000] 0.0: met_vcorefs_header:";
+
+static const char header_ms_dvfs[] = "met-info [000] 0.0: ms_vcorefs_header:";
+
+static int vcoredvfs_print_header(char *buf, int len)
+{
+	int ret = 0;
+	int idx = 0;
+	int num = 0;
+	char **header;
+
+	ret = snprintf(buf, PAGE_SIZE, "met-info [000] 0.0: met_vcorefs_cfg: NUM_OPP:%d\n", met_vcorefs_get_num_opp());
+
+	/* opp information */
+	num = met_vcorefs_get_opp_info_num();
+	header = met_vcorefs_get_opp_info_name();
+
+	ret += snprintf(buf + ret, PAGE_SIZE, header_dvfs);
+	for (idx = 0; idx < num; idx++)
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "%s,", header[idx]);
+	buf[strlen(buf)-1] = '\n';
+
+	/* source requests */
+	num = met_vcorefs_get_src_req_num();
+	header = met_vcorefs_get_src_req_name();
+
+	ret += snprintf(buf + ret, PAGE_SIZE - ret, header_ms_dvfs);
+	for (idx = 0; idx < num; idx++)
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "%s,", header[idx]);
+	buf[strlen(buf)-1] = '\n';
+
+	met_vcoredvfs.mode = 0;
+
+	return ret;
+}
+
+struct metdevice met_vcoredvfs = {
+	.name = "vcoredvfs",
+	.owner = THIS_MODULE,
+	.type = MET_TYPE_BUS,
+	.create_subfs = met_vcoredvfs_create,
+	.delete_subfs = met_vcoredvfs_delete,
+	.cpu_related = 0,
+	.start = met_vcoredvfs_start,
+	.stop = met_vcoredvfs_stop,
+	.polling_interval = 1,	/* ms */
+	.timed_polling = met_vcoredvfs_polling,
+	.print_help = vcoredvfs_print_help,
+	.print_header = vcoredvfs_print_header,
+};
diff --git a/src/devtools/met-driver/4.9/common/met_wall_time.c b/src/devtools/met-driver/4.9/common/met_wall_time.c
new file mode 100644
index 0000000..4fc1e1d
--- /dev/null
+++ b/src/devtools/met-driver/4.9/common/met_wall_time.c
@@ -0,0 +1,254 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <asm/div64.h>
+#include <linux/types.h>
+#include <linux/kallsyms.h>
+
+#define MET_USER_EVENT_SUPPORT
+#include "met_drv.h"
+#include "trace.h"
+#include "interface.h"
+
+#define MODE_CUSOM_CLKSRC       2
+struct metdevice met_wall_time;
+
+/**                                                                        */
+/* How to add a new clocksource:                                           */
+/*                                                                         */
+/* 1. add constant for new clocksource in #define-macro                    */
+/* 2. declare new weakref function                                         */
+/* 3. implement handler functions:                                         */
+/*     (1) clksrc_attr_t::*ready:                                          */
+/*         check if ...                                                    */
+/*         (i) clocksource correctly working                               */
+/*         (ii) weakref function is not null                               */
+/*     (2) clksrc_attr_t::*get_cnt: read clocksource from weakref function */
+/* 4. place attrs of new clocksource into clksrc_attr_tb                   */
+/* 5. update DEFAULT_CLKSRC_STR                                            */
+/* 6. update help message                                                  */
+/**                                                                        */
+
+#define __SYS_TIMER             0x0
+#define __GPT1                  0x1
+#define __GPT2                  0x2
+#define __GPT3                  0x3
+#define __GPT4                  0x4
+#define __GPT5                  0x5
+#define __GPT6                  0x6
+
+#define DEFAULT_CLKSRC_STR      "SYS_TIMER"
+
+extern u64 met_arch_counter_get_cntvct(void);
+u64 (*met_arch_counter_get_cntvct_symbol)(void);
+
+int __sys_timer_get_cnt(u8 clksrc, u64 *cycles)
+{
+	if (met_arch_counter_get_cntvct_symbol)
+		*cycles = met_arch_counter_get_cntvct_symbol();
+	return 0;
+}
+
+
+struct clksrc_attr_t {
+	u8 clksrc;
+	const char *clksrc_str;
+	/* checks if clksrc/get_cnt function is working/available */
+	int (*clksrc_ready)(u8 clksrc);
+	int (*clksrc_get_cnt)(u8 clksrc, u64 *cycles);
+};
+
+struct clksrc_attr_t clksrc_attr_tb[] = {
+	{__SYS_TIMER, "SYS_TIMER", NULL, __sys_timer_get_cnt},
+	/* {__GPT1, "GPT1", __gpt_timer_ready, __gpt_timer_get_cnt}, */
+	/* {__GPT2, "GPT2", __gpt_timer_ready, __gpt_timer_get_cnt}, */
+	/* {__GPT3, "GPT3", __gpt_timer_ready, __gpt_timer_get_cnt}, */
+	/* {__GPT4, "GPT4", __gpt_timer_ready, __gpt_timer_get_cnt}, */
+	/* {__GPT5, "GPT5", __gpt_timer_ready, __gpt_timer_get_cnt}, */
+	/* {__GPT6, "GPT6", __gpt_timer_ready, __gpt_timer_get_cnt}, */
+};
+
+static const struct clksrc_attr_t *lookup_clksrc_attr_tb(const char *clksrc_str, int len);
+static const struct clksrc_attr_t *wall_time_attr;
+
+/* definitions for auto-sampling of working freq. of clocksource */
+/* maximum tolerable error percentage(%) of sampled clock freq */
+#define FREQ_ERR_PERCENT        3
+
+/* expected working freq. of clocksources */
+static const u32 freq_level[] = { 32768, 13000000, 26000000 };
+
+static u32 lookup_freq_level(u32 freq);
+
+/* flag indicating whether sampling is on-going */
+static u32 do_sample;
+static u32 freq;
+static u64 start_us_ts;
+static u64 start_wall_time;
+/* end definitions for sampling of freq. */
+
+static void wall_time_start(void)
+{
+	met_arch_counter_get_cntvct_symbol = (void *)symbol_get(met_arch_counter_get_cntvct);
+
+	if (met_wall_time.mode != MODE_CUSOM_CLKSRC) {
+		wall_time_attr = lookup_clksrc_attr_tb(DEFAULT_CLKSRC_STR,
+						       strlen(DEFAULT_CLKSRC_STR));
+	}
+
+	freq = 0;
+	do_sample = 1;
+
+	if (wall_time_attr) {
+
+		/* XXX: always use CPU 0 */
+		start_us_ts = cpu_clock(0);
+		wall_time_attr->clksrc_get_cnt(wall_time_attr->clksrc, &start_wall_time);
+
+		/* us_ts = ap_ts/1000; */
+		do_div(start_us_ts, 1000);
+	}
+}
+
+noinline void met_ap_wall_time(unsigned long long ts, int cpu)
+{
+	u64 ap_ts;
+	u64 us_ts;
+	u64 sec;
+	u64 usec;
+	u64 cycles;
+	u64 f;
+
+	if (wall_time_attr) {
+
+		wall_time_attr->clksrc_get_cnt(wall_time_attr->clksrc, &cycles);
+		ap_ts = cpu_clock(cpu);
+
+		us_ts = ap_ts;
+		do_div(us_ts, 1000);	/* us_ts = ap_ts/1000; */
+
+		sec = us_ts;
+		usec = do_div(sec, 1000000);	/* sec = us_ts/1000000; usec = us_ts%1000000; */
+		MET_TRACE("TS.APTS=%llu.%06llu WCLK=%llu\n", (unsigned long long)sec,
+			   (unsigned long long)usec, (unsigned long long)cycles);
+
+		if (do_sample) {
+
+			do_sample = 0;
+
+			f = (cycles - start_wall_time) * 1000000;
+			do_div(f, us_ts - start_us_ts);
+
+			/* don't worry about the u64 -> u32 assignment,           */
+			/* sampled wall-clock freq is expected to be below 2^32-1 */
+			freq = lookup_freq_level(f);
+
+			/* debug message */
+			/* MET_TRACE("wall_time debug: result: %u," */
+			/*         "start cycle: %llu, end cycle: %llu, cycle diff: %llu," */
+			/*         "start us: %llu, end us: %llu, us diff: %llu", */
+			/*         f, */
+			/*         start_wall_time, cycles, cycles - start_wall_time, */
+			/*         start_us_ts, us_ts, us_ts - start_us_ts); */
+
+			if (freq != 0)
+				met_tag_oneshot_real(33880, "_WCLK_FREQ_", freq);
+		}
+	}
+}
+
+static const char help[] =
+"  --wall_time                             output wall-clock syncing info in system timer\n";
+/* "  --wall_time=SYS_TIMER|GPT[1-6]  output wall-clock syncing info in custom clocksource\n"; */
+
+static int wall_time_print_help(char *buf, int len)
+{
+	return snprintf(buf, PAGE_SIZE, help);
+}
+
+static const char *header =
+"met-info [000] 0.0: WCLK: %d\n"
+"met-info [000] 0.0: clocksource: %s\n";
+
+static int wall_time_print_header(char *buf, int len)
+{
+	return snprintf(buf, len, header,
+			freq == 0 ? -1 : freq,
+			wall_time_attr ? wall_time_attr->clksrc_str : "NONE");
+}
+
+static int wall_time_process_argument(const char *arg, int len)
+{
+	/* reset wall-time clocksource */
+	wall_time_attr = lookup_clksrc_attr_tb(arg, len);
+
+	if (!wall_time_attr) {
+		met_wall_time.mode = 0;
+		return -1;
+	}
+
+	met_wall_time.mode = MODE_CUSOM_CLKSRC;
+	return 0;
+}
+
+static const struct clksrc_attr_t *lookup_clksrc_attr_tb(const char *clksrc_str, int len)
+{
+	int i;
+	const struct clksrc_attr_t *attr;
+	int tb_nmemb = sizeof(clksrc_attr_tb) / sizeof(*clksrc_attr_tb);
+
+	for (i = 0; i < tb_nmemb; i++) {
+
+		attr = clksrc_attr_tb + i;
+
+		if (strlen(attr->clksrc_str) == len &&
+		    strncmp(clksrc_str, attr->clksrc_str, len) == 0) {
+			return attr;
+		}
+	}
+
+	return NULL;
+}
+
+static u32 lookup_freq_level(u32 freq)
+{
+
+	int ii;
+	int freq_nmemb = sizeof(freq_level) / sizeof(*freq_level);
+	u32 fdiff;
+
+	for (ii = 0; ii < freq_nmemb; ii++) {
+		fdiff = freq_level[ii] > freq ? freq_level[ii] - freq : freq - freq_level[ii];
+		if (fdiff < freq_level[ii] * FREQ_ERR_PERCENT / 100)
+			return freq_level[ii];
+	}
+
+	return 0;
+}
+
+struct metdevice met_wall_time = {
+	.name = "wall_time",
+	.owner = THIS_MODULE,
+	.type = MET_TYPE_BUS,
+	.cpu_related = 0,
+	.start = wall_time_start,
+	.polling_interval = 1000,
+	.timed_polling = met_ap_wall_time,
+	.print_help = wall_time_print_help,
+	.print_header = wall_time_print_header,
+	.process_argument = wall_time_process_argument,
+};
+EXPORT_SYMBOL(met_wall_time);
diff --git a/src/devtools/met-driver/4.9/common/mips_pmu_hw.c b/src/devtools/met-driver/4.9/common/mips_pmu_hw.c
new file mode 100644
index 0000000..19e836b
--- /dev/null
+++ b/src/devtools/met-driver/4.9/common/mips_pmu_hw.c
@@ -0,0 +1,438 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <asm/system.h>
+#include <linux/smp.h>
+
+#include "cpu_pmu.h"
+#include "mips_pmu_name.h"
+
+struct chip_pmu {
+	enum cpu_type_enum type;
+	struct pmu_desc **desc;
+	void *refptr;
+	const char *cpu_name;
+	unsigned int pmu_desc_size;
+	unsigned int max_hw_events;
+	unsigned int max_reg_count;
+};
+
+struct pmu_desc *mips_pmu_desc[MIPS_MAX_HWEVENTS];
+
+static struct chip_pmu chips[] = {
+	{CPU_1004K, mips_pmu_desc, (void *)mips_1004k_pmu_desc, "MIPS_1004K",
+	 MIPS_1004K_PMU_DESC_SIZE, MIPS_1004K_PMU_DESC_COUNT, PMU_1004K_MAX_HW_REGS},
+};
+
+static struct chip_pmu chip_unknown = { CPU_UNKNOWN, NULL, NULL, "Unknown CPU", 0, 0, 0 };
+
+#define CHIP_PMU_COUNT (sizeof(chips) / sizeof(struct chip_pmu))
+static struct chip_pmu *chip;
+#define M_CONFIG1_PC    (1 << 4)
+
+#define M_PERFCTL_EXL           (1  <<  0)
+#define M_PERFCTL_KERNEL        (1  <<  1)
+#define M_PERFCTL_SUPERVISOR        (1  <<  2)
+#define M_PERFCTL_USER          (1  <<  3)
+#define M_PERFCTL_INTERRUPT_ENABLE  (1  <<  4)
+#define M_PERFCTL_EVENT(event)      (((event) & 0x3ff)  << 5)
+#define M_PERFCTL_VPEID(vpe)        ((vpe)    << 16)
+
+#ifdef CONFIG_CPU_BMIPS5000
+#define M_PERFCTL_MT_EN(filter)     0
+#else				/* !CONFIG_CPU_BMIPS5000 */
+#define M_PERFCTL_MT_EN(filter)     ((filter) << 20)
+#endif				/* CONFIG_CPU_BMIPS5000 */
+
+#define    M_TC_EN_ALL          M_PERFCTL_MT_EN(0)
+#define    M_TC_EN_VPE          M_PERFCTL_MT_EN(1)
+#define    M_TC_EN_TC           M_PERFCTL_MT_EN(2)
+#define M_PERFCTL_TCID(tcid)        ((tcid)   << 22)
+#define M_PERFCTL_WIDE          (1  << 30)
+#define M_PERFCTL_MORE          (1  << 31)
+#define M_PERFCTL_TC            (1  << 30)
+
+#define M_PERFCTL_COUNT_EVENT_WHENEVER  (M_PERFCTL_EXL |        \
+		M_PERFCTL_KERNEL |      \
+		M_PERFCTL_USER |        \
+		M_PERFCTL_SUPERVISOR |      \
+		M_PERFCTL_INTERRUPT_ENABLE)
+
+#ifdef CONFIG_MIPS_MT_SMP
+#define M_PERFCTL_CONFIG_MASK       0x3fff801f
+#else
+#define M_PERFCTL_CONFIG_MASK       0x1f
+#endif
+#define M_PERFCTL_EVENT_MASK        0xfe0
+
+#define vpe_id()    0
+
+/* To get current TCID*/
+#define read_c0_tcbind() __read_32bit_c0_register($2, 2)
+
+struct cpu_hw_events {
+	unsigned int config_base[MIPS_MAX_HWEVENTS];
+	unsigned int saved_ctrl[MIPS_MAX_HWEVENTS];
+};
+
+DEFINE_PER_CPU(struct cpu_hw_events, cpu_hw_events) = {
+	.config_base = {
+	0, 0, 0, 0}, .saved_ctrl = {
+0, 0, 0, 0},};
+
+static enum cpu_type_enum mips_get_ic(void)
+{
+	unsigned int value = current_cpu_type();
+
+	/* pr_debug("ic value: %X\n", value); */
+	return value;
+}
+
+static int __n_counters(void)
+{
+	if (!(read_c0_config1() & M_CONFIG1_PC))
+		return 0;
+	if (!(read_c0_perfctrl0() & M_PERFCTL_MORE))
+		return 1;
+	if (!(read_c0_perfctrl1() & M_PERFCTL_MORE))
+		return 2;
+	if (!(read_c0_perfctrl2() & M_PERFCTL_MORE))
+		return 3;
+
+	return 4;
+}
+
+static int n_counters(void)
+{
+	int counters;
+
+	switch (current_cpu_type()) {
+	case CPU_R10000:
+		counters = 2;
+		break;
+	case CPU_R12000:
+	case CPU_R14000:
+		counters = 4;
+		break;
+	default:
+		counters = __n_counters();
+		break;
+	}
+
+	return counters;
+}
+
+static int mips_pmu_hw_get_counters(void)
+{
+	int count = n_counters();
+
+	/* pr_debug("pmu hw event nr: %d\n", count); */
+	return count;
+}
+
+static unsigned int mipsxx_pmu_swizzle_perf_idx(unsigned int idx)
+{
+	if (vpe_id() == 1)
+		idx = (idx + 2) & 3;
+	return idx;
+}
+
+static void mipsxx_pmu_write_counter(unsigned int idx, u64 val)
+{
+	idx = mipsxx_pmu_swizzle_perf_idx(idx);
+
+	switch (idx) {
+	case 0:
+		write_c0_perfcntr0(val);
+		return;
+	case 1:
+		write_c0_perfcntr1(val);
+		return;
+	case 2:
+		write_c0_perfcntr2(val);
+		return;
+	case 3:
+		write_c0_perfcntr3(val);
+		return;
+	}
+}
+
+static u64 mipsxx_pmu_read_counter(unsigned int idx)
+{
+	idx = mipsxx_pmu_swizzle_perf_idx(idx);
+
+	switch (idx) {
+	case 0:
+		/*
+		 * The counters are unsigned, we must cast to truncate
+		 * off the high bits.
+		 */
+		return (u32) read_c0_perfcntr0();
+	case 1:
+		return (u32) read_c0_perfcntr1();
+	case 2:
+		return (u32) read_c0_perfcntr2();
+	case 3:
+		return (u32) read_c0_perfcntr3();
+	default:
+		WARN_ONCE(1, "Invalid performance counter number (%d)\n", idx);
+		return 0;
+	}
+}
+
+
+static unsigned int mipsxx_pmu_read_control(unsigned int idx)
+{
+	idx = mipsxx_pmu_swizzle_perf_idx(idx);
+
+	switch (idx) {
+	case 0:
+		return read_c0_perfctrl0();
+	case 1:
+		return read_c0_perfctrl1();
+	case 2:
+		return read_c0_perfctrl2();
+	case 3:
+		return read_c0_perfctrl3();
+	default:
+		WARN_ONCE(1, "Invalid performance counter number (%d)\n", idx);
+		return 0;
+	}
+}
+
+static void mipsxx_pmu_write_control(unsigned int idx, unsigned int val)
+{
+	idx = mipsxx_pmu_swizzle_perf_idx(idx);
+
+	switch (idx) {
+	case 0:
+		write_c0_perfctrl0(val);
+		return;
+	case 1:
+		write_c0_perfctrl1(val);
+		return;
+	case 2:
+		write_c0_perfctrl2(val);
+		return;
+	case 3:
+		write_c0_perfctrl3(val);
+		return;
+	}
+}
+
+static int mipsxx_pmu_get_vpeid(void)
+{
+	return read_c0_tcbind() & 0xF;
+}
+
+static void mipsxx_pmu_reset_counters(int idx)
+{
+	switch (idx) {
+	case 3:
+		mipsxx_pmu_write_control(3, 0);
+		mipsxx_pmu_write_counter(3, 0);
+		break;
+	case 2:
+		mipsxx_pmu_write_control(2, 0);
+		mipsxx_pmu_write_counter(2, 0);
+		break;
+	case 1:
+		mipsxx_pmu_write_control(1, 0);
+		mipsxx_pmu_write_counter(1, 0);
+		break;
+	case 0:
+		mipsxx_pmu_write_control(0, 0);
+		mipsxx_pmu_write_counter(0, 0);
+		break;
+	}
+}
+
+static void mipsxx_pmu_enable_event(int idx, int event)
+{
+	struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
+	unsigned long flags;
+
+	WARN_ON(idx < 0 || idx >= chip->max_hw_events);
+	cpuc->saved_ctrl[idx] = M_PERFCTL_EVENT(event & 0xff) |
+	    M_PERFCTL_VPEID(mipsxx_pmu_get_vpeid()) |
+	    (cpuc->config_base[idx] & M_PERFCTL_CONFIG_MASK);
+#ifdef CONFIG_CPU_BMIPS5000
+	/* if (IS_ENABLED(CONFIG_CPU_BMIPS5000)) */
+	/* enable the counter for the calling thread */
+	cpuc->saved_ctrl[idx] |= (1 << (12 + vpe_id())) | M_PERFCTL_TC;
+#endif
+	/*
+	 * To enable pmu count
+	 */
+	local_irq_save(flags);
+	mipsxx_pmu_write_control(idx, cpuc->saved_ctrl[idx]);
+	local_irq_restore(flags);
+}
+
+static void mipsxx_pmu_disable_event(int idx)
+{
+	struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
+	unsigned long flags;
+
+	/* WARN_ON(idx < 0 || idx >= mipspmu.num_counters); */
+	WARN_ON(idx < 0 || idx >= chip->max_hw_events);
+
+	local_irq_save(flags);
+	cpuc->saved_ctrl[idx] = mipsxx_pmu_read_control(idx) & ~M_PERFCTL_COUNT_EVENT_WHENEVER;
+	mipsxx_pmu_write_control(idx, cpuc->saved_ctrl[idx]);
+	local_irq_restore(flags);
+}
+
+static int mips_pmu_hw_get_event_desc(int idx, int event, char *event_desc)
+{
+	int i;
+
+	if (event_desc == NULL) {
+		pr_debug("event_desc is NULL\n");
+		return -1;
+	}
+
+	for (i = 0; i < chip->max_reg_count; i++) {
+		if (chip->desc[idx][i].event == event) {
+			strncpy(event_desc, chip->desc[idx][i].name, MXSIZE_PMU_DESC - 1);
+			break;
+		}
+	}
+	if (i == chip->max_reg_count)
+		return -1;
+
+	return 0;
+}
+
+
+static int mips_pmu_hw_check_event(struct met_pmu *pmu, int idx, int event)
+{
+	int i;
+
+	/* to check index over run */
+	if (!chip)
+		return -1;
+
+	if (idx >= chip->max_hw_events)
+		return -1;
+
+	for (i = 0; i < chip->max_reg_count; i++) {
+		if (chip->desc[idx][i].event == event)
+			break;
+	}
+	if (i == chip->max_reg_count)
+		return -1;
+
+	return 0;
+}
+
+static void mips_pmu_hw_start(struct met_pmu *pmu, int count)
+{
+	int i;
+	int generic = count - 1;
+	struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
+
+	/* pr_debug("hw_start generic: %d\n", generic); */
+	for (i = 0; i < generic; i++) {
+		/* init config */
+		cpuc->config_base[i] = 0;
+		cpuc->config_base[i] |= M_TC_EN_VPE;
+		cpuc->config_base[i] |= M_PERFCTL_USER;
+		cpuc->config_base[i] |= M_PERFCTL_KERNEL;
+		cpuc->config_base[i] |= M_PERFCTL_EXL;
+		cpuc->config_base[i] |= M_PERFCTL_SUPERVISOR;
+		cpuc->config_base[i] &= M_PERFCTL_CONFIG_MASK;
+		 /**/ mipsxx_pmu_reset_counters(i);
+		if (pmu[i].mode == MODE_POLLING)
+			mipsxx_pmu_enable_event(i, pmu[i].event);
+	}
+	if (pmu[count - 1].mode == MODE_POLLING)
+		pr_debug("%s %d BUG!!! index over run!!\n", __func__, __LINE__);
+}
+
+static void mips_pmu_hw_stop(int count)
+{
+	int idx = 0;
+	int generic = count - 1;
+	/* pr_debug("reset %d\n", generic); */
+	for (idx = 0; idx < generic; idx++) {
+		mipsxx_pmu_reset_counters(idx);
+		mipsxx_pmu_disable_event(idx);
+	}
+}
+
+
+static unsigned int mips_pmu_hw_polling(struct met_pmu *pmu, int count, unsigned int *pmu_value)
+{
+	int i, cnt = 0;
+	int generic = count - 1;
+
+	for (i = 0; i < generic; i++) {
+		if (pmu[i].mode == MODE_POLLING) {
+			pmu_value[cnt] = mipsxx_pmu_read_counter(i);
+			cnt++;
+			mipsxx_pmu_reset_counters(i);
+			mipsxx_pmu_enable_event(i, pmu[i].event);
+		}
+	}
+	if (pmu[count - 1].mode == MODE_POLLING) {
+		pr_debug("%s %d BUG!!! index over run!!\n", __func__, __LINE__);
+		pmu_value[cnt] = 0xFFFF;
+		cnt++;
+	}
+
+	return cnt;
+}
+
+
+
+struct cpu_pmu_hw mips_pmu = {
+	.name = "mips_pmu",
+	.get_event_desc = mips_pmu_hw_get_event_desc,
+	.check_event = mips_pmu_hw_check_event,
+	.start = mips_pmu_hw_start,
+	.stop = mips_pmu_hw_stop,
+	.polling = mips_pmu_hw_polling,
+};
+
+struct cpu_pmu_hw *cpu_pmu_hw_init(void)
+{
+	int i = 0;
+	enum cpu_type_enum type;
+	int pmu_hw_count = 0;
+
+	type = mips_get_ic();
+
+	if (CPU_UNKNOWN == type || CPU_LAST == type) {
+		chip = &chip_unknown;
+		return NULL;
+	}
+	for (i = 0; i < CHIP_PMU_COUNT; i++) {
+		if (chips[i].type == type) {
+			chip = &(chips[i]);
+			break;
+		}
+	}
+	if (i == CHIP_PMU_COUNT) {
+		chip = &chip_unknown;
+		return NULL;
+	}
+
+	pmu_hw_count = mips_pmu_hw_get_counters();
+	for (i = 0; i < pmu_hw_count; i++)
+		chip->desc[i] = chip->refptr + (chip->pmu_desc_size * i);
+
+	mips_pmu.nr_cnt = pmu_hw_count + 1;
+	mips_pmu.cpu_name = chip->cpu_name;
+	return &mips_pmu;
+}
diff --git a/src/devtools/met-driver/4.9/common/mips_pmu_name.h b/src/devtools/met-driver/4.9/common/mips_pmu_name.h
new file mode 100644
index 0000000..d7ea26a
--- /dev/null
+++ b/src/devtools/met-driver/4.9/common/mips_pmu_name.h
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+
+#ifndef _MIPS_PMU_NAME_H_
+#define _MIPS_PMU_NAME_H_
+
+/* MIPS 1004K */
+#define MIPS_MAX_HWEVENTS		(4)
+
+#define PMU_1004K_MAX_HW_REGS	(128)
+struct pmu_desc mips_1004k_pmu_desc[][PMU_1004K_MAX_HW_REGS] = {
+	/* COUNT 0 */
+	{
+	 {0, "CPU_CYCLES"},
+	 {1, "CPU_INST"},
+	 {2, "BRANCH_INSNS"},
+	 {3, "JR_31_INSNS"},
+	 {4, "JR_NON_31_INSNS"},
+	 {5, "ITLB_ACCESSES"},
+	 {6, "DTLB_ACCESSES"},
+	 {7, "JTLB_INSN_ACCESSES"},
+	 {8, "JTLB_DATA_ACCESSES"},
+	 {9, "ICACHE_ACCESSES"},
+	 {10, "DCACHE_ACCESSES"},
+	 {11, "DCACHE_MISSES"},
+	 {12, "RESERVED"},
+	 {13, "STORE_MISS_INSNS"},
+	 {14, "INTEGER_INSNS"},
+	 {15, "LOAD_INSNS"},
+	 {16, "J_JAL_INSNS"},
+	 {17, "NO_OPS_INSNS"},
+	 {18, "ALL_STALLS"},
+	 {19, "SC_INSNS"},
+	 {20, "PREFETCH_INSNS"},
+	 {21, "L2_CACHE_WRITEBACKS"},
+	 {22, "L2_CACHE_MISSES"},
+	 {23, "EXCEPTIONS_TAKEN"},
+	 {24, "CACHE_FIXUP_CYCLES"},
+	 {25, "IFU_STALLS"},
+	 {26, "DSP_INSNS"},
+	 {27, "RESERVED"},
+	 {28, "POLICY_EVENTS"},
+	 {29, "ISPRAM_EVENTS"},
+	 {30, "COREEXTEND_EVENTS"},
+	 {31, "YIELD_EVENTS"},
+	 {32, "ITC_LOADS"},
+	 {33, "UNCACHED_LOAD_INSNS"},
+	 {34, "FORK_INSNS"},
+	 {35, "CP2_ARITH_INSNS"},
+	 {36, "INTERVENTION_STALLS"},
+	 {37, "ICACHE_MISS_STALLS"},
+	 {38, "RESERVED"},
+	 {39, "DCACHE_MISS_CYCLES"},
+	 {40, "UNCACHED_STALLS"},
+	 {41, "MDU_STALLS"},
+	 {42, "CP2_STALLS"},
+	 {43, "ISPRAM_STALLS"},
+	 {44, "CACHE_INSN_STALLS"},
+	 {45, "LOAD_USE_STALLS"},
+	 {46, "INTERLOCK_STALLS"},
+	 {47, "RELAX_STALLS"},
+	 {48, "IFU_FB_FULL_REFETCHES"},
+	 {49, "EJTAG_INSN_TRIGGERS"},
+	 {50, "FSB_LESS_25_FULL"},
+	 {51, "FSB_OVER_50_FULL"},
+	 {52, "LDQ_LESS_25_FULL"},
+	 {53, "LDQ_OVER_50_FULL"},
+	 {54, "WBB_LESS_25_FULL"},
+	 {55, "WBB_OVER_50_FULL"},
+	 {56, "INTERVENTION_HIT_COUNT"},
+	 {57, "INVALIDATE_INTERVENTION_COUNT"},
+	 {58, "EVICTION_COUNT"},
+	 {59, "MESI_INVAL_COUNT"},
+	 {60, "MESI_MODIFIED_COUNT"},
+	 {61, "SELF_INTERVENTION_LATENCY"},
+	 {62, "READ_RESPONSE_LATENCY"},
+	 {63, "RESERVED"},
+	 {64, "SI_PCEVENT1"},
+	 {65, "SI_PCEVENT3"},
+	 {66, "SI_PCEVENT5"},
+	 {67, "SI_PCEVENT7"},
+	 {-1, "RESERVED"},
+	 /* 68 - 127 for Reserved */
+	 },
+	/* COUNT 1 */
+	{
+	 {0, "CPU_CYCLES"},
+	 {1, "CPU_INST"},
+	 {2, "MISPREDICTED_BRANCH_INSNS"},
+	 {3, "JR_31_MISPREDICTIONS"},
+	 {4, "JR_31_NO_PREDICTIONS"},
+	 {5, "ITLB_MISSES"},
+	 {6, "DTLB_MISSES"},
+	 {7, "JTLB_INSN_MISSES"},
+	 {8, "JTLB_DATA_MISSES"},
+	 {9, "ICACHE_MISSES"},
+	 {10, "DCACHE_WRITEBACKS"},
+	 {11, "DCACHE_MISSES"},
+	 {12, "RESERVED"},
+	 {13, "LOAD_MISS_INSNS"},
+	 {14, "FPU_INSNS"},
+	 {15, "STORE_INSNS"},
+	 {16, "MIPS16_INSNS"},
+	 {17, "INT_MUL_DIV_INSNS"},
+	 {18, "REPLAYED_INSNS"},
+	 {19, "SC_INSNS_FAILED"},
+	 {20, "CACHE_HIT_PREFETCH_INSNS"},
+	 {21, "L2_CACHE_ACCESSES"},
+	 {22, "L2_CACHE_SINGLE_BIT_ERRORS"},
+	 {23, "SINGLE_THREADED_CYCLES"},
+	 {24, "REFETCHED_INSNS"},
+	 {25, "ALU_STALLS"},
+	 {26, "ALU_DSP_SATURATION_INSNS"},
+	 {27, "MDU_DSP_SATURATION_INSNS"},
+	 {28, "CP2_EVENTS"},
+	 {29, "DSPRAM_EVENTS"},
+	 {30, "RESERVED"},
+	 {31, "ITC_EVENT"},
+	 {33, "UNCACHED_STORE_INSNS"},
+	 {34, "YIELD_IN_COMP"},
+	 {35, "CP2_TO_FROM_INSNS"},
+	 {36, "INTERVENTION_MISS_STALLS"},
+	 {37, "DCACHE_MISS_STALLS"},
+	 {38, "RESERVED"},
+	 /* 38 was listed in OPROFILE web page, but not listed 1004k mips spec */
+	 /* {38, "FSB_INDEX_CONFLICT_STALLS"}, */
+	 {39, "L2_CACHE_MISS_CYCLES"},
+	 {40, "ITC_STALLS"},
+	 {41, "FPU_STALLS"},
+	 {42, "COREEXTEND_STALLS"},
+	 {43, "DSPRAM_STALLS"},
+	 {45, "ALU_TO_AGEN_STALLS"},
+	 {46, "MISPREDICTION_STALLS"},
+	 {47, "RESERVED"},
+	 {48, "FB_ENTRY_ALLOCATED_CYCLES"},
+	 {49, "EJTAG_DATA_TRIGGERS"},
+	 {50, "FSB_25_50_FULL"},
+	 {51, "FSB_FULL_STALLS"},
+	 {52, "LDQ_25_50_FULL"},
+	 {53, "LDQ_FULL_STALLS"},
+	 {54, "WBB_25_50_FULL"},
+	 {55, "WBB_FULL_STALLS"},
+	 {56, "INTERVENTION_COUNT"},
+	 {57, "INVALID_INTERVENT_HIT_CNT"},
+	 {58, "WRITEBACK_COUNT"},
+	 {59, "MESI_EXCLUSIVE_COUNT"},
+	 {60, "MESI_SHARED_COUNT"},
+	 {61, "SELF_INTERVENTION_COUNT"},
+	 {62, "READ_RESPONSE_COUNT"},
+	 {63, "RESERVED"},
+	 {64, "SI_PCEVENT0"},
+	 {65, "SI_PCEVENT2"},
+	 {66, "SI_PCEVENT4"},
+	 {67, "SI_PCEVENT6"},
+	 {-1, "RESERVED"},
+	 },
+};
+
+#define MIPS_1004K_PMU_DESC_SIZE (sizeof(mips_1004k_pmu_desc[0]))
+#define MIPS_1004K_PMU_DESC_COUNT (sizeof(mips_1004k_pmu_desc) / MIPS_1004K_PMU_DESC_SIZE)
+
+#endif				/* _V8_PMU_NAME_H_ */
diff --git a/src/devtools/met-driver/4.9/common/mtk_emi_bm.c b/src/devtools/met-driver/4.9/common/mtk_emi_bm.c
new file mode 100644
index 0000000..3c61470
--- /dev/null
+++ b/src/devtools/met-driver/4.9/common/mtk_emi_bm.c
@@ -0,0 +1,253 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <asm/io.h>
+#include <mt-plat/sync_write.h>
+#include <mt-plat/mtk_io.h>
+/* #include "mtk_typedefs.h" */
+#include "core_plf_init.h"
+#include "mtk_emi_bm.h"
+#include "met_drv.h"
+#include "interface.h"
+#if defined(CONFIG_MTK_TINYSYS_SSPM_SUPPORT) && defined(ONDIEMET_SUPPORT)
+#include "sspm/ondiemet_sspm.h"
+#endif
+
+#undef	DEBUG
+
+#define	emi_readl		readl
+#define	emi_reg_sync_writel	mt_reg_sync_writel
+
+#define MASK_MASTER	0xFF
+#define MASK_TRANS_TYPE	0xFF
+
+static void __iomem *BaseAddrEMI;
+const unsigned int emi_config[] = {
+	EMI_BMEN,
+	EMI_MSEL,
+	EMI_MSEL2,
+	EMI_MSEL3,
+	EMI_MSEL4,
+	EMI_MSEL5,
+	EMI_MSEL6,
+	EMI_MSEL7,
+	EMI_MSEL8,
+	EMI_MSEL9,
+	EMI_MSEL10,
+	EMI_BMID0,
+	EMI_BMID1,
+	EMI_BMID2,
+	EMI_BMID3,
+	EMI_BMID4,
+	EMI_BMID5,
+	EMI_BMID6,
+	EMI_BMID7,
+	EMI_BMID8,
+	EMI_BMID9,
+	EMI_BMID10,
+	EMI_BMEN1,
+	EMI_BMEN2,
+	EMI_BMRW0,
+	EMI_BMRW1
+};
+#define EMI_CONFIG_MX_NR (sizeof(emi_config)/sizeof(unsigned int))
+static unsigned int emi_config_val[EMI_CONFIG_MX_NR];
+
+int MET_BM_Init(void)
+{
+	/*emi*/
+	if (mt_cen_emi_base_get_symbol) {
+		BaseAddrEMI = mt_cen_emi_base_get_symbol();
+	} else {
+		pr_debug("mt_cen_emi_base_get_symbol = NULL\n");
+		PR_BOOTMSG_ONCE("mt_cen_emi_base_get_symbol = NULL\n");
+		BaseAddrEMI = 0;
+	}
+
+	if (BaseAddrEMI == 0) {
+		pr_debug("BaseAddrEMI = 0\n");
+		PR_BOOTMSG_ONCE("BaseAddrEMI = 0\n");
+		return -1;
+	}
+	pr_debug("MET EMI: map emi to %p\n", BaseAddrEMI);
+	PR_BOOTMSG("MET EMI: map emi to %p\n", BaseAddrEMI);
+
+	return 0;
+}
+
+void MET_BM_DeInit(void)
+{
+}
+
+void MET_BM_SaveCfg(void)
+{
+	int i;
+
+	for (i = 0; i < EMI_CONFIG_MX_NR; i++)
+		emi_config_val[i] = emi_readl(IOMEM(ADDR_EMI + emi_config[i]));
+}
+
+void MET_BM_RestoreCfg(void)
+{
+	int i;
+
+	for (i = 0; i < EMI_CONFIG_MX_NR; i++)
+		emi_reg_sync_writel(emi_config_val[i], ADDR_EMI + emi_config[i]);
+}
+
+int MET_BM_SetMonitorCounter(const unsigned int counter_num,
+			     const unsigned int master, const unsigned int trans_type)
+{
+	unsigned int value, addr;
+	const unsigned int iMask = (MASK_TRANS_TYPE << 8) | MASK_MASTER;
+
+	if (counter_num < 1 || counter_num > BM_COUNTER_MAX)
+		return BM_ERR_WRONG_REQ;
+
+
+	if (counter_num == 1) {
+		addr = EMI_BMEN;
+		value = (emi_readl(IOMEM(ADDR_EMI + addr)) & ~(iMask << 16)) |
+			((trans_type & MASK_TRANS_TYPE) << 24) | ((master & MASK_MASTER) << 16);
+	} else {
+		addr = (counter_num <= 3) ? EMI_MSEL : (EMI_MSEL2 + (counter_num / 2 - 2) * 8);
+
+
+		value = emi_readl(IOMEM(ADDR_EMI + addr)) & ~(iMask << ((counter_num % 2) * 16));
+
+
+		value |= (((trans_type & MASK_TRANS_TYPE) << 8) |
+			  (master & MASK_MASTER)) << ((counter_num % 2) * 16);
+	}
+
+	emi_reg_sync_writel(value, ADDR_EMI + addr);
+
+	return BM_REQ_OK;
+}
+
+int MET_BM_SetTtypeCounterRW(unsigned int bmrw0_val, unsigned int bmrw1_val)
+{
+
+	unsigned int value_origin;
+
+	value_origin = emi_readl(IOMEM(ADDR_EMI + EMI_BMRW0));
+	MET_TRACE("[MET_EMI_settype1] value_origin: %x\n", value_origin);
+	if (value_origin != bmrw0_val) {
+		emi_reg_sync_writel(bmrw0_val, ADDR_EMI + EMI_BMRW0);
+		MET_TRACE("[MET_EMI_settype1] bmrw0_val: %x, value_origin: %x\n", bmrw0_val,
+			   value_origin);
+	}
+
+	value_origin = emi_readl(IOMEM(ADDR_EMI + EMI_BMRW1));
+	MET_TRACE("[MET_EMI_settype2] value_origin: %x\n", value_origin);
+	if (value_origin != bmrw1_val) {
+		emi_reg_sync_writel(bmrw1_val, ADDR_EMI + EMI_BMRW1);
+		MET_TRACE("[MET_EMI_settype2] bmrw0_val: %x, value_origin: %x\n", bmrw1_val,
+			   value_origin);
+
+	}
+	return BM_REQ_OK;
+}
+
+int MET_BM_Set_WsctTsct_id_sel(unsigned int counter_num, unsigned int enable)
+{
+	unsigned int value;
+
+	if (counter_num > 3)
+		return BM_ERR_WRONG_REQ;
+
+	value =
+	    ((emi_readl(IOMEM(ADDR_EMI + EMI_BMEN2)) & (~(1 << (28 + counter_num)))) |
+	     (enable << (28 + counter_num)));
+	emi_reg_sync_writel(value, ADDR_EMI + EMI_BMEN2);
+
+	return BM_REQ_OK;
+}
+
+int MET_BM_SetbusID_En(const unsigned int counter_num,
+		       const unsigned int enable)
+{
+	unsigned int value;
+
+	if ((counter_num < 1 || counter_num > BM_COUNTER_MAX) || (enable > 1))
+		return BM_ERR_WRONG_REQ;
+
+	if (enable == 0) {
+
+		value = (emi_readl(IOMEM(ADDR_EMI + EMI_BMEN2))
+			 & ~(1 << (counter_num - 1)));
+	} else {
+
+		value = (emi_readl(IOMEM(ADDR_EMI + EMI_BMEN2))
+			 | (1 << (counter_num - 1)));
+	}
+	emi_reg_sync_writel(value, ADDR_EMI + EMI_BMEN2);
+
+	return BM_REQ_OK;
+}
+
+int MET_BM_SetbusID(const unsigned int counter_num,
+		    const unsigned int id)
+{
+	unsigned int value, addr, shift_num;
+
+	if ((counter_num < 1 || counter_num > BM_COUNTER_MAX))
+		return BM_ERR_WRONG_REQ;
+
+
+	addr = EMI_BMID0 + (counter_num - 1) / 2 * 4;
+	shift_num = ((counter_num - 1) % 2) * 16;
+
+	value = emi_readl(IOMEM(ADDR_EMI + addr)) & ~(EMI_BMID_MASK << shift_num);
+
+
+	if (id <= 0xffff)
+		value |= id << shift_num;
+
+	emi_reg_sync_writel(value, ADDR_EMI + addr);
+
+	return BM_REQ_OK;
+}
+
+int MET_BM_SetUltraHighFilter(const unsigned int counter_num, const unsigned int enable)
+{
+	unsigned int value;
+
+	if ((counter_num < 1 || counter_num > BM_COUNTER_MAX) || (enable > 1))
+		return BM_ERR_WRONG_REQ;
+
+
+	value = (emi_readl(IOMEM(ADDR_EMI + EMI_BMEN1))
+		 & ~(1 << (counter_num - 1)))
+		| (enable << (counter_num - 1));
+
+	emi_reg_sync_writel(value, ADDR_EMI + EMI_BMEN1);
+
+	return BM_REQ_OK;
+}
+
+int MET_BM_SetLatencyCounter(unsigned int enable)
+{
+	unsigned int value;
+
+	value = emi_readl(IOMEM(ADDR_EMI + EMI_BMEN2)) & ~(0x3 << 24);
+	if (enable == 1)
+		value |= (0x2 << 24);
+
+	emi_reg_sync_writel(value, ADDR_EMI + EMI_BMEN2);
+
+	return BM_REQ_OK;
+}
diff --git a/src/devtools/met-driver/4.9/common/mtk_emi_bm.h b/src/devtools/met-driver/4.9/common/mtk_emi_bm.h
new file mode 100644
index 0000000..43295be
--- /dev/null
+++ b/src/devtools/met-driver/4.9/common/mtk_emi_bm.h
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __MT_MET_EMI_BM_H__
+#define __MT_MET_EMI_BM_H__
+
+
+#define	ADDR_EMI		((unsigned long) BaseAddrEMI)
+
+/*========================================================*/
+/*EMI configuration by project*/
+/*Change config start*/
+/*========================================================*/
+#define _GP_1_Default	(_M0 | _M1)
+#define _GP_2_Default	(_M2 | _M5)
+#define _GP_3_Default	(_M6 | _M7)
+
+
+/*========================================================*/
+/*Change config end*/
+/*========================================================*/
+
+
+#define _M0		(0x01)
+#define _M1		(0x02)
+#define _M2		(0x04)
+#define _M3		(0x08)
+#define _M4		(0x10)
+#define _M5		(0x20)
+#define _M6		(0x40)
+#define _M7		(0x80)
+#define _ALL	(0xFF)
+
+enum BM_RW_Type {
+	BM_BOTH_READ_WRITE,
+	BM_READ_ONLY,
+	BM_WRITE_ONLY
+};
+
+enum {
+	BM_TRANS_TYPE_1BEAT = 0x0,
+	BM_TRANS_TYPE_2BEAT,
+	BM_TRANS_TYPE_3BEAT,
+	BM_TRANS_TYPE_4BEAT,
+	BM_TRANS_TYPE_5BEAT,
+	BM_TRANS_TYPE_6BEAT,
+	BM_TRANS_TYPE_7BEAT,
+	BM_TRANS_TYPE_8BEAT,
+	BM_TRANS_TYPE_9BEAT,
+	BM_TRANS_TYPE_10BEAT,
+	BM_TRANS_TYPE_11BEAT,
+	BM_TRANS_TYPE_12BEAT,
+	BM_TRANS_TYPE_13BEAT,
+	BM_TRANS_TYPE_14BEAT,
+	BM_TRANS_TYPE_15BEAT,
+	BM_TRANS_TYPE_16BEAT,
+	BM_TRANS_TYPE_1Byte = 0 << 4,
+	BM_TRANS_TYPE_2Byte = 1 << 4,
+	BM_TRANS_TYPE_4Byte = 2 << 4,
+	BM_TRANS_TYPE_8Byte = 3 << 4,
+	BM_TRANS_TYPE_16Byte = 4 << 4,
+	BM_TRANS_TYPE_32Byte = 5 << 4,
+	BM_TRANS_TYPE_BURST_WRAP = 0 << 7,
+	BM_TRANS_TYPE_BURST_INCR = 1 << 7
+};
+
+enum {
+	BM_TRANS_RW_DEFAULT = 0x0,
+	BM_TRANS_RW_READONLY,
+	BM_TRANS_RW_WRITEONLY,
+	BM_TRANS_RW_RWBOTH
+};
+
+
+#define EMI_BMID_MASK				(0xFFFF)
+#define BM_COUNTER_MAX				(21)
+
+#define BM_REQ_OK						(0)
+#define BM_ERR_WRONG_REQ				(-1)
+#define BM_ERR_OVERRUN					(-2)
+
+#define BM_TTYPE1_16_ENABLE			(0)
+#define BM_TTYPE1_16_DISABLE			(-1)
+#define BM_TTYPE17_21_ENABLE			(0)
+#define BM_TTYPE17_21_DISABLE			(-1)
+
+#if defined(CONFIG_MTK_TINYSYS_SSPM_SUPPORT) && defined(ONDIEMET_SUPPORT)
+enum BM_EMI_IPI_Type {
+	SET_BASE_EMI = 0x0,
+	SET_EBM_CONFIGS1 = 0x7,
+	SET_EBM_CONFIGS2 = 0x8,
+	SET_REGISTER_CB = 0x9,
+};
+#endif
+
+#define	EMI_OFF			0x0000
+#define EMI_BMEN		(0x400-EMI_OFF)
+#define EMI_MSEL		(0x440-EMI_OFF)
+#define EMI_MSEL2		(0x468-EMI_OFF)
+#define EMI_MSEL3		(0x470-EMI_OFF)
+#define EMI_MSEL4		(0x478-EMI_OFF)
+#define EMI_MSEL5		(0x480-EMI_OFF)
+#define EMI_MSEL6		(0x488-EMI_OFF)
+#define EMI_MSEL7		(0x490-EMI_OFF)
+#define EMI_MSEL8		(0x498-EMI_OFF)
+#define EMI_MSEL9		(0x4A0-EMI_OFF)
+#define EMI_MSEL10		(0x4A8-EMI_OFF)
+
+#define EMI_BMID0		(0x4B0-EMI_OFF)
+#define EMI_BMID1		(0x4B4-EMI_OFF)
+#define EMI_BMID2		(0x4B8-EMI_OFF)
+#define EMI_BMID3		(0x4BC-EMI_OFF)
+#define EMI_BMID4		(0x4C0-EMI_OFF)
+#define EMI_BMID5		(0x4C4-EMI_OFF)
+#define EMI_BMID6		(0x4C8-EMI_OFF)
+#define EMI_BMID7		(0x4CC-EMI_OFF)
+#define EMI_BMID8		(0x4D0-EMI_OFF)
+#define EMI_BMID9		(0x4D4-EMI_OFF)
+#define EMI_BMID10		(0x4D8-EMI_OFF)
+
+#define EMI_BMEN1		(0x4E0-EMI_OFF)
+#define EMI_BMEN2		(0x4E8-EMI_OFF)
+#define EMI_BMRW0		(0x4F8-EMI_OFF)
+#define EMI_BMRW1		(0x4FC-EMI_OFF)
+
+
+extern int MET_BM_Init(void);
+extern void MET_BM_DeInit(void);
+extern void MET_BM_SaveCfg(void);
+extern void MET_BM_RestoreCfg(void);
+extern int MET_BM_SetMonitorCounter(const unsigned int counter_num,
+				    const unsigned int master, const unsigned int trans_type);
+extern int MET_BM_SetTtypeCounterRW(unsigned int bmrw0_val, unsigned int bmrw1_val);
+extern int MET_BM_Set_WsctTsct_id_sel(unsigned int counter_num, unsigned int enable);
+extern int MET_BM_SetbusID_En(const unsigned int counter_num,
+			      const unsigned int enable);
+extern int MET_BM_SetbusID(const unsigned int counter_num,
+			   const unsigned int id);
+extern int MET_BM_SetUltraHighFilter(const unsigned int counter_num, const unsigned int enable);
+extern int MET_BM_SetLatencyCounter(unsigned int enable);
+#endif				/* !__MT_MET_EMI_BM_H__ */
diff --git a/src/devtools/met-driver/4.9/common/mtk_gpu_metmonitor.c b/src/devtools/met-driver/4.9/common/mtk_gpu_metmonitor.c
new file mode 100644
index 0000000..fadf640
--- /dev/null
+++ b/src/devtools/met-driver/4.9/common/mtk_gpu_metmonitor.c
@@ -0,0 +1,761 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <asm/page.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/syscalls.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <asm/uaccess.h>
+#include <linux/hrtimer.h>
+
+#include "met_drv.h"
+#include "trace.h"
+
+#include "mtk_gpu_metmonitor.h"
+#include "core_plf_init.h"
+#include "core_plf_trace.h"
+
+
+/*
+ * define if the hal implementation might re-schedule, cannot run inside softirq
+ * undefine this is better for sampling jitter if HAL support it
+ */
+#undef GPU_HAL_RUN_PREMPTIBLE
+
+#ifdef GPU_HAL_RUN_PREMPTIBLE
+static struct delayed_work gpu_dwork;
+static struct delayed_work gpu_pwr_dwork;
+#endif
+
+/* the mt_gpufreq_get_thermal_limit_freq use mutex_lock to do its job */
+/* so, change the gpu-dvfs implementation to dwork */
+static struct delayed_work gpu_dvfs_dwork;
+
+/*
+ * GPU monitor HAL comes from alps\mediatek\kernel\include\linux\mtk_gpu_utility.h
+ *
+ * mtk_get_gpu_memory_usage(unsigned int* pMemUsage) in unit of bytes
+ *
+ * mtk_get_gpu_xxx_loading are in unit of %
+*/
+
+enum MET_GPU_PROFILE_INDEX {
+	eMET_GPU_LOADING = 0,
+	eMET_GPU_BLOCK_LOADING,	/* 1 */
+	eMET_GPU_IDLE_LOADING,	/* 2 */
+	eMET_GPU_PROFILE_CNT
+};
+
+static unsigned long g_u4AvailableInfo;
+
+noinline void GPU_Loading(unsigned char cnt, unsigned int *value)
+{
+	switch (cnt) {
+	case 1:
+		MET_TRACE("%u\n", value[0]);
+		break;
+	case 2:
+		MET_TRACE("%u,%u\n", value[0], value[1]);
+		break;
+	case 3:
+		MET_TRACE("%u,%u,%u\n", value[0], value[1], value[2]);
+		break;
+	case 4:
+		MET_TRACE("%u,%u,%u,%u\n", value[0], value[1], value[2], value[3]);
+		break;
+	default:
+		break;
+	}
+
+}
+
+noinline void GPU_Sub_Loading(unsigned int loading)
+{
+	MET_TRACE("%u\n", loading);
+}
+
+noinline void GPU_3D_Fences_Count(int count)
+{
+	MET_TRACE("%d\n", count);
+}
+
+#ifdef GPU_HAL_RUN_PREMPTIBLE
+static void gpu_GPULoading(struct work_struct *work)
+{
+	unsigned int	pu4Value[eMET_GPU_PROFILE_CNT];
+	unsigned long	u4Index = 0;
+	unsigned int	loading = 0;
+	int		count = 0;
+
+	memset(pu4Value, 0x00, eMET_GPU_PROFILE_CNT);
+	if ((1 << eMET_GPU_LOADING) & g_u4AvailableInfo) {
+		if (mtk_get_gpu_loading_symbol && mtk_get_gpu_loading_symbol(&pu4Value[u4Index]))
+			u4Index += 1;
+	}
+
+	if ((1 << eMET_GPU_BLOCK_LOADING) & g_u4AvailableInfo) {
+		if (mtk_get_gpu_block_symbol && mtk_get_gpu_block_symbol(&pu4Value[u4Index]))
+			u4Index += 1;
+	}
+
+	if ((1 << eMET_GPU_IDLE_LOADING) & g_u4AvailableInfo) {
+		if (mtk_get_gpu_idle_symbol && mtk_get_gpu_idle_symbol(&pu4Value[u4Index]))
+			u4Index += 1;
+	}
+
+	if (g_u4AvailableInfo)
+		GPU_Loading(u4Index, pu4Value);
+
+	if (mtk_get_gpu_sub_loading_symbol && mtk_get_gpu_sub_loading_symbol(&loading))
+		GPU_Sub_Loading(loading);
+
+	if (mtk_get_3D_fences_count_symbol && mtk_get_3D_fences_count_symbol(&count))
+		GPU_3D_Fences_Count(count);
+}
+#else
+static void gpu_GPULoading(unsigned long long stamp, int cpu)
+{
+	unsigned int	pu4Value[eMET_GPU_PROFILE_CNT];
+	unsigned long	u4Index = 0;
+	unsigned int	loading = 0;
+	int		count = 0;
+
+	memset(pu4Value, 0x00, eMET_GPU_PROFILE_CNT);
+	if ((1 << eMET_GPU_LOADING) & g_u4AvailableInfo) {
+		if (mtk_get_gpu_loading_symbol) {
+			mtk_get_gpu_loading_symbol(&pu4Value[u4Index]);
+			u4Index += 1;
+		}
+	}
+
+	if ((1 << eMET_GPU_BLOCK_LOADING) & g_u4AvailableInfo) {
+		if (mtk_get_gpu_block_symbol) {
+			mtk_get_gpu_block_symbol(&pu4Value[u4Index]);
+			u4Index += 1;
+		}
+	}
+
+	if ((1 << eMET_GPU_IDLE_LOADING) & g_u4AvailableInfo) {
+		if (mtk_get_gpu_idle_symbol) {
+			mtk_get_gpu_idle_symbol(&pu4Value[u4Index]);
+			u4Index += 1;
+		}
+	}
+
+	if (g_u4AvailableInfo)
+		GPU_Loading(u4Index, pu4Value);
+
+	if (mtk_get_gpu_sub_loading_symbol) {
+		mtk_get_gpu_sub_loading_symbol(&loading);
+		GPU_Sub_Loading(loading);
+	}
+
+	if (mtk_get_3D_fences_count_symbol) {
+		mtk_get_3D_fences_count_symbol(&count);
+		GPU_3D_Fences_Count(count);
+	}
+}
+#endif
+
+static void gpu_monitor_start(void)
+{
+	if (mtk_get_gpu_loading_symbol)
+		g_u4AvailableInfo |= (1 << eMET_GPU_LOADING);
+	if (mtk_get_gpu_block_symbol)
+		g_u4AvailableInfo |= (1 << eMET_GPU_BLOCK_LOADING);
+	if (mtk_get_gpu_idle_symbol)
+		g_u4AvailableInfo |= (1 << eMET_GPU_IDLE_LOADING);
+
+#ifdef GPU_HAL_RUN_PREMPTIBLE
+	INIT_DELAYED_WORK(&gpu_dwork, gpu_GPULoading);
+#endif
+}
+
+#ifdef GPU_HAL_RUN_PREMPTIBLE
+static void gpu_monitor_stop(void)
+{
+	cancel_delayed_work_sync(&gpu_dwork);
+}
+
+static void GPULoadingNotify(unsigned long long stamp, int cpu)
+{
+	schedule_delayed_work(&gpu_dwork, 0);
+}
+#endif
+
+static char help[] =
+	"  --gpu				monitor gpu status\n";
+static int gpu_status_print_help(char *buf, int len)
+{
+	return snprintf(buf, PAGE_SIZE, help);
+}
+
+static char g_pComGPUStatusHeader[] =
+	"met-info [000] 0.0: met_gpu_loading_header: ";
+static int gpu_status_print_header(char *buf, int len)
+{
+	int ret = 0;
+
+	ret = snprintf(buf, PAGE_SIZE, "%s", g_pComGPUStatusHeader);
+
+	if ((1 << eMET_GPU_LOADING) & g_u4AvailableInfo)
+		ret += snprintf(buf+ret, PAGE_SIZE-ret, "%s", "Loading,");
+
+	if ((1 << eMET_GPU_BLOCK_LOADING) & g_u4AvailableInfo)
+		ret += snprintf(buf+ret, PAGE_SIZE-ret, "%s", "Blcok,");
+
+	if ((1 << eMET_GPU_IDLE_LOADING) & g_u4AvailableInfo)
+		ret += snprintf(buf+ret, PAGE_SIZE-ret, "%s", "Idle");
+
+	ret += snprintf(buf+ret, PAGE_SIZE-ret, "%s", "\n");
+
+	ret += snprintf(buf+ret, PAGE_SIZE-ret,
+			"met-info [000] 0.0: met_gpu_sub_loading_header: Loading\n");
+	ret += snprintf(buf+ret, PAGE_SIZE-ret,
+			"met-info [000] 0.0: met_gpu_3d_fences_count_header: Count\n");
+
+	return ret;
+}
+
+struct metdevice met_gpu = {
+	.name			= "gpu",
+	.owner			= THIS_MODULE,
+	.type			= MET_TYPE_BUS,
+	.cpu_related		= 0,
+	.start			= gpu_monitor_start,
+	.mode			= 0,
+	.polling_interval	= 1,	/* ms */
+#ifdef GPU_HAL_RUN_PREMPTIBLE
+	.timed_polling		= GPULoadingNotify,
+	.stop			= gpu_monitor_stop,
+#else
+	.timed_polling		= gpu_GPULoading,
+#endif
+	.print_help		= gpu_status_print_help,
+	.print_header		= gpu_status_print_header,
+};
+
+/*
+ * GPU DVFS Monitor
+ */
+#define	MTK_GPU_DVFS_TYPE_ITEM(type)	#type,
+static char *gpu_dvfs_type_name[] = MTK_GPU_DVFS_TYPE_LIST;
+#undef	MTK_GPU_DVFS_TYPE_ITEM
+
+static MTK_GPU_DVFS_TYPE gpu_dvfs_type_prev;
+static unsigned long gpu_dvfs_type_freq_prev;
+static unsigned int gpu_dvfs_type_freq[ARRAY_SIZE(gpu_dvfs_type_name)];
+
+noinline void GPU_DVFS(unsigned int Freq, unsigned int ThermalLimit,
+			unsigned long CustomBoost, unsigned long CustomUpbound)
+{
+	MET_TRACE("%u,%u,%lu,%lu\n", Freq, ThermalLimit, CustomBoost, CustomUpbound);
+}
+
+noinline void GPU_DVFS_TYPE(void)
+{
+	char	*SOB, *EOB;
+
+	MET_TRACE_GETBUF(&SOB, &EOB);
+	EOB = ms_formatD_EOL(EOB, ARRAY_SIZE(gpu_dvfs_type_freq), gpu_dvfs_type_freq);
+	MET_TRACE_PUTBUF(SOB, EOB);
+}
+
+noinline void GPU_DVFS_VSYNC(unsigned long freq)
+{
+	MET_TRACE("%lu\n", freq);
+}
+
+noinline void GPU_VSYNC_OFFSET_STATUS(unsigned int event_status, unsigned int debug_status)
+{
+	MET_TRACE("%u,%u\n", event_status, debug_status);
+}
+
+static void gpu_dvfs(void)
+{
+	unsigned int		freq = 0;
+	unsigned int		ThermalLimit = 0;
+	MTK_GPU_DVFS_TYPE	peType;
+	unsigned long		pulFreq = 0;
+	unsigned long		CustomBoost = 0;
+	unsigned long		CustomUpbound = 0;
+	unsigned int		event_status = 0;
+	unsigned int		debug_status = 0;
+
+	freq = mt_gpufreq_get_cur_freq_symbol ? mt_gpufreq_get_cur_freq_symbol() : 0;
+	ThermalLimit = mt_gpufreq_get_thermal_limit_freq_symbol ? mt_gpufreq_get_thermal_limit_freq_symbol() : 0;
+	if (mtk_get_custom_boost_gpu_freq_symbol)
+		mtk_get_custom_boost_gpu_freq_symbol(&CustomBoost);
+	if (mtk_get_custom_upbound_gpu_freq_symbol)
+		mtk_get_custom_upbound_gpu_freq_symbol(&CustomUpbound);
+	GPU_DVFS(freq, ThermalLimit, CustomBoost, CustomUpbound);
+
+	/* gpu dvfs type */
+	if (mtk_get_gpu_dvfs_from_symbol && mtk_get_gpu_dvfs_from_symbol(&peType, &pulFreq)) {
+		if (gpu_dvfs_type_prev != peType || gpu_dvfs_type_freq_prev != pulFreq) {
+			gpu_dvfs_type_freq[gpu_dvfs_type_prev] = 0;
+			gpu_dvfs_type_prev = peType;
+			gpu_dvfs_type_freq_prev = pulFreq;
+			gpu_dvfs_type_freq[gpu_dvfs_type_prev] = gpu_dvfs_type_freq_prev;
+			GPU_DVFS_TYPE();
+		}
+	}
+
+	if (mtk_get_vsync_based_target_freq_symbol && mtk_get_vsync_based_target_freq_symbol(&pulFreq))
+		GPU_DVFS_VSYNC(pulFreq);
+
+	if (mtk_get_vsync_offset_event_status_symbol && mtk_get_vsync_offset_debug_status_symbol) {
+		if (mtk_get_vsync_offset_event_status_symbol(&event_status)
+		    && mtk_get_vsync_offset_debug_status_symbol(&debug_status)) {
+			GPU_VSYNC_OFFSET_STATUS(event_status, debug_status);
+		}
+	}
+}
+
+static void gpu_dvfs_work(struct work_struct *work)
+{
+	gpu_dvfs();
+}
+
+static void gpu_dvfs_monitor_start(void)
+{
+	gpu_dvfs();
+	INIT_DELAYED_WORK(&gpu_dvfs_dwork, gpu_dvfs_work);
+}
+
+static void gpu_dvfs_monitor_stop(void)
+{
+	cancel_delayed_work_sync(&gpu_dvfs_dwork);
+	gpu_dvfs();
+}
+
+static void gpu_dvfs_monitor_polling(unsigned long long stamp, int cpu)
+{
+	schedule_delayed_work(&gpu_dvfs_dwork, 0);
+}
+
+static int gpu_dvfs_print_help(char *buf, int len)
+{
+	return snprintf(buf, PAGE_SIZE,
+			"  --gpu-dvfs				monitor gpu freq\n");
+}
+
+static int gpu_dvfs_print_header(char *buf, int len)
+{
+	int ret = 0;
+	int i = 0;
+
+	ret = snprintf(buf, PAGE_SIZE,
+			"met-info [000] 0.0: met_gpu_dvfs_header: ");
+	ret += snprintf(buf+ret, PAGE_SIZE-ret,
+			"Freq(kHz),ThermalLimit(kHz),CustomBoost,CustomUpbound\n");
+
+	ret += snprintf(buf+ret, PAGE_SIZE-ret,
+			"met-info [000] 0.0: met_gpu_dvfs_type_header: %s", gpu_dvfs_type_name[0]);
+	for (i = 1; i < ARRAY_SIZE(gpu_dvfs_type_name); i++)
+		ret += snprintf(buf+ret, PAGE_SIZE-ret, ",%s", gpu_dvfs_type_name[i]);
+	ret += snprintf(buf+ret, PAGE_SIZE-ret, "%s", "\n");
+
+	ret += snprintf(buf+ret, PAGE_SIZE-ret,
+			"met-info [000] 0.0: met_gpu_dvfs_vsync_header: VSYNC Based Freq\n");
+	ret += snprintf(buf+ret, PAGE_SIZE-ret,
+			"met-info [000] 0.0: met_gpu_vsync_offset_status_header: Event Status,Debug Status\n");
+
+	return ret;
+}
+
+struct metdevice met_gpudvfs = {
+	.name			= "gpu-dvfs",
+	.owner			= THIS_MODULE,
+	.type			= MET_TYPE_BUS,
+	.cpu_related		= 0,
+	.start			= gpu_dvfs_monitor_start,
+	.stop			= gpu_dvfs_monitor_stop,
+	.polling_interval	= 1,	/* ms */
+	.timed_polling		= gpu_dvfs_monitor_polling,
+	.print_help		= gpu_dvfs_print_help,
+	.print_header		= gpu_dvfs_print_header,
+	.ondiemet_mode		= 0,
+};
+
+/*
+ * GPU MEM monitor
+ */
+static unsigned long g_u4MemProfileIsOn;
+
+static void gpu_mem_monitor_start(void)
+{
+	if (!mtk_get_gpu_memory_usage_symbol)
+		return;
+
+#if 0
+	if (mtk_get_gpu_memory_usage_symbol(&u4Value))
+		g_u4MemProfileIsOn = 1;
+#endif
+	g_u4MemProfileIsOn = 1;
+}
+
+noinline void GPU_MEM(unsigned long long stamp, int cpu)
+{
+	unsigned int u4Value = 0;
+
+	if (!mtk_get_gpu_memory_usage_symbol)
+		return;
+
+	if (g_u4MemProfileIsOn == 1) {
+		mtk_get_gpu_memory_usage_symbol(&u4Value);
+		MET_TRACE("%d\n", u4Value);
+	}
+}
+
+static void gpu_mem_monitor_stop(void)
+{
+	g_u4MemProfileIsOn = 0;
+}
+
+static char help_mem[] =
+	"  --gpu-mem				monitor gpu memory status\n";
+static int gpu_mem_status_print_help(char *buf, int len)
+{
+	return snprintf(buf, PAGE_SIZE, help_mem);
+}
+
+static char g_pComGPUMemHeader[] =
+	"met-info [000] 0.0: met_gpu_mem_header: Usage\n";
+static int gpu_mem_status_print_header(char *buf, int len)
+{
+	return snprintf(buf, PAGE_SIZE, g_pComGPUMemHeader);
+}
+
+struct metdevice met_gpumem = {
+	.name			= "gpu-mem",
+	.owner			= THIS_MODULE,
+	.type			= MET_TYPE_BUS,
+	.cpu_related		= 0,
+	.start			= gpu_mem_monitor_start,
+	.stop			= gpu_mem_monitor_stop,
+	.mode			= 0,
+	.polling_interval	= 1,	/* ms */
+	.timed_polling		= GPU_MEM,
+	.print_help		= gpu_mem_status_print_help,
+	.print_header		= gpu_mem_status_print_header,
+};
+
+/*
+ * GPU power monitor
+ */
+static unsigned long g_u4PowerProfileIsOn;
+
+#ifdef GPU_HAL_RUN_PREMPTIBLE
+noinline void GPU_Power(struct work_struct *work)
+{
+	unsigned int u4Value = 0;
+
+	if (!mtk_get_gpu_power_loading_symbol)
+		return;
+
+	mtk_get_gpu_power_loading_symbol(&u4Value);
+	MET_TRACE("%d\n", u4Value);
+}
+
+static void GPU_PowerNotify(unsigned long long stamp, int cpu)
+{
+	if (g_u4PowerProfileIsOn == 1)
+		schedule_delayed_work(&gpu_pwr_dwork, 0);
+}
+#else
+noinline void GPU_Power(unsigned long long stamp, int cpu)
+{
+	unsigned int u4Value = 0;
+
+	if (!mtk_get_gpu_power_loading_symbol)
+		return;
+
+	if (g_u4PowerProfileIsOn == 1) {
+		mtk_get_gpu_power_loading_symbol(&u4Value);
+		MET_TRACE("%d\n", u4Value);
+	}
+}
+#endif
+
+static void gpu_Power_monitor_start(void)
+{
+	if (!mtk_get_gpu_power_loading_symbol)
+		return;
+
+#if 0
+	if (mtk_get_gpu_power_loading_symbol(&u4Value))
+		g_u4PowerProfileIsOn = 1;
+#endif
+	g_u4PowerProfileIsOn = 1;
+#ifdef GPU_HAL_RUN_PREMPTIBLE
+	INIT_DELAYED_WORK(&gpu_pwr_dwork, GPU_Power);
+#endif
+}
+
+static void gpu_Power_monitor_stop(void)
+{
+	g_u4PowerProfileIsOn = 0;
+
+#ifdef GPU_HAL_RUN_PREMPTIBLE
+	cancel_delayed_work_sync(&gpu_pwr_dwork);
+#endif
+}
+
+static char help_pwr[] =
+	"  --gpu-pwr				monitor gpu power status\n";
+static int gpu_Power_status_print_help(char *buf, int len)
+{
+	return snprintf(buf, PAGE_SIZE, help_pwr);
+}
+
+static char g_pComGPUPowerHeader[] =
+	"met-info [000] 0.0: met_gpu_power_header: Loading\n";
+static int gpu_Power_status_print_header(char *buf, int len)
+{
+	return snprintf(buf, PAGE_SIZE, g_pComGPUPowerHeader);
+}
+
+struct metdevice met_gpupwr = {
+	.name			= "gpu-pwr",
+	.owner			= THIS_MODULE,
+	.type			= MET_TYPE_BUS,
+	.cpu_related		= 0,
+	.start			= gpu_Power_monitor_start,
+	.stop			= gpu_Power_monitor_stop,
+	.mode			= 0,
+	.polling_interval	= 1,	/* ms */
+#ifdef GPU_HAL_RUN_PREMPTIBLE
+	.timed_polling		= GPU_PowerNotify,
+#else
+	.timed_polling		= GPU_Power,
+#endif
+	.print_help		= gpu_Power_status_print_help,
+	.print_header		= gpu_Power_status_print_header,
+};
+
+
+/*
+ * GPU PMU
+ */
+#define UNUSE_ARG(arg) ((void)arg)
+
+#ifdef GPU_HAL_RUN_PREMPTIBLE
+static struct delayed_work gpu_pmu_dwork;
+#endif
+
+#define MAX_PMU_STR_LEN (1024 * 5)
+
+static const char help_pmu[] = "  --gpu-pmu				monitor gpu pmu status";
+static const char header_pmu[] = "met-info [000] 0.0: met_gpu_pmu_header: ";
+static char pmu_str[MAX_PMU_STR_LEN];
+static int pmu_cnt;
+static int gpu_pwr_status = 1;
+static GPU_PMU *pmu_list;
+
+
+noinline void GPU_PMU_RAW(
+	unsigned long long stamp,
+	int cpu)
+{
+	bool ret;
+	int i = 0;
+	char *SOB, *EOB;
+	unsigned int value[pmu_cnt];
+
+	if (stamp == 0 && cpu == 0) {
+		for (i = 0; i < pmu_cnt; i++)
+			value[i] = 0;
+
+		MET_TRACE_GETBUF(&SOB, &EOB);
+		EOB = ms_formatH(EOB, pmu_cnt, value);
+		MET_TRACE_PUTBUF(SOB, EOB);
+		return;
+	}
+
+	if (mtk_get_gpu_pmu_swapnreset_symbol) {
+		ret = mtk_get_gpu_pmu_swapnreset_symbol(pmu_list, pmu_cnt);
+		if (ret) {
+			for (i = 0; i < pmu_cnt; i++) {
+				if (pmu_list[i].overflow)
+					pmu_list[i].value = 0xFFFFFFFF;
+				value[i] = pmu_list[i].value;
+			}
+			MET_TRACE_GETBUF(&SOB, &EOB);
+			EOB = ms_formatH(EOB, pmu_cnt, value);
+			MET_TRACE_PUTBUF(SOB, EOB);
+		}
+	}
+}
+
+static int create_gpu_pmu_list(void)
+{
+	int ret = 0;
+	int len = 0;
+	int i = 0;
+
+	if (mtk_get_gpu_pmu_init_symbol) {
+		ret = mtk_get_gpu_pmu_init(NULL, 0, &pmu_cnt);
+		if (pmu_cnt == 0 || ret == 0)
+			return 0;
+	} else
+		return 0;
+
+	pmu_list = kmalloc_array(pmu_cnt, sizeof(GPU_PMU), GFP_KERNEL);
+	if (pmu_list) {
+		memset(pmu_list, 0x00, sizeof(GPU_PMU)*pmu_cnt);
+		ret = mtk_get_gpu_pmu_init(pmu_list, pmu_cnt, NULL);
+
+		memset(pmu_str, 0x00, MAX_PMU_STR_LEN);
+		len = snprintf(pmu_str, MAX_PMU_STR_LEN, "%s", pmu_list[0].name);
+		for (i = 1; i < pmu_cnt; i++)
+			len += snprintf(pmu_str + len, MAX_PMU_STR_LEN - len, ",%s", pmu_list[i].name);
+
+		/*
+		* dummy read in order to reset GPU PMU counter
+		*/
+		if (mtk_get_gpu_pmu_swapnreset_symbol)
+			mtk_get_gpu_pmu_swapnreset_symbol(pmu_list, pmu_cnt);
+	}
+
+	return ret;
+}
+
+static void delete_gpu_pmu_list(void)
+{
+	kfree(pmu_list);
+	pmu_list = NULL;
+	pmu_cnt = 0;
+}
+
+static void gpu_pwr_status_cb(int on)
+{
+	MET_TRACE("on = %d\n", on);
+
+	if (on == 1) {
+		/*
+		* dummy read in order to reset GPU PMU counter
+		*/
+		if (mtk_get_gpu_pmu_swapnreset_symbol)
+			mtk_get_gpu_pmu_swapnreset_symbol(pmu_list, pmu_cnt);
+
+	} else {
+		GPU_PMU_RAW(1, 0);
+		GPU_PMU_RAW(0, 0);
+	}
+
+	gpu_pwr_status = on;
+}
+
+static void gpu_pmu_monitor_start(void)
+{
+	int ret;
+
+	ret = create_gpu_pmu_list();
+	if (ret == 0)
+		return;
+
+	if (mtk_register_gpu_power_change_symbol)
+		mtk_register_gpu_power_change_symbol("met_gpu", gpu_pwr_status_cb);
+
+#ifdef GPU_HAL_RUN_PREMPTIBLE
+	INIT_DELAYED_WORK(&gpu_pmu_dwork, GPU_PMU_RAW);
+#endif
+}
+
+static void gpu_pmu_monitor_stop(void)
+{
+#ifdef GPU_HAL_RUN_PREMPTIBLE
+	cancel_delayed_work_sync(&gpu_pmu_dwork);
+#endif
+
+	if (mtk_unregister_gpu_power_change_symbol)
+		mtk_unregister_gpu_power_change_symbol("met_gpu");
+	delete_gpu_pmu_list();
+
+#if 0
+	/* stop polling counter */
+	if (mtk_get_gpu_pmu_swapnreset_stop_symbol)
+		mtk_get_gpu_pmu_swapnreset_stop_symbol();
+	/* release resource */
+	if (mtk_get_gpu_pmu_deinit_symbol)
+		mtk_get_gpu_pmu_deinit_symbol();
+#endif
+}
+
+#ifdef GPU_HAL_RUN_PREMPTIBLE
+static void gpu_pmu_timed_polling_notify(
+	unsigned long long stamp,
+	int cpu)
+{
+	UNUSE_ARG(stamp);
+	UNUSE_ARG(cpu);
+
+	if (gpu_pwr_status == 1)
+		schedule_delayed_work(&gpu_pmu_dwork, 0);
+}
+#else
+static void gpu_pmu_timed_polling(
+	unsigned long long stamp,
+	int cpu)
+{
+	UNUSE_ARG(stamp);
+	UNUSE_ARG(cpu);
+
+	if (gpu_pwr_status == 1)
+		GPU_PMU_RAW(stamp, cpu);
+}
+#endif
+
+static int gpu_pmu_print_help(
+	char *buf,
+	int len)
+{
+	UNUSE_ARG(len);
+	return snprintf(buf, PAGE_SIZE, "%s\n", help_pmu);
+}
+
+static int gpu_pmu_print_header(
+	char *buf,
+	int len)
+{
+	len = 0;
+
+	len = snprintf(buf, PAGE_SIZE, "%s", header_pmu);
+	len += snprintf(buf + len, PAGE_SIZE - len, "%s\n", pmu_str);
+
+	return len;
+}
+
+struct metdevice met_gpu_pmu = {
+	.name			= "gpu-pmu",
+	.owner			= THIS_MODULE,
+	.type			= MET_TYPE_PMU,
+	.cpu_related		= 0,
+	.start			= gpu_pmu_monitor_start,
+	.stop			= gpu_pmu_monitor_stop,
+	.mode			= 0,
+	.polling_interval	= 1,	/* ms */
+#ifdef GPU_HAL_RUN_PREMPTIBLE
+	.timed_polling		= gpu_pmu_timed_polling_notify,
+#else
+	.timed_polling		= gpu_pmu_timed_polling,
+#endif
+	.print_help		= gpu_pmu_print_help,
+	.print_header		= gpu_pmu_print_header,
+};
diff --git a/src/devtools/met-driver/4.9/common/mtk_gpu_metmonitor.h b/src/devtools/met-driver/4.9/common/mtk_gpu_metmonitor.h
new file mode 100644
index 0000000..069c534
--- /dev/null
+++ b/src/devtools/met-driver/4.9/common/mtk_gpu_metmonitor.h
@@ -0,0 +1,18 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MT_GPU_METMONITOR_H_
+
+#define _MT_GPU_METMONITOR_H_
+
+#endif				/* _MT_GPU_METMONITOR_H_ */
diff --git a/src/devtools/met-driver/4.9/common/mtk_typedefs.h b/src/devtools/met-driver/4.9/common/mtk_typedefs.h
new file mode 100644
index 0000000..4424479
--- /dev/null
+++ b/src/devtools/met-driver/4.9/common/mtk_typedefs.h
@@ -0,0 +1,318 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MT_TYPEDEFS_H__
+
+/*
+ *  KOBJ ATTR Manipulations Macros
+ */
+
+#define KOBJ_ITEM_LIST(args...)		args
+
+/*
+ * Declaring KOBJ attributes
+ */
+#define DECLARE_KOBJ_ATTR(attr_name) \
+	static struct kobj_attribute attr_name##_attr = \
+		__ATTR(attr_name, 0664, attr_name##_show, attr_name##_store)
+
+#define DECLARE_KOBJ_ATTR_RO(attr_name) \
+	static struct kobj_attribute attr_name##_attr = \
+		__ATTR_RO(attr_name)
+
+/*
+ * Declaring KOBJ attributes with integer variable
+ */
+/* normal version */
+#define DECLARE_KOBJ_ATTR_SHOW_INT(attr_name, var_name) \
+	static ssize_t attr_name##_show( \
+		struct kobject *kobj, \
+		struct kobj_attribute *attr, \
+		char *buf) \
+	{ \
+		return snprintf(buf, PAGE_SIZE, "%d\n", var_name); \
+	}
+#define DECLARE_KOBJ_ATTR_STORE_INT(attr_name, var_name) \
+	static ssize_t attr_name##_store( \
+		struct kobject *kobj, \
+		struct kobj_attribute *attr, \
+		const char *buf, \
+		size_t n) \
+	{ \
+		int	val; \
+		if (kstrtoint(buf, 0, &val) != 0) { \
+			return -EINVAL; \
+		} \
+		var_name = val; \
+		return n; \
+	}
+#define DECLARE_KOBJ_ATTR_INT(attr_name, var_name) \
+	DECLARE_KOBJ_ATTR_SHOW_INT(attr_name, var_name) \
+	DECLARE_KOBJ_ATTR_STORE_INT(attr_name, var_name) \
+	DECLARE_KOBJ_ATTR(attr_name)
+#define DECLARE_KOBJ_ATTR_RO_INT(attr_name, var_name) \
+	DECLARE_KOBJ_ATTR_SHOW_INT(attr_name, var_name) \
+	DECLARE_KOBJ_ATTR_RO(attr_name)
+
+/* cond-check version */
+#define DECLARE_KOBJ_ATTR_STORE_INT_CHECK(attr_name, var_name, cond) \
+	static ssize_t attr_name##_store( \
+		struct kobject *kobj, \
+		struct kobj_attribute *attr, \
+		const char *buf, \
+		size_t n) \
+	{ \
+		int	var_name##temp = var_name; \
+		if (kstrtoint(buf, 0, &var_name) != 0) { \
+			var_name = var_name##temp; \
+			return -EINVAL; \
+		} \
+		if (cond) { \
+			return n; \
+		} else { \
+			var_name = var_name##temp; \
+			return -EINVAL; \
+		} \
+	}
+/* Note: the name of val in cond can NOT be the same as var_name */
+#define DECLARE_KOBJ_ATTR_INT_CHECK(attr_name, var_name, cond) \
+	DECLARE_KOBJ_ATTR_SHOW_INT(attr_name, var_name) \
+	DECLARE_KOBJ_ATTR_STORE_INT_CHECK(attr_name, var_name, cond) \
+	DECLARE_KOBJ_ATTR(attr_name)
+
+/* helper procedure version */
+#define DECLARE_KOBJ_ATTR_SHOW_INT_PROC(attr_name, var_name, func) \
+	static ssize_t attr_name##_show( \
+		struct kobject *kobj, \
+		struct kobj_attribute *attr, \
+		char *buf) \
+	{ \
+		return func(kobj, attr, buf, var_name); \
+	}
+#define DECLARE_KOBJ_ATTR_STORE_INT_PROC(attr_name, var_name, func) \
+	static ssize_t attr_name##_store( \
+		struct kobject *kobj, \
+		struct kobj_attribute *attr, \
+		const char *buf, \
+		size_t n) \
+	{ \
+		return func(kobj, attr, buf, n, &(var_name)); \
+	}
+#define DECLARE_KOBJ_ATTR_INT_PROC(attr_name, var_name, show, store) \
+	DECLARE_KOBJ_ATTR_SHOW_INT_PROC(attr_name, var_name, show) \
+	DECLARE_KOBJ_ATTR_STORE_INT_PROC(attr_name, var_name, store) \
+	DECLARE_KOBJ_ATTR(attr_name)
+
+/*
+ * Declaring KOBJ attributes with integer(hex) variable
+ */
+/* normal version */
+#define DECLARE_KOBJ_ATTR_SHOW_HEX(attr_name, var_name) \
+	static ssize_t attr_name##_show( \
+		struct kobject *kobj, \
+		struct kobj_attribute *attr, \
+		char *buf) \
+	{ \
+		return snprintf(buf, PAGE_SIZE, "%x\n", var_name); \
+	}
+#define DECLARE_KOBJ_ATTR_STORE_HEX(attr_name, var_name) \
+	static ssize_t attr_name##_store( \
+		struct kobject *kobj, \
+		struct kobj_attribute *attr, \
+		const char *buf, \
+		size_t n) \
+	{ \
+		unsigned int	val; \
+		if (kstrtouint(buf, 0, &val) != 0) { \
+			return -EINVAL; \
+		} \
+		var_name = val; \
+		return n; \
+	}
+#define DECLARE_KOBJ_ATTR_HEX(attr_name, var_name) \
+	DECLARE_KOBJ_ATTR_SHOW_HEX(attr_name, var_name) \
+	DECLARE_KOBJ_ATTR_STORE_HEX(attr_name, var_name) \
+	DECLARE_KOBJ_ATTR(attr_name)
+#define DECLARE_KOBJ_ATTR_RO_HEX(attr_name, var_name) \
+	DECLARE_KOBJ_ATTR_SHOW_HEX(attr_name, var_name) \
+	DECLARE_KOBJ_ATTR_RO(attr_name)
+
+/* cond-check version */
+#define DECLARE_KOBJ_ATTR_STORE_HEX_CHECK(attr_name, var_name, cond) \
+	static ssize_t attr_name##_store( \
+		struct kobject *kobj, \
+		struct kobj_attribute *attr, \
+		const char *buf, \
+		size_t n) \
+	{ \
+		unsigned int	var_name##temp = var_name; \
+		if (kstrtouint(buf, 0, &var_name) != 0) { \
+			var_name = var_name##temp; \
+			return -EINVAL; \
+		} \
+		if (cond) { \
+			return n; \
+		} else { \
+			var_name = var_name##temp; \
+			return -EINVAL; \
+		} \
+	}
+/* Note: the name of val in cond can NOT be the same as var_name */
+#define DECLARE_KOBJ_ATTR_HEX_CHECK(attr_name, var_name, cond) \
+	DECLARE_KOBJ_ATTR_SHOW_HEX(attr_name, var_name) \
+	DECLARE_KOBJ_ATTR_STORE_HEX_CHECK(attr_name, var_name, cond) \
+	DECLARE_KOBJ_ATTR(attr_name)
+
+/* helper procedure version */
+#define DECLARE_KOBJ_ATTR_SHOW_HEX_PROC(attr_name, var_name, func) \
+	static ssize_t attr_name##_show( \
+		struct kobject *kobj, \
+		struct kobj_attribute *attr, \
+		char *buf) \
+	{ \
+		return func(kobj, attr, buf, var_name); \
+	}
+#define DECLARE_KOBJ_ATTR_STORE_HEX_PROC(attr_name, var_name, func) \
+	static ssize_t attr_name##_store( \
+		struct kobject *kobj, \
+		struct kobj_attribute *attr, \
+		const char *buf, \
+		size_t n) \
+	{ \
+		return func(kobj, attr, buf, n, &(var_name)); \
+	}
+#define DECLARE_KOBJ_ATTR_HEX_PROC(attr_name, var_name, show, store) \
+	DECLARE_KOBJ_ATTR_SHOW_HEX_PROC(attr_name, var_name, show) \
+	DECLARE_KOBJ_ATTR_STORE_HEX_PROC(attr_name, var_name, store) \
+	DECLARE_KOBJ_ATTR(attr_name)
+
+/*
+ * Declaring KOBJ attributes with string variable
+ */
+#define DECLARE_KOBJ_ATTR_SHOW_STR(attr_name, var_name) \
+	static ssize_t attr_name##_show( \
+		struct kobject *kobj, \
+		struct kobj_attribute *attr, \
+		char *buf) \
+	{ \
+		return snprintf(buf, PAGE_SIZE, "%s", var_name); \
+	}
+
+#define DECLARE_KOBJ_ATTR_RO_STR(attr_name, var_name) \
+	DECLARE_KOBJ_ATTR_SHOW_STR(attr_name, var_name) \
+	DECLARE_KOBJ_ATTR_RO(attr_name)
+
+/*
+ * Declaring KOBJ attributes with integer list variable
+ */
+#define DECLARE_KOBJ_ATTR_INT_LIST_ITEM(list_name, list) \
+	static struct list_name##_list_item_t { \
+		int	key; \
+		int	val; \
+	} const list_name##_list_item[] = { list }
+#define DECLARE_KOBJ_ATTR_SHOW_INT_LIST(attr_name, var_name, list_name) \
+	static ssize_t attr_name##_show( \
+		struct kobject *kobj, \
+		struct kobj_attribute *attr, \
+		char *buf) \
+	{ \
+		int	i; \
+		for (i = 0; i < ARRAY_SIZE(list_name##_list_item); i++) { \
+			if (var_name == list_name##_list_item[i].key) { \
+				return snprintf(buf, \
+						PAGE_SIZE, \
+						"%d\n", \
+						list_name##_list_item[i].val); \
+			} \
+		} \
+		return snprintf(buf, PAGE_SIZE, "%d\n", -1); \
+	}
+#define DECLARE_KOBJ_ATTR_STORE_INT_LIST(attr_name, var_name, list_name) \
+	static ssize_t attr_name##_store( \
+		struct kobject *kobj, \
+		struct kobj_attribute *attr, \
+		const char *buf, \
+		size_t n) \
+	{ \
+		int	value; \
+		int	i; \
+		if (kstrtoint(buf, 10, &value) != 0) \
+			return -EINVAL; \
+		for (i = 0; i < ARRAY_SIZE(list_name##_list_item); i++) { \
+			if (value == list_name##_list_item[i].val) { \
+				var_name = list_name##_list_item[i].key; \
+				return n; \
+			} \
+		} \
+		return -EINVAL; \
+	}
+#define DECLARE_KOBJ_ATTR_INT_LIST(attr_name, var_name, list_name) \
+	DECLARE_KOBJ_ATTR_SHOW_INT_LIST(attr_name, var_name, list_name) \
+	DECLARE_KOBJ_ATTR_STORE_INT_LIST(attr_name, var_name, list_name) \
+	DECLARE_KOBJ_ATTR(attr_name)
+
+/*
+ * Declaring KOBJ attributes with string list variable
+ */
+#define DECLARE_KOBJ_ATTR_STR_LIST_ITEM(list_name, list) \
+	static struct list_name##_list_item_t { \
+		int	key; \
+		char	*val; \
+	} const list_name##_list_item[] = { list }
+#define DECLARE_KOBJ_ATTR_SHOW_STR_LIST(attr_name, var_name, list_name) \
+	static ssize_t attr_name##_show( \
+		struct kobject *kobj, \
+		struct kobj_attribute *attr, \
+		char *buf) \
+	{ \
+		int	i; \
+		for (i = 0; i < ARRAY_SIZE(list_name##_list_item); i++) { \
+			if (var_name == list_name##_list_item[i].key) { \
+				return snprintf(buf, \
+						PAGE_SIZE, \
+						"%s\n", \
+						list_name##_list_item[i].val); \
+			} \
+		} \
+		return snprintf(buf, PAGE_SIZE, "%s\n", "ERR"); \
+	}
+#define DECLARE_KOBJ_ATTR_STORE_STR_LIST(attr_name, var_name, list_name) \
+	static ssize_t attr_name##_store( \
+		struct kobject *kobj, \
+		struct kobj_attribute *attr, \
+		const char *buf, \
+		size_t n) \
+	{ \
+		int	i; \
+		for (i = 0; i < ARRAY_SIZE(list_name##_list_item); i++) { \
+			if (strncasecmp(buf, \
+					list_name##_list_item[i].val, \
+					strlen(list_name##_list_item[i].val)) == 0) { \
+				var_name = list_name##_list_item[i].key; \
+				return n; \
+			} \
+		} \
+		return -EINVAL; \
+	}
+#define DECLARE_KOBJ_ATTR_STR_LIST(attr_name, var_name, list_name) \
+	DECLARE_KOBJ_ATTR_SHOW_STR_LIST(attr_name, var_name, list_name) \
+	DECLARE_KOBJ_ATTR_STORE_STR_LIST(attr_name, var_name, list_name) \
+	DECLARE_KOBJ_ATTR(attr_name)
+
+/*
+ *  MET Debug Message
+ */
+#define METINFO(format, ...)	pr_debug("[MET]%s: "format, __func__, ##__VA_ARGS__)
+#define METERROR(format, ...)	pr_debug("[MET][ERR]%s: "format, __func__, ##__VA_ARGS__)
+
+#endif	/* _MT_TYPEDEFS_H__ */
diff --git a/src/devtools/met-driver/4.9/common/ondiemet.c b/src/devtools/met-driver/4.9/common/ondiemet.c
new file mode 100644
index 0000000..5247fa7
--- /dev/null
+++ b/src/devtools/met-driver/4.9/common/ondiemet.c
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "ondiemet.h"
+
+/* record enabled modules */
+unsigned int ondiemet_module[ONDIEMET_NUM];
+EXPORT_SYMBOL(ondiemet_module);
+
+void (*scp_start[ONDIEMET_NUM]) (void) = {
+sspm_start, NULL, NULL};
+
+void (*scp_stop[ONDIEMET_NUM]) (void) = {
+sspm_stop, NULL, NULL};
+
+void (*scp_extract[ONDIEMET_NUM]) (void) = {
+sspm_extract, NULL, NULL};
+
+/* record which MCU is started to generate data */
+int ondiemet_module_started[ONDIEMET_NUM];
+
+int ondiemet_attr_init(struct device *dev)
+{
+	int ret;
+
+	ret = sspm_attr_init(dev);
+	if (ret != 0) {
+		pr_debug("can not create device file: sspm related\n");
+		return ret;
+	}
+
+	return 0;
+
+}
+
+int ondiemet_attr_uninit(struct device *dev)
+{
+	int ret;
+
+	ret = sspm_attr_uninit(dev);
+	if (ret != 0) {
+		pr_debug("can not delete device file: sspm related\n");
+		return ret;
+	}
+
+	return 0;
+
+}
+
+void ondiemet_start(void)
+{
+	int i;
+
+	for (i = 0; i < ONDIEMET_NUM; i++) {
+		if (ondiemet_module[i] != 0) {
+			ondiemet_module_started[i] = 1;
+			(*scp_start[i]) ();
+		}
+	}
+}
+
+void ondiemet_stop(void)
+{
+	int i;
+
+	for (i = 0; i < ONDIEMET_NUM; i++) {
+		if (ondiemet_module[i] != 0) {
+			(*scp_stop[i]) ();
+			ondiemet_module_started[i] = 0;
+		}
+	}
+}
+
+void ondiemet_extract(void)
+{
+	int i;
+
+	for (i = 0; i < ONDIEMET_NUM; i++) {
+		if (ondiemet_module[i] != 0)
+			(*scp_extract[i]) ();
+	}
+}
diff --git a/src/devtools/met-driver/4.9/common/ondiemet.h b/src/devtools/met-driver/4.9/common/ondiemet.h
new file mode 100644
index 0000000..3fff604
--- /dev/null
+++ b/src/devtools/met-driver/4.9/common/ondiemet.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __ONDIEMET_H
+#define __ONDIEMET_H
+
+#include "ondiemet_log.h"
+
+extern void ondiemet_extract(void);
+extern void ondiemet_stop(void);
+extern void ondiemet_start(void);
+
+#define ONDIEMET_SSPM  0
+#define ONDIEMET_NUM  3		/* total number of supported */
+extern unsigned int ondiemet_module[];
+extern void sspm_start(void);
+extern void sspm_stop(void);
+extern void sspm_extract(void);
+extern int sspm_attr_init(struct device *dev);
+extern int sspm_attr_uninit(struct device *dev);
+
+extern int ondiemet_attr_init(struct device *dev);
+extern int ondiemet_attr_uninit(struct device *dev);
+
+extern int sspm_buffer_size;
+
+#endif				/* __ONDIEMET_H */
diff --git a/src/devtools/met-driver/4.9/common/ondiemet_log.c b/src/devtools/met-driver/4.9/common/ondiemet_log.c
new file mode 100644
index 0000000..4f3ad69
--- /dev/null
+++ b/src/devtools/met-driver/4.9/common/ondiemet_log.c
@@ -0,0 +1,516 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/debugfs.h>
+#include <linux/mutex.h>
+#include <linux/semaphore.h>
+#include <linux/freezer.h>
+#include <linux/uaccess.h>
+#include <linux/completion.h>
+
+#include "ondiemet_log.h"
+
+#define ONDIEMET_LOG_REQ 1
+/* TODO: abandon this constatnt */
+#define ONDIEMET_LOG_STOP 2
+
+#define PID_NONE (-1)
+
+#define ONDIEMET_LOG_STOP_MODE 0
+#define ONDIEMET_LOG_RUN_MODE 1
+#define ONDIEMET_LOG_DEBUG_MODE 2
+
+static int ondiemet_trace_run;
+static struct dentry *dbgfs_met_dir;
+
+struct mutex lock_tracef;
+struct ondiemet_log_req_q_t {
+	struct list_head listq;
+	struct mutex lockq;
+	/* struct semaphore new_evt_sema; */
+	struct completion new_evt_comp;
+	int closeq_flag;
+} ondiemet_log_req_q;
+
+struct ondiemet_log_req {
+	struct list_head list;
+	int cmd_type;
+	const char *src;
+	size_t num;
+
+	void (*on_fini_cb)(const void *p);
+	const void *param;
+};
+
+#define __ondiemet_log_req_init(req, cmd, s, n, pf, p)	\
+	do {						\
+		INIT_LIST_HEAD(&req->list);		\
+		req->cmd_type = cmd;			\
+		req->src = s;				\
+		req->num = n;				\
+		req->on_fini_cb = pf;			\
+		req->param = p;				\
+	} while (0)
+
+#define __ondiemet_log_req_fini(req)		        \
+	do {					        \
+		if (req->on_fini_cb)			\
+			req->on_fini_cb(req->param);	\
+		kfree(req);				\
+	} while (0)
+
+static void __ondiemet_log_req_q_init(struct ondiemet_log_req_q_t *q)
+{
+	INIT_LIST_HEAD(&q->listq);
+	mutex_init(&q->lockq);
+	/* sema_init(&q->new_evt_sema, 0); */
+	init_completion(&q->new_evt_comp);
+	q->closeq_flag = 1;
+}
+
+/* undequeue is seen as a roll-back operation, so it can be done even when the queue is closed */
+static void __ondiemet_log_req_undeq(struct ondiemet_log_req *req)
+{
+	mutex_lock(&ondiemet_log_req_q.lockq);
+	list_add(&req->list, &ondiemet_log_req_q.listq);
+	mutex_unlock(&ondiemet_log_req_q.lockq);
+
+	/* up(&ondiemet_log_req_q.new_evt_sema); */
+	complete(&ondiemet_log_req_q.new_evt_comp);
+}
+
+static int __ondiemet_log_req_enq(struct ondiemet_log_req *req)
+{
+	mutex_lock(&ondiemet_log_req_q.lockq);
+	if (ondiemet_log_req_q.closeq_flag) {
+		mutex_unlock(&ondiemet_log_req_q.lockq);
+		return -EBUSY;
+	}
+
+	list_add_tail(&req->list, &ondiemet_log_req_q.listq);
+	if (req->cmd_type == ONDIEMET_LOG_STOP)
+		ondiemet_log_req_q.closeq_flag = 1;
+	mutex_unlock(&ondiemet_log_req_q.lockq);
+
+	/* up(&ondiemet_log_req_q.new_evt_sema); */
+	complete(&ondiemet_log_req_q.new_evt_comp);
+
+	return 0;
+}
+
+int ondiemet_log_req_enq(const char *src, size_t num, void (*on_fini_cb)(const void *p), const void *param)
+{
+	struct ondiemet_log_req *req = kmalloc(sizeof(*req), GFP_KERNEL);
+
+	__ondiemet_log_req_init(req, ONDIEMET_LOG_REQ, src, num, on_fini_cb, param);
+	return __ondiemet_log_req_enq(req);
+}
+
+/*int down_freezable_interruptible(struct semaphore *sem) */
+int down_freezable_interruptible(struct completion *comp)
+{
+
+	int ret;
+
+	freezer_do_not_count();
+	/* ret = down_interruptible(sem); */
+	ret = wait_for_completion_interruptible(comp);
+	freezer_count();
+
+	return ret;
+}
+
+struct ondiemet_log_req *__ondiemet_log_req_deq(void)
+{
+	struct ondiemet_log_req *ret_req;
+
+	/*if (down_freezable_interruptible(&ondiemet_log_req_q.new_evt_sema))*/
+	if (down_freezable_interruptible(&ondiemet_log_req_q.new_evt_comp))
+		return NULL;
+
+	mutex_lock(&ondiemet_log_req_q.lockq);
+	ret_req = list_entry(ondiemet_log_req_q.listq.next, struct ondiemet_log_req, list);
+	list_del_init(&ret_req->list);
+	mutex_unlock(&ondiemet_log_req_q.lockq);
+
+	return ret_req;
+}
+
+void __ondiemet_log_req_open(void)
+{
+	mutex_lock(&ondiemet_log_req_q.lockq);
+	ondiemet_log_req_q.closeq_flag = 0;
+	mutex_unlock(&ondiemet_log_req_q.lockq);
+}
+
+int __ondiemet_log_req_closed(void)
+{
+	int ret;
+
+	mutex_lock(&ondiemet_log_req_q.lockq);
+	ret = ondiemet_log_req_q.closeq_flag && list_empty(&ondiemet_log_req_q.listq);
+	mutex_unlock(&ondiemet_log_req_q.lockq);
+
+	return ret;
+}
+
+int __ondiemet_log_req_working(void)
+{
+	int ret;
+
+	mutex_lock(&ondiemet_log_req_q.lockq);
+	ret = !ondiemet_log_req_q.closeq_flag;
+	mutex_unlock(&ondiemet_log_req_q.lockq);
+
+	return ret;
+}
+
+static void *__ondiemet_trace_seq_next(struct seq_file *seqf, loff_t *offset)
+{
+	struct ondiemet_log_req *next_req;
+
+	if (ondiemet_trace_run == ONDIEMET_LOG_DEBUG_MODE)
+		pr_debug("[met] __ondiemet_trace_seq_next: pid: %d\n", current->pid);
+
+	if (__ondiemet_log_req_closed())
+		return NULL;
+
+	next_req = __ondiemet_log_req_deq();
+
+	if (next_req == NULL)
+		return NULL;
+
+	if (next_req->cmd_type == ONDIEMET_LOG_STOP) {
+		__ondiemet_log_req_fini(next_req);
+		return NULL;
+	}
+
+	return (void *) next_req;
+}
+
+struct mutex lock_trace_owner_pid;
+pid_t trace_owner_pid = PID_NONE;
+static void *ondiemet_trace_seq_start(struct seq_file *seqf, loff_t *offset)
+{
+	void *ret;
+
+	if (ondiemet_trace_run == ONDIEMET_LOG_DEBUG_MODE) {
+		pr_debug("[met] ondiemet_trace_seq_start: locked_pid: %d, pid: %d, offset: %llu\n",
+			 trace_owner_pid, current->pid, *offset);
+	}
+
+	if (!mutex_trylock(&lock_tracef))
+		return NULL;
+
+	mutex_lock(&lock_trace_owner_pid);
+	trace_owner_pid = current->pid;
+	mutex_unlock(&lock_trace_owner_pid);
+
+	ret = __ondiemet_trace_seq_next(seqf, offset);
+
+	return ret;
+}
+
+static void *ondiemet_trace_seq_next(struct seq_file *seqf, void *p, loff_t *offset)
+{
+	if (ondiemet_trace_run == ONDIEMET_LOG_DEBUG_MODE)
+		pr_debug("[met] ondiemet_trace_seq_next: pid: %d\n", current->pid);
+
+	(*offset)++;
+	return __ondiemet_trace_seq_next(seqf, offset);
+}
+
+static int ondiemet_trace_seq_show(struct seq_file *seqf, void *p)
+{
+	struct ondiemet_log_req *req = (struct ondiemet_log_req *) p;
+	size_t l_sz;
+	size_t r_sz;
+	struct ondiemet_log_req *l_req;
+	struct ondiemet_log_req *r_req;
+	int ret;
+
+	if (ondiemet_trace_run == ONDIEMET_LOG_DEBUG_MODE)
+		pr_debug("[met] ondiemet_trace_seq_show: pid: %d\n", current->pid);
+
+	if (req->num >= seqf->size) {
+		l_req = kmalloc(sizeof(*req), GFP_KERNEL);
+		r_req = req;
+
+		l_sz = seqf->size >> 1;
+		r_sz = req->num - l_sz;
+		__ondiemet_log_req_init(l_req, ONDIEMET_LOG_REQ, req->src, l_sz, NULL, NULL);
+		__ondiemet_log_req_init(r_req, ONDIEMET_LOG_REQ, req->src + l_sz,
+					r_sz, req->on_fini_cb, req->param);
+
+		__ondiemet_log_req_undeq(r_req);
+		req = l_req;
+
+		if (ondiemet_trace_run == ONDIEMET_LOG_DEBUG_MODE)
+			pr_debug("[met] ondiemet_trace_seq_show: split request\n");
+	}
+
+	ret = seq_write(seqf, req->src, req->num);
+
+	if (ret) {
+		/* check if seq_file buffer overflows */
+		if (seqf->count == seqf->size) {
+			__ondiemet_log_req_undeq(req);
+		} else {
+			if (ondiemet_trace_run == ONDIEMET_LOG_DEBUG_MODE)
+				pr_debug("[met] ondiemet_trace_seq_show: reading trace record failed, some data may be lost or corrupted\n");
+			__ondiemet_log_req_fini(req);
+		}
+		return 0;
+	}
+
+	__ondiemet_log_req_fini(req);
+	return 0;
+}
+
+static void ondiemet_trace_seq_stop(struct seq_file *seqf, void *p)
+{
+	if (ondiemet_trace_run == ONDIEMET_LOG_DEBUG_MODE)
+		pr_debug("[met] ondiemet_trace_seq_stop: pid: %d\n", current->pid);
+
+	mutex_lock(&lock_trace_owner_pid);
+	if (current->pid == trace_owner_pid) {
+		trace_owner_pid = PID_NONE;
+		mutex_unlock(&lock_tracef);
+	}
+	mutex_unlock(&lock_trace_owner_pid);
+}
+
+static const struct seq_operations ondiemet_trace_seq_ops = {
+	.start = ondiemet_trace_seq_start,
+	.next = ondiemet_trace_seq_next,
+	.stop = ondiemet_trace_seq_stop,
+	.show = ondiemet_trace_seq_show
+};
+
+static int ondiemet_trace_open(struct inode *inode, struct file *fp)
+{
+	return seq_open(fp, &ondiemet_trace_seq_ops);
+}
+
+static const struct file_operations ondiemet_trace_fops = {
+	.owner = THIS_MODULE,
+	.open = ondiemet_trace_open,
+	.read = seq_read,
+	.llseek = seq_lseek,
+	.release = seq_release
+};
+
+/*struct semaphore log_start_sema;*/
+struct completion log_start_comp;
+int ondiemet_log_manager_start(void)
+{
+	int ret;
+
+	/* TODO: choose a better return value */
+	if (__ondiemet_log_req_working())
+		return -EINVAL;
+
+	if (!__ondiemet_log_req_closed()) {
+		/*ret = down_killable(&log_start_sema);*/
+		ret = wait_for_completion_killable(&log_start_comp);
+		if (ret)
+			return ret;
+	}
+
+	__ondiemet_log_req_open();
+
+	return 0;
+}
+
+/*struct semaphore log_stop_sema;*/
+struct completion log_stop_comp;
+static void __log_stop_cb(const void *p)
+{
+	/* up(&log_start_sema); */
+	/* up(&log_stop_sema); */
+	complete(&log_start_comp);
+	complete(&log_stop_comp);
+}
+
+int ondiemet_log_manager_stop(void)
+{
+	int ret;
+	struct ondiemet_log_req *req;
+
+	/* TODO: choose a better return value */
+	if (__ondiemet_log_req_closed())
+		return -EINVAL;
+
+	req = kmalloc(sizeof(*req), GFP_KERNEL);
+
+	__ondiemet_log_req_init(req, ONDIEMET_LOG_STOP, NULL, 0, __log_stop_cb, NULL);
+	/*sema_init(&log_start_sema, 0); */
+	/*sema_init(&log_stop_sema, 0); */
+	init_completion(&log_start_comp);
+	init_completion(&log_stop_comp);
+
+	ret = __ondiemet_log_req_enq(req);
+	if (ret)
+		return ret;
+
+	/* XXX: blocking may be break by SIGKILL */
+	/*return down_killable(&log_stop_sema);*/
+	return wait_for_completion_killable(&log_stop_comp);
+}
+
+int ondiemet_parse_num(const char *str, unsigned int *value, int len)
+{
+	int ret;
+
+	if (len <= 0)
+		return -1;
+
+	if ((len > 2) &&
+	    ((str[0] == '0') &&
+	     ((str[1] == 'x') || (str[1] == 'X')))) {
+		ret = kstrtouint(str, 16, value);
+	} else {
+		ret = kstrtouint(str, 10, value);
+	}
+
+	if (ret != 0)
+		return -1;
+
+	return 0;
+}
+
+/* XXX: seq_file will output only when a page is filled */
+static ssize_t ondiemet_log_write_store(struct device *dev,
+					struct device_attribute *attr,
+					const char *buf,
+					size_t count)
+{
+	char *plog = NULL;
+
+	plog = kmalloc_array(count, sizeof(*plog), GFP_KERNEL);
+	if (!plog) {
+		/* TODO: use a better error code */
+		return -EINVAL;
+	}
+
+	memcpy(plog, buf, count);
+
+	mutex_lock(&dev->mutex);
+	ondiemet_log_req_enq(plog, strnlen(plog, count), kfree, plog);
+	mutex_unlock(&dev->mutex);
+
+	return count;
+}
+
+static DEVICE_ATTR(ondiemet_log_write, 0664, NULL, ondiemet_log_write_store);
+
+static ssize_t ondiemet_log_run_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int sz;
+
+	mutex_lock(&dev->mutex);
+	sz = snprintf(buf, PAGE_SIZE, "%d\n", ondiemet_trace_run);
+	mutex_unlock(&dev->mutex);
+	return sz;
+}
+
+static ssize_t ondiemet_log_run_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+	int ret;
+	int prev_run_state;
+
+	mutex_lock(&dev->mutex);
+
+	prev_run_state = ondiemet_trace_run;
+
+	if (kstrtoint(buf, 10, &ondiemet_trace_run) != 0)
+		return -EINVAL;
+
+	if (ondiemet_trace_run <= ONDIEMET_LOG_STOP_MODE) {
+		ondiemet_trace_run = ONDIEMET_LOG_STOP_MODE;
+		ondiemet_log_manager_stop();
+
+		if (prev_run_state == ONDIEMET_LOG_DEBUG_MODE)
+			device_remove_file(dev, &dev_attr_ondiemet_log_write);
+	} else if (ondiemet_trace_run == ONDIEMET_LOG_RUN_MODE) {
+		ondiemet_trace_run = ONDIEMET_LOG_RUN_MODE;
+		ondiemet_log_manager_start();
+
+		if (prev_run_state == ONDIEMET_LOG_DEBUG_MODE)
+			device_remove_file(dev, &dev_attr_ondiemet_log_write);
+	} else {
+		ondiemet_trace_run = ONDIEMET_LOG_DEBUG_MODE;
+		ondiemet_log_manager_start();
+
+		if (prev_run_state != ONDIEMET_LOG_DEBUG_MODE) {
+			ret = device_create_file(dev, &dev_attr_ondiemet_log_write);
+			if (ret != 0)
+				pr_debug("[met] can not create device node: ondiemet_log_write\n");
+		}
+	}
+
+	mutex_unlock(&dev->mutex);
+
+	return count;
+}
+
+static DEVICE_ATTR(ondiemet_log_run, 0660, ondiemet_log_run_show, ondiemet_log_run_store);
+
+int ondiemet_log_manager_init(struct device *dev)
+{
+	int ret;
+	struct dentry *d;
+
+	mutex_init(&lock_tracef);
+
+	__ondiemet_log_req_q_init(&ondiemet_log_req_q);
+
+	/*sema_init(&log_start_sema, 0);*/
+	/*sema_init(&log_stop_sema, 0);*/
+	init_completion(&log_start_comp);
+	init_completion(&log_stop_comp);
+
+	dbgfs_met_dir = debugfs_create_dir("ondiemet", NULL);
+	if (!dbgfs_met_dir) {
+		pr_debug("[met] can not create debugfs directory: met\n");
+		return -ENOMEM;
+	}
+
+	mutex_init(&lock_trace_owner_pid);
+
+	d = debugfs_create_file("trace", 0644, dbgfs_met_dir, NULL, &ondiemet_trace_fops);
+	if (!d) {
+		pr_debug("[met] can not create devide node in debugfs: ondiemet_trace\n");
+		return -ENOMEM;
+	}
+
+	ondiemet_trace_run = __ondiemet_log_req_working();
+	ret = device_create_file(dev, &dev_attr_ondiemet_log_run);
+	if (ret != 0) {
+		pr_debug("[met] can not create device node: ondiemet_log_run\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+int ondiemet_log_manager_uninit(struct device *dev)
+{
+	device_remove_file(dev, &dev_attr_ondiemet_log_run);
+	debugfs_remove_recursive(dbgfs_met_dir);
+	return 0;
+}
diff --git a/src/devtools/met-driver/4.9/common/ondiemet_log.h b/src/devtools/met-driver/4.9/common/ondiemet_log.h
new file mode 100644
index 0000000..cfe8be9
--- /dev/null
+++ b/src/devtools/met-driver/4.9/common/ondiemet_log.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _ONDIEMET_LOG_H_
+#define _ONDIEMET_LOG_H_
+
+#include <linux/device.h>
+
+int ondiemet_log_manager_init(struct device *dev);
+int ondiemet_log_manager_uninit(struct device *dev);
+int ondiemet_log_manager_start(void);
+/* Log manager can be reactivated by inserting new requests, i.e., calling ondiemet_log_req_enq() */
+int ondiemet_log_manager_stop(void);
+int ondiemet_log_req_enq(const char *src, size_t num, void (*on_fini_cb) (const void *p),
+			 const void *param);
+
+#endif				/* _ONDIEMET_LOG_H_ */
diff --git a/src/devtools/met-driver/4.9/common/power.c b/src/devtools/met-driver/4.9/common/power.c
new file mode 100644
index 0000000..c57d907
--- /dev/null
+++ b/src/devtools/met-driver/4.9/common/power.c
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/cpufreq.h>
+#include <trace/events/power.h>
+
+#include "power.h"
+#include "met_drv.h"
+#include "met_kernel_symbol.h"
+
+noinline void cpu_frequency(unsigned int frequency, unsigned int cpu_id)
+{
+	/* suppose this symbol is available, otherwise, the met.ko will fail */
+	met_cpu_frequency_symbol(frequency, cpu_id);
+}
+
+void force_power_log(int cpu)
+{
+	struct cpufreq_policy *p;
+
+	if (cpu == POWER_LOG_ALL) {
+		for_each_possible_cpu(cpu) {
+			p = cpufreq_cpu_get(cpu);
+			if (p != NULL) {
+				cpu_frequency(p->cur, cpu);
+				cpufreq_cpu_put(p);
+			} else {
+				cpu_frequency(0, cpu);
+			}
+		}
+	} else {
+		p = cpufreq_cpu_get(cpu);
+		if (p != NULL) {
+			cpu_frequency(p->cur, cpu);
+			cpufreq_cpu_put(p);
+		} else {
+			cpu_frequency(0, cpu);
+		}
+	}
+}
+
+void force_power_log_val(unsigned int frequency, int cpu)
+{
+	cpu_frequency(frequency, cpu);
+}
diff --git a/src/devtools/met-driver/4.9/common/power.h b/src/devtools/met-driver/4.9/common/power.h
new file mode 100644
index 0000000..8a0e8f0
--- /dev/null
+++ b/src/devtools/met-driver/4.9/common/power.h
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _POWER_H_
+#define _POWER_H_
+
+#define POWER_LOG_ALL	-1
+void force_power_log(int cpu);
+void force_power_log_val(unsigned int frequency, int cpu);
+
+#endif				/* _POWER_H_ */
diff --git a/src/devtools/met-driver/4.9/common/sampler.c b/src/devtools/met-driver/4.9/common/sampler.c
new file mode 100644
index 0000000..1ed04c3
--- /dev/null
+++ b/src/devtools/met-driver/4.9/common/sampler.c
@@ -0,0 +1,665 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/cpu.h>
+#include <linux/sched.h>
+#include <linux/notifier.h>
+#include <linux/module.h>
+#include <linux/irq.h>
+#if 0				/* fix me later, no such file on current tree */
+#include <mach/mt_cpuxgpt.h>
+#endif
+#include <asm/arch_timer.h>
+
+#define	MET_USER_EVENT_SUPPORT
+#include "interface.h"
+#include "sampler.h"
+#include "met_struct.h"
+#include "util.h"
+#include "switch.h"
+#include "trace.h"
+#include "met_drv.h"
+#include "met_tag.h" /* for tracing_mark_write */
+
+#include "cpu_pmu.h"	/* for using kernel perf PMU driver */
+
+#include "met_kernel_symbol.h"
+
+#undef	DEBUG_CPU_NOTIFY
+/* #define DEBUG_CPU_NOTIFY */
+#if	defined(DEBUG_CPU_NOTIFY)
+#ifdef CONFIG_MET_MODULE
+#define	dbg_met_tag_oneshot	met_tag_oneshot_real
+#else
+#define	dbg_met_tag_oneshot	met_tag_oneshot
+#endif /* CONFIG_MET_MODULE */
+#else
+#define	dbg_met_tag_oneshot(class_id, name, value)	({ 0; })
+#endif
+
+static int start;
+static unsigned int online_cpu_map;
+static int curr_polling_cpu;
+static int cpu_related_cnt;
+
+static int preferred_cpu_list[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
+
+static int calc_preferred_polling_cpu(unsigned int cpu_map)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(preferred_cpu_list); i++) {
+		if (cpu_map & (1 << preferred_cpu_list[i]))
+			return preferred_cpu_list[i];
+	}
+
+	return -1;
+}
+
+static void wq_sync_buffer(struct work_struct *work)
+{
+	int cpu;
+	struct delayed_work *dw = container_of(work, struct delayed_work, work);
+	struct met_cpu_struct *met_cpu_ptr = container_of(dw, struct met_cpu_struct, dwork);
+
+	cpu = smp_processor_id();
+	if (met_cpu_ptr->cpu != cpu) {
+		/* panic("ERROR"); */
+		return;
+	}
+
+	/* sync_samples(cpu); */
+	/* don't re-add the work if we're shutting down */
+	if (met_cpu_ptr->work_enabled)
+		schedule_delayed_work(dw, DEFAULT_TIMER_EXPIRE);
+}
+
+static enum hrtimer_restart met_hrtimer_notify(struct hrtimer *hrtimer)
+{
+	int cpu;
+	int *count;
+	unsigned long long stamp;
+	struct met_cpu_struct *met_cpu_ptr = container_of(hrtimer, struct met_cpu_struct, hrtimer);
+	struct metdevice *c;
+#if	defined(DEBUG_CPU_NOTIFY)
+	char msg[32];
+#endif
+
+	cpu = smp_processor_id();
+#if	defined(DEBUG_CPU_NOTIFY)
+	{
+		char msg[32];
+
+		snprintf(msg, sizeof(msg), "met_hrtimer notify_%d", cpu);
+		dbg_met_tag_oneshot(0, msg, 1);
+	}
+#endif
+
+	if (met_cpu_ptr->cpu != cpu) {
+		/* panic("ERROR2"); */
+		dbg_met_tag_oneshot(0, msg, -3);
+		return HRTIMER_NORESTART;
+	}
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->ondiemet_mode == 0) {
+			if ((c->mode == 0) || (c->timed_polling == NULL))
+				continue;
+		} else if (c->ondiemet_mode == 1) {
+			if ((c->mode == 0) || (c->ondiemet_timed_polling == NULL))
+				continue;
+		} else if (c->ondiemet_mode == 2) {
+			if ((c->mode == 0) || ((c->timed_polling == NULL)
+					       && (c->ondiemet_timed_polling == NULL)))
+				continue;
+		}
+
+		count = per_cpu_ptr(c->polling_count, cpu);
+		if ((*count) > 0) {
+			(*count)--;
+			continue;
+		}
+
+		*(count) = c->polling_count_reload;
+
+		stamp = cpu_clock(cpu);
+
+		if (c->cpu_related == 0) {
+			if (cpu == curr_polling_cpu) {
+				if (c->ondiemet_mode == 0) {
+					c->timed_polling(stamp, 0);
+				} else if (c->ondiemet_mode == 1) {
+					c->ondiemet_timed_polling(stamp, 0);
+				} else if (c->ondiemet_mode == 2) {
+					if (c->timed_polling)
+						c->timed_polling(stamp, 0);
+					if (c->ondiemet_timed_polling)
+						c->ondiemet_timed_polling(stamp, 0);
+				}
+			}
+		} else {
+			if (c->ondiemet_mode == 0) {
+				c->timed_polling(stamp, cpu);
+			} else if (c->ondiemet_mode == 1) {
+				c->ondiemet_timed_polling(stamp, cpu);
+			} else if (c->ondiemet_mode == 2) {
+				if (c->timed_polling)
+					c->timed_polling(stamp, 0);
+				if (c->ondiemet_timed_polling)
+					c->ondiemet_timed_polling(stamp, 0);
+			}
+		}
+	}
+
+	if (met_cpu_ptr->hrtimer_online_check) {
+		online_cpu_map |= (1 << cpu);
+		met_cpu_ptr->hrtimer_online_check = 0;
+		dbg_met_tag_oneshot(0, "met_online check done", cpu);
+		if (calc_preferred_polling_cpu(online_cpu_map) == cpu) {
+			curr_polling_cpu = cpu;
+			dbg_met_tag_oneshot(0, "met_curr polling cpu", cpu);
+		}
+	}
+
+	if (met_cpu_ptr->work_enabled) {
+		hrtimer_forward_now(hrtimer, ns_to_ktime(DEFAULT_HRTIMER_EXPIRE));
+		dbg_met_tag_oneshot(0, msg, 0);
+		return HRTIMER_RESTART;
+	}
+	dbg_met_tag_oneshot(0, msg, 0);
+	return HRTIMER_NORESTART;
+}
+
+static void __met_hrtimer_start(void *unused)
+{
+	struct met_cpu_struct *met_cpu_ptr = NULL;
+	struct hrtimer *hrtimer = NULL;
+	/* struct delayed_work *dw; */
+	struct metdevice *c;
+
+	met_cpu_ptr = this_cpu_ptr(&met_cpu);
+#if	defined(DEBUG_CPU_NOTIFY)
+	{
+		char msg[32];
+
+		snprintf(msg, sizeof(msg), "met_hrtimer status_%d", met_cpu_ptr->cpu);
+		dbg_met_tag_oneshot(0, msg, 1);
+	}
+#endif
+	/*
+	 * do not open HRtimer when EVENT timer enable
+	 */
+	if (!(met_switch.mode & MT_SWITCH_EVENT_TIMER)) {
+		hrtimer = &met_cpu_ptr->hrtimer;
+		/* dw = &met_cpu_ptr->dwork; */
+
+		hrtimer_init(hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+		hrtimer->function = met_hrtimer_notify;
+	}
+
+	list_for_each_entry(c, &met_list, list) {
+		*(this_cpu_ptr(c->polling_count)) = 0;
+		if (c->ondiemet_mode == 0) {
+			if ((c->cpu_related) && (c->mode) && (c->start))
+				c->start();
+		} else if (c->ondiemet_mode == 1) {
+			if (((c->cpu_related)) && (c->mode) && (c->ondiemet_start))
+				c->ondiemet_start();
+		} else if (c->ondiemet_mode == 2) {
+			if ((c->cpu_related) && (c->mode) && (c->start))
+				c->start();
+			if (((c->cpu_related)) && (c->mode) && (c->ondiemet_start))
+				c->ondiemet_start();
+		}
+	}
+	/*
+	 * do not open HRtimer when EVENT timer enable
+	 */
+	if (!(met_switch.mode & MT_SWITCH_EVENT_TIMER)) {
+		if (DEFAULT_HRTIMER_EXPIRE) {
+			met_cpu_ptr->work_enabled = 1;
+			/* schedule_delayed_work_on(smp_processor_id(), dw, DEFAULT_TIMER_EXPIRE); */
+			hrtimer_start(hrtimer, ns_to_ktime(DEFAULT_HRTIMER_EXPIRE),
+				      HRTIMER_MODE_REL_PINNED);
+		}
+	}
+}
+
+static void __met_hrtimer_stop(void *unused)
+{
+	struct met_cpu_struct *met_cpu_ptr;
+	struct hrtimer *hrtimer;
+	/* struct delayed_work *dw; */
+	struct metdevice *c;
+
+	int cpu = smp_processor_id();
+	pr_debug("!!!!!!!! %s cpu = %d\n",  __FUNCTION__, cpu);
+
+	met_cpu_ptr = this_cpu_ptr(&met_cpu);
+#if	defined(DEBUG_CPU_NOTIFY)
+	{
+		char msg[32];
+
+		snprintf(msg, sizeof(msg), "met_hrtimer status_%d", met_cpu_ptr->cpu);
+		dbg_met_tag_oneshot(0, msg, 0);
+	}
+#endif
+	/*
+	 * do not open HRtimer when EVENT timer enable
+	 */
+	if (!(met_switch.mode & MT_SWITCH_EVENT_TIMER)) {
+		hrtimer = &met_cpu_ptr->hrtimer;
+		/* dw = &met_cpu_ptr->dwork; */
+
+		met_cpu_ptr->work_enabled = 0;
+		hrtimer_cancel(hrtimer);
+		pr_debug("!!!!!!!! %s hrtimer_cancel cpu = %d\n",  __FUNCTION__, cpu);
+		/* cancel_delayed_work_sync(dw); */
+	}
+	list_for_each_entry(c, &met_list, list) {
+		if (c->ondiemet_mode == 0) {
+			if ((c->cpu_related) && (c->mode) && (c->stop))
+				c->stop();
+		} else if (c->ondiemet_mode == 1) {
+			if ((c->cpu_related) && (c->mode) && (c->ondiemet_stop))
+				c->ondiemet_stop();
+		} else if (c->ondiemet_mode == 2) {
+			if ((c->cpu_related) && (c->mode) && (c->stop))
+				c->stop();
+			if ((c->cpu_related) && (c->mode) && (c->ondiemet_stop))
+				c->ondiemet_stop();
+		}
+		*(this_cpu_ptr(c->polling_count)) = 0;
+	}
+}
+
+static int met_pmu_cpu_notify(struct notifier_block *self, unsigned long action, void *hcpu)
+{
+	struct met_cpu_struct *met_cpu_ptr;
+	struct delayed_work *dw;
+	long cpu = (long)hcpu;
+	int preferred_polling_cpu;
+	struct metdevice *c;
+
+	pr_debug("!!!!!!!! %s_%ld, action=%d\n", __FUNCTION__, cpu, action);
+	if (start == 0)
+		return NOTIFY_OK;
+
+#if	defined(DEBUG_CPU_NOTIFY)
+	{
+		char msg[32];
+
+		snprintf(msg, sizeof(msg), "met_cpu notify_%ld", cpu);
+		dbg_met_tag_oneshot(0, msg, action);
+	}
+#elif	defined(PR_CPU_NOTIFY)
+	{
+		char msg[32];
+
+		if (met_cpu_notify) {
+			snprintf(msg, sizeof(msg), "met_cpu notify_%ld", cpu);
+			dbg_met_tag_oneshot(0, msg, action);
+		}
+	}
+#endif
+
+	if (cpu < 0 || cpu >= ARRAY_SIZE(preferred_cpu_list))
+		return NOTIFY_OK;
+
+	switch (action) {
+	case CPU_ONLINE:
+	case CPU_ONLINE_FROZEN:
+		met_cpu_ptr = &per_cpu(met_cpu, cpu);
+		met_cpu_ptr->hrtimer_online_check = 1;
+		dbg_met_tag_oneshot(0, "met_online check", cpu);
+
+		if (cpu_related_cnt == 0) {
+			/*printk("%s, %d: curr_polling_cpu is alive = %d\n",
+			 *		__func__, __LINE__, online_cpu_map & (1 << curr_polling_cpu));
+			 */
+
+			online_cpu_map |= (1 << cpu);
+
+			/* check curr_polling_cpu is alive, if it is down,
+			 * start current cpu hrtimer, and change it to be currr_pollling_cpu
+			 */
+			if ((online_cpu_map & (1 << curr_polling_cpu)) == 0) {
+				met_smp_call_function_single_symbol(cpu, __met_hrtimer_start, NULL, 1);
+				curr_polling_cpu = cpu;
+			}
+		} else
+			met_smp_call_function_single_symbol(cpu, __met_hrtimer_start, NULL, 1);
+
+#ifdef CONFIG_CPU_FREQ
+		force_power_log(cpu);
+#endif
+		list_for_each_entry(c, &met_list, list) {
+			if (c->cpu_state_notify)
+				c->cpu_state_notify(cpu, action);
+		}
+		break;
+
+	case CPU_DOWN_PREPARE:
+	case CPU_DOWN_PREPARE_FROZEN:
+		list_for_each_entry(c, &met_list, list) {
+			if (c->cpu_state_notify)
+				c->cpu_state_notify(cpu, action);
+		}
+
+		online_cpu_map &= ~(1 << cpu);
+		dbg_met_tag_oneshot(0, "met_offline cpu", cpu);
+		if (cpu == curr_polling_cpu) {
+			/* printk("%s, %d: curr_polling_cpu %d is down\n",
+			 *		__func__, __LINE__, curr_polling_cpu);
+			 */
+			preferred_polling_cpu = calc_preferred_polling_cpu(online_cpu_map);
+			/* printk("%s, %d: preferred_polling_cpu = %d\n",
+			 *		__func__, __LINE__, preferred_polling_cpu);
+			 */
+			if (preferred_polling_cpu != -1) {
+				curr_polling_cpu = preferred_polling_cpu;
+				dbg_met_tag_oneshot(0, "met_curr polling cpu", curr_polling_cpu);
+
+				if (cpu_related_cnt == 0)
+					/* printk("%s, %d: start cpu %d hrtimer start\n",
+					 *		__func__, __LINE__, curr_polling_cpu);
+					 */
+					met_smp_call_function_single_symbol(curr_polling_cpu, __met_hrtimer_start, NULL, 1);
+			}
+		}
+
+		met_smp_call_function_single_symbol(cpu, __met_hrtimer_stop, NULL, 1);
+
+		met_cpu_ptr = &per_cpu(met_cpu, cpu);
+		dw = &met_cpu_ptr->dwork;
+		cancel_delayed_work_sync(dw);
+
+		/* sync_samples(cpu); */
+		break;
+
+	case CPU_DOWN_FAILED:
+	case CPU_DOWN_FAILED_FROZEN:
+		met_cpu_ptr = &per_cpu(met_cpu, cpu);
+		met_cpu_ptr->hrtimer_online_check = 1;
+		dbg_met_tag_oneshot(0, "met_online check", cpu);
+
+		met_smp_call_function_single_symbol(cpu, __met_hrtimer_start, NULL, 1);
+		list_for_each_entry(c, &met_list, list) {
+			if (c->cpu_state_notify)
+				c->cpu_state_notify(cpu, action);
+		}
+		break;
+
+	case CPU_DEAD:
+	case CPU_DEAD_FROZEN:
+		list_for_each_entry(c, &met_list, list) {
+			if (c->cpu_state_notify)
+				c->cpu_state_notify(cpu, action);
+		}
+#ifdef CONFIG_CPU_FREQ
+		force_power_log_val(0, cpu);
+#endif
+		break;
+
+	default:
+		list_for_each_entry(c, &met_list, list) {
+			if (c->cpu_state_notify)
+				c->cpu_state_notify(cpu, action);
+		}
+	}
+
+	return NOTIFY_OK;
+}
+
+static struct notifier_block __refdata met_pmu_cpu_notifier = {
+	.notifier_call = met_pmu_cpu_notify,
+};
+
+int sampler_start(void)
+{
+	int ret, cpu;
+	struct met_cpu_struct *met_cpu_ptr;
+	struct metdevice *c;
+	int preferred_polling_cpu;
+
+	met_set_suspend_notify(0);
+
+#ifdef	CONFIG_CPU_FREQ
+	force_power_log(POWER_LOG_ALL);
+#endif
+
+	for_each_possible_cpu(cpu) {
+		met_cpu_ptr = &per_cpu(met_cpu, cpu);
+		met_cpu_ptr->work_enabled = 0;
+		met_cpu_ptr->hrtimer_online_check = 0;
+		hrtimer_init(&met_cpu_ptr->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+		met_cpu_ptr->hrtimer.function = met_hrtimer_notify;
+		INIT_DELAYED_WORK(&met_cpu_ptr->dwork, wq_sync_buffer);
+	}
+
+	start = 0;
+	ret = register_hotcpu_notifier(&met_pmu_cpu_notifier);
+
+	list_for_each_entry(c, &met_list, list) {
+
+		if (try_module_get(c->owner) == 0)
+			continue;
+
+		if ((c->mode) && (c->cpu_related == 1))
+			cpu_related_cnt = 1;
+
+		if (c->ondiemet_mode == 0) {
+			if ((!(c->cpu_related)) && (c->mode) && (c->start))
+				c->start();
+			else if ((c->cpu_related) && (c->mode) && (c->uniq_start))
+				c->uniq_start();
+		} else if (c->ondiemet_mode == 1) {
+			if ((!(c->cpu_related)) && (c->mode) && (c->ondiemet_start))
+				c->ondiemet_start();
+		} else if (c->ondiemet_mode == 2) {
+			if ((!(c->cpu_related)) && (c->mode) && (c->start))
+				c->start();
+			else if ((c->cpu_related) && (c->mode) && (c->uniq_start))
+				c->uniq_start();
+
+			if ((!(c->cpu_related)) && (c->mode) && (c->ondiemet_start))
+				c->ondiemet_start();
+		}
+	}
+
+	get_online_cpus();
+	online_cpu_map = 0;
+	for_each_online_cpu(cpu) {
+		online_cpu_map |= (1 << cpu);
+	}
+	dbg_met_tag_oneshot(0, "met_online cpu map", online_cpu_map);
+	pr_debug("!!!!!!!! %s met_online cpu map = 0x%8X\n",  __FUNCTION__, online_cpu_map);
+	preferred_polling_cpu = calc_preferred_polling_cpu(online_cpu_map);
+	if (preferred_polling_cpu != -1)
+		curr_polling_cpu = preferred_polling_cpu;
+	dbg_met_tag_oneshot(0, "met_curr polling cpu", curr_polling_cpu);
+	start = 1;
+
+	if (cpu_related_cnt == 0)
+		met_smp_call_function_single_symbol(curr_polling_cpu, __met_hrtimer_start, NULL, 1);
+	else {
+		//on_each_cpu(__met_hrtimer_start, NULL, 1);
+		for_each_online_cpu(cpu) {
+			met_smp_call_function_single_symbol(cpu, __met_hrtimer_start, NULL, 1);
+		}
+	}
+	put_online_cpus();
+
+	return ret;
+}
+
+void sampler_stop(void)
+{
+	int cpu;
+	struct met_cpu_struct *met_cpu_ptr;
+	struct metdevice *c;
+	struct delayed_work *dw;
+
+
+	get_online_cpus();
+	//on_each_cpu(__met_hrtimer_stop, NULL, 1);
+	online_cpu_map = 0;
+	for_each_online_cpu(cpu) {
+		online_cpu_map |= (1 << cpu);
+	}
+	pr_debug("!!!!!!!! %s met_online cpu map = 0x%8X\n",  __FUNCTION__, online_cpu_map);
+	for_each_online_cpu(cpu) {
+		met_smp_call_function_single_symbol(cpu, __met_hrtimer_stop, NULL, 1);
+	}
+
+	/* for_each_online_cpu(cpu) { */
+	for_each_possible_cpu(cpu) {	/* Just for case */
+		met_cpu_ptr = &per_cpu(met_cpu, cpu);
+		dw = &met_cpu_ptr->dwork;
+		cancel_delayed_work_sync(dw);
+		/* sync_samples(cpu); */
+	}
+	start = 0;
+	put_online_cpus();
+
+	unregister_hotcpu_notifier(&met_pmu_cpu_notifier);
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->ondiemet_mode == 0) {
+			if ((!(c->cpu_related)) && (c->mode) && (c->stop))
+				c->stop();
+			else if ((c->cpu_related) && (c->mode) && (c->uniq_stop))
+				c->uniq_stop();
+		} else if (c->ondiemet_mode == 1) {
+			if ((!(c->cpu_related)) && (c->mode) && (c->ondiemet_stop))
+				c->ondiemet_stop();
+		} else if (c->ondiemet_mode == 2) {
+			if ((!(c->cpu_related)) && (c->mode) && (c->stop))
+				c->stop();
+			else if ((c->cpu_related) && (c->mode) && (c->uniq_stop))
+				c->uniq_stop();
+
+			if ((!(c->cpu_related)) && (c->mode) && (c->ondiemet_stop))
+				c->ondiemet_stop();
+		}
+		module_put(c->owner);
+	}
+
+	cpu_related_cnt = 0;
+}
+
+#if 0 /* cann't use static now */
+enum {
+	MET_SUSPEND = 1,
+	MET_RESUME = 2,
+};
+
+static noinline void tracing_mark_write(int op)
+{
+	switch (op) {
+	case MET_SUSPEND:
+		MET_TRACE("C|0|MET_SUSPEND|1");
+		break;
+	case MET_RESUME:
+		MET_TRACE("C|0|MET_SUSPEND|0");
+		break;
+	}
+}
+#endif
+
+int met_hrtimer_suspend(void)
+{
+	struct metdevice *c;
+
+	met_set_suspend_notify(1);
+	/* tracing_mark_write(MET_SUSPEND); */
+	tracing_mark_write(TYPE_MET_SUSPEND, 0, 0, 0, 0, 0);
+	if (start == 0)
+		return 0;
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->suspend)
+			c->suspend();
+	}
+
+	/* get current COUNT */
+	MET_TRACE("TS: %llu GPT: %llX", sched_clock(), arch_counter_get_cntvct());
+	return 0;
+}
+
+void met_hrtimer_resume(void)
+{
+	struct metdevice *c;
+
+	/* get current COUNT */
+	MET_TRACE("TS: %llu GPT: %llX", sched_clock(), arch_counter_get_cntvct());
+
+	/* tracing_mark_write(MET_RESUME); */
+	tracing_mark_write(TYPE_MET_RESUME, 0, 0, 0, 0, 0);
+	if (start == 0)
+		return;
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->resume)
+			c->resume();
+	}
+}
+
+/*
+ * event timer:
+ * register IRQ, sched_switch event to monitor Polling count
+ * count can be printed at any live cpu.
+ */
+void met_event_timer_notify(void)
+{
+	unsigned long long stamp;
+	struct metdevice *c;
+	int cpu = -1;
+
+	if (start == 0)
+		return;
+
+	cpu = smp_processor_id();
+	list_for_each_entry(c, &met_list, list) {
+		stamp = local_clock();
+
+		if (c->prev_stamp == 0)
+			c->prev_stamp = stamp;
+
+		/* Critical Section Start */
+		/* try spinlock to prevent a event print twice between config time interval */
+		if (!spin_trylock(&(c->my_lock)))
+			continue;
+
+		/*
+		 * DEFAULT_HRTIMER_EXPIRE (met_hrtimer_expire):
+		 * sample_rate == 0 --> always print
+		 * sample_rate == 1000 --> print interval larger than 1 ms
+		 */
+		if (DEFAULT_HRTIMER_EXPIRE == 0 || (stamp - c->prev_stamp) < DEFAULT_HRTIMER_EXPIRE) {
+			spin_unlock(&(c->my_lock));
+			continue;
+		}
+
+		c->prev_stamp = stamp;
+		spin_unlock(&(c->my_lock));
+		/* Critical Section End */
+
+		if ((c->mode == 0) || (c->timed_polling == NULL))
+			continue;
+
+		stamp = local_clock();
+		c->timed_polling(stamp, cpu);
+	}
+}
+
diff --git a/src/devtools/met-driver/4.9/common/sampler.h b/src/devtools/met-driver/4.9/common/sampler.h
new file mode 100644
index 0000000..6089c2d
--- /dev/null
+++ b/src/devtools/met-driver/4.9/common/sampler.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _SAMPLER_H_
+#define _SAMPLER_H_
+
+/*
+ * sampling rate: 1ms
+ * log generating rate: 10ms
+ */
+#if 0
+#define DEFAULT_TIMER_EXPIRE (HZ / 100)
+#define DEFAULT_HRTIMER_EXPIRE (TICK_NSEC / 10)
+#else
+extern int met_timer_expire;	/* in jiffies */
+extern int met_hrtimer_expire;	/* in us */
+#define DEFAULT_TIMER_EXPIRE (met_timer_expire)
+#define DEFAULT_HRTIMER_EXPIRE (met_hrtimer_expire)
+#endif
+/*
+ * sampling rate: 10ms
+ * log generating rate: 100ms
+ */
+/* #define DEFAULT_TIMER_EXPIRE (HZ / 10) */
+/* #define DEFAULT_HRTIMER_EXPIRE (TICK_NSEC / 1) */
+
+int met_hrtimer_start(void);
+void met_hrtimer_stop(void);
+int sampler_start(void);
+void sampler_stop(void);
+
+extern struct list_head met_list;
+extern void add_cookie(struct pt_regs *regs, int cpu);
+extern int met_hrtimer_suspend(void);
+extern void met_hrtimer_resume(void);
+extern void met_event_timer_notify(void);
+
+#ifdef CONFIG_CPU_FREQ
+#include "power.h"
+#endif
+
+#endif				/* _SAMPLER_H_ */
diff --git a/src/devtools/met-driver/4.9/common/sspm/ondiemet_sspm.c b/src/devtools/met-driver/4.9/common/sspm/ondiemet_sspm.c
new file mode 100644
index 0000000..8c5711b
--- /dev/null
+++ b/src/devtools/met-driver/4.9/common/sspm/ondiemet_sspm.c
@@ -0,0 +1,494 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/delay.h>
+#include <linux/module.h> /* symbol_get */
+
+#define MET_USER_EVENT_SUPPORT
+#include "met_drv.h"
+
+#if defined(CONFIG_MTK_TINYSYS_SSPM_SUPPORT) && defined(ONDIEMET_SUPPORT)
+#include "ondiemet_sspm.h"
+
+#if defined(CONFIG_MTK_GMO_RAM_OPTIMIZE) || defined(CONFIG_MTK_MET_MEM_ALLOC)
+#ifdef CONFIG_MET_ARM_32BIT
+#include <asm/dma-mapping.h> /* arm_coherent_dma_ops */
+#else /* CONFIG_MET_ARM_32BIT */
+#include <linux/dma-mapping.h>
+#endif /* CONFIG_MET_ARM_32BIT */
+#else /* CONFIG_MTK_GMO_RAM_OPTIMIZE */
+#include "sspm_reservedmem.h"
+#include "sspm_reservedmem_define.h"
+#endif /* CONFIG_MTK_GMO_RAM_OPTIMIZE */
+
+dma_addr_t ondiemet_sspm_log_phy_addr;
+void *ondiemet_sspm_log_virt_addr;
+uint32_t ondiemet_sspm_log_size = 0x400000;
+
+/* SSPM_LOG_FILE 0 */
+/* SSPM_LOG_SRAM 1 */
+/* SSPM_LOG_DRAM 2 */
+int sspm_log_mode;
+/* SSPM_RUN_NORMAL mode 0 */
+/* SSPM_RUN_CONTINUOUS mode 1 */
+int sspm_run_mode;
+int met_sspm_log_discard = -1;
+int sspm_log_size = 500;
+
+int sspm_buffer_size;
+int sspm_buf_available;
+EXPORT_SYMBOL(sspm_buf_available);
+int sspm_buf_mapped = -1; /* get buffer by MET itself */
+
+static ssize_t sspm_buffer_size_show(struct device *dev, struct device_attribute *attr, char *buf);
+static DEVICE_ATTR(sspm_buffer_size, 0444, sspm_buffer_size_show, NULL);
+
+static ssize_t sspm_available_show(struct device *dev, struct device_attribute *attr, char *buf);
+static DEVICE_ATTR(sspm_available, 0444, sspm_available_show, NULL);
+
+static ssize_t sspm_log_discard_show(struct device *dev, struct device_attribute *attr, char *buf);
+static DEVICE_ATTR(sspm_log_discard, 0444, sspm_log_discard_show, NULL);
+
+static ssize_t sspm_log_mode_show(struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t sspm_log_mode_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
+static DEVICE_ATTR(sspm_log_mode, 0664, sspm_log_mode_show, sspm_log_mode_store);
+
+static ssize_t sspm_log_size_show(struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t sspm_log_size_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
+static DEVICE_ATTR(sspm_log_size, 0664, sspm_log_size_show, sspm_log_size_store);
+
+
+static ssize_t sspm_run_mode_show(struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t sspm_run_mode_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
+static DEVICE_ATTR(sspm_run_mode, 0664, sspm_run_mode_show, sspm_run_mode_store);
+
+static ssize_t sspm_modules_show(struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t sspm_modules_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
+static DEVICE_ATTR(sspm_modules, 0664, sspm_modules_show, sspm_modules_store);
+
+static ssize_t sspm_op_ctrl_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
+static DEVICE_ATTR(sspm_op_ctrl, 0220, NULL, sspm_op_ctrl_store);
+
+static ssize_t sspm_buffer_size_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int i;
+
+	mutex_lock(&dev->mutex);
+	i = snprintf(buf, PAGE_SIZE, "%d\n", sspm_buffer_size);
+	mutex_unlock(&dev->mutex);
+	return i;
+}
+
+static ssize_t sspm_available_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int i;
+
+	mutex_lock(&dev->mutex);
+	i = snprintf(buf, PAGE_SIZE, "%d\n", 1);
+	mutex_unlock(&dev->mutex);
+	return i;
+}
+
+static ssize_t sspm_log_discard_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int i;
+
+	mutex_lock(&dev->mutex);
+	i = snprintf(buf, PAGE_SIZE, "%d\n", met_sspm_log_discard);
+	mutex_unlock(&dev->mutex);
+	return i;
+}
+
+static ssize_t sspm_log_mode_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int i;
+
+	mutex_lock(&dev->mutex);
+	i = snprintf(buf, PAGE_SIZE, "%d\n", sspm_log_mode);
+	mutex_unlock(&dev->mutex);
+	return i;
+}
+
+static ssize_t sspm_log_mode_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+	int value;
+
+	if (kstrtoint(buf, 0, &value) != 0)
+		return -EINVAL;
+	mutex_lock(&dev->mutex);
+	sspm_log_mode = value;
+	mutex_unlock(&dev->mutex);
+	return count;
+}
+
+
+static ssize_t sspm_log_size_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int i;
+
+	mutex_lock(&dev->mutex);
+	i = snprintf(buf, PAGE_SIZE, "%d\n", sspm_log_size);
+	mutex_unlock(&dev->mutex);
+	return i;
+}
+
+static ssize_t sspm_log_size_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+	int value;
+
+	if (kstrtoint(buf, 0, &value) != 0)
+		return -EINVAL;
+	mutex_lock(&dev->mutex);
+	sspm_log_size = value;
+	mutex_unlock(&dev->mutex);
+	return count;
+}
+
+
+static ssize_t sspm_run_mode_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int i;
+
+	mutex_lock(&dev->mutex);
+	i = snprintf(buf, PAGE_SIZE, "%d\n", sspm_run_mode);
+	mutex_unlock(&dev->mutex);
+	return i;
+}
+
+static ssize_t sspm_run_mode_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+	int value;
+
+	if (kstrtoint(buf, 0, &value) != 0)
+		return -EINVAL;
+	mutex_lock(&dev->mutex);
+	sspm_run_mode = value;
+	mutex_unlock(&dev->mutex);
+	return count;
+}
+
+static ssize_t sspm_op_ctrl_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+	int value;
+
+	if (kstrtoint(buf, 0, &value) != 0)
+		return -EINVAL;
+	mutex_lock(&dev->mutex);
+	if (value == 1)
+		sspm_start();
+	else if (value == 2)
+		sspm_stop();
+	else if (value == 3)
+		sspm_extract();
+	else if (value == 4)
+		sspm_flush();
+	mutex_unlock(&dev->mutex);
+	return count;
+}
+
+static ssize_t sspm_modules_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int i;
+
+	mutex_lock(&dev->mutex);
+	i = snprintf(buf, PAGE_SIZE, "%x\n", ondiemet_module[ONDIEMET_SSPM]);
+	mutex_unlock(&dev->mutex);
+	return i;
+}
+
+static ssize_t sspm_modules_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+	uint32_t value;
+
+	if (kstrtouint(buf, 0, &value) != 0)
+		return -EINVAL;
+	mutex_lock(&dev->mutex);
+	ondiemet_module[ONDIEMET_SSPM] = value;
+	mutex_unlock(&dev->mutex);
+	return count;
+}
+
+int sspm_attr_init(struct device *dev)
+{
+	int ret;
+
+#if defined(CONFIG_MTK_GMO_RAM_OPTIMIZE) || defined(CONFIG_MTK_MET_MEM_ALLOC)
+#ifdef CONFIG_MET_ARM_32BIT
+	struct dma_map_ops *ops = (struct dma_map_ops *)symbol_get(arm_coherent_dma_ops);
+
+	if (ops && ops->alloc) {
+		dev->coherent_dma_mask = DMA_BIT_MASK(32);
+		ondiemet_sspm_log_virt_addr = ops->alloc(dev,
+						ondiemet_sspm_log_size,
+						&ondiemet_sspm_log_phy_addr,
+						GFP_KERNEL,
+						0);
+	}
+#else /* CONFIG_MET_ARM_32BIT */
+	/* dma_alloc */
+	ondiemet_sspm_log_virt_addr = dma_alloc_coherent(dev,
+			ondiemet_sspm_log_size,
+			&ondiemet_sspm_log_phy_addr,
+			GFP_KERNEL);
+#endif /* CONFIG_MET_ARM_32BIT */
+#else /* CONFIG_MTK_GMO_RAM_OPTIMIZE */
+
+	phys_addr_t (*sspm_reserve_mem_get_phys_sym)(unsigned int id) = NULL;
+	phys_addr_t (*sspm_reserve_mem_get_virt_sym)(unsigned int id) = NULL;
+	phys_addr_t (*sspm_reserve_mem_get_size_sym)(unsigned int id) = NULL;
+
+	sspm_reserve_mem_get_phys_sym = (phys_addr_t (*)(unsigned int id))symbol_get(sspm_reserve_mem_get_virt);
+	sspm_reserve_mem_get_virt_sym = (phys_addr_t (*)(unsigned int id))symbol_get(sspm_reserve_mem_get_phys);
+	sspm_reserve_mem_get_size_sym = (phys_addr_t (*)(unsigned int id))symbol_get(sspm_reserve_mem_get_size);
+	if (sspm_reserve_mem_get_phys_sym)
+		ondiemet_sspm_log_virt_addr = (void*)sspm_reserve_mem_get_virt(MET_MEM_ID);
+	if (sspm_reserve_mem_get_virt_sym)
+		ondiemet_sspm_log_phy_addr = sspm_reserve_mem_get_phys(MET_MEM_ID);
+	if (sspm_reserve_mem_get_size_sym)
+		ondiemet_sspm_log_size = sspm_reserve_mem_get_size(MET_MEM_ID);
+#endif /* CONFIG_MTK_GMO_RAM_OPTIMIZE */
+
+	ret = device_create_file(dev, &dev_attr_sspm_buffer_size);
+	if (ret != 0) {
+		pr_debug("can not create device file: sspm_buffer_size\n");
+		return ret;
+	}
+
+	ret = device_create_file(dev, &dev_attr_sspm_available);
+	if (ret != 0) {
+		pr_debug("can not create device file: sspm_available\n");
+		return ret;
+	}
+
+	ret = device_create_file(dev, &dev_attr_sspm_log_discard);
+	if (ret != 0) {
+		pr_debug("can not create device file: sspm_log_discard\n");
+		return ret;
+	}
+	ret = device_create_file(dev, &dev_attr_sspm_log_mode);
+	if (ret != 0) {
+		pr_debug("can not create device file: sspm_log_mode\n");
+		return ret;
+	}
+	ret = device_create_file(dev, &dev_attr_sspm_log_size);
+	if (ret != 0) {
+		pr_debug("can not create device file: sspm_log_size\n");
+		return ret;
+	}
+	ret = device_create_file(dev, &dev_attr_sspm_run_mode);
+	if (ret != 0) {
+		pr_debug("can not create device file: sspm_run_mode\n");
+		return ret;
+	}
+	ret = device_create_file(dev, &dev_attr_sspm_op_ctrl);
+	if (ret != 0) {
+		pr_debug("can not create device file: sspm_op_ctrl\n");
+		return ret;
+	}
+	ret = device_create_file(dev, &dev_attr_sspm_modules);
+	if (ret != 0) {
+		pr_debug("can not create device file: sspm_modules\n");
+		return ret;
+	}
+
+	if (ondiemet_sspm_log_virt_addr != NULL) {
+		start_sspm_ipi_recv_thread();
+		sspm_buf_available = 1;
+		sspm_buffer_size = ondiemet_sspm_log_size;
+	} else {
+		sspm_buf_available = 0;
+		sspm_buffer_size = -1;
+	}
+
+	return 0;
+}
+
+int sspm_attr_uninit(struct device *dev)
+{
+	/* dma_free */
+	if (ondiemet_sspm_log_virt_addr != NULL) {
+#if defined(CONFIG_MTK_GMO_RAM_OPTIMIZE) || defined(CONFIG_MTK_MET_MEM_ALLOC)
+#ifdef CONFIG_MET_ARM_32BIT
+		struct dma_map_ops *ops = (struct dma_map_ops *)symbol_get(arm_coherent_dma_ops);
+
+		if (ops && ops->free) {
+			ops->free(dev,
+				ondiemet_sspm_log_size,
+				ondiemet_sspm_log_virt_addr,
+				ondiemet_sspm_log_phy_addr,
+				0);
+		}
+#else /* CONFIG_MET_ARM_32BIT */
+		dma_free_coherent(dev,
+			ondiemet_sspm_log_size,
+			ondiemet_sspm_log_virt_addr,
+			ondiemet_sspm_log_phy_addr);
+#endif /* CONFIG_MET_ARM_32BIT */
+#endif /* CONFIG_MTK_GMO_RAM_OPTIMIZE */
+		ondiemet_sspm_log_virt_addr = NULL;
+		stop_sspm_ipi_recv_thread();
+	}
+
+	device_remove_file(dev, &dev_attr_sspm_buffer_size);
+	device_remove_file(dev, &dev_attr_sspm_available);
+	device_remove_file(dev, &dev_attr_sspm_log_discard);
+	device_remove_file(dev, &dev_attr_sspm_log_mode);
+	device_remove_file(dev, &dev_attr_sspm_log_size);
+	device_remove_file(dev, &dev_attr_sspm_run_mode);
+	device_remove_file(dev, &dev_attr_sspm_op_ctrl);
+	device_remove_file(dev, &dev_attr_sspm_modules);
+
+	return 0;
+}
+
+#if 0 /* move to sspm_attr_init() */
+void sspm_get_buffer_info(void)
+{
+	if (ondiemet_sspm_log_virt_addr != NULL) {
+		sspm_buf_available = 1;
+		sspm_buffer_size = ondiemet_sspm_log_size;
+	} else {
+		sspm_buf_available = 0;
+		sspm_buffer_size = -1;
+	}
+}
+#endif
+
+extern const char *met_get_platform_name(void);
+void sspm_start(void)
+{
+	int32_t ret = 0;
+	uint32_t rdata;
+	uint32_t ipi_buf[4];
+	const char* platform_name = NULL;
+	unsigned int platform_id = 0;
+	met_sspm_log_discard = -1;
+
+	/* clear DRAM buffer */
+	if (ondiemet_sspm_log_virt_addr != NULL)
+		memset_io((void *)ondiemet_sspm_log_virt_addr, 0, ondiemet_sspm_log_size);
+	else
+		return;
+
+	platform_name = met_get_platform_name();
+	if (platform_name) {
+		char buf[5];
+
+		memset(buf, 0x0, 5);
+		memcpy(buf, &platform_name[2], 4);
+		ret = kstrtouint(buf, 10, &platform_id);
+	}
+
+	/* send DRAM physical address */
+	ipi_buf[0] = MET_MAIN_ID | MET_BUFFER_INFO;
+	ipi_buf[1] = (unsigned int)ondiemet_sspm_log_phy_addr; /* address */
+	if (ret == 0)
+		ipi_buf[2] = platform_id;
+	else
+		ipi_buf[2] = 0;
+	ipi_buf[3] = 0;
+	ret = sspm_ipi_send_sync(IPI_ID_MET, IPI_OPT_WAIT, (void *)ipi_buf, 0, &rdata, 1);
+
+	/* start ondiemet now */
+	ipi_buf[0] = MET_MAIN_ID | MET_OP | MET_OP_START;
+	ipi_buf[1] = ondiemet_module[ONDIEMET_SSPM];
+	ipi_buf[2] = sspm_log_mode;
+	ipi_buf[3] = sspm_run_mode;
+	ret = sspm_ipi_send_sync(IPI_ID_MET, IPI_OPT_WAIT, (void *)ipi_buf, 0, &rdata, 1);
+}
+
+void sspm_stop(void)
+{
+	int32_t ret;
+	uint32_t rdata;
+	uint32_t ipi_buf[4];
+
+	if (sspm_buf_available == 1) {
+		ipi_buf[0] = MET_MAIN_ID|MET_OP|MET_OP_STOP;
+		ipi_buf[1] = 0;
+		ipi_buf[2] = 0;
+		ipi_buf[3] = 0;
+		ret = sspm_ipi_send_sync(IPI_ID_MET, IPI_OPT_WAIT, (void *)ipi_buf, 0, &rdata, 1);
+	}
+}
+
+void sspm_extract(void)
+{
+	int32_t ret;
+	uint32_t rdata;
+	uint32_t ipi_buf[4];
+	int32_t count;
+
+	count = 20;
+	if (sspm_buf_available == 1) {
+		while ((sspm_buffer_dumping == 1) && (count != 0)) {
+			msleep(50);
+			count--;
+		}
+		ipi_buf[0] = MET_MAIN_ID|MET_OP|MET_OP_EXTRACT;
+		ipi_buf[1] = 0;
+		ipi_buf[2] = 0;
+		ipi_buf[3] = 0;
+		ret = sspm_ipi_send_sync(IPI_ID_MET, IPI_OPT_WAIT, (void *)ipi_buf, 0, &rdata, 1);
+	}
+
+	if (sspm_run_mode == SSPM_RUN_NORMAL)
+		ondiemet_module[ONDIEMET_SSPM] = 0;
+}
+
+void sspm_flush(void)
+{
+	int32_t ret;
+	uint32_t rdata;
+	uint32_t ipi_buf[4];
+
+	if (sspm_buf_available == 1) {
+		ipi_buf[0] = MET_MAIN_ID|MET_OP|MET_OP_FLUSH;
+		ipi_buf[1] = 0;
+		ipi_buf[2] = 0;
+		ipi_buf[3] = 0;
+		ret = sspm_ipi_send_sync(IPI_ID_MET, IPI_OPT_WAIT, (void *)ipi_buf, 0, &rdata, 1);
+	}
+
+	if (sspm_run_mode == SSPM_RUN_NORMAL)
+		ondiemet_module[ONDIEMET_SSPM] = 0;
+}
+#else /* CONFIG_MTK_TINYSYS_SSPM_SUPPORT && ONDIEMET_SUPPORT */
+int sspm_buffer_size = -1;
+
+int sspm_attr_init(struct device *dev)
+{
+	return 0;
+}
+
+int sspm_attr_uninit(struct device *dev)
+{
+	return 0;
+}
+
+void sspm_start(void)
+{
+}
+
+void sspm_stop(void)
+{
+}
+
+void sspm_extract(void)
+{
+}
+
+void sspm_flush(void)
+{
+}
+
+#endif /* CONFIG_MTK_TINYSYS_SSPM_SUPPORT && ONDIEMET_SUPPORT */
diff --git a/src/devtools/met-driver/4.9/common/sspm/ondiemet_sspm.h b/src/devtools/met-driver/4.9/common/sspm/ondiemet_sspm.h
new file mode 100644
index 0000000..d1baadd
--- /dev/null
+++ b/src/devtools/met-driver/4.9/common/sspm/ondiemet_sspm.h
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __ONDIEMET_SSPM_H
+#define __ONDIEMET_SSPM_H
+
+#if defined(CONFIG_MTK_TINYSYS_SSPM_SUPPORT) && defined(ONDIEMET_SUPPORT)
+#include "ondiemet.h"
+#include "sspm_ipi.h"
+#include <linux/dma-mapping.h>
+
+/* we may use IPI_ID_PLATFORM for mt6759 to reduce SRAM */
+#ifndef IPI_ID_MET
+/* #define IPI_ID_MET IPI_ID_TST1 */
+#define IPI_ID_MET IPI_ID_PLATFORM
+#endif
+
+/* MET IPI command definition: mbox 0 */
+/* main func ID: bit[31-24]; sub func ID: bit[23-18]; argu 0: bit[17-0] */
+#define MET_MAIN_ID_MASK        0xff000000 /* bit 31 - 24 */
+#define MET_SUB_ID_MASK         0x00fc0000 /* bit 23 - 18 */
+#define MET_ARGU0_MASK          0x0003ffff /* bit 17 - 0 */
+#define FUNC_BIT_SHIFT          18
+#define MID_BIT_SHIFT           9
+#define MET_MAIN_ID             0x06000000
+/* handle argument and attribute */
+#define PROCESS_ARGU            0x01
+#define PROCESS_ATTR            0x02
+#define MODULE_ID_MASK          0x3fe00 /* bit 9 - 17 */
+#define ARGUMENT_MASK           0x01ff  /* bit 0 - 8 */
+
+/* the following command is used for AP to MD32 */
+#define MET_OP            (1 << FUNC_BIT_SHIFT)
+/* argu 0: start: 0x01; stop: 0x02; extract: 0x03 */
+#define MET_OP_START        0x00000001
+#define MET_OP_STOP         0x00000002
+#define MET_OP_EXTRACT      0x00000003
+#define MET_OP_FLUSH        0x00000004
+#define MET_SR            (2 << FUNC_BIT_SHIFT) /* sample rate */
+#define MET_MODULE        (3 << FUNC_BIT_SHIFT) /* module enable/disable */
+#define MET_ARGU          (4 << FUNC_BIT_SHIFT) /* argument passing */
+#define MET_ATTR          (5 << FUNC_BIT_SHIFT) /* attribute passing */
+/* system memory information for on-die-met log data */
+#define MET_BUFFER_INFO   (6 << FUNC_BIT_SHIFT)
+#define MET_TIMESTAMP     (7 << FUNC_BIT_SHIFT) /* timestamp info */
+#define MET_GPT           (8 << FUNC_BIT_SHIFT) /* GPT counter reading */
+#define MET_REQ_AP2MD     (9 << FUNC_BIT_SHIFT) /* user defined command */
+#define MET_RESP_AP2MD    (10 << FUNC_BIT_SHIFT) /* may no need */
+/* mode: bit 15 - 0: */
+/*  Bit 0: MD32 SRAM mode; Bit 1: System DRAM mode */
+/*  value: 0: output to next level of storage; 1: loop in its own storage */
+#define MET_DATA_MODE     (11 << FUNC_BIT_SHIFT) /* log output mode */
+/* start/stop read data into MD32 SRAM buffer; both DMA and met_printf() */
+#define MET_DATA_OP       (12 << FUNC_BIT_SHIFT) /* data read operation */
+/* the following command is used for MD32 to AP */
+#define MET_DUMP_BUFFER   (13 << FUNC_BIT_SHIFT)
+#define MET_REQ_MD2AP     (14 << FUNC_BIT_SHIFT) /* user defined command */
+#define MET_CLOSE_FILE    (15 << FUNC_BIT_SHIFT) /* Inform to close the SD file */
+#define MET_RESP_MD2AP    (16 << FUNC_BIT_SHIFT)
+#define MET_RUN_MODE      (17 << FUNC_BIT_SHIFT)
+
+/* Note: the module ID and its bit pattern should be fixed as below */
+/* DMA based module first */
+enum {
+	MID_PMQOS = 0,
+	MID_VCORE_DVFS,
+	MID_EMI,
+	MID_THERMAL_CPU,
+	MID_WALL_TIME,
+	MID_CPU_DVFS,
+	MID_GPU_DVFS,
+	MID_PTPOD,
+	MID_SPM,
+	MID_PROFILE,
+	MID_MET_TAG,
+	MID_TS,
+	MID_TS_ISR,
+	MID_TS_LAST,
+	MID_TS_ISR_LAST,
+	MID_SRAM_INFO,
+	MID_MET_STOP,
+	MID_IOP_MON,
+	MID_CPU_INFO_MAPPING,
+
+	MID_COMMON = 0x1F
+};
+
+#define ID_PMQOS       (1 << MID_PMQOS)
+#define ID_SMI         (1 << MID_SMI)
+#define ID_EMI         (1 << MID_EMI)
+#define ID_THERMAL_CPU (1 << MID_THERMAL_CPU)
+#define ID_WALL_TIME   (1 << MID_WALL_TIME)
+#define ID_CPU_DVFS    (1 << MID_CPU_DVFS)
+#define ID_GPU_DVFS    (1 << MID_GPU_DVFS)
+#define ID_VCORE_DVFS  (1 << MID_VCORE_DVFS)
+#define ID_PTPOD       (1 << MID_PTPOD)
+#define ID_SPM         (1 << MID_SPM)
+#define ID_PROFILE     (1 << MID_PROFILE)
+#define ID_COMMON      (1 << MID_COMMON)
+#define ID_CPU_INFO_MAPPING      (1 << MID_CPU_INFO_MAPPING)
+
+extern void ondiemet_extract(void);
+extern void ondiemet_stop(void);
+extern void ondiemet_start(void);
+
+extern void start_sspm_ipi_recv_thread(void);
+extern void stop_sspm_ipi_recv_thread(void);
+
+extern unsigned int ondiemet_ipi_buf[];
+
+/* extern phys_addr_t ondiemet_sspm_log_phy_addr, ondiemet_sspm_log_virt_addr; */
+extern dma_addr_t ondiemet_sspm_log_phy_addr;
+
+extern void *ondiemet_sspm_log_virt_addr;
+extern uint32_t ondiemet_sspm_log_size;
+
+extern int ondiemet_attr_init(struct device *dev);
+extern int ondiemet_attr_uninit(struct device *dev);
+extern int met_sspm_log_discard;
+
+#define SSPM_LOG_FILE 0
+#define SSPM_LOG_SRAM 1
+#define SSPM_LOG_DRAM 2
+extern int sspm_log_mode;
+#define SSPM_RUN_NORMAL 0
+#define SSPM_RUN_CONTINUOUS 1
+extern int sspm_run_mode;
+
+/* extern void sspm_get_buffer_info(void); */
+extern int sspm_buf_available;
+extern int sspm_buffer_dumping;
+
+void sspm_flush(void);
+
+#endif /* CONFIG_MTK_TINYSYS_SSPM_SUPPORT && ONDIEMET_SUPPORT */
+#endif /* __ONDIEMET_SSPM_H */
diff --git a/src/devtools/met-driver/4.9/common/sspm/sspm_common.c b/src/devtools/met-driver/4.9/common/sspm/sspm_common.c
new file mode 100644
index 0000000..b3e1066
--- /dev/null
+++ b/src/devtools/met-driver/4.9/common/sspm/sspm_common.c
@@ -0,0 +1,266 @@
+#include <linux/string.h>

+#include <linux/slab.h>

+#include <linux/kernel.h>

+

+#include "met_drv.h"

+#include "ondiemet_sspm.h"

+

+

+struct sspm_met_evnet_header {

+	unsigned int rts_event_id;

+	char *rts_event_name;

+	char *chart_line_name;

+	char *key_list;

+};

+

+

+enum {

+	#ifdef MET_SSPM_RTS_EVNET

+	#undef MET_SSPM_RTS_EVNET

+	#endif

+	#define MET_SSPM_RTS_EVNET(rts_event_id, key_list) rts_event_id,

+	#include "met_sspm_rts_event.h"

+

+	/**********************/

+	CUR_MET_RTS_EVENT_NUM,

+	MAX_MET_RTS_EVENT_NUM = 128

+};

+

+

+struct sspm_met_evnet_header met_evnet_header[MAX_MET_RTS_EVENT_NUM] = {

+	#ifdef MET_SSPM_RTS_EVNET

+	#undef MET_SSPM_RTS_EVNET

+	#endif

+	#define MET_SSPM_RTS_EVNET(rts_event_id, key_list) {rts_event_id, #rts_event_id, #rts_event_id, key_list},

+	#include "met_sspm_rts_event.h"

+};

+

+

+static void ondiemet_sspm_start(void);

+static void ondiemet_sspm_stop(void);

+static int ondiemet_sspm_print_help(char *buf, int len);

+static int ondiemet_sspm_process_argument(const char *arg, int len);

+static int ondiemet_sspm_print_header(char *buf, int len);

+

+

+static unsigned int event_id_flag0;

+static unsigned int event_id_flag1;

+static unsigned int event_id_flag2;

+static unsigned int event_id_flag3;

+static char *update_rts_event_tbl[MAX_MET_RTS_EVENT_NUM];

+static char sspm_help[] = "  --sspm_common=rts_event_name\n";

+static char header[] = 	"met-info [000] 0.0: sspm_common_header: ";

+

+struct metdevice met_sspm_common = {

+	.name = "sspm_common",

+	.owner = THIS_MODULE,

+	.type = MET_TYPE_BUS,

+	.cpu_related = 0,

+	.ondiemet_mode = 1,

+	.ondiemet_start = ondiemet_sspm_start,

+	.ondiemet_stop = ondiemet_sspm_stop,

+	.ondiemet_process_argument = ondiemet_sspm_process_argument,

+	.ondiemet_print_help = ondiemet_sspm_print_help,

+	.ondiemet_print_header = ondiemet_sspm_print_header,

+};

+

+

+static int ondiemet_sspm_print_help(char *buf, int len)

+{

+	return snprintf(buf, PAGE_SIZE, sspm_help);

+}

+

+

+static int ondiemet_sspm_print_header(char *buf, int len)

+{

+	int i;

+	int write_len;

+	int flag = 0;

+	static int is_dump_header = 0;

+	static int read_idx = 0;

+

+	len = 0;

+	met_sspm_common.header_read_again = 0;

+	if (is_dump_header == 0) {

+		len = snprintf(buf, PAGE_SIZE, "%s", header);

+		is_dump_header = 1;

+	}

+

+	for (i=read_idx; i<MAX_MET_RTS_EVENT_NUM; i++) {

+		if (met_evnet_header[i].chart_line_name) {

+			if (i <32) {

+				flag = 1<<i;

+				flag = event_id_flag0 & flag;

+			} else if (i >=32 && i < 64) {

+				flag = 1<<(i-32);

+				flag = event_id_flag1 & flag;

+			} else if (i >=64 && i < 96) {

+				flag = 1<<(i-64);

+				flag = event_id_flag2 & flag;

+			} else if (i >=96 && i < 128) {

+				flag = 1<<(i-96);

+				flag = event_id_flag3 & flag;

+			}

+			if (flag == 0)

+				continue;

+

+			write_len = strlen(met_evnet_header[i].chart_line_name) + strlen(met_evnet_header[i].key_list) + 3;

+			if ((len+write_len) < PAGE_SIZE) {

+				len += snprintf(buf+len, PAGE_SIZE-len, "%u,%s,%s;",

+					met_evnet_header[i].rts_event_id,

+					met_evnet_header[i].chart_line_name,

+					met_evnet_header[i].key_list);

+			} else {

+				met_sspm_common.header_read_again = 1;

+				read_idx = i;

+				return len;

+			}

+		}

+	}

+

+	if (i == MAX_MET_RTS_EVENT_NUM) {

+		is_dump_header = 0;

+		read_idx = 0;

+		buf[len-1] = '\n';

+		for (i=0; i<MAX_MET_RTS_EVENT_NUM; i++) {

+			if (update_rts_event_tbl[i]) {

+				kfree(update_rts_event_tbl[i]);

+				update_rts_event_tbl[i] = NULL;

+			}

+		}

+		met_sspm_common.mode = 0;

+		event_id_flag0 = 0;

+		event_id_flag1 = 0;

+		event_id_flag2 = 0;

+		event_id_flag3 = 0;

+	}

+

+	return len;

+}

+

+

+static void ondiemet_sspm_start(void)

+{

+	if (sspm_buf_available == 0)

+		return ;

+

+	/* ID_COMMON = 1<<MID_COMMON */

+	ondiemet_module[ONDIEMET_SSPM] |= ID_COMMON;

+}

+

+

+static void ondiemet_sspm_stop(void)

+{

+	if (sspm_buf_available == 0)

+		return ;

+}

+

+

+static void update_event_id_flag(int event_id)

+{

+	unsigned int ipi_buf[4];

+	unsigned int rdata;

+	unsigned int res;

+

+	if (sspm_buf_available == 0)

+		return ;

+

+	/* main func ID: bit[31-24]; sub func ID: bit[23-18]; argu 0: bit[17-0]

+	   #define MET_MAIN_ID_MASK		0xff000000

+	   #define FUNC_BIT_SHIFT		  18

+	   #define MET_ARGU				(4 << FUNC_BIT_SHIFT)

+	   #define MID_BIT_SHIFT		   9

+	*/

+	if (event_id <32) {

+		event_id_flag0 |= 1<<event_id;

+		ipi_buf[0] = MET_MAIN_ID | MET_ARGU | MID_COMMON<<MID_BIT_SHIFT | 1;

+		ipi_buf[1] = 0;

+		ipi_buf[2] = event_id_flag0;

+		ipi_buf[3] = 0;

+		res = sspm_ipi_send_sync(IPI_ID_MET, IPI_OPT_WAIT, (void *)ipi_buf, 0, &rdata, 1);

+	} else if (event_id >=32 && event_id < 64) {

+		event_id_flag1 |= 1<<(event_id-32);

+		ipi_buf[0] = MET_MAIN_ID | MET_ARGU | MID_COMMON<<MID_BIT_SHIFT | 1;

+		ipi_buf[1] = 1;

+		ipi_buf[2] = event_id_flag1;

+		ipi_buf[3] = 0;

+		res = sspm_ipi_send_sync(IPI_ID_MET, IPI_OPT_WAIT, (void *)ipi_buf, 0, &rdata, 1);

+	} else if (event_id >=64 && event_id < 96) {

+		event_id_flag2 |= 1<<(event_id-64);

+		ipi_buf[0] = MET_MAIN_ID | MET_ARGU | MID_COMMON<<MID_BIT_SHIFT | 1;

+		ipi_buf[1] = 2;

+		ipi_buf[2] = event_id_flag2;

+		ipi_buf[3] = 0;

+		res = sspm_ipi_send_sync(IPI_ID_MET, IPI_OPT_WAIT, (void *)ipi_buf, 0, &rdata, 1);

+	} else if (event_id >=96 && event_id < 128) {

+		event_id_flag3 = 1<<(event_id-96);

+		ipi_buf[0] |= MET_MAIN_ID | MET_ARGU | MID_COMMON<<MID_BIT_SHIFT | 1;

+		ipi_buf[1] = 3;

+		ipi_buf[2] = event_id_flag3;

+		ipi_buf[3] = 0;

+		res = sspm_ipi_send_sync(IPI_ID_MET, IPI_OPT_WAIT, (void *)ipi_buf, 0, &rdata, 1);

+	}

+	met_sspm_common.mode = 1;

+}

+

+

+static char *strdup(const char *s)

+{

+	char *p = kmalloc(strlen(s) + 1, GFP_KERNEL);

+

+	if (p)

+		strcpy(p, s);

+	return p;

+}

+

+

+static int ondiemet_sspm_process_argument(const char *arg, int len)

+{

+	int i;

+	int rts_event_id = -1;

+	int res;

+	char *line;

+	char *token;

+

+	for (i=0; met_evnet_header[i].rts_event_name && i<MAX_MET_RTS_EVENT_NUM; i++) {

+		if (strcmp(met_evnet_header[i].rts_event_name, arg) == 0) {

+			rts_event_id = i;

+			break;

+		}

+	}

+	if (strstarts(arg, "update_rts_event_tbl")) {

+		char *ptr;

+

+		/* update_rts_event_tbl=rts_event_id;rts_event_name;chart_line_name;key_list*/

+		line = strdup(arg);

+		if (line == NULL)

+			return -1;

+		ptr = line;

+		token = strsep(&line, "=");

+

+		/* rts_event_id, */

+		token = strsep(&line, ";");

+		res = kstrtoint(token, 10, &rts_event_id);

+		met_evnet_header[rts_event_id].rts_event_id = rts_event_id;

+

+		/* rts_event_name */

+		token = strsep(&line, ";");

+		met_evnet_header[rts_event_id].rts_event_name = token;

+

+		/* chart_line_name */

+		token = strsep(&line, ";");

+		met_evnet_header[rts_event_id].chart_line_name = token;

+

+		/* key_list */

+		token = strsep(&line, ";\n");

+		met_evnet_header[rts_event_id].key_list = token;

+

+		update_rts_event_tbl[rts_event_id] = ptr;

+	}

+

+	if (rts_event_id >=0)

+		update_event_id_flag(rts_event_id);

+

+	return 0;

+}

+EXPORT_SYMBOL(met_sspm_common);

diff --git a/src/devtools/met-driver/4.9/common/sspm/sspm_ipi_handle.c b/src/devtools/met-driver/4.9/common/sspm/sspm_ipi_handle.c
new file mode 100644
index 0000000..161d752
--- /dev/null
+++ b/src/devtools/met-driver/4.9/common/sspm/sspm_ipi_handle.c
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kthread.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/signal.h>
+#include <linux/semaphore.h>
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/syscalls.h>
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/fcntl.h>
+/* #include <asm/uaccess.h> */
+#include <linux/uaccess.h>
+#include <linux/delay.h>
+
+#include "ondiemet_sspm.h"
+#include "ondiemet_log.h"
+#include "interface.h"
+
+static struct ipi_action ondiemet_sspm_isr;
+static uint32_t log_size;
+static uint32_t recv_buf[4];
+static struct task_struct *ondiemet_sspm_recv_task;
+static int sspm_ipi_thread_started;
+int sspm_buffer_dumping;
+int sspm_recv_thread_comp;
+
+void log_done_cb(const void *p)
+{
+	uint32_t ret;
+	uint32_t rdata = 0;
+	uint32_t ipi_buf[4];
+	uint32_t opt = (p != NULL);
+
+	if (opt == 0) {
+		ipi_buf[0] = MET_MAIN_ID | MET_RESP_AP2MD;
+		ipi_buf[1] = MET_DUMP_BUFFER;
+		ipi_buf[2] = 0;
+		ipi_buf[3] = 0;
+		ret = sspm_ipi_send_sync(IPI_ID_MET, IPI_OPT_WAIT, (void *)ipi_buf, 0, &rdata, 1);
+	}
+}
+
+int ondiemet_sspm_recv_thread(void *data)
+{
+	uint32_t rdata = 0, cmd, ret;
+	uint32_t ridx, widx, wlen;
+
+	ondiemet_sspm_isr.data = (void *)recv_buf;
+	ret = sspm_ipi_recv_registration(IPI_ID_TST1, &ondiemet_sspm_isr);
+	do {
+		sspm_ipi_recv_wait(IPI_ID_TST1);
+		if (sspm_recv_thread_comp == 1) {
+			while (!kthread_should_stop())
+				;
+			return 0;
+		}
+		cmd = recv_buf[0] & MET_SUB_ID_MASK;
+		switch (cmd) {
+		case MET_DUMP_BUFFER:	/* mbox 1: start index; 2: size */
+			sspm_buffer_dumping = 1;
+			ridx = recv_buf[1];
+			widx = recv_buf[2];
+			log_size = recv_buf[3];
+			sspm_ipi_send_ack(IPI_ID_TST1, &rdata);
+			if (widx < ridx) {	/* wrapping occurs */
+				wlen = log_size - ridx;
+				ondiemet_log_req_enq((char *)(ondiemet_sspm_log_virt_addr) + (ridx << 2),
+				wlen * 4, log_done_cb, (void *)1);
+				ondiemet_log_req_enq((char *)(ondiemet_sspm_log_virt_addr),
+						     widx * 4, log_done_cb, (void *)0);
+			} else {
+				wlen = widx - ridx;
+				ondiemet_log_req_enq((char *)(ondiemet_sspm_log_virt_addr) + (ridx << 2),
+				wlen * 4, log_done_cb, (void *)0);
+			}
+			break;
+		case MET_CLOSE_FILE:	/* no argument */
+			/* do close file */
+			ridx = recv_buf[1];
+			widx = recv_buf[2];
+			met_sspm_log_discard = recv_buf[3];
+			if (widx < ridx) {	/* wrapping occurs */
+				wlen = log_size - ridx;
+				ondiemet_log_req_enq((char *)(ondiemet_sspm_log_virt_addr) + (ridx << 2),
+				wlen * 4, log_done_cb, (void *)1);
+				ondiemet_log_req_enq((char *)(ondiemet_sspm_log_virt_addr),
+						     widx * 4, log_done_cb, (void *)1);
+			} else {
+				wlen = widx - ridx;
+				ondiemet_log_req_enq((char *)(ondiemet_sspm_log_virt_addr) + (ridx << 2),
+				wlen * 4, log_done_cb, (void *)1);
+			}
+			ret = ondiemet_log_manager_stop();
+			/* pr_debug("MET_CLOSE_FILE: ret=%d log_discard=%d\n", ret, met_sspm_log_discard); */
+			sspm_ipi_send_ack(IPI_ID_TST1, &rdata);
+			if (sspm_run_mode == SSPM_RUN_CONTINUOUS) {
+				/* clear the memory */
+				memset_io((void *)ondiemet_sspm_log_virt_addr, 0,
+					  ondiemet_sspm_log_size);
+				/* re-start ondiemet again */
+				sspm_start();
+			}
+			break;
+		case MET_RESP_MD2AP:
+			sspm_ipi_send_ack(IPI_ID_TST1, &rdata);
+			sspm_buffer_dumping = 0;
+			break;
+		default:
+			sspm_ipi_send_ack(IPI_ID_TST1, &rdata);
+			break;
+		}
+	} while (!kthread_should_stop());
+	return 0;
+}
+
+void start_sspm_ipi_recv_thread(void)
+{
+	if (sspm_ipi_thread_started != 1) {
+		sspm_recv_thread_comp = 0;
+		ondiemet_sspm_recv_task =
+		    kthread_run(ondiemet_sspm_recv_thread, NULL, "ondiemet_sspm_recv");
+		if (IS_ERR(ondiemet_sspm_recv_task))
+			pr_debug("MET: Can not create ondiemet_sspm_recv\n");
+		else
+			sspm_ipi_thread_started = 1;
+	}
+}
+
+void stop_sspm_ipi_recv_thread(void)
+{
+	if (ondiemet_sspm_recv_task) {
+		sspm_recv_thread_comp = 1;
+		sspm_ipi_recv_complete(IPI_ID_TST1);
+		kthread_stop(ondiemet_sspm_recv_task);
+		ondiemet_sspm_recv_task = NULL;
+		sspm_ipi_thread_started = 0;
+		sspm_ipi_recv_unregistration(IPI_ID_TST1);
+	}
+}
+
diff --git a/src/devtools/met-driver/4.9/common/sspm/sspm_walltime.c b/src/devtools/met-driver/4.9/common/sspm/sspm_walltime.c
new file mode 100644
index 0000000..cc12b8b
--- /dev/null
+++ b/src/devtools/met-driver/4.9/common/sspm/sspm_walltime.c
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/random.h>
+#include <linux/fs.h>
+
+#include "met_drv.h"
+#include "trace.h"
+/* #include "plf_init.h" */
+#include "sspm/ondiemet_sspm.h"
+
+static void sspm_walltime_start(void)
+{
+	ondiemet_module[ONDIEMET_SSPM] |= ID_WALL_TIME;
+}
+
+static void sspm_walltime_stop(void)
+{
+}
+
+static const char sspm_walltime_header[] = "met-info [000] 0.0: sspm WCLK: freq\n";
+
+static int sspm_walltime_print_header(char *buf, int len)
+{
+	return snprintf(buf, PAGE_SIZE, sspm_walltime_header);
+}
+
+struct metdevice met_sspm_walltime = {
+	.name = "sspm_wall_time",
+	.owner = THIS_MODULE,
+	.type = MET_TYPE_BUS,
+	.cpu_related = 0,
+	.ondiemet_mode = 0,
+	.print_header = sspm_walltime_print_header,
+	.ondiemet_start = sspm_walltime_start,
+	.ondiemet_stop = sspm_walltime_stop,
+	.ondiemet_print_header = sspm_walltime_print_header,
+};
+EXPORT_SYMBOL(met_sspm_walltime);
diff --git a/src/devtools/met-driver/4.9/common/stat.c b/src/devtools/met-driver/4.9/common/stat.c
new file mode 100644
index 0000000..c84f7a9
--- /dev/null
+++ b/src/devtools/met-driver/4.9/common/stat.c
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/cpu.h>
+#include <linux/cpumask.h>
+/* #include <linux/fs.h> */
+#include <linux/init.h>
+#include <linux/version.h>
+#include <linux/interrupt.h>
+#include <linux/kernel_stat.h>
+/* #include <linux/proc_fs.h> */
+#include <linux/sched.h>
+/* #include <linux/seq_file.h> */
+/* #include <linux/slab.h> */
+#include <linux/time.h>
+#include <linux/irqnr.h>
+#include <linux/vmalloc.h>
+#include <asm/cputime.h>
+/* #include <asm-generic/cputime.h> */
+#include <linux/tick.h>
+/* #include <linux/jiffies.h> */
+
+#include <asm/page.h>
+#include <linux/slab.h>
+
+#include "stat.h"
+#include "met_drv.h"
+#include "trace.h"
+
+#define MS_STAT_FMT	"%5lu.%06lu"
+#define FMTLX7		",%llx,%llx,%llx,%llx,%llx,%llx,%llx\n"
+#define FMTLX10		",%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx\n"
+
+
+static DEFINE_PER_CPU(int, cpu_status);
+
+
+/* void ms_st(unsigned long long timestamp, unsigned char cnt, unsigned int *value) */
+noinline void ms_st(unsigned long long timestamp, unsigned char cnt, u64 *value)
+{
+	unsigned long nano_rem = do_div(timestamp, 1000000000);
+
+	switch (cnt) {
+	case 10:
+		MET_TRACE(MS_STAT_FMT FMTLX10, (unsigned long)(timestamp), (nano_rem/1000),
+			value[0], value[1], value[2], value[3], value[4],
+			value[5], value[6], value[7], value[8], value[9]);
+		break;
+	case 7:
+		MET_TRACE(MS_STAT_FMT FMTLX7, (unsigned long)(timestamp), (nano_rem/1000),
+			value[0], value[1], value[2], value[3], value[4],
+			value[5], value[6]);
+		break;
+	}
+}
+
+static void met_stat_start(void)
+{
+	int	cpu = raw_smp_processor_id();
+
+	if (get_ctrl_flags() & 1)
+		met_stat.mode = 0;
+
+	per_cpu(cpu_status, cpu) = CPU_ONLINE;
+}
+
+static void met_stat_stop(void)
+{
+}
+
+static int do_stat(void)
+{
+	return met_stat.mode;
+}
+
+u64 met_usecs_to_cputime64(u64 n)
+{
+#if (NSEC_PER_SEC % HZ) == 0
+	/* Common case, HZ = 100, 128, 200, 250, 256, 500, 512, 1000 etc. */
+	return div_u64(n, NSEC_PER_SEC / HZ);
+#elif (HZ % 512) == 0
+	/* overflow after 292 years if HZ = 1024 */
+	return div_u64(n * HZ / 512, NSEC_PER_SEC / 512);
+#else
+	/*
+	 * Generic case - optimized for cases where HZ is a multiple of 3.
+	 * overflow after 64.99 years, exact for HZ = 60, 72, 90, 120 etc.
+	 */
+	return div_u64(n * 9, (9ull * NSEC_PER_SEC + HZ / 2) / HZ);
+#endif
+}
+
+static u64 get_idle_time(int cpu)
+{
+	u64 idle, idle_time = get_cpu_idle_time_us(cpu, NULL);
+
+	if (idle_time == -1ULL) {
+		/* !NO_HZ so we can rely on cpustat.idle */
+		idle = kcpustat_cpu(cpu).cpustat[CPUTIME_IDLE];
+	} else
+		idle = met_usecs_to_cputime64(idle_time);
+
+	return idle;
+}
+
+static u64 get_iowait_time(int cpu)
+{
+	u64 iowait, iowait_time = get_cpu_iowait_time_us(cpu, NULL);
+
+	if (iowait_time == -1ULL) {
+		/* !NO_HZ so we can rely on cpustat.iowait */
+		iowait = kcpustat_cpu(cpu).cpustat[CPUTIME_IOWAIT];
+	} else
+		iowait = met_usecs_to_cputime64(iowait_time);
+
+	return iowait;
+}
+
+
+static unsigned int stat_os_polling(u64 *value, int i)
+{
+	int j = -1;
+
+	/* Copy values here to work around gcc-2.95.3, gcc-2.96 */
+	value[++j] = cputime64_to_clock_t(kcpustat_cpu(i).cpustat[CPUTIME_USER]);	/* user */
+	value[++j] = cputime64_to_clock_t(kcpustat_cpu(i).cpustat[CPUTIME_NICE]);	/* nice */
+	value[++j] = cputime64_to_clock_t(kcpustat_cpu(i).cpustat[CPUTIME_SYSTEM]);	/* system */
+	value[++j] = cputime64_to_clock_t(get_idle_time(i));	/* idle */
+	value[++j] = cputime64_to_clock_t(get_iowait_time(i));	/* iowait */
+	value[++j] = cputime64_to_clock_t(kcpustat_cpu(i).cpustat[CPUTIME_IRQ]);	/* irq */
+	value[++j] = cputime64_to_clock_t(kcpustat_cpu(i).cpustat[CPUTIME_SOFTIRQ]);	/* softirq */
+	value[++j] = cputime64_to_clock_t(kcpustat_cpu(i).cpustat[CPUTIME_STEAL]);	/* steal */
+	value[++j] = cputime64_to_clock_t(kcpustat_cpu(i).cpustat[CPUTIME_GUEST]);	/* guest */
+	value[++j] = cputime64_to_clock_t(kcpustat_cpu(i).cpustat[CPUTIME_GUEST_NICE]);	/* guest_nice */
+
+	return j + 1;
+}
+
+static void met_stat_polling(unsigned long long stamp, int cpu)
+{
+	unsigned char count;
+	u64 value[10] = {0};
+
+	if (per_cpu(cpu_status, cpu) != CPU_ONLINE)
+		return;
+
+	/* return; */
+	if (do_stat()) {
+		count = stat_os_polling(value, cpu);
+		if (count)
+			ms_st(stamp, count, value);
+	}
+}
+
+static const char header[] =
+	"met-info [000] 0.0: met_st_header: user,nice,system,idle,iowait,irq,softirq,steal,guest,guest_nice\n";
+
+static const char help[] = "  --stat                                monitor stat\n";
+
+
+static int met_stat_print_help(char *buf, int len)
+{
+	return snprintf(buf, PAGE_SIZE, help);
+}
+
+static int met_stat_print_header(char *buf, int len)
+{
+	return snprintf(buf, PAGE_SIZE, header);
+}
+
+static void met_stat_cpu_state_notify(long cpu, unsigned long action)
+{
+	per_cpu(cpu_status, cpu) = action;
+}
+
+
+struct metdevice met_stat = {
+	.name = "stat",
+	.type = MET_TYPE_PMU,
+	.cpu_related = 1,
+	.start = met_stat_start,
+	.stop = met_stat_stop,
+	.polling_interval = 30,
+	.timed_polling = met_stat_polling,
+	.print_help = met_stat_print_help,
+	.print_header = met_stat_print_header,
+	.cpu_state_notify = met_stat_cpu_state_notify,
+};
diff --git a/src/devtools/met-driver/4.9/common/stat.h b/src/devtools/met-driver/4.9/common/stat.h
new file mode 100644
index 0000000..8aabc5c
--- /dev/null
+++ b/src/devtools/met-driver/4.9/common/stat.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _STAT_H_
+#define _STAT_H_
+
+#include <linux/device.h>
+
+extern struct metdevice met_stat;
+
+int stat_reg(struct kobject *parent);
+void stat_unreg(void);
+
+void stat_start(void);
+void stat_stop(void);
+void stat_polling(unsigned long long stamp, int cpu);
+
+unsigned int get_ctrl_flags(void);
+
+#endif				/* _STAT_H_ */
diff --git a/src/devtools/met-driver/4.9/common/switch.c b/src/devtools/met-driver/4.9/common/switch.c
new file mode 100644
index 0000000..9ea3453
--- /dev/null
+++ b/src/devtools/met-driver/4.9/common/switch.c
@@ -0,0 +1,302 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/* include <asm/percpu.h> */
+#include <trace/events/sched.h>
+#include <linux/module.h>
+#include <trace/events/irq.h>
+#include <trace/events/power.h>
+
+#include "interface.h"
+#include "met_drv.h"
+#include "cpu_pmu.h"
+#include "switch.h"
+#include "sampler.h"
+#include "met_kernel_symbol.h"
+/* #include "trace.h" */
+
+/*
+ * IRQ_TIRGGER and CPU_IDLE_TRIGGER
+ */
+/* #define IRQ_TRIGGER */
+/* #define CPU_IDLE_TRIGGER */
+
+static DEFINE_PER_CPU(unsigned int, first_log);
+
+#ifdef __aarch64__
+/* #include <asm/compat.h> */
+#include <linux/compat.h>
+#endif
+
+noinline void mt_switch(struct task_struct *prev, struct task_struct *next)
+{
+	int cpu;
+	int prev_state = 0, next_state = 0;
+
+#ifdef __aarch64__
+	prev_state = !(is_compat_thread(task_thread_info(prev)));
+	next_state = !(is_compat_thread(task_thread_info(next)));
+#endif
+
+	cpu = smp_processor_id();
+	if (per_cpu(first_log, cpu)) {
+		MET_TRACE("%d, %d, %d, %d\n", prev->pid, prev_state, next->pid, next_state);
+		per_cpu(first_log, cpu) = 0;
+	}
+	if (prev_state != next_state)
+		MET_TRACE("%d, %d, %d, %d\n", prev->pid, prev_state, next->pid, next_state);
+}
+
+
+#if 0 /* move to kernel space */
+MET_DEFINE_PROBE(sched_switch,
+		 TP_PROTO(bool preempt, struct task_struct *prev, struct task_struct *next))
+{
+	/* speedup sched_switch callback handle */
+	if (met_switch.mode == 0)
+		return;
+
+	if (met_switch.mode & MT_SWITCH_EVENT_TIMER)
+		met_event_timer_notify();
+
+	if (met_switch.mode & MT_SWITCH_64_32BIT)
+		mt_switch(prev, next);
+
+	if (met_switch.mode & MT_SWITCH_SCHEDSWITCH) {
+		if (get_pmu_profiling_version() == 1)
+			cpupmu_polling(0, smp_processor_id());
+#ifdef MET_SUPPORT_CPUPMU_V2
+		else if (get_pmu_profiling_version() == 2)
+			cpupmu_polling_v2(0, smp_processor_id());
+#endif
+	}
+}
+#endif
+
+void met_sched_switch(struct task_struct *prev, struct task_struct *next)
+{
+	/* speedup sched_switch callback handle */
+	if (met_switch.mode == 0)
+		return;
+
+	if (met_switch.mode & MT_SWITCH_EVENT_TIMER)
+		met_event_timer_notify();
+
+	if (met_switch.mode & MT_SWITCH_64_32BIT)
+		mt_switch(prev, next);
+
+	/* met_perf_cpupmu_status: 0: stop, others: polling */
+	if ((met_switch.mode & MT_SWITCH_SCHEDSWITCH) && met_perf_cpupmu_status)
+		met_perf_cpupmu_polling(0, smp_processor_id());
+}
+
+#ifdef IRQ_TRIGGER
+MET_DEFINE_PROBE(irq_handler_entry, TP_PROTO(int irq, struct irqaction *action))
+{
+	if (met_switch.mode & MT_SWITCH_EVENT_TIMER) {
+		met_event_timer_notify();
+		return;
+	}
+}
+#endif
+
+#ifdef CPU_IDLE_TRIGGER
+MET_DEFINE_PROBE(cpu_idle, TP_PROTO(unsigned int state, unsigned int cpu_id))
+{
+	if (met_switch.mode & MT_SWITCH_EVENT_TIMER) {
+		met_event_timer_notify();
+		return;
+	}
+}
+#endif
+
+#ifdef MET_ANYTIME
+/*
+ * create related subfs file node
+ */
+
+static ssize_t default_on_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "1\n");
+}
+
+static struct kobj_attribute default_on_attr = __ATTR(default_on, 0664, default_on_show, NULL);
+static struct kobject *kobj_cpu;
+#endif
+
+static int met_switch_create_subfs(struct kobject *parent)
+{
+	int ret = 0;
+
+	/* register tracepoints */
+#if 0
+	if (MET_REGISTER_TRACE(sched_switch)) {
+		pr_debug("can not register callback of sched_switch\n");
+		return -ENODEV;
+	}
+#else
+	if (met_reg_switch_symbol)
+		ret = met_reg_switch_symbol();
+#endif
+#ifdef CPU_IDLE_TRIGGER
+	if (MET_REGISTER_TRACE(cpu_idle)) {
+		pr_debug("can not register callback of irq_handler_entry\n");
+		return -ENODEV;
+	}
+#endif
+#ifdef IRQ_TRIGGER
+	if (MET_REGISTER_TRACE(irq_handler_entry)) {
+		pr_debug("can not register callback of irq_handler_entry\n");
+		return -ENODEV;
+	}
+#endif
+
+#ifdef MET_ANYTIME
+	/*
+	 * to create default_on file node
+	 * let user space can know we can support MET default on
+	 */
+	kobj_cpu = parent;
+	ret = sysfs_create_file(kobj_cpu, &default_on_attr.attr);
+	if (ret != 0) {
+		pr_debug("Failed to create default_on in sysfs\n");
+		return -1;
+	}
+#endif
+
+	return ret;
+}
+
+
+static void met_switch_delete_subfs(void)
+{
+#ifdef MET_ANYTIME
+	if (kobj_cpu != NULL) {
+		sysfs_remove_file(kobj_cpu, &default_on_attr.attr);
+		kobj_cpu = NULL;
+	}
+#endif
+#ifdef IRQ_TRIGGER
+	MET_UNREGISTER_TRACE(irq_handler_entry);
+#endif
+#ifdef CPU_IDLE_TRIGGER
+	MET_UNREGISTER_TRACE(cpu_idle);
+#endif
+#if 0
+	MET_UNREGISTER_TRACE(sched_switch);
+#else
+	if (met_unreg_switch_symbol)
+		met_unreg_switch_symbol();
+#endif
+
+}
+
+
+static void (*cpu_timed_polling)(unsigned long long stamp, int cpu);
+/* static void (*cpu_tagged_polling)(unsigned long long stamp, int cpu); */
+
+static void met_switch_start(void)
+{
+	int cpu;
+
+	if (met_switch.mode & MT_SWITCH_SCHEDSWITCH) {
+		cpu_timed_polling = met_cpupmu.timed_polling;
+		/* cpu_tagged_polling = met_cpupmu.tagged_polling; */
+		met_cpupmu.timed_polling = NULL;
+		/* met_cpupmu.tagged_polling = NULL; */
+	}
+
+	for_each_possible_cpu(cpu) {
+		per_cpu(first_log, cpu) = 1;
+	}
+
+}
+
+
+static void met_switch_stop(void)
+{
+	int cpu;
+
+	if (met_switch.mode & MT_SWITCH_SCHEDSWITCH) {
+		met_cpupmu.timed_polling = cpu_timed_polling;
+		/* met_cpupmu.tagged_polling = cpu_tagged_polling; */
+	}
+
+	for_each_possible_cpu(cpu) {
+		per_cpu(first_log, cpu) = 0;
+	}
+
+}
+
+
+static int met_switch_process_argument(const char *arg, int len)
+{
+	unsigned int value;
+	/*ex: mxitem is 0x0005, max value should be (5-1) + (5-2) = 0x100 + 0x11 = 7 */
+	unsigned int max_value = ((MT_SWITCH_MX_ITEM * 2) - 3);
+
+
+	if (met_parse_num(arg, &value, len) < 0)
+		goto arg_switch_exit;
+
+	if ((value < 1) || (value > max_value))
+		goto arg_switch_exit;
+
+	met_switch.mode = value;
+	return 0;
+
+arg_switch_exit:
+	met_switch.mode = 0;
+	return -EINVAL;
+}
+
+static const char header[] =
+	"met-info [000] 0.0: met_switch_header: prev_pid,prev_state,next_pid,next_state\n";
+
+static const char help[] =
+"  --switch=mode                         mode:0x1 - output CPUPMU whenever sched_switch\n"
+"                                        mode:0x2 - output Aarch 32/64 state whenever state changed (no CPUPMU)\n"
+"                                        mode:0x4 - force output count at tag_start/tag_end\n"
+"                                        mode:0x8 - task switch timer\n"
+"                                        mode:0xF - mode 0x1 + 0x2 + 04 + 08\n";
+
+static int met_switch_print_help(char *buf, int len)
+{
+	return snprintf(buf, PAGE_SIZE, help);
+}
+
+static int met_switch_print_header(char *buf, int len)
+{
+	int ret = 0;
+
+	ret =
+	    snprintf(buf, PAGE_SIZE, "met-info [000] 0.0: mp_cpu_switch_base: %d\n",
+		     met_switch.mode);
+	if (met_switch.mode & MT_SWITCH_64_32BIT)
+		ret += snprintf(buf + ret, PAGE_SIZE, header);
+
+	return ret;
+}
+
+
+struct metdevice met_switch = {
+	.name = "switch",
+	.type = MET_TYPE_PMU,
+	.create_subfs = met_switch_create_subfs,
+	.delete_subfs = met_switch_delete_subfs,
+	.start = met_switch_start,
+	.stop = met_switch_stop,
+	.process_argument = met_switch_process_argument,
+	.print_help = met_switch_print_help,
+	.print_header = met_switch_print_header,
+};
diff --git a/src/devtools/met-driver/4.9/common/switch.h b/src/devtools/met-driver/4.9/common/switch.h
new file mode 100644
index 0000000..14397d7
--- /dev/null
+++ b/src/devtools/met-driver/4.9/common/switch.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __MT_SWITCH__
+#define __MT_SWITCH__
+/*
+ * =========================
+ * !!!!!!!!!!!NOTICE!!!!!!!!
+ * =========================
+ * MT_SWITCH OPTION must delcare as Mask value
+ * And sort them from smallest to largest
+ * MT_SWITCH_MX_ITEM was used to determine argument range
+*/
+enum {
+	/* =================== */
+	/* user define mt switch event */
+	/* =================== */
+	MT_SWITCH_SCHEDSWITCH = 0x0001,
+	MT_SWITCH_64_32BIT = 0x0002,
+	MT_SWITCH_TAGPOLLING = 0x0004,
+	MT_SWITCH_EVENT_TIMER = 0x0008,
+	/* =================== */
+	MT_SWITCH_MX_ITEM
+};
+
+extern struct metdevice met_switch;
+#endif
diff --git a/src/devtools/met-driver/4.9/common/trace.h b/src/devtools/met-driver/4.9/common/trace.h
new file mode 100644
index 0000000..f259b7a
--- /dev/null
+++ b/src/devtools/met-driver/4.9/common/trace.h
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _TRACE_H_
+#define _TRACE_H_
+
+
+extern void (*mp_cp_ptr)(unsigned long long timestamp,
+	       struct task_struct *task,
+	       unsigned long program_counter,
+	       unsigned long dcookie,
+	       unsigned long offset,
+	       unsigned char cnt, unsigned int *value);
+
+#define MP_FMT1	"%x\n"
+#define MP_FMT2	"%x,%x\n"
+#define MP_FMT3	"%x,%x,%x\n"
+#define MP_FMT4	"%x,%x,%x,%x\n"
+#define MP_FMT5	"%x,%x,%x,%x,%x\n"
+#define MP_FMT6	"%x,%x,%x,%x,%x,%x\n"
+#define MP_FMT7	"%x,%x,%x,%x,%x,%x,%x\n"
+#define MP_FMT8	"%x,%x,%x,%x,%x,%x,%x,%x\n"
+#define MP_FMT9	"%x,%x,%x,%x,%x,%x,%x,%x,%x\n"
+#define MP_FMT10 "%x,%x,%x,%x,%x,%x,%x,%x,%x,%x\n"
+#define MP_FMT11 "%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x\n"
+#define MP_FMT12 "%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x\n"
+#define MP_FMT13 "%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x\n"
+#define MP_FMT14 "%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x\n"
+#define MP_FMT15 "%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x\n"
+
+#define MET_GENERAL_PRINT(FUNC, count, value) \
+do { \
+	switch (count) { \
+	case 1: { \
+		FUNC(MP_FMT1, value[0]); \
+		} \
+		break; \
+	case 2: { \
+		FUNC(MP_FMT2, value[0], value[1]); \
+		} \
+		break; \
+	case 3: { \
+		FUNC(MP_FMT3, value[0], value[1], value[2]); \
+		} \
+		break; \
+	case 4: { \
+		FUNC(MP_FMT4, value[0], value[1], value[2], value[3]); \
+		} \
+		break; \
+	case 5: { \
+		FUNC(MP_FMT5, value[0], value[1], value[2], value[3], value[4]); \
+		} \
+		break; \
+	case 6: { \
+		FUNC(MP_FMT6, value[0], value[1], value[2], value[3], value[4], value[5]); \
+		} \
+		break; \
+	case 7: { \
+		FUNC(MP_FMT7, value[0], value[1], value[2], value[3], value[4], value[5], value[6]); \
+		} \
+		break; \
+	case 8: { \
+		FUNC(MP_FMT8, value[0], value[1], value[2], value[3], value[4], value[5], value[6], value[7]); \
+		} \
+		break; \
+	case 9: { \
+		FUNC(MP_FMT9, value[0], value[1], value[2], value[3], value[4], value[5], value[6], value[7], \
+				value[8]); \
+		} \
+		break; \
+	case 10: { \
+		FUNC(MP_FMT10, value[0], value[1], value[2], value[3], value[4], value[5], value[6], value[7], \
+				value[8], value[9]); \
+		} \
+		break; \
+	case 11: { \
+		FUNC(MP_FMT11, value[0], value[1], value[2], value[3], value[4], value[5], value[6], value[7], \
+				value[8], value[9], value[10]); \
+		} \
+		break; \
+	case 12: { \
+		FUNC(MP_FMT12, value[0], value[1], value[2], value[3], value[4], value[5], value[6], value[7], \
+				value[8], value[9], value[10], value[11]); \
+		} \
+		break; \
+	case 13: { \
+		FUNC(MP_FMT13, value[0], value[1], value[2], value[3], value[4], value[5], value[6], value[7], \
+				value[8], value[9], value[10], value[11], value[12]); \
+		} \
+		break; \
+	case 14: { \
+		FUNC(MP_FMT14, value[0], value[1], value[2], value[3], value[4], value[5], value[6], value[7], \
+				value[8], value[9], value[10], value[11], value[12], value[13]); \
+		} \
+		break; \
+	case 15: { \
+		FUNC(MP_FMT15, value[0], value[1], value[2], value[3], value[4], value[5], value[6], value[7], \
+				value[8], value[9], value[10], value[11], value[12], value[13], value[14]); \
+		} \
+		break; \
+	} \
+} while (0)
+#endif /* _TRACE_H_ */
diff --git a/src/devtools/met-driver/4.9/common/trace_event.c b/src/devtools/met-driver/4.9/common/trace_event.c
new file mode 100644
index 0000000..8e8be42
--- /dev/null
+++ b/src/devtools/met-driver/4.9/common/trace_event.c
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <asm/page.h>
+#include "interface.h"
+#include "met_drv.h"
+
+#ifdef	CONFIG_GPU_TRACEPOINTS
+#include <trace/events/gpu.h>
+
+#define show_secs_from_ns(ns) \
+	({ \
+		u64 t = ns + (NSEC_PER_USEC / 2); \
+		do_div(t, NSEC_PER_SEC); \
+		t; \
+	})
+
+#define show_usecs_from_ns(ns) \
+	({ \
+		u64 t = ns + (NSEC_PER_USEC / 2) ; \
+		u32 rem; \
+		do_div(t, NSEC_PER_USEC); \
+		rem = do_div(t, USEC_PER_SEC); \
+	})
+
+static int event_gpu_registered;
+static int event_gpu_enabled;
+
+noinline void gpu_sched_switch(const char *gpu_name, u64 timestamp,
+				      u32 next_ctx_id, s32 next_prio, u32 next_job_id)
+{
+	MET_TRACE("gpu_name=%s ts=%llu.%06lu next_ctx_id=%lu next_prio=%ld next_job_id=%lu\n",
+		   gpu_name,
+		   (unsigned long long)show_secs_from_ns(timestamp),
+		   (unsigned long)show_usecs_from_ns(timestamp),
+		   (unsigned long)next_ctx_id, (long)next_prio, (unsigned long)next_job_id);
+}
+
+MET_DEFINE_PROBE(gpu_sched_switch, TP_PROTO(const char *gpu_name, u64 timestamp,
+		u32 next_ctx_id, s32 next_prio, u32 next_job_id))
+{
+	gpu_sched_switch(gpu_name, timestamp, next_ctx_id, next_prio, next_job_id);
+}
+
+noinline void gpu_job_enqueue(u32 ctx_id, u32 job_id, const char *type)
+{
+	MET_TRACE("ctx_id=%lu job_id=%lu type=%s",
+		   (unsigned long)ctx_id, (unsigned long)job_id, type);
+}
+
+MET_DEFINE_PROBE(gpu_job_enqueue, TP_PROTO(u32 ctx_id, u32 job_id, const char *type))
+{
+	gpu_job_enqueue(ctx_id, job_id, type);
+}
+#endif
+
+static int reset_driver_stat(void)
+{
+#ifdef	CONFIG_GPU_TRACEPOINTS
+	event_gpu_enabled = 0;
+#endif
+	met_trace_event.mode = 0;
+	return 0;
+}
+
+
+
+static void met_event_start(void)
+{
+#ifdef	CONFIG_GPU_TRACEPOINTS
+	/* register trace event for gpu */
+	do {
+		if (!event_gpu_enabled)
+			break;
+		if (MET_REGISTER_TRACE(gpu_sched_switch)) {
+			pr_debug("can not register callback of gpu_sched_switch\n");
+			break;
+		}
+		if (MET_REGISTER_TRACE(gpu_job_enqueue)) {
+			pr_debug("can not register callback of gpu_job_enqueue\n");
+			MET_UNREGISTER_TRACE(gpu_sched_switch);
+			break;
+		}
+		event_gpu_registered = 1;
+	} while (0);
+#endif
+}
+
+static void met_event_stop(void)
+{
+#ifdef	CONFIG_GPU_TRACEPOINTS
+	/* unregister trace event for gpu */
+	if (event_gpu_registered) {
+		MET_UNREGISTER_TRACE(gpu_job_enqueue);
+		MET_UNREGISTER_TRACE(gpu_sched_switch);
+		event_gpu_registered = 0;
+	}
+#endif
+}
+
+static int met_event_process_argument(const char *arg, int len)
+{
+	int	ret = -1;
+
+#ifdef	CONFIG_GPU_TRACEPOINTS
+	if (strcasecmp(arg, "gpu") == 0) {
+		event_gpu_enabled = 1;
+		met_trace_event.mode = 1;
+		ret = 0;
+	}
+#endif
+
+	return ret;
+}
+
+static const char help[] = "\b"
+#ifdef	CONFIG_GPU_TRACEPOINTS
+	"  --event=gpu                           output gpu trace events\n"
+#endif
+	;
+
+static int met_event_print_help(char *buf, int len)
+{
+	return snprintf(buf, PAGE_SIZE, help);
+}
+
+static const char header[] =
+	"met-info [000] 0.0: met_ftrace_event:"
+#ifdef	CONFIG_GPU_TRACEPOINTS
+	" gpu:gpu_sched_switch gpu:gpu_job_enqueue"
+#endif
+	"\n";
+
+static int met_event_print_header(char *buf, int len)
+{
+	int	ret;
+
+	ret = snprintf(buf, PAGE_SIZE, header);
+	return ret;
+}
+
+struct metdevice met_trace_event = {
+	.name			= "event",
+	.type			= MET_TYPE_PMU,
+	.start			= met_event_start,
+	.stop			= met_event_stop,
+	.reset			= reset_driver_stat,
+	.process_argument	= met_event_process_argument,
+	.print_help		= met_event_print_help,
+	.print_header		= met_event_print_header,
+};
diff --git a/src/devtools/met-driver/4.9/common/util.c b/src/devtools/met-driver/4.9/common/util.c
new file mode 100644
index 0000000..051a3bd
--- /dev/null
+++ b/src/devtools/met-driver/4.9/common/util.c
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "util.h"
+#include <linux/fs.h>
+#include <linux/kernel.h>
+/* #include <asm/uaccess.h> */
+#include <linux/uaccess.h>
+
+#ifdef FILELOG
+
+static char tmp[1000] = { 0 };
+
+ /*TODO*/
+/**
+ * open file
+ * @param name path to open
+ * @return file pointer
+ */
+struct file *open_file(const char *name)
+{
+	struct file *fp = NULL;
+
+	fp = filp_open(name, O_WRONLY | O_APPEND /*| O_TRUNC */  | O_CREAT, 0664);
+	if (unlikely(fp == NULL)) {
+		pr_debug(KERNEL_INFO "can not open result file");
+		return NULL;
+	}
+	return fp;
+}
+
+/**
+ * write to file
+ * @param fp file pointer
+ * @param format format string
+ * @param ... variable-length subsequent arguments
+ */
+void write_file(struct file *fp, const char *format, ...)
+{
+	va_list va;
+	mm_segment_t fs = get_fs();
+
+	va_start(va, format);
+	vsnprintf(tmp, sizeof(tmp), format, va);
+	set_fs(KERNEL_DS);
+	vfs_write(fp, tmp, strlen(tmp), &(fp->f_pos));
+	set_fs(fs);
+	va_end(va);
+}
+
+/**
+ * close file
+ * @param fp file pointer
+ * @return exit code
+ */
+int close_file(struct file *fp)
+{
+	if (likely(fp != NULL)) {
+		filp_close(fp, NULL);
+		fp = NULL;
+		return 0;
+	}
+	pr_debug("cannot close file pointer:%p\n", fp);
+	return -1;
+}
+
+void filelog(char *str)
+{
+	struct file *fp;
+
+	fp = open_file("/data/met.log");
+	if (fp != NULL) {
+		write_file(fp, "%s", str);
+		close_file(fp);
+	}
+}
+
+#endif				/* FILELOG */
diff --git a/src/devtools/met-driver/4.9/common/util.h b/src/devtools/met-driver/4.9/common/util.h
new file mode 100644
index 0000000..5730376
--- /dev/null
+++ b/src/devtools/met-driver/4.9/common/util.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _SRC_UTIL_H_
+#define _SRC_UTIL_H_
+
+/* #define FILELOG 1 */
+
+#ifdef FILELOG
+void filelog(char *str);
+#else
+#define filelog(str)
+#endif
+
+#endif				/* _SRC_UTIL_H_ */
diff --git a/src/devtools/met-driver/4.9/common/v6_pmu_hw.c b/src/devtools/met-driver/4.9/common/v6_pmu_hw.c
new file mode 100644
index 0000000..24b133c
--- /dev/null
+++ b/src/devtools/met-driver/4.9/common/v6_pmu_hw.c
@@ -0,0 +1,242 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "cpu_pmu.h"
+
+/*******************************
+ *      ARM v6 operations      *
+ *******************************/
+#define ARMV6_PMCR_ENABLE               (1 << 0)
+#define ARMV6_PMCR_CTR01_RESET          (1 << 1)
+#define ARMV6_PMCR_CCOUNT_RESET         (1 << 2)
+#define ARMV6_PMCR_CCOUNT_DIV           (1 << 3)
+#define ARMV6_PMCR_COUNT0_IEN           (1 << 4)
+#define ARMV6_PMCR_COUNT1_IEN           (1 << 5)
+#define ARMV6_PMCR_CCOUNT_IEN           (1 << 6)
+#define ARMV6_PMCR_COUNT0_OVERFLOW      (1 << 8)
+#define ARMV6_PMCR_COUNT1_OVERFLOW      (1 << 9)
+#define ARMV6_PMCR_CCOUNT_OVERFLOW      (1 << 10)
+#define ARMV6_PMCR_EVT_COUNT0_SHIFT     20
+#define ARMV6_PMCR_EVT_COUNT0_MASK      (0xFF << ARMV6_PMCR_EVT_COUNT0_SHIFT)
+#define ARMV6_PMCR_EVT_COUNT1_SHIFT     12
+#define ARMV6_PMCR_EVT_COUNT1_MASK      (0xFF << ARMV6_PMCR_EVT_COUNT1_SHIFT)
+
+#define ARMV6_PMCR_OVERFLOWED_MASK \
+	(ARMV6_PMCR_COUNT0_OVERFLOW | ARMV6_PMCR_COUNT1_OVERFLOW | \
+	ARMV6_PMCR_CCOUNT_OVERFLOW)
+
+enum armv6_counters {
+	ARMV6_COUNTER0 = 0,
+	ARMV6_COUNTER1,
+	ARMV6_CYCLE_COUNTER,
+};
+
+static inline unsigned long armv6_pmcr_read(void)
+{
+	u32 val;
+
+	asm volatile ("mrc   p15, 0, %0, c15, c12, 0":"=r" (val));
+	return val;
+}
+
+static inline void armv6_pmcr_write(unsigned long val)
+{
+	asm volatile ("mcr   p15, 0, %0, c15, c12, 0"::"r" (val));
+}
+
+static inline unsigned int armv6_pmu_read_count(unsigned int idx)
+{
+	unsigned long value = 0;
+
+	if (idx == ARMV6_CYCLE_COUNTER)
+		asm volatile ("mrc   p15, 0, %0, c15, c12, 1":"=r" (value));
+	else if (idx == ARMV6_COUNTER0)
+		asm volatile ("mrc   p15, 0, %0, c15, c12, 2":"=r" (value));
+	else if (idx == ARMV6_COUNTER1)
+		asm volatile ("mrc   p15, 0, %0, c15, c12, 3":"=r" (value));
+
+	return value;
+}
+
+static inline void armv6_pmu_overflow(void)
+{
+	unsigned int val;
+
+	val = armv6_pmcr_read();
+	val |= ARMV6_PMCR_OVERFLOWED_MASK;
+	armv6_pmcr_write(val);
+}
+
+static inline unsigned int armv6_pmu_control_read(void)
+{
+	u32 val;
+
+	asm volatile ("mrc   p15, 0, %0, c15, c12, 0":"=r" (val));
+	return val;
+}
+
+static inline void armv6_pmu_control_write(unsigned int setting)
+{
+	unsigned long val;
+
+	val = armv6_pmcr_read();
+	val |= setting;
+	armv6_pmcr_write(val);
+}
+
+static void armv6_pmu_hw_reset_all(void)
+{
+	unsigned long val;
+
+	val = armv6_pmcr_read();
+	val &= ~ARMV6_PMCR_ENABLE;	/* disable all counters */
+	val |= (ARMV6_PMCR_CTR01_RESET | ARMV6_PMCR_CCOUNT_RESET);	/* reset CCNT, PMNC1/2 counter to zero */
+	armv6_pmcr_write(val);
+
+	armv6_pmu_overflow();
+}
+
+static void armv6pmu_enable_event(int idx, unsigned short config)
+{
+	unsigned long val, mask, evt;
+
+	if (idx == ARMV6_CYCLE_COUNTER) {
+		mask = 0;
+		evt = ARMV6_PMCR_CCOUNT_IEN;
+	} else if (idx == ARMV6_COUNTER0) {
+		mask = ARMV6_PMCR_EVT_COUNT0_MASK;
+		evt = (config << ARMV6_PMCR_EVT_COUNT0_SHIFT) | ARMV6_PMCR_COUNT0_IEN;
+	} else if (idx == ARMV6_COUNTER1) {
+		mask = ARMV6_PMCR_EVT_COUNT1_MASK;
+		evt = (config << ARMV6_PMCR_EVT_COUNT1_SHIFT) | ARMV6_PMCR_COUNT1_IEN;
+	} else {
+		pr_debug("invalid counter number (%d)\n", idx);
+		return;
+	}
+
+	/*
+	 * Mask out the current event and set the counter to count the event
+	 * that we're interested in.
+	 */
+	val = armv6_pmcr_read();
+	val &= ~mask;
+	val |= evt;
+	armv6_pmcr_write(val);
+}
+
+/***********************************
+ *      MET ARM v6 operations      *
+ ***********************************/
+enum ARM_TYPE {
+	ARM1136 = 0xB36,
+	ARM1156 = 0xB56,
+	ARM1176 = 0xB76,
+	CHIP_UNKNOWN = 0xFFF
+};
+
+struct chip_pmu {
+	enum ARM_TYPE type;
+};
+
+static struct chip_pmu chips[] = {
+	{ARM1136},
+	{ARM1156},
+	{ARM1176},
+};
+
+static int armv6_pmu_hw_check_event(struct met_pmu *pmu, int idx, int event)
+{
+	int i;
+
+	/* Check if event is duplicate */
+	for (i = 0; i < idx; i++) {
+		if (pmu[i].event == event)
+			break;
+	}
+	if (i < idx) {
+		/* pr_debug("++++++ found duplicate event 0x%02x i=%d\n", event, i); */
+		return -1;
+	}
+
+	return 0;
+}
+
+static void armv6_pmu_hw_start(struct met_pmu *pmu, int count)
+{
+	int i;
+	int generic = count - 1;
+
+	armv6_pmu_hw_reset_all();
+
+	for (i = 0; i < generic; i++) {
+		if (pmu[i].mode == MODE_POLLING)
+			armv6pmu_enable_event(i, pmu[i].event);
+	}
+
+	if (pmu[count - 1].mode == MODE_POLLING)
+		armv6pmu_enable_event(2, pmu[2].event);
+
+	armv6_pmu_control_write(ARMV6_PMCR_ENABLE);
+}
+
+static void armv6_pmu_hw_stop(int count)
+{
+	armv6_pmu_hw_reset_all();
+}
+
+static unsigned int armv6_pmu_hw_polling(struct met_pmu *pmu, int count, unsigned int *pmu_value)
+{
+	int i, cnt = 0;
+	int generic = count - 1;
+
+	for (i = 0; i < generic; i++) {
+		if (pmu[i].mode == MODE_POLLING) {
+			pmu_value[cnt] = armv6_pmu_read_count(i);
+			cnt++;
+		}
+	}
+
+	if (pmu[count - 1].mode == MODE_POLLING) {
+		pmu_value[cnt] = armv6_pmu_read_count(2);
+		cnt++;
+	}
+
+	armv6_pmu_control_write(ARMV6_PMCR_ENABLE | ARMV6_PMCR_CTR01_RESET |
+				ARMV6_PMCR_CCOUNT_RESET);
+
+	return cnt;
+}
+
+struct cpu_pmu_hw armv6_pmu = {
+	.name = "armv6_pmu",
+	.check_event = armv6_pmu_hw_check_event,
+	.start = armv6_pmu_hw_start,
+	.stop = armv6_pmu_hw_stop,
+	.polling = armv6_pmu_hw_polling,
+};
+
+struct cpu_pmu_hw *v6_cpu_pmu_hw_init(int typeid)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(chips); i++)
+		if (chips[i].type == typeid)
+			break;
+
+	if (i == ARRAY_SIZE(chips))
+		return NULL;
+
+	armv6_pmu.nr_cnt = 3;
+
+	return &armv6_pmu;
+}
diff --git a/src/devtools/met-driver/4.9/common/v6_pmu_hw.h b/src/devtools/met-driver/4.9/common/v6_pmu_hw.h
new file mode 100644
index 0000000..a532f13
--- /dev/null
+++ b/src/devtools/met-driver/4.9/common/v6_pmu_hw.h
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+
+#ifndef __V6_PMU_HW_H__
+#define __V6_PMU_HW_H__
+
+extern struct cpu_pmu_hw armv6_pmu;
+extern struct cpu_pmu_hw *v6_cpu_pmu_hw_init(int typeid);
+
+#endif
diff --git a/src/devtools/met-driver/4.9/common/v7_pmu_hw.c b/src/devtools/met-driver/4.9/common/v7_pmu_hw.c
new file mode 100644
index 0000000..931d25e
--- /dev/null
+++ b/src/devtools/met-driver/4.9/common/v7_pmu_hw.c
@@ -0,0 +1,269 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "cpu_pmu.h"
+#include "v6_pmu_hw.h"
+
+/*******************************
+ *      ARM v7 operations      *
+ *******************************/
+#define ARMV7_PMCR_E		(1 << 0)	/* enable all counters */
+#define ARMV7_PMCR_P		(1 << 1)
+#define ARMV7_PMCR_C		(1 << 2)
+#define ARMV7_PMCR_D		(1 << 3)
+#define ARMV7_PMCR_X		(1 << 4)
+#define ARMV7_PMCR_DP		(1 << 5)
+#define ARMV7_PMCR_N_SHIFT	11		/* Number of counters supported */
+#define ARMV7_PMCR_N_MASK	0x1f
+#define ARMV7_PMCR_MASK		0x3f		/* mask for writable bits */
+
+static unsigned int armv7_get_ic(void)
+{
+	unsigned int value;
+	/* Read Main ID Register */
+	asm volatile ("mrc p15, 0, %0, c0, c0, 0":"=r" (value));
+
+	value = (value & 0xffff) >> 4;	/* primary part number */
+	return value;
+}
+
+static inline void armv7_pmu_counter_select(unsigned int idx)
+{
+	asm volatile ("mcr p15, 0, %0, c9, c12, 5"::"r" (idx));
+	isb();
+}
+
+static inline void armv7_pmu_type_select(unsigned int idx, unsigned int type)
+{
+	armv7_pmu_counter_select(idx);
+	asm volatile ("mcr p15, 0, %0, c9, c13, 1"::"r" (type));
+}
+
+static inline unsigned int armv7_pmu_read_count(unsigned int idx)
+{
+	unsigned int value;
+
+	if (idx == 31) {
+		asm volatile ("mrc p15, 0, %0, c9, c13, 0":"=r" (value));
+	} else {
+		armv7_pmu_counter_select(idx);
+		asm volatile ("mrc p15, 0, %0, c9, c13, 2":"=r" (value));
+	}
+	return value;
+}
+
+static inline void armv7_pmu_write_count(int idx, u32 value)
+{
+	if (idx == 31) {
+		asm volatile ("mcr p15, 0, %0, c9, c13, 0"::"r" (value));
+	} else {
+		armv7_pmu_counter_select(idx);
+		asm volatile ("mcr p15, 0, %0, c9, c13, 2"::"r" (value));
+	}
+}
+
+static inline void armv7_pmu_enable_count(unsigned int idx)
+{
+	asm volatile ("mcr p15, 0, %0, c9, c12, 1"::"r" (1 << idx));
+}
+
+static inline void armv7_pmu_disable_count(unsigned int idx)
+{
+	asm volatile ("mcr p15, 0, %0, c9, c12, 2"::"r" (1 << idx));
+}
+
+static inline void armv7_pmu_enable_intr(unsigned int idx)
+{
+	asm volatile ("mcr p15, 0, %0, c9, c14, 1"::"r" (1 << idx));
+}
+
+static inline void armv7_pmu_disable_intr(unsigned int idx)
+{
+	asm volatile ("mcr p15, 0, %0, c9, c14, 2"::"r" (1 << idx));
+}
+
+static inline unsigned int armv7_pmu_overflow(void)
+{
+	unsigned int val;
+
+	asm volatile ("mrc p15, 0, %0, c9, c12, 3":"=r" (val));	/* read */
+	asm volatile ("mcr p15, 0, %0, c9, c12, 3"::"r" (val));
+	return val;
+}
+
+static inline unsigned int armv7_pmu_control_read(void)
+{
+	u32 val;
+
+	asm volatile ("mrc p15, 0, %0, c9, c12, 0":"=r" (val));
+	return val;
+}
+
+static inline void armv7_pmu_control_write(unsigned int val)
+{
+	val &= ARMV7_PMCR_MASK;
+	isb();
+	asm volatile ("mcr p15, 0, %0, c9, c12, 0"::"r" (val));
+}
+
+static int armv7_pmu_hw_get_counters(void)
+{
+	int count = armv7_pmu_control_read();
+	/* N, bits[15:11] */
+	count = ((count >> ARMV7_PMCR_N_SHIFT) & ARMV7_PMCR_N_MASK);
+	return count;
+}
+
+static void armv7_pmu_hw_reset_all(int generic_counters)
+{
+	int i;
+
+	armv7_pmu_control_write(ARMV7_PMCR_C | ARMV7_PMCR_P);
+	/* generic counter */
+	for (i = 0; i < generic_counters; i++) {
+		armv7_pmu_disable_intr(i);
+		armv7_pmu_disable_count(i);
+	}
+	/* cycle counter */
+	armv7_pmu_disable_intr(31);
+	armv7_pmu_disable_count(31);
+	armv7_pmu_overflow();	/* clear overflow */
+}
+
+/***********************************
+ *      MET ARM v7 operations      *
+ ***********************************/
+enum ARM_TYPE {
+	CORTEX_A7 = 0xC07,
+	CORTEX_A9 = 0xC09,
+	CORTEX_A12 = 0xC0D,
+	CORTEX_A15 = 0xC0F,
+	CORTEX_A17 = 0xC0E,
+	CORTEX_A53 = 0xD03,
+	CORTEX_A57 = 0xD07,
+	CHIP_UNKNOWN = 0xFFF
+};
+
+struct chip_pmu {
+	enum ARM_TYPE type;
+};
+
+static struct chip_pmu chips[] = {
+	{CORTEX_A7},
+	{CORTEX_A9},
+	{CORTEX_A12},
+	{CORTEX_A15},
+	{CORTEX_A17},
+	{CORTEX_A53},
+	{CORTEX_A57},
+};
+
+static int armv7_pmu_hw_check_event(struct met_pmu *pmu, int idx, int event)
+{
+	int i;
+
+	/* Check if event is duplicate */
+	for (i = 0; i < idx; i++) {
+		if (pmu[i].event == event)
+			break;
+	}
+	if (i < idx) {
+		/* pr_debug("++++++ found duplicate event 0x%02x i=%d\n", event, i); */
+		return -1;
+	}
+
+	return 0;
+}
+
+static void armv7_pmu_hw_start(struct met_pmu *pmu, int count)
+{
+	int i;
+	int generic = count - 1;
+
+	armv7_pmu_hw_reset_all(generic);
+	for (i = 0; i < generic; i++) {
+		if (pmu[i].mode == MODE_POLLING) {
+			armv7_pmu_type_select(i, pmu[i].event);
+			armv7_pmu_enable_count(i);
+		}
+	}
+	if (pmu[count - 1].mode == MODE_POLLING) {	/* cycle counter */
+		armv7_pmu_enable_count(31);
+	}
+	armv7_pmu_control_write(ARMV7_PMCR_E);
+}
+
+static void armv7_pmu_hw_stop(int count)
+{
+	int generic = count - 1;
+
+	armv7_pmu_hw_reset_all(generic);
+}
+
+static unsigned int armv7_pmu_hw_polling(struct met_pmu *pmu, int count, unsigned int *pmu_value)
+{
+	int i, cnt = 0;
+	int generic = count - 1;
+
+	for (i = 0; i < generic; i++) {
+		if (pmu[i].mode == MODE_POLLING) {
+			pmu_value[cnt] = armv7_pmu_read_count(i);
+			cnt++;
+		}
+	}
+	if (pmu[count - 1].mode == MODE_POLLING) {
+		pmu_value[cnt] = armv7_pmu_read_count(31);
+		cnt++;
+	}
+	armv7_pmu_control_write(ARMV7_PMCR_C | ARMV7_PMCR_P | ARMV7_PMCR_E);
+
+	return cnt;
+}
+
+static struct met_pmu	pmus[MXNR_CPU][MXNR_PMU_EVENTS];
+
+struct cpu_pmu_hw armv7_pmu = {
+	.name = "armv7_pmu",
+	.check_event = armv7_pmu_hw_check_event,
+	.start = armv7_pmu_hw_start,
+	.stop = armv7_pmu_hw_stop,
+	.polling = armv7_pmu_hw_polling,
+};
+
+struct cpu_pmu_hw *cpu_pmu_hw_init(void)
+{
+	int i;
+	enum ARM_TYPE type;
+	struct cpu_pmu_hw *pmu;
+
+	type = (enum ARM_TYPE)armv7_get_ic();
+	for (i = 0; i < ARRAY_SIZE(chips); i++)
+		if (chips[i].type == type)
+			break;
+
+	if (i < ARRAY_SIZE(chips)) {
+		armv7_pmu.nr_cnt = armv7_pmu_hw_get_counters() + 1;
+		pmu = &armv7_pmu;
+	} else
+		pmu = v6_cpu_pmu_hw_init(type);
+
+	if (pmu == NULL)
+		return NULL;
+
+	for (i = 0; i < MXNR_CPU; i++) {
+		pmu->event_count[i] = pmu->nr_cnt;
+		pmu->pmu[i] = pmus[i];
+	}
+
+	return pmu;
+}
diff --git a/src/devtools/met-driver/4.9/common/v8_dsu_hw.c b/src/devtools/met-driver/4.9/common/v8_dsu_hw.c
new file mode 100644
index 0000000..0b58c9a
--- /dev/null
+++ b/src/devtools/met-driver/4.9/common/v8_dsu_hw.c
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <asm/cpu.h>
+#include "met_kernel_symbol.h"
+#include "cpu_dsu.h"
+
+//dsu support 6 event
+#define DSU_EVENT_MAX_CNT 6
+
+static int armv8_dsu_hw_check_event(struct met_dsu *pmu, int idx, int event)
+{
+	int i;
+
+	/* Check if event is duplicate */
+	for (i = 0; i < idx; i++) {
+		if (pmu[i].event == event)
+			break;
+	}
+	if (i < idx) {
+		/* pr_debug("++++++ found duplicate event 0x%02x i=%d\n", event, i); */
+		return -1;
+	}
+	return 0;
+}
+
+
+static struct met_dsu	pmus[MXNR_DSU_EVENTS];
+
+struct cpu_dsu_hw armv8_dsu = {
+	.name = "armv8_dsu",
+	.check_event = armv8_dsu_hw_check_event,
+};
+
+static void init_dsu(void)
+{
+	armv8_dsu.event_count = DSU_EVENT_MAX_CNT;
+}
+
+struct cpu_dsu_hw *cpu_dsu_hw_init(void)
+{
+
+	init_dsu();
+	armv8_dsu.pmu = pmus;
+	return &armv8_dsu;
+}
diff --git a/src/devtools/met-driver/4.9/common/v8_pmu_hw.c b/src/devtools/met-driver/4.9/common/v8_pmu_hw.c
new file mode 100644
index 0000000..ebfd249
--- /dev/null
+++ b/src/devtools/met-driver/4.9/common/v8_pmu_hw.c
@@ -0,0 +1,259 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <asm/cpu.h>
+#include "met_kernel_symbol.h"
+#include "cpu_pmu.h"
+
+/*******************************
+ *      ARM v8 operations      *
+ *******************************/
+/*
+ * Per-CPU PMCR: config reg
+ */
+#define ARMV8_PMCR_E		(1 << 0)	/* Enable all counters */
+#define ARMV8_PMCR_P		(1 << 1)	/* Reset all counters */
+#define ARMV8_PMCR_C		(1 << 2)	/* Cycle counter reset */
+#define ARMV8_PMCR_D		(1 << 3)	/* CCNT counts every 64th cpu cycle */
+#define ARMV8_PMCR_X		(1 << 4)	/* Export to ETM */
+#define ARMV8_PMCR_DP		(1 << 5)	/* Disable CCNT if non-invasive debug */
+#define	ARMV8_PMCR_N_SHIFT	11		/* Number of counters supported */
+#define	ARMV8_PMCR_N_MASK	0x1f
+#define	ARMV8_PMCR_MASK		0x3f		/* Mask for writable bits */
+
+/*
+ * PMOVSR: counters overflow flag status reg
+ */
+#define	ARMV8_OVSR_MASK		0xffffffff	/* Mask for writable bits */
+#define	ARMV8_OVERFLOWED_MASK	ARMV8_OVSR_MASK
+
+static inline void armv8_pmu_counter_select(unsigned int idx)
+{
+	asm volatile ("msr pmselr_el0, %0"::"r" (idx));
+	isb();
+}
+
+static inline void armv8_pmu_type_select(unsigned int idx, unsigned int type)
+{
+	armv8_pmu_counter_select(idx);
+	asm volatile ("msr pmxevtyper_el0, %0"::"r" (type));
+}
+
+static inline unsigned int armv8_pmu_read_count(unsigned int idx)
+{
+	unsigned int value;
+
+	if (idx == 31) {
+		asm volatile ("mrs %0, pmccntr_el0":"=r" (value));
+	} else {
+		armv8_pmu_counter_select(idx);
+		asm volatile ("mrs %0, pmxevcntr_el0":"=r" (value));
+	}
+	return value;
+}
+
+static inline void armv8_pmu_enable_count(unsigned int idx)
+{
+	asm volatile ("msr pmcntenset_el0, %0"::"r" (1 << idx));
+}
+
+static inline void armv8_pmu_disable_count(unsigned int idx)
+{
+	asm volatile ("msr pmcntenclr_el0, %0"::"r" (1 << idx));
+}
+
+static inline void armv8_pmu_enable_intr(unsigned int idx)
+{
+	asm volatile ("msr pmintenset_el1, %0"::"r" (1 << idx));
+}
+
+static inline void armv8_pmu_disable_intr(unsigned int idx)
+{
+	asm volatile ("msr pmintenclr_el1, %0"::"r" (1 << idx));
+	isb();
+	asm volatile ("msr pmovsclr_el0, %0"::"r" (1 << idx));
+	isb();
+}
+
+static inline unsigned int armv8_pmu_overflow(void)
+{
+	unsigned int val;
+
+	asm volatile ("mrs %0, pmovsclr_el0":"=r" (val));	/* read */
+	val &= ARMV8_OVSR_MASK;
+	asm volatile ("mrs %0, pmovsclr_el0"::"r" (val));
+	return val;
+}
+
+static inline unsigned int armv8_pmu_control_read(void)
+{
+	unsigned int val;
+
+	asm volatile ("mrs %0, pmcr_el0":"=r" (val));
+	return val;
+}
+
+static inline void armv8_pmu_control_write(u32 val)
+{
+	val &= ARMV8_PMCR_MASK;
+	isb();
+	asm volatile ("msr pmcr_el0, %0"::"r" (val));
+}
+
+static void armv8_pmu_hw_reset_all(int generic_counters)
+{
+	int i;
+
+	armv8_pmu_control_write(ARMV8_PMCR_C | ARMV8_PMCR_P);
+	/* generic counter */
+	for (i = 0; i < generic_counters; i++) {
+		armv8_pmu_disable_intr(i);
+		armv8_pmu_disable_count(i);
+	}
+	/* cycle counter */
+	armv8_pmu_disable_intr(31);
+	armv8_pmu_disable_count(31);
+	armv8_pmu_overflow();	/* clear overflow */
+}
+
+/***********************************
+ *      MET ARM v8 operations      *
+ ***********************************/
+enum ARM_TYPE {
+	CORTEX_A53 = 0xD03,
+	CORTEX_A35 = 0xD04,
+	CORTEX_A55 = 0xD05,
+	CORTEX_A57 = 0xD07,
+	CORTEX_A72 = 0xD08,
+	CORTEX_A73 = 0xD09,
+	CORTEX_A75 = 0xD0A,
+	CHIP_UNKNOWN = 0xFFF
+};
+
+struct chip_pmu {
+	enum ARM_TYPE	type;
+	unsigned int	event_count;
+};
+
+static struct chip_pmu	chips[] = {
+	{CORTEX_A35, 6+1},
+	{CORTEX_A53, 6+1},
+	{CORTEX_A55, 6+1},
+	{CORTEX_A57, 6+1},
+	{CORTEX_A72, 6+1},
+	{CORTEX_A73, 6+1},
+	{CORTEX_A75, 6+1},
+};
+
+static int armv8_pmu_hw_check_event(struct met_pmu *pmu, int idx, int event)
+{
+	int i;
+
+	/* Check if event is duplicate */
+	for (i = 0; i < idx; i++) {
+		if (pmu[i].event == event)
+			break;
+	}
+	if (i < idx) {
+		/* pr_debug("++++++ found duplicate event 0x%02x i=%d\n", event, i); */
+		return -1;
+	}
+
+	return 0;
+}
+
+static void armv8_pmu_hw_start(struct met_pmu *pmu, int count)
+{
+	int i;
+	int generic = count - 1;
+
+	armv8_pmu_hw_reset_all(generic);
+	for (i = 0; i < generic; i++) {
+		if (pmu[i].mode == MODE_POLLING) {
+			armv8_pmu_type_select(i, pmu[i].event);
+			armv8_pmu_enable_count(i);
+		}
+	}
+	if (pmu[count - 1].mode == MODE_POLLING) {	/* cycle counter */
+		armv8_pmu_enable_count(31);
+	}
+	armv8_pmu_control_write(ARMV8_PMCR_E);
+}
+
+static void armv8_pmu_hw_stop(int count)
+{
+	int generic = count - 1;
+
+	armv8_pmu_hw_reset_all(generic);
+}
+
+static unsigned int armv8_pmu_hw_polling(struct met_pmu *pmu, int count, unsigned int *pmu_value)
+{
+	int i, cnt = 0;
+	int generic = count - 1;
+
+	for (i = 0; i < generic; i++) {
+		if (pmu[i].mode == MODE_POLLING) {
+			pmu_value[cnt] = armv8_pmu_read_count(i);
+			cnt++;
+		}
+	}
+	if (pmu[count - 1].mode == MODE_POLLING) {
+		pmu_value[cnt] = armv8_pmu_read_count(31);
+		cnt++;
+	}
+	armv8_pmu_control_write(ARMV8_PMCR_C | ARMV8_PMCR_P | ARMV8_PMCR_E);
+
+	return cnt;
+}
+
+static struct met_pmu	pmus[MXNR_CPU][MXNR_PMU_EVENTS];
+
+struct cpu_pmu_hw armv8_pmu = {
+	.name = "armv8_pmu",
+	.check_event = armv8_pmu_hw_check_event,
+	.start = armv8_pmu_hw_start,
+	.stop = armv8_pmu_hw_stop,
+	.polling = armv8_pmu_hw_polling,
+};
+
+static void init_pmus(void)
+{
+	int	cpu;
+	int	i;
+
+	for_each_possible_cpu(cpu) {
+		struct cpuinfo_arm64 *cpuinfo;
+		if (cpu >= MXNR_CPU)
+			continue;
+		met_get_cpuinfo_symbol(cpu, &cpuinfo);
+		/* PR_BOOTMSG("CPU[%d]: reg_midr = %x\n", cpu, cpuinfo->reg_midr); */
+		for (i = 0; i < ARRAY_SIZE(chips); i++) {
+			if (chips[i].type == (cpuinfo->reg_midr & 0xffff) >> 4) {
+				armv8_pmu.event_count[cpu] = chips[i].event_count;
+				break;
+			}
+		}
+	}
+}
+
+struct cpu_pmu_hw *cpu_pmu_hw_init(void)
+{
+	int	cpu;
+
+	init_pmus();
+	for (cpu = 0; cpu < MXNR_CPU; cpu++)
+		armv8_pmu.pmu[cpu] = pmus[cpu];
+
+	return &armv8_pmu;
+}
diff --git a/src/devtools/met-driver/4.9/common/version.h b/src/devtools/met-driver/4.9/common/version.h
new file mode 100644
index 0000000..7bb6c77
--- /dev/null
+++ b/src/devtools/met-driver/4.9/common/version.h
@@ -0,0 +1,14 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define MET_BACKEND_VERSION "6.1.0"
diff --git a/src/devtools/met-driver/4.9/default/Kbuild b/src/devtools/met-driver/4.9/default/Kbuild
new file mode 100644
index 0000000..9b2bb87
--- /dev/null
+++ b/src/devtools/met-driver/4.9/default/Kbuild
@@ -0,0 +1,6 @@
+obj-m := met.o
+
+ccflags-y += -I$(srctree)/include/
+
+met-y := default/met_main.o
+
diff --git a/src/devtools/met-driver/4.9/default/Kbuild.yocto b/src/devtools/met-driver/4.9/default/Kbuild.yocto
new file mode 100644
index 0000000..708af14
--- /dev/null
+++ b/src/devtools/met-driver/4.9/default/Kbuild.yocto
@@ -0,0 +1,6 @@
+obj-m := met.o
+
+ccflags-y += -I$(KERNEL_SRC)/include/
+
+met-y := default/met_main.o
+
diff --git a/src/devtools/met-driver/4.9/default/met_main.c b/src/devtools/met-driver/4.9/default/met_main.c
new file mode 100644
index 0000000..4a1ee2f
--- /dev/null
+++ b/src/devtools/met-driver/4.9/default/met_main.c
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/hrtimer.h>
+#include <linux/cpu.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/profile.h>
+#include <linux/dcache.h>
+#include <linux/types.h>
+#include <linux/dcookies.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+
+static int __init met_drv_init(void)
+{
+	printk("Hello MET default module\n");
+	return 0;
+}
+
+static void __exit met_drv_exit(void)
+{
+}
+module_init(met_drv_init);
+module_exit(met_drv_exit);
+
+MODULE_AUTHOR("DT_DM5");
+MODULE_DESCRIPTION("MET_DEFAULT");
+MODULE_LICENSE("GPL");
diff --git a/src/devtools/met-driver/4.9/mt8518/Kbuild.platform.include b/src/devtools/met-driver/4.9/mt8518/Kbuild.platform.include
new file mode 100644
index 0000000..0f9ecf8
--- /dev/null
+++ b/src/devtools/met-driver/4.9/mt8518/Kbuild.platform.include
@@ -0,0 +1,89 @@
+################################################################################
+# Include Path
+################################################################################
+MET_VCOREDVFS_INC := $(KERNEL_SRC)/drivers/misc/mediatek/base/power/include/vcorefs_v3
+MET_PTPOD_INC := $(KERNEL_SRC)/drivers/misc/mediatek/base/power/cpufreq_v1/src/mach/$(MTK_PLATFORM)/
+
+
+################################################################################
+# Feature Spec
+# CPUPMU_VERSION: V8_0/V8_2
+# EMI_SEDA_VERSION: SEDA2/SEDA3
+# EMI_DRAMC_VERSION: V1/V2
+################################################################################
+CPUPMU_VERSION := V8_0
+EMI_SEDA_VERSION := SEDA2
+EMI_DRAMC_VERSION := V2
+
+
+################################################################################
+# Feature On/Off
+################################################################################
+FEATURE_CPUDSU := n
+FEATURE_SSPM_EMI := n
+FEATURE_AP_EMI := y
+FEATURE_THERMAL := y
+FEATURE_GPU := n
+FEATURE_VCOREDVFS := n
+FEATURE_PTPOD := n
+FEATURE_WALLTIME := n
+FEATURE_ONDIEMET := n
+FEATURE_ONDIEMET_WALLTIME := n
+
+
+################################################################################
+# MET_AP_EMI
+################################################################################
+$(info FEATURE_AP_EMI = $(FEATURE_AP_EMI))
+$(info EMI_SEDA_VERSION = $(EMI_SEDA_VERSION))
+$(info EMI_DRAMC_VERSION = $(EMI_DRAMC_VERSION))
+
+MET_AP_EMI := $(if $(filter n,$(FEATURE_AP_EMI)),n,y)
+ifeq ($(MET_AP_EMI), y)
+    ccflags-y += -I$(KERNEL_SRC)/drivers/misc/mediatek/dramc/$(MTK_PLATFORM)/
+    ccflags-y += -I$(MET_PLF_DIR)/dramc/V2/
+
+    met-$(MET_AP_EMI) += $(MTK_PLATFORM)/emi/SEDA2/met_emi.o
+    met-$(MET_AP_EMI) += $(MTK_PLATFORM)/emi/SEDA2/mtk_emi_bm.o
+    met-$(MET_AP_EMI) += $(MTK_PLATFORM)/dramc/V2/mtk_dramc.o
+endif
+
+
+################################################################################
+# MET_THERMAL
+################################################################################
+FEATURE_THERMAL := $(if $(FEATURE_THERMAL),$(FEATURE_THERMAL),y)
+$(info FEATURE_THERMAL = $(FEATURE_THERMAL))
+
+ifneq ($(FEATURE_THERMAL), n)
+    ifeq ($(CONFIG_THERMAL),y)
+        MET_THERMAL := y
+
+        # for mtk_thermal.h
+        ifneq ("$(wildcard $(KERNEL_SRC)/drivers/misc/mediatek/include/mt-plat/$(MTK_PLATFORM)/include/mach/mtk_thermal.h)","")
+            ccflags-y += -I$(KERNEL_SRC)/drivers/misc/mediatek/include/mt-plat/$(MTK_PLATFORM)/include/mach/
+        else
+            MET_THERMAL = n
+            $(info ======== Missing $(KERNEL_SRC)/drivers/misc/mediatek/include/mt-plat/$(MTK_PLATFORM)/include/mach/mtk_thermal.h ========)
+
+            $(info ======== disable MET_THERMAL ========)
+        endif
+
+        # for mtk_thermal_monitor.h
+        ifneq ("$(wildcard $(KERNEL_SRC)/drivers/misc/mediatek/include/mt-plat/mtk_thermal_monitor.h)","")
+            ccflags-y += -I$(KERNEL_SRC)/drivers/misc/mediatek/include/mt-plat/
+        else
+            MET_THERMAL = n
+            $(info ======== Missing $(KERNEL_SRC)/drivers/misc/mediatek/include/mt-plat/mtk_thermal_monitor.h ========)
+            $(info ======== disable MET_THERMAL ========)
+        endif
+    else
+        $(info ======== CONFIG_THERMAL $(CONFIG_THERMAL) ========)
+        $(info ======== disable MET_THERMAL ========)
+        MET_THERMAL := n
+    endif
+else
+    MET_THERMAL := n
+endif
+
+met-$(MET_THERMAL) += $(MTK_PLATFORM)/met_thermal.o
diff --git a/src/devtools/met-driver/4.9/mt8518/dramc/V2/mtk_dramc.c b/src/devtools/met-driver/4.9/mt8518/dramc/V2/mtk_dramc.c
new file mode 100644
index 0000000..19b6dc5
--- /dev/null
+++ b/src/devtools/met-driver/4.9/mt8518/dramc/V2/mtk_dramc.c
@@ -0,0 +1,340 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/io.h>
+#include <mt-plat/sync_write.h>
+#include <mt-plat/mtk_io.h>
+
+#include "mtk_dramc_reg.h"
+
+#define BM_REQ_OK				(0)
+#define BM_ERR_WRONG_REQ		(-1)
+#define BM_ERR_OVERRUN			(-2)
+
+
+void __iomem *BaseAddrDRAMC[MAX_DRAMC_CHANN];
+void __iomem *BaseAddrDDRPHY_AO[2];
+void __iomem *BaseAddrDRAMC0_AO;
+
+	
+unsigned int MET_DRAMC_GetPageHitCount(enum DRAMC_Cnt_Type CountType, int chann)
+{
+	unsigned int iCount;
+	unsigned long addr_base = ADDR_DRAMC0;
+
+	if (chann == 0)
+		addr_base = ADDR_DRAMC0;
+	else if (chann == 1)
+		addr_base = ADDR_DRAMC1;
+	else if (chann == 2)
+		addr_base = ADDR_DRAMC2;
+	else if (chann == 3)
+		addr_base = ADDR_DRAMC3;
+	else
+		return BM_ERR_WRONG_REQ;
+
+	switch (CountType) {
+	case DRAMC_R2R:
+		iCount = readl(IOMEM(addr_base + DRAMC_R2R_PAGE_HIT));
+		break;
+	case DRAMC_R2W:
+		iCount = readl(IOMEM(addr_base + DRAMC_R2W_PAGE_HIT));
+		break;
+	case DRAMC_W2R:
+		iCount = readl(IOMEM(addr_base + DRAMC_W2R_PAGE_HIT));
+		break;
+	case DRAMC_W2W:
+		iCount = readl(IOMEM(addr_base + DRAMC_W2W_PAGE_HIT));
+		break;
+	case DRAMC_ALL:
+		iCount = readl(IOMEM(addr_base + DRAMC_R2R_PAGE_HIT)) +
+			 readl(IOMEM(addr_base + DRAMC_R2W_PAGE_HIT)) +
+			 readl(IOMEM(addr_base + DRAMC_W2R_PAGE_HIT)) +
+			 readl(IOMEM(addr_base + DRAMC_W2W_PAGE_HIT));
+		break;
+	default:
+		return BM_ERR_WRONG_REQ;
+	}
+
+	return iCount;
+}
+
+unsigned int MET_DRAMC_GetPageMissCount(enum DRAMC_Cnt_Type CountType, int chann)
+{
+	unsigned int iCount;
+	unsigned long addr_base = ADDR_DRAMC0;
+
+	if (chann == 0)
+		addr_base = ADDR_DRAMC0;
+	else if (chann == 1)
+		addr_base = ADDR_DRAMC1;
+	else if (chann == 2)
+		addr_base = ADDR_DRAMC2;
+	else if (chann == 3)
+		addr_base = ADDR_DRAMC3;
+	else
+		return BM_ERR_WRONG_REQ;
+
+	switch (CountType) {
+	case DRAMC_R2R:
+		iCount = readl(IOMEM(addr_base + DRAMC_R2R_PAGE_MISS));
+		break;
+	case DRAMC_R2W:
+		iCount = readl(IOMEM(addr_base + DRAMC_R2W_PAGE_MISS));
+		break;
+	case DRAMC_W2R:
+		iCount = readl(IOMEM(addr_base + DRAMC_W2R_PAGE_MISS));
+		break;
+	case DRAMC_W2W:
+		iCount = readl(IOMEM(addr_base + DRAMC_W2W_PAGE_MISS));
+		break;
+	case DRAMC_ALL:
+		iCount = readl(IOMEM(addr_base + DRAMC_R2R_PAGE_MISS)) +
+			 readl(IOMEM(addr_base + DRAMC_R2W_PAGE_MISS)) +
+			 readl(IOMEM(addr_base + DRAMC_W2R_PAGE_MISS)) +
+			 readl(IOMEM(addr_base + DRAMC_W2W_PAGE_MISS));
+		break;
+	default:
+		return BM_ERR_WRONG_REQ;
+	}
+
+	return iCount;
+}
+
+
+unsigned int MET_DRAMC_GetInterbankCount(enum DRAMC_Cnt_Type CountType, int chann)
+{
+	unsigned int iCount;
+	unsigned long addr_base = ADDR_DRAMC0;
+
+	if (chann == 0)
+		addr_base = ADDR_DRAMC0;
+	else if (chann == 1)
+		addr_base = ADDR_DRAMC1;
+	else if (chann == 2)
+		addr_base = ADDR_DRAMC2;
+	else if (chann == 3)
+		addr_base = ADDR_DRAMC3;
+	else
+		return BM_ERR_WRONG_REQ;
+
+	switch (CountType) {
+	case DRAMC_R2R:
+		iCount = readl(IOMEM(addr_base + DRAMC_R2R_INTERBANK));
+		break;
+	case DRAMC_R2W:
+		iCount = readl(IOMEM(addr_base + DRAMC_R2W_INTERBANK));
+		break;
+	case DRAMC_W2R:
+		iCount = readl(IOMEM(addr_base + DRAMC_W2R_INTERBANK));
+		break;
+	case DRAMC_W2W:
+		iCount = readl(IOMEM(addr_base + DRAMC_W2W_INTERBANK));
+		break;
+	case DRAMC_ALL:
+		iCount = readl(IOMEM(addr_base + DRAMC_R2R_INTERBANK)) +
+			 readl(IOMEM(addr_base + DRAMC_R2W_INTERBANK)) +
+			 readl(IOMEM(addr_base + DRAMC_W2R_INTERBANK)) +
+			 readl(IOMEM(addr_base + DRAMC_W2W_INTERBANK));
+		break;
+	default:
+		return BM_ERR_WRONG_REQ;
+	}
+
+	return iCount;
+}
+
+
+unsigned int MET_DRAMC_GetIdleCount(int chann)
+{
+	unsigned long addr_base = ADDR_DRAMC0;
+
+	if (chann == 0)
+		addr_base = ADDR_DRAMC0;
+	else if (chann == 1)
+		addr_base = ADDR_DRAMC1;
+	else if (chann == 2)
+		addr_base = ADDR_DRAMC2;
+	else if (chann == 3)
+		addr_base = ADDR_DRAMC3;
+	else
+		return BM_ERR_WRONG_REQ;
+
+	return readl(IOMEM(addr_base + DRAMC_IDLE_COUNT));
+}
+
+
+unsigned int MET_DRAMC_Misc_Status(int chann)
+{
+	unsigned long addr_base = ADDR_DRAMC0;
+
+	if (chann == 0)
+		addr_base = ADDR_DRAMC0;
+	else if (chann == 1)
+		addr_base = ADDR_DRAMC1;
+	else if (chann == 2)
+		addr_base = ADDR_DRAMC2;
+	else if (chann == 3)
+		addr_base = ADDR_DRAMC3;
+	else
+		return BM_ERR_WRONG_REQ;
+
+	return readl(IOMEM(addr_base + DRAMC_MISC_STATUSA));
+}
+
+
+unsigned int MET_DRAMC_RefPop(int chann)
+{
+	unsigned long addr_base = ADDR_DRAMC0;
+
+	if (chann == 0)
+		addr_base = ADDR_DRAMC0;
+	else if (chann == 1)
+		addr_base = ADDR_DRAMC1;
+	else if (chann == 2)
+		addr_base = ADDR_DRAMC2;
+	else if (chann == 3)
+		addr_base = ADDR_DRAMC3;
+	else
+		return BM_ERR_WRONG_REQ;
+
+	return readl(IOMEM(addr_base + DRAMC_REFRESH_POP));
+}
+
+
+unsigned int MET_DRAMC_Free26M(int chann)
+{
+	unsigned long addr_base = ADDR_DRAMC0;
+
+	if (chann == 0)
+		addr_base = ADDR_DRAMC0;
+	else if (chann == 1)
+		addr_base = ADDR_DRAMC1;
+	else if (chann == 2)
+		addr_base = ADDR_DRAMC2;
+	else if (chann == 3)
+		addr_base = ADDR_DRAMC3;
+	else
+		return BM_ERR_WRONG_REQ;
+
+	return readl(IOMEM(addr_base + DRAMC_FREERUN_26M));
+}
+
+
+unsigned int MET_DRAMC_RByte(int chann)
+{
+	unsigned long addr_base = ADDR_DRAMC0;
+
+	if (chann == 0)
+		addr_base = ADDR_DRAMC0;
+	else if (chann == 1)
+		addr_base = ADDR_DRAMC1;
+	else if (chann == 2)
+		addr_base = ADDR_DRAMC2;
+	else if (chann == 3)
+		addr_base = ADDR_DRAMC3;
+	else
+		return BM_ERR_WRONG_REQ;
+
+	return readl(IOMEM(addr_base + DRAMC_READ_BYTES));
+}
+
+
+unsigned int MET_DRAMC_WByte(int chann)
+{
+	unsigned long addr_base = ADDR_DRAMC0;
+
+	if (chann == 0)
+		addr_base = ADDR_DRAMC0;
+	else if (chann == 1)
+		addr_base = ADDR_DRAMC1;
+	else if (chann == 2)
+		addr_base = ADDR_DRAMC2;
+	else if (chann == 3)
+		addr_base = ADDR_DRAMC3;
+	else
+		return BM_ERR_WRONG_REQ;
+
+	return readl(IOMEM(addr_base + DRAMC_WRITE_BYTES));
+}
+
+
+unsigned int MET_DRAMC_DCM_CTRL(int chann)
+{
+	unsigned long addr_base;
+
+	if (chann == 0)
+		addr_base = (unsigned long)BaseAddrDDRPHY_AO[0];
+	else if (chann == 1)
+		addr_base = (unsigned long)BaseAddrDDRPHY_AO[1];
+	else
+		return BM_ERR_WRONG_REQ;
+
+	/* 	
+	 * #define DRAMC_DCM_CTRL		0x28C
+	 *	0x28c	MISC_CG_CTRL2	ddrphy_config_MISC    DCM control configuration 
+	 */
+	/* CH0_MEM_DCM_CTRL[25:21] DCM slowdown factor */
+	return ((readl(IOMEM(addr_base + DRAMC_DCM_CTRL)) & 0x03E00000) >> 21);
+}
+
+
+void MET_DRAMC_GetDebugCounter(int *value, int chann)
+{
+	int i;
+	unsigned long addr_base = ADDR_DRAMC0;
+
+	for (i = 0; i < chann; i++) {
+		if (i == 0)
+			addr_base = ADDR_DRAMC0;
+		else if (i == 1)
+			addr_base = ADDR_DRAMC1;
+		else if (i == 2)
+			addr_base = ADDR_DRAMC2;
+		else if (i == 3)
+			addr_base = ADDR_DRAMC3;
+		else
+			return;
+
+		value[DRAMC_Debug_MAX_CNT * i + BM_FREERUN_26M] = MET_DRAMC_Free26M(i);
+
+		value[DRAMC_Debug_MAX_CNT * i + BM_RK0_PRE_STANDBY] =
+			readl(IOMEM(addr_base + DRAMC_RK0_PRE_STANDBY));
+		value[DRAMC_Debug_MAX_CNT * i + BM_RK0_PRE_POWERDOWN] =
+			readl(IOMEM(addr_base + DRAMC_RK0_PRE_POWERDOWN));
+		value[DRAMC_Debug_MAX_CNT * i + BM_RK0_ACT_STANDBY] =
+			readl(IOMEM(addr_base + DRAMC_RK0_ACT_STANDBY));
+		value[DRAMC_Debug_MAX_CNT * i + BM_RK0_ACT_POWERDOWN] =
+			readl(IOMEM(addr_base + DRAMC_RK0_ACT_POWERDOWN));
+
+		value[DRAMC_Debug_MAX_CNT * i + BM_RK1_PRE_STANDBY] =
+			readl(IOMEM(addr_base + DRAMC_RK1_PRE_STANDBY));
+		value[DRAMC_Debug_MAX_CNT * i + BM_RK1_PRE_POWERDOWN] =
+			readl(IOMEM(addr_base + DRAMC_RK1_PRE_POWERDOWN));
+		value[DRAMC_Debug_MAX_CNT * i + BM_RK1_ACT_STANDBY] =
+			readl(IOMEM(addr_base + DRAMC_RK1_ACT_STANDBY));
+		value[DRAMC_Debug_MAX_CNT * i + BM_RK1_ACT_POWERDOWN] =
+			readl(IOMEM(addr_base + DRAMC_RK1_ACT_POWERDOWN));
+
+		value[DRAMC_Debug_MAX_CNT * i + BM_RK2_PRE_STANDBY] =
+			readl(IOMEM(addr_base + DRAMC_RK2_PRE_STANDBY));
+		value[DRAMC_Debug_MAX_CNT * i + BM_RK2_PRE_POWERDOWN] =
+			readl(IOMEM(addr_base + DRAMC_RK2_PRE_POWERDOWN));
+		value[DRAMC_Debug_MAX_CNT * i + BM_RK2_ACT_STANDBY] =
+			readl(IOMEM(addr_base + DRAMC_RK2_ACT_STANDBY));
+		value[DRAMC_Debug_MAX_CNT * i + BM_RK2_ACT_POWERDOWN] =
+			readl(IOMEM(addr_base + DRAMC_RK2_ACT_POWERDOWN));
+	}
+}
diff --git a/src/devtools/met-driver/4.9/mt8518/dramc/V2/mtk_dramc_reg.h b/src/devtools/met-driver/4.9/mt8518/dramc/V2/mtk_dramc_reg.h
new file mode 100644
index 0000000..60fdade
--- /dev/null
+++ b/src/devtools/met-driver/4.9/mt8518/dramc/V2/mtk_dramc_reg.h
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#ifndef __MTK_DRAMC_REG_H__
+#define __MTK_DRAMC_REG_H__
+#include <linux/compiler.h>
+
+#define DRAMC_VER 2
+
+#define MAX_DRAMC_CHANN 4
+extern void __iomem *BaseAddrDRAMC[MAX_DRAMC_CHANN];
+extern void __iomem *BaseAddrDDRPHY_AO[2];
+extern void __iomem *BaseAddrDRAMC0_AO;
+
+enum DRAMC_Debug_Type {
+	BM_FREERUN_26M = 0x0,
+	BM_RK0_PRE_STANDBY,
+	BM_RK0_PRE_POWERDOWN,
+	BM_RK0_ACT_STANDBY,
+	BM_RK0_ACT_POWERDOWN,
+	BM_RK1_PRE_STANDBY,
+	BM_RK1_PRE_POWERDOWN,
+	BM_RK1_ACT_STANDBY,
+	BM_RK1_ACT_POWERDOWN,
+	BM_RK2_PRE_STANDBY,
+	BM_RK2_PRE_POWERDOWN,
+	BM_RK2_ACT_STANDBY,
+	BM_RK2_ACT_POWERDOWN,
+	DRAMC_Debug_MAX_CNT
+};
+
+enum DRAMC_Cnt_Type {
+	DRAMC_R2R,
+	DRAMC_R2W,
+	DRAMC_W2R,
+	DRAMC_W2W,
+	DRAMC_ALL
+};
+
+#define ADDR_DRAMC0     ((unsigned long)BaseAddrDRAMC[0])
+#define ADDR_DRAMC1     ((unsigned long)BaseAddrDRAMC[1])
+#define ADDR_DRAMC2     ((unsigned long)BaseAddrDRAMC[2])
+#define ADDR_DRAMC3     ((unsigned long)BaseAddrDRAMC[3])
+
+/* DRAMC_NAO_Register */
+#define DRAMC_MISC_STATUSA      0x80
+
+#define DRAMC_REFRESH_POP       0x300
+#define DRAMC_FREERUN_26M       0x304
+#define DRAMC_R2R_PAGE_HIT      0x30C
+#define DRAMC_R2R_PAGE_MISS     0x310
+#define DRAMC_R2R_INTERBANK     0x314
+#define DRAMC_R2W_PAGE_HIT      0x318
+#define DRAMC_R2W_PAGE_MISS     0x31C
+#define DRAMC_R2W_INTERBANK     0x320
+#define DRAMC_W2R_PAGE_HIT      0x324
+#define DRAMC_W2R_PAGE_MISS     0x328
+#define DRAMC_W2R_INTERBANK     0x32C
+#define DRAMC_W2W_PAGE_HIT      0x330
+#define DRAMC_W2W_PAGE_MISS     0x334
+#define DRAMC_W2W_INTERBANK     0x338
+#define DRAMC_IDLE_COUNT        0x308
+#define DRAMC_RK0_PRE_STANDBY   0x33c
+#define DRAMC_RK0_PRE_POWERDOWN 0x340
+#define DRAMC_RK0_ACT_STANDBY   0x344
+#define DRAMC_RK0_ACT_POWERDOWN 0x348
+#define DRAMC_RK1_PRE_STANDBY   0x34c
+#define DRAMC_RK1_PRE_POWERDOWN 0x350
+#define DRAMC_RK1_ACT_STANDBY   0x354
+#define DRAMC_RK1_ACT_POWERDOWN 0x358
+#define DRAMC_RK2_PRE_STANDBY   0x35c
+#define DRAMC_RK2_PRE_POWERDOWN 0x360
+#define DRAMC_RK2_ACT_STANDBY   0x364
+#define DRAMC_RK2_ACT_POWERDOWN 0x368
+#define DRAMC_READ_BYTES        0x38c
+#define DRAMC_WRITE_BYTES       0x390
+
+
+/* DRAMC_PHY_Register */
+#define DRAMC_DCM_CTRL		0x28C
+
+extern unsigned int MET_DRAMC_GetPageHitCount(enum DRAMC_Cnt_Type CountType, int chann);
+extern unsigned int MET_DRAMC_GetPageMissCount(enum DRAMC_Cnt_Type CountType, int chann);
+extern unsigned int MET_DRAMC_GetInterbankCount(enum DRAMC_Cnt_Type CountType, int chann);
+extern unsigned int MET_DRAMC_GetIdleCount(int chann);
+extern unsigned int MET_DRAMC_Misc_Status(int chann);
+extern unsigned int MET_DRAMC_RefPop(int chann);
+extern unsigned int MET_DRAMC_Free26M(int chann);
+extern unsigned int MET_DRAMC_RByte(int chann);
+extern unsigned int MET_DRAMC_WByte(int chann);
+extern unsigned int MET_DRAMC_DCM_CTRL(int chann);
+
+/* Debug Counter status */
+extern void MET_DRAMC_GetDebugCounter(int *value, int chann);
+
+
+#endif /* __MTK_DRAMC_REG_H__ */
diff --git a/src/devtools/met-driver/4.9/mt8518/emi/SEDA2/met_emi.c b/src/devtools/met-driver/4.9/mt8518/emi/SEDA2/met_emi.c
new file mode 100644
index 0000000..43215da
--- /dev/null
+++ b/src/devtools/met-driver/4.9/mt8518/emi/SEDA2/met_emi.c
@@ -0,0 +1,1403 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/device.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/string.h>
+
+#ifdef CONFIG_ARM
+#include <asm/dma-mapping.h> /* arm_coherent_dma_ops */
+#endif /* CONFIG_ARM */
+
+#include <linux/dma-mapping.h>
+
+#define MET_USER_EVENT_SUPPORT
+#include "met_drv.h"
+#include "trace.h"
+#include "mtk_typedefs.h"
+#include "core_plf_init.h"
+#include "core_plf_trace.h"
+#include "mtk_emi_bm.h"
+#include "mtk_dramc_reg.h"
+#include "interface.h"
+
+
+/*======================================================================*/
+/*	Global variable definitions					*/
+/*======================================================================*/
+static unsigned int emi_dcm;
+
+/*ondiemet emi sampling interval in us */
+static int emi_tsct_enable;
+static int emi_mdct_enable;
+static int emi_TP_busfiltr_enable;
+
+static int emi_use_ondiemet;
+static int metemi_func_opt;
+
+static int met_emi_regdump;
+/* Dynamic MonitorCounter selection !!!EXPERIMENT!!! */
+static int msel_enable;
+static unsigned int msel_group1 = BM_MASTER_ALL;
+static unsigned int msel_group2 = BM_MASTER_ALL;
+static unsigned int msel_group3 = BM_MASTER_ALL;
+
+/* Global variables */
+static struct kobject *kobj_emi;
+static int rwtype = BM_BOTH_READ_WRITE;
+
+/* BW Limiter */
+#define CNT_COUNTDOWN   (0)
+static int countdown;
+static int bw_limiter_enable = BM_BW_LIMITER_ENABLE;
+
+/* TTYPE counter */
+static int ttype1_16_en = BM_TTYPE1_16_DISABLE;
+static int ttype17_21_en = BM_TTYPE17_21_DISABLE;
+
+static int dramc_pdir_enable;
+static int dram_chann_num = 1;
+
+
+
+/*======================================================================*/
+/*	EMI Test Operations						*/
+/*======================================================================*/
+static int times;
+
+static ssize_t test_apmcu_store(struct kobject *kobj,
+				struct kobj_attribute *attr,
+				const char *buf,
+				size_t n)
+{
+	int i;
+	unsigned int    *src_addr_v;
+	dma_addr_t src_addr_p;
+#ifdef CONFIG_ARM
+	struct dma_map_ops *ops = (struct dma_map_ops *)symbol_get(arm_dma_ops);
+#endif /* CONFIG_ARM */
+
+	if ((n == 0) || (buf == NULL))
+		return -EINVAL;
+	if (kstrtoint(buf, 10, &times) != 0)
+		return -EINVAL;
+	if (times < 0)
+		return -EINVAL;
+
+	if (times > 5000)       /* Less than 20MB */
+		return -EINVAL;
+
+#ifdef CONFIG_ARM
+	if (ops && ops->alloc) {
+		(met_device.this_device)->coherent_dma_mask = DMA_BIT_MASK(32);
+		src_addr_v = ops->alloc(met_device.this_device,
+						PAGE_SIZE,
+						&src_addr_p,
+						GFP_KERNEL,
+						0);
+	}
+#endif /* CONFIG_ARM */
+
+#ifdef CONFIG_ARM64
+	/* dma_alloc */
+	src_addr_v = dma_alloc_coherent(met_device.this_device,
+					PAGE_SIZE,
+					&src_addr_p,
+					GFP_KERNEL);
+#endif /* CONFIG_ARM64 */
+
+	if (src_addr_v == NULL) {
+#ifdef CONFIG_MET_MODULE
+		met_tag_oneshot_real(0, "test_apmcu dma alloc fail", PAGE_SIZE);
+#else
+		met_tag_oneshot(0, "test_apmcu dma alloc fail", PAGE_SIZE);
+#endif
+		return -ENOMEM;
+	}
+
+	/* testing */
+	preempt_disable();
+#ifdef CONFIG_MET_MODULE
+	met_tag_start_real(0, "TEST_EMI_APMCU");
+#else
+	met_tag_start(0, "TEST_EMI_APMCU");
+#endif
+	for (i = 0; i < times; i++) {
+		memset(src_addr_v, 2 * i, PAGE_SIZE);
+#ifdef CONFIG_MET_MODULE
+		met_tag_oneshot_real(0, "TEST_EMI_APMCU", PAGE_SIZE);
+#else
+		met_tag_oneshot(0, "TEST_EMI_APMCU", PAGE_SIZE);
+#endif
+	}
+
+#ifdef CONFIG_MET_MODULE
+	met_tag_end_real(0, "TEST_EMI_APMCU");
+#else
+	met_tag_end(0, "TEST_EMI_APMCU");
+#endif
+	/* the following function has no defined if MET is built as module */
+	/* preempt_enable_no_resched(); */
+	/* use this one to replace it: see met_drv.h */
+	my_preempt_enable();
+
+#ifdef CONFIG_ARM
+	/* dma_free */
+	if (ops && ops->free) {
+		ops->free(met_device.this_device,
+				  PAGE_SIZE,
+				  src_addr_v,
+				  src_addr_p,
+			0);
+	}
+#endif /* CONFIG_ARM */
+
+#ifdef CONFIG_ARM64
+	/* dma_free */
+	if (src_addr_v != NULL)
+		dma_free_coherent(met_device.this_device,
+				  PAGE_SIZE,
+				  src_addr_v,
+				  src_addr_p);
+#endif /* CONFIG_ARM64 */
+
+	return n;
+}
+
+/*======================================================================*/
+/*	KOBJ Declarations						*/
+/*======================================================================*/
+DECLARE_KOBJ_ATTR_INT(emi_tsct_enable, emi_tsct_enable);
+DECLARE_KOBJ_ATTR_INT(emi_mdct_enable, emi_mdct_enable);
+DECLARE_KOBJ_ATTR_INT(emi_TP_busfiltr_enable, emi_TP_busfiltr_enable);
+DECLARE_KOBJ_ATTR_INT(metemi_func_opt, metemi_func_opt);
+DECLARE_KOBJ_ATTR_INT(emi_regdump, met_emi_regdump);
+DECLARE_KOBJ_ATTR_INT(msel_enable, msel_enable);
+DECLARE_KOBJ_ATTR_HEX_CHECK(msel_group1, msel_group1, msel_group1 > 0 && msel_group1 <= BM_MASTER_ALL);
+DECLARE_KOBJ_ATTR_HEX_CHECK(msel_group2, msel_group2, msel_group2 > 0 && msel_group2 <= BM_MASTER_ALL);
+DECLARE_KOBJ_ATTR_HEX_CHECK(msel_group3, msel_group3, msel_group3 > 0 && msel_group3 <= BM_MASTER_ALL);
+
+/* KOBJ: test_apmcu */
+DECLARE_KOBJ_ATTR_SHOW_INT(test_apmcu, times);
+/* please refer to session: "EMI Test Operations" for store operation */
+DECLARE_KOBJ_ATTR(test_apmcu);
+
+
+/* KOBJ: rwtype */
+DECLARE_KOBJ_ATTR_INT_CHECK(rwtype, rwtype, rwtype >= 0 && rwtype <= BM_WRITE_ONLY);
+
+static unsigned int get_emi_clock_rate(unsigned int dram_data_rate_MHz)
+{
+	unsigned int DRAM_TYPE = TYPE_PCDDR3;
+
+	if (get_ddr_type_symbol) {
+		DRAM_TYPE = get_ddr_type_symbol();
+
+		if ((DRAM_TYPE == TYPE_LPDDR4) || (DRAM_TYPE == TYPE_PCDDR4))
+			return dram_data_rate_MHz / DRAM_EMI_BASECLOCK_RATE_LP4 / DRAM_DATARATE;
+		else
+			return dram_data_rate_MHz / DRAM_EMI_BASECLOCK_RATE_LP3 / DRAM_DATARATE;
+	} else {
+		METERROR("[%s][%d]get_ddr_type_symbol = NULL , use the TYPE_LPDDR3 setting\n", __func__, __LINE__);
+		return dram_data_rate_MHz / DRAM_EMI_BASECLOCK_RATE_LP3 / DRAM_DATARATE;
+	}
+}
+
+/* KOBJ: emi_clock_rate */
+static ssize_t emi_clock_rate_show(struct kobject *kobj,
+				   struct kobj_attribute *attr,
+				   char *buf)
+{
+	unsigned int dram_data_rate_MHz;
+
+	if (get_dram_data_rate_symbol) {
+		dram_data_rate_MHz = get_dram_data_rate_symbol();
+	} else {
+		METERROR("get_dram_data_rate_symbol = NULL\n");
+		dram_data_rate_MHz = 0;
+	}
+
+	return snprintf(buf, PAGE_SIZE, "%d\n",
+			get_emi_clock_rate(dram_data_rate_MHz));
+}
+
+DECLARE_KOBJ_ATTR_RO(emi_clock_rate);
+
+/* KOBJ: ttype1_16_en */
+DECLARE_KOBJ_ATTR_STR_LIST_ITEM(
+	ttype1_16_en,
+	KOBJ_ITEM_LIST(
+		{ BM_TTYPE1_16_ENABLE,   "ENABLE" },
+		{ BM_TTYPE1_16_DISABLE,  "DISABLE" }));
+DECLARE_KOBJ_ATTR_STR_LIST(ttype1_16_en, ttype1_16_en, ttype1_16_en);
+
+/* KOBJ: ttype17_21_en */
+DECLARE_KOBJ_ATTR_STR_LIST_ITEM(
+	ttype17_21_en,
+	KOBJ_ITEM_LIST(
+		{ BM_TTYPE17_21_ENABLE,  "ENABLE" },
+		{ BM_TTYPE17_21_DISABLE, "DISABLE" }));
+DECLARE_KOBJ_ATTR_STR_LIST(ttype17_21_en, ttype17_21_en, ttype17_21_en);
+
+/* KOBJ: bw_limiter_enable */
+DECLARE_KOBJ_ATTR_STR_LIST_ITEM(
+	bw_limiter_enable,
+	KOBJ_ITEM_LIST(
+		{ BM_BW_LIMITER_ENABLE,  "ENABLE" },
+		{ BM_BW_LIMITER_DISABLE, "DISABLE" }));
+
+DECLARE_KOBJ_ATTR_STR_LIST(bw_limiter_enable, bw_limiter_enable, bw_limiter_enable);
+
+/* KOBJ: ttype_master */
+DECLARE_KOBJ_ATTR_STR_LIST_ITEM(
+	ttype_master,
+	KOBJ_ITEM_LIST(
+		{ BM_MASTER_M0,  "M0" },
+		{ BM_MASTER_M1,  "M1" },
+		{ BM_MASTER_M2,  "M2" },
+		{ BM_MASTER_M3,  "M3" },
+		{ BM_MASTER_M4,  "M4" },
+		{ BM_MASTER_M5,  "M5" },
+		{ BM_MASTER_M6,  "M6" },
+		{ BM_MASTER_M7,  "M7" }));
+
+
+/* KOBJ: ttypeX_nbeat, ttypeX_nbyte, ttypeX_burst */
+DECLARE_KOBJ_ATTR_INT_LIST_ITEM(
+	ttype_nbeat,
+	KOBJ_ITEM_LIST(
+		{ BM_TRANS_TYPE_1BEAT,   1 },
+		{ BM_TRANS_TYPE_2BEAT,   2 },
+		{ BM_TRANS_TYPE_3BEAT,   3 },
+		{ BM_TRANS_TYPE_4BEAT,   4 },
+		{ BM_TRANS_TYPE_5BEAT,   5 },
+		{ BM_TRANS_TYPE_6BEAT,   6 },
+		{ BM_TRANS_TYPE_7BEAT,   7 },
+		{ BM_TRANS_TYPE_8BEAT,   8 },
+		{ BM_TRANS_TYPE_9BEAT,   9 },
+		{ BM_TRANS_TYPE_10BEAT,  10 },
+		{ BM_TRANS_TYPE_11BEAT,  11 },
+		{ BM_TRANS_TYPE_12BEAT,  12 },
+		{ BM_TRANS_TYPE_13BEAT,  13 },
+		{ BM_TRANS_TYPE_14BEAT,  14 },
+		{ BM_TRANS_TYPE_15BEAT,  15 },
+		{ BM_TRANS_TYPE_16BEAT,  16 }));
+DECLARE_KOBJ_ATTR_INT_LIST_ITEM(
+	ttype_nbyte,
+	KOBJ_ITEM_LIST(
+		{ BM_TRANS_TYPE_1Byte,   1 },
+		{ BM_TRANS_TYPE_2Byte,   2 },
+		{ BM_TRANS_TYPE_4Byte,   4 },
+		{ BM_TRANS_TYPE_8Byte,   8 },
+		{ BM_TRANS_TYPE_16Byte,  16 },
+		{ BM_TRANS_TYPE_32Byte,  32 }));
+DECLARE_KOBJ_ATTR_STR_LIST_ITEM(
+	ttype_burst,
+	KOBJ_ITEM_LIST(
+		{ BM_TRANS_TYPE_BURST_INCR,      "INCR" },
+		{ BM_TRANS_TYPE_BURST_WRAP,      "WRAP" }));
+
+DECLARE_KOBJ_ATTR_STR_LIST_ITEM(
+	ttype_rw,
+	KOBJ_ITEM_LIST(
+		{ BM_TRANS_RW_DEFAULT,   "DEFAULT" },
+		{ BM_TRANS_RW_READONLY,  "R" },
+		{ BM_TRANS_RW_WRITEONLY, "W" },
+		{ BM_TRANS_RW_RWBOTH,    "BOTH" }));
+
+DECLARE_KOBJ_ATTR_INT(dramc_pdir_enable, dramc_pdir_enable);
+
+/*enable high priority filter*/
+static int high_priority_filter;
+DECLARE_KOBJ_ATTR_HEX(high_priority_filter, high_priority_filter);
+
+/**/
+static int ttype_master_val[21];
+static int ttype_busid_val[21];
+static int ttype_nbeat_val[21];
+static int ttype_nbyte_val[21];
+static int ttype_burst_val[21];
+static int ttype_rw_val[21];
+
+#define DECLARE_KOBJ_TTYPE_MASTER(nr) \
+	DECLARE_KOBJ_ATTR_STR_LIST(ttype ## nr ## _master, ttype_master_val[nr - 1], ttype_master)
+
+#define DECLARE_KOBJ_TTYPE_NBEAT(nr) \
+	DECLARE_KOBJ_ATTR_INT_LIST(ttype ## nr ## _nbeat, ttype_nbeat_val[nr - 1], ttype_nbeat)
+
+#define DECLARE_KOBJ_TTYPE_NBYTE(nr) \
+	DECLARE_KOBJ_ATTR_INT_LIST(ttype ## nr ## _nbyte, ttype_nbyte_val[nr - 1], ttype_nbyte)
+
+#define DECLARE_KOBJ_TTYPE_BURST(nr) \
+	DECLARE_KOBJ_ATTR_STR_LIST(ttype ## nr ## _burst, ttype_burst_val[nr - 1], ttype_burst)
+
+#define DECLARE_KOBJ_TTYPE_RW(nr) \
+	DECLARE_KOBJ_ATTR_STR_LIST(ttype ## nr ## _rw, ttype_rw_val[nr - 1], ttype_rw)
+
+#define DECLARE_KOBJ_TTYPE_BUSID_VAL(nr) \
+	DECLARE_KOBJ_ATTR_HEX(ttype ## nr ## _busid, ttype_busid_val[nr - 1])
+
+DECLARE_KOBJ_TTYPE_MASTER(1);
+DECLARE_KOBJ_TTYPE_NBEAT(1);
+DECLARE_KOBJ_TTYPE_NBYTE(1);
+DECLARE_KOBJ_TTYPE_BURST(1);
+DECLARE_KOBJ_TTYPE_RW(1);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(1);
+
+DECLARE_KOBJ_TTYPE_MASTER(2);
+DECLARE_KOBJ_TTYPE_NBEAT(2);
+DECLARE_KOBJ_TTYPE_NBYTE(2);
+DECLARE_KOBJ_TTYPE_BURST(2);
+DECLARE_KOBJ_TTYPE_RW(2);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(2);
+
+DECLARE_KOBJ_TTYPE_MASTER(3);
+DECLARE_KOBJ_TTYPE_NBEAT(3);
+DECLARE_KOBJ_TTYPE_NBYTE(3);
+DECLARE_KOBJ_TTYPE_BURST(3);
+DECLARE_KOBJ_TTYPE_RW(3);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(3);
+
+DECLARE_KOBJ_TTYPE_MASTER(4);
+DECLARE_KOBJ_TTYPE_NBEAT(4);
+DECLARE_KOBJ_TTYPE_NBYTE(4);
+DECLARE_KOBJ_TTYPE_BURST(4);
+DECLARE_KOBJ_TTYPE_RW(4);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(4);
+
+DECLARE_KOBJ_TTYPE_MASTER(5);
+DECLARE_KOBJ_TTYPE_NBEAT(5);
+DECLARE_KOBJ_TTYPE_NBYTE(5);
+DECLARE_KOBJ_TTYPE_BURST(5);
+DECLARE_KOBJ_TTYPE_RW(5);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(5);
+
+DECLARE_KOBJ_TTYPE_MASTER(6);
+DECLARE_KOBJ_TTYPE_NBEAT(6);
+DECLARE_KOBJ_TTYPE_NBYTE(6);
+DECLARE_KOBJ_TTYPE_BURST(6);
+DECLARE_KOBJ_TTYPE_RW(6);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(6);
+
+DECLARE_KOBJ_TTYPE_MASTER(7);
+DECLARE_KOBJ_TTYPE_NBEAT(7);
+DECLARE_KOBJ_TTYPE_NBYTE(7);
+DECLARE_KOBJ_TTYPE_BURST(7);
+DECLARE_KOBJ_TTYPE_RW(7);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(7);
+
+DECLARE_KOBJ_TTYPE_MASTER(8);
+DECLARE_KOBJ_TTYPE_NBEAT(8);
+DECLARE_KOBJ_TTYPE_NBYTE(8);
+DECLARE_KOBJ_TTYPE_BURST(8);
+DECLARE_KOBJ_TTYPE_RW(8);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(8);
+
+DECLARE_KOBJ_TTYPE_MASTER(9);
+DECLARE_KOBJ_TTYPE_NBEAT(9);
+DECLARE_KOBJ_TTYPE_NBYTE(9);
+DECLARE_KOBJ_TTYPE_BURST(9);
+DECLARE_KOBJ_TTYPE_RW(9);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(9);
+
+DECLARE_KOBJ_TTYPE_MASTER(10);
+DECLARE_KOBJ_TTYPE_NBEAT(10);
+DECLARE_KOBJ_TTYPE_NBYTE(10);
+DECLARE_KOBJ_TTYPE_BURST(10);
+DECLARE_KOBJ_TTYPE_RW(10);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(10);
+
+DECLARE_KOBJ_TTYPE_MASTER(11);
+DECLARE_KOBJ_TTYPE_NBEAT(11);
+DECLARE_KOBJ_TTYPE_NBYTE(11);
+DECLARE_KOBJ_TTYPE_BURST(11);
+DECLARE_KOBJ_TTYPE_RW(11);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(11);
+
+DECLARE_KOBJ_TTYPE_MASTER(12);
+DECLARE_KOBJ_TTYPE_NBEAT(12);
+DECLARE_KOBJ_TTYPE_NBYTE(12);
+DECLARE_KOBJ_TTYPE_BURST(12);
+DECLARE_KOBJ_TTYPE_RW(12);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(12);
+
+DECLARE_KOBJ_TTYPE_MASTER(13);
+DECLARE_KOBJ_TTYPE_NBEAT(13);
+DECLARE_KOBJ_TTYPE_NBYTE(13);
+DECLARE_KOBJ_TTYPE_BURST(13);
+DECLARE_KOBJ_TTYPE_RW(13);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(13);
+
+DECLARE_KOBJ_TTYPE_MASTER(14);
+DECLARE_KOBJ_TTYPE_NBEAT(14);
+DECLARE_KOBJ_TTYPE_NBYTE(14);
+DECLARE_KOBJ_TTYPE_BURST(14);
+DECLARE_KOBJ_TTYPE_RW(14);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(14);
+
+DECLARE_KOBJ_TTYPE_MASTER(15);
+DECLARE_KOBJ_TTYPE_NBEAT(15);
+DECLARE_KOBJ_TTYPE_NBYTE(15);
+DECLARE_KOBJ_TTYPE_BURST(15);
+DECLARE_KOBJ_TTYPE_RW(15);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(15);
+
+DECLARE_KOBJ_TTYPE_MASTER(16);
+DECLARE_KOBJ_TTYPE_NBEAT(16);
+DECLARE_KOBJ_TTYPE_NBYTE(16);
+DECLARE_KOBJ_TTYPE_BURST(16);
+DECLARE_KOBJ_TTYPE_RW(16);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(16);
+
+DECLARE_KOBJ_TTYPE_MASTER(17);
+DECLARE_KOBJ_TTYPE_NBEAT(17);
+DECLARE_KOBJ_TTYPE_NBYTE(17);
+DECLARE_KOBJ_TTYPE_BURST(17);
+DECLARE_KOBJ_TTYPE_RW(17);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(17);
+
+DECLARE_KOBJ_TTYPE_MASTER(18);
+DECLARE_KOBJ_TTYPE_NBEAT(18);
+DECLARE_KOBJ_TTYPE_NBYTE(18);
+DECLARE_KOBJ_TTYPE_BURST(18);
+DECLARE_KOBJ_TTYPE_RW(18);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(18);
+
+DECLARE_KOBJ_TTYPE_MASTER(19);
+DECLARE_KOBJ_TTYPE_NBEAT(19);
+DECLARE_KOBJ_TTYPE_NBYTE(19);
+DECLARE_KOBJ_TTYPE_BURST(19);
+DECLARE_KOBJ_TTYPE_RW(19);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(19);
+
+DECLARE_KOBJ_TTYPE_MASTER(20);
+DECLARE_KOBJ_TTYPE_NBEAT(20);
+DECLARE_KOBJ_TTYPE_NBYTE(20);
+DECLARE_KOBJ_TTYPE_BURST(20);
+DECLARE_KOBJ_TTYPE_RW(20);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(20);
+
+DECLARE_KOBJ_TTYPE_MASTER(21);
+DECLARE_KOBJ_TTYPE_NBEAT(21);
+DECLARE_KOBJ_TTYPE_NBYTE(21);
+DECLARE_KOBJ_TTYPE_BURST(21);
+DECLARE_KOBJ_TTYPE_RW(21);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(21);
+
+/**/
+#define KOBJ_ATTR_ITEM_SERIAL_FNODE(nr) \
+	do { \
+		KOBJ_ATTR_ITEM(ttype ## nr ## _master); \
+		KOBJ_ATTR_ITEM(ttype ## nr ## _nbeat); \
+		KOBJ_ATTR_ITEM(ttype ## nr ## _nbyte); \
+		KOBJ_ATTR_ITEM(ttype ## nr ## _burst); \
+		KOBJ_ATTR_ITEM(ttype ## nr ## _busid); \
+		KOBJ_ATTR_ITEM(ttype ## nr ## _rw); \
+	} while (0)
+
+#define KOBJ_ATTR_LIST \
+	do { \
+		KOBJ_ATTR_ITEM(high_priority_filter); \
+		KOBJ_ATTR_ITEM(metemi_func_opt); \
+		KOBJ_ATTR_ITEM(emi_tsct_enable); \
+		KOBJ_ATTR_ITEM(emi_mdct_enable); \
+		KOBJ_ATTR_ITEM(emi_TP_busfiltr_enable); \
+		KOBJ_ATTR_ITEM(emi_regdump); \
+		KOBJ_ATTR_ITEM(msel_enable); \
+		KOBJ_ATTR_ITEM(msel_group1); \
+		KOBJ_ATTR_ITEM(msel_group2); \
+		KOBJ_ATTR_ITEM(msel_group3); \
+		KOBJ_ATTR_ITEM(emi_clock_rate); \
+		KOBJ_ATTR_ITEM(rwtype); \
+		KOBJ_ATTR_ITEM(ttype17_21_en); \
+		KOBJ_ATTR_ITEM(ttype1_16_en); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(1); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(2); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(3); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(4); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(5); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(6); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(7); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(8); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(9); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(10); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(11); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(12); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(13); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(14); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(15); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(16); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(17); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(18); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(19); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(20); \
+		KOBJ_ATTR_ITEM_SERIAL_FNODE(21); \
+		KOBJ_ATTR_ITEM(bw_limiter_enable); \
+		KOBJ_ATTR_ITEM(dramc_pdir_enable); \
+		KOBJ_ATTR_ITEM(test_apmcu); \
+	} while (0)
+
+
+
+/*======================================================================*/
+/*	EMI output														*/
+/*======================================================================*/
+noinline void ms_emi(unsigned char cnt, unsigned int *value)
+{
+	char *SOB, *EOB;
+
+	MET_TRACE_GETBUF(&SOB, &EOB);
+	EOB = ms_formatH_EOL(EOB, cnt, value);
+	MET_TRACE_PUTBUF(SOB, EOB);
+}
+
+noinline void ms_emi_ext(unsigned char cnt, unsigned int *value)
+{
+	char *SOB, *EOB;
+
+	MET_TRACE_GETBUF(&SOB, &EOB);
+	EOB = ms_formatH_EOL(EOB, cnt, value);
+	MET_TRACE_PUTBUF(SOB, EOB);
+}
+
+noinline void ms_emi_tsct(unsigned char cnt, unsigned int *value)
+{
+	char *SOB, *EOB;
+
+	MET_TRACE_GETBUF(&SOB, &EOB);
+	EOB = ms_formatH_EOL(EOB, cnt, value);
+	MET_TRACE_PUTBUF(SOB, EOB);
+}
+
+noinline void ms_emi_mdct(unsigned char cnt, unsigned int *value)
+{
+	char *SOB, *EOB;
+
+	MET_TRACE_GETBUF(&SOB, &EOB);
+	EOB = ms_formatH_EOL(EOB, cnt, value);
+	MET_TRACE_PUTBUF(SOB, EOB);
+}
+
+noinline void ms_ttype(unsigned char cnt, unsigned int *value)
+{
+	char *SOB, *EOB;
+
+	MET_TRACE_GETBUF(&SOB, &EOB);
+	EOB = ms_formatH_EOL(EOB, cnt, value);
+	MET_TRACE_PUTBUF(SOB, EOB);
+}
+
+noinline void ms_bw_limiter(unsigned char cnt, unsigned int *value)
+{
+	char *SOB, *EOB;
+
+	MET_TRACE_GETBUF(&SOB, &EOB);
+	EOB = ms_formatH_EOL(EOB, cnt, value);
+	MET_TRACE_PUTBUF(SOB, EOB);
+}
+
+noinline void ms_dramc(unsigned char cnt, unsigned int *value)
+{
+	char *SOB, *EOB;
+
+	MET_TRACE_GETBUF(&SOB, &EOB);
+	EOB = ms_formatH_EOL(EOB, cnt, value);
+	MET_TRACE_PUTBUF(SOB, EOB);
+}
+
+noinline void DRAM_DVFS(unsigned int dram_data_rate_MHz)
+{
+	MET_TRACE("%u\n", dram_data_rate_MHz);
+}
+
+
+/*======================================================================*/
+/*	EMI Operations														*/
+/*======================================================================*/
+static void emi_init(void)
+{
+	unsigned int bmrw0_val, bmrw1_val, i, enable;
+	unsigned int msel_group_val[4];
+
+	/*save origianl EMI config*/
+	MET_BM_SaveCfg();
+
+	/* get dram channel number */
+	dram_chann_num = MET_EMI_GetDramChannNum();
+
+	/* Init. EMI bus monitor */
+	MET_BM_SetReadWriteType(rwtype);
+
+	if ((ttype1_16_en != BM_TTYPE1_16_ENABLE) && (emi_TP_busfiltr_enable != 1)) {
+		if (msel_enable) {
+			msel_group_val[0] = BM_MASTER_ALL;
+			msel_group_val[1] = msel_group1;
+			msel_group_val[2] = msel_group2;
+			msel_group_val[3] = msel_group3;
+		} else {
+			msel_group_val[0] = BM_MASTER_ALL;
+			msel_group_val[1] = BM_MASTER_ALL;
+			msel_group_val[2] = BM_MASTER_ALL;
+			msel_group_val[3] = BM_MASTER_ALL;
+		}
+
+		MET_BM_SetLatencyCounter(1);    /*enable latency count*/
+
+		for (i = 1; i <= 4; i++) {
+			MET_BM_SetMonitorCounter(i,
+						 msel_group_val[i - 1] & BM_MASTER_ALL,
+						 BM_TRANS_TYPE_4BEAT |
+						 BM_TRANS_TYPE_8Byte |
+						 BM_TRANS_TYPE_BURST_WRAP);
+			MET_BM_SetbusID(i, 0);
+			MET_BM_SetbusID_En(i, 0);       /*disable ttype bus sel*/
+		}
+		for (i = 0; i < 4; i++)
+			MET_BM_Set_WsctTsct_id_sel(i, 0);       /*disable tp filter*/
+
+	} else if ((ttype1_16_en != BM_TTYPE1_16_ENABLE) && (emi_TP_busfiltr_enable == 1)) {
+		MET_BM_SetLatencyCounter(1);    /*enable latency count*/
+
+		for (i = 1; i <= 4; i++) {
+			MET_BM_SetMonitorCounter(i,
+						 ttype_master_val[i - 1],
+						 ttype_nbeat_val[i - 1] |
+						 ttype_nbyte_val[i - 1] |
+						 ttype_burst_val[i - 1]);
+			MET_BM_SetbusID(i, ttype_busid_val[i - 1]);
+			MET_BM_SetbusID_En(i, 0);       /*disable ttype bus sel*/
+		}
+		for (i = 0; i < 4; i++)
+			MET_BM_Set_WsctTsct_id_sel(i, 1);       /*enable tp filter*/
+
+	} else if ((ttype1_16_en == BM_TTYPE1_16_ENABLE) && (emi_TP_busfiltr_enable != 1)) {
+		MET_BM_SetLatencyCounter(0);    /*disable latency count*/
+
+		for (i = 1; i <= 16; i++) {
+			MET_BM_SetMonitorCounter(i,
+						 ttype_master_val[i - 1],
+						 ttype_nbeat_val[i - 1] |
+						 ttype_nbyte_val[i - 1] |
+						 ttype_burst_val[i - 1]);
+
+			MET_BM_SetbusID(i, ttype_busid_val[i - 1]);
+			/*disenable ttype bus sel if busid > 0xff_ff*/
+			MET_BM_SetbusID_En(i, (ttype_busid_val[i - 1] > 0xffff) ? 0 : 1);
+		}
+		for (i = 0; i < 4; i++)
+			MET_BM_Set_WsctTsct_id_sel(i, 0);       /*disable tp filter*/
+	} else {	/* (ttype1_16_en == BM_TTYPE1_16_ENABLE)  &&  (emi_TP_busfiltr_enable == 1) */
+		MET_BM_SetLatencyCounter(0);    /*disable latency count*/
+
+		for (i = 1; i <= 16; i++) {
+			MET_BM_SetMonitorCounter(i,
+						 ttype_master_val[i - 1],
+						 ttype_nbeat_val[i - 1] |
+						 ttype_nbyte_val[i - 1] |
+						 ttype_burst_val[i - 1]);
+
+			MET_BM_SetbusID(i, ttype_busid_val[i - 1]);
+			/*disable ttype bus sel if busid > 0xff_ff*/
+			MET_BM_SetbusID_En(i, (ttype_busid_val[i - 1] > 0xffff) ? 0 : 1);
+		}
+		for (i = 0; i < 4; i++)
+			MET_BM_Set_WsctTsct_id_sel(i, 1);       /*enable tp filter*/
+	}
+
+	if (ttype17_21_en == BM_TTYPE17_21_ENABLE) {
+		for (i = 17; i <= 21; i++) {
+			MET_BM_SetMonitorCounter(i,
+						 ttype_master_val[i - 1],
+						 ttype_nbeat_val[i - 1] |
+						 ttype_nbyte_val[i - 1] |
+						 ttype_burst_val[i - 1]);
+			MET_BM_SetbusID(i, ttype_busid_val[i - 1]);
+			/*disable ttype bus sel if busid > 0xff_ff*/
+			MET_BM_SetbusID_En(i, (ttype_busid_val[i - 1] > 0xffff) ? 0 : 1);
+		}
+	}
+
+	bmrw0_val = 0;
+	for (i = 0; i < 16; i++)
+		bmrw0_val |= (ttype_rw_val[i] << (i * 2));
+
+	bmrw1_val = 0;
+	for (i = 16; i < 21; i++)
+		bmrw1_val |= (ttype_rw_val[i] << ((i-16) * 2));
+
+	MET_BM_SetTtypeCounterRW(bmrw0_val, bmrw1_val);
+
+	for (i = 0; i < BM_COUNTER_MAX; i++) {
+		if ((high_priority_filter & (1 << i)) == 0)
+			enable = 0;
+		else
+			enable = 1;
+
+		MET_BM_SetUltraHighFilter(i + 1, enable);
+	}
+
+	met_record_dramc_dcm_enable_flag();
+
+	if (met_emi_regdump == 1)
+		emi_dump_reg();
+}
+
+
+static void emi_uninit(void)
+{
+	MET_BM_RestoreCfg();
+}
+
+static inline void emi_start(void)
+{
+	MET_BM_Enable(1);
+}
+
+static inline void emi_stop(void)
+{
+	MET_BM_Enable(0);
+}
+
+static inline int do_emi(void)
+{
+	return met_ap_emi.mode;
+}
+
+static unsigned int emi_bw_limiter(unsigned int *__restrict__ array)
+{
+	int idx = 0;
+	unsigned int dram_data_rate_MHz;
+
+	if (get_dram_data_rate_symbol) {
+		dram_data_rate_MHz = get_dram_data_rate_symbol();
+	} else {
+		METERROR("get_dram_data_rate_symbol = NULL\n");
+		dram_data_rate_MHz = 0;
+	}
+
+	/* print dram data rate */
+	DRAM_DVFS(dram_data_rate_MHz);
+
+	/* get correct dram_clock_rate */
+	array[idx++] = dram_data_rate_MHz;
+
+	/* get correct ARB A->LAST */
+	array[idx++] = MET_EMI_GetARBA();
+	array[idx++] = MET_EMI_GetARBB();
+	array[idx++] = MET_EMI_GetARBC();
+	array[idx++] = MET_EMI_GetARBD();
+	array[idx++] = MET_EMI_GetARBE();
+	array[idx++] = MET_EMI_GetARBF();
+	array[idx++] = MET_EMI_GetARBG();
+	array[idx++] = MET_EMI_GetARBH();
+	/* EMI Total BW Thresholds */
+	array[idx++] = MET_EMI_GetBWCT0();
+	array[idx++] = MET_EMI_GetBWCT1();
+	array[idx++] = MET_EMI_GetBWCT2();
+	array[idx++] = MET_EMI_GetBWCT3();
+	array[idx++] = MET_EMI_GetBWCT4();
+	array[idx++] = MET_EMI_GetBWST0();
+	array[idx++] = MET_EMI_GetBWST1();
+	/* EMI C+G BW Thresholds */
+	array[idx++] = MET_EMI_GetBWCT0_2ND();
+	array[idx++] = MET_EMI_GetBWCT1_2ND();
+	array[idx++] = MET_EMI_GetBWST_2ND();
+
+	return idx;
+}
+
+
+static void _ms_dramc(unsigned int *__restrict__ dramc_pdir_value, int dram_chann_num)
+{
+	MET_DRAMC_GetDebugCounter(dramc_pdir_value, dram_chann_num);
+}
+
+
+static unsigned int emi_polling(unsigned int *__restrict__ emi_value, unsigned int *__restrict__ emi_tsct,
+				unsigned int *__restrict__ emi_ttype_value, unsigned int *__restrict__ dramc_pdir_value,
+				unsigned int *__restrict__ emi_mdct_value)
+{
+	int j = 4;              /* skip 4 WSCTs */
+	int i = 0;              /* ttype start at 0 */
+	int k = 0;              /* tsct start at 0 */
+	int n;
+
+	MET_BM_Pause();
+
+	/* Get Word Count */
+	emi_value[0] = MET_BM_GetWordCount(1);  /* All */
+	emi_value[1] = MET_BM_GetWordCount(2);  /* Group 1 */
+	emi_value[2] = MET_BM_GetWordCount(3);  /* Group 2 */
+	emi_value[3] = MET_BM_GetWordCount(4);  /* Group 3 */
+
+	if (ttype1_16_en != BM_TTYPE1_16_ENABLE) {      /*1~21 NOT for ttype*/
+		/* Get Latency */
+		j += MET_BM_GetLatencyCycle(emi_value + j, 1, 8);
+
+		/* Get Trans. */
+		j += MET_BM_GetLatencyCycle(emi_value + j, 9, 16);
+	} else {
+		for (n = 4; n < 20; n++)
+			emi_value[n] = 0;
+		j = 20;
+
+		i += MET_BM_GetLatencyCycle(emi_ttype_value + i, 1, 8);
+
+		/* Get Trans. */
+		i += MET_BM_GetLatencyCycle(emi_ttype_value + i, 9, 16);
+	}
+
+	/* Get BACT/BSCT/BCNT/WACT/DCM_CTRL */
+	emi_value[j++] = MET_BM_GetBandwidthWordCount(); /* 20 */
+	emi_value[j++] = MET_BM_GetOverheadWordCount();
+	emi_value[j++] = MET_BM_GetBusCycCount();
+	emi_value[j++] = MET_BM_GetWordAllCount();
+	emi_value[j++] = MET_DRAMC_DCM_CTRL(0);
+
+	/* Get TACT */
+	emi_value[j++] = MET_BM_GetTransAllCount();
+
+	/* Get PageHist/PageMiss/InterBank/Idle */
+	for (n = 0; n < dram_chann_num; n++) {
+		emi_value[j++] = MET_DRAMC_GetPageHitCount(DRAMC_ALL, n);
+		emi_value[j++] = MET_DRAMC_GetPageMissCount(DRAMC_ALL, n);
+		emi_value[j++] = MET_DRAMC_GetInterbankCount(DRAMC_ALL, n);
+		emi_value[j++] = MET_DRAMC_GetIdleCount(n);
+
+		/*
+			10	8	REFRESH_RATE	RO	PUBLIC	3'b011
+			"Refresh rate reading from LPDDR2/LPDDR3
+			001: 4 x tREFI
+			010: 2 x tREFI
+			011: 1 x tREFI
+			100: 0.5 x tREFI, only for LPDDR3
+			101: 0.25 x tREFI
+			110: 2.25 x tREFI
+			Others: Refer to LPDDR2/LPDDR3 spec."
+		*/
+		emi_value[j++] = ((MET_DRAMC_Misc_Status(n) >> 8) & 0x7); /* refresh rate */
+		emi_value[j++] = MET_DRAMC_RefPop(n);
+		emi_value[j++] = MET_DRAMC_Free26M(n);
+		emi_value[j++] = MET_DRAMC_RByte(n);
+		emi_value[j++] = MET_DRAMC_WByte(n);
+	}
+	/* TTYPE */
+	if (ttype17_21_en == BM_TTYPE17_21_ENABLE)    /*17~21 for ttype*/
+		MET_BM_GetLatencyCycle(emi_ttype_value + 16, 17, 21);
+
+	/* Get tsct */
+	if (emi_tsct_enable == 1) {
+		emi_tsct[k++] = MET_BM_GetTransCount(1);
+		emi_tsct[k++] = MET_BM_GetTransCount(2);
+		emi_tsct[k++] = MET_BM_GetTransCount(3);
+	}
+	/*get mdct rsv buffer*/
+	if (emi_mdct_enable == 1) {
+		emi_mdct_value[0] = (MET_BM_GetMDCT() >> 16) & 0x7;
+		emi_mdct_value[1] = (MET_BM_GetMDCT_2() & 0x7);
+	}
+
+	if (dramc_pdir_enable == 1 && DRAMC_VER >= 2 )
+		_ms_dramc(dramc_pdir_value, dram_chann_num);
+
+	MET_BM_Continue();
+	MET_BM_Clear_Start();
+
+	return j;
+}
+
+
+/*======================================================================*/
+/*	MET Device Operations						*/
+/*======================================================================*/
+static int emi_inited;
+
+static int met_emi_create(struct kobject *parent)
+{
+	int ret = 0;
+	int i;
+
+	for (i = 0; i < 21; i++) {
+		ttype_master_val[i] = BM_MASTER_M0;
+		ttype_nbeat_val[i] = BM_TRANS_TYPE_1BEAT;
+		ttype_nbyte_val[i] = BM_TRANS_TYPE_8Byte;
+		ttype_burst_val[i] = BM_TRANS_TYPE_BURST_INCR;
+		ttype_busid_val[i] = 0xfffff;   /*default disable ttype bus sel if busid > 0xff_ff */
+		ttype_rw_val[i] =  BM_TRANS_RW_DEFAULT;
+	}
+
+	ret = MET_BM_Init();
+	if (ret != 0) {
+		pr_notice("MET_BM_Init failed!!!\n");
+		ret = 0;        /* will retry later */
+	} else {
+		emi_inited = 1;
+	}
+
+	kobj_emi = parent;
+
+#define KOBJ_ATTR_ITEM(attr_name) \
+	do { \
+		ret = sysfs_create_file(kobj_emi, &attr_name ## _attr.attr); \
+		if (ret != 0) { \
+			pr_notice("Failed to create " #attr_name " in sysfs\n"); \
+			return ret; \
+		} \
+	} while (0)
+	KOBJ_ATTR_LIST;
+#undef  KOBJ_ATTR_ITEM
+
+	return ret;
+}
+
+
+static void met_emi_delete(void)
+{
+#define KOBJ_ATTR_ITEM(attr_name) \
+	sysfs_remove_file(kobj_emi, &attr_name ## _attr.attr)
+	if (kobj_emi != NULL) {
+		KOBJ_ATTR_LIST;
+		kobj_emi = NULL;
+	}
+#undef  KOBJ_ATTR_ITEM
+
+	if (emi_inited)
+		MET_BM_DeInit();
+}
+
+
+static void met_emi_start(void)
+{
+	unsigned int bw_limiter[NIDX_BL];
+
+	/* save EMI DCM setting */
+	emi_dcm = MET_BM_GetEmiDcm();
+
+	if (!emi_inited) {
+		if (MET_BM_Init() != 0) {
+			met_ap_emi.mode = 0;
+			pr_notice("MET_BM_Init failed!!!\n");
+			return;
+		}
+		emi_inited = 1;
+	}
+
+	if (do_emi()) {
+		emi_init();
+		MET_BM_Clear_Start();
+
+		/* Draw the first BW Limiter point */
+		if (bw_limiter_enable == BM_BW_LIMITER_ENABLE) {
+			emi_bw_limiter(bw_limiter);
+			ms_bw_limiter(NIDX_BL, bw_limiter);
+			/* init countdown value */
+			countdown = CNT_COUNTDOWN;
+		}
+	}
+
+	/* disable EMI DCM
+	 * 31:24 Disable EMI DCM
+	 *	0: enable DCM
+	 *	1: disable DCM
+	 */
+	MET_BM_SetEmiDcm(0xFF);
+}
+
+
+static void met_emi_stop(void)
+{
+	unsigned int bw_limiter[NIDX_BL];
+
+	if (!emi_inited)
+		return;
+
+	/* restore EMI DCM setting */
+	MET_BM_SetEmiDcm(emi_dcm);
+
+	if (met_emi_regdump == 1)
+		emi_dump_reg();
+
+	if (do_emi()) {
+		/* Draw the last BW Limiter point */
+		if (bw_limiter_enable == BM_BW_LIMITER_ENABLE) {
+			/*
+			 * Skip drawing when we just draw
+			 * the point at last polling.
+			 */
+			if (countdown < CNT_COUNTDOWN) {
+				emi_bw_limiter(bw_limiter);
+				ms_bw_limiter(NIDX_BL, bw_limiter);
+			}
+		}
+
+		emi_stop();
+		emi_uninit();
+	}
+}
+
+
+static void met_emi_polling(unsigned long long stamp, int cpu)
+{
+	unsigned int emi_value[NIDX];
+	unsigned int emi_tsct[3];
+	unsigned int emi_ttype_value[21];
+	unsigned int dramc_pdir_value[DRAMC_Debug_MAX_CNT * NCH];
+	unsigned int emi_mdct_value[2];
+
+	if (!do_emi())
+		return;
+
+	/* get emi & dramc counters */
+	emi_value[0] = 0;       /* 0: pure linux MET , 0xa5: OnDieMET*/
+	emi_value[1] = 0;       /* EBM pause duration (ns)*/
+	emi_polling(emi_value + 2, emi_tsct, emi_ttype_value, dramc_pdir_value, emi_mdct_value);
+
+	/* get and output BW Limiter */
+	if (bw_limiter_enable == BM_BW_LIMITER_ENABLE) {
+		unsigned int bw_limiter[NIDX_BL];
+
+		if (countdown > 0) {
+			countdown--;
+		} else {
+			emi_bw_limiter(bw_limiter);
+			ms_bw_limiter(NIDX_BL, bw_limiter);
+			/* reload countdown value */
+			countdown = CNT_COUNTDOWN;
+		}
+	}
+
+	/* output emi */
+	ms_emi(NIDX_EMI - NTTYPE + (NCNT * dram_chann_num), emi_value);
+
+	/* output tsct*/
+	if (emi_tsct_enable == 1)
+		ms_emi_tsct(3, emi_tsct);
+
+	/* output mdct*/
+	if (emi_mdct_enable == 1)
+		ms_emi_mdct(2, emi_mdct_value);
+
+	/* output dramc*/
+	if (dramc_pdir_enable == 1 && DRAMC_VER >= 2 )
+		ms_dramc(DRAMC_Debug_MAX_CNT * dram_chann_num, dramc_pdir_value);
+
+	/* output ms_ttype */
+	if (ttype17_21_en == BM_TTYPE17_21_ENABLE)
+		ms_ttype(21, emi_ttype_value);
+
+}
+
+
+static void met_emi_resume(void)
+{
+	/* return directly when emi was closed */
+	if (!do_emi())
+		return;
+
+	/* remap EMI_BM related reg*/
+	emi_init();
+
+	/* restarn counting */
+	MET_BM_Clear_Start();
+}
+
+
+static const char help[] = "  --emi                                 monitor EMI banwidth\n";
+static int emi_print_help(char *buf, int len)
+{
+	return snprintf(buf, PAGE_SIZE, help);
+}
+
+
+#define TTYPE_NAME_STR_LEN  64
+/* static char ttype_name[21][TTYPE_NAME_STR_LEN]; */
+static int emi_print_header(char *buf, int len)
+{
+	int ret = 0;
+	int i = 0;
+	unsigned int dram_data_rate_MHz;
+	unsigned int DRAM_TYPE;
+
+	if ((ttype1_16_en != BM_TTYPE1_16_ENABLE) && (emi_TP_busfiltr_enable != 1)) {
+		/* master selection header */
+		if (msel_enable) {
+			ret += snprintf(buf + ret, PAGE_SIZE - ret,
+					"met-info [000] 0.0: met_emi_msel: %x,%x,%x\n",
+					msel_group1 & BM_MASTER_ALL,
+					msel_group2 & BM_MASTER_ALL,
+					msel_group3 & BM_MASTER_ALL);
+		} else {
+			ret += snprintf(buf + ret, PAGE_SIZE - ret,
+					"met-info [000] 0.0: met_emi_msel: %x,%x,%x\n",
+					BM_MASTER_ALL & BM_MASTER_ALL,
+					BM_MASTER_ALL & BM_MASTER_ALL,
+					BM_MASTER_ALL & BM_MASTER_ALL);
+		}
+	} else {
+		/*ttype master if BM_TTYPE1_16_ENABLE or emi_TP_busfiltr_enable*/
+		ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"met-info [000] 0.0: met_emi_ttype_master: %x,%x,%x,%x\n",
+				ttype_master_val[0], ttype_master_val[1], ttype_master_val[2], ttype_master_val[3]);
+
+		if (emi_TP_busfiltr_enable == 1) {
+			/* busID if emi_TP_busfiltr_enable*/
+			ret += snprintf(buf + ret, PAGE_SIZE - ret,
+					"met-info [000] 0.0: met_emi_ttype_busid: %x,%x,%x,%x\n",
+					ttype_busid_val[0], ttype_busid_val[1], ttype_busid_val[2], ttype_busid_val[3]);
+		}
+	}
+
+	/*RW type header*/
+	ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_emi_rw_cfg: ");
+	if (rwtype == BM_READ_ONLY)
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "R");
+	else if (rwtype == BM_WRITE_ONLY)
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "W");
+	else
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "BOTH");
+
+	for (i = 0; i < 21; i++) {
+		if (ttype_rw_val[i] == BM_TRANS_RW_DEFAULT)
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, ",DEFAULT");
+		else if (ttype_rw_val[i] == BM_TRANS_RW_READONLY)
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, ",R");
+		else if (ttype_rw_val[i] == BM_TRANS_RW_WRITEONLY)
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, ",W");
+		else    /*BM_TRANS_RW_RWBOTH*/
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, ",BOTH");
+	}
+	ret += snprintf(buf + ret, PAGE_SIZE - ret, "\n");
+
+	/*ultra header*/
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"met-info [000] 0.0: met_emi_ultra_filter: %x\n", high_priority_filter);
+
+	/* ttype header */
+	if (ttype17_21_en == BM_TTYPE17_21_ENABLE) {
+		int i = 0;
+		int j = 0;
+
+		/* ttype master list */
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_emi_ttype_master_list: ");
+		for (i = 0; i < 21; i++) {
+			for (j = 0; j < ARRAY_SIZE(ttype_master_list_item); j++) {
+				if (ttype_master_val[i] == ttype_master_list_item[j].key) {
+					ret += snprintf(buf + ret, PAGE_SIZE - ret, "%s,", ttype_master_list_item[j].val);
+				}
+			}
+		}
+		/* remove the last comma */
+		snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+		/* ttype busid list */
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_emi_ttype_busid_list: ");
+		for (i = 0; i < 21; i++)
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, "%x,", ttype_busid_val[i]);
+
+		snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+		/* ttype nbeat list */
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_emi_ttype_nbeat_list: ");
+		for (i = 0; i < 21; i++) {
+			for (j = 0; j < ARRAY_SIZE(ttype_nbeat_list_item); j++) {
+				if (ttype_nbeat_val[i] == ttype_nbeat_list_item[j].key) {
+					ret += snprintf(buf + ret, PAGE_SIZE - ret, "%d,", ttype_nbeat_list_item[j].val);
+				}
+			}
+		}
+		snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+		/* ttype nbyte list */
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_emi_ttype_nbyte_list: ");
+		for (i = 0; i < 21; i++) {
+			for (j = 0; j < ARRAY_SIZE(ttype_nbyte_list_item); j++) {
+				if (ttype_nbyte_val[i] == ttype_nbyte_list_item[j].key) {
+					ret += snprintf(buf + ret, PAGE_SIZE - ret, "%d,", ttype_nbyte_list_item[j].val);
+				}
+			}
+		}
+		snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+		/* ttype burst list */
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_emi_ttype_burst_list: ");
+		for (i = 0; i < 21; i++) {
+			for (j = 0; j < ARRAY_SIZE(ttype_burst_list_item); j++) {
+				if (ttype_burst_val[i] == ttype_burst_list_item[j].key) {
+					ret += snprintf(buf + ret, PAGE_SIZE - ret, "%s,", ttype_burst_list_item[j].val);
+				}
+			}
+		}
+		snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+		/* ttype enable */
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_emi_ttype_enable: %d,%d\n",ttype1_16_en, ttype17_21_en);
+	}
+
+	/*IP version*/
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"met-info [000] 0.0: DRAMC_VER: %d\n", DRAMC_VER);
+
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"met-info [000] 0.0: EMI_VER: %d.%d\n", EMI_VER_MAJOR, EMI_VER_MINOR);
+
+	dram_chann_num = MET_EMI_GetDramChannNum();
+	if (get_ddr_type_symbol) {
+		DRAM_TYPE = get_ddr_type_symbol();
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_dram_type: %d\n", DRAM_TYPE);
+
+		if ((DRAM_TYPE == TYPE_LPDDR4) || (DRAM_TYPE == TYPE_PCDDR4))
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_dram_chann_num_header: %d,%d,%d,%d\n",
+					dram_chann_num, DRAM_EMI_BASECLOCK_RATE_LP4,
+					DRAM_IO_BUS_WIDTH_LP4, DRAM_DATARATE);
+		else
+			ret += snprintf(buf + ret , PAGE_SIZE -ret, "met-info [000] 0.0: met_dram_chann_num_header: %d,%d,%d,%d\n",
+					dram_chann_num, DRAM_EMI_BASECLOCK_RATE_LP3,
+					DRAM_IO_BUS_WIDTH_LP3, DRAM_DATARATE);
+	} else
+		METERROR("[%s][%d]get_ddr_type_symbol = NULL , use the TYPE_LPDDR3 setting\n", __func__, __LINE__);
+
+	/* metemi_func_opt for middleware */
+	ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: metemi_func_opt_header: %d\n", metemi_func_opt);
+
+	/* met_emi_clockrate */
+	if (get_dram_data_rate_symbol) {
+		dram_data_rate_MHz = get_dram_data_rate_symbol();
+	} else {
+		METERROR("get_dram_data_rate_symbol = NULL\n");
+		dram_data_rate_MHz = 0;
+	}
+
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"met-info [000] 0.0: met_dram_clockrate: %d\n",
+			dram_data_rate_MHz);
+
+	/* 1 : by ondiemet, 0: by pure linux */
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"met-info [000] 0.0: emi_use_ondiemet: %u\n",
+			emi_use_ondiemet);
+
+	/*dram bank num*/
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"met-info [000] 0.0: met_dram_rank_num_header: %u,%u\n", MET_EMI_GetDramRankNum(),
+				MET_EMI_GetDramRankNum());
+
+	/* ms_emi header */
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"# ms_emi: TS0,TS1,GP0_WSCT,GP1_WSCT,GP2_WSCT,GP3_WSCT,");
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"M0_LATENCY,M1_LATENCY,M2_LATENCY,M3_LATENCY,M4_LATENCY,M5_LATENCY,M6_LATENCY,M7_LATENCY,");
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"M0_TRANS,M1_TRANS,M2_TRANS,M3_TRANS,M4_TRANS,M5_TRANS,M6_TRANS,M7_TRANS,");
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"BACT,BSCT,BCNT,WACT,DCM_CTRL,TACT,");
+
+	for (i = 0; i < dram_chann_num; i++) {
+		if (i != 0)
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, ",");
+		ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"PageHit_%d,PageMiss_%d,InterBank_%d,Idle_%d,", i, i, i, i);
+		ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"mr4_%d,refresh_pop_%d,freerun_26m_%d,", i, i, i);
+		ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"read_bytes_%d,write_bytes_%d", i, i);
+	}
+	ret += snprintf(buf + ret, PAGE_SIZE - ret, "\n");
+
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"met-info [000] 0.0: met_emi_header: TS0,TS1,GP0_WSCT,GP1_WSCT,GP2_WSCT,GP3_WSCT,");
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"M0_LATENCY,M1_LATENCY,M2_LATENCY,M3_LATENCY,M4_LATENCY,M5_LATENCY,M6_LATENCY,M7_LATENCY,");
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"M0_TRANS,M1_TRANS,M2_TRANS,M3_TRANS,M4_TRANS,M5_TRANS,M6_TRANS,M7_TRANS,");
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"BACT,BSCT,BCNT,WACT,DCM_CTRL,TACT,");
+
+	for (i = 0; i < dram_chann_num; i++) {
+		if (i != 0)
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, ",");
+		ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"PageHit_%d,PageMiss_%d,InterBank_%d,Idle_%d,", i, i, i, i);
+		ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"mr4_%d,refresh_pop_%d,freerun_26m_%d,", i, i, i);
+		ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"read_bytes_%d,write_bytes_%d", i, i);
+	}
+	ret += snprintf(buf + ret, PAGE_SIZE - ret, "\n");
+
+	/*TSCT header*/
+	if (emi_tsct_enable == 1) {
+		ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"met-info [000] 0.0: ms_emi_tsct_header: ms_emi_tsct,");
+		ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"tsct1,tsct2,tsct3\n");
+	}
+
+	/*MDCT header*/
+	if (emi_mdct_enable == 1) {
+		ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"met-info [000] 0.0: ms_emi_mdct_header: ms_emi_mdct,");
+		ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"RD_ULTRA,RD_MDMCU\n");
+	}
+
+	/* met_bw_limiter_header */
+	if (bw_limiter_enable == BM_BW_LIMITER_ENABLE) {
+		ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"met-info [000] 0.0: met_bw_limiter_header: CLK,");
+		ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"ARBA,ARBB,ARBC,ARBD,ARBE,ARBF,ARBG,ARBH,BWCT0,BWCT1,BWCT2,BWCT3,BWCT4,BWST0,BWST1,BWCT0_2ND,BWCT1_2ND,BWST_2ND\n");
+	}
+
+	/* DRAM DVFS header */
+	ret += snprintf(buf + ret, PAGE_SIZE - ret,
+			"met-info [000] 0.0: DRAM_DVFS_header: datarate(MHz)\n");
+
+	/*PDIR met_dramc_header*/
+	if (dramc_pdir_enable == 1 && DRAMC_VER >= 2 ) {
+		ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"met-info [000] 0.0: met_dramc_header: ");
+		for (i = 0; i < dram_chann_num; i++) {
+			if (i != 0)
+				ret += snprintf(buf + ret, PAGE_SIZE - ret, ",");
+			ret += snprintf(buf + ret, PAGE_SIZE - ret, "freerun_26m_%d,", i);
+			ret += snprintf(buf + ret, PAGE_SIZE - ret,
+					"rk0_pre_sb_%d,rk0_pre_pd_%d,rk0_act_sb_%d,rk0_act_pd_%d,", i, i, i, i);
+			ret += snprintf(buf + ret, PAGE_SIZE - ret,
+					"rk1_pre_sb_%d,rk1_pre_pd_%d,rk1_act_sb_%d,rk1_act_pd_%d,", i, i, i, i);
+			ret += snprintf(buf + ret, PAGE_SIZE - ret,
+					"rk2_pre_sb_%d,rk2_pre_pd_%d,rk2_act_sb_%d,rk2_act_pd_%d", i, i, i, i);
+		}
+		ret += snprintf(buf + ret, PAGE_SIZE - ret, "\n");
+	}
+
+	return ret;
+}
+
+
+struct metdevice met_ap_emi = {
+	.name			= "emi",
+	.owner			= THIS_MODULE,
+	.type			= MET_TYPE_BUS,
+	.create_subfs	= met_emi_create,
+	.delete_subfs	= met_emi_delete,
+	.cpu_related	= 0,
+	.start			= met_emi_start,
+	.stop			= met_emi_stop,
+	.resume			= met_emi_resume,
+	.timed_polling	= met_emi_polling,
+	.print_help		= emi_print_help,
+	.print_header	= emi_print_header,
+	.ondiemet_mode	= 0,
+};
diff --git a/src/devtools/met-driver/4.9/mt8518/emi/SEDA2/mtk_emi_bm.c b/src/devtools/met-driver/4.9/mt8518/emi/SEDA2/mtk_emi_bm.c
new file mode 100644
index 0000000..81976c0
--- /dev/null
+++ b/src/devtools/met-driver/4.9/mt8518/emi/SEDA2/mtk_emi_bm.c
@@ -0,0 +1,1045 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/io.h>
+#include <mt-plat/sync_write.h>
+#include <mt-plat/mtk_io.h>
+#include "mtk_typedefs.h"
+#include "core_plf_init.h"
+#include "mtk_emi_bm.h"
+#include "mtk_dramc_reg.h"
+#include "met_drv.h"
+#include "interface.h"
+
+#undef	DEBUG
+#undef	debug_reg
+#ifdef	debug_reg
+static inline unsigned int emi_readl(void __iomem *padr)
+{
+	unsigned int tmp;
+
+	tmp = readl(padr);
+	MET_TRACE("[MET_EMI] RD_Reg: %p: %08x\n", padr, tmp);
+	return tmp;
+}
+
+
+static inline void __emi_reg_sync_writel(unsigned int data, void __iomem *padr)
+{
+	unsigned int tmp;
+
+	mt_reg_sync_writel(data, padr);
+	tmp = readl(padr);
+	MET_TRACE("[MET_EMI] WR_Reg: %p: %08x, %08x\n", padr, data, tmp);
+}
+
+
+#define emi_reg_sync_writel(data, adr)  __emi_reg_sync_writel(data, IOMEM(adr))
+
+#else
+#define emi_readl               readl
+#define emi_reg_sync_writel     mt_reg_sync_writel
+#endif
+
+#define MASK_MASTER     0xFF
+#define MASK_TRANS_TYPE 0xFF
+
+static int dram_chann_num;
+static void __iomem *BaseAddrEMI;
+static void __iomem *BaseAddrCHN_EMI[2];
+
+static int dramc0_dcm_enable;
+static int dramc1_dcm_enable;
+
+#define CH0_MISC_CG_CTRL0 (((unsigned long) BaseAddrDDRPHY_AO[0]) + 0x284)
+#define CH1_MISC_CG_CTRL0 (((unsigned long) BaseAddrDDRPHY_AO[1]) + 0x284)
+const unsigned int emi_config[] = {
+	EMI_BMEN,
+	EMI_MSEL,
+	EMI_MSEL2,
+	EMI_MSEL3,
+	EMI_MSEL4,
+	EMI_MSEL5,
+	EMI_MSEL6,
+	EMI_MSEL7,
+	EMI_MSEL8,
+	EMI_MSEL9,
+	EMI_MSEL10,
+	EMI_BMID0,
+	EMI_BMID1,
+	EMI_BMID2,
+	EMI_BMID3,
+	EMI_BMID4,
+	EMI_BMID5,
+	EMI_BMID6,
+	EMI_BMID7,
+	EMI_BMID8,
+	EMI_BMID9,
+	EMI_BMID10,
+	EMI_BMEN1,
+	EMI_BMEN2,
+	EMI_BMRW0,
+	EMI_BMRW1
+};
+#define EMI_CONFIG_MX_NR (sizeof(emi_config)/sizeof(unsigned int))
+static unsigned int emi_config_val[EMI_CONFIG_MX_NR];
+
+
+/*
+ *   MET_REG_BSET/MET_REG_BCLR:
+ *   reading value before set and clear
+ */
+static inline void MET_REG_BSET(unsigned long reg, u32 shift)
+{
+	unsigned int read_val = 0;
+
+	read_val = emi_readl(IOMEM(reg));
+	emi_reg_sync_writel(read_val | (1 << shift), reg);
+}
+
+
+static inline void MET_REG_BCLR(unsigned long reg, u32 shift)
+{
+	unsigned int read_val = 0;
+
+	read_val = emi_readl(IOMEM(reg));
+	emi_reg_sync_writel(read_val & (~((1 << shift) & 0xFFFFFFFF)), reg);
+}
+
+
+int MET_BM_Init(void)
+{
+	int i;
+	int idx;
+
+	/*emi*/
+	if (!mt_cen_emi_base_get_symbol) {
+		METERROR("[%d]mt_cen_emi_base_get_symbol = NULL\n", __LINE__);
+		PR_BOOTMSG_ONCE("[%d]mt_cen_emi_base_get_symbol = NULL\n", __LINE__);
+		return -1;
+	}
+
+	BaseAddrEMI = mt_cen_emi_base_get_symbol();
+	if (BaseAddrEMI == 0) {
+		METERROR("BaseAddrEMI = 0\n");
+		PR_BOOTMSG_ONCE("BaseAddrEMI = 0\n");
+		return -1;
+	}
+
+	METINFO("MET EMI: map emi to %p\n", BaseAddrEMI);
+	PR_BOOTMSG("MET EMI: map emi to %p\n", BaseAddrEMI);
+
+	/* get dram channel number */
+	dram_chann_num = MET_EMI_GetDramChannNum();
+	METINFO("[%s][%d]dram_chann_num = %d\n", __func__, __LINE__, dram_chann_num);
+
+	if (dram_chann_num > MAX_DRAMC_CHANN) {
+		METERROR("dram_chann_num %d > %d\n", dram_chann_num, MAX_DRAMC_CHANN);
+		PR_BOOTMSG("dram_chann_num %d > %d\n", dram_chann_num, MAX_DRAMC_CHANN);
+		return -1;
+	}
+
+	if (!mt_dramc_nao_chn_base_get_symbol) {
+		METERROR("mt_dramc_nao_cha_base_get = NULL\n");
+		PR_BOOTMSG_ONCE("mt_dramc_nao_cha_base_get = NULL\n");
+		return -1;
+	}
+
+	for (i = 0; i < dram_chann_num; i++) {
+		BaseAddrDRAMC[i] = mt_dramc_nao_chn_base_get_symbol(i);
+		if (BaseAddrDRAMC[i] == 0) {
+			METERROR("BaseAddrDRAMC%d = 0\n", i);
+			PR_BOOTMSG_ONCE("BaseAddrDRAMC%d = 0\n", i);
+			return -1;
+		}
+
+		METINFO("MET EMI: map nao dramc%c to %p\n",'A'+i, BaseAddrDRAMC[i]);
+		PR_BOOTMSG("MET EMI: map nao dramc%c to %p\n", 'A'+i, BaseAddrDRAMC[i]);
+	}
+
+	/*dram DRAMC_DTS_DDRPHY_AO*/
+	/* get DRS base address */
+	if (!mt_ddrphy_chn_base_get_symbol) {
+		METERROR("mt_ddrphy_chn_base_get = NULL\n");
+		PR_BOOTMSG_ONCE("mt_ddrphy_chn_base_get = NULL\n");
+		return -1;
+	}
+
+#if 0
+	if (!mt_chn_emi_base_get_symbol) {
+		METERROR("mt_chn_emi_base_get_symbol = NULL\n");
+		PR_BOOTMSG_ONCE("mt_chn_emi_base_get_symbol = NULL\n");
+		return -1;
+	}
+#endif
+
+	for (i = 1; i <= dram_chann_num && i < 3; i++) {
+		idx = i - 1;
+		BaseAddrDDRPHY_AO[idx] = mt_ddrphy_chn_base_get_symbol(idx);
+		if (BaseAddrDDRPHY_AO[idx] == 0) {
+			METERROR("BaseAddrDDRPHY_AO[%d] = 0\n", idx);
+			PR_BOOTMSG_ONCE("BaseAddrDDRPHY_AO[%d] = 0\n", idx);
+			return -1;
+		}
+
+		METINFO("MET EMI: map ddrphy%d AO to %p\n", idx, BaseAddrDDRPHY_AO[idx]);
+		PR_BOOTMSG("MET EMI: map ddrphy%d AO to %p\n", idx, BaseAddrDDRPHY_AO[idx]);
+#if 0
+		BaseAddrCHN_EMI[idx] = mt_chn_emi_base_get_symbol(idx);
+		if (BaseAddrCHN_EMI[idx] == 0) {
+			METERROR("BaseAddrCHN_EMI[%d] = 0\n", idx);
+			PR_BOOTMSG_ONCE("BaseAddrCHN_EMI[%d] = 0\n", idx);
+			return -1;
+		}
+
+		METINFO("MET EMI: map BaseAddrCHN_EMI[%d] to %p\n", idx, BaseAddrCHN_EMI[idx]);
+		PR_BOOTMSG("MET EMI: map BaseAddrCHN_EMI[%d] to %p\n", idx, BaseAddrCHN_EMI[idx]);
+#endif
+	}
+
+	/*dram DRAMC_DTS_DRAMC0_AO*/
+	if (!mt_dramc_chn_base_get_symbol) {
+		METERROR("mt_dramc_chn_base_get = NULL\n");
+		PR_BOOTMSG_ONCE("mt_dramc_chn_base_get = NULL\n");
+		return -1;
+	}
+
+	BaseAddrDRAMC0_AO = mt_dramc_chn_base_get_symbol(0);
+	if (BaseAddrDRAMC0_AO == 0) {
+		METERROR("BaseAddrDRAMC0_AO = 0\n");
+		PR_BOOTMSG_ONCE("BaseAddrDRAMC0_AO = 0\n");
+		return -1;
+	}
+
+	METINFO("MET EMI: map AO dramcA to %p\n", BaseAddrDRAMC0_AO);
+	PR_BOOTMSG("MET EMI: map AO dramcA to %p\n", BaseAddrDRAMC0_AO);
+
+	return 0;
+}
+
+
+void MET_BM_DeInit(void)
+{
+}
+
+
+void MET_BM_SaveCfg(void)
+{
+	int i;
+
+	for (i = 0; i < EMI_CONFIG_MX_NR; i++)
+		emi_config_val[i] = emi_readl(IOMEM(ADDR_EMI + emi_config[i]));
+}
+
+
+void MET_BM_RestoreCfg(void)
+{
+	int i;
+
+	for (i = 0; i < EMI_CONFIG_MX_NR; i++)
+		emi_reg_sync_writel(emi_config_val[i], ADDR_EMI + emi_config[i]);
+}
+
+
+void MET_BM_Clear_Start(void)
+{
+	/* Force EMI idle low */
+	MET_REG_BSET(ADDR_EMI + EMI_BMEN, BUS_MON_IDLE_SHIFT);
+
+	/* Disable dramc dcm */
+	switch (dram_chann_num) {
+	case 1:
+		MET_REG_BSET(CH0_MISC_CG_CTRL0, DRAMC_CG_SHIFT);
+		break;
+	case 2:
+		MET_REG_BSET(CH0_MISC_CG_CTRL0, DRAMC_CG_SHIFT);
+		MET_REG_BSET(CH1_MISC_CG_CTRL0, DRAMC_CG_SHIFT);
+		break;
+	default:
+		METERROR("Error: dram_chann_num = %d\n", dram_chann_num);
+	}
+
+	/* Disable EBM */
+	MET_REG_BCLR(ADDR_EMI + EMI_BMEN, BUS_MON_EN_SHIFT);
+
+	/* Enable EBM */
+	MET_REG_BSET(ADDR_EMI + EMI_BMEN, BUS_MON_EN_SHIFT);
+
+	/* Enable EMI dcm */
+	MET_REG_BCLR(ADDR_EMI + EMI_BMEN, BUS_MON_IDLE_SHIFT);
+
+	/* restore dramc dcm */
+	switch (dram_chann_num) {
+	case 1:
+		if (dramc0_dcm_enable)
+			MET_REG_BCLR(CH0_MISC_CG_CTRL0, DRAMC_CG_SHIFT);
+		else
+			MET_REG_BSET(CH0_MISC_CG_CTRL0, DRAMC_CG_SHIFT);
+		break;
+	case 2:
+		if (dramc0_dcm_enable)
+			MET_REG_BCLR(CH0_MISC_CG_CTRL0, DRAMC_CG_SHIFT);
+		else
+			MET_REG_BSET(CH0_MISC_CG_CTRL0, DRAMC_CG_SHIFT);
+
+		if (dramc1_dcm_enable)
+			MET_REG_BCLR(CH1_MISC_CG_CTRL0, DRAMC_CG_SHIFT);
+		else
+			MET_REG_BSET(CH1_MISC_CG_CTRL0, DRAMC_CG_SHIFT);
+		break;
+	default:
+		METERROR("Error: dram_chann_num = %d\n", dram_chann_num);
+	}
+}
+
+
+void MET_BM_Enable(const unsigned int enable)
+{
+	unsigned long int value_check;
+	int i = 0;
+
+	while (i < 100) {
+		/* Force EMI idle low */
+		MET_REG_BSET(ADDR_EMI + EMI_BMEN, BUS_MON_IDLE_SHIFT);
+
+		/* disable dramc dcm */
+		switch (dram_chann_num) {
+		case 1:
+			MET_REG_BSET(CH0_MISC_CG_CTRL0, DRAMC_CG_SHIFT);
+			break;
+		case 2:
+			MET_REG_BSET(CH0_MISC_CG_CTRL0, DRAMC_CG_SHIFT);
+			MET_REG_BSET(CH1_MISC_CG_CTRL0, DRAMC_CG_SHIFT);
+			break;
+		default:
+			METERROR("Error: dram_chann_num = %d\n", dram_chann_num);
+		}
+
+		if (enable == 0)
+			/* Disable EBM */
+			MET_REG_BCLR(ADDR_EMI + EMI_BMEN, BUS_MON_EN_SHIFT);
+		else
+			/* Enable EBM */
+			MET_REG_BSET(ADDR_EMI + EMI_BMEN, BUS_MON_EN_SHIFT);
+
+		/* Enable EMI dcm */
+		MET_REG_BCLR(ADDR_EMI + EMI_BMEN, BUS_MON_IDLE_SHIFT);
+
+		/* restore dramc dcm */
+		switch (dram_chann_num) {
+		case 1:
+			if (dramc0_dcm_enable)
+				MET_REG_BCLR(CH0_MISC_CG_CTRL0, DRAMC_CG_SHIFT);
+			else
+				MET_REG_BSET(CH0_MISC_CG_CTRL0, DRAMC_CG_SHIFT);
+			break;
+		case 2:
+			if (dramc0_dcm_enable)
+				MET_REG_BCLR(CH0_MISC_CG_CTRL0, DRAMC_CG_SHIFT);
+			else
+				MET_REG_BSET(CH0_MISC_CG_CTRL0, DRAMC_CG_SHIFT);
+
+			if (dramc1_dcm_enable)
+				MET_REG_BCLR(CH1_MISC_CG_CTRL0, DRAMC_CG_SHIFT);
+			else
+				MET_REG_BSET(CH1_MISC_CG_CTRL0, DRAMC_CG_SHIFT);
+			break;
+		default:
+			METERROR("Error: dram_chann_num = %d\n", dram_chann_num);
+		}
+
+		value_check = emi_readl(IOMEM(ADDR_EMI + EMI_BMEN));
+
+		if (enable == 0) {
+			/* EN == 0, IDLE == 0 when EMI RESET */
+			if (!test_bit(BUS_MON_EN_SHIFT, &value_check)
+			    && !test_bit(BUS_MON_IDLE_SHIFT, &value_check)) {
+				break;
+			}
+		} else {
+			/* EN == 1, IDLE == 0 when EMI START */
+			if (test_bit(BUS_MON_EN_SHIFT, &value_check)
+			    && !test_bit(BUS_MON_IDLE_SHIFT, &value_check)) {
+				break;
+			}
+		}
+		i++;
+	}
+
+	/*MET_TRACE("[MET_BM_ENABLE] value_check: %lx, enable = %d\n", value_check, enable); */
+
+}
+
+
+#if 0
+void BM_Disable(void)
+{
+	const unsigned int value = emi_readl(IOMEM(ADDR_EMI + EMI_BMEN));
+
+	emi_reg_sync_writel(value & (~BUS_MON_EN), ADDR_EMI + EMI_BMEN);
+}
+#endif
+
+
+void MET_BM_Pause(void)
+{
+	const unsigned int value = emi_readl(IOMEM(ADDR_EMI + EMI_BMEN));
+
+	emi_reg_sync_writel(value | (1 << BUS_MON_PAUSE_SHIFT), ADDR_EMI + EMI_BMEN);
+}
+
+
+void MET_BM_Continue(void)
+{
+	const unsigned int value = emi_readl(IOMEM(ADDR_EMI + EMI_BMEN));
+
+	emi_reg_sync_writel(value & (~(1 << BUS_MON_PAUSE_SHIFT)), ADDR_EMI + EMI_BMEN);
+}
+
+
+unsigned int MET_BM_IsOverrun(void)
+{
+	/*
+	 * return 0 if EMI_BCNT(bus cycle counts) or
+	 * EMI_WACT(total word counts) is overrun,
+	 * otherwise return an !0 value
+	 */
+	const unsigned int value = emi_readl(IOMEM(ADDR_EMI + EMI_BMEN));
+
+	return (value & (1 << BC_OVERRUN_SHIFT));
+}
+
+
+unsigned int MET_BM_GetReadWriteType(void)
+{
+	const unsigned int value = emi_readl(IOMEM(ADDR_EMI + EMI_BMEN));
+
+	return ((value & 0xFFFFFFCF) >> 4);
+}
+
+
+void MET_BM_SetReadWriteType(const unsigned int ReadWriteType)
+{
+	const unsigned int value = emi_readl(IOMEM(ADDR_EMI + EMI_BMEN));
+
+	/*
+	 * ReadWriteType: 00/11 --> both R/W
+	 *                   01 --> only R
+	 *                   10 --> only W
+	 */
+	emi_reg_sync_writel((value & 0xFFFFFFCF) | (ReadWriteType << 4), ADDR_EMI + EMI_BMEN);
+}
+
+
+int MET_BM_GetBusCycCount(void)
+{
+	return MET_BM_IsOverrun() ? BM_ERR_OVERRUN : emi_readl(IOMEM(ADDR_EMI + EMI_BCNT));	/*Bus cycle counter */
+}
+
+
+unsigned int MET_BM_GetTransAllCount(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI + EMI_TACT));
+}
+
+
+int MET_BM_GetTransCount(const unsigned int counter_num)
+{
+	unsigned int iCount;
+
+	switch (counter_num) {
+	case 1:
+		iCount = emi_readl(IOMEM(ADDR_EMI + EMI_TSCT));
+		break;
+
+	case 2:
+		iCount = emi_readl(IOMEM(ADDR_EMI + EMI_TSCT2));
+		break;
+
+	case 3:
+		iCount = emi_readl(IOMEM(ADDR_EMI + EMI_TSCT3));
+		break;
+
+	default:
+		return BM_ERR_WRONG_REQ;
+	}
+
+	return iCount;
+}
+
+
+int MET_BM_GetWordAllCount(void)
+{
+	return MET_BM_IsOverrun() ? BM_ERR_OVERRUN : emi_readl(IOMEM(ADDR_EMI + EMI_WACT));
+}
+
+
+int MET_BM_GetWordCount(const unsigned int counter_num)
+{
+	unsigned int iCount;
+
+	switch (counter_num) {
+	case 1:
+		iCount = emi_readl(IOMEM(ADDR_EMI + EMI_WSCT));
+		break;
+
+	case 2:
+		iCount = emi_readl(IOMEM(ADDR_EMI + EMI_WSCT2));
+		break;
+
+	case 3:
+		iCount = emi_readl(IOMEM(ADDR_EMI + EMI_WSCT3));
+		break;
+
+	case 4:
+		iCount = emi_readl(IOMEM(ADDR_EMI + EMI_WSCT4));
+		break;
+
+	default:
+		return BM_ERR_WRONG_REQ;
+	}
+
+	return iCount;
+}
+
+
+unsigned int MET_BM_GetBandwidthWordCount(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI + EMI_BACT));	/*Bandwidth counter for access */
+}
+
+
+unsigned int MET_BM_GetOverheadWordCount(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI + EMI_BSCT));	/*Overhead counter */
+}
+
+
+int MET_BM_GetTransTypeCount(const unsigned int counter_num)
+{
+	return (counter_num < 1 || counter_num > BM_COUNTER_MAX)
+	    ? BM_ERR_WRONG_REQ : emi_readl(IOMEM(ADDR_EMI + EMI_TTYPE1 + (counter_num - 1) * 8));
+}
+
+
+int MET_BM_GetMDCT(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI + EMI_MDCT));
+}
+
+
+int MET_BM_GetMDCT_2(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI + EMI_MDCT_2ND));
+}
+
+
+int MET_BM_SetMDCT_MDMCU(unsigned int mdmcu_rd_buf)
+{
+	unsigned int value_origin;
+
+	value_origin = emi_readl(IOMEM(ADDR_EMI + EMI_MDCT_2ND));
+	MET_TRACE("[MET_BM_SetMDCT_MDMCU] value_origin: %x\n", value_origin);
+
+	value_origin = value_origin & ~(0x7);
+	value_origin = value_origin | ((mdmcu_rd_buf) & 0x7);
+
+	emi_reg_sync_writel(value_origin, ADDR_EMI + EMI_MDCT_2ND);
+
+	return BM_REQ_OK;
+}
+
+
+int MET_BM_GetMonitorCounter(const unsigned int counter_num,
+			     unsigned int *master, unsigned int *trans_type)
+{
+	unsigned int value, addr;
+
+	if (counter_num < 1 || counter_num > BM_COUNTER_MAX)
+		return BM_ERR_WRONG_REQ;
+
+
+	if (counter_num == 1) {
+		addr = EMI_BMEN;
+		value = emi_readl(IOMEM(ADDR_EMI + addr));
+		*master = (value >> 16) & MASK_MASTER;
+		*trans_type = (value >> 24) & MASK_TRANS_TYPE;
+	} else {
+		addr = (counter_num <= 3) ? EMI_MSEL : (EMI_MSEL2 + (counter_num / 2 - 2) * 8);
+		value = emi_readl(IOMEM(ADDR_EMI + addr)) >> ((counter_num % 2) * 16);
+		*master = value & MASK_MASTER;
+		*trans_type = (value >> 8) & MASK_TRANS_TYPE;
+	}
+
+	return BM_REQ_OK;
+}
+
+
+int MET_BM_SetMonitorCounter(const unsigned int counter_num,
+			     const unsigned int master, const unsigned int trans_type)
+{
+	unsigned int value, addr;
+	const unsigned int iMask = (MASK_TRANS_TYPE << 8) | MASK_MASTER;
+
+	if (counter_num < 1 || counter_num > BM_COUNTER_MAX)
+		return BM_ERR_WRONG_REQ;
+
+
+	if (counter_num == 1) {
+		addr = EMI_BMEN;
+		value = (emi_readl(IOMEM(ADDR_EMI + addr)) & ~(iMask << 16)) |
+		    ((trans_type & MASK_TRANS_TYPE) << 24) | ((master & MASK_MASTER) << 16);
+	} else {
+		addr = (counter_num <= 3) ? EMI_MSEL : (EMI_MSEL2 + (counter_num / 2 - 2) * 8);
+
+		/* clear master and transaction type fields */
+		value = emi_readl(IOMEM(ADDR_EMI + addr)) & ~(iMask << ((counter_num % 2) * 16));
+
+		/* set master and transaction type fields */
+		value |= (((trans_type & MASK_TRANS_TYPE) << 8) |
+			  (master & MASK_MASTER)) << ((counter_num % 2) * 16);
+	}
+
+	emi_reg_sync_writel(value, ADDR_EMI + addr);
+
+	return BM_REQ_OK;
+}
+
+
+int MET_BM_SetTtypeCounterRW(unsigned int bmrw0_val, unsigned int bmrw1_val)
+{
+
+	unsigned int value_origin;
+
+	value_origin = emi_readl(IOMEM(ADDR_EMI + EMI_BMRW0));
+	MET_TRACE("[MET_EMI_settype1] value_origin: %x\n", value_origin);
+	if (value_origin != bmrw0_val) {
+		emi_reg_sync_writel(bmrw0_val, ADDR_EMI + EMI_BMRW0);
+		MET_TRACE("[MET_EMI_settype1] bmrw0_val: %x, value_origin: %x\n", bmrw0_val,
+			   value_origin);
+	}
+
+
+	value_origin = emi_readl(IOMEM(ADDR_EMI + EMI_BMRW1));
+	MET_TRACE("[MET_EMI_settype2] value_origin: %x\n", value_origin);
+	if (value_origin != bmrw1_val) {
+		emi_reg_sync_writel(bmrw1_val, ADDR_EMI + EMI_BMRW1);
+		MET_TRACE("[MET_EMI_settype2] bmrw0_val: %x, value_origin: %x\n", bmrw1_val,
+			   value_origin);
+
+	}
+	return BM_REQ_OK;
+}
+
+
+int MET_BM_Set_WsctTsct_id_sel(unsigned int counter_num, unsigned int enable)
+{
+	unsigned int value;
+
+	if (counter_num > 3)
+		return BM_ERR_WRONG_REQ;
+
+	value =
+	    ((emi_readl(IOMEM(ADDR_EMI + EMI_BMEN2)) & (~(1 << (28 + counter_num)))) |
+	     (enable << (28 + counter_num)));
+	emi_reg_sync_writel(value, ADDR_EMI + EMI_BMEN2);
+
+	return BM_REQ_OK;
+}
+
+
+int MET_BM_SetMaster(const unsigned int counter_num, const unsigned int master)
+{
+	unsigned int value, addr;
+	const unsigned int iMask = 0x7F;
+
+	if (counter_num < 1 || counter_num > BM_COUNTER_MAX)
+		return BM_ERR_WRONG_REQ;
+
+
+	if (counter_num == 1) {
+		addr = EMI_BMEN;
+		value =
+		    (emi_readl(IOMEM(ADDR_EMI + addr)) & ~(iMask << 16)) | ((master & iMask) << 16);
+	} else {
+		addr = (counter_num <= 3) ? EMI_MSEL : (EMI_MSEL2 + (counter_num / 2 - 2) * 8);
+
+		/* clear master and transaction type fields */
+		value = emi_readl(IOMEM(ADDR_EMI + addr)) & ~(iMask << ((counter_num % 2) * 16));
+
+		/* set master and transaction type fields */
+		value |= ((master & iMask) << ((counter_num % 2) * 16));
+	}
+
+	emi_reg_sync_writel(value, ADDR_EMI + addr);
+
+	return BM_REQ_OK;
+}
+
+
+int MET_BM_SetbusID_En(const unsigned int counter_num,
+		       const unsigned int enable)
+{
+	unsigned int value;
+
+	if ((counter_num < 1 || counter_num > BM_COUNTER_MAX) || (enable > 1))
+		return BM_ERR_WRONG_REQ;
+
+	if (enable == 0) {
+		/* clear  EMI ID selection Enabling   SEL_ID_EN */
+		value = (emi_readl(IOMEM(ADDR_EMI + EMI_BMEN2))
+			 & ~(1 << (counter_num - 1)));
+	} else {
+		/* enable  EMI ID selection Enabling   SEL_ID_EN */
+		value = (emi_readl(IOMEM(ADDR_EMI + EMI_BMEN2))
+			 | (1 << (counter_num - 1)));
+	}
+	emi_reg_sync_writel(value, ADDR_EMI + EMI_BMEN2);
+
+	return BM_REQ_OK;
+}
+
+
+int MET_BM_SetbusID(const unsigned int counter_num,
+		    const unsigned int id)
+{
+	unsigned int value, addr, shift_num;
+
+	if ((counter_num < 1 || counter_num > BM_COUNTER_MAX))
+		return BM_ERR_WRONG_REQ;
+
+	/* offset of EMI_BMIDx register */
+	addr = EMI_BMID0 + (counter_num - 1) / 2 * 4;
+	shift_num = ((counter_num - 1) % 2) * 16;
+	/* clear SELx_ID field */
+	value = emi_readl(IOMEM(ADDR_EMI + addr)) & ~(EMI_BMID_MASK << shift_num);
+
+	/* set SELx_ID field */
+	if (id <= 0xffff)       /*bigger then 0xff_ff : no select busid in master, reset busid as 0*/
+		value |= id << shift_num;
+
+	emi_reg_sync_writel(value, ADDR_EMI + addr);
+
+	return BM_REQ_OK;
+}
+
+
+int MET_BM_SetUltraHighFilter(const unsigned int counter_num, const unsigned int enable)
+{
+	unsigned int value;
+
+	if ((counter_num < 1 || counter_num > BM_COUNTER_MAX) || (enable > 1))
+		return BM_ERR_WRONG_REQ;
+
+
+	value = (emi_readl(IOMEM(ADDR_EMI + EMI_BMEN1))
+		 & ~(1 << (counter_num - 1)))
+		| (enable << (counter_num - 1));
+
+	emi_reg_sync_writel(value, ADDR_EMI + EMI_BMEN1);
+
+	return BM_REQ_OK;
+}
+
+
+int MET_BM_SetLatencyCounter(unsigned int enable)
+{
+	unsigned int value;
+
+	value = emi_readl(IOMEM(ADDR_EMI + EMI_BMEN2)) & ~(0x3 << 24);
+	/*
+	 * emi_ttype1 -- emi_ttype8 change as total latencies
+	 * for m0 -- m7,
+	 * and emi_ttype9 -- emi_ttype16 change as total transaction counts
+	 * for m0 -- m7
+	 */
+	if (enable == 1)
+		value |= (0x2 << 24);
+
+	emi_reg_sync_writel(value, ADDR_EMI + EMI_BMEN2);
+
+	return BM_REQ_OK;
+}
+
+
+int MET_BM_GetLatencyCycle(unsigned int *__restrict__ emi_value,
+				const unsigned int begin, const unsigned int end)
+{
+	int i, j = 0;
+
+	for (i = begin - 1; i < end; i++)
+		emi_value[j++] = emi_readl(IOMEM(ADDR_EMI + (EMI_TTYPE1 + i * 8)));
+
+	return j;
+}
+
+
+unsigned int MET_BM_GetEmiDcm(void)
+{
+	return (emi_readl(IOMEM(ADDR_EMI + EMI_CONM)) >> 24);
+}
+
+
+int MET_BM_SetEmiDcm(const unsigned int setting)
+{
+	unsigned int value;
+
+	value = emi_readl(IOMEM(ADDR_EMI + EMI_CONM));
+	emi_reg_sync_writel((value & 0x00FFFFFF) | (setting << 24), ADDR_EMI + EMI_CONM);
+
+	return BM_REQ_OK;
+}
+
+
+unsigned int MET_EMI_GetMDCT(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI + EMI_MDCT));
+}
+
+
+unsigned int MET_EMI_GetMDCT_2ND(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI + EMI_MDCT_2ND));
+}
+
+
+unsigned int MET_EMI_GetARBA(void)
+{
+	/* EMI_ARBA EMI Bandwidth Filter Control M0/1 */
+	return emi_readl(IOMEM(ADDR_EMI + EMI_ARBA));
+}
+
+
+unsigned int MET_EMI_GetARBB(void)
+{
+	/* need the ATBB for mt6739 */
+	return emi_readl(IOMEM(ADDR_EMI+EMI_ARBB));
+}
+
+
+unsigned int MET_EMI_GetARBC(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI + EMI_ARBC));
+}
+
+
+unsigned int MET_EMI_GetARBD(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI + EMI_ARBD));
+}
+
+
+unsigned int MET_EMI_GetARBE(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI + EMI_ARBE));
+}
+
+
+unsigned int MET_EMI_GetARBF(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI + EMI_ARBF));
+}
+
+
+unsigned int MET_EMI_GetARBG(void)
+{
+	return 0;
+}
+
+
+unsigned int MET_EMI_GetARBH(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI + EMI_ARBH));
+}
+
+
+/* Total BW status*/
+unsigned int MET_EMI_GetBWCT0(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI + EMI_BWCT0));
+}
+
+
+/* Total BW status*/
+unsigned int MET_EMI_GetBWCT1(void)
+{
+	return 0;
+}
+
+
+/* Total BW status*/
+unsigned int MET_EMI_GetBWCT2(void)
+{
+	return 0;
+}
+
+
+/* Total BW status*/
+unsigned int MET_EMI_GetBWCT3(void)
+{
+	return 0;
+}
+
+
+/* Total BW status*/
+unsigned int MET_EMI_GetBWCT4(void)
+{
+	return 0;
+}
+
+
+/* Total BW status*/
+unsigned int MET_EMI_GetBWST0(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI + EMI_BWST0));
+}
+
+
+/* Total BW status*/
+unsigned int MET_EMI_GetBWST1(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI + EMI_BWST1));
+}
+
+
+/* C+G BW status*/
+unsigned int MET_EMI_GetBWCT0_2ND(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI + EMI_BWCT0_2ND));
+}
+
+
+/* C+G BW status*/
+unsigned int MET_EMI_GetBWCT1_2ND(void)
+{
+	return 0;
+}
+
+
+/* C+G BW status*/
+unsigned int MET_EMI_GetBWST_2ND(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI + EMI_BWST_2ND));
+}
+
+
+unsigned int MET_EMI_GetBMRW0(void)
+{
+	return readl(IOMEM(ADDR_EMI + EMI_BMRW0));
+}
+
+
+void emi_dump_reg(void)
+{
+	int i;
+
+	MET_TRACE("[emi_regdump]\n");
+	for (i = 0x400; i < 0x500; i = i + 16)
+		MET_TRACE("%4x__ %8x %8x %8x %8x\n", i, readl(IOMEM(ADDR_EMI + i)),
+			   readl(IOMEM(ADDR_EMI + i + 4)), readl(IOMEM(ADDR_EMI + i + 8)),
+			   readl(IOMEM(ADDR_EMI + i + 12)));
+}
+
+
+unsigned int MET_EMI_GetDramChannNum(void)
+{
+#if 0
+	int num = -1;
+
+	if (BaseAddrEMI) {
+		num = emi_readl(IOMEM(ADDR_EMI + EMI_CONA));
+		num = ((num >> 8) & 0x0000003);
+	} else {
+		return 1;
+	}
+
+	if (num == M0_DOUBLE_HALF_BW_1CH)
+		return 1;
+	else if (num == M0_DOUBLE_HALF_BW_2CH)
+		return 2;
+	else if (num == M0_DOUBLE_HALF_BW_4CH)
+		return 4;
+	else                    /* default return single channel */
+		return 1;
+#endif 
+
+	return 1;
+}
+
+
+unsigned int MET_EMI_GetDramRankNum(void)
+{
+	int dual_rank = 0;
+
+	if (BaseAddrEMI) {
+		dual_rank = emi_readl(IOMEM(ADDR_EMI + EMI_CONA));
+		dual_rank = ((dual_rank >> 17) & RANK_MASK);
+	} else {
+		return DUAL_RANK;
+	}
+
+	if (dual_rank == DISABLE_DUAL_RANK_MODE)
+		return ONE_RANK;
+	else			/* default return dual rank */
+		return DUAL_RANK;
+}
+
+
+/*
+ * Dual Channel:
+ * Enables two rank for CHB
+ * Quad Channel:
+ * Enables two rank  for CHC and CHD
+ * 0: Disable dual rank mode
+ * 1: Enable dual rank mode
+ */
+unsigned int MET_EMI_GetDramRankNum_CHN1(void)
+{
+	int dual_rank = 0;
+
+	if (BaseAddrEMI) {
+		dual_rank = emi_readl(IOMEM(ADDR_EMI + EMI_CONA));
+		dual_rank = ((dual_rank >> 16) & RANK_MASK);
+	} else {
+		return DUAL_RANK;
+	}
+
+	if (dual_rank == DISABLE_DUAL_RANK_MODE)
+		return ONE_RANK;
+	else			/* default return dual rank */
+		return DUAL_RANK;
+}
+
+
+void met_record_dramc_dcm_enable_flag(void)
+{
+	int reg_val;
+
+	switch (dram_chann_num) {
+	case 1:
+		reg_val = emi_readl(IOMEM(CH0_MISC_CG_CTRL0));
+		dramc0_dcm_enable = !((reg_val >> DRAMC_CG_SHIFT) & 0x1);
+		break;
+	case 2:
+		reg_val = emi_readl(IOMEM(CH0_MISC_CG_CTRL0));
+		dramc0_dcm_enable = !((reg_val >> DRAMC_CG_SHIFT) & 0x1);
+
+		reg_val = emi_readl(IOMEM(CH1_MISC_CG_CTRL0));
+		dramc1_dcm_enable = !((reg_val >> DRAMC_CG_SHIFT) & 0x1);
+		break;
+	default:
+		METERROR("Error: dram_chann_num = %d\n", dram_chann_num);
+	}
+}
diff --git a/src/devtools/met-driver/4.9/mt8518/emi/SEDA2/mtk_emi_bm.h b/src/devtools/met-driver/4.9/mt8518/emi/SEDA2/mtk_emi_bm.h
new file mode 100644
index 0000000..48518a9
--- /dev/null
+++ b/src/devtools/met-driver/4.9/mt8518/emi/SEDA2/mtk_emi_bm.h
@@ -0,0 +1,318 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#ifndef __MT_MET_EMI_BM_H__
+#define __MT_MET_EMI_BM_H__
+
+#define EMI_VER_MAJOR  2
+#define EMI_VER_MINOR  0
+
+#define DEF_BM_RW_TYPE          (BM_BOTH_READ_WRITE)
+#define NTS                     2
+#define NWSCT                   4
+#define NLATENCY                8
+#define NTRANS                  8
+#define NALL                    (3 + 2 + 1)
+#define NTTYPE                  5
+#define NIDX_EMI                (NTS + NWSCT + NLATENCY + NTRANS + NALL + NTTYPE)
+
+#define NCNT                    9
+#define NCH                     2
+#define NIDX_DRAMC              (NCNT * NCH)
+#define NIDX                    (NIDX_EMI + NIDX_DRAMC)
+
+#define NCLK                    1
+#define NARB                    8
+#define NBW                     10
+#define NIDX_BL                 (NCLK + NARB + NBW)
+
+/* 1000 To Khz and 4x freq & 2x data rate for LPDDR4 */
+/* 1000 To Khz and 2x freq & 2x data rate for LPDDR3*/
+/* TBD: calculate emi clock rate from DRAM DATA RATE */
+/*dram baseclock/EMI clock  :	LP4=4	LP3=2	*/
+#define DRAM_EMI_BASECLOCK_RATE_LP4     4
+#define DRAM_EMI_BASECLOCK_RATE_LP3     2
+/*dram io width  :	LP4=x16		LP3=x32 */
+#define DRAM_IO_BUS_WIDTH_LP4           16
+#define DRAM_IO_BUS_WIDTH_LP3           32
+/*dram datarate  :	DDR=double */
+#define DRAM_DATARATE   		2
+
+#define ADDR_EMI        ((unsigned long)BaseAddrEMI)
+
+#define BM_MASTER_M0    (0x01)
+#define BM_MASTER_M1    (0x02)
+#define BM_MASTER_M2    (0x04)
+#define BM_MASTER_M3    (0x08)
+#define BM_MASTER_M4    (0x10)
+#define BM_MASTER_M5    (0x20)
+#define BM_MASTER_M6    (0x40)
+#define BM_MASTER_M7    (0x80)
+#define BM_MASTER_ALL   (0xFF)
+
+
+enum BM_RW_Type {
+	BM_BOTH_READ_WRITE,
+	BM_READ_ONLY,
+	BM_WRITE_ONLY
+};
+
+enum {
+	BM_TRANS_TYPE_1BEAT = 0x0,
+	BM_TRANS_TYPE_2BEAT,
+	BM_TRANS_TYPE_3BEAT,
+	BM_TRANS_TYPE_4BEAT,
+	BM_TRANS_TYPE_5BEAT,
+	BM_TRANS_TYPE_6BEAT,
+	BM_TRANS_TYPE_7BEAT,
+	BM_TRANS_TYPE_8BEAT,
+	BM_TRANS_TYPE_9BEAT,
+	BM_TRANS_TYPE_10BEAT,
+	BM_TRANS_TYPE_11BEAT,
+	BM_TRANS_TYPE_12BEAT,
+	BM_TRANS_TYPE_13BEAT,
+	BM_TRANS_TYPE_14BEAT,
+	BM_TRANS_TYPE_15BEAT,
+	BM_TRANS_TYPE_16BEAT,
+	BM_TRANS_TYPE_1Byte = 0 << 4,
+	BM_TRANS_TYPE_2Byte = 1 << 4,
+	BM_TRANS_TYPE_4Byte = 2 << 4,
+	BM_TRANS_TYPE_8Byte = 3 << 4,
+	BM_TRANS_TYPE_16Byte = 4 << 4,
+	BM_TRANS_TYPE_32Byte = 5 << 4,
+	BM_TRANS_TYPE_BURST_WRAP = 0 << 7,
+	BM_TRANS_TYPE_BURST_INCR = 1 << 7
+};
+
+enum {
+	BM_TRANS_RW_DEFAULT = 0x0,
+	BM_TRANS_RW_READONLY,
+	BM_TRANS_RW_WRITEONLY,
+	BM_TRANS_RW_RWBOTH
+};
+
+
+/*coda busid 12bit, but HW support 16 bit*/
+#define EMI_BMID_MASK				(0xFFFF)
+#define BM_COUNTER_MAX				(21)
+
+/*
+*#define BUS_MON_EN		(0x00000001)
+*#define BUS_MON_PAUSE		(0x00000002)
+*#define BUS_MON_IDLE		(0x00000008)
+*#define BC_OVERRUN		(0x00000100)
+*/
+enum {
+	BUS_MON_EN_SHIFT = 0,
+	BUS_MON_PAUSE_SHIFT = 1,
+	BUS_MON_IDLE_SHIFT = 3,
+	BC_OVERRUN_SHIFT = 8,
+	DRAMC_CG_SHIFT = 9,
+};
+
+#define BM_REQ_OK			(0)
+#define BM_ERR_WRONG_REQ		(-1)
+#define BM_ERR_OVERRUN			(-2)
+
+#define BM_WSCT_TSCT_IDSEL_ENABLE	(0)
+#define BM_WSCT_TSCT_IDSEL_DISABLE	(-1)
+#define BM_TTYPE1_16_ENABLE		(0)
+#define BM_TTYPE1_16_DISABLE		(-1)
+#define BM_TTYPE17_21_ENABLE		(0)
+#define BM_TTYPE17_21_DISABLE		(-1)
+#define BM_BW_LIMITER_ENABLE		(0)
+#define BM_BW_LIMITER_DISABLE		(-1)
+
+#define M0_DOUBLE_HALF_BW_1CH		(0x0)
+#define M0_DOUBLE_HALF_BW_2CH		(0x1)
+#define M0_DOUBLE_HALF_BW_4CH		(0x2)
+
+/* EMI Rank configuration */
+enum {
+	DISABLE_DUAL_RANK_MODE = 0,
+	ENABLE_DUAL_RANK_MODE,
+};
+
+enum DDRTYPE {
+	TYPE_LPDDR3 = 1,
+	TYPE_LPDDR4,
+	TYPE_PCDDR3,
+	TYPE_PCDDR4
+};
+
+
+#define RANK_MASK 		(0x1)
+#define ONE_RANK 		(1)
+#define DUAL_RANK 		(2)
+
+#define	EMI_OFF			(0x0000)
+#define EMI_CONA		(0x000-EMI_OFF)
+#define EMI_CONH		(0x038-EMI_OFF)
+#define EMI_CONM		(0x060-EMI_OFF)
+#define EMI_CONO		(0x070-EMI_OFF)
+
+#define EMI_MDCT		(0x078 - EMI_OFF)
+#define EMI_MDCT_2ND		(0x07C - EMI_OFF)
+
+#define EMI_ARBA                (0x100 - EMI_OFF)
+#define EMI_ARBB                (0x108 - EMI_OFF)
+#define EMI_ARBC                (0x110 - EMI_OFF)
+#define EMI_ARBD                (0x118 - EMI_OFF)
+#define EMI_ARBE                (0x120 - EMI_OFF)
+#define EMI_ARBF                (0x128 - EMI_OFF)
+#define EMI_ARBH                (0x138 - EMI_OFF)
+
+#define EMI_BMEN                (0x400 - EMI_OFF)
+#define EMI_BCNT                (0x408 - EMI_OFF)
+#define EMI_TACT                (0x410 - EMI_OFF)
+#define EMI_TSCT                (0x418 - EMI_OFF)
+#define EMI_WACT                (0x420 - EMI_OFF)
+#define EMI_WSCT                (0x428 - EMI_OFF)
+#define EMI_BACT                (0x430 - EMI_OFF)
+#define EMI_BSCT                (0x438 - EMI_OFF)
+
+#define EMI_MSEL                (0x440 - EMI_OFF)
+#define EMI_TSCT2               (0x448 - EMI_OFF)
+#define EMI_TSCT3               (0x450 - EMI_OFF)
+#define EMI_WSCT2               (0x458 - EMI_OFF)
+#define EMI_WSCT3               (0x460 - EMI_OFF)
+#define EMI_WSCT4               (0x464 - EMI_OFF)
+#define EMI_MSEL2               (0x468 - EMI_OFF)
+#define EMI_MSEL3               (0x470 - EMI_OFF)
+#define EMI_MSEL4               (0x478 - EMI_OFF)
+#define EMI_MSEL5               (0x480 - EMI_OFF)
+#define EMI_MSEL6               (0x488 - EMI_OFF)
+#define EMI_MSEL7               (0x490 - EMI_OFF)
+#define EMI_MSEL8               (0x498 - EMI_OFF)
+#define EMI_MSEL9               (0x4A0 - EMI_OFF)
+#define EMI_MSEL10              (0x4A8 - EMI_OFF)
+
+#define EMI_BMID0               (0x4B0 - EMI_OFF)
+#define EMI_BMID1               (0x4B4 - EMI_OFF)
+#define EMI_BMID2               (0x4B8 - EMI_OFF)
+#define EMI_BMID3               (0x4BC - EMI_OFF)
+#define EMI_BMID4               (0x4C0 - EMI_OFF)
+#define EMI_BMID5               (0x4C4 - EMI_OFF)
+#define EMI_BMID6               (0x4C8 - EMI_OFF)
+#define EMI_BMID7               (0x4CC - EMI_OFF)
+#define EMI_BMID8               (0x4D0 - EMI_OFF)
+#define EMI_BMID9               (0x4D4 - EMI_OFF)
+#define EMI_BMID10              (0x4D8 - EMI_OFF)
+
+#define EMI_BMEN1               (0x4E0 - EMI_OFF)
+#define EMI_BMEN2               (0x4E8 - EMI_OFF)
+#define EMI_BMRW0               (0x4F8 - EMI_OFF)
+#define EMI_BMRW1               (0x4FC - EMI_OFF)
+#define EMI_TTYPE1              (0x500 - EMI_OFF)
+#define EMI_TTYPE2              (0x508 - EMI_OFF)
+#define EMI_TTYPE3              (0x510 - EMI_OFF)
+#define EMI_TTYPE4              (0x518 - EMI_OFF)
+#define EMI_TTYPE5              (0x520 - EMI_OFF)
+#define EMI_TTYPE6              (0x528 - EMI_OFF)
+#define EMI_TTYPE7              (0x530 - EMI_OFF)
+#define EMI_TTYPE8              (0x538 - EMI_OFF)
+#define EMI_TTYPE9              (0x540 - EMI_OFF)
+#define EMI_TTYPE10             (0x548 - EMI_OFF)
+#define EMI_TTYPE11             (0x550 - EMI_OFF)
+#define EMI_TTYPE12             (0x558 - EMI_OFF)
+#define EMI_TTYPE13             (0x560 - EMI_OFF)
+#define EMI_TTYPE14             (0x568 - EMI_OFF)
+#define EMI_TTYPE15             (0x570 - EMI_OFF)
+#define EMI_TTYPE16             (0x578 - EMI_OFF)
+#define EMI_TTYPE17             (0x580 - EMI_OFF)
+#define EMI_TTYPE18             (0x588 - EMI_OFF)
+#define EMI_TTYPE19             (0x590 - EMI_OFF)
+#define EMI_TTYPE20             (0x598 - EMI_OFF)
+#define EMI_TTYPE21             (0x5A0 - EMI_OFF)
+
+#define EMI_BWCT0               (0x5B0 - EMI_OFF)
+#define EMI_BWST0               (0x5C4 - EMI_OFF)
+#define EMI_BWST1               (0x5C8 - EMI_OFF)
+
+#define EMI_BWCT0_2ND   	(0x6A0 - EMI_OFF)
+#define EMI_BWST_2ND    	(0x6A8 - EMI_OFF)
+
+
+extern void emi_dump_reg(void);
+extern int MET_BM_Init(void);
+extern void MET_BM_DeInit(void);
+extern void MET_BM_SaveCfg(void);
+extern void MET_BM_RestoreCfg(void);
+extern void MET_BM_Enable(const unsigned int enable);
+extern void MET_BM_Pause(void);
+extern void MET_BM_Continue(void);
+extern unsigned int MET_BM_IsOverrun(void);
+extern unsigned int MET_BM_GetReadWriteType(void);
+extern void MET_BM_SetReadWriteType(const unsigned int ReadWriteType);
+extern int MET_BM_GetBusCycCount(void);
+extern unsigned int MET_BM_GetTransAllCount(void);
+extern int MET_BM_GetTransCount(const unsigned int counter_num);
+extern int MET_BM_GetWordAllCount(void);
+extern int MET_BM_GetWordCount(const unsigned int counter_num);
+extern unsigned int MET_BM_GetBandwidthWordCount(void);
+extern unsigned int MET_BM_GetOverheadWordCount(void);
+extern int MET_BM_GetTransTypeCount(const unsigned int counter_num);
+extern int MET_BM_GetMDCT(void);
+extern int MET_BM_GetMDCT_2(void);
+extern int MET_BM_GetMonitorCounter(const unsigned int counter_num,
+				    unsigned int *master, unsigned int *trans_type);
+extern int MET_BM_SetMDCT_MDMCU(unsigned int mdmcu_rd_buf);
+extern int MET_BM_SetMonitorCounter(const unsigned int counter_num,
+				    const unsigned int master, const unsigned int trans_type);
+extern int MET_BM_SetTtypeCounterRW(unsigned int bmrw0_val, unsigned int bmrw1_val);
+extern int MET_BM_Set_WsctTsct_id_sel(unsigned int counter_num, unsigned int enable);
+extern int MET_BM_SetMaster(const unsigned int counter_num, const unsigned int master);
+extern int MET_BM_SetbusID_En(const unsigned int counter_num,
+			      const unsigned int enable);
+extern int MET_BM_SetbusID(const unsigned int counter_num,
+			   const unsigned int id);
+extern int MET_BM_SetUltraHighFilter(const unsigned int counter_num, const unsigned int enable);
+extern int MET_BM_SetLatencyCounter(unsigned int enable);
+int MET_BM_GetLatencyCycle(unsigned int *__restrict__ emi_value,
+				const unsigned int begin, const unsigned int end);
+extern unsigned int MET_BM_GetEmiDcm(void);
+extern int MET_BM_SetEmiDcm(const unsigned int setting);
+extern void MET_BM_Clear_Start(void);
+extern unsigned int MET_EMI_GetDramRankNum(void);
+extern unsigned int MET_EMI_GetDramRankNum_CHN1(void);
+
+
+/* Config */
+unsigned int MET_EMI_GetARBA(void);
+unsigned int MET_EMI_GetARBB(void);
+unsigned int MET_EMI_GetARBC(void);
+unsigned int MET_EMI_GetARBD(void);
+unsigned int MET_EMI_GetARBE(void);
+unsigned int MET_EMI_GetARBF(void);
+unsigned int MET_EMI_GetARBG(void);
+unsigned int MET_EMI_GetARBH(void);
+
+/* Total BW status */
+extern unsigned int MET_EMI_GetBWCT0(void);
+extern unsigned int MET_EMI_GetBWCT1(void);
+extern unsigned int MET_EMI_GetBWCT2(void);
+extern unsigned int MET_EMI_GetBWCT3(void);
+extern unsigned int MET_EMI_GetBWCT4(void);
+extern unsigned int MET_EMI_GetBWST0(void);
+extern unsigned int MET_EMI_GetBWST1(void);
+
+/* C+G BW */
+extern unsigned int MET_EMI_GetBWCT0_2ND(void);
+extern unsigned int MET_EMI_GetBWCT1_2ND(void);
+extern unsigned int MET_EMI_GetBWST_2ND(void);
+
+unsigned int MET_EMI_GetBMRW0(void);
+unsigned int MET_EMI_GetDramChannNum(void);
+
+extern void met_record_dramc_dcm_enable_flag(void);
+
+#endif                          /* !__MT_MET_EMI_BM_H__ */
diff --git a/src/devtools/met-driver/4.9/mt8518/met_sspm_rts_event.h b/src/devtools/met-driver/4.9/mt8518/met_sspm_rts_event.h
new file mode 100644
index 0000000..0afc833
--- /dev/null
+++ b/src/devtools/met-driver/4.9/mt8518/met_sspm_rts_event.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2016 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See http://www.gnu.org/licenses/gpl-2.0.html for more details.
+ */
+MET_SSPM_RTS_EVNET(SSPM_PTPOD, "_id,voltage")
+MET_SSPM_RTS_EVNET(SSPM_MET_UNIT_TEST, "test")
+MET_SSPM_RTS_EVNET(SSPM_CM_MGR_NON_WFX, "non_wfx_0,non_wfx_1,non_wfx_2,non_wfx_3,non_wfx_4,non_wfx_5,non_wfx_6,non_wfx_7")
+MET_SSPM_RTS_EVNET(SSPM_CM_MGR_LOADING, "ratio,cps")
+MET_SSPM_RTS_EVNET(SSPM_CM_MGR_POWER, "c_up_array_0,c_up_array_1,c_down_array_0,c_down_array_1,c_up_0,c_up_1,c_down_0,c_dwon_1,c_up,c_down,v_up,v_down,v2f_0,v2f_1")
+MET_SSPM_RTS_EVNET(SSPM_CM_MGR_OPP, "v_dram_opp,v_dram_opp_cur,c_opp_cur_0,c_opp_cur_1,d_times_up,d_times_down")
+MET_SSPM_RTS_EVNET(SSPM_CM_MGR_RATIO, "ratio_max_0,ratio_max_1,ratio_0,ratio_1,ratio_2,ratio_3,ratio_4,ratio_5,ratio_6,ratio_7")
+MET_SSPM_RTS_EVNET(SSPM_CM_MGR_BW, "total_bw")
+MET_SSPM_RTS_EVNET(SSPM_CM_MGR_CP_RATIO, "up0,up1,up2,up3,down0,down1,down2,down3")
+MET_SSPM_RTS_EVNET(SSPM_CM_MGR_VP_RATIO, "up0,up1,up2,up3,down0,down1,down2,down3")
+MET_SSPM_RTS_EVNET(SSPM_CM_MGR_DE_TIMES, "up0,up1,up2,up3,down0,down1,down2,down3,reset")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_PWR, "cpu_L,cpu_B,gpu,cam,img,ipe,mdp,disp,adsp,venc,vdec,dramc,infra_top,aphy_vcore,aphy_vddq_0p6v,aphy_vm_0p75v,aphy_vio_1p2v,aphy_vio_1p8v,dram_vddq_0p6v,dram_vdd2_1p1v,dram_vdd1_1p8v,vpu,mdla")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_FREQ_VOLT, "vproc2,vproc1,cpuL_freq,cpuB_freq,cpu_L_opp,cpu_B_opp,cci_volt,cci_freq,cci_opp,vgpu,gpu_freq,vcore,ddr_freq,vvpu,vpu0_freq,vpu1_freq,vmdla,mdla_freq")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_CPU_ACTIVE_RATIO, "cpu0,cpu1,cpu2,cpu3,cpu4,cpu5,cpu6,cpu7")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_CPU_IDLE_RATIO, "cpu0,cpu1,cpu2,cpu3,cpu4,cpu5,cpu6,cpu7")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_CPU_OFF_RATIO, "cpu0,cpu1,cpu2,cpu3,cpu4,cpu5,cpu6,cpu7")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_CPU_STALL_RATIO, "cpu0,cpu1,cpu2,cpu3,cpu4,cpu5,cpu6,cpu7")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_CPU_PMU_L3DCR, "cpu0,cpu1,cpu2,cpu3,cpu4,cpu5,cpu6,cpu7")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_CPU_PMU_INST_SPEC, "cpu0,cpu1,cpu2,cpu3,cpu4,cpu5,cpu6,cpu7")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_CPU_PMU_CYCLES, "cpu0,cpu1,cpu2,cpu3,cpu4,cpu5,cpu6,cpu7")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_CPU_NON_WFX_CTR, "cpu0,cpu1,cpu2,cpu3,cpu4,cpu5,cpu6,cpu7")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_CLUSTER_STATE_RATIO, "active,idle,off")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_MCUSYS_STATE_RATIO, "active,idle,off")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_CPU_LKG_PWR, "cpu_L,cpu_B,cluster,mcusys")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_CAM_STATE_RATIO, "RAW_A_active,RAW_B_active,RAW_C_active,idle,off")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_IMG_STATE_RATIO, "P2_active,P2_idle,MFB_active,WPE_active,off")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_IPE_STATE_RATIO, "FDVT_active,DVP_active,DVS_active,DV_idle,off")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_MDP_STATE_RATIO, "active,off")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_DISP_STATE_RATIO, "active,off")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_ADSP_STATE_RATIO, "active,off")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_VENC_STATE_RATIO, "active,idle,off")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_VDEC_STATE_RATIO, "active,idle,off")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_INFRA_STATE_RATIO, "dact,cact,idle,dcm")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_EMI_BW, "total_r,total_w,cpu")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_MEM_IDX, "read_bw,write_bw,srr_pct,pdir_pct,phr_pct,acc_util,mr4")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_VPU0_STATE_RATIO, "active,idle,off")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_VPU1_STATE_RATIO, "active,idle,off")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_MDLA_ACTIVE_RATIO, "pool,dw,fc,conv,ewe,sb")
+MET_SSPM_RTS_EVNET(SSPM_SWPM_MDLA_TOTAL_CYCLES, "total_cycles")
+MET_SSPM_RTS_EVNET(__SSPM_APUSYS_QOS_CNT__, "bw_VPU0,bw_VPU1,bw_MDLA0,bw_MDAL1,lt_VPU0,lt_VPU1,lt_MDLA0,lt_MDLA1")
+MET_SSPM_RTS_EVNET(__SSPM_GPU_APU_SSC_CNT__, "GPU_0_R,GPU_0_W,APU_0_R,APU_0_W,GPU_1_R,GPU_1_W,APU_1_R,APU_1_W")
diff --git a/src/devtools/met-driver/4.9/mt8518/met_thermal.c b/src/devtools/met-driver/4.9/mt8518/met_thermal.c
new file mode 100644
index 0000000..23c32b9
--- /dev/null
+++ b/src/devtools/met-driver/4.9/mt8518/met_thermal.c
@@ -0,0 +1,342 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <asm/page.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/syscalls.h>
+#include <linux/mm.h>
+/* #include <asm/uaccess.h> */
+#include <linux/uaccess.h>
+#include <linux/hrtimer.h>
+
+#include "met_drv.h"
+#include "trace.h"
+#include "core_plf_trace.h"
+#include "core_plf_init.h"
+
+/*define if the thermal sensor driver use its own timer to sampling the code , otherwise undefine it */
+/*define it is better for sampling jitter if thermal sensor driver supports */
+/* this define is phase out */
+/* #define MET_USE_THERMALDRIVER_TIMER */
+
+noinline void ms_th(const unsigned char cnt, unsigned int *value)
+{
+	char *SOB, *EOB;
+
+	MET_TRACE_GETBUF(&SOB, &EOB);
+	EOB = ms_formatD_EOL(EOB, cnt, value);
+	MET_TRACE_PUTBUF(SOB, EOB);
+}
+
+static unsigned int CheckAvailableThermalSensor(unsigned int a_u4DoCheck)
+{
+	static unsigned int u4AvailableSensor;
+
+	unsigned int u4Index;
+
+	if (!a_u4DoCheck)
+		return u4AvailableSensor;
+
+	/*Do check */
+	if (MTK_THERMAL_SENSOR_COUNT > 32)
+		return 0;
+
+	if (mtk_thermal_get_temp_symbol == NULL)
+		return 0;
+
+	u4AvailableSensor = 0;
+
+	for (u4Index = 0; u4Index < MTK_THERMAL_SENSOR_COUNT; u4Index++) {
+		if (mtk_thermal_get_temp_symbol(u4Index) == (-127000) || mtk_thermal_get_temp_symbol(u4Index) < 0)
+			u4AvailableSensor &= (~(1 << u4Index));
+		else
+			u4AvailableSensor |= (1 << u4Index);
+	}
+
+	return u4AvailableSensor;
+}
+
+static int do_thermal(void)
+{
+	static int do_thermal = -1;
+
+	if (do_thermal != -1)
+		return do_thermal;
+
+	if (met_thermal.mode == 0)
+		do_thermal = 0;
+	else
+		do_thermal = met_thermal.mode;
+	return do_thermal;
+}
+
+static unsigned int get_thermal(unsigned int *value)
+{
+	int j = -1;
+	int i;
+	unsigned int u4ValidSensors = 0;
+
+	/*Do check */
+	if (mtk_thermal_get_temp_symbol == NULL)
+		return 0;
+
+	u4ValidSensors = CheckAvailableThermalSensor(0);
+
+	for (i = 0; i < MTK_THERMAL_SENSOR_COUNT; i++) {
+		if (u4ValidSensors & (1 << i))
+			value[++j] = mtk_thermal_get_temp_symbol(i);
+	}
+
+	return j + 1;
+}
+
+static void wq_get_thermal(struct work_struct *work)
+{
+	unsigned char count = 0;
+	unsigned int thermal_value[MTK_THERMAL_SENSOR_COUNT];	/*Note here */
+
+	int cpu;
+
+	cpu = smp_processor_id();
+	if (do_thermal()) {
+		count = get_thermal(thermal_value);
+		if (count)
+			ms_th(count, thermal_value);
+	}
+}
+
+#ifdef MET_USE_THERMALDRIVER_TIMER
+
+static void thermal_start(void)
+{
+	CheckAvailableThermalSensor(1);
+	/* get extern symbol by symbol_get */
+	if (mt_thermalsampler_registerCB_symbol)
+		mt_thermalsampler_registerCB_symbol(wq_get_thermal);
+}
+
+static void thermal_stop(void)
+{
+	if (mt_thermalsampler_registerCB_symbol)
+		mt_thermalsampler_registerCB_symbol(NULL);
+}
+
+#else
+
+struct delayed_work dwork;
+static void thermal_start(void)
+{
+	CheckAvailableThermalSensor(1);
+	/*pr_debug("Thermal Sample:0x%x\n",CheckAvailableThermalSensor(0)); */
+	INIT_DELAYED_WORK(&dwork, wq_get_thermal);
+}
+
+static void thermal_stop(void)
+{
+	cancel_delayed_work_sync(&dwork);
+}
+
+static void thermal_polling(unsigned long long stamp, int cpu)
+{
+	schedule_delayed_work(&dwork, 0);
+}
+
+#endif
+
+static const char help[] = "  --thermal                             monitor thermal\n";
+static int thermal_print_help(char *buf, int len)
+{
+	return snprintf(buf, PAGE_SIZE, help);
+}
+
+static const char g_pThermalHeader[] = "met-info [000] 0.0: thermal_header: ms_th";
+
+static int thermal_print_header(char *buf, int len)
+{
+	char buffer[256];
+	char ts_buf[8] = {0};
+	unsigned long u4Cnt = 0;
+	unsigned int u4ValidSensor = 0;
+	int i = 0, ts_sz = 0;
+
+	u4ValidSensor = CheckAvailableThermalSensor(0);
+
+	strncpy(buffer, g_pThermalHeader, 256 - 1);
+
+	ts_sz = sizeof(ts_buf);
+
+	for ( i = 0 ; i < MTK_THERMAL_SENSOR_COUNT ; i++) {
+		if ((1 << i) & u4ValidSensor) {
+			snprintf(ts_buf, ts_sz, ",tab_%d", (i + 1));
+			strncat(buffer, ts_buf, 256 - 1);
+			u4Cnt += 1;
+		}
+		memset(ts_buf, '\0', ts_sz);
+	}
+
+	strncat(buffer, "\n", 256 - 1);
+
+	return snprintf(buf, PAGE_SIZE, "%s", buffer);
+}
+
+struct metdevice met_thermal = {
+	.name = "thermal",
+	.owner = THIS_MODULE,
+	.type = MET_TYPE_BUS,
+	.cpu_related = 0,
+	.ondiemet_mode = 0,
+	.start = thermal_start,
+	.stop = thermal_stop,
+#ifdef MET_USE_THERMALDRIVER_TIMER
+#else
+	.polling_interval = 50,	/* ms */
+	.timed_polling = thermal_polling,
+	.tagged_polling = thermal_polling,
+#endif
+	.print_help = thermal_print_help,
+	.print_header = thermal_print_header,
+};
+
+
+
+/*CPU sensors */
+static unsigned int CheckAvailableCPUThermalSensor(unsigned int a_u4DoCheck)
+{
+	static unsigned int u4AvailableSensor;
+
+	unsigned int u4Index;
+
+	if (tscpu_get_cpu_temp_met_symbol == NULL)
+		return 0;
+
+	if (!a_u4DoCheck)
+		return u4AvailableSensor;
+
+	u4AvailableSensor = 0;
+
+	for (u4Index = 0; u4Index < MTK_THERMAL_SENSOR_CPU_COUNT; u4Index++) {
+		if (u4Index == ATM_GPU_LIMIT)
+			continue;
+
+		if (tscpu_get_cpu_temp_met_symbol(u4Index) == (-127000) || tscpu_get_cpu_temp_met_symbol(u4Index) < 0)
+			u4AvailableSensor &= (~(1 << u4Index));
+		else
+			u4AvailableSensor |= (1 << u4Index);
+	}
+
+	return u4AvailableSensor;
+}
+
+noinline void CPUTS(void)
+{
+	unsigned int u4Index1 = 0, u4Index2 = 0;
+	unsigned int u4ValidSensors = 0;
+	int i4TSValue[MTK_THERMAL_SENSOR_CPU_COUNT];
+	int i = 0, len = 0, total_len = 0, sz;
+	char str_buff[128] = {0};
+
+	sz = sizeof(str_buff);
+
+	if (tscpu_get_cpu_temp_met_symbol == NULL)
+		return;
+
+	memset(i4TSValue, 0, sizeof(int) * MTK_THERMAL_SENSOR_CPU_COUNT);
+
+	u4ValidSensors = CheckAvailableCPUThermalSensor(0);
+
+	for (; u4Index1 < MTK_THERMAL_SENSOR_CPU_COUNT; u4Index1++) {
+		if (u4ValidSensors & (1 << u4Index1)) {
+			i4TSValue[u4Index2] = tscpu_get_cpu_temp_met_symbol(u4Index1);
+			u4Index2 += 1;
+		}
+	}
+
+	len = snprintf(str_buff + total_len, 8, "%d", i4TSValue[0]);
+	if (len >= 0 && len < sz)
+		total_len += len;
+
+	for (i = 1 ; i < u4Index2 ; i++) {
+		len = snprintf(str_buff + total_len, 8, ",%d", i4TSValue[i]);
+		if (len >= 0 && len < sz)
+			total_len += len;
+	}
+
+	MET_TRACE("%s\n", str_buff);
+}
+
+static void thermal_CPU_start(void)
+{
+	CheckAvailableCPUThermalSensor(1);
+
+	/* get extern symbol by symbol_get */
+	if (mt_thermalsampler_registerCB_symbol)
+		mt_thermalsampler_registerCB_symbol(CPUTS);
+}
+
+static void thermal_CPU_stop(void)
+{
+	/* release extern symbol by symbol_put */
+	if (mt_thermalsampler_registerCB_symbol)
+		mt_thermalsampler_registerCB_symbol(NULL);
+}
+
+static const char help_cpu[] = "  --thermal-cpu                         monitor cpu temperature\n";
+static int thermal_CPU_print_help(char *buf, int len)
+{
+	return snprintf(buf, PAGE_SIZE, help_cpu);
+}
+
+static const char g_pCPUThermalHeader[] = "met-info [000] 0.0: thermal_cpu_header: CPUTS";
+
+static int thermal_CPU_print_header(char *buf, int len)
+{
+	char buffer[256];
+	char ts_buf[8] = {0} ;
+	unsigned long u4Cnt = 0;
+	unsigned int u4ValidSensor = 0;
+	int i = 0, ts_sz = 0;
+
+	u4ValidSensor = CheckAvailableCPUThermalSensor(0);
+
+	strncpy(buffer, g_pCPUThermalHeader, 256 - 1);
+
+	ts_sz = sizeof(ts_buf);
+
+	for ( i = 0 ; i < MTK_THERMAL_SENSOR_CPU_COUNT ; i++) {
+		if ((1 << i) & u4ValidSensor) {
+			snprintf(ts_buf, ts_sz, ",tab_%d", (i + 1));
+			strncat(buffer, ts_buf, 256 - 1);
+			u4Cnt += 1;
+		}
+		memset(ts_buf, '\0', ts_sz);
+	}
+
+	strncat(buffer, "\n", 256 - 1);
+
+	return snprintf(buf, PAGE_SIZE, "%s", buffer);
+}
+
+struct metdevice met_thermal_cpu = {
+	.name = "thermal-cpu",
+	.owner = THIS_MODULE,
+	.type = MET_TYPE_BUS,
+	.cpu_related = 0,
+	.start = thermal_CPU_start,
+	.stop = thermal_CPU_stop,
+	.print_help = thermal_CPU_print_help,
+	.print_header = thermal_CPU_print_header,
+	.ondiemet_mode = 0,
+};
diff --git a/src/devtools/met-driver/met_api/Kbuild b/src/devtools/met-driver/met_api/Kbuild
new file mode 100644
index 0000000..a425f1f
--- /dev/null
+++ b/src/devtools/met-driver/met_api/Kbuild
@@ -0,0 +1,7 @@
+$(info ======== Buildin met_api ... ========)
+
+ifeq ($(ARCH), arm)
+ccflags-y += -DCONFIG_MET_ARM_32BIT
+endif   # ifeq ($(ARCH), arm)
+
+obj-y += met_api.o
diff --git a/src/devtools/met-driver/met_api/met_api.c b/src/devtools/met-driver/met_api/met_api.c
new file mode 100644
index 0000000..8e4ca5e
--- /dev/null
+++ b/src/devtools/met-driver/met_api/met_api.c
@@ -0,0 +1,398 @@
+/*
+ * Copyright (C) 2017 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See http://www.gnu.org/licenses/gpl-2.0.html for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/tracepoint.h>
+#include <trace/events/sched.h>
+#include <trace/events/power.h>
+#include <linux/dma-mapping.h>
+
+#include <linux/kallsyms.h>
+#include <linux/printk.h>
+#include <linux/perf_event.h>
+#include <linux/kthread.h>
+#include <asm/arch_timer.h>
+#include <asm/cpu.h>
+#include <linux/smp.h> /* arch_send_call_function_single_ipi */
+
+/******************************************************************************
+ * Tracepoints
+ ******************************************************************************/
+#define MET_DEFINE_PROBE(probe_name, proto) \
+		static void probe_##probe_name(void *data, PARAMS(proto))
+#define MET_REGISTER_TRACE(probe_name) \
+		register_trace_##probe_name(probe_##probe_name, NULL)
+#define MET_UNREGISTER_TRACE(probe_name) \
+		unregister_trace_##probe_name(probe_##probe_name, NULL)
+
+struct met_api_tbl {
+	int (*met_tag_start)(unsigned int class_id,
+			     const char *name);
+	int (*met_tag_end)(unsigned int class_id,
+			   const char *name);
+	int (*met_tag_async_start)(unsigned int class_id,
+				   const char *name,
+				   unsigned int cookie);
+	int (*met_tag_async_end)(unsigned int class_id,
+				 const char *name,
+				 unsigned int cookie);
+	int (*met_tag_oneshot)(unsigned int class_id,
+			       const char *name,
+			       unsigned int value);
+	int (*met_tag_userdata)(char *pData);
+	int (*met_tag_dump)(unsigned int class_id,
+			    const char *name,
+			    void *data,
+			    unsigned int length);
+	int (*met_tag_disable)(unsigned int class_id);
+	int (*met_tag_enable)(unsigned int class_id);
+	int (*met_set_dump_buffer)(int size);
+	int (*met_save_dump_buffer)(const char *pathname);
+	int (*met_save_log)(const char *pathname);
+	int (*met_show_bw_limiter)(void);
+	int (*met_reg_bw_limiter)(void *fp);
+	int (*met_show_clk_tree)(const char *name,
+				 unsigned int addr,
+				 unsigned int status);
+	int (*met_reg_clk_tree)(void *fp);
+	void (*met_sched_switch)(struct task_struct *prev,
+				 struct task_struct *next);
+	int (*enable_met_backlight_tag)(void);
+	int (*output_met_backlight_tag)(int level);
+};
+
+struct met_api_tbl met_ext_api;
+EXPORT_SYMBOL(met_ext_api);
+
+#if 0
+int met_tag_init(void)
+{
+	return 0;
+}
+EXPORT_SYMBOL(met_tag_init);
+
+int met_tag_uninit(void)
+{
+	return 0;
+}
+EXPORT_SYMBOL(met_tag_uninit);
+#endif
+
+int met_tag_start(unsigned int class_id, const char *name)
+{
+	if (met_ext_api.met_tag_start)
+		return met_ext_api.met_tag_start(class_id, name);
+	return 0;
+}
+EXPORT_SYMBOL(met_tag_start);
+
+int met_tag_end(unsigned int class_id, const char *name)
+{
+	if (met_ext_api.met_tag_end)
+		return met_ext_api.met_tag_end(class_id, name);
+	return 0;
+}
+EXPORT_SYMBOL(met_tag_end);
+
+int met_tag_async_start(unsigned int class_id,
+			const char *name,
+			unsigned int cookie)
+{
+	if (met_ext_api.met_tag_async_start)
+		return met_ext_api.met_tag_async_start(class_id, name, cookie);
+	return 0;
+}
+EXPORT_SYMBOL(met_tag_async_start);
+
+int met_tag_async_end(unsigned int class_id,
+		      const char *name,
+		      unsigned int cookie)
+{
+	if (met_ext_api.met_tag_async_end)
+		return met_ext_api.met_tag_async_end(class_id, name, cookie);
+	return 0;
+}
+EXPORT_SYMBOL(met_tag_async_end);
+
+int met_tag_oneshot(unsigned int class_id, const char *name, unsigned int value)
+{
+	if (met_ext_api.met_tag_oneshot)
+		return met_ext_api.met_tag_oneshot(class_id, name, value);
+	return 0;
+}
+EXPORT_SYMBOL(met_tag_oneshot);
+
+int met_tag_userdata(char *pData)
+{
+	if (met_ext_api.met_tag_userdata)
+		return met_ext_api.met_tag_userdata(pData);
+	return 0;
+}
+EXPORT_SYMBOL(met_tag_userdata);
+
+int met_tag_dump(unsigned int class_id,
+		 const char *name,
+		 void *data,
+		 unsigned int length)
+{
+	if (met_ext_api.met_tag_dump)
+		return met_ext_api.met_tag_dump(class_id, name, data, length);
+	return 0;
+}
+EXPORT_SYMBOL(met_tag_dump);
+
+int met_tag_disable(unsigned int class_id)
+{
+	if (met_ext_api.met_tag_disable)
+		return met_ext_api.met_tag_disable(class_id);
+	return 0;
+}
+EXPORT_SYMBOL(met_tag_disable);
+
+int met_tag_enable(unsigned int class_id)
+{
+	if (met_ext_api.met_tag_enable)
+		return met_ext_api.met_tag_enable(class_id);
+	return 0;
+}
+EXPORT_SYMBOL(met_tag_enable);
+
+int met_set_dump_buffer(int size)
+{
+	if (met_ext_api.met_set_dump_buffer)
+		return met_ext_api.met_set_dump_buffer(size);
+	return 0;
+}
+EXPORT_SYMBOL(met_set_dump_buffer);
+
+int met_save_dump_buffer(const char *pathname)
+{
+	if (met_ext_api.met_save_dump_buffer)
+		return met_ext_api.met_save_dump_buffer(pathname);
+	return 0;
+}
+EXPORT_SYMBOL(met_save_dump_buffer);
+
+int met_save_log(const char *pathname)
+{
+	if (met_ext_api.met_save_log)
+		return met_ext_api.met_save_log(pathname);
+	return 0;
+}
+EXPORT_SYMBOL(met_save_log);
+
+int met_show_bw_limiter(void)
+{
+	if (met_ext_api.met_show_bw_limiter)
+		return met_ext_api.met_show_bw_limiter();
+	return 0;
+}
+EXPORT_SYMBOL(met_show_bw_limiter);
+
+int met_reg_bw_limiter(void *fp)
+{
+	if (met_ext_api.met_reg_bw_limiter)
+		return met_ext_api.met_reg_bw_limiter(fp);
+	return 0;
+}
+EXPORT_SYMBOL(met_reg_bw_limiter);
+
+int met_show_clk_tree(const char *name,
+				unsigned int addr,
+				unsigned int status)
+{
+	if (met_ext_api.met_show_clk_tree)
+		return met_ext_api.met_show_clk_tree(name, addr, status);
+	return 0;
+}
+EXPORT_SYMBOL(met_show_clk_tree);
+
+int met_reg_clk_tree(void *fp)
+{
+	if (met_ext_api.met_reg_clk_tree)
+		return met_ext_api.met_reg_clk_tree(fp);
+	return 0;
+}
+EXPORT_SYMBOL(met_reg_clk_tree);
+
+MET_DEFINE_PROBE(sched_switch,
+		 TP_PROTO(bool preempt,
+			  struct task_struct *prev,
+			  struct task_struct *next))
+{
+	if (met_ext_api.met_sched_switch)
+		met_ext_api.met_sched_switch(prev, next);
+}
+
+int met_reg_switch(void)
+{
+	if (MET_REGISTER_TRACE(sched_switch)) {
+		pr_debug("can not register callback of sched_switch\n");
+		return -ENODEV;
+	} else
+		return 0;
+}
+EXPORT_SYMBOL(met_reg_switch);
+
+void met_unreg_switch(void)
+{
+	MET_UNREGISTER_TRACE(sched_switch);
+}
+EXPORT_SYMBOL(met_unreg_switch);
+
+#if	defined(CONFIG_MET_ARM_32BIT)
+void met_get_cpuinfo(int cpu, struct cpuinfo_arm **cpuinfo)
+{
+	*cpuinfo = &per_cpu(cpu_data, cpu);
+}
+#else
+void met_get_cpuinfo(int cpu, struct cpuinfo_arm64 **cpuinfo)
+{
+	*cpuinfo = &per_cpu(cpu_data, cpu);
+}
+#endif
+EXPORT_SYMBOL(met_get_cpuinfo);
+
+void met_cpu_frequency(unsigned int frequency, unsigned int cpu_id)
+{
+	trace_cpu_frequency(frequency, cpu_id);
+}
+EXPORT_SYMBOL(met_cpu_frequency);
+
+void met_tracing_record_cmdline(struct task_struct *tsk)
+{
+	tracing_record_cmdline(tsk);
+}
+EXPORT_SYMBOL(met_tracing_record_cmdline);
+
+void met_set_kptr_restrict(int value)
+{
+	kptr_restrict = value;
+}
+EXPORT_SYMBOL(met_set_kptr_restrict);
+
+int met_get_kptr_restrict(void)
+{
+	return kptr_restrict;
+}
+EXPORT_SYMBOL(met_get_kptr_restrict);
+
+void met_arch_setup_dma_ops(struct device *dev)
+{
+	arch_setup_dma_ops(dev, 0, 0, NULL, false);
+}
+EXPORT_SYMBOL(met_arch_setup_dma_ops);
+
+#if 1
+int enable_met_backlight_tag(void)
+{
+	if (met_ext_api.enable_met_backlight_tag)
+		return met_ext_api.enable_met_backlight_tag();
+	return 0;
+}
+EXPORT_SYMBOL(enable_met_backlight_tag);
+
+int output_met_backlight_tag(int level)
+{
+	if (met_ext_api.output_met_backlight_tag)
+		return met_ext_api.output_met_backlight_tag(level);
+	return 0;
+}
+EXPORT_SYMBOL(output_met_backlight_tag);
+#endif
+
+/* the following handle weak function in met_drv.h */
+void met_mmsys_event_gce_thread_begin(ulong thread_no, ulong task_handle,
+				ulong engineFlag, void *pCmd, ulong size)
+{
+}
+EXPORT_SYMBOL(met_mmsys_event_gce_thread_begin);
+
+void met_mmsys_event_gce_thread_end(ulong thread_no,
+				    ulong task_handle,
+				    ulong engineFlag)
+{
+}
+EXPORT_SYMBOL(met_mmsys_event_gce_thread_end);
+
+void met_mmsys_event_disp_sof(int mutex_id)
+{
+}
+EXPORT_SYMBOL(met_mmsys_event_disp_sof);
+
+void met_mmsys_event_disp_mutex_eof(int mutex_id)
+{
+}
+EXPORT_SYMBOL(met_mmsys_event_disp_mutex_eof);
+
+void met_mmsys_event_disp_ovl_eof(int ovl_id)
+{
+}
+EXPORT_SYMBOL(met_mmsys_event_disp_ovl_eof);
+
+void met_mmsys_config_isp_base_addr(unsigned long *isp_reg_list)
+{
+}
+EXPORT_SYMBOL(met_mmsys_config_isp_base_addr);
+
+void met_mmsys_event_isp_pass1_begin(int sensor_id)
+{
+}
+EXPORT_SYMBOL(met_mmsys_event_isp_pass1_begin);
+
+void met_mmsys_event_isp_pass1_end(int sensor_id)
+{
+}
+EXPORT_SYMBOL(met_mmsys_event_isp_pass1_end);
+
+void met_show_pmic_info(unsigned int RegNum, unsigned int pmic_reg)
+{
+}
+EXPORT_SYMBOL(met_show_pmic_info);
+
+u64 met_perf_event_read_local(struct perf_event *ev)
+{
+	return perf_event_read_local(ev);
+}
+EXPORT_SYMBOL(met_perf_event_read_local);
+
+struct task_struct *met_kthread_create_on_cpu(int (*threadfn)(void *data),
+				void *data, unsigned int cpu,
+				const char *namefmt)
+{
+	return kthread_create_on_cpu(threadfn, data, cpu, namefmt);
+}
+EXPORT_SYMBOL(met_kthread_create_on_cpu);
+
+int met_smp_call_function_single(
+	int cpu,
+	smp_call_func_t func,
+	void *info,
+	int wait)
+{
+	return smp_call_function_single(cpu, func, info, wait);
+}
+EXPORT_SYMBOL(met_smp_call_function_single);
+
+u64 met_arch_counter_get_cntvct(void)
+{
+	return arch_counter_get_cntvct();
+}
+EXPORT_SYMBOL(met_arch_counter_get_cntvct);
+
+void met_arch_send_call_function_single_ipi(int cpu)
+{
+	return arch_send_call_function_single_ipi(cpu);
+}
+EXPORT_SYMBOL(met_arch_send_call_function_single_ipi);
diff --git a/src/devtools/met-driver/met_drv/Kconfig b/src/devtools/met-driver/met_drv/Kconfig
new file mode 100644
index 0000000..6e2c8b7
--- /dev/null
+++ b/src/devtools/met-driver/met_drv/Kconfig
@@ -0,0 +1,11 @@
+menu "MET"
+
+config MTK_MET
+	tristate "MET core driver"
+	default n
+	---help---
+	  The core module of MTK MET profiling tool.
+	  We can use the config to define MET core driver.
+	  It's depend on MTK_FTRACE
+endmenu
+
diff --git a/src/devtools/met-driver/met_drv/Makefile b/src/devtools/met-driver/met_drv/Makefile
new file mode 100644
index 0000000..6a98cfa
--- /dev/null
+++ b/src/devtools/met-driver/met_drv/Makefile
@@ -0,0 +1,33 @@
+ifeq (y,$(CONFIG_MODULES))
+
+ifeq ($(CONFIG_FTRACE),y)
+    ifeq ($(CONFIG_TRACING),y)
+        FTRACE_READY := y
+    endif
+endif
+
+#PLATFORM := $(subst ",,$(CONFIG_MTK_PLATFORM))
+PLATFORM := mt2712
+MET_COMMON_USE := yes
+
+$(info ******** Start to build met_drv for $(PLATFORM) ********)
+
+
+ifeq ($(MET_COMMON_USE),yes)
+    # met common code structure, new build flow
+    MET_COMMON_DIR := $(src)/common
+    ifeq ($(FTRACE_READY),y)
+        include $(MET_COMMON_DIR)/Kbuild
+        ccflags-y += -DCONFIG_MET_MODULE -DMET_PLF_USE
+        ccflags-y += -I$(MET_COMMON_DIR)/
+    else
+        $(warning Not building met.ko due to CONFIG_FTRACE/CONFIG_TRACING is not set)
+    endif
+else
+    $(warning not support $(PLATFORM), build met default)
+    MET_DEF_DIR := $(src)
+    include $(MET_DEF_DIR)/default/Kbuild
+endif
+else
+$(warning Not building met.ko due to CONFIG_MODULES is not set)
+endif
diff --git a/src/devtools/met-driver/met_drv/common/Kbuild b/src/devtools/met-driver/met_drv/common/Kbuild
new file mode 100644
index 0000000..4620264
--- /dev/null
+++ b/src/devtools/met-driver/met_drv/common/Kbuild
@@ -0,0 +1,66 @@
+$(info ======== Build met.ko ... ========)
+
+
+MET_CORE := common
+obj-m := met.o
+ccflags-y += -I$(srctree)/include/
+ccflags-y += -I$(srctree)/drivers/misc/mediatek/include/
+ccflags-y += -I$(srctree)/drivers/misc/mediatek/include/mt-plat/$(MTK_PLATFORM)/include/
+ccflags-y += -I$(srctree)/../vendor/mediatek/kernel_modules/met_drv/4.9/common/
+ccflags-y += -I$(srctree)/../vendor/mediatek/kernel_modules/met_drv/4.9/$(MTK_PLATFORM)/
+ccflags-y += $(EXTRA_ARGS) $(EXTRA_CFLAGS)
+
+met-y := $(MET_CORE)/met_main.o \
+         $(MET_CORE)/interface.o \
+         $(MET_CORE)/sampler.o \
+         $(MET_CORE)/util.o \
+         $(MET_CORE)/core_plf_init.o \
+         $(MET_CORE)/core_plf_trace.o \
+         $(MET_CORE)/ondiemet.o \
+         $(MET_CORE)/ondiemet_log.o \
+         $(MET_CORE)/sspm/ondiemet_sspm.o
+
+#CFLAGS_interface.o += -DMET_USER_EVENT_SUPPORT
+#CFLAGS_met_tag_ex.o += -DMET_USER_EVENT_SUPPORT
+
+$(info ARCH = $(ARCH))
+ifeq ($(ARCH), mips)
+met-y += $(MET_CORE)/mips_pmu_hw.o
+endif #ifeq ($(ARCH), mips)
+
+ifeq ($(ARCH), arm)
+ccflags-y += -DCONFIG_MET_ARM_32BIT
+met-y += $(MET_CORE)/cpu_pmu.o
+met-y += $(MET_CORE)/v7_pmu_hw.o
+met-y += $(MET_CORE)/v6_pmu_hw.o
+endif #ifeq ($(ARCH), arm)
+
+ifeq ($(ARCH), arm64)
+ccflags-y += -DMET_SUPPORT_CPUPMU_V2
+met-y += $(MET_CORE)/cpu_pmu.o
+met-y += $(MET_CORE)/v8_pmu_hw.o
+met-y += $(MET_CORE)/cpu_pmu_v2.o
+met-y += $(MET_CORE)/v8_pmu_hw_v2.o
+endif
+
+$(info CONFIG_CPU_FREQ = $(CONFIG_CPU_FREQ))
+ifeq ($(CONFIG_CPU_FREQ),y)
+    met-y += $(MET_CORE)/power.o
+endif
+
+################################################################################
+# MET_EMI
+################################################################################
+MET_EMI := y
+
+met-$(MET_EMI) += $(MET_CORE)/met_emi.o $(MET_CORE)/mtk_emi_bm.o
+
+################################################################################
+# MET_GPU
+################################################################################
+MET_GPU := y
+
+met-$(MET_GPU) += $(MET_CORE)/mtk_gpu_metmonitor.o
+
+
+ccflags-y += $(foreach v, $(filter MET_%,$(.VARIABLES)), $(if $(filter $($(v)),y),-D$(v)))
diff --git a/src/devtools/met-driver/met_drv/common/core_plf_init.c b/src/devtools/met-driver/met_drv/common/core_plf_init.c
new file mode 100644
index 0000000..21c6c1f
--- /dev/null
+++ b/src/devtools/met-driver/met_drv/common/core_plf_init.c
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/kallsyms.h>
+#include "met_drv.h"
+#include "met_api_tbl.h"
+#include "interface.h"
+#include "core_plf_init.h"
+
+#undef	DEBUG
+
+#ifdef MET_EMI
+/*
+ *   EMI Monitor
+ */
+void *(*mt_cen_emi_base_get_symbol)(void);
+void *(*mt_chn_emi_base_get_symbol)(int chn);
+unsigned int (*get_dram_data_rate_symbol)(void);
+#if 0 /* New APIs for mt_dramc_nao base get */
+void *(*mt_dramc_nao_cha_base_get_symbol)(void);
+void *(*mt_dramc_nao_chb_base_get_symbol)(void);
+void *(*mt_dramc_nao_chc_base_get_symbol)(void);
+void *(*mt_dramc_nao_chd_base_get_symbol)(void);
+void *(*mt_ddrphy_cha_base_get_symbol)(void);
+void *(*mt_dramc_cha_base_get_symbol)(void);
+#else
+void *(*mt_dramc_nao_chn_base_get_symbol)(int channel);
+void *(*mt_ddrphy_chn_base_get_symbol)(int channel);
+void *(*mt_dramc_chn_base_get_symbol)(int channel);
+#endif
+
+/*
+ *   DRAM
+ */
+void (*mt_dramfreq_setfreq_registerCB_symbol)(dram_sampler_func pCB);
+#endif /* MET_EMI */
+
+#ifdef MET_GPU
+/*
+ *   GPU
+ */
+int (*mtk_get_gpu_loading_symbol)(void);
+int (*mtk_gpufreq_get_cur_freq_symbol)(void);
+unsigned int (*mtk_get_gpu_memory_usage_symbol)(void);
+#endif /* MET_GPU */
+
+static int met_symbol_get(void)
+{
+#define _MET_SYMBOL_GET(_func_name_) \
+	do { \
+		_func_name_##_symbol = (void *)symbol_get(_func_name_); \
+		if (_func_name_##_symbol == NULL) { \
+			pr_debug("MET ext. symbol : %s is not found!\n", #_func_name_); \
+			PR_BOOTMSG_ONCE("MET ext. symbol : %s is not found!\n", #_func_name_); \
+		} \
+	} while (0)
+
+#ifdef MET_EMI
+    _MET_SYMBOL_GET(mt_cen_emi_base_get);
+    _MET_SYMBOL_GET(mt_chn_emi_base_get);
+    _MET_SYMBOL_GET(get_dram_data_rate);
+#if 0 /* New APIs for mt_dramc_nao base get */
+    _MET_SYMBOL_GET(mt_dramc_nao_cha_base_get);
+    _MET_SYMBOL_GET(mt_dramc_nao_chb_base_get);
+    _MET_SYMBOL_GET(mt_dramc_nao_chc_base_get);
+    _MET_SYMBOL_GET(mt_dramc_nao_chd_base_get);
+    _MET_SYMBOL_GET(mt_ddrphy_cha_base_get);
+    _MET_SYMBOL_GET(mt_dramc_cha_base_get);
+#else
+    _MET_SYMBOL_GET(mt_dramc_nao_chn_base_get);
+    _MET_SYMBOL_GET(mt_ddrphy_chn_base_get);
+    _MET_SYMBOL_GET(mt_dramc_chn_base_get);
+#endif
+    _MET_SYMBOL_GET(mt_dramfreq_setfreq_registerCB);
+#endif /* MET_EMI */
+
+#ifdef MET_GPU
+	_MET_SYMBOL_GET(mtk_get_gpu_loading);
+	_MET_SYMBOL_GET(mtk_gpufreq_get_cur_freq);
+	_MET_SYMBOL_GET(mtk_get_gpu_memory_usage);
+#endif /* MET_GPU */
+
+	return 0;
+}
+
+static int met_symbol_put(void)
+{
+#define _MET_SYMBOL_PUT(_func_name_) { \
+		if (_func_name_##_symbol) { \
+			symbol_put(_func_name_); \
+			_func_name_##_symbol = NULL; \
+		} \
+	}
+
+#ifdef MET_EMI
+    _MET_SYMBOL_PUT(mt_cen_emi_base_get);
+    _MET_SYMBOL_PUT(mt_chn_emi_base_get);
+    _MET_SYMBOL_PUT(get_dram_data_rate);
+#if 0 /* New APIs for mt_dramc_nao base get */
+    _MET_SYMBOL_PUT(mt_dramc_nao_cha_base_get);
+    _MET_SYMBOL_PUT(mt_dramc_nao_chb_base_get);
+    _MET_SYMBOL_PUT(mt_dramc_nao_chc_base_get);
+    _MET_SYMBOL_PUT(mt_dramc_nao_chd_base_get);
+    _MET_SYMBOL_PUT(mt_ddrphy_cha_base_get);
+    _MET_SYMBOL_PUT(mt_dramc_cha_base_get);
+#else
+    _MET_SYMBOL_PUT(mt_dramc_nao_chn_base_get);
+    _MET_SYMBOL_PUT(mt_ddrphy_chn_base_get);
+    _MET_SYMBOL_PUT(mt_dramc_chn_base_get);
+#endif
+    _MET_SYMBOL_PUT(mt_dramfreq_setfreq_registerCB);
+#endif /* MET_EMI */
+
+#ifdef MET_GPU
+	_MET_SYMBOL_PUT(mtk_get_gpu_loading);
+	_MET_SYMBOL_PUT(mtk_gpufreq_get_cur_freq);
+	_MET_SYMBOL_PUT(mtk_get_gpu_memory_usage);
+#endif /* MET_GPU */
+
+	return 0;
+}
+
+int core_plf_init(void)
+{
+	/*initial met external symbol*/
+	met_symbol_get();
+
+#ifdef MET_EMI
+    met_register(&met_emi);
+#endif
+
+#ifdef MET_GPU
+	met_register(&met_gpu);
+	met_register(&met_gpudvfs);
+	met_register(&met_gpumem);
+#endif
+
+	return 0;
+}
+
+void core_plf_exit(void)
+{
+	/*release met external symbol*/
+	met_symbol_put();
+
+#ifdef MET_EMI
+    met_deregister(&met_emi);
+#endif
+
+#ifdef MET_GPU
+	met_deregister(&met_gpu);
+	met_deregister(&met_gpudvfs);
+	met_deregister(&met_gpumem);
+#endif
+}
diff --git a/src/devtools/met-driver/met_drv/common/core_plf_init.h b/src/devtools/met-driver/met_drv/common/core_plf_init.h
new file mode 100644
index 0000000..c1eb04b
--- /dev/null
+++ b/src/devtools/met-driver/met_drv/common/core_plf_init.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __CORE_PLF_INIT_H__
+#define __CORE_PLF_INIT_H__
+
+extern struct miscdevice met_device;
+
+/*
+ *   MET External Symbol
+ */
+
+#ifdef MET_EMI
+/*
+ *   EMI Monitor
+ */
+extern unsigned int get_dram_data_rate(void);       /* in Mhz */
+extern void *mt_cen_emi_base_get(void);
+extern void *mt_chn_emi_base_get(void);
+
+#if 0 /* New APIs for mt_dramc_nao base get */
+extern void *mt_dramc_nao_cha_base_get(void);
+extern void *mt_dramc_nao_chb_base_get(void);
+extern void *mt_dramc_nao_chc_base_get(void);
+extern void *mt_dramc_nao_chd_base_get(void);
+extern void *mt_ddrphy_cha_base_get(void);
+extern void *mt_dramc_cha_base_get(void);
+#else
+extern void *mt_dramc_nao_chn_base_get(int channel);
+extern void *mt_ddrphy_chn_base_get(int channel);
+extern void *mt_dramc_chn_base_get(int channel);
+#endif
+
+extern unsigned int (*get_dram_data_rate_symbol)(void); /* in Mhz */
+extern void *(*mt_cen_emi_base_get_symbol)(void);
+extern void *(*mt_chn_emi_base_get_symbol)(int chn);
+#if 0 /* New APIs for mt_dramc_nao base get */
+extern void *(*mt_dramc_nao_cha_base_get_symbol)(void);
+extern void *(*mt_dramc_nao_chb_base_get_symbol)(void);
+extern void *(*mt_dramc_nao_chc_base_get_symbol)(void);
+extern void *(*mt_dramc_nao_chd_base_get_symbol)(void);
+extern void *(*mt_ddrphy_cha_base_get_symbol)(void);
+extern void *(*mt_dramc_cha_base_get_symbol)(void);
+#else
+extern void *(*mt_dramc_nao_chn_base_get_symbol)(int channel);
+extern void *(*mt_ddrphy_chn_base_get_symbol)(int channel);
+extern void *(*mt_dramc_chn_base_get_symbol)(int channel);
+#endif
+
+/*
+ *   DRAM
+ */
+typedef void (*dram_sampler_func) (unsigned int);
+extern void (*mt_dramfreq_setfreq_registerCB_symbol)(dram_sampler_func pCB);
+
+/*mt_dramc.c do not have header file, declare here */
+extern void mt_dramfreq_setfreq_registerCB(dram_sampler_func pCB);
+
+extern struct metdevice met_emi;
+#endif
+
+#ifdef MET_GPU
+/*
+ *   GPU
+ */
+//#include <mtk_gpu_utility.h>
+//#include <mtk_gpufreq.h>
+
+extern int mtk_get_gpu_loading(void);
+extern int mtk_gpufreq_get_cur_freq(void);
+extern unsigned int mtk_get_gpu_memory_usage(void);
+
+extern int (*mtk_get_gpu_loading_symbol)(void);
+extern int (*mtk_gpufreq_get_cur_freq_symbol)(void);
+extern unsigned int (*mtk_get_gpu_memory_usage_symbol)(void);
+
+extern struct metdevice met_gpu;
+extern struct metdevice met_gpudvfs;
+extern struct metdevice met_gpumem;
+#endif /* MET_GPU */
+
+#endif /*__CORE_PLF_INIT_H__*/
diff --git a/src/devtools/met-driver/met_drv/common/core_plf_trace.c b/src/devtools/met-driver/met_drv/common/core_plf_trace.c
new file mode 100644
index 0000000..937f63b
--- /dev/null
+++ b/src/devtools/met-driver/met_drv/common/core_plf_trace.c
@@ -0,0 +1,376 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+#include "met_drv.h"
+#include "interface.h"
+#include "trace.h"
+
+char *ms_formatH(char *__restrict__ buf, unsigned char cnt, unsigned int *__restrict__ value)
+{
+	char *s = buf;
+	int len;
+
+	if (cnt == 0) {
+		buf[0] = '\0';
+		return buf;
+	}
+
+	switch (cnt % 4) {
+	case 1:
+		len = sprintf(s, "%x", value[0]);
+		s += len;
+		value += 1;
+		cnt -= 1;
+		break;
+	case 2:
+		len = sprintf(s, "%x,%x", value[0], value[1]);
+		s += len;
+		value += 2;
+		cnt -= 2;
+		break;
+	case 3:
+		len = sprintf(s, "%x,%x,%x", value[0], value[1], value[2]);
+		s += len;
+		value += 3;
+		cnt -= 3;
+		break;
+	case 0:
+		len = sprintf(s, "%x,%x,%x,%x", value[0], value[1], value[2], value[3]);
+		s += len;
+		value += 4;
+		cnt -= 4;
+		break;
+	}
+
+	while (cnt) {
+		len = sprintf(s, ",%x,%x,%x,%x", value[0], value[1], value[2], value[3]);
+		s += len;
+		value += 4;
+		cnt -= 4;
+	}
+
+	s[0] = '\0';
+
+	return s;
+}
+EXPORT_SYMBOL(ms_formatH);
+
+char *ms_formatD(char *__restrict__ buf, unsigned char cnt, unsigned int *__restrict__ value)
+{
+	char *s = buf;
+	int len;
+
+	if (cnt == 0) {
+		buf[0] = '\0';
+		return buf;
+	}
+
+	switch (cnt % 4) {
+	case 1:
+		len = sprintf(s, "%u", value[0]);
+		s += len;
+		value += 1;
+		cnt -= 1;
+		break;
+	case 2:
+		len = sprintf(s, "%u,%u", value[0], value[1]);
+		s += len;
+		value += 2;
+		cnt -= 2;
+		break;
+	case 3:
+		len = sprintf(s, "%u,%u,%u", value[0], value[1], value[2]);
+		s += len;
+		value += 3;
+		cnt -= 3;
+		break;
+	case 0:
+		len = sprintf(s, "%u,%u,%u,%u", value[0], value[1], value[2], value[3]);
+		s += len;
+		value += 4;
+		cnt -= 4;
+		break;
+	}
+
+	while (cnt) {
+		len = sprintf(s, ",%u,%u,%u,%u", value[0], value[1], value[2], value[3]);
+		s += len;
+		value += 4;
+		cnt -= 4;
+	}
+
+	s[0] = '\0';
+
+	return s;
+}
+EXPORT_SYMBOL(ms_formatD);
+
+char *ms_formatH_ulong(char *__restrict__ buf, unsigned char cnt, unsigned long *__restrict__ value)
+{
+	char *s = buf;
+	int len;
+
+	if (cnt == 0) {
+		buf[0] = '\0';
+		return buf;
+	}
+
+	switch (cnt % 4) {
+	case 1:
+		len = sprintf(s, "%lx", value[0]);
+		s += len;
+		value += 1;
+		cnt -= 1;
+		break;
+	case 2:
+		len = sprintf(s, "%lx,%lx", value[0], value[1]);
+		s += len;
+		value += 2;
+		cnt -= 2;
+		break;
+	case 3:
+		len = sprintf(s, "%lx,%lx,%lx", value[0], value[1], value[2]);
+		s += len;
+		value += 3;
+		cnt -= 3;
+		break;
+	case 0:
+		len = sprintf(s, "%lx,%lx,%lx,%lx", value[0], value[1], value[2], value[3]);
+		s += len;
+		value += 4;
+		cnt -= 4;
+		break;
+	}
+
+	while (cnt) {
+		len = sprintf(s, ",%lx,%lx,%lx,%lx", value[0], value[1], value[2], value[3]);
+		s += len;
+		value += 4;
+		cnt -= 4;
+	}
+
+	s[0] = '\0';
+
+	return buf;
+}
+EXPORT_SYMBOL(ms_formatH_ulong);
+
+char *ms_formatD_ulong(char *__restrict__ buf, unsigned char cnt, unsigned long *__restrict__ value)
+{
+	char *s = buf;
+	int len;
+
+	if (cnt == 0) {
+		buf[0] = '\0';
+		return buf;
+	}
+
+	switch (cnt % 4) {
+	case 1:
+		len = sprintf(s, "%lu", value[0]);
+		s += len;
+		value += 1;
+		cnt -= 1;
+		break;
+	case 2:
+		len = sprintf(s, "%lu,%lu", value[0], value[1]);
+		s += len;
+		value += 2;
+		cnt -= 2;
+		break;
+	case 3:
+		len = sprintf(s, "%lu,%lu,%lu", value[0], value[1], value[2]);
+		s += len;
+		value += 3;
+		cnt -= 3;
+		break;
+	case 0:
+		len = sprintf(s, "%lu,%lu,%lu,%lu", value[0], value[1], value[2], value[3]);
+		s += len;
+		value += 4;
+		cnt -= 4;
+		break;
+	}
+
+	while (cnt) {
+		len = sprintf(s, ",%lu,%lu,%lu,%lu", value[0], value[1], value[2], value[3]);
+		s += len;
+		value += 4;
+		cnt -= 4;
+	}
+
+	s[0] = '\0';
+
+	return buf;
+}
+EXPORT_SYMBOL(ms_formatD_ulong);
+
+char *ms_formatH_EOL(char *__restrict__ buf, unsigned char cnt, unsigned int *__restrict__ value)
+{
+	char *s = buf;
+	int len;
+
+	if (cnt == 0) {
+		buf[0] = '\0';
+		return buf;
+	}
+
+	switch (cnt % 4) {
+	case 1:
+		len = sprintf(s, "%x", value[0]);
+		s += len;
+		value += 1;
+		cnt -= 1;
+		break;
+	case 2:
+		len = sprintf(s, "%x,%x", value[0], value[1]);
+		s += len;
+		value += 2;
+		cnt -= 2;
+		break;
+	case 3:
+		len = sprintf(s, "%x,%x,%x", value[0], value[1], value[2]);
+		s += len;
+		value += 3;
+		cnt -= 3;
+		break;
+	case 0:
+		len = sprintf(s, "%x,%x,%x,%x", value[0], value[1], value[2], value[3]);
+		s += len;
+		value += 4;
+		cnt -= 4;
+		break;
+	}
+
+	while (cnt) {
+		len = sprintf(s, ",%x,%x,%x,%x", value[0], value[1], value[2], value[3]);
+		s += len;
+		value += 4;
+		cnt -= 4;
+	}
+
+	s[0] = '\n';
+	s[1] = '\0';
+
+	return s + 1;
+}
+EXPORT_SYMBOL(ms_formatH_EOL);
+
+char *ms_formatD_EOL(char *__restrict__ buf, unsigned char cnt, unsigned int *__restrict__ value)
+{
+	char *s = buf;
+	int len;
+
+	if (cnt == 0) {
+		buf[0] = '\0';
+		return buf;
+	}
+
+	switch (cnt % 4) {
+	case 1:
+		len = sprintf(s, "%u", value[0]);
+		s += len;
+		value += 1;
+		cnt -= 1;
+		break;
+	case 2:
+		len = sprintf(s, "%u,%u", value[0], value[1]);
+		s += len;
+		value += 2;
+		cnt -= 2;
+		break;
+	case 3:
+		len = sprintf(s, "%u,%u,%u", value[0], value[1], value[2]);
+		s += len;
+		value += 3;
+		cnt -= 3;
+		break;
+	case 0:
+		len = sprintf(s, "%u,%u,%u,%u", value[0], value[1], value[2], value[3]);
+		s += len;
+		value += 4;
+		cnt -= 4;
+		break;
+	}
+
+	while (cnt) {
+		len = sprintf(s, ",%u,%u,%u,%u", value[0], value[1], value[2], value[3]);
+		s += len;
+		value += 4;
+		cnt -= 4;
+	}
+
+	s[0] = '\n';
+	s[1] = '\0';
+
+	return s + 1;
+}
+EXPORT_SYMBOL(ms_formatD_EOL);
+
+noinline void ms_bw_limiter(unsigned char cnt, unsigned int *value)
+{
+    char *SOB, *EOB;
+
+    MET_TRACE_GETBUF(&SOB, &EOB);
+    EOB = ms_formatH_EOL(EOB, cnt, value);
+    MET_TRACE_PUTBUF(SOB, EOB);
+}
+
+noinline void ms_emi(unsigned char cnt, unsigned int *value)
+{
+    char *SOB, *EOB;
+
+    MET_TRACE_GETBUF(&SOB, &EOB);
+    EOB = ms_formatH_EOL(EOB, cnt, value);
+    MET_TRACE_PUTBUF(SOB, EOB);
+}
+
+noinline void ms_emi_tsct(unsigned char cnt, unsigned int *value)
+{
+    char *SOB, *EOB;
+
+    MET_TRACE_GETBUF(&SOB, &EOB);
+    EOB = ms_formatH_EOL(EOB, cnt, value);
+    MET_TRACE_PUTBUF(SOB, EOB);
+}
+
+noinline void ms_emi_mdct(unsigned char cnt, unsigned int *value)
+{
+    char *SOB, *EOB;
+
+    MET_TRACE_GETBUF(&SOB, &EOB);
+    EOB = ms_formatH_EOL(EOB, cnt, value);
+    MET_TRACE_PUTBUF(SOB, EOB);
+}
+
+noinline void ms_ttype(unsigned char cnt, unsigned int *value)
+{
+    char *SOB, *EOB;
+
+    MET_TRACE_GETBUF(&SOB, &EOB);
+    EOB = ms_formatH_EOL(EOB, cnt, value);
+    MET_TRACE_PUTBUF(SOB, EOB);
+}
+
+noinline void ms_dramc(unsigned char cnt, unsigned int *value)
+{
+    char *SOB, *EOB;
+
+    MET_TRACE_GETBUF(&SOB, &EOB);
+    EOB = ms_formatH_EOL(EOB, cnt, value);
+    MET_TRACE_PUTBUF(SOB, EOB);
+}
+
diff --git a/src/devtools/met-driver/met_drv/common/core_plf_trace.h b/src/devtools/met-driver/met_drv/common/core_plf_trace.h
new file mode 100644
index 0000000..8dc1e6c
--- /dev/null
+++ b/src/devtools/met-driver/met_drv/common/core_plf_trace.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _CORE_PLF_TRACE_H_
+#define _CORE_PLF_TRACE_H_
+
+#define	HVALUE_SIZE	9	/* 8 chars (max value ffffffff) + 1 char (',' or NULL) */
+#define	DVALUE_SIZE	12	/* 10 chars (max value 4,294,967,295) + 1 char (',' or NULL) */
+
+char *ms_formatH(char *__restrict__ buf, unsigned char cnt, unsigned int *__restrict__ value);
+char *core_ms_formatD(char *__restrict__ buf, unsigned char cnt, unsigned int *__restrict__ value);
+char *ms_formatH_ulong(char *__restrict__ buf, unsigned char cnt,
+		       unsigned long *__restrict__ value);
+char *ms_formatD_ulong(char *__restrict__ buf, unsigned char cnt,
+		       unsigned long *__restrict__ value);
+char *ms_formatH_EOL(char *__restrict__ buf, unsigned char cnt, unsigned int *__restrict__ value);
+char *ms_formatD_EOL(char *__restrict__ buf, unsigned char cnt, unsigned int *__restrict__ value);
+
+void ms_emi(unsigned char cnt, unsigned int *value);
+void ms_emi_tsct(unsigned char cnt, unsigned int *value);
+void ms_emi_mdct(unsigned char cnt, unsigned int *value);
+
+void ms_ttype(unsigned char cnt, unsigned int *value);
+void ms_bw_limiter(unsigned char cnt, unsigned int *value);
+
+void ms_dramc(unsigned char cnt, unsigned int *value);
+
+#endif	/* _CORE_PLF_TRACE_H_ */
diff --git a/src/devtools/met-driver/met_drv/common/cpu_pmu.c b/src/devtools/met-driver/met_drv/common/cpu_pmu.c
new file mode 100644
index 0000000..e2568b8
--- /dev/null
+++ b/src/devtools/met-driver/met_drv/common/cpu_pmu.c
@@ -0,0 +1,620 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/* include <asm/page.h> */
+#include <linux/slab.h>
+#include <linux/version.h>
+
+#include "interface.h"
+#include "trace.h"
+#include "cpu_pmu.h"
+#include "met_drv.h"
+
+#define MET_USER_EVENT_SUPPORT
+
+#include <linux/kthread.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/signal.h>
+#include <linux/workqueue.h>
+#include <linux/perf_event.h>
+
+#include "met_kernel_symbol.h"
+#include "interface.h"
+
+static int counter_cnt;
+
+struct metdevice met_cpupmu;
+struct cpu_pmu_hw *cpu_pmu;
+static int nr_counters;
+
+static struct kobject *kobj_cpu;
+static struct met_pmu *pmu;
+static int nr_arg;
+
+
+#define CNTMAX 8
+static DEFINE_PER_CPU(unsigned long long[CNTMAX], perfCurr);
+static DEFINE_PER_CPU(unsigned long long[CNTMAX], perfPrev);
+static DEFINE_PER_CPU(int[CNTMAX], perfCntFirst);
+static DEFINE_PER_CPU(struct perf_event * [CNTMAX], pevent);
+static DEFINE_PER_CPU(struct perf_event_attr [CNTMAX], pevent_attr);
+static DEFINE_PER_CPU(int, perfSet);
+static DEFINE_PER_CPU(unsigned int, perf_task_init_done);
+static DEFINE_PER_CPU(unsigned int, perf_cpuid);
+
+static DEFINE_PER_CPU(struct delayed_work, cpu_pmu_dwork);
+static DEFINE_PER_CPU(struct delayed_work *, perf_delayed_work_setup);
+
+static inline int reset_driver_stat(int counters)
+{
+	int i;
+
+	nr_arg = 0;
+	counter_cnt = 0;
+	met_cpupmu.mode = 0;
+	for (i = 0; i < counters; i++) {
+		pmu[i].mode = MODE_DISABLED;
+		pmu[i].event = 0;
+		pmu[i].freq = 0;
+	}
+
+	return 0;
+}
+
+static inline struct met_pmu *lookup_pmu(struct kobject *kobj)
+{
+	int i;
+
+	for (i = 0; i < nr_counters; i++) {
+		if (pmu[i].kobj_cpu_pmu == kobj)
+			return &pmu[i];
+	}
+	return NULL;
+}
+
+static ssize_t count_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%d\n", nr_counters - 1);
+}
+
+static ssize_t count_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t n)
+{
+	return -EINVAL;
+}
+
+static ssize_t event_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	struct met_pmu *p = lookup_pmu(kobj);
+
+	if (p != NULL)
+		return snprintf(buf, PAGE_SIZE, "0x%hx\n", p->event);
+
+	return -EINVAL;
+}
+
+static ssize_t event_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t n)
+{
+	struct met_pmu *p = lookup_pmu(kobj);
+	unsigned short event;
+
+	if (p != NULL) {
+		if (sscanf(buf, "0x%hx", &event) != 1)
+			return -EINVAL;
+
+		if (p == &(pmu[nr_counters - 1])) {	/* cycle counter */
+			if (event != 0xff)
+				return -EINVAL;
+		} else {
+			if (cpu_pmu->check_event(pmu, nr_arg, event) < 0)
+				return -EINVAL;
+		}
+
+		p->event = event;
+		return n;
+	}
+	return -EINVAL;
+}
+
+static ssize_t mode_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	struct met_pmu *p = lookup_pmu(kobj);
+
+	if (p != NULL) {
+		switch (p->mode) {
+		case 0:
+			return snprintf(buf, PAGE_SIZE, "%hhd (disabled)\n", p->mode);
+		case 1:
+			return snprintf(buf, PAGE_SIZE, "%hhd (interrupt)\n", p->mode);
+		case 2:
+			return snprintf(buf, PAGE_SIZE, "%hhd (polling)\n", p->mode);
+		}
+	}
+	return -EINVAL;
+}
+
+static ssize_t mode_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t n)
+{
+	unsigned int mode;
+	struct met_pmu *p = lookup_pmu(kobj);
+
+	if (p != NULL) {
+		if (kstrtouint(buf, 0, &mode) != 0)
+			return -EINVAL;
+
+		if (mode <= 2) {
+			p->mode = (unsigned char)mode;
+			if (mode > 0)
+				met_cpupmu.mode = 1;
+			return n;
+		}
+	}
+	return -EINVAL;
+}
+
+static ssize_t freq_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	struct met_pmu *p = lookup_pmu(kobj);
+
+	if (p != NULL)
+		return snprintf(buf, PAGE_SIZE, "%ld\n", p->freq);
+
+	return -EINVAL;
+}
+
+static ssize_t freq_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t n)
+{
+	struct met_pmu *p = lookup_pmu(kobj);
+
+	if (p != NULL) {
+		if (kstrtoul(buf, 0, &(p->freq)) != 0)
+			return -EINVAL;
+
+		return n;
+	}
+	return -EINVAL;
+}
+
+static struct kobj_attribute count_attr = __ATTR(count, 0664, count_show, count_store);
+static struct kobj_attribute event_attr = __ATTR(event, 0664, event_show, event_store);
+static struct kobj_attribute mode_attr = __ATTR(mode, 0664, mode_show, mode_store);
+static struct kobj_attribute freq_attr = __ATTR(freq, 0664, freq_show, freq_store);
+
+static int cpupmu_create_subfs(struct kobject *parent)
+{
+	int ret = 0;
+	int i;
+	char buf[16];
+
+	cpu_pmu = cpu_pmu_hw_init();
+	if (cpu_pmu == NULL) {
+		PR_BOOTMSG("Failed to init CPU PMU HW!!\n");
+		return -ENODEV;
+	}
+	nr_counters = cpu_pmu->nr_cnt;
+
+	pmu = kmalloc_array(nr_counters, sizeof(struct met_pmu), GFP_KERNEL);
+	if (pmu == NULL)
+		return -ENOMEM;
+
+	memset(pmu, 0, sizeof(struct met_pmu) * nr_counters);
+	cpu_pmu->pmu = pmu;
+	kobj_cpu = parent;
+
+	ret = sysfs_create_file(kobj_cpu, &count_attr.attr);
+	if (ret != 0) {
+		PR_BOOTMSG("Failed to create count in sysfs\n");
+		goto out;
+	}
+
+	for (i = 0; i < nr_counters; i++) {
+		snprintf(buf, sizeof(buf), "%d", i);
+		pmu[i].kobj_cpu_pmu = kobject_create_and_add(buf, kobj_cpu);
+
+		ret = sysfs_create_file(pmu[i].kobj_cpu_pmu, &event_attr.attr);
+		if (ret != 0) {
+			PR_BOOTMSG("Failed to create event in sysfs\n");
+			goto out;
+		}
+
+		ret = sysfs_create_file(pmu[i].kobj_cpu_pmu, &mode_attr.attr);
+		if (ret != 0) {
+			PR_BOOTMSG("Failed to create mode in sysfs\n");
+			goto out;
+		}
+
+		ret = sysfs_create_file(pmu[i].kobj_cpu_pmu, &freq_attr.attr);
+		if (ret != 0) {
+			PR_BOOTMSG("Failed to create freq in sysfs\n");
+			goto out;
+		}
+	}
+
+ out:
+	if (ret != 0) {
+		if (pmu != NULL) {
+			kfree(pmu);
+			pmu = NULL;
+		}
+	}
+	return ret;
+}
+
+static void cpupmu_delete_subfs(void)
+{
+	int i;
+
+	if (kobj_cpu != NULL) {
+		for (i = 0; i < nr_counters; i++) {
+			sysfs_remove_file(pmu[i].kobj_cpu_pmu, &event_attr.attr);
+			sysfs_remove_file(pmu[i].kobj_cpu_pmu, &mode_attr.attr);
+			sysfs_remove_file(pmu[i].kobj_cpu_pmu, &freq_attr.attr);
+			kobject_del(pmu[i].kobj_cpu_pmu);
+			kobject_put(pmu[i].kobj_cpu_pmu);
+			pmu[i].kobj_cpu_pmu = NULL;
+		}
+		sysfs_remove_file(kobj_cpu, &count_attr.attr);
+		kobj_cpu = NULL;
+	}
+
+	if (pmu != NULL) {
+		kfree(pmu);
+		pmu = NULL;
+	}
+
+	cpu_pmu  = NULL;
+}
+
+noinline void mp_cpu(unsigned char cnt, unsigned int *value)
+{
+	MET_GENERAL_PRINT(MET_TRACE, cnt, value);
+}
+
+static void dummy_handler(struct perf_event *event, struct perf_sample_data *data,
+			  struct pt_regs *regs)
+{
+/* Required as perf_event_create_kernel_counter() requires an overflow handler, even though all we do is poll */
+}
+
+void perf_cpupmu_polling(unsigned long long stamp, int cpu)
+{
+	int i, count;
+	long long int delta;
+	struct perf_event *ev;
+	unsigned int pmu_value[MXNR_CPU];
+
+	MET_TRACE("counter_cnt = %d\n", counter_cnt);
+	if (per_cpu(perfSet, cpu) == 0)
+		return;
+
+	memset(pmu_value, 0, sizeof(pmu_value));
+	count = 0;
+	for (i = 0; i < nr_counters; i++) {
+		if (pmu[i].mode == 0)
+			continue;
+
+		ev = per_cpu(pevent, cpu)[i];
+		if ((ev != NULL) && (ev->state == PERF_EVENT_STATE_ACTIVE)) {
+			per_cpu(perfCurr, cpu)[i] = met_perf_event_read_local_symbol(ev);
+			delta = (long long int)(per_cpu(perfCurr, cpu)[i] - per_cpu(perfPrev, cpu)[i]);
+			if (delta < 0)
+				delta *= (-1);
+			per_cpu(perfPrev, cpu)[i] = per_cpu(perfCurr, cpu)[i];
+			if (per_cpu(perfCntFirst, cpu)[i] == 1) {
+				/* we shall omit delta counter when we get first counter */
+				per_cpu(perfCntFirst, cpu)[i] = 0;
+				continue;
+			}
+			pmu_value[i] = (unsigned int) delta;
+			count++;
+		}
+	}
+
+	MET_TRACE("count = %d, counter_cnt = %d\n", count, counter_cnt);
+
+	if (count == counter_cnt)
+		mp_cpu(count, pmu_value);
+}
+
+static int perf_thread_set_perf_events(unsigned int cpu)
+{
+	int i, size;
+	struct perf_event *ev;
+
+	size = sizeof(struct perf_event_attr);
+	if (per_cpu(perfSet, cpu) == 0) {
+		for (i = 0; i < nr_counters; i++) {
+			per_cpu(pevent, cpu)[i] = NULL;
+			if (!pmu[i].mode)	/* Skip disabled counters */
+				continue;
+
+			per_cpu(perfPrev, cpu)[i] = 0;
+			per_cpu(perfCurr, cpu)[i] = 0;
+			memset(&per_cpu(pevent_attr, cpu)[i], 0, size);
+			per_cpu(pevent_attr, cpu)[i].config = pmu[i].event;
+			per_cpu(pevent_attr, cpu)[i].type = PERF_TYPE_RAW;
+			per_cpu(pevent_attr, cpu)[i].size = size;
+			per_cpu(pevent_attr, cpu)[i].sample_period = 0;
+			per_cpu(pevent_attr, cpu)[i].pinned = 1;
+			if (pmu[i].event == 0xff) {
+				per_cpu(pevent_attr, cpu)[i].type = PERF_TYPE_HARDWARE;
+				per_cpu(pevent_attr, cpu)[i].config = PERF_COUNT_HW_CPU_CYCLES;
+			}
+
+			per_cpu(pevent, cpu)[i] =
+			    perf_event_create_kernel_counter(&per_cpu(pevent_attr, cpu)[i], cpu, NULL,
+				     dummy_handler, NULL);
+			if (IS_ERR(per_cpu(pevent, cpu)[i])) {
+				per_cpu(pevent, cpu)[i] = NULL;
+				PR_BOOTMSG("CPU=%d, %s:%d\n", cpu, __FUNCTION__, __LINE__);
+				continue;
+			}
+
+			if (per_cpu(pevent, cpu)[i]->state != PERF_EVENT_STATE_ACTIVE) {
+				perf_event_release_kernel(per_cpu(pevent, cpu)[i]);
+				per_cpu(pevent, cpu)[i] = NULL;
+				PR_BOOTMSG("CPU=%d, %s:%d\n", cpu, __FUNCTION__, __LINE__);
+				continue;
+			}
+
+			ev = per_cpu(pevent, cpu)[i];
+			if (ev != NULL)
+				perf_event_enable(ev);
+			per_cpu(perfCntFirst, cpu)[i] = 1;
+		} /* for all PMU counter */
+		per_cpu(perfSet, cpu) = 1;
+	} /* for perfSet */
+	return 0;
+}
+
+
+static void perf_thread_setup(struct work_struct *work)
+{
+	unsigned int cpu;
+	struct delayed_work *dwork = to_delayed_work(work);
+
+	cpu = dwork->cpu;
+	if (per_cpu(perf_task_init_done, cpu) == 0) {
+		per_cpu(perf_task_init_done, cpu) = 1;
+		perf_thread_set_perf_events(cpu);
+	}
+
+	return;
+}
+
+void met_perf_cpupmu_online(unsigned int cpu)
+{
+	if (met_cpupmu.mode == 0)
+		return;
+
+	per_cpu(perf_cpuid, cpu) = cpu;
+	if (per_cpu(perf_delayed_work_setup, cpu) == NULL) {
+		struct delayed_work *dwork;
+
+		dwork = &per_cpu(cpu_pmu_dwork, cpu);
+		dwork->cpu = cpu;
+		INIT_DELAYED_WORK(dwork, perf_thread_setup);
+		schedule_delayed_work(dwork, 0);
+		per_cpu(perf_delayed_work_setup, cpu) = dwork;
+	}
+}
+
+
+void met_perf_cpupmu_down(void *data)
+{
+	unsigned int cpu;
+	unsigned int i;
+	struct perf_event *ev;
+
+	cpu = *((unsigned int *)data);
+	if (met_cpupmu.mode == 0)
+		return;
+	if (per_cpu(perfSet, cpu) == 0)
+		return;
+	per_cpu(perfSet, cpu) = 0;
+	for (i = 0; i < nr_counters; i++) {
+		if (!pmu[i].mode)
+			continue;
+		ev = per_cpu(pevent, cpu)[i];
+		if ((ev != NULL) && (ev->state == PERF_EVENT_STATE_ACTIVE)) {
+			perf_event_disable(ev);
+			perf_event_release_kernel(ev);
+		}
+	}
+	per_cpu(perf_task_init_done, cpu) = 0;
+	per_cpu(perf_delayed_work_setup, cpu) = NULL;
+}
+
+void met_perf_cpupmu_stop(void)
+{
+	unsigned int cpu;
+
+	for_each_online_cpu(cpu) {
+		per_cpu(perf_cpuid, cpu) = cpu;
+		met_perf_cpupmu_down((void *)&per_cpu(perf_cpuid, cpu));
+	}
+}
+
+void met_perf_cpupmu_start(void)
+{
+	unsigned int cpu;
+
+	for_each_online_cpu(cpu) {
+		met_perf_cpupmu_online(cpu);
+	}
+}
+
+void cpupmu_polling(unsigned long long stamp, int cpu)
+{
+	int count;
+	unsigned int pmu_value[MXNR_CPU];
+
+	if (met_cpu_pmu_method == 0) {
+		count = cpu_pmu->polling(pmu, nr_counters, pmu_value);
+		mp_cpu(count, pmu_value);
+	} else {
+		perf_cpupmu_polling(stamp, cpu);
+	}
+}
+
+static void cpupmu_start(void)
+{
+	if (met_cpu_pmu_method == 0) {
+		nr_arg = 0;
+		cpu_pmu->start(pmu, nr_counters);
+	}
+}
+
+static void cpupmu_stop(void)
+{
+	if (met_cpu_pmu_method == 0)
+		cpu_pmu->stop(nr_counters);
+}
+
+static const char cache_line_header[] =
+	"met-info [000] 0.0: met_cpu_cache_line_size: %d\n";
+static const char header_n[] =
+	"# mp_cpu: pmu_value1, ...\n"
+	"met-info [000] 0.0: met_cpu_header: 0x%x:%s";
+static const char header[] =
+	"# mp_cpu: pmu_value1, ...\n"
+	"met-info [000] 0.0: met_cpu_header: 0x%x";
+
+static const char help[] =
+	"  --pmu-cpu-evt=EVENT                   select CPU-PMU events. in %s,\n"
+	"                                        you can enable at most \"%d general purpose events\"\n"
+	"                                        plus \"one special 0xff (CPU_CYCLE) event\"\n";
+
+static int cpupmu_print_help(char *buf, int len)
+{
+	return snprintf(buf, PAGE_SIZE, help, cpu_pmu->cpu_name, nr_counters - 1);
+}
+
+static int cpupmu_print_header(char *buf, int len)
+{
+	int i, ret, first;
+	char name[32];
+
+	first = 1;
+	ret = 0;
+
+	/*append CPU PMU access method*/
+	if (met_cpu_pmu_method == 0)
+		ret += snprintf(buf + ret, PAGE_SIZE,
+			"met-info [000] 0.0: CPU_PMU_method: PMU registers\n");
+	else
+		ret += snprintf(buf + ret, PAGE_SIZE,
+			"met-info [000] 0.0: CPU_PMU_method: perf APIs\n");
+
+	/*append cache line size*/
+	ret += snprintf(buf + ret, PAGE_SIZE - ret, cache_line_header, cache_line_size());
+
+	for (i = 0; i < nr_counters; i++) {
+		if (pmu[i].mode == 0)
+			continue;
+		if (cpu_pmu->get_event_desc && 0 == cpu_pmu->get_event_desc(i, pmu[i].event, name)) {
+			if (first) {
+				ret += snprintf(buf + ret, PAGE_SIZE - ret, header_n, pmu[i].event, name);
+				first = 0;
+			} else {
+				ret += snprintf(buf + ret, PAGE_SIZE - ret, ",0x%x:%s", pmu[i].event, name);
+			}
+		} else {
+			if (first) {
+				ret += snprintf(buf + ret, PAGE_SIZE - ret, header, pmu[i].event);
+				first = 0;
+			} else {
+				ret += snprintf(buf + ret, PAGE_SIZE - ret, ",0x%x", pmu[i].event);
+			}
+		}
+		pmu[i].mode = 0;
+
+	}
+
+	ret += snprintf(buf + ret, PAGE_SIZE - ret, "\n");
+	met_cpupmu.mode = 0;
+	reset_driver_stat(nr_counters);
+	nr_arg = 0;
+	return ret;
+}
+
+/*
+ * "met-cmd --start --pmu_cpu_evt=0x3"
+ */
+static int cpupmu_process_argument(const char *arg, int len)
+{
+	unsigned int value;
+
+	if (met_cpu_pmu_method == 0)
+		nr_counters = cpu_pmu->nr_cnt;
+	else
+		nr_counters = perf_num_counters();
+
+	if (nr_counters == 0)
+		goto arg_out;
+
+	if (met_parse_num(arg, &value, len) < 0)
+		goto arg_out;
+
+	if (cpu_pmu->check_event(pmu, nr_arg, value) < 0)
+		goto arg_out;
+
+	if (value == 0xff) {
+		if (met_cpu_pmu_method == 0) {
+			pmu[nr_counters - 1].mode = MODE_POLLING;
+			pmu[nr_counters - 1].event = 0xff;
+			pmu[nr_counters - 1].freq = 0;
+		} else {
+			if (nr_arg > (nr_counters - 1))
+				goto arg_out;
+
+			pmu[nr_arg].mode = MODE_POLLING;
+			pmu[nr_arg].event = value;
+			pmu[nr_arg].freq = 0;
+			nr_arg++;
+		}
+	} else {
+
+		if (nr_arg >= (nr_counters - 1))
+			goto arg_out;
+
+		pmu[nr_arg].mode = MODE_POLLING;
+		pmu[nr_arg].event = value;
+		pmu[nr_arg].freq = 0;
+		nr_arg++;
+	}
+	counter_cnt++;
+
+	met_cpupmu.mode = 1;
+	return 0;
+
+arg_out:
+	reset_driver_stat(nr_counters);
+	return -EINVAL;
+}
+
+struct metdevice met_cpupmu = {
+	.name = "cpu",
+	.type = MET_TYPE_PMU,
+	.cpu_related = 1,
+	.create_subfs = cpupmu_create_subfs,
+	.delete_subfs = cpupmu_delete_subfs,
+	.start = cpupmu_start,
+	.stop = cpupmu_stop,
+	.polling_interval = 1,
+	.timed_polling = cpupmu_polling,
+	.print_help = cpupmu_print_help,
+	.print_header = cpupmu_print_header,
+	.process_argument = cpupmu_process_argument
+};
diff --git a/src/devtools/met-driver/met_drv/common/cpu_pmu.h b/src/devtools/met-driver/met_drv/common/cpu_pmu.h
new file mode 100644
index 0000000..013a063
--- /dev/null
+++ b/src/devtools/met-driver/met_drv/common/cpu_pmu.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _PMU_H_
+#define _PMU_H_
+
+#include <linux/device.h>
+
+#define MODE_DISABLED	0
+#define MODE_INTERRUPT	1
+#define MODE_POLLING	2
+
+#define MXSIZE_PMU_DESC 32
+#define MXNR_CPU 16
+
+struct met_pmu {
+	unsigned char mode;
+	unsigned short event;
+	unsigned long freq;
+	struct kobject *kobj_cpu_pmu;
+};
+
+struct cpu_pmu_hw {
+	const char *name;
+	const char *cpu_name;
+	int nr_cnt;
+	int (*get_event_desc)(int idx, int event, char *event_desc);
+	int (*check_event)(struct met_pmu *pmu, int idx, int event);
+	void (*start)(struct met_pmu *pmu, int count);
+	void (*stop)(int count);
+	unsigned int (*polling)(struct met_pmu *pmu, int count, unsigned int *pmu_value);
+	struct met_pmu *pmu;
+};
+
+struct pmu_desc {
+	unsigned int event;
+	char name[MXSIZE_PMU_DESC];
+};
+
+struct cpu_pmu_hw *cpu_pmu_hw_init(void);
+
+extern struct cpu_pmu_hw *cpu_pmu;
+extern noinline void mp_cpu(unsigned char cnt, unsigned int *value);
+
+extern void met_perf_cpupmu_start(void);
+extern void met_perf_cpupmu_stop(void);
+extern void met_perf_cpupmu_online(unsigned int cpu);
+extern void met_perf_cpupmu_down(void *cpu);
+extern void cpupmu_polling(unsigned long long stamp, int cpu);
+
+#endif				/* _PMU_H_ */
diff --git a/src/devtools/met-driver/met_drv/common/cpu_pmu_v2.c b/src/devtools/met-driver/met_drv/common/cpu_pmu_v2.c
new file mode 100644
index 0000000..371fa54
--- /dev/null
+++ b/src/devtools/met-driver/met_drv/common/cpu_pmu_v2.c
@@ -0,0 +1,672 @@
+/*

+ * Copyright (C) 2018 MediaTek Inc.

+ *

+ * 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.

+ *

+ * This program is distributed in the hope that it will be useful,

+ * but WITHOUT ANY WARRANTY; without even the implied warranty of

+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the

+ * GNU General Public License for more details.

+ */

+

+#include <linux/slab.h>

+#include <linux/version.h>

+

+#include "interface.h"

+#include "trace.h"

+#include "cpu_pmu_v2.h"

+#include "v8_pmu_hw_v2.h"

+#include "met_drv.h"

+

+

+#define MET_USER_EVENT_SUPPORT

+

+#include <linux/kthread.h>

+#include <linux/kernel.h>

+#include <linux/sched.h>

+#include <linux/wait.h>

+#include <linux/signal.h>

+#include <linux/workqueue.h>

+#include <linux/perf_event.h>

+#include "met_kernel_symbol.h"

+

+

+/*******************************************************************************

+*				Type Define

+*******************************************************************************/

+#define CNTMAX 8

+

+

+/*******************************************************************************

+*				Fuction Pototypes

+*******************************************************************************/

+static inline struct met_pmu_v2 *get_met_pmu_by_cpu_id(const unsigned int cpu);

+static inline void set_met_pmu_by_cpu_id(const unsigned int cpu, struct met_pmu_v2 *met_pmu);

+

+static int reset_driver_stat(void);

+static struct met_pmu_v2 *lookup_pmu(struct kobject *kobj);

+

+static ssize_t mode_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf);

+

+static int cpupmu_create_subfs(struct kobject *parent);

+static void cpupmu_delete_subfs(void);

+static void _cpupmu_start(void *info);

+static void cpupmu_start(void);

+static void _cpupmu_stop(void *info);

+static void cpupmu_stop(void);

+static void cpupmu_polling(unsigned long long stamp, int cpu);

+extern void cpupmu_polling_v2(unsigned long long stamp, int cpu);

+static int cpupmu_print_help(char *buf, int len);

+static int cpupmu_print_header(char *buf, int len);

+static int cpupmu_process_argument(const char *arg, int len);

+

+

+/*******************************************************************************

+*				Globe Variables

+*******************************************************************************/

+static int module_status;

+

+struct cpu_pmu_hw_v2 *met_pmu_hw_v2;

+

+static unsigned int gPMU_CNT[2*MXNR_CPU_V2];

+static unsigned int gMAX_PMU_HW_CNT;

+

+static struct kobject *gKOBJ_CPU;

+static struct met_pmu_v2 *gMET_PMU[2*MXNR_CPU_V2];

+

+static struct kobj_attribute mode_attr = __ATTR(mode, 0444, mode_show, NULL);

+

+static const char cache_line_header[] =

+	"met-info [000] 0.0: met_cpu_cache_line_size: %d\n";

+static const char header[] =

+	"met-info [000] 0.0: met_cpu_header_v2: ";

+static const char help[] =

+	"  --cpu-pmu=CORE_ID:EVENT	     select CPU-PMU events. in %s,\n"

+	"				      you can enable at most \"%d general purpose events\"\n"

+	"				      plus \"one special 0xff (CPU_CYCLE) event\"\n";

+

+static DEFINE_PER_CPU(int[CNTMAX], perfCurr);

+static DEFINE_PER_CPU(int[CNTMAX], perfPrev);

+static DEFINE_PER_CPU(struct perf_event * [CNTMAX], pevent);

+static DEFINE_PER_CPU(struct perf_event_attr [CNTMAX], pevent_attr);

+static DEFINE_PER_CPU(int, perfSet);

+static DEFINE_PER_CPU(unsigned int, perf_task_init_done);

+static DEFINE_PER_CPU(unsigned int, perf_cpuid);

+

+static DEFINE_PER_CPU(struct delayed_work, cpu_pmu_dwork);

+static DEFINE_PER_CPU(struct delayed_work *, perf_delayed_work_setup);

+

+struct metdevice met_cpupmu_v2 = {

+	.name = "cpu-pmu",

+	.type = MET_TYPE_PMU,

+	.cpu_related = 1,

+	.create_subfs = cpupmu_create_subfs,

+	.delete_subfs = cpupmu_delete_subfs,

+	.start = cpupmu_start,

+	.stop = cpupmu_stop,

+	.polling_interval = 1,

+	.timed_polling = cpupmu_polling,

+	.print_help = cpupmu_print_help,

+	.print_header = cpupmu_print_header,

+	.process_argument = cpupmu_process_argument

+};

+

+

+/*******************************************************************************

+*				Iplement Start

+*******************************************************************************/

+static inline struct met_pmu_v2 *get_met_pmu_by_cpu_id(const unsigned int cpu)

+{

+	if (cpu < MXNR_CPU_V2)

+		return gMET_PMU[cpu];

+	else

+		return NULL;

+}

+

+

+static inline void set_met_pmu_by_cpu_id(const unsigned int cpu, struct met_pmu_v2 *met_pmu)

+{

+	if (cpu < MXNR_CPU_V2)

+		gMET_PMU[cpu] = met_pmu;

+}

+

+

+static int reset_driver_stat()

+{

+	int i;

+	int cpu;

+	struct met_pmu_v2 *met_pmu;

+

+	met_cpupmu_v2.mode = 0;

+	for_each_possible_cpu(cpu) {

+		met_pmu = get_met_pmu_by_cpu_id(cpu);

+		for (i = 0; i < gMAX_PMU_HW_CNT; i++) {

+			met_pmu[i].mode = MODE_DISABLED;

+			met_pmu[i].event = 0;

+		}

+		gPMU_CNT[cpu] = 0;

+	}

+	module_status = 0;

+	return 0;

+}

+

+

+static struct met_pmu_v2 *lookup_pmu(struct kobject *kobj)

+{

+	int i;

+	int cpu;

+	struct met_pmu_v2 *met_pmu;

+

+	for_each_possible_cpu(cpu) {

+		met_pmu = get_met_pmu_by_cpu_id(cpu);

+		for (i = 0; i < gMAX_PMU_HW_CNT; i++) {

+			if (met_pmu[i].kobj_cpu_pmu == kobj)

+				return &met_pmu[i];

+		}

+	}

+	return NULL;

+}

+

+

+static ssize_t mode_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)

+{

+	struct met_pmu_v2 *p = lookup_pmu(kobj);

+

+	if (p != NULL) {

+		switch (p->mode) {

+		case 0:

+			return snprintf(buf, PAGE_SIZE, "%hhd (disabled)\n", p->mode);

+		case 1:

+			return snprintf(buf, PAGE_SIZE, "%hhd (interrupt)\n", p->mode);

+		case 2:

+			return snprintf(buf, PAGE_SIZE, "%hhd (polling)\n", p->mode);

+		}

+	}

+	return -EINVAL;

+}

+

+

+static int cpupmu_create_subfs(struct kobject *parent)

+{

+	int ret = 0;

+	unsigned int i;

+	unsigned int cpu;

+	char buf[16];

+	struct met_pmu_v2 *met_pmu;

+

+	met_pmu_hw_v2 = cpu_pmu_hw_init_v2();

+	if (met_pmu_hw_v2 == NULL) {

+		PR_BOOTMSG("Failed to init CPU PMU HW!!\n");

+		return -ENODEV;

+	}

+	gMAX_PMU_HW_CNT = met_pmu_hw_v2->max_hw_count;

+

+	gKOBJ_CPU = parent;

+	for_each_possible_cpu(cpu) {

+		met_pmu = kmalloc_array(gMAX_PMU_HW_CNT, sizeof(struct met_pmu_v2), GFP_KERNEL);

+		if (met_pmu != NULL) {

+			memset(met_pmu, 0x0, gMAX_PMU_HW_CNT * sizeof(struct met_pmu_v2));

+			met_pmu_hw_v2->met_pmu[cpu] = met_pmu;

+			set_met_pmu_by_cpu_id(cpu, met_pmu);

+		} else

+			ret = -ENOMEM;

+

+		for (i = 0; i < gMAX_PMU_HW_CNT; i++) {

+			snprintf(buf, sizeof(buf), "CPU-%d-%d", cpu, i);

+			met_pmu[i].kobj_cpu_pmu = kobject_create_and_add(buf, gKOBJ_CPU);

+			if (met_pmu[i].kobj_cpu_pmu) {

+				ret = sysfs_create_file(met_pmu[i].kobj_cpu_pmu, &mode_attr.attr);

+				if (ret != 0) {

+					PR_BOOTMSG("Failed to create mode in sysfs\n");

+					goto out;

+				}

+			}

+		}

+	}

+ out:

+	if (ret != 0) {

+		for_each_possible_cpu(cpu) {

+			met_pmu = get_met_pmu_by_cpu_id(cpu);

+			if (met_pmu != NULL) {

+				kfree(met_pmu);

+				set_met_pmu_by_cpu_id(cpu, NULL);

+			}

+		}

+	}

+	return ret;

+}

+

+

+static void cpupmu_delete_subfs(void)

+{

+	unsigned int i;

+	unsigned int cpu;

+	struct met_pmu_v2 *met_pmu;

+

+	for_each_possible_cpu(cpu) {

+		met_pmu = get_met_pmu_by_cpu_id(cpu);

+		if (met_pmu != NULL) {

+			for (i = 0; i < gMAX_PMU_HW_CNT; i++) {

+				sysfs_remove_file(met_pmu[i].kobj_cpu_pmu, &mode_attr.attr);

+				kobject_del(met_pmu[i].kobj_cpu_pmu);

+				kobject_put(met_pmu[i].kobj_cpu_pmu);

+				met_pmu[i].kobj_cpu_pmu = NULL;

+			}

+			kfree(met_pmu);

+		}

+		set_met_pmu_by_cpu_id(cpu, NULL);

+	}

+

+	if (gKOBJ_CPU != NULL) {

+		gKOBJ_CPU = NULL;

+	}

+

+	met_pmu_hw_v2  = NULL;

+}

+

+

+noinline void mp_cpu_v2(unsigned char cnt, unsigned int *value)

+{

+        if (cnt < MXNR_CPU_V2)

+	        MET_GENERAL_PRINT(MET_TRACE, cnt, value);

+}

+

+

+static void dummy_handler(struct perf_event *event, struct perf_sample_data *data,

+			  struct pt_regs *regs)

+{

+/* Required as perf_event_create_kernel_counter() requires an overflow handler, even though all we do is poll */

+}

+

+

+void perf_cpupmu_polling_v2(unsigned long long stamp, int cpu)

+{

+	int i, count, delta;

+	struct perf_event *ev;

+	unsigned int pmu_value[MXNR_CPU_V2];

+	struct met_pmu_v2 *met_pmu;

+

+	if (per_cpu(perfSet, cpu) == 0)

+		return;

+

+	memset(pmu_value, 0, sizeof(pmu_value));

+	count = 0;

+	met_pmu = get_met_pmu_by_cpu_id(cpu);

+	for (i = 0; i < gMAX_PMU_HW_CNT; i++) {

+		if (met_pmu[i].mode == 0)

+			continue;

+

+		ev = per_cpu(pevent, cpu)[i];

+		if ((ev != NULL) && (ev->state == PERF_EVENT_STATE_ACTIVE)) {

+			if (per_cpu(perfPrev, cpu)[i] == 0) {

+				per_cpu(perfPrev, cpu)[i] = met_perf_event_read_local(ev);

+				continue;

+			}

+			per_cpu(perfCurr, cpu)[i] = met_perf_event_read_local(ev);

+			delta = per_cpu(perfCurr, cpu)[i] - per_cpu(perfPrev, cpu)[i];

+			per_cpu(perfPrev, cpu)[i] = per_cpu(perfCurr, cpu)[i];

+			if (delta < 0)

+				delta *= -1;

+			pmu_value[i] = delta;

+			count++;

+		}

+	}

+

+	if (count == gPMU_CNT[cpu])

+		mp_cpu_v2(count, pmu_value);

+}

+

+

+static int perf_thread_set_perf_events_v2(unsigned int cpu)

+{

+	int i, size;

+	struct perf_event *ev;

+	struct met_pmu_v2 *met_pmu;

+

+	size = sizeof(struct perf_event_attr);

+	if (per_cpu(perfSet, cpu) == 0) {

+		met_pmu = get_met_pmu_by_cpu_id(cpu);

+		for (i = 0; i < gMAX_PMU_HW_CNT; i++) {

+			per_cpu(pevent, cpu)[i] = NULL;

+			if (!met_pmu[i].mode) {/* Skip disabled counters */

+				continue;

+			}

+			per_cpu(perfPrev, cpu)[i] = 0;

+			per_cpu(perfCurr, cpu)[i] = 0;

+			memset(&per_cpu(pevent_attr, cpu)[i], 0, size);

+			per_cpu(pevent_attr, cpu)[i].config = met_pmu[i].event;

+			per_cpu(pevent_attr, cpu)[i].type = PERF_TYPE_RAW;

+			per_cpu(pevent_attr, cpu)[i].size = size;

+			per_cpu(pevent_attr, cpu)[i].sample_period = 0;

+			per_cpu(pevent_attr, cpu)[i].pinned = 1;

+			if (met_pmu[i].event == 0xff) {

+				per_cpu(pevent_attr, cpu)[i].type = PERF_TYPE_HARDWARE;

+				per_cpu(pevent_attr, cpu)[i].config = PERF_COUNT_HW_CPU_CYCLES;

+			}

+

+			per_cpu(pevent, cpu)[i] =

+			    perf_event_create_kernel_counter(&per_cpu(pevent_attr, cpu)[i], cpu, NULL,

+				     dummy_handler, NULL);

+			if (IS_ERR(per_cpu(pevent, cpu)[i])) {

+				per_cpu(pevent, cpu)[i] = NULL;

+				PR_BOOTMSG("CPU=%d, %s:%d\n", cpu, __FUNCTION__, __LINE__);

+				continue;

+			}

+

+			if (per_cpu(pevent, cpu)[i]->state != PERF_EVENT_STATE_ACTIVE) {

+				perf_event_release_kernel(per_cpu(pevent, cpu)[i]);

+				per_cpu(pevent, cpu)[i] = NULL;

+				PR_BOOTMSG("CPU=%d, %s:%d\n", cpu, __FUNCTION__, __LINE__);

+				continue;

+			}

+

+			ev = per_cpu(pevent, cpu)[i];

+			if (ev != NULL) {

+				perf_event_enable(ev);

+			}

+		} /* for all PMU counter */

+		per_cpu(perfSet, cpu) = 1;

+	} /* for perfSet */

+	return 0;

+}

+

+

+static void perf_thread_setup_v2(struct work_struct *work)

+{

+	unsigned int cpu;

+	struct delayed_work *dwork = to_delayed_work(work);

+

+	cpu = dwork->cpu;

+	if (per_cpu(perf_task_init_done, cpu) == 0) {

+		per_cpu(perf_task_init_done, cpu) = 1;

+		perf_thread_set_perf_events_v2(cpu);

+	}

+

+	return ;

+}

+

+

+void met_perf_cpupmu_online_v2(unsigned int cpu)

+{

+	if (met_cpupmu_v2.mode == 0) {

+		PR_BOOTMSG("CPU=%d, %s:%d\n", cpu, __FUNCTION__, __LINE__);

+		return;

+	}

+

+	per_cpu(perf_cpuid, cpu) = cpu;

+	if (per_cpu(perf_delayed_work_setup, cpu) == NULL) {

+		struct delayed_work *dwork;

+

+		dwork = &per_cpu(cpu_pmu_dwork, cpu);

+		dwork->cpu = cpu;

+		INIT_DELAYED_WORK(dwork, perf_thread_setup_v2);

+		schedule_delayed_work(dwork, 0);

+		per_cpu(perf_delayed_work_setup, cpu) = dwork;

+	}

+}

+

+

+void met_perf_cpupmu_down_v2(void *data)

+{

+	unsigned int cpu;

+	unsigned int i;

+	struct perf_event *ev;

+	struct met_pmu_v2 *met_pmu;

+

+	cpu = *((unsigned int *)data);

+	if (met_cpupmu_v2.mode == 0)

+		return;

+	if (per_cpu(perfSet, cpu) == 0)

+		return;

+

+	met_pmu = get_met_pmu_by_cpu_id(cpu);

+	per_cpu(perfSet, cpu) = 0;

+	for (i = 0; i < gMAX_PMU_HW_CNT; i++) {

+		if (!met_pmu[i].mode)

+			continue;

+		ev = per_cpu(pevent, cpu)[i];

+		if ((ev != NULL) && (ev->state == PERF_EVENT_STATE_ACTIVE)) {

+			perf_event_disable(ev);

+			perf_event_release_kernel(ev);

+		}

+	}

+	per_cpu(perf_task_init_done, cpu) = 0;

+	per_cpu(perf_delayed_work_setup, cpu) = NULL;

+}

+

+

+void met_perf_cpupmu_start_v2(void)

+{

+	unsigned int cpu;

+

+	for_each_online_cpu(cpu) {

+		met_perf_cpupmu_online_v2(cpu);

+	}

+}

+

+

+void met_perf_cpupmu_stop_v2(void)

+{

+	unsigned int cpu;

+

+	for_each_online_cpu(cpu) {

+		per_cpu(perf_cpuid, cpu) = cpu;

+		met_perf_cpupmu_down_v2((void *)&per_cpu(perf_cpuid, cpu));

+	}

+}

+

+

+static void cpupmu_polling(unsigned long long stamp, int cpu)

+{

+	int count;

+	struct met_pmu_v2 *met_pmu;

+	unsigned int pmu_value[MXNR_CPU_V2];

+

+	met_pmu = get_met_pmu_by_cpu_id(cpu);

+	if (met_cpu_pmu_method == 0) {

+		count = met_pmu_hw_v2->polling(met_pmu, gMAX_PMU_HW_CNT, pmu_value);

+		mp_cpu_v2(count, pmu_value);

+	} else

+		perf_cpupmu_polling_v2(stamp, cpu);

+}

+

+

+void cpupmu_polling_v2(unsigned long long stamp, int cpu)

+{

+	cpupmu_polling(stamp, cpu);

+}

+

+

+static void _cpupmu_start(void *info)

+{

+	unsigned int *cpu = (unsigned int *)info;

+	struct met_pmu_v2 *met_pmu;

+

+	met_pmu = get_met_pmu_by_cpu_id(*cpu);

+	met_pmu_hw_v2->start(met_pmu, gMAX_PMU_HW_CNT);

+}

+

+static void cpupmu_start(void)

+{

+	if (module_status == 1) {

+		PR_BOOTMSG("%s:%d\n", __FUNCTION__, __LINE__);

+		return;

+	}

+

+	if (met_cpu_pmu_method == 0) {

+		int this_cpu = smp_processor_id();

+		int cpu;

+

+		for_each_possible_cpu(cpu) {

+			if (cpu == this_cpu)

+				_cpupmu_start(&cpu);

+			else

+				met_smp_call_function_single_symbol(cpu, _cpupmu_start, &cpu, 1);

+		}

+	}

+	module_status = 1;

+}

+

+static void _cpupmu_stop(void *info)

+{

+	(void)info;

+

+	met_pmu_hw_v2->stop(gMAX_PMU_HW_CNT);

+}

+

+static void cpupmu_stop(void)

+{

+	if (module_status == 0) {

+		PR_BOOTMSG("%s:%d\n", __FUNCTION__, __LINE__);

+		return;

+	}

+

+	if (met_cpu_pmu_method == 0) {

+		int this_cpu = smp_processor_id();

+		int cpu;

+

+		for_each_possible_cpu(cpu) {

+			if (cpu == this_cpu)

+				_cpupmu_stop(&cpu);

+			else

+				met_smp_call_function_single_symbol(cpu, _cpupmu_stop, &cpu, 1);

+		}

+	}

+	module_status = 0;

+}

+

+static int cpupmu_print_help(char *buf, int len)

+{

+	return snprintf(buf, PAGE_SIZE, help, met_pmu_hw_v2->name, gMAX_PMU_HW_CNT - 1);

+}

+

+static int cpupmu_print_header(char *buf, int len)

+{

+	int i;

+	int ret = 0;

+	int pmu_cnt = 0;

+	char name[32];

+	unsigned int cpu;

+	struct met_pmu_v2 *met_pmu;

+

+	/*append CPU PMU access method*/

+	if (met_cpu_pmu_method == 0)

+		ret += snprintf(buf + ret, PAGE_SIZE,

+			"met-info [000] 0.0: CPU_PMU_method: PMU registers\n");

+	else

+		ret += snprintf(buf + ret, PAGE_SIZE,

+			"met-info [000] 0.0: CPU_PMU_method: perf APIs\n");

+

+	/*append cache line size*/

+	ret += snprintf(buf + ret, PAGE_SIZE - ret, cache_line_header, cache_line_size());

+	ret += snprintf(buf + ret, PAGE_SIZE - ret, header);

+	for_each_online_cpu(cpu) {

+		int cnt = 0;

+

+		pmu_cnt = gPMU_CNT[cpu];

+		met_pmu = get_met_pmu_by_cpu_id(cpu);

+		for (i = 0; i < pmu_cnt; i++) {

+			if (met_pmu[i].mode == 0)

+				continue;

+

+			if (met_pmu_hw_v2->get_event_desc && 0 == met_pmu_hw_v2->get_event_desc(met_pmu[i].event, name)) {

+				if (cnt == 0) {

+					ret += snprintf(buf + ret, PAGE_SIZE - ret, "CPU-%d=0x%x:%s", cpu, met_pmu[i].event, name);

+					cnt++;

+				} else

+					ret += snprintf(buf + ret, PAGE_SIZE - ret, ",0x%x:%s", met_pmu[i].event, name);

+			}

+			met_pmu[i].mode = 0;

+		}

+		if (cnt > 0 && cpu < MXNR_CPU_V2 - 1)

+			ret += snprintf(buf + ret, PAGE_SIZE - ret, ";");

+	}

+	ret += snprintf(buf + ret, PAGE_SIZE - ret, "\n");

+	met_cpupmu_v2.mode = 0;

+	reset_driver_stat();

+	return ret;

+}

+

+/*

+ * "met-cmd --start --pmu_core_evt=0:0x3,0x16,0x17"

+ */

+static int cpupmu_process_argument(const char *arg, int len)

+{

+	int ret;

+	unsigned int cpu;

+	unsigned int value;

+	unsigned int idx = 0;

+	char *str = NULL;

+	char *token = NULL;

+	struct met_pmu_v2 *met_pmu = NULL;

+

+	if (met_cpu_pmu_method == 0)

+		gMAX_PMU_HW_CNT = met_pmu_hw_v2->max_hw_count;

+	else

+		gMAX_PMU_HW_CNT = perf_num_counters();

+

+	if (gMAX_PMU_HW_CNT == 0) {

+		PR_BOOTMSG("%s:%d\n", __FUNCTION__, __LINE__);

+		goto arg_out;

+	}

+

+	str = kstrdup(arg, GFP_KERNEL);

+	token = strsep(&str, ":");

+	ret = met_parse_num(token, &cpu, strlen(token));

+	if (ret != 0) {

+		PR_BOOTMSG("%s:%d\n", __FUNCTION__, __LINE__);

+		goto arg_out;

+	}

+

+	met_pmu = get_met_pmu_by_cpu_id(cpu);

+	while (token && met_pmu && idx < gMAX_PMU_HW_CNT) {

+		token = strsep(&str, ",\r\n");

+		if (token) {

+			ret = met_parse_num(token, &value, strlen(token));

+			if (ret != 0) {

+				PR_BOOTMSG("%s:%d\n", __FUNCTION__, __LINE__);

+				goto arg_out;

+			}

+			if (value != 0xff) {

+				if (idx >= (gMAX_PMU_HW_CNT - 1)) {

+					PR_BOOTMSG("%s:%d\n", __FUNCTION__, __LINE__);

+					goto arg_out;

+				}

+				met_pmu[idx].mode = MODE_POLLING;

+				met_pmu[idx].event = value;

+				idx++;

+				gPMU_CNT[cpu]++;

+			} else {

+				if (met_cpu_pmu_method == 0) {

+					met_pmu[gMAX_PMU_HW_CNT - 1].mode = MODE_POLLING;

+					met_pmu[gMAX_PMU_HW_CNT - 1].event = 0xff;

+					gPMU_CNT[cpu]++;

+				} else {

+					if (idx > (gMAX_PMU_HW_CNT - 1)) {

+						PR_BOOTMSG("%s:%d\n", __FUNCTION__, __LINE__);

+						goto arg_out;

+					}

+					met_pmu[idx].mode = MODE_POLLING;

+					met_pmu[idx].event = 0xff;

+					idx++;

+					gPMU_CNT[cpu]++;

+				}

+			}

+			if (met_pmu_hw_v2->check_event(met_pmu, gPMU_CNT[cpu], value) < 0) {

+				PR_BOOTMSG("%s:%d\n", __FUNCTION__, __LINE__);

+				goto arg_out;

+			}

+		}

+	}

+	met_cpupmu_v2.mode = 1;

+	module_status = 0;

+	return 0;

+

+arg_out:

+	if (str)

+		kfree(str);

+	reset_driver_stat();

+	return -EINVAL;

+}

diff --git a/src/devtools/met-driver/met_drv/common/cpu_pmu_v2.h b/src/devtools/met-driver/met_drv/common/cpu_pmu_v2.h
new file mode 100644
index 0000000..6af56bb
--- /dev/null
+++ b/src/devtools/met-driver/met_drv/common/cpu_pmu_v2.h
@@ -0,0 +1,85 @@
+/*

+ * Copyright (C) 2018 MediaTek Inc.

+ *

+ * 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.

+ *

+ * This program is distributed in the hope that it will be useful,

+ * but WITHOUT ANY WARRANTY; without even the implied warranty of

+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the

+ * GNU General Public License for more details.

+ */

+

+#ifndef _PMU_V2_H_

+#define _PMU_V2_H_

+

+#include <linux/device.h>

+#include <linux/threads.h> /* NR_CPUS */

+

+#define MODE_DISABLED	0

+#define MODE_INTERRUPT	1

+#define MODE_POLLING	2

+#define MXSIZE_PMU_DESC 32

+#define MXNR_CPU_V2     NR_CPUS

+

+

+/*******************************************************************************

+*                                Type Define

+*******************************************************************************/

+struct met_pmu_v2 {

+	unsigned char mode;

+	unsigned short event;

+	struct kobject *kobj_cpu_pmu;

+        const char *cpu_name;

+};

+

+enum ARM_TYPE_v2 {

+	CORTEX_A53 = 0xD03,

+	CORTEX_A35 = 0xD04,

+	CORTEX_A57 = 0xD07,

+	CORTEX_A72 = 0xD08,

+	CORTEX_A73 = 0xD09,

+	CHIP_UNKNOWN = 0xFFF

+};

+

+struct pmu_desc_v2 {

+	unsigned int event;

+	char name[MXSIZE_PMU_DESC];

+};

+

+struct chip_pmu_v2 {

+	enum ARM_TYPE_v2 type;

+	struct pmu_desc_v2 *desc;

+        unsigned int pmu_count;

+	unsigned int hw_count;

+	const char *cpu_name;

+};

+

+struct cpu_pmu_hw_v2 {

+	const char *name;

+        int max_hw_count;

+	int (*get_event_desc)(int event, char *event_desc);

+	int (*check_event)(struct met_pmu_v2 *pmu, int idx, int event);

+	void (*start)(struct met_pmu_v2 *pmu, int count);

+	void (*stop)(int count);

+	unsigned int (*polling)(struct met_pmu_v2 *pmu, int count, unsigned int *pmu_value);

+	struct met_pmu_v2 *met_pmu[MXNR_CPU_V2];

+        struct chip_pmu_v2 *chip_pmu[MXNR_CPU_V2];

+};

+

+

+extern struct cpu_pmu_hw_v2 *cpu_pmu_hw_v2;

+

+

+/*******************************************************************************

+*                                Fuction Pototypes

+*******************************************************************************/

+extern noinline void mp_cpu_v2(unsigned char cnt, unsigned int *value);

+extern void met_perf_cpupmu_online_v2(unsigned int cpu);

+extern void met_perf_cpupmu_down_v2(void *cpu);

+extern void met_perf_cpupmu_start_v2(void);

+extern void met_perf_cpupmu_stop_v2(void);

+extern void cpupmu_polling_v2(unsigned long long stamp, int cpu);

+

+#endif /* _PMU_V2_H_ */

diff --git a/src/devtools/met-driver/met_drv/common/interface.c b/src/devtools/met-driver/met_drv/common/interface.c
new file mode 100644
index 0000000..f4818f0
--- /dev/null
+++ b/src/devtools/met-driver/met_drv/common/interface.c
@@ -0,0 +1,1370 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/fs.h>
+#include <linux/sched.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/miscdevice.h>
+#include <linux/kallsyms.h>
+#include <linux/syscore_ops.h>
+#include <linux/dma-mapping.h>
+#include "interface.h"
+#include "sampler.h"
+#include "util.h"
+
+#include "ondiemet.h"
+
+#include "met_drv.h"
+#include "met_tag.h"
+#include "met_kernel_symbol.h"
+#include "met_api_tbl.h"
+#include "version.h"
+
+extern int enable_met_backlight_tag(void);
+extern int output_met_backlight_tag(int level);
+
+static int run = -1;
+static int sample_rate = 1000;	/* Default: 1000 Hz */
+static int met_suspend_compensation_mode;
+static int met_suspend_compensation_flag;
+
+/*
+ * met_cpu_pmu_method:
+ *   0: MET pmu driver
+ *   1: perf APIs
+ */
+unsigned int met_cpu_pmu_method = 1;
+
+int met_hrtimer_expire;		/* in ns */
+int met_timer_expire;		/* in jiffies */
+unsigned int ctrl_flags;
+int met_mode;
+EXPORT_SYMBOL(met_mode);
+
+DEFINE_PER_CPU(char[MET_STRBUF_SIZE], met_strbuf_nmi);
+EXPORT_SYMBOL(met_strbuf_nmi);
+
+DEFINE_PER_CPU(char[MET_STRBUF_SIZE], met_strbuf_irq);
+EXPORT_SYMBOL(met_strbuf_irq);
+
+DEFINE_PER_CPU(char[MET_STRBUF_SIZE], met_strbuf_sirq);
+EXPORT_SYMBOL(met_strbuf_sirq);
+
+DEFINE_PER_CPU(char[MET_STRBUF_SIZE], met_strbuf);
+EXPORT_SYMBOL(met_strbuf);
+
+static void calc_timer_value(int rate)
+{
+	sample_rate = rate;
+
+	if (rate == 0) {
+		met_hrtimer_expire = 0;
+		met_timer_expire = 0;
+		return;
+	}
+
+	met_hrtimer_expire = 1000000000 / rate;
+
+	/* Case 1: hrtimer < 1 OS tick, met_timer_expire = 1 OS tick */
+	if (rate > HZ)
+		met_timer_expire = 1;
+	/* Case 2: hrtimer > 1 OS tick, met_timer_expire is hrtimer + 1 OS tick */
+	else
+		met_timer_expire = (HZ / rate) + 1;
+
+	/* pr_debug("JBK HZ=%d, met_hrtimer_expire=%d ns, met_timer_expire=%d ticks\n", */
+	/* HZ, met_hrtimer_expire, met_timer_expire); */
+}
+
+int met_parse_num(const char *str, unsigned int *value, int len)
+{
+	int ret;
+
+	if (len <= 0)
+		return -1;
+
+	if ((len > 2) &&
+		((str[0] == '0') &&
+		((str[1] == 'x') || (str[1] == 'X')))) {
+		ret = kstrtoint(str, 16, value);
+	} else {
+		ret = kstrtoint(str, 10, value);
+	}
+
+	if (ret != 0)
+		return -1;
+
+	return 0;
+}
+
+void met_set_suspend_notify(int flag)
+{
+	if (met_suspend_compensation_mode == 1)
+		met_suspend_compensation_flag = flag;
+	else
+		met_suspend_compensation_flag = 0;
+}
+
+LIST_HEAD(met_list);
+static struct kobject *kobj_misc;
+static struct kobject *kobj_pmu;
+static struct kobject *kobj_bus;
+
+static ssize_t ver_show(struct device *dev, struct device_attribute *attr, char *buf);
+static DEVICE_ATTR(ver, 0444, ver_show, NULL);
+
+static ssize_t plf_show(struct device *dev, struct device_attribute *attr, char *buf);
+static DEVICE_ATTR(plf, 0444, plf_show, NULL);
+
+static ssize_t core_topology_show(struct device *dev, struct device_attribute *attr, char *buf);
+static DEVICE_ATTR(core_topology, 0444, core_topology_show, NULL);
+
+static ssize_t devices_show(struct device *dev, struct device_attribute *attr, char *buf);
+static DEVICE_ATTR(devices, 0444, devices_show, NULL);
+
+static ssize_t ctrl_show(struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t ctrl_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			  size_t count);
+static DEVICE_ATTR(ctrl, 0664, ctrl_show, ctrl_store);
+
+static ssize_t spr_show(struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t spr_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			 size_t count);
+static DEVICE_ATTR(sample_rate, 0664, spr_show, spr_store);
+
+static ssize_t run_show(struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t run_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			 size_t count);
+static DEVICE_ATTR(run, 0664, run_show, run_store);
+
+static ssize_t ksym_show(struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t ksym_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			 size_t count);
+static DEVICE_ATTR(ksym, 0664, ksym_show, ksym_store);
+
+static ssize_t cpu_pmu_method_show(struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t cpu_pmu_method_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			  size_t count);
+static DEVICE_ATTR(cpu_pmu_method, 0664, cpu_pmu_method_show, cpu_pmu_method_store);
+
+#ifdef PR_CPU_NOTIFY
+int met_cpu_notify;
+static ssize_t cpu_notify_show(struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t cpu_notify_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			  size_t count);
+static DEVICE_ATTR(cpu_notify, 0664, cpu_notify_show, cpu_notify_store);
+#endif
+
+#ifdef CONFIG_CPU_FREQ
+static ssize_t dvfs_show(struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t dvfs_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			  size_t count);
+static DEVICE_ATTR(dvfs, 0664, dvfs_show, dvfs_store);
+#endif
+
+static ssize_t suspend_compensation_enable_show(struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t suspend_compensation_enable_store(struct device *dev, struct device_attribute *attr,
+							const char *buf, size_t count);
+static DEVICE_ATTR(suspend_compensation_enable, 0664, suspend_compensation_enable_show,
+			suspend_compensation_enable_store);
+
+static ssize_t suspend_compensation_flag_show(struct device *dev, struct device_attribute *attr, char *buf);
+static DEVICE_ATTR(suspend_compensation_flag, 0444, suspend_compensation_flag_show, NULL);
+
+static ssize_t ipi_test_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			 size_t count);
+static DEVICE_ATTR(ipi_test, 0220, NULL, ipi_test_store);
+
+static const struct file_operations met_file_ops = {
+	.owner = THIS_MODULE
+};
+
+struct miscdevice met_device = {
+	.minor = MISC_DYNAMIC_MINOR,
+	.name = "met",
+	.mode = 0664,
+	.fops = &met_file_ops
+};
+EXPORT_SYMBOL(met_device);
+
+static int met_run(void)
+{
+	sampler_start();
+#ifdef MET_USER_EVENT_SUPPORT
+	bltab.flag &= (~MET_CLASS_ALL);
+#endif
+	ondiemet_start();
+	return 0;
+}
+
+static void met_stop(void)
+{
+#ifdef MET_USER_EVENT_SUPPORT
+	bltab.flag |= MET_CLASS_ALL;
+#endif
+	sampler_stop();
+	/* the met.ko will be use by script "cat ...", release it */
+	if ((ondiemet_module[ONDIEMET_SSPM] == 0) || (sspm_buffer_size == -1))
+		ondiemet_log_manager_stop();
+	ondiemet_stop();
+	ondiemet_extract();
+}
+
+static ssize_t ver_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int i;
+
+	mutex_lock(&dev->mutex);
+	i = snprintf(buf, PAGE_SIZE, "%s\n", MET_BACKEND_VERSION);
+	mutex_unlock(&dev->mutex);
+	return i;
+}
+
+static ssize_t devices_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int len, total_len = 0;
+	struct metdevice *c = NULL;
+
+	mutex_lock(&dev->mutex);
+	list_for_each_entry(c, &met_list, list) {
+		len = 0;
+		if (c->type == MET_TYPE_PMU)
+			len = snprintf(buf, PAGE_SIZE - total_len, "pmu/%s:0\n", c->name);
+		else if (c->type == MET_TYPE_BUS)
+			len = snprintf(buf, PAGE_SIZE - total_len, "bus/%s:0\n", c->name);
+		else if (c->type == MET_TYPE_MISC)
+			len = snprintf(buf, PAGE_SIZE - total_len, "misc/%s:0\n", c->name);
+
+		if (c->ondiemet_mode == 0) {
+			if (c->process_argument)
+				buf[len - 2]++;
+		} else if (c->ondiemet_mode == 1) {
+			if (c->ondiemet_process_argument)
+				buf[len - 2]++;
+		} else if (c->ondiemet_mode == 2) {
+			if (c->process_argument)
+				buf[len - 2]++;
+			if (c->ondiemet_process_argument)
+				buf[len - 2]++;
+		}
+
+		buf += len;
+		total_len += len;
+	}
+
+	mutex_unlock(&dev->mutex);
+	return total_len;
+}
+
+static char met_platform[16] = "none";
+static ssize_t plf_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int i;
+
+	mutex_lock(&dev->mutex);
+	i = snprintf(buf, PAGE_SIZE, "%s\n", met_platform);
+	mutex_unlock(&dev->mutex);
+	return i;
+}
+
+static char met_topology[64] = "none";
+static ssize_t core_topology_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int i;
+
+	mutex_lock(&dev->mutex);
+	i = snprintf(buf, PAGE_SIZE, "%s\n", met_topology);
+	mutex_unlock(&dev->mutex);
+	return i;
+}
+
+static ssize_t ctrl_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%d\n", ctrl_flags);
+}
+
+static ssize_t ctrl_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			  size_t count)
+{
+	unsigned int value = 0;
+
+	if (met_parse_num(buf, &value, count) < 0)
+		return -EINVAL;
+
+	ctrl_flags = value;
+	return count;
+}
+
+static ssize_t cpu_pmu_method_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%d\n", met_cpu_pmu_method);
+}
+
+static ssize_t cpu_pmu_method_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			  size_t count)
+{
+	unsigned int value;
+
+	if (met_parse_num(buf, &value, count) < 0)
+		return -EINVAL;
+
+	met_cpu_pmu_method = value;
+	return count;
+}
+
+
+static void _test_trace_ipi_raise(void *info)
+{
+	unsigned int *cpu = (unsigned int *)info;
+	void (*arch_send_call_function_single_ipi_sym)(int cpu) = NULL;
+
+	arch_send_call_function_single_ipi_sym = (void *)symbol_get(met_arch_send_call_function_single_ipi);
+	if (arch_send_call_function_single_ipi_sym)
+		arch_send_call_function_single_ipi_sym(*cpu);
+}
+
+
+static ssize_t ipi_test_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			 size_t count)
+{
+	int this_cpu = smp_processor_id();
+	unsigned int cpu = 0;
+	unsigned int value;
+
+	if (met_parse_num(buf, &value, count) < 0)
+		return -EINVAL;
+
+	cpu = value;
+	if (cpu == this_cpu)
+		_test_trace_ipi_raise(&cpu);
+	else
+		met_smp_call_function_single_symbol(cpu, _test_trace_ipi_raise, &cpu, 1);
+
+	return count;
+}
+
+
+#if	defined(MET_BOOT_MSG)
+char met_boot_msg_tmp[256];
+char met_boot_msg[PAGE_SIZE];
+int met_boot_msg_idx;
+
+int pr_bootmsg(int str_len, char *str)
+{
+	if (met_boot_msg_idx+str_len+1 > PAGE_SIZE)
+		return -1;
+	memcpy(met_boot_msg+met_boot_msg_idx, str, str_len);
+	met_boot_msg_idx += str_len;
+	return 0;
+}
+
+static ssize_t bootmsg_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int	i;
+
+	mutex_lock(&dev->mutex);
+	i = snprintf(buf, PAGE_SIZE, "%s\n", met_boot_msg);
+	mutex_unlock(&dev->mutex);
+	return i;
+}
+
+static DEVICE_ATTR_RO(bootmsg);
+EXPORT_SYMBOL(met_boot_msg_tmp);
+EXPORT_SYMBOL(pr_bootmsg);
+#endif
+
+static ssize_t spr_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int i;
+
+	mutex_lock(&dev->mutex);
+	i = snprintf(buf, PAGE_SIZE, "%d\n", sample_rate);
+	mutex_unlock(&dev->mutex);
+	return i;
+}
+
+static ssize_t spr_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			 size_t count)
+{
+	int value;
+	struct metdevice *c = NULL;
+
+	mutex_lock(&dev->mutex);
+
+	if ((run == 1) || (count == 0) || (buf == NULL)) {
+		mutex_unlock(&dev->mutex);
+		return -EINVAL;
+	}
+	if (kstrtoint(buf, 0, &value) != 0) {
+		mutex_unlock(&dev->mutex);
+		return -EINVAL;
+	}
+
+	if ((value < 0) || (value > 10000)) {
+		mutex_unlock(&dev->mutex);
+		return -EINVAL;
+	}
+
+	calc_timer_value(value);
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->polling_interval > 0)
+			c->polling_count_reload = ((c->polling_interval * sample_rate) - 1) / 1000;
+		else
+			c->polling_count_reload = 0;
+	}
+
+	mutex_unlock(&dev->mutex);
+
+	return count;
+}
+
+static ssize_t run_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int i;
+
+	mutex_lock(&dev->mutex);
+	i = snprintf(buf, PAGE_SIZE, "%d\n", run);
+	mutex_unlock(&dev->mutex);
+	return i;
+}
+
+static ssize_t run_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			 size_t count)
+{
+	int value;
+
+	mutex_lock(&dev->mutex);
+
+	if ((count == 0) || (buf == NULL)) {
+		mutex_unlock(&dev->mutex);
+		return -EINVAL;
+	}
+	if (kstrtoint(buf, 0, &value) != 0) {
+		mutex_unlock(&dev->mutex);
+		return -EINVAL;
+	}
+
+	switch (value) {
+	case 1:
+		if (run != 1) {
+			run = 1;
+			met_run();
+		}
+		break;
+	case 0:
+		if (run != 0) {
+			if (run == 1) {
+				met_stop();
+#ifdef MET_USER_EVENT_SUPPORT
+#ifdef CONFIG_MET_MODULE
+				met_save_dump_buffer_real("/data/trace.dump");
+#else
+				met_save_dump_buffer("/data/trace.dump");
+#endif
+#endif
+				run = 0;
+			} else
+				/* run == -1 */
+				run = 0;
+		}
+		break;
+	case -1:
+		if (run != -1) {
+			if (run == 1)
+				met_stop();
+
+			run = -1;
+		}
+		break;
+	default:
+		mutex_unlock(&dev->mutex);
+		return -EINVAL;
+	}
+
+	mutex_unlock(&dev->mutex);
+
+	return count;
+}
+
+static unsigned int met_ksym_addr;
+static char met_func_name[512];
+static ssize_t ksym_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int i;
+	int len = 0;
+	int idx = 0;
+
+	mutex_lock(&dev->mutex);
+	if (met_ksym_addr != 0)
+		len = sprint_symbol_no_offset(met_func_name, met_ksym_addr);
+	if (len != 0) {
+		for (idx = 0; idx < 512; idx++)
+			if (met_func_name[idx] == ' ')
+				met_func_name[idx] = '\0';
+		i = snprintf(buf, PAGE_SIZE, "%s\n", met_func_name);
+	} else
+		i = snprintf(buf, PAGE_SIZE, "ksymlookup fail(%x)\n", met_ksym_addr);
+
+	mutex_unlock(&dev->mutex);
+	return i;
+}
+
+static ssize_t ksym_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			 size_t count)
+{
+	mutex_lock(&dev->mutex);
+
+	if ((count == 0) || (buf == NULL)) {
+		mutex_unlock(&dev->mutex);
+		return -EINVAL;
+	}
+	if (kstrtoint(buf, 16, &met_ksym_addr) != 0) {
+		mutex_unlock(&dev->mutex);
+		return -EINVAL;
+	}
+
+	mutex_unlock(&dev->mutex);
+
+	return count;
+}
+
+#if	defined(PR_CPU_NOTIFY)
+static ssize_t cpu_notify_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int i;
+
+	i = snprintf(buf, PAGE_SIZE, "%d\n", met_cpu_notify);
+	return i;
+}
+
+static ssize_t cpu_notify_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			  size_t count)
+{
+	if ((count == 0) || (buf == NULL))
+		return -EINVAL;
+
+	if (kstrtoint(buf, 0, &met_cpu_notify) != 0)
+		return -EINVAL;
+
+	return count;
+}
+#endif
+
+#ifdef CONFIG_CPU_FREQ
+static ssize_t dvfs_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int i;
+
+	i = snprintf(buf, PAGE_SIZE, "%d\n", 0);
+	return i;
+}
+
+static ssize_t dvfs_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			  size_t count)
+{
+	return count;
+}
+#endif
+
+static ssize_t suspend_compensation_enable_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int ret;
+
+	ret = snprintf(buf, PAGE_SIZE, "%d\n", met_suspend_compensation_mode);
+
+	return ret;
+}
+
+static ssize_t suspend_compensation_enable_store(struct device *dev, struct device_attribute *attr,
+			const char *buf, size_t count)
+{
+	int value;
+
+	if ((count == 0) || (buf == NULL))
+		return -EINVAL;
+
+	if (kstrtoint(buf, 0, &value) != 0)
+		return -EINVAL;
+
+	if (value < 0)
+		return -EINVAL;
+
+	met_suspend_compensation_mode = value;
+
+	return count;
+}
+
+static ssize_t suspend_compensation_flag_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int ret;
+
+	ret = snprintf(buf, PAGE_SIZE, "%d\n", met_suspend_compensation_flag);
+
+	return ret;
+}
+
+static ssize_t hash_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	return 0;
+}
+
+static ssize_t hash_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			 size_t count)
+{
+	return 0;
+}
+
+static DEVICE_ATTR(hash, 0664, hash_show, hash_store);
+
+static ssize_t mode_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	struct metdevice *c = NULL;
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->kobj == kobj)
+			break;
+	}
+	if (c == NULL)
+		return -ENOENT;
+
+	return snprintf(buf, PAGE_SIZE, "%d\n", c->mode);
+}
+
+static ssize_t mode_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf,
+			  size_t n)
+{
+	struct metdevice *c = NULL;
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->kobj == kobj)
+			break;
+	}
+	if (c == NULL)
+		return -ENOENT;
+
+	if (kstrtoint(buf, 0, &(c->mode)) != 0)
+		return -EINVAL;
+
+	return n;
+}
+
+static struct kobj_attribute mode_attr = __ATTR(mode, 0664, mode_show, mode_store);
+
+static ssize_t ondiemet_mode_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	struct metdevice *c = NULL;
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->kobj == kobj)
+			break;
+	}
+	if (c == NULL)
+		return -ENOENT;
+
+	return snprintf(buf, PAGE_SIZE, "%d\n", c->ondiemet_mode);
+}
+
+static ssize_t ondiemet_mode_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf,
+			  size_t n)
+{
+	struct metdevice *c = NULL;
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->kobj == kobj)
+			break;
+	}
+	if (c == NULL)
+		return -ENOENT;
+
+	if (kstrtoint(buf, 0, &(c->ondiemet_mode)) != 0)
+		return -EINVAL;
+
+	return n;
+}
+
+static struct kobj_attribute ondiemet_mode_attr = __ATTR(ondiemet_mode, 0664, ondiemet_mode_show, ondiemet_mode_store);
+
+static ssize_t polling_interval_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	int interval = 1;
+	struct metdevice *c = NULL;
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->kobj == kobj)
+			break;
+	}
+	if (c == NULL)
+		return -ENOENT;
+
+	if (c->polling_interval)
+		interval = c->polling_interval;
+
+	return snprintf(buf, PAGE_SIZE, "%d\n", interval);
+}
+
+static ssize_t polling_interval_store(struct kobject *kobj, struct kobj_attribute *attr,
+				      const char *buf, size_t n)
+{
+	struct metdevice *c = NULL;
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->kobj == kobj)
+			break;
+	}
+	if (c == NULL)
+		return -ENOENT;
+
+	if (kstrtoint(buf, 0, &(c->polling_interval)) != 0)
+		return -EINVAL;
+
+	if (c->polling_interval > 0)
+		c->polling_count_reload = ((c->polling_interval * sample_rate) - 1) / 1000;
+	else
+		c->polling_count_reload = 0;
+
+	return n;
+}
+
+static struct kobj_attribute polling_interval_attr =
+__ATTR(polling_ms, 0664, polling_interval_show, polling_interval_store);
+
+static ssize_t header_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	struct metdevice *c = NULL;
+	ssize_t count = 0;
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->kobj == kobj)
+			break;
+	}
+	if (c == NULL)
+		return -ENOENT;
+
+	if (c->ondiemet_mode == 0) {
+		if ((c->mode) && (c->print_header))
+			return c->print_header(buf, PAGE_SIZE);
+	} else if (c->ondiemet_mode == 1) {
+		if ((c->mode) && (c->ondiemet_print_header))
+			return c->ondiemet_print_header(buf, PAGE_SIZE);
+	} else if (c->ondiemet_mode == 2) {
+		if ((c->mode) && (c->print_header))
+			count = c->print_header(buf, PAGE_SIZE);
+		if (count < PAGE_SIZE) {
+			if ((c->mode) && (c->ondiemet_print_header))
+				count += c->ondiemet_print_header(buf+count, PAGE_SIZE - count);
+		}
+		return count;
+	}
+
+	return 0;
+}
+
+static struct kobj_attribute header_attr = __ATTR(header, 0444, header_show, NULL);
+
+static ssize_t help_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	struct metdevice *c = NULL;
+	ssize_t count = 0;
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->kobj == kobj)
+			break;
+	}
+	if (c == NULL)
+		return -ENOENT;
+
+	if (c->ondiemet_mode == 0) {
+		if (c->print_help)
+			return c->print_help(buf, PAGE_SIZE);
+	} else if (c->ondiemet_mode == 1) {
+		if (c->ondiemet_print_help)
+			return c->ondiemet_print_help(buf, PAGE_SIZE);
+	} else if (c->ondiemet_mode == 2) {
+		if (c->print_help)
+			count = c->print_help(buf, PAGE_SIZE);
+		if (count < PAGE_SIZE) {
+			if (c->ondiemet_print_help)
+				count += c->ondiemet_print_help(buf+count, PAGE_SIZE - count);
+		}
+		return count;
+	}
+
+	return 0;
+}
+
+static struct kobj_attribute help_attr = __ATTR(help, 0444, help_show, NULL);
+
+static int argu_status = -1;
+static ssize_t argu_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf,
+			  size_t n)
+{
+	int ret = 0;
+	struct metdevice *c = NULL;
+
+	argu_status = -1;
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->kobj == kobj)
+			break;
+	}
+	if (c == NULL)
+		return -ENOENT;
+
+	if (c->ondiemet_mode == 0) {
+		if (c->process_argument)
+			ret = c->process_argument(buf, (int)n);
+	} else if (c->ondiemet_mode == 1) {
+		if (c->ondiemet_process_argument)
+			ret = c->ondiemet_process_argument(buf, (int)n);
+	} else if (c->ondiemet_mode == 2) {
+		if (c->process_argument)
+			ret = c->process_argument(buf, (int)n);
+		if (c->ondiemet_process_argument)
+			ret = c->ondiemet_process_argument(buf, (int)n);
+	}
+
+	if (ret != 0)
+		return -EINVAL;
+
+	argu_status = 0;
+	return n;
+}
+
+static ssize_t argu_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%d\n", argu_status);
+}
+
+static struct kobj_attribute argu_attr = __ATTR(argu, 0664, argu_show, argu_store);
+
+static ssize_t reset_store(struct kobject *kobj,
+			struct kobj_attribute *attr,
+			const char *buf,
+			size_t n)
+{
+	int ret = 0;
+	struct metdevice *c = NULL;
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->kobj == kobj)
+			break;
+	}
+	if (c == NULL)
+		return -ENOENT;
+
+	if (c->ondiemet_mode == 0) {
+		if (c->reset)
+			ret = c->reset();
+		else
+			c->mode = 0;
+	} else if (c->ondiemet_mode == 1) {
+		if (c->ondiemet_reset)
+			ret = c->ondiemet_reset();
+	} else if (c->ondiemet_mode == 2) {
+		if (c->reset)
+			ret = c->reset();
+		else
+			c->mode = 0;
+		if (c->ondiemet_reset)
+			ret = c->ondiemet_reset();
+	}
+
+	if (ret != 0)
+		return -EINVAL;
+
+	return n;
+}
+
+static struct kobj_attribute reset_attr = __ATTR(reset, 0220, NULL, reset_store);
+
+static ssize_t header_read_again_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	struct metdevice *c = NULL;
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->kobj == kobj)
+			break;
+	}
+	if (c == NULL)
+		return -ENOENT;
+
+	return snprintf(buf, PAGE_SIZE, "%d\n", c->header_read_again);
+}
+
+static struct kobj_attribute header_read_again_attr = __ATTR(header_read_again, 0664, header_read_again_show, NULL);
+
+
+int met_register(struct metdevice *met)
+{
+	int ret, cpu;
+	struct metdevice *c;
+
+	list_for_each_entry(c, &met_list, list) {
+		if (!strcmp(c->name, met->name))
+			return -EEXIST;
+	}
+
+	PR_BOOTMSG("met_register %s ...\n", met->name);
+
+	INIT_LIST_HEAD(&met->list);
+
+	/* Allocate timer count for per CPU */
+	met->polling_count = alloc_percpu(int);
+	if (met->polling_count == NULL)
+		return -EINVAL;
+
+	for_each_possible_cpu(cpu)
+		*(per_cpu_ptr(met->polling_count, cpu)) = 0;
+
+	if (met->polling_interval > 0) {
+		ret = ((met->polling_interval * sample_rate) - 1) / 1000;
+		met->polling_count_reload = ret;
+	} else
+		met->polling_count_reload = 0;
+
+	met->kobj = NULL;
+
+	if (met->type == MET_TYPE_BUS)
+		met->kobj = kobject_create_and_add(met->name, kobj_bus);
+	else if (met->type == MET_TYPE_PMU)
+		met->kobj = kobject_create_and_add(met->name, kobj_pmu);
+	else if (met->type == MET_TYPE_MISC)
+		met->kobj = kobject_create_and_add(met->name, kobj_misc);
+	else {
+		ret = -EINVAL;
+		goto err_out;
+	}
+
+	if (met->kobj == NULL) {
+		ret = -EINVAL;
+		goto err_out;
+	}
+
+	if (met->create_subfs) {
+		ret = met->create_subfs(met->kobj);
+		if (ret)
+			goto err_out;
+	}
+
+	ret = sysfs_create_file(met->kobj, &mode_attr.attr);
+	if (ret)
+		goto err_out;
+
+
+	ret = sysfs_create_file(met->kobj, &ondiemet_mode_attr.attr);
+	if (ret)
+		goto err_out;
+
+	ret = sysfs_create_file(met->kobj, &polling_interval_attr.attr);
+	if (ret)
+		goto err_out;
+
+	ret = sysfs_create_file(met->kobj, &header_read_again_attr.attr);
+	if (ret)
+		goto err_out;
+
+	if (met->print_header || met->ondiemet_print_header) {
+		ret = sysfs_create_file(met->kobj, &header_attr.attr);
+		if (ret)
+			goto err_out;
+	}
+
+	if (met->print_help || met->ondiemet_print_help) {
+		ret = sysfs_create_file(met->kobj, &help_attr.attr);
+		if (ret)
+			goto err_out;
+	}
+
+	if (met->process_argument || met->ondiemet_process_argument) {
+		ret = sysfs_create_file(met->kobj, &argu_attr.attr);
+		if (ret)
+			goto err_out;
+	}
+
+	if (met->reset) {
+		ret = sysfs_create_file(met->kobj, &reset_attr.attr);
+		if (ret)
+			goto err_out;
+	}
+
+	spin_lock_init(&met->my_lock);
+
+	list_add(&met->list, &met_list);
+	return 0;
+
+ err_out:
+
+	if (met->polling_count)
+		free_percpu(met->polling_count);
+
+	if (met->kobj) {
+		kobject_del(met->kobj);
+		kobject_put(met->kobj);
+		met->kobj = NULL;
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL(met_register);
+
+int met_deregister(struct metdevice *met)
+{
+	struct metdevice *c = NULL;
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c == met)
+			break;
+	}
+	if (c != met)
+		return -ENOENT;
+
+	if (met->print_header || met->ondiemet_print_header)
+		sysfs_remove_file(met->kobj, &header_attr.attr);
+
+	if (met->print_help || met->ondiemet_print_help)
+		sysfs_remove_file(met->kobj, &help_attr.attr);
+
+	if (met->process_argument || met->ondiemet_process_argument)
+		sysfs_remove_file(met->kobj, &argu_attr.attr);
+
+	sysfs_remove_file(met->kobj, &reset_attr.attr);
+	sysfs_remove_file(met->kobj, &header_read_again_attr.attr);
+	sysfs_remove_file(met->kobj, &polling_interval_attr.attr);
+	sysfs_remove_file(met->kobj, &mode_attr.attr);
+	sysfs_remove_file(met->kobj, &ondiemet_mode_attr.attr);
+
+	if (met->delete_subfs)
+		met->delete_subfs();
+
+	kobject_del(met->kobj);
+	kobject_put(met->kobj);
+	met->kobj = NULL;
+
+	if (met->polling_count)
+		free_percpu(met->polling_count);
+
+	list_del(&met->list);
+	return 0;
+}
+EXPORT_SYMBOL(met_deregister);
+
+int met_set_platform(const char *plf_name, int flag)
+{
+	strncpy(met_platform, plf_name, sizeof(met_platform) - 1);
+#if 0
+	int ret;
+
+	if (flag) {
+		ret = device_create_file(met_device.this_device, &dev_attr_plf);
+		if (ret != 0) {
+			pr_debug("can not create device file: plf\n");
+			return ret;
+		}
+		strncpy(met_platform, plf_name, sizeof(met_platform) - 1);
+	} else
+		device_remove_file(met_device.this_device, &dev_attr_plf);
+
+#endif
+	return 0;
+}
+EXPORT_SYMBOL(met_set_platform);
+
+int met_set_topology(const char *topology_name, int flag)
+{
+	strncpy(met_topology, topology_name, sizeof(met_topology) - 1);
+#if 0
+	int ret;
+
+	if (flag) {
+		ret = device_create_file(met_device.this_device, &dev_attr_core_topology);
+		if (ret != 0) {
+			pr_debug("can not create device file: topology\n");
+			return ret;
+		}
+		strncpy(met_topology, topology_name, sizeof(met_topology) - 1);
+	} else {
+		device_remove_file(met_device.this_device, &dev_attr_core_topology);
+	}
+#endif
+	return 0;
+}
+EXPORT_SYMBOL(met_set_topology);
+
+#include "met_struct.h"
+
+void force_sample(void *unused)
+{
+	int cpu;
+	unsigned long long stamp;
+	struct metdevice *c;
+	struct met_cpu_struct *met_cpu_ptr;
+
+	if ((run != 1) || (sample_rate == 0))
+		return;
+
+	/* to avoid met tag is coming after __met_hrtimer_stop and before run=-1 */
+	met_cpu_ptr = this_cpu_ptr(&met_cpu);
+	if (met_cpu_ptr->work_enabled == 0)
+		return;
+
+	cpu = smp_processor_id();
+
+	stamp = cpu_clock(cpu);
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->ondiemet_mode == 0) {
+			if ((c->mode != 0) && (c->tagged_polling != NULL))
+				c->tagged_polling(stamp, 0);
+		} else if (c->ondiemet_mode == 1) {
+			if ((c->mode != 0) && (c->ondiemet_tagged_polling != NULL))
+				c->ondiemet_tagged_polling(stamp, 0);
+		} else if (c->ondiemet_mode == 2) {
+			if ((c->mode != 0) && (c->tagged_polling != NULL))
+				c->tagged_polling(stamp, 0);
+			if ((c->mode != 0) && (c->ondiemet_tagged_polling != NULL))
+				c->ondiemet_tagged_polling(stamp, 0);
+		}
+	}
+}
+
+#define MET_SUSPEND_HAND
+#ifdef MET_SUSPEND_HAND
+static struct syscore_ops met_hrtimer_ops = {
+	.suspend = met_hrtimer_suspend,
+	.resume = met_hrtimer_resume,
+};
+#endif
+
+int fs_reg(void)
+{
+	int ret = 0;
+
+	ctrl_flags = 0;
+	met_mode = 0;
+
+#ifdef MET_SUSPEND_HAND
+	/* suspend/resume function handle register */
+	register_syscore_ops(&met_hrtimer_ops);
+#endif
+
+	calc_timer_value(sample_rate);
+
+	ret = misc_register(&met_device);
+	if (ret != 0) {
+		pr_debug("misc register failed\n");
+		return ret;
+	}
+
+	/* dma map config */
+	/* arch_setup_dma_ops(met_device.this_device, 0, 0, NULL, false); */
+	met_arch_setup_dma_ops_symbol(met_device.this_device);
+
+	ret = device_create_file(met_device.this_device, &dev_attr_ksym);
+	if (ret != 0) {
+		pr_debug("can not create device file: ksym\n");
+		return ret;
+	}
+
+	ret = device_create_file(met_device.this_device, &dev_attr_run);
+	if (ret != 0) {
+		pr_debug("can not create device file: run\n");
+		return ret;
+	}
+
+#if	defined(PR_CPU_NOTIFY)
+	ret = device_create_file(met_device.this_device, &dev_attr_cpu_notify);
+	if (ret != 0) {
+		pr_debug("can not create device file: cpu_notify\n");
+		return ret;
+	}
+#endif
+
+#ifdef CONFIG_CPU_FREQ
+	ret = device_create_file(met_device.this_device, &dev_attr_dvfs);
+	if (ret != 0) {
+		pr_debug("can not create device file: dvfs\n");
+		return ret;
+	}
+#endif
+
+	ret = device_create_file(met_device.this_device, &dev_attr_suspend_compensation_enable);
+	if (ret != 0) {
+		pr_debug("can not create device file: suspend_compensation_enable\n");
+		return ret;
+	}
+
+	ret = device_create_file(met_device.this_device, &dev_attr_suspend_compensation_flag);
+	if (ret != 0) {
+		pr_debug("can not create device file: suspend_compensation_enable\n");
+		return ret;
+	}
+
+	ret = device_create_file(met_device.this_device, &dev_attr_ver);
+	if (ret != 0) {
+		pr_debug("can not create device file: ver\n");
+		return ret;
+	}
+
+	ret = device_create_file(met_device.this_device, &dev_attr_devices);
+	if (ret != 0) {
+		pr_debug("can not create device file: devices\n");
+		return ret;
+	}
+
+	ret = device_create_file(met_device.this_device, &dev_attr_ctrl);
+	if (ret != 0) {
+		pr_debug("can not create device file: ctrl\n");
+		return ret;
+	}
+
+	ret = device_create_file(met_device.this_device, &dev_attr_cpu_pmu_method);
+	if (ret != 0) {
+		pr_debug("can not create device file: cpu_pmu_method\n");
+		return ret;
+	}
+
+#if	defined(MET_BOOT_MSG)
+	ret = device_create_file(met_device.this_device, &dev_attr_bootmsg);
+	if (ret != 0) {
+		pr_debug("can not create device file: bootmsg\n");
+		return ret;
+	}
+#endif
+
+	ret = device_create_file(met_device.this_device, &dev_attr_sample_rate);
+	if (ret != 0) {
+		pr_debug("can not create device file: sample_rate\n");
+		return ret;
+	}
+
+	ret = device_create_file(met_device.this_device, &dev_attr_core_topology);
+	if (ret != 0) {
+		pr_debug("can not create device file: topology\n");
+		return ret;
+	}
+
+	ret = device_create_file(met_device.this_device, &dev_attr_plf);
+	if (ret != 0) {
+		pr_debug("can not create device file: plf\n");
+		return ret;
+	}
+
+	ret = device_create_file(met_device.this_device, &dev_attr_hash);
+	if (ret != 0) {
+		pr_debug("can not create device file: hash\n");
+		return ret;
+	}
+
+	ret = device_create_file(met_device.this_device, &dev_attr_ipi_test);
+	if (ret != 0) {
+		pr_debug("can not create device file: ipi_test\n");
+		return ret;
+	}
+
+	kobj_misc = kobject_create_and_add("misc", &met_device.this_device->kobj);
+	if (kobj_misc == NULL) {
+		pr_debug("can not create kobject: kobj_misc\n");
+		return ret;
+	}
+
+	kobj_pmu = kobject_create_and_add("pmu", &met_device.this_device->kobj);
+	if (kobj_pmu == NULL) {
+		pr_debug("can not create kobject: kobj_pmu\n");
+		return ret;
+	}
+
+	kobj_bus = kobject_create_and_add("bus", &met_device.this_device->kobj);
+	if (kobj_bus == NULL) {
+		pr_debug("can not create kobject: kobj_bus\n");
+		return ret;
+	}
+
+	met_register(&met_cpupmu);
+#ifdef MET_SUPPORT_CPUPMU_V2
+	met_register(&met_cpupmu_v2);
+#endif
+#ifdef MET_USER_EVENT_SUPPORT
+	tag_reg((struct file_operations * const) met_device.fops, &met_device.this_device->kobj);
+#endif
+
+	ondiemet_log_manager_init(met_device.this_device);
+	ondiemet_attr_init(met_device.this_device);
+
+	return ret;
+}
+
+void fs_unreg(void)
+{
+	if (run == 1)
+		met_stop();
+
+	run = -1;
+
+#ifdef MET_USER_EVENT_SUPPORT
+	tag_unreg();
+#endif
+	met_deregister(&met_cpupmu);
+#ifdef MET_SUPPORT_CPUPMU_V2
+	met_deregister(&met_cpupmu_v2);
+#endif
+
+	kobject_del(kobj_misc);
+	kobject_put(kobj_misc);
+	kobj_misc = NULL;
+	kobject_del(kobj_pmu);
+	kobject_put(kobj_pmu);
+	kobj_pmu = NULL;
+	kobject_del(kobj_bus);
+	kobject_put(kobj_bus);
+	kobj_bus = NULL;
+
+	device_remove_file(met_device.this_device, &dev_attr_ksym);
+
+	device_remove_file(met_device.this_device, &dev_attr_run);
+#ifdef PR_CPU_NOTIFY
+	device_remove_file(met_device.this_device, &dev_attr_cpu_notify);
+#endif
+#ifdef CONFIG_CPU_FREQ
+	device_remove_file(met_device.this_device, &dev_attr_dvfs);
+#endif
+	device_remove_file(met_device.this_device, &dev_attr_suspend_compensation_enable);
+	device_remove_file(met_device.this_device, &dev_attr_suspend_compensation_flag);
+
+	device_remove_file(met_device.this_device, &dev_attr_ver);
+	device_remove_file(met_device.this_device, &dev_attr_devices);
+	device_remove_file(met_device.this_device, &dev_attr_sample_rate);
+
+	device_remove_file(met_device.this_device, &dev_attr_ctrl);
+	device_remove_file(met_device.this_device, &dev_attr_cpu_pmu_method);
+
+	device_remove_file(met_device.this_device, &dev_attr_core_topology);
+	device_remove_file(met_device.this_device, &dev_attr_plf);
+	device_remove_file(met_device.this_device, &dev_attr_hash);
+	device_remove_file(met_device.this_device, &dev_attr_ipi_test);
+
+	ondiemet_log_manager_uninit(met_device.this_device);
+	ondiemet_attr_uninit(met_device.this_device);
+
+	misc_deregister(&met_device);
+#ifdef MET_SUSPEND_HAND
+	/* suspend/resume function handle register */
+	unregister_syscore_ops(&met_hrtimer_ops);
+#endif
+}
+
+unsigned int get_ctrl_flags(void)
+{
+	return ctrl_flags;
+}
diff --git a/src/devtools/met-driver/met_drv/common/interface.h b/src/devtools/met-driver/met_drv/common/interface.h
new file mode 100644
index 0000000..00c26a6
--- /dev/null
+++ b/src/devtools/met-driver/met_drv/common/interface.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __INTERFACE_H__
+#define __INTERFACE_H__
+
+#include <linux/fs.h>
+
+#ifdef MET_USER_EVENT_SUPPORT
+extern int tag_reg(struct file_operations *const fops, struct kobject *kobj);
+extern int tag_unreg(void);
+#include "met_drv.h"
+#include "met_tag.h"
+extern struct bltable_t bltab;
+#endif
+
+extern struct metdevice met_stat;
+extern struct metdevice met_cpupmu;
+extern struct metdevice met_cpupmu_v2;
+extern struct metdevice met_cookie;
+extern struct metdevice met_memstat;
+extern struct metdevice met_switch;
+extern struct metdevice met_trace_event;
+extern struct metdevice met_dummy_header;
+extern struct metdevice met_backlight;
+
+/* This variable will decide which method to access the CPU PMU counter */
+/*     0: access registers directly */
+/*     others: via Linux perf driver */
+extern unsigned int met_cpu_pmu_method;
+
+extern int met_parse_num(const char *str, unsigned int *value, int len);
+extern void met_set_suspend_notify(int flag);
+
+#define	PR_CPU_NOTIFY
+#if	defined(PR_CPU_NOTIFY)
+extern int met_cpu_notify;
+#endif
+
+//#undef	MET_BOOT_MSG
+#define	MET_BOOT_MSG
+#if	defined(MET_BOOT_MSG)
+extern char met_boot_msg_tmp[256];
+extern int pr_bootmsg(int str_len, char *str);
+#define	PR_BOOTMSG(fmt, args...) { \
+	int str_len = snprintf(met_boot_msg_tmp, sizeof(met_boot_msg_tmp), \
+			       fmt, ##args); \
+	pr_bootmsg(str_len, met_boot_msg_tmp); }
+#define	PR_BOOTMSG_ONCE(fmt, args...) { \
+	static int once; \
+	if (!once) { \
+		int str_len = snprintf(met_boot_msg_tmp, \
+				       sizeof(met_boot_msg_tmp), \
+				       fmt, ##args); \
+		pr_bootmsg(str_len, met_boot_msg_tmp); \
+		once = 1; \
+	} }
+#else
+#define	pr_bootmsg(str_len, str)
+#define PR_BOOTMSG(fmt, args...)
+#define	PR_BOOTMSG_ONCE(fmt, args...)
+#endif
+
+#endif	/* __INTERFACE_H__ */
diff --git a/src/devtools/met-driver/met_drv/common/met_api_tbl.h b/src/devtools/met-driver/met_drv/common/met_api_tbl.h
new file mode 100644
index 0000000..f93ae5f
--- /dev/null
+++ b/src/devtools/met-driver/met_drv/common/met_api_tbl.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+struct met_api_tbl {
+	int (*met_tag_start)(unsigned int class_id, const char *name);
+	int (*met_tag_end)(unsigned int class_id, const char *name);
+	int (*met_tag_async_start)(unsigned int class_id, const char *name, unsigned int cookie);
+	int (*met_tag_async_end)(unsigned int class_id, const char *name, unsigned int cookie);
+	int (*met_tag_oneshot)(unsigned int class_id, const char *name, unsigned int value);
+	int (*met_tag_userdata)(char *pData);
+	int (*met_tag_dump)(unsigned int class_id, const char *name, void *data, unsigned int length);
+	int (*met_tag_disable)(unsigned int class_id);
+	int (*met_tag_enable)(unsigned int class_id);
+	int (*met_set_dump_buffer)(int size);
+	int (*met_save_dump_buffer)(const char *pathname);
+	int (*met_save_log)(const char *pathname);
+	int (*met_show_bw_limiter)(void);
+	int (*met_reg_bw_limiter)(void *fp);
+	int (*met_show_clk_tree)(const char *name, unsigned int addr, unsigned int status);
+	int (*met_reg_clk_tree)(void *fp);
+	void (*met_sched_switch)(struct task_struct *prev, struct task_struct *next);
+	int (*enable_met_backlight_tag)(void);
+	int (*output_met_backlight_tag)(int level);
+};
+
+extern struct met_api_tbl met_ext_api;
diff --git a/src/devtools/met-driver/met_drv/common/met_drv.h b/src/devtools/met-driver/met_drv/common/met_drv.h
new file mode 100644
index 0000000..448aee1
--- /dev/null
+++ b/src/devtools/met-driver/met_drv/common/met_drv.h
@@ -0,0 +1,266 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+
+#ifndef MET_DRV
+#define MET_DRV
+
+#include <linux/version.h>
+#include <linux/preempt.h>
+#include <linux/device.h>
+#include <linux/percpu.h>
+#include <linux/hardirq.h>
+#include <linux/clk.h>
+
+extern int met_mode;
+extern int core_plf_init(void);
+extern void core_plf_exit(void);
+
+#define MET_MODE_TRACE_CMD_OFFSET	(1)
+#define MET_MODE_TRACE_CMD			(1<<MET_MODE_TRACE_CMD_OFFSET)
+
+#ifdef CONFIG_MET_MODULE
+#define my_preempt_enable() preempt_enable()
+#else
+#define my_preempt_enable() preempt_enable_no_resched()
+#endif
+
+#define MET_STRBUF_SIZE		1024
+DECLARE_PER_CPU(char[MET_STRBUF_SIZE], met_strbuf_nmi);
+DECLARE_PER_CPU(char[MET_STRBUF_SIZE], met_strbuf_irq);
+DECLARE_PER_CPU(char[MET_STRBUF_SIZE], met_strbuf_sirq);
+DECLARE_PER_CPU(char[MET_STRBUF_SIZE], met_strbuf);
+
+#ifdef CONFIG_TRACING
+#define TRACE_PUTS(p) \
+	do { \
+		trace_puts(p);; \
+	} while (0)
+#else
+#define TRACE_PUTS(p) do {} while (0)
+#endif
+
+#define GET_MET_TRACE_BUFFER_ENTER_CRITICAL() \
+	({ \
+		char *pmet_strbuf; \
+		preempt_disable(); \
+		if (in_nmi()) \
+			pmet_strbuf = per_cpu(met_strbuf_nmi, smp_processor_id()); \
+		else if (in_irq()) \
+			pmet_strbuf = per_cpu(met_strbuf_irq, smp_processor_id()); \
+		else if (in_softirq()) \
+			pmet_strbuf = per_cpu(met_strbuf_sirq, smp_processor_id()); \
+		else \
+			pmet_strbuf = per_cpu(met_strbuf, smp_processor_id()); \
+		pmet_strbuf;\
+	})
+
+#define PUT_MET_TRACE_BUFFER_EXIT_CRITICAL(pmet_strbuf) \
+	do {\
+		if (pmet_strbuf)\
+			TRACE_PUTS(pmet_strbuf); \
+		my_preempt_enable(); \
+	} while (0)
+
+#define MET_TRACE(FORMAT, args...) \
+	do { \
+		char *pmet_strbuf; \
+		preempt_disable(); \
+		if (in_nmi()) \
+			pmet_strbuf = per_cpu(met_strbuf_nmi, smp_processor_id()); \
+		else if (in_irq()) \
+			pmet_strbuf = per_cpu(met_strbuf_irq, smp_processor_id()); \
+		else if (in_softirq()) \
+			pmet_strbuf = per_cpu(met_strbuf_sirq, smp_processor_id()); \
+		else \
+			pmet_strbuf = per_cpu(met_strbuf, smp_processor_id()); \
+		if (met_mode & MET_MODE_TRACE_CMD) \
+			snprintf(pmet_strbuf, MET_STRBUF_SIZE, "%s: " FORMAT, __func__, ##args); \
+		else \
+			snprintf(pmet_strbuf, MET_STRBUF_SIZE, FORMAT, ##args); \
+		TRACE_PUTS(pmet_strbuf); \
+		my_preempt_enable(); \
+	} while (0)
+
+/*
+ * SOB: start of buf
+ * EOB: end of buf
+ */
+#define MET_TRACE_GETBUF(pSOB, pEOB) \
+	({ \
+		preempt_disable(); \
+		if (in_nmi()) \
+			*pSOB = per_cpu(met_strbuf_nmi, smp_processor_id()); \
+		else if (in_irq()) \
+			*pSOB = per_cpu(met_strbuf_irq, smp_processor_id()); \
+		else if (in_softirq()) \
+			*pSOB = per_cpu(met_strbuf_sirq, smp_processor_id()); \
+		else \
+			*pSOB = per_cpu(met_strbuf, smp_processor_id()); \
+		*pEOB = *pSOB; \
+		if (met_mode & MET_MODE_TRACE_CMD) \
+			*pEOB += snprintf(*pEOB, MET_STRBUF_SIZE, "%s: ", __func__); \
+	})
+
+#define MET_TRACE_PUTBUF(SOB, EOB) \
+	({ \
+		__trace_puts(_THIS_IP_, (SOB), (uintptr_t)((EOB)-(SOB))); \
+		my_preempt_enable(); \
+	})
+
+#define MET_FTRACE_DUMP(TRACE_NAME, args...)			\
+	do {							\
+		trace_##TRACE_NAME(args);;			\
+	} while (0)
+
+
+#define MET_TYPE_PMU	1
+#define MET_TYPE_BUS	2
+#define MET_TYPE_MISC	3
+
+struct metdevice {
+	struct list_head list;
+	int type;
+	const char *name;
+	struct module *owner;
+	struct kobject *kobj;
+
+	int (*create_subfs)(struct kobject *parent);
+	void (*delete_subfs)(void);
+	int mode;
+	int ondiemet_mode;	/* new for ondiemet; 1: call ondiemet functions */
+	int cpu_related;
+	int polling_interval;
+	int polling_count_reload;
+	int __percpu *polling_count;
+	int header_read_again;	/*for header size > 1 page */
+	void (*start)(void);
+	void (*stop)(void);
+	int (*reset)(void);
+	void (*timed_polling)(unsigned long long stamp, int cpu);
+	void (*tagged_polling)(unsigned long long stamp, int cpu);
+	int (*print_help)(char *buf, int len);
+	int (*print_header)(char *buf, int len);
+	int (*process_argument)(const char *arg, int len);
+
+	void (*ondiemet_start)(void);
+	void (*ondiemet_stop)(void);
+	int (*ondiemet_reset)(void);
+	int (*ondiemet_print_help)(char *buf, int len);
+	int (*ondiemet_print_header)(char *buf, int len);
+	int (*ondiemet_process_argument)(const char *arg, int len);
+	void (*ondiemet_timed_polling)(unsigned long long stamp, int cpu);
+	void (*ondiemet_tagged_polling)(unsigned long long stamp, int cpu);
+
+	struct list_head exlist;	/* for linked list before register */
+	void (*suspend)(void);
+	void (*resume)(void);
+
+	unsigned long long prev_stamp;
+	spinlock_t my_lock;
+	void *reversed1;
+};
+
+int met_register(struct metdevice *met);
+int met_deregister(struct metdevice *met);
+int met_set_platform(const char *plf_name, int flag);
+int met_set_topology(const char *topology_name, int flag);
+int met_devlink_add(struct metdevice *met);
+int met_devlink_del(struct metdevice *met);
+int met_devlink_register_all(void);
+int met_devlink_deregister_all(void);
+
+int fs_reg(void);
+void fs_unreg(void);
+
+/******************************************************************************
+ * Tracepoints
+ ******************************************************************************/
+#define MET_DEFINE_PROBE(probe_name, proto) \
+		static void probe_##probe_name(void *data, PARAMS(proto))
+#define MET_REGISTER_TRACE(probe_name) \
+		register_trace_##probe_name(probe_##probe_name, NULL)
+#define MET_UNREGISTER_TRACE(probe_name) \
+		unregister_trace_##probe_name(probe_##probe_name, NULL)
+
+
+/* ====================== Tagging API ================================ */
+
+#define MAX_EVENT_CLASS	31
+#define MAX_TAGNAME_LEN	128
+#define MET_CLASS_ALL	0x80000000
+
+/* IOCTL commands of MET tagging */
+struct mtag_cmd_t {
+	unsigned int class_id;
+	unsigned int value;
+	unsigned int slen;
+	char tname[MAX_TAGNAME_LEN];
+	void *data;
+	unsigned int size;
+};
+
+#define TYPE_START		1
+#define TYPE_END		2
+#define TYPE_ONESHOT		3
+#define TYPE_ENABLE		4
+#define TYPE_DISABLE		5
+#define TYPE_REC_SET		6
+#define TYPE_DUMP		7
+#define TYPE_DUMP_SIZE		8
+#define TYPE_DUMP_SAVE		9
+#define TYPE_USRDATA		10
+#define TYPE_DUMP_AGAIN		11
+#define TYPE_ASYNC_START	12
+#define TYPE_ASYNC_END		13
+#define TYPE_MET_SUSPEND	15
+#define TYPE_MET_RESUME		16
+
+/* Use 'm' as magic number */
+#define MTAG_IOC_MAGIC  'm'
+/* Please use a different 8-bit number in your code */
+#define MTAG_CMD_START		_IOW(MTAG_IOC_MAGIC, TYPE_START, struct mtag_cmd_t)
+#define MTAG_CMD_END		_IOW(MTAG_IOC_MAGIC, TYPE_END, struct mtag_cmd_t)
+#define MTAG_CMD_ONESHOT	_IOW(MTAG_IOC_MAGIC, TYPE_ONESHOT, struct mtag_cmd_t)
+#define MTAG_CMD_ENABLE		_IOW(MTAG_IOC_MAGIC, TYPE_ENABLE, int)
+#define MTAG_CMD_DISABLE	_IOW(MTAG_IOC_MAGIC, TYPE_DISABLE, int)
+#define MTAG_CMD_REC_SET	_IOW(MTAG_IOC_MAGIC, TYPE_REC_SET, int)
+#define MTAG_CMD_DUMP		_IOW(MTAG_IOC_MAGIC, TYPE_DUMP, struct mtag_cmd_t)
+#define MTAG_CMD_DUMP_SIZE	_IOWR(MTAG_IOC_MAGIC, TYPE_DUMP_SIZE, int)
+#define MTAG_CMD_DUMP_SAVE	_IOW(MTAG_IOC_MAGIC, TYPE_DUMP_SAVE, struct mtag_cmd_t)
+#define MTAG_CMD_USRDATA	_IOW(MTAG_IOC_MAGIC, TYPE_USRDATA, struct mtag_cmd_t)
+#define MTAG_CMD_DUMP_AGAIN	_IOW(MTAG_IOC_MAGIC, TYPE_DUMP_AGAIN, void *)
+#define MTAG_CMD_ASYNC_START	_IOW(MTAG_IOC_MAGIC, TYPE_ASYNC_START, struct mtag_cmd_t)
+#define MTAG_CMD_ASYNC_END	_IOW(MTAG_IOC_MAGIC, TYPE_ASYNC_END, struct mtag_cmd_t)
+
+/* include file */
+extern int met_tag_start_real(unsigned int class_id, const char *name);
+extern int met_tag_end_real(unsigned int class_id, const char *name);
+extern int met_tag_async_start_real(unsigned int class_id, const char *name, unsigned int cookie);
+extern int met_tag_async_end_real(unsigned int class_id, const char *name, unsigned int cookie);
+extern int met_tag_oneshot_real(unsigned int class_id, const char *name, unsigned int value);
+extern int met_tag_userdata_real(char *pData);
+extern int met_tag_dump_real(unsigned int class_id, const char *name, void *data, unsigned int length);
+extern int met_tag_disable_real(unsigned int class_id);
+extern int met_tag_enable_real(unsigned int class_id);
+extern int met_set_dump_buffer_real(int size);
+extern int met_save_dump_buffer_real(const char *pathname);
+extern int met_save_log_real(const char *pathname);
+extern int met_show_bw_limiter_real(void);
+extern int met_reg_bw_limiter_real(void *fp);
+extern int met_show_clk_tree_real(const char *name, unsigned int addr, unsigned int status);
+extern int met_reg_clk_tree_real(void *fp);
+extern int enable_met_backlight_tag(void);
+extern int output_met_backlight_tag(int level);
+
+#endif	/* MET_DRV */
diff --git a/src/devtools/met-driver/met_drv/common/met_emi.c b/src/devtools/met-driver/met_drv/common/met_emi.c
new file mode 100644
index 0000000..7f18f4a
--- /dev/null
+++ b/src/devtools/met-driver/met_drv/common/met_emi.c
@@ -0,0 +1,1107 @@
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/device.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/dma-mapping.h>
+#include <linux/string.h>
+
+#define MET_USER_EVENT_SUPPORT
+#include "met_drv.h"
+#include "trace.h"
+
+#include "mtk_typedefs.h"
+#include "core_plf_init.h"
+#include "core_plf_trace.h"
+#include "mtk_emi_bm.h"
+
+extern struct miscdevice met_device;
+
+/*======================================================================*/
+/*	Global variable definitions					*/
+/*======================================================================*/
+/*ondiemet emi sampling interval in us */
+int ondiemet_emi_polling_200us;
+int emi_tsct_enable;
+int emi_mdct_enable;
+
+
+int emi_use_ondiemet;
+int metemi_func_opt;
+
+int met_emi_regdump;
+/*WSCT/TSCT id selection enable*/
+int emi_wsct_tsct_id_selection[4];
+/* Dynamic MonitorCounter selection !!!EXPERIMENT!!! */
+static int msel_enable;
+static unsigned int msel_group1 = BM_Master_GP_1_Default;
+static unsigned int msel_group2 = BM_Master_GP_2_Default;
+static unsigned int msel_group3 = BM_Master_GP_3_Default;
+
+/* CVS Added changeable buffer for testing */
+static int mdmcu_sel_enable;
+static unsigned int rd_mdmcu_rsv_num = 0x5;
+
+/* Global variables */
+static struct kobject *kobj_emi;
+static int rwtype = BM_BOTH_READ_WRITE;
+
+/* BW Limiter */
+/*#define CNT_COUNTDOWN	(1000-1)*/		/* 1000 * 1ms = 1sec */
+#define CNT_COUNTDOWN	(0)			/* 1ms */
+static int countdown;
+static int bw_limiter_enable = BM_BW_LIMITER_ENABLE;
+
+/* TTYPE counter */
+static int ttype1_16_en = BM_TTYPE1_16_DISABLE;
+static int ttype17_21_en = BM_TTYPE17_21_DISABLE;
+
+unsigned int fmem_divider_freq_1;
+unsigned int fmem_divider_freq_2;
+
+static int dramc_pdir_enable;
+static int dram_chann_num = 1;
+
+/*======================================================================*/
+/*	EMI Test Operations						*/
+/*======================================================================*/
+static int times;
+
+static ssize_t test_apmcu_store(struct kobject *kobj,
+		struct kobj_attribute *attr,
+		const char *buf,
+		size_t n)
+{
+	int		i;
+	unsigned int	*src_addr_v;
+	dma_addr_t	src_addr_p;
+
+	if ((n == 0) || (buf == NULL))
+		return -EINVAL;
+	if (sscanf(buf, "%d", &times) != 1)
+		return -EINVAL;
+	if (times < 0)
+		return -EINVAL;
+
+	if (times > 5000)	/* Less than 20MB */
+		return -EINVAL;
+
+	/* dma_alloc */
+	src_addr_v = dma_alloc_coherent(met_device.this_device,
+			PAGE_SIZE,
+			&src_addr_p,
+			GFP_KERNEL);
+	if (src_addr_v == NULL) {
+	/*	met_tag_oneshot(0, "test_apmcu dma alloc fail", PAGE_SIZE); */
+		return -ENOMEM;
+	}
+	/* testing */
+	preempt_disable();
+	/* met_tag_start(0, "TEST_EMI_APMCU"); */
+	for (i = 0; i < times; i++) {
+		memset(src_addr_v, 2*i, PAGE_SIZE);
+		/* met_tag_oneshot(0, "TEST_EMI_APMCU", PAGE_SIZE); */
+	}
+	/* met_tag_end(0, "TEST_EMI_APMCU"); */
+	preempt_enable();
+
+	/* dma_free */
+	if (src_addr_v != NULL)
+		dma_free_coherent(met_device.this_device,
+				PAGE_SIZE,
+				src_addr_v,
+				src_addr_p);
+	return n;
+}
+
+/*======================================================================*/
+/*	KOBJ Declarations						*/
+/*======================================================================*/
+DECLARE_KOBJ_ATTR_INT(ondiemet_emi_polling_200us, ondiemet_emi_polling_200us)
+DECLARE_KOBJ_ATTR_INT(emi_tsct_enable, emi_tsct_enable)
+DECLARE_KOBJ_ATTR_INT(emi_mdct_enable, emi_mdct_enable)
+DECLARE_KOBJ_ATTR_INT(metemi_func_opt, metemi_func_opt)
+DECLARE_KOBJ_ATTR_INT(emi_regdump, met_emi_regdump)
+DECLARE_KOBJ_ATTR_INT(emi_wsct_tsct_id_selection1, emi_wsct_tsct_id_selection[0])
+DECLARE_KOBJ_ATTR_INT(emi_wsct_tsct_id_selection2, emi_wsct_tsct_id_selection[1])
+DECLARE_KOBJ_ATTR_INT(emi_wsct_tsct_id_selection3, emi_wsct_tsct_id_selection[2])
+DECLARE_KOBJ_ATTR_INT(emi_wsct_tsct_id_selection4, emi_wsct_tsct_id_selection[3])
+/* KOBJ: Dynamic MonitorCounter selection !!!EXPERIMENT!!! */
+DECLARE_KOBJ_ATTR_INT(msel_enable, msel_enable)
+DECLARE_KOBJ_ATTR_HEX_CHECK(msel_group1, msel_group1, msel_group1 > 0 && msel_group1 <= BM_MASTER_ALL)
+DECLARE_KOBJ_ATTR_HEX_CHECK(msel_group2, msel_group2, msel_group2 > 0 && msel_group2 <= BM_MASTER_ALL)
+DECLARE_KOBJ_ATTR_HEX_CHECK(msel_group3, msel_group3, msel_group3 > 0 && msel_group3 <= BM_MASTER_ALL)
+DECLARE_KOBJ_ATTR_INT(mdmcu_sel_enable, mdmcu_sel_enable)
+DECLARE_KOBJ_ATTR_INT(rd_mdmcu_rsv_num, rd_mdmcu_rsv_num)
+
+
+/* KOBJ: rwtype */
+DECLARE_KOBJ_ATTR_INT_CHECK(rwtype, rwtype, rwtype >= 0 && rwtype <= BM_WRITE_ONLY)
+
+static unsigned int get_emi_clock_rate(unsigned int dram_data_rate_MHz)
+{
+	return dram_data_rate_MHz/DRAM_EMI_BASECLOCK_RATE/DRAM_DATARATE;
+}
+
+/* KOBJ: emi_clock_rate */
+static ssize_t emi_clock_rate_show(struct kobject *kobj,
+		struct kobj_attribute *attr,
+		char *buf)
+{
+	unsigned int	dram_data_rate_MHz;
+
+	dram_data_rate_MHz = get_dram_data_rate();
+	return snprintf(buf, PAGE_SIZE, "%d\n",
+			get_emi_clock_rate(dram_data_rate_MHz));
+}
+
+DECLARE_KOBJ_ATTR_RO(emi_clock_rate)
+
+static ssize_t dram_data_rate_show(struct kobject *kobj,
+		struct kobj_attribute *attr,
+		char *buf)
+{
+	unsigned int	dram_data_rate_MHz;
+
+	dram_data_rate_MHz = get_dram_data_rate();
+	return snprintf(buf, PAGE_SIZE, "%d\n", dram_data_rate_MHz);
+}
+
+DECLARE_KOBJ_ATTR_RO(dram_data_rate)
+
+/* KOBJ: ttype1_16_en */
+DECLARE_KOBJ_ATTR_STR_LIST_ITEM(
+		ttype1_16_en,
+		KOBJ_ITEM_LIST(
+			{BM_TTYPE1_16_ENABLE,	"ENABLE"},
+			{BM_TTYPE1_16_DISABLE,	"DISABLE"}
+			)
+		)
+DECLARE_KOBJ_ATTR_STR_LIST(ttype1_16_en, ttype1_16_en, ttype1_16_en)
+
+/* KOBJ: ttype17_21_en */
+DECLARE_KOBJ_ATTR_STR_LIST_ITEM(
+		ttype17_21_en,
+		KOBJ_ITEM_LIST(
+			{BM_TTYPE17_21_ENABLE,	"ENABLE"},
+			{BM_TTYPE17_21_DISABLE,	"DISABLE"}
+			)
+		)
+DECLARE_KOBJ_ATTR_STR_LIST(ttype17_21_en, ttype17_21_en, ttype17_21_en)
+
+/* KOBJ: bw_limiter_enable */
+DECLARE_KOBJ_ATTR_STR_LIST_ITEM(
+		bw_limiter_enable,
+		KOBJ_ITEM_LIST(
+			{BM_BW_LIMITER_ENABLE,	"ENABLE"},
+			{BM_BW_LIMITER_DISABLE,	"DISABLE"}
+			)
+		)
+
+DECLARE_KOBJ_ATTR_STR_LIST(bw_limiter_enable, bw_limiter_enable, bw_limiter_enable)
+
+/* KOBJ: ttype_master */
+DECLARE_KOBJ_ATTR_STR_LIST_ITEM(
+		ttype_master,
+		KOBJ_ITEM_LIST(
+			{BM_MASTER_M0,	"M0"},
+			{BM_MASTER_M1,	"M1"},
+			{BM_MASTER_M2,	"M2"},
+			{BM_MASTER_M3,	"M3"},
+			{BM_MASTER_M4,	"M4"},
+			{BM_MASTER_M5,	"M5"},
+			{BM_MASTER_M6,	"M6"},
+			{BM_MASTER_M7,	"M7"}
+			)
+		)
+
+
+/* KOBJ: ttypeX_nbeat, ttypeX_nbyte, ttypeX_burst */
+DECLARE_KOBJ_ATTR_INT_LIST_ITEM(
+		ttype_nbeat,
+		KOBJ_ITEM_LIST(
+			{BM_TRANS_TYPE_1BEAT,	1},
+			{BM_TRANS_TYPE_2BEAT,	2},
+			{BM_TRANS_TYPE_3BEAT,	3},
+			{BM_TRANS_TYPE_4BEAT,	4},
+			{BM_TRANS_TYPE_5BEAT,	5},
+			{BM_TRANS_TYPE_6BEAT,	6},
+			{BM_TRANS_TYPE_7BEAT,	7},
+			{BM_TRANS_TYPE_8BEAT,	8},
+			{BM_TRANS_TYPE_9BEAT,	9},
+			{BM_TRANS_TYPE_10BEAT,	10},
+			{BM_TRANS_TYPE_11BEAT,	11},
+			{BM_TRANS_TYPE_12BEAT,	12},
+			{BM_TRANS_TYPE_13BEAT,	13},
+			{BM_TRANS_TYPE_14BEAT,	14},
+			{BM_TRANS_TYPE_15BEAT,	15},
+			{BM_TRANS_TYPE_16BEAT,	16}
+			)
+		)
+DECLARE_KOBJ_ATTR_INT_LIST_ITEM(
+		ttype_nbyte,
+		KOBJ_ITEM_LIST(
+			{BM_TRANS_TYPE_1Byte,	1},
+			{BM_TRANS_TYPE_2Byte,	2},
+			{BM_TRANS_TYPE_4Byte,	4},
+			{BM_TRANS_TYPE_8Byte,	8},
+			{BM_TRANS_TYPE_16Byte,	16},
+			{BM_TRANS_TYPE_32Byte,	32}
+			)
+		)
+DECLARE_KOBJ_ATTR_STR_LIST_ITEM(
+		ttype_burst,
+		KOBJ_ITEM_LIST(
+			{BM_TRANS_TYPE_BURST_INCR,	"INCR"},
+			{BM_TRANS_TYPE_BURST_WRAP,	"WRAP"}
+			)
+		)
+
+DECLARE_KOBJ_ATTR_STR_LIST_ITEM(
+		ttype_rw,
+		KOBJ_ITEM_LIST(
+			{BM_TRANS_RW_DEFAULT,	"DEFAULT"},
+			{BM_TRANS_RW_READONLY,	"R"},
+			{BM_TRANS_RW_WRITEONLY,	"W"},
+			{BM_TRANS_RW_RWBOTH,	"BOTH"}
+			)
+		)
+
+/* KOBJ: test_apmcu */
+DECLARE_KOBJ_ATTR_SHOW_INT(test_apmcu, times)
+/* please refer to session: "EMI Test Operations" for store operation */
+DECLARE_KOBJ_ATTR(test_apmcu)
+
+DECLARE_KOBJ_ATTR_INT(dramc_pdir_enable, dramc_pdir_enable)
+
+/*enable high priority filter*/
+static int high_priority_filter;
+DECLARE_KOBJ_ATTR_HEX(high_priority_filter, high_priority_filter)
+
+
+/**/
+static int ttype_master_val[21];
+static int ttype_busid_val[21];
+static int ttype_nbeat_val[21];
+static int ttype_nbyte_val[21];
+static int ttype_burst_val[21];
+static int ttype_rw_val[21];
+
+#define DECLARE_KOBJ_SERIAL_FNODE(nr) \
+	DECLARE_KOBJ_ATTR_STR_LIST(ttype##nr##_master, ttype_master_val[nr-1], ttype_master) \
+	DECLARE_KOBJ_ATTR_HEX(ttype##nr##_busid, ttype_busid_val[nr-1]) \
+	DECLARE_KOBJ_ATTR_INT_LIST(ttype##nr##_nbeat, ttype_nbeat_val[nr-1], ttype_nbeat) \
+	DECLARE_KOBJ_ATTR_INT_LIST(ttype##nr##_nbyte, ttype_nbyte_val[nr-1], ttype_nbyte) \
+	DECLARE_KOBJ_ATTR_STR_LIST(ttype##nr##_burst, ttype_burst_val[nr-1], ttype_burst) \
+	DECLARE_KOBJ_ATTR_STR_LIST(ttype##nr##_rw, ttype_rw_val[nr-1], ttype_rw)
+
+DECLARE_KOBJ_SERIAL_FNODE(1)
+DECLARE_KOBJ_SERIAL_FNODE(2)
+DECLARE_KOBJ_SERIAL_FNODE(3)
+DECLARE_KOBJ_SERIAL_FNODE(4)
+DECLARE_KOBJ_SERIAL_FNODE(5)
+DECLARE_KOBJ_SERIAL_FNODE(6)
+DECLARE_KOBJ_SERIAL_FNODE(7)
+DECLARE_KOBJ_SERIAL_FNODE(8)
+DECLARE_KOBJ_SERIAL_FNODE(9)
+DECLARE_KOBJ_SERIAL_FNODE(10)
+DECLARE_KOBJ_SERIAL_FNODE(11)
+DECLARE_KOBJ_SERIAL_FNODE(12)
+DECLARE_KOBJ_SERIAL_FNODE(13)
+DECLARE_KOBJ_SERIAL_FNODE(14)
+DECLARE_KOBJ_SERIAL_FNODE(15)
+DECLARE_KOBJ_SERIAL_FNODE(16)
+DECLARE_KOBJ_SERIAL_FNODE(17)
+DECLARE_KOBJ_SERIAL_FNODE(18)
+DECLARE_KOBJ_SERIAL_FNODE(19)
+DECLARE_KOBJ_SERIAL_FNODE(20)
+DECLARE_KOBJ_SERIAL_FNODE(21)
+
+	/**/
+#define KOBJ_ATTR_ITEM_SERIAL_FNODE(nr) \
+	KOBJ_ATTR_ITEM(ttype##nr##_master) \
+	KOBJ_ATTR_ITEM(ttype##nr##_nbeat) \
+	KOBJ_ATTR_ITEM(ttype##nr##_nbyte) \
+	KOBJ_ATTR_ITEM(ttype##nr##_burst) \
+	KOBJ_ATTR_ITEM(ttype##nr##_busid) \
+	KOBJ_ATTR_ITEM(ttype##nr##_rw) \
+
+#define KOBJ_ATTR_LIST \
+	KOBJ_ATTR_ITEM(high_priority_filter) \
+	KOBJ_ATTR_ITEM(metemi_func_opt) \
+	KOBJ_ATTR_ITEM(emi_tsct_enable) \
+	KOBJ_ATTR_ITEM(emi_mdct_enable) \
+	KOBJ_ATTR_ITEM(ondiemet_emi_polling_200us) \
+	KOBJ_ATTR_ITEM(emi_regdump) \
+	KOBJ_ATTR_ITEM(emi_wsct_tsct_id_selection1) \
+	KOBJ_ATTR_ITEM(emi_wsct_tsct_id_selection2) \
+	KOBJ_ATTR_ITEM(emi_wsct_tsct_id_selection3) \
+	KOBJ_ATTR_ITEM(emi_wsct_tsct_id_selection4) \
+	KOBJ_ATTR_ITEM(msel_enable) \
+	KOBJ_ATTR_ITEM(msel_group1) \
+	KOBJ_ATTR_ITEM(msel_group2) \
+	KOBJ_ATTR_ITEM(msel_group3) \
+	KOBJ_ATTR_ITEM(emi_clock_rate) \
+	KOBJ_ATTR_ITEM(dram_data_rate) \
+	KOBJ_ATTR_ITEM(rwtype) \
+	KOBJ_ATTR_ITEM(ttype17_21_en) \
+	KOBJ_ATTR_ITEM(ttype1_16_en) \
+	KOBJ_ATTR_ITEM_SERIAL_FNODE(1) \
+	KOBJ_ATTR_ITEM_SERIAL_FNODE(2) \
+	KOBJ_ATTR_ITEM_SERIAL_FNODE(3) \
+	KOBJ_ATTR_ITEM_SERIAL_FNODE(4) \
+	KOBJ_ATTR_ITEM_SERIAL_FNODE(5) \
+	KOBJ_ATTR_ITEM_SERIAL_FNODE(6) \
+	KOBJ_ATTR_ITEM_SERIAL_FNODE(7) \
+	KOBJ_ATTR_ITEM_SERIAL_FNODE(8) \
+	KOBJ_ATTR_ITEM_SERIAL_FNODE(9) \
+	KOBJ_ATTR_ITEM_SERIAL_FNODE(10) \
+	KOBJ_ATTR_ITEM_SERIAL_FNODE(11) \
+	KOBJ_ATTR_ITEM_SERIAL_FNODE(12) \
+	KOBJ_ATTR_ITEM_SERIAL_FNODE(13) \
+	KOBJ_ATTR_ITEM_SERIAL_FNODE(14) \
+	KOBJ_ATTR_ITEM_SERIAL_FNODE(15) \
+	KOBJ_ATTR_ITEM_SERIAL_FNODE(16) \
+	KOBJ_ATTR_ITEM_SERIAL_FNODE(17) \
+	KOBJ_ATTR_ITEM_SERIAL_FNODE(18) \
+	KOBJ_ATTR_ITEM_SERIAL_FNODE(19) \
+	KOBJ_ATTR_ITEM_SERIAL_FNODE(20) \
+	KOBJ_ATTR_ITEM_SERIAL_FNODE(21) \
+	KOBJ_ATTR_ITEM(test_apmcu) \
+	KOBJ_ATTR_ITEM(bw_limiter_enable) \
+	KOBJ_ATTR_ITEM(dramc_pdir_enable) \
+	KOBJ_ATTR_ITEM(mdmcu_sel_enable) \
+	KOBJ_ATTR_ITEM(rd_mdmcu_rsv_num) \
+
+/*======================================================================*/
+/*	EMI Operations							*/
+/*======================================================================*/
+static void emi_init(void)
+{
+	unsigned int bmrw0_val, bmrw1_val, i, enable;
+	unsigned int msel_group1_val, msel_group2_val, msel_group3_val;
+
+	/* get dram channel number */
+	dram_chann_num = MET_EMI_GetDramChannNum();
+
+	/* Init. EMI bus monitor */
+	MET_BM_SetReadWriteType(rwtype);
+
+	/* MSEL1: ALL */
+	MET_BM_SetMonitorCounter(1,
+		BM_MASTER_ALL,
+		BM_TRANS_TYPE_4BEAT |
+		BM_TRANS_TYPE_8Byte |
+		BM_TRANS_TYPE_BURST_WRAP);
+	if (msel_enable) {
+		msel_group1_val = msel_group1;
+		msel_group2_val = msel_group2;
+		msel_group3_val = msel_group3;
+	} else {
+		msel_group1_val = BM_Master_GP_1_Default;
+		msel_group2_val = BM_Master_GP_2_Default;
+		msel_group3_val = BM_Master_GP_3_Default;
+	}
+
+	/* MSEL2: msel_group1 */
+	MET_BM_SetMonitorCounter(2,
+		msel_group1_val & BM_MASTER_ALL,
+		BM_TRANS_TYPE_4BEAT |
+		BM_TRANS_TYPE_8Byte |
+		BM_TRANS_TYPE_BURST_WRAP);
+	/* MSEL3: msel_group2 */
+	MET_BM_SetMonitorCounter(3,
+		msel_group2_val & BM_MASTER_ALL,
+		BM_TRANS_TYPE_4BEAT |
+		BM_TRANS_TYPE_8Byte |
+		BM_TRANS_TYPE_BURST_WRAP);
+	/* MSEL4: msel_group3 */
+	MET_BM_SetMonitorCounter(4,
+		msel_group3_val & BM_MASTER_ALL,
+		BM_TRANS_TYPE_4BEAT |
+		BM_TRANS_TYPE_8Byte |
+		BM_TRANS_TYPE_BURST_WRAP);
+
+
+	if (ttype1_16_en == BM_TTYPE1_16_ENABLE)	{
+		MET_BM_SetLatencyCounter(0);
+
+		for (i = 1; i <= 16; i++) {
+			MET_BM_SetMonitorCounter(i,
+					ttype_master_val[i - 1],
+					ttype_nbeat_val[i - 1] |
+					ttype_nbyte_val[i - 1] |
+					ttype_burst_val[i - 1]);
+			MET_BM_SetIDSelect(i, ttype_busid_val[i - 1], (ttype_busid_val[i - 1] >= 0xffff) ? 0 : 1);
+		}
+	} else {
+		MET_BM_SetLatencyCounter(1);
+	}
+
+	if (ttype17_21_en == BM_TTYPE17_21_ENABLE) {
+		for (i = 17; i <= 21; i++) {
+			MET_BM_SetMonitorCounter(i,
+					ttype_master_val[i - 1],
+					ttype_nbeat_val[i - 1] |
+					ttype_nbyte_val[i - 1] |
+					ttype_burst_val[i - 1]);
+			MET_BM_SetIDSelect(i, ttype_busid_val[i - 1], (ttype_busid_val[i - 1] >= 0xffff) ? 0 : 1);
+		}
+	}
+
+	bmrw0_val = (
+			(ttype_rw_val[0] << 0) | (ttype_rw_val[1] << 2) |
+			(ttype_rw_val[2] << 4) | (ttype_rw_val[3] << 6) |
+			(ttype_rw_val[4] << 8) | (ttype_rw_val[5] << 10) |
+			(ttype_rw_val[6] << 12) | (ttype_rw_val[7] << 14) |
+			(ttype_rw_val[8] << 16) | (ttype_rw_val[9] << 18) |
+			(ttype_rw_val[10] << 20) | (ttype_rw_val[11] << 22) |
+			(ttype_rw_val[12] << 24) | (ttype_rw_val[13] << 26) |
+			(ttype_rw_val[14] << 28) | (ttype_rw_val[15] << 30));
+
+	bmrw1_val = (
+			(ttype_rw_val[16] << 0) | (ttype_rw_val[17] << 2) |
+			(ttype_rw_val[18] << 4) | (ttype_rw_val[19] << 6) |
+			(ttype_rw_val[20] << 8));
+
+	MET_BM_SetTtypeCounterRW(bmrw0_val, bmrw1_val);
+
+	for (i = 0; i < 4; i++)
+		MET_BM_Set_WsctTsct_id_sel(i, emi_wsct_tsct_id_selection[i]);
+
+	for (i = 0; i < BM_COUNTER_MAX; i++) {
+		if ((high_priority_filter & (1<<i)) == 0)
+			enable = 0;
+		else
+			enable = 1;
+
+		MET_BM_SetUltraHighFilter(i+1, enable);
+	}
+
+	if (met_emi_regdump == 1)
+		emi_dump_reg();
+}
+
+static void emi_uninit(void)
+{
+}
+
+static inline void emi_start(void)
+{
+	MET_BM_Enable(1);
+}
+
+static inline void emi_stop(void)
+{
+	MET_BM_Enable(0);
+}
+
+static inline int do_emi(void)
+{
+	return met_emi.mode;
+}
+
+noinline void DRAM_DVFS(unsigned int dram_data_rate_MHz)
+{
+	MET_TRACE("%u\n", dram_data_rate_MHz);
+}
+
+static unsigned int emi_bw_limiter(unsigned int *__restrict__ array)
+{
+	int		idx = 0;
+	unsigned int	dram_data_rate_MHz;
+
+	dram_data_rate_MHz = get_dram_data_rate();
+
+	/* print dram data rate */
+	DRAM_DVFS(dram_data_rate_MHz);
+
+	/* get correct dram_clock_rate */
+	array[idx++] = dram_data_rate_MHz;
+
+	/* get correct ARB A->LAST */
+	array[idx++] = MET_EMI_GetARBA();
+	array[idx++] = MET_EMI_GetARBB();
+	array[idx++] = MET_EMI_GetARBC();
+	array[idx++] = MET_EMI_GetARBD();
+	array[idx++] = MET_EMI_GetARBE();
+	array[idx++] = MET_EMI_GetARBF();
+	array[idx++] = MET_EMI_GetARBG();
+	array[idx++] = MET_EMI_GetARBH();
+	/* EMI Total BW Thresholds */
+	array[idx++] = MET_EMI_GetBWCT0();
+	array[idx++] = MET_EMI_GetBWCT1();
+	array[idx++] = MET_EMI_GetBWCT2();
+	array[idx++] = MET_EMI_GetBWCT3();
+	array[idx++] = MET_EMI_GetBWCT4();
+	array[idx++] = MET_EMI_GetBWST0();
+	array[idx++] = MET_EMI_GetBWST1();
+	/* EMI C+G BW Thresholds */
+	array[idx++] = MET_EMI_GetBWCT0_2ND();
+	array[idx++] = MET_EMI_GetBWCT1_2ND();
+	array[idx++] = MET_EMI_GetBWST_2ND();
+
+	return idx;
+}
+
+
+static void _ms_dramc(unsigned int *__restrict__ dramc_pdir_value, int dram_chann_num)
+{
+	MET_DRAMC_GetDebugCounter(dramc_pdir_value, dram_chann_num);
+}
+
+static unsigned int emi_polling(unsigned int *__restrict__ emi_value, unsigned int *__restrict__ emi_tsct,
+	unsigned int *__restrict__ emi_ttype_value, unsigned int *__restrict__ dramc_pdir_value,
+	unsigned int *__restrict__ emi_mdct_value)
+{
+	int	j = 4;		/* skip 4 WSCTs */
+	int	i = 0;		/* ttype start at 0 */
+	int	k = 0;		/* tsct start at 0 */
+	int n;
+
+	MET_BM_Pause();
+
+	if (ttype1_16_en != BM_TTYPE1_16_ENABLE) {	/*1~21 NOT for ttype*/
+
+		/* Get Word Count */
+
+		emi_value[0] = MET_BM_GetWordCount(1);	/* All */
+		emi_value[1] = MET_BM_GetWordCount(2);	/* Group 1 */
+		emi_value[2] = MET_BM_GetWordCount(3);	/* Group 2 */
+		emi_value[3] = MET_BM_GetWordCount(4);	/* Group 3 */
+
+
+		/* Get Latency */
+		emi_value[j++] = MET_BM_GetLatencyCycle(1);
+		emi_value[j++] = MET_BM_GetLatencyCycle(2);
+		emi_value[j++] = MET_BM_GetLatencyCycle(3);
+		emi_value[j++] = MET_BM_GetLatencyCycle(4);
+		emi_value[j++] = MET_BM_GetLatencyCycle(5);
+		emi_value[j++] = MET_BM_GetLatencyCycle(6);
+		emi_value[j++] = MET_BM_GetLatencyCycle(7);
+		emi_value[j++] = MET_BM_GetLatencyCycle(8);
+
+		/* Get Trans. */
+		emi_value[j++] = MET_BM_GetLatencyCycle(9);
+		emi_value[j++] = MET_BM_GetLatencyCycle(10);
+		emi_value[j++] = MET_BM_GetLatencyCycle(11);
+		emi_value[j++] = MET_BM_GetLatencyCycle(12);
+		emi_value[j++] = MET_BM_GetLatencyCycle(13);
+		emi_value[j++] = MET_BM_GetLatencyCycle(14);
+		emi_value[j++] = MET_BM_GetLatencyCycle(15);
+		emi_value[j++] = MET_BM_GetLatencyCycle(16);
+	} else {
+		for (n = 0; n < 20; n++)
+			emi_value[n] = 0;
+		j = 20;
+
+		emi_ttype_value[i++] = MET_BM_GetLatencyCycle(1);
+		emi_ttype_value[i++] = MET_BM_GetLatencyCycle(2);
+		emi_ttype_value[i++] = MET_BM_GetLatencyCycle(3);
+		emi_ttype_value[i++] = MET_BM_GetLatencyCycle(4);
+		emi_ttype_value[i++] = MET_BM_GetLatencyCycle(5);
+		emi_ttype_value[i++] = MET_BM_GetLatencyCycle(6);
+		emi_ttype_value[i++] = MET_BM_GetLatencyCycle(7);
+		emi_ttype_value[i++] = MET_BM_GetLatencyCycle(8);
+
+		/* Get Trans. */
+		emi_ttype_value[i++] = MET_BM_GetLatencyCycle(9);
+		emi_ttype_value[i++] = MET_BM_GetLatencyCycle(10);
+		emi_ttype_value[i++] = MET_BM_GetLatencyCycle(11);
+		emi_ttype_value[i++] = MET_BM_GetLatencyCycle(12);
+		emi_ttype_value[i++] = MET_BM_GetLatencyCycle(13);
+		emi_ttype_value[i++] = MET_BM_GetLatencyCycle(14);
+		emi_ttype_value[i++] = MET_BM_GetLatencyCycle(15);
+		emi_ttype_value[i++] = MET_BM_GetLatencyCycle(16);
+	}
+
+	/* Get BACT/BSCT/BCNT */
+	emi_value[j++] = MET_BM_GetBandwidthWordCount();
+	emi_value[j++] = MET_BM_GetOverheadWordCount();
+	emi_value[j++] = MET_BM_GetBusCycCount();
+	/* Get PageHist/PageMiss/InterBank/Idle */
+	for (n = 0; n < dram_chann_num; n++) {
+#if 1
+		/* TBD */
+		emi_value[j++] = MET_DRAMC_GetPageHitCount(DRAMC_ALL, n);
+		emi_value[j++] = MET_DRAMC_GetPageMissCount(DRAMC_ALL, n);
+		emi_value[j++] = MET_DRAMC_GetInterbankCount(DRAMC_ALL, n);
+		emi_value[j++] = MET_DRAMC_GetIdleCount(n);
+		emi_value[j++] = ((MET_DRAMC_Misc_Status(n) >> 8) & 0x7);
+		emi_value[j++] = MET_DRAMC_RefPop(n);
+		emi_value[j++] = MET_DRAMC_Free26M(n);
+		emi_value[j++] = MET_DRAMC_RByte(n);
+		emi_value[j++] = MET_DRAMC_WByte(n);
+#else
+		emi_value[j++] = 0;
+		emi_value[j++] = 0;
+		emi_value[j++] = 0;
+		emi_value[j++] = 0;
+		emi_value[j++] = 0;
+		emi_value[j++] = 0;
+		emi_value[j++] = 0;
+		emi_value[j++] = 0;
+		emi_value[j++] = 0;
+#endif
+	}
+	/* TTYPE */
+	if (ttype17_21_en == BM_TTYPE17_21_ENABLE) {	/*17~21 for ttype*/
+		emi_ttype_value[16] = MET_BM_GetLatencyCycle(17);
+		emi_ttype_value[17] = MET_BM_GetLatencyCycle(18);
+		emi_ttype_value[18] = MET_BM_GetLatencyCycle(19);
+		emi_ttype_value[19] = MET_BM_GetLatencyCycle(20);
+		emi_ttype_value[20] = MET_BM_GetLatencyCycle(21);
+	}
+
+	/* Get tsct */
+	if (emi_tsct_enable == 1) {
+		emi_tsct[k++] = MET_BM_GetTransCount(1);
+		emi_tsct[k++] = MET_BM_GetTransCount(2);
+		emi_tsct[k++] = MET_BM_GetTransCount(3);
+	}
+	/*get mdct rsv buffer*/
+	if (emi_mdct_enable == 1) {
+		emi_mdct_value[0] = (MET_BM_GetMDCT() >> 16) & 0x7;
+		emi_mdct_value[1] = (MET_BM_GetMDCT_2() & 0x7);
+	}
+
+	if (dramc_pdir_enable == 1)
+		_ms_dramc(dramc_pdir_value, dram_chann_num);
+
+	MET_BM_Continue();
+	MET_BM_Enable(0);
+	MET_BM_Enable(1);
+
+	return j;
+}
+
+/*======================================================================*/
+/*	MET Device Operations						*/
+/*======================================================================*/
+static int emi_inited;
+
+static int met_emi_create(struct kobject *parent)
+{
+	int ret = 0;
+	int i;
+
+	for (i = 0; i < 21; i++) {
+		ttype_master_val[i] = BM_MASTER_M0;
+		ttype_nbeat_val[i] = BM_TRANS_TYPE_1BEAT;
+		ttype_nbyte_val[i] = BM_TRANS_TYPE_8Byte;
+		ttype_burst_val[i] = BM_TRANS_TYPE_BURST_INCR;
+		ttype_busid_val[i] = 0xffff;
+		ttype_rw_val[i] =  BM_TRANS_RW_DEFAULT;
+	}
+
+	ret = MET_BM_Init();
+	if (ret != 0) {
+		pr_err("MET_BM_Init failed!!!\n");
+		ret = 0;	/* will retry later */
+	} else {
+		emi_inited = 1;
+	}
+
+	kobj_emi = parent;
+
+#define	KOBJ_ATTR_ITEM(attr_name) \
+	ret = sysfs_create_file(kobj_emi, &attr_name##_attr.attr); \
+	if (ret != 0) { \
+		pr_err("Failed to create " #attr_name " in sysfs\n"); \
+		return ret; \
+	}
+	KOBJ_ATTR_LIST
+#undef	KOBJ_ATTR_ITEM
+
+	return ret;
+}
+
+static void met_emi_delete(void)
+{
+#define	KOBJ_ATTR_ITEM(attr_name) \
+	sysfs_remove_file(kobj_emi, &attr_name##_attr.attr);
+	if (kobj_emi != NULL) {
+		KOBJ_ATTR_LIST
+			kobj_emi = NULL;
+	}
+#undef	KOBJ_ATTR_ITEM
+
+	if (emi_inited)
+		MET_BM_DeInit();
+}
+
+static void met_emi_start(void)
+{
+	unsigned int bw_limiter[NIDX_BL];
+
+	if (!emi_inited) {
+		if (MET_BM_Init() != 0) {
+			met_emi.mode = 0;
+			pr_err("MET_BM_Init failed!!!\n");
+			return;
+		}
+		emi_inited = 1;
+	}
+
+	if (do_emi()) {
+		emi_init();
+		emi_stop();
+		emi_start();
+
+		/* Draw the first BW Limiter point */
+		if (bw_limiter_enable == BM_BW_LIMITER_ENABLE) {
+			emi_bw_limiter(bw_limiter);
+			ms_bw_limiter(NIDX_BL, bw_limiter);
+			/* init countdown value */
+			countdown = CNT_COUNTDOWN;
+		}
+	}
+}
+
+static void met_emi_stop(void)
+{
+	unsigned int	bw_limiter[NIDX_BL];
+
+	if (!emi_inited)
+		return;
+
+	if (met_emi_regdump == 1)
+		emi_dump_reg();
+
+	if (do_emi()) {
+		/* Draw the last BW Limiter point */
+		if (bw_limiter_enable == BM_BW_LIMITER_ENABLE) {
+			/*
+			 * Skip drawing when we just draw
+			 * the point at last polling.
+			 */
+			if (countdown < CNT_COUNTDOWN) {
+				emi_bw_limiter(bw_limiter);
+				ms_bw_limiter(NIDX_BL, bw_limiter);
+			}
+		}
+
+		emi_stop();
+		emi_uninit();
+	}
+}
+
+static void met_emi_polling(unsigned long long stamp, int cpu)
+{
+	unsigned int	emi_value[NIDX];
+	unsigned int	emi_tsct[3];
+	unsigned int	emi_ttype_value[21];
+	unsigned int	dramc_pdir_value[DRAMC_Debug_MAX_CNT*NCH];
+	unsigned int	emi_mdct_value[2];
+
+	if (!do_emi())
+		return;
+
+	/* get emi & dramc counters */
+	emi_value[0] = 0;	/* 0: pure linux MET , 0xa5: OnDieMET*/
+	emi_value[1] = 0;	/* EBM pause duration (ns)*/
+	emi_polling(emi_value+2, emi_tsct, emi_ttype_value, dramc_pdir_value, emi_mdct_value);
+
+	/* get and output BW Limiter */
+	if (bw_limiter_enable == BM_BW_LIMITER_ENABLE) {
+		unsigned int bw_limiter[NIDX_BL];
+
+		if (countdown > 0) {
+			countdown--;
+		} else {
+			emi_bw_limiter(bw_limiter);
+			ms_bw_limiter(NIDX_BL, bw_limiter);
+			/* reload countdown value */
+			countdown = CNT_COUNTDOWN;
+		}
+	}
+
+	/* output emi */
+	ms_emi(NIDX_EMI-NTTYPE + (NCNT*dram_chann_num), emi_value);
+	/* output tsct*/
+
+
+
+	if (emi_tsct_enable == 1)
+		ms_emi_tsct(3, emi_tsct);
+
+	/* output mdct*/
+	if (emi_mdct_enable == 1)
+		ms_emi_mdct(2, emi_mdct_value);
+
+	/* output dramc*/
+	if (dramc_pdir_enable == 1)
+		ms_dramc(DRAMC_Debug_MAX_CNT*dram_chann_num, dramc_pdir_value);
+
+	/* output ms_ttype */
+	if ((ttype1_16_en == BM_TTYPE1_16_ENABLE) && (ttype17_21_en == BM_TTYPE17_21_ENABLE))
+		ms_ttype(21, emi_ttype_value);
+	else if (ttype17_21_en == BM_TTYPE17_21_ENABLE)
+		ms_ttype(5, (emi_ttype_value + 16));
+
+	/* adjust MDMCU buffer */
+	if (mdmcu_sel_enable == 1)
+		MET_BM_SetMDCT_MDMCU(rd_mdmcu_rsv_num);
+}
+
+static char help[] = "  --emi                                 monitor EMI banwidth\n";
+static int emi_print_help(char *buf, int len)
+{
+	return snprintf(buf, PAGE_SIZE, help);
+}
+
+
+#define TTYPE_NAME_STR_LEN  64
+static char ttype_name[21][TTYPE_NAME_STR_LEN];
+static int emi_print_header(char *buf, int len)
+{
+	int	ret = 0;
+	int	ret_m[21];
+	int	i = 0;
+	unsigned int	dram_data_rate_MHz;
+
+	/*ttype header info*/
+	for (i = 0; i < 21; i++) {
+
+		int k;
+
+		/*busid >= 0xffff    not specific bus id , show all on specificmaster*/
+		if (ttype_busid_val[i] >= 0xffff) {
+
+			int j;
+
+			for (j = 0; j < ARRAY_SIZE(ttype_master_list_item); j++) {
+				if (ttype_master_val[i] == ttype_master_list_item[j].key) {
+					ret_m[i] = snprintf(ttype_name[i], TTYPE_NAME_STR_LEN, "ttyp%d_%s",
+						i+1, ttype_master_list_item[j].val);/*master*/
+					break;
+				}
+			}
+			if (j == ARRAY_SIZE(ttype_master_list_item))
+				ret_m[i] = snprintf(ttype_name[i], TTYPE_NAME_STR_LEN, "ttyp%d_%s",
+					i+1, "unknown");
+		} else {
+			ret_m[i] = snprintf(ttype_name[i], TTYPE_NAME_STR_LEN, "ttyp%d_%x",
+				i+1, ttype_busid_val[i]);/*busID*/
+		}
+
+		/*show beat type*/
+		for (k = 0; k < ARRAY_SIZE(ttype_nbeat_list_item); k++) {
+
+			if (ttype_nbeat_val[i] == ttype_nbeat_list_item[k].key)
+				ret_m[i] += snprintf(ttype_name[i]+ret_m[i], TTYPE_NAME_STR_LEN-ret_m[i], "_%d",
+					ttype_nbeat_list_item[k].val);/*beat*/
+		}
+
+		/*show byte type*/
+		for (k = 0; k < ARRAY_SIZE(ttype_nbyte_list_item); k++) {
+
+			if (ttype_nbyte_val[i] == ttype_nbyte_list_item[k].key)
+				ret_m[i] += snprintf(ttype_name[i]+ret_m[i], TTYPE_NAME_STR_LEN-ret_m[i], "x%d",
+					ttype_nbyte_list_item[k].val);/*byte*/
+		}
+
+		/*show burst type*/
+		for (k = 0; k < ARRAY_SIZE(ttype_burst_list_item); k++) {
+
+			if (ttype_burst_val[i] == ttype_burst_list_item[k].key)
+				ret_m[i] += snprintf(ttype_name[i]+ret_m[i], TTYPE_NAME_STR_LEN-ret_m[i], "_%s",
+					ttype_burst_list_item[k].val);/*burst*/
+		}
+
+		/*show rw type*/
+		for (k = 0; k < ARRAY_SIZE(ttype_rw_list_item); k++) {
+
+			if (ttype_rw_val[i] == ttype_rw_list_item[k].key)
+				ret_m[i] += snprintf(ttype_name[i]+ret_m[i], TTYPE_NAME_STR_LEN-ret_m[i], "_%s",
+					ttype_rw_list_item[k].val);/*rw*/
+		}
+	}
+
+	dram_chann_num = MET_EMI_GetDramChannNum();
+	/* met_dram_chann_num_header */
+	/* TBD: what is VID */
+	ret = snprintf(buf, PAGE_SIZE, "met-info [000] 0.0: met_dram_chann_num_header: %d,%d,%d,%d,%d\n",
+			dram_chann_num, DRAM_EMI_BASECLOCK_RATE, DRAM_IO_BUS_WIDTH, DRAM_DATARATE, 0);
+
+	/* metemi_func_opt for middleware */
+	ret += snprintf(buf+ret, PAGE_SIZE-ret, "met-info [000] 0.0: metemi_func_opt_header: %d\n",
+			metemi_func_opt);
+
+	dram_data_rate_MHz = get_dram_data_rate();
+	ret += snprintf(buf+ret, PAGE_SIZE-ret,
+			"met-info [000] 0.0: met_dram_clockrate: %u\n",
+			dram_data_rate_MHz);
+
+	/*master port mapping*/
+	ret += snprintf(buf+ret, PAGE_SIZE-ret,
+			"met-info [000] 0.0: met_emi_mport_map: %s,%s,%s,%s,%s,%s,%s,%s\n",
+			BM_Master_M0_name, BM_Master_M1_name, BM_Master_M2_name, BM_Master_M3_name,
+			BM_Master_M4_name, BM_Master_M5_name, BM_Master_M6_name, BM_Master_M7_name);
+
+	/* not to change by default master port sequency */
+	ret += snprintf(buf+ret, PAGE_SIZE-ret,
+			"met-info [000] 0.0: met_emi_mgroup_map: %x,%x,%x,%x\n",
+			BM_Master_GP_AP, BM_Master_GP_MM, BM_Master_GP_GPU, BM_Master_GP_PERI);
+
+	/* 1 : by ondiemet, 0: by pure linux */
+	ret += snprintf(buf+ret, PAGE_SIZE-ret,
+		       "met-info [000] 0.0: emi_use_ondiemet: %u\n",
+		       emi_use_ondiemet);
+
+	/* msel header */
+	if (msel_enable) {
+		ret += snprintf(buf+ret, PAGE_SIZE-ret,
+				"met-info [000] 0.0: met_emi_msel: %x,%x,%x\n",
+				msel_group1 & BM_MASTER_ALL,
+				msel_group2 & BM_MASTER_ALL,
+				msel_group3 & BM_MASTER_ALL);
+	} else {
+		ret += snprintf(buf+ret, PAGE_SIZE-ret,
+				"met-info [000] 0.0: met_emi_msel: %x,%x,%x\n",
+				BM_Master_GP_1_Default & BM_MASTER_ALL,
+				BM_Master_GP_2_Default & BM_MASTER_ALL,
+				BM_Master_GP_3_Default & BM_MASTER_ALL);
+	}
+
+	/* ms_emi */
+	ret += snprintf(buf+ret, PAGE_SIZE-ret,
+			"# ms_emi: TS0,TS1,GP0_WSCT,GP1_WSCT,GP2_WSCT,GP3_WSCT,");
+	ret += snprintf(buf+ret, PAGE_SIZE-ret,
+			"M0_LATENCY,M1_LATENCY,M2_LATENCY,M3_LATENCY,M4_LATENCY,M5_LATENCY,M6_LATENCY,M7_LATENCY,");
+	ret += snprintf(buf+ret, PAGE_SIZE-ret,
+			"M0_TRANS,M1_TRANS,M2_TRANS,M3_TRANS,M4_TRANS,M5_TRANS,M6_TRANS,M7_TRANS,");
+	ret += snprintf(buf+ret, PAGE_SIZE-ret,
+			"BACT,BSCT,BCNT,");
+
+	for (i = 0; i < dram_chann_num; i++) {
+		if (i != 0)
+			ret += snprintf(buf+ret, PAGE_SIZE-ret,
+				",");
+		ret += snprintf(buf+ret, PAGE_SIZE-ret,
+				"PageHit_%d,PageMiss_%d,InterBank_%d,Idle_%d,", i, i, i, i);
+		ret += snprintf(buf+ret, PAGE_SIZE-ret,
+				"mr4_%d,refresh_pop_%d,freerun_26m_%d,", i, i, i);
+		ret += snprintf(buf+ret, PAGE_SIZE-ret,
+				"read_bytes_%d,write_bytes_%d", i, i);
+	}
+	ret += snprintf(buf+ret, PAGE_SIZE-ret,
+			"\n");
+
+
+	ret += snprintf(buf+ret, PAGE_SIZE-ret,
+			"met-info [000] 0.0: met_emi_header: TS0,TS1,GP0_WSCT,GP1_WSCT,GP2_WSCT,GP3_WSCT,");
+	ret += snprintf(buf+ret, PAGE_SIZE-ret,
+			"M0_LATENCY,M1_LATENCY,M2_LATENCY,M3_LATENCY,M4_LATENCY,M5_LATENCY,M6_LATENCY,M7_LATENCY,");
+	ret += snprintf(buf+ret, PAGE_SIZE-ret,
+			"M0_TRANS,M1_TRANS,M2_TRANS,M3_TRANS,M4_TRANS,M5_TRANS,M6_TRANS,M7_TRANS,");
+	ret += snprintf(buf+ret, PAGE_SIZE-ret,
+			"BACT,BSCT,BCNT,");
+
+	for (i = 0; i < dram_chann_num; i++) {
+		if (i != 0)
+			ret += snprintf(buf+ret, PAGE_SIZE-ret,
+				",");
+		ret += snprintf(buf+ret, PAGE_SIZE-ret,
+				"PageHit_%d,PageMiss_%d,InterBank_%d,Idle_%d,", i, i, i, i);
+		ret += snprintf(buf+ret, PAGE_SIZE-ret,
+				"mr4_%d,refresh_pop_%d,freerun_26m_%d,", i, i, i);
+		ret += snprintf(buf+ret, PAGE_SIZE-ret,
+				"read_bytes_%d,write_bytes_%d", i, i);
+	}
+	ret += snprintf(buf+ret, PAGE_SIZE-ret,
+			"\n");
+
+
+	/*TSCT header*/
+	if (emi_tsct_enable == 1) {
+		ret += snprintf(buf+ret, PAGE_SIZE-ret,
+				"met-info [000] 0.0: ms_ud_sys_header: ms_emi_tsct,");
+		ret += snprintf(buf+ret, PAGE_SIZE-ret,
+				"tsct1,tsct2,tsct3,x,x,x\n");
+	}
+
+
+	/*MDCT header*/
+	if (emi_mdct_enable == 1) {
+		ret += snprintf(buf+ret, PAGE_SIZE-ret,
+				"met-info [000] 0.0: ms_ud_sys_header: ms_emi_mdct,");
+		ret += snprintf(buf+ret, PAGE_SIZE-ret,
+				"RD_ULTRA,RD_MDMCU,d,d\n");
+		ret += snprintf(buf+ret, PAGE_SIZE-ret,
+				"met-info [000] 0.0: ms_ud_sys_description: ms_emi_mdct:");
+		ret += snprintf(buf+ret, PAGE_SIZE-ret,
+				"CHART_TYPE=histogram_edge;STATISTICS_METHOD=histogram_edge;CHART_RESAMPLE_METHOD=del_dup\n");
+	}
+
+	/*ttype header*/
+	if ((ttype1_16_en == BM_TTYPE1_16_ENABLE) && (ttype17_21_en == BM_TTYPE17_21_ENABLE)) {
+		/*header = ttype1~21t*/
+		int i;
+
+		ret += snprintf(buf+ret, PAGE_SIZE-ret,	"met-info [000] 0.0: ms_ud_sys_header: ms_ttype,");
+		for (i = 0; i < 21; i++)
+			ret += snprintf(buf+ret, PAGE_SIZE-ret, "%s,", ttype_name[i]);
+
+		ret += snprintf(buf+ret, PAGE_SIZE-ret, "x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x\n");
+
+	} else if (ttype17_21_en == BM_TTYPE17_21_ENABLE) {
+		/*header = ttype17~21t*/
+		int i;
+
+		ret += snprintf(buf+ret, PAGE_SIZE-ret, "met-info [000] 0.0: ms_ud_sys_header: ms_ttype,");
+
+		for (i = 16; i < 21; i++)
+			ret += snprintf(buf+ret, PAGE_SIZE-ret, "%s,", ttype_name[i]);
+
+		ret += snprintf(buf+ret, PAGE_SIZE-ret, "x,x,x,x,x\n");
+	}
+
+	/* met_bw_limiter_header */
+	if (bw_limiter_enable == BM_BW_LIMITER_ENABLE) {
+		ret += snprintf(buf+ret, PAGE_SIZE-ret,
+				"met-info [000] 0.0: met_bw_limiter_header: CLK,");
+		ret += snprintf(buf+ret, PAGE_SIZE-ret,
+				"ARBA,ARBB,ARBC,ARBD,ARBE,ARBF,ARBG,ARBH,BWCT0,BWCT1,BWCT2,BWCT3,BWCT4,BWST0,BWST1,BWCT0_2ND,BWCT1_2ND,BWST_2ND\n");
+	}
+	/* DRAM DVFS */
+	ret += snprintf(buf+ret, PAGE_SIZE-ret,
+			"met-info [000] 0.0: ms_ud_sys_header: DRAM_DVFS,datarate(MHz),d\n");
+	ret += snprintf(buf+ret, PAGE_SIZE-ret,
+			"met-info [000] 0.0: ms_ud_sys_description: DRAM_DVFS:");
+	ret += snprintf(buf+ret, PAGE_SIZE-ret,
+			"CHART_TYPE=histogram_edge;STATISTICS_METHOD=histogram_edge;CHART_RESAMPLE_METHOD=del_dup\n");
+
+	/*PDIR met_dramc_header*/
+	if (dramc_pdir_enable == 1) {
+		ret += snprintf(buf+ret, PAGE_SIZE-ret,
+			"met-info [000] 0.0: met_dramc_header: ");
+		for (i = 0; i < dram_chann_num; i++) {
+			if (i != 0)
+				ret += snprintf(buf+ret, PAGE_SIZE-ret,
+					",");
+			ret += snprintf(buf+ret, PAGE_SIZE-ret,
+				"rk0_pre_sb_%d,rk0_pre_pd_%d,rk0_act_sb_%d,rk0_act_pd_%d,", i, i, i, i);
+			ret += snprintf(buf+ret, PAGE_SIZE-ret,
+				"rk1_pre_sb_%d,rk1_pre_pd_%d,rk1_act_sb_%d,rk1_act_pd_%d,", i, i, i, i);
+			ret += snprintf(buf+ret, PAGE_SIZE-ret,
+				"rk2_pre_sb_%d,rk2_pre_pd_%d,rk2_act_sb_%d,rk2_act_pd_%d", i, i, i, i);
+		}
+		ret += snprintf(buf+ret, PAGE_SIZE-ret, "\n");
+	}
+	return ret;
+}
+
+struct metdevice met_emi = {
+	.name			= "emi",
+	.owner			= THIS_MODULE,
+	.type			= MET_TYPE_BUS,
+	.create_subfs	= met_emi_create,
+	.delete_subfs	= met_emi_delete,
+	.cpu_related	= 0,
+	.start			= met_emi_start,
+	.stop			= met_emi_stop,
+	.timed_polling	= met_emi_polling,
+	.print_help		= emi_print_help,
+	.print_header	= emi_print_header,
+};
diff --git a/src/devtools/met-driver/met_drv/common/met_kernel_symbol.h b/src/devtools/met-driver/met_drv/common/met_kernel_symbol.h
new file mode 100644
index 0000000..27ff39e
--- /dev/null
+++ b/src/devtools/met-driver/met_drv/common/met_kernel_symbol.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef MET_KERNEL_SYMBOL
+#define MET_KERNEL_SYMBOL
+
+/*lookup symbol*/
+#include <asm/cpu.h>
+#include <linux/kallsyms.h>
+#include <linux/perf_event.h>
+#include <linux/kthread.h>
+
+#if	defined(CONFIG_MET_ARM_32BIT)
+extern void met_get_cpuinfo(int cpu, struct cpuinfo_arm **cpuinfo);
+extern void (*met_get_cpuinfo_symbol)(int cpu, struct cpuinfo_arm **cpuinfo);
+#else
+extern void met_get_cpuinfo(int cpu, struct cpuinfo_arm64 **cpuinfo);
+extern void (*met_get_cpuinfo_symbol)(int cpu, struct cpuinfo_arm64 **cpuinfo);
+#endif
+
+extern void (*tracing_record_cmdline_symbol)(struct task_struct *tsk);
+extern void met_cpu_frequency(unsigned int frequency, unsigned int cpu_id);
+extern void (*met_cpu_frequency_symbol)(unsigned int frequency, unsigned int cpu_id);
+extern void (*met_arch_setup_dma_ops_symbol)(struct device *dev);
+extern u64 (*met_perf_event_read_local_symbol)(struct perf_event *ev);
+extern struct task_struct *(*met_kthread_create_on_cpu_symbol)(int (*threadfn)(void *data),
+				void *data, unsigned int cpu,
+				const char *namefmt);
+extern int (*met_smp_call_function_single_symbol)(int cpu, smp_call_func_t func, void *info, int wait);
+
+extern void met_tracing_record_cmdline(struct task_struct *tsk);
+extern int met_reg_switch(void);
+extern int (*met_reg_switch_symbol)(void);
+extern void met_unreg_switch(void);
+extern void (*met_unreg_switch_symbol)(void);
+extern void met_arch_setup_dma_ops(struct device *dev);
+extern u64 met_perf_event_read_local(struct perf_event *ev);
+extern struct task_struct *met_kthread_create_on_cpu(int (*threadfn)(void *data),
+				void *data, unsigned int cpu,
+				const char *namefmt);
+extern int met_smp_call_function_single(int cpu, smp_call_func_t func, void *info, int wait);
+extern void met_arch_send_call_function_single_ipi(int cpu);
+#endif	/* MET_KERNEL_SYMBOL */
diff --git a/src/devtools/met-driver/met_drv/common/met_main.c b/src/devtools/met-driver/met_drv/common/met_main.c
new file mode 100644
index 0000000..ca7edc4
--- /dev/null
+++ b/src/devtools/met-driver/met_drv/common/met_main.c
@@ -0,0 +1,293 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/hrtimer.h>
+#include <linux/cpu.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/profile.h>
+#include <linux/dcache.h>
+#include <linux/types.h>
+#include <linux/dcookies.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+
+#include <asm/irq_regs.h>
+
+#include "met_struct.h"
+#include "met_drv.h"
+#include "met_kernel_symbol.h"
+#include "interface.h"
+#include <linux/of.h>
+
+
+extern struct device_node *of_root;
+static const char *platform_name;
+
+struct cpu_type_name {
+	char full_name[32];
+	char abbr_name[8];
+};
+
+static struct cpu_type_name met_known_cpu_type[] = {
+	{"arm,cortex-a35", "CA35"},
+	{"arm,cortex-a53", "CA53"},
+	{"arm,cortex-a72", "CA72"},
+	{"arm,cortex-a73", "CA73"},
+};
+#define MET_KNOWN_CPU_TYPE_COUNT \
+	(sizeof(met_known_cpu_type)/sizeof(struct cpu_type_name))
+
+static char met_cpu_topology[64];
+
+#if	defined(CONFIG_MET_ARM_32BIT)
+void (*met_get_cpuinfo_symbol)(int cpu, struct cpuinfo_arm **cpuinfo);
+#else
+void (*met_get_cpuinfo_symbol)(int cpu, struct cpuinfo_arm64 **cpuinfo);
+#endif
+
+void (*tracing_record_cmdline_symbol)(struct task_struct *tsk);
+void (*met_cpu_frequency_symbol)(unsigned int frequency, unsigned int cpu_id);
+int (*met_reg_switch_symbol)(void);
+void (*met_unreg_switch_symbol)(void);
+
+void (*met_arch_setup_dma_ops_symbol)(struct device *dev);
+u64 (*met_perf_event_read_local_symbol)(struct perf_event *ev);
+struct task_struct *(*met_kthread_create_on_cpu_symbol)(int (*threadfn)(void *data),
+				void *data, unsigned int cpu,
+				const char *namefmt);
+int (*met_smp_call_function_single_symbol)(int cpu, smp_call_func_t func, void *info, int wait);
+
+
+const char *met_get_platform_name(void)
+{
+	return platform_name;
+}
+EXPORT_SYMBOL(met_get_platform_name);
+
+static void get_cpu_type_name(const char *compatible, char *cpu_type)
+{
+	int i;
+
+	for (i = 0; i < MET_KNOWN_CPU_TYPE_COUNT; i++) {
+		if (!strncmp(compatible, met_known_cpu_type[i].full_name,
+					strlen(met_known_cpu_type[i].full_name)))
+			strncpy(cpu_type, met_known_cpu_type[i].abbr_name,
+					strlen(met_known_cpu_type[i].abbr_name) + 1);
+	}
+}
+
+static void met_set_cpu_topology(int core_id, int cluster_core_num)
+{
+	int i, buf_len = strlen(met_cpu_topology);
+	struct device_node *node = NULL;
+	const char *prev_cptb = NULL;
+	const char *cptb;
+	char cpu_type[16];
+
+	for (i = 0; i < cluster_core_num; i++) {
+		node = of_get_cpu_node(core_id + i, NULL);
+		if (node) {
+			cptb = of_get_property(node, "compatible", NULL);
+			if (cptb) {
+				get_cpu_type_name(cptb, cpu_type);
+				if (prev_cptb == NULL)
+					/* first write:  write core_type & core_number */
+					buf_len += snprintf(met_cpu_topology + buf_len,
+								sizeof(met_cpu_topology) - buf_len,
+								"%s:%d", cpu_type, core_id + i);
+				else if (!strncmp(prev_cptb, cptb, strlen(prev_cptb)))
+					/* cpu type is the same with before */
+					/* write core_number */
+					buf_len += snprintf(met_cpu_topology + buf_len,
+								sizeof(met_cpu_topology) - buf_len,
+								",%d", core_id + i);
+				else
+					/* cpu type is different with before */
+					/* write core_type & core_number */
+					buf_len += snprintf(met_cpu_topology + buf_len,
+								sizeof(met_cpu_topology) - buf_len,
+								"|%s:%d", cpu_type, core_id + i);
+
+				prev_cptb = cptb;
+			}
+		}
+	}
+}
+
+static int met_create_cpu_topology(void)
+{
+	int i, len;
+	struct device_node *node = NULL;
+	int start_core_id = 0;
+	int cluster_num = 0, cluster_core_num = 0;
+	char cluster_name[16];
+
+	node = of_find_node_by_name(NULL, "cpu-map");
+	if (node) {
+		cluster_num = of_get_child_count(node);
+
+		for (i = 0; i < cluster_num; i++) {
+			snprintf(cluster_name, sizeof(cluster_name), "cluster%d", i);
+			node = of_find_node_by_name(NULL, cluster_name);
+			if (node) {
+				cluster_core_num = of_get_child_count(node);
+
+				/* "|" use to separate different cluster */
+				if (i > 0) {
+					len = strlen(met_cpu_topology);
+					snprintf(met_cpu_topology + len, sizeof(met_cpu_topology) - len, "|");
+				}
+
+				met_set_cpu_topology(start_core_id, cluster_core_num);
+				start_core_id = cluster_core_num;
+			}
+		}
+	}
+
+	return strlen(met_cpu_topology);
+}
+
+static int met_kernel_symbol_get(void)
+{
+	int ret = 0;
+
+	if (met_get_cpuinfo_symbol == NULL)
+		met_get_cpuinfo_symbol = (void *)symbol_get(met_get_cpuinfo);
+	if (met_get_cpuinfo_symbol == NULL)
+		return -2;
+
+	if (tracing_record_cmdline_symbol == NULL)
+		tracing_record_cmdline_symbol = (void *)symbol_get(met_tracing_record_cmdline);
+	if (tracing_record_cmdline_symbol == NULL)
+		ret = -3;
+
+	if (met_cpu_frequency_symbol == NULL)
+		met_cpu_frequency_symbol = (void *)symbol_get(met_cpu_frequency);
+	if (met_cpu_frequency_symbol == NULL)
+		ret = -4;
+
+	if (met_reg_switch_symbol == NULL)
+		met_reg_switch_symbol = (void *)symbol_get(met_reg_switch);
+	if (met_reg_switch_symbol == NULL)
+		ret = -5;
+
+	if (met_unreg_switch_symbol == NULL)
+		met_unreg_switch_symbol = (void *)symbol_get(met_unreg_switch);
+	if (met_unreg_switch_symbol == NULL)
+		ret = -6;
+
+	if (met_arch_setup_dma_ops_symbol == NULL)
+		met_arch_setup_dma_ops_symbol = (void *)symbol_get(met_arch_setup_dma_ops);
+	if (met_arch_setup_dma_ops_symbol == NULL)
+		ret = -7;
+
+	if (met_perf_event_read_local_symbol == NULL)
+		met_perf_event_read_local_symbol = (void *)symbol_get(met_perf_event_read_local);
+	if (met_perf_event_read_local_symbol == NULL)
+		ret = -8;
+
+	if (met_kthread_create_on_cpu_symbol == NULL)
+		met_kthread_create_on_cpu_symbol = (void *)symbol_get(met_kthread_create_on_cpu);
+	if (met_kthread_create_on_cpu_symbol == NULL)
+		ret = -9;
+
+	if (met_smp_call_function_single_symbol == NULL)
+		met_smp_call_function_single_symbol = (void *)symbol_get(met_smp_call_function_single);
+	if (met_smp_call_function_single_symbol == NULL)
+		ret = -10;
+	return ret;
+}
+
+DEFINE_PER_CPU(struct met_cpu_struct, met_cpu);
+
+static int __init met_drv_init(void)
+{
+	int cpu;
+	int ret;
+	int cpu_topology_len;
+	struct met_cpu_struct *met_cpu_ptr;
+
+	for_each_possible_cpu(cpu) {
+		met_cpu_ptr = &per_cpu(met_cpu, cpu);
+		/* snprintf(&(met_cpu_ptr->name[0]), sizeof(met_cpu_ptr->name), "met%02d", cpu); */
+		met_cpu_ptr->cpu = cpu;
+	}
+
+	ret = met_kernel_symbol_get();
+	if (ret) {
+		pr_notice("[MET] met_kernel_symbol_get fail, ret = %d\n", ret);
+		return ret;
+	}
+	fs_reg();
+
+	if (of_root){
+		/*
+			mt6765.dts
+			model = "MT6765";
+			compatible = "mediatek,MT6765";
+			interrupt-parent = <&sysirq>;
+		*/
+		if (of_root->properties) {
+			of_property_read_string(of_root, of_root->properties->name, &platform_name);
+			PR_BOOTMSG("platform_name=%s\n", platform_name);
+		}
+	}
+	if (platform_name) {
+		char buf[7];
+
+		memset(buf, 0x0, 7);
+		buf[0] = 'm';
+		buf[1] = 't';
+		strncpy(&buf[2], &platform_name[11], 4);
+		met_set_platform(buf, 1);
+		PR_BOOTMSG("buf=%s\n", buf);
+	}
+
+	cpu_topology_len = met_create_cpu_topology();
+	if (cpu_topology_len)
+		met_set_topology(met_cpu_topology, 1);
+
+#ifdef MET_PLF_USE
+	core_plf_init();
+#endif
+	return 0;
+}
+
+static void __exit met_drv_exit(void)
+{
+	if (met_cpu_frequency_symbol)
+		symbol_put(met_cpu_frequency);
+	if (met_reg_switch_symbol)
+		symbol_put(met_reg_switch);
+	if (met_unreg_switch_symbol)
+		symbol_put(met_unreg_switch);
+	if (tracing_record_cmdline_symbol)
+		symbol_put(met_tracing_record_cmdline);
+	if (met_get_cpuinfo_symbol)
+		symbol_put(met_get_cpuinfo);
+
+#ifdef MET_PLF_USE
+	core_plf_exit();
+#endif
+	fs_unreg();
+
+}
+module_init(met_drv_init);
+module_exit(met_drv_exit);
+
+MODULE_AUTHOR("DT_DM5");
+MODULE_DESCRIPTION("MET_CORE");
+MODULE_LICENSE("GPL");
diff --git a/src/devtools/met-driver/met_drv/common/met_struct.h b/src/devtools/met-driver/met_drv/common/met_struct.h
new file mode 100644
index 0000000..a74ff78
--- /dev/null
+++ b/src/devtools/met-driver/met_drv/common/met_struct.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MET_STRUCT_H_
+#define _MET_STRUCT_H_
+
+#include <linux/hrtimer.h>
+
+struct met_cpu_struct {
+	struct hrtimer hrtimer;
+	struct delayed_work dwork;
+/* struct kmem_cache *cachep; */
+/* struct list_head sample_head; */
+/* spinlock_t list_lock; */
+/* struct mutex list_sync_lock; */
+	int work_enabled;
+	int cpu;
+	int hrtimer_online_check;
+/* char name[16]; */
+};
+
+DECLARE_PER_CPU(struct met_cpu_struct, met_cpu);
+
+#endif				/* _MET_STRUCT_H_ */
diff --git a/src/devtools/met-driver/met_drv/common/met_tag.h b/src/devtools/met-driver/met_drv/common/met_tag.h
new file mode 100644
index 0000000..04b5e09
--- /dev/null
+++ b/src/devtools/met-driver/met_drv/common/met_tag.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __MET_TAG_EX_H__
+#define __MET_TAG_EX_H__
+
+#ifdef BUILD_WITH_MET
+void force_sample(void *unused);
+#else
+#include <linux/string.h>
+#endif
+
+/* Black List Table */
+struct bltable_t {
+	struct mutex mlock;
+	/* flag - Bit31: Global ON/OFF; Bit0~30: ON/OF slot map of class_id */
+	unsigned int flag;
+	int class_id[MAX_EVENT_CLASS];
+};
+
+extern void met_sched_switch(struct task_struct *prev, struct task_struct *next);
+
+extern int tracing_mark_write(int type, unsigned int class_id,
+		const char *name, unsigned int value,
+		unsigned int value2, unsigned int value3);
+
+#endif				/* __MET_TAG_EX_H__ */
diff --git a/src/devtools/met-driver/met_drv/common/mips_pmu_hw.c b/src/devtools/met-driver/met_drv/common/mips_pmu_hw.c
new file mode 100644
index 0000000..19e836b
--- /dev/null
+++ b/src/devtools/met-driver/met_drv/common/mips_pmu_hw.c
@@ -0,0 +1,438 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <asm/system.h>
+#include <linux/smp.h>
+
+#include "cpu_pmu.h"
+#include "mips_pmu_name.h"
+
+struct chip_pmu {
+	enum cpu_type_enum type;
+	struct pmu_desc **desc;
+	void *refptr;
+	const char *cpu_name;
+	unsigned int pmu_desc_size;
+	unsigned int max_hw_events;
+	unsigned int max_reg_count;
+};
+
+struct pmu_desc *mips_pmu_desc[MIPS_MAX_HWEVENTS];
+
+static struct chip_pmu chips[] = {
+	{CPU_1004K, mips_pmu_desc, (void *)mips_1004k_pmu_desc, "MIPS_1004K",
+	 MIPS_1004K_PMU_DESC_SIZE, MIPS_1004K_PMU_DESC_COUNT, PMU_1004K_MAX_HW_REGS},
+};
+
+static struct chip_pmu chip_unknown = { CPU_UNKNOWN, NULL, NULL, "Unknown CPU", 0, 0, 0 };
+
+#define CHIP_PMU_COUNT (sizeof(chips) / sizeof(struct chip_pmu))
+static struct chip_pmu *chip;
+#define M_CONFIG1_PC    (1 << 4)
+
+#define M_PERFCTL_EXL           (1  <<  0)
+#define M_PERFCTL_KERNEL        (1  <<  1)
+#define M_PERFCTL_SUPERVISOR        (1  <<  2)
+#define M_PERFCTL_USER          (1  <<  3)
+#define M_PERFCTL_INTERRUPT_ENABLE  (1  <<  4)
+#define M_PERFCTL_EVENT(event)      (((event) & 0x3ff)  << 5)
+#define M_PERFCTL_VPEID(vpe)        ((vpe)    << 16)
+
+#ifdef CONFIG_CPU_BMIPS5000
+#define M_PERFCTL_MT_EN(filter)     0
+#else				/* !CONFIG_CPU_BMIPS5000 */
+#define M_PERFCTL_MT_EN(filter)     ((filter) << 20)
+#endif				/* CONFIG_CPU_BMIPS5000 */
+
+#define    M_TC_EN_ALL          M_PERFCTL_MT_EN(0)
+#define    M_TC_EN_VPE          M_PERFCTL_MT_EN(1)
+#define    M_TC_EN_TC           M_PERFCTL_MT_EN(2)
+#define M_PERFCTL_TCID(tcid)        ((tcid)   << 22)
+#define M_PERFCTL_WIDE          (1  << 30)
+#define M_PERFCTL_MORE          (1  << 31)
+#define M_PERFCTL_TC            (1  << 30)
+
+#define M_PERFCTL_COUNT_EVENT_WHENEVER  (M_PERFCTL_EXL |        \
+		M_PERFCTL_KERNEL |      \
+		M_PERFCTL_USER |        \
+		M_PERFCTL_SUPERVISOR |      \
+		M_PERFCTL_INTERRUPT_ENABLE)
+
+#ifdef CONFIG_MIPS_MT_SMP
+#define M_PERFCTL_CONFIG_MASK       0x3fff801f
+#else
+#define M_PERFCTL_CONFIG_MASK       0x1f
+#endif
+#define M_PERFCTL_EVENT_MASK        0xfe0
+
+#define vpe_id()    0
+
+/* To get current TCID*/
+#define read_c0_tcbind() __read_32bit_c0_register($2, 2)
+
+struct cpu_hw_events {
+	unsigned int config_base[MIPS_MAX_HWEVENTS];
+	unsigned int saved_ctrl[MIPS_MAX_HWEVENTS];
+};
+
+DEFINE_PER_CPU(struct cpu_hw_events, cpu_hw_events) = {
+	.config_base = {
+	0, 0, 0, 0}, .saved_ctrl = {
+0, 0, 0, 0},};
+
+static enum cpu_type_enum mips_get_ic(void)
+{
+	unsigned int value = current_cpu_type();
+
+	/* pr_debug("ic value: %X\n", value); */
+	return value;
+}
+
+static int __n_counters(void)
+{
+	if (!(read_c0_config1() & M_CONFIG1_PC))
+		return 0;
+	if (!(read_c0_perfctrl0() & M_PERFCTL_MORE))
+		return 1;
+	if (!(read_c0_perfctrl1() & M_PERFCTL_MORE))
+		return 2;
+	if (!(read_c0_perfctrl2() & M_PERFCTL_MORE))
+		return 3;
+
+	return 4;
+}
+
+static int n_counters(void)
+{
+	int counters;
+
+	switch (current_cpu_type()) {
+	case CPU_R10000:
+		counters = 2;
+		break;
+	case CPU_R12000:
+	case CPU_R14000:
+		counters = 4;
+		break;
+	default:
+		counters = __n_counters();
+		break;
+	}
+
+	return counters;
+}
+
+static int mips_pmu_hw_get_counters(void)
+{
+	int count = n_counters();
+
+	/* pr_debug("pmu hw event nr: %d\n", count); */
+	return count;
+}
+
+static unsigned int mipsxx_pmu_swizzle_perf_idx(unsigned int idx)
+{
+	if (vpe_id() == 1)
+		idx = (idx + 2) & 3;
+	return idx;
+}
+
+static void mipsxx_pmu_write_counter(unsigned int idx, u64 val)
+{
+	idx = mipsxx_pmu_swizzle_perf_idx(idx);
+
+	switch (idx) {
+	case 0:
+		write_c0_perfcntr0(val);
+		return;
+	case 1:
+		write_c0_perfcntr1(val);
+		return;
+	case 2:
+		write_c0_perfcntr2(val);
+		return;
+	case 3:
+		write_c0_perfcntr3(val);
+		return;
+	}
+}
+
+static u64 mipsxx_pmu_read_counter(unsigned int idx)
+{
+	idx = mipsxx_pmu_swizzle_perf_idx(idx);
+
+	switch (idx) {
+	case 0:
+		/*
+		 * The counters are unsigned, we must cast to truncate
+		 * off the high bits.
+		 */
+		return (u32) read_c0_perfcntr0();
+	case 1:
+		return (u32) read_c0_perfcntr1();
+	case 2:
+		return (u32) read_c0_perfcntr2();
+	case 3:
+		return (u32) read_c0_perfcntr3();
+	default:
+		WARN_ONCE(1, "Invalid performance counter number (%d)\n", idx);
+		return 0;
+	}
+}
+
+
+static unsigned int mipsxx_pmu_read_control(unsigned int idx)
+{
+	idx = mipsxx_pmu_swizzle_perf_idx(idx);
+
+	switch (idx) {
+	case 0:
+		return read_c0_perfctrl0();
+	case 1:
+		return read_c0_perfctrl1();
+	case 2:
+		return read_c0_perfctrl2();
+	case 3:
+		return read_c0_perfctrl3();
+	default:
+		WARN_ONCE(1, "Invalid performance counter number (%d)\n", idx);
+		return 0;
+	}
+}
+
+static void mipsxx_pmu_write_control(unsigned int idx, unsigned int val)
+{
+	idx = mipsxx_pmu_swizzle_perf_idx(idx);
+
+	switch (idx) {
+	case 0:
+		write_c0_perfctrl0(val);
+		return;
+	case 1:
+		write_c0_perfctrl1(val);
+		return;
+	case 2:
+		write_c0_perfctrl2(val);
+		return;
+	case 3:
+		write_c0_perfctrl3(val);
+		return;
+	}
+}
+
+static int mipsxx_pmu_get_vpeid(void)
+{
+	return read_c0_tcbind() & 0xF;
+}
+
+static void mipsxx_pmu_reset_counters(int idx)
+{
+	switch (idx) {
+	case 3:
+		mipsxx_pmu_write_control(3, 0);
+		mipsxx_pmu_write_counter(3, 0);
+		break;
+	case 2:
+		mipsxx_pmu_write_control(2, 0);
+		mipsxx_pmu_write_counter(2, 0);
+		break;
+	case 1:
+		mipsxx_pmu_write_control(1, 0);
+		mipsxx_pmu_write_counter(1, 0);
+		break;
+	case 0:
+		mipsxx_pmu_write_control(0, 0);
+		mipsxx_pmu_write_counter(0, 0);
+		break;
+	}
+}
+
+static void mipsxx_pmu_enable_event(int idx, int event)
+{
+	struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
+	unsigned long flags;
+
+	WARN_ON(idx < 0 || idx >= chip->max_hw_events);
+	cpuc->saved_ctrl[idx] = M_PERFCTL_EVENT(event & 0xff) |
+	    M_PERFCTL_VPEID(mipsxx_pmu_get_vpeid()) |
+	    (cpuc->config_base[idx] & M_PERFCTL_CONFIG_MASK);
+#ifdef CONFIG_CPU_BMIPS5000
+	/* if (IS_ENABLED(CONFIG_CPU_BMIPS5000)) */
+	/* enable the counter for the calling thread */
+	cpuc->saved_ctrl[idx] |= (1 << (12 + vpe_id())) | M_PERFCTL_TC;
+#endif
+	/*
+	 * To enable pmu count
+	 */
+	local_irq_save(flags);
+	mipsxx_pmu_write_control(idx, cpuc->saved_ctrl[idx]);
+	local_irq_restore(flags);
+}
+
+static void mipsxx_pmu_disable_event(int idx)
+{
+	struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
+	unsigned long flags;
+
+	/* WARN_ON(idx < 0 || idx >= mipspmu.num_counters); */
+	WARN_ON(idx < 0 || idx >= chip->max_hw_events);
+
+	local_irq_save(flags);
+	cpuc->saved_ctrl[idx] = mipsxx_pmu_read_control(idx) & ~M_PERFCTL_COUNT_EVENT_WHENEVER;
+	mipsxx_pmu_write_control(idx, cpuc->saved_ctrl[idx]);
+	local_irq_restore(flags);
+}
+
+static int mips_pmu_hw_get_event_desc(int idx, int event, char *event_desc)
+{
+	int i;
+
+	if (event_desc == NULL) {
+		pr_debug("event_desc is NULL\n");
+		return -1;
+	}
+
+	for (i = 0; i < chip->max_reg_count; i++) {
+		if (chip->desc[idx][i].event == event) {
+			strncpy(event_desc, chip->desc[idx][i].name, MXSIZE_PMU_DESC - 1);
+			break;
+		}
+	}
+	if (i == chip->max_reg_count)
+		return -1;
+
+	return 0;
+}
+
+
+static int mips_pmu_hw_check_event(struct met_pmu *pmu, int idx, int event)
+{
+	int i;
+
+	/* to check index over run */
+	if (!chip)
+		return -1;
+
+	if (idx >= chip->max_hw_events)
+		return -1;
+
+	for (i = 0; i < chip->max_reg_count; i++) {
+		if (chip->desc[idx][i].event == event)
+			break;
+	}
+	if (i == chip->max_reg_count)
+		return -1;
+
+	return 0;
+}
+
+static void mips_pmu_hw_start(struct met_pmu *pmu, int count)
+{
+	int i;
+	int generic = count - 1;
+	struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
+
+	/* pr_debug("hw_start generic: %d\n", generic); */
+	for (i = 0; i < generic; i++) {
+		/* init config */
+		cpuc->config_base[i] = 0;
+		cpuc->config_base[i] |= M_TC_EN_VPE;
+		cpuc->config_base[i] |= M_PERFCTL_USER;
+		cpuc->config_base[i] |= M_PERFCTL_KERNEL;
+		cpuc->config_base[i] |= M_PERFCTL_EXL;
+		cpuc->config_base[i] |= M_PERFCTL_SUPERVISOR;
+		cpuc->config_base[i] &= M_PERFCTL_CONFIG_MASK;
+		 /**/ mipsxx_pmu_reset_counters(i);
+		if (pmu[i].mode == MODE_POLLING)
+			mipsxx_pmu_enable_event(i, pmu[i].event);
+	}
+	if (pmu[count - 1].mode == MODE_POLLING)
+		pr_debug("%s %d BUG!!! index over run!!\n", __func__, __LINE__);
+}
+
+static void mips_pmu_hw_stop(int count)
+{
+	int idx = 0;
+	int generic = count - 1;
+	/* pr_debug("reset %d\n", generic); */
+	for (idx = 0; idx < generic; idx++) {
+		mipsxx_pmu_reset_counters(idx);
+		mipsxx_pmu_disable_event(idx);
+	}
+}
+
+
+static unsigned int mips_pmu_hw_polling(struct met_pmu *pmu, int count, unsigned int *pmu_value)
+{
+	int i, cnt = 0;
+	int generic = count - 1;
+
+	for (i = 0; i < generic; i++) {
+		if (pmu[i].mode == MODE_POLLING) {
+			pmu_value[cnt] = mipsxx_pmu_read_counter(i);
+			cnt++;
+			mipsxx_pmu_reset_counters(i);
+			mipsxx_pmu_enable_event(i, pmu[i].event);
+		}
+	}
+	if (pmu[count - 1].mode == MODE_POLLING) {
+		pr_debug("%s %d BUG!!! index over run!!\n", __func__, __LINE__);
+		pmu_value[cnt] = 0xFFFF;
+		cnt++;
+	}
+
+	return cnt;
+}
+
+
+
+struct cpu_pmu_hw mips_pmu = {
+	.name = "mips_pmu",
+	.get_event_desc = mips_pmu_hw_get_event_desc,
+	.check_event = mips_pmu_hw_check_event,
+	.start = mips_pmu_hw_start,
+	.stop = mips_pmu_hw_stop,
+	.polling = mips_pmu_hw_polling,
+};
+
+struct cpu_pmu_hw *cpu_pmu_hw_init(void)
+{
+	int i = 0;
+	enum cpu_type_enum type;
+	int pmu_hw_count = 0;
+
+	type = mips_get_ic();
+
+	if (CPU_UNKNOWN == type || CPU_LAST == type) {
+		chip = &chip_unknown;
+		return NULL;
+	}
+	for (i = 0; i < CHIP_PMU_COUNT; i++) {
+		if (chips[i].type == type) {
+			chip = &(chips[i]);
+			break;
+		}
+	}
+	if (i == CHIP_PMU_COUNT) {
+		chip = &chip_unknown;
+		return NULL;
+	}
+
+	pmu_hw_count = mips_pmu_hw_get_counters();
+	for (i = 0; i < pmu_hw_count; i++)
+		chip->desc[i] = chip->refptr + (chip->pmu_desc_size * i);
+
+	mips_pmu.nr_cnt = pmu_hw_count + 1;
+	mips_pmu.cpu_name = chip->cpu_name;
+	return &mips_pmu;
+}
diff --git a/src/devtools/met-driver/met_drv/common/mips_pmu_name.h b/src/devtools/met-driver/met_drv/common/mips_pmu_name.h
new file mode 100644
index 0000000..d7ea26a
--- /dev/null
+++ b/src/devtools/met-driver/met_drv/common/mips_pmu_name.h
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+
+#ifndef _MIPS_PMU_NAME_H_
+#define _MIPS_PMU_NAME_H_
+
+/* MIPS 1004K */
+#define MIPS_MAX_HWEVENTS		(4)
+
+#define PMU_1004K_MAX_HW_REGS	(128)
+struct pmu_desc mips_1004k_pmu_desc[][PMU_1004K_MAX_HW_REGS] = {
+	/* COUNT 0 */
+	{
+	 {0, "CPU_CYCLES"},
+	 {1, "CPU_INST"},
+	 {2, "BRANCH_INSNS"},
+	 {3, "JR_31_INSNS"},
+	 {4, "JR_NON_31_INSNS"},
+	 {5, "ITLB_ACCESSES"},
+	 {6, "DTLB_ACCESSES"},
+	 {7, "JTLB_INSN_ACCESSES"},
+	 {8, "JTLB_DATA_ACCESSES"},
+	 {9, "ICACHE_ACCESSES"},
+	 {10, "DCACHE_ACCESSES"},
+	 {11, "DCACHE_MISSES"},
+	 {12, "RESERVED"},
+	 {13, "STORE_MISS_INSNS"},
+	 {14, "INTEGER_INSNS"},
+	 {15, "LOAD_INSNS"},
+	 {16, "J_JAL_INSNS"},
+	 {17, "NO_OPS_INSNS"},
+	 {18, "ALL_STALLS"},
+	 {19, "SC_INSNS"},
+	 {20, "PREFETCH_INSNS"},
+	 {21, "L2_CACHE_WRITEBACKS"},
+	 {22, "L2_CACHE_MISSES"},
+	 {23, "EXCEPTIONS_TAKEN"},
+	 {24, "CACHE_FIXUP_CYCLES"},
+	 {25, "IFU_STALLS"},
+	 {26, "DSP_INSNS"},
+	 {27, "RESERVED"},
+	 {28, "POLICY_EVENTS"},
+	 {29, "ISPRAM_EVENTS"},
+	 {30, "COREEXTEND_EVENTS"},
+	 {31, "YIELD_EVENTS"},
+	 {32, "ITC_LOADS"},
+	 {33, "UNCACHED_LOAD_INSNS"},
+	 {34, "FORK_INSNS"},
+	 {35, "CP2_ARITH_INSNS"},
+	 {36, "INTERVENTION_STALLS"},
+	 {37, "ICACHE_MISS_STALLS"},
+	 {38, "RESERVED"},
+	 {39, "DCACHE_MISS_CYCLES"},
+	 {40, "UNCACHED_STALLS"},
+	 {41, "MDU_STALLS"},
+	 {42, "CP2_STALLS"},
+	 {43, "ISPRAM_STALLS"},
+	 {44, "CACHE_INSN_STALLS"},
+	 {45, "LOAD_USE_STALLS"},
+	 {46, "INTERLOCK_STALLS"},
+	 {47, "RELAX_STALLS"},
+	 {48, "IFU_FB_FULL_REFETCHES"},
+	 {49, "EJTAG_INSN_TRIGGERS"},
+	 {50, "FSB_LESS_25_FULL"},
+	 {51, "FSB_OVER_50_FULL"},
+	 {52, "LDQ_LESS_25_FULL"},
+	 {53, "LDQ_OVER_50_FULL"},
+	 {54, "WBB_LESS_25_FULL"},
+	 {55, "WBB_OVER_50_FULL"},
+	 {56, "INTERVENTION_HIT_COUNT"},
+	 {57, "INVALIDATE_INTERVENTION_COUNT"},
+	 {58, "EVICTION_COUNT"},
+	 {59, "MESI_INVAL_COUNT"},
+	 {60, "MESI_MODIFIED_COUNT"},
+	 {61, "SELF_INTERVENTION_LATENCY"},
+	 {62, "READ_RESPONSE_LATENCY"},
+	 {63, "RESERVED"},
+	 {64, "SI_PCEVENT1"},
+	 {65, "SI_PCEVENT3"},
+	 {66, "SI_PCEVENT5"},
+	 {67, "SI_PCEVENT7"},
+	 {-1, "RESERVED"},
+	 /* 68 - 127 for Reserved */
+	 },
+	/* COUNT 1 */
+	{
+	 {0, "CPU_CYCLES"},
+	 {1, "CPU_INST"},
+	 {2, "MISPREDICTED_BRANCH_INSNS"},
+	 {3, "JR_31_MISPREDICTIONS"},
+	 {4, "JR_31_NO_PREDICTIONS"},
+	 {5, "ITLB_MISSES"},
+	 {6, "DTLB_MISSES"},
+	 {7, "JTLB_INSN_MISSES"},
+	 {8, "JTLB_DATA_MISSES"},
+	 {9, "ICACHE_MISSES"},
+	 {10, "DCACHE_WRITEBACKS"},
+	 {11, "DCACHE_MISSES"},
+	 {12, "RESERVED"},
+	 {13, "LOAD_MISS_INSNS"},
+	 {14, "FPU_INSNS"},
+	 {15, "STORE_INSNS"},
+	 {16, "MIPS16_INSNS"},
+	 {17, "INT_MUL_DIV_INSNS"},
+	 {18, "REPLAYED_INSNS"},
+	 {19, "SC_INSNS_FAILED"},
+	 {20, "CACHE_HIT_PREFETCH_INSNS"},
+	 {21, "L2_CACHE_ACCESSES"},
+	 {22, "L2_CACHE_SINGLE_BIT_ERRORS"},
+	 {23, "SINGLE_THREADED_CYCLES"},
+	 {24, "REFETCHED_INSNS"},
+	 {25, "ALU_STALLS"},
+	 {26, "ALU_DSP_SATURATION_INSNS"},
+	 {27, "MDU_DSP_SATURATION_INSNS"},
+	 {28, "CP2_EVENTS"},
+	 {29, "DSPRAM_EVENTS"},
+	 {30, "RESERVED"},
+	 {31, "ITC_EVENT"},
+	 {33, "UNCACHED_STORE_INSNS"},
+	 {34, "YIELD_IN_COMP"},
+	 {35, "CP2_TO_FROM_INSNS"},
+	 {36, "INTERVENTION_MISS_STALLS"},
+	 {37, "DCACHE_MISS_STALLS"},
+	 {38, "RESERVED"},
+	 /* 38 was listed in OPROFILE web page, but not listed 1004k mips spec */
+	 /* {38, "FSB_INDEX_CONFLICT_STALLS"}, */
+	 {39, "L2_CACHE_MISS_CYCLES"},
+	 {40, "ITC_STALLS"},
+	 {41, "FPU_STALLS"},
+	 {42, "COREEXTEND_STALLS"},
+	 {43, "DSPRAM_STALLS"},
+	 {45, "ALU_TO_AGEN_STALLS"},
+	 {46, "MISPREDICTION_STALLS"},
+	 {47, "RESERVED"},
+	 {48, "FB_ENTRY_ALLOCATED_CYCLES"},
+	 {49, "EJTAG_DATA_TRIGGERS"},
+	 {50, "FSB_25_50_FULL"},
+	 {51, "FSB_FULL_STALLS"},
+	 {52, "LDQ_25_50_FULL"},
+	 {53, "LDQ_FULL_STALLS"},
+	 {54, "WBB_25_50_FULL"},
+	 {55, "WBB_FULL_STALLS"},
+	 {56, "INTERVENTION_COUNT"},
+	 {57, "INVALID_INTERVENT_HIT_CNT"},
+	 {58, "WRITEBACK_COUNT"},
+	 {59, "MESI_EXCLUSIVE_COUNT"},
+	 {60, "MESI_SHARED_COUNT"},
+	 {61, "SELF_INTERVENTION_COUNT"},
+	 {62, "READ_RESPONSE_COUNT"},
+	 {63, "RESERVED"},
+	 {64, "SI_PCEVENT0"},
+	 {65, "SI_PCEVENT2"},
+	 {66, "SI_PCEVENT4"},
+	 {67, "SI_PCEVENT6"},
+	 {-1, "RESERVED"},
+	 },
+};
+
+#define MIPS_1004K_PMU_DESC_SIZE (sizeof(mips_1004k_pmu_desc[0]))
+#define MIPS_1004K_PMU_DESC_COUNT (sizeof(mips_1004k_pmu_desc) / MIPS_1004K_PMU_DESC_SIZE)
+
+#endif				/* _V8_PMU_NAME_H_ */
diff --git a/src/devtools/met-driver/met_drv/common/mtk_dramc.c b/src/devtools/met-driver/met_drv/common/mtk_dramc.c
new file mode 100644
index 0000000..22de7af
--- /dev/null
+++ b/src/devtools/met-driver/met_drv/common/mtk_dramc.c
@@ -0,0 +1,511 @@
+/* Copyright Statement:
+ *
+ * This software/firmware and related documentation ("MediaTek Software") are
+ * protected under relevant copyright laws. The information contained herein
+ * is confidential and proprietary to MediaTek Inc. and/or its licensors.
+ * Without the prior written permission of MediaTek inc. and/or its licensors,
+ * any reproduction, modification, use or disclosure of MediaTek Software,
+ * and information contained herein, in whole or in part, shall be strictly prohibited.
+ */
+/* MediaTek Inc. (C) 2012. All rights reserved.
+ *
+ * BY OPENING THIS FILE, RECEIVER HEREBY UNEQUIVOCALLY ACKNOWLEDGES AND AGREES
+ * THAT THE SOFTWARE/FIRMWARE AND ITS DOCUMENTATIONS ("MEDIATEK SOFTWARE")
+ * RECEIVED FROM MEDIATEK AND/OR ITS REPRESENTATIVES ARE PROVIDED TO RECEIVER ON
+ * AN "AS-IS" BASIS ONLY. MEDIATEK EXPRESSLY DISCLAIMS ANY AND ALL WARRANTIES,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NONINFRINGEMENT.
+ * NEITHER DOES MEDIATEK PROVIDE ANY WARRANTY WHATSOEVER WITH RESPECT TO THE
+ * SOFTWARE OF ANY THIRD PARTY WHICH MAY BE USED BY, INCORPORATED IN, OR
+ * SUPPLIED WITH THE MEDIATEK SOFTWARE, AND RECEIVER AGREES TO LOOK ONLY TO SUCH
+ * THIRD PARTY FOR ANY WARRANTY CLAIM RELATING THERETO. RECEIVER EXPRESSLY ACKNOWLEDGES
+ * THAT IT IS RECEIVER'S SOLE RESPONSIBILITY TO OBTAIN FROM ANY THIRD PARTY ALL PROPER LICENSES
+ * CONTAINED IN MEDIATEK SOFTWARE. MEDIATEK SHALL ALSO NOT BE RESPONSIBLE FOR ANY MEDIATEK
+ * SOFTWARE RELEASES MADE TO RECEIVER'S SPECIFICATION OR TO CONFORM TO A PARTICULAR
+ * STANDARD OR OPEN FORUM. RECEIVER'S SOLE AND EXCLUSIVE REMEDY AND MEDIATEK'S ENTIRE AND
+ * CUMULATIVE LIABILITY WITH RESPECT TO THE MEDIATEK SOFTWARE RELEASED HEREUNDER WILL BE,
+ * AT MEDIATEK'S OPTION, TO REVISE OR REPLACE THE MEDIATEK SOFTWARE AT ISSUE,
+ * OR REFUND ANY SOFTWARE LICENSE FEES OR SERVICE CHARGE PAID BY RECEIVER TO
+ * MEDIATEK FOR SUCH MEDIATEK SOFTWARE AT ISSUE.
+ *
+ * The following software/firmware and/or related documentation ("MediaTek Software")
+ * have been modified by MediaTek Inc. All revisions are subject to any receiver's
+ * applicable license agreements with MediaTek Inc.
+ */
+
+#include <linux/io.h>
+#include <asm/cacheflush.h>
+#include <asm/uaccess.h>
+/* #include <asm/system.h> */
+#include "mtk_dramc.h"
+//#include "x_hal_io.h"
+
+/* #define DRAMC_DEBUG */
+#ifndef DRAMC_DEBUG
+#define DRAMC_LOG(fmt, ...)
+#else
+#define DRAMC_LOG(fmt, arg...) printk("%s:%d:"fmt, __FILE__, __LINE__,##arg);
+#endif
+
+/* #define TEST_AGENT */
+
+
+#if 1
+struct dramc_desc_t dramc_desc[DRAMC_MX_CHANNUM][DRAMC_MX_GRPNUM][DRAMC_MX_NUM_IN_AGRP] = DRAMC_AGENT_TABLE;
+#else
+struct dramc_desc_t dramc_desc[DRAMC_MX_CHANNUM][DRAMC_MX_GRPNUM][DRAMC_MX_NUM_IN_AGRP] =
+{
+/* 	/////////////////////////////////////////////////////////
+	define channel A releationship between group and name
+	///////////////////////////////////////////////////////// */
+	{
+		{
+			/* GRP 1 */
+			{0, 	"00_audio"},
+			{1, 	"01_demux/gcpu/ddi"},
+			{2, 	"02_vbi/3d/tve/dolby"},
+			{3, 	"03_xpscaler_ip/tddc"},
+			{4, 	"04_mib"},
+			{5, 	"05_b2r"},
+			{6, 	"06_cpu"},
+			{7, 	"07_scpos"},
+			{8,	"08_vdec_vld"},
+			{9, 	"09_audio_dsp1"},
+			{10, 	"10_3d_gpu"},
+			{11,	"11_2d_graph/jpgdec/osd/gfx_imgrsz/2d_graph_cmd/irt_dma/png"},
+			{12,	"12_ethernet/demod_isdbt/ci_spi/rs232/usb2/usb3/usb3_d"},
+			{13,	"13_osd"},
+			{14,	"14_venc/vp8_encoder"},
+			{15,	"15_test0/audio_dsp0"},
+			{-1,	""}
+		},
+		{
+			/* GRP 2 */
+			{16,	"16_vdec_lat"},
+			{17,	"17_mmu"},
+			{18,	"18_memc"},
+			{19,	"19_video_imgrsz0/video_imgrsz2"},
+			{20,	"20_arm11"},
+			{21,	"21_msdc/emmc/gdma"},
+			{22,	"22_vdec_mc"},
+			{30,	"30_agent_30"},
+			{-1,	""}
+		},
+		{
+			/* GRP 3 */
+			{23,	"23_cpu_bim_read"},
+			{24,	"24_demux/gcpu/ddi "},
+			{25,	"25_nfi_dma/sfalsh_dma/lzhs/ci_spi"},
+			{26,	"26_rs232"},
+			{27,	"27_agent_27"},
+			{28,	"28_agent_28"},
+			{29,	"29_agent_29"},
+			{31,	"31_agent_31"},
+			{-1,	""}
+		}
+	},
+
+/* 	///////////////////////////////////////////////////////////
+	define channel B releationship between group and name
+	///////////////////////////////////////////////////////// */
+	{
+		{
+			/* GRP 1 */
+			{0, 	"00_audio"},
+			{1, 	"01_demux/gcpu/ddi"},
+			{2, 	"02_vbi/3d/tve/dolby"},
+			{3, 	"03_xpscaler_ip/tddc"},
+			{4, 	"04_mib"},
+			{5, 	"05_b2r"},
+			{6, 	"06_cpu"},
+			{7, 	"07_scpos"},
+			{8,	"08_vdec_vld"},
+			{9, 	"09_audio_dsp1"},
+			{10, 	"10_3d_gpu"},
+			{11,	"11_2d_graph/jpgdec/osd/gfx_imgrsz/2d_graph_cmd/irt_dma/png"},
+			{12,	"12_ethernet/demod_isdbt/ci_spi/rs232/usb2/usb3/usb3_d"},
+			{13,	"13_osd"},
+			{14,	"14_venc/vp8_encoder"},
+			{15,	"15_test0/audio_dsp0"},
+			{-1,	""}
+		},
+		{
+			/* GRP 2 */
+			{16,	"16_vdec_lat"},
+			{17,	"17_mmu"},
+			{18,	"18_memc"},
+			{19,	"19_video_imgrsz0/video_imgrsz2"},
+			{20,	"20_arm11"},
+			{21,	"21_msdc/emmc/gdma"},
+			{22,	"22_vdec_mc"},
+			{30,	"30_agent_30"},
+			{-1,	""}
+		},
+		{
+			/* GRP 3 */
+			{23,	"23_cpu_bim_read"},
+			{24,	"24_demux/gcpu/ddi "},
+			{25,	"25_nfi_dma/sfalsh_dma/lzhs/ci_spi"},
+			{26,	"26_rs232"},
+			{27,	"27_agent_27"},
+			{28,	"28_agent_28"},
+			{29,	"29_agent_29"},
+			{31,	"31_agent_31"},
+			{-1,	""}
+		}
+	},
+
+/* 	///////////////////////////////////////////////////////////
+	define channel C releationship between group and name
+	/////////////////////////////////////////////////////////// */
+	{
+		{
+			/* GRP 1 */
+			{0, 	"00_audio"},
+			{1, 	"01_demux/gcpu/ddi"},
+			{2, 	"02_vbi/3d/tve/dolby"},
+			{3, 	"03_xpscaler_ip/tddc"},
+			{4, 	"04_mib"},
+			{5, 	"05_b2r"},
+			{6, 	"06_cpu"},
+			{7, 	"07_scpos"},
+			{8,	"08_vdec_vld"},
+			{9, 	"09_audio_dsp1"},
+			{10, 	"10_3d_gpu"},
+			{11,	"11_2d_graph/jpgdec/osd/gfx_imgrsz/2d_graph_cmd/irt_dma/png"},
+			{12,	"12_ethernet/demod_isdbt/ci_spi/rs232/usb2/usb3/usb3_d"},
+			{13,	"13_osd"},
+			{14,	"14_venc/vp8_encoder"},
+			{15,	"15_test0/audio_dsp0"},
+			{-1,	""}
+		},
+		{
+			/* GRP 2 */
+			{16,	"16_vdec_lat"},
+			{17,	"17_mmu"},
+			{18,	"18_memc"},
+			{19,	"19_video_imgrsz0/video_imgrsz2"},
+			{20,	"20_arm11"},
+			{21,	"21_msdc/emmc/gdma"},
+			{22,	"22_vdec_mc"},
+			{30,	"30_agent_30"},
+			{-1,	""}
+		},
+		{
+			/* GRP 3 */
+			{23,	"23_cpu_bim_read"},
+			{24,	"24_demux/gcpu/ddi "},
+			{25,	"25_nfi_dma/sfalsh_dma/lzhs/ci_spi"},
+			{26,	"26_rs232"},
+			{27,	"27_agent_27"},
+			{28,	"28_agent_28"},
+			{29,	"29_agent_29"},
+			{31,	"31_agent_31"},
+			{-1,	""}
+		}
+	}
+
+};
+#endif
+
+static unsigned long dramc_base[DRAMC_MX_CHANNUM] =
+{
+	MET_DRAMC0_BASE,
+};
+
+/*
+force to 32 bit means current is 16 bit
+not force to 32 bit means current is 32 bit
+*/
+static unsigned long dramc_ext32_off[DRAMC_MX_CHANNUM] =
+{
+	DRAM_CHA_FORCE32,
+};
+
+
+static inline unsigned int dramc_reg_read(unsigned long addr)
+{
+#if 0
+#ifdef CONFIG_64BIT
+	unsigned int value = 0;
+	value = HAL_READ32((void*)addr);
+#else
+	unsigned int value = readl((void*)addr);
+#endif
+#endif
+
+	unsigned int value = readl((void*)addr);
+
+	mb();
+	return value;
+}
+
+static inline void dramc_reg_write(unsigned long addr, unsigned int value)
+{
+#if 0
+	/* make sure writel() be completed before outer_sync() */
+#ifdef CONFIG_64BIT
+	HAL_WRITE32((void*)addr, value);
+#else
+	writel(value, (void*)addr);
+#endif
+#endif
+
+	writel(value, (void*)addr);
+	mb();
+}
+
+#define DRAMC_SET_VALUE(target, value, shift, bit) \
+do { \
+	volatile u32 temp = dramc_reg_read(target); \
+	u32 mask1 = (~(((0xFFFFFFFF >> (32 - bit))<< shift))); \
+	u32 mask2 = ((0xFFFFFFFF >> (32 - bit))<< shift); \
+	dramc_reg_write(target,(temp & mask1) | ((value << shift) & mask2)); \
+} while (0)
+
+void DRAMC_Init(int chan)
+{
+	unsigned long base = dramc_base[chan];
+	volatile u32 temp_reg = 0;
+
+	DRAMC_LOG("%s chann: %d ext_32_value: %x ext_32bit: %d\n", __FUNCTION__, chan, dramc_ext32_off[chan], DRMAC_IS_FORCE32(dramc_ext32_off[chan]));
+	/* determine the number of dram for the corresponding dramc */
+	DRAMC_SET_VALUE(DRAMC_MON_BM(base),  DRMAC_IS_FORCE32(dramc_ext32_off[chan]), DRAMC_BM_DMBW32B_SHIFT, 1);
+
+	/* disable all group */
+	DRAMC_SET_VALUE(DRAMC_MON_BM(base), 0, DRAMC_BM_GROUP1_ENABLE_SHIFT, 1);
+	DRAMC_SET_VALUE(DRAMC_MON_BM(base), 0, DRAMC_BM_GROUP2_ENABLE_SHIFT, 1);
+	DRAMC_SET_VALUE(DRAMC_MON_BM(base), 0, DRAMC_BM_GROUP3_ENABLE_SHIFT, 1);
+	DRAMC_SET_VALUE(DRAMC_MON_BM(base), 0, DRAMC_BM_FREEZE_SHIFT, 1);
+	temp_reg = dramc_reg_read(DRAMC_MON_BM(base));
+	DRAMC_LOG("%s MON_BM: %X\n", __FUNCTION__, temp_reg);
+
+#ifdef TEST_AGENT
+	u32 reg;
+	reg = base + 0;
+	dramc_reg_write(reg, 0xC050EF00);
+
+	reg = base + 0x100;
+	if (0 == chan)
+		dramc_reg_write(reg, 0x3C553000);
+	else if (1 == chan)
+		dramc_reg_write(reg, 0x5FFD0000);
+	else if (2 == chan)
+		dramc_reg_write(reg, 0x85D20000);
+
+	reg = base + 0x104;
+	dramc_reg_write(reg, 0x8000);
+
+	reg = base + 0x118;
+	dramc_reg_write(reg, 0x0600110D);
+	dramc_reg_write(reg, 0x1600110D);
+
+	reg = base + 0x160;
+	temp_reg = dramc_reg_read(reg);
+#endif
+}
+
+void DRAMC_MaxLtcyMode(int chan, int enable)
+{
+	unsigned long base = dramc_base[chan];
+
+	if (1 == enable) {
+		DRAMC_SET_VALUE(DRAMC_MON_BM(base), 0, DRAMC_BM_PACNTEN, 1);
+		DRAMC_SET_VALUE(DRAMC_MON_BM(base), 1, DRAMC_BM_REQCNTEN, 1);
+	} else {
+		DRAMC_SET_VALUE(DRAMC_MON_BM(base), 0, DRAMC_BM_PACNTEN, 1);
+		DRAMC_SET_VALUE(DRAMC_MON_BM(base), 0, DRAMC_BM_REQCNTEN, 1);
+	}
+}
+
+void DRAMC_Enable(int chan, int group, int agent)
+{
+	unsigned long base = dramc_base[chan];
+	volatile u32 temp_reg = 0;
+
+	/* disable all group */
+	switch (group) {
+	case 1:
+		DRAMC_SET_VALUE(DRAMC_MON_BM(base), agent, DRAMC_BM_GROUP1_AGENT_ID_SHIFT, DRAMC_BM_GROUP1_AGENT_ID_LEN);
+		DRAMC_SET_VALUE(DRAMC_MON_BM(base), 1, DRAMC_BM_GROUP1_ENABLE_SHIFT, 1);
+		break;
+
+	case 2:
+		DRAMC_SET_VALUE(DRAMC_MON_BM(base), agent, DRAMC_BM_GROUP2_AGENT_ID_SHIFT, DRAMC_BM_GROUP2_AGENT_ID_LEN);
+		DRAMC_SET_VALUE(DRAMC_MON_BM(base), 1, DRAMC_BM_GROUP2_ENABLE_SHIFT, 1);
+		break;
+
+	case 3:
+		DRAMC_SET_VALUE(DRAMC_MON_BM(base), agent, DRAMC_BM_GROUP3_AGENT_ID_SHIFT, DRAMC_BM_GROUP3_AGENT_ID_LEN);
+		DRAMC_SET_VALUE(DRAMC_MON_BM(base), 1, DRAMC_BM_GROUP3_ENABLE_SHIFT, 1);
+		break;
+	}
+	temp_reg = dramc_reg_read(DRAMC_MON_BM(base));
+	DRAMC_LOG("%s MON_BM: %X\n", __FUNCTION__, temp_reg);
+}
+
+
+void DRAMC_Disable(int chan)
+{
+	unsigned long base = dramc_base[chan];
+	volatile u32 temp_reg = 0;
+
+	/* disable all group */
+	DRAMC_SET_VALUE(DRAMC_MON_BM(base), 0, DRAMC_BM_GROUP1_ENABLE_SHIFT, 1);
+	DRAMC_SET_VALUE(DRAMC_MON_BM(base), 0, DRAMC_BM_GROUP2_ENABLE_SHIFT, 1);
+	DRAMC_SET_VALUE(DRAMC_MON_BM(base), 0, DRAMC_BM_GROUP3_ENABLE_SHIFT, 1);
+
+	/* fire the BM function */
+	DRAMC_SET_VALUE(DRAMC_MON_BM(base), 0, DRAMC_BM_FREEZE_SHIFT, 1);
+	temp_reg = dramc_reg_read(DRAMC_MON_BM(base));
+	DRAMC_LOG("%s MON_BM: %X\n", __FUNCTION__, temp_reg);
+}
+
+void DRAMC_Freeze(int chan)
+{
+	unsigned long base = dramc_base[chan];
+	volatile u32 temp_reg = dramc_reg_read(DRAMC_MON_BM(base));
+
+	/* Freeze the BM function */
+	DRAMC_SET_VALUE(DRAMC_MON_BM(base), 1, DRAMC_BM_FREEZE_SHIFT, 1);
+	temp_reg = dramc_reg_read(DRAMC_MON_BM(base));
+	DRAMC_LOG("%s MON_BM: %X\n", __FUNCTION__, temp_reg);
+}
+
+void DRAMC_ConfigTargetCount(int chan, u32 count)
+{
+	unsigned long base = dramc_base[chan];
+	volatile u32 temp_reg = 0;
+
+	dramc_reg_write(DRAMC_MON_BMCYC(base), 0xFFFFFFFF);
+	temp_reg = dramc_reg_read(DRAMC_MON_BMCYC(base));
+	DRAMC_LOG("%s MON_BMCYC: %X\n", __FUNCTION__, temp_reg);
+	return;
+}
+
+u32 DRAMC_GetMaxLtcy(int chan)
+{
+	unsigned long base = dramc_base[chan];
+
+	return dramc_reg_read(DRAMC_MON_ROBM4(base));
+}
+
+u32 DRAMC_GetDramcFreq(void)
+{
+	unsigned long tcm_dramc_flags = 0;
+	tcm_dramc_flags = dramc_reg_read(TCM_DRAM_FLAGS_ADDR);
+	return TCMGET_DDR_CLK(tcm_dramc_flags);
+}
+
+
+u32 DRAMC_GetCycleCount(int chan, int group)
+{
+	unsigned long base = dramc_base[chan];
+
+	switch (group) {
+	case 1:
+		return dramc_reg_read(DRAMC_MON_ROBM0(base));
+
+	case 2:
+		return dramc_reg_read(DRAMC_MON_ROBM1(base));
+
+	case 3:
+		return dramc_reg_read(DRAMC_MON_ROBM2(base));
+	}
+	return 0;
+}
+
+u32 DRAMC_GetTotalCycleCount(int chan)
+{
+	unsigned long base = dramc_base[chan];
+
+	return dramc_reg_read(DRAMC_MON_ROBM3(base));
+}
+
+int DRAMC_CheckCntIsOverFlow(u32 count)
+{
+	if (0xFFFFFFFF == count) {
+		return 1;
+	}
+	return 0;
+}
+
+void DRAMC_WriteMode(int chan, int enable)
+{
+	unsigned long base = dramc_base[chan];
+
+	if (1 == enable) {
+		DRAMC_SET_VALUE(DRAMC_MON_BM(base), 1, DRAMC_BM_WREN_SHIFT, 1);
+	} else {
+		DRAMC_SET_VALUE(DRAMC_MON_BM(base), 0, DRAMC_BM_WREN_SHIFT, 1);
+	}
+}
+
+void DRAMC_ReadMode(int chan, int enable)
+{
+	unsigned long base = dramc_base[chan];
+
+	if (1 == enable) {
+		DRAMC_SET_VALUE(DRAMC_MON_BM(base), 1, DRAMC_BM_RDEN_SHIFT, 1);
+	} else {
+		DRAMC_SET_VALUE(DRAMC_MON_BM(base), 0, DRAMC_BM_RDEN_SHIFT, 1);
+	}
+}
+
+unsigned int DRAMC_GetEfuseValue(void)
+{
+	volatile u32 efuse_reg = 0;
+
+	efuse_reg = dramc_reg_read(DRAMC_EFUSE_BIT_ADDRESS);
+
+	return (efuse_reg & DRAMC_EFUSE_BIT_MASK);
+}
+
+void DRAMC_GetGroup1AgentCounter(int channel, unsigned int* counter)
+{
+	unsigned long base = dramc_base[channel];
+	unsigned long agent_base = 0;
+	int		   i = 0;
+
+	agent_base = DRAMC_MON_AGENT_BASE(base);
+	for (i=0; i<DRAMC_MON_AGENT_NUM; i++) {
+		counter[i] = dramc_reg_read(agent_base + i*sizeof(int));
+	}
+	smp_rmb();
+
+	return ;
+}
+
+void DRAMC_GetGroup1Latency(int channel, unsigned int* latency)
+{
+	unsigned long base = dramc_base[channel];
+	unsigned long lty_base = 0;
+	int		   i = 0;
+
+	lty_base = DRAMC_MON_LTY_AGENT_BASE(base);
+	for (i=0; i<DRAMC_MON_LTY_AGENT_NUM; i++) {
+		latency[i] = dramc_reg_read(lty_base + i*sizeof(int));
+	}
+	smp_rmb();
+
+	return ;
+}
+
+void DRAMC_GetGroup1MaxLatency(int channel, unsigned int* max_ltcy)
+{
+	unsigned long base = dramc_base[channel];
+	unsigned long max_lty_base = 0;
+	unsigned int  value = 0;
+	int		   i = 0;
+
+	/* max latency counter is 16 bit*/
+	max_lty_base = DRAMC_MON_MAX_LTY_AGENT_BASE(base);
+	for (i=0; i<DRAMC_MON_LTY_AGENT_NUM/sizeof(short); i++) {
+		value = dramc_reg_read(max_lty_base + i*sizeof(int));
+		max_ltcy[2*i] = value & 0x0000FFFF;
+		max_ltcy[2*i+1] = (value & 0xFFFF0000) >> 16;
+	}
+	smp_rmb();
+
+	return ;
+}
diff --git a/src/devtools/met-driver/met_drv/common/mtk_dramc.h b/src/devtools/met-driver/met_drv/common/mtk_dramc.h
new file mode 100644
index 0000000..1f2e8a1
--- /dev/null
+++ b/src/devtools/met-driver/met_drv/common/mtk_dramc.h
@@ -0,0 +1,300 @@
+/* Copyright Statement:
+ *
+ * This software/firmware and related documentation ("MediaTek Software") are
+ * protected under relevant copyright laws. The information contained herein
+ * is confidential and proprietary to MediaTek Inc. and/or its licensors.
+ * Without the prior written permission of MediaTek inc. and/or its licensors,
+ * any reproduction, modification, use or disclosure of MediaTek Software,
+ * and information contained herein, in whole or in part, shall be strictly prohibited.
+ */
+/* MediaTek Inc. (C) 2012. All rights reserved.
+ *
+ * BY OPENING THIS FILE, RECEIVER HEREBY UNEQUIVOCALLY ACKNOWLEDGES AND AGREES
+ * THAT THE SOFTWARE/FIRMWARE AND ITS DOCUMENTATIONS ("MEDIATEK SOFTWARE")
+ * RECEIVED FROM MEDIATEK AND/OR ITS REPRESENTATIVES ARE PROVIDED TO RECEIVER ON
+ * AN "AS-IS" BASIS ONLY. MEDIATEK EXPRESSLY DISCLAIMS ANY AND ALL WARRANTIES,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NONINFRINGEMENT.
+ * NEITHER DOES MEDIATEK PROVIDE ANY WARRANTY WHATSOEVER WITH RESPECT TO THE
+ * SOFTWARE OF ANY THIRD PARTY WHICH MAY BE USED BY, INCORPORATED IN, OR
+ * SUPPLIED WITH THE MEDIATEK SOFTWARE, AND RECEIVER AGREES TO LOOK ONLY TO SUCH
+ * THIRD PARTY FOR ANY WARRANTY CLAIM RELATING THERETO. RECEIVER EXPRESSLY ACKNOWLEDGES
+ * THAT IT IS RECEIVER'S SOLE RESPONSIBILITY TO OBTAIN FROM ANY THIRD PARTY ALL PROPER LICENSES
+ * CONTAINED IN MEDIATEK SOFTWARE. MEDIATEK SHALL ALSO NOT BE RESPONSIBLE FOR ANY MEDIATEK
+ * SOFTWARE RELEASES MADE TO RECEIVER'S SPECIFICATION OR TO CONFORM TO A PARTICULAR
+ * STANDARD OR OPEN FORUM. RECEIVER'S SOLE AND EXCLUSIVE REMEDY AND MEDIATEK'S ENTIRE AND
+ * CUMULATIVE LIABILITY WITH RESPECT TO THE MEDIATEK SOFTWARE RELEASED HEREUNDER WILL BE,
+ * AT MEDIATEK'S OPTION, TO REVISE OR REPLACE THE MEDIATEK SOFTWARE AT ISSUE,
+ * OR REFUND ANY SOFTWARE LICENSE FEES OR SERVICE CHARGE PAID BY RECEIVER TO
+ * MEDIATEK FOR SUCH MEDIATEK SOFTWARE AT ISSUE.
+ *
+ * The following software/firmware and/or related documentation ("MediaTek Software")
+ * have been modified by MediaTek Inc. All revisions are subject to any receiver's
+ * applicable license agreements with MediaTek Inc.
+ */
+
+#ifndef __MT_DRAMC_H__
+#define __MT_DRAMC_H__
+#include <linux/types.h>
+//#include "x_hal_io.h"
+
+/* define BM function base address */
+#define MET_DRAMC0_BASE                         (0xF0006000)
+#define MET_DRAMC1_BASE                         (0xF0010000)
+#define MET_DRAMC2_BASE                         (0xF0011000)
+#define TCM_DRAM_FLAGS_ADDR                     (0xF00080F8)
+#define DRAM_CLOCK_MASK                         (0x00000FFF)
+
+#define BASE_DDR_CLK                            1000000
+#define TCMGET_DDR_CLK(p)                       ((p & DRAM_CLOCK_MASK) * BASE_DDR_CLK)
+
+/* define BM function offset */
+#define DRAMC_MON_BM(i)                         (i + 0x80)
+#define DRAMC_MON_BMCYC(i)                      (i + 0x8C) /* Bus monitor cycle number, BMCYC */
+#define DRAMC_MON_ROBM0(i)                      (i + 0x90) /* Bus monitor counter for group 1,BMLENGP1_CNT */
+#define DRAMC_MON_ROBM1(i)                      (i + 0x94) /* Bus monitor counter for group 2,BMLENGP2_CNT */
+#define DRAMC_MON_ROBM2(i)                      (i + 0x98) /* Bus monitor counter for group 3,BMLENGP3_CNT */
+#define DRAMC_MON_ROBM3(i)                      (i + 0x9C) /* bus monitor cycle counter,BM_CYC_CNT */
+#define DRAMC_MON_ROBM4(i)                      (i + 0xA0) /* Maximum burst length, 16'h0000, MAX_BSTCNT */
+
+/* define BM function shift */
+#define DRAMC_BM_PACNTEN                        (31) /* Bus monitor function select for number of precharge and active */
+#define DRAMC_BM_REQCNTEN                       (30) /* Bus monitor function select for number of REQ='1'&ALE='1' */
+#define DRAMC_BM_CHGPRIEN                       (29) /* On-line change agent priority enabling */
+#define DRAMC_BM_FREEZE_SHIFT                   (28) /* Bus monitor freeze */
+#define DRAMC_BM_DMBW32B_SHIFT                  (27) /* 1:External 32Bit data bus, 0:External 16Bit data bus */
+#define DRAMC_BM_WREN_SHIFT                     (25) /* Bus monitor only monitor write cycle enabling */
+#define DRAMC_BM_RDEN_SHIFT                     (24) /* Bus monitor only monitor read cycle enabling */
+#define DRAMC_BM_GROUP3_ENABLE_SHIFT            (23) /* Bus monitor for Group 3 agents enabling */
+#define DRAMC_BM_GROUP3_AGENT_ID_SHIFT          (20) /* Bus monitor for Group 3 agent-ID */
+#define DRAMC_BM_GROUP3_AGENT_ID_LEN            (3)
+#define DRAMC_BM_GROUP2_ENABLE_SHIFT            (19) /* Bus monitor for Group 2 agents enabling */
+#define DRAMC_BM_GROUP2_AGENT_ID_SHIFT          (16) /* Bus monitor for Group 2 agent-ID */
+#define DRAMC_BM_GROUP2_AGENT_ID_LEN            (3)
+#define DRAMC_BM_GROUP1_ENABLE_SHIFT            (15) /* Bus monitor for Group 1 agents enabling */
+#define DRAMC_BM_GROUP1_AGENT_ID_SHIFT          (8) /* Bus monitor for Group 1 agent-ID or */
+#define DRAMC_BM_GROUP1_AGENT_ID_LEN            (5)
+#define DRAMC_BM_BSTCNTEN                       (7) /* Monitor burst length enable */
+#define DRAMC_BM_R_DMBSTCNT_SEL                 (6) /* Latency mode select */
+#define DRAMC_BM_R_DMLATRECEN                   (5) /* Agent latency record enable */
+#define DRAMC_BM_BSTCNT                         (0) /* Agent-ID of the burst length monitored agent */
+#define DRAMC_BM_BSTCNT_LEN                     (4)
+
+/* define TCM function base address */
+#ifndef IO_VIRT
+#define IO_VIRT                                 (0xF0000000)
+#endif
+
+#define REG_RW_GPRB6                            (0x00f8) /* RISC Byte General Purpose Register 6 */
+#define TCM_SRAM_ADDR                           (IO_VIRT + 0x8000)
+
+#ifdef CONFIG_64BIT
+#define TCM_DRAM_FLAGS                          (*((volatile unsigned long*)(OFFSET_64BIT + TCM_SRAM_ADDR + REG_RW_GPRB6)))
+#else
+#define TCM_DRAM_FLAGS                          (*((volatile unsigned long*)(TCM_SRAM_ADDR + REG_RW_GPRB6)))
+#endif
+
+#define CHA_FORCE32_SHIFT                       (22)
+#define DRAM_CHA_FORCE32                        (1U << CHA_FORCE32_SHIFT)
+#define CHB_FORCE32_SHIFT                       (23)
+#define DRAM_CHB_FORCE32                        (1U << CHB_FORCE32_SHIFT)
+#define CHC_FORCE32_SHIFT                       (31)
+#define DRAM_CHC_FORCE32                        (1U << CHC_FORCE32_SHIFT)
+
+
+/* to check dramc is ext 32 bit or not*/
+#define DRMAC_IS_FORCE32(p)                     ((TCM_DRAM_FLAGS & p) ? 0 : 1)
+
+/* generate random cycle to return fake report */
+/* #define DRAMC_FAKE_REPORT */
+
+/* define dramc spec */
+#define DRAMC_MX_CHANNUM                        (1)
+#define DRAMC_MX_GRPNUM                         (3)
+#define DRAMC_MX_NUM_IN_AGRP                    (32)
+#define DRAMC_MX_AGENT_NAMEBUF                  (96)
+
+/* define all group id */
+#define DRAMC_ALL_GROUP_AGENT_ID                (0x20)
+#define DRAMC_MX_LATENCY                        (0x21)
+
+/* efuse bit[15:14]
+00:4G
+01:3G
+10:2G
+11:1G */
+#define DRAMC_EFUSE_BIT_ADDRESS                 (0xF0008664)
+#define DRAMC_EFUSE_BIT_MASK                    (0x0000C000)
+#define DRAMC_EFUSE_BIT_1G                      (0x0000C000)
+#define DRAMC_EFUSE_BIT_2G                      (0x00008000)
+#define DRAMC_EFUSE_BIT_3G                      (0x00004000)
+#define DRAMC_EFUSE_BIT_4G                      (0x00000000)
+
+#define DRAM_DDRPHY_CHA_BANK                    (IO_VIRT + 0x7A000)
+#define DRAM_DDRPHY_CHB_BANK                    (IO_VIRT + 0x7B000)
+#define DRAM_DDRPHY_CHC_BANK                    (IO_VIRT + 0x7C000)
+
+#define DRAM_BASE                               (IO_VIRT + 0x07000)
+#define DRAM_CHB_BASE                           (IO_VIRT + 0x0F000)
+#define DRAM_CHC_BASE                           (IO_VIRT + 0x15000)
+
+/* Bandwidth counter for agent0-15 */
+#define DRAMC_MON_AGENT_NUM                     (16)
+#define DRAMC_MON_AGENT_BASE(i)                 (i + 0x2C0)
+#define DRAMC_MON_AGENT0(i)                     (i + 0x2C0)
+#define DRAMC_MON_AGENT1(i)                     (i + 0x2C4)
+#define DRAMC_MON_AGENT2(i)                     (i + 0x2C8)
+#define DRAMC_MON_AGENT3(i)                     (i + 0x2CC)
+#define DRAMC_MON_AGENT4(i)                     (i + 0x2D0)
+#define DRAMC_MON_AGENT5(i)                     (i + 0x2D4)
+#define DRAMC_MON_AGENT6(i)                     (i + 0x2D8)
+#define DRAMC_MON_AGENT7(i)                     (i + 0x2DC)
+#define DRAMC_MON_AGENT8(i)                     (i + 0x2E0)
+#define DRAMC_MON_AGENT9(i)                     (i + 0x2E4)
+#define DRAMC_MON_AGENT10(i)                    (i + 0x2E8)
+#define DRAMC_MON_AGENT11(i)                    (i + 0x2EC)
+#define DRAMC_MON_AGENT12(i)                    (i + 0x2F0)
+#define DRAMC_MON_AGENT13(i)                    (i + 0x2F4)
+#define DRAMC_MON_AGENT14(i)                    (i + 0x2F8)
+#define DRAMC_MON_AGENT15(i)                    (i + 0x2FC)
+
+/* total latency counter for agent0-15 */
+#define DRAMC_MON_LTY_AGENT_NUM                 (16)
+#define DRAMC_MON_LTY_AGENT_BASE(i)             (i + 0x300)
+#define DRAMC_MON_LTY_AGENT0(i)                 (i + 0x300)
+#define DRAMC_MON_LTY_AGENT1(i)                 (i + 0x304)
+#define DRAMC_MON_LTY_AGENT2(i)                 (i + 0x308)
+#define DRAMC_MON_LTY_AGENT3(i)                 (i + 0x30C)
+#define DRAMC_MON_LTY_AGENT4(i)                 (i + 0x310)
+#define DRAMC_MON_LTY_AGENT5(i)                 (i + 0x314)
+#define DRAMC_MON_LTY_AGENT6(i)                 (i + 0x318)
+#define DRAMC_MON_LTY_AGENT7(i)                 (i + 0x31C)
+#define DRAMC_MON_LTY_AGENT8(i)                 (i + 0x320)
+#define DRAMC_MON_LTY_AGENT9(i)                 (i + 0x324)
+#define DRAMC_MON_LTY_AGENT10(i)                (i + 0x328)
+#define DRAMC_MON_LTY_AGENT11(i)                (i + 0x32C)
+#define DRAMC_MON_LTY_AGENT12(i)                (i + 0x330)
+#define DRAMC_MON_LTY_AGENT13(i)                (i + 0x334)
+#define DRAMC_MON_LTY_AGENT14(i)                (i + 0x338)
+#define DRAMC_MON_LTY_AGENT15(i)                (i + 0x33C)
+
+/* max latency counter for agent0-15 */
+#define DRAMC_MON_MAX_LTY_AGENT_NUM             (16)
+#define DRAMC_MON_MAX_LTY_AGENT_BASE(i)         (i + 0x340)
+#define DRAMC_MON_MAX_LTY_AGENT0(i)             (i + 0x340)
+#define DRAMC_MON_MAX_LTY_AGENT1(i)             (i + 0x340)
+#define DRAMC_MON_MAX_LTY_AGENT2(i)             (i + 0x344)
+#define DRAMC_MON_MAX_LTY_AGENT3(i)             (i + 0x344)
+#define DRAMC_MON_MAX_LTY_AGENT4(i)             (i + 0x348)
+#define DRAMC_MON_MAX_LTY_AGENT5(i)             (i + 0x348)
+#define DRAMC_MON_MAX_LTY_AGENT6(i)             (i + 0x34C)
+#define DRAMC_MON_MAX_LTY_AGENT7(i)             (i + 0x34C)
+#define DRAMC_MON_MAX_LTY_AGENT8(i)             (i + 0x350)
+#define DRAMC_MON_MAX_LTY_AGENT9(i)             (i + 0x350)
+#define DRAMC_MON_MAX_LTY_AGENT10(i)            (i + 0x354)
+#define DRAMC_MON_MAX_LTY_AGENT11(i)            (i + 0x354)
+#define DRAMC_MON_MAX_LTY_AGENT12(i)            (i + 0x358)
+#define DRAMC_MON_MAX_LTY_AGENT13(i)            (i + 0x358)
+#define DRAMC_MON_MAX_LTY_AGENT14(i)            (i + 0x35C)
+#define DRAMC_MON_MAX_LTY_AGENT15(i)            (i + 0x35C)
+
+struct dramc_desc_t {
+    int agent_id;
+    char name[DRAMC_MX_AGENT_NAMEBUF];
+};
+
+#define DRAMC_AGENT_TABLE \
+{ \
+	/* define channel A releationship between group and name */ \
+	{ \
+		{ \
+			/* GRP 1 */ \
+			{0, 	"00_audio"}, \
+			{1, 	"01_demux/gcpu/ddi"}, \
+			{2, 	"02_vbi/3d/tve"}, \
+			{3, 	"03_xpscaler_ip/tddc"}, \
+			{4, 	"04_none"}, \
+			{5, 	"05_audio_dsp_low1"}, \
+			{6, 	"06_2d_graph/2dgraph_cmd/irt_dma"}, \
+			{7, 	"07_ether/ci-spi/rs232"}, \
+			{8,	"08_demod_isdbt"}, \
+			{9, 	"09_usb2"}, \
+			{10, 	"10_usb3_d"}, \
+			{11,	"11_mmu"}, \
+			{12,	"12_arm11"}, \
+			{13,	"13_msdc/emmc"}, \
+			{14,	"14_gdma"}, \
+			{15,	"15_test0/audio_dsp0"}, \
+			{-1,	""} \
+		}, \
+		{ \
+			/* GRP 2 */ \
+			{16,	"16_nfi_dma/sfalsh_dma/lzhs/ci_spi"}, \
+			{17,	"17_ufozip"}, \
+			{18,	"18_usb3_c"}, \
+			{19,	"19_none"}, \
+			{20,	"20_none"}, \
+			{21,	"21_none"}, \
+			{22,	"22_none"}, \
+			{30,	"30_none"}, \
+			{-1,	""} \
+		}, \
+		{ \
+			/* GRP 3 */ \
+			{23,	"23_none"}, \
+			{24,	"24_none"}, \
+			{25,	"25_none"}, \
+			{26,	"26_none"}, \
+			{27,	"27_none"}, \
+			{28,	"28_none"}, \
+			{29,	"29_none"}, \
+			{31,	"31_none"}, \
+			{-1,	""} \
+		} \
+	} \
+}
+
+/* some marco and static inline function define */
+static inline int FIND_NEXT_AGENT(u64 mask, int agent)
+{
+    int idx = 0;
+    int mx_cnt = sizeof(u64)*8;
+    int temp_agent = agent + 1;
+
+    for (idx = 0 ; idx < mx_cnt; idx++,temp_agent++) {
+        if (mx_cnt == temp_agent)
+            temp_agent = 0;
+        if ((1ULL<<temp_agent) & mask)
+            break;
+    }
+    if (idx == mx_cnt)
+        return -1;
+    return temp_agent;
+}
+
+#define GROUP_ID(agent) ((agent <= 0xF)?1:((agent<=0x17)?2:3))
+#define GROUP1_MASK     (0xFFFF)
+
+
+/* function prototype */
+extern void DRAMC_Init(int chan);
+extern void DRAMC_MaxLtcyMode(int chan, int enable);
+extern void DRAMC_Enable(int chan, int group, int agent);
+extern void DRAMC_Disable(int chan);
+extern void DRAMC_Freeze(int chan);
+extern void DRAMC_ConfigTargetCount(int chan, u32 count);
+extern u32 DRAMC_GetMaxLtcy(int chan);
+extern u32 DRAMC_GetCycleCount(int chan, int group);
+extern u32 DRAMC_GetTotalCycleCount(int chan);
+extern u32 DRAMC_GetDramcFreq(void);
+extern int DRAMC_CheckCntIsOverFlow(u32 count);
+
+extern void DRAMC_WriteMode(int chan, int enable);
+extern void DRAMC_ReadMode(int chan, int enable);
+extern unsigned int DRAMC_GetEfuseValue(void);
+
+extern void DRAMC_GetGroup1AgentCounter(int chan, unsigned int* counter);
+extern void DRAMC_GetGroup1Latency(int chan, unsigned int* latency);
+extern void DRAMC_GetGroup1MaxLatency(int chan, unsigned int* max_ltcy);
+
+#endif
diff --git a/src/devtools/met-driver/met_drv/common/mtk_emi_bm.c b/src/devtools/met-driver/met_drv/common/mtk_emi_bm.c
new file mode 100644
index 0000000..d7ecb7c
--- /dev/null
+++ b/src/devtools/met-driver/met_drv/common/mtk_emi_bm.c
@@ -0,0 +1,1362 @@
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/io.h>
+#include <asm/cacheflush.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+//#include <mt-plat/sync_write.h>
+//#include <mt-plat/mtk_io.h>
+//#include "x_hal_io.h"
+//#include "x_hal_5381.h"
+#include "mtk_typedefs.h"
+#include "core_plf_init.h"
+#include "mtk_emi_bm.h"
+#include "mtk_dramc.h"
+#include "met_drv.h"
+#include "interface.h"
+
+#define IOMEM(x)	((void __force __iomem *)(x))
+
+#define mt_reg_sync_writel(v, a) \
+	do {    \
+		__raw_writel((v), (void __force __iomem *)((a)));   \
+		mb();  \
+	} while (0)
+
+#if 0
+#undef  MET_BOOT_MSG
+#if     defined(MET_BOOT_MSG)
+extern char met_boot_msg_tmp[256];
+extern int pr_bootmsg(int str_len, char *str);
+#define PR_BOOTMSG(fmt, args...) { \
+        int str_len = snprintf(met_boot_msg_tmp, sizeof(met_boot_msg_tmp), \
+                               fmt, ##args); \
+        pr_bootmsg(str_len, met_boot_msg_tmp); }
+#define PR_BOOTMSG_ONCE(fmt, args...) { \
+        static int once; \
+        if (!once) { \
+                int str_len = snprintf(met_boot_msg_tmp, \
+                                       sizeof(met_boot_msg_tmp), \
+                                       fmt, ##args); \
+                pr_bootmsg(str_len, met_boot_msg_tmp); \
+                once = 1; \
+        } }
+#else
+#define pr_bootmsg(str_len, str)
+#define PR_BOOTMSG(fmt, args...)
+#define PR_BOOTMSG_ONCE(fmt, args...)
+#endif
+#endif
+
+//static unsigned long gEMIBaseAddr = EMI_REG_BASE;
+
+
+/* MET internal define */
+#ifndef DRAMC0_NAO_BASE
+#define DRAMC0_NAO_BASE             (IO_VIRT + 0x204000)
+#endif
+#ifndef DRAMC1_NAO_BASE
+#define DRAMC1_NAO_BASE             (IO_VIRT + 0x20C000)
+#endif
+#ifndef DRAMC2_NAO_BASE
+#define DRAMC2_NAO_BASE             (IO_VIRT + 0x214000)
+#endif
+#ifndef DRAMC3_NAO_BASE
+#define DRAMC3_NAO_BASE             (IO_VIRT + 0x21C000)
+#endif
+static unsigned long gDRAMCBaseAddr[] =
+{
+	DRAMC0_NAO_BASE,
+	DRAMC1_NAO_BASE,
+	DRAMC2_NAO_BASE,
+	DRAMC3_NAO_BASE,
+};
+
+#define MX_DRAMC_CHAN_NR (sizeof(gDRAMCBaseAddr)/sizeof(gDRAMCBaseAddr[0]))
+
+/* GET EMI Base Address */
+#if 0
+unsigned long get_emi_base_addr(void)
+{
+	return gEMIBaseAddr;
+}
+#endif
+
+unsigned long get_dram_nao_base_addr(int idx)
+{
+	if (idx >= MX_DRAMC_CHAN_NR) {
+		METERROR("Invalid DRAMC idx: %d !\n", idx);
+		PR_BOOTMSG_ONCE("Invalid DRAMC idx: %d !\n", idx);
+		return 0;
+	}
+	return gDRAMCBaseAddr[idx];
+}
+
+#undef	DEBUG
+#undef	debug_reg
+
+#ifdef	debug_reg
+static inline unsigned int emi_readl(void __iomem *padr)
+{
+	unsigned int tmp;
+
+	tmp = readl(padr);
+	MET_TRACE("[MET_EMI] RD_Reg: %p: %08x\n", padr, tmp);
+	return tmp;
+}
+
+static inline void __emi_reg_sync_writel(unsigned int data, void __iomem *padr)
+{
+	unsigned int tmp;
+
+	mt_reg_sync_writel(data, padr);
+	tmp = readl(padr);
+	MET_TRACE("[MET_EMI] WR_Reg: %p: %08x, %08x\n", padr, data, tmp);
+}
+
+#define emi_reg_sync_writel(data, adr)	__emi_reg_sync_writel(data, IOMEM(adr))
+
+#else
+#define	emi_readl		readl
+#define	emi_reg_sync_writel	mt_reg_sync_writel
+#endif
+
+#define MASK_MASTER	0xFF
+#define MASK_TRANS_TYPE	0xFF
+
+static void __iomem *BaseAddrEMI;
+static void __iomem *BaseAddrDRAMC0;
+static void __iomem *BaseAddrDRAMC1;
+static void __iomem *BaseAddrDRAMC2;
+static void __iomem *BaseAddrDRAMC3;
+static void __iomem *AddrDramDatarate;
+
+/*
+*   MET_REG_BSET/MET_REG_BCLR:
+*   reading value before set and clear
+*/
+static inline void MET_REG_BSET(unsigned long reg, u32 shift)
+{
+	volatile unsigned int read_val = 0;
+
+	read_val = emi_readl(IOMEM(reg));
+	emi_reg_sync_writel(read_val | (1<<shift), reg);
+}
+
+static inline void MET_REG_BCLR(unsigned long reg, u32 shift)
+{
+	volatile unsigned int read_val = 0;
+
+	read_val = emi_readl(IOMEM(reg));
+	emi_reg_sync_writel(read_val & (~((1<<shift) & 0xFFFFFFFF)), reg);
+}
+
+#define GET_EMI_FROM_DTS
+//#undef GET_EMI_FROM_DTS
+
+int MET_BM_Init(void)
+{
+#ifdef GET_EMI_FROM_DTS
+	struct device_node *node;
+
+	/* Device Tree Path */
+	node = of_find_compatible_node(NULL, NULL, of_emi_desc);
+	if (!node) {
+		METINFO("node of_emi_desc not found\n");
+		PR_BOOTMSG_ONCE("node of_emi_desc not found\n");
+		return -1;
+	}
+
+	BaseAddrEMI = (void *)of_iomap(node, 0);
+#else
+	BaseAddrEMI = (void*)get_emi_base_addr();
+#endif
+	if (BaseAddrEMI == 0) {
+		METERROR("BaseAddrEMI = 0\n");
+		PR_BOOTMSG_ONCE("BaseAddrEMI = 0\n");
+		return -1;
+	}
+	METINFO("MET EMI: map emi to %p\n", BaseAddrEMI);
+	PR_BOOTMSG("MET EMI: map emi to %p\n", BaseAddrEMI);
+
+#ifdef GET_EMI_FROM_DTS
+	/* DRAMC TBD */
+	/* MAP NAO (NON-AO) DRAMC address */
+	node = of_find_compatible_node(NULL, NULL, of_dramc_desc);
+	if (!node) {
+		METINFO("node of_dramc_desc not found\n");
+		PR_BOOTMSG_ONCE("node of_dramc_desc not found\n");
+		return -1;
+	}
+	BaseAddrDRAMC0 = (void *)of_iomap(node, 0);
+	BaseAddrDRAMC1 = (void *)of_iomap(node, 1);
+	BaseAddrDRAMC2 = (void *)of_iomap(node, 2);
+	BaseAddrDRAMC3 = (void *)of_iomap(node, 3);
+#else
+	BaseAddrDRAMC0 = (void *) get_dram_nao_base_addr(0);
+	BaseAddrDRAMC1 = (void *) get_dram_nao_base_addr(1);
+	BaseAddrDRAMC2 = (void *) get_dram_nao_base_addr(2);
+	BaseAddrDRAMC3 = (void *) get_dram_nao_base_addr(3);
+#endif
+
+	AddrDramDatarate = ioremap_nocache(0x10100000, 0x1000);
+	if (AddrDramDatarate == NULL) {
+		pr_err("ioremap AddrDramDatarate fail ...\n");
+		return -1;
+	}
+
+	if (BaseAddrDRAMC0 == 0
+		|| BaseAddrDRAMC1 == 0
+		|| BaseAddrDRAMC2 == 0
+		|| BaseAddrDRAMC3 == 0
+	) {
+		METERROR("BaseAddrDRAMC0 = %p\n", BaseAddrDRAMC0);
+		METERROR("BaseAddrDRAMC1 = %p\n", BaseAddrDRAMC1);
+		METERROR("BaseAddrDRAMC2 = %p\n", BaseAddrDRAMC2);
+		METERROR("BaseAddrDRAMC3 = %p\n", BaseAddrDRAMC3);
+		PR_BOOTMSG_ONCE("BaseAddrDRAMC0 = %p\n", BaseAddrDRAMC0);
+		PR_BOOTMSG_ONCE("BaseAddrDRAMC1 = %p\n", BaseAddrDRAMC1);
+		PR_BOOTMSG_ONCE("BaseAddrDRAMC2 = %p\n", BaseAddrDRAMC2);
+		PR_BOOTMSG_ONCE("BaseAddrDRAMC3 = %p\n", BaseAddrDRAMC3);
+		return -1;
+	}
+	METINFO("MET EMI: map nao dramcA to %p\n", BaseAddrDRAMC0);
+	METINFO("MET EMI: map nao dramcB to %p\n", BaseAddrDRAMC1);
+	METINFO("MET EMI: map nao dramcC to %p\n", BaseAddrDRAMC2);
+	METINFO("MET EMI: map nao dramcD to %p\n", BaseAddrDRAMC3);
+	PR_BOOTMSG("MET EMI: map nao dramcA to %p\n", BaseAddrDRAMC0);
+	PR_BOOTMSG("MET EMI: map nao dramcB to %p\n", BaseAddrDRAMC1);
+	PR_BOOTMSG("MET EMI: map nao dramcC to %p\n", BaseAddrDRAMC2);
+	PR_BOOTMSG("MET EMI: map nao dramcD to %p\n", BaseAddrDRAMC3);
+	return 0;
+}
+
+void MET_BM_DeInit(void)
+{
+}
+
+void MET_BM_Enable(const unsigned int enable)
+{
+	volatile unsigned long int value_check;
+	int i = 0;
+
+	while (i < 100) {
+
+		if (enable == 0) {
+			/* SET BIT IDLE */
+			MET_REG_BSET(ADDR_EMI+EMI_BMEN, BUS_MON_IDLE_SHIFT);
+
+			/* CLR BIT EN */
+			MET_REG_BCLR(ADDR_EMI+EMI_BMEN, BUS_MON_EN_SHIFT);
+
+			/* CLR BIT IDLE */
+			MET_REG_BCLR(ADDR_EMI+EMI_BMEN, BUS_MON_IDLE_SHIFT);
+		} else {
+			/* CLR BIT IDLE */
+			MET_REG_BCLR(ADDR_EMI+EMI_BMEN, BUS_MON_IDLE_SHIFT);
+
+			/* SET BIT EN */
+			MET_REG_BSET(ADDR_EMI+EMI_BMEN, BUS_MON_EN_SHIFT);
+		}
+
+		value_check = emi_readl(IOMEM(ADDR_EMI+EMI_BMEN));
+
+		if (enable == 0) {
+			/* EN == 0, IDLE == 0 when EMI RESET*/
+			if (!test_bit(BUS_MON_EN_SHIFT, &value_check)
+					&& !test_bit(BUS_MON_IDLE_SHIFT, &value_check)) {
+				break;
+			}
+		} else {
+			/* EN == 1, IDLE == 0 when EMI START*/
+			if (test_bit(BUS_MON_EN_SHIFT, &value_check)
+					&& !test_bit(BUS_MON_IDLE_SHIFT, &value_check)) {
+				break;
+			}
+		}
+		i++;
+	}
+	for (i = 0; i < MX_DRAMC_CHAN_NR; i++) {
+		if (enable == 0)
+			/* Disable Dramc Bus Monitor */
+			MET_DRAMC_BMEnable(i, 0);
+		else
+			/* Enable Dramc Bus Monitor */
+			MET_DRAMC_BMEnable(i, 1);
+	}
+	/*MET_TRACE("[MET_BM_ENABLE] value_check: %lx, enable = %d\n", value_check, enable);*/
+
+}
+
+void MET_BM_Pause(void)
+{
+	int i = 0;
+	const unsigned int value = emi_readl(IOMEM(ADDR_EMI+EMI_BMEN));
+
+	emi_reg_sync_writel(value | (1<<BUS_MON_PAUSE_SHIFT), ADDR_EMI+EMI_BMEN);
+	for (i = 0; i < MX_DRAMC_CHAN_NR; i++) {
+		/* Pause Dramc Bus Monitor */
+		MET_DRAMC_BMPause(i, 1);
+	}
+}
+
+void MET_BM_Continue(void)
+{
+	int i = 0;
+	const unsigned int value = emi_readl(IOMEM(ADDR_EMI+EMI_BMEN));
+
+	emi_reg_sync_writel(value & (~(1<<BUS_MON_PAUSE_SHIFT)), ADDR_EMI+EMI_BMEN);
+	for (i = 0; i < MX_DRAMC_CHAN_NR; i++) {
+		/* Enable Dramc Bus Monitor */
+		MET_DRAMC_BMPause(i, 0);
+	}
+}
+
+unsigned int MET_BM_IsOverrun(void)
+{
+	/*
+	 * return 0 if EMI_BCNT(bus cycle counts) or
+	 * EMI_WACT(total word counts) is overrun,
+	 * otherwise return an !0 value
+	 */
+	const unsigned int value = emi_readl(IOMEM(ADDR_EMI+EMI_BMEN));
+
+	return (value & (1<<BC_OVERRUN_SHIFT));
+}
+
+unsigned int MET_BM_GetReadWriteType(void)
+{
+	const unsigned int value = emi_readl(IOMEM(ADDR_EMI+EMI_BMEN));
+
+	return ((value & 0xFFFFFFCF) >> 4);
+}
+
+void MET_BM_SetReadWriteType(const unsigned int ReadWriteType)
+{
+	const unsigned int value = emi_readl(IOMEM(ADDR_EMI+EMI_BMEN));
+
+	/*
+	 * ReadWriteType: 00/11 --> both R/W
+	 *                   01 --> only R
+	 *                   10 --> only W
+	 */
+	emi_reg_sync_writel((value & 0xFFFFFFCF) | (ReadWriteType << 4), ADDR_EMI+EMI_BMEN);
+}
+
+int MET_BM_GetBusCycCount(void)
+{
+	return MET_BM_IsOverrun() ? BM_ERR_OVERRUN : emi_readl(IOMEM(ADDR_EMI+EMI_BCNT));/*Bus cycle counter*/
+}
+
+unsigned int MET_BM_GetTransAllCount(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI+EMI_TACT));
+}
+
+int MET_BM_GetTransCount(const unsigned int counter_num)
+{
+	unsigned int iCount;
+
+	switch (counter_num) {
+	case 1:
+		iCount = emi_readl(IOMEM(ADDR_EMI+EMI_TSCT));
+		break;
+
+	case 2:
+		iCount = emi_readl(IOMEM(ADDR_EMI+EMI_TSCT2));
+		break;
+
+	case 3:
+		iCount = emi_readl(IOMEM(ADDR_EMI+EMI_TSCT3));
+		break;
+
+	default:
+		return BM_ERR_WRONG_REQ;
+	}
+
+	return iCount;
+}
+
+int MET_BM_GetWordAllCount(void)
+{
+	return MET_BM_IsOverrun() ? BM_ERR_OVERRUN : emi_readl(IOMEM(ADDR_EMI+EMI_WACT));
+}
+
+int MET_BM_GetWordCount(const unsigned int counter_num)
+{
+	unsigned int iCount;
+
+	switch (counter_num) {
+	case 1:
+		iCount = emi_readl(IOMEM(ADDR_EMI+EMI_WSCT));
+		break;
+
+	case 2:
+		iCount = emi_readl(IOMEM(ADDR_EMI+EMI_WSCT2));
+		break;
+
+	case 3:
+		iCount = emi_readl(IOMEM(ADDR_EMI+EMI_WSCT3));
+		break;
+
+	case 4:
+		iCount = emi_readl(IOMEM(ADDR_EMI+EMI_WSCT4));
+		break;
+
+	default:
+		return BM_ERR_WRONG_REQ;
+	}
+
+	return iCount;
+}
+
+unsigned int MET_BM_GetBandwidthWordCount(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI+EMI_BACT));/*Bandwidth counter for access*/
+}
+
+unsigned int MET_BM_GetOverheadWordCount(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI+EMI_BSCT));/*Overhead counter*/
+}
+
+int MET_BM_GetTransTypeCount(const unsigned int counter_num)
+{
+	return (counter_num < 1 || counter_num > BM_COUNTER_MAX)
+		? BM_ERR_WRONG_REQ : emi_readl(IOMEM(ADDR_EMI+EMI_TTYPE1 + (counter_num - 1) * 8));
+}
+
+int MET_BM_GetMDCT(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI+EMI_MDCT));
+}
+
+int MET_BM_GetMDCT_2(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI+EMI_MDCT_2ND));
+}
+
+int MET_BM_SetMDCT_MDMCU(unsigned int mdmcu_rd_buf)
+{
+	volatile unsigned int value_origin;
+
+	value_origin = emi_readl(IOMEM(ADDR_EMI+EMI_MDCT_2ND));
+	MET_TRACE("[MET_BM_SetMDCT_MDMCU] value_origin: %x\n", value_origin);
+
+	value_origin = value_origin & ~(0x7);
+	value_origin = value_origin | ((mdmcu_rd_buf)&0x7);
+
+	emi_reg_sync_writel(value_origin, ADDR_EMI+EMI_MDCT_2ND);
+
+	return BM_REQ_OK;
+}
+
+
+int MET_BM_GetMonitorCounter(const unsigned int counter_num,
+		unsigned int *master,
+		unsigned int *trans_type)
+{
+	unsigned int value, addr;
+
+	if (counter_num < 1 || counter_num > BM_COUNTER_MAX)
+		return BM_ERR_WRONG_REQ;
+
+
+	if (counter_num == 1) {
+		addr = EMI_BMEN;
+		value = emi_readl(IOMEM(ADDR_EMI+addr));
+		*master = (value>>16) & MASK_MASTER;
+		*trans_type = (value>>24) & MASK_TRANS_TYPE;
+	} else {
+		addr = (counter_num <= 3) ?
+			EMI_MSEL : (EMI_MSEL2 + (counter_num / 2 - 2) * 8);
+		value = emi_readl(IOMEM(ADDR_EMI+addr)) >> ((counter_num % 2) * 16);
+		*master = value & MASK_MASTER;
+		*trans_type = (value>>8) & MASK_TRANS_TYPE;
+	}
+
+	return BM_REQ_OK;
+}
+
+int MET_BM_SetMonitorCounter(const unsigned int counter_num,
+		const unsigned int master,
+		const unsigned int trans_type)
+{
+	unsigned int value, addr;
+	const unsigned int iMask = (MASK_TRANS_TYPE << 8) | MASK_MASTER;
+
+	if (counter_num < 1 || counter_num > BM_COUNTER_MAX)
+		return BM_ERR_WRONG_REQ;
+
+
+	if (counter_num == 1) {
+		addr = EMI_BMEN;
+		value = (emi_readl(IOMEM(ADDR_EMI+addr)) & ~(iMask << 16)) |
+			((trans_type & MASK_TRANS_TYPE) << 24) |
+			((master & MASK_MASTER) << 16);
+	} else {
+		addr = (counter_num <= 3) ?
+			EMI_MSEL : (EMI_MSEL2 + (counter_num / 2 - 2) * 8);
+
+		/* clear master and transaction type fields */
+		value = emi_readl(IOMEM(ADDR_EMI+addr)) & ~(iMask << ((counter_num % 2) * 16));
+
+		/* set master and transaction type fields */
+		value |= (((trans_type & MASK_TRANS_TYPE) << 8) |
+				(master & MASK_MASTER)) <<
+			((counter_num % 2) * 16);
+	}
+
+	emi_reg_sync_writel(value, ADDR_EMI+addr);
+
+	return BM_REQ_OK;
+}
+
+int MET_BM_SetTtypeCounterRW(unsigned int bmrw0_val, unsigned int bmrw1_val)
+{
+
+	volatile unsigned int value_origin;
+
+	value_origin = emi_readl(IOMEM(ADDR_EMI+EMI_BMRW0));
+	MET_TRACE("[MET_EMI_settype1] value_origin: %x\n", value_origin);
+	if (value_origin != bmrw0_val) {
+		emi_reg_sync_writel(bmrw0_val, ADDR_EMI+EMI_BMRW0);
+		MET_TRACE("[MET_EMI_settype1] bmrw0_val: %x, value_origin: %x\n", bmrw0_val, value_origin);
+	}
+
+
+	value_origin = emi_readl(IOMEM(ADDR_EMI+EMI_BMRW1));
+	MET_TRACE("[MET_EMI_settype2] value_origin: %x\n", value_origin);
+	if (value_origin != bmrw1_val) {
+		emi_reg_sync_writel(bmrw1_val, ADDR_EMI+EMI_BMRW1);
+		MET_TRACE("[MET_EMI_settype2] bmrw0_val: %x, value_origin: %x\n", bmrw1_val, value_origin);
+
+	}
+	return BM_REQ_OK;
+}
+
+int MET_BM_Set_WsctTsct_id_sel(unsigned int counter_num, unsigned int enable)
+{
+	unsigned int value;
+
+	if (counter_num > 3)
+		return BM_ERR_WRONG_REQ;
+
+	value = ((emi_readl(IOMEM(ADDR_EMI+EMI_BMEN2)) & (~(1 << (28+counter_num)))) | (enable << (28+counter_num)));
+	emi_reg_sync_writel(value, ADDR_EMI+EMI_BMEN2);
+
+	return BM_REQ_OK;
+}
+
+int MET_BM_SetMaster(const unsigned int counter_num, const unsigned int master)
+{
+	unsigned int value, addr;
+	const unsigned int iMask = 0x7F;
+
+	if (counter_num < 1 || counter_num > BM_COUNTER_MAX)
+		return BM_ERR_WRONG_REQ;
+
+
+	if (counter_num == 1) {
+		addr = EMI_BMEN;
+		value = (emi_readl(IOMEM(ADDR_EMI+addr)) & ~(iMask << 16)) | ((master & iMask) << 16);
+	} else {
+		addr = (counter_num <= 3) ? EMI_MSEL : (EMI_MSEL2 + (counter_num / 2 - 2) * 8);
+
+		/* clear master and transaction type fields */
+		value = emi_readl(IOMEM(ADDR_EMI+addr)) & ~(iMask << ((counter_num % 2) * 16));
+
+		/* set master and transaction type fields */
+		value |= ((master & iMask) << ((counter_num % 2) * 16));
+	}
+
+	emi_reg_sync_writel(value, ADDR_EMI+addr);
+
+	return BM_REQ_OK;
+}
+
+int MET_BM_SetIDSelect(const unsigned int counter_num,
+		const unsigned int id,
+		const unsigned int enable)
+{
+	unsigned int value, addr, shift_num;
+
+	if (enable == 0) {
+
+		value = (emi_readl(IOMEM(ADDR_EMI+EMI_BMEN2))
+				& ~(1 << (counter_num - 1)));
+
+
+		emi_reg_sync_writel(value, ADDR_EMI+EMI_BMEN2);
+		return BM_REQ_OK;
+	}
+
+	if ((counter_num < 1 || counter_num > BM_COUNTER_MAX) || (id > EMI_BMID_MASK) || (enable > 1))
+		return BM_ERR_WRONG_REQ;
+
+
+	addr = EMI_BMID0 + (counter_num - 1) / 2 * 4;
+
+	/* field's offset in the target EMI_BMIDx register */
+	shift_num = ((counter_num - 1) % 2) * 16;
+
+	/* clear SELx_ID field */
+	value = emi_readl(IOMEM(ADDR_EMI+addr)) & ~(EMI_BMID_MASK << shift_num);
+
+	/* set SELx_ID field */
+	value |= id << shift_num;
+
+	emi_reg_sync_writel(value, ADDR_EMI+addr);
+
+	value = (emi_readl(IOMEM(ADDR_EMI+EMI_BMEN2))
+		& ~(1 << (counter_num - 1)))
+		| (enable << (counter_num - 1));
+
+	emi_reg_sync_writel(value, ADDR_EMI+EMI_BMEN2);
+
+	return BM_REQ_OK;
+}
+
+int MET_BM_SetUltraHighFilter(const unsigned int counter_num, const unsigned int enable)
+{
+	unsigned int value;
+
+	if ((counter_num < 1 || counter_num > BM_COUNTER_MAX) || (enable > 1))
+		return BM_ERR_WRONG_REQ;
+
+
+	value = (emi_readl(IOMEM(ADDR_EMI+EMI_BMEN1))
+		& ~(1 << (counter_num - 1)))
+		| (enable << (counter_num - 1));
+
+	emi_reg_sync_writel(value, ADDR_EMI+EMI_BMEN1);
+
+	return BM_REQ_OK;
+}
+
+int MET_BM_SetLatencyCounter(unsigned int enable)
+{
+	unsigned int value;
+
+	value = emi_readl(IOMEM(ADDR_EMI+EMI_BMEN2)) & ~(0x3 << 24);
+	/*
+	 * emi_ttype1 -- emi_ttype8 change as total latencies
+	 * for m0 -- m7,
+	 * and emi_ttype9 -- emi_ttype16 change as total transaction counts
+	 * for m0 -- m7
+	 */
+	if (enable == 1)
+		value |= (0x2 << 24);
+
+	emi_reg_sync_writel(value, ADDR_EMI+EMI_BMEN2);
+
+	return BM_REQ_OK;
+}
+
+int MET_BM_GetLatencyCycle(const unsigned int counter_num)
+{
+	unsigned int cycle_count;
+
+	switch (counter_num) {
+	case 1:
+		cycle_count = emi_readl(IOMEM(ADDR_EMI+EMI_TTYPE1));
+		break;
+	case 2:
+		cycle_count = emi_readl(IOMEM(ADDR_EMI+EMI_TTYPE2));
+		break;
+	case 3:
+		cycle_count = emi_readl(IOMEM(ADDR_EMI+EMI_TTYPE3));
+		break;
+	case 4:
+		cycle_count = emi_readl(IOMEM(ADDR_EMI+EMI_TTYPE4));
+		break;
+	case 5:
+		cycle_count = emi_readl(IOMEM(ADDR_EMI+EMI_TTYPE5));
+		break;
+	case 6:
+		cycle_count = emi_readl(IOMEM(ADDR_EMI+EMI_TTYPE6));
+		break;
+	case 7:
+		cycle_count = emi_readl(IOMEM(ADDR_EMI+EMI_TTYPE7));
+		break;
+	case 8:
+		cycle_count = emi_readl(IOMEM(ADDR_EMI+EMI_TTYPE8));
+		break;
+	case 9:
+		cycle_count = emi_readl(IOMEM(ADDR_EMI+EMI_TTYPE9));
+		break;
+	case 10:
+		cycle_count = emi_readl(IOMEM(ADDR_EMI+EMI_TTYPE10));
+		break;
+	case 11:
+		cycle_count = emi_readl(IOMEM(ADDR_EMI+EMI_TTYPE11));
+		break;
+	case 12:
+		cycle_count = emi_readl(IOMEM(ADDR_EMI+EMI_TTYPE12));
+		break;
+	case 13:
+		cycle_count = emi_readl(IOMEM(ADDR_EMI+EMI_TTYPE13));
+		break;
+	case 14:
+		cycle_count = emi_readl(IOMEM(ADDR_EMI+EMI_TTYPE14));
+		break;
+	case 15:
+		cycle_count = emi_readl(IOMEM(ADDR_EMI+EMI_TTYPE15));
+		break;
+	case 16:
+		cycle_count = emi_readl(IOMEM(ADDR_EMI+EMI_TTYPE16));
+		break;
+	case 17:
+		cycle_count = emi_readl(IOMEM(ADDR_EMI+EMI_TTYPE17));
+		break;
+	case 18:
+		cycle_count = emi_readl(IOMEM(ADDR_EMI+EMI_TTYPE18));
+		break;
+	case 19:
+		cycle_count = emi_readl(IOMEM(ADDR_EMI+EMI_TTYPE19));
+		break;
+	case 20:
+		cycle_count = emi_readl(IOMEM(ADDR_EMI+EMI_TTYPE20));
+		break;
+	case 21:
+		cycle_count = emi_readl(IOMEM(ADDR_EMI+EMI_TTYPE21));
+		break;
+	default:
+		return BM_ERR_WRONG_REQ;
+	}
+
+	return cycle_count;
+}
+
+unsigned int MET_BM_GetEmiDcm(void)
+{
+	return (emi_readl(IOMEM(ADDR_EMI+EMI_CONM))>>24);
+}
+
+int MET_BM_SetEmiDcm(const unsigned int setting)
+{
+	unsigned int value;
+
+	value = emi_readl(IOMEM(ADDR_EMI+EMI_CONM));
+	emi_reg_sync_writel((value & 0x00FFFFFF) | (setting << 24), ADDR_EMI+EMI_CONM);
+
+	return BM_REQ_OK;
+}
+
+/* DRAMC */
+unsigned int MET_DRAMC_GetPageHitCount(DRAMC_Cnt_Type CountType, int chann)
+{
+	unsigned int iCount;
+	unsigned long addr_base = ADDR_DRAMC0;
+
+	if (chann == 0)
+		addr_base = ADDR_DRAMC0;
+	else if (chann == 1)
+		addr_base = ADDR_DRAMC1;
+	else if (chann == 2)
+		addr_base = ADDR_DRAMC2;
+	else if (chann == 3)
+		addr_base = ADDR_DRAMC3;
+	else
+		return BM_ERR_WRONG_REQ;
+
+
+	switch (CountType) {
+	case DRAMC_R2R:
+		iCount = emi_readl(IOMEM(addr_base+DRAMC_R2R_PAGE_HIT));
+		break;
+	case DRAMC_R2W:
+		iCount = emi_readl(IOMEM(addr_base+DRAMC_R2W_PAGE_HIT));
+		break;
+	case DRAMC_W2R:
+		iCount = emi_readl(IOMEM(addr_base+DRAMC_W2R_PAGE_HIT));
+		break;
+	case DRAMC_W2W:
+		iCount = emi_readl(IOMEM(addr_base+DRAMC_W2W_PAGE_HIT));
+		break;
+	case DRAMC_ALL:
+		iCount = emi_readl(IOMEM(addr_base+DRAMC_R2R_PAGE_HIT)) +
+			 emi_readl(IOMEM(addr_base+DRAMC_R2W_PAGE_HIT)) +
+			 emi_readl(IOMEM(addr_base+DRAMC_W2R_PAGE_HIT)) +
+			 emi_readl(IOMEM(addr_base+DRAMC_W2W_PAGE_HIT));
+		break;
+	default:
+		return BM_ERR_WRONG_REQ;
+	}
+
+	return iCount;
+}
+
+unsigned int MET_DRAMC_GetPageMissCount(DRAMC_Cnt_Type CountType, int chann)
+{
+	unsigned int iCount;
+	unsigned long addr_base = ADDR_DRAMC0;
+
+	if (chann == 0)
+		addr_base = ADDR_DRAMC0;
+	else if (chann == 1)
+		addr_base = ADDR_DRAMC1;
+	else if (chann == 2)
+		addr_base = ADDR_DRAMC2;
+	else if (chann == 3)
+		addr_base = ADDR_DRAMC3;
+	else
+		return BM_ERR_WRONG_REQ;
+
+
+	switch (CountType) {
+	case DRAMC_R2R:
+		iCount = emi_readl(IOMEM(addr_base+DRAMC_R2R_PAGE_MISS));
+		break;
+	case DRAMC_R2W:
+		iCount = emi_readl(IOMEM(addr_base+DRAMC_R2W_PAGE_MISS));
+		break;
+	case DRAMC_W2R:
+		iCount = emi_readl(IOMEM(addr_base+DRAMC_W2R_PAGE_MISS));
+		break;
+	case DRAMC_W2W:
+		iCount = emi_readl(IOMEM(addr_base+DRAMC_W2W_PAGE_MISS));
+		break;
+	case DRAMC_ALL:
+		iCount = emi_readl(IOMEM(addr_base+DRAMC_R2R_PAGE_MISS)) +
+			 emi_readl(IOMEM(addr_base+DRAMC_R2W_PAGE_MISS)) +
+			 emi_readl(IOMEM(addr_base+DRAMC_W2R_PAGE_MISS)) +
+			 emi_readl(IOMEM(addr_base+DRAMC_W2W_PAGE_MISS));
+		break;
+	default:
+		return BM_ERR_WRONG_REQ;
+	}
+
+	return iCount;
+}
+
+unsigned int MET_DRAMC_GetInterbankCount(DRAMC_Cnt_Type CountType, int chann)
+{
+	unsigned int iCount;
+	unsigned long addr_base = ADDR_DRAMC0;
+
+	if (chann == 0)
+		addr_base = ADDR_DRAMC0;
+	else if (chann == 1)
+		addr_base = ADDR_DRAMC1;
+	else if (chann == 2)
+		addr_base = ADDR_DRAMC2;
+	else if (chann == 3)
+		addr_base = ADDR_DRAMC3;
+	else
+		return BM_ERR_WRONG_REQ;
+
+
+	switch (CountType) {
+	case DRAMC_R2R:
+		iCount = emi_readl(IOMEM(addr_base+DRAMC_R2R_INTERBANK));
+		break;
+	case DRAMC_R2W:
+		iCount = emi_readl(IOMEM(addr_base+DRAMC_R2W_INTERBANK));
+		break;
+	case DRAMC_W2R:
+		iCount = emi_readl(IOMEM(addr_base+DRAMC_W2R_INTERBANK));
+		break;
+	case DRAMC_W2W:
+		iCount = emi_readl(IOMEM(addr_base+DRAMC_W2W_INTERBANK));
+		break;
+	case DRAMC_ALL:
+		iCount = emi_readl(IOMEM(addr_base+DRAMC_R2R_INTERBANK)) +
+			 emi_readl(IOMEM(addr_base+DRAMC_R2W_INTERBANK)) +
+			 emi_readl(IOMEM(addr_base+DRAMC_W2R_INTERBANK)) +
+			 emi_readl(IOMEM(addr_base+DRAMC_W2W_INTERBANK));
+		break;
+	default:
+		return BM_ERR_WRONG_REQ;
+	}
+
+	return iCount;
+}
+
+unsigned int MET_DRAMC_GetIdleCount(int chann)
+{
+	unsigned long addr_base = ADDR_DRAMC0;
+
+	if (chann == 0)
+		addr_base = ADDR_DRAMC0;
+	else if (chann == 1)
+		addr_base = ADDR_DRAMC1;
+	else if (chann == 2)
+		addr_base = ADDR_DRAMC2;
+	else if (chann == 3)
+		addr_base = ADDR_DRAMC3;
+	else
+		return BM_ERR_WRONG_REQ;
+
+	return emi_readl(IOMEM(addr_base+DRAMC_IDLE_COUNT));
+}
+
+unsigned int MET_DRAMC_Misc_Status(int chann)
+{
+	unsigned long addr_base = ADDR_DRAMC0;
+
+	if (chann == 0)
+		addr_base = ADDR_DRAMC0;
+	else if (chann == 1)
+		addr_base = ADDR_DRAMC1;
+	else if (chann == 2)
+		addr_base = ADDR_DRAMC2;
+	else if (chann == 3)
+		addr_base = ADDR_DRAMC3;
+	else
+		return BM_ERR_WRONG_REQ;
+
+	return emi_readl(IOMEM(addr_base+DRAMC_MISC_STATUSA));
+}
+
+int MET_DRAMC_BMPause(int chann, int set)
+{
+	unsigned long addr_base = ADDR_DRAMC0;
+	unsigned int value;
+
+	if (chann == 0)
+		addr_base = ADDR_DRAMC0;
+	else if (chann == 1)
+		addr_base = ADDR_DRAMC1;
+	else if (chann == 2)
+		addr_base = ADDR_DRAMC2;
+	else if (chann == 3)
+		addr_base = ADDR_DRAMC3;
+	else
+		return BM_ERR_WRONG_REQ;
+
+	value = emi_readl(IOMEM(addr_base+DRAMC_DMMONITOR));
+	if (set == 0) {
+		/* Continue DRAMC Monitor*/
+		value = (value & 0xFFFFFFFB);
+	}
+	else if (set == 1) {
+		/* Pause DRAMC Monitor*/
+		value = (value | 0x00000004);
+	}
+	else
+		return BM_ERR_WRONG_REQ;
+	emi_reg_sync_writel(value, addr_base+DRAMC_DMMONITOR);
+
+	return BM_REQ_OK;
+}
+
+
+int MET_DRAMC_BMEnable(int chann, int set)
+{
+	unsigned long addr_base = ADDR_DRAMC0;
+	unsigned int value;
+
+	if (chann == 0)
+		addr_base = ADDR_DRAMC0;
+	else if (chann == 1)
+		addr_base = ADDR_DRAMC1;
+	else if (chann == 2)
+		addr_base = ADDR_DRAMC2;
+	else if (chann == 3)
+		addr_base = ADDR_DRAMC3;
+	else
+		return BM_ERR_WRONG_REQ;
+
+	value = emi_readl(IOMEM(addr_base+DRAMC_DMMONITOR));
+	if (set == 0) {
+		/* Disable DRAMC Monitor*/
+		value = (value & 0xFFFFFFF7);
+	}
+	else if (set == 1) {
+		/* Enable DRAMC Monitor*/
+		value = (value | 0x00000008);
+	}
+	else
+		return BM_ERR_WRONG_REQ;
+	emi_reg_sync_writel(value, addr_base+DRAMC_DMMONITOR);
+
+	return BM_REQ_OK;
+}
+
+unsigned int MET_DRAMC_RefPop(int chann)
+{
+	unsigned long addr_base = ADDR_DRAMC0;
+
+	if (chann == 0)
+		addr_base = ADDR_DRAMC0;
+	else if (chann == 1)
+		addr_base = ADDR_DRAMC1;
+	else if (chann == 2)
+		addr_base = ADDR_DRAMC2;
+	else if (chann == 3)
+		addr_base = ADDR_DRAMC3;
+	else
+		return BM_ERR_WRONG_REQ;
+
+	return emi_readl(IOMEM(addr_base+DRAMC_REFRESH_POP));
+}
+
+
+
+unsigned int MET_DRAMC_Free26M(int chann)
+{
+	unsigned long addr_base = ADDR_DRAMC0;
+
+	if (chann == 0)
+		addr_base = ADDR_DRAMC0;
+	else if (chann == 1)
+		addr_base = ADDR_DRAMC1;
+	else if (chann == 2)
+		addr_base = ADDR_DRAMC2;
+	else if (chann == 3)
+		addr_base = ADDR_DRAMC3;
+	else
+		return BM_ERR_WRONG_REQ;
+
+	return emi_readl(IOMEM(addr_base+DRAMC_FREERUN_26M));
+}
+
+unsigned int MET_DRAMC_RByte(int chann)
+{
+	unsigned long addr_base = ADDR_DRAMC0;
+
+	if (chann == 0)
+		addr_base = ADDR_DRAMC0;
+	else if (chann == 1)
+		addr_base = ADDR_DRAMC1;
+	else if (chann == 2)
+		addr_base = ADDR_DRAMC2;
+	else if (chann == 3)
+		addr_base = ADDR_DRAMC3;
+	else
+		return BM_ERR_WRONG_REQ;
+
+	return emi_readl(IOMEM(addr_base+DRAMC_READ_BYTES));
+}
+
+unsigned int MET_DRAMC_WByte(int chann)
+{
+	unsigned long addr_base = ADDR_DRAMC0;
+
+	if (chann == 0)
+		addr_base = ADDR_DRAMC0;
+	else if (chann == 1)
+		addr_base = ADDR_DRAMC1;
+	else if (chann == 2)
+		addr_base = ADDR_DRAMC2;
+	else if (chann == 3)
+		addr_base = ADDR_DRAMC3;
+	else
+		return BM_ERR_WRONG_REQ;
+
+	return emi_readl(IOMEM(addr_base+DRAMC_WRITE_BYTES));
+}
+
+unsigned int MET_EMI_GetMDCT(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI+EMI_MDCT));
+}
+
+unsigned int MET_EMI_GetMDCT_2ND(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI+EMI_MDCT_2ND));
+}
+
+
+unsigned int MET_EMI_GetARBA(void)
+{
+	/* EMI_ARBA EMI Bandwidth Filter Control M0/1 */
+	return emi_readl(IOMEM(ADDR_EMI+EMI_ARBA));
+}
+
+unsigned int MET_EMI_GetARBB(void)
+{
+	/* return emi_readl(IOMEM(ADDR_EMI+EMI_ARBB)); */
+	/* Note: Olympus Coda doesn't has this regiester, So we return 0 */
+	return 0;
+}
+
+unsigned int MET_EMI_GetARBC(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI+EMI_ARBC));
+}
+
+unsigned int MET_EMI_GetARBD(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI+EMI_ARBD));
+}
+
+unsigned int MET_EMI_GetARBE(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI+EMI_ARBE));
+}
+
+unsigned int MET_EMI_GetARBF(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI+EMI_ARBF));
+}
+
+unsigned int MET_EMI_GetARBG(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI+EMI_ARBG));
+}
+
+unsigned int MET_EMI_GetARBH(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI+EMI_ARBH));
+}
+
+/* Total BW status*/
+unsigned int MET_EMI_GetBWCT0(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI+EMI_BWCT0));
+}
+
+/* Total BW status*/
+unsigned int MET_EMI_GetBWCT1(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI+EMI_BWCT1));
+}
+
+/* Total BW status*/
+unsigned int MET_EMI_GetBWCT2(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI+EMI_BWCT2));
+}
+
+/* Total BW status*/
+unsigned int MET_EMI_GetBWCT3(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI+EMI_BWCT3));
+}
+
+/* Total BW status*/
+unsigned int MET_EMI_GetBWCT4(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI+EMI_BWCT4));
+}
+
+/* Total BW status*/
+unsigned int MET_EMI_GetBWST0(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI+EMI_BWST0));
+}
+
+/* Total BW status*/
+unsigned int MET_EMI_GetBWST1(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI+EMI_BWST1));
+}
+
+
+/* C+G BW status*/
+unsigned int MET_EMI_GetBWCT0_2ND(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI+EMI_BWCT0_2ND));
+}
+
+/* C+G BW status*/
+unsigned int MET_EMI_GetBWCT1_2ND(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI+EMI_BWCT1_2ND));
+}
+
+/* C+G BW status*/
+unsigned int MET_EMI_GetBWST_2ND(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI+EMI_BWST_2ND));
+}
+
+unsigned int MET_EMI_GetBMRW0(void)
+{
+	return readl(IOMEM(ADDR_EMI+EMI_BMRW0));
+}
+
+void emi_dump_reg(void)
+{
+	int i;
+
+	MET_TRACE("[emi_regdump]\n");
+	for (i = 0x400; i < 0x500; i = i+16)
+		MET_TRACE("%4x__ %8x %8x %8x %8x\n", i,  readl(IOMEM(ADDR_EMI+i)),
+			readl(IOMEM(ADDR_EMI+i+4)), readl(IOMEM(ADDR_EMI+i+8)), readl(IOMEM(ADDR_EMI+i+12)));
+}
+
+unsigned int MET_EMI_GetDramChannNum(void)
+{
+	int num = -1;
+
+	if (BaseAddrEMI) {
+		num = emi_readl(IOMEM(ADDR_EMI+EMI_CONA));
+		num = ((num >> 8) & 0x0000003);
+	} else {
+		return 1;
+	}
+
+	if (num == M0_DOUBLE_HALF_BW_1CH)
+		return 1;
+	else if (num == M0_DOUBLE_HALF_BW_2CH)
+		return 2;
+	else if (num == M0_DOUBLE_HALF_BW_4CH)
+		return 4;
+
+	else		/* default return single channel */
+		return 1;
+
+}
+
+void MET_DRAMC_GetDebugCounter(int *value, int chann)
+{
+	int i;
+	unsigned long addr_base = ADDR_DRAMC0;
+
+	for (i = 0; i < chann; i++)	{
+
+		if (i == 0)
+			addr_base = ADDR_DRAMC0;
+		else if (i == 1)
+			addr_base = ADDR_DRAMC1;
+		else if (i == 2)
+			addr_base = ADDR_DRAMC2;
+		else if (i == 3)
+			addr_base = ADDR_DRAMC3;
+		else
+			return;
+
+
+		value[DRAMC_Debug_MAX_CNT*i + BM_RK0_PRE_STANDBY] =
+			emi_readl(IOMEM(addr_base + DRAMC_RK0_PRE_STANDBY));
+		value[DRAMC_Debug_MAX_CNT*i + BM_RK0_PRE_POWERDOWN] =
+			emi_readl(IOMEM(addr_base + DRAMC_RK0_PRE_POWERDOWN));
+		value[DRAMC_Debug_MAX_CNT*i + BM_RK0_ACT_STANDBY] =
+			emi_readl(IOMEM(addr_base + DRAMC_RK0_ACT_STANDBY));
+		value[DRAMC_Debug_MAX_CNT*i + BM_RK0_ACT_POWERDOWN] =
+			emi_readl(IOMEM(addr_base + DRAMC_RK0_ACT_POWERDOWN));
+
+		value[DRAMC_Debug_MAX_CNT*i + BM_RK1_PRE_STANDBY] =
+			emi_readl(IOMEM(addr_base + DRAMC_RK1_PRE_STANDBY));
+		value[DRAMC_Debug_MAX_CNT*i + BM_RK1_PRE_POWERDOWN] =
+			emi_readl(IOMEM(addr_base + DRAMC_RK1_PRE_POWERDOWN));
+		value[DRAMC_Debug_MAX_CNT*i + BM_RK1_ACT_STANDBY] =
+			emi_readl(IOMEM(addr_base + DRAMC_RK1_ACT_STANDBY));
+		value[DRAMC_Debug_MAX_CNT*i + BM_RK1_ACT_POWERDOWN] =
+			emi_readl(IOMEM(addr_base + DRAMC_RK1_ACT_POWERDOWN));
+
+		value[DRAMC_Debug_MAX_CNT*i + BM_RK2_PRE_STANDBY] =
+			emi_readl(IOMEM(addr_base + DRAMC_RK2_PRE_STANDBY));
+		value[DRAMC_Debug_MAX_CNT*i + BM_RK2_PRE_POWERDOWN] =
+			emi_readl(IOMEM(addr_base + DRAMC_RK2_PRE_POWERDOWN));
+		value[DRAMC_Debug_MAX_CNT*i + BM_RK2_ACT_STANDBY] =
+			emi_readl(IOMEM(addr_base + DRAMC_RK2_ACT_STANDBY));
+		value[DRAMC_Debug_MAX_CNT*i + BM_RK2_ACT_POWERDOWN] =
+			emi_readl(IOMEM(addr_base + DRAMC_RK2_ACT_POWERDOWN));
+	}
+}
+#if 1
+static inline unsigned int dramc_reg_read(unsigned long addr)
+{
+#if 0
+#ifdef CONFIG_64BIT
+	unsigned int value = 0;
+	value = HAL_READ32((void*)addr);
+#else
+	unsigned int value = readl((void*)addr);
+#endif
+#endif
+
+	unsigned int value = readl((void*)addr);
+
+	mb();
+	return value;
+}
+
+#if 0
+u32 DRAMC_GetDramcFreq(void)
+{
+	unsigned long tcm_dramc_flags = 0;
+	tcm_dramc_flags = dramc_reg_read(TCM_DRAM_FLAGS_ADDR);
+	return TCMGET_DDR_CLK(tcm_dramc_flags);
+}
+
+unsigned int get_dram_data_rate(void)
+{
+	return DRAMC_GetDramcFreq()/1000000;
+}
+#else
+
+#define DRAM_DATARATE_REG	0x10100D9C
+
+u32 DRAMC_GetDramcFreq(void)
+{
+	unsigned long reg_val;
+	unsigned long dram_datarate = 0;
+
+	reg_val = dramc_reg_read((unsigned long)(AddrDramDatarate + 0xD9C));
+//	pr_err("dram datarate reg val = 0x%lx, reg_val[9-16] = %lu\n", reg_val, (reg_val >> 9) & 0xff);
+	dram_datarate = (((dramc_reg_read((unsigned long)(AddrDramDatarate + 0xD9C))) >> 16) & 0x1ffff) * 26 / 256;
+
+	return dram_datarate;
+}
+
+unsigned int get_dram_data_rate(void)
+{
+	return DRAMC_GetDramcFreq();
+}
+#endif
+
+
+#else
+#define DRAMC_AO_CHA_BASE_ADDR (DRAM_BASE)
+#define PDEF_DRAMC0_CHA_REG_0E4 IOMEM((DRAMC_AO_CHA_BASE_ADDR + 0x00e4))
+#define PDEF_DRAMC0_CHA_REG_010 IOMEM((DRAMC_AO_CHA_BASE_ADDR + 0x0010))
+#define DDRPHY_CHA_BASE_ADDR (DRAM_DDRPHY_CHA_BANK)
+unsigned int DRAM_TYPE = 0;
+enum DDRTYPE {
+        TYPE_LPDDR3 = 1,
+        TYPE_LPDDR4,
+        TYPE_LPDDR4X
+};
+
+static unsigned int get_shuffle_status(void)
+{
+	/* HPM = 0, LPM = 1, ULPM = 2; */
+	return (emi_readl(PDEF_DRAMC0_CHA_REG_0E4) & 0x6) >> 1;
+}
+
+unsigned int get_dram_data_rate(void)
+{
+	return DRAMC_GetDramcFreq();
+
+	unsigned long addr_base = DDRPHY_CHA_BASE_ADDR;
+	unsigned int u4ShuLevel, u4SDM_PCW, u4PREDIV, u4POSDIV, u4CKDIV4, u4VCOFreq, u4DataRate = 0;
+	if (DRAM_TYPE == 0)
+		DRAM_TYPE = (emi_readl(PDEF_DRAMC0_CHA_REG_010) & 0x1C00) >> 10;
+	u4ShuLevel = get_shuffle_status();
+
+	u4SDM_PCW = emi_readl(IOMEM(addr_base + 0xd94 + 0x500 * u4ShuLevel)) >> 16;
+	u4PREDIV = (emi_readl(IOMEM(addr_base + 0xda0 + 0x500 * u4ShuLevel)) & 0x000c0000) >> 18;
+	u4POSDIV = emi_readl(IOMEM(addr_base + 0xda0 + 0x500 * u4ShuLevel)) & 0x00000007;
+	u4CKDIV4 = (emi_readl(IOMEM(addr_base + 0xd18 + 0x500 * u4ShuLevel)) & 0x08000000) >> 27;
+
+	u4VCOFreq = ((52>>u4PREDIV)*(u4SDM_PCW>>8))>>u4POSDIV;
+
+	u4DataRate = u4VCOFreq>>u4CKDIV4;
+
+	/* pr_err("[DRAMC Driver] u4ShuLevel=%d, PCW=0x%X, u4PREDIV=%d, u4POSDIV=%d, CKDIV4=%d, DataRate=%d\n",
+	 * u4ShuLevel, u4SDM_PCW, u4PREDIV, u4POSDIV, u4CKDIV4, u4DataRate);
+	 */
+
+//	if (DRAM_TYPE == TYPE_LPDDR4X) {
+		if (u4DataRate == 3198)
+			u4DataRate = 3200;
+		else if (u4DataRate == 2652)
+			u4DataRate = 2667;
+		else if (u4DataRate == 1599)
+			u4DataRate = 1600;
+		else if (u4DataRate == 799)
+			u4DataRate = 800;
+		else
+			u4DataRate = 0;
+//	} else
+//		u4DataRate = 0;
+
+	return u4DataRate;
+}
+#endif
diff --git a/src/devtools/met-driver/met_drv/common/mtk_emi_bm.h b/src/devtools/met-driver/met_drv/common/mtk_emi_bm.h
new file mode 100644
index 0000000..921d80d
--- /dev/null
+++ b/src/devtools/met-driver/met_drv/common/mtk_emi_bm.h
@@ -0,0 +1,414 @@
+#ifndef __MT_MET_EMI_BM_H__
+#define __MT_MET_EMI_BM_H__
+
+
+
+
+#define DEF_BM_RW_TYPE		(BM_BOTH_READ_WRITE)
+#define NTS					2
+#define NWSCT				4
+#define NLATENCY			8
+#define NTRANS				8
+#define NALL					3
+#define NTTYPE				5
+#define NIDX_EMI			(NTS + NWSCT + NLATENCY + NTRANS + NALL + NTTYPE)
+
+#define NCNT					9
+#define NCH					4
+#define NIDX_DRAMC			(NCNT * NCH)
+#define NIDX					(NIDX_EMI + NIDX_DRAMC)
+
+#define NCLK					1
+#define NARB					8
+#define NBW					10
+#define NIDX_BL				(NCLK + NARB + NBW)
+
+/* 1000 To Khz and 4x freq & 2x data rate for LPDDR4 */
+/* 1000 To Khz and 2x freq & 2x data rate for LPDDR3*/
+/* TBD: calculate emi clock rate from DRAM DATA RATE */
+
+/*dram baseclock/EMI clock  :	LP4=4	LP3=2	*/
+#define DRAM_EMI_BASECLOCK_RATE	4
+/*dram io width  :	LP4=x16		LP3=x32 or x16	*/
+#define DRAM_IO_BUS_WIDTH		16
+/*dram datarate  :	DDR=double */
+#define DRAM_DATARATE	2
+
+#define	ADDR_EMI		((unsigned long) BaseAddrEMI)
+#define	ADDR_DRAMC0	((unsigned long) BaseAddrDRAMC0)
+#define	ADDR_DRAMC1	((unsigned long) BaseAddrDRAMC1)
+#define	ADDR_DRAMC2	((unsigned long) BaseAddrDRAMC2)
+#define	ADDR_DRAMC3	((unsigned long) BaseAddrDRAMC3)
+
+static const char of_emi_desc[] = "mediatek,mt2712-emi";
+static const char of_dramc_desc[] = "mediatek,mt2712-dramc";
+
+typedef enum {
+	DRAMC_DTS_DRAMC0_AO = 0x0,
+	DRAMC_DTS_DRAMC0_NAO = 0x4,
+	DRAMC_DTS_DRAMC1_NAO = 0x5,
+	DRAMC_DTS_DRAMC2_NAO = 0x6,
+	DRAMC_DTS_DRAMC3_NAO = 0x7,
+	DRAMC_DTS_DDRPHY0_AO = 0x8,
+}	BM_DRAMC_DTS_INDEX;
+
+#define BM_Master_M0_name	"m0_APMCU0"
+#define BM_Master_M1_name	"m1_APMCU1"
+#define BM_Master_M2_name	"m2_MM1_M1"
+#define BM_Master_M3_name	"m3_MM2_M1"
+#define BM_Master_M4_name	"m4_MM2_M0"
+#define BM_Master_M5_name	"m5_MM1_M0"
+#define BM_Master_M6_name	"m6_PERI"
+#define BM_Master_M7_name	"m7_GPU"
+
+#define BM_Master_GP_AP	(BM_MASTER_M0 | BM_MASTER_M1)
+#define BM_Master_GP_MM	(BM_MASTER_M2 | BM_MASTER_M3 | BM_MASTER_M4 | BM_MASTER_M5)
+#define BM_Master_GP_GPU	(BM_MASTER_M7)
+#define BM_Master_GP_PERI	(BM_MASTER_M6)
+
+
+/*Need no change by project*/
+#define BM_Master_GP_1_Default	BM_Master_GP_AP
+#define BM_Master_GP_2_Default	BM_Master_GP_MM
+#define BM_Master_GP_3_Default	BM_Master_GP_GPU
+
+#define BM_MASTER_M0		(0x01)
+#define BM_MASTER_M1		(0x02)
+#define BM_MASTER_M2		(0x04)
+#define BM_MASTER_M3		(0x08)
+#define BM_MASTER_M4		(0x10)
+#define BM_MASTER_M5		(0x20)
+#define BM_MASTER_M6		(0x40)
+#define BM_MASTER_M7		(0x80)
+#define BM_MASTER_ALL		(0xFF)
+
+typedef enum {
+	BM_RK0_PRE_STANDBY = 0x0,
+	BM_RK0_PRE_POWERDOWN,
+	BM_RK0_ACT_STANDBY,
+	BM_RK0_ACT_POWERDOWN,
+	BM_RK1_PRE_STANDBY,
+	BM_RK1_PRE_POWERDOWN,
+	BM_RK1_ACT_STANDBY,
+	BM_RK1_ACT_POWERDOWN,
+	BM_RK2_PRE_STANDBY,
+	BM_RK2_PRE_POWERDOWN,
+	BM_RK2_ACT_STANDBY,
+	BM_RK2_ACT_POWERDOWN,
+	DRAMC_Debug_MAX_CNT
+} DRAMC_Debug_Type;
+
+typedef enum {
+	DRAMC_R2R,
+	DRAMC_R2W,
+	DRAMC_W2R,
+	DRAMC_W2W,
+	DRAMC_ALL
+} DRAMC_Cnt_Type;
+
+typedef enum {
+	BM_BOTH_READ_WRITE,
+	BM_READ_ONLY,
+	BM_WRITE_ONLY
+} BM_RW_Type;
+
+enum {
+	BM_TRANS_TYPE_1BEAT = 0x0,
+	BM_TRANS_TYPE_2BEAT,
+	BM_TRANS_TYPE_3BEAT,
+	BM_TRANS_TYPE_4BEAT,
+	BM_TRANS_TYPE_5BEAT,
+	BM_TRANS_TYPE_6BEAT,
+	BM_TRANS_TYPE_7BEAT,
+	BM_TRANS_TYPE_8BEAT,
+	BM_TRANS_TYPE_9BEAT,
+	BM_TRANS_TYPE_10BEAT,
+	BM_TRANS_TYPE_11BEAT,
+	BM_TRANS_TYPE_12BEAT,
+	BM_TRANS_TYPE_13BEAT,
+	BM_TRANS_TYPE_14BEAT,
+	BM_TRANS_TYPE_15BEAT,
+	BM_TRANS_TYPE_16BEAT,
+	BM_TRANS_TYPE_1Byte = 0 << 4,
+	BM_TRANS_TYPE_2Byte = 1 << 4,
+	BM_TRANS_TYPE_4Byte = 2 << 4,
+	BM_TRANS_TYPE_8Byte = 3 << 4,
+	BM_TRANS_TYPE_16Byte = 4 << 4,
+	BM_TRANS_TYPE_32Byte = 5 << 4,
+	BM_TRANS_TYPE_BURST_WRAP = 0 << 7,
+	BM_TRANS_TYPE_BURST_INCR = 1 << 7
+};
+
+enum {
+	BM_TRANS_RW_DEFAULT = 0x0,
+	BM_TRANS_RW_READONLY,
+	BM_TRANS_RW_WRITEONLY,
+	BM_TRANS_RW_RWBOTH
+};
+
+
+/*coda busid 12bit, but HW support 16 bit*/
+#define EMI_BMID_MASK					(0xFFFF)
+#define BM_COUNTER_MAX				(21)
+
+/*
+*#define BUS_MON_EN		    (0x00000001)
+*#define BUS_MON_PAUSE		(0x00000002)
+*#define BUS_MON_IDLE		(0x00000008)
+*#define BC_OVERRUN		    (0x00000100)
+*/
+enum {
+	BUS_MON_EN_SHIFT = 0,
+	BUS_MON_PAUSE_SHIFT = 1,
+	BUS_MON_IDLE_SHIFT = 3,
+	BC_OVERRUN_SHIFT = 8,
+};
+
+#define BM_REQ_OK						(0)
+#define BM_ERR_WRONG_REQ				(-1)
+#define BM_ERR_OVERRUN					(-2)
+
+#define BM_WSCT_TSCT_IDSEL_ENABLE	(0)
+#define BM_WSCT_TSCT_IDSEL_DISABLE	(-1)
+#define BM_TTYPE1_16_ENABLE			(0)
+#define BM_TTYPE1_16_DISABLE			(-1)
+#define BM_TTYPE17_21_ENABLE			(0)
+#define BM_TTYPE17_21_DISABLE			(-1)
+#define BM_BW_LIMITER_ENABLE			(0)
+#define BM_BW_LIMITER_DISABLE			(-1)
+
+#define M0_DOUBLE_HALF_BW_1CH	(0x0)
+#define M0_DOUBLE_HALF_BW_2CH	(0x1)
+#define M0_DOUBLE_HALF_BW_4CH	(0x2)
+
+#ifdef CONFIG_MTK_TINYSYS_SSPM_SUPPORT
+/*ondiemet emi ipi command*/
+typedef enum {
+	SET_BASE_EMI = 0x0,
+	SET_BASE_DRAMC0,
+	SET_BASE_DRAMC1,
+	SET_BASE_DRAMC2,
+	SET_BASE_DRAMC3,
+	SET_BASE_DDRPHY0AO,
+	SET_BASE_DRAMC0_AO,
+	SET_EBM_CONFIGS,
+}	BM_EMI_IPI_Type;
+#endif
+
+#define	EMI_OFF			0x0000
+#define EMI_CONA		(0x000-EMI_OFF)
+#define EMI_CONH		(0x038-EMI_OFF)
+#define EMI_CONM		(0x060-EMI_OFF)
+#define EMI_CONO		(0x070-EMI_OFF)
+
+#define EMI_MDCT		(0x078-EMI_OFF)
+#define EMI_MDCT_2ND    (0x07C-EMI_OFF)
+
+#define EMI_ARBA		(0x100-EMI_OFF)
+#define EMI_ARBB		(0x108-EMI_OFF)
+#define EMI_ARBC		(0x110-EMI_OFF)
+#define EMI_ARBD		(0x118-EMI_OFF)
+#define EMI_ARBE		(0x120-EMI_OFF)
+#define EMI_ARBF		(0x128-EMI_OFF)
+#define EMI_ARBG		(0x130-EMI_OFF)
+#define EMI_ARBG_2ND    (0x134-EMI_OFF)
+#define EMI_ARBH		(0x138-EMI_OFF)
+
+#define EMI_BMEN		(0x400-EMI_OFF)
+#define EMI_BCNT		(0x408-EMI_OFF)
+#define EMI_TACT		(0x410-EMI_OFF)
+#define EMI_TSCT		(0x418-EMI_OFF)
+#define EMI_WACT		(0x420-EMI_OFF)
+#define EMI_WSCT		(0x428-EMI_OFF)
+#define EMI_BACT		(0x430-EMI_OFF)
+#define EMI_BSCT		(0x438-EMI_OFF)
+
+#define EMI_MSEL		(0x440-EMI_OFF)
+#define EMI_TSCT2		(0x448-EMI_OFF)
+#define EMI_TSCT3		(0x450-EMI_OFF)
+#define EMI_WSCT2		(0x458-EMI_OFF)
+#define EMI_WSCT3		(0x460-EMI_OFF)
+#define EMI_WSCT4		(0x464-EMI_OFF)
+#define EMI_MSEL2		(0x468-EMI_OFF)
+#define EMI_MSEL3		(0x470-EMI_OFF)
+#define EMI_MSEL4		(0x478-EMI_OFF)
+#define EMI_MSEL5		(0x480-EMI_OFF)
+#define EMI_MSEL6		(0x488-EMI_OFF)
+#define EMI_MSEL7		(0x490-EMI_OFF)
+#define EMI_MSEL8		(0x498-EMI_OFF)
+#define EMI_MSEL9		(0x4A0-EMI_OFF)
+#define EMI_MSEL10		(0x4A8-EMI_OFF)
+
+#define EMI_BMID0		(0x4B0-EMI_OFF)
+#define EMI_BMID1		(0x4B4-EMI_OFF)
+#define EMI_BMID2		(0x4B8-EMI_OFF)
+#define EMI_BMID3		(0x4BC-EMI_OFF)
+#define EMI_BMID4		(0x4C0-EMI_OFF)
+#define EMI_BMID5		(0x4C4-EMI_OFF)
+#define EMI_BMID6		(0x4C8-EMI_OFF)
+#define EMI_BMID7		(0x4CC-EMI_OFF)
+#define EMI_BMID8		(0x4D0-EMI_OFF)
+#define EMI_BMID9		(0x4D4-EMI_OFF)
+#define EMI_BMID10		(0x4D8-EMI_OFF)
+
+#define EMI_BMEN1		(0x4E0-EMI_OFF)
+#define EMI_BMEN2		(0x4E8-EMI_OFF)
+#define EMI_BMRW0		(0x4F8-EMI_OFF)
+#define EMI_BMRW1		(0x4FC-EMI_OFF)
+#define EMI_TTYPE1		(0x500-EMI_OFF)
+#define EMI_TTYPE2		(0x508-EMI_OFF)
+#define EMI_TTYPE3		(0x510-EMI_OFF)
+#define EMI_TTYPE4		(0x518-EMI_OFF)
+#define EMI_TTYPE5		(0x520-EMI_OFF)
+#define EMI_TTYPE6		(0x528-EMI_OFF)
+#define EMI_TTYPE7		(0x530-EMI_OFF)
+#define EMI_TTYPE8		(0x538-EMI_OFF)
+#define EMI_TTYPE9		(0x540-EMI_OFF)
+#define EMI_TTYPE10		(0x548-EMI_OFF)
+#define EMI_TTYPE11		(0x550-EMI_OFF)
+#define EMI_TTYPE12		(0x558-EMI_OFF)
+#define EMI_TTYPE13		(0x560-EMI_OFF)
+#define EMI_TTYPE14		(0x568-EMI_OFF)
+#define EMI_TTYPE15		(0x570-EMI_OFF)
+#define EMI_TTYPE16		(0x578-EMI_OFF)
+#define EMI_TTYPE17		(0x580-EMI_OFF)
+#define EMI_TTYPE18		(0x588-EMI_OFF)
+#define EMI_TTYPE19		(0x590-EMI_OFF)
+#define EMI_TTYPE20		(0x598-EMI_OFF)
+#define EMI_TTYPE21		(0x5A0-EMI_OFF)
+
+#define EMI_BWCT0		(0x5B0-EMI_OFF)
+#define EMI_BWCT1		(0x5B4-EMI_OFF)
+#define EMI_BWCT2		(0x5B8-EMI_OFF)
+#define EMI_BWCT3		(0x5BC-EMI_OFF)
+#define EMI_BWCT4		(0x5C0-EMI_OFF)
+#define EMI_BWST0		(0x5C4-EMI_OFF)
+#define EMI_BWST1		(0x5C8-EMI_OFF)
+
+#define EMI_BWCT0_2ND	(0x6A0-EMI_OFF)
+#define EMI_BWCT1_2ND	(0x6A4-EMI_OFF)
+#define EMI_BWST_2ND	(0x6A8-EMI_OFF)
+
+#define DRAMC_DMMONITOR		0x24
+#define DRAMC_MISC_STATUSA	0x80
+#define DRAMC_REFRESH_POP       0x300
+#define DRAMC_FREERUN_26M       0x304
+#define DRAMC_R2R_PAGE_HIT	0x30C
+#define DRAMC_R2R_PAGE_MISS	0x310
+#define DRAMC_R2R_INTERBANK	0x314
+#define DRAMC_R2W_PAGE_HIT	0x318
+#define DRAMC_R2W_PAGE_MISS	0x31C
+#define DRAMC_R2W_INTERBANK	0x320
+#define DRAMC_W2R_PAGE_HIT	0x324
+#define DRAMC_W2R_PAGE_MISS	0x328
+#define DRAMC_W2R_INTERBANK	0x32C
+#define DRAMC_W2W_PAGE_HIT	0x330
+#define DRAMC_W2W_PAGE_MISS	0x334
+#define DRAMC_W2W_INTERBANK	0x338
+#define DRAMC_IDLE_COUNT	0x308
+#define DRAMC_RK0_PRE_STANDBY   0x33c
+#define DRAMC_RK0_PRE_POWERDOWN 0x340
+#define DRAMC_RK0_ACT_STANDBY   0x344
+#define DRAMC_RK0_ACT_POWERDOWN 0x348
+#define DRAMC_RK1_PRE_STANDBY   0x34c
+#define DRAMC_RK1_PRE_POWERDOWN 0x350
+#define DRAMC_RK1_ACT_STANDBY   0x354
+#define DRAMC_RK1_ACT_POWERDOWN 0x358
+#define DRAMC_RK2_PRE_STANDBY   0x35c
+#define DRAMC_RK2_PRE_POWERDOWN 0x360
+#define DRAMC_RK2_ACT_STANDBY   0x364
+#define DRAMC_RK2_ACT_POWERDOWN 0x368
+#define DRAMC_READ_BYTES	0x38c
+#define DRAMC_WRITE_BYTES	0x390
+
+
+extern void emi_dump_reg(void);
+extern int MET_BM_Init(void);
+extern void MET_BM_DeInit(void);
+extern void MET_BM_Enable(const unsigned int enable);
+extern void MET_BM_Pause(void);
+extern void MET_BM_Continue(void);
+extern unsigned int MET_BM_IsOverrun(void);
+extern unsigned int MET_BM_GetReadWriteType(void);
+extern void MET_BM_SetReadWriteType(const unsigned int ReadWriteType);
+extern int MET_BM_GetBusCycCount(void);
+extern unsigned int MET_BM_GetTransAllCount(void);
+extern int MET_BM_GetTransCount(const unsigned int counter_num);
+extern int MET_BM_GetWordAllCount(void);
+extern int MET_BM_GetWordCount(const unsigned int counter_num);
+extern unsigned int MET_BM_GetBandwidthWordCount(void);
+extern unsigned int MET_BM_GetOverheadWordCount(void);
+extern int MET_BM_GetTransTypeCount(const unsigned int counter_num);
+extern int MET_BM_GetMDCT(void);
+extern int MET_BM_GetMDCT_2(void);
+extern int MET_BM_GetMonitorCounter(const unsigned int counter_num,
+		unsigned int *master,
+		unsigned int *trans_type);
+extern int MET_BM_SetMDCT_MDMCU(unsigned int mdmcu_rd_buf);
+extern int MET_BM_SetMonitorCounter(const unsigned int counter_num,
+		const unsigned int master,
+		const unsigned int trans_type);
+extern int MET_BM_SetTtypeCounterRW(unsigned int bmrw0_val, unsigned int bmrw1_val);
+extern int MET_BM_Set_WsctTsct_id_sel(unsigned int counter_num, unsigned int enable);
+extern int MET_BM_SetMaster(const unsigned int counter_num,
+		const unsigned int master);
+extern int MET_BM_SetIDSelect(const unsigned int counter_num,
+		const unsigned int id,
+		const unsigned int enable);
+extern int MET_BM_SetUltraHighFilter(const unsigned int counter_num,
+		const unsigned int enable);
+extern int MET_BM_SetLatencyCounter(unsigned int enable);
+extern int MET_BM_GetLatencyCycle(const unsigned int counter_num);
+extern unsigned int MET_BM_GetEmiDcm(void);
+extern int MET_BM_SetEmiDcm(const unsigned int setting);
+
+/* DRAMC */
+extern unsigned int MET_DRAMC_GetPageHitCount(DRAMC_Cnt_Type CountType, int chann);
+extern unsigned int MET_DRAMC_GetPageMissCount(DRAMC_Cnt_Type CountType, int chann);
+extern unsigned int MET_DRAMC_GetInterbankCount(DRAMC_Cnt_Type CountType, int chann);
+extern unsigned int MET_DRAMC_GetIdleCount(int chann);
+extern unsigned int MET_DRAMC_Misc_Status(int chann);
+extern unsigned int MET_DRAMC_RefPop(int chann);
+extern unsigned int MET_DRAMC_Free26M(int chann);
+extern unsigned int MET_DRAMC_RByte(int chann);
+extern unsigned int MET_DRAMC_WByte(int chann);
+extern int MET_DRAMC_BMEnable(int chann, int set);
+extern int MET_DRAMC_BMPause(int chann, int set);
+extern unsigned int get_dram_data_rate(void);
+
+/* Config */
+unsigned int MET_EMI_GetARBA(void);
+unsigned int MET_EMI_GetARBB(void);
+unsigned int MET_EMI_GetARBC(void);
+unsigned int MET_EMI_GetARBD(void);
+unsigned int MET_EMI_GetARBE(void);
+unsigned int MET_EMI_GetARBF(void);
+unsigned int MET_EMI_GetARBG(void);
+unsigned int MET_EMI_GetARBH(void);
+
+/* Total BW status */
+extern unsigned int MET_EMI_GetBWCT0(void);
+extern unsigned int MET_EMI_GetBWCT1(void);
+extern unsigned int MET_EMI_GetBWCT2(void);
+extern unsigned int MET_EMI_GetBWCT3(void);
+extern unsigned int MET_EMI_GetBWCT4(void);
+extern unsigned int MET_EMI_GetBWST0(void);
+extern unsigned int MET_EMI_GetBWST1(void);
+/* C+G BW */
+extern unsigned int MET_EMI_GetBWCT0_2ND(void);
+extern unsigned int MET_EMI_GetBWCT1_2ND(void);
+extern unsigned int MET_EMI_GetBWST_2ND(void);
+
+unsigned int MET_EMI_GetBMRW0(void);
+unsigned int MET_EMI_GetDramChannNum(void);
+
+/* Debug Counter status */
+void MET_DRAMC_GetDebugCounter(int *value, int chann);
+
+/* ondiemet*/
+void MET_BM_IPI_baseaddr(void);
+void met_emi_phyaddr_debug(void);
+
+
+
+#endif  /* !__MT_MET_EMI_BM_H__ */
diff --git a/src/devtools/met-driver/met_drv/common/mtk_gpu_metmonitor.c b/src/devtools/met-driver/met_drv/common/mtk_gpu_metmonitor.c
new file mode 100644
index 0000000..52ae832
--- /dev/null
+++ b/src/devtools/met-driver/met_drv/common/mtk_gpu_metmonitor.c
@@ -0,0 +1,797 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <asm/page.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/syscalls.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <asm/uaccess.h>
+#include <linux/hrtimer.h>
+
+#include "met_drv.h"
+#include "trace.h"
+
+#include "mtk_gpu_metmonitor.h"
+#include "core_plf_init.h"
+#include "core_plf_trace.h"
+
+
+/*
+ * define if the hal implementation might re-schedule, cannot run inside softirq
+ * undefine this is better for sampling jitter if HAL support it
+ */
+#undef GPU_HAL_RUN_PREMPTIBLE
+
+#ifdef GPU_HAL_RUN_PREMPTIBLE
+static struct delayed_work gpu_dwork;
+//static struct delayed_work gpu_pwr_dwork;
+#endif
+
+/* the mt_gpufreq_get_thermal_limit_freq use mutex_lock to do its job */
+/* so, change the gpu-dvfs implementation to dwork */
+static struct delayed_work gpu_dvfs_dwork;
+
+/*
+ * GPU monitor HAL comes from alps\mediatek\kernel\include\linux\mtk_gpu_utility.h
+ *
+ * mtk_get_gpu_memory_usage(unsigned int* pMemUsage) in unit of bytes
+ *
+ * mtk_get_gpu_xxx_loading are in unit of %
+*/
+
+enum MET_GPU_PROFILE_INDEX {
+	eMET_GPU_LOADING = 0,
+	eMET_GPU_BLOCK_LOADING,	/* 1 */
+	eMET_GPU_IDLE_LOADING,	/* 2 */
+	eMET_GPU_PROFILE_CNT
+};
+
+static unsigned long g_u4AvailableInfo;
+
+noinline void GPU_Loading(unsigned char cnt, int *value)
+{
+	switch (cnt) {
+	case 1:
+		MET_TRACE("%d\n", value[0]);
+		break;
+	case 2:
+		MET_TRACE("%d,%d\n", value[0], value[1]);
+		break;
+	case 3:
+		MET_TRACE("%d,%d,%d\n", value[0], value[1], value[2]);
+		break;
+	case 4:
+		MET_TRACE("%d,%d,%d,%d\n", value[0], value[1], value[2], value[3]);
+		break;
+	default:
+		break;
+	}
+
+}
+
+noinline void GPU_Sub_Loading(unsigned int loading)
+{
+	MET_TRACE("%u\n", loading);
+}
+
+noinline void GPU_3D_Fences_Count(int count)
+{
+	MET_TRACE("%d\n", count);
+}
+
+#ifdef GPU_HAL_RUN_PREMPTIBLE
+static void gpu_GPULoading(struct work_struct *work)
+{
+	unsigned int	pu4Value[eMET_GPU_PROFILE_CNT];
+	unsigned long	u4Index = 0;
+	unsigned int	loading = 0;
+	int		count = 0;
+
+	memset(pu4Value, 0x00, sizeof(unsigned int)*eMET_GPU_PROFILE_CNT);
+	if ((1 << eMET_GPU_LOADING) & g_u4AvailableInfo) {
+		if (mtk_get_gpu_loading_symbol && mtk_get_gpu_loading_symbol(&pu4Value[u4Index]))
+			u4Index += 1;
+	}
+
+	if ((1 << eMET_GPU_BLOCK_LOADING) & g_u4AvailableInfo) {
+		if (mtk_get_gpu_block_symbol && mtk_get_gpu_block_symbol(&pu4Value[u4Index]))
+			u4Index += 1;
+	}
+
+	if ((1 << eMET_GPU_IDLE_LOADING) & g_u4AvailableInfo) {
+		if (mtk_get_gpu_idle_symbol && mtk_get_gpu_idle_symbol(&pu4Value[u4Index]))
+			u4Index += 1;
+	}
+
+	if (g_u4AvailableInfo)
+		GPU_Loading(u4Index, pu4Value);
+
+	if (mtk_get_gpu_sub_loading_symbol && mtk_get_gpu_sub_loading_symbol(&loading))
+		GPU_Sub_Loading(loading);
+
+	if (mtk_get_3D_fences_count_symbol && mtk_get_3D_fences_count_symbol(&count))
+		GPU_3D_Fences_Count(count);
+}
+#else
+static void gpu_GPULoading(unsigned long long stamp, int cpu)
+{
+	int	pu4Value[eMET_GPU_PROFILE_CNT];
+	unsigned long	u4Index = 0;
+//	unsigned int	loading = 0;
+//	int		count = 0;
+
+	memset(pu4Value, 0x00, sizeof(int)*eMET_GPU_PROFILE_CNT);
+	if ((1 << eMET_GPU_LOADING) & g_u4AvailableInfo) {
+		if (mtk_get_gpu_loading_symbol)
+			pu4Value[u4Index] = mtk_get_gpu_loading_symbol();
+		else
+			pu4Value[u4Index] = -1;
+
+		u4Index += 1;
+	}
+
+#if 0
+	if ((1 << eMET_GPU_BLOCK_LOADING) & g_u4AvailableInfo) {
+		if (mtk_get_gpu_block_symbol) {
+			mtk_get_gpu_block_symbol(&pu4Value[u4Index]);
+			u4Index += 1;
+		}
+	}
+
+	if ((1 << eMET_GPU_IDLE_LOADING) & g_u4AvailableInfo) {
+		if (mtk_get_gpu_idle_symbol) {
+			mtk_get_gpu_idle_symbol(&pu4Value[u4Index]);
+			u4Index += 1;
+		}
+	}
+#endif
+
+	if (g_u4AvailableInfo)
+		GPU_Loading(u4Index, pu4Value);
+
+#if 0
+	if (mtk_get_gpu_sub_loading_symbol) {
+		mtk_get_gpu_sub_loading_symbol(&loading);
+		GPU_Sub_Loading(loading);
+	}
+
+	if (mtk_get_3D_fences_count_symbol) {
+		mtk_get_3D_fences_count_symbol(&count);
+		GPU_3D_Fences_Count(count);
+	}
+#endif
+}
+#endif
+
+static void gpu_monitor_start(void)
+{
+	if (mtk_get_gpu_loading_symbol) {
+		g_u4AvailableInfo |= (1 << eMET_GPU_LOADING);
+	}
+#if 0
+	if (mtk_get_gpu_block_symbol)
+		g_u4AvailableInfo |= (1 << eMET_GPU_BLOCK_LOADING);
+	if (mtk_get_gpu_idle_symbol)
+		g_u4AvailableInfo |= (1 << eMET_GPU_IDLE_LOADING);
+#endif
+
+#ifdef GPU_HAL_RUN_PREMPTIBLE
+	INIT_DELAYED_WORK(&gpu_dwork, gpu_GPULoading);
+#endif
+}
+
+#ifdef GPU_HAL_RUN_PREMPTIBLE
+static void gpu_monitor_stop(void)
+{
+	cancel_delayed_work_sync(&gpu_dwork);
+}
+
+static void GPULoadingNotify(unsigned long long stamp, int cpu)
+{
+	schedule_delayed_work(&gpu_dwork, 0);
+}
+#endif
+
+static char help[] =
+	"  --gpu				monitor gpu status\n";
+static int gpu_status_print_help(char *buf, int len)
+{
+	return snprintf(buf, PAGE_SIZE, help);
+}
+
+static char g_pComGPUStatusHeader[] =
+	"met-info [000] 0.0: met_gpu_loading_header: ";
+static int gpu_status_print_header(char *buf, int len)
+{
+	int ret = 0;
+
+	ret = snprintf(buf, PAGE_SIZE, "%s", g_pComGPUStatusHeader);
+
+	if ((1 << eMET_GPU_LOADING) & g_u4AvailableInfo)
+		ret += snprintf(buf+ret, PAGE_SIZE-ret, "%s", "Loading\n");
+
+#if 0
+	if ((1 << eMET_GPU_BLOCK_LOADING) & g_u4AvailableInfo)
+		ret += snprintf(buf+ret, PAGE_SIZE-ret, "%s", "Blcok,");
+
+	if ((1 << eMET_GPU_IDLE_LOADING) & g_u4AvailableInfo)
+		ret += snprintf(buf+ret, PAGE_SIZE-ret, "%s", "Idle");
+
+	ret += snprintf(buf+ret, PAGE_SIZE-ret, "%s", "\n");
+
+	ret += snprintf(buf+ret, PAGE_SIZE-ret,
+			"met-info [000] 0.0: met_gpu_sub_loading_header: Loading\n");
+	ret += snprintf(buf+ret, PAGE_SIZE-ret,
+			"met-info [000] 0.0: met_gpu_3d_fences_count_header: Count\n");
+#endif
+
+	return ret;
+}
+
+struct metdevice met_gpu = {
+	.name			= "gpu",
+	.owner			= THIS_MODULE,
+	.type			= MET_TYPE_BUS,
+	.cpu_related		= 0,
+	.start			= gpu_monitor_start,
+	.mode			= 0,
+	.polling_interval	= 1,	/* ms */
+#ifdef GPU_HAL_RUN_PREMPTIBLE
+	.timed_polling		= GPULoadingNotify,
+	.stop			= gpu_monitor_stop,
+#else
+	.timed_polling		= gpu_GPULoading,
+#endif
+	.print_help		= gpu_status_print_help,
+	.print_header		= gpu_status_print_header,
+};
+
+/*
+ * GPU DVFS Monitor
+ */
+#if 0
+#define	MTK_GPU_DVFS_TYPE_ITEM(type)	#type,
+static char *gpu_dvfs_type_name[] = MTK_GPU_DVFS_TYPE_LIST;
+#undef	MTK_GPU_DVFS_TYPE_ITEM
+
+static MTK_GPU_DVFS_TYPE gpu_dvfs_type_prev;
+static unsigned long gpu_dvfs_type_freq_prev;
+static unsigned int gpu_dvfs_type_freq[ARRAY_SIZE(gpu_dvfs_type_name)];
+#endif
+
+#if 0
+noinline void GPU_DVFS(unsigned int Freq, unsigned int ThermalLimit,
+			unsigned long CustomBoost, unsigned long CustomUpbound)
+{
+	MET_TRACE("%u,%u,%lu,%lu\n", Freq, ThermalLimit, CustomBoost, CustomUpbound);
+}
+#else
+noinline void GPU_DVFS(int Freq)
+{
+	MET_TRACE("%d\n", Freq);
+}
+#endif
+
+#if 0
+noinline void GPU_DVFS_TYPE(void)
+{
+	char	*SOB, *EOB;
+
+	MET_TRACE_GETBUF(&SOB, &EOB);
+	EOB = ms_formatD_EOL(EOB, ARRAY_SIZE(gpu_dvfs_type_freq), gpu_dvfs_type_freq);
+	MET_TRACE_PUTBUF(SOB, EOB);
+}
+
+noinline void GPU_DVFS_VSYNC(unsigned long freq)
+{
+	MET_TRACE("%lu\n", freq);
+}
+
+noinline void GPU_VSYNC_OFFSET_STATUS(unsigned int event_status, unsigned int debug_status)
+{
+	MET_TRACE("%u,%u\n", event_status, debug_status);
+}
+#endif
+
+static void gpu_dvfs(void)
+{
+	int		freq = 0;
+#if 0
+	unsigned int		ThermalLimit = 0;
+	MTK_GPU_DVFS_TYPE	peType;
+	unsigned long		pulFreq = 0;
+	unsigned long		CustomBoost = 0;
+	unsigned long		CustomUpbound = 0;
+	unsigned int		event_status = 0;
+	unsigned int		debug_status = 0;
+#endif
+
+	freq = mtk_gpufreq_get_cur_freq_symbol ? mtk_gpufreq_get_cur_freq_symbol() : 0;
+#if 0
+	ThermalLimit = mt_gpufreq_get_thermal_limit_freq_symbol ? mt_gpufreq_get_thermal_limit_freq_symbol() : 0;
+	if (mtk_get_custom_boost_gpu_freq_symbol)
+		mtk_get_custom_boost_gpu_freq_symbol(&CustomBoost);
+	if (mtk_get_custom_upbound_gpu_freq_symbol)
+		mtk_get_custom_upbound_gpu_freq_symbol(&CustomUpbound);
+#endif
+//	GPU_DVFS(freq, ThermalLimit, CustomBoost, CustomUpbound);
+	GPU_DVFS(freq);
+
+#if 0
+	/* gpu dvfs type */
+	if (mtk_get_gpu_dvfs_from_symbol && mtk_get_gpu_dvfs_from_symbol(&peType, &pulFreq)) {
+		if (gpu_dvfs_type_prev != peType || gpu_dvfs_type_freq_prev != pulFreq) {
+			gpu_dvfs_type_freq[gpu_dvfs_type_prev] = 0;
+			gpu_dvfs_type_prev = peType;
+			gpu_dvfs_type_freq_prev = pulFreq;
+			gpu_dvfs_type_freq[gpu_dvfs_type_prev] = gpu_dvfs_type_freq_prev;
+			GPU_DVFS_TYPE();
+		}
+	}
+
+	if (mtk_get_vsync_based_target_freq_symbol && mtk_get_vsync_based_target_freq_symbol(&pulFreq))
+		GPU_DVFS_VSYNC(pulFreq);
+
+	if (mtk_get_vsync_offset_event_status_symbol && mtk_get_vsync_offset_debug_status_symbol) {
+		if (mtk_get_vsync_offset_event_status_symbol(&event_status)
+		    && mtk_get_vsync_offset_debug_status_symbol(&debug_status)) {
+			GPU_VSYNC_OFFSET_STATUS(event_status, debug_status);
+		}
+	}
+#endif
+}
+
+static void gpu_dvfs_work(struct work_struct *work)
+{
+	gpu_dvfs();
+}
+
+static void gpu_dvfs_monitor_start(void)
+{
+	gpu_dvfs();
+	INIT_DELAYED_WORK(&gpu_dvfs_dwork, gpu_dvfs_work);
+}
+
+static void gpu_dvfs_monitor_stop(void)
+{
+	cancel_delayed_work_sync(&gpu_dvfs_dwork);
+	gpu_dvfs();
+}
+
+static void gpu_dvfs_monitor_polling(unsigned long long stamp, int cpu)
+{
+	schedule_delayed_work(&gpu_dvfs_dwork, 0);
+}
+
+static int gpu_dvfs_print_help(char *buf, int len)
+{
+	return snprintf(buf, PAGE_SIZE,
+			"  --gpu-dvfs				monitor gpu freq\n");
+}
+
+static int gpu_dvfs_print_header(char *buf, int len)
+{
+	int ret = 0;
+//	int i = 0;
+
+	ret = snprintf(buf, PAGE_SIZE,
+			"met-info [000] 0.0: met_gpu_dvfs_header: ");
+#if 0
+	ret += snprintf(buf+ret, PAGE_SIZE-ret,
+			"Freq(kHz),ThermalLimit(kHz),CustomBoost,CustomUpbound\n");
+#else
+	ret += snprintf(buf+ret, PAGE_SIZE-ret,
+			"Freq(kHz)\n");
+#endif
+
+#if 0
+	ret += snprintf(buf+ret, PAGE_SIZE-ret,
+			"met-info [000] 0.0: met_gpu_dvfs_type_header: %s", gpu_dvfs_type_name[0]);
+	for (i = 1; i < ARRAY_SIZE(gpu_dvfs_type_name); i++)
+		ret += snprintf(buf+ret, PAGE_SIZE-ret, ",%s", gpu_dvfs_type_name[i]);
+	ret += snprintf(buf+ret, PAGE_SIZE-ret, "%s", "\n");
+
+	ret += snprintf(buf+ret, PAGE_SIZE-ret,
+			"met-info [000] 0.0: met_gpu_dvfs_vsync_header: VSYNC Based Freq\n");
+	ret += snprintf(buf+ret, PAGE_SIZE-ret,
+			"met-info [000] 0.0: met_gpu_vsync_offset_status_header: Event Status,Debug Status\n");
+#endif
+
+	return ret;
+}
+
+struct metdevice met_gpudvfs = {
+	.name			= "gpu-dvfs",
+	.owner			= THIS_MODULE,
+	.type			= MET_TYPE_BUS,
+	.cpu_related		= 0,
+	.start			= gpu_dvfs_monitor_start,
+	.stop			= gpu_dvfs_monitor_stop,
+	.polling_interval	= 1,	/* ms */
+	.timed_polling		= gpu_dvfs_monitor_polling,
+	.print_help		= gpu_dvfs_print_help,
+	.print_header		= gpu_dvfs_print_header,
+	.ondiemet_mode		= 0,
+};
+
+/*
+ * GPU MEM monitor
+ */
+static unsigned long g_u4MemProfileIsOn;
+
+static void gpu_mem_monitor_start(void)
+{
+	if (!mtk_get_gpu_memory_usage_symbol)
+		return;
+
+	g_u4MemProfileIsOn = 1;
+}
+
+noinline void GPU_MEM(unsigned long long stamp, int cpu)
+{
+	unsigned int u4Value = 0;
+
+	if (!mtk_get_gpu_memory_usage_symbol)
+		return;
+
+	if (g_u4MemProfileIsOn == 1) {
+		u4Value = mtk_get_gpu_memory_usage_symbol();
+		MET_TRACE("%d\n", u4Value);
+	}
+}
+
+static void gpu_mem_monitor_stop(void)
+{
+	g_u4MemProfileIsOn = 0;
+}
+
+static char help_mem[] =
+	"  --gpu-mem				monitor gpu memory status\n";
+static int gpu_mem_status_print_help(char *buf, int len)
+{
+	return snprintf(buf, PAGE_SIZE, help_mem);
+}
+
+static char g_pComGPUMemHeader[] =
+	"met-info [000] 0.0: met_gpu_mem_header: Usage(Byte)\n";
+static int gpu_mem_status_print_header(char *buf, int len)
+{
+	return snprintf(buf, PAGE_SIZE, g_pComGPUMemHeader);
+}
+
+struct metdevice met_gpumem = {
+	.name			= "gpu-mem",
+	.owner			= THIS_MODULE,
+	.type			= MET_TYPE_BUS,
+	.cpu_related		= 0,
+	.start			= gpu_mem_monitor_start,
+	.stop			= gpu_mem_monitor_stop,
+	.mode			= 0,
+	.polling_interval	= 1,	/* ms */
+	.timed_polling		= GPU_MEM,
+	.print_help		= gpu_mem_status_print_help,
+	.print_header		= gpu_mem_status_print_header,
+};
+
+#if 0
+
+/*
+ * GPU power monitor
+ */
+static unsigned long g_u4PowerProfileIsOn;
+
+#ifdef GPU_HAL_RUN_PREMPTIBLE
+noinline void GPU_Power(struct work_struct *work)
+{
+	unsigned int u4Value = 0;
+
+	if (!mtk_get_gpu_power_loading_symbol)
+		return;
+
+	mtk_get_gpu_power_loading_symbol(&u4Value);
+	MET_TRACE("%d\n", u4Value);
+}
+
+static void GPU_PowerNotify(unsigned long long stamp, int cpu)
+{
+	if (g_u4PowerProfileIsOn == 1)
+		schedule_delayed_work(&gpu_pwr_dwork, 0);
+}
+#else
+noinline void GPU_Power(unsigned long long stamp, int cpu)
+{
+	unsigned int u4Value = 0;
+
+	if (!mtk_get_gpu_power_loading_symbol)
+		return;
+
+	if (g_u4PowerProfileIsOn == 1) {
+		mtk_get_gpu_power_loading_symbol(&u4Value);
+		MET_TRACE("%d\n", u4Value);
+	}
+}
+#endif
+
+static void gpu_Power_monitor_start(void)
+{
+	if (!mtk_get_gpu_power_loading_symbol)
+		return;
+
+#if 0
+	if (mtk_get_gpu_power_loading_symbol(&u4Value))
+		g_u4PowerProfileIsOn = 1;
+#endif
+	g_u4PowerProfileIsOn = 1;
+#ifdef GPU_HAL_RUN_PREMPTIBLE
+	INIT_DELAYED_WORK(&gpu_pwr_dwork, GPU_Power);
+#endif
+}
+
+static void gpu_Power_monitor_stop(void)
+{
+	g_u4PowerProfileIsOn = 0;
+
+#ifdef GPU_HAL_RUN_PREMPTIBLE
+	cancel_delayed_work_sync(&gpu_pwr_dwork);
+#endif
+}
+
+static char help_pwr[] =
+	"  --gpu-pwr				monitor gpu power status\n";
+static int gpu_Power_status_print_help(char *buf, int len)
+{
+	return snprintf(buf, PAGE_SIZE, help_pwr);
+}
+
+static char g_pComGPUPowerHeader[] =
+	"met-info [000] 0.0: met_gpu_power_header: Loading\n";
+static int gpu_Power_status_print_header(char *buf, int len)
+{
+	return snprintf(buf, PAGE_SIZE, g_pComGPUPowerHeader);
+}
+
+struct metdevice met_gpupwr = {
+	.name			= "gpu-pwr",
+	.owner			= THIS_MODULE,
+	.type			= MET_TYPE_BUS,
+	.cpu_related		= 0,
+	.start			= gpu_Power_monitor_start,
+	.stop			= gpu_Power_monitor_stop,
+	.mode			= 0,
+	.polling_interval	= 1,	/* ms */
+#ifdef GPU_HAL_RUN_PREMPTIBLE
+	.timed_polling		= GPU_PowerNotify,
+#else
+	.timed_polling		= GPU_Power,
+#endif
+	.print_help		= gpu_Power_status_print_help,
+	.print_header		= gpu_Power_status_print_header,
+};
+
+
+/*
+ * GPU PMU
+ */
+#define UNUSE_ARG(arg) ((void)arg)
+
+#ifdef GPU_HAL_RUN_PREMPTIBLE
+static struct delayed_work gpu_pmu_dwork;
+#endif
+
+#define MAX_PMU_STR_LEN (1024 * 5)
+
+static const char help_pmu[] = "  --gpu-pmu				monitor gpu pmu status";
+static const char header_pmu[] = "met-info [000] 0.0: met_gpu_pmu_header: ";
+static char pmu_str[MAX_PMU_STR_LEN];
+static int pmu_cnt;
+static int gpu_pwr_status = 1;
+static GPU_PMU *pmu_list;
+
+
+noinline void GPU_PMU_RAW(
+	unsigned long long stamp,
+	int cpu)
+{
+	bool ret;
+	int i = 0;
+	char *SOB, *EOB;
+	unsigned int value[pmu_cnt];
+
+	if (stamp == 0 && cpu == 0) {
+		for (i = 0; i < pmu_cnt; i++)
+			value[i] = 0;
+
+		MET_TRACE_GETBUF(&SOB, &EOB);
+		EOB = ms_formatH(EOB, pmu_cnt, value);
+		MET_TRACE_PUTBUF(SOB, EOB);
+		return;
+	}
+
+	if (mtk_get_gpu_pmu_swapnreset_symbol) {
+		ret = mtk_get_gpu_pmu_swapnreset_symbol(pmu_list, pmu_cnt);
+		if (ret) {
+			for (i = 0; i < pmu_cnt; i++) {
+				if (pmu_list[i].overflow)
+					pmu_list[i].value = 0xFFFFFFFF;
+				value[i] = pmu_list[i].value;
+			}
+			MET_TRACE_GETBUF(&SOB, &EOB);
+			EOB = ms_formatH(EOB, pmu_cnt, value);
+			MET_TRACE_PUTBUF(SOB, EOB);
+		}
+	}
+}
+
+static int create_gpu_pmu_list(void)
+{
+	int ret = 0;
+	int len = 0;
+	int i = 0;
+
+	if (mtk_get_gpu_pmu_init_symbol) {
+		ret = mtk_get_gpu_pmu_init(NULL, 0, &pmu_cnt);
+		if (pmu_cnt == 0 || ret == 0)
+			return 0;
+	} else
+		return 0;
+
+	pmu_list = kmalloc_array(pmu_cnt, sizeof(GPU_PMU), GFP_KERNEL);
+	if (pmu_list) {
+		memset(pmu_list, 0x00, sizeof(GPU_PMU)*pmu_cnt);
+		ret = mtk_get_gpu_pmu_init(pmu_list, pmu_cnt, NULL);
+
+		memset(pmu_str, 0x00, MAX_PMU_STR_LEN);
+		len = snprintf(pmu_str, MAX_PMU_STR_LEN, "%s", pmu_list[0].name);
+		for (i = 1; i < pmu_cnt; i++)
+			len += snprintf(pmu_str + len, MAX_PMU_STR_LEN - len, ",%s", pmu_list[i].name);
+
+		/*
+		* dummy read in order to reset GPU PMU counter
+		*/
+		if (mtk_get_gpu_pmu_swapnreset_symbol)
+			mtk_get_gpu_pmu_swapnreset_symbol(pmu_list, pmu_cnt);
+	}
+
+	return ret;
+}
+
+static void delete_gpu_pmu_list(void)
+{
+	kfree(pmu_list);
+	pmu_list = NULL;
+	pmu_cnt = 0;
+}
+
+static void gpu_pwr_status_cb(int on)
+{
+	MET_TRACE("on = %d\n", on);
+
+	if (on == 1) {
+		/*
+		* dummy read in order to reset GPU PMU counter
+		*/
+		if (mtk_get_gpu_pmu_swapnreset_symbol)
+			mtk_get_gpu_pmu_swapnreset_symbol(pmu_list, pmu_cnt);
+
+	} else {
+		GPU_PMU_RAW(1, 0);
+		GPU_PMU_RAW(0, 0);
+	}
+
+	gpu_pwr_status = on;
+}
+
+static void gpu_pmu_monitor_start(void)
+{
+	int ret;
+
+	ret = create_gpu_pmu_list();
+	if (ret == 0)
+		return;
+
+	if (mtk_register_gpu_power_change_symbol)
+		mtk_register_gpu_power_change_symbol("met_gpu", gpu_pwr_status_cb);
+
+#ifdef GPU_HAL_RUN_PREMPTIBLE
+	INIT_DELAYED_WORK(&gpu_pmu_dwork, GPU_PMU_RAW);
+#endif
+}
+
+static void gpu_pmu_monitor_stop(void)
+{
+#ifdef GPU_HAL_RUN_PREMPTIBLE
+	cancel_delayed_work_sync(&gpu_pmu_dwork);
+#endif
+
+	if (mtk_unregister_gpu_power_change_symbol)
+		mtk_unregister_gpu_power_change_symbol("met_gpu");
+	delete_gpu_pmu_list();
+
+#if 0
+	/* stop polling counter */
+	if (mtk_get_gpu_pmu_swapnreset_stop_symbol)
+		mtk_get_gpu_pmu_swapnreset_stop_symbol();
+	/* release resource */
+	if (mtk_get_gpu_pmu_deinit_symbol)
+		mtk_get_gpu_pmu_deinit_symbol();
+#endif
+}
+
+#ifdef GPU_HAL_RUN_PREMPTIBLE
+static void gpu_pmu_timed_polling_notify(
+	unsigned long long stamp,
+	int cpu)
+{
+	UNUSE_ARG(stamp);
+	UNUSE_ARG(cpu);
+
+	if (gpu_pwr_status == 1)
+		schedule_delayed_work(&gpu_pmu_dwork, 0);
+}
+#else
+static void gpu_pmu_timed_polling(
+	unsigned long long stamp,
+	int cpu)
+{
+	UNUSE_ARG(stamp);
+	UNUSE_ARG(cpu);
+
+	if (gpu_pwr_status == 1)
+		GPU_PMU_RAW(stamp, cpu);
+}
+#endif
+
+static int gpu_pmu_print_help(
+	char *buf,
+	int len)
+{
+	UNUSE_ARG(len);
+	return snprintf(buf, PAGE_SIZE, "%s\n", help_pmu);
+}
+
+static int gpu_pmu_print_header(
+	char *buf,
+	int len)
+{
+	len = 0;
+
+	len = snprintf(buf, PAGE_SIZE, "%s", header_pmu);
+	len += snprintf(buf + len, PAGE_SIZE - len, "%s\n", pmu_str);
+
+	return len;
+}
+
+struct metdevice met_gpu_pmu = {
+	.name			= "gpu-pmu",
+	.owner			= THIS_MODULE,
+	.type			= MET_TYPE_PMU,
+	.cpu_related		= 0,
+	.start			= gpu_pmu_monitor_start,
+	.stop			= gpu_pmu_monitor_stop,
+	.mode			= 0,
+	.polling_interval	= 1,	/* ms */
+#ifdef GPU_HAL_RUN_PREMPTIBLE
+	.timed_polling		= gpu_pmu_timed_polling_notify,
+#else
+	.timed_polling		= gpu_pmu_timed_polling,
+#endif
+	.print_help		= gpu_pmu_print_help,
+	.print_header		= gpu_pmu_print_header,
+};
+
+#endif
diff --git a/src/devtools/met-driver/met_drv/common/mtk_gpu_metmonitor.h b/src/devtools/met-driver/met_drv/common/mtk_gpu_metmonitor.h
new file mode 100644
index 0000000..069c534
--- /dev/null
+++ b/src/devtools/met-driver/met_drv/common/mtk_gpu_metmonitor.h
@@ -0,0 +1,18 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MT_GPU_METMONITOR_H_
+
+#define _MT_GPU_METMONITOR_H_
+
+#endif				/* _MT_GPU_METMONITOR_H_ */
diff --git a/src/devtools/met-driver/met_drv/common/mtk_typedefs.h b/src/devtools/met-driver/met_drv/common/mtk_typedefs.h
new file mode 100644
index 0000000..2b7af2d
--- /dev/null
+++ b/src/devtools/met-driver/met_drv/common/mtk_typedefs.h
@@ -0,0 +1,318 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MT_TYPEDEFS_H__
+
+/*
+ *  KOBJ ATTR Manipulations Macros
+ */
+
+#define KOBJ_ITEM_LIST(args...)		args
+
+/*
+ * Declaring KOBJ attributes
+ */
+#define DECLARE_KOBJ_ATTR(attr_name) \
+	static struct kobj_attribute attr_name##_attr = \
+		__ATTR(attr_name, 0664, attr_name##_show, attr_name##_store);
+
+#define DECLARE_KOBJ_ATTR_RO(attr_name) \
+	static struct kobj_attribute attr_name##_attr = \
+		__ATTR_RO(attr_name);
+
+/*
+ * Declaring KOBJ attributes with integer variable
+ */
+/* normal version */
+#define DECLARE_KOBJ_ATTR_SHOW_INT(attr_name, var_name) \
+	static ssize_t attr_name##_show( \
+		struct kobject *kobj, \
+		struct kobj_attribute *attr, \
+		char *buf) \
+	{ \
+		return snprintf(buf, PAGE_SIZE, "%d\n", var_name); \
+	}
+#define DECLARE_KOBJ_ATTR_STORE_INT(attr_name, var_name) \
+	static ssize_t attr_name##_store( \
+		struct kobject *kobj, \
+		struct kobj_attribute *attr, \
+		const char *buf, \
+		size_t n) \
+	{ \
+		int	val; \
+		if (kstrtoint(buf, 0, &val) != 0) { \
+			return -EINVAL; \
+		} \
+		var_name = val; \
+		return n; \
+	}
+#define DECLARE_KOBJ_ATTR_INT(attr_name, var_name) \
+	DECLARE_KOBJ_ATTR_SHOW_INT(attr_name, var_name) \
+	DECLARE_KOBJ_ATTR_STORE_INT(attr_name, var_name) \
+	DECLARE_KOBJ_ATTR(attr_name)
+#define DECLARE_KOBJ_ATTR_RO_INT(attr_name, var_name) \
+	DECLARE_KOBJ_ATTR_SHOW_INT(attr_name, var_name) \
+	DECLARE_KOBJ_ATTR_RO(attr_name)
+
+/* cond-check version */
+#define DECLARE_KOBJ_ATTR_STORE_INT_CHECK(attr_name, var_name, cond) \
+	static ssize_t attr_name##_store( \
+		struct kobject *kobj, \
+		struct kobj_attribute *attr, \
+		const char *buf, \
+		size_t n) \
+	{ \
+		int	var_name##temp = var_name; \
+		if (kstrtoint(buf, 0, &var_name) != 0) { \
+			var_name = var_name##temp; \
+			return -EINVAL; \
+		} \
+		if (cond) { \
+			return n; \
+		} else { \
+			var_name = var_name##temp; \
+			return -EINVAL; \
+		} \
+	}
+/* Note: the name of val in cond can NOT be the same as var_name */
+#define DECLARE_KOBJ_ATTR_INT_CHECK(attr_name, var_name, cond) \
+	DECLARE_KOBJ_ATTR_SHOW_INT(attr_name, var_name) \
+	DECLARE_KOBJ_ATTR_STORE_INT_CHECK(attr_name, var_name, cond) \
+	DECLARE_KOBJ_ATTR(attr_name)
+
+/* helper procedure version */
+#define DECLARE_KOBJ_ATTR_SHOW_INT_PROC(attr_name, var_name, func) \
+	static ssize_t attr_name##_show( \
+		struct kobject *kobj, \
+		struct kobj_attribute *attr, \
+		char *buf) \
+	{ \
+		return func(kobj, attr, buf, var_name); \
+	}
+#define DECLARE_KOBJ_ATTR_STORE_INT_PROC(attr_name, var_name, func) \
+	static ssize_t attr_name##_store( \
+		struct kobject *kobj, \
+		struct kobj_attribute *attr, \
+		const char *buf, \
+		size_t n) \
+	{ \
+		return func(kobj, attr, buf, n, &(var_name)); \
+	}
+#define DECLARE_KOBJ_ATTR_INT_PROC(attr_name, var_name, show, store) \
+	DECLARE_KOBJ_ATTR_SHOW_INT_PROC(attr_name, var_name, show) \
+	DECLARE_KOBJ_ATTR_STORE_INT_PROC(attr_name, var_name, store) \
+	DECLARE_KOBJ_ATTR(attr_name)
+
+/*
+ * Declaring KOBJ attributes with integer(hex) variable
+ */
+/* normal version */
+#define DECLARE_KOBJ_ATTR_SHOW_HEX(attr_name, var_name) \
+	static ssize_t attr_name##_show( \
+		struct kobject *kobj, \
+		struct kobj_attribute *attr, \
+		char *buf) \
+	{ \
+		return snprintf(buf, PAGE_SIZE, "%x\n", var_name); \
+	}
+#define DECLARE_KOBJ_ATTR_STORE_HEX(attr_name, var_name) \
+	static ssize_t attr_name##_store( \
+		struct kobject *kobj, \
+		struct kobj_attribute *attr, \
+		const char *buf, \
+		size_t n) \
+	{ \
+		unsigned int	val; \
+		if (kstrtouint(buf, 0, &val) != 0) { \
+			return -EINVAL; \
+		} \
+		var_name = val; \
+		return n; \
+	}
+#define DECLARE_KOBJ_ATTR_HEX(attr_name, var_name) \
+	DECLARE_KOBJ_ATTR_SHOW_HEX(attr_name, var_name) \
+	DECLARE_KOBJ_ATTR_STORE_HEX(attr_name, var_name) \
+	DECLARE_KOBJ_ATTR(attr_name)
+#define DECLARE_KOBJ_ATTR_RO_HEX(attr_name, var_name) \
+	DECLARE_KOBJ_ATTR_SHOW_HEX(attr_name, var_name) \
+	DECLARE_KOBJ_ATTR_RO(attr_name)
+
+/* cond-check version */
+#define DECLARE_KOBJ_ATTR_STORE_HEX_CHECK(attr_name, var_name, cond) \
+	static ssize_t attr_name##_store( \
+		struct kobject *kobj, \
+		struct kobj_attribute *attr, \
+		const char *buf, \
+		size_t n) \
+	{ \
+		unsigned int	var_name##temp = var_name; \
+		if (kstrtouint(buf, 0, &var_name) != 0) { \
+			var_name = var_name##temp; \
+			return -EINVAL; \
+		} \
+		if (cond) { \
+			return n; \
+		} else { \
+			var_name = var_name##temp; \
+			return -EINVAL; \
+		} \
+	}
+/* Note: the name of val in cond can NOT be the same as var_name */
+#define DECLARE_KOBJ_ATTR_HEX_CHECK(attr_name, var_name, cond) \
+	DECLARE_KOBJ_ATTR_SHOW_HEX(attr_name, var_name) \
+	DECLARE_KOBJ_ATTR_STORE_HEX_CHECK(attr_name, var_name, cond) \
+	DECLARE_KOBJ_ATTR(attr_name)
+
+/* helper procedure version */
+#define DECLARE_KOBJ_ATTR_SHOW_HEX_PROC(attr_name, var_name, func) \
+	static ssize_t attr_name##_show( \
+		struct kobject *kobj, \
+		struct kobj_attribute *attr, \
+		char *buf) \
+	{ \
+		return func(kobj, attr, buf, var_name); \
+	}
+#define DECLARE_KOBJ_ATTR_STORE_HEX_PROC(attr_name, var_name, func) \
+	static ssize_t attr_name##_store( \
+		struct kobject *kobj, \
+		struct kobj_attribute *attr, \
+		const char *buf, \
+		size_t n) \
+	{ \
+		return func(kobj, attr, buf, n, &(var_name)); \
+	}
+#define DECLARE_KOBJ_ATTR_HEX_PROC(attr_name, var_name, show, store) \
+	DECLARE_KOBJ_ATTR_SHOW_HEX_PROC(attr_name, var_name, show) \
+	DECLARE_KOBJ_ATTR_STORE_HEX_PROC(attr_name, var_name, store) \
+	DECLARE_KOBJ_ATTR(attr_name)
+
+/*
+ * Declaring KOBJ attributes with string variable
+ */
+#define DECLARE_KOBJ_ATTR_SHOW_STR(attr_name, var_name) \
+	static ssize_t attr_name##_show( \
+		struct kobject *kobj, \
+		struct kobj_attribute *attr, \
+		char *buf) \
+	{ \
+		return snprintf(buf, PAGE_SIZE, "%s", var_name); \
+	}
+
+#define DECLARE_KOBJ_ATTR_RO_STR(attr_name, var_name) \
+	DECLARE_KOBJ_ATTR_SHOW_STR(attr_name, var_name) \
+	DECLARE_KOBJ_ATTR_RO(attr_name)
+
+/*
+ * Declaring KOBJ attributes with integer list variable
+ */
+#define DECLARE_KOBJ_ATTR_INT_LIST_ITEM(list_name, list) \
+	static struct list_name##_list_item_t { \
+		int	key; \
+		int	val; \
+	} const list_name##_list_item[] = { list };
+#define DECLARE_KOBJ_ATTR_SHOW_INT_LIST(attr_name, var_name, list_name) \
+	static ssize_t attr_name##_show( \
+		struct kobject *kobj, \
+		struct kobj_attribute *attr, \
+		char *buf) \
+	{ \
+		int	i; \
+		for (i = 0; i < ARRAY_SIZE(list_name##_list_item); i++) { \
+			if (var_name == list_name##_list_item[i].key) { \
+				return snprintf(buf, \
+						PAGE_SIZE, \
+						"%d\n", \
+						list_name##_list_item[i].val); \
+			} \
+		} \
+		return snprintf(buf, PAGE_SIZE, "%d\n", -1); \
+	}
+#define DECLARE_KOBJ_ATTR_STORE_INT_LIST(attr_name, var_name, list_name) \
+	static ssize_t attr_name##_store( \
+		struct kobject *kobj, \
+		struct kobj_attribute *attr, \
+		const char *buf, \
+		size_t n) \
+	{ \
+		int	value; \
+		int	i; \
+		if (kstrtoint(buf, 10, &value) != 0) \
+			return -EINVAL; \
+		for (i = 0; i < ARRAY_SIZE(list_name##_list_item); i++) { \
+			if (value == list_name##_list_item[i].val) { \
+				var_name = list_name##_list_item[i].key; \
+				return n; \
+			} \
+		} \
+		return -EINVAL; \
+	}
+#define DECLARE_KOBJ_ATTR_INT_LIST(attr_name, var_name, list_name) \
+	DECLARE_KOBJ_ATTR_SHOW_INT_LIST(attr_name, var_name, list_name) \
+	DECLARE_KOBJ_ATTR_STORE_INT_LIST(attr_name, var_name, list_name) \
+	DECLARE_KOBJ_ATTR(attr_name)
+
+/*
+ * Declaring KOBJ attributes with string list variable
+ */
+#define DECLARE_KOBJ_ATTR_STR_LIST_ITEM(list_name, list) \
+	static struct list_name##_list_item_t { \
+		int	key; \
+		char	*val; \
+	} const list_name##_list_item[] = { list };
+#define DECLARE_KOBJ_ATTR_SHOW_STR_LIST(attr_name, var_name, list_name) \
+	static ssize_t attr_name##_show( \
+		struct kobject *kobj, \
+		struct kobj_attribute *attr, \
+		char *buf) \
+	{ \
+		int	i; \
+		for (i = 0; i < ARRAY_SIZE(list_name##_list_item); i++) { \
+			if (var_name == list_name##_list_item[i].key) { \
+				return snprintf(buf, \
+						PAGE_SIZE, \
+						"%s\n", \
+						list_name##_list_item[i].val); \
+			} \
+		} \
+		return snprintf(buf, PAGE_SIZE, "%s\n", "ERR"); \
+	}
+#define DECLARE_KOBJ_ATTR_STORE_STR_LIST(attr_name, var_name, list_name) \
+	static ssize_t attr_name##_store( \
+		struct kobject *kobj, \
+		struct kobj_attribute *attr, \
+		const char *buf, \
+		size_t n) \
+	{ \
+		int	i; \
+		for (i = 0; i < ARRAY_SIZE(list_name##_list_item); i++) { \
+			if (strncasecmp(buf, \
+					list_name##_list_item[i].val, \
+					strlen(list_name##_list_item[i].val)) == 0) { \
+				var_name = list_name##_list_item[i].key; \
+				return n; \
+			} \
+		} \
+		return -EINVAL; \
+	}
+#define DECLARE_KOBJ_ATTR_STR_LIST(attr_name, var_name, list_name) \
+	DECLARE_KOBJ_ATTR_SHOW_STR_LIST(attr_name, var_name, list_name) \
+	DECLARE_KOBJ_ATTR_STORE_STR_LIST(attr_name, var_name, list_name) \
+	DECLARE_KOBJ_ATTR(attr_name)
+
+/*
+ *  MET Debug Message
+ */
+#define METINFO(format, ...)	pr_debug("[MET]%s: "format, __func__, ##__VA_ARGS__)
+#define METERROR(format, ...)	pr_debug("[MET][ERR]%s: "format, __func__, ##__VA_ARGS__)
+
+#endif	/* _MT_TYPEDEFS_H__ */
diff --git a/src/devtools/met-driver/met_drv/common/ondiemet.c b/src/devtools/met-driver/met_drv/common/ondiemet.c
new file mode 100644
index 0000000..5247fa7
--- /dev/null
+++ b/src/devtools/met-driver/met_drv/common/ondiemet.c
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "ondiemet.h"
+
+/* record enabled modules */
+unsigned int ondiemet_module[ONDIEMET_NUM];
+EXPORT_SYMBOL(ondiemet_module);
+
+void (*scp_start[ONDIEMET_NUM]) (void) = {
+sspm_start, NULL, NULL};
+
+void (*scp_stop[ONDIEMET_NUM]) (void) = {
+sspm_stop, NULL, NULL};
+
+void (*scp_extract[ONDIEMET_NUM]) (void) = {
+sspm_extract, NULL, NULL};
+
+/* record which MCU is started to generate data */
+int ondiemet_module_started[ONDIEMET_NUM];
+
+int ondiemet_attr_init(struct device *dev)
+{
+	int ret;
+
+	ret = sspm_attr_init(dev);
+	if (ret != 0) {
+		pr_debug("can not create device file: sspm related\n");
+		return ret;
+	}
+
+	return 0;
+
+}
+
+int ondiemet_attr_uninit(struct device *dev)
+{
+	int ret;
+
+	ret = sspm_attr_uninit(dev);
+	if (ret != 0) {
+		pr_debug("can not delete device file: sspm related\n");
+		return ret;
+	}
+
+	return 0;
+
+}
+
+void ondiemet_start(void)
+{
+	int i;
+
+	for (i = 0; i < ONDIEMET_NUM; i++) {
+		if (ondiemet_module[i] != 0) {
+			ondiemet_module_started[i] = 1;
+			(*scp_start[i]) ();
+		}
+	}
+}
+
+void ondiemet_stop(void)
+{
+	int i;
+
+	for (i = 0; i < ONDIEMET_NUM; i++) {
+		if (ondiemet_module[i] != 0) {
+			(*scp_stop[i]) ();
+			ondiemet_module_started[i] = 0;
+		}
+	}
+}
+
+void ondiemet_extract(void)
+{
+	int i;
+
+	for (i = 0; i < ONDIEMET_NUM; i++) {
+		if (ondiemet_module[i] != 0)
+			(*scp_extract[i]) ();
+	}
+}
diff --git a/src/devtools/met-driver/met_drv/common/ondiemet.h b/src/devtools/met-driver/met_drv/common/ondiemet.h
new file mode 100644
index 0000000..3fff604
--- /dev/null
+++ b/src/devtools/met-driver/met_drv/common/ondiemet.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __ONDIEMET_H
+#define __ONDIEMET_H
+
+#include "ondiemet_log.h"
+
+extern void ondiemet_extract(void);
+extern void ondiemet_stop(void);
+extern void ondiemet_start(void);
+
+#define ONDIEMET_SSPM  0
+#define ONDIEMET_NUM  3		/* total number of supported */
+extern unsigned int ondiemet_module[];
+extern void sspm_start(void);
+extern void sspm_stop(void);
+extern void sspm_extract(void);
+extern int sspm_attr_init(struct device *dev);
+extern int sspm_attr_uninit(struct device *dev);
+
+extern int ondiemet_attr_init(struct device *dev);
+extern int ondiemet_attr_uninit(struct device *dev);
+
+extern int sspm_buffer_size;
+
+#endif				/* __ONDIEMET_H */
diff --git a/src/devtools/met-driver/met_drv/common/ondiemet_log.c b/src/devtools/met-driver/met_drv/common/ondiemet_log.c
new file mode 100644
index 0000000..4f3ad69
--- /dev/null
+++ b/src/devtools/met-driver/met_drv/common/ondiemet_log.c
@@ -0,0 +1,516 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/debugfs.h>
+#include <linux/mutex.h>
+#include <linux/semaphore.h>
+#include <linux/freezer.h>
+#include <linux/uaccess.h>
+#include <linux/completion.h>
+
+#include "ondiemet_log.h"
+
+#define ONDIEMET_LOG_REQ 1
+/* TODO: abandon this constatnt */
+#define ONDIEMET_LOG_STOP 2
+
+#define PID_NONE (-1)
+
+#define ONDIEMET_LOG_STOP_MODE 0
+#define ONDIEMET_LOG_RUN_MODE 1
+#define ONDIEMET_LOG_DEBUG_MODE 2
+
+static int ondiemet_trace_run;
+static struct dentry *dbgfs_met_dir;
+
+struct mutex lock_tracef;
+struct ondiemet_log_req_q_t {
+	struct list_head listq;
+	struct mutex lockq;
+	/* struct semaphore new_evt_sema; */
+	struct completion new_evt_comp;
+	int closeq_flag;
+} ondiemet_log_req_q;
+
+struct ondiemet_log_req {
+	struct list_head list;
+	int cmd_type;
+	const char *src;
+	size_t num;
+
+	void (*on_fini_cb)(const void *p);
+	const void *param;
+};
+
+#define __ondiemet_log_req_init(req, cmd, s, n, pf, p)	\
+	do {						\
+		INIT_LIST_HEAD(&req->list);		\
+		req->cmd_type = cmd;			\
+		req->src = s;				\
+		req->num = n;				\
+		req->on_fini_cb = pf;			\
+		req->param = p;				\
+	} while (0)
+
+#define __ondiemet_log_req_fini(req)		        \
+	do {					        \
+		if (req->on_fini_cb)			\
+			req->on_fini_cb(req->param);	\
+		kfree(req);				\
+	} while (0)
+
+static void __ondiemet_log_req_q_init(struct ondiemet_log_req_q_t *q)
+{
+	INIT_LIST_HEAD(&q->listq);
+	mutex_init(&q->lockq);
+	/* sema_init(&q->new_evt_sema, 0); */
+	init_completion(&q->new_evt_comp);
+	q->closeq_flag = 1;
+}
+
+/* undequeue is seen as a roll-back operation, so it can be done even when the queue is closed */
+static void __ondiemet_log_req_undeq(struct ondiemet_log_req *req)
+{
+	mutex_lock(&ondiemet_log_req_q.lockq);
+	list_add(&req->list, &ondiemet_log_req_q.listq);
+	mutex_unlock(&ondiemet_log_req_q.lockq);
+
+	/* up(&ondiemet_log_req_q.new_evt_sema); */
+	complete(&ondiemet_log_req_q.new_evt_comp);
+}
+
+static int __ondiemet_log_req_enq(struct ondiemet_log_req *req)
+{
+	mutex_lock(&ondiemet_log_req_q.lockq);
+	if (ondiemet_log_req_q.closeq_flag) {
+		mutex_unlock(&ondiemet_log_req_q.lockq);
+		return -EBUSY;
+	}
+
+	list_add_tail(&req->list, &ondiemet_log_req_q.listq);
+	if (req->cmd_type == ONDIEMET_LOG_STOP)
+		ondiemet_log_req_q.closeq_flag = 1;
+	mutex_unlock(&ondiemet_log_req_q.lockq);
+
+	/* up(&ondiemet_log_req_q.new_evt_sema); */
+	complete(&ondiemet_log_req_q.new_evt_comp);
+
+	return 0;
+}
+
+int ondiemet_log_req_enq(const char *src, size_t num, void (*on_fini_cb)(const void *p), const void *param)
+{
+	struct ondiemet_log_req *req = kmalloc(sizeof(*req), GFP_KERNEL);
+
+	__ondiemet_log_req_init(req, ONDIEMET_LOG_REQ, src, num, on_fini_cb, param);
+	return __ondiemet_log_req_enq(req);
+}
+
+/*int down_freezable_interruptible(struct semaphore *sem) */
+int down_freezable_interruptible(struct completion *comp)
+{
+
+	int ret;
+
+	freezer_do_not_count();
+	/* ret = down_interruptible(sem); */
+	ret = wait_for_completion_interruptible(comp);
+	freezer_count();
+
+	return ret;
+}
+
+struct ondiemet_log_req *__ondiemet_log_req_deq(void)
+{
+	struct ondiemet_log_req *ret_req;
+
+	/*if (down_freezable_interruptible(&ondiemet_log_req_q.new_evt_sema))*/
+	if (down_freezable_interruptible(&ondiemet_log_req_q.new_evt_comp))
+		return NULL;
+
+	mutex_lock(&ondiemet_log_req_q.lockq);
+	ret_req = list_entry(ondiemet_log_req_q.listq.next, struct ondiemet_log_req, list);
+	list_del_init(&ret_req->list);
+	mutex_unlock(&ondiemet_log_req_q.lockq);
+
+	return ret_req;
+}
+
+void __ondiemet_log_req_open(void)
+{
+	mutex_lock(&ondiemet_log_req_q.lockq);
+	ondiemet_log_req_q.closeq_flag = 0;
+	mutex_unlock(&ondiemet_log_req_q.lockq);
+}
+
+int __ondiemet_log_req_closed(void)
+{
+	int ret;
+
+	mutex_lock(&ondiemet_log_req_q.lockq);
+	ret = ondiemet_log_req_q.closeq_flag && list_empty(&ondiemet_log_req_q.listq);
+	mutex_unlock(&ondiemet_log_req_q.lockq);
+
+	return ret;
+}
+
+int __ondiemet_log_req_working(void)
+{
+	int ret;
+
+	mutex_lock(&ondiemet_log_req_q.lockq);
+	ret = !ondiemet_log_req_q.closeq_flag;
+	mutex_unlock(&ondiemet_log_req_q.lockq);
+
+	return ret;
+}
+
+static void *__ondiemet_trace_seq_next(struct seq_file *seqf, loff_t *offset)
+{
+	struct ondiemet_log_req *next_req;
+
+	if (ondiemet_trace_run == ONDIEMET_LOG_DEBUG_MODE)
+		pr_debug("[met] __ondiemet_trace_seq_next: pid: %d\n", current->pid);
+
+	if (__ondiemet_log_req_closed())
+		return NULL;
+
+	next_req = __ondiemet_log_req_deq();
+
+	if (next_req == NULL)
+		return NULL;
+
+	if (next_req->cmd_type == ONDIEMET_LOG_STOP) {
+		__ondiemet_log_req_fini(next_req);
+		return NULL;
+	}
+
+	return (void *) next_req;
+}
+
+struct mutex lock_trace_owner_pid;
+pid_t trace_owner_pid = PID_NONE;
+static void *ondiemet_trace_seq_start(struct seq_file *seqf, loff_t *offset)
+{
+	void *ret;
+
+	if (ondiemet_trace_run == ONDIEMET_LOG_DEBUG_MODE) {
+		pr_debug("[met] ondiemet_trace_seq_start: locked_pid: %d, pid: %d, offset: %llu\n",
+			 trace_owner_pid, current->pid, *offset);
+	}
+
+	if (!mutex_trylock(&lock_tracef))
+		return NULL;
+
+	mutex_lock(&lock_trace_owner_pid);
+	trace_owner_pid = current->pid;
+	mutex_unlock(&lock_trace_owner_pid);
+
+	ret = __ondiemet_trace_seq_next(seqf, offset);
+
+	return ret;
+}
+
+static void *ondiemet_trace_seq_next(struct seq_file *seqf, void *p, loff_t *offset)
+{
+	if (ondiemet_trace_run == ONDIEMET_LOG_DEBUG_MODE)
+		pr_debug("[met] ondiemet_trace_seq_next: pid: %d\n", current->pid);
+
+	(*offset)++;
+	return __ondiemet_trace_seq_next(seqf, offset);
+}
+
+static int ondiemet_trace_seq_show(struct seq_file *seqf, void *p)
+{
+	struct ondiemet_log_req *req = (struct ondiemet_log_req *) p;
+	size_t l_sz;
+	size_t r_sz;
+	struct ondiemet_log_req *l_req;
+	struct ondiemet_log_req *r_req;
+	int ret;
+
+	if (ondiemet_trace_run == ONDIEMET_LOG_DEBUG_MODE)
+		pr_debug("[met] ondiemet_trace_seq_show: pid: %d\n", current->pid);
+
+	if (req->num >= seqf->size) {
+		l_req = kmalloc(sizeof(*req), GFP_KERNEL);
+		r_req = req;
+
+		l_sz = seqf->size >> 1;
+		r_sz = req->num - l_sz;
+		__ondiemet_log_req_init(l_req, ONDIEMET_LOG_REQ, req->src, l_sz, NULL, NULL);
+		__ondiemet_log_req_init(r_req, ONDIEMET_LOG_REQ, req->src + l_sz,
+					r_sz, req->on_fini_cb, req->param);
+
+		__ondiemet_log_req_undeq(r_req);
+		req = l_req;
+
+		if (ondiemet_trace_run == ONDIEMET_LOG_DEBUG_MODE)
+			pr_debug("[met] ondiemet_trace_seq_show: split request\n");
+	}
+
+	ret = seq_write(seqf, req->src, req->num);
+
+	if (ret) {
+		/* check if seq_file buffer overflows */
+		if (seqf->count == seqf->size) {
+			__ondiemet_log_req_undeq(req);
+		} else {
+			if (ondiemet_trace_run == ONDIEMET_LOG_DEBUG_MODE)
+				pr_debug("[met] ondiemet_trace_seq_show: reading trace record failed, some data may be lost or corrupted\n");
+			__ondiemet_log_req_fini(req);
+		}
+		return 0;
+	}
+
+	__ondiemet_log_req_fini(req);
+	return 0;
+}
+
+static void ondiemet_trace_seq_stop(struct seq_file *seqf, void *p)
+{
+	if (ondiemet_trace_run == ONDIEMET_LOG_DEBUG_MODE)
+		pr_debug("[met] ondiemet_trace_seq_stop: pid: %d\n", current->pid);
+
+	mutex_lock(&lock_trace_owner_pid);
+	if (current->pid == trace_owner_pid) {
+		trace_owner_pid = PID_NONE;
+		mutex_unlock(&lock_tracef);
+	}
+	mutex_unlock(&lock_trace_owner_pid);
+}
+
+static const struct seq_operations ondiemet_trace_seq_ops = {
+	.start = ondiemet_trace_seq_start,
+	.next = ondiemet_trace_seq_next,
+	.stop = ondiemet_trace_seq_stop,
+	.show = ondiemet_trace_seq_show
+};
+
+static int ondiemet_trace_open(struct inode *inode, struct file *fp)
+{
+	return seq_open(fp, &ondiemet_trace_seq_ops);
+}
+
+static const struct file_operations ondiemet_trace_fops = {
+	.owner = THIS_MODULE,
+	.open = ondiemet_trace_open,
+	.read = seq_read,
+	.llseek = seq_lseek,
+	.release = seq_release
+};
+
+/*struct semaphore log_start_sema;*/
+struct completion log_start_comp;
+int ondiemet_log_manager_start(void)
+{
+	int ret;
+
+	/* TODO: choose a better return value */
+	if (__ondiemet_log_req_working())
+		return -EINVAL;
+
+	if (!__ondiemet_log_req_closed()) {
+		/*ret = down_killable(&log_start_sema);*/
+		ret = wait_for_completion_killable(&log_start_comp);
+		if (ret)
+			return ret;
+	}
+
+	__ondiemet_log_req_open();
+
+	return 0;
+}
+
+/*struct semaphore log_stop_sema;*/
+struct completion log_stop_comp;
+static void __log_stop_cb(const void *p)
+{
+	/* up(&log_start_sema); */
+	/* up(&log_stop_sema); */
+	complete(&log_start_comp);
+	complete(&log_stop_comp);
+}
+
+int ondiemet_log_manager_stop(void)
+{
+	int ret;
+	struct ondiemet_log_req *req;
+
+	/* TODO: choose a better return value */
+	if (__ondiemet_log_req_closed())
+		return -EINVAL;
+
+	req = kmalloc(sizeof(*req), GFP_KERNEL);
+
+	__ondiemet_log_req_init(req, ONDIEMET_LOG_STOP, NULL, 0, __log_stop_cb, NULL);
+	/*sema_init(&log_start_sema, 0); */
+	/*sema_init(&log_stop_sema, 0); */
+	init_completion(&log_start_comp);
+	init_completion(&log_stop_comp);
+
+	ret = __ondiemet_log_req_enq(req);
+	if (ret)
+		return ret;
+
+	/* XXX: blocking may be break by SIGKILL */
+	/*return down_killable(&log_stop_sema);*/
+	return wait_for_completion_killable(&log_stop_comp);
+}
+
+int ondiemet_parse_num(const char *str, unsigned int *value, int len)
+{
+	int ret;
+
+	if (len <= 0)
+		return -1;
+
+	if ((len > 2) &&
+	    ((str[0] == '0') &&
+	     ((str[1] == 'x') || (str[1] == 'X')))) {
+		ret = kstrtouint(str, 16, value);
+	} else {
+		ret = kstrtouint(str, 10, value);
+	}
+
+	if (ret != 0)
+		return -1;
+
+	return 0;
+}
+
+/* XXX: seq_file will output only when a page is filled */
+static ssize_t ondiemet_log_write_store(struct device *dev,
+					struct device_attribute *attr,
+					const char *buf,
+					size_t count)
+{
+	char *plog = NULL;
+
+	plog = kmalloc_array(count, sizeof(*plog), GFP_KERNEL);
+	if (!plog) {
+		/* TODO: use a better error code */
+		return -EINVAL;
+	}
+
+	memcpy(plog, buf, count);
+
+	mutex_lock(&dev->mutex);
+	ondiemet_log_req_enq(plog, strnlen(plog, count), kfree, plog);
+	mutex_unlock(&dev->mutex);
+
+	return count;
+}
+
+static DEVICE_ATTR(ondiemet_log_write, 0664, NULL, ondiemet_log_write_store);
+
+static ssize_t ondiemet_log_run_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int sz;
+
+	mutex_lock(&dev->mutex);
+	sz = snprintf(buf, PAGE_SIZE, "%d\n", ondiemet_trace_run);
+	mutex_unlock(&dev->mutex);
+	return sz;
+}
+
+static ssize_t ondiemet_log_run_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+	int ret;
+	int prev_run_state;
+
+	mutex_lock(&dev->mutex);
+
+	prev_run_state = ondiemet_trace_run;
+
+	if (kstrtoint(buf, 10, &ondiemet_trace_run) != 0)
+		return -EINVAL;
+
+	if (ondiemet_trace_run <= ONDIEMET_LOG_STOP_MODE) {
+		ondiemet_trace_run = ONDIEMET_LOG_STOP_MODE;
+		ondiemet_log_manager_stop();
+
+		if (prev_run_state == ONDIEMET_LOG_DEBUG_MODE)
+			device_remove_file(dev, &dev_attr_ondiemet_log_write);
+	} else if (ondiemet_trace_run == ONDIEMET_LOG_RUN_MODE) {
+		ondiemet_trace_run = ONDIEMET_LOG_RUN_MODE;
+		ondiemet_log_manager_start();
+
+		if (prev_run_state == ONDIEMET_LOG_DEBUG_MODE)
+			device_remove_file(dev, &dev_attr_ondiemet_log_write);
+	} else {
+		ondiemet_trace_run = ONDIEMET_LOG_DEBUG_MODE;
+		ondiemet_log_manager_start();
+
+		if (prev_run_state != ONDIEMET_LOG_DEBUG_MODE) {
+			ret = device_create_file(dev, &dev_attr_ondiemet_log_write);
+			if (ret != 0)
+				pr_debug("[met] can not create device node: ondiemet_log_write\n");
+		}
+	}
+
+	mutex_unlock(&dev->mutex);
+
+	return count;
+}
+
+static DEVICE_ATTR(ondiemet_log_run, 0660, ondiemet_log_run_show, ondiemet_log_run_store);
+
+int ondiemet_log_manager_init(struct device *dev)
+{
+	int ret;
+	struct dentry *d;
+
+	mutex_init(&lock_tracef);
+
+	__ondiemet_log_req_q_init(&ondiemet_log_req_q);
+
+	/*sema_init(&log_start_sema, 0);*/
+	/*sema_init(&log_stop_sema, 0);*/
+	init_completion(&log_start_comp);
+	init_completion(&log_stop_comp);
+
+	dbgfs_met_dir = debugfs_create_dir("ondiemet", NULL);
+	if (!dbgfs_met_dir) {
+		pr_debug("[met] can not create debugfs directory: met\n");
+		return -ENOMEM;
+	}
+
+	mutex_init(&lock_trace_owner_pid);
+
+	d = debugfs_create_file("trace", 0644, dbgfs_met_dir, NULL, &ondiemet_trace_fops);
+	if (!d) {
+		pr_debug("[met] can not create devide node in debugfs: ondiemet_trace\n");
+		return -ENOMEM;
+	}
+
+	ondiemet_trace_run = __ondiemet_log_req_working();
+	ret = device_create_file(dev, &dev_attr_ondiemet_log_run);
+	if (ret != 0) {
+		pr_debug("[met] can not create device node: ondiemet_log_run\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+int ondiemet_log_manager_uninit(struct device *dev)
+{
+	device_remove_file(dev, &dev_attr_ondiemet_log_run);
+	debugfs_remove_recursive(dbgfs_met_dir);
+	return 0;
+}
diff --git a/src/devtools/met-driver/met_drv/common/ondiemet_log.h b/src/devtools/met-driver/met_drv/common/ondiemet_log.h
new file mode 100644
index 0000000..cfe8be9
--- /dev/null
+++ b/src/devtools/met-driver/met_drv/common/ondiemet_log.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _ONDIEMET_LOG_H_
+#define _ONDIEMET_LOG_H_
+
+#include <linux/device.h>
+
+int ondiemet_log_manager_init(struct device *dev);
+int ondiemet_log_manager_uninit(struct device *dev);
+int ondiemet_log_manager_start(void);
+/* Log manager can be reactivated by inserting new requests, i.e., calling ondiemet_log_req_enq() */
+int ondiemet_log_manager_stop(void);
+int ondiemet_log_req_enq(const char *src, size_t num, void (*on_fini_cb) (const void *p),
+			 const void *param);
+
+#endif				/* _ONDIEMET_LOG_H_ */
diff --git a/src/devtools/met-driver/met_drv/common/power.c b/src/devtools/met-driver/met_drv/common/power.c
new file mode 100644
index 0000000..c57d907
--- /dev/null
+++ b/src/devtools/met-driver/met_drv/common/power.c
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/cpufreq.h>
+#include <trace/events/power.h>
+
+#include "power.h"
+#include "met_drv.h"
+#include "met_kernel_symbol.h"
+
+noinline void cpu_frequency(unsigned int frequency, unsigned int cpu_id)
+{
+	/* suppose this symbol is available, otherwise, the met.ko will fail */
+	met_cpu_frequency_symbol(frequency, cpu_id);
+}
+
+void force_power_log(int cpu)
+{
+	struct cpufreq_policy *p;
+
+	if (cpu == POWER_LOG_ALL) {
+		for_each_possible_cpu(cpu) {
+			p = cpufreq_cpu_get(cpu);
+			if (p != NULL) {
+				cpu_frequency(p->cur, cpu);
+				cpufreq_cpu_put(p);
+			} else {
+				cpu_frequency(0, cpu);
+			}
+		}
+	} else {
+		p = cpufreq_cpu_get(cpu);
+		if (p != NULL) {
+			cpu_frequency(p->cur, cpu);
+			cpufreq_cpu_put(p);
+		} else {
+			cpu_frequency(0, cpu);
+		}
+	}
+}
+
+void force_power_log_val(unsigned int frequency, int cpu)
+{
+	cpu_frequency(frequency, cpu);
+}
diff --git a/src/devtools/met-driver/met_drv/common/power.h b/src/devtools/met-driver/met_drv/common/power.h
new file mode 100644
index 0000000..8a0e8f0
--- /dev/null
+++ b/src/devtools/met-driver/met_drv/common/power.h
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _POWER_H_
+#define _POWER_H_
+
+#define POWER_LOG_ALL	-1
+void force_power_log(int cpu);
+void force_power_log_val(unsigned int frequency, int cpu);
+
+#endif				/* _POWER_H_ */
diff --git a/src/devtools/met-driver/met_drv/common/sampler.c b/src/devtools/met-driver/met_drv/common/sampler.c
new file mode 100644
index 0000000..057b4bc
--- /dev/null
+++ b/src/devtools/met-driver/met_drv/common/sampler.c
@@ -0,0 +1,714 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/cpu.h>
+#include <linux/sched.h>
+#include <linux/notifier.h>
+#include <linux/module.h>
+#include <linux/irq.h>
+#if 0				/* fix me later, no such file on current tree */
+#include <mach/mt_cpuxgpt.h>
+#endif
+#include <asm/arch_timer.h>
+
+#define	MET_USER_EVENT_SUPPORT
+#include "interface.h"
+#include "sampler.h"
+#include "met_struct.h"
+#include "util.h"
+#include "switch.h"
+#include "trace.h"
+#include "met_drv.h"
+#include "met_tag.h" /* for tracing_mark_write */
+
+#include "cpu_pmu.h" /* for using kernel perf PMU driver */
+#include "cpu_pmu_v2.h" /* for using kernel perf PMU v2 driver */
+#include "met_kernel_symbol.h"
+
+#undef	DEBUG_CPU_NOTIFY
+/* #define DEBUG_CPU_NOTIFY */
+#if	defined(DEBUG_CPU_NOTIFY)
+#ifdef CONFIG_MET_MODULE
+#define	dbg_met_tag_oneshot	met_tag_oneshot_real
+#else
+#define	dbg_met_tag_oneshot	met_tag_oneshot
+#endif /* CONFIG_MET_MODULE */
+#else
+#define	dbg_met_tag_oneshot(class_id, name, value)	({ 0; })
+#endif
+
+static int start;
+static unsigned int online_cpu_map;
+static int curr_polling_cpu;
+static int cpu_related_cnt;
+
+static int pmu_profiling_version = 0;
+
+static DEFINE_PER_CPU(unsigned int, perf_cpuid);
+
+static int preferred_cpu_list[] = { 0, 4, 1, 2, 3, 5, 6, 7 };
+
+int get_pmu_profiling_version()
+{
+	return pmu_profiling_version;
+}
+
+static int calc_preferred_polling_cpu(unsigned int cpu_map)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(preferred_cpu_list); i++) {
+		if (cpu_map & (1 << preferred_cpu_list[i]))
+			return preferred_cpu_list[i];
+	}
+
+	return -1;
+}
+
+static void wq_sync_buffer(struct work_struct *work)
+{
+	int cpu;
+	struct delayed_work *dw = container_of(work, struct delayed_work, work);
+	struct met_cpu_struct *met_cpu_ptr = container_of(dw, struct met_cpu_struct, dwork);
+
+	cpu = smp_processor_id();
+	if (met_cpu_ptr->cpu != cpu) {
+		/* panic("ERROR"); */
+		return;
+	}
+
+	/* sync_samples(cpu); */
+	/* don't re-add the work if we're shutting down */
+	if (met_cpu_ptr->work_enabled)
+		schedule_delayed_work(dw, DEFAULT_TIMER_EXPIRE);
+}
+
+static enum hrtimer_restart met_hrtimer_notify(struct hrtimer *hrtimer)
+{
+	int cpu;
+	int *count;
+	unsigned long long stamp;
+	struct met_cpu_struct *met_cpu_ptr = container_of(hrtimer, struct met_cpu_struct, hrtimer);
+	struct metdevice *c;
+#if	defined(DEBUG_CPU_NOTIFY)
+	char msg[32];
+#endif
+
+	cpu = smp_processor_id();
+#if	defined(DEBUG_CPU_NOTIFY)
+	{
+		char msg[32];
+
+		snprintf(msg, sizeof(msg), "met_hrtimer notify_%d", cpu);
+		dbg_met_tag_oneshot(0, msg, 1);
+	}
+#endif
+
+	if (met_cpu_ptr->cpu != cpu) {
+		/* panic("ERROR2"); */
+		dbg_met_tag_oneshot(0, msg, -3);
+		return HRTIMER_NORESTART;
+	}
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->ondiemet_mode == 0) {
+			if ((c->mode == 0) || (c->timed_polling == NULL))
+				continue;
+		} else if (c->ondiemet_mode == 1) {
+			if ((c->mode == 0) || (c->ondiemet_timed_polling == NULL))
+				continue;
+		} else if (c->ondiemet_mode == 2) {
+			if ((c->mode == 0) || ((c->timed_polling == NULL)
+					       && (c->ondiemet_timed_polling == NULL)))
+				continue;
+		}
+
+		count = per_cpu_ptr(c->polling_count, cpu);
+		if ((*count) > 0) {
+			(*count)--;
+			continue;
+		}
+
+		*(count) = c->polling_count_reload;
+
+		stamp = cpu_clock(cpu);
+
+		if (c->cpu_related == 0) {
+			if (cpu == curr_polling_cpu) {
+				if (c->ondiemet_mode == 0) {
+					c->timed_polling(stamp, 0);
+				} else if (c->ondiemet_mode == 1) {
+					c->ondiemet_timed_polling(stamp, 0);
+				} else if (c->ondiemet_mode == 2) {
+					if (c->timed_polling)
+						c->timed_polling(stamp, 0);
+					if (c->ondiemet_timed_polling)
+						c->ondiemet_timed_polling(stamp, 0);
+				}
+			}
+		} else {
+			if (c->ondiemet_mode == 0) {
+				c->timed_polling(stamp, cpu);
+			} else if (c->ondiemet_mode == 1) {
+				c->ondiemet_timed_polling(stamp, cpu);
+			} else if (c->ondiemet_mode == 2) {
+				if (c->timed_polling)
+					c->timed_polling(stamp, 0);
+				if (c->ondiemet_timed_polling)
+					c->ondiemet_timed_polling(stamp, 0);
+			}
+		}
+	}
+
+	if (met_cpu_ptr->hrtimer_online_check) {
+		online_cpu_map |= (1 << cpu);
+		met_cpu_ptr->hrtimer_online_check = 0;
+		dbg_met_tag_oneshot(0, "met_online check done", cpu);
+		if (calc_preferred_polling_cpu(online_cpu_map) == cpu) {
+			curr_polling_cpu = cpu;
+			dbg_met_tag_oneshot(0, "met_curr polling cpu", cpu);
+		}
+	}
+
+	if (met_cpu_ptr->work_enabled) {
+		hrtimer_forward_now(hrtimer, ns_to_ktime(DEFAULT_HRTIMER_EXPIRE));
+		dbg_met_tag_oneshot(0, msg, 0);
+		return HRTIMER_RESTART;
+	}
+	dbg_met_tag_oneshot(0, msg, 0);
+	return HRTIMER_NORESTART;
+}
+
+static void __met_hrtimer_start(void *unused)
+{
+	struct met_cpu_struct *met_cpu_ptr = NULL;
+	struct hrtimer *hrtimer = NULL;
+	/* struct delayed_work *dw; */
+	struct metdevice *c;
+
+	met_cpu_ptr = this_cpu_ptr(&met_cpu);
+#if	defined(DEBUG_CPU_NOTIFY)
+	{
+		char msg[32];
+
+		snprintf(msg, sizeof(msg), "met_hrtimer status_%d", met_cpu_ptr->cpu);
+		dbg_met_tag_oneshot(0, msg, 1);
+	}
+#endif
+	/*
+	 * do not open HRtimer when EVENT timer enable
+	 */
+//	if (!(met_switch.mode & MT_SWITCH_EVENT_TIMER)) {
+		hrtimer = &met_cpu_ptr->hrtimer;
+		/* dw = &met_cpu_ptr->dwork; */
+
+		hrtimer_init(hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+		hrtimer->function = met_hrtimer_notify;
+//	}
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->ondiemet_mode == 0) {
+			if ((c->cpu_related) && (c->mode) && (c->start))
+				c->start();
+		} else if (c->ondiemet_mode == 1) {
+			if (((c->cpu_related)) && (c->mode) && (c->ondiemet_start))
+				c->ondiemet_start();
+		} else if (c->ondiemet_mode == 2) {
+			if ((c->cpu_related) && (c->mode) && (c->start))
+				c->start();
+			if (((c->cpu_related)) && (c->mode) && (c->ondiemet_start))
+				c->ondiemet_start();
+		}
+	}
+	/*
+	 * do not open HRtimer when EVENT timer enable
+	 */
+//	if (!(met_switch.mode & MT_SWITCH_EVENT_TIMER)) {
+		if (DEFAULT_HRTIMER_EXPIRE) {
+			met_cpu_ptr->work_enabled = 1;
+			/* schedule_delayed_work_on(smp_processor_id(), dw, DEFAULT_TIMER_EXPIRE); */
+			hrtimer_start(hrtimer, ns_to_ktime(DEFAULT_HRTIMER_EXPIRE),
+				      HRTIMER_MODE_REL_PINNED);
+		}
+//	}
+}
+
+static void __met_hrtimer_stop(void *unused)
+{
+	struct met_cpu_struct *met_cpu_ptr;
+	struct hrtimer *hrtimer;
+	/* struct delayed_work *dw; */
+	struct metdevice *c;
+
+	met_cpu_ptr = this_cpu_ptr(&met_cpu);
+#if	defined(DEBUG_CPU_NOTIFY)
+	{
+		char msg[32];
+
+		snprintf(msg, sizeof(msg), "met_hrtimer status_%d", met_cpu_ptr->cpu);
+		dbg_met_tag_oneshot(0, msg, 0);
+	}
+#endif
+	/*
+	 * do not open HRtimer when EVENT timer enable
+	 */
+//	if (!(met_switch.mode & MT_SWITCH_EVENT_TIMER)) {
+		hrtimer = &met_cpu_ptr->hrtimer;
+		/* dw = &met_cpu_ptr->dwork; */
+
+		met_cpu_ptr->work_enabled = 0;
+		hrtimer_cancel(hrtimer);
+		/* cancel_delayed_work_sync(dw); */
+//	}
+	list_for_each_entry(c, &met_list, list) {
+		if (c->ondiemet_mode == 0) {
+			if ((c->cpu_related) && (c->mode) && (c->stop))
+				c->stop();
+		} else if (c->ondiemet_mode == 1) {
+			if ((c->cpu_related) && (c->mode) && (c->ondiemet_stop))
+				c->ondiemet_stop();
+		} else if (c->ondiemet_mode == 2) {
+			if ((c->cpu_related) && (c->mode) && (c->stop))
+				c->stop();
+			if ((c->cpu_related) && (c->mode) && (c->ondiemet_stop))
+				c->ondiemet_stop();
+		}
+	}
+}
+
+static int met_pmu_cpu_notify(struct notifier_block *self, unsigned long action, void *hcpu)
+{
+	struct met_cpu_struct *met_cpu_ptr;
+	struct delayed_work *dw;
+	long cpu = (long)hcpu;
+	int preferred_polling_cpu;
+
+	if (start == 0)
+		return NOTIFY_OK;
+
+#if	defined(DEBUG_CPU_NOTIFY)
+	{
+		char msg[32];
+
+		snprintf(msg, sizeof(msg), "met_cpu notify_%ld", cpu);
+		dbg_met_tag_oneshot(0, msg, action);
+	}
+#elif	defined(PR_CPU_NOTIFY)
+	{
+		char msg[32];
+
+		if (met_cpu_notify) {
+			snprintf(msg, sizeof(msg), "met_cpu notify_%ld", cpu);
+			dbg_met_tag_oneshot(0, msg, action);
+		}
+	}
+#endif
+
+	if (cpu < 0 || cpu >= ARRAY_SIZE(preferred_cpu_list))
+		return NOTIFY_OK;
+
+	switch (action) {
+	case CPU_ONLINE:
+	case CPU_ONLINE_FROZEN:
+		met_cpu_ptr = &per_cpu(met_cpu, cpu);
+		met_cpu_ptr->hrtimer_online_check = 1;
+		dbg_met_tag_oneshot(0, "met_online check", cpu);
+
+		if (cpu_related_cnt == 0) {
+			/*printk("%s, %d: curr_polling_cpu is alive = %d\n",
+			 *		__func__, __LINE__, online_cpu_map & (1 << curr_polling_cpu));
+			 */
+
+			online_cpu_map |= (1 << cpu);
+
+			/* check curr_polling_cpu is alive, if it is down,
+			 * start current cpu hrtimer, and change it to be currr_pollling_cpu
+			 */
+			if ((online_cpu_map & (1 << curr_polling_cpu)) == 0) {
+				met_smp_call_function_single_symbol(cpu, __met_hrtimer_start, NULL, 1);
+				curr_polling_cpu = cpu;
+			}
+		} else
+			met_smp_call_function_single_symbol(cpu, __met_hrtimer_start, NULL, 1);
+
+		if (met_cpu_pmu_method != 0) {
+			if (pmu_profiling_version == 1)
+				met_perf_cpupmu_online(cpu);
+#ifdef MET_SUPPORT_CPUPMU_V2
+			else if (pmu_profiling_version == 2)
+				met_perf_cpupmu_online_v2(cpu);
+#endif
+		}
+
+#ifdef CONFIG_CPU_FREQ
+		force_power_log(cpu);
+#endif
+		break;
+
+	case CPU_DOWN_PREPARE:
+	case CPU_DOWN_PREPARE_FROZEN:
+		online_cpu_map &= ~(1 << cpu);
+		dbg_met_tag_oneshot(0, "met_offline cpu", cpu);
+		if (cpu == curr_polling_cpu) {
+			/* printk("%s, %d: curr_polling_cpu %d is down\n",
+			 *		__func__, __LINE__, curr_polling_cpu);
+			 */
+			preferred_polling_cpu = calc_preferred_polling_cpu(online_cpu_map);
+			/* printk("%s, %d: preferred_polling_cpu = %d\n",
+			 *		__func__, __LINE__, preferred_polling_cpu);
+			 */
+			if (preferred_polling_cpu != -1) {
+				curr_polling_cpu = preferred_polling_cpu;
+				dbg_met_tag_oneshot(0, "met_curr polling cpu", curr_polling_cpu);
+
+				if (cpu_related_cnt == 0)
+					/* printk("%s, %d: start cpu %d hrtimer start\n",
+					 *		__func__, __LINE__, curr_polling_cpu);
+					 */
+					met_smp_call_function_single_symbol(curr_polling_cpu, __met_hrtimer_start, NULL, 1);
+			}
+		}
+
+		met_smp_call_function_single_symbol(cpu, __met_hrtimer_stop, NULL, 1);
+		if (met_cpu_pmu_method != 0) {
+			if (pmu_profiling_version == 1) {
+				per_cpu(perf_cpuid, cpu) = cpu;
+				met_smp_call_function_single_symbol(cpu, met_perf_cpupmu_down, (void *)&per_cpu(perf_cpuid, cpu), 1);
+			}
+#ifdef MET_SUPPORT_CPUPMU_V2
+			else if (pmu_profiling_version == 2) {
+				per_cpu(perf_cpuid, cpu) = cpu;
+				met_smp_call_function_single_symbol(cpu, met_perf_cpupmu_down_v2, (void *)&per_cpu(perf_cpuid, cpu), 1);
+			}
+#endif
+		}
+
+		met_cpu_ptr = &per_cpu(met_cpu, cpu);
+		dw = &met_cpu_ptr->dwork;
+		cancel_delayed_work_sync(dw);
+
+		/* sync_samples(cpu); */
+		break;
+
+	case CPU_DOWN_FAILED:
+	case CPU_DOWN_FAILED_FROZEN:
+		met_cpu_ptr = &per_cpu(met_cpu, cpu);
+		met_cpu_ptr->hrtimer_online_check = 1;
+		dbg_met_tag_oneshot(0, "met_online check", cpu);
+
+		met_smp_call_function_single_symbol(cpu, __met_hrtimer_start, NULL, 1);
+		break;
+
+	case CPU_DEAD:
+	case CPU_DEAD_FROZEN:
+#ifdef CONFIG_CPU_FREQ
+		force_power_log_val(0, cpu);
+#endif
+		break;
+	}
+	return NOTIFY_OK;
+}
+
+static struct notifier_block __refdata met_pmu_cpu_notifier = {
+	.notifier_call = met_pmu_cpu_notify,
+};
+
+int sampler_start(void)
+{
+	int ret, cpu;
+	struct met_cpu_struct *met_cpu_ptr;
+	struct metdevice *c;
+	int preferred_polling_cpu;
+
+	met_set_suspend_notify(0);
+
+#ifdef	CONFIG_CPU_FREQ
+	force_power_log(POWER_LOG_ALL);
+#endif
+
+	for_each_possible_cpu(cpu) {
+		met_cpu_ptr = &per_cpu(met_cpu, cpu);
+		met_cpu_ptr->work_enabled = 0;
+		met_cpu_ptr->hrtimer_online_check = 0;
+		hrtimer_init(&met_cpu_ptr->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+		met_cpu_ptr->hrtimer.function = met_hrtimer_notify;
+		INIT_DELAYED_WORK(&met_cpu_ptr->dwork, wq_sync_buffer);
+	}
+
+	start = 0;
+	ret = register_hotcpu_notifier(&met_pmu_cpu_notifier);
+
+	list_for_each_entry(c, &met_list, list) {
+
+		if (try_module_get(c->owner) == 0)
+			continue;
+#ifdef CONFIG_MET_ARM_32BIT
+		if (strcmp(c->name, "cpu") == 0) {
+			if ((c->mode) && (c->start)) {
+				pmu_profiling_version = 1;
+				cpu_related_cnt = 1;
+				if (met_cpu_pmu_method != 0)
+					met_perf_cpupmu_start();
+				else
+					c->start();
+			}
+			continue;
+		}
+#endif
+
+#ifdef MET_SUPPORT_CPUPMU_V2
+		if (strcmp(c->name, "cpu-pmu") == 0) {
+			if ((c->mode) && (c->start)) {
+				pmu_profiling_version = 2;
+				cpu_related_cnt = 1;
+				if (met_cpu_pmu_method != 0)
+					met_perf_cpupmu_start_v2();
+				else
+					c->start();
+			}
+			continue;
+		} else if (strcmp(c->name, "cpu") == 0) {
+			if ((c->mode) && (c->start)) {
+				pmu_profiling_version = 1;
+				cpu_related_cnt = 1;
+				if (met_cpu_pmu_method != 0)
+					met_perf_cpupmu_start();
+				else
+					c->start();
+			}
+			continue;
+		}
+#endif
+		if ((c->mode) && (c->cpu_related == 1))
+			cpu_related_cnt = 1;
+
+		if (c->ondiemet_mode == 0) {
+			if ((!(c->cpu_related)) && (c->mode) && (c->start))
+				c->start();
+		} else if (c->ondiemet_mode == 1) {
+			if ((!(c->cpu_related)) && (c->mode) && (c->ondiemet_start))
+				c->ondiemet_start();
+		} else if (c->ondiemet_mode == 2) {
+			if ((!(c->cpu_related)) && (c->mode) && (c->start))
+				c->start();
+			if ((!(c->cpu_related)) && (c->mode) && (c->ondiemet_start))
+				c->ondiemet_start();
+		}
+	}
+
+	get_online_cpus();
+	online_cpu_map = 0;
+	for_each_online_cpu(cpu) {
+		online_cpu_map |= (1 << cpu);
+	}
+	dbg_met_tag_oneshot(0, "met_online cpu map", online_cpu_map);
+	preferred_polling_cpu = calc_preferred_polling_cpu(online_cpu_map);
+	if (preferred_polling_cpu != -1)
+		curr_polling_cpu = preferred_polling_cpu;
+	dbg_met_tag_oneshot(0, "met_curr polling cpu", curr_polling_cpu);
+	start = 1;
+
+	if (cpu_related_cnt == 0)
+		met_smp_call_function_single_symbol(curr_polling_cpu, __met_hrtimer_start, NULL, 1);
+	else
+		on_each_cpu(__met_hrtimer_start, NULL, 1);
+	put_online_cpus();
+
+	return ret;
+}
+
+void sampler_stop(void)
+{
+	int cpu;
+	struct met_cpu_struct *met_cpu_ptr;
+	struct metdevice *c;
+	struct delayed_work *dw;
+
+	get_online_cpus();
+
+	on_each_cpu(__met_hrtimer_stop, NULL, 1);
+/* for_each_online_cpu(cpu) { */
+	for_each_possible_cpu(cpu) {	/* Just for case */
+		met_cpu_ptr = &per_cpu(met_cpu, cpu);
+		dw = &met_cpu_ptr->dwork;
+		cancel_delayed_work_sync(dw);
+		/* sync_samples(cpu); */
+	}
+
+	start = 0;
+	put_online_cpus();
+
+	unregister_hotcpu_notifier(&met_pmu_cpu_notifier);
+
+	list_for_each_entry(c, &met_list, list) {
+#ifdef CONFIG_MET_ARM_32BIT
+		if (strcmp(c->name, "cpu") == 0) {
+			pmu_profiling_version = 0;
+			if ((c->mode) && (c->stop)) {
+				if (met_cpu_pmu_method != 0)
+					met_perf_cpupmu_stop();
+				else
+					c->stop();
+			}
+			module_put(c->owner);
+			continue;
+		}
+#endif
+
+#ifdef MET_SUPPORT_CPUPMU_V2
+		if (strcmp(c->name, "cpu-pmu") == 0) {
+			pmu_profiling_version = 0;
+			if ((c->mode) && (c->stop)) {
+				if (met_cpu_pmu_method != 0)
+					met_perf_cpupmu_stop_v2();
+				else
+					c->stop();
+			}
+			module_put(c->owner);
+			continue;
+		}
+		else if (strcmp(c->name, "cpu") == 0) {
+			pmu_profiling_version = 0;
+			if ((c->mode) && (c->stop)) {
+				if (met_cpu_pmu_method != 0)
+					met_perf_cpupmu_stop();
+				else
+					c->stop();
+			}
+			module_put(c->owner);
+			continue;
+		}
+#endif
+		if (c->ondiemet_mode == 0) {
+			if ((!(c->cpu_related)) && (c->mode) && (c->stop))
+				c->stop();
+		} else if (c->ondiemet_mode == 1) {
+			if ((!(c->cpu_related)) && (c->mode) && (c->ondiemet_stop))
+				c->ondiemet_stop();
+		} else if (c->ondiemet_mode == 2) {
+			if ((!(c->cpu_related)) && (c->mode) && (c->stop))
+				c->stop();
+			if ((!(c->cpu_related)) && (c->mode) && (c->ondiemet_stop))
+				c->ondiemet_stop();
+		}
+		module_put(c->owner);
+	}
+
+	cpu_related_cnt = 0;
+}
+
+#if 0 /* cann't use static now */
+enum {
+	MET_SUSPEND = 1,
+	MET_RESUME = 2,
+};
+
+static noinline void tracing_mark_write(int op)
+{
+	switch (op) {
+	case MET_SUSPEND:
+		MET_TRACE("C|0|MET_SUSPEND|1");
+		break;
+	case MET_RESUME:
+		MET_TRACE("C|0|MET_SUSPEND|0");
+		break;
+	}
+}
+#endif
+
+int met_hrtimer_suspend(void)
+{
+	struct metdevice *c;
+
+	met_set_suspend_notify(1);
+	/* tracing_mark_write(MET_SUSPEND); */
+//	tracing_mark_write(TYPE_MET_SUSPEND, 0, 0, 0, 0, 0);
+	if (start == 0)
+		return 0;
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->suspend)
+			c->suspend();
+	}
+
+	/* get current COUNT */
+	MET_TRACE("TS: %llu GPT: %llX", sched_clock(), arch_counter_get_cntvct());
+	return 0;
+}
+
+void met_hrtimer_resume(void)
+{
+	struct metdevice *c;
+
+	/* get current COUNT */
+	MET_TRACE("TS: %llu GPT: %llX", sched_clock(), arch_counter_get_cntvct());
+
+	/* tracing_mark_write(MET_RESUME); */
+//	tracing_mark_write(TYPE_MET_RESUME, 0, 0, 0, 0, 0);
+	if (start == 0)
+		return;
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->resume)
+			c->resume();
+	}
+}
+
+/*
+ * event timer:
+ * register IRQ, sched_switch event to monitor Polling count
+ * count can be printed at any live cpu.
+ */
+void met_event_timer_notify(void)
+{
+	unsigned long long stamp;
+	struct metdevice *c;
+	int cpu = -1;
+
+	if (start == 0)
+		return;
+
+	cpu = smp_processor_id();
+	list_for_each_entry(c, &met_list, list) {
+		stamp = local_clock();
+
+		if (c->prev_stamp == 0)
+			c->prev_stamp = stamp;
+
+		/* Critical Section Start */
+		/* try spinlock to prevent a event print twice between config time interval */
+		if (!spin_trylock(&(c->my_lock)))
+			continue;
+
+		/*
+		 * DEFAULT_HRTIMER_EXPIRE (met_hrtimer_expire):
+		 * sample_rate == 0 --> always print
+		 * sample_rate == 1000 --> print interval larger than 1 ms
+		 */
+		if (DEFAULT_HRTIMER_EXPIRE == 0 || (stamp - c->prev_stamp) < DEFAULT_HRTIMER_EXPIRE) {
+			spin_unlock(&(c->my_lock));
+			continue;
+		}
+
+		c->prev_stamp = stamp;
+		spin_unlock(&(c->my_lock));
+		/* Critical Section End */
+
+		if ((c->mode == 0) || (c->timed_polling == NULL))
+			continue;
+
+		stamp = local_clock();
+		c->timed_polling(stamp, cpu);
+	}
+}
+
diff --git a/src/devtools/met-driver/met_drv/common/sampler.h b/src/devtools/met-driver/met_drv/common/sampler.h
new file mode 100644
index 0000000..ae780c0
--- /dev/null
+++ b/src/devtools/met-driver/met_drv/common/sampler.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _SAMPLER_H_
+#define _SAMPLER_H_
+
+/*
+ * sampling rate: 1ms
+ * log generating rate: 10ms
+ */
+#if 0
+#define DEFAULT_TIMER_EXPIRE (HZ / 100)
+#define DEFAULT_HRTIMER_EXPIRE (TICK_NSEC / 10)
+#else
+extern int met_timer_expire;	/* in jiffies */
+extern int met_hrtimer_expire;	/* in us */
+#define DEFAULT_TIMER_EXPIRE (met_timer_expire)
+#define DEFAULT_HRTIMER_EXPIRE (met_hrtimer_expire)
+#endif
+/*
+ * sampling rate: 10ms
+ * log generating rate: 100ms
+ */
+/* #define DEFAULT_TIMER_EXPIRE (HZ / 10) */
+/* #define DEFAULT_HRTIMER_EXPIRE (TICK_NSEC / 1) */
+
+int met_hrtimer_start(void);
+void met_hrtimer_stop(void);
+int sampler_start(void);
+void sampler_stop(void);
+
+extern struct list_head met_list;
+extern void add_cookie(struct pt_regs *regs, int cpu);
+extern int met_hrtimer_suspend(void);
+extern void met_hrtimer_resume(void);
+extern void met_event_timer_notify(void);
+
+extern int get_pmu_profiling_version(void);
+
+#ifdef CONFIG_CPU_FREQ
+#include "power.h"
+#endif
+
+#endif				/* _SAMPLER_H_ */
diff --git a/src/devtools/met-driver/met_drv/common/sspm/ondiemet_sspm.c b/src/devtools/met-driver/met_drv/common/sspm/ondiemet_sspm.c
new file mode 100644
index 0000000..08db3fa
--- /dev/null
+++ b/src/devtools/met-driver/met_drv/common/sspm/ondiemet_sspm.c
@@ -0,0 +1,493 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/delay.h>
+#include <linux/module.h> /* symbol_get */
+
+#include "ondiemet_sspm.h"
+#define MET_USER_EVENT_SUPPORT
+#include "met_drv.h"
+
+#ifdef CONFIG_MTK_TINYSYS_SSPM_SUPPORT
+#ifdef CONFIG_MTK_GMO_RAM_OPTIMIZE
+#ifdef CONFIG_MET_ARM_32BIT
+#include <asm/dma-mapping.h> /* arm_coherent_dma_ops */
+#else /* CONFIG_MET_ARM_32BIT */
+#include <linux/dma-mapping.h>
+#endif /* CONFIG_MET_ARM_32BIT */
+#else /* CONFIG_MTK_GMO_RAM_OPTIMIZE */
+#include "sspm_reservedmem.h"
+#include "sspm_reservedmem_define.h"
+#endif /* CONFIG_MTK_GMO_RAM_OPTIMIZE */
+
+dma_addr_t ondiemet_sspm_log_phy_addr;
+void *ondiemet_sspm_log_virt_addr;
+uint32_t ondiemet_sspm_log_size = 0x400000;
+
+/* SSPM_LOG_FILE 0 */
+/* SSPM_LOG_SRAM 1 */
+/* SSPM_LOG_DRAM 2 */
+int sspm_log_mode;
+/* SSPM_RUN_NORMAL mode 0 */
+/* SSPM_RUN_CONTINUOUS mode 1 */
+int sspm_run_mode;
+int met_sspm_log_discard = -1;
+int sspm_log_size = 100;
+
+int sspm_buffer_size;
+int sspm_buf_available;
+EXPORT_SYMBOL(sspm_buf_available);
+int sspm_buf_mapped = -1; /* get buffer by MET itself */
+
+static ssize_t sspm_buffer_size_show(struct device *dev, struct device_attribute *attr, char *buf);
+static DEVICE_ATTR(sspm_buffer_size, 0444, sspm_buffer_size_show, NULL);
+
+static ssize_t sspm_available_show(struct device *dev, struct device_attribute *attr, char *buf);
+static DEVICE_ATTR(sspm_available, 0444, sspm_available_show, NULL);
+
+static ssize_t sspm_log_discard_show(struct device *dev, struct device_attribute *attr, char *buf);
+static DEVICE_ATTR(sspm_log_discard, 0444, sspm_log_discard_show, NULL);
+
+static ssize_t sspm_log_mode_show(struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t sspm_log_mode_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
+static DEVICE_ATTR(sspm_log_mode, 0664, sspm_log_mode_show, sspm_log_mode_store);
+
+static ssize_t sspm_log_size_show(struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t sspm_log_size_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
+static DEVICE_ATTR(sspm_log_size, 0664, sspm_log_size_show, sspm_log_size_store);
+
+
+static ssize_t sspm_run_mode_show(struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t sspm_run_mode_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
+static DEVICE_ATTR(sspm_run_mode, 0664, sspm_run_mode_show, sspm_run_mode_store);
+
+static ssize_t sspm_modules_show(struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t sspm_modules_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
+static DEVICE_ATTR(sspm_modules, 0664, sspm_modules_show, sspm_modules_store);
+
+static ssize_t sspm_op_ctrl_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
+static DEVICE_ATTR(sspm_op_ctrl, 0220, NULL, sspm_op_ctrl_store);
+
+static ssize_t sspm_buffer_size_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int i;
+
+	mutex_lock(&dev->mutex);
+	i = snprintf(buf, PAGE_SIZE, "%d\n", sspm_buffer_size);
+	mutex_unlock(&dev->mutex);
+	return i;
+}
+
+static ssize_t sspm_available_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int i;
+
+	mutex_lock(&dev->mutex);
+	i = snprintf(buf, PAGE_SIZE, "%d\n", 1);
+	mutex_unlock(&dev->mutex);
+	return i;
+}
+
+static ssize_t sspm_log_discard_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int i;
+
+	mutex_lock(&dev->mutex);
+	i = snprintf(buf, PAGE_SIZE, "%d\n", met_sspm_log_discard);
+	mutex_unlock(&dev->mutex);
+	return i;
+}
+
+static ssize_t sspm_log_mode_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int i;
+
+	mutex_lock(&dev->mutex);
+	i = snprintf(buf, PAGE_SIZE, "%d\n", sspm_log_mode);
+	mutex_unlock(&dev->mutex);
+	return i;
+}
+
+static ssize_t sspm_log_mode_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+	int value;
+
+	if (kstrtoint(buf, 0, &value) != 0)
+		return -EINVAL;
+	mutex_lock(&dev->mutex);
+	sspm_log_mode = value;
+	mutex_unlock(&dev->mutex);
+	return count;
+}
+
+
+static ssize_t sspm_log_size_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int i;
+
+	mutex_lock(&dev->mutex);
+	i = snprintf(buf, PAGE_SIZE, "%d\n", sspm_log_size);
+	mutex_unlock(&dev->mutex);
+	return i;
+}
+
+static ssize_t sspm_log_size_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+	int value;
+
+	if (kstrtoint(buf, 0, &value) != 0)
+		return -EINVAL;
+	mutex_lock(&dev->mutex);
+	sspm_log_size = value;
+	mutex_unlock(&dev->mutex);
+	return count;
+}
+
+
+static ssize_t sspm_run_mode_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int i;
+
+	mutex_lock(&dev->mutex);
+	i = snprintf(buf, PAGE_SIZE, "%d\n", sspm_run_mode);
+	mutex_unlock(&dev->mutex);
+	return i;
+}
+
+static ssize_t sspm_run_mode_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+	int value;
+
+	if (kstrtoint(buf, 0, &value) != 0)
+		return -EINVAL;
+	mutex_lock(&dev->mutex);
+	sspm_run_mode = value;
+	mutex_unlock(&dev->mutex);
+	return count;
+}
+
+static ssize_t sspm_op_ctrl_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+	int value;
+
+	if (kstrtoint(buf, 0, &value) != 0)
+		return -EINVAL;
+	mutex_lock(&dev->mutex);
+	if (value == 1)
+		sspm_start();
+	else if (value == 2)
+		sspm_stop();
+	else if (value == 3)
+		sspm_extract();
+	else if (value == 4)
+		sspm_flush();
+	mutex_unlock(&dev->mutex);
+	return count;
+}
+
+static ssize_t sspm_modules_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int i;
+
+	mutex_lock(&dev->mutex);
+	i = snprintf(buf, PAGE_SIZE, "%x\n", ondiemet_module[ONDIEMET_SSPM]);
+	mutex_unlock(&dev->mutex);
+	return i;
+}
+
+static ssize_t sspm_modules_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+	uint32_t value;
+
+	if (kstrtouint(buf, 0, &value) != 0)
+		return -EINVAL;
+	mutex_lock(&dev->mutex);
+	ondiemet_module[ONDIEMET_SSPM] = value;
+	mutex_unlock(&dev->mutex);
+	return count;
+}
+
+int sspm_attr_init(struct device *dev)
+{
+	int ret;
+
+#ifdef CONFIG_MTK_GMO_RAM_OPTIMIZE
+#ifdef CONFIG_MET_ARM_32BIT
+	struct dma_map_ops *ops = (struct dma_map_ops *)symbol_get(arm_coherent_dma_ops);
+
+	if (ops && ops->alloc) {
+		dev->coherent_dma_mask = DMA_BIT_MASK(32);
+		ondiemet_sspm_log_virt_addr = ops->alloc(dev,
+						ondiemet_sspm_log_size,
+						&ondiemet_sspm_log_phy_addr,
+						GFP_KERNEL,
+						0);
+	}
+#else /* CONFIG_MET_ARM_32BIT */
+	/* dma_alloc */
+	ondiemet_sspm_log_virt_addr = dma_alloc_coherent(dev,
+			ondiemet_sspm_log_size,
+			&ondiemet_sspm_log_phy_addr,
+			GFP_KERNEL);
+#endif /* CONFIG_MET_ARM_32BIT */
+#else /* CONFIG_MTK_GMO_RAM_OPTIMIZE */
+
+	phys_addr_t (*sspm_reserve_mem_get_phys_sym)(unsigned int id) = NULL;
+	phys_addr_t (*sspm_reserve_mem_get_virt_sym)(unsigned int id) = NULL;
+	phys_addr_t (*sspm_reserve_mem_get_size_sym)(unsigned int id) = NULL;
+
+	sspm_reserve_mem_get_phys_sym = (phys_addr_t (*)(unsigned int id))symbol_get(sspm_reserve_mem_get_virt);
+	sspm_reserve_mem_get_virt_sym = (phys_addr_t (*)(unsigned int id))symbol_get(sspm_reserve_mem_get_phys);
+	sspm_reserve_mem_get_size_sym = (phys_addr_t (*)(unsigned int id))symbol_get(sspm_reserve_mem_get_size);
+	if (sspm_reserve_mem_get_phys_sym)
+		ondiemet_sspm_log_virt_addr = (void*)sspm_reserve_mem_get_virt(MET_MEM_ID);
+	if (sspm_reserve_mem_get_virt_sym)
+		ondiemet_sspm_log_phy_addr = sspm_reserve_mem_get_phys(MET_MEM_ID);
+	if (sspm_reserve_mem_get_size_sym)
+		ondiemet_sspm_log_size = sspm_reserve_mem_get_size(MET_MEM_ID);
+#endif /* CONFIG_MTK_GMO_RAM_OPTIMIZE */
+
+	ret = device_create_file(dev, &dev_attr_sspm_buffer_size);
+	if (ret != 0) {
+		pr_debug("can not create device file: sspm_buffer_size\n");
+		return ret;
+	}
+
+	ret = device_create_file(dev, &dev_attr_sspm_available);
+	if (ret != 0) {
+		pr_debug("can not create device file: sspm_available\n");
+		return ret;
+	}
+
+	ret = device_create_file(dev, &dev_attr_sspm_log_discard);
+	if (ret != 0) {
+		pr_debug("can not create device file: sspm_log_discard\n");
+		return ret;
+	}
+	ret = device_create_file(dev, &dev_attr_sspm_log_mode);
+	if (ret != 0) {
+		pr_debug("can not create device file: sspm_log_mode\n");
+		return ret;
+	}
+	ret = device_create_file(dev, &dev_attr_sspm_log_size);
+	if (ret != 0) {
+		pr_debug("can not create device file: sspm_log_size\n");
+		return ret;
+	}
+	ret = device_create_file(dev, &dev_attr_sspm_run_mode);
+	if (ret != 0) {
+		pr_debug("can not create device file: sspm_run_mode\n");
+		return ret;
+	}
+	ret = device_create_file(dev, &dev_attr_sspm_op_ctrl);
+	if (ret != 0) {
+		pr_debug("can not create device file: sspm_op_ctrl\n");
+		return ret;
+	}
+	ret = device_create_file(dev, &dev_attr_sspm_modules);
+	if (ret != 0) {
+		pr_debug("can not create device file: sspm_modules\n");
+		return ret;
+	}
+
+	if (ondiemet_sspm_log_virt_addr != NULL) {
+		start_sspm_ipi_recv_thread();
+		sspm_buf_available = 1;
+		sspm_buffer_size = ondiemet_sspm_log_size;
+	} else {
+		sspm_buf_available = 0;
+		sspm_buffer_size = -1;
+	}
+
+	return 0;
+}
+
+int sspm_attr_uninit(struct device *dev)
+{
+	/* dma_free */
+	if (ondiemet_sspm_log_virt_addr != NULL) {
+#ifdef CONFIG_MTK_GMO_RAM_OPTIMIZE
+#ifdef CONFIG_MET_ARM_32BIT
+		struct dma_map_ops *ops = (struct dma_map_ops *)symbol_get(arm_coherent_dma_ops);
+
+		if (ops && ops->free) {
+			ops->free(dev,
+				ondiemet_sspm_log_size,
+				ondiemet_sspm_log_virt_addr,
+				ondiemet_sspm_log_phy_addr,
+				0);
+		}
+#else /* CONFIG_MET_ARM_32BIT */
+		dma_free_coherent(dev,
+			ondiemet_sspm_log_size,
+			ondiemet_sspm_log_virt_addr,
+			ondiemet_sspm_log_phy_addr);
+#endif /* CONFIG_MET_ARM_32BIT */
+#endif /* CONFIG_MTK_GMO_RAM_OPTIMIZE */
+		ondiemet_sspm_log_virt_addr = NULL;
+		stop_sspm_ipi_recv_thread();
+	}
+
+	device_remove_file(dev, &dev_attr_sspm_buffer_size);
+	device_remove_file(dev, &dev_attr_sspm_available);
+	device_remove_file(dev, &dev_attr_sspm_log_discard);
+	device_remove_file(dev, &dev_attr_sspm_log_mode);
+	device_remove_file(dev, &dev_attr_sspm_log_size);
+	device_remove_file(dev, &dev_attr_sspm_run_mode);
+	device_remove_file(dev, &dev_attr_sspm_op_ctrl);
+	device_remove_file(dev, &dev_attr_sspm_modules);
+
+	return 0;
+}
+
+#if 0 /* move to sspm_attr_init() */
+void sspm_get_buffer_info(void)
+{
+	if (ondiemet_sspm_log_virt_addr != NULL) {
+		sspm_buf_available = 1;
+		sspm_buffer_size = ondiemet_sspm_log_size;
+	} else {
+		sspm_buf_available = 0;
+		sspm_buffer_size = -1;
+	}
+}
+#endif
+
+extern const char *met_get_platform_name(void);
+void sspm_start(void)
+{
+	int32_t ret = 0;
+	uint32_t rdata;
+	uint32_t ipi_buf[4];
+	const char* platform_name = NULL;
+	unsigned int platform_id = 0;
+	met_sspm_log_discard = -1;
+
+	/* clear DRAM buffer */
+	if (ondiemet_sspm_log_virt_addr != NULL)
+		memset_io((void *)ondiemet_sspm_log_virt_addr, 0, ondiemet_sspm_log_size);
+	else
+		return;
+
+	platform_name = met_get_platform_name();
+	if (platform_name) {
+		char buf[5];
+
+		memset(buf, 0x0, 5);
+		memcpy(buf, &platform_name[2], 4);
+		ret = kstrtouint(buf, 10, &platform_id);
+	}
+
+	/* send DRAM physical address */
+	ipi_buf[0] = MET_MAIN_ID | MET_BUFFER_INFO;
+	ipi_buf[1] = (unsigned int)ondiemet_sspm_log_phy_addr; /* address */
+	if (ret == 0)
+		ipi_buf[2] = platform_id;
+	else
+		ipi_buf[2] = 0;
+	ipi_buf[3] = 0;
+	ret = sspm_ipi_send_sync(IPI_ID_MET, IPI_OPT_WAIT, (void *)ipi_buf, 0, &rdata, 1);
+
+	/* start ondiemet now */
+	ipi_buf[0] = MET_MAIN_ID | MET_OP | MET_OP_START;
+	ipi_buf[1] = ondiemet_module[ONDIEMET_SSPM];
+	ipi_buf[2] = sspm_log_mode;
+	ipi_buf[3] = sspm_run_mode;
+	ret = sspm_ipi_send_sync(IPI_ID_MET, IPI_OPT_WAIT, (void *)ipi_buf, 0, &rdata, 1);
+}
+
+void sspm_stop(void)
+{
+	int32_t ret;
+	uint32_t rdata;
+	uint32_t ipi_buf[4];
+
+	if (sspm_buf_available == 1) {
+		ipi_buf[0] = MET_MAIN_ID|MET_OP|MET_OP_STOP;
+		ipi_buf[1] = 0;
+		ipi_buf[2] = 0;
+		ipi_buf[3] = 0;
+		ret = sspm_ipi_send_sync(IPI_ID_MET, IPI_OPT_WAIT, (void *)ipi_buf, 0, &rdata, 1);
+	}
+}
+
+void sspm_extract(void)
+{
+	int32_t ret;
+	uint32_t rdata;
+	uint32_t ipi_buf[4];
+	int32_t count;
+
+	count = 20;
+	if (sspm_buf_available == 1) {
+		while ((sspm_buffer_dumping == 1) && (count != 0)) {
+			msleep(50);
+			count--;
+		}
+		ipi_buf[0] = MET_MAIN_ID|MET_OP|MET_OP_EXTRACT;
+		ipi_buf[1] = 0;
+		ipi_buf[2] = 0;
+		ipi_buf[3] = 0;
+		ret = sspm_ipi_send_sync(IPI_ID_MET, IPI_OPT_WAIT, (void *)ipi_buf, 0, &rdata, 1);
+	}
+
+	if (sspm_run_mode == SSPM_RUN_NORMAL)
+		ondiemet_module[ONDIEMET_SSPM] = 0;
+}
+
+void sspm_flush(void)
+{
+	int32_t ret;
+	uint32_t rdata;
+	uint32_t ipi_buf[4];
+
+	if (sspm_buf_available == 1) {
+		ipi_buf[0] = MET_MAIN_ID|MET_OP|MET_OP_FLUSH;
+		ipi_buf[1] = 0;
+		ipi_buf[2] = 0;
+		ipi_buf[3] = 0;
+		ret = sspm_ipi_send_sync(IPI_ID_MET, IPI_OPT_WAIT, (void *)ipi_buf, 0, &rdata, 1);
+	}
+
+	if (sspm_run_mode == SSPM_RUN_NORMAL)
+		ondiemet_module[ONDIEMET_SSPM] = 0;
+}
+#else /* CONFIG_MTK_TINYSYS_SSPM_SUPPORT */
+int sspm_buffer_size = -1;
+
+int sspm_attr_init(struct device *dev)
+{
+	return 0;
+}
+
+int sspm_attr_uninit(struct device *dev)
+{
+	return 0;
+}
+
+void sspm_start(void)
+{
+}
+
+void sspm_stop(void)
+{
+}
+
+void sspm_extract(void)
+{
+}
+
+void sspm_flush(void)
+{
+}
+
+#endif /* CONFIG_MTK_TINYSYS_SSPM_SUPPORT */
diff --git a/src/devtools/met-driver/met_drv/common/sspm/ondiemet_sspm.h b/src/devtools/met-driver/met_drv/common/sspm/ondiemet_sspm.h
new file mode 100644
index 0000000..6fa37c9
--- /dev/null
+++ b/src/devtools/met-driver/met_drv/common/sspm/ondiemet_sspm.h
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __ONDIEMET_SSPM_H
+#define __ONDIEMET_SSPM_H
+
+#ifdef CONFIG_MTK_TINYSYS_SSPM_SUPPORT
+#include "ondiemet.h"
+#include "sspm_ipi.h"
+#include <linux/dma-mapping.h>
+
+/* we may use IPI_ID_PLATFORM for mt6759 to reduce SRAM */
+#ifndef IPI_ID_MET
+/* #define IPI_ID_MET IPI_ID_TST1 */
+#define IPI_ID_MET IPI_ID_PLATFORM
+#endif
+
+/* MET IPI command definition: mbox 0 */
+/* main func ID: bit[31-24]; sub func ID: bit[23-18]; argu 0: bit[17-0] */
+#define MET_MAIN_ID_MASK        0xff000000 /* bit 31 - 24 */
+#define MET_SUB_ID_MASK         0x00fc0000 /* bit 23 - 18 */
+#define MET_ARGU0_MASK          0x0003ffff /* bit 17 - 0 */
+#define FUNC_BIT_SHIFT          18
+#define MID_BIT_SHIFT           9
+#define MET_MAIN_ID             0x06000000
+/* handle argument and attribute */
+#define PROCESS_ARGU            0x01
+#define PROCESS_ATTR            0x02
+#define MODULE_ID_MASK          0x3fe00 /* bit 9 - 17 */
+#define ARGUMENT_MASK           0x01ff  /* bit 0 - 8 */
+
+/* the following command is used for AP to MD32 */
+#define MET_OP            (1 << FUNC_BIT_SHIFT)
+/* argu 0: start: 0x01; stop: 0x02; extract: 0x03 */
+#define MET_OP_START        0x00000001
+#define MET_OP_STOP         0x00000002
+#define MET_OP_EXTRACT      0x00000003
+#define MET_OP_FLUSH        0x00000004
+#define MET_SR            (2 << FUNC_BIT_SHIFT) /* sample rate */
+#define MET_MODULE        (3 << FUNC_BIT_SHIFT) /* module enable/disable */
+#define MET_ARGU          (4 << FUNC_BIT_SHIFT) /* argument passing */
+#define MET_ATTR          (5 << FUNC_BIT_SHIFT) /* attribute passing */
+/* system memory information for on-die-met log data */
+#define MET_BUFFER_INFO   (6 << FUNC_BIT_SHIFT)
+#define MET_TIMESTAMP     (7 << FUNC_BIT_SHIFT) /* timestamp info */
+#define MET_GPT           (8 << FUNC_BIT_SHIFT) /* GPT counter reading */
+#define MET_REQ_AP2MD     (9 << FUNC_BIT_SHIFT) /* user defined command */
+#define MET_RESP_AP2MD    (10 << FUNC_BIT_SHIFT) /* may no need */
+/* mode: bit 15 - 0: */
+/*  Bit 0: MD32 SRAM mode; Bit 1: System DRAM mode */
+/*  value: 0: output to next level of storage; 1: loop in its own storage */
+#define MET_DATA_MODE     (11 << FUNC_BIT_SHIFT) /* log output mode */
+/* start/stop read data into MD32 SRAM buffer; both DMA and met_printf() */
+#define MET_DATA_OP       (12 << FUNC_BIT_SHIFT) /* data read operation */
+/* the following command is used for MD32 to AP */
+#define MET_DUMP_BUFFER   (13 << FUNC_BIT_SHIFT)
+#define MET_REQ_MD2AP     (14 << FUNC_BIT_SHIFT) /* user defined command */
+#define MET_CLOSE_FILE    (15 << FUNC_BIT_SHIFT) /* Inform to close the SD file */
+#define MET_RESP_MD2AP    (16 << FUNC_BIT_SHIFT)
+#define MET_RUN_MODE      (17 << FUNC_BIT_SHIFT)
+
+/* Note: the module ID and its bit pattern should be fixed as below */
+/* DMA based module first */
+enum {
+	MID_PMQOS = 0,
+	MID_VCORE_DVFS,
+	MID_EMI,
+	MID_THERMAL_CPU,
+	MID_WALL_TIME,
+	MID_CPU_DVFS,
+	MID_GPU_DVFS,
+	MID_PTPOD,
+	MID_SPM,
+	MID_PROFILE,
+
+	MID_COMMON = 0x1F
+};
+
+#define ID_PMQOS       (1 << MID_PMQOS)
+#define ID_SMI         (1 << MID_SMI)
+#define ID_EMI         (1 << MID_EMI)
+#define ID_THERMAL_CPU (1 << MID_THERMAL_CPU)
+#define ID_WALL_TIME   (1 << MID_WALL_TIME)
+#define ID_CPU_DVFS    (1 << MID_CPU_DVFS)
+#define ID_GPU_DVFS    (1 << MID_GPU_DVFS)
+#define ID_VCORE_DVFS  (1 << MID_VCORE_DVFS)
+#define ID_PTPOD       (1 << MID_PTPOD)
+#define ID_SPM         (1 << MID_SPM)
+#define ID_PROFILE     (1 << MID_PROFILE)
+#define ID_COMMON      (1 << MID_COMMON)
+
+
+extern void ondiemet_extract(void);
+extern void ondiemet_stop(void);
+extern void ondiemet_start(void);
+
+extern void start_sspm_ipi_recv_thread(void);
+extern void stop_sspm_ipi_recv_thread(void);
+
+extern unsigned int ondiemet_ipi_buf[];
+
+/* extern phys_addr_t ondiemet_sspm_log_phy_addr, ondiemet_sspm_log_virt_addr; */
+extern dma_addr_t ondiemet_sspm_log_phy_addr;
+
+extern void *ondiemet_sspm_log_virt_addr;
+extern uint32_t ondiemet_sspm_log_size;
+
+extern int ondiemet_attr_init(struct device *dev);
+extern int ondiemet_attr_uninit(struct device *dev);
+extern int met_sspm_log_discard;
+
+#define SSPM_LOG_FILE 0
+#define SSPM_LOG_SRAM 1
+#define SSPM_LOG_DRAM 2
+extern int sspm_log_mode;
+#define SSPM_RUN_NORMAL 0
+#define SSPM_RUN_CONTINUOUS 1
+extern int sspm_run_mode;
+
+/* extern void sspm_get_buffer_info(void); */
+extern int sspm_buf_available;
+extern int sspm_buffer_dumping;
+
+void sspm_flush(void);
+
+#endif /* CONFIG_MTK_TINYSYS_SSPM_SUPPORT */
+#endif /* __ONDIEMET_SSPM_H */
diff --git a/src/devtools/met-driver/met_drv/common/switch.h b/src/devtools/met-driver/met_drv/common/switch.h
new file mode 100644
index 0000000..14397d7
--- /dev/null
+++ b/src/devtools/met-driver/met_drv/common/switch.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __MT_SWITCH__
+#define __MT_SWITCH__
+/*
+ * =========================
+ * !!!!!!!!!!!NOTICE!!!!!!!!
+ * =========================
+ * MT_SWITCH OPTION must delcare as Mask value
+ * And sort them from smallest to largest
+ * MT_SWITCH_MX_ITEM was used to determine argument range
+*/
+enum {
+	/* =================== */
+	/* user define mt switch event */
+	/* =================== */
+	MT_SWITCH_SCHEDSWITCH = 0x0001,
+	MT_SWITCH_64_32BIT = 0x0002,
+	MT_SWITCH_TAGPOLLING = 0x0004,
+	MT_SWITCH_EVENT_TIMER = 0x0008,
+	/* =================== */
+	MT_SWITCH_MX_ITEM
+};
+
+extern struct metdevice met_switch;
+#endif
diff --git a/src/devtools/met-driver/met_drv/common/trace.h b/src/devtools/met-driver/met_drv/common/trace.h
new file mode 100644
index 0000000..f259b7a
--- /dev/null
+++ b/src/devtools/met-driver/met_drv/common/trace.h
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _TRACE_H_
+#define _TRACE_H_
+
+
+extern void (*mp_cp_ptr)(unsigned long long timestamp,
+	       struct task_struct *task,
+	       unsigned long program_counter,
+	       unsigned long dcookie,
+	       unsigned long offset,
+	       unsigned char cnt, unsigned int *value);
+
+#define MP_FMT1	"%x\n"
+#define MP_FMT2	"%x,%x\n"
+#define MP_FMT3	"%x,%x,%x\n"
+#define MP_FMT4	"%x,%x,%x,%x\n"
+#define MP_FMT5	"%x,%x,%x,%x,%x\n"
+#define MP_FMT6	"%x,%x,%x,%x,%x,%x\n"
+#define MP_FMT7	"%x,%x,%x,%x,%x,%x,%x\n"
+#define MP_FMT8	"%x,%x,%x,%x,%x,%x,%x,%x\n"
+#define MP_FMT9	"%x,%x,%x,%x,%x,%x,%x,%x,%x\n"
+#define MP_FMT10 "%x,%x,%x,%x,%x,%x,%x,%x,%x,%x\n"
+#define MP_FMT11 "%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x\n"
+#define MP_FMT12 "%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x\n"
+#define MP_FMT13 "%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x\n"
+#define MP_FMT14 "%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x\n"
+#define MP_FMT15 "%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x\n"
+
+#define MET_GENERAL_PRINT(FUNC, count, value) \
+do { \
+	switch (count) { \
+	case 1: { \
+		FUNC(MP_FMT1, value[0]); \
+		} \
+		break; \
+	case 2: { \
+		FUNC(MP_FMT2, value[0], value[1]); \
+		} \
+		break; \
+	case 3: { \
+		FUNC(MP_FMT3, value[0], value[1], value[2]); \
+		} \
+		break; \
+	case 4: { \
+		FUNC(MP_FMT4, value[0], value[1], value[2], value[3]); \
+		} \
+		break; \
+	case 5: { \
+		FUNC(MP_FMT5, value[0], value[1], value[2], value[3], value[4]); \
+		} \
+		break; \
+	case 6: { \
+		FUNC(MP_FMT6, value[0], value[1], value[2], value[3], value[4], value[5]); \
+		} \
+		break; \
+	case 7: { \
+		FUNC(MP_FMT7, value[0], value[1], value[2], value[3], value[4], value[5], value[6]); \
+		} \
+		break; \
+	case 8: { \
+		FUNC(MP_FMT8, value[0], value[1], value[2], value[3], value[4], value[5], value[6], value[7]); \
+		} \
+		break; \
+	case 9: { \
+		FUNC(MP_FMT9, value[0], value[1], value[2], value[3], value[4], value[5], value[6], value[7], \
+				value[8]); \
+		} \
+		break; \
+	case 10: { \
+		FUNC(MP_FMT10, value[0], value[1], value[2], value[3], value[4], value[5], value[6], value[7], \
+				value[8], value[9]); \
+		} \
+		break; \
+	case 11: { \
+		FUNC(MP_FMT11, value[0], value[1], value[2], value[3], value[4], value[5], value[6], value[7], \
+				value[8], value[9], value[10]); \
+		} \
+		break; \
+	case 12: { \
+		FUNC(MP_FMT12, value[0], value[1], value[2], value[3], value[4], value[5], value[6], value[7], \
+				value[8], value[9], value[10], value[11]); \
+		} \
+		break; \
+	case 13: { \
+		FUNC(MP_FMT13, value[0], value[1], value[2], value[3], value[4], value[5], value[6], value[7], \
+				value[8], value[9], value[10], value[11], value[12]); \
+		} \
+		break; \
+	case 14: { \
+		FUNC(MP_FMT14, value[0], value[1], value[2], value[3], value[4], value[5], value[6], value[7], \
+				value[8], value[9], value[10], value[11], value[12], value[13]); \
+		} \
+		break; \
+	case 15: { \
+		FUNC(MP_FMT15, value[0], value[1], value[2], value[3], value[4], value[5], value[6], value[7], \
+				value[8], value[9], value[10], value[11], value[12], value[13], value[14]); \
+		} \
+		break; \
+	} \
+} while (0)
+#endif /* _TRACE_H_ */
diff --git a/src/devtools/met-driver/met_drv/common/util.c b/src/devtools/met-driver/met_drv/common/util.c
new file mode 100644
index 0000000..051a3bd
--- /dev/null
+++ b/src/devtools/met-driver/met_drv/common/util.c
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "util.h"
+#include <linux/fs.h>
+#include <linux/kernel.h>
+/* #include <asm/uaccess.h> */
+#include <linux/uaccess.h>
+
+#ifdef FILELOG
+
+static char tmp[1000] = { 0 };
+
+ /*TODO*/
+/**
+ * open file
+ * @param name path to open
+ * @return file pointer
+ */
+struct file *open_file(const char *name)
+{
+	struct file *fp = NULL;
+
+	fp = filp_open(name, O_WRONLY | O_APPEND /*| O_TRUNC */  | O_CREAT, 0664);
+	if (unlikely(fp == NULL)) {
+		pr_debug(KERNEL_INFO "can not open result file");
+		return NULL;
+	}
+	return fp;
+}
+
+/**
+ * write to file
+ * @param fp file pointer
+ * @param format format string
+ * @param ... variable-length subsequent arguments
+ */
+void write_file(struct file *fp, const char *format, ...)
+{
+	va_list va;
+	mm_segment_t fs = get_fs();
+
+	va_start(va, format);
+	vsnprintf(tmp, sizeof(tmp), format, va);
+	set_fs(KERNEL_DS);
+	vfs_write(fp, tmp, strlen(tmp), &(fp->f_pos));
+	set_fs(fs);
+	va_end(va);
+}
+
+/**
+ * close file
+ * @param fp file pointer
+ * @return exit code
+ */
+int close_file(struct file *fp)
+{
+	if (likely(fp != NULL)) {
+		filp_close(fp, NULL);
+		fp = NULL;
+		return 0;
+	}
+	pr_debug("cannot close file pointer:%p\n", fp);
+	return -1;
+}
+
+void filelog(char *str)
+{
+	struct file *fp;
+
+	fp = open_file("/data/met.log");
+	if (fp != NULL) {
+		write_file(fp, "%s", str);
+		close_file(fp);
+	}
+}
+
+#endif				/* FILELOG */
diff --git a/src/devtools/met-driver/met_drv/common/util.h b/src/devtools/met-driver/met_drv/common/util.h
new file mode 100644
index 0000000..5730376
--- /dev/null
+++ b/src/devtools/met-driver/met_drv/common/util.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _SRC_UTIL_H_
+#define _SRC_UTIL_H_
+
+/* #define FILELOG 1 */
+
+#ifdef FILELOG
+void filelog(char *str);
+#else
+#define filelog(str)
+#endif
+
+#endif				/* _SRC_UTIL_H_ */
diff --git a/src/devtools/met-driver/met_drv/common/v6_pmu_hw.c b/src/devtools/met-driver/met_drv/common/v6_pmu_hw.c
new file mode 100644
index 0000000..d5758ed
--- /dev/null
+++ b/src/devtools/met-driver/met_drv/common/v6_pmu_hw.c
@@ -0,0 +1,292 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+
+/* include <asm/system.h> */
+#include <linux/smp.h>
+#include "cpu_pmu.h"
+#include "v6_pmu_name.h"
+
+enum ARM_TYPE {
+	ARM1136 = 0xB36,
+	ARM1156 = 0xB56,
+	ARM1176 = 0xB76,
+	CHIP_UNKNOWN = 0xFFF
+};
+
+struct chip_pmu {
+	enum ARM_TYPE type;
+	struct pmu_desc *desc;
+	unsigned int count;
+	const char *cpu_name;
+};
+
+static struct chip_pmu chips[] = {
+	{ARM1136, arm11_pmu_desc, ARM11_PMU_DESC_COUNT, "arm1136"},
+	{ARM1156, arm11_pmu_desc, ARM11_PMU_DESC_COUNT, "arm1156"},
+	{ARM1176, arm11_pmu_desc, ARM11_PMU_DESC_COUNT, "arm1176"},
+};
+static struct chip_pmu chip_unknown = { CHIP_UNKNOWN, NULL, 0, "Unknown CPU" };
+
+#define CHIP_PMU_COUNT (sizeof(chips) / sizeof(struct chip_pmu))
+
+static struct chip_pmu *chip;
+
+/* define V6_PMU_HW_DEBUG */
+#ifdef V6_PMU_HW_DEBUG
+#define v6pmu_hw_debug(fmt, arg...)     pr_debug(fmt, ##arg)
+#else
+#define v6pmu_hw_debug(fmt, arg...)     do {} while (0)
+#endif
+
+#define ARMV6_PMCR_ENABLE               (1 << 0)
+#define ARMV6_PMCR_CTR01_RESET          (1 << 1)
+#define ARMV6_PMCR_CCOUNT_RESET         (1 << 2)
+#define ARMV6_PMCR_CCOUNT_DIV           (1 << 3)
+#define ARMV6_PMCR_COUNT0_IEN           (1 << 4)
+#define ARMV6_PMCR_COUNT1_IEN           (1 << 5)
+#define ARMV6_PMCR_CCOUNT_IEN           (1 << 6)
+#define ARMV6_PMCR_COUNT0_OVERFLOW      (1 << 8)
+#define ARMV6_PMCR_COUNT1_OVERFLOW      (1 << 9)
+#define ARMV6_PMCR_CCOUNT_OVERFLOW      (1 << 10)
+#define ARMV6_PMCR_EVT_COUNT0_SHIFT     20
+#define ARMV6_PMCR_EVT_COUNT0_MASK      (0xFF << ARMV6_PMCR_EVT_COUNT0_SHIFT)
+#define ARMV6_PMCR_EVT_COUNT1_SHIFT     12
+#define ARMV6_PMCR_EVT_COUNT1_MASK      (0xFF << ARMV6_PMCR_EVT_COUNT1_SHIFT)
+
+#define ARMV6_PMCR_OVERFLOWED_MASK \
+	(ARMV6_PMCR_COUNT0_OVERFLOW | ARMV6_PMCR_COUNT1_OVERFLOW | \
+	ARMV6_PMCR_CCOUNT_OVERFLOW)
+
+enum armv6_counters {
+	ARMV6_COUNTER0 = 0,
+	ARMV6_COUNTER1,
+	ARMV6_CYCLE_COUNTER,
+};
+
+static inline unsigned long armv6_pmcr_read(void)
+{
+	u32 val;
+
+	asm volatile ("mrc   p15, 0, %0, c15, c12, 0":"=r" (val));
+	return val;
+}
+
+static inline void armv6_pmcr_write(unsigned long val)
+{
+	asm volatile ("mcr   p15, 0, %0, c15, c12, 0"::"r" (val));
+}
+
+static inline unsigned int armv6_pmu_read_count(unsigned int idx)
+{
+	unsigned long value = 0;
+
+	if (idx == ARMV6_CYCLE_COUNTER)
+		asm volatile ("mrc   p15, 0, %0, c15, c12, 1":"=r" (value));
+	else
+if (idx == ARMV6_COUNTER0)
+	asm volatile ("mrc   p15, 0, %0, c15, c12, 2":"=r" (value));
+	else
+if (idx == ARMV6_COUNTER1)
+	asm volatile ("mrc   p15, 0, %0, c15, c12, 3":"=r" (value));
+
+	return value;
+}
+
+static inline void armv6_pmu_overflow(void)
+{
+	unsigned int val;
+
+	val = armv6_pmcr_read();
+	val |= ARMV6_PMCR_OVERFLOWED_MASK;
+	armv6_pmcr_write(val);
+}
+
+static inline unsigned int armv6_pmu_control_read(void)
+{
+	u32 val;
+
+	asm volatile ("mrc   p15, 0, %0, c15, c12, 0":"=r" (val));
+	return val;
+}
+
+static inline void armv6_pmu_control_write(unsigned int setting)
+{
+	unsigned long val;
+
+	val = armv6_pmcr_read();
+	val |= setting;
+	armv6_pmcr_write(val);
+}
+
+static void armv6_pmu_hw_reset_all(void)
+{
+	unsigned long val;
+
+	val = armv6_pmcr_read();
+	val &= ~ARMV6_PMCR_ENABLE;	/* disable all counters */
+	val |= (ARMV6_PMCR_CTR01_RESET | ARMV6_PMCR_CCOUNT_RESET);	/* reset CCNT, PMNC1/2 counter to zero */
+	armv6_pmcr_write(val);
+
+	armv6_pmu_overflow();
+}
+
+static void armv6pmu_enable_event(int idx, unsigned short config)
+{
+	unsigned long val, mask, evt;
+
+	if (idx == ARMV6_CYCLE_COUNTER) {
+		mask = 0;
+		evt = ARMV6_PMCR_CCOUNT_IEN;
+	} else if (idx == ARMV6_COUNTER0) {
+		mask = ARMV6_PMCR_EVT_COUNT0_MASK;
+		evt = (config << ARMV6_PMCR_EVT_COUNT0_SHIFT) | ARMV6_PMCR_COUNT0_IEN;
+	} else if (idx == ARMV6_COUNTER1) {
+		mask = ARMV6_PMCR_EVT_COUNT1_MASK;
+		evt = (config << ARMV6_PMCR_EVT_COUNT1_SHIFT) | ARMV6_PMCR_COUNT1_IEN;
+	} else {
+		pr_debug("invalid counter number (%d)\n", idx);
+		return;
+	}
+
+	/*
+	 * Mask out the current event and set the counter to count the event
+	 * that we're interested in.
+	 */
+	val = armv6_pmcr_read();
+	val &= ~mask;
+	val |= evt;
+	armv6_pmcr_write(val);
+}
+
+static int armv6_pmu_hw_get_event_desc(int i, int event, char *event_desc)
+{
+	if (event_desc == NULL)
+		return -1;
+
+	for (i = 0; i < chip->count; i++) {
+		if (chip->desc[i].event == event) {
+			strncpy(event_desc, chip->desc[i].name, MXSIZE_PMU_DESC - 1);
+			break;
+		}
+	}
+
+	if (i == chip->count)
+		return -1;
+
+	return 0;
+}
+
+static int armv6_pmu_hw_check_event(struct met_pmu *pmu, int idx, int event)
+{
+	int i;
+
+	/* Check if event is duplicate */
+	for (i = 0; i < idx; i++) {
+		if (pmu[i].event == event)
+			break;
+	}
+	if (i < idx) {
+		/* pr_debug("++++++ found duplicate event 0x%02x i=%d\n", event, i); */
+		return -1;
+	}
+
+	for (i = 0; i < chip->count; i++) {
+		if (chip->desc[i].event == event)
+			break;
+	}
+
+	if (i == chip->count)
+		return -1;
+
+	return 0;
+}
+
+static void armv6_pmu_hw_start(struct met_pmu *pmu, int count)
+{
+	int i;
+	int generic = count - 1;
+
+	armv6_pmu_hw_reset_all();
+
+	for (i = 0; i < generic; i++) {
+		if (pmu[i].mode == MODE_POLLING)
+			armv6pmu_enable_event(i, pmu[i].event);
+	}
+
+	if (pmu[count - 1].mode == MODE_POLLING)
+		armv6pmu_enable_event(2, pmu[2].event);
+
+	armv6_pmu_control_write(ARMV6_PMCR_ENABLE);
+}
+
+static void armv6_pmu_hw_stop(int count)
+{
+	armv6_pmu_hw_reset_all();
+}
+
+static unsigned int armv6_pmu_hw_polling(struct met_pmu *pmu, int count, unsigned int *pmu_value)
+{
+	int i, cnt = 0;
+	int generic = count - 1;
+
+	for (i = 0; i < generic; i++) {
+		if (pmu[i].mode == MODE_POLLING) {
+			pmu_value[cnt] = armv6_pmu_read_count(i);
+			cnt++;
+		}
+	}
+
+	if (pmu[count - 1].mode == MODE_POLLING) {
+		pmu_value[cnt] = armv6_pmu_read_count(2);
+		cnt++;
+	}
+
+	armv6_pmu_control_write(ARMV6_PMCR_ENABLE | ARMV6_PMCR_CTR01_RESET |
+				ARMV6_PMCR_CCOUNT_RESET);
+
+	return cnt;
+}
+
+struct cpu_pmu_hw armv6_pmu = {
+	.name = "armv6_pmu",
+	.get_event_desc = armv6_pmu_hw_get_event_desc,
+	.check_event = armv6_pmu_hw_check_event,
+	.start = armv6_pmu_hw_start,
+	.stop = armv6_pmu_hw_stop,
+	.polling = armv6_pmu_hw_polling,
+};
+
+struct cpu_pmu_hw *v6_cpu_pmu_hw_init(int typeid)
+{
+	int i;
+
+	for (i = 0; i < CHIP_PMU_COUNT; i++) {
+		if (chips[i].type == typeid) {
+			chip = &(chips[i]);
+
+			break;
+		}
+	}
+
+	if (chip == NULL) {
+		chip = &chip_unknown;
+
+		return NULL;
+	}
+
+	armv6_pmu.nr_cnt = 3;
+	armv6_pmu.cpu_name = chip->cpu_name;
+
+	return &armv6_pmu;
+}
diff --git a/src/devtools/met-driver/met_drv/common/v6_pmu_hw.h b/src/devtools/met-driver/met_drv/common/v6_pmu_hw.h
new file mode 100644
index 0000000..a532f13
--- /dev/null
+++ b/src/devtools/met-driver/met_drv/common/v6_pmu_hw.h
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+
+#ifndef __V6_PMU_HW_H__
+#define __V6_PMU_HW_H__
+
+extern struct cpu_pmu_hw armv6_pmu;
+extern struct cpu_pmu_hw *v6_cpu_pmu_hw_init(int typeid);
+
+#endif
diff --git a/src/devtools/met-driver/met_drv/common/v6_pmu_name.h b/src/devtools/met-driver/met_drv/common/v6_pmu_name.h
new file mode 100644
index 0000000..c1cdd91
--- /dev/null
+++ b/src/devtools/met-driver/met_drv/common/v6_pmu_name.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+
+#ifndef _V6_PMU_NAME_H_
+#define _V6_PMU_NAME_H_
+
+/* ARM11 */
+struct pmu_desc arm11_pmu_desc[] = {
+	{0x00, "ICACHE_MISS"},
+	{0x01, "IBUF_STALL"},
+	{0x02, "DDEP_STALL"},
+	{0x03, "ITLB_MISS"},
+	{0x04, "DTLB_MISS"},
+	{0x05, "BR_EXEC"},
+	{0x06, "BR_MISPREDICT"},
+	{0x07, "CPU_INST"},
+	{0x09, "DCACHE_HIT"},
+	{0x0A, "L1D_CACHE"},
+	{0x0B, "L1D_CACHE_REFILL"},
+	{0x0C, "DCACHE_WBACK"},
+	{0x0D, "SW_PC_CHANGE"},
+	{0x0F, "MAIN_TLB_MISS"},
+	{0x10, "EXPL_D_ACCESS"},
+	{0x11, "LSU_FULL_STALL"},
+	{0x12, "WBUF_DRAINED"},
+	{0xFF, "CPU_CYCLES"},
+	{0x20, "NOP"},
+};
+
+#define ARM11_PMU_DESC_COUNT (sizeof(arm11_pmu_desc) / sizeof(struct pmu_desc))
+
+#endif				/* _V6_PMU_NAME_H_ */
diff --git a/src/devtools/met-driver/met_drv/common/v7_pmu_hw.c b/src/devtools/met-driver/met_drv/common/v7_pmu_hw.c
new file mode 100644
index 0000000..161bf22
--- /dev/null
+++ b/src/devtools/met-driver/met_drv/common/v7_pmu_hw.c
@@ -0,0 +1,303 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/* include <asm/system.h> */
+#include <linux/smp.h>
+#include "cpu_pmu.h"
+#include "v6_pmu_hw.h"
+#include "v7_pmu_name.h"
+#include "v8_pmu_name.h"	/* for 32-bit build of arm64 cpu */
+
+#define ARMV7_PMCR_E		(1 << 0)	/* enable all counters */
+#define ARMV7_PMCR_P		(1 << 1)
+#define ARMV7_PMCR_C		(1 << 2)
+#define ARMV7_PMCR_D		(1 << 3)
+#define ARMV7_PMCR_X		(1 << 4)
+#define ARMV7_PMCR_DP		(1 << 5)
+#define ARMV7_PMCR_N_SHIFT		11	/* Number of counters supported */
+#define ARMV7_PMCR_N_MASK		0x1f
+#define ARMV7_PMCR_MASK			0x3f	/* mask for writable bits */
+
+
+enum ARM_TYPE {
+	CORTEX_A7 = 0xC07,
+	CORTEX_A9 = 0xC09,
+	CORTEX_A12 = 0xC0D,
+	CORTEX_A15 = 0xC0F,
+	CORTEX_A17 = 0xC0E,
+	CORTEX_A53 = 0xD03,
+	CORTEX_A57 = 0xD07,
+	CHIP_UNKNOWN = 0xFFF
+};
+
+struct chip_pmu {
+	enum ARM_TYPE type;
+	struct pmu_desc *desc;
+	unsigned int count;
+	const char *cpu_name;
+};
+
+static struct chip_pmu chips[] = {
+	{CORTEX_A7, a7_pmu_desc, A7_PMU_DESC_COUNT, "Cortex-A7"},
+	{CORTEX_A9, a9_pmu_desc, A9_PMU_DESC_COUNT, "Cortex-A9"},
+	{CORTEX_A12, a7_pmu_desc, A7_PMU_DESC_COUNT, "Cortex-A12"},
+	{CORTEX_A15, a7_pmu_desc, A7_PMU_DESC_COUNT, "Cortex-A15"},
+	{CORTEX_A17, a7_pmu_desc, A7_PMU_DESC_COUNT, "Cortex-A17"},
+	{CORTEX_A53, a53_pmu_desc, A53_PMU_DESC_COUNT, "Cortex-A53"},
+	{CORTEX_A57, a7_pmu_desc, A7_PMU_DESC_COUNT, "Cortex-A57"},
+};
+static struct chip_pmu chip_unknown = { CHIP_UNKNOWN, NULL, 0, "Unknown CPU" };
+
+#define CHIP_PMU_COUNT (sizeof(chips) / sizeof(struct chip_pmu))
+
+static struct chip_pmu *chip;
+
+static enum ARM_TYPE armv7_get_ic(void)
+{
+	unsigned int value;
+	/* Read Main ID Register */
+	asm volatile ("mrc p15, 0, %0, c0, c0, 0":"=r" (value));
+
+	value = (value & 0xffff) >> 4;	/* primary part number */
+	return value;
+}
+
+static inline void armv7_pmu_counter_select(unsigned int idx)
+{
+	asm volatile ("mcr p15, 0, %0, c9, c12, 5"::"r" (idx));
+	isb();
+}
+
+static inline void armv7_pmu_type_select(unsigned int idx, unsigned int type)
+{
+	armv7_pmu_counter_select(idx);
+	asm volatile ("mcr p15, 0, %0, c9, c13, 1"::"r" (type));
+}
+
+static inline unsigned int armv7_pmu_read_count(unsigned int idx)
+{
+	unsigned int value;
+
+	if (idx == 31) {
+		asm volatile ("mrc p15, 0, %0, c9, c13, 0":"=r" (value));
+	} else {
+		armv7_pmu_counter_select(idx);
+		asm volatile ("mrc p15, 0, %0, c9, c13, 2":"=r" (value));
+	}
+	return value;
+}
+
+static inline void armv7_pmu_write_count(int idx, u32 value)
+{
+	if (idx == 31) {
+		asm volatile ("mcr p15, 0, %0, c9, c13, 0"::"r" (value));
+	} else {
+		armv7_pmu_counter_select(idx);
+		asm volatile ("mcr p15, 0, %0, c9, c13, 2"::"r" (value));
+	}
+}
+
+static inline void armv7_pmu_enable_count(unsigned int idx)
+{
+	asm volatile ("mcr p15, 0, %0, c9, c12, 1"::"r" (1 << idx));
+}
+
+static inline void armv7_pmu_disable_count(unsigned int idx)
+{
+	asm volatile ("mcr p15, 0, %0, c9, c12, 2"::"r" (1 << idx));
+}
+
+static inline void armv7_pmu_enable_intr(unsigned int idx)
+{
+	asm volatile ("mcr p15, 0, %0, c9, c14, 1"::"r" (1 << idx));
+}
+
+static inline void armv7_pmu_disable_intr(unsigned int idx)
+{
+	asm volatile ("mcr p15, 0, %0, c9, c14, 2"::"r" (1 << idx));
+}
+
+static inline unsigned int armv7_pmu_overflow(void)
+{
+	unsigned int val;
+
+	asm volatile ("mrc p15, 0, %0, c9, c12, 3":"=r" (val));	/* read */
+	asm volatile ("mcr p15, 0, %0, c9, c12, 3"::"r" (val));
+	return val;
+}
+
+static inline unsigned int armv7_pmu_control_read(void)
+{
+	u32 val;
+
+	asm volatile ("mrc p15, 0, %0, c9, c12, 0":"=r" (val));
+	return val;
+}
+
+static inline void armv7_pmu_control_write(unsigned int val)
+{
+	val &= ARMV7_PMCR_MASK;
+	isb();
+	asm volatile ("mcr p15, 0, %0, c9, c12, 0"::"r" (val));
+}
+
+static int armv7_pmu_hw_get_counters(void)
+{
+	int count = armv7_pmu_control_read();
+	/* N, bits[15:11] */
+	count = ((count >> ARMV7_PMCR_N_SHIFT) & ARMV7_PMCR_N_MASK);
+	return count;
+}
+
+static void armv7_pmu_hw_reset_all(int generic_counters)
+{
+	int i;
+
+	armv7_pmu_control_write(ARMV7_PMCR_C | ARMV7_PMCR_P);
+	/* generic counter */
+	for (i = 0; i < generic_counters; i++) {
+		armv7_pmu_disable_intr(i);
+		armv7_pmu_disable_count(i);
+	}
+	/* cycle counter */
+	armv7_pmu_disable_intr(31);
+	armv7_pmu_disable_count(31);
+	armv7_pmu_overflow();	/* clear overflow */
+}
+
+static int armv7_pmu_hw_get_event_desc(int i, int event, char *event_desc)
+{
+	if (event_desc == NULL)
+		return -1;
+
+	for (i = 0; i < chip->count; i++) {
+		if (chip->desc[i].event == event) {
+			strncpy(event_desc, chip->desc[i].name, MXSIZE_PMU_DESC - 1);
+			break;
+		}
+	}
+	if (i == chip->count)
+		return -1;
+
+	return 0;
+}
+
+static int armv7_pmu_hw_check_event(struct met_pmu *pmu, int idx, int event)
+{
+	int i;
+
+	/* Check if event is duplicate */
+	for (i = 0; i < idx; i++) {
+		if (pmu[i].event == event)
+			break;
+	}
+	if (i < idx) {
+		/* pr_debug("++++++ found duplicate event 0x%02x i=%d\n", event, i); */
+		return -1;
+	}
+
+	for (i = 0; i < chip->count; i++) {
+		if (chip->desc[i].event == event)
+			break;
+	}
+
+	if (i == chip->count)
+		return -1;
+
+	return 0;
+}
+
+static void armv7_pmu_hw_start(struct met_pmu *pmu, int count)
+{
+	int i;
+	int generic = count - 1;
+
+	armv7_pmu_hw_reset_all(generic);
+	for (i = 0; i < generic; i++) {
+		if (pmu[i].mode == MODE_POLLING) {
+			armv7_pmu_type_select(i, pmu[i].event);
+			armv7_pmu_enable_count(i);
+		}
+	}
+	if (pmu[count - 1].mode == MODE_POLLING) {	/* cycle counter */
+		armv7_pmu_enable_count(31);
+	}
+	armv7_pmu_control_write(ARMV7_PMCR_E);
+}
+
+static void armv7_pmu_hw_stop(int count)
+{
+	int generic = count - 1;
+
+	armv7_pmu_hw_reset_all(generic);
+}
+
+static unsigned int armv7_pmu_hw_polling(struct met_pmu *pmu, int count, unsigned int *pmu_value)
+{
+	int i, cnt = 0;
+	int generic = count - 1;
+
+	for (i = 0; i < generic; i++) {
+		if (pmu[i].mode == MODE_POLLING) {
+			pmu_value[cnt] = armv7_pmu_read_count(i);
+			cnt++;
+		}
+	}
+	if (pmu[count - 1].mode == MODE_POLLING) {
+		pmu_value[cnt] = armv7_pmu_read_count(31);
+		cnt++;
+	}
+	armv7_pmu_control_write(ARMV7_PMCR_C | ARMV7_PMCR_P | ARMV7_PMCR_E);
+
+	return cnt;
+}
+
+
+struct cpu_pmu_hw armv7_pmu = {
+	.name = "armv7_pmu",
+	.get_event_desc = armv7_pmu_hw_get_event_desc,
+	.check_event = armv7_pmu_hw_check_event,
+	.start = armv7_pmu_hw_start,
+	.stop = armv7_pmu_hw_stop,
+	.polling = armv7_pmu_hw_polling,
+};
+
+struct cpu_pmu_hw *cpu_pmu_hw_init(void)
+{
+	int i;
+	enum ARM_TYPE type;
+	struct cpu_pmu_hw *pmu = NULL;
+
+	type = armv7_get_ic();
+	for (i = 0; i < CHIP_PMU_COUNT; i++) {
+		if (chips[i].type == type) {
+			chip = &(chips[i]);
+			break;
+		}
+	}
+
+	if (chip != NULL) {
+		armv7_pmu.nr_cnt = armv7_pmu_hw_get_counters() + 1;
+		armv7_pmu.cpu_name = chip->cpu_name;
+		pmu = &armv7_pmu;
+	} else {
+		pmu = v6_cpu_pmu_hw_init(type);
+	}
+
+	if ((chip == NULL) && (pmu == NULL)) {
+		chip = &chip_unknown;
+		return NULL;
+	}
+
+	return pmu;
+}
diff --git a/src/devtools/met-driver/met_drv/common/v7_pmu_name.h b/src/devtools/met-driver/met_drv/common/v7_pmu_name.h
new file mode 100644
index 0000000..3219cd9
--- /dev/null
+++ b/src/devtools/met-driver/met_drv/common/v7_pmu_name.h
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _V7_PMU_NAME_H_
+#define _V7_PMU_NAME_H_
+
+/* Cortex-A7 */
+struct pmu_desc a7_pmu_desc[] = {
+	{0x00, "SW_INCR"},
+	{0x01, "L1I_CACHE_REFILL"},
+	{0x02, "L1I_TLB_REFILL"},
+	{0x03, "L1D_CACHE_REFILL"},
+	{0x04, "L1D_CACHE"},
+	{0x05, "L1D_TLB_REFILL"},
+	{0x06, "LD_RETIRED"},
+	{0x07, "ST_RETIRED"},
+	/* {0x08, "INST_RETIRED"}, */
+	{0x08, "CPU_INST"},
+	{0x09, "EXC_TAKEN"},
+	{0x0A, "EXC_RETURN"},
+	{0x0B, "CID_WRITE_RETIRED"},
+	{0x0C, "PC_WRITE_RETIRED"},
+	{0x0D, "BR_IMMED_RETIRED"},
+	{0x0E, "BR_RETURN_RETIRED"},
+	{0x0F, "UNALIGNED_LDST_RETIRED"},
+	{0x10, "BR_MIS_PRED"},
+	{0x12, "BR_PRED"},
+	{0x13, "MEM_ACCESS"},
+	{0x14, "L1I_CACHE"},
+	{0x15, "L1D_CACHE_WB"},
+	{0x16, "L2D_CACHE"},
+	{0x17, "L2D_CACHE_REFILL"},
+	{0x18, "L2D_CACHE_WB"},
+	{0x19, "BUS_ACCESS"},
+	{0x1D, "BUS_CYCLES"},
+	{0x60, "BUS_READ_ACCESS"},
+	{0x61, "BUS_WRITE_ACCESS"},
+	{0x86, "IRQ_EXC_TAKEN"},
+	{0x87, "FIQ_EXC_TAKEN"},
+	{0xC0, "EXT_MEM_REQ"},
+	{0xC1, "NO_CACHE_EXT_MEM_REQ"},
+	{0xC2, "PREFETCH_LINEFILL"},
+	{0xC3, "PREFETCH_LINEFILL_DROPPED"},
+	{0xC4, "ENT_READ_ALLOC_MODE"},
+	{0xC5, "READ_ALLOC_MODE"},
+	{0xC7, "ETM_EXT_OUT0"},
+	{0xC8, "ETM_EXT_OUT1"},
+	{0xC9, "DATA_WRITE_STALL"},
+	{0xCA, "DATA_READ_SNOOP_CLUSTER"},
+	{0xFF, "CPU_CYCLES"}
+};
+
+/* Cortex-A9 */
+struct pmu_desc a9_pmu_desc[] = {
+	{0x00, "SW_INCR"},
+	{0x01, "L1I_CACHE_REFILL"},
+	{0x02, "L1I_TLB_REFILL"},
+	{0x03, "L1D_CACHE_REFILL"},
+	{0x04, "L1D_CACHE"},
+	{0x05, "L1D_TLB_REFILL"},
+	{0x06, "LD_RETIRED"},
+	{0x07, "ST_RETIRED"},
+	{0x09, "EXC_TAKEN"},
+	{0x0A, "EXC_RETURN"},
+	{0x0B, "CID_WRITE_RETIRED"},
+	{0x0C, "PC_WRITE_RETIRED"},
+	{0x0D, "BR_IMMED_RETIRED"},
+	{0x0F, "UNALIGNED_LDST_RETIRED"},
+	{0x10, "BR_MIS_PRED"},
+	{0x12, "BR_PRED"},
+	{0x40, "JAVA_BC_EXEC"},
+	{0x41, "SW_JAVA_BC_EXEC"},
+	{0x42, "JAZELLE_BB_EXEC"},
+	{0x50, "CO_LF_MISS"},
+	{0x51, "CO_LF_HIT"},
+	{0x60, "ICACHE_DEP_STALL"},
+	{0x61, "DCACHE_DEP_STALL"},
+	{0x62, "M_TLB_STALL"},
+	{0x63, "STREX_PASSED"},
+	{0x64, "STREX_FAILED"},
+	{0x65, "DATA_EVICT"},
+	{0x66, "ISSUE_NO_DISP"},
+	{0x67, "ISSUE_EMPTY"},
+	/* {0x68, "INS_RENAME"}, */
+	{0x68, "CPU_INST"},
+	{0x6E, "PRED_FN_RET"},
+	{0x70, "MAIN_EXEC_INST"},
+	{0x71, "SEC_EXEC_INST"},
+	{0x72, "LOAD_STORE_INST"},
+	{0x73, "FLOAT_INST_RR"},
+	{0x74, "NEON_INST_RR"},
+	{0x80, "STALL_PLD"},
+	{0x81, "STALL_WRITE"},
+	{0x82, "STALL_INST_M_TLB_MISS"},
+	{0x83, "STALL_DATA_M_TLB_MISS"},
+	{0x84, "STALL_INST_U_TLB"},
+	{0x85, "STALL_DATA_U_TLB"},
+	{0x86, "STALL_DMB"},
+	{0x8A, "INT_CLK_EN"},
+	{0x8B, "DATA_E_CLK_EN"},
+	{0x90, "ISB_INST"},
+	{0x91, "DSB_INST"},
+	{0x92, "INS_DMB"},
+	{0x93, "EXT_IRQ"},
+	{0xA0, "PLE_CACHE_REQ_COMP"},
+	{0xA1, "PLE_CACHE_REQ_SKP"},
+	{0xA2, "PLE_FIFO_FLUSH"},
+	{0xA3, "PLE_REQ_COMP"},
+	{0xA4, "PLE_FIFO_OF"},
+	{0xA5, "PLE_REQ_PRG"},
+	{0xFF, "CPU_CYCLES"}
+};
+
+#define A7_PMU_DESC_COUNT (sizeof(a7_pmu_desc) / sizeof(struct pmu_desc))
+#define A9_PMU_DESC_COUNT (sizeof(a9_pmu_desc) / sizeof(struct pmu_desc))
+
+#endif				/* _V7_PMU_NAME_H_ */
diff --git a/src/devtools/met-driver/met_drv/common/v8_pmu_hw.c b/src/devtools/met-driver/met_drv/common/v8_pmu_hw.c
new file mode 100644
index 0000000..225e1c6
--- /dev/null
+++ b/src/devtools/met-driver/met_drv/common/v8_pmu_hw.c
@@ -0,0 +1,304 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/smp.h>
+#include "interface.h"
+#include "cpu_pmu.h"
+#include "v8_pmu_name.h"
+
+/*
+ * Per-CPU PMCR: config reg
+ */
+#define ARMV8_PMCR_E		(1 << 0)	/* Enable all counters */
+#define ARMV8_PMCR_P		(1 << 1)	/* Reset all counters */
+#define ARMV8_PMCR_C		(1 << 2)	/* Cycle counter reset */
+#define ARMV8_PMCR_D		(1 << 3)	/* CCNT counts every 64th cpu cycle */
+#define ARMV8_PMCR_X		(1 << 4)	/* Export to ETM */
+#define ARMV8_PMCR_DP		(1 << 5)	/* Disable CCNT if non-invasive debug */
+#define	ARMV8_PMCR_N_SHIFT	11	/* Number of counters supported */
+#define	ARMV8_PMCR_N_MASK	0x1f
+#define	ARMV8_PMCR_MASK		0x3f	/* Mask for writable bits */
+
+/*
+ * PMOVSR: counters overflow flag status reg
+ */
+#define	ARMV8_OVSR_MASK		0xffffffff	/* Mask for writable bits */
+#define	ARMV8_OVERFLOWED_MASK	ARMV8_OVSR_MASK
+
+
+enum ARM_TYPE {
+	CORTEX_A53 = 0xD03,
+	CORTEX_A35 = 0xD04,
+	CORTEX_A57 = 0xD07,
+	CORTEX_A72 = 0xD08,
+	CORTEX_A73 = 0xD09,
+	CHIP_UNKNOWN = 0xFFF
+};
+
+struct chip_pmu {
+	enum ARM_TYPE type;
+	struct pmu_desc *desc;
+	unsigned int count;
+	const char *cpu_name;
+};
+
+static struct chip_pmu chips[] = {
+	{CORTEX_A53, a53_pmu_desc, A53_PMU_DESC_COUNT, "Cortex-A7L"},
+	{CORTEX_A35, a53_pmu_desc, A53_PMU_DESC_COUNT, "Cortex-A35"},
+	{CORTEX_A57, a53_pmu_desc, A53_PMU_DESC_COUNT, "Cortex-A57"},
+	{CORTEX_A72, a53_pmu_desc, A53_PMU_DESC_COUNT, "Cortex-A72"},
+	{CORTEX_A73, a53_pmu_desc, A53_PMU_DESC_COUNT, "Cortex-A73"},
+};
+static struct chip_pmu chip_unknown = { CHIP_UNKNOWN, NULL, 0, "Unknown CPU" };
+
+#define CHIP_PMU_COUNT (sizeof(chips) / sizeof(struct chip_pmu))
+
+static struct chip_pmu *chip;
+
+static enum ARM_TYPE armv8_get_ic(void)
+{
+	unsigned int value;
+	/* Read Main ID Register */
+	asm("mrs %0, midr_el1":"=r"(value));
+
+	value = (value & 0xffff) >> 4;	/* primary part number */
+	return value;
+}
+
+static inline void armv8_pmu_counter_select(unsigned int idx)
+{
+	asm volatile ("msr pmselr_el0, %0"::"r" (idx));
+	isb();
+}
+
+static inline void armv8_pmu_type_select(unsigned int idx, unsigned int type)
+{
+	armv8_pmu_counter_select(idx);
+	asm volatile ("msr pmxevtyper_el0, %0"::"r" (type));
+}
+
+static inline unsigned int armv8_pmu_read_count(unsigned int idx)
+{
+	unsigned int value;
+
+	if (idx == 31) {
+		asm volatile ("mrs %0, pmccntr_el0":"=r" (value));
+	} else {
+		armv8_pmu_counter_select(idx);
+		asm volatile ("mrs %0, pmxevcntr_el0":"=r" (value));
+	}
+	return value;
+}
+
+static inline void armv8_pmu_write_count(int idx, u32 value)
+{
+	if (idx == 31) {
+		asm volatile ("msr pmccntr_el0, %0"::"r" (value));
+	} else {
+		armv8_pmu_counter_select(idx);
+		asm volatile ("msr pmxevcntr_el0, %0"::"r" (value));
+	}
+}
+
+static inline void armv8_pmu_enable_count(unsigned int idx)
+{
+	asm volatile ("msr pmcntenset_el0, %0"::"r" (1 << idx));
+}
+
+static inline void armv8_pmu_disable_count(unsigned int idx)
+{
+	asm volatile ("msr pmcntenclr_el0, %0"::"r" (1 << idx));
+}
+
+static inline void armv8_pmu_enable_intr(unsigned int idx)
+{
+	asm volatile ("msr pmintenset_el1, %0"::"r" (1 << idx));
+}
+
+static inline void armv8_pmu_disable_intr(unsigned int idx)
+{
+	asm volatile ("msr pmintenclr_el1, %0"::"r" (1 << idx));
+	isb();
+	asm volatile ("msr pmovsclr_el0, %0"::"r" (1 << idx));
+	isb();
+}
+
+static inline unsigned int armv8_pmu_overflow(void)
+{
+	unsigned int val;
+
+	asm volatile ("mrs %0, pmovsclr_el0":"=r" (val));	/* read */
+	val &= ARMV8_OVSR_MASK;
+	asm volatile ("mrs %0, pmovsclr_el0"::"r" (val));
+	return val;
+}
+
+static inline unsigned int armv8_pmu_control_read(void)
+{
+	unsigned int val;
+
+	asm volatile ("mrs %0, pmcr_el0":"=r" (val));
+	return val;
+}
+
+static inline void armv8_pmu_control_write(u32 val)
+{
+	val &= ARMV8_PMCR_MASK;
+	isb();
+	asm volatile ("msr pmcr_el0, %0"::"r" (val));
+}
+
+static int armv8_pmu_hw_get_counters(void)
+{
+	int count = armv8_pmu_control_read();
+	/* N, bits[15:11] */
+	count = ((count >> ARMV8_PMCR_N_SHIFT) & ARMV8_PMCR_N_MASK);
+	return count;
+}
+
+static void armv8_pmu_hw_reset_all(int generic_counters)
+{
+	int i;
+
+	armv8_pmu_control_write(ARMV8_PMCR_C | ARMV8_PMCR_P);
+	/* generic counter */
+	for (i = 0; i < generic_counters; i++) {
+		armv8_pmu_disable_intr(i);
+		armv8_pmu_disable_count(i);
+	}
+	/* cycle counter */
+	armv8_pmu_disable_intr(31);
+	armv8_pmu_disable_count(31);
+	armv8_pmu_overflow();	/* clear overflow */
+}
+
+static int armv8_pmu_hw_get_event_desc(int i, int event, char *event_desc)
+{
+	if (event_desc == NULL)
+		return -1;
+
+	for (i = 0; i < chip->count; i++) {
+		if (chip->desc[i].event == event) {
+			strncpy(event_desc, chip->desc[i].name, MXSIZE_PMU_DESC - 1);
+			break;
+		}
+	}
+	if (i == chip->count)
+		return -1;
+
+	return 0;
+}
+
+static int armv8_pmu_hw_check_event(struct met_pmu *pmu, int idx, int event)
+{
+	int i;
+
+	/* Check if event is duplicate */
+	for (i = 0; i < idx; i++) {
+		if (pmu[i].event == event)
+			break;
+	}
+	if (i < idx) {
+		/* pr_debug("++++++ found duplicate event 0x%02x i=%d\n", event, i); */
+		return -1;
+	}
+
+	for (i = 0; i < chip->count; i++) {
+		if (chip->desc[i].event == event)
+			break;
+	}
+
+	if (i == chip->count)
+		return -1;
+
+	return 0;
+}
+
+static void armv8_pmu_hw_start(struct met_pmu *pmu, int count)
+{
+	int i;
+	int generic = count - 1;
+
+	armv8_pmu_hw_reset_all(generic);
+	for (i = 0; i < generic; i++) {
+		if (pmu[i].mode == MODE_POLLING) {
+			armv8_pmu_type_select(i, pmu[i].event);
+			armv8_pmu_enable_count(i);
+		}
+	}
+	if (pmu[count - 1].mode == MODE_POLLING) {	/* cycle counter */
+		armv8_pmu_enable_count(31);
+	}
+	armv8_pmu_control_write(ARMV8_PMCR_E);
+}
+
+static void armv8_pmu_hw_stop(int count)
+{
+	int generic = count - 1;
+
+	armv8_pmu_hw_reset_all(generic);
+}
+
+static unsigned int armv8_pmu_hw_polling(struct met_pmu *pmu, int count, unsigned int *pmu_value)
+{
+	int i, cnt = 0;
+	int generic = count - 1;
+
+	for (i = 0; i < generic; i++) {
+		if (pmu[i].mode == MODE_POLLING) {
+			pmu_value[cnt] = armv8_pmu_read_count(i);
+			cnt++;
+		}
+	}
+	if (pmu[count - 1].mode == MODE_POLLING) {
+		pmu_value[cnt] = armv8_pmu_read_count(31);
+		cnt++;
+	}
+	armv8_pmu_control_write(ARMV8_PMCR_C | ARMV8_PMCR_P | ARMV8_PMCR_E);
+
+	return cnt;
+}
+
+
+struct cpu_pmu_hw armv8_pmu = {
+	.name = "armv8_pmu",
+	.get_event_desc = armv8_pmu_hw_get_event_desc,
+	.check_event = armv8_pmu_hw_check_event,
+	.start = armv8_pmu_hw_start,
+	.stop = armv8_pmu_hw_stop,
+	.polling = armv8_pmu_hw_polling,
+};
+
+struct cpu_pmu_hw *cpu_pmu_hw_init(void)
+{
+	int i;
+	enum ARM_TYPE type;
+
+	type = armv8_get_ic();
+	PR_BOOTMSG("CPU TYPE - v8: %x\n", (unsigned int)type);
+	for (i = 0; i < CHIP_PMU_COUNT; i++) {
+		if (chips[i].type == type) {
+			chip = &(chips[i]);
+			break;
+		}
+	}
+	if (i == CHIP_PMU_COUNT) {
+		chip = &chip_unknown;
+		return NULL;
+	}
+
+	armv8_pmu.nr_cnt = armv8_pmu_hw_get_counters() + 1;
+	armv8_pmu.cpu_name = chip->cpu_name;
+
+	return &armv8_pmu;
+}
diff --git a/src/devtools/met-driver/met_drv/common/v8_pmu_hw_v2.c b/src/devtools/met-driver/met_drv/common/v8_pmu_hw_v2.c
new file mode 100644
index 0000000..25a4ed1
--- /dev/null
+++ b/src/devtools/met-driver/met_drv/common/v8_pmu_hw_v2.c
@@ -0,0 +1,497 @@
+/*

+ * Copyright (C) 2018 MediaTek Inc.

+ *

+ * 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.

+ *

+ * This program is distributed in the hope that it will be useful,

+ * but WITHOUT ANY WARRANTY; without even the implied warranty of

+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the

+ * GNU General Public License for more details.

+ */

+

+#include <linux/smp.h> /* on_each_cpu */

+#include <linux/cpumask.h> /* for_each_possible_cpu(cpu) */

+

+#include "interface.h"

+#include "cpu_pmu_v2.h"

+#include "v8_pmu_hw_v2.h"

+#include "met_kernel_symbol.h"

+

+

+/*******************************************************************************

+*				Type Define

+*******************************************************************************/

+/*

+ * Per-CPU PMCR: config reg

+ */

+#define ARMV8_PMCR_E		(1 << 0)	/* Enable all counters */

+#define ARMV8_PMCR_P		(1 << 1)	/* Reset all counters */

+#define ARMV8_PMCR_C		(1 << 2)	/* Cycle counter reset */

+#define ARMV8_PMCR_D		(1 << 3)	/* CCNT counts every 64th cpu cycle */

+#define ARMV8_PMCR_X		(1 << 4)	/* Export to ETM */

+#define ARMV8_PMCR_DP		(1 << 5)	/* Disable CCNT if non-invasive debug */

+#define	ARMV8_PMCR_N_SHIFT	11		/* Number of counters supported */

+#define	ARMV8_PMCR_N_MASK	0x1f

+#define	ARMV8_PMCR_MASK		0x3f		/* Mask for writable bits */

+

+/*

+ * PMOVSR: counters overflow flag status reg

+ */

+#define	ARMV8_OVSR_MASK		0xffffffff	/* Mask for writable bits */

+#define	ARMV8_OVERFLOWED_MASK	ARMV8_OVSR_MASK

+

+

+/*******************************************************************************

+*				Fuction Pototypes

+*******************************************************************************/

+static int armv8_pmu_hw_get_event_desc(int event, char *event_desc);

+static int armv8_pmu_hw_check_event(struct met_pmu_v2 *pmu, int idx, int event);

+static void armv8_pmu_hw_start(struct met_pmu_v2 *pmu, int count);

+static void armv8_pmu_hw_stop(int count);

+static unsigned int armv8_pmu_hw_polling(struct met_pmu_v2 *pmu, int count, unsigned int *pmu_value);

+

+

+/*******************************************************************************

+*				Globe Variables

+*******************************************************************************/

+struct pmu_desc_v2 a53_pmu_desc_v2[] = {

+	{0x00, "SW_INCR"},

+	{0x01, "L1I_CACHE_REFILL"},

+	{0x02, "L1I_TLB_REFILL"},

+	{0x03, "L1D_CACHE_REFILL"},

+	{0x04, "L1D_CACHE"},

+	{0x05, "L1D_TLB_REFILL"},

+	{0x06, "LD_RETIRED"},

+	{0x07, "ST_RETIRED"},

+	{0x08, "INST_RETIRED"},

+	{0x09, "EXC_TAKEN"},

+	{0x0A, "EXC_RETURN"},

+	{0x0B, "CID_WRITE_RETIRED"},

+	{0x0C, "PC_WRITE_RETIRED"},

+	{0x0D, "BR_IMMED_RETIRED"},

+	{0x0E, "BR_RETURN_RETIRED"},

+	{0x0F, "UNALIGNED_LDST_RETIRED"},

+

+	{0x10, "BR_MIS_PRED"},

+	{0x11, "CPU_CYCLES"},

+	{0x12, "BR_PRED"},

+	{0x13, "MEM_ACCESS"},

+	{0x14, "L1I_CACHE"},

+	{0x15, "L1D_CACHE_WB"},

+	{0x16, "L2D_CACHE"},

+	{0x17, "L2D_CACHE_REFILL"},

+	{0x18, "L2D_CACHE_WB"},

+	{0x19, "BUS_ACCESS"},

+	{0x1A, "MEMORY_ERROR"},

+	{0x1D, "BUS_CYCLES"},

+	{0x1E, "CHAIN"},

+

+	{0x60, "BUS_READ_ACCESS"},

+	{0x61, "BUS_WRITE_ACCESS"},

+

+	{0x7A, "BR_INDIRECT_SPEC"},

+

+	{0x86, "IRQ_EXC_TAKEN"},

+	{0x87, "FIQ_EXC_TAKEN"},

+

+	{0xC0, "EXT_MEM_REQ"},

+	{0xC1, "NO_CACHE_EXT_MEM_REQ"},

+	{0xC2, "PREFETCH_LINEFILL"},

+	{0xC4, "ENT_READ_ALLOC_MODE"},

+	{0xC5, "READ_ALLOC_MODE"},

+	{0xC6, "PRE_DECODE_ERROR"},

+	{0xC7, "WRITE_STALL"},

+	{0xC8, "SCU_SNOOP_DATA_FROM_ANOTHER_CPU"},

+	{0xC9, "CONDITIONAL_BRANCH_EXE"},

+	{0xCA, "INDIRECT_BRANCH_MISPREDICT"},

+	{0xCB, "INDIRECT_BRANCH_MISPREDICT_ADDR"},/*"INDIRECT_BRANCH_MISPREDICT_ADDR_MISSCOMPARE" */

+	{0xCC, "COND_BRANCH_MISPREDICT"},

+

+	{0xD0, "L1_INST_CACHE_MEM_ERR"},

+

+	{0xE1, "ICACHE_MISS_STALL"},

+	{0xE2, "DPU_IQ_EMPTY"},

+	{0xE4, "NOT_FPU_NEON_INTERLOCK"},

+	{0xE5, "LOAD_STORE_INTERLOCK"},

+	{0xE6, "FPU_NEON_INTERLOCK"},

+	{0xE7, "LOAD_MISS_STALL"},

+	{0xE8, "STORE_STALL"},

+	{0xFF, "CPU_CYCLES"}

+};

+#define A53_PMU_DESC_COUNT (sizeof(a53_pmu_desc_v2) / sizeof(struct pmu_desc_v2))

+

+/* Cortex-A73 */

+struct pmu_desc_v2 a73_pmu_desc_v2[] = {

+	{0x00, "SW_INCR"},

+	{0x01, "L1I_CACHE_REFILL"},

+	{0x02, "L1I_TLB_REFILL"},

+	{0x03, "L1D_CACHE_REFILL"},

+	{0x04, "L1D_CACHE"},

+	{0x05, "L1D_TLB_REFILL"},

+	{0x08, "INST_RETIRED"},

+	{0x09, "EXC_TAKEN"},

+	{0x0A, "EXC_RETURN"},

+	{0x0B, "CID_WRITE_RETIRED"},

+	{0x0C, "PC_WRITE_RETIRED"},

+	{0x0D, "BR_IMMED_RETIRED"},

+	{0x0E, "BR_RETURN_RETIRED"},

+

+	{0x10, "BR_MIS_PRED"},

+	{0x11, "CPU_CYCLES"},

+	{0x12, "BR_PRED"},

+	{0x13, "MEM_ACCESS"},

+	{0x14, "L1I_CACHE"},

+	{0x15, "L1D_CACHE_WB"},

+	{0x16, "L2D_CACHE"},

+	{0x17, "L2D_CACHE_REFILL"},

+	{0x18, "L2D_CACHE_WB"},

+	{0x19, "BUS_ACCESS"},

+	{0x1B, "INT_SPEC"},

+	{0x1C, "TTBR_WRITE_RETIRED"},

+	{0x1D, "BUS_CYCLES"},

+	{0x1E, "CHAIN"},

+

+	{0x40, "L1D_CACHE_RD"},

+	{0x41, "L1D_CACHE_WR"},

+

+	{0x50, "L2D_CACHE_RD"},

+	{0x51, "L2D_CACHE_WR"},

+	{0x56, "L2D_CACHE_WB_VICTIM"},

+	{0x57, "L2D_CACHE_WB_CLEAN"},

+	{0x58, "L2D_CACHE_INVAL"},

+

+	{0x62, "BUS_ACCESS_SHARED"},

+	{0x63, "BUS_ACCESS_NOT_SHARED"},

+	{0x64, "BUS_ACCESS_NORMAL"},

+	{0x65, "BUS_ACCESS_SO_DIV"},

+	{0x66, "MEM_ACCESS_RD"},

+	{0x67, "MEM_ACCESS_WR"},

+	{0x6A, "UNALIGNED_LDST_SPEC"},

+	{0x6C, "LDREX_SPEC"},

+	{0x6E, "STREC_FAIL_SPEC"},

+

+	{0x70, "LD_SPEC"},

+	{0x71, "ST_SPEC"},

+	{0x72, "LDST_SPEC"},

+	{0x73, "DP_SPEC"},

+	{0x74, "ASE_SPEC"},

+	{0x75, "VFP_SPEC"},

+	{0x77, "CRYPTO_SPEC"},

+	{0x7A, "BR_INDIRECT_SPEC"},

+	{0x7C, "ISB_SPEC"},

+	{0x7D, "DSB_SPEC"},

+	{0x7E, "DMB_SPEC"},

+

+	{0x8A, "EXC_HVC"},

+

+	{0xC0, "LF_STALL"},

+	{0xC1, "PTW_STALL"},

+	{0xC2, "I_TAG_RAM_RD"},

+	{0xC3, "I_DATA_RAM_RD"},

+	{0xC4, "I_BTAC_RAM_RD"},

+

+	{0xD3, "D_LSU_SLOT_FULL"},

+	{0xD8, "LS_IQ_FULL"},

+	{0xD9, "DP_IQ_FULL"},

+	{0xDA, "DE_IQ_FULL"},

+	{0xDC, "EXC_TRAP_HYP"},

+	{0xDE, "ETM_EXT_OUT0"},

+	{0xDF, "ETM_EXT_OUT1"},

+

+	{0xE0, "MMU_PTW"},

+	{0xE1, "MMU_PTW_ST1"},

+	{0xE2, "MMU_PTW_ST2"},

+	{0xE3, "MMU_PTW_LSU"},

+	{0xE4, "MMU_PTW_ISIDE"},

+	{0xE5, "MMU_PTW_PLD"},

+	{0xE6, "MMU_PTW_CP15"},

+	{0xE7, "PLD_UTLB_REFILL"},

+	{0xE8, "CP15_UTLB_REFILL"},

+	{0xE9, "UTLB_FLUSH"},

+	{0xEA, "TLB_ACESS"},

+	{0xEB, "TLB_MISS"},

+	{0xEC, "DCACHE_SELF_HIT_VIPT"},

+        {0xEE, "CYCLES_L2_IDLE"},

+        {0xEF, "CPU_DECODE_UNIT_STALLED"},

+	{0xFF, "CPU_CYCLES"}

+};

+#define A73_PMU_DESC_COUNT (sizeof(a73_pmu_desc_v2) / sizeof(struct pmu_desc_v2))

+

+

+static struct chip_pmu_v2 *gChip[MXNR_CPU_V2];

+static struct chip_pmu_v2 chips[] = {

+	{CORTEX_A53, a53_pmu_desc_v2, A53_PMU_DESC_COUNT, 0, "Cortex-A53"},

+	{CORTEX_A35, a53_pmu_desc_v2, A53_PMU_DESC_COUNT, 0, "Cortex-A35"},

+	{CORTEX_A57, a53_pmu_desc_v2, A53_PMU_DESC_COUNT, 0, "Cortex-A57"},

+	{CORTEX_A72, a53_pmu_desc_v2, A53_PMU_DESC_COUNT, 0, "Cortex-A72"},

+	{CORTEX_A73, a73_pmu_desc_v2, A73_PMU_DESC_COUNT, 0, "Cortex-A73"},

+};

+static struct chip_pmu_v2 chip_unknown = { CHIP_UNKNOWN, NULL, 0, 0, "Unknown CPU" };

+#define CHIP_PMU_COUNT (sizeof(chips) / sizeof(struct chip_pmu_v2))

+

+struct cpu_pmu_hw_v2 armv8_pmu_v2 = {

+	.name = "armv8_pmu",

+	.get_event_desc = armv8_pmu_hw_get_event_desc,

+	.check_event = armv8_pmu_hw_check_event,

+	.start = armv8_pmu_hw_start,

+	.stop = armv8_pmu_hw_stop,

+	.polling = armv8_pmu_hw_polling,

+};

+

+

+/*******************************************************************************

+*				Iplement Start

+*******************************************************************************/

+static struct chip_pmu_v2 *get_chip_pmu_by_cpu_id(int cpu)

+{

+	return gChip[cpu];

+}

+

+static void set_chip_pmu_by_cpu_id(int cpu, struct chip_pmu_v2 *chip)

+{

+	gChip[cpu] = chip;

+}

+

+static inline void armv8_pmu_counter_select(unsigned int idx)

+{

+	asm volatile ("msr pmselr_el0, %0"::"r" (idx));

+	isb();

+}

+

+static inline void armv8_pmu_type_select(unsigned int idx, unsigned int type)

+{

+	armv8_pmu_counter_select(idx);

+	asm volatile ("msr pmxevtyper_el0, %0"::"r" (type));

+}

+

+static inline unsigned int armv8_pmu_read_count(unsigned int idx)

+{

+	unsigned int value;

+

+	if (idx == 31) {

+		asm volatile ("mrs %0, pmccntr_el0":"=r" (value));

+	} else {

+		armv8_pmu_counter_select(idx);

+		asm volatile ("mrs %0, pmxevcntr_el0":"=r" (value));

+	}

+	return value;

+}

+

+static inline void armv8_pmu_write_count(int idx, u32 value)

+{

+	if (idx == 31) {

+		asm volatile ("msr pmccntr_el0, %0"::"r" (value));

+	} else {

+		armv8_pmu_counter_select(idx);

+		asm volatile ("msr pmxevcntr_el0, %0"::"r" (value));

+	}

+}

+

+static inline void armv8_pmu_enable_count(unsigned int idx)

+{

+	asm volatile ("msr pmcntenset_el0, %0"::"r" (1 << idx));

+}

+

+static inline void armv8_pmu_disable_count(unsigned int idx)

+{

+	asm volatile ("msr pmcntenclr_el0, %0"::"r" (1 << idx));

+}

+

+static inline void armv8_pmu_enable_intr(unsigned int idx)

+{

+	asm volatile ("msr pmintenset_el1, %0"::"r" (1 << idx));

+}

+

+static inline void armv8_pmu_disable_intr(unsigned int idx)

+{

+	asm volatile ("msr pmintenclr_el1, %0"::"r" (1 << idx));

+	isb();

+	asm volatile ("msr pmovsclr_el0, %0"::"r" (1 << idx));

+	isb();

+}

+

+static inline unsigned int armv8_pmu_overflow(void)

+{

+	unsigned int val;

+

+	asm volatile ("mrs %0, pmovsclr_el0":"=r" (val));	/* read */

+	val &= ARMV8_OVSR_MASK;

+	asm volatile ("mrs %0, pmovsclr_el0"::"r" (val));

+	return val;

+}

+

+static inline unsigned int armv8_pmu_control_read(void)

+{

+	unsigned int val;

+

+	asm volatile ("mrs %0, pmcr_el0":"=r" (val));

+	return val;

+}

+

+static inline void armv8_pmu_control_write(u32 val)

+{

+	val &= ARMV8_PMCR_MASK;

+	isb();

+	asm volatile ("msr pmcr_el0, %0"::"r" (val));

+}

+

+static int armv8_pmu_hw_get_counters(void)

+{

+	int count = armv8_pmu_control_read();

+	/* N, bits[15:11] */

+	count = ((count >> ARMV8_PMCR_N_SHIFT) & ARMV8_PMCR_N_MASK);

+	return count;

+}

+

+static void armv8_pmu_hw_reset_all(int generic_counters)

+{

+	int i;

+

+	armv8_pmu_control_write(ARMV8_PMCR_C | ARMV8_PMCR_P);

+	/* generic counter */

+	for (i = 0; i < generic_counters; i++) {

+		armv8_pmu_disable_intr(i);

+		armv8_pmu_disable_count(i);

+	}

+	/* cycle counter */

+	armv8_pmu_disable_intr(31);

+	armv8_pmu_disable_count(31);

+	armv8_pmu_overflow();	/* clear overflow */

+}

+

+static int armv8_pmu_hw_get_event_desc(int event, char *event_desc)

+{

+	int i;

+	int this_cpu;

+	struct chip_pmu_v2 *chip_pmu;

+

+	this_cpu = smp_processor_id();

+	chip_pmu = get_chip_pmu_by_cpu_id(this_cpu);

+	if (event_desc == NULL)

+		return -1;

+

+	for (i = 0; i < chip_pmu->pmu_count; i++) {

+		if (chip_pmu->desc[i].event == event) {

+			strncpy(event_desc, chip_pmu->desc[i].name, MXSIZE_PMU_DESC - 1);

+			break;

+		}

+	}

+	if (i == chip_pmu->pmu_count)

+		return -1;

+

+	return 0;

+}

+

+static int armv8_pmu_hw_check_event(struct met_pmu_v2 *pmu, int idx, int event)

+{

+	int this_cpu;

+	struct chip_pmu_v2 *chip_pmu;

+	int i;

+

+	this_cpu = smp_processor_id();

+	chip_pmu = get_chip_pmu_by_cpu_id(this_cpu);

+	for (i = 0; i < chip_pmu->pmu_count; i++) {

+		if (chip_pmu->desc[i].event == event)

+			break;

+	}

+

+	if (i == chip_pmu->pmu_count) {                

+                PR_BOOTMSG("%s:%d => i=%d, pmu_count=%d\n", __FUNCTION__, __LINE__, i, chip_pmu->pmu_count);

+                return -1;

+        }

+

+	return 0;

+}

+

+static void armv8_pmu_hw_start(struct met_pmu_v2 *pmu, int count)

+{

+	int i;

+	int generic = count - 1;

+

+	armv8_pmu_hw_reset_all(generic);

+	for (i = 0; i < generic; i++) {

+		if (pmu[i].mode == MODE_POLLING) {

+			armv8_pmu_type_select(i, pmu[i].event);

+			armv8_pmu_enable_count(i);

+		}

+	}

+	if (pmu[count - 1].mode == MODE_POLLING)

+		armv8_pmu_enable_count(31);

+

+	armv8_pmu_control_write(ARMV8_PMCR_E);

+}

+

+static void armv8_pmu_hw_stop(int count)

+{

+	int generic = count - 1;

+

+	armv8_pmu_hw_reset_all(generic);

+}

+

+static unsigned int armv8_pmu_hw_polling(struct met_pmu_v2 *pmu, int count, unsigned int *pmu_value)

+{

+	int i, cnt = 0;

+	int generic = count - 1;

+

+	for (i = 0; i < generic; i++) {

+		if (pmu[i].mode == MODE_POLLING) {

+			pmu_value[cnt] = armv8_pmu_read_count(i);

+			cnt++;

+		}

+	}

+	if (pmu[count - 1].mode == MODE_POLLING) {

+		pmu_value[cnt] = armv8_pmu_read_count(31);

+		cnt++;

+	}

+	armv8_pmu_control_write(ARMV8_PMCR_C | ARMV8_PMCR_P | ARMV8_PMCR_E);

+

+	return cnt;

+}

+

+static void armv8_get_ic(void *info)

+{

+	unsigned int value;

+	unsigned int *type = (unsigned int *)info;

+

+	/* Read Main ID Register */

+	asm("mrs %0, midr_el1":"=r"(value));

+	*type = (value & 0xffff) >> 4;	/* primary part number */

+}

+

+struct cpu_pmu_hw_v2 *cpu_pmu_hw_init_v2(void)

+{

+	enum ARM_TYPE_v2 type;

+	struct chip_pmu_v2 *chip;

+	int this_cpu = smp_processor_id();

+	int cpu;

+	int i;

+

+	for_each_possible_cpu(cpu) {

+		if (cpu == this_cpu)

+			armv8_get_ic(&type);

+		else

+			met_smp_call_function_single_symbol(cpu, armv8_get_ic, &type, 1);

+

+		PR_BOOTMSG("CPU TYPE - v8: %x\n", (unsigned int)type);

+		for (i = 0; i < CHIP_PMU_COUNT; i++) {

+			if (chips[i].type == type) {

+				chip = &(chips[i]);

+				chip->hw_count = armv8_pmu_hw_get_counters() + 1;

+				set_chip_pmu_by_cpu_id(cpu, chip);

+				armv8_pmu_v2.chip_pmu[cpu] = chip;

+				if (chip->hw_count >= armv8_pmu_v2.max_hw_count)

+					armv8_pmu_v2.max_hw_count = chip->hw_count;

+				break;

+			}

+		}

+		if (i == CHIP_PMU_COUNT) {

+			set_chip_pmu_by_cpu_id(cpu, &chip_unknown);

+			return NULL;

+		}

+	}

+

+	return &armv8_pmu_v2;

+}

+

diff --git a/src/devtools/met-driver/met_drv/common/v8_pmu_hw_v2.h b/src/devtools/met-driver/met_drv/common/v8_pmu_hw_v2.h
new file mode 100644
index 0000000..4666d0c
--- /dev/null
+++ b/src/devtools/met-driver/met_drv/common/v8_pmu_hw_v2.h
@@ -0,0 +1,24 @@
+/*

+ * Copyright (C) 2018 MediaTek Inc.

+ *

+ * 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.

+ *

+ * This program is distributed in the hope that it will be useful,

+ * but WITHOUT ANY WARRANTY; without even the implied warranty of

+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the

+ * GNU General Public License for more details.

+ */

+

+#ifndef _V8_PMU_V2_NAME_H_

+#define _V8_PMU_V2_NAME_H_

+

+#include "cpu_pmu_v2.h"

+

+

+/*******************************************************************************

+*                                Fuction Pototypes

+*******************************************************************************/

+struct cpu_pmu_hw_v2 *cpu_pmu_hw_init_v2(void);

+#endif				/* _V8_PMU_V2_NAME_H_ */

diff --git a/src/devtools/met-driver/met_drv/common/v8_pmu_name.h b/src/devtools/met-driver/met_drv/common/v8_pmu_name.h
new file mode 100644
index 0000000..d3bf3b1
--- /dev/null
+++ b/src/devtools/met-driver/met_drv/common/v8_pmu_name.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+
+#ifndef _V8_PMU_NAME_H_
+#define _V8_PMU_NAME_H_
+
+/* Cortex-A53 */
+/* CA53 & CA73 intersection event list */
+struct pmu_desc a53_pmu_desc[] = {
+	{0x00, "SW_INCR"},
+	{0x01, "L1I_CACHE_REFILL"},
+	{0x02, "L1I_TLB_REFILL"},
+	{0x03, "L1D_CACHE_REFILL"},
+	{0x04, "L1D_CACHE"},
+	{0x05, "L1D_TLB_REFILL"},
+/*	{0x06, "LD_RETIRED"}, */
+/*	{0x07, "ST_RETIRED"}, */
+	{0x08, "INST_RETIRED"},
+	{0x09, "EXC_TAKEN"},
+	{0x0A, "EXC_RETURN"},
+	{0x0B, "CID_WRITE_RETIRED"},
+/*	{0x0C, "PC_WRITE_RETIRED"}, */
+/*	{0x0D, "BR_IMMED_RETIRED"}, */
+/*	{0x0E, "BR_RETURN_RETIRED"}, */
+/*	{0x0F, "UNALIGNED_LDST_RETIRED"}, */
+	{0x10, "BR_MIS_PRED"},
+	{0x11, "CPU_CYCLES"},
+	{0x12, "BR_PRED"},
+	{0x13, "MEM_ACCESS"},
+	{0x14, "L1I_CACHE"},
+	{0x15, "L1D_CACHE_WB"},
+	{0x16, "L2D_CACHE"},
+	{0x17, "L2D_CACHE_REFILL"},
+	{0x18, "L2D_CACHE_WB"},
+	{0x19, "BUS_ACCESS"},
+	{0x1A, "MEMORY_ERROR"},
+	{0x1D, "BUS_CYCLES"},
+	{0x60, "BUS_READ_ACCESS"},
+	{0x61, "BUS_WRITE_ACCESS"},
+	{0x86, "IRQ_EXC_TAKEN"},
+	{0x87, "FIQ_EXC_TAKEN"},
+/*	{0xC0, "EXT_MEM_REQ"}, */
+/*	{0xC1, "NO_CACHE_EXT_MEM_REQ"}, */
+/*	{0xC2, "PREFETCH_LINEFILL"}, */
+/*	{0xC4, "ENT_READ_ALLOC_MODE"}, */
+/*	{0xC5, "READ_ALLOC_MODE"}, */
+/*	{0xC6, "PRE_DECODE_ERROR"}, */
+/*	{0xC7, "WRITE_STALL"}, */
+/*	{0xC8, "SCU_SNOOP_DATA_FROM_ANOTHER_CPU"}, */
+/*	{0xC9, "CONDITIONAL_BRANCH_EXE"}, */
+/*	{0xCA, "INDIRECT_BRANCH_MISPREDICT"}, */
+/*	{0xCB, "INDIRECT_BRANCH_MISPREDICT_ADDR"},"INDIRECT_BRANCH_MISPREDICT_ADDR_MISSCOMPARE" */
+/*	{0xCC, "COND_BRANCH_MISPREDICT"}, */
+/*	{0xD0, "L1_INST_CACHE_MEM_ERR"}, */
+/*	{0xE1, "ICACHE_MISS_STALL"}, */
+/*	{0xE2, "DPU_IQ_EMPTY"}, */
+/*	{0xE4, "NOT_FPU_NEON_INTERLOCK"}, */
+/*	{0xE5, "LOAD_STORE_INTERLOCK"}, */
+/*	{0xE6, "FPU_NEON_INTERLOCK"}, */
+/*	{0xE7, "LOAD_MISS_STALL"}, */
+/*	{0xE8, "STORE_STALL"}, */
+	{0xFF, "CPU_CYCLES"}
+};
+
+#define A53_PMU_DESC_COUNT (sizeof(a53_pmu_desc) / sizeof(struct pmu_desc))
+
+#endif				/* _V8_PMU_NAME_H_ */
diff --git a/src/devtools/met-driver/met_drv/common/version.h b/src/devtools/met-driver/met_drv/common/version.h
new file mode 100644
index 0000000..7bb6c77
--- /dev/null
+++ b/src/devtools/met-driver/met_drv/common/version.h
@@ -0,0 +1,14 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define MET_BACKEND_VERSION "6.1.0"
diff --git a/src/devtools/met-driver/met_drv/default/Kbuild b/src/devtools/met-driver/met_drv/default/Kbuild
new file mode 100644
index 0000000..9b2bb87
--- /dev/null
+++ b/src/devtools/met-driver/met_drv/default/Kbuild
@@ -0,0 +1,6 @@
+obj-m := met.o
+
+ccflags-y += -I$(srctree)/include/
+
+met-y := default/met_main.o
+
diff --git a/src/devtools/met-driver/met_drv/default/met_main.c b/src/devtools/met-driver/met_drv/default/met_main.c
new file mode 100644
index 0000000..4a1ee2f
--- /dev/null
+++ b/src/devtools/met-driver/met_drv/default/met_main.c
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/hrtimer.h>
+#include <linux/cpu.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/profile.h>
+#include <linux/dcache.h>
+#include <linux/types.h>
+#include <linux/dcookies.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+
+static int __init met_drv_init(void)
+{
+	printk("Hello MET default module\n");
+	return 0;
+}
+
+static void __exit met_drv_exit(void)
+{
+}
+module_init(met_drv_init);
+module_exit(met_drv_exit);
+
+MODULE_AUTHOR("DT_DM5");
+MODULE_DESCRIPTION("MET_DEFAULT");
+MODULE_LICENSE("GPL");
diff --git a/src/devtools/mrdump/Android.mk b/src/devtools/mrdump/Android.mk
new file mode 100644
index 0000000..6c4dff5
--- /dev/null
+++ b/src/devtools/mrdump/Android.mk
@@ -0,0 +1,3 @@
+LOCAL_PATH := $(my-dir)
+
+include $(call first-makefiles-under,$(LOCAL_PATH))
diff --git a/src/devtools/mrdump/Makefile b/src/devtools/mrdump/Makefile
new file mode 100644
index 0000000..d6a0f39
--- /dev/null
+++ b/src/devtools/mrdump/Makefile
@@ -0,0 +1,20 @@
+BUILT_MODULE = mrdump_tool
+
+.PHONY: all install clean
+
+all:
+	$(warning "aee makefile all")
+	$(foreach m, $(BUILT_MODULE), make -f  Makefile.$(m);)
+
+clean:
+	$(warning "aee makefile clean")
+	$(foreach m, $(BUILT_MODULE), make -f Makefile.$(m) clean;)
+
+bindir ?= /usr/bin
+libdir ?= /usr/lib
+
+install:
+	#	install -d ${DESTDIR}${libdir}
+	install -d ${DESTDIR}${bindir}
+
+	install -m 0755 mrdump_tool ${DESTDIR}$(bindir)
diff --git a/src/devtools/mrdump/Makefile.mrdump_tool b/src/devtools/mrdump/Makefile.mrdump_tool
new file mode 100644
index 0000000..a384a85
--- /dev/null
+++ b/src/devtools/mrdump/Makefile.mrdump_tool
@@ -0,0 +1,45 @@
+TARGET := mrdump_tool
+
+FLAGS := -Werror \
+         -D__YOCTO_OS__ \
+         -D_GNU_SOURCE
+
+INCLUDES := -I.
+
+bindir ?= /usr/bin
+
+VPATH = mrdump_tool_source
+
+SRCS := mrdump_log.c \
+        mrdump_defaults.c \
+        mrdump_common.c \
+        mrdump_status.c \
+        mrdump_support_fiemap.c \
+        mrdump_support_ext4.c \
+        mrdump_support_f2fs.c \
+        mrdump_support_mpart.c \
+        mrdump_tool.c
+
+
+OBJS := ${SRCS:%.c=%.o}
+
+LDFLAGS = -L.-lz
+
+.PHONY: all
+all : $(TARGET)
+
+${TARGET}: ${OBJS}
+	${CC} ${OBJS} $(LDFLAGS) -o $@
+
+.PHONY: clean
+clean:
+	$(warning "makefile clean")
+	rm -rf $(OBJS) $(TARGET)
+
+%.o: %.c
+	${CC} $(CFLAGS) -c $< -o $@ $(INCLUDES) $(FLAGS)
+
+.PHONY: install
+install:
+	install -d ${DESTDIR}${bindir}
+	install -m 0755 $(TARGET) ${DESTDIR}${bindir}
diff --git a/src/devtools/mrdump/README b/src/devtools/mrdump/README
new file mode 100644
index 0000000..ca482a3
--- /dev/null
+++ b/src/devtools/mrdump/README
@@ -0,0 +1,7 @@
+# Preliminary version: still need to clean up.
+
+This module creates the MRDUMP feature related function with source release to the customer.
+
+- mrdump_tool: binary on phone which could help to do the mrdump tasks.
+
+- mrdump_host_cmd: executable file on windows, currently to get full ram dump thru usb dump.
diff --git a/src/devtools/mrdump/mrdump_tool_source/Android.mk b/src/devtools/mrdump/mrdump_tool_source/Android.mk
new file mode 100644
index 0000000..f0eec8b
--- /dev/null
+++ b/src/devtools/mrdump/mrdump_tool_source/Android.mk
@@ -0,0 +1,21 @@
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := \
+	mrdump_log.c \
+	mrdump_common.c \
+	mrdump_status.c \
+	mrdump_support_fiemap.c \
+	mrdump_support_ext4.c \
+	mrdump_support_f2fs.c \
+	mrdump_support_mpart.c \
+	mrdump_tool.c
+
+LOCAL_CFLAGS += -D__ANDROID__
+
+LOCAL_MODULE := mrdump_tool
+LOCAL_PROPRIETARY_MODULE := true
+LOCAL_MODULE_OWNER := mtk
+LOCAL_MODULE_TAGS := optional
+LOCAL_SHARED_LIBRARIES := libcutils libz liblog
+include $(MTK_EXECUTABLE)
diff --git a/src/devtools/mrdump/mrdump_tool_source/mrdump_common.c b/src/devtools/mrdump/mrdump_tool_source/mrdump_common.c
new file mode 100644
index 0000000..39c50af
--- /dev/null
+++ b/src/devtools/mrdump/mrdump_tool_source/mrdump_common.c
@@ -0,0 +1,205 @@
+/* uint64_t ...*/
+#include <inttypes.h>
+#include <stdbool.h>
+
+/* struct stat, open */
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+/* fsync(), close() */
+#include <unistd.h>
+
+/* strerror */
+#include <string.h>
+/* errno */
+#include <errno.h>
+
+/* statfs() and statvfs() */
+#include <sys/statfs.h>
+#include <sys/statvfs.h>
+#include <sys/vfs.h>
+
+/* ioctl, BLKGETSIZE64 */
+#include <linux/fs.h>
+#include <sys/ioctl.h>
+
+#ifdef __ANDROID__
+/* Property */
+#include <cutils/properties.h>
+#include <sys/system_properties.h>
+#elif defined(__YOCTO_OS__)
+#include <stdio.h>
+#endif
+
+/* mrdump related */
+#include "mrdump_log.h"
+#include "mrdump_common.h"
+#include "mrdump_support_ext4.h"
+#include "mrdump_support_f2fs.h"
+#include "mrdump_status_private.h"
+
+void mrdump_close(int fd)
+{
+    fsync(fd);
+    close(fd);
+}
+
+int mrdump_file_is_exist(const char *path)
+{
+    struct stat s;
+    if (!path)
+        return 0;
+    if (stat(path, &s) != 0)
+        return 0;
+    return 1;
+}
+
+int mrdump_get_data_os(void)
+{
+    if (mount_as_ext4(MRDUMP_EXT4_MOUNT_POINT))
+        return MRDUMP_DATA_FS_EXT4;
+
+    if (mount_as_f2fs(MRDUMP_EXT4_MOUNT_POINT))
+        return MRDUMP_DATA_FS_F2FS;
+
+    MD_LOGI("%s: unknown os\n", __func__);
+    return MRDUMP_DATA_FS_NONE;
+}
+
+uint64_t mrdump_get_partition_free_size(const char *mountp)
+{
+    struct statvfs vfs;
+    uint64_t psize;
+
+    if(statvfs(mountp, &vfs) == 0) {
+        psize = (uint64_t) vfs.f_frsize * (uint64_t) vfs.f_bfree;
+        MD_LOGD("%s: size of %s: %" PRId64 " bytes.\n", __func__, mountp, psize);
+        return psize;
+    }
+
+    MD_LOGE("%s: statvfs of %s got failed(%d), %s\n", __func__, mountp, errno, strerror(errno));
+    return 0;
+}
+
+uint64_t mrdump_get_partition_size(char *fullpath)
+{
+    int fd = open(fullpath, O_RDONLY);
+    if (0 > fd) {
+        MD_LOGE("%s: open fullpath failed. (%s)\n", __func__, fullpath);
+        return 0;
+    }
+
+    uint64_t psize;
+    int ret = ioctl(fd, BLKGETSIZE64, &psize);
+    if (0 > ret) {
+        MD_LOGE("%s: ioctl BLKGETSIZE64 failed. (%s)\n", __func__, fullpath);
+        mrdump_close(fd);
+        return 0;
+    }
+
+    mrdump_close(fd);
+
+    return psize;
+}
+
+static char *mrdump_get_device_node_from_fstab(const char *fstab, const char *mountp)
+{
+#ifdef __ANDROID_
+    int ret;
+    FILE *fp;
+    char c, myline[1024];
+    char *delim="\x09\x20";
+    char *DeviceNode, *MountPoint;
+
+    DeviceNode = NULL;
+    fp = fopen(fstab, "r");
+    if(fp == NULL)
+        return NULL;
+
+    while(!feof(fp)) {
+
+        // getline
+        ret = fscanf(fp, "%[^\n]", myline);
+
+        // strtok strings
+        if(ret > 0) {
+            if(myline[0] == '/') {
+                DeviceNode = strtok(myline, delim);
+                MountPoint = strtok(NULL, delim);
+                if(MountPoint != NULL) {
+                    if(!strcmp(MountPoint, mountp)) {
+                        fclose(fp);
+                        return strdup(DeviceNode);
+                    }
+                }
+            }
+        }
+
+        /* clear newline character */
+        ret = fscanf(fp, "%c", &c);
+        if (ret != 1) {
+            MD_LOGE("%s: not EOL.", __func__);
+            fclose(fp);
+            return NULL;
+        }
+    }
+    fclose(fp);
+    return NULL;
+#elif defined(__YOCTO_OS__)
+#endif
+}
+
+#ifdef __ANDROID_
+static const char *fstab_path_prefix[] = {
+    "/vendor/etc/fstab",
+    "/fstab",
+    NULL
+};
+#elif defined(__YOCTO_OS__)
+static const char expdb_dev[] = "/dev/disk/by-partlabel/expdb";
+static const char preallocate_dev[] = "/dev/disk/by-partlabel/log";
+#endif
+
+char *mrdump_get_device_node(const char *mountp)
+{
+#ifdef __ANDROID_
+    int i;
+    char *DeviceNode = NULL;
+    char fstab_filename[PROPERTY_VALUE_MAX];
+
+    /* get hardware parts */
+    char propbuf[PROPERTY_VALUE_MAX];
+    if(property_get("ro.hardware", propbuf, NULL) == 0)
+        property_get("ro.board.platform", propbuf, "");
+
+    for(i = 0; fstab_path_prefix[i] != NULL; i++) {
+        snprintf(fstab_filename, sizeof(fstab_filename), "%s.%s", fstab_path_prefix[i], propbuf);
+        if(mrdump_file_is_exist(fstab_filename))
+            DeviceNode =  mrdump_get_device_node_from_fstab(fstab_filename, mountp);
+        if(DeviceNode)
+            return DeviceNode;
+    }
+
+    /* search for path: /fstab */
+    DeviceNode =  mrdump_get_device_node_from_fstab("/fstab", mountp);
+    char fstab_filename[PATH_MAX];
+
+    for (i = 0; fstab_path_prefix[i] != NULL; i++) {
+        snprintf(fstab_filename, sizeof(fstab_filename), "%s", fstab_path_prefix[i]);
+        if(mrdump_file_is_exist(fstab_filename))
+            DeviceNode =  mrdump_get_device_node_from_fstab(fstab_filename, mountp);
+        if(DeviceNode)
+            return DeviceNode;
+    }
+    return DeviceNode;
+#elif defined(__YOCTO_OS__)
+    if (!strncmp(mountp, MRDUMP_EXPDB_NAME, sizeof(MRDUMP_EXPDB_NAME)))
+	return strdup(expdb_dev);
+
+    if (!strncmp(mountp, MRDUMP_EXT4_MOUNT_POINT, sizeof(MRDUMP_EXT4_MOUNT_POINT)))
+        return strdup(preallocate_dev);
+
+    return NULL;
+#endif
+}
diff --git a/src/devtools/mrdump/mrdump_tool_source/mrdump_common.h b/src/devtools/mrdump/mrdump_tool_source/mrdump_common.h
new file mode 100644
index 0000000..7a53b0f
--- /dev/null
+++ b/src/devtools/mrdump/mrdump_tool_source/mrdump_common.h
@@ -0,0 +1,31 @@
+#pragma once
+
+#ifdef __YOCTO_OS__
+#include <stdint.h>
+// typedef unsigned long long int uint64_t;
+#define strlcpy strncpy
+#endif
+
+/* variables definition */
+#define MRDUMP_DATA_FS_NONE    0
+#define MRDUMP_DATA_FS_EXT4    1
+#define MRDUMP_DATA_FS_F2FS    2
+#define MRDUMP_1K              1024
+#define MRDUMP_1M              (MRDUMP_1K * MRDUMP_1K)
+
+/* mrdump related */
+#define MRDUMP_DATA_PARTITION  "/dev/block/platform/bootdevice/by-name/userdata"
+#define MRDUMP_REST_BLOCKS     1024
+#define MRDUMP_REST_SPACE      (MRDUMP_1M * MRDUMP_REST_BLOCKS)
+
+/* mrdump flow control */
+#define MRDUMP_MAX_BW_REQ      100
+#define MRDUMP_MAX_BANDWIDTH   (MRDUMP_1M * MRDUMP_MAX_BW_REQ)
+
+/* Function Prototypes */
+void mrdump_close(int fd);
+int mrdump_file_is_exist(const char *path);
+int mrdump_get_data_os(void);
+uint64_t mrdump_get_partition_free_size(const char *mountp);
+uint64_t mrdump_get_partition_size(char *fullpath);
+char *mrdump_get_device_node(const char *mountp);
diff --git a/src/devtools/mrdump/mrdump_tool_source/mrdump_defaults.c b/src/devtools/mrdump/mrdump_tool_source/mrdump_defaults.c
new file mode 100644
index 0000000..e92d8db
--- /dev/null
+++ b/src/devtools/mrdump/mrdump_tool_source/mrdump_defaults.c
@@ -0,0 +1,13 @@
+#include <string.h>
+
+const char* sysenv_get(const char *name) {
+    if (!strncmp(name, "mrdump_allocate_size", sizeof("mrdump_allocate_size")))
+        return ("fullmem");
+    if (!strncmp(name, "mrdump_output", sizeof("mrdump_output")))
+        return ("internal-storage");
+    return NULL;
+}
+
+int sysenv_set(const char *name, const char *value) {
+    return 0;
+}
diff --git a/src/devtools/mrdump/mrdump_tool_source/mrdump_defaults.h b/src/devtools/mrdump/mrdump_tool_source/mrdump_defaults.h
new file mode 100644
index 0000000..2e93b26
--- /dev/null
+++ b/src/devtools/mrdump/mrdump_tool_source/mrdump_defaults.h
@@ -0,0 +1,2 @@
+extern const char* sysenv_get(const char *name);
+extern int sysenv_set(const char *name, const char *value);
diff --git a/src/devtools/mrdump/mrdump_tool_source/mrdump_log.c b/src/devtools/mrdump/mrdump_tool_source/mrdump_log.c
new file mode 100644
index 0000000..3f3972b
--- /dev/null
+++ b/src/devtools/mrdump/mrdump_tool_source/mrdump_log.c
@@ -0,0 +1,71 @@
+/* vprintf, vsnprintf */
+#include <stdio.h>
+
+/* exit(), atoi() */
+#include <stdlib.h>
+
+#ifdef __YOCTO_OS__
+#include <stdarg.h>
+#endif
+
+/* mrdump related */
+#include "mrdump_log.h"
+
+
+static int mdlog_level = LOG_INFO;
+static int mdlog_write_syslog = 1;
+
+void mdlog_init(int level, int write_syslog)
+{
+    mdlog_level = level;
+    mdlog_write_syslog = write_syslog;
+    openlog("mrdump_tool", 0, 0);
+}
+
+void mdlog_printf(int log_prio, const char *fmt, ...)
+{
+    va_list ap;
+
+    if (log_prio > mdlog_level) {
+        return;
+    }
+
+    va_start(ap, fmt);
+    if (mdlog_write_syslog) {
+        vsyslog(log_prio, fmt, ap);
+    }
+    else {
+        char prefix;
+        if (log_prio <= LOG_ERR) {
+            prefix = 'E';
+        } else if (log_prio == LOG_WARNING) {
+            prefix = 'W';
+        } else if (log_prio <= LOG_INFO) {
+            prefix = 'I';
+        } else {
+            prefix = 'D';
+        }
+        putc(prefix, stderr);
+        putc(':', stderr);
+        vfprintf(stderr, fmt, ap);
+    }
+    va_end(ap);
+}
+
+void error(const char *msg, ...)
+{
+    va_list ap;
+    char msgbuf[128];
+
+    va_start(ap, msg);
+    if (mdlog_write_syslog) {
+        vsyslog(LOG_ERR, msg, ap);
+    }
+    else {
+        vsnprintf(msgbuf, sizeof(msgbuf), msg, ap);
+        fprintf(stderr, "Error: %s", msgbuf);
+    }
+    va_end(ap);
+
+    exit(2);
+}
diff --git a/src/devtools/mrdump/mrdump_tool_source/mrdump_log.h b/src/devtools/mrdump/mrdump_tool_source/mrdump_log.h
new file mode 100644
index 0000000..b8bee70
--- /dev/null
+++ b/src/devtools/mrdump/mrdump_tool_source/mrdump_log.h
@@ -0,0 +1,26 @@
+#pragma once
+
+#include <syslog.h>
+
+void mdlog_init(int level, int write_syslog);
+
+void mdlog_printf(int log_prio, const char *fmt, ...);
+
+void error(const char *msg, ...) __attribute__((noreturn));
+
+/* LOG macro support */
+#define DEBUG_BY_CONSOLE
+#include <stdio.h>
+#if defined(DEBUG_BY_CONSOLE)
+#define MD_LOGV(...) printf(__VA_ARGS__)
+#define MD_LOGD(...) printf(__VA_ARGS__)
+#define MD_LOGI(...) printf(__VA_ARGS__)
+#define MD_LOGW(...) printf(__VA_ARGS__)
+#define MD_LOGE(...) printf(__VA_ARGS__)
+#else
+#define MD_LOGV(...) mdlog_printf(LOG_DEBUG, __VA_ARGS__)
+#define MD_LOGD(...) mdlog_printf(LOG_DEBUG, __VA_ARGS__)
+#define MD_LOGI(...) mdlog_printf(LOG_INFO, __VA_ARGS__)
+#define MD_LOGW(...) mdlog_printf(LOG_WARNING, __VA_ARGS__)
+#define MD_LOGE(...) mdlog_printf(LOG_ERR, __VA_ARGS__)
+#endif
diff --git a/src/devtools/mrdump/mrdump_tool_source/mrdump_status.c b/src/devtools/mrdump/mrdump_tool_source/mrdump_status.c
new file mode 100644
index 0000000..edc4a2e
--- /dev/null
+++ b/src/devtools/mrdump/mrdump_tool_source/mrdump_status.c
@@ -0,0 +1,265 @@
+/* printf */
+#include <stdio.h>
+
+/* open */
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+/* read */
+#include <unistd.h>
+
+/* free */
+#include <stdlib.h>
+
+/* ioctl */
+#include <sys/ioctl.h>
+
+/* lseek64 */
+#ifndef _LARGEFILE64_SOURCE
+#define _LARGEFILE64_SOURCE
+#endif
+#include <sys/types.h>
+#include <unistd.h>
+
+/* bzero */
+#include <strings.h>
+
+/* strlen, memset, strncpy, strtok... */
+#include <string.h>
+
+/* errno */
+#include <errno.h>
+
+/* BLKGETSIZE64 */
+#include <linux/fs.h>
+
+/* mrdump related */
+#include "mrdump_log.h"
+#include "mrdump_common.h"
+#include "mrdump_status.h"
+#include "mrdump_status_private.h"
+
+#ifdef __YOCTO_OS__
+#define strlcpy strncpy
+#endif
+
+static int file_read_string(const char* path, char *content, int len)
+{
+    if (len <= 0) {
+        return -2;
+    }
+
+    int fd = open(path, O_RDONLY);
+    if (fd < 0) {
+        return -1;
+    }
+
+    /* Preserved NULL byte */
+    len--;
+    int size = 0;
+    do {
+        int ret = read(fd, content + size, len - size);
+        if (ret <= 0)
+            break;
+        size = size + ret;
+    } while (size < len);
+    content[size] = 0;
+
+    close(fd);
+    return size;
+}
+
+static int expdb_open(struct partinfo *partinfo)
+{
+    uint64_t part_size;
+    uint32_t part_blksize;
+
+    memset(partinfo, 0, sizeof(struct partinfo));
+
+    char *pp = mrdump_get_device_node(MRDUMP_EXPDB_NAME);
+    if (pp == NULL) {
+        MD_LOGE("%s: No expdb partition found", __func__);
+        return -1;
+    }
+
+    int fd = open(pp, O_RDWR);
+    if (fd < 0) {
+        MD_LOGE("%s: open expdb failed(%d)", __func__, errno);
+        free(pp);
+        return -1;
+    }
+    free(pp);
+
+    if (ioctl(fd, BLKGETSIZE64, &part_size) < 0) {
+        MD_LOGE("%s, get expdb partition size fail(%d)", __func__, errno);
+        close(fd);
+        return -1;
+    }
+
+    if (ioctl(fd, BLKSSZGET, &part_blksize) < 0) {
+        MD_LOGE("%s, get sector size fail(%d)", __func__, errno);
+        close(fd);
+        return -1;
+    }
+
+    partinfo->fd = fd;
+    partinfo->size = part_size;
+    partinfo->blksize = part_blksize;
+
+    return 0;
+}
+
+int mrdump_is_supported(void)
+{
+    char kversion[16], lversion[16];
+    int klen, llen;
+
+    bzero(kversion, sizeof(kversion));
+    bzero(lversion, sizeof(lversion));
+
+    if (file_read_string(MRDUMP_KVER, kversion, sizeof(kversion)) < 0) {
+        MD_LOGE("%s: cannot get kernel version\n", __func__);
+        return 0;
+    }
+
+    klen = strlen(kversion);
+    if (klen == 0) {
+        MD_LOGE("%s: null kernel version\n", __func__);
+        return 0;
+    }
+
+    if (file_read_string(MRDUMP_LVER, lversion, sizeof(lversion)) < 0) {
+        MD_LOGE("%s: cannot get lk version\n", __func__);
+        return 0;
+    }
+
+    llen = strlen(lversion);
+    if (llen == 0) {
+        MD_LOGE("%s: null lk version\n", __func__);
+        return 0;
+    }
+
+    if ((klen != llen) || (strncmp(kversion, lversion, klen) != 0)) {
+        MD_LOGE("%s: kernel and lk version mismatched.\n", __func__);
+        return 0;
+    }
+
+    MD_LOGI("%s: true\n", __func__);
+    return 1;
+}
+
+bool mrdump_status_clear(void)
+{
+    struct partinfo partinfo;
+
+    if (expdb_open(&partinfo) >= 0) {
+	if (lseek64(partinfo.fd, partinfo.size - MRDUMP_OFFSET, SEEK_SET) < 0) {
+	    MD_LOGE("%s: Can't seek part fd %d\n", __func__, partinfo.fd);
+	    close(partinfo.fd);
+	    return false;
+	}
+
+	struct mrdump_cblock_result cblock_result;
+	if (read(partinfo.fd, &cblock_result, sizeof(struct mrdump_cblock_result)) != sizeof(struct mrdump_cblock_result)) {
+	    MD_LOGE("%s: Can't read part fd %d\n", __func__, partinfo.fd);
+	    close(partinfo.fd);
+	    return false;
+	}
+	memset(cblock_result.status, 0, sizeof(cblock_result.status));
+	strncpy(cblock_result.status, "CLEAR", 5);
+
+	if (lseek64(partinfo.fd, partinfo.size - MRDUMP_OFFSET, SEEK_SET) < 0) {
+	    MD_LOGE("%s: Can't seek part fd %d\n", __func__, partinfo.fd);
+	    close(partinfo.fd);
+	    return false;
+	}
+	if (write(partinfo.fd, &cblock_result, sizeof(struct mrdump_cblock_result)) != sizeof(struct mrdump_cblock_result)) {
+	    MD_LOGE("%s: Can't write part fd %d\n", __func__, partinfo.fd);
+	    close(partinfo.fd);
+	    return false;
+	}
+	close(partinfo.fd);
+	return true;
+    }
+    return false;
+}
+
+bool mrdump_status_get(struct mrdump_status_result *result)
+{
+    memset(result, 0, sizeof(struct mrdump_status_result));
+    result->struct_size = sizeof(struct mrdump_status_result);
+
+    struct partinfo partinfo;
+    if (expdb_open(&partinfo) >= 0) {
+	if (lseek64(partinfo.fd, partinfo.size - MRDUMP_OFFSET, SEEK_SET) < 0) {
+	    MD_LOGE("%s: Can't seek part fd %d\n", __func__, partinfo.fd);
+	    close(partinfo.fd);
+	    return false;
+	}
+
+	struct mrdump_cblock_result cblock_result;
+	if (read(partinfo.fd, &cblock_result, sizeof(struct mrdump_cblock_result)) != sizeof(struct mrdump_cblock_result)) {
+	    MD_LOGE("%s: Can't read part fd %d\n", __func__, partinfo.fd);
+	    close(partinfo.fd);
+	    return false;
+	}
+	close(partinfo.fd);
+
+	if (strcmp(cblock_result.sig, MRDUMP_SIG) != 0) {
+	    MD_LOGE("%s: Signature mismatched (result: %s)\n", __func__, cblock_result.sig);
+	    return false;
+	}
+	/* Copy/parsing status line */
+	strncpy(result->status_line, cblock_result.status, sizeof(cblock_result.status));
+	result->status_line[sizeof(result->status_line) - 1] = 0;
+
+	char *saveptr;
+	cblock_result.status[sizeof(cblock_result.status) - 1] = 0;
+	char *strval = strtok_r(cblock_result.status, "\n", &saveptr);
+	if (strval != NULL) {
+	    if (strcmp(strval, "OK") == 0) {
+		result->status = MRDUMP_STATUS_OK;
+		result->output = MRDUMP_OUTPUT_NULL;
+
+		do {
+		    strval = strtok_r(NULL, "\n", &saveptr);
+		    if (strval != NULL) {
+                        if (strncmp(strval, "OUTPUT:", 7) == 0) {
+			    if (strcmp(strval + 7, "EXT4_DATA") == 0) {
+				result->output = MRDUMP_OUTPUT_DATA_FS;
+			    }
+			    else if (strcmp(strval + 7, "PARTITION_DATA") == 0) {
+				result->output = MRDUMP_OUTPUT_PARTITION;
+			    }
+			    else {
+				return false;
+			    }
+			}
+			else if (strncmp(strval, "MODE:", 5) == 0) {
+			    strlcpy(result->mode, strval + 5, sizeof(result->mode));
+			}
+		    }
+		} while (strval != NULL);
+	    }
+	    else if (strcmp(strval, "NONE") == 0) {
+		result->status = MRDUMP_STATUS_NONE;
+	    }
+	    else if (strcmp(strval, "CLEAR") == 0) {
+		result->status = MRDUMP_STATUS_NONE;
+	    }
+	    else {
+		result->status = MRDUMP_STATUS_FAILED;
+	    }
+	}
+	else {
+	    MD_LOGE("%s: status parsing error \"%s\"\n", __func__, cblock_result.status);
+	    return false;
+	}
+
+	strncpy(result->log_buf, cblock_result.log_buf, sizeof(cblock_result.log_buf));
+	result->log_buf[sizeof(result->log_buf) - 1] = 0;
+	return true;
+    }
+    return false;
+}
diff --git a/src/devtools/mrdump/mrdump_tool_source/mrdump_status.h b/src/devtools/mrdump/mrdump_tool_source/mrdump_status.h
new file mode 100644
index 0000000..8212626
--- /dev/null
+++ b/src/devtools/mrdump/mrdump_tool_source/mrdump_status.h
@@ -0,0 +1,40 @@
+#if !defined(__MRDUMP_H__)
+#define __MRDUMP_H__
+
+#include <stdbool.h>
+#include <stdint.h>
+
+typedef enum {
+    MRDUMP_STATUS_NONE,
+    MRDUMP_STATUS_FAILED,
+    MRDUMP_STATUS_OK,
+} MRDUMP_STATUS;
+
+typedef enum {
+    MRDUMP_OUTPUT_NULL,
+    MRDUMP_OUTPUT_USB,
+    MRDUMP_OUTPUT_DATA_FS,
+    MRDUMP_OUTPUT_UNUSED, /* VFAT */
+    MRDUMP_OUTPUT_PARTITION,
+} MRDUMP_OUTPUT;
+
+#define MRDUMP_OUTPUT_EXT4_DATA MRDUMP_OUTPUT_DATA_FS
+#define MRDUMP_OUTPUT_PARTITION_DATA MRDUMP_OUTPUT_PARTITION
+
+struct mrdump_status_result {
+    uint32_t struct_size;
+
+    MRDUMP_STATUS status;
+    MRDUMP_OUTPUT output;
+    char mode[32];
+
+    char status_line[128];
+    char log_buf[2048];
+};
+
+/* public api */
+int mrdump_is_supported(void);
+bool mrdump_status_clear(void);
+bool mrdump_status_get(struct mrdump_status_result *result);
+
+#endif
diff --git a/src/devtools/mrdump/mrdump_tool_source/mrdump_status_private.h b/src/devtools/mrdump/mrdump_tool_source/mrdump_status_private.h
new file mode 100644
index 0000000..00651cd
--- /dev/null
+++ b/src/devtools/mrdump/mrdump_tool_source/mrdump_status_private.h
@@ -0,0 +1,26 @@
+#if !defined(__MRDUMP_USER_PRIVATE_H__)
+#define __MRDUMP_USER_PRIVATE_H__
+
+#include <stdbool.h>
+#include <stdint.h>
+
+/* DRAM KLOG at MRDUMP area of expdb, offset from bottom = 3145728 - 16384 = 3129344 */
+#define MRDUMP_OFFSET 3145728
+#define MRDUMP_EXPDB_NAME "/expdb"
+#define MRDUMP_KVER "/sys/module/mrdump/version"
+#define MRDUMP_LVER "/sys/module/mrdump/parameters/lk"
+#define MRDUMP_SIG "MRDUMP08"
+
+struct __attribute__((__packed__)) mrdump_cblock_result {
+    char sig[9];
+    char status[128];
+    char log_buf[2048];
+};
+
+struct partinfo {
+    int fd;
+    uint64_t size;
+    uint32_t blksize;
+};
+
+#endif
diff --git a/src/devtools/mrdump/mrdump_tool_source/mrdump_support_ext4.c b/src/devtools/mrdump/mrdump_tool_source/mrdump_support_ext4.c
new file mode 100644
index 0000000..1d00e94
--- /dev/null
+++ b/src/devtools/mrdump/mrdump_tool_source/mrdump_support_ext4.c
@@ -0,0 +1,1399 @@
+/* print */
+#include <stdio.h>
+
+/* uint64_t ...*/
+#include <inttypes.h>
+#include <stdbool.h>
+
+/* __u32 */
+#include <linux/types.h>
+
+/* struct stat, open */
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+/* malloc */
+#include <stdlib.h>
+
+/* strerror */
+#include <string.h>
+/* errno */
+#include <errno.h>
+
+/* figetbsz */
+#include <linux/fs.h>
+#include <sys/ioctl.h>
+
+/* statfs() and statvfs() */
+#include <sys/statfs.h>
+#include <sys/statvfs.h>
+#include <sys/vfs.h>
+
+/* crc32, Z_NULL */
+#include <zlib.h>
+
+/* syscall, __NR_fallocate */
+#include <sys/syscall.h>
+
+/* time */
+#include <time.h>
+
+/* sysenv */
+#include "mrdump_defaults.h"
+
+/* mrdump related */
+#include "mrdump_log.h"
+#include "mrdump_common.h"
+#include "mrdump_status.h"
+#include "mrdump_support_fiemap.h"
+#include "mrdump_support_ext4.h"
+#include "mrdump_support_f2fs.h"
+#include "mrdump_support_mpart.h"
+
+/* add for block verification */
+static uint64_t lba_marker_time;
+static struct marked_block_data bdata;
+
+struct palloc_file {
+    int fd;
+    int blksize;
+    int lbaooo;
+};
+
+static struct palloc_file *palloc_file_open(const char *allocfile)
+{
+    struct stat statinfo;
+
+    struct palloc_file *pfile = malloc(sizeof(struct palloc_file));
+    pfile->fd = -1;
+    pfile->blksize = 0;
+    pfile->lbaooo = 0;
+
+    /////////////////////////////////////////////////////
+    // open file handle
+    pfile->fd = open(allocfile, O_RDONLY);
+    if(0 > pfile->fd) {
+        MD_LOGE("%s: file(%s) marker open failed(%d), %s\n", __func__, allocfile, errno, strerror(errno));
+        return NULL;
+    }
+    // Get file statinfo
+    if(0 > fstat(pfile->fd, &statinfo)) {
+        MD_LOGE("%s: marker stat failed(%d), %s\n", __func__, errno, strerror(errno));
+        goto cleanup;
+    }
+
+    if(0 > ioctl(pfile->fd, FIGETBSZ, &pfile->blksize)) {
+        MD_LOGE("%s: FIGETGSZ failed(%d), %s", __func__, errno, strerror(errno));
+        goto cleanup;
+    }
+
+    pfile->lbaooo = mrdump_fiemap_get_entry_lba(pfile->fd, pfile->blksize, 0);
+    if (pfile->lbaooo < 0) {
+        MD_LOGE("%s: get lba failed", __func__);
+        goto cleanup;
+    }
+    return pfile;
+
+  cleanup:
+    mrdump_close(pfile->fd);
+    pfile->fd = -1;
+    pfile->blksize = 0;
+    pfile->lbaooo = 0;
+    return NULL;
+}
+
+void palloc_file_close(struct palloc_file *pfile)
+{
+    mrdump_close(pfile->fd);
+    free(pfile);
+}
+
+static int fop_file_write_string(const char *path, const char *content, ...)
+{
+    int ret_val = 0;
+
+    int fd = open(path, O_WRONLY);
+    if (fd >= 0) {
+        char content_buf[1024];
+        va_list ap;
+
+        va_start(ap, content);
+        int n = vsnprintf(content_buf, sizeof(content_buf), content, ap);
+        va_end(ap);
+        int val = write(fd, content_buf, n);
+        if (val != n) {
+            if (val < 0) {
+                ret_val = -errno;
+            }
+            else {
+                ret_val = -EIO;
+            }
+        }
+        mrdump_close(fd);
+        if (ret_val == 0) {
+            ret_val = n;
+        }
+    }
+    else {
+        ret_val = -errno;
+    }
+    return ret_val;
+}
+
+////////////////////////////////////////////
+// common function (static in this file)  //
+////////////////////////////////////////////
+static bool ext4_setup_attr(char const *allocfile, bool enabled)
+{
+    int fd;
+    unsigned int myflags;
+
+    if(allocfile == NULL) {
+        allocfile = MRDUMP_EXT4_ALLOCATE_FILE;
+    }
+
+    fd = open(allocfile, O_RDONLY);
+    if(0 > fd) {
+        MD_LOGE("%s: open allocfile failed(%d), %s\n", __func__, errno, strerror(errno));
+        return false;
+    }
+
+    if(0 > ioctl(fd, FS_IOC_GETFLAGS, &myflags)) {
+        MD_LOGE("%s: FS_IOC_GETFLAGS failed(%d), %s\n", __func__, errno, strerror(errno));
+        return false;
+    }
+
+    if (enabled) {
+        myflags |= (FS_SECRM_FL | FS_IMMUTABLE_FL);
+    }
+    else {
+        myflags &= ~(FS_SECRM_FL | FS_IMMUTABLE_FL);
+    }
+    if(0 > ioctl(fd, FS_IOC_SETFLAGS, &myflags)) {
+        MD_LOGE("%s: FS_IOC_SETFLAGS failed(%d), %s\n", __func__, errno, strerror(errno));
+        return false;
+    }
+
+    mrdump_close(fd);
+    return true;
+}
+
+static off64_t ext4_block_lseek(int fd, unsigned lba, int blksize)
+{
+    unsigned long long location = (unsigned long long) lba * (unsigned long long) blksize;
+    return lseek64(fd, location, SEEK_SET);
+}
+
+static int ext4_open_device_node(const char *mountp)
+{
+    char *devicenode = mrdump_get_device_node(mountp);
+    if(devicenode == NULL) {
+        MD_LOGE("%s: Get devicenode return null\n", __func__);
+        return -1;
+    }
+    int fd = open(devicenode, O_RDWR);
+    if(0 > fd) {
+        MD_LOGE("%s: open devicenode failed(%d), %s\n", __func__, errno, strerror(errno));
+        free(devicenode);
+        return -1;
+    }
+    free(devicenode);
+    return fd;
+}
+
+bool mount_as_ext4(const char *mountp)
+{
+    struct statfs fs;
+    if(statfs(mountp, &fs) == 0) {
+        /*
+         * bionic header didn't define EXT4_SUPER_MAGIC
+         * use EXT3_SUPER_MAGIC instead
+         */
+        if(fs.f_type == EXT3_SUPER_MAGIC)
+            return true;
+        return false;
+    }
+    MD_LOGE("%s: %s statfs error.\n", __func__, mountp);
+    return false;
+}
+
+static int check_a_data_block(int myfds, int mybs, uint32_t lba)
+{
+    unsigned long long offset = (unsigned long long)lba * (unsigned long long)mybs;
+    struct marked_block_data output_data;
+    unsigned int mycrc = crc32(0, Z_NULL, 0);
+    mycrc = crc32(mycrc, (void *)&bdata, (mybs-4));
+    bdata.crc = mycrc;
+    bdata.lba = lba;
+
+    if(0 > pread64(myfds, (void *)&output_data, mybs, offset)) {
+        MD_LOGE("%s: write block error!(%d), %s", __func__, errno, strerror(errno));
+        return -1;
+    };
+
+    // check lba
+    if (bdata.lba != output_data.lba) {
+        MD_LOGE("Read BlockData LBA failed (c:%u, v:%u)\n", bdata.lba, output_data.lba);
+        return -1;
+    }
+    // check timestamp
+    if (bdata.timestamp != output_data.timestamp) {
+        MD_LOGE("Read BlockData timestamp failed (c:%u, v:%u)\n", bdata.timestamp, output_data.timestamp);
+        return -1;
+    }
+    // check crc
+    if (bdata.crc != output_data.crc) {
+        MD_LOGE("Read BlockData crc32 failed (c:%u, v:%u)\n", bdata.crc, output_data.crc);
+        return -1;
+    }
+
+    return 0;
+}
+
+static int format_a_data_block(int myfds, int mybs, uint32_t lba)
+{
+    unsigned long long offset = (unsigned long long)lba * (unsigned long long)mybs;
+
+    bdata.lba = lba;
+    // (no change) bdata.timestamp = lba_marker_time;
+    unsigned int mycrc = crc32(0, Z_NULL, 0);
+    mycrc = crc32(mycrc, (void *)&bdata, (mybs-4));
+    bdata.crc = mycrc;
+
+    if(0 > pwrite64(myfds, (void *)&bdata, mybs, offset)) {
+        MD_LOGE("%s: write block error!(%d), %s", __func__, errno, strerror(errno));
+        return -1;
+    };
+
+    return 0;
+}
+
+static int ext4_get_next_bidx(int myfd, unsigned int *pBlock, unsigned int bbidx, unsigned int bs, unsigned int moves)
+{
+    unsigned int rlba, mycrc;
+
+    if(bbidx > MRDUMP_EXT4_LBA_PER_BLOCK)
+        return -1;
+
+    if(bbidx == MRDUMP_EXT4_LBA_PER_BLOCK) {
+        rlba = pBlock[bbidx];
+        // seek to location
+        if(-1 == ext4_block_lseek(myfd, rlba, bs)) {
+            MD_LOGE("%s: lseek64 location failed(%d), %s", __func__, errno, strerror(errno));
+            return -1;
+        }
+        // read to pBlock
+        if(0 > read(myfd, pBlock, bs)) {
+            MD_LOGE("%s: read lbaooo error!(%d), %s", __func__, errno, strerror(errno));
+            return -1;
+        }
+        // check crc32
+        mycrc = crc32(0, Z_NULL, 0);
+        mycrc = crc32(mycrc, (void *)pBlock, (bs-4));
+        if(mycrc != pBlock[1023]) {
+            MD_LOGE("%s: crc32 error!(%d), %s", __func__, errno, strerror(errno));
+            return -1;
+        }
+        bbidx = 0;
+    } else {
+        bbidx+=moves;
+    }
+    return bbidx;
+}
+
+static unsigned int ext4_num_to_join(unsigned int *pBlock, unsigned int bbidx)
+{
+    unsigned int i, j;
+    for(i=0, j=0; i<MRDUMP_EXT4_MAX_CONTINUE; i++) {
+        if((pBlock[bbidx+i] - pBlock[bbidx]) == i) {
+            j++;
+            continue;
+        }
+        break;
+    }
+    return j;
+}
+
+static int construct_a_block(int myfds, int mybs, int myloop, int myoffset, struct fiemap_info *mapinfo, unsigned int rows)
+{
+    unsigned int i, lba, num, mycrc;
+    unsigned int block[1024]; // assume max BLOCKSIZE=4096, int(4byte) for addressing
+
+    // Zero blocks
+    memset(block, 0, sizeof(block));
+
+    // number of address per block
+    // 1024 = Normal_LBA[1022] + Next_LBA + crc32
+    num = (mybs+sizeof(int)-1) / sizeof(int);
+
+    // Indexing by 0. --> (i-1)
+    // i     = 1~MRDUMP_EXT4_LBA_PER_BLOCK(1022)
+    // index = 0~1021
+    i = 1;
+    while(i < (num - 1)) {
+        lba = myoffset + (i-1);
+        lba = mrdump_fiemap_get_lba_of_block(mapinfo, rows, lba);
+        if(0 > lba) {
+            MD_LOGE("%s: mrdump_fiemap_get_lba_of_block(1021) failed(%d), %s", __func__, errno, strerror(errno));
+            return -1;
+        }
+        block[(i-1)] = lba;
+        i++;
+    }
+
+    /* coverity: to avoid while(i < (num - 1)) taking false branch */
+    if (i < 2) {
+        MD_LOGE("%s: number of address per block should be more than 2\n", __func__);
+        return -1;
+    }
+
+    // i     = 1023
+    // index = MRDUMP_EXT4_LBA_PER_BLOCK(1022)
+    if (block[(i-2)] == 0) {
+        // End of File
+        block[(i-1)] = 0;
+    } else {
+        // lba = (myloop+1); --> Need ++ For InfoLBA[EXT4_LBA_INFO_NUM];
+        // block starts from 2nd Block
+        lba = myloop + 2;
+        lba = mrdump_fiemap_get_lba_of_block(mapinfo, rows, lba);
+        if(0 > lba) {
+            MD_LOGE("%s: mrdump_fiemap_get_lba_of_block(1022) failed(%d), %s", __func__, errno, strerror(errno));
+            return -1;
+        }
+        block[(i-1)] = lba;
+    }
+
+    // i     = 1023: crc32
+    // index = 1023: (NO block[i+1] now...)
+    // PS: index = 1023 now... i takes no changes.
+    mycrc = crc32(0, Z_NULL, 0);
+    mycrc = crc32(mycrc, (void *)block, (mybs-4));
+    block[i] = mycrc;
+
+    // Prepare to write onto the Blocks in the front ...
+    //
+    // the address (LBA) where to write on.
+    // lba = myloop; --> Need ++ For InfoLBA[EXT4_LBA_INFO_NUM];
+    lba = myloop+1;
+    lba = mrdump_fiemap_get_lba_of_block(mapinfo, rows, lba);
+    if(0 > lba) {
+        MD_LOGE("%s: mrdump_fiemap_get_lba_of_block(write) failed(%d), %s", __func__, errno, strerror(errno));
+        return -1;
+    }
+    // Write block onto location;
+    if (-1 == ext4_block_lseek(myfds, lba, mybs)) {
+        MD_LOGE("%s: lseek64 location failed(%d), %s", __func__, errno, strerror(errno));
+        return -1;
+    }
+    if(0 > write(myfds, (void *)block, sizeof(block))) {
+        MD_LOGE("%s: write block error!(%d), %s", __func__, errno, strerror(errno));
+        return -1;
+    }
+
+    // return the new offset
+    // i = 1023
+    if (block[(i-1)] == 0) {
+        myoffset = 0;
+    } else {
+        myoffset = myoffset + (i - 1);
+    }
+
+    return (int)myoffset;
+}
+
+static void mrdump_dump_pafile_info(const uint8_t lba[MRDUMP_PAF_TOTAL_SIZE])
+{
+    uint32_t version = *(uint32_t *)(lba);
+    uint32_t info_lba = *(uint32_t *)(lba + MRDUMP_PAF_INFO_LBA);
+    uint32_t addr_lba = *(uint32_t *)(lba + MRDUMP_PAF_ADDR_LBA);
+    uint64_t filesize = *(uint64_t *)(lba + MRDUMP_PAF_ALLOCSIZE);
+    uint64_t coredump_size = *(uint64_t *)(lba + MRDUMP_PAF_COREDUMPSIZE);
+    uint64_t timestamp = *(uint64_t *)(lba + MRDUMP_PAF_TIMESTAMP);
+    uint32_t crcval = *(uint64_t *)(lba + MRDUMP_PAF_CRC32);
+
+
+    MD_LOGD("PAFILE-INFO version %u info-lba %u addr-lba %u file-size %llu coredump-size %llu timestamp %llx crc %x\n",
+            version, info_lba, addr_lba, filesize, coredump_size, timestamp, crcval);
+}
+
+/* lba format
+ *  0: version      (2 bytes)
+ *  2:reserved      (2 bytes)
+ *  4:info lba      (4 bytes)
+ *  8:addr lba      (4 bytes)
+ * 12:allocate size (8 bytes)
+ * 20:coredump size (8 bytes)
+ * 28:timestamp     (8 bytes)
+ * 36:CRC32         (4 bytes)
+ */
+static bool fs_lba_mark_header(const struct palloc_file *pfile)
+{
+    int num_blocks, rsv_blocks;
+    struct stat statinfo;
+    unsigned int mycrc, block;
+    uint8_t InfoLBA[MRDUMP_PAF_TOTAL_SIZE], *pinfo;
+
+    // Get file statinfo
+    if(0 > fstat(pfile->fd, &statinfo)) {
+        MD_LOGE("%s: marker stat failed(%d), %s\n", __func__, errno, strerror(errno));
+        return false;
+    }
+    // Get Blocksize and counting blocks
+    uint64_t blksize = pfile->blksize;
+
+    // Counting blocks
+    num_blocks = (statinfo.st_size + blksize - 1) / blksize;
+
+    /////////////////////////////////////////////////////
+    // Zero InfoLBA ...
+    memset(InfoLBA, 0, sizeof(InfoLBA));
+    pinfo = &InfoLBA[0];
+
+    /* Setup pafile info version */
+    *(uint16_t *)pinfo = MRDUMP_PAF_VERSION;
+
+    /* Flags */
+    *(uint16_t *)(pinfo + 2) = 0;
+
+    // Get info LBA
+    block = mrdump_fiemap_get_entry_lba(pfile->fd, blksize, 0);
+    *(uint32_t *)(pinfo + MRDUMP_PAF_INFO_LBA) = block;
+
+    // Get 2nd LBA
+    if(2 > mrdump_fiemap_get_entry_tot(pfile->fd, blksize, 0))
+        *(uint32_t *)(pinfo + MRDUMP_PAF_ADDR_LBA) = mrdump_fiemap_get_entry_lba(pfile->fd, blksize, 1);
+    else
+        *(uint32_t *)(pinfo + MRDUMP_PAF_ADDR_LBA) = block + 1;
+
+    // Reserved space for Header and Tailer
+    rsv_blocks = num_blocks/MRDUMP_EXT4_LBA_PER_BLOCK;   // Header
+    rsv_blocks+= 2;                 // Tailer
+    *(uint64_t *)(pinfo + MRDUMP_PAF_ALLOCSIZE) = (num_blocks - rsv_blocks) * blksize;
+
+    // Set core dump size to zero
+    *(uint64_t *)(pinfo + MRDUMP_PAF_COREDUMPSIZE) = 0UL;
+
+    // Set timestamp to lba_marker_time
+    *(uint64_t *)(pinfo + MRDUMP_PAF_TIMESTAMP) = lba_marker_time;
+
+    // check crc32 of InfoLBA
+    mycrc = crc32(0, Z_NULL, 0);
+    mycrc = crc32(mycrc, (void *)InfoLBA, MRDUMP_LBA_DATAONLY);
+    *(uint32_t *)(pinfo + MRDUMP_PAF_CRC32) = mycrc;
+
+    int fd = ext4_open_device_node(MRDUMP_EXT4_MOUNT_POINT);
+    if (fd < 0) {
+        return false;
+    }
+    // Save InfoLBA First
+    if(-1 == ext4_block_lseek(fd, block, blksize)) {
+        MD_LOGE("%s: marker header lseek64 InfoLBA failed(%d), %s\n", __func__, errno, strerror(errno));
+        mrdump_close(fd);
+        return false;
+    }
+    if(0 > write(fd, (void *)InfoLBA, (sizeof(InfoLBA)))) {
+        MD_LOGE("%s: marker header write InfoLBA error!(%d), %s\n", __func__, errno, strerror(errno));
+        mrdump_close(fd);
+        return false;
+    }
+
+    mrdump_close(fd);
+    return true;
+}
+
+static bool fs_lba_mark_body(const struct palloc_file *pfile)
+{
+    int             fds;
+    int             i, blksize, num_blocks, loop, offset;
+    struct stat     statinfo;
+
+    // Get file statinfo
+    if(0 > fstat(pfile->fd, &statinfo)) {
+        MD_LOGE("%s: marker stat failed(%d), %s\n", __func__, errno, strerror(errno));
+        return false;
+    }
+    // Get Blocksize and counting blocks
+    blksize = pfile->blksize;
+    num_blocks = (statinfo.st_size + blksize - 1) / blksize;
+
+    /////////////////////////////////////////////////////
+    // begin to record (2-layer block writing)
+    char *devicenode = mrdump_get_device_node(MRDUMP_EXT4_MOUNT_POINT);
+    if(devicenode == NULL) {
+        MD_LOGE("%s: get devicenode failed(%d), %s\n", __func__, errno, strerror(errno));
+        return false;
+    } else {
+        fds = open(devicenode, O_RDWR);
+        if(0 > fds) {
+            free(devicenode);
+            MD_LOGE("%s: open devicenode failed(%d), %s\n", __func__, errno, strerror(errno));
+            return false;
+        }
+    }
+    free(devicenode);
+
+    // for performance, we must finish fiemap here... but not in the loop
+    unsigned int my_num = mrdump_fiemap_total_entries(pfile->fd);
+    if(my_num == 0) {
+        MD_LOGE("%s: mrdump_fiemap_total_entries error!(%d), %s\n", __func__, errno, strerror(errno));
+        mrdump_close(fds);
+        return false;
+    }
+
+#ifdef MRDUMP_DEBUG
+    MD_LOGI("%s: my_num=(%06u)\n", __func__, my_num);
+#endif
+
+    struct fiemap_info *myinfo = malloc(my_num * sizeof(struct fiemap_info));
+    if(!mrdump_fiemap_get_entries(pfile->fd, blksize, myinfo, my_num)) {
+        MD_LOGE("%s: mrdump_fiemap_get_entries error!(%d), %s\n", __func__, errno, strerror(errno));
+        mrdump_close(fds);
+        return false;
+    }
+
+#ifdef MRDUMP_DEBUG
+    for(i=0; i<(int)my_num; i++) {
+        MD_LOGI("%s: i=%03d, LBA=%06u, TOT=%06u\n", __func__, i, myinfo[i].lba, myinfo[i].tot);
+    }
+#endif
+
+    // go
+    loop = (blksize / sizeof(unsigned int)) - 2;
+    loop = (num_blocks + loop - 1) / loop;
+#ifdef MRDUMP_DEBUG
+    MD_LOGI("%s: num_blocks=%d, my_num=%u\n", __func__, num_blocks, my_num);
+#endif
+    offset = loop + 1;
+    for(i=0; i<loop; i++) {
+        offset = construct_a_block(fds, blksize, i, offset, myinfo, my_num);
+        if (offset == -1) {
+            MD_LOGE("%s: marker construct block error!(%d), %s\n", __func__, errno, strerror(errno));
+            mrdump_close(fds);
+            free(myinfo);
+            return false;
+        }
+    }
+    mrdump_close(fds);
+    free(myinfo);
+    return true;
+}
+
+static bool ext4_fallocate(const char *allocfile, uint64_t allocsize)
+{
+    int fd;
+
+    if((allocfile == NULL) || (allocsize == 0)) {
+        MD_LOGE("%s: allocfile is NULL or allocsize = %" PRIu64 "\n", __func__, allocsize);
+        return false;
+    }
+
+    while(!mrdump_file_is_exist(AE_DUMPSYS_DATA_PATH)) {
+        MD_LOGI("%s: wait until %s ready.\n", __func__, AE_DUMPSYS_DATA_PATH);
+        sleep(1);
+    }
+
+    if(mount_as_f2fs(MRDUMP_EXT4_MOUNT_POINT)) {
+
+        allocsize = ((allocsize + F2FS_MAPSIZE - 1) / F2FS_MAPSIZE) * F2FS_MAPSIZE;
+        if(!f2fs_fallocate(allocfile, allocsize)) {
+            MD_LOGE("%s: f2fs_fallocate failed (allocfile=%s, allocsize=%" PRIu64 ")\n", __func__, allocfile, allocsize);
+            return false;
+        }
+
+        fd = open(allocfile, O_RDONLY);
+        if(0 > fd) {
+            MD_LOGE("%s: open fd failed.\n", __func__);
+            return false;
+        }
+
+        unsigned int val = 1234;
+        if(0 > ioctl(fd, F2FS_IOC_GET_PIN_FILE , &val)) {
+            MD_LOGE("%s: F2FS_IOC_GET_PIN_FILE(%u) failed(%d), %s\n", __func__, val, errno, strerror(errno));
+        }
+        MD_LOGI("%s: file(%s:%" PRIu64 ") Skip GC (%u times)\n", __func__, allocfile, allocsize, val);
+
+        mrdump_close(fd);
+
+    } else {
+
+        fd = open(allocfile, O_RDWR | O_CREAT, 0400);
+        if(0 > fd) {
+            MD_LOGE("%s: open allocfile failed(%d), %s\n", __func__, errno, strerror(errno));
+            return false;
+        }
+
+#if (!__LP64__)
+        uint32_t low = allocsize & 0xffffffff;
+        uint32_t high = allocsize >> 32;
+
+        if(0 > syscall(__NR_fallocate, fd, 0, 0, 0, low, high)) {
+            MD_LOGE("%s: fallocate32 failed(allocfile=%s, allocsize=%" PRIu64 ")\n", __func__, allocfile, allocsize);
+            mrdump_close(fd);
+            unlink(allocfile);
+            return false;
+        }
+#else
+        if(0 > syscall(__NR_fallocate, fd, 0, 0, allocsize)) {
+            MD_LOGE("%s: fallocate64 failed(allocfile=%s, allocsize=%" PRIu64 ")\n", __func__, allocfile, allocsize);
+            mrdump_close(fd);
+            unlink(allocfile);
+            return false;
+        }
+#endif
+        mrdump_close(fd);
+    }
+
+    return true;
+}
+
+static bool palloc_file_remove(const char *allocfile)
+{
+    /* Ignore attribute setting failed */
+    fop_file_write_string(MRDUMP_EXT4_PARA_LBAOOO, "0");
+
+    if (mrdump_file_is_exist(allocfile)) {
+        ext4_setup_attr(allocfile, false);
+        if (unlink(allocfile) != 0) {
+            MD_LOGE("%s: Cannot unlink %s(%d), %s\n", __func__, allocfile, errno, strerror(errno));
+            return false;
+        }
+    }
+    return true;
+}
+
+static bool mrdump_read_pafile_info(const struct palloc_file *pfile, int device_fd,
+                                    struct mrdump_pafile_info *info)
+{
+    memset(info, 0, sizeof(struct mrdump_pafile_info));
+
+    uint8_t block0[MRDUMP_PAF_TOTAL_SIZE];
+    memset(block0, 0, sizeof(block0));
+
+    if (-1 == ext4_block_lseek(device_fd, pfile->lbaooo, pfile->blksize)) {
+        MD_LOGE("%s: lseek64 InfoLBA failed(%d), %s\n", __func__, errno, strerror(errno));
+        return false;
+    }
+
+    if (0 > read(device_fd, block0, sizeof(block0))) {
+        MD_LOGE("%s: read InfoLBA error (%d), %s\n", __func__, errno, strerror(errno));
+        return false;
+    }
+
+    uint8_t *bufp = &block0[0];
+    unsigned int crcval = crc32(0, Z_NULL, 0);
+    crcval = crc32(crcval, (void *)block0, MRDUMP_LBA_DATAONLY);
+    if (crcval != *(uint32_t *)&block0[MRDUMP_PAF_CRC32]) {
+        MD_LOGE("%s: LBA info CRC error (c:%08x, v:%08x)\n", __func__, crcval,
+                *(uint32_t *)&block0[MRDUMP_PAF_CRC32]);
+        return false;
+    }
+
+    uint16_t version = *(uint16_t *)&block0[0];
+    if (version != MRDUMP_PAF_VERSION) {
+        MD_LOGE("%s: LBA version mismatch (c:%d, v:%d)\n", __func__, MRDUMP_PAF_VERSION, version);
+        mrdump_dump_pafile_info(block0);
+        return false;
+    }
+    info->info_lba = *(uint32_t *)(bufp + MRDUMP_PAF_INFO_LBA);
+    info->addr_lba = *(uint32_t *)(bufp + MRDUMP_PAF_ADDR_LBA);
+    info->filesize = *(uint64_t *)(bufp + MRDUMP_PAF_ALLOCSIZE);
+    info->coredump_size = *(uint64_t *)(bufp + MRDUMP_PAF_COREDUMPSIZE);
+    info->timestamp = *(uint64_t *)(bufp + MRDUMP_PAF_TIMESTAMP);
+    return true;
+}
+
+static bool mark_data_block(const struct palloc_file *pfile)
+{
+    int             fds, fdd; /* fd of src and dst */
+    int             i, j, blksize, num_blocks, loop;
+    struct stat     statinfo;
+    unsigned int    lba, BlockLBA[1024];
+
+    // Get file statinfo
+    if(0 > fstat(pfile->fd, &statinfo)) {
+        MD_LOGE("%s: marker stat failed(%d), %s\n", __func__, errno, strerror(errno));
+        return false;
+    }
+    // Get Blocksize and counting blocks
+    blksize = pfile->blksize;
+    num_blocks = (statinfo.st_size + blksize - 1) / blksize;
+
+    /////////////////////////////////////////////////////
+    // begin to record (2-layer block writing)
+    char *devicenode = mrdump_get_device_node(MRDUMP_EXT4_MOUNT_POINT);
+    if(devicenode == NULL) {
+        MD_LOGE("%s: get devicenode failed(%d), %s\n", __func__, errno, strerror(errno));
+        return false;
+    } else {
+        fds = open(devicenode, O_RDONLY);
+        if(0 > fds) {
+            free(devicenode);
+            MD_LOGE("%s: open devicenode failed(%d), %s\n", __func__, errno, strerror(errno));
+            return false;
+        }
+        fdd = open(devicenode, O_WRONLY);
+        if(0 > fdd) {
+            mrdump_close(fds);
+            free(devicenode);
+            MD_LOGE("%s: open devicenode failed(%d), %s\n", __func__, errno, strerror(errno));
+            return false;
+        }
+    }
+    free(devicenode);
+
+    // Check each Block (Now: InfoLBA + Address Block)
+    // 1. InfoLBA blocks
+    struct mrdump_pafile_info info;
+    if (!mrdump_read_pafile_info(pfile, fds, &info)) {
+        mrdump_close(fds);
+        mrdump_close(fdd);
+        return false;
+    }
+
+    // go for Address Area
+    bool stop_here = false;
+    lba = (unsigned int)info.addr_lba;
+    bdata.timestamp = info.timestamp;
+    loop = (blksize / sizeof(unsigned int)) - 2;    // 1022
+    loop = (num_blocks + loop - 1) / loop;          // num of blocks which Address Area really occupied
+
+    for(i=0; i<loop; i++) {
+#ifdef MRDUMMP_DEBUG
+        MD_LOGE("%s: i(%03d) loop(%03d)\n", __func__, i, loop);
+#endif
+        if(-1 == ext4_block_lseek(fds, lba, blksize)) {
+            MD_LOGE("%s: bdata lseek64 BlockLBA failed(%d), %s\n", __func__, errno, strerror(errno));
+            mrdump_close(fds);
+            mrdump_close(fdd);
+            return false;
+        }
+        if(0 > read(fds, (void *)BlockLBA, blksize)) {
+            MD_LOGE("%s: bdata read BlockLBA error!(%d), %s\n", __func__, errno, strerror(errno));
+            mrdump_close(fds);
+            mrdump_close(fdd);
+            return false;
+        }
+
+        for(j=0; j<MRDUMP_EXT4_LBA_PER_BLOCK; j++) {
+            lba = BlockLBA[j];
+
+            if(lba == 0)
+                stop_here = true;
+
+            if(format_a_data_block(fdd, blksize, lba) < 0) {
+                MD_LOGE("%s: format_data_block error! j=%d (%d), %s\n", __func__, j, errno, strerror(errno));
+                mrdump_close(fds);
+                mrdump_close(fdd);
+                return false;
+            }
+        }
+        lba = BlockLBA[j];
+
+        if(stop_here)
+            break;
+    }
+    mrdump_close(fds);
+    mrdump_close(fdd);
+    return true;
+}
+
+static bool fs_lba_maker(const struct palloc_file *pfile, bool need_to_reinit)
+{
+#ifdef MRDUMP_DEBUG
+    MD_LOGI("%s start... need_to_reinit=(%d)\n", __func__, need_to_reinit);
+#endif
+
+    if (pfile == NULL)
+        MD_LOGE("%s pfile is null.\n", __func__);
+    else
+        MD_LOGE("%s pfile is not null.\n", __func__);
+
+    if (need_to_reinit) {
+        lba_marker_time = (uint64_t)time(NULL);
+        MD_LOGI("%s: lba_marker_time(%llx)\n", __func__, lba_marker_time);
+
+        if (!fs_lba_mark_header(pfile)) {
+            MD_LOGE("%s: mark header failed(%d), %s\n", __func__, errno, strerror(errno));
+            return false;
+        }
+
+        if (!fs_lba_mark_body(pfile)) {
+            MD_LOGE("%s: mark body failed(%d), %s\n", __func__, errno, strerror(errno));
+            return false;
+        }
+    }
+
+    if (MRDUMP_DATA_FS_F2FS == mrdump_get_data_os()) {
+        if(!mark_data_block(pfile)) {
+            MD_LOGE("%s: mark data block failed(%d), %s\n", __func__, errno, strerror(errno));
+            return false;
+        }
+    }
+
+    sync();
+#ifdef MRDUMP_DEBUG
+    MD_LOGI("%s end...\n", __func__);
+#endif
+    return true;
+}
+
+static struct palloc_file *ext4_new_fallocfile(const char *allocfile, uint64_t allocsize)
+{
+    uint64_t psize = mrdump_get_partition_free_size(MRDUMP_EXT4_MOUNT_POINT);
+    if (psize < (allocsize + MRDUMP_REST_SPACE)) {
+        MD_LOGE("Error: Partition %s has no enough free space(%" PRIu64 "MB), allocate size %" PRIu64 "MB\n",
+                MRDUMP_EXT4_MOUNT_POINT, psize / (1024 * 1024), allocsize / (1024 * 1024));
+        return NULL;
+    }
+
+    if (!ext4_fallocate(allocfile, allocsize)) {
+        MD_LOGE("%s: new fallocate failed(%d), %s\n", __func__, errno, strerror(errno));
+        return NULL;
+    }
+
+    if (!ext4_setup_attr(allocfile, true)) {
+        MD_LOGE("%s: ext4_setup_attr failed(%d), %s\n", __func__, errno, strerror(errno));
+        return NULL;
+    }
+
+    struct palloc_file *pfile = palloc_file_open(allocfile);
+    if (pfile == NULL) {
+        MD_LOGE("%s: palloc_file_open failed(%d), %s\n", __func__, errno, strerror(errno));
+        return NULL;
+    }
+
+    if (!fs_lba_maker(pfile, true)) {
+        palloc_file_close(pfile);
+        MD_LOGE("%s: lba marker failed(%d), %s\n", __func__, errno, strerror(errno));
+        return NULL;
+    }
+
+    // sync when finish lba_marker in new_fallocate.
+    sync();
+
+    return pfile;
+}
+
+static int ext4_bdata_is_ok(const struct palloc_file *pfile)
+{
+    int fds, num_blocks, j;
+    struct stat statinfo;
+    unsigned int mycrc, BlockLBA[1024];
+    unsigned int i;
+    bool stop_here = false;
+
+    // Get file statinfo
+    if(0 > fstat(pfile->fd, &statinfo)) {
+        MD_LOGE("%s: bdata stat failed(%d), %s\n", __func__, errno, strerror(errno));
+        return BDATA_STATE_FILE_ACCESS_ERROR;
+    }
+
+    // Counting blocks
+    num_blocks = (statinfo.st_size + statinfo.st_blksize - 1) / statinfo.st_blksize;
+    num_blocks = (num_blocks + MRDUMP_EXT4_LBA_PER_BLOCK - 1) / MRDUMP_EXT4_LBA_PER_BLOCK;
+
+    // open
+    char *devicenode = mrdump_get_device_node(MRDUMP_EXT4_MOUNT_POINT);
+    if(devicenode == NULL) {
+        MD_LOGE("%s: get devicenode failed(%d), %s\n", __func__, errno, strerror(errno));
+        return BDATA_STATE_FILE_ACCESS_ERROR;
+    } else {
+        fds = open(devicenode, O_RDWR);
+        if(0 > fds) {
+            free(devicenode);
+            MD_LOGE("%s: open devicenode failed(%d), %s\n", __func__, errno, strerror(errno));
+            return BDATA_STATE_FILE_ACCESS_ERROR;
+        }
+    }
+    free(devicenode);
+
+    // Check each Block (Now: InfoLBA + Address Block)
+    // 1. InfoLBA blocks
+    struct mrdump_pafile_info info;
+    if (!mrdump_read_pafile_info(pfile, fds, &info)) {
+        mrdump_close(fds);
+        return BDATA_STATE_BLOCK_HEADER_ERROR;
+    }
+
+    //get timestamp
+    bdata.timestamp = info.timestamp;
+
+    // for performance, we must finish fiemap here... but not in the loop
+    unsigned int my_num = mrdump_fiemap_total_entries(pfile->fd);
+    if(my_num == 0) {
+        MD_LOGE("%s: mrdump_fiemap_total_entries error!(%d), %s\n", __func__, errno, strerror(errno));
+        mrdump_close(fds);
+        return BDATA_STATE_BLOCK_HEADER_ERROR;
+    }
+    struct fiemap_info *myinfo = malloc(my_num * sizeof(struct fiemap_info));
+    if(!mrdump_fiemap_get_entries(pfile->fd, pfile->blksize, myinfo, my_num)) {
+        MD_LOGE("%s: mrdump_fiemap_get_entries error!(%d), %s\n", __func__, errno, strerror(errno));
+        mrdump_close(fds);
+        return BDATA_STATE_BLOCK_HEADER_ERROR;
+    }
+
+    // 2. Address Block sector wait until patch in lk
+
+    if (mount_as_ext4(MRDUMP_EXT4_MOUNT_POINT) && info.coredump_size == ULLONG_MAX) {
+        MD_LOGE("coredump size is incorrect\n");
+        return BDATA_STATE_BLOCK_DATA_ERROR;
+    }
+    for (i=1; i<(unsigned int)num_blocks; i++) {
+        unsigned int lba = (unsigned int)mrdump_fiemap_get_lba_of_block(myinfo, my_num, i);
+        if(-1 == ext4_block_lseek(fds, lba, pfile->blksize)) {
+            MD_LOGE("%s: bdata lseek64 BlockLBA failed(%d), %s\n", __func__, errno, strerror(errno));
+            free(myinfo);
+            mrdump_close(fds);
+            return BDATA_STATE_BLOCK_HEADER_ERROR;
+        }
+        if(0 > read(fds, (void *)BlockLBA, pfile->blksize)) {
+            MD_LOGE("%s: bdata read BlockLBA error!(%d), %s\n", __func__, errno, strerror(errno));
+            free(myinfo);
+            mrdump_close(fds);
+            return BDATA_STATE_BLOCK_HEADER_ERROR;
+        }
+        mycrc = crc32(0, Z_NULL, 0);
+        mycrc = crc32(mycrc, (void *)BlockLBA, (pfile->blksize-4));
+        if (mycrc != BlockLBA[1023]) {
+            MD_LOGE("%s: bdata BlockLBA %d CRC error(%08x, %08x)\n", __func__, i, mycrc, BlockLBA[1023]);
+            free(myinfo);
+            mrdump_close(fds);
+            return BDATA_STATE_BLOCK_HEADER_ERROR;
+        }
+    }
+    // data block vaildation for F2FS
+    if (mount_as_f2fs(MRDUMP_EXT4_MOUNT_POINT) && info.coredump_size == ULLONG_MAX) {
+        for(i=1; i<(unsigned int)num_blocks; i++) {
+            unsigned int lba = (unsigned int)mrdump_fiemap_get_lba_of_block(myinfo, my_num, i);
+            if(-1 == ext4_block_lseek(fds, lba, pfile->blksize)) {
+                MD_LOGE("%s: bdata lseek64 BlockLBA failed(%d), %s\n", __func__, errno, strerror(errno));
+                mrdump_close(fds);
+                return BDATA_STATE_BLOCK_DATA_ERROR;
+            }
+            if(0 > read(fds, (void *)BlockLBA, pfile->blksize)) {
+                MD_LOGE("%s: bdata read BlockLBA error!(%d), %s\n", __func__, errno, strerror(errno));
+                mrdump_close(fds);
+                return BDATA_STATE_BLOCK_DATA_ERROR;
+            }
+
+            for(j=0; j<MRDUMP_EXT4_LBA_PER_BLOCK; j++) {
+                lba = BlockLBA[j];
+
+                if(lba == 0)
+                    stop_here = true;
+
+                if(check_a_data_block(fds, pfile->blksize, lba) < 0) {
+                    MD_LOGE("%s: format_data_block error! j=%d \n", __func__, j);
+                    mrdump_close(fds);
+                    return BDATA_STATE_BLOCK_DATA_ERROR;
+                }
+            }
+            lba = BlockLBA[j];
+
+            if(stop_here)
+                break;
+        }
+    }
+
+    mrdump_close(fds);
+    free(myinfo);
+    return BDATA_STATE_CHECK_PASS;
+}
+
+////////////////////////////////////////////
+// export function (extern of other files)//
+////////////////////////////////////////////
+
+bool mrdump_file_get_info(const char *allocfile, struct mrdump_pafile_info *info)
+{
+    struct palloc_file *pfile = palloc_file_open(allocfile);
+    if (pfile == NULL) {
+        return false;
+    }
+    int device_fd = ext4_open_device_node(MRDUMP_EXT4_MOUNT_POINT);
+    if (device_fd < 0) {
+        palloc_file_close(pfile);
+        return false;
+    }
+    bool retval = mrdump_read_pafile_info(pfile, device_fd, info);
+    palloc_file_close(pfile);
+    mrdump_close(device_fd);
+    return retval;
+}
+
+static uint64_t ext4_default_filesize(int memsize)
+{
+    int ret = 0;
+    FILE *fp;
+    char c, myline[1024];
+    char *delim="\x09\x20";
+    uint64_t MySize;
+
+    fp = fopen("/proc/meminfo", "r");
+    if(fp == NULL)
+        return ret;
+
+    while(!feof(fp)) {
+
+        // getline
+        ret = fscanf(fp, "%[^\n]", myline);
+
+        // strtok strings
+        MySize = 0;
+        if(ret > 0) {
+            char *mystr = strtok(myline, delim);
+            if(mystr != NULL) {
+                if(!strcmp(mystr, "MemTotal:")) {
+                    mystr = strtok(NULL, delim);
+                    if(mystr != NULL) {
+                        MySize = atol(mystr);
+                    }
+                    break;
+                }
+            }
+        }
+
+        /* clear newline character */
+        ret = fscanf(fp, "%c", &c);
+        if (ret != 1) {
+            MD_LOGE("%s: not EOL.", __func__);
+            fclose(fp);
+            break;
+        }
+    }
+    fclose(fp);
+
+    // Count proper MySize about half size of MemTotal;
+    ret = ((MySize*1000) + (1000*1000) - 1)/(1000000);
+    if(memsize == DEFAULT_HALFMEM)
+        ret/= 2;
+    MySize = (uint64_t)ret*1024*1024;
+
+    return (MySize);
+}
+
+uint64_t mrdump_file_default_filesize(void)
+{
+    uint64_t fsize = 0;
+    int  f_mem = USERDEFINED_MEM;
+    const char *mrdump_allocate_size = sysenv_get("mrdump_allocate_size");
+
+printf("mrdump_allocate_size = %s\n", mrdump_allocate_size);
+    if ((mrdump_allocate_size == NULL) || !strncmp(mrdump_allocate_size, "fullmem", 7)) {
+        fsize = ext4_default_filesize(DEFAULT_FULLMEM);
+        f_mem = DEFAULT_FULLMEM;
+    }
+    else if ((!strncmp(mrdump_allocate_size, "0", 1))) {
+        fsize = 0;
+        f_mem = DEFAULT_DISABLE;
+    }
+    else if (!strncmp(mrdump_allocate_size, "halfmem", 7)) {
+        fsize = ext4_default_filesize(DEFAULT_HALFMEM);
+        f_mem = DEFAULT_HALFMEM;
+    }
+    // nnn or others
+    else if(f_mem == USERDEFINED_MEM) {
+        int value = atoi(mrdump_allocate_size);
+        if(value > 0) {
+            fsize = (uint64_t)value*1024*1024;
+        }
+        else {
+            fsize = 0;
+        }
+    }
+
+    return fsize;
+}
+
+////////////////////////////////////////////
+// User API                                  //
+////////////////////////////////////////////
+static bool mrdump_ext4_reinit_allocfile(const char *allocfile, uint64_t realsize, bool reset)
+{
+    int check_state = 0;
+
+    /* pre-allocate only in internal-storage:ext4 */
+    const char *output_dev = sysenv_get("mrdump_output");
+    MD_LOGI("%s: output-dev(%s)\n", __func__, output_dev);
+    if  ((output_dev != NULL) && (strcmp(output_dev, "internal-storage") != 0)) {
+        palloc_file_remove(allocfile);
+        return false;
+    }
+
+    if (realsize > 0) {
+        struct palloc_file *pfile = NULL;
+        if (!mrdump_file_is_exist(allocfile)) {
+            pfile = ext4_new_fallocfile(allocfile, realsize);
+            if (pfile == NULL) {
+                MD_LOGE("%s: fallocate failed at new creation.\n", __func__);
+                return false;
+            }
+        } else {
+            pfile = palloc_file_open(allocfile);
+            if (pfile == NULL) {
+                MD_LOGE("%s: palloc_file_open failed.\n", __func__);
+                goto cleanup;
+            }
+
+            // existed allocate
+            // Check Block Data validity
+            check_state = ext4_bdata_is_ok(pfile);
+            if ((check_state == BDATA_STATE_FILE_ACCESS_ERROR) || (check_state == BDATA_STATE_BLOCK_HEADER_ERROR)) {
+                MD_LOGI("%s: Address Blocks checked: incorrect.\n", __func__);
+                palloc_file_close(pfile);
+                palloc_file_remove(allocfile);
+                pfile = ext4_new_fallocfile(allocfile, realsize);
+                if (pfile == NULL) {
+                    MD_LOGE("%s: Address Blocks not correct, re-fallocate.\n", __func__);
+                    return false;
+                }
+            } else if (check_state == BDATA_STATE_BLOCK_DATA_ERROR) {
+                MD_LOGE("BlockData Verification failed\n");
+                if (!fs_lba_maker(pfile, true)) {
+                    MD_LOGE("%s: lba marker failed, removing pre-allocate-file.\n", __func__);
+                    palloc_file_close(pfile);
+                    goto cleanup;
+                }
+            } else {
+                MD_LOGI("%s: Address Blocks checked: correct.\n", __func__);
+                if (reset) {
+                    if (!fs_lba_maker(pfile, true)) {
+                        MD_LOGE("%s: lba marker failed, removing pre-allocate-file.\n", __func__);
+                        palloc_file_close(pfile);
+                        goto cleanup;
+                    }
+                }
+            }
+        }
+
+        int retval = fop_file_write_string(MRDUMP_EXT4_PARA_LBAOOO, "%u\n", (unsigned int) pfile->lbaooo);
+        if (retval < 0) {
+            MD_LOGE("%s: write %s failed(%d), %s\n", __func__, MRDUMP_EXT4_PARA_LBAOOO,
+                    -retval, strerror(-retval));
+            palloc_file_close(pfile);
+            goto cleanup;
+        }
+        palloc_file_close(pfile);
+        MD_LOGI("%s: LBAOOO ready\n", __func__);
+        return true;
+    }
+    MD_LOGI("Allocate size %llx set to zero, remove preallocate file\n", realsize);
+  cleanup:
+    palloc_file_remove(allocfile);
+    return false;
+}
+
+bool mrdump_file_fetch_zip_coredump(const char *outfile)
+{
+    struct mrdump_pafile_info lbainfo;
+    unsigned int       blknum;
+    unsigned int       rlba, bidx, BlockLBA[1024];
+    unsigned int       len, mylen;
+    unsigned char      MyData[MRDUMP_EXT4_EXSPACE];
+    int       fpRead, fpWrite, ret;
+
+    // outfile
+    if(outfile == NULL) {
+        MD_LOGE("%s: outfile is NULL! (%d), %s\n", __func__, errno, strerror(errno));
+        return false;
+    }
+
+    struct palloc_file *pfile = palloc_file_open(MRDUMP_EXT4_ALLOCATE_FILE);
+    if (pfile == 0) {
+        return false;
+    }
+
+    fpRead = ext4_open_device_node(MRDUMP_EXT4_MOUNT_POINT);
+    if (fpRead < -1) {
+        goto cleanup0;
+    }
+
+    if (!mrdump_read_pafile_info(pfile, fpRead, &lbainfo)) {
+        mrdump_close(fpRead);
+        goto cleanup0;
+    }
+    if (lbainfo.coredump_size == 0) {
+        MD_LOGI("Ramdump size is 0, no data to dump\n");
+        mrdump_close(fpRead);
+        goto cleanup0;
+    }
+
+    // Write handle
+    fpWrite = open(outfile, O_RDWR | O_CREAT | O_TRUNC, 0600);
+    if(0 > fpWrite) {
+        MD_LOGE("%s: open Write handle open failed(%d), %s\n", __func__, errno, strerror(errno));
+        mrdump_close(fpRead);
+        goto cleanup0;
+    }
+
+    // Init BlockLBA
+    rlba = lbainfo.addr_lba;
+    if(-1 == ext4_block_lseek(fpRead, rlba, pfile->blksize)) {
+        MD_LOGE("%s: lseek64 Init BlockLBA failed(%d), %s\n", __func__, errno, strerror(errno));
+        mrdump_close(fpRead);
+        mrdump_close(fpWrite);
+        unlink(outfile);
+        goto cleanup0;
+    }
+    if(0 > read(fpRead, BlockLBA, sizeof(BlockLBA))) {
+        MD_LOGE("%s: fetch read BlockLBA error!(%d), %s\n", __func__, errno, strerror(errno));
+        mrdump_close(fpRead);
+        mrdump_close(fpWrite);
+        unlink(outfile);
+        goto cleanup0;
+    }
+
+    // Fetching data
+    bidx    = 0;
+    rlba    =  BlockLBA[bidx];
+
+    uint64_t delaylen = 0;
+    uint64_t coresize = lbainfo.coredump_size;
+    while(coresize > 0) {
+
+        // counting coutinue datas...
+        blknum = ext4_num_to_join((unsigned int *)BlockLBA, bidx);
+        len = blknum * pfile->blksize;
+        if(coresize < len) {
+            mylen = coresize;
+        } else {
+            mylen = len;
+        }
+
+        // Reading data to MyData
+        if(-1 == ext4_block_lseek(fpRead, rlba, pfile->blksize)) {
+            MD_LOGE("%s: lseek64 Read MyData failed(%d), %s", __func__, errno, strerror(errno));
+            mrdump_close(fpRead);
+            mrdump_close(fpWrite);
+            unlink(outfile);
+            goto cleanup0;
+        }
+        if(0 > read(fpRead, MyData, mylen)) {
+            MD_LOGE("%s: fetch MyData error!(%d), %s", __func__, errno, strerror(errno));
+            mrdump_close(fpRead);
+            mrdump_close(fpWrite);
+            unlink(outfile);
+            goto cleanup0;
+        }
+        if(0 > write(fpWrite, MyData, mylen)) {
+            MD_LOGE("%s: fetch MyData error!(%d), %s", __func__, errno, strerror(errno));
+            mrdump_close(fpRead);
+            mrdump_close(fpWrite);
+            unlink(outfile);
+            goto cleanup0;
+        }
+
+        ret = ext4_get_next_bidx(fpRead, (unsigned int *)BlockLBA, bidx, pfile->blksize, blknum);
+        if(ret < 0){
+            MD_LOGE("%s: fpRead failed to get next block idx(%d), %s", __func__, errno, strerror(errno));
+            mrdump_close(fpRead);
+            mrdump_close(fpWrite);
+            unlink(outfile);
+            goto cleanup0;
+        }
+        bidx = (unsigned int)ret;
+
+        if(bidx > MRDUMP_EXT4_LBA_PER_BLOCK) {
+            mrdump_close(fpRead);
+            mrdump_close(fpWrite);
+            unlink(outfile);
+            goto cleanup0;
+        }
+        if(bidx == MRDUMP_EXT4_LBA_PER_BLOCK) {
+            ret = ext4_get_next_bidx(fpRead, (unsigned int *)BlockLBA, bidx, pfile->blksize, blknum);
+            if(ret < 0) {
+                MD_LOGE("%s: bidx(1022) failed to get next block idx(%d), %s", __func__, errno, strerror(errno));
+                mrdump_close(fpRead);
+                mrdump_close(fpWrite);
+                unlink(outfile);
+                goto cleanup0;
+            }
+            bidx = (unsigned int)ret;
+        }
+        rlba = BlockLBA[bidx];
+        coresize -= mylen;
+
+        /* flow control */
+        delaylen += mylen;
+        if (delaylen >= MRDUMP_MAX_BANDWIDTH) {
+            delaylen = 0;
+            fdatasync(fpWrite);
+            sleep(1);
+        }
+    }
+
+    mrdump_close(fpRead);
+    mrdump_close(fpWrite);
+
+    MD_LOGI("Ramdump write to %s size %" PRId64 "\n", outfile, lbainfo.coredump_size);
+    return true;
+
+  cleanup0:
+    palloc_file_close(pfile);
+    return false;
+}
+
+void mrdump_file_set_maxsize(int mrdump_size)
+{
+    palloc_file_remove(MRDUMP_EXT4_ALLOCATE_FILE);
+
+    char s[64];
+    uint64_t fsize = 0;
+
+    if(mrdump_size < 0)
+        mrdump_size = 0;
+
+    switch(mrdump_size) {
+    case DEFAULT_DISABLE:
+        sysenv_set("mrdump_allocate_size", "0");
+        fsize = 0;
+        break;
+    case DEFAULT_HALFMEM:
+        sysenv_set("mrdump_allocate_size", "halfmem");
+        fsize = mrdump_file_default_filesize();
+        break;
+    case DEFAULT_FULLMEM:
+        sysenv_set("mrdump_allocate_size", "fullmem");
+        fsize = mrdump_file_default_filesize();
+        break;
+    default:
+        snprintf(s, sizeof(s), "%d", mrdump_size);
+        sysenv_set("mrdump_allocate_size", s);
+        fsize = (uint64_t)mrdump_size*1024*1024;
+        break;
+    }
+
+    if (mrdump_ext4_reinit_allocfile(MRDUMP_EXT4_ALLOCATE_FILE, fsize, true)) {
+        printf("set MT-RAMDUMP allocated file size => %" PRIu64 " MB].\n", (fsize/1024/1024));
+    }
+}
+
+/* System startup setup
+ * Sanity check
+ * 1. Check if /data partition filesystem is supported
+ * 2. Check if lk/kernel MT-RAMDUMP support is enabled
+ * 3. Check if mrdump file size is set to non-zero
+ * If above condition is failed, remove pre-allocated file and disable MRDUMP.
+ * Try to pre-allocated file
+ * 1. If file doesn't exist, create a new file
+ * 2. If file exist and header/body corrupted, re-create a new file
+ */
+void mrdump_file_setup(bool reset)
+{
+    /* check if mount as ext4 partition or f2fs */
+    if(MRDUMP_DATA_FS_NONE == mrdump_get_data_os()) {
+        MD_LOGE("Unsupport file system type.");
+        return;
+    }
+
+    if (!mrdump_is_supported()) {
+        MD_LOGE("MRDUMP is not supported\n");
+        palloc_file_remove(MRDUMP_EXT4_ALLOCATE_FILE);
+        return;
+    }
+
+    mrdump_ext4_reinit_allocfile(MRDUMP_EXT4_ALLOCATE_FILE, mrdump_file_default_filesize(), reset);
+}
diff --git a/src/devtools/mrdump/mrdump_tool_source/mrdump_support_ext4.h b/src/devtools/mrdump/mrdump_tool_source/mrdump_support_ext4.h
new file mode 100644
index 0000000..eb05c11
--- /dev/null
+++ b/src/devtools/mrdump/mrdump_tool_source/mrdump_support_ext4.h
@@ -0,0 +1,80 @@
+#pragma once
+
+/* debug on/off */
+#define MRDUMP_DEBUG
+
+/* for statfs */
+#define EXT3_SUPER_MAGIC 0xEF53
+
+/* Variables defined */
+#define AE_DUMPSYS_DATA_PATH        "/log/mtklog"
+#define MRDUMP_EXT4_NEW_FILE        true
+#define MRDUMP_EXT4_OLD_FILE        false
+#define MRDUMP_EXT4_PARA_LBAOOO     "/sys/module/mrdump/parameters/lbaooo"
+#define MRDUMP_EXT4_PROC_MOUNTS     "/proc/self/mounts"
+//#define MRDUMP_EXT4_MOUNT_POINT     "/data"
+#define MRDUMP_EXT4_MOUNT_POINT     "/log"
+#define MRDUMP_EXT4_ALLOCATE_FILE   AE_DUMPSYS_DATA_PATH"/mrdump_preallocated"
+#define MRDUMP_EXT4_MIN_ALLOCATE    256
+#define MRDUMP_EXT4_BLKSIZE         4096
+#define MRDUMP_EXT4_MAX_CONTINUE    64
+#define MRDUMP_EXT4_EXSPACE         (MRDUMP_EXT4_BLKSIZE*MRDUMP_EXT4_MAX_CONTINUE)
+#define MRDUMP_EXT4_LBA_PER_BLOCK   1022
+
+/*
+ * v1: support allocate size > 4G
+ * v2: support timestamp
+ */
+#define MRDUMP_PAF_VERSION 0x0002
+
+#define MRDUMP_PAF_INFO_LBA      4
+#define MRDUMP_PAF_ADDR_LBA      8
+#define MRDUMP_PAF_ALLOCSIZE    12
+#define MRDUMP_PAF_COREDUMPSIZE 20
+#define MRDUMP_PAF_TIMESTAMP    28
+#define MRDUMP_PAF_CRC32        36
+#define MRDUMP_LBA_DATAONLY     MRDUMP_PAF_CRC32
+#define MRDUMP_PAF_TOTAL_SIZE   40
+
+typedef enum {
+    DEFAULT_DISABLE,
+    DEFAULT_HALFMEM,
+    DEFAULT_FULLMEM,
+    USERDEFINED_MEM
+} MRDUMP_DEFAULT_SIZE;
+
+typedef enum {
+    BDATA_STATE_CHECK_PASS,
+    BDATA_STATE_FILE_ACCESS_ERROR,
+    BDATA_STATE_BLOCK_HEADER_ERROR,
+    BDATA_STATE_BLOCK_DATA_ERROR,
+} MRDUMP_BDATA_STATE;
+
+/* for chattr */
+#define FS_IOC_GETFLAGS                 _IOR('f', 1, long)
+#define FS_IOC_SETFLAGS                 _IOW('f', 2, long)
+#define FS_SECRM_FL                     0x00000001 /* Secure deletion */
+#define FS_IMMUTABLE_FL                 0x00000010 /* Immutable file */
+
+struct mrdump_pafile_info {
+    uint32_t info_lba;
+    uint32_t addr_lba;
+    uint64_t filesize;
+    uint64_t coredump_size;
+    uint64_t timestamp;
+};
+
+struct __attribute__((__packed__)) marked_block_data {
+    uint32_t lba;
+    uint64_t zero_padding[510];
+    uint64_t timestamp;
+    uint32_t crc;
+};
+
+/* Function Prototypes */
+void mrdump_file_set_maxsize(int mrdump_size);
+void mrdump_file_setup(bool reset);
+bool mrdump_file_get_info(const char *allocfile, struct mrdump_pafile_info *info);
+uint64_t mrdump_file_default_filesize(void);
+bool mrdump_file_fetch_zip_coredump(const char *outfile);
+bool mount_as_ext4(const char *mountp);
diff --git a/src/devtools/mrdump/mrdump_tool_source/mrdump_support_f2fs.c b/src/devtools/mrdump/mrdump_tool_source/mrdump_support_f2fs.c
new file mode 100644
index 0000000..dbeb418
--- /dev/null
+++ b/src/devtools/mrdump/mrdump_tool_source/mrdump_support_f2fs.c
@@ -0,0 +1,94 @@
+/* uint64_t ...*/
+#include <inttypes.h>
+#include <stdbool.h>
+
+/* statfs() */
+#include <sys/statfs.h>
+
+/* mmap, munmap */
+#include <sys/mman.h>
+
+/* fsync(), close(), write(), unlink() */
+#include <unistd.h>
+
+/* __u32 */
+#include <linux/types.h>
+
+/* ioctl */
+//#include <linux/fs.h>
+#include <sys/ioctl.h>
+
+/* struct stat, open */
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+/* strerror */
+#include <string.h>
+/* errno */
+#include <errno.h>
+
+/* mrdump related */
+#include "mrdump_log.h"
+#include "mrdump_common.h"
+#include "mrdump_support_f2fs.h"
+
+bool mount_as_f2fs(const char *mountp)
+{
+    struct statfs fs;
+    if(statfs(mountp, &fs) == 0) {
+        if(fs.f_type == F2FS_SUPER_MAGIC)
+            return true;
+
+        return false;
+    }
+    MD_LOGE("%s: %s statfs error.\n", __func__, mountp);
+    return false;
+}
+
+bool f2fs_fallocate(const char *allocfile, uint64_t allocsize)
+{
+    int i;
+    int zQ = (allocsize + F2FS_MAPSIZE - 1) / F2FS_MAPSIZE;
+    unsigned int val;
+
+    if((allocfile == NULL) || (allocsize == 0)) {
+        MD_LOGE("%s: allocfile is NULL or allocsize = %" PRIu64 "\n", __func__, allocsize);
+        return false;
+    }
+
+    void *ZeroPage = mmap(NULL, F2FS_MAPSIZE, PROT_READ, MAP_ANONYMOUS | MAP_SHARED, -1, 0);
+    if(ZeroPage == NULL) {
+        MD_LOGE("%s: ZeroPage map failed.\n", __func__);
+        return false;
+    }
+
+    int fd = open(allocfile, O_RDWR | O_CREAT, 0400);
+    if(0 > fd) {
+        MD_LOGE("%s: open fd failed.\n", __func__);
+        munmap(ZeroPage, F2FS_MAPSIZE);
+        return false;
+    }
+
+    for(i=0; i<zQ; i++) {
+        if(0 >= write(fd,ZeroPage, F2FS_MAPSIZE)) {
+            MD_LOGE("%s: ZeroPage write failed\n", __func__);
+            close(fd);
+            unlink(allocfile);
+            munmap(ZeroPage, F2FS_MAPSIZE);
+            return false;
+        }
+    }
+
+    fsync(fd);
+
+    val = 31337;
+    if(0 > ioctl(fd, F2FS_IOC_SET_PIN_FILE, &val)) {
+        MD_LOGE("%s: F2FS_IOC_SET_PIN_FILE(%u) failed(%d), %s\n", __func__, val, errno, strerror(errno));
+    }
+
+    close(fd);
+    munmap(ZeroPage, F2FS_MAPSIZE);
+
+    return true;
+}
diff --git a/src/devtools/mrdump/mrdump_tool_source/mrdump_support_f2fs.h b/src/devtools/mrdump/mrdump_tool_source/mrdump_support_f2fs.h
new file mode 100644
index 0000000..8408201
--- /dev/null
+++ b/src/devtools/mrdump/mrdump_tool_source/mrdump_support_f2fs.h
@@ -0,0 +1,14 @@
+#pragma once
+
+/* Map size for ZeroPage */
+#define F2FS_SUPER_MAGIC 0xF2F52010
+#define F2FS_MAPSIZE (1024*1024*8)
+
+/* for IOCTL */
+#define F2FS_IOCTL_MAGIC        0xf5
+#define F2FS_IOC_SET_PIN_FILE   _IOW(F2FS_IOCTL_MAGIC, 13, __u32)
+#define F2FS_IOC_GET_PIN_FILE   _IOR(F2FS_IOCTL_MAGIC, 14, __u32)
+
+/* Function Prototypes */
+bool mount_as_f2fs(const char *mountp);
+bool f2fs_fallocate(const char *allocfile, uint64_t allocsize);
diff --git a/src/devtools/mrdump/mrdump_tool_source/mrdump_support_fiemap.c b/src/devtools/mrdump/mrdump_tool_source/mrdump_support_fiemap.c
new file mode 100644
index 0000000..9481947
--- /dev/null
+++ b/src/devtools/mrdump/mrdump_tool_source/mrdump_support_fiemap.c
@@ -0,0 +1,194 @@
+/* printf */
+#include <stdio.h>
+
+/* uint64_t ...*/
+#include <inttypes.h>
+#include <stdbool.h>
+
+/* malloc */
+#include <stdlib.h>
+
+/* ioctl, fibmap, fiemap */
+#include <linux/fs.h>
+#include <sys/ioctl.h>
+#include <linux/fiemap.h>
+
+/* strerror */
+#include <string.h>
+/* errno */
+#include <errno.h>
+
+/* mrdump related */
+#include "mrdump_log.h"
+#include "mrdump_common.h"
+#include "mrdump_support_fiemap.h"
+
+static unsigned int fiemap_total_entries(int fd)
+{
+    int is_last, rows = 0;
+
+    // Preparing for fiemap work
+    struct fiemap *fiemap = (struct fiemap *)malloc(sizeof(struct fiemap) + sizeof(struct fiemap_extent));
+    unsigned long long lstart = 0;             // logical input mapping star
+#if (__LP64__)
+    unsigned long long llength = ~0ULL;        // logical input mapping length
+#else
+    unsigned long long llength = 0xFFFFFFFF;
+#endif
+
+    fiemap->fm_start = lstart;
+    fiemap->fm_length = llength;
+    fiemap->fm_flags = 0;
+    fiemap->fm_extent_count = 1;
+    fiemap->fm_mapped_extents = 0;   // output only
+
+    do {
+        if(0 > ioctl(fd, FS_IOC_FIEMAP, (unsigned long)fiemap)) {
+            MD_LOGE("%s: FIEMAP ioctl failed!(%d), %s\n", __func__, errno, strerror(errno));
+            return 0;
+        }
+
+        if(fiemap->fm_mapped_extents == 0) {
+            MD_LOGE("%s: FIEMAP: fm_mapped_extents = 0(%d), %s\n", __func__, errno, strerror(errno));
+            return 0;
+        }
+
+        // check if the last extent
+        is_last = fiemap->fm_extents[0].fe_flags & FIEMAP_EXTENT_LAST;
+
+        // Set up the next call arguments
+        if(!is_last) {
+            unsigned long long foo = fiemap->fm_extents[0].fe_logical + fiemap->fm_extents[0].fe_length;
+            fiemap->fm_start = foo;
+            fiemap->fm_length = lstart + llength - foo;
+            fiemap->fm_flags = 0;
+            fiemap->fm_extent_count = 1;
+        }
+
+        rows++;
+
+    } while(!is_last);
+
+    free(fiemap);
+    return rows;
+
+}
+
+static bool fiemap_get_entries(int fd, unsigned int blksize, struct fiemap_info *mapinfo, unsigned int rows)
+{
+    struct fiemap_info *myinfo = mapinfo;
+
+    // Preparing for fiemap work
+    struct fiemap *fiemap = (struct fiemap *)malloc(sizeof(struct fiemap) + sizeof(struct fiemap_extent));
+    unsigned long long lstart = 0;             // logical input mapping star
+#if (__LP64__)
+    unsigned long long llength = ~0ULL;        // logical input mapping length
+#else
+    unsigned long long llength = 0xFFFFFFFF;
+#endif
+
+    fiemap->fm_start = lstart;
+    fiemap->fm_length = llength;
+    fiemap->fm_flags = 0;
+    fiemap->fm_extent_count = 1;
+    fiemap->fm_mapped_extents = 0;   // output only
+
+    unsigned int i, is_last;
+    for(i=0; i<rows; i++)
+    {
+        if(0 > ioctl(fd, FS_IOC_FIEMAP, (unsigned long)fiemap)) {
+            printf("%s: FIEMAP ioctl failed!(%d), %s\n", __func__, errno, strerror(errno));
+            return false;
+        }
+
+        // check if the last extent
+        is_last = fiemap->fm_extents[0].fe_flags & FIEMAP_EXTENT_LAST;
+
+        // basic parameters
+        myinfo->lba = (unsigned int)(fiemap->fm_extents[0].fe_physical/blksize);
+        myinfo->tot = (unsigned int)(fiemap->fm_extents[0].fe_length / blksize);
+
+        // Set up the next call arguments
+        if(!is_last) {
+
+            unsigned long long foo = fiemap->fm_extents[0].fe_logical + fiemap->fm_extents[0].fe_length;
+            fiemap->fm_start = foo;
+            fiemap->fm_length = lstart + llength - foo;
+            fiemap->fm_flags = 0;
+            fiemap->fm_extent_count = 1;
+        }
+        myinfo++;
+    }
+    free(fiemap);
+    return true;
+}
+
+static int fiemap_get_entry_lba(int fd, unsigned int blksize, unsigned int rows)
+{
+    unsigned int lba = 0, num = (unsigned int)fiemap_total_entries(fd);
+    if(num > 0) {
+        struct fiemap_info *myinfo = malloc(num * sizeof(struct fiemap_info));
+        if(fiemap_get_entries(fd, blksize, myinfo, num)) {
+            lba = myinfo[rows].lba;
+            free(myinfo);
+            return (int)lba;
+        }
+        free(myinfo);
+    }
+    return -1;
+}
+
+static int fiemap_get_entry_tot(int fd, unsigned int blksize, unsigned int rows)
+{
+    unsigned int tot = 0, num = (unsigned int)fiemap_total_entries(fd);
+    if(num > 0) {
+        struct fiemap_info *myinfo = malloc(num * sizeof(struct fiemap_info));
+        if(fiemap_get_entries(fd, blksize, myinfo, num)) {
+            tot = myinfo[rows].tot;
+            free(myinfo);
+            return (int)tot;
+        }
+        free(myinfo);
+    }
+    return -1;
+}
+
+static unsigned int fiemap_get_lba_of_block(struct fiemap_info *myinfo, unsigned int rows, unsigned int block)
+{
+    unsigned int i, lba = 0, tot = 0;
+    for(i=0; i<rows; i++) {
+        tot = myinfo[i].tot;
+        if(block < tot) {
+            lba = myinfo[i].lba + block;
+            break;
+        } else {
+            block = block - tot;
+        }
+    }
+    return lba;
+}
+
+unsigned int mrdump_fiemap_total_entries(int fd)
+{
+    return fiemap_total_entries(fd);
+}
+
+bool mrdump_fiemap_get_entries(int fd, unsigned int blksize, struct fiemap_info *mapinfo, unsigned int rows)
+{
+    return fiemap_get_entries(fd, blksize, mapinfo, rows);
+}
+
+unsigned int mrdump_fiemap_get_lba_of_block(struct fiemap_info *myinfo, unsigned int num, unsigned int block)
+{
+    return fiemap_get_lba_of_block(myinfo, num, block);
+}
+
+int mrdump_fiemap_get_entry_tot(int fd, unsigned int blksize, unsigned int rows)
+{
+    return fiemap_get_entry_tot(fd, blksize, rows);
+}
+
+int mrdump_fiemap_get_entry_lba(int fd, unsigned int blksize, unsigned int rows)
+{
+    return fiemap_get_entry_lba(fd, blksize, rows);
+}
diff --git a/src/devtools/mrdump/mrdump_tool_source/mrdump_support_fiemap.h b/src/devtools/mrdump/mrdump_tool_source/mrdump_support_fiemap.h
new file mode 100644
index 0000000..f703a13
--- /dev/null
+++ b/src/devtools/mrdump/mrdump_tool_source/mrdump_support_fiemap.h
@@ -0,0 +1,13 @@
+#pragma once
+
+struct fiemap_info {
+    __u32 lba;
+    __u32 tot;
+};
+
+/* Function Prototypes */
+unsigned int mrdump_fiemap_total_entries(int fd);
+bool mrdump_fiemap_get_entries(int fd, unsigned int blksize, struct fiemap_info *mapinfo, unsigned int rows);
+unsigned int mrdump_fiemap_get_lba_of_block(struct fiemap_info *myinfo, unsigned int num, unsigned int block);
+int mrdump_fiemap_get_entry_tot(int fd, unsigned int blksize, unsigned int rows);
+int mrdump_fiemap_get_entry_lba(int fd, unsigned int blksize, unsigned int rows);
diff --git a/src/devtools/mrdump/mrdump_tool_source/mrdump_support_mpart.c b/src/devtools/mrdump/mrdump_tool_source/mrdump_support_mpart.c
new file mode 100644
index 0000000..924e00c
--- /dev/null
+++ b/src/devtools/mrdump/mrdump_tool_source/mrdump_support_mpart.c
@@ -0,0 +1,281 @@
+/* strlcpy */
+#include <stdio.h>
+
+/* uint64_t ...*/
+#include <inttypes.h>
+#include <stdbool.h>
+
+/* struct stat, lstat, open, execl */
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+/* malloc */
+#include <stdlib.h>
+
+/* strlen */
+#include <string.h>
+
+/* strerror */
+#include <string.h>
+/* errno */
+#include <errno.h>
+
+/* bzero */
+#include <strings.h>
+
+/* ctime */
+#include <time.h>
+
+/* crc32, Z_NULL */
+#include <zlib.h>
+
+/* mrdump related */
+#include "mrdump_log.h"
+#include "mrdump_common.h"
+#include "mrdump_support_mpart.h"
+#include "mrdump_support_ext4.h"
+
+/* remember to free the fullpath when it is no longer required. */
+static char *get_partition_fullpath(char *linkfile)
+{
+    struct stat sa;
+    if (lstat(linkfile, &sa) == -1) {
+        MD_LOGE("%s: no mrdump partition.\n", __func__);
+        return NULL;
+    }
+
+    char *fullpath = malloc(sa.st_size + 1);
+    if (fullpath == NULL) {
+        MD_LOGE("%s: insufficient memory\n");
+        return NULL;
+    }
+
+    ssize_t r = readlink(linkfile, fullpath, sa.st_size + 1);
+    if (r == -1) {
+        MD_LOGE("%s: lstat\n");
+        return NULL;
+    }
+    if (r > sa.st_size) {
+        MD_LOGE("%s: symlink increased in size "
+                    "between lstat() and readlink()\n");
+        return NULL;
+    }
+    fullpath[r] = '\0';
+
+    return fullpath;
+}
+
+/* remember to free the pname when it is no longer required. */
+static char *get_partition_name(char *fullpath)
+{
+    char *pfile = fullpath;
+    if (!pfile) {
+        MD_LOGE("%s: no such full path\n", __func__);
+        return NULL;
+    }
+    int len = strlen(pfile);
+
+    char *pname = malloc(len);
+    if (pname == NULL) {
+        MD_LOGE("%s: malloc failed\n", __func__);
+        return NULL;
+    }
+
+    char *delim="\x2f";
+    char *p = strtok(pfile, delim);
+    while (p) {
+        p = strtok(NULL, delim);
+        if (p) {
+            bzero(pname, len);
+            strlcpy(pname, p, len);
+        }
+    }
+
+    return pname;
+}
+
+int mrdump_check_partition(void)
+{
+    /* get real path from symbolic file */
+    char *fullpath = get_partition_fullpath(MRDUMP_MPART_PARTITION);
+    if (!fullpath) {
+        MD_LOGE("%s: no such fullpath\n", __func__);
+        return 0;
+    }
+
+    /* get device node name */
+    char *realpath= strdup(fullpath);
+    char *pname = get_partition_name(realpath);
+    if (!pname) {
+        MD_LOGE("%s: cannot get the partition name\n", __func__);
+        return 0;
+    }
+    free(realpath);
+
+    /* get partition size */
+    uint64_t psize = mrdump_get_partition_size(fullpath);
+
+    /* get stat */
+    struct stat sb;
+    if (stat(fullpath, &sb) == -1) {
+        MD_LOGE("%s: no mrdump partition.\n", __func__);
+        return 0;
+    }
+
+    /* parition informations, reference to EXAMPLE of manpage STAT(2) */
+    MD_LOGI("PARTNAME:                  mrdump\n");
+    MD_LOGI("LINK:                      %s\n", MRDUMP_MPART_PARTITION);
+    MD_LOGI("Partition size:            %llu bytes\n", psize);
+    MD_LOGI("DEVNAME:                   %s\n", pname);
+    MD_LOGI("FILE:                      %s\n", fullpath);
+    MD_LOGI("File type:                 ");
+    switch (sb.st_mode & S_IFMT) {
+        case S_IFBLK:  MD_LOGI("block device\n");            break;
+        case S_IFCHR:  MD_LOGI("character device\n");        break;
+        case S_IFDIR:  MD_LOGI("directory\n");               break;
+        case S_IFIFO:  MD_LOGI("FIFO/pipe\n");               break;
+        case S_IFLNK:  MD_LOGI("symlink\n");                 break;
+        case S_IFREG:  MD_LOGI("regular file\n");            break;
+        case S_IFSOCK: MD_LOGI("socket\n");                  break;
+        default:       MD_LOGI("unknown?\n");                break;
+    }
+    MD_LOGI("Ownership:                 UID=%ld   GID=%ld\n",
+            (long) sb.st_uid, (long) sb.st_gid);
+    MD_LOGI("I-node number:             %ld\n", (long) sb.st_ino);
+    MD_LOGI("Mode:                      %lo (octal)\n", (unsigned long) sb.st_mode);
+    MD_LOGI("Link count:                %ld\n", (long) sb.st_nlink);
+    MD_LOGI("Last status change:        %s\n", ctime(&sb.st_ctime));
+    MD_LOGI("Last file access:          %s\n", ctime(&sb.st_atime));
+    MD_LOGI("Last file modification:    %s\n", ctime(&sb.st_mtime));
+
+    /* remember to free fullpath and pname */
+    free(fullpath);
+    free(pname);
+
+    return 1;
+}
+
+static bool mrdump_read_pafile_info(int device_fd,
+                                    struct mrdump_pafile_info *info)
+{
+    memset(info, 0, sizeof(struct mrdump_pafile_info));
+
+    uint8_t block0[MRDUMP_PAF_TOTAL_SIZE];
+    memset(block0, 0, sizeof(block0));
+
+    if (0 > read(device_fd, block0, sizeof(block0))) {
+        MD_LOGE("%s: read InfoLBA error (%d), %s\n", __func__, errno, strerror(errno));
+        return false;
+    }
+
+    uint8_t *bufp = &block0[0];
+    unsigned int crcval = crc32(0, Z_NULL, 0);
+    crcval = crc32(crcval, (void *)block0, MRDUMP_LBA_DATAONLY);
+    if (crcval != *(uint32_t *)&block0[MRDUMP_PAF_CRC32]) {
+        MD_LOGE("%s: LBA info CRC error (c:%08x, v:%08x)\n", __func__, crcval,
+                *(uint32_t *)&block0[MRDUMP_PAF_CRC32]);
+        return false;
+    }
+
+    info->coredump_size = *(uint64_t *)(bufp + MRDUMP_PAF_COREDUMPSIZE);
+    return true;
+}
+
+bool mrdump_file_fetch_zip_coredump_partition(const char *outfile)
+{
+    struct mrdump_pafile_info lbainfo;
+    unsigned int       read_len, mylen;
+    unsigned char      MyData[MRDUMP_PARTITION_EXSPACE];
+    int                fpRead, fpWrite;
+
+    // outfile
+    if(outfile == NULL) {
+        MD_LOGE("%s: outfile is NULL! (%d), %s\n", __func__, errno, strerror(errno));
+        return false;
+    }
+
+    fpRead = open(MRDUMP_MPART_PARTITION, O_RDWR);
+    if (fpRead < 0) {
+        MD_LOGE("mrdump partition %s open failed (%d), %s\n",
+                MRDUMP_MPART_PARTITION, errno, strerror(errno));
+        return false;
+    }
+
+    if (!mrdump_read_pafile_info(fpRead, &lbainfo)) {
+        mrdump_close(fpRead);
+        return false;
+    }
+
+    if (lbainfo.coredump_size == 0) {
+        MD_LOGI("mrdump partition have 0 size dump, no data to dump\n");
+        mrdump_close(fpRead);
+        return false;
+    }
+
+    // Write handle
+    fpWrite = open(outfile, O_RDWR | O_CREAT | O_TRUNC, 0600);
+    if(0 > fpWrite) {
+        MD_LOGE("%s: open Write handle open failed(%d), %s\n", __func__, errno, strerror(errno));
+        mrdump_close(fpRead);
+        goto cleanup0;
+    }
+
+    uint64_t delaylen = 0;
+    uint64_t coresize = lbainfo.coredump_size;
+
+    if (lseek64(fpRead, BLK_SIZE, SEEK_SET) == (off64_t) -1) {
+        MD_LOGE("%s: lseek64 Read MyData failed(%d), %s", __func__, errno, strerror(errno));
+        mrdump_close(fpRead);
+        mrdump_close(fpWrite);
+        unlink(outfile);
+        goto cleanup0;
+    }
+
+    while(coresize > 0) {
+
+        // counting coutinue datas...
+        read_len = MAX_READ_BLK * BLK_SIZE;
+
+        if(coresize < read_len) {
+            mylen = coresize;
+        } else {
+            mylen = read_len;
+        }
+
+        if(0 > read(fpRead, MyData, mylen)) {
+            MD_LOGE("%s: fetch MyData error!(%d), %s", __func__, errno, strerror(errno));
+            mrdump_close(fpRead);
+            mrdump_close(fpWrite);
+            unlink(outfile);
+            goto cleanup0;
+        }
+        if(0 > write(fpWrite, MyData, mylen)) {
+            MD_LOGE("%s: fetch MyData error!(%d), %s", __func__, errno, strerror(errno));
+            mrdump_close(fpRead);
+            mrdump_close(fpWrite);
+            unlink(outfile);
+            goto cleanup0;
+        }
+
+        coresize -= mylen;
+
+        /* flow control */
+        delaylen += mylen;
+        if (delaylen >= MRDUMP_MAX_BANDWIDTH) {
+            delaylen = 0;
+            fdatasync(fpWrite);
+            sleep(1);
+        }
+    }
+
+    mrdump_close(fpRead);
+    mrdump_close(fpWrite);
+
+    MD_LOGI("Ramdump write to %s size %" PRId64 "\n", outfile, lbainfo.coredump_size);
+    return true;
+
+  cleanup0:
+    return false;
+}
diff --git a/src/devtools/mrdump/mrdump_tool_source/mrdump_support_mpart.h b/src/devtools/mrdump/mrdump_tool_source/mrdump_support_mpart.h
new file mode 100644
index 0000000..fb791f0
--- /dev/null
+++ b/src/devtools/mrdump/mrdump_tool_source/mrdump_support_mpart.h
@@ -0,0 +1,12 @@
+#pragma once
+
+/* for DPART(Dedicated Partition) solution */
+#define MRDUMP_MPART_PARTITION "/dev/block/platform/bootdevice/by-name/mrdump"
+#define MRDUMP_MPART_START_OFFSET 4096
+#define MAX_READ_BLK 64
+#define BLK_SIZE 4096
+#define MRDUMP_PARTITION_EXSPACE MAX_READ_BLK*BLK_SIZE
+
+/* functions */
+int mrdump_check_partition(void);
+bool mrdump_file_fetch_zip_coredump_partition(const char *outfile);
diff --git a/src/devtools/mrdump/mrdump_tool_source/mrdump_tool.c b/src/devtools/mrdump/mrdump_tool_source/mrdump_tool.c
new file mode 100644
index 0000000..085aabe
--- /dev/null
+++ b/src/devtools/mrdump/mrdump_tool_source/mrdump_tool.c
@@ -0,0 +1,405 @@
+/* uint64_t ...*/
+#include <inttypes.h>
+#include <stdbool.h>
+
+/* vprintf, vsnprintf */
+#include <stdio.h>
+
+/* exit(), atoi() */
+#include <stdlib.h>
+
+/* getopt, optind */
+#include <unistd.h>
+
+/* sysenv */
+#include "mrdump_defaults.h"
+
+/* strcmp */
+#include <string.h>
+
+#include <getopt.h>
+/* mrdump related */
+#include "mrdump_log.h"
+#include "mrdump_common.h"
+#include "mrdump_status.h"
+#include "mrdump_support_ext4.h"
+#include "mrdump_support_mpart.h"
+
+static void usage(const char *prog) __attribute__((noreturn));
+static void usage(const char *prog)
+{
+    printf("Usage\n"
+        "\t%1$s is-supported\n\n"
+        "\t%1$s status-get\n"
+        "\t%1$s status-log\n"
+        "\t%1$s status-clear\n\n"
+        "\t%1$s file-setup\n"
+        "\t%1$s file-allocate n\n"
+        "\t\tn is 0(disable file output) 1(halfmem) 2(fullmem) >256\n"
+        "\t%1$s file-extract-core [-r] filename\n"
+        "\t\t-r\tre-init pre-allocated file\n"
+        "\t%1$s file-info\n\n"
+        "\t%1$s mem-size-set n\n"
+        "\t\tn is between 64 ~ 16384(m), 0 is output total-mem-size\n\n"
+        "\t%1$s output-set output\n"
+        "\t\tsetting mrdump output device\n"
+        "\t\t  none\n"
+        "\t\t  null\n"
+        "\t\t  usb\n"
+        "\t\t  partition: mrdump partition\n"
+        "\t\t  internal-storage: ext4, f2fs\n"
+        "\t%1$s output-get\n"
+        "\t\tgetting mrdump output device\n",
+        prog);
+    exit(1);
+}
+
+static void dump_status_ok(const struct mrdump_status_result *result)
+{
+    printf("Ok\n");
+    printf("\tMode: %s\n\tOutput: ", result->mode);
+
+    switch (result->output) {
+    case MRDUMP_OUTPUT_NULL:
+        printf("null\n");
+        break;
+    case MRDUMP_OUTPUT_USB:
+        printf("usb\n");
+        break;
+    case MRDUMP_OUTPUT_DATA_FS:
+        printf("ext4/data partition\n");
+        break;
+    case MRDUMP_OUTPUT_PARTITION:
+        printf("dynamic mrdump partition\n");
+        break;
+    default:
+        printf("not supported\n");
+        break;
+    }
+}
+
+static int file_setup_command(int argc, char * __attribute__((unused)) argv[])
+{
+    if (!mrdump_is_supported()) {
+        error("file-setup not allowed in this mode.\n");
+    }
+    if (argc != 1) {
+        error("Invalid file-setup command argument\n");
+    }
+    mrdump_file_setup(false);
+    return 0;
+}
+
+static void file_allocate_command(int argc, char *argv[])
+{
+    if (!mrdump_is_supported()) {
+        error("file-allocate not allowed in this mode.\n");
+    }
+    if (argc != 2) {
+        error("Invaid file-allocate command argument\n");
+    }
+    int size_m = atoi(argv[1]);
+
+    if (size_m < 0)
+        size_m = 0;
+
+    if ((size_m <= 2) || (size_m >= MRDUMP_EXT4_MIN_ALLOCATE)) {
+        // enable condition: only 0, 1, 2, >256
+        mrdump_file_set_maxsize(size_m);
+    }
+    else {
+        error("Invalid dump size %d\n", size_m);
+    }
+}
+
+static int file_extract_core_command(int argc, char *argv[])
+{
+    int opt;
+    bool reinit = false;
+
+    while ((opt = getopt(argc, argv, "r")) != -1) {
+        switch (opt) {
+        case 'r':
+            reinit = true;
+            break;
+        default:
+            error("Invalid file-extract-core parameter\n");
+        }
+    }
+    if (optind >= argc) {
+        error("Expected filename after options\n");
+    }
+
+    const char *fn = argv[optind];
+    const char *output_dev = sysenv_get("mrdump_output");
+
+    MD_LOGI("%s: mrdump_output= %s\n",__func__, output_dev);
+
+    if (output_dev) {
+        if (!strncmp(output_dev, "partition", 9)) {
+            MD_LOGI("%s: partition solution\n",__func__);
+            if (mrdump_file_fetch_zip_coredump_partition(fn)) {
+                return 0;
+            }
+        }
+        else if (!strncmp(output_dev, "internal-storage", 21)) {
+            MD_LOGI("%s: ext4 solution\n",__func__);
+            if (mrdump_file_fetch_zip_coredump(fn)) {
+                mrdump_file_setup(reinit);
+                return 0;
+            }
+        }
+    }
+
+    error("Fetching Coredump data failed\n");
+}
+
+static int file_info_command(int argc, char * __attribute__((unused)) argv[])
+{
+    if (argc != 1) {
+        error("Invalid file-info command argument\n");
+    }
+
+    struct mrdump_pafile_info info;
+    if (mrdump_file_get_info(MRDUMP_EXT4_ALLOCATE_FILE, &info)) {
+        printf("\tInfo LBA :      %" PRIu32 "\n"
+               "\tAddress LBA :   %" PRIu32 "\n"
+               "\tFile Size :     %" PRIu64 "\n"
+               "\tCoredump Size : %" PRIu64 "\n"
+               "\tTimestamp :     %" PRIx64 "\n",
+               info.info_lba, info.addr_lba,
+               info.filesize, info.coredump_size,
+               info.timestamp
+              );
+        return 0;
+    }
+    else {
+        error("Cannot get pre-allocate file info\n");
+    }
+}
+
+static void mem_size_set_command(int argc, char *argv[])
+{
+    char msize[5];
+
+    if (argc != 2) {
+        error("Invaid mem-size-set command argument\n");
+    }
+
+    int size_m = atoi(argv[1]);
+    if ((size_m == 0) || ((size_m >= 64) && (size_m <= 16 * 1024))) {
+        if (size_m != 0) {
+            snprintf(msize, sizeof(msize), "%d", size_m);
+            if (sysenv_set("mrdump_mem_size", msize) == 0) {
+                MD_LOGI("mem-size-set done.\n");
+            }
+        }
+        else {
+            if (sysenv_set("mrdump_mem_size", "") == 0) {
+                MD_LOGI("total-mem-size done.\n");
+            }
+            else {
+                error("failed to set memory dump size, plz try again later.\n");
+            }
+        }
+    }
+    else {
+        error("Invalid memory dump size\n");
+    }
+}
+
+static void output_set_command(int argc, char *argv[])
+{
+    if(argc < 2) {
+        error("Invalid output device, valid input [none, null, usb, partition, internal-storage]\n");
+    }
+    else {
+        const char *output_dev = argv[1];
+        int need_reboot = 0;
+        const char *prev_output_dev = sysenv_get("mrdump_output");
+
+        if (strcmp(prev_output_dev, "partition") == 0) {
+            if (mrdump_check_partition()) {
+                need_reboot = 1;
+            }
+        }
+        if (strcmp(output_dev, "partition") == 0) {
+            if (!mrdump_check_partition()) {
+                error("mrdump partition doesn't exist, cannot dump to partition.\n");
+            }
+            need_reboot = 1;
+        }
+        else if (strcmp(output_dev, "none") &&
+                 strcmp(output_dev, "null") &&
+                 strcmp(output_dev, "usb") &&
+                 strcmp(output_dev, "internal-storage")) {
+            error("Unknown output %s\n", output_dev);
+        }
+
+        if (sysenv_set("mrdump_output", output_dev) == 0) {
+            MD_LOGI("mrdump_output = %s\n", output_dev);
+        }
+        else {
+            error("output-set failed.(%s)\n", output_dev);
+        }
+
+        if (strcmp(output_dev, "internal-storage") == 0) {
+            mrdump_file_set_maxsize(DEFAULT_FULLMEM);
+        }
+        else {
+            mrdump_file_set_maxsize(DEFAULT_DISABLE);
+        }
+
+        if (need_reboot) {
+            if (0 > execl("/system/bin/reboot", "reboot", NULL, NULL))
+                error("%s: failed to reboot into LK for partition resize.\n", __func__);
+        }
+    }
+}
+
+static void status_get_command(int __attribute((unused)) argc,
+                               char * __attribute ((unused)) argv[])
+{
+    struct mrdump_status_result result;
+    if (mrdump_status_get(&result)) {
+        printf("MT-RAMDUMP\n\tStatus:");
+        switch (result.status) {
+        case MRDUMP_STATUS_NONE:
+            printf("None\n");
+            break;
+        case MRDUMP_STATUS_FAILED:
+            printf("Failed\n");
+            break;
+        case MRDUMP_STATUS_OK:
+            dump_status_ok(&result);
+            break;
+        }
+    }
+    else {
+        error("MT-RAMDUMP get status failed\n");
+    }
+}
+
+static int parse_log_level(const char *log_level)
+{
+    if (strcmp(log_level, "debug") == 0)
+        return LOG_DEBUG;
+    else if (strcmp(log_level, "info") == 0)
+        return LOG_INFO;
+    else if (strcmp(log_level, "warn") == 0)
+        return LOG_WARNING;
+    else if (strcmp(log_level, "error") == 0)
+        return LOG_ERR;
+    return LOG_WARNING;
+}
+
+int main(int argc, char *argv[])
+{
+    int log_level = LOG_WARNING;
+    int log_syslog = 0;
+
+    static struct option long_options[]= {
+        {"help", no_argument, 0, 0},
+        {"log-level", required_argument, 0, 0},
+        {"log-syslog", no_argument, 0, 0},
+        {0, 0, 0, 0},
+    };
+
+    while (1) {
+        int option_index, c;
+        c = getopt_long(argc, argv, "+", long_options, &option_index);
+        if (c == -1)
+            break;
+
+        switch (c) {
+        case 0:
+            if (strcmp(long_options[option_index].name, "log-level") == 0) {
+                log_level = parse_log_level(optarg);
+            }
+            else if (strcmp(long_options[option_index].name, "log-syslog") == 0) {
+                log_syslog = 1;
+            }
+            else if (strcmp(long_options[option_index].name, "help") == 0) {
+                usage(argv[0]);
+            }
+            break;
+
+        default:
+            usage(argv[0]);
+        }
+    }
+
+    mdlog_init(log_level, log_syslog);
+
+    const int command_argc = argc - optind;
+    if (command_argc < 1) {
+        error("No command given\n");
+    }
+    const char *command = argv[optind];
+    char **command_argv = &argv[optind];
+
+    if (strcmp(command, "is-supported") == 0) {
+        if (mrdump_is_supported()) {
+            printf("MT-RAMDUMP support ok\n");
+        }
+        else {
+            printf("MT-RAMDUMP not support\n");
+        }
+    }
+    else if (strcmp(command, "status-get") == 0) {
+        status_get_command(command_argc, command_argv);
+    }
+    else if (strcmp(command, "status-log") == 0) {
+        struct mrdump_status_result result;
+        bool res = mrdump_status_get(&result);
+
+        printf("=>status line:\n%s\n=>log:\n%s\n", result.status_line, result.log_buf);
+        if (!res) {
+            error("MT-RAMDUMP get status failed\n");
+        }
+    }
+    else if (strcmp(command, "status-clear") == 0) {
+        if (!mrdump_is_supported()) {
+            error("MT-RAMDUMP not support\n");
+        }
+        if (!mrdump_status_clear()) {
+            error("MT-RAMDUMP Status clear failed\n");
+        }
+    }
+    else if (strcmp(command, "file-setup") == 0) {
+        file_setup_command(command_argc, command_argv);
+    }
+    else if (strcmp(command, "file-allocate") == 0) {
+        file_allocate_command(command_argc, command_argv);
+    }
+    else if (strcmp(command, "file-extract-core") == 0) {
+        file_extract_core_command(command_argc, command_argv);
+    }
+    else if (strcmp(command, "file-info") == 0) {
+        file_info_command(command_argc, command_argv);
+    }
+    else if (strcmp(command, "mem-size-set") == 0) {
+        mem_size_set_command(command_argc, command_argv);
+    }
+    else if (strcmp(command, "output-set") == 0) {
+        output_set_command(command_argc, command_argv);
+    }
+    else if (strcmp(command, "partition") == 0) {
+        /* ignored "-Wunused-result" */
+        if (1 == mrdump_check_partition()) {;}
+    }
+    else if (strcmp(command, "output-get") == 0) {
+        const char *output_dev = sysenv_get("mrdump_output");
+        if (!output_dev) {
+            printf("default\n");
+        }
+        else {
+            printf("%s\n", output_dev);
+        }
+    }
+    else {
+        error("Unknown command %s\n", command);
+    }
+
+    return 0;
+}
diff --git a/src/devtools/packer/LICENSE b/src/devtools/packer/LICENSE
new file mode 100644
index 0000000..88a087d
--- /dev/null
+++ b/src/devtools/packer/LICENSE
@@ -0,0 +1,36 @@
+/* Copyright Statement:
+ *
+ * This software/firmware and related documentation ("MediaTek Software") are
+ * protected under relevant copyright laws. The information contained herein is
+ * confidential and proprietary to MediaTek Inc. and/or its licensors. Without
+ * the prior written permission of MediaTek inc. and/or its licensors, any
+ * reproduction, modification, use or disclosure of MediaTek Software, and
+ * information contained herein, in whole or in part, shall be strictly
+ * prohibited.
+ *
+ * Copyright  (C) [2020]  MediaTek Inc. All rights reserved.
+ *
+ * BY OPENING THIS FILE, RECEIVER HEREBY UNEQUIVOCALLY ACKNOWLEDGES AND AGREES
+ * THAT THE SOFTWARE/FIRMWARE AND ITS DOCUMENTATIONS ("MEDIATEK SOFTWARE")
+ * RECEIVED FROM MEDIATEK AND/OR ITS REPRESENTATIVES ARE PROVIDED TO RECEIVER
+ * ON AN "AS-IS" BASIS ONLY. MEDIATEK EXPRESSLY DISCLAIMS ANY AND ALL
+ * WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR
+ * NONINFRINGEMENT. NEITHER DOES MEDIATEK PROVIDE ANY WARRANTY WHATSOEVER WITH
+ * RESPECT TO THE SOFTWARE OF ANY THIRD PARTY WHICH MAY BE USED BY,
+ * INCORPORATED IN, OR SUPPLIED WITH THE MEDIATEK SOFTWARE, AND RECEIVER AGREES
+ * TO LOOK ONLY TO SUCH THIRD PARTY FOR ANY WARRANTY CLAIM RELATING THERETO.
+ * RECEIVER EXPRESSLY ACKNOWLEDGES THAT IT IS RECEIVER'S SOLE RESPONSIBILITY TO
+ * OBTAIN FROM ANY THIRD PARTY ALL PROPER LICENSES CONTAINED IN MEDIATEK
+ * SOFTWARE. MEDIATEK SHALL ALSO NOT BE RESPONSIBLE FOR ANY MEDIATEK SOFTWARE
+ * RELEASES MADE TO RECEIVER'S SPECIFICATION OR TO CONFORM TO A PARTICULAR
+ * STANDARD OR OPEN FORUM. RECEIVER'S SOLE AND EXCLUSIVE REMEDY AND MEDIATEK'S
+ * ENTIRE AND CUMULATIVE LIABILITY WITH RESPECT TO THE MEDIATEK SOFTWARE
+ * RELEASED HEREUNDER WILL BE, AT MEDIATEK'S OPTION, TO REVISE OR REPLACE THE
+ * MEDIATEK SOFTWARE AT ISSUE, OR REFUND ANY SOFTWARE LICENSE FEES OR SERVICE
+ * CHARGE PAID BY RECEIVER TO MEDIATEK FOR SUCH MEDIATEK SOFTWARE AT ISSUE.
+ *
+ * The following software/firmware and/or related documentation ("MediaTek
+ * Software") have been modified by MediaTek Inc. All revisions are subject to
+ * any receiver's applicable license agreements with MediaTek Inc.
+ */
diff --git a/src/devtools/packer/mt2731/cryptoSB.py b/src/devtools/packer/mt2731/cryptoSB.py
new file mode 100644
index 0000000..a481b17
--- /dev/null
+++ b/src/devtools/packer/mt2731/cryptoSB.py
@@ -0,0 +1,490 @@
+import binascii
+import Crypto.Random.OSRNG.posix as RNG
+from Crypto.Cipher import AES
+from Crypto.Hash import MD5
+from Crypto.Hash import SHA
+from Crypto.Hash import SHA224
+from Crypto.Hash import SHA256
+from Crypto.Hash import SHA384
+from Crypto.Hash import SHA512
+from Crypto.Hash import HMAC
+from Crypto.Util import Counter
+import CryptoPlus
+from CryptoPlus.Cipher import python_AES as CP_python_AES
+import ecdsa
+from ecdsa import SigningKey, VerifyingKey
+from ecdsa.curves import NIST192p, NIST224p, NIST256p, NIST384p
+from ecdsa.util import sigencode_string
+from hashlib import sha1, sha224, sha256, sha384
+
+''' Select ECDSA Curve '''
+__CURVE = 'prime256v1'
+
+AUTH_ECDSA_NISTP256 = 0
+AUTH_ECDSA_NISTP384 = 1
+AUTH_AES_CMAC = 2
+AUTH_HMAC_SHA256 = 3
+
+AUTH_ECDSA_NISTP192 = 10
+AUTH_ECDSA_NISTP224 = 11
+
+''' Select ECDSA module '''
+
+''' Select Key Mode '''
+__K_STRING = 0
+__K_PEM = 1
+__K_DER = 2
+
+
+def crypto_pad_to_align(x, align):
+    if(align == 0):
+        return x
+    else:
+        size = len(x)
+        #print("size 0x%x" %size)
+        pad_size = (align - size % align) % align
+        for i in range(0, pad_size, 1):
+            x += b'\x00'
+        #cryptoSB.dump(x)
+        return x
+
+
+# This module is python-ecdsa (warner/python-ecdsa)
+class pythonECDSA:
+	def __init__(self, priv=None, pub=None, curve=None):
+		#print("pure-python ECDSA init")
+                None
+	def sign_string(self, privk, msg, sbc_auth_alg):
+		if(sbc_auth_alg == AUTH_ECDSA_NISTP256):
+			sk = SigningKey.from_string(privk, curve=NIST256p, hashfunc=sha256)
+			signature_str = sk.sign(msg, hashfunc=sha256, sigencode=sigencode_string)
+		elif(sbc_auth_alg == AUTH_ECDSA_NISTP384):
+			sk = SigningKey.from_string(privk, curve=NIST384p, hashfunc=sha384)
+			signature_str = sk.sign(msg, hashfunc=sha384, sigencode=sigencode_string)
+		elif(sbc_auth_alg == AUTH_ECDSA_NISTP192):
+			sk = SigningKey.from_string(privk, curve=NIST192p, hashfunc=sha1)
+			signature_str = sk.sign(msg, hashfunc=sha1, sigencode=sigencode_string)
+		elif(sbc_auth_alg == AUTH_ECDSA_NISTP224):
+			sk = SigningKey.from_string(privk, curve=NIST224p, hashfunc=sha224)
+			signature_str = sk.sign(msg, hashfunc=sha224, sigencode=sigencode_string)
+		return signature_str
+	def sign_pem(self, privk, msg, sbc_auth_alg):
+		#print("    run sign pem")
+		if(sbc_auth_alg == AUTH_ECDSA_NISTP256):
+			sk = SigningKey.from_pem(privk, hashfunc=sha256)
+			signature_pem = sk.sign(msg, hashfunc=sha256)
+
+		elif(sbc_auth_alg == AUTH_ECDSA_NISTP384):
+			sk = SigningKey.from_pem(privk, hashfunc=sha384)
+			signature_pem = sk.sign(msg, hashfunc=sha384)
+
+		elif(sbc_auth_alg == AUTH_ECDSA_NISTP192):
+			sk = SigningKey.from_pem(privk, hashfunc=sha1)
+			signature_pem = sk.sign(msg, hashfunc=sha1)
+
+		elif(sbc_auth_alg == AUTH_ECDSA_NISTP224):
+			sk = SigningKey.from_pem(privk, hashfunc=sha224)
+			signature_pem = sk.sign(msg, hashfunc=sha224)
+
+		return signature_pem
+	def sign_der(self, privk, msg, sbc_auth_alg):
+		#print("    run sign der")
+		if(sbc_auth_alg == AUTH_ECDSA_NISTP256):
+			sk = SigningKey.from_der(privk, hashfunc=sha256)
+			signature_der = sk.sign(msg, hashfunc=sha256)
+
+		elif(sbc_auth_alg == AUTH_ECDSA_NISTP384):
+			sk = SigningKey.from_der(privk, hashfunc=sha384)
+			signature_der = sk.sign(msg, hashfunc=sha384)
+
+		elif(sbc_auth_alg == AUTH_ECDSA_NISTP192):
+			sk = SigningKey.from_der(privk, hashfunc=sha1)
+			signature_der = sk.sign(msg, hashfunc=sha1)
+
+		elif(sbc_auth_alg == AUTH_ECDSA_NISTP224):
+			sk = SigningKey.from_der(privk, hashfunc=sha224)
+			signature_der = sk.sign(msg, hashfunc=sha224)
+
+
+		return signature_der
+	def verify_string(self, pubk, signature_str, msg, sbc_auth_alg):
+		if(sbc_auth_alg == AUTH_ECDSA_NISTP256):
+			vk = VerifyingKey.from_string(pubk, curve=NIST256p, hashfunc=sha256)
+			ret = vk.verify(signature_str, msg)
+		elif(sbc_auth_alg == AUTH_ECDSA_NISTP384):
+			vk = VerifyingKey.from_string(pubk, curve=NIST384p, hashfunc=sha384)
+			ret = vk.verify(signature_str, msg)
+		elif(sbc_auth_alg == AUTH_ECDSA_NISTP192):
+			vk = VerifyingKey.from_string(pubk, curve=NIST192p, hashfunc=sha1)
+			ret = vk.verify(signature_str, msg)
+		elif(sbc_auth_alg == AUTH_ECDSA_NISTP224):
+			vk = VerifyingKey.from_string(pubk, curve=NIST224p, hashfunc=sha224)
+			ret = vk.verify(signature_str, msg)
+		return ret
+	def verify_pem(self, pubk, signature_pem, msg, sbc_auth_alg):
+		#print("    run verify pem")
+		if(sbc_auth_alg == AUTH_ECDSA_NISTP256):
+			vk = VerifyingKey.from_pem(pubk)
+			ret = vk.verify(signature_pem, msg, hashfunc=sha256)
+
+		elif(sbc_auth_alg == AUTH_ECDSA_NISTP384):
+			vk = VerifyingKey.from_pem(pubk)
+			ret = vk.verify(signature_pem, msg, hashfunc=sha384)
+
+		elif(sbc_auth_alg == AUTH_ECDSA_NISTP192):
+			vk = VerifyingKey.from_pem(pubk)
+			ret = vk.verify(signature_pem, msg, hashfunc=sha1)
+
+		elif(sbc_auth_alg == AUTH_ECDSA_NISTP224):
+			vk = VerifyingKey.from_pem(pubk)
+			ret = vk.verify(signature_pem, msg, hashfunc=sha224)
+		return ret
+	def verify_der(self, pubk, signature_der, msg, sbc_auth_alg):
+		#print("    run verify der")
+		if(sbc_auth_alg == AUTH_ECDSA_NISTP256):
+			vk = VerifyingKey.from_der(pubk)
+			ret = vk.verify(signature_der, msg, hashfunc=sha256)
+
+		elif(sbc_auth_alg == AUTH_ECDSA_NISTP384):
+			vk = VerifyingKey.from_der(pubk)
+			ret = vk.verify(signature_der, msg, hashfunc=sha384)
+
+		elif(sbc_auth_alg == AUTH_ECDSA_NISTP192):
+			vk = VerifyingKey.from_der(pubk)
+			ret = vk.verify(signature_der, msg, hashfunc=sha1)
+
+		elif(sbc_auth_alg == AUTH_ECDSA_NISTP224):
+			vk = VerifyingKey.from_der(pubk)
+			ret = vk.verify(signature_der, msg, hashfunc=sha224)
+		return ret
+	def gen_key_string(self, sbc_auth_alg):
+		if(sbc_auth_alg == AUTH_ECDSA_NISTP256):
+			sk = SigningKey.generate(curve=NIST256p, hashfunc=sha256)
+			vk = sk.get_verifying_key()
+		elif(sbc_auth_alg == AUTH_ECDSA_NISTP384):
+			sk = SigningKey.generate(curve=NIST384p, hashfunc=sha384)
+			vk = sk.get_verifying_key()
+		elif(sbc_auth_alg == AUTH_ECDSA_NISTP192):
+			sk = SigningKey.generate(curve=NIST192p, hashfunc=sha1)
+			vk = sk.get_verifying_key()
+		elif(sbc_auth_alg == AUTH_ECDSA_NISTP224):
+			sk = SigningKey.generate(curve=NIST224p, hashfunc=sha224)
+			vk = sk.get_verifying_key()
+		privk_str = sk.to_string()
+		pubk_str = vk.to_string()
+		return privk_str, pubk_str
+	def gen_key_pem(self, sbc_auth_alg):
+		if(sbc_auth_alg == AUTH_ECDSA_NISTP256):
+			sk = SigningKey.generate(curve=NIST256p, hashfunc=sha256)
+			vk = sk.get_verifying_key()
+		elif(sbc_auth_alg == AUTH_ECDSA_NISTP384):
+			sk = SigningKey.generate(curve=NIST384p, hashfunc=sha384)
+			vk = sk.get_verifying_key()
+		elif(sbc_auth_alg == AUTH_ECDSA_NISTP192):
+			sk = SigningKey.generate(curve=NIST192p, hashfunc=sha1)
+			vk = sk.get_verifying_key()
+		elif(sbc_auth_alg == AUTH_ECDSA_NISTP224):
+			sk = SigningKey.generate(curve=NIST224p, hashfunc=sha224)
+			vk = sk.get_verifying_key()
+		privk_pem = sk.to_pem()
+		pubk_pem = vk.to_pem()
+		return privk_pem, pubk_pem
+	def gen_key_der(self, sbc_auth_alg):
+		if(sbc_auth_alg == AUTH_ECDSA_NISTP256):
+			sk = SigningKey.generate(curve=NIST256p, hashfunc=sha256)
+			vk = sk.get_verifying_key()
+		elif(sbc_auth_alg == AUTH_ECDSA_NISTP384):
+			sk = SigningKey.generate(curve=NIST384p, hashfunc=sha384)
+			vk = sk.get_verifying_key()
+		elif(sbc_auth_alg == AUTH_ECDSA_NISTP384):
+			sk = SigningKey.generate(curve=NIST192p, hashfunc=sha1)
+			vk = sk.get_verifying_key()
+		elif(sbc_auth_alg == AUTH_ECDSA_NISTP224):
+			sk = SigningKey.generate(curve=NIST224p, hashfunc=sha224)
+			vk = sk.get_verifying_key()
+		privk_der = sk.to_der()
+		pubk_der = vk.to_der()
+		return privk_der, pubk_der
+
+class AESCipher:
+	def __init__(self, key, blk_sz):
+		#self.key = md5(key.encode('utf8')).hexdigest()
+		self.key = key
+		self.blk_sz = blk_sz
+		keylen = len(key)
+		if(keylen != blk_sz):
+		    print("ERROR: key length fail:")
+		    return 0
+
+	def aes_encrypt(self, data):
+		#data = pad(data)
+		#print(":".join("{:02x}".format(ord(e)) for e in data))
+		cryptor = AES.new(self.key, AES.MODE_ECB)
+		return cryptor.encrypt(data)
+
+	def aes_decrypt(self, data):
+		#data = pad(data)
+		#print(":".join("{:02x}".format(ord(e)) for e in data))
+		cryptor = AES.new(self.key, AES.MODE_ECB)
+		return cryptor.decrypt(data)
+
+	def aes_cbc_encrypt(self, data, iv):
+		cryptor = AES.new(self.key, AES.MODE_CBC, iv)
+		return cryptor.encrypt(data)
+
+	def aes_cbc_decrypt(self, data, iv):
+		cryptor = AES.new(self.key, AES.MODE_CBC, iv)
+		return cryptor.decrypt(data)
+
+	def aes_ctr_encrypt_only_counter(self, data, prefix_const, init_counter):
+		#ctr_e = Counter.new(64, prefix = prefix_const)
+		#cryptor = AES.new(self.key, AES.MODE_CTR, counter=lambda: init_counter)
+
+		ctr_e = Counter.new(128, initial_value=long(init_counter.encode('hex'), 16), allow_wraparound=True)
+		cryptor = AES.new(self.key, AES.MODE_CTR, counter=ctr_e)
+
+		return cryptor.encrypt(data)
+
+	def aes_ctr_decrypt_only_counter(self, data, prefix_const, init_counter):
+		#ctr_d = Counter.new(128, prefix = prefix_const)
+		#cryptor = AES.new(self.key, AES.MODE_CTR, counter=lambda: init_counter)
+
+		ctr_d = Counter.new(128, initial_value=long(init_counter.encode('hex'), 16), allow_wraparound=True)
+		cryptor = AES.new(self.key, AES.MODE_CTR, counter=ctr_d)
+		return cryptor.decrypt(data)
+
+	def aes_ctr_encrypt(self, data, prefix_str, initvalue):
+		#print("0x%x" %long(initvalue.encode('hex'), 16))
+		#print(len(prefix_str))
+		nbits = (128 - (len(prefix_str)*8))
+		ctr_e = Counter.new(nbits, prefix = prefix_str, initial_value=long(initvalue.encode('hex'), 16), allow_wraparound=True)
+		cryptor = AES.new(self.key, AES.MODE_CTR, counter=ctr_e)
+
+		return cryptor.encrypt(data)
+
+	def aes_ctr_decrypt(self, data, prefix_str, initvalue):
+		nbits = (128 - (len(prefix_str)*8))
+		ctr_d = Counter.new(nbits, prefix = prefix_str, initial_value=long(initvalue.encode('hex'), 16), allow_wraparound=True)
+		cryptor = AES.new(self.key, AES.MODE_CTR, counter=ctr_d)
+
+		return cryptor.decrypt(data)
+
+
+def strip_key(key):
+    #src_key = key.lstrip("x").rstrip("L")
+    src_key = key[2:].rstrip("L")
+    key_byte = binascii.a2b_hex(src_key)
+    return key_byte
+
+def my_img_enc(data, key, iv, img_enc_alg, align=None):
+    #dump(data)
+
+    #align
+    bin = crypto_pad_to_align(data, align)
+
+    if(img_enc_alg == 0):
+        # AES 128 ECB
+        aes = AESCipher(key, 16)
+        encmsg = aes.aes_encrypt(bin)
+        #print("AES 128 ECB image result:")
+        #dump(encmsg)
+    elif(img_enc_alg == 1):
+        # AES 128 CBC
+        aes = AESCipher(key, 16)
+        encmsg = aes.aes_cbc_encrypt(bin, iv)
+        #print("AES 128 CBC image result:")
+        #dump(encmsg)
+    elif(img_enc_alg == 2):
+        # AES 256 ECB
+        aes = AESCipher(key, 32)
+        encmsg = aes.aes_encrypt(bin)
+        #print("AES 256 ECB image result:")
+        #dump(encmsg)
+    elif(img_enc_alg == 3):
+        # AES 256 CBC
+        aes = AESCipher(key, 32)
+        encmsg = aes.aes_cbc_encrypt(bin, iv)
+        #print("AES 256 CBC image result:")
+        #dump(encmsg)
+
+    return encmsg
+
+# dump str to bytes
+def dump(data):
+	for i in range(0, len(data)):
+		print ("0x%02x,"%ord(data[i])),
+		if(((i+1)%16) == 0):
+			print("")
+
+class HASH:
+	def __init__(self):
+		#print("//class HASH init")
+                None
+
+	def hash_sha1(self, data):
+		#print("SHA1:")
+		hash = SHA.new()
+		hash.update(data)
+		digest = hash.digest()
+		return digest
+	def hash_sha224(self, data):
+		#print("SHA224:")
+		hash = SHA224.new()
+		hash.update(data)
+		digest = hash.digest()
+		return digest
+	def hash_sha256(self, data):
+		#print("SHA256:")
+		hash = SHA256.new()
+		hash.update(data)
+		digest = hash.digest()
+		return digest
+	def hash_sha384(self, data):
+		#print("SHA384:")
+		hash = SHA384.new()
+		hash.update(data)
+		digest = hash.digest()
+		return digest
+
+	def hash_sha512(self, data):
+		#print("SHA512:")
+		hash = SHA512.new()
+		hash.update(data)
+		digest = hash.digest()
+		return digest
+	def hash_md5(self, data):
+		#print("MD5:")
+		hash = MD5.new()
+		hash.update(data)
+		digest = hash.digest()
+		return digest
+
+def sb_hash(data, sbc_auth_alg):
+    #print("run my_hash:")
+
+    #myhash = HASH()
+
+    if((sbc_auth_alg == 1) or (sbc_auth_alg == 5) ):
+        sbhash = HASH()
+        digest = sbhash.hash_sha384(data)
+        return digest
+    elif(sbc_auth_alg == 0) or (sbc_auth_alg == 2) or (sbc_auth_alg == 3) or (sbc_auth_alg == 4):
+        sbhash = HASH()
+        digest = sbhash.hash_sha256(data)
+        return digest
+    elif((sbc_auth_alg == 6)):
+        sbhash = HASH()
+        digest = sbhash.hash_sha512(data)
+        return digest
+    else:
+        print("ERROR: run sb_hash fail")
+
+
+
+def hmac_sha256(key, bdata):
+
+    b = HMAC.new(key, digestmod=SHA256)
+    b.update(bdata)
+    digest = b.digest()
+    #digest = b.hexdigest()
+    #dump(digest)
+
+    return digest
+
+def aescmac(key, msg):
+    #print("AES CMAC: ")
+    cipher = CP_python_AES.new(key, CP_python_AES.MODE_CMAC)
+    out = cipher.encrypt(msg)
+
+    #outhex = cipher.encrypt(msg).encode('hex')
+    #out = outhex.decode('hex')
+    return out
+
+def ecdsa_sign_nistp256_string(privk, msg):
+	ecdsa = pythonECDSA()
+	signature = ecdsa.sign_string(privk, msg, AUTH_ECDSA_NISTP256)
+
+	return signature
+
+def ecdsa_sign_nistp256_pem(privk, msg):
+	ecdsa = pythonECDSA()
+	signature = ecdsa.sign_pem(privk, msg, AUTH_ECDSA_NISTP256)
+	return signature
+
+def ecdsa_sign_nistp256_der(privk, msg):
+	ecdsa = pythonECDSA()
+	signature = ecdsa.sign_der(privk, msg, AUTH_ECDSA_NISTP256)
+
+	return signature
+
+def ecdsa_sign_nistp384_string(privk, msg):
+	ecdsa = pythonECDSA()
+	signature = ecdsa.sign_string(privk, msg, AUTH_ECDSA_NISTP384)
+	return signature
+
+def ecdsa_sign_nistp384_pem(privk, msg):
+	ecdsa = pythonECDSA()
+	signature = ecdsa.sign_pem(privk, msg, AUTH_ECDSA_NISTP384)
+
+	return signature
+
+def ecdsa_sign_nistp384_der(privk, msg):
+	ecdsa = pythonECDSA()
+	signature = ecdsa.sign_der(privk, msg, AUTH_ECDSA_NISTP384)
+
+	return signature
+
+
+def pubkey_string(pubk, sbc_auth_alg):
+	sk = VerifyingKey.from_pem(pubk)
+	return sk.to_string()
+def privkey_string(privk, sbc_auth_alg):
+	if(sbc_auth_alg == AUTH_ECDSA_NISTP256):
+		sk = SigningKey.from_pem(privk, hashfunc=sha256)
+		return sk.to_string()
+	else:
+		sk = SigningKey.from_pem(privk, hashfunc=sha384)
+		return sk.to_string()
+
+def sb_sign(privk, pubk, msg, sbc_auth_alg, key_mode=__K_STRING):
+    #print("Start sb_sign:")
+
+    if(sbc_auth_alg == AUTH_ECDSA_NISTP256):
+        #pyelliptic ECDSA NIST p256
+        if(key_mode == __K_STRING):
+            signature = ecdsa_sign_nistp256_string(privk, msg)
+            #another use
+            #ecdsa = pythonECDSA()
+	    #signature = ecdsa.sign_string(privk, msg, AUTH_ECDSA_NISTP256)
+
+        elif(key_mode == __K_PEM):
+            signature = ecdsa_sign_nistp256_pem(privk, msg)
+
+        elif(key_mode == __K_DER):
+            signature = ecdsa_sign_nistp256_der(privk, msg)
+        return signature
+
+    elif(sbc_auth_alg == AUTH_ECDSA_NISTP384):
+        #pyelliptic ECDSA_NIST_p384
+        if(key_mode == __K_STRING):
+            signature = ecdsa_sign_nistp384_string(privk, msg)
+
+        elif(key_mode == __K_PEM):
+            signature = ecdsa_sign_nistp384_pem(privk, msg)
+
+        elif(key_mode == __K_DER):
+            signature = ecdsa_sign_nistp384_der(privk, msg)
+
+        return signature
+
+    elif(sbc_auth_alg == AUTH_AES_CMAC):
+        #AES128_CMAC
+        signature = aescmac(privk, msg)
+        return signature
+    elif(sbc_auth_alg == AUTH_HMAC_SHA256):
+        #HMAC-SHA256
+        signature = hmac_sha256(privk, msg)
+        return signature
+    else:
+        print("ERROR: wrong authentication algorithm!!")
+
diff --git a/src/devtools/packer/mt2731/mipack.py b/src/devtools/packer/mt2731/mipack.py
new file mode 100644
index 0000000..c38d979
--- /dev/null
+++ b/src/devtools/packer/mt2731/mipack.py
@@ -0,0 +1,693 @@
+#!/usr/bin/python
+# -*- coding: utf8 -*-
+
+import json
+import struct
+import sys
+import binascii
+import cryptoSB
+import copy
+
+from jsoncomment import JsonComment
+
+__Align = 16
+__Align_16_en = 0
+
+__TEST = 0
+
+g_magic_size = 8
+g_total_len_size = 4
+g_bl_version_size = 4
+g_img_number_size = 4
+g_ls_cmd_number_size = 4
+g_mi_header_info_size = (g_magic_size + g_total_len_size + g_bl_version_size + g_img_number_size + g_ls_cmd_number_size)
+
+g_img_header_size = 96
+g_ls_cmd_size = 8
+
+g_img_oneoffset_table_size = 4
+g_img_onehash_table_size = 64
+
+g_public_key_size = 96
+g_sbc_auth_alg_size = 4
+g_sbc_auth_inf_size = 4
+g_sbc_size = (g_public_key_size + g_sbc_auth_alg_size + g_sbc_auth_inf_size)
+
+g_boot_hash_size = 64
+g_signature_size = 96
+g_auth_size = (g_boot_hash_size + g_signature_size)
+
+def to_int(s):
+    if isinstance(s, (str, unicode)):
+        return int(s, 0)
+    else:
+        return s
+
+def byte_to_int(bytes):
+    result = 0
+
+    for b in bytes:
+        result = result*256 + ord(b)
+
+    return result
+
+def read_desc(mi_desc_fn):
+    #desc = json.load(open(mi_desc_fn))
+
+    parser = JsonComment(json)
+    desc = parser.loads(open(mi_desc_fn).read())
+
+    for img in desc['images']:
+        img['img_enc_alg'] = to_int(img['img_enc_alg'])
+        img['img_enc_inf'] = to_int(img['img_enc_inf'])
+
+        '''0627 remove int assign avoid key start is (0x00..)'''
+        #img['img_iv'] = to_int(img['img_iv'])
+
+    return desc
+
+
+def gen_ver_magic(major, minor, revision):
+    major = to_int(major)
+    minor = to_int(minor)
+    revision = to_int(revision)
+
+    x = major ^ 0xaa
+    y = minor ^ 0x55
+    z = revision ^ 0x99
+    checksum = (x + y + z) & 0xff
+
+    return (major << 0) | (minor << 8) | (revision << 16) | (checksum << 24)
+
+def my_gen_ver_magic(major, minor, revision):
+    major = to_int(major)
+    minor = to_int(minor)
+    revision = to_int(revision)
+
+    x = major ^ 0xaa
+    y = minor ^ 0x55
+    z = revision ^ 0x99
+    checksum = (x + y + z) & 0xff
+
+    highmagic = byte_to_int(b'\x27\x21\xca\xfe')
+    #print(hex(highmagic))
+
+    return (major << 0) | (minor << 8) | (revision << 16) | (checksum << 24), highmagic
+
+def my_gen_mtk_magic(magic):
+    magic_str = my_to_bytes(magic, g_magic_size, endianess='little')
+
+    lowmagic = byte_to_int(magic_str[4:8])
+    highmagic = byte_to_int(magic_str[0:4])
+
+    return lowmagic, highmagic
+
+def pad_to_align(align, current_size):
+    if not align:
+        return ''
+
+    pad_size = (align - current_size % align) % align
+    pad = '\0' * pad_size
+
+    return pad
+
+
+def dump(data):
+    for i in range(0, len(data)):
+        if(i%16 == 0):
+            print("[%04X]" % i),
+
+        print("%02x" % ord(data[i])),
+        if(((i+1)%16) == 0):
+            print
+
+def pack_load_script(mi_desc):
+    ls_script = []
+    reserved = 0
+    images = mi_desc['images']
+    img_num = len(images)
+
+    ls_cmds = mi_desc['load_srcipt_cmd']
+    ls_cmds_num = len(ls_cmds)
+    #print("ls_cmds_num: %d" %ls_cmds_num)
+
+    for i in range(ls_cmds_num):
+        ls_cmd = ls_cmds[i]
+        cmd = ls_cmd['cmd']
+
+        if cmd == "LOAD":
+            cmd_id = 0
+            img_file = ls_cmd['img_file']
+            addr = to_int(ls_cmd['addr'])
+
+            img_id = -1
+            for j in range(img_num):
+                img = images[j]
+                img_binary_name = img['img_file']
+                if img_file == img_binary_name:
+                    img_id = j
+                    break
+
+            if img_id == -1:
+                print("Please check img file name")
+                return
+
+            packed_cmd = struct.pack('<BBHI', cmd_id, img_id, reserved, addr)
+            #dump(packed_cmd)
+            #print
+        elif cmd == "MCU-ENTRY":
+            cmd_id = 2
+            mcu_id =  ls_cmd['mcu_id']
+            addr = to_int(ls_cmd['addr'])
+            packed_cmd = struct.pack('<BBHI', cmd_id, mcu_id, reserved, addr)
+            #dump(packed_cmd)
+            #print
+        elif cmd == "MCU_RESET-ENTRY":
+            cmd_id = 1
+            mcu_id =  ls_cmd['mcu_id']
+            addr = to_int(ls_cmd['addr'])
+            packed_cmd = struct.pack('<BBHI', cmd_id, mcu_id, reserved, addr)
+            #dump(packed_cmd)
+            #print
+        else:
+            print("unknown command: %s" %cmd)
+            return
+
+        ls_script += packed_cmd
+
+    #print("load script:")
+    #dump(ls_script)
+
+    return ls_script
+
+
+def my_pack_images(mi_desc, privk, enckey, align=None, img_dir=''):
+    '''
+    mipack_file
+        mi_header_info
+            u32 magic
+            u32 total_len
+            u32 bl_version
+            u32 img_number (N)
+            u32 load_script_cmd_number (M)
+
+        img_info[1]~img_info[N]
+            u32     img_length
+            u32     img_offset
+            64Byes  img_hash
+            u32     img_enc_inf
+            u32     img_enc_alg
+            16Bytes img_iv
+
+        load_script_cmd[1]~load_script_cmd[M]
+            u64     cmd
+
+        sbc data
+            96Bytes public_key
+            u32     sbc_auth_inf
+            u32     sbc_auth_alg
+
+        auth data
+            64Bytes boothash
+            96Bytes signature
+
+        [img binary 1]
+        ...
+        [img binary 1]
+    '''
+
+    key_mode = 0
+
+    mi_header_struct = struct.Struct('<IIIIII')
+    sbc_struct = struct.Struct('<II')
+
+    #run hash
+    hash = cryptoSB.HASH()
+
+    # ver_magic
+    #version = mi_desc['version']
+    #major, minor, revision = version.split('.')
+    #ver_magic_l, ver_magic_h = my_gen_ver_magic(major, minor, revision)
+
+    ver_magic_l, ver_magic_h = my_gen_mtk_magic(to_int(mi_desc['magic_num']))
+
+    # bl region
+    bl_version = mi_desc['bl_version']
+    images = mi_desc['images']
+    img_num = len(images)
+    ls_cmds = mi_desc['load_srcipt_cmd']
+    ls_cmds_num = len(ls_cmds)
+
+    '''size of bl + imgl + sbc + auth'''
+    # Fixed size section: mi_header, sbc data, auth data
+    # Non-fixed size section: img_info, load script
+    bl_total_len = g_mi_header_info_size + g_sbc_size + g_auth_size
+    bl_total_len += img_num * g_img_header_size
+    bl_total_len += ls_cmds_num * g_ls_cmd_size
+
+
+    #print("==== multi-image header size ====")
+    #print("g_mi_header_info_size=%d, g_sbc_size=%d, g_auth_size=%d, bl_total_len = %d" %(g_mi_header_info_size, g_sbc_size, g_auth_size, bl_total_len))
+
+    #img_header_table = []
+    img_headers = []
+    bins = []
+    sbc_region = []
+    hashtable = []
+    imagetmp = [[], [], [], [], [], [], [], [], [], []]
+    packtmp = []
+    blh_hash = []
+    signaturelist = []
+
+    imagesize = []
+
+
+    # sbc region
+    ##pub_key = to_int(mi_desc['public_key'])
+    ##public_key = my_binify(pub_key)
+    '''20180627 remove int assign avoid key start is (0x00..)'''
+    pub_key = (mi_desc['public_key'])
+    public_key = cryptoSB.strip_key(pub_key)
+    public_key_append = my_pad_to_align(public_key, 96)
+    sbc_region.append(public_key_append)
+    pubk = b'\x04' + public_key
+
+    #print("======= sbc region =========")
+    sbc_auth_alg = mi_desc['sbc_auth_alg']
+    #print("sbc_auth_alg = 0x%x" %sbc_auth_alg)
+    sbc_auth_inf = mi_desc['sbc_auth_inf']
+    #print("sbc_auth_inf = 0x%x" %sbc_auth_inf)
+    sbc = sbc_struct.pack(sbc_auth_inf, sbc_auth_alg)
+    sbc_region.append(sbc)
+
+
+    # pad before 1st image
+
+    #pad = pad_to_align(64, expected_size)
+    #bins.append(pad)
+    #expected_size += len(pad)
+
+
+    # images
+    # 16 bytes aligned
+    img_offset = (bl_total_len + 15) & (~15)
+    img_offset_table = []
+
+    for i in range(img_num):
+        #print("\n\n======== Start image step: =========")
+
+
+        img = images[i]
+        img_file = img['img_file']
+        img_enc_inf = img['img_enc_inf']
+        img_enc_alg = img['img_enc_alg']
+        #print("img_enc_inf=0x%x" %img_enc_inf)
+
+        bin = open(img_dir + '/' + img_file, 'rb').read()
+
+        iv_int = img['img_iv']
+        #iv = my_binify(iv_int)
+        '''0627 remove int assign avoid key start is (0x00..)'''
+        iv = cryptoSB.strip_key(iv_int)
+        iv0= int(iv[0:4].encode('hex'), 16)
+        iv1= int(iv[4:8].encode('hex'), 16)
+        iv2= int(iv[8:12].encode('hex'), 16)
+        iv3= int(iv[12:16].encode('hex'), 16)
+        #print type(iv)
+        #print("IV:")
+        #cryptoSB.dump(iv)
+
+
+        if(img_enc_inf == 1):
+            '''image encrypt'''
+            out = cryptoSB.my_img_enc(bin, enckey, iv, img_enc_alg, __Align)
+            bins.append(out)
+            #bins = out
+        elif(img_enc_inf == 2):
+            '''image encrypt'''
+            out = cryptoSB.my_img_enc(bin, enckey, iv, img_enc_alg, __Align)
+            bins.append(out)
+            #bins = out
+        else:
+            '''plaintext image'''
+            out = my_pad_to_align(bin, __Align)
+            bins.append(out)
+            #bins = out
+
+        # binary length should be 16 bytes aligned
+        length = len(out)
+
+        imagesize.append(length)
+        #print("")
+        #print("image[%d] offset : 0x%x" %(i, img_offset))
+        #print("image[%d] size   : 0x%x" %(i, imagesize[i]))
+
+        imagetmp[i] = copy.copy(bins)
+
+        img_str = ''.join(bins)
+        #print type(img_str)
+        #print("========= image[%d] binary ==========" %i)
+        #cryptoSB.dump(img_str)
+
+
+        # hash each (image header + image binary)
+        #print('')
+        #print("========= image[%d] binary hash ==========" %i)
+        hashvalue = cryptoSB.sb_hash(img_str, sbc_auth_alg)
+        imghash = my_pad_to_align(hashvalue, 64)
+        #cryptoSB.dump(imghash)
+
+        # img_header
+        img_hdr = struct.pack('<II',
+                                length,
+                                img_offset)
+        img_hdr += "".join(imghash)
+        '''20180719 fix IV order fail'''
+        img_hdr += struct.pack('<II16s',
+                                img_enc_inf,
+                                img_enc_alg,
+                                iv)
+
+
+        img_offset_table.append(str(img_offset))
+        img_offset += length
+        #img_offset_table.append(my_to_bytes(img_offset, 4, endianess='little'))
+
+        #print("\n=====>")
+        #print("image[%d] header info :" %i)
+        #cryptoSB.dump(img_hdr)
+        img_headers.append(img_hdr)
+
+        #img_headers.remove(img_hdr)
+        while len(bins) > 0:
+            bins.pop()
+
+
+    #print("\n\nSTART to pack all sections ...")
+    pack = []
+
+    #print("======== append mi_header info ==========")
+    total_len = int(img_offset_table[img_num-1]) + int(imagesize[img_num-1])
+
+    mi_header = mi_header_struct.pack(
+                        ver_magic_l,
+                        ver_magic_h,
+                        total_len,
+                        bl_version,
+                        img_num,
+                        ls_cmds_num)
+
+
+    pack.append(mi_header)
+
+    # append image info
+    for i in range(img_num):
+        pack += img_headers[i]
+
+
+    ls_script = pack_load_script(mi_desc)
+    if ls_script == None:
+        print("pack_load_script fail")
+        return
+
+    # append load script
+    pack += ls_script
+
+    # append sbc data
+    pack += sbc_region
+
+    # for easy view. please remove it while release final
+    '''align for (bl + imgl + sbc)'''
+    if(__Align_16_en == 1):
+        padnum = pad_to_align(16, len(''.join(pack)))
+        pack.append(padnum)
+
+    #print("======== append mi_header hash: ==========")
+    bl_header = ''.join(pack)
+    #cryptoSB.dump(bl_header)
+    blh = copy.copy(bl_header)
+    boothash = cryptoSB.sb_hash(blh, sbc_auth_alg)
+    boothash_append = my_pad_to_align(boothash, g_boot_hash_size)
+    #cryptoSB.dump(boothash_append)
+    blh_hash.append(boothash_append)
+
+    # append hash
+    pack += blh_hash
+
+    #print("======== append mi_header signature: =========")
+    #privk = "\xc1\xbe\xe4\xfa\x86\xaf\x86\x84\x67\x7c\xae\xee\xa8\x8a\xb0\x72\x3e\x55\x4a\xef\x01\x60\xb8\xfc\x65\x3c\x0e\x00\x08\x0f\x4f\x78"
+    #pubk = "\x04\x14\xc1\xcf\x10\x99\x9d\x3a\x98\xf3\x71\xb8\xd8\x9b\x3b\x26\xb2\x9e\xe1\xbd\x99\xf3\xe0\x39\x3d\x34\x21\x6a\x6f\x49\x58\x7a\xb1\xdd\x8a\xba\x7a\x9d\x02\x99\x5f\xda\xa0\xb8\x62\x82\xae\xc2\xd0\xc6\x88\xc2\x26\x03\x97\x86\x65\x46\xbb\x20\xc9\xd1\x44\xb9\x84"
+
+    if sbc_auth_inf == 0:
+        padbytes = '\0' * g_signature_size
+        signaturelist.append(padbytes)
+    else:
+        pem_key_format = mi_desc['pem_key_format']
+        if(pem_key_format == 1):
+            '''PEM format'''
+            key_mode = 1
+        elif(pem_key_format == 2):
+            '''DER format'''
+            key_mode = 2
+        else:
+            '''String format'''
+            key_mode = 0
+
+        #ecdsa = cryptoSB.ECDSA()
+        #signature = ecdsa.sign(privk, pubk, boothash, sbc_auth_alg)
+        ''' 20180616 fix the sb_sign msg error : not boothash -> blh is right'''
+        signature = cryptoSB.sb_sign(privk, pubk, blh, sbc_auth_alg, key_mode)
+        #print("signature size = %d" %len(signature))
+        signature_append = my_pad_to_align(signature, g_signature_size)
+        #cryptoSB.dump(signature_append)
+        signaturelist.append(signature_append)
+
+        #check verify
+        #ret = ecdsa.verify(pubk, boothash, signature, sbc_auth_alg)
+        #print("ecdsa verify: %s" %ret)
+
+
+    #dump("".join(signaturelist))
+    # append signature
+    pack += signaturelist
+
+    # for easy view, please remove it while release final
+    '''align for (bl + imgl + sbc + auth)'''
+    if(__Align_16_en == 1):
+        padnum = pad_to_align(16, len(''.join(pack)))
+        pack.append(padnum)
+
+    # append image binary
+    for i in range(img_num):
+        offset = int(img_offset_table[i])
+        pad_num = offset - len(''.join(pack))
+        #print("offset = %d" %offset)
+        #print("pad_num = %d" %pad_num)
+
+        padbytes = '\0' * pad_num
+        pack.append(padbytes)
+        pack += imagetmp[i]
+
+    #print(len(''.join(pack)))
+
+    # clear list
+    while len(signaturelist) > 0:
+        signaturelist.pop()
+
+
+    return ''.join(pack)
+
+'''support to_bytes to python 2.7'''
+def my_to_bytes(n, length, endianess='big'):
+    h = '%x' % n
+    s = ('0'*(len(h) % 2) + h).zfill(length*2).decode('hex')
+    return s if endianess == 'big' else s[::-1]
+
+'''long to byte string'''
+def my_binify(x):
+    h = hex(x)[2:].rstrip('L')
+    return binascii.unhexlify(h)
+
+def my_binify2(x):
+    if(hex(x)[0:2] == '0x'):
+        h = hex(x)[2:].rstrip('L')
+    else:
+        h = hex(x).rstrip('L')
+
+    return binascii.unhexlify(h)
+
+def my_pad_to_align(x, align):
+    if(align == 0):
+        return x
+    else:
+        size = len(x)
+        #print("size 0x%x" %size)
+        pad_size = (align - size % align) % align
+        for i in range(0, pad_size, 1):
+            x += b'\x00'
+
+        #cryptoSB.dump(x)
+        return x
+
+
+def img_enc(mi_desc, key, align=None):
+
+    images = mi_desc['images']
+    img_num = len(images)
+
+    img = images[0]
+    #load_addr = img['load_addr']
+    #entrypoint = img['entrypoint']
+    img_file = img['img_file']
+    img_enc_alg = img['img_enc_alg']
+    #print(img_enc_alg)
+    img_enc_inf = img['img_enc_inf']
+    #print(img_enc_inf)
+    iv = img['img_iv']
+    iv = my_binify(iv)
+    #print type(iv)
+    #print("img_enc dump:")
+    #cryptoSB.dump(iv)
+
+    bin = open(img_file, 'rb').read()
+    #cryptoSB.dump(bin)
+
+    #align
+    bin = my_pad_to_align(bin, 64)
+
+    aes = cryptoSB.AESCipher(key, 16)
+    encmsg = aes.aes_encrypt(bin)
+    #print("result image enc:")
+    #cryptoSB.dump(encmsg)
+
+    return encmsg
+
+
+
+
+if __name__ == '__main__':
+    import os, sys, getopt
+
+
+    def print_usage():
+        print ('usage:', os.path.basename(sys.argv[0]), "[options] <image config>.json\n", \
+            'options:\n', \
+            '\t[-o | --output out.img]\n', \
+            '\t[-h | --help]\n', \
+            '\t[-i | --input]\n', \
+            '\t[-k | --prikey hexkey e.g. 0x0102..]\n', \
+            '\t[-s | --enckey hexkey e.g. 0x0102..]\n', \
+            '\t[-p | --pemdir <pem path>\n', \
+            '\t[-d | --imgdir <image path>\n')
+
+
+    def main():
+        opts, args = getopt.getopt(sys.argv[1:],
+                                   'ho:a:i:k:s:p:d:',
+                                   ['help', 'output=', 'align=', 'input=',
+                                    'prikey=', 'enckey=', 'pemdir=', 'imgdir=']
+                                  )
+
+        out_name = None
+        align = 0
+        infile_name = None
+        aeskey = None
+        pubkey = None
+        privk = None
+        pem_dir = "binfile"
+        img_dir = ''
+
+        for o, a in opts:
+            if o in ('-h', '--help'):
+                print_usage()
+                sys.exit()
+            elif o in ('-o', '--output'):
+                out_name = a
+            elif o in ('-a', '--align'):
+                align = int(a)
+            elif o in ('-i', '--input'):
+                ## doesn't need currently
+                infile_name = a
+            elif o in ('-k', '--prikey'):
+                privkey = a
+            elif o in ('-s', '--enckey'):
+                aeskey = a
+            elif o in ('-p', '--pemdir'):
+                pem_dir = a
+            elif o in ('-d', '--imgdir'):
+                img_dir = a
+            else:
+                print_usage()
+                sys.exit(1)
+
+        if len(args) >= 1:
+            mi_desc_fn = args[0]
+        else:
+            print_usage()
+            sys.exit(1)
+
+        if not out_name:
+            fn, ext = os.path.splitext(mi_desc_fn)
+            out_name = fn + '.img'
+
+        """ read json script """
+        mi_desc = read_desc(mi_desc_fn)
+
+        #mipack = pack_images(mi_desc, align)
+
+        #print 'output: %s (%d bytes)' % (out_name, len(mipack))
+        #open(out_name, 'wb').write(mipack)
+
+        cmd_line_key = mi_desc['cmd_line_key']
+        sign_priv_key = mi_desc['sign_priv_key']
+        aes_enc_sym_key = mi_desc['aes_enc_sym_key']
+
+        """ Where is the key input from """
+
+        pem_key_format = mi_desc['pem_key_format']
+        if(pem_key_format == 1):
+            sbc_auth_alg = mi_desc['sbc_auth_alg']
+            if(sbc_auth_alg == 0):
+                key_path = pem_dir + "/ecdsa_p256_private.pem"
+                #print(key_path)
+                privk = open(key_path,"rb").read()
+                #privk = open("binfile/ecdsa_p256_private.pem","rb").read()
+                #pubk_pem = open("binfile/ecdsa_p256_public.pem","rb").read()
+            elif(sbc_auth_alg == 1):
+                privk = open("binfile/ecdsa_p384_private.pem","rb").read()
+
+            key = cryptoSB.strip_key(aes_enc_sym_key)
+
+        else:
+            if(cmd_line_key == 1):
+                key = cryptoSB.strip_key(aeskey)
+                privk = cryptoSB.strip_key(privkey)
+                #print("dump privkey:")
+                #cryptoSB.dump(privk)
+            elif(cmd_line_key == 0):
+                key = cryptoSB.strip_key(aes_enc_sym_key)
+                privk = cryptoSB.strip_key(sign_priv_key)
+                #print("dump privkey:")
+                #cryptoSB.dump(privk)
+            else:
+                prnit("ERROR: please check cmd_line_key json")
+
+
+
+        #run hash
+        #hash = cryptoSB.HASH()
+        #imghash = hash.hash_sha256(enc_data)
+        #cryptoSB.dump(imghash)
+
+        mipack = my_pack_images(mi_desc, privk, key, align, img_dir)
+        if mipack == None:
+            print("my_pack_images fail")
+            return
+
+        #out_name = 'my_' + fn + '.img'
+        #out_name = fn + '.img'
+        print('output: %s (%d bytes)' % (out_name, len(mipack)))
+        open(out_name, 'wb').write(mipack)
+
+    main()
+
diff --git a/src/devtools/packer/mt2735/cryptoSB.py b/src/devtools/packer/mt2735/cryptoSB.py
new file mode 100644
index 0000000..1c44a61
--- /dev/null
+++ b/src/devtools/packer/mt2735/cryptoSB.py
@@ -0,0 +1,615 @@
+import binascii
+import Crypto.Random.OSRNG.posix as RNG
+from Crypto.Cipher import AES
+from Crypto.Hash import MD5
+from Crypto.Hash import SHA
+from Crypto.Hash import SHA224
+from Crypto.Hash import SHA256
+from Crypto.Hash import SHA384
+from Crypto.Hash import SHA512
+from Crypto.Hash import HMAC
+from Crypto.Util import Counter
+import CryptoPlus
+from CryptoPlus.Cipher import python_AES as CP_python_AES
+import ecdsa
+from ecdsa import SigningKey, VerifyingKey
+from ecdsa.curves import NIST192p, NIST224p, NIST256p, NIST384p, NIST521p
+from ecdsa.util import sigencode_string
+from hashlib import sha1, sha224, sha256, sha384, sha512
+
+#from pycrypto_lib.SelfTest.Signature import test_pkcs1_15_mytest as rsa_mytest
+#from pycrypto_lib.SelfTest.Signature import test_pkcs1_pss_mytest as rsa_pss_mytest
+import test_pkcs1_15_mytest as rsa_mytest
+import test_pkcs1_pss_mytest as rsa_pss_mytest
+
+''' Select ECDSA Curve '''
+__CURVE = 'prime256v1'
+
+AUTH_ECDSA_NISTP256 = 0
+AUTH_ECDSA_NISTP384 = 1
+AUTH_ECDSA_NISTP521 = 2
+AUTH_RSA2048 = 3
+AUTH_RSA3072 = 4
+AUTH_RSA4096 = 5
+
+AUTH_AES_CMAC = 12
+AUTH_HMAC_SHA256 = 13
+
+AUTH_ECDSA_NISTP192 = 10
+AUTH_ECDSA_NISTP224 = 11
+
+
+''' Select ECDSA module '''
+
+''' Select Key Mode '''
+__K_STRING = 0
+__K_PEM = 1
+__K_DER = 2
+
+
+def crypto_pad_to_align(x, align):
+    if(align == 0):
+        return x
+    else:
+        size = len(x)
+        #print("size 0x%x" %size)
+        pad_size = (align - size % align) % align
+        for i in range(0, pad_size, 1):
+            x += b'\x00'
+        #cryptoSB.dump(x)
+        return x
+
+
+# This module is python-ecdsa (warner/python-ecdsa)
+class pythonECDSA:
+	def __init__(self, priv=None, pub=None, curve=None):
+		#print("pure-python ECDSA init")
+                None
+	def sign_string(self, privk, msg, sbc_auth_alg, sbc_auth_alg_hash):
+		if(sbc_auth_alg == AUTH_ECDSA_NISTP256):
+			if(sbc_auth_alg_hash == 0):
+				sk = SigningKey.from_string(privk, curve=NIST256p, hashfunc=sha256)
+				signature_str = sk.sign(msg, hashfunc=sha256, sigencode=sigencode_string)
+			elif(sbc_auth_alg_hash == 1):
+				sk = SigningKey.from_string(privk, curve=NIST256p, hashfunc=sha384)
+				signature_str = sk.sign(msg, hashfunc=sha384, sigencode=sigencode_string)
+			elif(sbc_auth_alg_hash == 2):
+				sk = SigningKey.from_string(privk, curve=NIST256p, hashfunc=sha512)
+				signature_str = sk.sign(msg, hashfunc=sha512, sigencode=sigencode_string)
+		elif(sbc_auth_alg == AUTH_ECDSA_NISTP384):
+			if(sbc_auth_alg_hash == 0):
+				sk = SigningKey.from_string(privk, curve=NIST384p, hashfunc=sha256)
+				signature_str = sk.sign(msg, hashfunc=sha256, sigencode=sigencode_string)
+			elif(sbc_auth_alg_hash == 1):
+				sk = SigningKey.from_string(privk, curve=NIST384p, hashfunc=sha384)
+				signature_str = sk.sign(msg, hashfunc=sha384, sigencode=sigencode_string)
+			elif(sbc_auth_alg_hash == 2):
+				sk = SigningKey.from_string(privk, curve=NIST384p, hashfunc=sha512)
+				signature_str = sk.sign(msg, hashfunc=sha512, sigencode=sigencode_string)
+		elif(sbc_auth_alg == AUTH_ECDSA_NISTP521):
+			if(sbc_auth_alg_hash == 0):
+				sk = SigningKey.from_string(privk, curve=NIST521p, hashfunc=sha256)
+				signature_str = sk.sign(msg, hashfunc=sha256, sigencode=sigencode_string)
+			elif(sbc_auth_alg_hash == 1):
+				sk = SigningKey.from_string(privk, curve=NIST521p, hashfunc=sha384)
+				signature_str = sk.sign(msg, hashfunc=sha384, sigencode=sigencode_string)
+			elif(sbc_auth_alg_hash == 2):
+				sk = SigningKey.from_string(privk, curve=NIST521p, hashfunc=sha512)
+				signature_str = sk.sign(msg, hashfunc=sha512, sigencode=sigencode_string)
+		elif(sbc_auth_alg == AUTH_ECDSA_NISTP192):
+			sk = SigningKey.from_string(privk, curve=NIST192p, hashfunc=sha256)
+			signature_str = sk.sign(msg, hashfunc=sha256, sigencode=sigencode_string)
+		elif(sbc_auth_alg == AUTH_ECDSA_NISTP224):
+			sk = SigningKey.from_string(privk, curve=NIST224p, hashfunc=sha256)
+			signature_str = sk.sign(msg, hashfunc=sha256, sigencode=sigencode_string)
+		return signature_str
+	def sign_pem(self, privk, msg, sbc_auth_alg):
+		#print("    run sign pem")
+		if(sbc_auth_alg == AUTH_ECDSA_NISTP256):
+			sk = SigningKey.from_pem(privk, hashfunc=sha256)
+			signature_pem = sk.sign(msg, hashfunc=sha256)
+
+		elif(sbc_auth_alg == AUTH_ECDSA_NISTP384):
+			sk = SigningKey.from_pem(privk, hashfunc=sha384)
+			signature_pem = sk.sign(msg, hashfunc=sha384)
+
+		elif(sbc_auth_alg == AUTH_ECDSA_NISTP192):
+			sk = SigningKey.from_pem(privk, hashfunc=sha1)
+			signature_pem = sk.sign(msg, hashfunc=sha1)
+
+		elif(sbc_auth_alg == AUTH_ECDSA_NISTP224):
+			sk = SigningKey.from_pem(privk, hashfunc=sha224)
+			signature_pem = sk.sign(msg, hashfunc=sha224)
+
+		return signature_pem
+	def sign_der(self, privk, msg, sbc_auth_alg):
+		#print("    run sign der")
+		if(sbc_auth_alg == AUTH_ECDSA_NISTP256):
+			sk = SigningKey.from_der(privk, hashfunc=sha256)
+			signature_der = sk.sign(msg, hashfunc=sha256)
+
+		elif(sbc_auth_alg == AUTH_ECDSA_NISTP384):
+			sk = SigningKey.from_der(privk, hashfunc=sha384)
+			signature_der = sk.sign(msg, hashfunc=sha384)
+
+		elif(sbc_auth_alg == AUTH_ECDSA_NISTP192):
+			sk = SigningKey.from_der(privk, hashfunc=sha1)
+			signature_der = sk.sign(msg, hashfunc=sha1)
+
+		elif(sbc_auth_alg == AUTH_ECDSA_NISTP224):
+			sk = SigningKey.from_der(privk, hashfunc=sha224)
+			signature_der = sk.sign(msg, hashfunc=sha224)
+
+
+		return signature_der
+	def verify_string(self, pubk, signature_str, msg, sbc_auth_alg):
+		if(sbc_auth_alg == AUTH_ECDSA_NISTP256):
+			vk = VerifyingKey.from_string(pubk, curve=NIST256p, hashfunc=sha256)
+			ret = vk.verify(signature_str, msg)
+		elif(sbc_auth_alg == AUTH_ECDSA_NISTP384):
+			vk = VerifyingKey.from_string(pubk, curve=NIST384p, hashfunc=sha384)
+			ret = vk.verify(signature_str, msg)
+		elif(sbc_auth_alg == AUTH_ECDSA_NISTP192):
+			vk = VerifyingKey.from_string(pubk, curve=NIST192p, hashfunc=sha1)
+			ret = vk.verify(signature_str, msg)
+		elif(sbc_auth_alg == AUTH_ECDSA_NISTP224):
+			vk = VerifyingKey.from_string(pubk, curve=NIST224p, hashfunc=sha224)
+			ret = vk.verify(signature_str, msg)
+		return ret
+	def verify_pem(self, pubk, signature_pem, msg, sbc_auth_alg):
+		#print("    run verify pem")
+		if(sbc_auth_alg == AUTH_ECDSA_NISTP256):
+			vk = VerifyingKey.from_pem(pubk)
+			ret = vk.verify(signature_pem, msg, hashfunc=sha256)
+
+		elif(sbc_auth_alg == AUTH_ECDSA_NISTP384):
+			vk = VerifyingKey.from_pem(pubk)
+			ret = vk.verify(signature_pem, msg, hashfunc=sha384)
+
+		elif(sbc_auth_alg == AUTH_ECDSA_NISTP192):
+			vk = VerifyingKey.from_pem(pubk)
+			ret = vk.verify(signature_pem, msg, hashfunc=sha1)
+
+		elif(sbc_auth_alg == AUTH_ECDSA_NISTP224):
+			vk = VerifyingKey.from_pem(pubk)
+			ret = vk.verify(signature_pem, msg, hashfunc=sha224)
+		return ret
+	def verify_der(self, pubk, signature_der, msg, sbc_auth_alg):
+		#print("    run verify der")
+		if(sbc_auth_alg == AUTH_ECDSA_NISTP256):
+			vk = VerifyingKey.from_der(pubk)
+			ret = vk.verify(signature_der, msg, hashfunc=sha256)
+
+		elif(sbc_auth_alg == AUTH_ECDSA_NISTP384):
+			vk = VerifyingKey.from_der(pubk)
+			ret = vk.verify(signature_der, msg, hashfunc=sha384)
+
+		elif(sbc_auth_alg == AUTH_ECDSA_NISTP192):
+			vk = VerifyingKey.from_der(pubk)
+			ret = vk.verify(signature_der, msg, hashfunc=sha1)
+
+		elif(sbc_auth_alg == AUTH_ECDSA_NISTP224):
+			vk = VerifyingKey.from_der(pubk)
+			ret = vk.verify(signature_der, msg, hashfunc=sha224)
+		return ret
+	def gen_key_string(self, sbc_auth_alg):
+		if(sbc_auth_alg == AUTH_ECDSA_NISTP256):
+			sk = SigningKey.generate(curve=NIST256p, hashfunc=sha256)
+			vk = sk.get_verifying_key()
+		elif(sbc_auth_alg == AUTH_ECDSA_NISTP384):
+			sk = SigningKey.generate(curve=NIST384p, hashfunc=sha384)
+			vk = sk.get_verifying_key()
+		elif(sbc_auth_alg == AUTH_ECDSA_NISTP192):
+			sk = SigningKey.generate(curve=NIST192p, hashfunc=sha1)
+			vk = sk.get_verifying_key()
+		elif(sbc_auth_alg == AUTH_ECDSA_NISTP224):
+			sk = SigningKey.generate(curve=NIST224p, hashfunc=sha224)
+			vk = sk.get_verifying_key()
+		privk_str = sk.to_string()
+		pubk_str = vk.to_string()
+		return privk_str, pubk_str
+	def gen_key_pem(self, sbc_auth_alg):
+		if(sbc_auth_alg == AUTH_ECDSA_NISTP256):
+			sk = SigningKey.generate(curve=NIST256p, hashfunc=sha256)
+			vk = sk.get_verifying_key()
+		elif(sbc_auth_alg == AUTH_ECDSA_NISTP384):
+			sk = SigningKey.generate(curve=NIST384p, hashfunc=sha384)
+			vk = sk.get_verifying_key()
+		elif(sbc_auth_alg == AUTH_ECDSA_NISTP192):
+			sk = SigningKey.generate(curve=NIST192p, hashfunc=sha1)
+			vk = sk.get_verifying_key()
+		elif(sbc_auth_alg == AUTH_ECDSA_NISTP224):
+			sk = SigningKey.generate(curve=NIST224p, hashfunc=sha224)
+			vk = sk.get_verifying_key()
+		privk_pem = sk.to_pem()
+		pubk_pem = vk.to_pem()
+		return privk_pem, pubk_pem
+	def gen_key_der(self, sbc_auth_alg):
+		if(sbc_auth_alg == AUTH_ECDSA_NISTP256):
+			sk = SigningKey.generate(curve=NIST256p, hashfunc=sha256)
+			vk = sk.get_verifying_key()
+		elif(sbc_auth_alg == AUTH_ECDSA_NISTP384):
+			sk = SigningKey.generate(curve=NIST384p, hashfunc=sha384)
+			vk = sk.get_verifying_key()
+		elif(sbc_auth_alg == AUTH_ECDSA_NISTP384):
+			sk = SigningKey.generate(curve=NIST192p, hashfunc=sha1)
+			vk = sk.get_verifying_key()
+		elif(sbc_auth_alg == AUTH_ECDSA_NISTP224):
+			sk = SigningKey.generate(curve=NIST224p, hashfunc=sha224)
+			vk = sk.get_verifying_key()
+		privk_der = sk.to_der()
+		pubk_der = vk.to_der()
+		return privk_der, pubk_der
+
+class AESCipher:
+	def __init__(self, key, blk_sz):
+		#self.key = md5(key.encode('utf8')).hexdigest()
+		self.key = key
+		self.blk_sz = blk_sz
+		keylen = len(key)
+		if(keylen != blk_sz):
+		    print("ERROR: key length fail:")
+		    return 0
+
+	def aes_encrypt(self, data):
+		#data = pad(data)
+		#print(":".join("{:02x}".format(ord(e)) for e in data))
+		cryptor = AES.new(self.key, AES.MODE_ECB)
+		return cryptor.encrypt(data)
+
+	def aes_decrypt(self, data):
+		#data = pad(data)
+		#print(":".join("{:02x}".format(ord(e)) for e in data))
+		cryptor = AES.new(self.key, AES.MODE_ECB)
+		return cryptor.decrypt(data)
+
+	def aes_cbc_encrypt(self, data, iv):
+		cryptor = AES.new(self.key, AES.MODE_CBC, iv)
+		return cryptor.encrypt(data)
+
+	def aes_cbc_decrypt(self, data, iv):
+		cryptor = AES.new(self.key, AES.MODE_CBC, iv)
+		return cryptor.decrypt(data)
+
+	def aes_ctr_encrypt_only_counter(self, data, prefix_const, init_counter):
+		#ctr_e = Counter.new(64, prefix = prefix_const)
+		#cryptor = AES.new(self.key, AES.MODE_CTR, counter=lambda: init_counter)
+
+		ctr_e = Counter.new(128, initial_value=long(init_counter.encode('hex'), 16), allow_wraparound=True)
+		cryptor = AES.new(self.key, AES.MODE_CTR, counter=ctr_e)
+
+		return cryptor.encrypt(data)
+
+	def aes_ctr_decrypt_only_counter(self, data, prefix_const, init_counter):
+		#ctr_d = Counter.new(128, prefix = prefix_const)
+		#cryptor = AES.new(self.key, AES.MODE_CTR, counter=lambda: init_counter)
+
+		ctr_d = Counter.new(128, initial_value=long(init_counter.encode('hex'), 16), allow_wraparound=True)
+		cryptor = AES.new(self.key, AES.MODE_CTR, counter=ctr_d)
+		return cryptor.decrypt(data)
+
+	def aes_ctr_encrypt(self, data, prefix_str, initvalue):
+		#print("0x%x" %long(initvalue.encode('hex'), 16))
+		#print(len(prefix_str))
+		nbits = (128 - (len(prefix_str)*8))
+		ctr_e = Counter.new(nbits, prefix = prefix_str, initial_value=long(initvalue.encode('hex'), 16), allow_wraparound=True)
+		cryptor = AES.new(self.key, AES.MODE_CTR, counter=ctr_e)
+
+		return cryptor.encrypt(data)
+
+	def aes_ctr_decrypt(self, data, prefix_str, initvalue):
+		nbits = (128 - (len(prefix_str)*8))
+		ctr_d = Counter.new(nbits, prefix = prefix_str, initial_value=long(initvalue.encode('hex'), 16), allow_wraparound=True)
+		cryptor = AES.new(self.key, AES.MODE_CTR, counter=ctr_d)
+
+		return cryptor.decrypt(data)
+
+
+def strip_key(key):
+    #src_key = key.lstrip("x").rstrip("L")
+    src_key = key[2:].rstrip("L")
+    key_byte = binascii.a2b_hex(src_key)
+    return key_byte
+
+def my_a2b_hex(key):
+    key_byte = binascii.a2b_hex(key)
+    return key_byte
+
+def my_img_enc(data, key, iv, img_enc_alg, align=None):
+    #dump(data)
+
+    #align
+    bin = crypto_pad_to_align(data, align)
+
+    if(img_enc_alg == 0):
+        # AES 128 ECB
+        aes = AESCipher(key, 16)
+        encmsg = aes.aes_encrypt(bin)
+        #print("AES 128 ECB image result:")
+        #dump(encmsg)
+    elif(img_enc_alg == 1):
+        # AES 128 CBC
+        aes = AESCipher(key, 16)
+        encmsg = aes.aes_cbc_encrypt(bin, iv)
+        #print("AES 128 CBC image result:")
+        #dump(encmsg)
+    elif(img_enc_alg == 2):
+        # AES 256 ECB
+        aes = AESCipher(key, 32)
+        encmsg = aes.aes_encrypt(bin)
+        #print("AES 256 ECB image result:")
+        #dump(encmsg)
+    elif(img_enc_alg == 3):
+        # AES 256 CBC
+        aes = AESCipher(key, 32)
+        encmsg = aes.aes_cbc_encrypt(bin, iv)
+        #print("AES 256 CBC image result:")
+        #dump(encmsg)
+
+    return encmsg
+
+# dump str to bytes
+def dump(data):
+	for i in range(0, len(data)):
+		print ("0x%02x,"%ord(data[i])),
+		if(((i+1)%16) == 0):
+			print("")
+
+class HASH:
+	def __init__(self):
+		#print("//class HASH init")
+                None
+
+	def hash_sha1(self, data):
+		#print("SHA1:")
+		hash = SHA.new()
+		hash.update(data)
+		digest = hash.digest()
+		return digest
+	def hash_sha224(self, data):
+		#print("SHA224:")
+		hash = SHA224.new()
+		hash.update(data)
+		digest = hash.digest()
+		return digest
+	def hash_sha256(self, data):
+		#print("SHA256:")
+		hash = SHA256.new()
+		hash.update(data)
+		digest = hash.digest()
+		return digest
+	def hash_sha384(self, data):
+		#print("SHA384:")
+		hash = SHA384.new()
+		hash.update(data)
+		digest = hash.digest()
+		return digest
+
+	def hash_sha512(self, data):
+		#print("SHA512:")
+		hash = SHA512.new()
+		hash.update(data)
+		digest = hash.digest()
+		return digest
+	def hash_md5(self, data):
+		#print("MD5:")
+		hash = MD5.new()
+		hash.update(data)
+		digest = hash.digest()
+		return digest
+
+'''
+def sb_hash(data, sbc_auth_alg):
+    #print("run my_hash:")
+
+    #myhash = HASH()
+
+    if((sbc_auth_alg == 1) or (sbc_auth_alg == 5) ):
+        sbhash = HASH()
+        digest = sbhash.hash_sha384(data)
+        return digest
+    elif(sbc_auth_alg == 0) or (sbc_auth_alg == 2) or (sbc_auth_alg == 3) or (sbc_auth_alg == 4):
+        sbhash = HASH()
+        digest = sbhash.hash_sha256(data)
+        return digest
+    elif((sbc_auth_alg == 6)):
+        sbhash = HASH()
+        digest = sbhash.hash_sha512(data)
+        return digest
+    else:
+        print("ERROR: run sb_hash fail")
+'''
+def sb_hash(data, sbc_auth_alg_hash):
+    #print("run my_hash:")
+
+    #myhash = HASH()
+
+    if(sbc_auth_alg_hash == 0):
+        sbhash = HASH()
+        digest = sbhash.hash_sha256(data)
+        return digest
+    elif(sbc_auth_alg_hash == 1):
+        sbhash = HASH()
+        digest = sbhash.hash_sha384(data)
+        return digest
+    elif(sbc_auth_alg_hash == 2):
+        sbhash = HASH()
+        digest = sbhash.hash_sha512(data)
+        return digest
+    else:
+        print("ERROR: run sb_hash fail")
+
+
+def hmac_sha256(key, bdata):
+
+    b = HMAC.new(key, digestmod=SHA256)
+    b.update(bdata)
+    digest = b.digest()
+    #digest = b.hexdigest()
+    #dump(digest)
+
+    return digest
+
+def aescmac(key, msg):
+    #print("AES CMAC: ")
+    cipher = CP_python_AES.new(key, CP_python_AES.MODE_CMAC)
+    out = cipher.encrypt(msg)
+
+    #outhex = cipher.encrypt(msg).encode('hex')
+    #out = outhex.decode('hex')
+    return out
+
+def ecdsa_sign_nistp256_string(privk, msg, sbc_auth_alg_hash):
+	ecdsa = pythonECDSA()
+	signature = ecdsa.sign_string(privk, msg, AUTH_ECDSA_NISTP256, sbc_auth_alg_hash)
+
+	return signature
+
+def ecdsa_sign_nistp256_pem(privk, msg):
+	ecdsa = pythonECDSA()
+	signature = ecdsa.sign_pem(privk, msg, AUTH_ECDSA_NISTP256)
+	return signature
+
+def ecdsa_sign_nistp256_der(privk, msg):
+	ecdsa = pythonECDSA()
+	signature = ecdsa.sign_der(privk, msg, AUTH_ECDSA_NISTP256)
+
+	return signature
+
+def ecdsa_sign_nistp384_string(privk, msg, sbc_auth_alg_hash):
+	ecdsa = pythonECDSA()
+	signature = ecdsa.sign_string(privk, msg, AUTH_ECDSA_NISTP384, sbc_auth_alg_hash)
+	return signature
+
+def ecdsa_sign_nistp384_pem(privk, msg):
+	ecdsa = pythonECDSA()
+	signature = ecdsa.sign_pem(privk, msg, AUTH_ECDSA_NISTP384)
+
+	return signature
+
+def ecdsa_sign_nistp384_der(privk, msg):
+	ecdsa = pythonECDSA()
+	signature = ecdsa.sign_der(privk, msg, AUTH_ECDSA_NISTP384)
+
+	return signature
+
+def ecdsa_sign_nistp521_string(privk, msg, sbc_auth_alg_hash):
+	ecdsa = pythonECDSA()
+	signature = ecdsa.sign_string(privk, msg, AUTH_ECDSA_NISTP521, sbc_auth_alg_hash)
+	return signature
+
+def pubkey_string(pubk, sbc_auth_alg):
+	sk = VerifyingKey.from_pem(pubk)
+	return sk.to_string()
+def privkey_string(privk, sbc_auth_alg):
+	if(sbc_auth_alg == AUTH_ECDSA_NISTP256):
+		sk = SigningKey.from_pem(privk, hashfunc=sha256)
+		return sk.to_string()
+	else:
+		sk = SigningKey.from_pem(privk, hashfunc=sha384)
+		return sk.to_string()
+
+def sb_sign(privk, pubk, msg, sbc_auth_alg, sbc_auth_alg_hash, rsa_parse, sbc_rsa_pad_sel, key_mode=__K_STRING):
+    print("Start sb_sign:")
+
+    if(sbc_auth_alg == AUTH_ECDSA_NISTP256):
+        #pyelliptic ECDSA NIST p256
+        if(key_mode == __K_STRING):
+            signature = ecdsa_sign_nistp256_string(privk, msg, sbc_auth_alg_hash)
+            #another use
+            #ecdsa = pythonECDSA()
+	    #signature = ecdsa.sign_string(privk, msg, AUTH_ECDSA_NISTP256)
+
+        elif(key_mode == __K_PEM):
+            signature = ecdsa_sign_nistp256_pem(privk, msg)
+
+        elif(key_mode == __K_DER):
+            signature = ecdsa_sign_nistp256_der(privk, msg)
+        return signature
+
+    elif(sbc_auth_alg == AUTH_ECDSA_NISTP384):
+        #pyelliptic ECDSA_NIST_p384
+        if(key_mode == __K_STRING):
+            signature = ecdsa_sign_nistp384_string(privk, msg, sbc_auth_alg_hash)
+
+        elif(key_mode == __K_PEM):
+            signature = ecdsa_sign_nistp384_pem(privk, msg)
+
+        elif(key_mode == __K_DER):
+            signature = ecdsa_sign_nistp384_der(privk, msg)
+
+        return signature
+
+    elif(sbc_auth_alg == AUTH_ECDSA_NISTP521):
+        #pyelliptic ECDSA_NIST_p521
+        if(key_mode == __K_STRING):
+            signature = ecdsa_sign_nistp521_string(privk, msg, sbc_auth_alg_hash)
+        return signature	
+    elif(sbc_auth_alg == AUTH_RSA2048):
+        if(sbc_rsa_pad_sel == 0):
+            print("auth rsa2048 pkcs1_v15 padding")
+            myrsa = rsa_mytest.sbc_rsa_sign1_mytest(rsa_parse, msg)
+            print("mytest rsa pkcs1_15 sign:")
+            signature = myrsa.sbc_rsa_sign1_json()
+            print("mytest rsa pkcs1_15 verify:")
+            myrsa.sbc_rsa_verify1()
+        elif(sbc_rsa_pad_sel == 1):
+            print("auth rsa2048 pkcs1_pss padding")
+            myrsa = rsa_pss_mytest.testSign1_mytest(rsa_parse, msg)
+            print("mytest rsa pkcs1_pss sign:")
+            signature = myrsa.sbc_rsa_sign1_json()
+            print("mytest rsa pkcs1_pss verify:")
+            myrsa.verify1_mytest()
+        else:
+            print("ERROR: padding selection fail")
+        return signature
+
+    elif(sbc_auth_alg == AUTH_RSA3072):
+        if(sbc_rsa_pad_sel == 0):
+            print("auth rsa3072 pkcs1_v15 padding")
+            myrsa = rsa_mytest.sbc_rsa_sign1_mytest(rsa_parse, msg)
+            print("mytest rsa pkcs1_15 sign:")
+            signature = myrsa.sbc_rsa_sign1_json()
+            print("mytest rsa pkcs1_15 verify:")
+            myrsa.sbc_rsa_verify1()
+        elif(sbc_rsa_pad_sel == 1):
+            print("auth rsa3072 pkcs1_pss padding")
+            myrsa = rsa_pss_mytest.testSign1_mytest(rsa_parse, msg)
+            print("mytest rsa pkcs1_pss sign:")
+            signature = myrsa.sbc_rsa_sign1_json()
+            print("mytest rsa pkcs1_pss verify:")
+            myrsa.verify1_mytest()
+        else:
+            print("ERROR: padding selection fail")
+        return signature
+
+    elif(sbc_auth_alg == AUTH_RSA4096):
+        if(sbc_rsa_pad_sel == 0):
+            print("auth rsa4096 pkcs1_v15 padding")
+            myrsa = rsa_mytest.sbc_rsa_sign1_mytest(rsa_parse, msg)
+            print("mytest rsa pkcs1_15 sign:")
+            signature = myrsa.sbc_rsa_sign1_json()
+            print("mytest rsa pkcs1_15 verify:")
+            myrsa.sbc_rsa_verify1()
+        elif(sbc_rsa_pad_sel == 1):
+            print("auth rsa4096 pkcs1_pss padding")
+            myrsa = rsa_pss_mytest.testSign1_mytest(rsa_parse, msg)
+            print("mytest rsa pkcs1_pss sign:")
+            signature = myrsa.sbc_rsa_sign1_json()
+            print("mytest rsa pkcs1_pss verify:")
+            myrsa.verify1_mytest()
+        else:
+            print("ERROR: padding selection fail")
+        return signature
+
+    elif(sbc_auth_alg == AUTH_AES_CMAC):
+        #AES128_CMAC
+        signature = aescmac(privk, msg)
+        return signature
+    elif(sbc_auth_alg == AUTH_HMAC_SHA256):
+        #HMAC-SHA256
+        signature = hmac_sha256(privk, msg)
+        return signature
+    else:
+        print("ERROR: wrong authentication algorithm!!")
+
diff --git a/src/devtools/packer/mt2735/mipack.py b/src/devtools/packer/mt2735/mipack.py
new file mode 100644
index 0000000..8148381
--- /dev/null
+++ b/src/devtools/packer/mt2735/mipack.py
@@ -0,0 +1,879 @@
+#!/usr/bin/python
+# -*- coding: utf8 -*-
+
+import json
+import struct
+import sys
+import binascii
+import cryptoSB
+import copy
+
+from jsoncomment import JsonComment
+
+__Align = 16
+__Align_16_en = 0
+
+__TEST = 0
+
+__Align_pub_key_apend = 512
+__Align_sign_size = 512
+
+g_magic_size = 8
+g_total_len_size = 4
+g_bl_version_size = 4
+g_img_number_size = 4
+g_ls_cmd_number_size = 4
+g_mi_header_info_size = (g_magic_size + g_total_len_size + g_bl_version_size + g_img_number_size + g_ls_cmd_number_size)
+
+g_img_header_size = 96
+g_ls_cmd_size = 8
+
+g_img_oneoffset_table_size = 4
+g_img_onehash_table_size = 64
+
+g_public_key_size = __Align_pub_key_apend
+g_rsa_n_size = __Align_pub_key_apend
+g_sbc_auth_inf_size = 4
+g_sbc_auth_key_hash_size = 4
+g_sbc_auth_alg_size = 4
+g_sbc_auth_alg_hash_size = 4
+g_sbc_rsa_pad_sel_size = 4
+g_sbc_pub_key_len_size = 4
+g_sbc_size = (g_public_key_size + g_rsa_n_size + g_sbc_auth_inf_size + g_sbc_auth_key_hash_size + g_sbc_auth_alg_size + g_sbc_auth_alg_hash_size + g_sbc_rsa_pad_sel_size + g_sbc_pub_key_len_size)
+
+g_boot_hash_size = 64
+g_signature_size = __Align_sign_size
+g_auth_size = (g_boot_hash_size + g_signature_size)
+
+g_ck_magic_size = 16
+g_ck_soc_id_size = 32
+g_ck_pub_key_size = __Align_pub_key_apend
+g_ck_auth_algo_size = 4
+g_ck_put_key_len_size = 4
+g_ck_signature_size = __Align_sign_size
+g_ck_size = (g_ck_magic_size + g_ck_soc_id_size + g_ck_pub_key_size + g_ck_auth_algo_size + g_ck_put_key_len_size + g_ck_signature_size)
+
+def to_int(s):
+    if isinstance(s, (str, unicode)):
+        return int(s, 0)
+    else:
+        return s
+
+def byte_to_int(bytes):
+    result = 0
+
+    for b in bytes:
+        result = result*256 + ord(b)
+
+    return result
+
+def read_desc(mi_desc_fn):
+    #desc = json.load(open(mi_desc_fn))
+
+    parser = JsonComment(json)
+    desc = parser.loads(open(mi_desc_fn).read())
+
+    for img in desc['images']:
+        img['img_enc_alg'] = to_int(img['img_enc_alg'])
+        img['img_enc_inf'] = to_int(img['img_enc_inf'])
+
+        '''0627 remove int assign avoid key start is (0x00..)'''
+        #img['img_iv'] = to_int(img['img_iv'])
+
+    return desc
+
+
+def gen_ver_magic(major, minor, revision):
+    major = to_int(major)
+    minor = to_int(minor)
+    revision = to_int(revision)
+
+    x = major ^ 0xaa
+    y = minor ^ 0x55
+    z = revision ^ 0x99
+    checksum = (x + y + z) & 0xff
+
+    return (major << 0) | (minor << 8) | (revision << 16) | (checksum << 24)
+
+def my_gen_ver_magic(major, minor, revision):
+    major = to_int(major)
+    minor = to_int(minor)
+    revision = to_int(revision)
+
+    x = major ^ 0xaa
+    y = minor ^ 0x55
+    z = revision ^ 0x99
+    checksum = (x + y + z) & 0xff
+
+    highmagic = byte_to_int(b'\x27\x21\xca\xfe')
+    #print(hex(highmagic))
+
+    return (major << 0) | (minor << 8) | (revision << 16) | (checksum << 24), highmagic
+
+def my_gen_mtk_magic(magic):
+    magic_str = my_to_bytes(magic, g_magic_size, endianess='little')
+
+    lowmagic = byte_to_int(magic_str[4:8])
+    highmagic = byte_to_int(magic_str[0:4])
+
+    return lowmagic, highmagic
+
+def pad_to_align(align, current_size):
+    if not align:
+        return ''
+
+    pad_size = (align - current_size % align) % align
+    pad = '\0' * pad_size
+
+    return pad
+
+
+def dump(data):
+    for i in range(0, len(data)):
+        if(i%16 == 0):
+            print("[%04X]" % i),
+
+        print("%02x" % ord(data[i])),
+        if(((i+1)%16) == 0):
+            print
+
+def pack_load_script(mi_desc):
+    ls_script = []
+    reserved = 0
+    images = mi_desc['images']
+    img_num = len(images)
+
+    ls_cmds = mi_desc['load_srcipt_cmd']
+    ls_cmds_num = len(ls_cmds)
+    #print("ls_cmds_num: %d" %ls_cmds_num)
+
+    for i in range(ls_cmds_num):
+        ls_cmd = ls_cmds[i]
+        cmd = ls_cmd['cmd']
+
+        if cmd == "LOAD":
+            cmd_id = 0
+            img_file = ls_cmd['img_file']
+            addr = to_int(ls_cmd['addr'])
+
+            img_id = -1
+            for j in range(img_num):
+                img = images[j]
+                img_binary_name = img['img_file']
+                if img_file == img_binary_name:
+                    img_id = j
+                    break
+
+            if img_id == -1:
+                print("Please check img file name")
+                return
+
+            packed_cmd = struct.pack('<BBHI', cmd_id, img_id, reserved, addr)
+            #dump(packed_cmd)
+            #print
+        elif cmd == "MCU-ENTRY":
+            cmd_id = 2
+            mcu_id =  ls_cmd['mcu_id']
+            addr = to_int(ls_cmd['addr'])
+            packed_cmd = struct.pack('<BBHI', cmd_id, mcu_id, reserved, addr)
+            #dump(packed_cmd)
+            #print
+        elif cmd == "MCU_RESET-ENTRY":
+            cmd_id = 1
+            mcu_id =  ls_cmd['mcu_id']
+            addr = to_int(ls_cmd['addr'])
+            packed_cmd = struct.pack('<BBHI', cmd_id, mcu_id, reserved, addr)
+            #dump(packed_cmd)
+            #print
+        else:
+            print("unknown command: %s" %cmd)
+            return
+
+        ls_script += packed_cmd
+
+    #print("load script:")
+    #dump(ls_script)
+
+    return ls_script
+
+def rsa_data_parser(mi_desc, sbc_rsa_pad_sel):
+    rsa_dict = {"n":'', "e":'', "d":''}
+    rsa_parse = []
+
+    if(sbc_rsa_pad_sel == 0):
+        rsa_d = mi_desc['rsa_data']
+        rsa_data = rsa_d[0]
+
+        rsa_dict['n'] = rsa_data['n']
+        rsa_dict['e'] = rsa_data['e']
+        rsa_dict['d'] = rsa_data['d']
+        rsa_parse.append(rsa_dict)
+        #print(rsa_parse)
+    
+        #rsa_parse.append(rsa_data['n'])
+        #rsa_parse.append(rsa_data['e'])
+        #rsa_parse.append(rsa_data['d'])
+        rsa_parse.append(rsa_data['msg2hash'])
+        rsa_parse.append(rsa_data['signvalue'])
+        rsa_parse.append(rsa_data['shafunc'])
+        rsa_parse.append(rsa_data['msgtest'])
+        print("dump rsa_data_parser")
+        #print(rsa_parse)
+        #print(rsa_parse[0])
+    else:
+        rsa_d = mi_desc['rsa_data_pss']
+        rsa_data = rsa_d[0]
+
+        rsa_dict['n'] = rsa_data['n']
+        rsa_dict['e'] = rsa_data['e']
+        rsa_dict['d'] = rsa_data['d']
+        rsa_parse.append(rsa_dict)
+    
+        rsa_parse.append(rsa_data['msg2hash'])
+        rsa_parse.append(rsa_data['signvalue'])
+        rsa_parse.append(rsa_data['salt'])
+        rsa_parse.append(rsa_data['shafunc'])
+        rsa_parse.append(rsa_data['msgtest'])
+        print("dump rsa_data_parser")
+        print(rsa_parse)
+
+    return rsa_parse
+
+def ck_rsa_data_parser(mi_desc, sbc_rsa_pad_sel):
+    rsa_dict = {"n":'', "e":'', "d":''}
+    rsa_parse = []
+
+    if(sbc_rsa_pad_sel == 0):
+        rsa_d = mi_desc['ck_rsa_data']
+        rsa_data = rsa_d[0]
+
+        rsa_dict['n'] = rsa_data['n']
+        rsa_dict['e'] = rsa_data['e']
+        rsa_dict['d'] = rsa_data['d']
+        rsa_parse.append(rsa_dict)
+        #print(rsa_parse)
+
+        #rsa_parse.append(rsa_data['n'])
+        #rsa_parse.append(rsa_data['e'])
+        #rsa_parse.append(rsa_data['d'])
+        rsa_parse.append(rsa_data['msg2hash'])
+        rsa_parse.append(rsa_data['signvalue'])
+        rsa_parse.append(rsa_data['shafunc'])
+        rsa_parse.append(rsa_data['msgtest'])
+        print("dump rsa_data_parser")
+        #print(rsa_parse)
+        #print(rsa_parse[0])
+    else:
+        rsa_d = mi_desc['ck_rsa_data_pss']
+        rsa_data = rsa_d[0]
+
+        rsa_dict['n'] = rsa_data['n']
+        rsa_dict['e'] = rsa_data['e']
+        rsa_dict['d'] = rsa_data['d']
+        rsa_parse.append(rsa_dict)
+
+        rsa_parse.append(rsa_data['msg2hash'])
+        rsa_parse.append(rsa_data['signvalue'])
+        rsa_parse.append(rsa_data['salt'])
+        rsa_parse.append(rsa_data['shafunc'])
+        rsa_parse.append(rsa_data['msgtest'])
+        print("dump rsa_data_parser")
+        print(rsa_parse)
+
+    return rsa_parse
+
+def my_pack_images(mi_desc, privk, enckey, align=None, img_dir=''):
+    '''
+    mipack_file
+        mi_header_info
+            u32 magic
+            u32 total_len
+            u32 bl_version
+            u32 img_number (N)
+            u32 load_script_cmd_number (M)
+
+        img_info[1]~img_info[N]
+            u32     img_length
+            u32     img_offset
+            64Byes  img_hash
+            u32     img_enc_inf
+            u32     img_enc_alg
+            16Bytes img_iv
+
+        load_script_cmd[1]~load_script_cmd[M]
+            u64     cmd
+
+        sbc data
+            512Bytes sbc_pub_key
+            512Bytes sbc_rsa_n
+            u32     sbc_auth_info
+            u32     sbc_key_hash 
+            u32     sbc_auth_algo_dsa
+            u32     sbc_auth_algo_hash
+            u32     sbc_rsa_pad_sel
+            u32     sbc_pub_key_len
+
+        auth data
+            64Bytes boothash
+            512Bytes signature
+
+        [img binary 1]
+        ...
+        [img binary 1]
+    '''
+
+    key_mode = 0
+
+    mi_header_struct = struct.Struct('<IIIIII')
+    sbc_struct = struct.Struct('<IIIIII')
+    ck_struct = struct.Struct('<II')
+
+
+    #run hash
+    hash = cryptoSB.HASH()
+
+    # ver_magic
+    #version = mi_desc['version']
+    #major, minor, revision = version.split('.')
+    #ver_magic_l, ver_magic_h = my_gen_ver_magic(major, minor, revision)
+
+    ver_magic_l, ver_magic_h = my_gen_mtk_magic(to_int(mi_desc['magic_num']))
+
+    # bl region
+    bl_version = mi_desc['bl_version']
+    images = mi_desc['images']
+    img_num = len(images)
+    ls_cmds = mi_desc['load_srcipt_cmd']
+    ls_cmds_num = len(ls_cmds)
+
+    '''size of bl + imgl + sbc + auth'''
+    # Fixed size section: mi_header, sbc data, auth data
+    # Non-fixed size section: img_info, load script
+    bl_total_len = g_mi_header_info_size + g_sbc_size + g_auth_size
+    #bl_total_len = g_mi_header_info_size + g_sbc_size + g_auth_size + g_ck_size
+    bl_total_len += img_num * g_img_header_size
+    bl_total_len += ls_cmds_num * g_ls_cmd_size
+
+
+    #print("==== multi-image header size ====")
+    #print("g_mi_header_info_size=%d, g_sbc_size=%d, g_auth_size=%d, bl_total_len = %d" %(g_mi_header_info_size, g_sbc_size, g_auth_size, bl_total_len))
+
+    #img_header_table = []
+    img_headers = []
+    bins = []
+    sbc_region = []
+    hashtable = []
+    imagetmp = [[], [], [], [], [], [], [], [], [], []]
+    packtmp = []
+    blh_hash = []
+    signaturelist = []
+    ck_region = []
+    cksignaturelist = []
+
+    imagesize = []
+
+
+    sbc_auth_alg = mi_desc['sbc_auth_alg']
+    sbc_rsa_pad_sel = mi_desc['sbc_rsa_pad_sel']
+    ''' parer rsa test pattern '''
+    rsa_parse = rsa_data_parser(mi_desc, sbc_rsa_pad_sel)
+
+    # sbc region
+    ##pub_key = to_int(mi_desc['public_key'])
+    ##public_key = my_binify(pub_key)
+    '''20180627 remove int assign avoid key start is (0x00..)'''
+    pub_key = (mi_desc['public_key'])
+    public_key = cryptoSB.strip_key(pub_key)
+    pubk = b'\x04' + public_key
+
+    rsa_e = cryptoSB.my_a2b_hex(rsa_parse[0]['e'])
+    rsa_n = cryptoSB.my_a2b_hex(rsa_parse[0]['n']) 
+    if((sbc_auth_alg == 0) or (sbc_auth_alg == 1) or (sbc_auth_alg == 2)):
+        public_key_append = my_pad_to_align(public_key, __Align_pub_key_apend)
+        sbc_region.append(public_key_append)
+
+        #sbc_rsa_n = (mi_desc['sbc_rsa_n'])
+        #rsa_modulus = cryptoSB.strip_key(sbc_rsa_n)
+        rsa_modulus_append = my_pad_to_align(rsa_n, __Align_pub_key_apend)
+        sbc_region.append(rsa_modulus_append)
+    elif((sbc_auth_alg == 3) or (sbc_auth_alg == 4) or (sbc_auth_alg == 5)):
+        rsa_e_append = my_pad_to_align(rsa_e, __Align_pub_key_apend)
+        sbc_region.append(rsa_e_append)
+        rsa_n_append = my_pad_to_align(rsa_n, __Align_pub_key_apend)
+        sbc_region.append(rsa_n_append)
+
+
+    #print("======= sbc region =========")
+    sbc_auth_inf = mi_desc['sbc_auth_inf']
+    #print("sbc_auth_inf = 0x%x" %sbc_auth_inf)
+    sbc_key_hash = mi_desc['sbc_key_hash']
+    sbc_auth_alg = mi_desc['sbc_auth_alg']
+    #print("sbc_auth_alg = 0x%x" %sbc_auth_alg)
+    sbc_auth_alg_hash = mi_desc['sbc_auth_alg_hash']
+    sbc_rsa_pad_sel = mi_desc['sbc_rsa_pad_sel']
+    sbc_pub_key_len = mi_desc['sbc_pub_key_len']
+    sbc = sbc_struct.pack(sbc_auth_inf, sbc_key_hash, sbc_auth_alg, sbc_auth_alg_hash, sbc_rsa_pad_sel, sbc_pub_key_len)
+    sbc_region.append(sbc)
+
+	#print("======= ck region =========")
+    ck_mg = (mi_desc['ck_magic_num'])
+    ck_magic = cryptoSB.strip_key(ck_mg)
+    ck_region.append(ck_magic)
+    ck_sid = (mi_desc['ck_soc_id'])
+    ck_soc_id = cryptoSB.strip_key(ck_sid)
+    ck_region.append(ck_soc_id)
+    ck_pkey = (mi_desc['ck_pub_key'])
+
+    ck_pub_key = cryptoSB.strip_key(ck_pkey)
+    ck_pub_key_append = my_pad_to_align(ck_pub_key, __Align_pub_key_apend)
+    ck_region.append(ck_pub_key_append)
+    ck_pubk = b'\x04' + ck_pub_key
+
+    ck_rsa_n = (mi_desc['ck_rsa_n'])
+    ck_rsa_modulus = cryptoSB.strip_key(ck_rsa_n)
+    ck_rsa_modulus_append = my_pad_to_align(ck_rsa_modulus, __Align_pub_key_apend)
+    ck_region.append(ck_rsa_modulus_append)
+
+    ck_auth_algo = mi_desc['ck_auth_algo']
+    ck_pub_key_len = mi_desc['ck_pub_key_len']
+    ck = ck_struct.pack(ck_auth_algo, ck_pub_key_len)
+    ck_region.append(ck)
+
+    ck_en = mi_desc['change_key_en']
+    sign_ck_priv_key = mi_desc['sign_ck_priv_key']
+    signckprivk = cryptoSB.strip_key(sign_ck_priv_key)
+
+    # pad before 1st image
+
+    #pad = pad_to_align(64, expected_size)
+    #bins.append(pad)
+    #expected_size += len(pad)
+
+
+    # images
+    # 512 bytes aligned (20200310)
+    img_offset = (bl_total_len + 511) & (~511)
+    img_offset_table = []
+
+    for i in range(img_num):
+        #print("\n\n======== Start image step: =========")
+
+
+        img = images[i]
+        img_file = img['img_file']
+        img_enc_inf = img['img_enc_inf']
+        img_enc_alg = img['img_enc_alg']
+        #print("img_enc_inf=0x%x" %img_enc_inf)
+
+        bin = open(img_dir + '/' + img_file, 'rb').read()
+
+        iv_int = img['img_iv']
+        #iv = my_binify(iv_int)
+        '''0627 remove int assign avoid key start is (0x00..)'''
+        iv = cryptoSB.strip_key(iv_int)
+        iv0= int(iv[0:4].encode('hex'), 16)
+        iv1= int(iv[4:8].encode('hex'), 16)
+        iv2= int(iv[8:12].encode('hex'), 16)
+        iv3= int(iv[12:16].encode('hex'), 16)
+        #print type(iv)
+        #print("IV:")
+        #cryptoSB.dump(iv)
+
+
+        if(img_enc_inf == 1):
+            '''image encrypt'''
+            out = cryptoSB.my_img_enc(bin, enckey, iv, img_enc_alg, __Align)
+            bins.append(out)
+            #bins = out
+        elif(img_enc_inf == 2):
+            '''image encrypt'''
+            out = cryptoSB.my_img_enc(bin, enckey, iv, img_enc_alg, __Align)
+            bins.append(out)
+            #bins = out
+        else:
+            '''plaintext image'''
+            out = my_pad_to_align(bin, __Align)
+            bins.append(out)
+            #bins = out
+
+        # binary length should be 16 bytes aligned
+        length = len(out)
+
+        imagesize.append(length)
+        #print("")
+        #print("image[%d] offset : 0x%x" %(i, img_offset))
+        #print("image[%d] size   : 0x%x" %(i, imagesize[i]))
+
+        imagetmp[i] = copy.copy(bins)
+
+        img_str = ''.join(bins)
+        #print type(img_str)
+        #print("========= image[%d] binary ==========" %i)
+        #cryptoSB.dump(img_str)
+
+
+        # hash each (image header + image binary)
+        #print('')
+        #print("========= image[%d] binary hash ==========" %i)
+        hashvalue = cryptoSB.sb_hash(img_str, sbc_auth_alg_hash)
+        imghash = my_pad_to_align(hashvalue, g_boot_hash_size)
+        #cryptoSB.dump(imghash)
+
+        # img_header
+        img_hdr = struct.pack('<II',
+                                length,
+                                img_offset)
+        img_hdr += "".join(imghash)
+        '''20180719 fix IV order fail'''
+        img_hdr += struct.pack('<II16s',
+                                img_enc_inf,
+                                img_enc_alg,
+                                iv)
+
+
+        img_offset_table.append(str(img_offset))
+        length = (length + 511) & (~511)
+        img_offset += length
+        #img_offset_table.append(my_to_bytes(img_offset, 4, endianess='little'))
+
+        #print("\n=====>")
+        #print("image[%d] header info :" %i)
+        #cryptoSB.dump(img_hdr)
+        img_headers.append(img_hdr)
+
+        #img_headers.remove(img_hdr)
+        while len(bins) > 0:
+            bins.pop()
+
+
+    #print("\n\nSTART to pack all sections ...")
+    pack = []
+
+    #print("======== append mi_header info ==========")
+    total_len = int(img_offset_table[img_num-1]) + int(imagesize[img_num-1])
+
+    mi_header = mi_header_struct.pack(
+                        ver_magic_l,
+                        ver_magic_h,
+                        total_len,
+                        bl_version,
+                        img_num,
+                        ls_cmds_num)
+
+
+    pack.append(mi_header)
+
+    # append image info
+    for i in range(img_num):
+        pack += img_headers[i]
+
+
+    ls_script = pack_load_script(mi_desc)
+    if ls_script == None:
+        print("pack_load_script fail")
+        return
+
+    # append load script
+    pack += ls_script
+
+    # append sbc data
+    pack += sbc_region
+
+    # for easy view. please remove it while release final
+    '''align for (bl + imgl + sbc)'''
+    if(__Align_16_en == 1):
+        padnum = pad_to_align(16, len(''.join(pack)))
+        pack.append(padnum)
+
+    #print("======== append mi_header hash: ==========")
+    bl_header = ''.join(pack)
+    #cryptoSB.dump(bl_header)
+    blh = copy.copy(bl_header)
+    boothash = cryptoSB.sb_hash(blh, sbc_auth_alg_hash)
+    boothash_append = my_pad_to_align(boothash, g_boot_hash_size)
+    #cryptoSB.dump(boothash_append)
+    blh_hash.append(boothash_append)
+
+    # append hash
+    pack += blh_hash
+
+    #print("======== append mi_header signature: =========")
+    #privk = "\xc1\xbe\xe4\xfa\x86\xaf\x86\x84\x67\x7c\xae\xee\xa8\x8a\xb0\x72\x3e\x55\x4a\xef\x01\x60\xb8\xfc\x65\x3c\x0e\x00\x08\x0f\x4f\x78"
+    #pubk = "\x04\x14\xc1\xcf\x10\x99\x9d\x3a\x98\xf3\x71\xb8\xd8\x9b\x3b\x26\xb2\x9e\xe1\xbd\x99\xf3\xe0\x39\x3d\x34\x21\x6a\x6f\x49\x58\x7a\xb1\xdd\x8a\xba\x7a\x9d\x02\x99\x5f\xda\xa0\xb8\x62\x82\xae\xc2\xd0\xc6\x88\xc2\x26\x03\x97\x86\x65\x46\xbb\x20\xc9\xd1\x44\xb9\x84"
+
+    if sbc_auth_inf == 0:
+        padbytes = '\0' * g_signature_size
+        signaturelist.append(padbytes)
+    else:
+        pem_key_format = mi_desc['pem_key_format']
+        if(pem_key_format == 1):
+            '''PEM format'''
+            key_mode = 1
+        elif(pem_key_format == 2):
+            '''DER format'''
+            key_mode = 2
+        else:
+            '''String format'''
+            key_mode = 0
+
+        #ecdsa = cryptoSB.ECDSA()
+        #signature = ecdsa.sign(privk, pubk, boothash, sbc_auth_alg)
+        ''' 20180616 fix the sb_sign msg error : not boothash -> blh is right'''
+        if(ck_en == 0):
+            print("original format: no change key:")
+            signature = cryptoSB.sb_sign(privk, pubk, blh, sbc_auth_alg, sbc_auth_alg_hash, rsa_parse, sbc_rsa_pad_sel, key_mode)
+        else:
+            signature = cryptoSB.sb_sign(signckprivk, pubk, blh, sbc_auth_alg, sbc_auth_alg_hash, ck_rsa_parse, sbc_rsa_pad_sel, key_mode)
+
+        #print("signature size = %d" %len(signature))
+        signature_append = my_pad_to_align(signature, g_signature_size)
+        #cryptoSB.dump(signature_append)
+        signaturelist.append(signature_append)
+
+        #check verify
+        #ret = ecdsa.verify(pubk, boothash, signature, sbc_auth_alg)
+        #print("ecdsa verify: %s" %ret)
+
+
+    #dump("".join(signaturelist))
+    # append signature
+    pack += signaturelist
+
+    # for easy view, please remove it while release final
+    '''align for (bl + imgl + sbc + auth + ck)'''
+    if(__Align_16_en == 1):
+        padnum = pad_to_align(16, len(''.join(pack)))
+        pack.append(padnum)
+
+    # append ck field
+    ''' Skipped: no use on 2735
+    pack += ck_region
+
+    ck_field = ''.join(ck_region)
+    cryptoSB.dump(ck_field)
+    ckf = copy.copy(ck_field)
+
+    ck_signature = cryptoSB.sb_sign(privk, pubk, ckf, sbc_auth_alg, sbc_auth_alg_hash, rsa_parse, sbc_rsa_pad_sel, key_mode)
+    #print("ck_signature size = %d" %len(ck_signature))
+    ck_signature_append = my_pad_to_align(ck_signature, g_signature_size)
+    #cryptoSB.dump(ck_signature_append)
+    cksignaturelist.append(ck_signature_append)
+
+    pack += cksignaturelist
+    '''
+    # end append ck field
+
+    # append image binary
+    for i in range(img_num):
+        offset = int(img_offset_table[i])
+        pad_num = offset - len(''.join(pack))
+        #print("offset = %d" %offset)
+        #print("pad_num = %d" %pad_num)
+
+        padbytes = '\0' * pad_num
+        pack.append(padbytes)
+        pack += imagetmp[i]
+
+    #print(len(''.join(pack)))
+
+    # clear list
+    while len(signaturelist) > 0:
+        signaturelist.pop()
+
+
+    return ''.join(pack)
+
+'''support to_bytes to python 2.7'''
+def my_to_bytes(n, length, endianess='big'):
+    h = '%x' % n
+    s = ('0'*(len(h) % 2) + h).zfill(length*2).decode('hex')
+    return s if endianess == 'big' else s[::-1]
+
+'''long to byte string'''
+def my_binify(x):
+    h = hex(x)[2:].rstrip('L')
+    return binascii.unhexlify(h)
+
+def my_binify2(x):
+    if(hex(x)[0:2] == '0x'):
+        h = hex(x)[2:].rstrip('L')
+    else:
+        h = hex(x).rstrip('L')
+
+    return binascii.unhexlify(h)
+
+def my_pad_to_align(x, align):
+    if(align == 0):
+        return x
+    else:
+        size = len(x)
+        #print("size 0x%x" %size)
+        pad_size = (align - size % align) % align
+        for i in range(0, pad_size, 1):
+            x += b'\x00'
+
+        #cryptoSB.dump(x)
+        return x
+
+
+def img_enc(mi_desc, key, align=None):
+
+    images = mi_desc['images']
+    img_num = len(images)
+
+    img = images[0]
+    #load_addr = img['load_addr']
+    #entrypoint = img['entrypoint']
+    img_file = img['img_file']
+    img_enc_alg = img['img_enc_alg']
+    #print(img_enc_alg)
+    img_enc_inf = img['img_enc_inf']
+    #print(img_enc_inf)
+    iv = img['img_iv']
+    iv = my_binify(iv)
+    #print type(iv)
+    #print("img_enc dump:")
+    #cryptoSB.dump(iv)
+
+    bin = open(img_file, 'rb').read()
+    #cryptoSB.dump(bin)
+
+    #align
+    bin = my_pad_to_align(bin, 64)
+
+    aes = cryptoSB.AESCipher(key, 16)
+    encmsg = aes.aes_encrypt(bin)
+    #print("result image enc:")
+    #cryptoSB.dump(encmsg)
+
+    return encmsg
+
+
+
+
+if __name__ == '__main__':
+    import os, sys, getopt
+
+
+    def print_usage():
+        print ('usage:', os.path.basename(sys.argv[0]), "[options] <image config>.json\n", \
+            'options:\n', \
+            '\t[-o | --output out.img]\n', \
+            '\t[-h | --help]\n', \
+            '\t[-i | --input]\n', \
+            '\t[-k | --prikey hexkey e.g. 0x0102..]\n', \
+            '\t[-s | --enckey hexkey e.g. 0x0102..]\n', \
+            '\t[-p | --pemdir <pem path>\n', \
+            '\t[-d | --imgdir <image path>\n')
+
+
+    def main():
+        opts, args = getopt.getopt(sys.argv[1:],
+                                   'ho:a:i:k:s:p:d:',
+                                   ['help', 'output=', 'align=', 'input=',
+                                    'prikey=', 'enckey=', 'pemdir=', 'imgdir=']
+                                  )
+
+        out_name = None
+        align = 0
+        infile_name = None
+        aeskey = None
+        pubkey = None
+        privk = None
+        pem_dir = "binfile"
+        img_dir = ''
+
+        for o, a in opts:
+            if o in ('-h', '--help'):
+                print_usage()
+                sys.exit()
+            elif o in ('-o', '--output'):
+                out_name = a
+            elif o in ('-a', '--align'):
+                align = int(a)
+            elif o in ('-i', '--input'):
+                ## doesn't need currently
+                infile_name = a
+            elif o in ('-k', '--prikey'):
+                privkey = a
+            elif o in ('-s', '--enckey'):
+                aeskey = a
+            elif o in ('-p', '--pemdir'):
+                pem_dir = a
+            elif o in ('-d', '--imgdir'):
+                img_dir = a
+            else:
+                print_usage()
+                sys.exit(1)
+
+        if len(args) >= 1:
+            mi_desc_fn = args[0]
+        else:
+            print_usage()
+            sys.exit(1)
+
+        if not out_name:
+            fn, ext = os.path.splitext(mi_desc_fn)
+            out_name = fn + '.img'
+
+        """ read json script """
+        mi_desc = read_desc(mi_desc_fn)
+
+        #mipack = pack_images(mi_desc, align)
+
+        #print 'output: %s (%d bytes)' % (out_name, len(mipack))
+        #open(out_name, 'wb').write(mipack)
+
+        cmd_line_key = mi_desc['cmd_line_key']
+        sign_priv_key = mi_desc['sign_priv_key']
+        aes_enc_sym_key = mi_desc['aes_enc_sym_key']
+
+        """ Where is the key input from """
+
+        pem_key_format = mi_desc['pem_key_format']
+        if(pem_key_format == 1):
+            sbc_auth_alg = mi_desc['sbc_auth_alg']
+            if(sbc_auth_alg == 0):
+                key_path = pem_dir + "/ecdsa_p256_private.pem"
+                #print(key_path)
+                privk = open(key_path,"rb").read()
+                #privk = open("binfile/ecdsa_p256_private.pem","rb").read()
+                #pubk_pem = open("binfile/ecdsa_p256_public.pem","rb").read()
+            elif(sbc_auth_alg == 1):
+                privk = open("binfile/ecdsa_p384_private.pem","rb").read()
+
+            key = cryptoSB.strip_key(aes_enc_sym_key)
+
+        else:
+            if(cmd_line_key == 1):
+                key = cryptoSB.strip_key(aeskey)
+                privk = cryptoSB.strip_key(privkey)
+                #print("dump privkey:")
+                #cryptoSB.dump(privk)
+            elif(cmd_line_key == 0):
+                key = cryptoSB.strip_key(aes_enc_sym_key)
+                privk = cryptoSB.strip_key(sign_priv_key)
+                #print("dump privkey:")
+                #cryptoSB.dump(privk)
+            else:
+                prnit("ERROR: please check cmd_line_key json")
+
+
+
+        #run hash
+        #hash = cryptoSB.HASH()
+        #imghash = hash.hash_sha256(enc_data)
+        #cryptoSB.dump(imghash)
+
+        mipack = my_pack_images(mi_desc, privk, key, align, img_dir)
+        if mipack == None:
+            print("my_pack_images fail")
+            return
+
+        #out_name = 'my_' + fn + '.img'
+        #out_name = fn + '.img'
+        print('output: %s (%d bytes)' % (out_name, len(mipack)))
+        open(out_name, 'wb').write(mipack)
+
+    main()
+
diff --git a/src/devtools/packer/mt2735/test_pkcs1_15_mytest.py b/src/devtools/packer/mt2735/test_pkcs1_15_mytest.py
new file mode 100644
index 0000000..d15b1e0
--- /dev/null
+++ b/src/devtools/packer/mt2735/test_pkcs1_15_mytest.py
@@ -0,0 +1,620 @@
+# -*- coding: utf-8 -*-
+#
+#  SelfTest/Signature/test_pkcs1_15.py: Self-test for PKCS#1 v1.5 signatures
+#
+# ===================================================================
+# The contents of this file are dedicated to the public domain.  To
+# the extent that dedication to the public domain is not available,
+# everyone is granted a worldwide, perpetual, royalty-free,
+# non-exclusive license to exercise all rights associated with the
+# contents of this file for any purpose whatsoever.
+# No rights are reserved.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+# ===================================================================
+
+__revision__ = "$Id$"
+
+import unittest
+
+from Crypto.PublicKey import RSA
+from Crypto.SelfTest.st_common import list_test_cases, a2b_hex, b2a_hex
+from Crypto.Hash import *
+from Crypto import Random
+from Crypto.Signature import PKCS1_v1_5 as PKCS
+from Crypto.Util.py3compat import *
+
+def isStr(s):
+        t = ''
+        try:
+                t += s
+        except TypeError:
+                return 0
+        return 1
+
+def rws(t):
+    """Remove white spaces, tabs, and new lines from a string"""
+    for c in ['\n', '\t', ' ']:
+        t = t.replace(c,'')
+    return t
+
+def t2b(t):
+    """Convert a text string with bytes in hex form to a byte string"""
+    clean = b(rws(t))
+    if len(clean)%2 == 1:
+        raise ValueError("Even number of characters expected")
+    return a2b_hex(clean)
+
+# dump str to bytes
+def rsa_dump(data):
+	print("-----------pkcs1v15_mytest dump:----------")
+	for i in range(0, len(data)):
+		print ("0x%02x,"%ord(data[i])),
+		if(((i+1)%16) == 0):
+			print("")
+	print("-----------pkcs1v15_mytest dump end----------")
+
+
+class PKCS1_15_Tests(unittest.TestCase):
+
+        # List of tuples with test data for PKCS#1 v1.5.
+        # Each tuple is made up by:
+        #       Item #0: dictionary with RSA key component, or key to import
+        #       Item #1: data to hash and sign
+        #       Item #2: signature of the data #1, done with the key #0, after
+        #                hashing it with #3
+        #       Item #3: hash object generator
+
+        _testData = (
+
+                #
+                # Taken from ftp://ftp.rsa.com/pub/pkcs/ascii/examples.asc
+                # "Some Examples of the PKCS Standards", 1999
+                #
+                (
+
+                # Private key, from 2.1
+                {
+                'n':'''0a 66 79 1d c6 98 81 68 de 7a b7 74 19 bb 7f b0 c0 01 c6
+                    27 10 27 00 75 14 29 42 e1 9a 8d 8c 51 d0 53 b3 e3 78 2a 1d
+                    e5 dc 5a f4 eb e9 94 68 17 01 14 a1 df e6 7c dc 9a 9a f5 5d
+                    65 56 20 bb ab''',
+                'e':'''01 00
+                    01''',
+                'd':'''01 23 c5 b6 1b a3 6e db 1d 36 79 90 41 99 a8 9e a8 0c 09
+                    b9 12 2e 14 00 c0 9a dc f7 78 46 76 d0 1d 23 35 6a 7d 44 d6
+                    bd 8b d5 0e 94 bf c7 23 fa 87 d8 86 2b 75 17 76 91 c1 1d 75
+                    76 92 df 88 81'''
+                },
+                # Data to sign, from 3.1
+                '''30 81 a4 02 01 00 30 42 31 0b 30 09 06
+                03 55 04 06 13 02 55 53 31 1d 30 1b 06 03 55 04 0a 13 14
+                45 78 61 6d 70 6c 65 20 4f 72 67 61 6e 69 7a 61 74 69 6f
+                6e 31 14 30 12 06 03 55 04 03 13 0b 54 65 73 74 20 55 73
+                65 72 20 31 30 5b 30 0d 06 09 2a 86 48 86 f7 0d 01 01 01
+                05 00 03 4a 00 30 47 02 40
+                0a 66 79 1d c6 98 81 68 de 7a b7 74 19 bb 7f b0
+                c0 01 c6 27 10 27 00 75 14 29 42 e1 9a 8d 8c 51
+                d0 53 b3 e3 78 2a 1d e5 dc 5a f4 eb e9 94 68 17
+                01 14 a1 df e6 7c dc 9a 9a f5 5d 65 56 20 bb ab
+                02 03 01 00 01''',
+                # Signature, from 3.2 (at the very end)
+                '''06 db 36 cb 18 d3 47 5b 9c 01 db 3c 78 95 28 08
+                02 79 bb ae ff 2b 7d 55 8e d6 61 59 87 c8 51 86
+                3f 8a 6c 2c ff bc 89 c3 f7 5a 18 d9 6b 12 7c 71
+                7d 54 d0 d8 04 8d a8 a0 54 46 26 d1 7a 2a 8f be''',
+                MD2
+                ),
+
+                #
+                # RSA keypair generated with openssl
+                #
+                (
+                """-----BEGIN RSA PRIVATE KEY-----
+                MIIBOwIBAAJBAL8eJ5AKoIsjURpcEoGubZMxLD7+kT+TLr7UkvEtFrRhDDKMtuII
+                q19FrL4pUIMymPMSLBn3hJLe30Dw48GQM4UCAwEAAQJACUSDEp8RTe32ftq8IwG8
+                Wojl5mAd1wFiIOrZ/Uv8b963WJOJiuQcVN29vxU5+My9GPZ7RA3hrDBEAoHUDPrI
+                OQIhAPIPLz4dphiD9imAkivY31Rc5AfHJiQRA7XixTcjEkojAiEAyh/pJHks/Mlr
+                +rdPNEpotBjfV4M4BkgGAA/ipcmaAjcCIQCHvhwwKVBLzzTscT2HeUdEeBMoiXXK
+                JACAr3sJQJGxIQIgarRp+m1WSKV1MciwMaTOnbU7wxFs9DP1pva76lYBzgUCIQC9
+                n0CnZCJ6IZYqSt0H5N7+Q+2Ro64nuwV/OSQfM6sBwQ==
+                -----END RSA PRIVATE KEY-----""",
+                "This is a test\x0a",
+                #
+                # PKCS#1 signature computed with openssl
+                #
+                '''4a700a16432a291a3194646952687d5316458b8b86fb0a25aa30e0dcecdb
+                442676759ac63d56ec1499c3ae4c0013c2053cabd5b5804848994541ac16
+                fa243a4d''',
+                SHA
+                ),
+
+                #
+                # Test vector from http://www.di-mgt.com.au/rsa_alg.html#signpkcs1
+                #
+                (
+                {
+                    'n':'''E08973398DD8F5F5E88776397F4EB005BB5383DE0FB7ABDC7DC775290D052E6D
+                    12DFA68626D4D26FAA5829FC97ECFA82510F3080BEB1509E4644F12CBBD832CF
+                    C6686F07D9B060ACBEEE34096A13F5F7050593DF5EBA3556D961FF197FC981E6
+                    F86CEA874070EFAC6D2C749F2DFA553AB9997702A648528C4EF357385774575F''',
+                    'e':'''010001''',
+                    'd':'''00A403C327477634346CA686B57949014B2E8AD2C862B2C7D748096A8B91F736
+                    F275D6E8CD15906027314735644D95CD6763CEB49F56AC2F376E1CEE0EBF282D
+                    F439906F34D86E085BD5656AD841F313D72D395EFE33CBFF29E4030B3D05A28F
+                    B7F18EA27637B07957D32F2BDE8706227D04665EC91BAF8B1AC3EC9144AB7F21'''
+                },
+                "abc",
+                '''60AD5A78FB4A4030EC542C8974CD15F55384E836554CEDD9A322D5F4135C6267
+                A9D20970C54E6651070B0144D43844C899320DD8FA7819F7EBC6A7715287332E
+                C8675C136183B3F8A1F81EF969418267130A756FDBB2C71D9A667446E34E0EAD
+                9CF31BFB66F816F319D0B7E430A5F2891553986E003720261C7E9022C0D9F11F''',
+                SHA
+                )
+
+        )
+
+        def testSign1(self):
+                print("ttest")
+                for i in range(len(self._testData)):
+                        row = self._testData[i]
+                        # Build the key
+                        if isStr(row[0]):
+                                key = RSA.importKey(row[0])
+                        else:
+                                comps = [ long(rws(row[0][x]),16) for x in ('n','e','d') ]
+                                key = RSA.construct(comps)
+                        h = row[3].new()
+                        # Data to sign can either be in hex form or not
+                        try:
+                            h.update(t2b(row[1]))
+                        except:
+                            h.update(b(row[1]))
+                        # The real test
+                        signer = PKCS.new(key)
+                        self.failUnless(signer.can_sign())
+                        s = signer.sign(h)
+                        self.assertEqual(s, t2b(row[2]))
+
+        def testVerify1(self):
+                for i in range(len(self._testData)):
+                        row = self._testData[i]
+                        # Build the key
+                        if isStr(row[0]):
+                                key = RSA.importKey(row[0]).publickey()
+                        else:
+                                comps = [ long(rws(row[0][x]),16) for x in ('n','e') ]
+                                key = RSA.construct(comps)
+                        h = row[3].new()
+                        # Data to sign can either be in hex form or not
+                        try:
+                            h.update(t2b(row[1]))
+                        except:
+                            h.update(b(row[1]))
+                        # The real test
+                        verifier = PKCS.new(key)
+                        self.failIf(verifier.can_sign())
+                        result = verifier.verify(h, t2b(row[2]))
+                        self.failUnless(result)
+
+        def testSignVerify(self):
+                        rng = Random.new().read
+                        key = RSA.generate(1024, rng)
+
+                        for hashmod in (MD2,MD5,SHA,SHA224,SHA256,SHA384,SHA512,RIPEMD):
+                            h = hashmod.new()
+                            h.update(b('blah blah blah'))
+
+                            signer = PKCS.new(key)
+                            s = signer.sign(h)
+                            result = signer.verify(h, s)
+                            self.failUnless(result)
+
+
+
+class testSign1_mytest():
+        # List of tuples with test data for PKCS#1 v1.5.
+        # Each tuple is made up by:
+        #       Item #0: dictionary with RSA key component, or key to import
+        #       Item #1: data to hash and sign
+        #       Item #2: signature of the data #1, done with the key #0, after
+        #                hashing it with #3
+        #       Item #3: hash object generator
+
+        def __init__(self):
+                _testData = (
+
+                #
+                # Taken from ftp://ftp.rsa.com/pub/pkcs/ascii/examples.asc
+                # "Some Examples of the PKCS Standards", 1999
+                #
+                (
+
+                # Private key, from 2.1
+                {
+                'n':'''0a 66 79 1d c6 98 81 68 de 7a b7 74 19 bb 7f b0 c0 01 c6
+                    27 10 27 00 75 14 29 42 e1 9a 8d 8c 51 d0 53 b3 e3 78 2a 1d
+                    e5 dc 5a f4 eb e9 94 68 17 01 14 a1 df e6 7c dc 9a 9a f5 5d
+                    65 56 20 bb ab''',
+                'e':'''01 00
+                    01''',
+                'd':'''01 23 c5 b6 1b a3 6e db 1d 36 79 90 41 99 a8 9e a8 0c 09
+                    b9 12 2e 14 00 c0 9a dc f7 78 46 76 d0 1d 23 35 6a 7d 44 d6
+                    bd 8b d5 0e 94 bf c7 23 fa 87 d8 86 2b 75 17 76 91 c1 1d 75
+                    76 92 df 88 81'''
+                },
+                # Data to sign, from 3.1
+                '''30 81 a4 02 01 00 30 42 31 0b 30 09 06
+                03 55 04 06 13 02 55 53 31 1d 30 1b 06 03 55 04 0a 13 14
+                45 78 61 6d 70 6c 65 20 4f 72 67 61 6e 69 7a 61 74 69 6f
+                6e 31 14 30 12 06 03 55 04 03 13 0b 54 65 73 74 20 55 73
+                65 72 20 31 30 5b 30 0d 06 09 2a 86 48 86 f7 0d 01 01 01
+                05 00 03 4a 00 30 47 02 40
+                0a 66 79 1d c6 98 81 68 de 7a b7 74 19 bb 7f b0
+                c0 01 c6 27 10 27 00 75 14 29 42 e1 9a 8d 8c 51
+                d0 53 b3 e3 78 2a 1d e5 dc 5a f4 eb e9 94 68 17
+                01 14 a1 df e6 7c dc 9a 9a f5 5d 65 56 20 bb ab
+                02 03 01 00 01''',
+                # Signature, from 3.2 (at the very end)
+                '''06 db 36 cb 18 d3 47 5b 9c 01 db 3c 78 95 28 08
+                02 79 bb ae ff 2b 7d 55 8e d6 61 59 87 c8 51 86
+                3f 8a 6c 2c ff bc 89 c3 f7 5a 18 d9 6b 12 7c 71
+                7d 54 d0 d8 04 8d a8 a0 54 46 26 d1 7a 2a 8f be''',
+                MD2
+                ),
+
+                #
+                # RSA keypair generated with openssl
+                #
+                (
+                """-----BEGIN RSA PRIVATE KEY-----
+                MIIBOwIBAAJBAL8eJ5AKoIsjURpcEoGubZMxLD7+kT+TLr7UkvEtFrRhDDKMtuII
+                q19FrL4pUIMymPMSLBn3hJLe30Dw48GQM4UCAwEAAQJACUSDEp8RTe32ftq8IwG8
+                Wojl5mAd1wFiIOrZ/Uv8b963WJOJiuQcVN29vxU5+My9GPZ7RA3hrDBEAoHUDPrI
+                OQIhAPIPLz4dphiD9imAkivY31Rc5AfHJiQRA7XixTcjEkojAiEAyh/pJHks/Mlr
+                +rdPNEpotBjfV4M4BkgGAA/ipcmaAjcCIQCHvhwwKVBLzzTscT2HeUdEeBMoiXXK
+                JACAr3sJQJGxIQIgarRp+m1WSKV1MciwMaTOnbU7wxFs9DP1pva76lYBzgUCIQC9
+                n0CnZCJ6IZYqSt0H5N7+Q+2Ro64nuwV/OSQfM6sBwQ==
+                -----END RSA PRIVATE KEY-----""",
+                "This is a test\x0a",
+                #
+                # PKCS#1 signature computed with openssl
+                #
+                '''4a700a16432a291a3194646952687d5316458b8b86fb0a25aa30e0dcecdb
+                442676759ac63d56ec1499c3ae4c0013c2053cabd5b5804848994541ac16
+                fa243a4d''',
+                SHA
+                ),
+
+                #
+                # Test vector from http://www.di-mgt.com.au/rsa_alg.html#signpkcs1
+                #
+                (
+                {
+                    'n':'''E08973398DD8F5F5E88776397F4EB005BB5383DE0FB7ABDC7DC775290D052E6D
+                    12DFA68626D4D26FAA5829FC97ECFA82510F3080BEB1509E4644F12CBBD832CF
+                    C6686F07D9B060ACBEEE34096A13F5F7050593DF5EBA3556D961FF197FC981E6
+                    F86CEA874070EFAC6D2C749F2DFA553AB9997702A648528C4EF357385774575F''',
+                    'e':'''010001''',
+                    'd':'''00A403C327477634346CA686B57949014B2E8AD2C862B2C7D748096A8B91F736
+                    F275D6E8CD15906027314735644D95CD6763CEB49F56AC2F376E1CEE0EBF282D
+                    F439906F34D86E085BD5656AD841F313D72D395EFE33CBFF29E4030B3D05A28F
+                    B7F18EA27637B07957D32F2BDE8706227D04665EC91BAF8B1AC3EC9144AB7F21'''
+                },
+                "abc",
+                '''60AD5A78FB4A4030EC542C8974CD15F55384E836554CEDD9A322D5F4135C6267
+                A9D20970C54E6651070B0144D43844C899320DD8FA7819F7EBC6A7715287332E
+                C8675C136183B3F8A1F81EF969418267130A756FDBB2C71D9A667446E34E0EAD
+                9CF31BFB66F816F319D0B7E430A5F2891553986E003720261C7E9022C0D9F11F''',
+                SHA
+                ),
+
+                #RSA 2048 + SHA1 + py_rsakey_golden1.pem (generate by pycrypto_rsa_sign_pkca1v15_sha1)
+                (
+                {
+                    'n':'''b9bd2e2ed0f531dc00a0f44bf36caa1c2358d3c48645cb51bec95a3a38fb7f99
+                    fc646814da5f6b410ea9897fa0d8bfa8a1bd21065a66e105918175248a6b089c
+                    39dc9805f03ab4f9a3f43684c8f9b8cd7fbe2ab120eeda08200c370cb51fe725
+                    8f72130a962e551581157aec40bc99435d4cc50e74a878a428b0dc739cd518b9
+                    8089b162ca609ce84bce7cf303a0174d1520505775e57f685b63b8e16646486d
+                    131582b08be3b7b379a7e076791fe32537bc464e847dcb4aed95286a3b70af32
+                    0f30e9ac44c26ce8cf093e3f851a9d96ebd6876f93f330df94c4cf07b1dddab1
+                    60d287aebe0fa4cf050fe9fb5cb04f8f85c5d3454ef6bc17581a41793359c61b''',
+                    'e':'''010001''',
+                    'd':'''3a44ef4820a5cb8e8963f5401e8de8900b46c2a8d4b6f0a224886695fd6a690d
+                    95f0a9f660cbae8a2a5f659374928b0e0c2f993cb4ffb1785dbd8f52775a3cc9
+                    461dbe539e99cc4cc4f2c867bfe517e4e03e7886391eb5a7f6e5de1f4e7343c0
+                    b4cfc4c97d73eb9d6371a9ae946096745bc0c9d14f27cb3134d2c7563dcd5c83
+                    cc2c817446175812cb4040275e6b4c91d3bad61de64a3cc14973e11108170f18
+                    6ce1b124457e7bcc28768d69ae60bd4e2e275d72b6eedb157cdc1bbd2e802c86
+                    b478c0c76cd2fa2e4ed54f274a84f57a50716c0ff8df37a2ad409b1a3335332c
+                    d4225868dd4cfd4304b3eb4884630e1977fad20c7ce9ed9467632658296a0601'''
+                },
+                "abc",
+                '''7b80ab542c939b7f8b042b2ac653f2792d139caf83bd612a8a29685ac52fd8ff
+                   faa2ddb09547f4719ec60c8f9942c3356d36d087d869ffa84384246076017dcc
+                   abe38d4cf6f00155216a29aad02673f61dfbac98869be64e40ffd888975e6203
+                   ef5dca5f82f28deb02ff1406cc079173309becb97b00c867007aaf2be0e9d355
+                   64c03df71c50ec5132ac61ceade753c9ecce164ae0d4315f0fa308fbe900e75d
+                   51130992df550f7732fbc4c849e1f3c1a13927fe2c73f450cb33496ef1213567
+                   6cbfa1d0c39dd9137582807aae36c88e556a6255b7499f22bf4ef03371514ca6
+                   23d2ccf1d6b0896ac0572d175e92c9e47699f0962ef2c4a924a7f300aff847ac''',
+                SHA
+                )
+        )
+
+                self._testData = _testData
+
+        def testsign1_mytest(self):
+                print("mytest rsa sign")
+                for i in range(len(self._testData)):
+                        row = self._testData[i]
+                        # Build the key
+                        if isStr(row[0]):
+                                print("testData[%d]" %i)
+                                key = RSA.importKey(row[0])
+                        else:
+                                print("e testData[%d]" %i)
+                                comps = [ long(rws(row[0][x]),16) for x in ('n','e','d') ]
+                                key = RSA.construct(comps)
+                        h = row[3].new()
+                        # Data to sign can either be in hex form or not
+                        try:
+                            print("try")
+                            h.update(t2b(row[1]))
+                        except:
+                            print("except")
+                            h.update(b(row[1]))
+                        # The real test
+                        signer = PKCS.new(key)
+                        t_flag = signer.can_sign()
+                        if t_flag:
+                                print("mytest: can sign")
+                        else:
+                                print("mytest: can't sign")
+                        s = signer.sign(h)
+                        rsa_dump(s)
+                        if s == t2b(row[2]):
+                                print("signature compare pass")
+                        else:
+                                print("signature compare fail")
+
+        def testverify1_mytest(self):
+                print("mytest rsa verify")
+                for i in range(len(self._testData)):
+                        row = self._testData[i]
+                        # Build the key
+                        if isStr(row[0]):
+                                key = RSA.importKey(row[0]).publickey()
+                        else:
+                                comps = [ long(rws(row[0][x]),16) for x in ('n','e') ]
+                                key = RSA.construct(comps)
+                        h = row[3].new()
+                        # Data to sign can either be in hex form or not
+                        try:
+                            h.update(t2b(row[1]))
+                        except:
+                            h.update(b(row[1]))
+                        # The real test
+                        verifier = PKCS.new(key)
+                        t_flag = verifier.can_sign()
+                        if t_flag:
+                                print("mytest: can't verify")
+                        else:
+                                print("mytest: can verify")
+                        result = verifier.verify(h, t2b(row[2]))
+                        if result:
+                                print("verify pass")
+                        else:
+                                print("verify fail")
+
+
+class sbc_rsa_sign1_mytest():
+        # List of tuples with test data for PKCS#1 v1.5.
+        # Each tuple is made up by:
+        #       Item #0: dictionary with RSA key component, or key to import
+        #       Item #1: data to hash and sign
+        #       Item #2: signature of the data #1, done with the key #0, after
+        #                hashing it with #3
+        #       Item #3: hash object generator
+
+        def __init__(self, rsa_parse, msg):
+                _testData = (
+
+                #
+                # RSA keypair generated with openssl
+                #
+                
+
+                #
+                # Test vector from http://www.di-mgt.com.au/rsa_alg.html#signpkcs1
+                #
+                (
+                {
+                    'n':'''E08973398DD8F5F5E88776397F4EB005BB5383DE0FB7ABDC7DC775290D052E6D
+                    12DFA68626D4D26FAA5829FC97ECFA82510F3080BEB1509E4644F12CBBD832CF
+                    C6686F07D9B060ACBEEE34096A13F5F7050593DF5EBA3556D961FF197FC981E6
+                    F86CEA874070EFAC6D2C749F2DFA553AB9997702A648528C4EF357385774575F''',
+                    'e':'''010001''',
+                    'd':'''00A403C327477634346CA686B57949014B2E8AD2C862B2C7D748096A8B91F736
+                    F275D6E8CD15906027314735644D95CD6763CEB49F56AC2F376E1CEE0EBF282D
+                    F439906F34D86E085BD5656AD841F313D72D395EFE33CBFF29E4030B3D05A28F
+                    B7F18EA27637B07957D32F2BDE8706227D04665EC91BAF8B1AC3EC9144AB7F21'''
+                },
+                "abc",
+                '''60AD5A78FB4A4030EC542C8974CD15F55384E836554CEDD9A322D5F4135C6267
+                A9D20970C54E6651070B0144D43844C899320DD8FA7819F7EBC6A7715287332E
+                C8675C136183B3F8A1F81EF969418267130A756FDBB2C71D9A667446E34E0EAD
+                9CF31BFB66F816F319D0B7E430A5F2891553986E003720261C7E9022C0D9F11F''',
+                SHA
+                ),
+
+                #RSA 2048 + SHA1 + py_rsakey_golden1.pem (generate by pycrypto_rsa_sign_pkca1v15_sha1)
+                (
+                {
+                    'n':'''b9bd2e2ed0f531dc00a0f44bf36caa1c2358d3c48645cb51bec95a3a38fb7f99
+                    fc646814da5f6b410ea9897fa0d8bfa8a1bd21065a66e105918175248a6b089c
+                    39dc9805f03ab4f9a3f43684c8f9b8cd7fbe2ab120eeda08200c370cb51fe725
+                    8f72130a962e551581157aec40bc99435d4cc50e74a878a428b0dc739cd518b9
+                    8089b162ca609ce84bce7cf303a0174d1520505775e57f685b63b8e16646486d
+                    131582b08be3b7b379a7e076791fe32537bc464e847dcb4aed95286a3b70af32
+                    0f30e9ac44c26ce8cf093e3f851a9d96ebd6876f93f330df94c4cf07b1dddab1
+                    60d287aebe0fa4cf050fe9fb5cb04f8f85c5d3454ef6bc17581a41793359c61b''',
+                    'e':'''010001''',
+                    'd':'''3a44ef4820a5cb8e8963f5401e8de8900b46c2a8d4b6f0a224886695fd6a690d
+                    95f0a9f660cbae8a2a5f659374928b0e0c2f993cb4ffb1785dbd8f52775a3cc9
+                    461dbe539e99cc4cc4f2c867bfe517e4e03e7886391eb5a7f6e5de1f4e7343c0
+                    b4cfc4c97d73eb9d6371a9ae946096745bc0c9d14f27cb3134d2c7563dcd5c83
+                    cc2c817446175812cb4040275e6b4c91d3bad61de64a3cc14973e11108170f18
+                    6ce1b124457e7bcc28768d69ae60bd4e2e275d72b6eedb157cdc1bbd2e802c86
+                    b478c0c76cd2fa2e4ed54f274a84f57a50716c0ff8df37a2ad409b1a3335332c
+                    d4225868dd4cfd4304b3eb4884630e1977fad20c7ce9ed9467632658296a0601'''
+                },
+                "abc",
+                '''7b80ab542c939b7f8b042b2ac653f2792d139caf83bd612a8a29685ac52fd8ff
+                   faa2ddb09547f4719ec60c8f9942c3356d36d087d869ffa84384246076017dcc
+                   abe38d4cf6f00155216a29aad02673f61dfbac98869be64e40ffd888975e6203
+                   ef5dca5f82f28deb02ff1406cc079173309becb97b00c867007aaf2be0e9d355
+                   64c03df71c50ec5132ac61ceade753c9ecce164ae0d4315f0fa308fbe900e75d
+                   51130992df550f7732fbc4c849e1f3c1a13927fe2c73f450cb33496ef1213567
+                   6cbfa1d0c39dd9137582807aae36c88e556a6255b7499f22bf4ef03371514ca6
+                   23d2ccf1d6b0896ac0572d175e92c9e47699f0962ef2c4a924a7f300aff847ac''',
+                SHA
+                )
+        )
+
+                self._testData = _testData
+                self.rsa_parse = rsa_parse
+                self.msg = msg
+
+        def sbc_rsa_sign1(self):
+                print("mytest rsa sign")
+                for i in range(len(self._testData)):
+                        row = self._testData[i]
+                        # Build the key
+                        if isStr(row[0]):
+                                print("testData[%d]" %i)
+                                key = RSA.importKey(row[0])
+                        else:
+                                print("e testData[%d]" %i)
+                                comps = [ long(rws(row[0][x]),16) for x in ('n','e','d') ]
+                                key = RSA.construct(comps)
+                        h = row[3].new()
+                        # Data to sign can either be in hex form or not
+                        try:
+                            print("try")
+                            h.update(t2b(row[1]))
+                        except:
+                            print("except")
+                            h.update(b(row[1]))
+                        # The real test
+                        signer = PKCS.new(key)
+                        t_flag = signer.can_sign()
+                        if t_flag:
+                                print("mytest: can sign")
+                        else:
+                                print("mytest: can't sign")
+                        s = signer.sign(h)
+                        rsa_dump(s)
+                        if s == t2b(row[2]):
+                                print("signature compare pass")
+                        else:
+                                print("signature compare fail")
+                        return s
+
+        def sbc_rsa_sign1_json(self):
+                print("mytest rsa sign json")
+                row = self.rsa_parse
+                #print(self.rsa_parse)
+                # Build the key
+                if isStr(row[0]):
+                    #print("testData[%d]" %i)
+                    key = RSA.importKey(row[0])
+                else:
+                    #print("e testData[%d]" %i)
+                    comps = [ long(rws(row[0][x]),16) for x in ('n','e','d') ]
+                    key = RSA.construct(comps)
+
+                if(row[3] == "SHA"):
+                    h = SHA.new()
+                elif(row[3] == "SHA256"):
+                    h = SHA256.new()
+                elif(row[3] == "SHA384"):
+                    h = SHA384.new()
+                elif(row[3] == "SHA512"):
+                    h = SHA512.new()
+                else:
+                    h = SHA256.new()
+                # Data to sign can either be in hex form or not
+                if(row[4] == 1):
+                    try:
+                        print("try")
+                        h.update(t2b(row[1]))
+                    except:
+                        print("except")
+                        h.update(b(row[1]))
+                else:
+                    h.update(self.msg)
+                rsa_dump(h.digest())
+
+                # The real test
+                signer = PKCS.new(key)
+                t_flag = signer.can_sign()
+                if t_flag:
+                        print("mytest: can sign")
+                else:
+                        print("mytest: can't sign")
+                s = signer.sign(h)
+                rsa_dump(s)
+                if s == t2b(row[2]):
+                        print("signature compare pass")
+                else:
+                        print("signature compare fail")
+                return s
+
+        def sbc_rsa_verify1(self):
+                print("mytest rsa verify")
+                for i in range(len(self._testData)):
+                        row = self._testData[i]
+                        # Build the key
+                        if isStr(row[0]):
+                                key = RSA.importKey(row[0]).publickey()
+                        else:
+                                comps = [ long(rws(row[0][x]),16) for x in ('n','e') ]
+                                key = RSA.construct(comps)
+                        h = row[3].new()
+                        # Data to sign can either be in hex form or not
+                        try:
+                            h.update(t2b(row[1]))
+                        except:
+                            h.update(b(row[1]))
+                        # The real test
+                        verifier = PKCS.new(key)
+                        t_flag = verifier.can_sign()
+                        if t_flag:
+                                print("mytest: can't verify")
+                        else:
+                                print("mytest: can verify")
+                        result = verifier.verify(h, t2b(row[2]))
+                        if result:
+                                print("verify pass")
+                        else:
+                                print("verify fail")
+
+def get_tests(config={}):
+    tests = []
+    tests += list_test_cases(PKCS1_15_Tests)
+    return tests
+
+if __name__ == '__main__':
+    suite = lambda: unittest.TestSuite(get_tests())
+    unittest.main(defaultTest='suite')
+
+# vim:set ts=4 sw=4 sts=4 expandtab:
diff --git a/src/devtools/packer/mt2735/test_pkcs1_pss_mytest.py b/src/devtools/packer/mt2735/test_pkcs1_pss_mytest.py
new file mode 100644
index 0000000..555e1fb
--- /dev/null
+++ b/src/devtools/packer/mt2735/test_pkcs1_pss_mytest.py
@@ -0,0 +1,845 @@
+# -*- coding: utf-8 -*-
+#
+#  SelfTest/Signature/test_pkcs1_pss.py: Self-test for PKCS#1 PSS signatures
+#
+# ===================================================================
+# The contents of this file are dedicated to the public domain.  To
+# the extent that dedication to the public domain is not available,
+# everyone is granted a worldwide, perpetual, royalty-free,
+# non-exclusive license to exercise all rights associated with the
+# contents of this file for any purpose whatsoever.
+# No rights are reserved.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+# ===================================================================
+
+from __future__ import nested_scopes
+
+__revision__ = "$Id$"
+
+import unittest
+
+from Crypto.PublicKey import RSA
+from Crypto import Random
+from Crypto.SelfTest.st_common import list_test_cases, a2b_hex, b2a_hex
+from Crypto.Hash import *
+from Crypto.Signature import PKCS1_PSS as PKCS
+from Crypto.Util.py3compat import *
+
+def isStr(s):
+        t = ''
+        try:
+                t += s
+        except TypeError:
+                return 0
+        return 1
+
+def rws(t):
+    """Remove white spaces, tabs, and new lines from a string"""
+    for c in ['\t', '\n', ' ']:
+        t = t.replace(c,'')
+    return t
+
+def t2b(t):
+    """Convert a text string with bytes in hex form to a byte string"""
+    clean = b(rws(t))
+    if len(clean)%2 == 1:
+        raise ValueError("Even number of characters expected")
+    return a2b_hex(clean)
+
+# dump str to bytes
+def rsa_dump(data):
+	print("-----------pss_mytest dump:----------")
+	for i in range(0, len(data)):
+		print ("0x%02x,"%ord(data[i])),
+		if(((i+1)%16) == 0):
+			print("")
+	print("-----------pss_mytest dump end-----------")
+
+# Helper class to count how many bytes have been requested
+# from the key's private RNG, w/o counting those used for blinding
+class MyKey:
+    def __init__(self, key):
+        self._key = key
+        self.n = key.n
+        self.asked = 0
+    def _randfunc(self, N):
+        self.asked += N
+        return self._key._randfunc(N)
+    def sign(self, m):
+        return self._key.sign(m)
+    def has_private(self):
+        return self._key.has_private()
+    def decrypt(self, m):
+        return self._key.decrypt(m)
+    def verify(self, m, p):
+        return self._key.verify(m, p)
+    def encrypt(self, m, p):
+        return self._key.encrypt(m, p)
+
+class PKCS1_PSS_Tests(unittest.TestCase):
+
+        # List of tuples with test data for PKCS#1 PSS
+        # Each tuple is made up by:
+        #       Item #0: dictionary with RSA key component, or key to import
+        #       Item #1: data to hash and sign
+        #       Item #2: signature of the data #1, done with the key #0,
+        #                and salt #3 after hashing it with #4
+        #       Item #3: salt
+        #       Item #4: hash object generator
+
+        _testData = (
+
+                #
+                # From in pss-vect.txt to be found in
+                # ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1-vec.zip
+                #
+                (
+                # Private key
+                {
+                'n':'''a2 ba 40 ee 07 e3 b2 bd 2f 02 ce 22 7f 36 a1 95
+                02 44 86 e4 9c 19 cb 41 bb bd fb ba 98 b2 2b 0e
+                57 7c 2e ea ff a2 0d 88 3a 76 e6 5e 39 4c 69 d4
+                b3 c0 5a 1e 8f ad da 27 ed b2 a4 2b c0 00 fe 88
+                8b 9b 32 c2 2d 15 ad d0 cd 76 b3 e7 93 6e 19 95
+                5b 22 0d d1 7d 4e a9 04 b1 ec 10 2b 2e 4d e7 75
+                12 22 aa 99 15 10 24 c7 cb 41 cc 5e a2 1d 00 ee
+                b4 1f 7c 80 08 34 d2 c6 e0 6b ce 3b ce 7e a9 a5''',
+                'e':'''01 00 01''',
+                # In the test vector, only p and q were given...
+                # d is computed offline as e^{-1} mod (p-1)(q-1)
+                'd':'''50e2c3e38d886110288dfc68a9533e7e12e27d2aa56
+                d2cdb3fb6efa990bcff29e1d2987fb711962860e7391b1ce01
+                ebadb9e812d2fbdfaf25df4ae26110a6d7a26f0b810f54875e
+                17dd5c9fb6d641761245b81e79f8c88f0e55a6dcd5f133abd3
+                5f8f4ec80adf1bf86277a582894cb6ebcd2162f1c7534f1f49
+                47b129151b71'''
+                },
+
+                # Data to sign
+                '''85 9e ef 2f d7 8a ca 00 30 8b dc 47 11 93 bf 55
+                bf 9d 78 db 8f 8a 67 2b 48 46 34 f3 c9 c2 6e 64
+                78 ae 10 26 0f e0 dd 8c 08 2e 53 a5 29 3a f2 17
+                3c d5 0c 6d 5d 35 4f eb f7 8b 26 02 1c 25 c0 27
+                12 e7 8c d4 69 4c 9f 46 97 77 e4 51 e7 f8 e9 e0
+                4c d3 73 9c 6b bf ed ae 48 7f b5 56 44 e9 ca 74
+                ff 77 a5 3c b7 29 80 2f 6e d4 a5 ff a8 ba 15 98
+                90 fc''',
+                # Signature
+                '''8d aa 62 7d 3d e7 59 5d 63 05 6c 7e c6 59 e5 44
+                06 f1 06 10 12 8b aa e8 21 c8 b2 a0 f3 93 6d 54
+                dc 3b dc e4 66 89 f6 b7 95 1b b1 8e 84 05 42 76
+                97 18 d5 71 5d 21 0d 85 ef bb 59 61 92 03 2c 42
+                be 4c 29 97 2c 85 62 75 eb 6d 5a 45 f0 5f 51 87
+                6f c6 74 3d ed dd 28 ca ec 9b b3 0e a9 9e 02 c3
+                48 82 69 60 4f e4 97 f7 4c cd 7c 7f ca 16 71 89
+                71 23 cb d3 0d ef 5d 54 a2 b5 53 6a d9 0a 74 7e''',
+                # Salt
+                '''e3 b5 d5 d0 02 c1 bc e5 0c 2b 65 ef 88 a1 88 d8
+                3b ce 7e 61''',
+                # Hash algorithm
+                SHA
+                ),
+
+                #
+                # Example 1.1 to be found in
+                # ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1-vec.zip
+                #
+                (
+                # Private key
+                {
+                'n':'''a5 6e 4a 0e 70 10 17 58 9a 51 87 dc 7e a8 41 d1
+                56 f2 ec 0e 36 ad 52 a4 4d fe b1 e6 1f 7a d9 91
+                d8 c5 10 56 ff ed b1 62 b4 c0 f2 83 a1 2a 88 a3
+                94 df f5 26 ab 72 91 cb b3 07 ce ab fc e0 b1 df
+                d5 cd 95 08 09 6d 5b 2b 8b 6d f5 d6 71 ef 63 77
+                c0 92 1c b2 3c 27 0a 70 e2 59 8e 6f f8 9d 19 f1
+                05 ac c2 d3 f0 cb 35 f2 92 80 e1 38 6b 6f 64 c4
+                ef 22 e1 e1 f2 0d 0c e8 cf fb 22 49 bd 9a 21 37''',
+                'e':'''01 00 01''',
+                'd':'''33 a5 04 2a 90 b2 7d 4f 54 51 ca 9b bb d0 b4 47
+                71 a1 01 af 88 43 40 ae f9 88 5f 2a 4b be 92 e8
+                94 a7 24 ac 3c 56 8c 8f 97 85 3a d0 7c 02 66 c8
+                c6 a3 ca 09 29 f1 e8 f1 12 31 88 44 29 fc 4d 9a
+                e5 5f ee 89 6a 10 ce 70 7c 3e d7 e7 34 e4 47 27
+                a3 95 74 50 1a 53 26 83 10 9c 2a ba ca ba 28 3c
+                31 b4 bd 2f 53 c3 ee 37 e3 52 ce e3 4f 9e 50 3b
+                d8 0c 06 22 ad 79 c6 dc ee 88 35 47 c6 a3 b3 25'''
+                },
+                # Message
+                '''cd c8 7d a2 23 d7 86 df 3b 45 e0 bb bc 72 13 26
+                d1 ee 2a f8 06 cc 31 54 75 cc 6f 0d 9c 66 e1 b6
+                23 71 d4 5c e2 39 2e 1a c9 28 44 c3 10 10 2f 15
+                6a 0d 8d 52 c1 f4 c4 0b a3 aa 65 09 57 86 cb 76
+                97 57 a6 56 3b a9 58 fe d0 bc c9 84 e8 b5 17 a3
+                d5 f5 15 b2 3b 8a 41 e7 4a a8 67 69 3f 90 df b0
+                61 a6 e8 6d fa ae e6 44 72 c0 0e 5f 20 94 57 29
+                cb eb e7 7f 06 ce 78 e0 8f 40 98 fb a4 1f 9d 61
+                93 c0 31 7e 8b 60 d4 b6 08 4a cb 42 d2 9e 38 08
+                a3 bc 37 2d 85 e3 31 17 0f cb f7 cc 72 d0 b7 1c
+                29 66 48 b3 a4 d1 0f 41 62 95 d0 80 7a a6 25 ca
+                b2 74 4f d9 ea 8f d2 23 c4 25 37 02 98 28 bd 16
+                be 02 54 6f 13 0f d2 e3 3b 93 6d 26 76 e0 8a ed
+                1b 73 31 8b 75 0a 01 67 d0''',
+                # Signature
+                '''90 74 30 8f b5 98 e9 70 1b 22 94 38 8e 52 f9 71
+                fa ac 2b 60 a5 14 5a f1 85 df 52 87 b5 ed 28 87
+                e5 7c e7 fd 44 dc 86 34 e4 07 c8 e0 e4 36 0b c2
+                26 f3 ec 22 7f 9d 9e 54 63 8e 8d 31 f5 05 12 15
+                df 6e bb 9c 2f 95 79 aa 77 59 8a 38 f9 14 b5 b9
+                c1 bd 83 c4 e2 f9 f3 82 a0 d0 aa 35 42 ff ee 65
+                98 4a 60 1b c6 9e b2 8d eb 27 dc a1 2c 82 c2 d4
+                c3 f6 6c d5 00 f1 ff 2b 99 4d 8a 4e 30 cb b3 3c''',
+                # Salt
+                '''de e9 59 c7 e0 64 11 36 14 20 ff 80 18 5e d5 7f
+                3e 67 76 af''',
+                # Hash
+                SHA
+                ),
+
+                #
+                # Example 1.2 to be found in
+                # ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1-vec.zip
+                #
+                (
+                # Private key
+                {
+                'n':'''a5 6e 4a 0e 70 10 17 58 9a 51 87 dc 7e a8 41 d1
+                56 f2 ec 0e 36 ad 52 a4 4d fe b1 e6 1f 7a d9 91
+                d8 c5 10 56 ff ed b1 62 b4 c0 f2 83 a1 2a 88 a3
+                94 df f5 26 ab 72 91 cb b3 07 ce ab fc e0 b1 df
+                d5 cd 95 08 09 6d 5b 2b 8b 6d f5 d6 71 ef 63 77
+                c0 92 1c b2 3c 27 0a 70 e2 59 8e 6f f8 9d 19 f1
+                05 ac c2 d3 f0 cb 35 f2 92 80 e1 38 6b 6f 64 c4
+                ef 22 e1 e1 f2 0d 0c e8 cf fb 22 49 bd 9a 21 37''',
+                'e':'''01 00 01''',
+                'd':'''33 a5 04 2a 90 b2 7d 4f 54 51 ca 9b bb d0 b4 47
+                71 a1 01 af 88 43 40 ae f9 88 5f 2a 4b be 92 e8
+                94 a7 24 ac 3c 56 8c 8f 97 85 3a d0 7c 02 66 c8
+                c6 a3 ca 09 29 f1 e8 f1 12 31 88 44 29 fc 4d 9a
+                e5 5f ee 89 6a 10 ce 70 7c 3e d7 e7 34 e4 47 27
+                a3 95 74 50 1a 53 26 83 10 9c 2a ba ca ba 28 3c
+                31 b4 bd 2f 53 c3 ee 37 e3 52 ce e3 4f 9e 50 3b
+                d8 0c 06 22 ad 79 c6 dc ee 88 35 47 c6 a3 b3 25'''
+                },
+                # Message
+                '''85 13 84 cd fe 81 9c 22 ed 6c 4c cb 30 da eb 5c
+                f0 59 bc 8e 11 66 b7 e3 53 0c 4c 23 3e 2b 5f 8f
+                71 a1 cc a5 82 d4 3e cc 72 b1 bc a1 6d fc 70 13
+                22 6b 9e''',
+                # Signature
+                '''3e f7 f4 6e 83 1b f9 2b 32 27 41 42 a5 85 ff ce
+                fb dc a7 b3 2a e9 0d 10 fb 0f 0c 72 99 84 f0 4e
+                f2 9a 9d f0 78 07 75 ce 43 73 9b 97 83 83 90 db
+                0a 55 05 e6 3d e9 27 02 8d 9d 29 b2 19 ca 2c 45
+                17 83 25 58 a5 5d 69 4a 6d 25 b9 da b6 60 03 c4
+                cc cd 90 78 02 19 3b e5 17 0d 26 14 7d 37 b9 35
+                90 24 1b e5 1c 25 05 5f 47 ef 62 75 2c fb e2 14
+                18 fa fe 98 c2 2c 4d 4d 47 72 4f db 56 69 e8 43''',
+                # Salt
+                '''ef 28 69 fa 40 c3 46 cb 18 3d ab 3d 7b ff c9 8f
+                d5 6d f4 2d''',
+                # Hash
+                SHA
+                ),
+
+                #
+                # Example 2.1 to be found in
+                # ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1-vec.zip
+                #
+                (
+                # Private key
+                {
+                'n':'''01 d4 0c 1b cf 97 a6 8a e7 cd bd 8a 7b f3 e3 4f
+                a1 9d cc a4 ef 75 a4 74 54 37 5f 94 51 4d 88 fe
+                d0 06 fb 82 9f 84 19 ff 87 d6 31 5d a6 8a 1f f3
+                a0 93 8e 9a bb 34 64 01 1c 30 3a d9 91 99 cf 0c
+                7c 7a 8b 47 7d ce 82 9e 88 44 f6 25 b1 15 e5 e9
+                c4 a5 9c f8 f8 11 3b 68 34 33 6a 2f d2 68 9b 47
+                2c bb 5e 5c ab e6 74 35 0c 59 b6 c1 7e 17 68 74
+                fb 42 f8 fc 3d 17 6a 01 7e dc 61 fd 32 6c 4b 33
+                c9''',
+                'e':'''01 00 01''',
+                'd':'''02 7d 14 7e 46 73 05 73 77 fd 1e a2 01 56 57 72
+                17 6a 7d c3 83 58 d3 76 04 56 85 a2 e7 87 c2 3c
+                15 57 6b c1 6b 9f 44 44 02 d6 bf c5 d9 8a 3e 88
+                ea 13 ef 67 c3 53 ec a0 c0 dd ba 92 55 bd 7b 8b
+                b5 0a 64 4a fd fd 1d d5 16 95 b2 52 d2 2e 73 18
+                d1 b6 68 7a 1c 10 ff 75 54 5f 3d b0 fe 60 2d 5f
+                2b 7f 29 4e 36 01 ea b7 b9 d1 ce cd 76 7f 64 69
+                2e 3e 53 6c a2 84 6c b0 c2 dd 48 6a 39 fa 75 b1'''
+                },
+                # Message
+                '''da ba 03 20 66 26 3f ae db 65 98 48 11 52 78 a5
+                2c 44 fa a3 a7 6f 37 51 5e d3 36 32 10 72 c4 0a
+                9d 9b 53 bc 05 01 40 78 ad f5 20 87 51 46 aa e7
+                0f f0 60 22 6d cb 7b 1f 1f c2 7e 93 60''',
+                # Signature
+                '''01 4c 5b a5 33 83 28 cc c6 e7 a9 0b f1 c0 ab 3f
+                d6 06 ff 47 96 d3 c1 2e 4b 63 9e d9 13 6a 5f ec
+                6c 16 d8 88 4b dd 99 cf dc 52 14 56 b0 74 2b 73
+                68 68 cf 90 de 09 9a db 8d 5f fd 1d ef f3 9b a4
+                00 7a b7 46 ce fd b2 2d 7d f0 e2 25 f5 46 27 dc
+                65 46 61 31 72 1b 90 af 44 53 63 a8 35 8b 9f 60
+                76 42 f7 8f ab 0a b0 f4 3b 71 68 d6 4b ae 70 d8
+                82 78 48 d8 ef 1e 42 1c 57 54 dd f4 2c 25 89 b5
+                b3''',
+                # Salt
+                '''57 bf 16 0b cb 02 bb 1d c7 28 0c f0 45 85 30 b7
+                d2 83 2f f7''',
+                SHA
+                ),
+
+                #
+                # Example 8.1 to be found in
+                # ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1-vec.zip
+                #
+                (
+                # Private key
+                {
+                'n':'''49 53 70 a1 fb 18 54 3c 16 d3 63 1e 31 63 25 5d
+                f6 2b e6 ee e8 90 d5 f2 55 09 e4 f7 78 a8 ea 6f
+                bb bc df 85 df f6 4e 0d 97 20 03 ab 36 81 fb ba
+                6d d4 1f d5 41 82 9b 2e 58 2d e9 f2 a4 a4 e0 a2
+                d0 90 0b ef 47 53 db 3c ee 0e e0 6c 7d fa e8 b1
+                d5 3b 59 53 21 8f 9c ce ea 69 5b 08 66 8e de aa
+                dc ed 94 63 b1 d7 90 d5 eb f2 7e 91 15 b4 6c ad
+                4d 9a 2b 8e fa b0 56 1b 08 10 34 47 39 ad a0 73
+                3f''',
+                'e':'''01 00 01''',
+                'd':'''6c 66 ff e9 89 80 c3 8f cd ea b5 15 98 98 83 61
+                65 f4 b4 b8 17 c4 f6 a8 d4 86 ee 4e a9 13 0f e9
+                b9 09 2b d1 36 d1 84 f9 5f 50 4a 60 7e ac 56 58
+                46 d2 fd d6 59 7a 89 67 c7 39 6e f9 5a 6e ee bb
+                45 78 a6 43 96 6d ca 4d 8e e3 de 84 2d e6 32 79
+                c6 18 15 9c 1a b5 4a 89 43 7b 6a 61 20 e4 93 0a
+                fb 52 a4 ba 6c ed 8a 49 47 ac 64 b3 0a 34 97 cb
+                e7 01 c2 d6 26 6d 51 72 19 ad 0e c6 d3 47 db e9'''
+                },
+                # Message
+                '''81 33 2f 4b e6 29 48 41 5e a1 d8 99 79 2e ea cf
+                6c 6e 1d b1 da 8b e1 3b 5c ea 41 db 2f ed 46 70
+                92 e1 ff 39 89 14 c7 14 25 97 75 f5 95 f8 54 7f
+                73 56 92 a5 75 e6 92 3a f7 8f 22 c6 99 7d db 90
+                fb 6f 72 d7 bb 0d d5 74 4a 31 de cd 3d c3 68 58
+                49 83 6e d3 4a ec 59 63 04 ad 11 84 3c 4f 88 48
+                9f 20 97 35 f5 fb 7f da f7 ce c8 ad dc 58 18 16
+                8f 88 0a cb f4 90 d5 10 05 b7 a8 e8 4e 43 e5 42
+                87 97 75 71 dd 99 ee a4 b1 61 eb 2d f1 f5 10 8f
+                12 a4 14 2a 83 32 2e db 05 a7 54 87 a3 43 5c 9a
+                78 ce 53 ed 93 bc 55 08 57 d7 a9 fb''',
+                # Signature
+                '''02 62 ac 25 4b fa 77 f3 c1 ac a2 2c 51 79 f8 f0
+                40 42 2b 3c 5b af d4 0a 8f 21 cf 0f a5 a6 67 cc
+                d5 99 3d 42 db af b4 09 c5 20 e2 5f ce 2b 1e e1
+                e7 16 57 7f 1e fa 17 f3 da 28 05 2f 40 f0 41 9b
+                23 10 6d 78 45 aa f0 11 25 b6 98 e7 a4 df e9 2d
+                39 67 bb 00 c4 d0 d3 5b a3 55 2a b9 a8 b3 ee f0
+                7c 7f ec db c5 42 4a c4 db 1e 20 cb 37 d0 b2 74
+                47 69 94 0e a9 07 e1 7f bb ca 67 3b 20 52 23 80
+                c5''',
+                # Salt
+                '''1d 65 49 1d 79 c8 64 b3 73 00 9b e6 f6 f2 46 7b
+                ac 4c 78 fa''',
+                SHA
+                )
+        )
+
+        def testSign1(self):
+                for i in range(len(self._testData)):
+                        # Build the key
+                        comps = [ long(rws(self._testData[i][0][x]),16) for x in ('n','e','d') ]
+                        key = MyKey(RSA.construct(comps))
+                        # Hash function
+                        h = self._testData[i][4].new()
+                        # Data to sign
+                        h.update(t2b(self._testData[i][1]))
+                        # Salt
+                        test_salt = t2b(self._testData[i][3])
+                        key._randfunc = lambda N: test_salt
+                        # The real test
+                        signer = PKCS.new(key)
+                        self.failUnless(signer.can_sign())
+                        s = signer.sign(h)
+                        self.assertEqual(s, t2b(self._testData[i][2]))
+
+        def testVerify1(self):
+               for i in range(len(self._testData)):
+                        # Build the key
+                        comps = [ long(rws(self._testData[i][0][x]),16) for x in ('n','e') ]
+                        key = MyKey(RSA.construct(comps))
+                        # Hash function
+                        h = self._testData[i][4].new()
+                        # Data to sign
+                        h.update(t2b(self._testData[i][1]))
+                        # Salt
+                        test_salt = t2b(self._testData[i][3])
+                        # The real test
+                        key._randfunc = lambda N: test_salt
+                        verifier = PKCS.new(key)
+                        self.failIf(verifier.can_sign())
+                        result = verifier.verify(h, t2b(self._testData[i][2]))
+                        self.failUnless(result)
+
+        def testSignVerify(self):
+                        h = SHA.new()
+                        h.update(b('blah blah blah'))
+
+                        rng = Random.new().read
+                        key = MyKey(RSA.generate(1024,rng))
+                         
+                        # Helper function to monitor what's request from MGF
+                        global mgfcalls
+                        def newMGF(seed,maskLen):
+                            global mgfcalls
+                            mgfcalls += 1
+                            return bchr(0x00)*maskLen
+
+                        # Verify that PSS is friendly to all ciphers
+                        for hashmod in (MD2,MD5,SHA,SHA224,SHA256,SHA384,RIPEMD):
+                            h = hashmod.new()
+                            h.update(b('blah blah blah'))
+
+                            # Verify that sign() asks for as many random bytes
+                            # as the hash output size
+                            key.asked = 0
+                            signer = PKCS.new(key)
+                            s = signer.sign(h)
+                            self.failUnless(signer.verify(h, s))
+                            self.assertEqual(key.asked, h.digest_size)
+
+                        h = SHA.new()
+                        h.update(b('blah blah blah'))
+
+                        # Verify that sign() uses a different salt length
+                        for sLen in (0,3,21):
+                            key.asked = 0
+                            signer = PKCS.new(key, saltLen=sLen)
+                            s = signer.sign(h)
+                            self.assertEqual(key.asked, sLen)
+                            self.failUnless(signer.verify(h, s))
+
+                        # Verify that sign() uses the custom MGF
+                        mgfcalls = 0
+                        signer = PKCS.new(key, newMGF)
+                        s = signer.sign(h)
+                        self.assertEqual(mgfcalls, 1)
+                        self.failUnless(signer.verify(h, s))
+
+                        # Verify that sign() does not call the RNG
+                        # when salt length is 0, even when a new MGF is provided
+                        key.asked = 0
+                        mgfcalls = 0
+                        signer = PKCS.new(key, newMGF, 0)
+                        s = signer.sign(h)
+                        self.assertEqual(key.asked,0)
+                        self.assertEqual(mgfcalls, 1)
+                        self.failUnless(signer.verify(h, s))
+
+
+class testSign1_mytest():
+
+        # List of tuples with test data for PKCS#1 PSS
+        # Each tuple is made up by:
+        #       Item #0: dictionary with RSA key component, or key to import
+        #       Item #1: data to hash and sign
+        #       Item #2: signature of the data #1, done with the key #0,
+        #                and salt #3 after hashing it with #4
+        #       Item #3: salt
+        #       Item #4: hash object generator
+
+        def __init__(self, rsa_parse, msg):
+                _testData = (
+
+                        #
+                        # From in pss-vect.txt to be found in
+                        # ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1-vec.zip
+                        #
+                        (
+                        # Private key
+                        {
+                        'n':'''a2 ba 40 ee 07 e3 b2 bd 2f 02 ce 22 7f 36 a1 95
+                        02 44 86 e4 9c 19 cb 41 bb bd fb ba 98 b2 2b 0e
+                        57 7c 2e ea ff a2 0d 88 3a 76 e6 5e 39 4c 69 d4
+                        b3 c0 5a 1e 8f ad da 27 ed b2 a4 2b c0 00 fe 88
+                        8b 9b 32 c2 2d 15 ad d0 cd 76 b3 e7 93 6e 19 95
+                        5b 22 0d d1 7d 4e a9 04 b1 ec 10 2b 2e 4d e7 75
+                        12 22 aa 99 15 10 24 c7 cb 41 cc 5e a2 1d 00 ee
+                        b4 1f 7c 80 08 34 d2 c6 e0 6b ce 3b ce 7e a9 a5''',
+                        'e':'''01 00 01''',
+                        # In the test vector, only p and q were given...
+                        # d is computed offline as e^{-1} mod (p-1)(q-1)
+                        'd':'''50e2c3e38d886110288dfc68a9533e7e12e27d2aa56
+                        d2cdb3fb6efa990bcff29e1d2987fb711962860e7391b1ce01
+                        ebadb9e812d2fbdfaf25df4ae26110a6d7a26f0b810f54875e
+                        17dd5c9fb6d641761245b81e79f8c88f0e55a6dcd5f133abd3
+                        5f8f4ec80adf1bf86277a582894cb6ebcd2162f1c7534f1f49
+                        47b129151b71'''
+                        },
+
+                        # Data to sign
+                        '''85 9e ef 2f d7 8a ca 00 30 8b dc 47 11 93 bf 55
+                        bf 9d 78 db 8f 8a 67 2b 48 46 34 f3 c9 c2 6e 64
+                        78 ae 10 26 0f e0 dd 8c 08 2e 53 a5 29 3a f2 17
+                        3c d5 0c 6d 5d 35 4f eb f7 8b 26 02 1c 25 c0 27
+                        12 e7 8c d4 69 4c 9f 46 97 77 e4 51 e7 f8 e9 e0
+                        4c d3 73 9c 6b bf ed ae 48 7f b5 56 44 e9 ca 74
+                        ff 77 a5 3c b7 29 80 2f 6e d4 a5 ff a8 ba 15 98
+                        90 fc''',
+                        # Signature
+                        '''8d aa 62 7d 3d e7 59 5d 63 05 6c 7e c6 59 e5 44
+                        06 f1 06 10 12 8b aa e8 21 c8 b2 a0 f3 93 6d 54
+                        dc 3b dc e4 66 89 f6 b7 95 1b b1 8e 84 05 42 76
+                        97 18 d5 71 5d 21 0d 85 ef bb 59 61 92 03 2c 42
+                        be 4c 29 97 2c 85 62 75 eb 6d 5a 45 f0 5f 51 87
+                        6f c6 74 3d ed dd 28 ca ec 9b b3 0e a9 9e 02 c3
+                        48 82 69 60 4f e4 97 f7 4c cd 7c 7f ca 16 71 89
+                        71 23 cb d3 0d ef 5d 54 a2 b5 53 6a d9 0a 74 7e''',
+                        # Salt
+                        '''e3 b5 d5 d0 02 c1 bc e5 0c 2b 65 ef 88 a1 88 d8
+                        3b ce 7e 61''',
+                        # Hash algorithm
+                        SHA
+                        ),
+
+                        #
+                        # Example 1.1 to be found in
+                        # ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1-vec.zip
+                        #
+                        (
+                        # Private key
+                        {
+                        'n':'''a5 6e 4a 0e 70 10 17 58 9a 51 87 dc 7e a8 41 d1
+                        56 f2 ec 0e 36 ad 52 a4 4d fe b1 e6 1f 7a d9 91
+                        d8 c5 10 56 ff ed b1 62 b4 c0 f2 83 a1 2a 88 a3
+                        94 df f5 26 ab 72 91 cb b3 07 ce ab fc e0 b1 df
+                        d5 cd 95 08 09 6d 5b 2b 8b 6d f5 d6 71 ef 63 77
+                        c0 92 1c b2 3c 27 0a 70 e2 59 8e 6f f8 9d 19 f1
+                        05 ac c2 d3 f0 cb 35 f2 92 80 e1 38 6b 6f 64 c4
+                        ef 22 e1 e1 f2 0d 0c e8 cf fb 22 49 bd 9a 21 37''',
+                        'e':'''01 00 01''',
+                        'd':'''33 a5 04 2a 90 b2 7d 4f 54 51 ca 9b bb d0 b4 47
+                        71 a1 01 af 88 43 40 ae f9 88 5f 2a 4b be 92 e8
+                        94 a7 24 ac 3c 56 8c 8f 97 85 3a d0 7c 02 66 c8
+                        c6 a3 ca 09 29 f1 e8 f1 12 31 88 44 29 fc 4d 9a
+                        e5 5f ee 89 6a 10 ce 70 7c 3e d7 e7 34 e4 47 27
+                        a3 95 74 50 1a 53 26 83 10 9c 2a ba ca ba 28 3c
+                        31 b4 bd 2f 53 c3 ee 37 e3 52 ce e3 4f 9e 50 3b
+                        d8 0c 06 22 ad 79 c6 dc ee 88 35 47 c6 a3 b3 25'''
+                        },
+                        # Message
+                        '''cd c8 7d a2 23 d7 86 df 3b 45 e0 bb bc 72 13 26
+                        d1 ee 2a f8 06 cc 31 54 75 cc 6f 0d 9c 66 e1 b6
+                        23 71 d4 5c e2 39 2e 1a c9 28 44 c3 10 10 2f 15
+                        6a 0d 8d 52 c1 f4 c4 0b a3 aa 65 09 57 86 cb 76
+                        97 57 a6 56 3b a9 58 fe d0 bc c9 84 e8 b5 17 a3
+                        d5 f5 15 b2 3b 8a 41 e7 4a a8 67 69 3f 90 df b0
+                        61 a6 e8 6d fa ae e6 44 72 c0 0e 5f 20 94 57 29
+                        cb eb e7 7f 06 ce 78 e0 8f 40 98 fb a4 1f 9d 61
+                        93 c0 31 7e 8b 60 d4 b6 08 4a cb 42 d2 9e 38 08
+                        a3 bc 37 2d 85 e3 31 17 0f cb f7 cc 72 d0 b7 1c
+                        29 66 48 b3 a4 d1 0f 41 62 95 d0 80 7a a6 25 ca
+                        b2 74 4f d9 ea 8f d2 23 c4 25 37 02 98 28 bd 16
+                        be 02 54 6f 13 0f d2 e3 3b 93 6d 26 76 e0 8a ed
+                        1b 73 31 8b 75 0a 01 67 d0''',
+                        # Signature
+                        '''90 74 30 8f b5 98 e9 70 1b 22 94 38 8e 52 f9 71
+                        fa ac 2b 60 a5 14 5a f1 85 df 52 87 b5 ed 28 87
+                        e5 7c e7 fd 44 dc 86 34 e4 07 c8 e0 e4 36 0b c2
+                        26 f3 ec 22 7f 9d 9e 54 63 8e 8d 31 f5 05 12 15
+                        df 6e bb 9c 2f 95 79 aa 77 59 8a 38 f9 14 b5 b9
+                        c1 bd 83 c4 e2 f9 f3 82 a0 d0 aa 35 42 ff ee 65
+                        98 4a 60 1b c6 9e b2 8d eb 27 dc a1 2c 82 c2 d4
+                        c3 f6 6c d5 00 f1 ff 2b 99 4d 8a 4e 30 cb b3 3c''',
+                        # Salt
+                        '''de e9 59 c7 e0 64 11 36 14 20 ff 80 18 5e d5 7f
+                        3e 67 76 af''',
+                        # Hash
+                        SHA
+                        ),
+
+                        (
+                        # Private key
+                        """-----BEGIN RSA PRIVATE KEY-----
+                        MIICXQIBAAKBgQC1OZn2ExXQ5z/1XhBomiy7j6YGQU7EuBXcvnvizU6fGkdcLWKA
+                        MBQJxHOnCJwUSvCecea58P4WSDaAGk/Rbd3y1b1Akr7ilcpW+l0ymfF95pBIMBt1
+                        gXDaHtaM1GgR4H0FisR68fHm2LDMNdGdVdGz3vqpUBboVx7fmLyEAxswuQIDAQAB
+                        AoGBAKkGWW/mqxFVrhSnL/yv14r0VvA8lz/pRhLF4vxNasgAFZCGj/lpXjch8JBY
+                        +mH+51+Qcpb4i7OylIp0f/+gTkGuSAYPYwzwqG3ALwAF4Z5i8qKmklQtEJ+9p7lu
+                        POivMzla4Qx7bvBp+Bq4/8lyg5o+D279MN68455wub516+7RAkEAz+InzJO8H8lN
+                        OKpysNCPx9WQNkpMLODRlOYxDg6/QiHNBshT+Kd8tU9xT8fqPWCybfrYNKDWEahz
+                        C/QVPWR5JQJBAN8r1qZo0KWvOay81ogC6aLoLauJX6UHuhxN1U1cS/AjdSJezWZp
+                        L3zmS9iSJbTyojzwGTly6e+2kGHYafLwlwUCQGD+Ujd/jrz9/Yu863pYNV215W2P
+                        SO9/jgn9RVIby10dzD2n5pYT/3nRMTtaOY6duWLbMVmM2ZSvlc4z+l0ErgkCQA8F
+                        SVY7+ccdCxm4QAw5ffYsAF7qSRi3c2xSRMgHLUlFaa3diZ+Z9stGSNaTx5vtNMQ7
+                        OMdoF5y9wewD/+WNEe0CQQCPXCxOab5++0AB25wqwP2Zk/nFiNznd7+Rk/TIS4c+
+                        +C8/NPfkYJD7aNcpflFvyeLAWJgtSs+ebVHYpEZpUnY5
+                        -----END RSA PRIVATE KEY-----""",
+                        # Data to sign
+                        "abc",
+                        # Signature
+                        '''7e a9 02 59 20 eb a1 b1 67 99 56 44 72 88 00 3d
+                        11 4a 3d 16 40 d2 77 6b 60 62 06 b6 19 f0 7e de
+                        52 93 0d d9 74 25 44 fd b3 4f 5c cf fb b7 98 34
+                        ae ad e3 9e 79 93 fc 5f 4b 48 d0 08 95 c4 66 5e
+                        d5 17 7a 5b 05 a2 7e fb 21 72 68 12 f7 6d 3b fe
+                        97 30 81 c5 75 de e4 26 6c d6 d8 28 0b ae 33 a0
+                        f6 9d f0 23 2f f7 12 0b 40 a3 37 7d 25 bf 60 c9
+                        84 d4 19 27 61 27 f6 a0 62 8b 52 32 26 9a 0c 77''',
+                        # Salt
+                        '''25 5a 3c e4 34 ae e1 41 09 a5 96 f6 a6 7e 34 b6
+                        8e 56 3f 2d 92 07 41 55 f3 e4 a5 9e 47 98 d2 e4''',
+                        # Hash algorithm
+                        SHA256
+                        ),
+
+                        (
+                        # Private key
+                        """-----BEGIN RSA PRIVATE KEY-----
+                        MIIJKwIBAAKCAgEA3zT2hUJNVJPMixm5F6GJl1oNIVPyV3AlcSmSdHkpPZH9LuRd
+                        NEQ1Yv5PuHSFw8PSKmySg+5uMeZudconbCOAoF2pnHgMAw2unm4wc7GJf//E/k7f
+                        UwCG6VD/8aO2U0Fkiwo5XpbSe91fmtqXAsyoqBvAsJAdGGGtede7httEa/PhHf2R
+                        qzzDdyx5VUnZm4IolN7Q25J730L5mOlkwOzH0uuE7zlnwDnMV6sbsFopwiyI012l
+                        Tmj0gIVC2nTCRiJxctRT7Hxiqtq+/PR1rLTzN/80uhN0nHGkHJKHtyJRojjkkwPR
+                        1QugU3A5P569i6ZvncOYq7o5gpaPzmaR7el1h7z057oIiHPo+bSfhhnN8Lc1qo0D
+                        BNkKhxLuC3W8yd0nndGzNYAKK1VCxL06zYIyyokrx/Q1+RSkKcvmcJcHVQRkSlh9
+                        QSyoUTCGdVRcOugP1wXoPkAFFfjep8+xLNnS61Rb58O+P7Jr/0970eMz0ZlM16aq
+                        P3jpyBy16vv/EVPsBcQR29W4Xl7ngJzXIXpWG8YKZ2n9zTOVhXQgdEevUhREIE8o
+                        fsKA8NnAsKbaKixplR86HXCE3fLiJxICDHHfXDRxsAyDCAav7u1I6P0Z8MVaQWGx
+                        XApyljlViaV1xDKgqhfMqKA488Butza5RV9/rJjDtcl/ANK5yza7tlSKYr0CAwEA
+                        AQKCAgEAnrz71Smh9VRnU0Wn7LZlM85HKDybTLPk7OWz0kGYosEAXijqYBFiJlTW
+                        PUghGWhaPvGYAVu/4p0OUZbDEpTtFR+HUE/Puxwv41xZ+O157B3p6zIMgOsPBz8j
+                        xqW3NN58sqVCx5Jbftug8nAilUsXZvbVwru69WwCA3T5WJ73ug5nOvzqa+161XY7
+                        k/xBVebrFXSg9IbosY7gE29oyAuc0NPOZpNcxchVGsQFnRWCPSWO6ULgHTNnLz+W
+                        m+YNg/Na219FkBml0fxfd2Yjif/mORy2Ut8jP19SZz+OZR9zDvuHE0bUI79w9eYJ
+                        0MUD6B0lo/1GxlpaLBUHkLJBdgFiIyJ6kbt0ol0TAL6oHxvK/m/6K8hKmlnsyf2J
+                        OJV4ijzWotP9Ogwb2ZoXncRvD8m28MAYXbLOVVJEEWqxaGFti62CR7dDAZ3dNg8t
+                        IotGC1OlYJlCuGIUvBQC9SHeaFCrRofEPKBJjpuYSpj1RmwR9LQIJLDg2xeiEMj8
+                        3uiSZu81ygClaFaMwHiedV/gASSXYqhcVF9dvzvQ+M7oG8b7TLJ8MhWB+cUjA5we
+                        Yq47uOSmW9wS1rid3YuHtsZEZ9wzXaaM8yX2ELlaJ3GiaMGB/ww/T0St8VEwdG9J
+                        DKWWlcIFy2p1TlpA69G8nv7oI4Im3kLJlQa10/f44hQ7UaFOh8ECggEBAOb8rD+9
+                        P1IGWPwbe1AG7U8PtbJs0g+jldJ/WIxG9T9hIhO5QTVBgQoSmBE6weedbO0EydVf
+                        QIE1yJ8aRCukL5j5X1drXKJbbdC4z569aRPZwcRRdgQ3DbZImgK/JUwNaVwB6lMe
+                        QLU84CXDPZAufakKKeBQvxXSJ+WqxlFh7adcRePwS70KtJuoLkP0/8AVIhZE79bj
+                        pZxYBa7zD4UV9Dvyu0QWYnOVo29cCxgWiA61o8hZn0d5QFpe1fnuayNrzDNEttnb
+                        8Mw7WhsbO7dAri7/SkCY16HyhWXXypEM6VcvdHSV5McqO+bo1Bnv1VPyPTLr38W/
+                        8PlYB41DcJ8/HeMCggEBAPdgnOfZDRmByz8aKB6HJ6RM0tR837ak4KxVcXJ2RYPY
+                        acF9ybS8rpE5LuP/lS2/qtHMT4aPqvNvPPl1gUw+mmrmzYLmhKgCyD0SiYqC1kH4
+                        zlfODQTOe1ZdaGFbyIwrf8GhPPQmdiDqAzWsdIDv4WU/pJXlboAb4sTPVxSuGnAH
+                        ZOHkWQRDBhjZp0ASMpNc+EgqBZ2MaKijxKuZQP9JX7Pi0Gzy6p2t0y03ZOUpL4QW
+                        5RxET7Idr3jYua9Ckb9+Z+A/5xQE6XHv4BIwshW33Kdp147Jp3pUnwU/BWSKRo2a
+                        cLFb5APcJxf17rzH17BzJ/wT/f/R5dqKqX0xDkXyXt8CggEBAKW+lRK/hxoid+Of
+                        DKLl9Y6PpT88mpaiOTVsL2uo1v39guhIPCQstp01rmxejxjVe32vu+whhzWwFB8m
+                        R9hQ3d6UwfcGkNfnZysoEcLEAww9aq6mDVsE7g/olKp34hlqXjmpHi06PhBOeEBg
+                        kmJNGdob8uSEzDiqLfq8ycVH0bIPog7nNFGXxvUSfvBAcuuvvl/gY4D8pK9E8f8R
+                        ZgeHM1N43ysCLO7nZvjXQRatxD9Z0wZGWOZ+eNDf0AahL7ug9EuteM9m5KiHxiZB
+                        Kl5aSSJsCSnFAqWwUkw7xZzDeQQYhyXJEPpgsc8FLnzV/WtZHNTXMCDDk/Q3WGPj
+                        CoMaVSUCggEBAO2hnjhNVXsaJo7QZpekx1zZ+3DpH9IIolaJoXgNywszGawoVFnN
+                        IngYkUWtn7UZuFLey8n8OoCsa8tKqFEAlj94xRQosmfefFHGe99VfdJT2ouYQQNM
+                        AkjdY8aab5TOuGPdqnryc2l8wmaN6kBtrnwfXdAsXDCuGPFsJz+TW4wgXjVssGIa
+                        rEG02yf+Ex1iIpLX4xsL5QGSh/s7NafF6SwZBpSR6PRdJU37N223WZCumZnEuTXl
+                        tEkHD6Ae93kXSVuupyCg80ti3UE8C+Y2/7zGPK5KYhpuLW/RsTF6bvnZ0MVe+zMG
+                        CXvH4HTyF+zFQjSxU76p9/4uU1ASjp58i8sCggEBANdzShhwJnaxKW7e7hiLA5k1
+                        G579ubQfDZy/UJOMeDzmrhpcVN5+cH5afFHMHQczzY0GkBkHz4zgN2euOY/Vh/RR
+                        F+rwzwBisLKq2Oiwu5oPKLD5aIFxbOahzSI8N8Upk7J/pBGlFYeg/13bJtb1DrMV
+                        tslxK8cJa2RFUesi2Jy9kz2bFaKIub8rSBCoXhLbccjZwV2W93oDOU44KnXauFvt
+                        UJlFTRbTsf1YII46Zl4dFstWAb+R6DYHNU+xFpJpSVAeOW7R4i4pq7FKl1Xrr0nu
+                        X7iTAH08FfZMzQxQ/jazDvc22rgMGbGuGkjHqFntiBsEkFDW9s3lMjrMy8xPcLk=
+                        -----END RSA PRIVATE KEY-----""",
+                        # Data to sign
+                        "abc",
+                        # Signature
+                        '''70 1b 61 7e 96 cf b5 9e 59 af ed eb c2 b3 55 fd 
+                        64 c2 6c b4 c7 ad 62 19 af ed f3 b0 82 fa 1d e7 
+                        8f 6f 01 a1 74 67 48 9b a6 c0 ba 7f dc ce 91 49 
+                        7c da d3 d0 aa c8 e9 4d be 9f ea 1f 62 0e 11 55 
+                        4e 82 03 c3 fb 4a f4 ba d9 ab 08 e4 8f 07 ba f0 
+                        ed 5d 6e f7 8b 27 9d 08 dd b4 36 b5 3b 89 b2 e0 
+                        f3 43 b2 9b 4a e6 b1 03 b4 87 5d a9 58 3c 2b 6f 
+                        fa 22 0c 28 d9 28 7a b6 c4 7f 03 f0 21 55 d0 be 
+                        12 0a 8a e9 c5 3c e5 d2 6b e3 9a ce 92 87 e4 10 
+                        5f e0 66 db 46 66 e5 72 be b4 3c d1 8e cf 1a ea 
+                        ba 69 65 09 e9 a1 04 55 3c b5 92 87 57 97 00 cf 
+                        47 20 e8 8a df 93 18 6a cf 2b d3 af 55 8f 55 d5 
+                        78 a0 fa 27 e8 f5 f5 a6 3b b5 ec f1 c2 8d b5 f9 
+                        68 de ca b4 e2 4a 00 cd f5 19 47 8e 36 f4 0e 4f 
+                        5d 34 a3 ec 63 39 08 15 69 b2 83 6d f7 b0 99 3b 
+                        b0 bf 2b e6 ae 9a 60 5b 43 e4 18 18 ec be 09 ba 
+                        85 8a dc 77 75 cd c6 eb 98 d6 b0 9c de a4 d7 1a 
+                        df 2c 3f aa 7c 52 ff 3e 0f 87 20 2b dd 93 92 49 
+                        43 f8 b9 d4 8e de c5 3b 37 a5 ee 8c 68 cc 6d ff 
+                        c6 98 c6 af 00 b8 f4 bf b7 7f 51 b0 53 a5 68 fb 
+                        bf b2 62 57 2e cd 7c c0 0e cc 3e 69 3b a5 a2 bf 
+                        3f ad c6 1b 66 39 57 71 e3 b7 c8 20 b6 51 46 58 
+                        70 be 64 96 38 c4 e1 51 3b 63 68 3c 38 fb d4 b2 
+                        60 ed 0f 2e b5 cf 1e 2c 9c ec 7f b9 19 51 3b 4c 
+                        30 0d 74 a0 e6 f6 14 ea ff cd c5 2e 6a cd c2 f5 
+                        53 3b f4 64 1a 44 9e 16 64 d6 82 f5 7d 1a 47 00 
+                        51 04 90 1b 8e b7 91 d6 73 91 46 74 bd ea 08 86 
+                        41 e4 f5 e5 04 61 f8 bd a4 b7 56 6a 0b a2 b3 ee 
+                        00 f8 cc 04 28 36 de 99 05 2b e2 eb 92 ff 26 1d 
+                        db 29 5a 72 51 a4 b7 f7 ea 5c 27 53 68 0d 89 8c 
+                        1f 17 87 85 9c 1d 8d 42 b4 cb 42 49 c7 0c 1e df 
+                        e0 8e ee e3 1e 77 5d 16 5f 75 31 1c 5c af 3e 02
+                        ''',
+                        # Salt
+                        '''c8 e6 96 a2 a5 5b ab 2f eb b8 42 89 f8 de 0c 8d
+                        8d 82 99 b8 25 b8 f2 35 73 2c 49 d4 d1 f3 04 4d''',
+                        # Hash algorithm
+                        SHA256
+                        ),
+                )
+
+                self._testData = _testData
+                self.rsa_parse = rsa_parse
+                self.msg = msg
+
+        def sign1_mytest(self):
+                for i in range(len(self._testData)):
+                        print("Test vector[%d]" %i)
+                        # Build the key
+                        if isStr(self._testData[i][0]):
+                                print("pem")
+                                key = RSA.importKey(self._testData[i][0])
+                        else:
+                                comps = [ long(rws(self._testData[i][0][x]),16) for x in ('n','e','d') ]
+                                key = MyKey(RSA.construct(comps))
+                        # Hash function
+                        h = self._testData[i][4].new()
+                        # Data to sign
+                        try:
+                                print("try")
+                                h.update(t2b(self._testData[i][1]))
+                        except:
+                                print("except")
+                                h.update(b(self._testData[i][1]))
+                        # Salt
+                        test_salt = t2b(self._testData[i][3])
+                        key._randfunc = lambda N: test_salt
+                        # The real test
+                        signer = PKCS.new(key)
+                        t_flag = signer.can_sign()
+                        if t_flag:
+                                print("mytest: can sign")
+                        else:
+                                print("mytest: can't sign")
+                        s = signer.sign(h)
+                        print("salt:")
+                        #rsa_dump(test_salt)
+                        rsa_dump(key._randfunc(test_salt))
+                        print("signature:")
+                        rsa_dump(s)
+
+                        if s == t2b(self._testData[i][2]):
+                                print("[Pass] RSA PSS signature compare pass")
+                        else:
+                                print("[Fail] PSA PSS signature compare fail")
+
+        def sbc_rsa_sign1_json(self):
+                row = self.rsa_parse
+                # Build the key
+                if isStr(row[0]):
+                        print("pem")
+                        key = RSA.importKey(row[0])
+                else:
+                        comps = [ long(rws(row[0][x]),16) for x in ('n','e','d') ]
+                        key = MyKey(RSA.construct(comps))
+                # Hash function
+                if(row[4] == "SHA"):
+                    h = SHA.new()
+                elif(row[4] == "SHA256"):
+                    h = SHA256.new()
+                elif(row[4] == "SHA384"):
+                    h = SHA384.new()
+                elif(row[4] == "SHA512"):
+                    h = SHA512.new()
+                else:
+                    h = SHA256.new()
+                # Data to sign
+                if(row[5] == 1):
+                        try:
+                                print("try")
+                                h.update(t2b(row[1]))
+                        except:
+                                print("except")
+                                h.update(b(row[1]))
+                else:
+                        h.update(self.msg)
+                rsa_dump(h.digest())
+
+                # Salt
+                test_salt = t2b(row[3])
+                key._randfunc = lambda N: test_salt
+                # The real test
+                signer = PKCS.new(key)
+                t_flag = signer.can_sign()
+                if t_flag:
+                        print("mytest: can sign")
+                else:
+                        print("mytest: can't sign")
+                s = signer.sign(h)
+                print("salt:")
+                #rsa_dump(test_salt)
+                rsa_dump(key._randfunc(test_salt))
+                print("signature:")
+                rsa_dump(s)
+
+                if s == t2b(row[2]):
+                        print("[Pass] RSA PSS signature compare pass")
+                else:
+                        print("[Fail] PSA PSS signature compare fail")
+                return s
+
+        def verify1_mytest(self):
+               for i in range(len(self._testData)):
+                        print("Test vector[%d]" %i)
+                        row = self._testData[i]
+                        # Build the key
+                        if isStr(row[0]):
+                                key = RSA.importKey(row[0]).publickey()
+                        else:
+                                comps = [ long(rws(self._testData[i][0][x]),16) for x in ('n','e') ]
+                                key = MyKey(RSA.construct(comps))
+                        # Hash function
+                        h = self._testData[i][4].new()
+                        # Data to sign
+                        try:
+                                h.update(t2b(self._testData[i][1]))
+                                # Salt
+                                test_salt = t2b(self._testData[i][3])
+                        except:
+                                h.update(b(self._testData[i][1]))
+                                # Salt
+                                test_salt = b(self._testData[i][3])
+                        # The real test
+                        key._randfunc = lambda N: test_salt
+                        verifier = PKCS.new(key)
+                        t_flag = verifier.can_sign()
+                        if t_flag:
+                                print("mytest: can't verify")
+                        else:
+                                print("mytest: can verify")
+                        result = verifier.verify(h, t2b(self._testData[i][2]))
+                        if result:
+                                print("[Pass] RSA PSS verify pass")
+                        else:
+                                print("[Fail] RSA PSS verify fail")
+
+
+def get_tests(config={}):
+    tests = []
+    tests += list_test_cases(PKCS1_PSS_Tests)
+    return tests
+
+if __name__ == '__main__':
+    suite = lambda: unittest.TestSuite(get_tests())
+    unittest.main(defaultTest='suite')
+
+# vim:set ts=4 sw=4 sts=4