import PULS_20160108
[GitHub/mt8127/android_kernel_alcatel_ttab.git] / drivers / misc / mediatek / magnetometer / yamaha529 / yamaha529.c
diff --git a/drivers/misc/mediatek/magnetometer/yamaha529/yamaha529.c b/drivers/misc/mediatek/magnetometer/yamaha529/yamaha529.c
new file mode 100644 (file)
index 0000000..f968c80
--- /dev/null
@@ -0,0 +1,4185 @@
+/* yamaha529.c - YAMAHA529 compass driver
+ *
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT 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 "yamaha529.h"
+
+static u8 *I2CDMABuf_va = NULL;
+static u32 I2CDMABuf_pa = NULL;
+static void (*current_time)(int *sec, int *msec) = NULL;
+
+static int
+utimeval_init(struct utimeval *val)
+{
+    if (val == NULL) {
+        return -1;
+    }
+    val->tv_sec = val->tv_msec = 0;
+    return 0;
+}
+
+static int
+utimeval_is_initial(struct utimeval *val)
+{
+    if (val == NULL) {
+        return 0;
+    }
+    return val->tv_sec == 0 && val->tv_msec == 0;
+}
+
+static int
+utimeval_is_overflow(struct utimeval *val)
+{
+    int32_t max;
+
+    if (val == NULL) {
+        return 0;
+    }
+
+    max = (int32_t) ((uint32_t) 0xffffffff / (uint32_t) 1000);
+    if (val->tv_sec > max) {
+        return 1; /* overflow */
+    }
+    else if (val->tv_sec == max) {
+        if (val->tv_msec > (int32_t)((uint32_t)0xffffffff % (uint32_t)1000)) {
+            return 1; /* overflow */
+        }
+    }
+
+    return 0;
+}
+
+static struct utimeval
+utimeval_plus(struct utimeval *first, struct utimeval *second)
+{
+    struct utimeval result = {0, 0};
+    int32_t tmp;
+
+    if (first == NULL || second == NULL) {
+        return result;
+    }
+
+    tmp = first->tv_sec + second->tv_sec;
+    if (first->tv_sec >= 0 && second->tv_sec >= 0 && tmp < 0) {
+        goto overflow;
+    }
+    if (first->tv_sec < 0 && second->tv_sec < 0 && tmp >= 0) {
+        goto underflow;
+    }
+
+    result.tv_sec = tmp;
+    result.tv_msec = first->tv_msec + second->tv_msec;
+    if (1000 <= result.tv_msec) {
+        tmp = result.tv_sec + result.tv_msec / 1000;
+        if (result.tv_sec >= 0 && result.tv_msec >= 0 && tmp < 0) {
+            goto overflow;
+        }
+        result.tv_sec = tmp;
+        result.tv_msec = result.tv_msec % 1000;
+    }
+    if (result.tv_msec < 0) {
+        tmp = result.tv_sec + result.tv_msec / 1000 - 1;
+        if (result.tv_sec < 0 && result.tv_msec < 0 && tmp >= 0) {
+            goto underflow;
+        }
+        result.tv_sec = tmp;
+        result.tv_msec = result.tv_msec % 1000 + 1000;
+    }
+
+    return result;
+
+overflow:
+    result.tv_sec = 0x7fffffff;
+    result.tv_msec = 999;
+    return result;
+
+underflow:
+    result.tv_sec = 0x80000000;
+    result.tv_msec = 0;
+    return result;
+}
+
+static struct utimeval
+utimeval_minus(struct utimeval *first, struct utimeval *second)
+{
+    struct utimeval result = {0, 0}, tmp;
+
+    if (first == NULL || second == NULL || second->tv_sec == (int)0x80000000) {
+        return result;
+    }
+
+    tmp.tv_sec = -second->tv_sec;
+    tmp.tv_msec = -second->tv_msec;
+    return utimeval_plus(first, &tmp);
+}
+
+static int
+utimeval_less_than(struct utimeval *first, struct utimeval *second)
+{
+    if (first == NULL || second == NULL) {
+        return 0;
+    }
+
+    if (first->tv_sec > second->tv_sec) {
+        return 1;
+    }
+    else if (first->tv_sec < second->tv_sec) {
+        return 0;
+    }
+    else {
+        if (first->tv_msec > second->tv_msec) {
+            return 1;
+        }
+        else {
+            return 0;
+        }
+    }
+}
+
+static int
+utimeval_greater_than(struct utimeval *first, struct utimeval *second)
+{
+    if (first == NULL || second == NULL) {
+        return 0;
+    }
+
+    if (first->tv_sec < second->tv_sec) {
+        return 1;
+    }
+    else if (first->tv_sec > second->tv_sec) {
+        return 0;
+    }
+    else {
+        if (first->tv_msec < second->tv_msec) {
+            return 1;
+        }
+        else {
+            return 0;
+        }
+    }
+}
+
+static int
+utimeval_greater_or_equal(struct utimeval *first,
+                         struct utimeval *second)
+{
+    return !utimeval_less_than(first, second);
+}
+
+static int
+utimeval_greater_than_zero(struct utimeval *val)
+{
+    struct utimeval zero = {0, 0};
+    return utimeval_greater_than(&zero, val);
+}
+
+static int
+utimeval_less_than_zero(struct utimeval *val)
+{
+    struct utimeval zero = {0, 0};
+    return utimeval_less_than(&zero, val);
+}
+
+static struct utimeval *
+msec_to_utimeval(struct utimeval *result, uint32_t msec)
+{
+    if (result == NULL) {
+        return result;
+    }
+    result->tv_sec = msec / 1000;
+    result->tv_msec = msec % 1000;
+
+    return result;
+}
+
+static uint32_t
+utimeval_to_msec(struct utimeval *val)
+{
+    if (val == NULL) {
+        return 0;
+    }
+    if (utimeval_less_than_zero(val)) {
+        return 0;
+    }
+    if (utimeval_is_overflow(val)) {
+        return 0xffffffff;
+    }
+
+    return val->tv_sec * 1000 + val->tv_msec;
+}
+
+static struct utimeval
+utimer_calc_next_time(struct utimer *ut, struct utimeval *cur)
+{
+    struct utimeval result = {0, 0}, delay;
+
+    if (ut == NULL || cur == NULL) {
+        return result;
+    }
+
+    utimer_update_with_curtime(ut, cur);
+    if (utimer_is_timeout(ut)) {
+        result = *cur;
+    }
+    else {
+        delay = utimeval_minus(&ut->delay_ms, &ut->total_time);
+        result = utimeval_plus(cur, &delay);
+    }
+
+    return result;
+}
+
+static struct utimeval
+utimer_current_time(void)
+{
+    struct utimeval tv;
+    int sec, msec;
+
+    if (current_time != NULL) {
+        current_time(&sec, &msec);
+    }
+    else {
+        sec = 0, msec = 0;
+    }
+    tv.tv_sec = sec;
+    tv.tv_msec = msec;
+
+    return tv;
+}
+
+static int
+utimer_clear(struct utimer *ut)
+{
+    if (ut == NULL) {
+        return -1;
+    }
+    utimeval_init(&ut->prev_time);
+    utimeval_init(&ut->total_time);
+
+    return 0;
+}
+
+static int
+utimer_update_with_curtime(struct utimer *ut, struct utimeval *cur)
+{
+    struct utimeval tmp;
+
+    if (ut == NULL || cur == NULL) {
+        return -1;
+    }
+    if (utimeval_is_initial(&ut->prev_time)) {
+        ut->prev_time = *cur;
+    }
+    if (utimeval_greater_than_zero(&ut->delay_ms)) {
+        tmp = utimeval_minus(cur, &ut->prev_time);
+        ut->total_time = utimeval_plus(&tmp, &ut->total_time);
+        ut->prev_time = *cur;
+    }
+
+    return 0;
+}
+
+static int
+utimer_update(struct utimer *ut)
+{
+    struct utimeval cur;
+
+    if (ut == NULL) {
+        return -1;
+    }
+    cur = utimer_current_time();
+    utimer_update_with_curtime(ut, &cur);
+    return 0;
+}
+
+static int
+utimer_is_timeout(struct utimer *ut)
+{
+    if (ut == NULL) {
+        return 0;
+    }
+    if (utimeval_greater_than_zero(&ut->delay_ms)) {
+        return utimeval_greater_or_equal(&ut->delay_ms, &ut->total_time);
+    }
+    else {
+        return 1;
+    }
+}
+
+static int
+utimer_clear_timeout(struct utimer *ut)
+{
+    uint32_t delay, total;
+
+    if (ut == NULL) {
+        return -1;
+    }
+
+    delay = utimeval_to_msec(&ut->delay_ms);
+    if (delay == 0 || utimeval_is_overflow(&ut->total_time)) {
+        total = 0;
+    }
+    else {
+        total = utimeval_to_msec(&ut->total_time);
+        if (delay * 2 <= total) {
+            total = delay;
+        }
+        else {
+            total = total % delay;
+        }
+    }
+    msec_to_utimeval(&ut->total_time, total);
+
+    return 0;
+}
+
+static uint32_t
+utimer_sleep_time_with_curtime(struct utimer *ut, struct utimeval *cur)
+{
+    struct utimeval tv;
+
+    if (ut == NULL || cur == NULL) {
+        return 0;
+    }
+    tv = utimer_calc_next_time(ut, cur);
+    tv = utimeval_minus(&tv, cur);
+    if (utimeval_less_than_zero(&tv)) {
+        return 0;
+    }
+
+    return utimeval_to_msec(&tv);
+}
+
+static uint32_t
+utimer_sleep_time(struct utimer *ut)
+{
+    struct utimeval cur;
+
+    if (ut == NULL) {
+        return 0;
+    }
+
+    cur = utimer_current_time();
+    return utimer_sleep_time_with_curtime(ut, &cur);
+}
+
+static int
+utimer_init(struct utimer *ut, uint32_t delay_ms)
+{
+    if (ut == NULL) {
+        return -1;
+    }
+    utimer_clear(ut);
+    msec_to_utimeval(&ut->delay_ms, delay_ms);
+
+    return 0;
+}
+
+static uint32_t
+utimer_get_total_time(struct utimer *ut)
+{
+    return utimeval_to_msec(&ut->total_time);
+}
+
+static uint32_t
+utimer_get_delay(struct utimer *ut)
+{
+    if (ut == NULL) {
+        return -1;
+    }
+    return utimeval_to_msec(&ut->delay_ms);
+}
+
+static int
+utimer_set_delay(struct utimer *ut, uint32_t delay_ms)
+{
+    return utimer_init(ut, delay_ms);
+}
+
+static void
+utimer_lib_init(void (*func)(int *sec, int *msec))
+{
+    current_time = func;
+}
+static uint8_t i2c_slave_address = 0x00;
+static struct yas529_machdep_func machdep_func;
+
+static int
+yas529_mach_i2c_open(void)
+{
+    if (machdep_func.i2c_open == NULL) {
+        return -1;
+    }
+    return machdep_func.i2c_open();
+}
+
+static int
+yas529_mach_i2c_close(void)
+{
+    if (machdep_func.i2c_close == NULL) {
+        return -1;
+    }
+    return machdep_func.i2c_close();
+}
+
+static int
+yas529_mach_i2c_set_slave_address(uint8_t slave)
+{
+    i2c_slave_address = slave;
+    return 0;
+}
+
+static int
+yas529_mach_i2c_write(uint8_t size, const uint8_t *data)
+{
+    if (machdep_func.i2c_write == NULL) {
+        return -1;
+    }
+    return machdep_func.i2c_write(i2c_slave_address, data, size);
+}
+
+static int
+yas529_mach_i2c_read(uint8_t size, uint8_t *data)
+{
+    if (machdep_func.i2c_read == NULL) {
+        return -1;
+    }
+    return machdep_func.i2c_read(i2c_slave_address, data, size);
+}
+
+static void
+yas529_mach_sleep(uint16_t millisec)
+{
+    if (machdep_func.msleep == NULL) {
+        return;
+    }
+    machdep_func.msleep((int)millisec);
+}
+
+/*------------------------------------------------------------------------------
+                        Compact Driver Measure Functions
+ -----------------------------------------------------------------------------*/
+
+struct yas529_compact_driver {
+    uint8_t raw_calreg[9];
+    int8_t roughoffset_is_set;
+    int16_t matrix[9];
+    int16_t correction_m[9];
+    int8_t temp_coeff[3];
+    int8_t initialized;
+    int16_t temperature;
+};
+
+static struct yas529_compact_driver c_driver = {
+    {0, 0, 0, 0, 0, 0, 0, 0, 0},
+    0,
+    {0, 0, 0, 0, 0, 0, 0, 0, 0},
+    {0, 0, 0, 0, 0, 0, 0, 0, 0},
+    {0, 0, 0},
+    0,
+    -1
+};
+
+static int
+yas529_cdrv_check_transformation_matrix(const int8_t *p)
+{
+    int i, j;
+    int n;
+
+    for (i = 0; i < 3; ++i) {
+        n = 0;
+        for (j = 0; j < 3; ++j) {
+            if (p[i*3 + j] == 1 || p[i*3 + j] == -1) {
+                n++;
+            }
+        }
+        if (n != 1) {
+            return -1;
+        }
+    }
+
+    for (i = 0; i < 3; ++i) {
+        n = 0;
+        for (j = 0; j < 3; ++j) {
+            if (p[j*3 + i] == 1 || p[j*3 + i] == -1) {
+                n++;
+            }
+        }
+        if (n != 1) {
+            return -1;
+        }
+    }
+
+    return 0;
+}
+
+static int
+yas529_cdrv_make_correction_matrix(const int8_t *p, const int16_t *matrix,
+                                   int16_t *ans)
+{
+    int i, j, k;
+    int16_t temp16;
+
+    for (i = 0; i < 3; ++i) {
+        for (j = 0; j < 3; ++j) {
+            temp16 = 0;
+            for (k = 0; k < 3; ++k) {
+                temp16 += p[i*3 + k] * matrix[k*3 + j];
+            }
+            ans[i*3 + j] = temp16;
+        }
+    }
+
+    return 0;
+}
+
+static int
+yas529_cdrv_transform(const int16_t *matrix, const int32_t *raw, int32_t *data)
+{
+    int i, j;
+    int32_t temp;
+
+    for (i = 0; i < 3; ++i) {
+        temp = 0;
+        for (j = 0; j < 3; ++j) {
+            temp += (int32_t)matrix[i*3 + j] * raw[j];
+        }
+        data[i] = temp;
+    }
+
+    return 0;
+}
+
+static int
+yas529_cdrv_msens_correction(const int32_t *raw, uint16_t temperature,
+                             int32_t *data)
+{
+    static const int16_t center16[3] = {
+        16 * YAS529_CDRV_CENTER_X,
+        16 * YAS529_CDRV_CENTER_Y1,
+        16 * YAS529_CDRV_CENTER_Y2
+    };
+    int32_t temp_rawdata[3];
+    int32_t raw_xyz[3];
+    int32_t temps32;
+    int i;
+
+    for (i = 0; i < 3; i++) {
+        temp_rawdata[i] = raw[i];
+    }
+
+    for (i = 0; i < 3; ++i) {
+        temp_rawdata[i] <<= 4;
+        temp_rawdata[i] -= center16[i];
+
+        /*
+          Memo:
+          The number '3 / 100' is approximated to '0x7ae1 / 2^20'.
+        */
+
+        temps32 = ((int32_t)temperature - YAS529_CDRV_CENTER_T) *  c_driver.temp_coeff[i] * 0x7ae1;
+        if (temps32 >= 0) {
+            temp_rawdata[i] -= (int16_t)(temps32 >> 16);
+        }
+        else {
+            temp_rawdata[i] += (int16_t)((-temps32) >> 16);
+        }
+    }
+
+    raw_xyz[0] = - temp_rawdata[0];
+    raw_xyz[1] = temp_rawdata[2] - temp_rawdata[1];
+    raw_xyz[2] = temp_rawdata[2] + temp_rawdata[1];
+
+    yas529_cdrv_transform(c_driver.correction_m, raw_xyz, data);
+
+    for (i = 0; i < 3; ++i) {
+        data[i] /= 1600;
+    }
+
+    return 0;
+}
+
+static int
+yas529_cdrv_actuate_initcoil(void)
+{
+    int i;
+    static const uint8_t InitCoilTable[16] = {
+        0x90, 0x81, 0x91, 0x82, 0x92, 0x83, 0x93, 0x84,
+        0x94, 0x85, 0x95, 0x86, 0x96, 0x87, 0x97, 0x80
+    };
+
+    if (!c_driver.initialized) {
+        return YAS529_CDRV_ERR_NOT_INITIALIZED;
+    }
+
+    if (yas529_mach_i2c_set_slave_address(MS3CDRV_I2C_SLAVE_ADDRESS) < 0) {
+        return YAS529_CDRV_ERR_I2CCTRL;
+    }
+
+    for (i = 0; i < 16; ++i) {
+        if (yas529_mach_i2c_write(1, &InitCoilTable[i]) < 0) {
+            return YAS529_CDRV_ERR_I2CCTRL;
+        }
+    }
+
+    return YAS529_CDRV_NO_ERROR;
+}
+
+static int
+yas529_cdrv_measure_rough_offset(uint8_t *rough_offset)
+{
+    int i;
+    uint8_t dat;
+    uint8_t buf[6];
+    int rv = YAS529_CDRV_NO_ERROR;
+
+    if (rough_offset == NULL) {
+        return YAS529_CDRV_ERR_ARG;
+    }
+
+    if (!c_driver.initialized) {
+        return YAS529_CDRV_ERR_NOT_INITIALIZED;
+    }
+
+    if (yas529_mach_i2c_set_slave_address(MS3CDRV_I2C_SLAVE_ADDRESS) < 0) {
+        return YAS529_CDRV_ERR_I2CCTRL;
+    }
+
+    dat = MS3CDRV_RDSEL_MEASURE;
+    if (yas529_mach_i2c_write(1, &dat) < 0) {
+        return YAS529_CDRV_ERR_I2CCTRL;
+    }
+
+    dat = MS3CDRV_CMD_MEASURE_ROUGHOFFSET;
+    if (yas529_mach_i2c_write(1, &dat) < 0) {
+        return YAS529_CDRV_ERR_I2CCTRL;
+    }
+
+    yas529_mach_sleep(MS3CDRV_WAIT_MEASURE_ROUGHOFFSET);
+
+    if (yas529_mach_i2c_read(6, buf) < 0) {
+        return YAS529_CDRV_ERR_I2CCTRL;
+    }
+
+    if (buf[0] & 0x80) {
+        return YAS529_CDRV_ERR_BUSY;
+    }
+
+    for (i = 0; i < 3; ++i) {
+        rough_offset[2 - i] = ((buf[i << 1] & 0x7) << 8) | buf[(i << 1) | 1];
+    }
+
+    if (rough_offset[0] <= YAS529_CDRV_ROUGHOFFSET_MEASURE_UF_VALUE
+        || rough_offset[0] >= YAS529_CDRV_ROUGHOFFSET_MEASURE_OF_VALUE) {
+        rv |= YAS529_CDRV_MEASURE_X_OFUF;
+    }
+    if (rough_offset[1] <= YAS529_CDRV_ROUGHOFFSET_MEASURE_UF_VALUE
+        || rough_offset[1] >= YAS529_CDRV_ROUGHOFFSET_MEASURE_OF_VALUE) {
+        rv |= YAS529_CDRV_MEASURE_Y1_OFUF;
+    }
+    if (rough_offset[2] <= YAS529_CDRV_ROUGHOFFSET_MEASURE_UF_VALUE
+        || rough_offset[2] >= YAS529_CDRV_ROUGHOFFSET_MEASURE_OF_VALUE) {
+        rv |= YAS529_CDRV_MEASURE_Y2_OFUF;
+    }
+
+    return rv;
+}
+
+static int
+yas529_cdrv_set_rough_offset(const uint8_t *rough_offset)
+{
+    int i;
+    uint8_t dat;
+    static const uint8_t addr[3] = { 0x20, 0x40, 0x60};
+    uint8_t tmp[3];
+    int rv = YAS529_CDRV_NO_ERROR;
+
+    if (rough_offset == NULL) {
+        return YAS529_CDRV_ERR_ARG;
+    }
+    if (rough_offset[0] > 32 || rough_offset[1] > 32 || rough_offset[2] > 32) {
+        return YAS529_CDRV_ERR_ARG;
+    }
+
+    if (!c_driver.initialized) {
+        return YAS529_CDRV_ERR_NOT_INITIALIZED;
+    }
+
+    if (yas529_mach_i2c_set_slave_address(MS3CDRV_I2C_SLAVE_ADDRESS) < 0) {
+        return YAS529_CDRV_ERR_I2CCTRL;
+    }
+
+    for (i = 0; i < 3; i++) {
+        tmp[i] = rough_offset[i];
+    }
+
+    for (i = 0; i < 3; ++i) {
+        if (tmp[i] <= 5) {
+            tmp[i] = 0;
+        }
+        else {
+            tmp[i] -= 5;
+        }
+    }
+    for (i = 0; i < 3; ++i) {
+        dat = addr[i] | tmp[i];
+        if (yas529_mach_i2c_write(1, &dat) < 0) {
+            return YAS529_CDRV_ERR_I2CCTRL;
+        }
+    }
+
+    c_driver.roughoffset_is_set = 1;
+
+    return rv;
+}
+
+static int
+yas529_cdrv_measure_preparation(void)
+{
+    uint8_t dat;
+
+    if (yas529_mach_i2c_set_slave_address(MS3CDRV_I2C_SLAVE_ADDRESS) < 0) {
+        return YAS529_CDRV_ERR_I2CCTRL;
+    }
+
+    dat = MS3CDRV_RDSEL_MEASURE;
+    if (yas529_mach_i2c_write(1, &dat) < 0) {
+        return YAS529_CDRV_ERR_I2CCTRL;
+    }
+
+    return YAS529_CDRV_NO_ERROR;
+}
+
+static int
+yas529_cdrv_measure_sub(int32_t *msens, int32_t *raw, int16_t *t)
+{
+    uint8_t dat;
+    uint8_t buf[8];
+    uint8_t rv = YAS529_CDRV_NO_ERROR;
+    int32_t temp_msens[3];
+    int i;
+
+    if (c_driver.roughoffset_is_set == 0) {
+        return YAS529_CDRV_ERR_ROUGHOFFSET_NOT_WRITTEN;
+    }
+
+    dat = MS3CDRV_CMD_MEASURE_XY1Y2T;
+    if (yas529_mach_i2c_write(1, &dat) < 0) {
+        return YAS529_CDRV_ERR_I2CCTRL;
+    }
+
+    for (i = 0; i < MS3CDRV_WAIT_MEASURE_XY1Y2T; i++) {
+        yas529_mach_sleep(1);
+
+        if (yas529_mach_i2c_read(8, buf) < 0) {
+            return YAS529_CDRV_ERR_I2CCTRL;
+        }
+        if (!(buf[0] & 0x80)) {
+            break;
+        }
+    }
+
+    if (buf[0] & 0x80) {
+        return YAS529_CDRV_ERR_BUSY;
+    }
+
+    for (i = 0; i < 3; ++i) {
+        temp_msens[2 - i] = ((buf[i << 1] & 0x7) << 8) + buf[(i << 1) | 1];
+    }
+    c_driver.temperature = ((buf[6] & 0x7) << 8) + buf[7];
+
+    if (temp_msens[0] <= YAS529_CDRV_NORMAL_MEASURE_UF_VALUE
+        || temp_msens[0] >= YAS529_CDRV_NORMAL_MEASURE_OF_VALUE) {
+        rv |= YAS529_CDRV_MEASURE_X_OFUF;
+    }
+    if (temp_msens[1] <= YAS529_CDRV_NORMAL_MEASURE_UF_VALUE
+        || temp_msens[1] >= YAS529_CDRV_NORMAL_MEASURE_OF_VALUE) {
+        rv |= YAS529_CDRV_MEASURE_Y1_OFUF;
+    }
+    if (temp_msens[2] <= YAS529_CDRV_NORMAL_MEASURE_UF_VALUE
+        || temp_msens[2] >= YAS529_CDRV_NORMAL_MEASURE_OF_VALUE) {
+        rv |= YAS529_CDRV_MEASURE_Y2_OFUF;
+    }
+
+    yas529_cdrv_msens_correction(temp_msens, c_driver.temperature, msens);
+
+    if (raw != NULL) {
+        for (i = 0; i < 3; i++) {
+            raw[i] = temp_msens[i];
+        }
+    }
+    if (t != NULL) {
+        *t = c_driver.temperature;
+    }
+
+    return (int)rv;
+}
+
+static int
+yas529_cdrv_measure(int32_t *msens, int32_t *raw, int16_t *t)
+{
+    int rv;
+
+    if (!c_driver.initialized) {
+        return YAS529_CDRV_ERR_NOT_INITIALIZED;
+    }
+
+    if (msens == NULL) {
+        return YAS529_CDRV_ERR_ARG;
+    }
+
+    rv = yas529_cdrv_measure_preparation();
+    if (rv < 0) {
+        return rv;
+    }
+
+    rv = yas529_cdrv_measure_sub(msens, raw, t);
+
+    return rv;
+}
+
+static int
+yas529_cdrv_recalc_fine_offset(int32_t *prev_fine_offset,
+                               int32_t *new_fine_offset,
+                               uint8_t *prev_rough_offset,
+                               uint8_t *new_rough_offset)
+{
+    int32_t tmp[3], resolution[9], base[3];
+    int32_t raw[3];
+    int32_t diff, i;
+
+    if (!c_driver.initialized) {
+        return YAS529_CDRV_ERR_NOT_INITIALIZED;
+    }
+
+    if (prev_fine_offset == NULL || new_fine_offset == NULL
+            || prev_rough_offset == NULL || new_rough_offset == NULL) {
+        return YAS529_CDRV_ERR_ARG;
+    }
+
+    raw[0] = raw[1] = raw[2] = 0;
+    yas529_cdrv_msens_correction(raw, c_driver.temperature, base);
+    for (i = 0; i < 3; i++) {
+        raw[0] = raw[1] = raw[2] = 0;
+        raw[i] = 226;
+        yas529_cdrv_msens_correction(raw, c_driver.temperature, &resolution[i*3]);
+        resolution[i*3 + 0] -= base[0];
+        resolution[i*3 + 1] -= base[1];
+        resolution[i*3 + 2] -= base[2];
+    }
+
+    for (i = 0; i < 3; i++) {
+        tmp[i] = prev_fine_offset[i];
+    }
+    for (i = 0; i < 3; i++) {
+        diff = (int32_t)new_rough_offset[i] - (int32_t)prev_rough_offset[i];
+        while (diff > 0) {
+            tmp[0] -= resolution[i*3 + 0];
+            tmp[1] -= resolution[i*3 + 1];
+            tmp[2] -= resolution[i*3 + 2];
+            diff--;
+        }
+        while (diff < 0) {
+            tmp[0] += resolution[i*3 + 0];
+            tmp[1] += resolution[i*3 + 1];
+            tmp[2] += resolution[i*3 + 2];
+            diff++;
+        }
+    }
+    for (i = 0; i < 3; i++) {
+        new_fine_offset[i] = tmp[i];
+    }
+
+    return 0;
+}
+
+static int
+yas529_cdrv_set_transformatiom_matrix(const int8_t *transform)
+{
+    if (transform == NULL) {
+        return YAS529_CDRV_ERR_ARG;
+    }
+
+    if (!c_driver.initialized) {
+        return YAS529_CDRV_ERR_NOT_INITIALIZED;
+    }
+
+    if (yas529_cdrv_check_transformation_matrix(transform) < 0) {
+        return YAS529_CDRV_ERR_ARG;
+    }
+
+    yas529_cdrv_make_correction_matrix(transform, c_driver.matrix, c_driver.correction_m);
+
+    return 0;
+}
+
+static int
+yas529_cdrv_init(const int8_t *transform, struct yas529_machdep_func *func)
+{
+    int i;
+    uint8_t dat;
+    uint8_t *buf = c_driver.raw_calreg;
+    uint8_t tempu8;
+       uint8_t devid[2];
+
+    if (transform == NULL || func == NULL) {
+        return YAS529_CDRV_ERR_ARG;
+    }
+    machdep_func = *func;
+
+    if (yas529_cdrv_check_transformation_matrix(transform) < 0) {
+        return YAS529_CDRV_ERR_ARG;
+    }
+
+    c_driver.roughoffset_is_set = 0;
+
+    if (!c_driver.initialized) {
+        if (yas529_mach_i2c_open() < 0) {
+            return YAS529_CDRV_ERR_I2CCTRL;
+        }
+    }
+
+    if (yas529_mach_i2c_set_slave_address(MS3CDRV_I2C_SLAVE_ADDRESS) < 0) {
+        yas529_mach_i2c_close();
+        return YAS529_CDRV_ERR_I2CCTRL;
+    }
+
+       dat = YAS529_RDSEL_DEVICEID;
+       if (yas529_mach_i2c_write(1, &dat) < 0) {
+        yas529_mach_i2c_close();
+        return YAS529_CDRV_ERR_I2CCTRL;
+    }  
+       yas529_mach_sleep(10);
+       int retry = 0;
+       do
+       {
+               //yas529_mach_sleep(10000);
+               if (yas529_mach_i2c_read(2, devid) < 0)
+               {
+                       printk("i2c read failed\n");
+                       continue;
+               }
+               if ((devid[1]) != YAS529_DEVICE_ID) {
+                       printk("device id 0x%X not match!!\n", (unsigned int)devid[1]);
+               } 
+               else 
+               {
+                       printk("device is found\n");
+                       break;
+               }
+                       
+               yas529_mach_sleep(1);
+               retry++;
+       }while(retry < 5);
+    /* preparation to read CAL register */
+    dat = MS3CDRV_CMD_MEASURE_ROUGHOFFSET;
+    if (yas529_mach_i2c_write(1, &dat) < 0) {
+        yas529_mach_i2c_close();
+        return YAS529_CDRV_ERR_I2CCTRL;
+    }
+    yas529_mach_sleep(MS3CDRV_WAIT_MEASURE_ROUGHOFFSET);
+
+    dat = MS3CDRV_RDSEL_CALREGISTER;
+    if (yas529_mach_i2c_write(1, &dat) < 0) {
+        yas529_mach_i2c_close();
+        return YAS529_CDRV_ERR_I2CCTRL;
+    }
+
+    /* dummy read */
+    if (yas529_mach_i2c_read(9, buf) < 0) {
+        yas529_mach_i2c_close();
+        return YAS529_CDRV_ERR_I2CCTRL;
+    }
+
+    if (yas529_mach_i2c_read(9, buf) < 0) {
+        yas529_mach_i2c_close();
+        return YAS529_CDRV_ERR_I2CCTRL;
+    }
+
+       MSE_ERR("initial data, [0]: %d, [1]: %d, [2]: %d, [3]: %d, [4]: %d, [5]: %d,[6]: %d, [7]: %d, [8]: %d! \n",
+               buf[0], buf[1], buf[2], buf[3], buf[4],buf[5], buf[6], buf[7], buf[8]);
+
+    c_driver.matrix[0] = 100;
+
+    tempu8 = (buf[0] & 0xfc) >> 2;
+    c_driver.matrix[1] = tempu8 - 0x20;
+
+    tempu8 = ((buf[0] & 0x3) << 2) | ((buf[1] & 0xc0) >> 6);
+    c_driver.matrix[2] = tempu8 - 8;
+
+    tempu8 = buf[1] & 0x3f;
+    c_driver.matrix[3] = tempu8 - 0x20;
+
+    tempu8 = (buf[2] & 0xfc) >> 2;
+    c_driver.matrix[4] = tempu8 + 38;
+
+    tempu8 = ((buf[2] & 0x3) << 4) | (buf[3] & 0xf0) >> 4;
+    c_driver.matrix[5] = tempu8 - 0x20;
+
+    tempu8 = ((buf[3] & 0xf) << 2) | ((buf[4] & 0xc0) >> 6);
+    c_driver.matrix[6] = tempu8 - 0x20;
+
+    tempu8 = buf[4] & 0x3f;
+    c_driver.matrix[7] = tempu8 - 0x20;
+
+    tempu8 = (buf[5] & 0xfe) >> 1;
+    c_driver.matrix[8] = tempu8 + 66;
+
+    yas529_cdrv_make_correction_matrix(transform, c_driver.matrix, c_driver.correction_m);
+
+    for (i = 0; i < 3; ++i) {
+        c_driver.temp_coeff[i] = (int8_t)((int16_t)buf[i + 6] - 0x80);
+    }
+
+    c_driver.initialized = 1;
+
+    return YAS529_CDRV_NO_ERROR;
+}
+
+static int
+yas529_cdrv_term(void)
+{
+    if (!c_driver.initialized) {
+        return YAS529_CDRV_ERR_NOT_INITIALIZED;
+    }
+    c_driver.initialized = 0;
+
+    if (yas529_mach_i2c_close() < 0) {
+        return YAS529_CDRV_ERR_I2CCTRL;
+    }
+
+    return YAS529_CDRV_NO_ERROR;
+}
+
+#define YAS529_DEFAULT_CALIB_INTERVAL   (50)    /* 50 msecs */
+#define YAS529_DEFAULT_DATA_INTERVAL    (200)   /* 200 msecs */
+#define YAS529_INITCOIL_INTERVAL        (3000)  /* 3 seconds */
+#define YAS529_INITCOIL_GIVEUP_INTERVAL (180000) /* 180 seconds */
+#define YAS529_INTENSITY_THRESH0        (120)   /* 120 uT */
+#define YAS529_INTENSITY_THRESH1        (90)    /* 90 uT */
+#define YAS529_DETECT_OVERFLOW_INTERVAL (0)     /* 0 second */
+#define YAS529_MAX_FILTER_LEN           (30)
+#define YAS529_DEFAULT_FILTER_LEN       (10)
+
+#define MEASURE_ERROR_DELAY             (100)
+#define MEASURE_STATE_NORMAL            (0)
+#define MEASURE_STATE_INIT_COIL         (1)
+#define MEASURE_STATE_ROUGH_MEASURE     (2)
+#define MEASURE_RESULT_OVER_UNDER_FLOW  (0x07)
+
+#define INTENSITY_THRESHOLD_1 \
+    ((YAS529_INTENSITY_THRESH1)*(YAS529_INTENSITY_THRESH1))
+#define INTENSITY_THRESHOLD_0 \
+    ((YAS529_INTENSITY_THRESH0)*(YAS529_INTENSITY_THRESH0))
+#define MIN(a, b)       ((a) > (b) ? (b) : (a))
+
+static const int8_t YAS529_TRANSFORMATION[][9] = {
+    { 0, 1, 0,-1, 0, 0, 0, 0, 1 },
+    {-1, 0, 0, 0,-1, 0, 0, 0, 1 },
+    { 0,-1, 0, 1, 0, 0, 0, 0, 1 },
+    { 1, 0, 0, 0, 1, 0, 0, 0, 1 },
+    { 0,-1, 0,-1, 0, 0, 0, 0,-1 },
+    { 1, 0, 0, 0,-1, 0, 0, 0,-1 },
+    { 0, 1, 0, 1, 0, 0, 0, 0,-1 },
+    {-1, 0, 0, 0, 1, 0, 0, 0,-1 },
+};
+
+static const int supported_data_interval[] = {20, 60, 100, 200, 1000};
+static const int supported_calib_interval[] = {60, 60, 50, 50, 50};
+static const int32_t INVALID_FINE_OFFSET[] = {0x7fffffff, 0x7fffffff, 0x7fffffff};
+static const uint8_t INVALID_ROUGH_OFFSET[] = {0xff, 0xff, 0xff};
+
+struct yas529_fir_filter {
+    int num;
+    int filter_len;
+    int index;
+    int32_t sequence[YAS529_MAX_FILTER_LEN];
+};
+
+struct yas529_driver {
+    struct geomagnetic_hwdep_callback callback;
+
+    struct utimer data_timer;
+    struct utimer initcoil_timer;
+    struct utimer initcoil_giveup_timer;
+    struct utimer detect_overflow_timer;
+
+    int32_t fine_offset[3];
+    int accuracy;
+    uint8_t rough_offset[3];
+    int32_t prev_magnetic[3];
+    int32_t prev_magnetic_raw[3];
+    int measure_state;
+    int active;
+    int overflow;
+    int initcoil_gaveup;
+    int position;
+    int delay_timer_use_data;
+    int delay_timer_interval;
+    int delay_timer_counter;
+    int filter_enable;
+    int filter_len;
+    struct yas529_fir_filter filter[3];
+};
+
+
+static struct yas529_driver this_driver = {
+    .measure_state = MEASURE_STATE_NORMAL,
+    .active = 0,
+    .overflow = 0,
+    .initcoil_gaveup = 0,
+    .position = 0,
+    .delay_timer_use_data = 0,
+    .delay_timer_interval = (YAS529_DEFAULT_DATA_INTERVAL
+                           / YAS529_DEFAULT_CALIB_INTERVAL),
+    .delay_timer_counter = (YAS529_DEFAULT_DATA_INTERVAL
+                           / YAS529_DEFAULT_CALIB_INTERVAL),
+    .filter_len = YAS529_DEFAULT_FILTER_LEN,
+};
+
+static int
+lock(void)
+{
+    if (this_driver.callback.lock != NULL) {
+        if (this_driver.callback.lock() < 0) {
+            return YAS529_ERROR_RESTARTSYS;
+        }
+    }
+    return 0;
+}
+
+static int
+unlock(void)
+{
+    if (this_driver.callback.unlock != NULL) {
+        if (this_driver.callback.unlock() < 0) {
+            return YAS529_ERROR_RESTARTSYS;
+        }
+    }
+    return 0;
+}
+
+static void
+fir_filter_init(struct yas529_fir_filter *filter, int len)
+{
+    int i;
+
+    filter->num = 0;
+    filter->index = 0;
+    filter->filter_len = len;
+
+    for (i = 0; i < filter->filter_len; ++i) {
+        filter->sequence[i] = 0;
+    }
+}
+
+static int32_t
+fir_filter_filter(struct yas529_fir_filter *filter, int32_t in)
+{
+    int32_t out = 0;
+    int i;
+
+    if (filter->filter_len == 0) {
+        return in;
+    }
+    if (filter->num < filter->filter_len) {
+        filter->sequence[filter->index++] = in;
+        filter->num++;
+        return in;
+    }
+    else {
+        if (filter->filter_len <= filter->index) {
+            filter->index = 0;
+        }
+        filter->sequence[filter->index++] = in;
+
+        for (i = 0; i < filter->filter_len; i++) {
+            out += filter->sequence[i];
+        }
+        return out / filter->filter_len;
+    }
+}
+
+static void
+filter_init(struct yas529_driver *d)
+{
+    int i;
+
+    for (i = 0; i < 3; i++) {
+        fir_filter_init(&d->filter[i], d->filter_len);
+    }
+}
+
+static void
+filter_filter(struct yas529_driver *d, int32_t *orig, int32_t *filtered)
+{
+    int i;
+
+    for (i = 0; i < 3; i++) {
+        filtered[i] = fir_filter_filter(&d->filter[i], orig[i]);
+    }
+}
+
+static int32_t
+calc_intensity(const int32_t *p)
+{
+    int32_t intensity = 0;
+    int i;
+
+    for (i = 0; i < 3; i++) {
+        intensity += ((p[i] / 1000) + 1) * ((p[i] / 1000) + 1);
+    }
+
+    return intensity;
+}
+
+static int
+is_valid_rough_offset(const uint8_t *p)
+{
+    return (p != NULL && (p[0] < 33) && (p[1] < 33) && (p[2] < 33));
+}
+
+static int
+is_valid_fine_offset(const int32_t *p)
+{
+    return (p != NULL
+            && (-0x4000 <= p[0] && p[0] <= 0x4000)
+            && (-0x4000 <= p[1] && p[1] <= 0x4000)
+            && (-0x4000 <= p[2] && p[2] <= 0x4000));
+}
+
+static int
+is_rough_offset_differ(const uint8_t *p0, const uint8_t *p1)
+{
+    return (p0[0] != p1[0] || p0[1] != p1[1] || p0[2] != p1[2]);
+}
+
+static int
+is_fine_offset_differ(const int32_t *p0, const int32_t *p1)
+{
+    return (p0[0] != p1[0] || p0[1] != p1[1] || p0[2] != p1[2]);
+}
+
+static int
+get_overflow(struct yas529_driver *d)
+{
+    return d->overflow;
+}
+
+static void
+set_overflow(struct yas529_driver *d, const int overflow)
+{
+    if (d->overflow != overflow) {
+        d->overflow = overflow;
+    }
+}
+
+static int
+get_initcoil_gaveup(struct yas529_driver *d)
+{
+    return d->initcoil_gaveup;
+}
+
+static void
+set_initcoil_gaveup(struct yas529_driver *d, const int initcoil_gaveup)
+{
+    d->initcoil_gaveup = initcoil_gaveup;
+}
+
+static int32_t *
+get_fine_offset(struct yas529_driver *d)
+{
+    return d->fine_offset;
+}
+
+static void
+set_fine_offset(struct yas529_driver *d, const int32_t *offset)
+{
+    int i;
+
+    if (is_fine_offset_differ(d->fine_offset, offset)) {
+        for (i = 0; i < 3; i++) {
+            d->fine_offset[i] = offset[i];
+        }
+    }
+}
+
+static uint8_t *
+get_rough_offset(struct yas529_driver *d)
+{
+    return d->rough_offset;
+}
+
+static void
+set_rough_offset(struct yas529_driver *d, const uint8_t *offset)
+{
+    int i;
+
+    if (is_rough_offset_differ(d->rough_offset, offset)) {
+        for (i = 0; i < 3; i++) {
+            d->rough_offset[i] = offset[i];
+        }
+    }
+}
+
+static int
+get_accuracy(struct yas529_driver *d)
+{
+    return d->accuracy;
+}
+
+static void
+set_accuracy(struct yas529_driver *d, const int accuracy)
+{
+    d->accuracy = accuracy;
+}
+
+static int
+get_active(struct yas529_driver *d)
+{
+    return d->active;
+}
+
+static void
+set_active(struct yas529_driver *d, const int active)
+{
+    d->active = active;
+}
+
+static int
+get_position(struct yas529_driver *d)
+{
+    return d->position;
+}
+
+static void
+set_position(struct yas529_driver *d, const int position)
+{
+    d->position = position;
+}
+
+static int
+get_measure_state(struct yas529_driver *d)
+{
+    return d->measure_state;
+}
+
+static void
+set_measure_state(struct yas529_driver *d, const int state)
+{
+    MSE_LOG("state(%d)\n", state);
+    d->measure_state = state;
+}
+
+static struct utimer *
+get_data_timer(struct yas529_driver *d)
+{
+    return &d->data_timer;
+}
+
+static struct utimer *
+get_initcoil_timer(struct yas529_driver *d)
+{
+    return &d->initcoil_timer;
+}
+
+static struct utimer *
+get_initcoil_giveup_timer(struct yas529_driver *d)
+{
+    return &d->initcoil_giveup_timer;
+}
+
+static struct utimer *
+get_detect_overflow_timer(struct yas529_driver *d)
+{
+    return &d->detect_overflow_timer;
+}
+
+static int
+get_delay_timer_use_data(struct yas529_driver *d)
+{
+    return d->delay_timer_use_data;
+}
+
+static void
+set_delay_timer_use_data(struct yas529_driver *d, int flag)
+{
+    d->delay_timer_use_data = !!flag;
+}
+
+static int
+get_delay_timer_interval(struct yas529_driver *d)
+{
+    return d->delay_timer_interval;
+}
+
+static void
+set_delay_timer_interval(struct yas529_driver *d, int interval)
+{
+    d->delay_timer_interval = interval;
+}
+
+static int
+get_delay_timer_counter(struct yas529_driver *d)
+{
+    return d->delay_timer_counter;
+}
+
+static void
+set_delay_timer_counter(struct yas529_driver *d, int counter)
+{
+    d->delay_timer_counter = counter;
+}
+
+static int
+get_filter_enable(struct yas529_driver *d)
+{
+    return d->filter_enable;
+}
+
+static void
+set_filter_enable(struct yas529_driver *d, int enable)
+{
+    if (!d->filter_enable && enable) {
+        filter_init(d);
+    }
+    d->filter_enable = enable;
+}
+
+static int
+get_filter_len(struct yas529_driver *d)
+{
+    return d->filter_len;
+}
+
+static void
+set_filter_len(struct yas529_driver *d, int len)
+{
+    if (d->filter_len < 0) {
+        d->filter_len = 0;
+    }
+    if (d->filter_len > YAS529_MAX_FILTER_LEN) {
+        d->filter_len = YAS529_MAX_FILTER_LEN;
+    }
+    d->filter_len = len;
+    filter_init(d);
+}
+
+static int32_t*
+get_previous_magnetic(struct yas529_driver *d)
+{
+    return d->prev_magnetic;
+}
+
+static void
+set_previous_magnetic(struct yas529_driver *d, int32_t *data)
+{
+    int i;
+    for (i = 0; i < 3; i++) {
+        d->prev_magnetic[i] = data[i];
+    }
+}
+
+static int32_t*
+get_previous_magnetic_raw(struct yas529_driver *d)
+{
+    return d->prev_magnetic_raw;
+}
+
+static void
+set_previous_magnetic_raw(struct yas529_driver *d, int32_t *data)
+{
+    int i;
+    for (i = 0; i < 3; i++) {
+        d->prev_magnetic_raw[i] = data[i];
+    }
+}
+
+static int
+init_coil(struct yas529_driver *d)
+{
+    int rt;
+
+    MSE_LOG("init_coil IN\n");
+
+    utimer_update(get_initcoil_timer(d));
+    if (!get_initcoil_gaveup(d)) {
+        utimer_update(get_initcoil_giveup_timer(d));
+        if (utimer_is_timeout(get_initcoil_giveup_timer(d))) {
+            utimer_clear_timeout(get_initcoil_giveup_timer(d));
+            set_initcoil_gaveup(d, TRUE);
+        }
+    }
+    if (utimer_is_timeout(get_initcoil_timer(d)) && !get_initcoil_gaveup(d)) {
+        utimer_clear_timeout(get_initcoil_timer(d));
+        MSE_LOG("init_coil!\n");
+        if ((rt = yas529_cdrv_actuate_initcoil()) < 0) {
+            MSE_ERR("yas529_cdrv_actuate_initcoil failed[%d]\n", rt);
+            return rt;
+        }
+        if (get_overflow(d) || !is_valid_rough_offset(get_rough_offset(d))) {
+            set_measure_state(d, MEASURE_STATE_ROUGH_MEASURE);
+        }
+        else {
+            set_measure_state(d, MEASURE_STATE_NORMAL);
+        }
+    }
+
+    MSE_LOG("init_coil OUT\n");
+
+    return 0;
+}
+
+static int
+measure_rough_offset(struct yas529_driver *d)
+{
+    uint8_t roffset[3];
+    int32_t moffset[3];
+    int rt, result = 0, i;
+
+    MSE_LOG("measure_rough_offset IN\n");
+
+    if ((rt = yas529_cdrv_measure_rough_offset(roffset)) < 0) {
+        MSE_ERR("yas529_cdrv_measure_rough_offset failed[%d]\n", rt);
+        return rt;
+    }
+
+    MSE_LOG("rough offset[%d][%d][%d]\n", roffset[0], roffset[1], roffset[2]);
+
+    if (rt & MEASURE_RESULT_OVER_UNDER_FLOW) {
+        MSE_LOG("yas529_cdrv_measure_rough_offset overflow x[%d] y[%d] z[%d]\n",
+                !!(rt&0x01), !!(rt&0x02), !!(rt&0x04));
+
+        set_overflow(d, TRUE);
+        set_measure_state(d, MEASURE_STATE_INIT_COIL);
+        set_accuracy(d, 0);
+        return YAS529_REPORT_OVERFLOW_OCCURED;
+    }
+
+    for (i = 0; i < 3; i++) {
+        moffset[i] = get_fine_offset(d)[i];
+    }
+    if (is_rough_offset_differ(get_rough_offset(d), roffset)) {
+        if (is_valid_rough_offset(get_rough_offset(d))
+                && is_valid_fine_offset(get_fine_offset(d))) {
+            yas529_cdrv_recalc_fine_offset(get_fine_offset(d),
+                    moffset,
+                    get_rough_offset(d),
+                    roffset);
+            result |= YAS529_REPORT_FINE_OFFSET_CHANGED;
+        }
+    }
+
+    if ((rt = yas529_cdrv_set_rough_offset(roffset)) < 0) {
+        MSE_ERR("yas529_cdrv_set_rough_offset failed[%d]\n", rt);
+        return rt;
+    }
+
+    result |= YAS529_REPORT_ROUGH_OFFSET_CHANGED;
+    set_rough_offset(d, roffset);
+    if (is_valid_fine_offset(moffset)) {
+        set_fine_offset(d, moffset);
+    }
+    set_measure_state(d, MEASURE_STATE_NORMAL);
+
+    MSE_LOG("measure_rough_offset OUT\n");
+
+    return result;
+}
+
+static int
+measure_msensor_normal(struct yas529_driver *d, int32_t *raw, int32_t *magnetic)
+{
+    int rt = 0, result, i;
+    int32_t filtered[3];
+
+    MSE_LOG("measure_msensor_normal IN\n");
+
+    result = 0;
+    if ((rt = yas529_cdrv_measure(raw, NULL, NULL)) < 0) {
+        MSE_ERR("yas529_cdrv_measure failed[%d]\n", rt);
+        return rt;
+    }
+    if (rt & MEASURE_RESULT_OVER_UNDER_FLOW) {
+        MSE_LOG("yas529_cdrv_measure overflow x[%d] y[%d] z[%d]\n",
+                !!(rt&0x01), !!(rt&0x02), !!(rt&0x04));
+        utimer_update(get_detect_overflow_timer(d));
+        set_overflow(d, TRUE);
+        if (utimer_is_timeout(get_detect_overflow_timer(d))) {
+            utimer_clear_timeout(get_detect_overflow_timer(d));
+            result |= YAS529_REPORT_OVERFLOW_OCCURED;
+            set_accuracy(d, 0);
+        }
+        if (get_measure_state(d) == MEASURE_STATE_NORMAL) {
+            set_measure_state(d, MEASURE_STATE_INIT_COIL);
+        }
+    }
+    else {
+        utimer_clear(get_detect_overflow_timer(d));
+        set_overflow(d, FALSE);
+        if (get_measure_state(d) == MEASURE_STATE_NORMAL) {
+            utimer_clear(get_initcoil_timer(d));
+            utimer_clear(get_initcoil_giveup_timer(d));
+        }
+    }
+    if (get_filter_enable(d)) {
+        filter_filter(d, raw, filtered);
+    }
+
+    if (is_valid_fine_offset(get_fine_offset(d))) {
+        for (i = 0; i < 3; i++) {
+            magnetic[i] = get_filter_enable(d)
+                                    ? filtered[i] - get_fine_offset(d)[i]
+                                    : raw[i] - get_fine_offset(d)[i];
+        }
+    }
+    else {
+        for (i = 0; i < 3; i++) {
+            magnetic[i] = get_filter_enable(d) ? filtered[i] : raw[i];
+        }
+    }
+    for (i = 0; i < 3; i++) {
+        magnetic[i] *= 400; /* typically raw * 400 is nT in unit */
+    }
+
+    MSE_LOG("measure_msensor_normal OUT\n");
+
+    return result;
+}
+
+static int
+measure_msensor(struct yas529_driver *d, int32_t *raw, int32_t *magnetic)
+{
+    int result, i;
+
+    MSE_LOG("measure_msensor IN\n");
+
+    for (i = 0; i < 3; i++) {
+        raw[i] = get_previous_magnetic_raw(d)[i];
+        magnetic[i] = get_previous_magnetic(d)[i];
+    }
+
+    result = 0;
+    switch (get_measure_state(d)) {
+    case MEASURE_STATE_INIT_COIL:
+        result = init_coil(d);
+        break;
+    case MEASURE_STATE_ROUGH_MEASURE:
+        result = measure_rough_offset(d);
+        break;
+    case MEASURE_STATE_NORMAL:
+        result = 0;
+        break;
+    default:
+        result = -1;
+        break;
+    }
+
+    if (result < 0) {
+        return result;
+    }
+
+    if (!(result & YAS529_REPORT_OVERFLOW_OCCURED)) {
+        result |= measure_msensor_normal(d, raw, magnetic);
+    }
+    set_previous_magnetic(d, magnetic);
+    set_previous_magnetic_raw(d, raw);
+
+    MSE_LOG("measure_msensor OUT\n");
+
+    return result;
+}
+
+static int
+measure(struct yas529_driver *d, int32_t *magnetic, int32_t *raw,
+        int *accuracy, uint32_t *time_delay)
+{
+    int32_t intensity = 0;
+    int result;
+    int counter;
+    uint32_t total = 0;
+
+    MSE_LOG("measure IN\n");
+
+    utimer_update(get_data_timer(d));
+
+    if((result = measure_msensor(d, raw, magnetic)) < 0)
+       {
+        return result;
+    }
+
+    counter = get_delay_timer_counter(d);
+    total = utimer_get_total_time(get_data_timer(d));
+    if (utimer_get_delay(get_data_timer(d)) > 0) {
+        counter -= total / utimer_get_delay(get_data_timer(d));
+    }
+    else {
+        counter = 0;
+    }
+
+    if (utimer_is_timeout(get_data_timer(d))) {
+        utimer_clear_timeout(get_data_timer(d));
+
+        if (get_delay_timer_use_data(d)) {
+            result |= YAS529_REPORT_DATA;
+            if (counter <= 0) {
+                result |= YAS529_REPORT_CALIB;
+            }
+        }
+        else {
+            result |= YAS529_REPORT_CALIB;
+            if (counter <= 0) {
+                result |= YAS529_REPORT_DATA;
+            }
+        }
+    }
+
+    if (counter <= 0) {
+        set_delay_timer_counter(d, get_delay_timer_interval(d));
+    }
+    else {
+        set_delay_timer_counter(d, counter);
+    }
+
+    intensity = calc_intensity(magnetic);
+    *accuracy = get_accuracy(d);
+    if (get_accuracy(d) > 1 && intensity >= INTENSITY_THRESHOLD_1) {
+        *accuracy  = 1;
+    }
+    else if (get_accuracy(d) > 2 && intensity >= INTENSITY_THRESHOLD_0) {
+        *accuracy  = 2;
+    }
+
+    *time_delay = utimer_sleep_time(get_data_timer(d));
+
+    MSE_LOG("measure OUT [%d]\n", result);
+
+    return result;
+}
+
+static int
+activate(struct yas529_driver *d)
+{
+    MSE_LOG("activate IN\n");
+
+    (void) d;
+
+    MSE_LOG("activate OUT\n");
+
+    return 0;
+}
+
+static int
+deactivate(struct yas529_driver *d)
+{
+    MSE_LOG("deactivate IN\n");
+
+    /* restart initialization coil give up timer */
+    utimer_clear(get_initcoil_giveup_timer(d));
+    set_initcoil_gaveup(d, FALSE);
+
+    MSE_LOG("deactivate OUT\n");
+
+    return 0;
+}
+
+static int
+ioctl_get_driver_state(struct yas529_driver *d, int32_t *fine_offset,
+                 uint8_t *rough_offset, int *accuracy)
+{
+    int i;
+
+    MSE_LOG("ioctl_get_driver_state IN\n");
+
+    for (i = 0; i < 3; i++) {
+        rough_offset[i] = (int32_t)get_rough_offset(d)[i];
+    }
+    for (i = 0; i < 3; i++) {
+        fine_offset[i] = get_fine_offset(d)[i];
+    }
+    *accuracy = get_accuracy(d);
+    MSE_LOG("\t r[%u][%u][%u] f[%d][%d][%d] a[%d]\n",
+            rough_offset[0],
+            rough_offset[1],
+            rough_offset[2],
+            fine_offset[0],
+            fine_offset[1],
+            fine_offset[2],
+            *accuracy);
+
+    MSE_LOG("ioctl_get_driver_state OUT\n");
+
+    return 0;
+}
+
+static int
+ioctl_set_driver_state(struct yas529_driver *d, int32_t *fine_offset,
+                 uint8_t *rough_offset, int accuracy)
+{
+    MSE_LOG("ioctl_set_driver_state IN\n");
+
+    set_rough_offset(d, rough_offset);
+    set_fine_offset(d, fine_offset);
+    set_accuracy(d, accuracy);
+    MSE_LOG("\t r[%d][%d][%d] f[%d][%d][%d] a[%d]\n",
+            get_rough_offset(d)[0],
+            get_rough_offset(d)[1],
+            get_rough_offset(d)[2],
+            get_fine_offset(d)[0],
+            get_fine_offset(d)[1],
+            get_fine_offset(d)[2],
+            get_accuracy(d));
+
+    if (is_valid_rough_offset(get_rough_offset(d))) {
+        yas529_cdrv_set_rough_offset(get_rough_offset(d));
+    }
+    else {
+        set_measure_state(d, MEASURE_STATE_ROUGH_MEASURE);
+    }
+
+    MSE_LOG("ioctl_set_driver_state OUT\n");
+
+    return 0;
+}
+
+static int
+yas529_init_nolock(struct yas529_driver *d)
+{
+    struct yas529_machdep_func func;
+    int rt;
+
+    MSE_LOG("yas529_init_nolock IN\n");
+
+    func.i2c_open = d->callback.i2c_open;
+    func.i2c_close = d->callback.i2c_close;
+    func.i2c_write = d->callback.i2c_write;
+    func.i2c_read = d->callback.i2c_read;
+    func.msleep = d->callback.msleep;
+
+    if ((rt = yas529_cdrv_init(YAS529_TRANSFORMATION[get_position(d)], &func)) < 0) {
+        MSE_ERR("yas529_cdrv_init failed[%d]\n", rt);
+        return rt;
+    }
+
+    utimer_lib_init(this_driver.callback.current_time);
+    utimer_init(get_data_timer(d), 50);
+    utimer_init(get_initcoil_timer(d), YAS529_INITCOIL_INTERVAL);
+    utimer_init(get_initcoil_giveup_timer(d), YAS529_INITCOIL_GIVEUP_INTERVAL);
+    utimer_init(get_detect_overflow_timer(d), YAS529_DETECT_OVERFLOW_INTERVAL);
+    set_initcoil_gaveup(d, FALSE);
+
+    set_fine_offset(d, INVALID_FINE_OFFSET);
+    set_rough_offset(d, INVALID_ROUGH_OFFSET);
+    set_accuracy(d, 0);
+
+    if ((rt = yas529_cdrv_actuate_initcoil()) < 0) {
+        MSE_ERR("yas529_cdrv_actuate_initcoil failed[%d]\n", rt);
+        set_measure_state(d, MEASURE_STATE_INIT_COIL);
+    }
+    else {
+        set_measure_state(d, MEASURE_STATE_ROUGH_MEASURE);
+    }
+
+    MSE_LOG("yas529_init_nolock OUT\n");
+
+    return 0;
+}
+
+static void
+yas529_term_nolock(struct yas529_driver *d)
+{
+    MSE_LOG("yas529_term_nolock\n");
+
+    (void) d;
+    yas529_cdrv_term();
+
+    MSE_LOG("yas529_term_nolock out\n");
+}
+
+static int
+yas529_set_enable_nolock(struct yas529_driver *d, int active)
+{
+    if (active) {
+        if (!get_active(d)) {
+            if (activate(d) < 0) {
+                return -1;
+            }
+            set_active(d, TRUE);
+            return 0;
+        }
+    }
+    else {
+        if (get_active(d)) {
+            if (deactivate(d) < 0) {
+                return -1;
+            }
+            set_active(d, FALSE);
+            return 0;
+        }
+    }
+    return 0;
+}
+
+static int
+yas529_get_enable_nolock(struct yas529_driver *d)
+{
+    return get_active(d);
+}
+
+static int
+yas529_set_filter_enable_nolock(struct yas529_driver *d, int enable)
+{
+    set_filter_enable(d, enable);
+    return 0;
+}
+
+static int
+yas529_get_filter_enable_nolock(struct yas529_driver *d)
+{
+    return get_filter_enable(d);
+}
+
+static int
+yas529_set_filter_len_nolock(struct yas529_driver *d, int len)
+{
+    set_filter_len(d, len);
+    return 0;
+}
+
+static int
+yas529_get_filter_len_nolock(struct yas529_driver *d)
+{
+    return get_filter_len(d);
+}
+
+static int
+check_interval(int ms)
+{
+    int index;
+
+    if (ms == 0) {
+        ms = supported_data_interval[0];
+    }
+    for (index = 0; index < NELEMS(supported_data_interval); index++) {
+        if (ms == supported_data_interval[index]) {
+            return index;
+        }
+    }
+    return -1;
+}
+
+static int
+yas529_set_delay_nolock(struct yas529_driver *d, int ms)
+{
+    int index;
+    uint32_t delay_data, delay_calib;
+
+    if ((index = check_interval(ms)) < 0) {
+        return -1;
+    }
+    delay_data = supported_data_interval[index];
+    delay_calib = supported_calib_interval[index];
+    set_delay_timer_use_data(d, delay_data < delay_calib);
+    if (delay_data < delay_calib) {
+        set_delay_timer_interval(d, delay_calib / delay_data);
+        set_delay_timer_counter(d, delay_calib / delay_data);
+        utimer_set_delay(get_data_timer(d), supported_data_interval[index]);
+    }
+    else {
+        set_delay_timer_interval(d, delay_data / delay_calib);
+        set_delay_timer_counter(d, delay_data / delay_calib);
+        utimer_set_delay(get_data_timer(d), supported_calib_interval[index]);
+    }
+
+    return 0;
+}
+
+static int yas529_get_delay_nolock(struct yas529_driver *d, int *ms)
+{
+       if(get_delay_timer_use_data(d))
+       {
+               *ms = utimer_get_delay(get_data_timer(d));
+       }
+       else
+       {
+               *ms = utimer_get_delay(get_data_timer(d)) * get_delay_timer_interval(d);
+       }
+       return 0;
+}
+
+static int
+yas529_set_position_nolock(struct yas529_driver *d, int position)
+{
+    set_position(d, position);
+    yas529_cdrv_set_transformatiom_matrix(YAS529_TRANSFORMATION[position]);
+    return 0;
+}
+
+static int
+yas529_get_position_nolock(struct yas529_driver *d, int *position)
+{
+    *position = get_position(d);
+    return 0;
+}
+
+static int
+yas529_measure(int32_t *magnetic, int32_t *raw, int32_t *accuracy, uint32_t *time_delay_ms)
+{
+       uint32_t time_delay = MEASURE_ERROR_DELAY;
+       int rt;
+
+       MSE_LOG("yas529_measure IN\n");
+
+       if(magnetic == NULL || raw == NULL || accuracy == NULL
+               || time_delay_ms == NULL)
+       {
+               return YAS529_ERROR_ARG;
+       }
+
+       if(lock() < 0)
+       {
+               return YAS529_ERROR_RESTARTSYS;
+       }
+
+       *time_delay_ms = MEASURE_ERROR_DELAY;
+       rt = 0;
+
+       if(get_active(&this_driver))
+       {
+               rt = measure(&this_driver, magnetic, raw, accuracy, &time_delay);
+               if(rt >= 0)
+               {
+                       *time_delay_ms = time_delay;
+                       if(*time_delay_ms > 0)
+                       {
+                               *time_delay_ms += 1; /* for the system that the time is in usec  unit */
+                       }
+               }
+       }
+       else
+       {
+               rt = YAS529_ERROR_NOT_ACTIVE;
+       }
+
+       if(unlock() < 0)
+       {
+               return YAS529_ERROR_RESTARTSYS;
+       }
+
+       MSE_LOG("yas529_measure OUT[%d]\n", rt);
+
+       return rt;
+}
+
+static int
+yas529_get_enable(void)
+{
+    int rt;
+
+    MSE_LOG("yas529_get_enable\n");
+
+    if (lock() < 0) {
+        return YAS529_ERROR_RESTARTSYS;
+    }
+
+    rt = yas529_get_enable_nolock(&this_driver);
+
+    if (unlock() < 0) {
+        return YAS529_ERROR_RESTARTSYS;
+    }
+
+    MSE_LOG("yas529_get_enable OUT[%d]\n", rt);
+
+    return rt;
+}
+
+static int
+yas529_set_enable(int enable)
+{
+    int rt;
+
+    MSE_LOG("yas529_set_enable IN\n");
+
+    if (lock() < 0) {
+        return YAS529_ERROR_RESTARTSYS;
+    }
+
+    rt = yas529_set_enable_nolock(&this_driver, enable);
+
+    if (unlock() < 0) {
+        return YAS529_ERROR_RESTARTSYS;
+    }
+
+    MSE_LOG("yas529_set_enable OUT\n");
+
+    return rt;
+}
+
+static int
+yas529_get_filter_enable(void)
+{
+    int rt;
+
+    MSE_LOG("yas529_get_filter_enable\n");
+
+    if (lock() < 0) {
+        return YAS529_ERROR_RESTARTSYS;
+    }
+
+    rt = yas529_get_filter_enable_nolock(&this_driver);
+
+    if (unlock() < 0) {
+        return YAS529_ERROR_RESTARTSYS;
+    }
+
+    MSE_LOG("yas529_get_filter_enable OUT[%d]\n", rt);
+
+    return rt;
+}
+
+static int
+yas529_set_filter_enable(int enable)
+{
+    int rt;
+
+    MSE_LOG("yas529_set_filter_enable IN\n");
+
+    if (lock() < 0) {
+        return YAS529_ERROR_RESTARTSYS;
+    }
+
+    rt = yas529_set_filter_enable_nolock(&this_driver, enable);
+
+    if (unlock() < 0) {
+        return YAS529_ERROR_RESTARTSYS;
+    }
+
+    MSE_LOG("yas529_set_filter_enable OUT\n");
+
+    return rt;
+}
+
+static int
+yas529_get_filter_len(void)
+{
+    int rt;
+
+    MSE_LOG("yas529_get_filter_len\n");
+
+    if (lock() < 0) {
+        return YAS529_ERROR_RESTARTSYS;
+    }
+
+    rt = yas529_get_filter_len_nolock(&this_driver);
+
+    if (unlock() < 0) {
+        return YAS529_ERROR_RESTARTSYS;
+    }
+
+    MSE_LOG("yas529_get_filter_len[%d] OUT\n", rt);
+
+    return rt;
+}
+
+static int
+yas529_set_filter_len(int len)
+{
+    int rt;
+
+    MSE_LOG("yas529_set_filter_len IN\n");
+
+    if (lock() < 0) {
+        return YAS529_ERROR_RESTARTSYS;
+    }
+
+    rt = yas529_set_filter_len_nolock(&this_driver, len);
+
+    if (unlock() < 0) {
+        return YAS529_ERROR_RESTARTSYS;
+    }
+
+    MSE_LOG("yas529_set_filter_len OUT\n");
+
+    return rt;
+}
+
+static int
+yas529_get_delay(void)
+{
+       int ms;
+
+       MSE_LOG("yas529_get_delay\n");
+
+       if(lock() < 0)
+       {
+               return YAS529_ERROR_RESTARTSYS;
+       }
+
+       yas529_get_delay_nolock(&this_driver, &ms);
+
+       if(unlock() < 0)
+       {
+               return YAS529_ERROR_RESTARTSYS;
+       }
+
+       MSE_LOG("yas529_get_delay[%d] OUT\n", ms);
+
+       return ms;
+}
+
+static int
+yas529_set_delay(int delay)
+{
+    int rt;
+
+    MSE_LOG("yas529_set_delay\n");
+
+    if (lock() < 0) {
+        return YAS529_ERROR_RESTARTSYS;
+    }
+
+    rt = yas529_set_delay_nolock(&this_driver, delay);
+
+    if (unlock() < 0) {
+        return YAS529_ERROR_RESTARTSYS;
+    }
+
+    MSE_LOG("yas529_set_delay OUT\n");
+
+    return rt;
+}
+
+static int
+yas529_get_position(void)
+{
+    int position = 0;
+
+    MSE_LOG("yas529_get_position\n");
+
+    if (lock() < 0) {
+        return YAS529_ERROR_RESTARTSYS;
+    }
+
+    yas529_get_position_nolock(&this_driver, &position);
+
+    if (unlock() < 0) {
+        return YAS529_ERROR_RESTARTSYS;
+    }
+
+    MSE_LOG("yas529_get_position[%d] OUT\n", position);
+
+    return position;
+}
+
+static int
+yas529_set_position(int position)
+{
+    int rt;
+
+    if (position < 0 || 7 < position) {
+        return YAS529_ERROR_ARG;
+    }
+
+    MSE_LOG("yas529_set_position\n");
+
+    if (lock() < 0) {
+        return YAS529_ERROR_RESTARTSYS;
+    }
+
+    rt = yas529_set_position_nolock(&this_driver, position);
+
+    if (unlock() < 0) {
+        return YAS529_ERROR_RESTARTSYS;
+    }
+
+    MSE_LOG("yas529_set_position[%d] OUT\n", position);
+
+    return rt;
+}
+
+static int
+yas529_dev_ioctl_get_driver_state(unsigned long args)
+{
+    struct yas529_driver_state *p;
+    int32_t fine_offset[3];
+    uint8_t rough_offset[3];
+    int accuracy;
+    int i;
+
+    MSE_LOG("yas529_dev_ioctl_get_driver_state [%p]\n", (void*)args);
+
+    p = (struct yas529_driver_state *) args;
+    ioctl_get_driver_state(&this_driver, fine_offset, rough_offset, &accuracy);
+
+    for (i = 0; i < 3; i++) {
+        p->fine_offset[i] = fine_offset[i];
+        p->rough_offset[i] = rough_offset[i];
+    }
+    p->accuracy = accuracy;
+
+    MSE_LOG("yas529_dev_ioctl_get_driver_state OUT\n");
+
+    return 0;
+}
+
+static int
+yas529_dev_ioctl_set_driver_state(unsigned long args)
+{
+    struct yas529_driver_state *p;
+    int32_t fine_offset[3];
+    uint8_t rough_offset[3];
+    int accuracy;
+    int i;
+
+    MSE_LOG("yas529_dev_ioctl_set_driver_state [%p]\n", (void*)args);
+
+    p = (struct yas529_driver_state *) args;
+    for (i = 0; i < 3; i++) {
+        fine_offset[i] = p->fine_offset[i];
+        rough_offset[i] = p->rough_offset[i];
+    }
+    accuracy = p->accuracy;
+
+    ioctl_set_driver_state(&this_driver, fine_offset, rough_offset, accuracy);
+
+    MSE_LOG("yas529_dev_ioctl_set_driver_state OUT\n");
+
+    return 0;
+}
+
+static int
+yas529_dev_ioctl(unsigned int cmd, unsigned long args)
+{
+       int rt;
+
+       MSE_LOG("yas529_dev_ioctl IN [%d][%p]\n", cmd, (void*)args);
+
+       if((void*)args == NULL)
+       {
+               return YAS529_ERROR_ARG;
+       }
+
+       if(lock() < 0)
+       {
+               return YAS529_ERROR_RESTARTSYS;
+       }
+
+       switch (cmd)
+       {
+               case YAS529_IOC_GET_DRIVER_STATE:
+                       rt = yas529_dev_ioctl_get_driver_state(args);
+                       break;
+                       
+               case YAS529_IOC_SET_DRIVER_STATE:
+                       rt = yas529_dev_ioctl_set_driver_state(args);
+                       break;
+                       
+               default:
+                       rt = -1;
+                       break;
+       }
+
+       if(unlock() < 0)
+       {
+               return YAS529_ERROR_RESTARTSYS;
+       }
+
+       MSE_LOG("yas529_dev_ioctl OUT[%d]\n", rt);
+
+       return rt;
+}
+
+static int
+yas529_init(void)
+{
+       int rt;
+
+       MSE_LOG("yas529_init\n");
+
+       if(lock() < 0)
+       {
+               return YAS529_ERROR_RESTARTSYS;
+       }
+
+       rt = yas529_init_nolock(&this_driver);
+
+       if(unlock() < 0)
+       {
+               return YAS529_ERROR_RESTARTSYS;
+       }
+
+       return rt;
+}
+
+static int
+yas529_term(void)
+{
+    MSE_LOG("yas529_term\n");
+
+    if (lock() < 0) {
+        return YAS529_ERROR_RESTARTSYS;
+    }
+
+    yas529_term_nolock(&this_driver);
+
+    if (unlock() < 0) {
+        return YAS529_ERROR_RESTARTSYS;
+    }
+
+    return 0;
+}
+
+static int
+geomagnetic_driver_init(struct geomagnetic_hwdep_driver *hwdep_driver)
+{
+    if (hwdep_driver == NULL) {
+        return -1;
+    }
+
+    MSE_LOG("geomagnetic_driver_init\n");
+
+    hwdep_driver->init = yas529_init;
+    hwdep_driver->term = yas529_term;
+    hwdep_driver->get_enable = yas529_get_enable;
+    hwdep_driver->set_enable = yas529_set_enable;
+    hwdep_driver->get_filter_enable = yas529_get_filter_enable;
+    hwdep_driver->set_filter_enable = yas529_set_filter_enable;
+    hwdep_driver->get_filter_len = yas529_get_filter_len;
+    hwdep_driver->set_filter_len = yas529_set_filter_len;
+    hwdep_driver->get_delay = yas529_get_delay;
+    hwdep_driver->set_delay = yas529_set_delay;
+    hwdep_driver->get_position = yas529_get_position;
+    hwdep_driver->set_position = yas529_set_position;
+    hwdep_driver->measure = yas529_measure;
+    hwdep_driver->ioctl = yas529_dev_ioctl;
+    this_driver.callback.lock = hwdep_driver->callback.lock;
+    this_driver.callback.unlock = hwdep_driver->callback.unlock;
+    this_driver.callback.i2c_open = hwdep_driver->callback.i2c_open;
+    this_driver.callback.i2c_close = hwdep_driver->callback.i2c_close;
+    this_driver.callback.i2c_write = hwdep_driver->callback.i2c_write;
+    this_driver.callback.i2c_read = hwdep_driver->callback.i2c_read;
+    this_driver.callback.msleep = hwdep_driver->callback.msleep;
+    this_driver.callback.current_time = hwdep_driver->callback.current_time;
+
+    return 0;
+}
+
+#include <asm/atomic.h>
+#include <linux/types.h>
+#include <linux/i2c.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/miscdevice.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/mutex.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+
+
+#include <linux/hwmsensor.h>
+#include <linux/hwmsen_dev.h>
+#include <linux/sensors_io.h>
+#include <cust_mag.h>
+
+/*----------------------------------------------------------------------------*/
+#define YAMAHA529_DEV_NAME         "yamaha529"
+#define DRIVER_VERSION          "1.0.1"
+/*----------------------------------------------------------------------------*/
+#define YAMAHA529_AXIS_X            0
+#define YAMAHA529_AXIS_Y            1
+#define YAMAHA529_AXIS_Z            2
+#define YAMAHA529_AXES_NUM          3
+
+#define DRIVER_DEBUG   0
+
+/*----------------------------------------------------------------------------*/
+#define MSE_TAG                  "MSENSOR"
+
+#if DRIVER_DEBUG
+#define MSE_FUN(f)               printk(KERN_INFO MSE_TAG" %s\r\n", __FUNCTION__)
+#define MSE_ERR(fmt, args...)    printk(KERN_ERR MSE_TAG" %s %d : \r\n"fmt, __FUNCTION__, __LINE__, ##args)
+#define MSE_LOG(fmt, args...)    printk(KERN_INFO MSE_TAG fmt, ##args)
+#define MSE_VER(fmt, args...)   ((void)0)
+#else
+#define MSE_FUN(f)             
+#define MSE_ERR(fmt, args...)
+#define MSE_LOG(fmt, args...) 
+#define MSE_VER(fmt, args...) 
+
+#endif
+
+/*----------------------------------------------------------------------------*/
+/*----------------------------------------------------------------------------*/
+static const struct i2c_device_id yamaha529_i2c_id[] = {{YAMAHA529_DEV_NAME,0},{}};
+/*the adapter id will be available in customization*/
+static unsigned short yamaha529_force[] = {0x00, YAMAHA304_I2C_ADDRESS, I2C_CLIENT_END, I2C_CLIENT_END};
+static const unsigned short *const yamaha529_forces[] = { yamaha529_force, NULL };
+static struct i2c_client_address_data yamaha529_addr_data = { .forces = yamaha529_forces,};
+/*----------------------------------------------------------------------------*/
+static int yamaha529_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id); 
+static int yamaha529_i2c_remove(struct i2c_client *client);
+static int yamaha529_i2c_detect(struct i2c_client *client, int kind, struct i2c_board_info *info);
+
+static struct platform_driver yamaha_sensor_driver;
+
+
+#define ABS_STATUS              (ABS_BRAKE)
+#define ABS_WAKE                (ABS_MISC)
+
+#define ABS_RAW_DISTORTION      (ABS_THROTTLE)
+#define ABS_RAW_THRESHOLD       (ABS_RUDDER)
+#define ABS_RAW_SHAPE           (ABS_WHEEL)
+#define ABS_RAW_REPORT          (ABS_GAS)
+
+#define DEBUG 0
+
+static int geomagnetic_lock(void);
+static int geomagnetic_unlock(void);
+static int geomagnetic_i2c_open(void);
+static int geomagnetic_i2c_close(void);
+static int geomagnetic_i2c_read(uint8_t slave, uint8_t *buf, int len);
+static int geomagnetic_i2c_write(uint8_t slave, const uint8_t *buf, int len);
+static void geomagnetic_msleep(int ms);
+static void geomagnetic_current_time(int32_t *sec, int32_t *msec);
+
+struct geomagnetic_data {
+       struct input_dev *input_data;
+       struct input_dev *input_raw;
+       struct delayed_work work;
+       struct semaphore driver_lock;
+       struct semaphore multi_lock;
+       struct mag_hw *hw;
+       atomic_t last_data[3];
+       atomic_t last_status;
+       atomic_t enable;
+       atomic_t filter_enable;
+       atomic_t filter_len;
+       atomic_t delay;
+       int32_t threshold;
+       int32_t distortion[3];
+       int32_t shape;
+       struct yas529_driver_state driver_state;
+       struct i2c_client *client;
+       atomic_t layout;   
+       atomic_t trace;
+#if DEBUG
+       int suspend;
+#endif
+};
+
+static struct geomagnetic_hwdep_driver hwdep_driver = {
+    .callback = {
+        .lock           = geomagnetic_lock,
+        .unlock         = geomagnetic_unlock,
+        .i2c_open       = geomagnetic_i2c_open,
+        .i2c_close      = geomagnetic_i2c_close,
+        .i2c_read       = geomagnetic_i2c_read,
+        .i2c_write      = geomagnetic_i2c_write,
+        .msleep         = geomagnetic_msleep,
+        .current_time   = geomagnetic_current_time,
+    },
+};
+
+static struct i2c_client *this_client = NULL;
+
+static int
+geomagnetic_multi_lock(void)
+{
+    struct geomagnetic_data *data = NULL;
+    int rt;
+
+    if (this_client == NULL) {
+        return -1;
+    }
+
+    data = i2c_get_clientdata(this_client);
+    rt = down_interruptible(&data->multi_lock);
+    if (rt < 0) {
+        up(&data->multi_lock);
+    }
+    return rt;
+}
+
+static int
+geomagnetic_multi_unlock(void)
+{
+    struct geomagnetic_data *data = NULL;
+
+    if (this_client == NULL) {
+        return -1;
+    }
+
+    data = i2c_get_clientdata(this_client);
+    up(&data->multi_lock);
+    return 0;
+}
+
+static int
+geomagnetic_enable(struct geomagnetic_data *data)
+{
+    if (!atomic_cmpxchg(&data->enable, 0, 1)) {
+        schedule_delayed_work(&data->work, 0);
+    }
+
+    return 0;
+}
+
+static int
+geomagnetic_disable(struct geomagnetic_data *data)
+{
+    if (atomic_cmpxchg(&data->enable, 1, 0)) {
+        cancel_delayed_work_sync(&data->work);
+    }
+
+    return 0;
+}
+
+/* Sysfs interface */
+static
+int
+yas529_msensor_get_delay(void)
+{
+    struct geomagnetic_data *data = i2c_get_clientdata(this_client);
+    int delay;
+
+    if (geomagnetic_multi_lock() < 0) {
+        return -1;
+    }
+
+    delay = atomic_read(&data->delay);
+
+    geomagnetic_multi_unlock();
+
+    return delay;
+}
+
+
+static ssize_t
+geomagnetic_delay_show(struct device *dev,
+        struct device_attribute *attr,
+        char *buf)
+{
+    return sprintf(buf, "%d\n", yas529_msensor_get_delay());
+}
+
+
+static
+int
+yas529_msensor_set_delay(int msec)
+{
+    struct geomagnetic_data *data = i2c_get_clientdata(this_client);
+
+    if (geomagnetic_multi_lock() < 0) {
+        return -1;
+    }
+
+    if (hwdep_driver.set_delay == NULL) {
+        return -1;
+    }
+
+    if (hwdep_driver.set_delay(msec) == 0) {
+        atomic_set(&data->delay, msec);
+    }
+
+    geomagnetic_multi_unlock();
+
+    return 0;
+}
+
+
+static ssize_t
+geomagnetic_delay_store(struct device *dev,
+        struct device_attribute *attr,
+        const char *buf,
+        size_t count)
+{
+    int value = simple_strtoul(buf, NULL, 10);
+
+    yas529_msensor_set_delay(value);
+
+    return count;
+}
+
+
+static
+int
+yas529_msensor_get_enable(void)
+{
+    struct geomagnetic_data *data = i2c_get_clientdata(this_client);
+    int enable;
+
+    if (geomagnetic_multi_lock() < 0) {
+        return -1;
+    }
+
+    enable = atomic_read(&data->enable);
+
+    geomagnetic_multi_unlock();
+
+    return enable;
+}
+
+
+static ssize_t
+geomagnetic_enable_show(struct device *dev,
+        struct device_attribute *attr,
+        char *buf)
+{
+    return sprintf(buf, "%d\n", yas529_msensor_get_enable());
+}
+
+
+static
+int
+yas529_msensor_set_enable(int enable)
+{
+    struct geomagnetic_data *data = i2c_get_clientdata(this_client);
+
+    if (hwdep_driver.set_enable == NULL) {
+        return -1;
+    }
+
+    if (geomagnetic_multi_lock() < 0) {
+        return -1;
+    }
+
+    if (enable) {
+        hwdep_driver.set_enable(enable);
+        geomagnetic_enable(data);
+    }
+    else {
+        geomagnetic_disable(data);
+        hwdep_driver.set_enable(enable);
+    }
+
+    geomagnetic_multi_unlock();
+
+    return 0;
+}
+
+
+static ssize_t
+geomagnetic_enable_store(struct device *dev,
+        struct device_attribute *attr,
+        const char *buf,
+        size_t count)
+{
+    int value;
+
+    value = simple_strtoul(buf, NULL, 10);
+    if (value != 0 && value != 1) {
+        return count;
+    }
+
+    yas529_msensor_set_enable(value);
+
+    return count;
+}
+
+static ssize_t
+geomagnetic_filter_enable_show(struct device *dev,
+        struct device_attribute *attr,
+        char *buf)
+{
+    struct input_dev *input_data = to_input_dev(dev);
+    struct geomagnetic_data *data = input_get_drvdata(input_data);
+
+    return sprintf(buf, "%d\n", atomic_read(&data->filter_enable));
+}
+
+static ssize_t
+geomagnetic_filter_enable_store(struct device *dev,
+        struct device_attribute *attr,
+        const char *buf,
+        size_t count)
+{
+    struct input_dev *input_data = to_input_dev(dev);
+    struct geomagnetic_data *data = input_get_drvdata(input_data);
+    int value;
+
+    if (hwdep_driver.set_filter_enable == NULL) {
+        return -ENOTTY;
+    }
+
+    value = simple_strtoul(buf, NULL, 10);
+    if (value != 0 && value != 1) {
+        return count;
+    }
+    if (geomagnetic_multi_lock() < 0) {
+        return count;
+    }
+
+    if (value) {
+        hwdep_driver.set_filter_enable(value);
+    }
+    else {
+        hwdep_driver.set_filter_enable(value);
+    }
+    atomic_set(&data->filter_enable, value);
+
+    geomagnetic_multi_unlock();
+
+    return count;
+}
+
+static ssize_t
+geomagnetic_filter_len_show(struct device *dev,
+        struct device_attribute *attr,
+        char *buf)
+{
+    struct input_dev *input_data = to_input_dev(dev);
+    struct geomagnetic_data *data = input_get_drvdata(input_data);
+
+    return sprintf(buf, "%d\n", atomic_read(&data->filter_len));
+}
+
+static ssize_t
+geomagnetic_filter_len_store(struct device *dev,
+        struct device_attribute *attr,
+        const char *buf,
+        size_t count)
+{
+    struct input_dev *input_data = to_input_dev(dev);
+    struct geomagnetic_data *data = input_get_drvdata(input_data);
+    int value;
+
+    if (hwdep_driver.set_filter_len == NULL) {
+        return -ENOTTY;
+    }
+
+    value = simple_strtoul(buf, NULL, 10);
+    if (geomagnetic_multi_lock() < 0) {
+        return count;
+    }
+
+    if (value) {
+        hwdep_driver.set_filter_len(value);
+    }
+    else {
+        hwdep_driver.set_filter_len(value);
+    }
+    atomic_set(&data->filter_len, value);
+
+    geomagnetic_multi_unlock();
+
+    return count;
+}
+
+static ssize_t
+geomagnetic_position_show(struct device *dev,
+        struct device_attribute *attr,
+        char *buf)
+{
+    if (hwdep_driver.get_position == NULL) {
+        return -ENOTTY;
+    }
+    return sprintf(buf, "%d\n", hwdep_driver.get_position());
+}
+
+static ssize_t
+geomagnetic_position_store(struct device *dev,
+        struct device_attribute *attr,
+        const char *buf,
+        size_t count)
+{
+    int value = simple_strtoul(buf, NULL, 10);
+
+    if (hwdep_driver.set_position == NULL) {
+        return -ENOTTY;
+    }
+    hwdep_driver.set_position(value);
+
+    return count;
+}
+
+static ssize_t
+geomagnetic_data_show(struct device *dev,
+        struct device_attribute *attr,
+        char *buf)
+{
+    struct input_dev *input_data = to_input_dev(dev);
+    struct geomagnetic_data *data = input_get_drvdata(input_data);
+    int rt;
+
+    rt = sprintf(buf, "%d %d %d\n",
+            atomic_read(&data->last_data[0]),
+            atomic_read(&data->last_data[1]),
+            atomic_read(&data->last_data[2]));
+
+    return rt;
+}
+
+static ssize_t
+geomagnetic_status_show(struct device *dev,
+        struct device_attribute *attr,
+        char *buf)
+{
+    struct input_dev *input_data = to_input_dev(dev);
+    struct geomagnetic_data *data = input_get_drvdata(input_data);
+    int rt;
+
+    rt = sprintf(buf, "%d\n", atomic_read(&data->last_status));
+
+    return rt;
+}
+
+static ssize_t
+geomagnetic_wake_store(struct device *dev,
+        struct device_attribute *attr,
+        const char *buf,
+        size_t count)
+{
+    struct input_dev *input_data = to_input_dev(dev);
+    struct geomagnetic_data *data = input_get_drvdata(input_data);
+    static int16_t cnt = 1;
+
+    input_report_abs(data->input_data, ABS_WAKE, cnt++);
+
+    return count;
+}
+
+#if DEBUG
+
+static int geomagnetic_suspend(struct i2c_client *client, pm_message_t mesg);
+static int geomagnetic_resume(struct i2c_client *client);
+
+static ssize_t
+geomagnetic_debug_suspend_show(struct device *dev,
+        struct device_attribute *attr, char *buf)
+{
+    struct input_dev *input = to_input_dev(dev);
+    struct geomagnetic_data *data = input_get_drvdata(input);
+
+    return sprintf(buf, "%d\n", data->suspend);
+}
+
+static ssize_t
+geomagnetic_debug_suspend_store(struct device *dev,
+        struct device_attribute *attr,
+        const char *buf, size_t count)
+{
+    unsigned long suspend = simple_strtoul(buf, NULL, 10);
+
+    if (suspend) {
+        pm_message_t msg;
+        memset(&msg, 0, sizeof(msg));
+        geomagnetic_suspend(this_client, msg);
+    } else {
+        geomagnetic_resume(this_client);
+    }
+
+    return count;
+}
+
+#endif /* DEBUG */
+
+static DEVICE_ATTR(delay, S_IRUGO|S_IWUSR|S_IWGRP,
+        geomagnetic_delay_show, geomagnetic_delay_store);
+static DEVICE_ATTR(enable, S_IRUGO|S_IWUSR|S_IWGRP,
+        geomagnetic_enable_show, geomagnetic_enable_store);
+static DEVICE_ATTR(filter_enable, S_IRUGO|S_IWUSR|S_IWGRP,
+        geomagnetic_filter_enable_show, geomagnetic_filter_enable_store);
+static DEVICE_ATTR(filter_len, S_IRUGO|S_IWUSR|S_IWGRP,
+        geomagnetic_filter_len_show, geomagnetic_filter_len_store);
+static DEVICE_ATTR(data, S_IRUGO, geomagnetic_data_show, NULL);
+static DEVICE_ATTR(status, S_IRUGO, geomagnetic_status_show, NULL);
+static DEVICE_ATTR(wake, S_IWUSR|S_IWGRP, NULL, geomagnetic_wake_store);
+static DEVICE_ATTR(position, S_IRUGO|S_IWUSR,
+        geomagnetic_position_show, geomagnetic_position_store);
+#if DEBUG
+static DEVICE_ATTR(debug_suspend, S_IRUGO|S_IWUSR,
+        geomagnetic_debug_suspend_show, geomagnetic_debug_suspend_store);
+#endif /* DEBUG */
+
+static struct attribute *geomagnetic_attributes[] = {
+    &dev_attr_delay.attr,
+    &dev_attr_enable.attr,
+    &dev_attr_filter_enable.attr,
+    &dev_attr_filter_len.attr,
+    &dev_attr_data.attr,
+    &dev_attr_status.attr,
+    &dev_attr_wake.attr,
+    &dev_attr_position.attr,
+#if DEBUG
+    &dev_attr_debug_suspend.attr,
+#endif /* DEBUG */
+    NULL
+};
+
+static struct attribute_group geomagnetic_attribute_group = {
+    .attrs = geomagnetic_attributes
+};
+
+
+static ssize_t
+geomagnetic_threshold_show(struct device *dev,
+        struct device_attribute *attr,
+        char *buf)
+{
+    struct input_dev *input_raw = to_input_dev(dev);
+    struct geomagnetic_data *data = input_get_drvdata(input_raw);
+    int threshold;
+
+    geomagnetic_multi_lock();
+
+    threshold = data->threshold;
+
+    geomagnetic_multi_unlock();
+
+    return sprintf(buf, "%d\n", threshold);
+}
+
+static ssize_t
+geomagnetic_threshold_store(struct device *dev,
+        struct device_attribute *attr,
+        const char *buf,
+        size_t count)
+{
+    struct input_dev *input_raw = to_input_dev(dev);
+    struct geomagnetic_data *data = input_get_drvdata(input_raw);
+    int value = simple_strtoul(buf, NULL, 10);
+
+    geomagnetic_multi_lock();
+
+    if (0 <= value && value <= 2) {
+        data->threshold = value;
+        input_report_abs(data->input_raw, ABS_RAW_THRESHOLD, value);
+    }
+
+    geomagnetic_multi_unlock();
+
+    return count;
+}
+
+static ssize_t
+geomagnetic_distortion_show(struct device *dev,
+        struct device_attribute *attr,
+        char *buf)
+{
+    struct input_dev *input_raw = to_input_dev(dev);
+    struct geomagnetic_data *data = input_get_drvdata(input_raw);
+    int rt;
+
+    geomagnetic_multi_lock();
+
+    rt = sprintf(buf, "%d %d %d\n",
+            data->distortion[0],
+            data->distortion[1],
+            data->distortion[2]);
+
+    geomagnetic_multi_unlock();
+
+    return rt;
+}
+
+static ssize_t
+geomagnetic_distortion_store(struct device *dev,
+        struct device_attribute *attr,
+        const char *buf,
+        size_t count)
+{
+    struct input_dev *input_raw = to_input_dev(dev);
+    struct geomagnetic_data *data = input_get_drvdata(input_raw);
+    int32_t distortion[3];
+    static int32_t val = 1;
+    int i;
+
+    geomagnetic_multi_lock();
+
+    sscanf(buf, "%d %d %d",
+            &distortion[0],
+            &distortion[1],
+            &distortion[2]);
+    if (distortion[0] > 0 && distortion[1] > 0 && distortion[2] > 0) {
+        for (i = 0; i < 3; i++) {
+            data->distortion[i] = distortion[i];
+        }
+        input_report_abs(data->input_raw, ABS_RAW_DISTORTION, val++);
+    }
+
+    geomagnetic_multi_unlock();
+
+    return count;
+}
+
+static ssize_t
+geomagnetic_shape_show(struct device *dev,
+        struct device_attribute *attr,
+        char *buf)
+{
+    struct input_dev *input_raw = to_input_dev(dev);
+    struct geomagnetic_data *data = input_get_drvdata(input_raw);
+    int shape;
+
+    geomagnetic_multi_lock();
+
+    shape = data->shape;
+
+    geomagnetic_multi_unlock();
+
+    return sprintf(buf, "%d\n", shape);
+}
+
+static ssize_t
+geomagnetic_shape_store(struct device *dev,
+        struct device_attribute *attr,
+        const char *buf,
+        size_t count)
+{
+    struct input_dev *input_raw = to_input_dev(dev);
+    struct geomagnetic_data *data = input_get_drvdata(input_raw);
+    int value = simple_strtoul(buf, NULL, 10);
+
+    geomagnetic_multi_lock();
+
+    if (0 <= value && value <= 1) {
+        data->shape = value;
+        input_report_abs(data->input_raw, ABS_RAW_SHAPE, value);
+    }
+
+    geomagnetic_multi_unlock();
+
+    return count;
+}
+
+static ssize_t
+geomagnetic_offsets_show(struct device *dev,
+        struct device_attribute *attr,
+        char *buf)
+{
+    struct input_dev *input_raw = to_input_dev(dev);
+    struct geomagnetic_data *data = input_get_drvdata(input_raw);
+    struct yas529_driver_state state;
+
+    geomagnetic_multi_lock();
+
+    state = data->driver_state;
+
+    geomagnetic_multi_unlock();
+
+    return sprintf(buf, "%u %u %u %d %d %d %d\n",
+            state.rough_offset[0],
+            state.rough_offset[1],
+            state.rough_offset[2],
+            state.fine_offset[0],
+            state.fine_offset[1],
+            state.fine_offset[2],
+            state.accuracy);
+}
+
+static ssize_t
+geomagnetic_offsets_store(struct device *dev,
+        struct device_attribute *attr,
+        const char *buf,
+        size_t count)
+{
+    struct input_dev *input_raw = to_input_dev(dev);
+    struct geomagnetic_data *data = input_get_drvdata(input_raw);
+    struct yas529_driver_state state;
+    uint32_t rough_offset[3];
+    int i;
+
+    geomagnetic_multi_lock();
+
+    sscanf(buf, "%u %u %u %d %d %d %d",
+            &rough_offset[0],
+            &rough_offset[1],
+            &rough_offset[2],
+            &state.fine_offset[0],
+            &state.fine_offset[1],
+            &state.fine_offset[2],
+            &state.accuracy);
+    for (i = 0; i < 3; i++) {
+        state.rough_offset[i] = (uint8_t)rough_offset[i];
+    }
+    hwdep_driver.ioctl(YAS529_IOC_SET_DRIVER_STATE,
+                        (unsigned long) &state);
+    data->driver_state = state;
+
+    geomagnetic_multi_unlock();
+
+    return count;
+}
+
+static DEVICE_ATTR(threshold, S_IRUGO|S_IWUSR,
+        geomagnetic_threshold_show, geomagnetic_threshold_store);
+static DEVICE_ATTR(distortion, S_IRUGO|S_IWUSR,
+        geomagnetic_distortion_show, geomagnetic_distortion_store);
+static DEVICE_ATTR(shape, S_IRUGO|S_IWUSR,
+        geomagnetic_shape_show, geomagnetic_shape_store);
+static DEVICE_ATTR(offsets, S_IRUGO|S_IWUSR,
+        geomagnetic_offsets_show, geomagnetic_offsets_store);
+
+static struct attribute *geomagnetic_raw_attributes[] = {
+    &dev_attr_threshold.attr,
+    &dev_attr_distortion.attr,
+    &dev_attr_shape.attr,
+    &dev_attr_offsets.attr,
+    NULL
+};
+
+static struct attribute_group geomagnetic_raw_attribute_group = {
+    .attrs = geomagnetic_raw_attributes
+};
+
+
+/* Interface Functions for Lower Layer */
+
+static void
+geomagnetic_input_work_func(struct work_struct *work)
+{
+    struct geomagnetic_data *data = container_of((struct delayed_work *)work,
+            struct geomagnetic_data, work);
+    int32_t magdata[3], raw[3];
+    uint32_t time_delay_ms = 100;
+    struct yas529_driver_state state;
+    int accuracy, rt, i;
+
+    if (hwdep_driver.measure == NULL || hwdep_driver.ioctl == NULL) {
+        return;
+    }
+
+    rt = hwdep_driver.measure(magdata, raw, &accuracy, &time_delay_ms);
+    if (rt < 0) {
+        MSE_ERR("measure failed[%d]\n", rt);
+    }
+
+    if (rt >= 0) {
+        if (rt & YAS529_REPORT_DATA) {
+            for (i = 0; i < 3; i++) {
+                atomic_set(&data->last_data[i], magdata[i]);
+            }
+            atomic_set(&data->last_status, accuracy);
+
+            /* report magnetic data in [nT] */
+            input_report_abs(data->input_data, ABS_X, magdata[0]);
+            input_report_abs(data->input_data, ABS_Y, magdata[1]);
+            input_report_abs(data->input_data, ABS_Z, magdata[2]);
+            input_report_abs(data->input_data, ABS_STATUS, accuracy);
+            input_sync(data->input_data);
+        }
+
+        if ((rt & YAS529_REPORT_OVERFLOW_OCCURED)
+                || (rt & YAS529_REPORT_ROUGH_OFFSET_CHANGED)
+                || (rt & YAS529_REPORT_FINE_OFFSET_CHANGED)) {
+            static uint16_t count = 1;
+            int code = 0;
+            int value = 0;
+
+            hwdep_driver.ioctl(YAS529_IOC_GET_DRIVER_STATE,
+                            (unsigned long) &state);
+            geomagnetic_multi_lock();
+            data->driver_state = state;
+            geomagnetic_multi_unlock();
+
+            /* report event */
+            code |= (rt & YAS529_REPORT_OVERFLOW_OCCURED);
+            code |= (rt & YAS529_REPORT_ROUGH_OFFSET_CHANGED);
+            code |= (rt & YAS529_REPORT_FINE_OFFSET_CHANGED);
+            value = (count++ << 16) | (code);
+            input_report_abs(data->input_raw, ABS_RAW_REPORT, value);
+        }
+
+        if (rt & YAS529_REPORT_CALIB) {
+            /* report raw magnetic data */
+            input_report_abs(data->input_raw, ABS_X, raw[0]);
+            input_report_abs(data->input_raw, ABS_Y, raw[1]);
+            input_report_abs(data->input_raw, ABS_Z, raw[2]);
+            input_sync(data->input_raw);
+        }
+    }
+    else {
+        time_delay_ms = 100;
+    }
+
+    if (time_delay_ms > 0) {
+        schedule_delayed_work(&data->work,
+                msecs_to_jiffies(time_delay_ms) + 1);
+    }
+    else {
+        schedule_delayed_work(&data->work, 0);
+    }
+}
+
+static int
+geomagnetic_suspend(struct i2c_client *client, pm_message_t mesg)
+{
+    struct geomagnetic_data *data = i2c_get_clientdata(client);
+
+    if (atomic_read(&data->enable)) {
+        cancel_delayed_work_sync(&data->work);
+    }
+#if DEBUG
+    data->suspend = 1;
+#endif
+
+    return 0;
+}
+
+static int
+geomagnetic_resume(struct i2c_client *client)
+{
+    struct geomagnetic_data *data = i2c_get_clientdata(client);
+
+    if (atomic_read(&data->enable)) {
+        schedule_delayed_work(&data->work, 0);
+    }
+
+#if DEBUG
+    data->suspend = 0;
+#endif
+
+    return 0;
+}
+
+static int
+geomagnetic_i2c_write(uint8_t slave, const uint8_t *buf, int len)
+{
+       int i = 0;
+       for(i = 0 ; i < len; i++)
+       {
+               I2CDMABuf_va[i] = buf[i];
+       }
+
+       if(len < 8)
+       {
+               this_client->addr = this_client->addr & I2C_MASK_FLAG;
+               //MSE_ERR("Sensor non-dma write timing is %x!\r\n", this_client->timing);
+               return i2c_master_send(this_client, buf, len);
+       }
+       else
+       {
+               this_client->addr = this_client->addr & I2C_MASK_FLAG | I2C_DMA_FLAG;
+               //MSE_ERR("Sensor dma timing is %x!\r\n", this_client->timing);
+               return i2c_master_send(this_client, I2CDMABuf_pa, len);
+       }    
+}
+
+static int
+geomagnetic_i2c_read(uint8_t slave, uint8_t *buf, int len)
+{
+       int i = 0, err = 0;
+
+       if(len < 8)
+       {
+               this_client->addr = this_client->addr & I2C_MASK_FLAG;
+               //MSE_ERR("Sensor non-dma read timing is %x!\r\n", this_client->timing);
+               return i2c_master_recv(this_client, buf, len);
+       }
+       else
+       {
+               this_client->addr = this_client->addr & I2C_MASK_FLAG | I2C_DMA_FLAG;
+               //MSE_ERR("Sensor dma read timing is %x!\r\n", this_client->timing);
+               err = i2c_master_recv(this_client, I2CDMABuf_pa, len);
+               
+           if(err < 0)
+           {
+                       return err;
+               }
+
+               for(i = 0; i < len; i++)
+               {
+                       buf[i] = I2CDMABuf_va[i];
+               }
+       }
+}
+
+static int
+geomagnetic_i2c_open(void)
+{
+    return 0;
+}
+
+static int
+geomagnetic_i2c_close(void)
+{
+    return 0;
+}
+
+static int
+geomagnetic_lock(void)
+{
+    struct geomagnetic_data *data = NULL;
+    int rt;
+
+    if (this_client == NULL) {
+        return -1;
+    }
+
+    data = i2c_get_clientdata(this_client);
+    rt = down_interruptible(&data->driver_lock);
+    if (rt < 0) {
+        up(&data->driver_lock);
+    }
+    return rt;
+}
+
+static int
+geomagnetic_unlock(void)
+{
+    struct geomagnetic_data *data = NULL;
+
+    if (this_client == NULL) {
+        return -1;
+    }
+
+    data = i2c_get_clientdata(this_client);
+    up(&data->driver_lock);
+    return 0;
+}
+
+static void
+geomagnetic_msleep(int ms)
+{
+    msleep(ms);
+}
+
+static void
+geomagnetic_current_time(int32_t *sec, int32_t *msec)
+{
+    struct timeval tv;
+
+    do_gettimeofday(&tv);
+
+    *sec = tv.tv_sec;
+    *msec = tv.tv_usec / 1000;
+}
+
+/*----------------------------------------------------------------------------*/
+static int yamaha529_open(struct inode *inode, struct file *file)
+{    
+       int ret = -1;
+       
+       yas529_msensor_set_enable(1);   // start sample polling
+       ret = nonseekable_open(inode, file);
+       
+       return ret;
+}
+/*----------------------------------------------------------------------------*/
+static int yamaha529_release(struct inode *inode, struct file *file)
+{      
+       yas529_msensor_set_enable(0); // stop sample polling
+       return 0;
+}
+
+static int
+ioctl_init(unsigned long args)
+{
+    /* nothing to do */
+    return 0;
+}
+
+static int
+ioctl_read_sensordata(unsigned long args)
+{
+    struct geomagnetic_data *data = i2c_get_clientdata(this_client);
+    char buf[64], *p;
+
+    p = (char *) args;
+    sprintf(buf, "%x %x %x",
+            atomic_read(&data->last_data[0]),
+            atomic_read(&data->last_data[1]),
+            atomic_read(&data->last_data[2]));
+    if (copy_to_user(p, buf, strlen(buf)+1)) {
+        return -EFAULT;
+    }
+
+    return 0;
+}
+
+static int
+ioctl_read_calidata(unsigned long args)
+{
+    struct geomagnetic_data *data = i2c_get_clientdata(this_client);
+    int buf[7], *p, i;
+
+    p = (int *) args;
+    if (is_valid_fine_offset(data->driver_state.fine_offset)) {
+        for (i = 0; i < 3; i++) {
+            buf[i] = data->driver_state.fine_offset[i] * 1000;
+        }
+    }
+    else {
+        for (i = 0; i < 3; i++) {
+            buf[i] = 0;
+        }
+    }
+    buf[3] = 0;
+    buf[4] = 0;
+    buf[5] = 0;
+    buf[6] = atomic_read(&data->last_status);
+    if (copy_to_user(p, buf, sizeof(buf))) {
+        return -EFAULT;
+    }
+
+    return 0;
+}
+
+/*----------------------------------------------------------------------------*/
+static int yamaha529_ioctl(struct inode *inode, struct file *file, unsigned int cmd,unsigned long arg)
+{
+       int valuebuf[4];
+       int calidata[7];
+       int controlbuf[10];
+       char strbuf[64];
+       void __user *data;
+       int retval=0;
+       int mode=0;
+
+       geomagnetic_multi_lock();
+//     MSE_FUN(f);
+
+       switch (cmd)
+       {
+               case MSENSOR_IOCTL_INIT:
+                       data = (void __user *) arg;
+                       retval = ioctl_init(data);         
+                       break;
+
+               case MSENSOR_IOCTL_SET_POSTURE:                 
+                       break;        
+
+               case MSENSOR_IOCTL_SET_CALIDATA:                        
+                       break;                                
+
+               case MSENSOR_IOCTL_READ_CHIPINFO:
+                       data = (void __user *) arg;
+                       if(data == NULL)
+                       {
+                               MSE_ERR("IO parameter pointer is NULL!\r\n");
+                               break;
+                       }               
+                       
+                       if(copy_to_user(data, "yas529", sizeof("yas529")))
+                       {
+                               retval = -EFAULT;
+                               goto err_out;
+                       }                
+                       break;
+
+               case MSENSOR_IOCTL_READ_SENSORDATA:
+                       
+                       retval = ioctl_read_sensordata(arg);
+                                      
+                       break;                
+
+               case MSENSOR_IOCTL_READ_POSTUREDATA:                               
+                       break;            
+
+               case MSENSOR_IOCTL_READ_CALIDATA:
+                       
+                       retval = ioctl_read_calidata(arg);
+                                      
+                       break;
+
+               case MSENSOR_IOCTL_READ_CONTROL:                                                   
+                       break;
+
+               case MSENSOR_IOCTL_SET_CONTROL:                   
+                       break;
+
+               case MSENSOR_IOCTL_SET_MODE:                                    
+                       break;
+                   
+               default:
+                       MSE_ERR("%s not supported = 0x%04x", __FUNCTION__, cmd);
+                       retval = -ENOIOCTLCMD;
+                       break;
+               }
+
+       geomagnetic_multi_unlock();
+       err_out:
+       return retval;
+
+}
+/*----------------------------------------------------------------------------*/
+static struct file_operations yamaha529_fops = {
+       .owner = THIS_MODULE,
+       .open = yamaha529_open,
+       .release = yamaha529_release,
+       .ioctl = yamaha529_ioctl,
+};
+/*----------------------------------------------------------------------------*/
+static struct miscdevice yamaha529_device = {
+    .minor = MISC_DYNAMIC_MINOR,
+    .name = "msensor",
+    .fops = &yamaha529_fops,
+};
+/*----------------------------------------------------------------------------*/
+int yamaha529_operate(void* self, uint32_t command, void* buff_in, int size_in,
+               void* buff_out, int size_out, int* actualout)
+{
+       int err = 0;
+       int value, sample_delay, i;
+       hwm_sensor_data* msensor_data;
+       struct geomagnetic_data *data = i2c_get_clientdata(this_client);
+
+//     geomagnetic_multi_lock();
+       MSE_FUN(f);
+       switch (command)
+       {
+               case SENSOR_DELAY:
+                       if((buff_in == NULL) || (size_in < sizeof(int)))
+                       {
+                               MSE_ERR("Set delay parameter error!\n");
+                               err = -EINVAL;
+                       }
+                       else
+                       {
+                               value = *(int *)buff_in;
+                               if(value <= 20)
+                               {
+                                       sample_delay = 20;
+                               }                               
+                               yas529_msensor_set_delay(sample_delay);
+                       }       
+                       break;
+
+               case SENSOR_ENABLE:
+                       if((buff_in == NULL) || (size_in < sizeof(int)))
+                       {
+                               MSE_ERR("Enable sensor parameter error!\n");
+                               err = -EINVAL;
+                       }
+                       else
+                       {
+                               value = *(int *)buff_in;                                
+                               yas529_msensor_set_enable(value);
+                               // TODO: turn device into standby or normal mode
+                       }
+                       break;
+
+               case SENSOR_GET_DATA:
+                       if((buff_out == NULL) || (size_out< sizeof(hwm_sensor_data)))
+                       {
+                               MSE_ERR("get sensor data parameter error!\n");
+                               err = -EINVAL;
+                       }
+                       else
+                       {
+                               
+
+                               msensor_data = (hwm_sensor_data *)buff_out;
+
+                               msensor_data->values[0] = atomic_read(&data->last_data[0]),
+                               msensor_data->values[1] = atomic_read(&data->last_data[1]),
+                               msensor_data->values[2] = atomic_read(&data->last_data[2]),
+                               msensor_data->status = atomic_read(&data->last_status);
+                                                               
+                               msensor_data->value_divide = 1000;
+/*
+                               switch (status)
+                               {
+                           case 1: case 2:
+                               msensor_data->status = SENSOR_STATUS_ACCURACY_HIGH;
+                               break;
+                           case 3:
+                               msensor_data->status = SENSOR_STATUS_ACCURACY_MEDIUM;
+                               break;
+                           case 4:
+                               msensor_data->status = SENSOR_STATUS_ACCURACY_LOW;
+                               break;
+                           default:        
+                               msensor_data->status = SENSOR_STATUS_UNRELIABLE;
+                               break;
+                               }
+*/                             
+                               
+                       }
+                       break;
+               default:
+                       MSE_ERR("msensor operate function no this parameter %d!\n", command);
+                       err = -1;
+                       break;
+       }
+
+//     geomagnetic_multi_unlock();
+       
+       return err;
+
+}
+
+/*
+int yamaha529_orientation_operate(void* self, uint32_t command, void* buff_in, int size_in,
+               void* buff_out, int size_out, int* actualout)
+{
+       int err = 0;
+       int value, sample_delay, status;
+//     hwm_sensor_data* osensor_data;
+
+/*     
+       //MSE_FUN(f);
+       switch (command)
+       {
+               case SENSOR_DELAY:
+                       if((buff_in == NULL) || (size_in < sizeof(int)))
+                       {
+                               MSE_ERR("Set delay parameter error!\n");
+                               err = -EINVAL;
+                       }
+                       else
+                       {
+                               value = *(int *)buff_in;
+                               if(value <= 20)
+                               {
+                                       sample_delay = 20;
+                               }
+                               
+                               
+                               yamaha529mid_data.controldata[0] = sample_delay;  // Loop Delay
+                       }       
+                       break;
+
+               case SENSOR_ENABLE:
+                       if((buff_in == NULL) || (size_in < sizeof(int)))
+                       {
+                               MSE_ERR("Enable sensor parameter error!\n");
+                               err = -EINVAL;
+                       }
+                       else
+                       {
+                               value = *(int *)buff_in;
+                               read_lock(&yamaha529mid_data.ctrllock);
+                               if(value = 1)
+                               {
+                                       yamaha529mid_data.controldata[7] |= SENSOR_ORIENTATION;
+                               }
+                               else
+                               {
+                                       yamaha529mid_data.controldata[7] &= ~SENSOR_ORIENTATION;
+                               }                               
+                               read_unlock(&yamaha529mid_data.ctrllock);
+                               // Do nothing
+                       }
+                       break;
+
+               case SENSOR_GET_DATA:
+                       if((buff_out == NULL) || (size_out< sizeof(hwm_sensor_data)))
+                       {
+                               MSE_ERR("get sensor data parameter error!\n");
+                               err = -EINVAL;
+                       }
+                       else
+                       {
+                               osensor_data = (hwm_sensor_data *)buff_out;
+                               read_lock(&yamaha529mid_data.datalock);
+                               osensor_data->values[0] = yamaha529mid_data.yaw;
+                               osensor_data->values[1] = yamaha529mid_data.pitch;
+                               osensor_data->values[2] = yamaha529mid_data.roll;
+                               status = yamaha529mid_data.mag_status;
+                               read_unlock(&yamaha529mid_data.datalock); 
+                               
+                               
+                               osensor_data->value_divide = ORIENTATION_ACCURACY_RATE;                         
+                       }
+
+                       switch (status)
+               {
+                   case 1: case 2:
+                       osensor_data->status = SENSOR_STATUS_ACCURACY_HIGH;
+                       break;
+                   case 3:
+                       osensor_data->status = SENSOR_STATUS_ACCURACY_MEDIUM;
+                       break;
+                   case 4:
+                       osensor_data->status = SENSOR_STATUS_ACCURACY_LOW;
+                       break;
+                   default:        
+                       osensor_data->status = SENSOR_STATUS_UNRELIABLE;
+                       break;    
+               }
+                       break;
+               default:
+                       MSE_ERR("gsensor operate function no this parameter %d!\n", command);
+                       err = -1;
+                       break;
+       }
+       
+       return err;
+
+}
+*/
+
+
+
+
+static int yamaha529_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+       struct geomagnetic_data *data = NULL;
+    struct input_dev *input_data = NULL, *input_raw = NULL;
+    int rt, sysfs_created = 0, sysfs_raw_created = 0;
+    int data_registered = 0, raw_registered = 0, i;
+       struct hwmsen_object sobj_m, sobj_o;
+
+    i2c_set_clientdata(client, NULL);
+    data = kzalloc(sizeof(struct geomagnetic_data), GFP_KERNEL);
+    if (data == NULL) {
+        rt = -ENOMEM;
+        goto err;
+    }
+
+       data->hw = get_cust_mag_hw();
+       
+    data->threshold = YAS529_DEFAULT_THRESHOLD;
+    for (i = 0; i < 3; i++) {
+        data->distortion[i] = YAS529_DEFAULT_DISTORTION;
+    }
+    data->shape = YAS529_DEFAULT_SHAPE;
+    atomic_set(&data->enable, 0);
+    for (i = 0; i < 3; i++) {
+        atomic_set(&data->last_data[i], 0);
+    }
+    atomic_set(&data->last_status, 0);
+    INIT_DELAYED_WORK(&data->work, geomagnetic_input_work_func);
+    init_MUTEX(&data->driver_lock);
+    init_MUTEX(&data->multi_lock);
+
+       I2CDMABuf_va = (u8 *)dma_alloc_coherent(NULL, 4096, &I2CDMABuf_pa, GFP_KERNEL);
+    if(!I2CDMABuf_va)
+       {
+               MSE_ERR("Allocate DMA I2C Buffer failed!\n");
+               goto err;
+       }
+
+    input_data = input_allocate_device();
+    if (input_data == NULL) {
+        rt = -ENOMEM;
+        printk(KERN_ERR
+               "geomagnetic_probe: Failed to allocate input_data device\n");
+        goto err;
+    }
+
+    input_data->name = GEOMAGNETIC_INPUT_NAME;
+    input_data->id.bustype = BUS_I2C;
+    set_bit(EV_ABS, input_data->evbit);
+    input_set_capability(input_data, EV_ABS, ABS_X);
+    input_set_capability(input_data, EV_ABS, ABS_Y);
+    input_set_capability(input_data, EV_ABS, ABS_Z);
+    input_set_capability(input_data, EV_ABS, ABS_STATUS);
+    input_set_capability(input_data, EV_ABS, ABS_WAKE);
+    input_data->dev.parent = &client->dev;
+
+    rt = input_register_device(input_data);
+    if (rt) {
+        printk(KERN_ERR
+               "geomagnetic_probe: Unable to register input_data device: %s\n",
+               input_data->name);
+        goto err;
+    }
+    data_registered = 1;
+
+    rt = sysfs_create_group(&input_data->dev.kobj,
+            &geomagnetic_attribute_group);
+    if (rt) {
+        printk(KERN_ERR
+               "geomagnetic_probe: sysfs_create_group failed[%s]\n",
+               input_data->name);
+        goto err;
+    }
+    sysfs_created = 1;
+
+    input_raw = input_allocate_device();
+    if (input_raw == NULL) {
+        rt = -ENOMEM;
+        printk(KERN_ERR
+               "geomagnetic_probe: Failed to allocate input_raw device\n");
+        goto err;
+    }
+
+    input_raw->name = GEOMAGNETIC_INPUT_RAW_NAME;
+    input_raw->id.bustype = BUS_I2C;
+    set_bit(EV_ABS, input_raw->evbit);
+    input_set_capability(input_raw, EV_ABS, ABS_X);
+    input_set_capability(input_raw, EV_ABS, ABS_Y);
+    input_set_capability(input_raw, EV_ABS, ABS_Z);
+    input_set_capability(input_raw, EV_ABS, ABS_RAW_DISTORTION);
+    input_set_capability(input_raw, EV_ABS, ABS_RAW_THRESHOLD);
+    input_set_capability(input_raw, EV_ABS, ABS_RAW_SHAPE);
+    input_set_capability(input_raw, EV_ABS, ABS_RAW_REPORT);
+    input_raw->dev.parent = &client->dev;
+
+    rt = input_register_device(input_raw);
+    if (rt) {
+        printk(KERN_ERR
+               "geomagnetic_probe: Unable to register input_raw device: %s\n",
+               input_raw->name);
+        goto err;
+    }
+    raw_registered = 1;
+
+    rt = sysfs_create_group(&input_raw->dev.kobj,
+            &geomagnetic_raw_attribute_group);
+    if (rt) {
+        printk(KERN_ERR
+               "geomagnetic_probe: sysfs_create_group failed[%s]\n",
+               input_data->name);
+        goto err;
+    }
+    sysfs_raw_created = 1;     
+
+    this_client = client;
+       //client->addr = client->addr & I2C_MASK_FLAG | I2C_DMA_FLAG;
+       client->timing = 200;
+    data->input_raw = input_raw;
+    data->input_data = input_data;
+       data->client = client;
+    input_set_drvdata(input_data, data);
+    input_set_drvdata(input_raw, data);
+    i2c_set_clientdata(client, data);
+
+    if ((rt = geomagnetic_driver_init(&hwdep_driver)) < 0) {
+        printk(KERN_ERR "geomagnetic_driver_init failed[%d]\n", rt);
+        goto err;
+    }
+    if(hwdep_driver.init != NULL) {
+        if ((rt = hwdep_driver.init()) < 0) {
+            printk(KERN_ERR "hwdep_driver.init() failed[%d]\n", rt);
+            goto err;
+        }
+    }
+    if (hwdep_driver.set_position != NULL) {
+        if (hwdep_driver.set_position(data->hw->direction) < 0) {
+            printk(KERN_ERR "hwdep_driver.set_position() failed[%d]\n", rt);
+            goto err;
+        }
+    }
+    if (hwdep_driver.ioctl != NULL) {
+        if (hwdep_driver.ioctl(YAS529_IOC_GET_DRIVER_STATE,
+                    (unsigned long)&data->driver_state) < 0) {
+            printk(KERN_ERR "hwdep_driver get_driver_state failed\n");
+            goto err;
+        }
+    }
+    if (hwdep_driver.get_delay != NULL) {
+        atomic_set(&data->delay, hwdep_driver.get_delay());
+    }
+    if (hwdep_driver.get_filter_enable != NULL) {
+        atomic_set(&data->filter_enable, hwdep_driver.get_filter_enable());
+    }
+    if (hwdep_driver.get_filter_len != NULL) {
+        atomic_set(&data->filter_len, hwdep_driver.get_filter_len());
+    }
+
+       if(rt = misc_register(&yamaha529_device))
+       {
+               MSE_ERR("yamaha529_device register failed\n");
+               goto err;
+       }    
+
+       sobj_m.self = data;
+    sobj_m.polling = 1;
+    sobj_m.sensor_operate = yamaha529_operate;
+       if(rt = hwmsen_attach(ID_MAGNETIC, &sobj_m))
+       {
+               MSE_ERR("attach fail = %d\n", rt);
+               goto err;
+       }
+
+       /*
+       sobj_o.self = data;
+    sobj_o.polling = 1;
+    sobj_o.sensor_operate = yamaha529_orientation_operate;
+       if(rt = hwmsen_attach(ID_ORIENTATION, &sobj_o))
+       {
+               MSE_ERR("attach fail = %d\n", rt);
+               goto err;
+       }
+       */
+    return 0;
+
+err:
+    if (data != NULL) {
+        if (input_raw != NULL) {
+            if (sysfs_raw_created) {
+                sysfs_remove_group(&input_raw->dev.kobj,
+                        &geomagnetic_raw_attribute_group);
+            }
+            if (raw_registered) {
+                input_unregister_device(input_raw);
+            }
+            else {
+                input_free_device(input_raw);
+            }
+        }
+        if (input_data != NULL) {
+            if (sysfs_created) {
+                sysfs_remove_group(&input_data->dev.kobj,
+                        &geomagnetic_attribute_group);
+            }
+            if (data_registered) {
+                input_unregister_device(input_data);
+            }
+            else {
+                input_free_device(input_data);
+            }
+        }
+        kfree(data);
+    }
+
+    return rt;
+}
+
+/*----------------------------------------------------------------------------*/
+static struct i2c_driver yamaha529_i2c_driver = {
+    .driver = {
+        .owner = THIS_MODULE, 
+        .name  = YAMAHA529_DEV_NAME,
+    },
+       .probe      = yamaha529_i2c_probe,
+       .remove     = yamaha529_i2c_remove,
+       .detect     = yamaha529_i2c_detect,
+       .suspend    = geomagnetic_suspend,
+       .resume     = geomagnetic_resume,
+
+       .id_table = yamaha529_i2c_id,
+       .address_data = &yamaha529_addr_data,
+};
+/*----------------------------------------------------------------------------*/
+static atomic_t dev_open_count;
+/*----------------------------------------------------------------------------*/
+
+
+static int yamaha529_i2c_detect(struct i2c_client *client, int kind, struct i2c_board_info *info) 
+{    
+       strcpy(info->type, YAMAHA529_DEV_NAME);
+       return 0;
+}
+
+
+
+/*----------------------------------------------------------------------------*/
+static int yamaha529_i2c_remove(struct i2c_client *client)
+{
+       int err;
+       struct geomagnetic_data *data = i2c_get_clientdata(client);
+
+       if(data != NULL)
+       {
+               geomagnetic_disable(data);
+               if(hwdep_driver.term != NULL)
+               {
+                       hwdep_driver.term();
+               }
+
+               input_unregister_device(data->input_raw);
+        sysfs_remove_group(&data->input_data->dev.kobj,
+                &geomagnetic_attribute_group);
+        sysfs_remove_group(&data->input_raw->dev.kobj,
+                &geomagnetic_raw_attribute_group);
+        input_unregister_device(data->input_data);
+        kfree(data);
+               kfree(data);
+    }
+
+       if(I2CDMABuf_va)
+       {
+               dma_free_coherent(NULL, 4096, I2CDMABuf_va, I2CDMABuf_pa);
+               I2CDMABuf_va = NULL;
+               I2CDMABuf_pa = 0;
+       }
+        
+       this_client = NULL;
+       i2c_unregister_device(client);
+       kfree(i2c_get_clientdata(client));      
+       misc_deregister(&yamaha529_device);    
+       return 0;
+}
+/*----------------------------------------------------------------------------*/
+static int yamaha_probe(struct platform_device *pdev) 
+{
+       struct mag_hw *hw = get_cust_mag_hw();
+
+       //yamaha529_power(hw, 1);    
+       yamaha529_force[0] = hw->i2c_num;
+       if(i2c_add_driver(&yamaha529_i2c_driver))
+       {
+               MSE_ERR("add driver error\n");
+               return -1;
+       } 
+       return 0;
+}
+/*----------------------------------------------------------------------------*/
+static int yamaha_remove(struct platform_device *pdev)
+{
+       struct mag_hw *hw = get_cust_mag_hw();
+
+       MSE_FUN();    
+       //yamaha529_power(hw, 0);    
+       atomic_set(&dev_open_count, 0);  
+       i2c_del_driver(&yamaha529_i2c_driver);
+       return 0;
+}
+/*----------------------------------------------------------------------------*/
+#if 0
+static struct platform_driver yamaha_sensor_driver = {
+       .probe      = yamaha_probe,
+       .remove     = yamaha_remove,    
+       .driver     = {
+               .name  = "msensor",
+               .owner = THIS_MODULE,
+       }
+};
+#endif
+
+#ifdef CONFIG_OF
+static const struct of_device_id yamaha_of_match[] = {
+       { .compatible = "mediatek,msensor", },
+       {},
+};
+#endif
+
+static struct platform_driver yamaha_sensor_driver =
+{
+       .probe      = yamaha_probe,
+       .remove     = yamaha_remove,    
+       .driver     = 
+       {
+               .name = "msensor",
+        #ifdef CONFIG_OF
+               .of_match_table = yamaha_of_match,
+               #endif
+       }
+};
+
+/*----------------------------------------------------------------------------*/
+static int __init yamaha529_init(void)
+{
+       MSE_FUN();
+       if(platform_driver_register(&yamaha_sensor_driver))
+       {
+               MSE_ERR("failed to register driver");
+               return -ENODEV;
+       }
+       return 0;    
+}
+/*----------------------------------------------------------------------------*/
+static void __exit yamaha529_exit(void)
+{
+       MSE_FUN();
+       platform_driver_unregister(&yamaha_sensor_driver);
+}
+/*----------------------------------------------------------------------------*/
+
+
+module_init(yamaha529_init);
+module_exit(yamaha529_exit);
+
+MODULE_AUTHOR("Yamaha Corporation");
+MODULE_DESCRIPTION("YAS529 Geomagnetic Sensor Driver");
+MODULE_LICENSE( "GPL" );
+MODULE_VERSION("1.2.0");
+