123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608 |
- /*
- * Copyright (C) 2014, Samsung Electronics Co. Ltd. All Rights Reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT 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 "ssp.h"
- #define SSP_DEV (&data->spi->dev)
- #define SSP_GET_MESSAGE_TYPE(data) (data & (3 << SSP_RW))
- /*
- * SSP -> AP Instruction
- * They tell what packet type can be expected. In the future there will
- * be less of them. BYPASS means common sensor packets with accel, gyro,
- * hrm etc. data. LIBRARY and META are mock-up's for now.
- */
- #define SSP_MSG2AP_INST_BYPASS_DATA 0x37
- #define SSP_MSG2AP_INST_LIBRARY_DATA 0x01
- #define SSP_MSG2AP_INST_DEBUG_DATA 0x03
- #define SSP_MSG2AP_INST_BIG_DATA 0x04
- #define SSP_MSG2AP_INST_META_DATA 0x05
- #define SSP_MSG2AP_INST_TIME_SYNC 0x06
- #define SSP_MSG2AP_INST_RESET 0x07
- #define SSP_UNIMPLEMENTED -1
- struct ssp_msg_header {
- u8 cmd;
- __le16 length;
- __le16 options;
- __le32 data;
- } __attribute__((__packed__));
- struct ssp_msg {
- u16 length;
- u16 options;
- struct list_head list;
- struct completion *done;
- struct ssp_msg_header *h;
- char *buffer;
- };
- static const int ssp_offset_map[SSP_SENSOR_MAX] = {
- [SSP_ACCELEROMETER_SENSOR] = SSP_ACCELEROMETER_SIZE +
- SSP_TIME_SIZE,
- [SSP_GYROSCOPE_SENSOR] = SSP_GYROSCOPE_SIZE +
- SSP_TIME_SIZE,
- [SSP_GEOMAGNETIC_UNCALIB_SENSOR] = SSP_UNIMPLEMENTED,
- [SSP_GEOMAGNETIC_RAW] = SSP_UNIMPLEMENTED,
- [SSP_GEOMAGNETIC_SENSOR] = SSP_UNIMPLEMENTED,
- [SSP_PRESSURE_SENSOR] = SSP_UNIMPLEMENTED,
- [SSP_GESTURE_SENSOR] = SSP_UNIMPLEMENTED,
- [SSP_PROXIMITY_SENSOR] = SSP_UNIMPLEMENTED,
- [SSP_TEMPERATURE_HUMIDITY_SENSOR] = SSP_UNIMPLEMENTED,
- [SSP_LIGHT_SENSOR] = SSP_UNIMPLEMENTED,
- [SSP_PROXIMITY_RAW] = SSP_UNIMPLEMENTED,
- [SSP_ORIENTATION_SENSOR] = SSP_UNIMPLEMENTED,
- [SSP_STEP_DETECTOR] = SSP_UNIMPLEMENTED,
- [SSP_SIG_MOTION_SENSOR] = SSP_UNIMPLEMENTED,
- [SSP_GYRO_UNCALIB_SENSOR] = SSP_UNIMPLEMENTED,
- [SSP_GAME_ROTATION_VECTOR] = SSP_UNIMPLEMENTED,
- [SSP_ROTATION_VECTOR] = SSP_UNIMPLEMENTED,
- [SSP_STEP_COUNTER] = SSP_UNIMPLEMENTED,
- [SSP_BIO_HRM_RAW] = SSP_BIO_HRM_RAW_SIZE +
- SSP_TIME_SIZE,
- [SSP_BIO_HRM_RAW_FAC] = SSP_BIO_HRM_RAW_FAC_SIZE +
- SSP_TIME_SIZE,
- [SSP_BIO_HRM_LIB] = SSP_BIO_HRM_LIB_SIZE +
- SSP_TIME_SIZE,
- };
- #define SSP_HEADER_SIZE (sizeof(struct ssp_msg_header))
- #define SSP_HEADER_SIZE_ALIGNED (ALIGN(SSP_HEADER_SIZE, 4))
- static struct ssp_msg *ssp_create_msg(u8 cmd, u16 len, u16 opt, u32 data)
- {
- struct ssp_msg_header h;
- struct ssp_msg *msg;
- msg = kzalloc(sizeof(*msg), GFP_KERNEL);
- if (!msg)
- return NULL;
- h.cmd = cmd;
- h.length = cpu_to_le16(len);
- h.options = cpu_to_le16(opt);
- h.data = cpu_to_le32(data);
- msg->buffer = kzalloc(SSP_HEADER_SIZE_ALIGNED + len,
- GFP_KERNEL | GFP_DMA);
- if (!msg->buffer) {
- kfree(msg);
- return NULL;
- }
- msg->length = len;
- msg->options = opt;
- memcpy(msg->buffer, &h, SSP_HEADER_SIZE);
- return msg;
- }
- /*
- * It is a bit heavy to do it this way but often the function is used to compose
- * the message from smaller chunks which are placed on the stack. Often the
- * chunks are small so memcpy should be optimalized.
- */
- static inline void ssp_fill_buffer(struct ssp_msg *m, unsigned int offset,
- const void *src, unsigned int len)
- {
- memcpy(&m->buffer[SSP_HEADER_SIZE_ALIGNED + offset], src, len);
- }
- static inline void ssp_get_buffer(struct ssp_msg *m, unsigned int offset,
- void *dest, unsigned int len)
- {
- memcpy(dest, &m->buffer[SSP_HEADER_SIZE_ALIGNED + offset], len);
- }
- #define SSP_GET_BUFFER_AT_INDEX(m, index) \
- (m->buffer[SSP_HEADER_SIZE_ALIGNED + index])
- #define SSP_SET_BUFFER_AT_INDEX(m, index, val) \
- (m->buffer[SSP_HEADER_SIZE_ALIGNED + index] = val)
- static void ssp_clean_msg(struct ssp_msg *m)
- {
- kfree(m->buffer);
- kfree(m);
- }
- static int ssp_print_mcu_debug(char *data_frame, int *data_index,
- int received_len)
- {
- int length = data_frame[(*data_index)++];
- if (length > received_len - *data_index || length <= 0) {
- ssp_dbg("[SSP]: MSG From MCU-invalid debug length(%d/%d)\n",
- length, received_len);
- return length ? length : -EPROTO;
- }
- ssp_dbg("[SSP]: MSG From MCU - %s\n", &data_frame[*data_index]);
- *data_index += length;
- return 0;
- }
- /*
- * It was designed that way - additional lines to some kind of handshake,
- * please do not ask why - only the firmware guy can know it.
- */
- static int ssp_check_lines(struct ssp_data *data, bool state)
- {
- int delay_cnt = 0;
- gpio_set_value_cansleep(data->ap_mcu_gpio, state);
- while (gpio_get_value_cansleep(data->mcu_ap_gpio) != state) {
- usleep_range(3000, 3500);
- if (data->shut_down || delay_cnt++ > 500) {
- dev_err(SSP_DEV, "%s:timeout, hw ack wait fail %d\n",
- __func__, state);
- if (!state)
- gpio_set_value_cansleep(data->ap_mcu_gpio, 1);
- return -ETIMEDOUT;
- }
- }
- return 0;
- }
- static int ssp_do_transfer(struct ssp_data *data, struct ssp_msg *msg,
- struct completion *done, int timeout)
- {
- int status;
- /*
- * check if this is a short one way message or the whole transfer has
- * second part after an interrupt
- */
- const bool use_no_irq = msg->length == 0;
- if (data->shut_down)
- return -EPERM;
- msg->done = done;
- mutex_lock(&data->comm_lock);
- status = ssp_check_lines(data, false);
- if (status < 0)
- goto _error_locked;
- status = spi_write(data->spi, msg->buffer, SSP_HEADER_SIZE);
- if (status < 0) {
- gpio_set_value_cansleep(data->ap_mcu_gpio, 1);
- dev_err(SSP_DEV, "%s spi_write fail\n", __func__);
- goto _error_locked;
- }
- if (!use_no_irq) {
- mutex_lock(&data->pending_lock);
- list_add_tail(&msg->list, &data->pending_list);
- mutex_unlock(&data->pending_lock);
- }
- status = ssp_check_lines(data, true);
- if (status < 0) {
- if (!use_no_irq) {
- mutex_lock(&data->pending_lock);
- list_del(&msg->list);
- mutex_unlock(&data->pending_lock);
- }
- goto _error_locked;
- }
- mutex_unlock(&data->comm_lock);
- if (!use_no_irq && done)
- if (wait_for_completion_timeout(done,
- msecs_to_jiffies(timeout)) ==
- 0) {
- mutex_lock(&data->pending_lock);
- list_del(&msg->list);
- mutex_unlock(&data->pending_lock);
- data->timeout_cnt++;
- return -ETIMEDOUT;
- }
- return 0;
- _error_locked:
- mutex_unlock(&data->comm_lock);
- data->timeout_cnt++;
- return status;
- }
- static inline int ssp_spi_sync_command(struct ssp_data *data,
- struct ssp_msg *msg)
- {
- return ssp_do_transfer(data, msg, NULL, 0);
- }
- static int ssp_spi_sync(struct ssp_data *data, struct ssp_msg *msg,
- int timeout)
- {
- DECLARE_COMPLETION_ONSTACK(done);
- if (WARN_ON(!msg->length))
- return -EPERM;
- return ssp_do_transfer(data, msg, &done, timeout);
- }
- static int ssp_handle_big_data(struct ssp_data *data, char *dataframe, int *idx)
- {
- /* mock-up, it will be changed with adding another sensor types */
- *idx += 8;
- return 0;
- }
- static int ssp_parse_dataframe(struct ssp_data *data, char *dataframe, int len)
- {
- int idx, sd;
- struct timespec ts;
- struct ssp_sensor_data *spd;
- struct iio_dev **indio_devs = data->sensor_devs;
- getnstimeofday(&ts);
- for (idx = 0; idx < len;) {
- switch (dataframe[idx++]) {
- case SSP_MSG2AP_INST_BYPASS_DATA:
- sd = dataframe[idx++];
- if (sd < 0 || sd >= SSP_SENSOR_MAX) {
- dev_err(SSP_DEV,
- "Mcu data frame1 error %d\n", sd);
- return -EPROTO;
- }
- if (indio_devs[sd]) {
- spd = iio_priv(indio_devs[sd]);
- if (spd->process_data)
- spd->process_data(indio_devs[sd],
- &dataframe[idx],
- data->timestamp);
- } else {
- dev_err(SSP_DEV, "no client for frame\n");
- }
- idx += ssp_offset_map[sd];
- break;
- case SSP_MSG2AP_INST_DEBUG_DATA:
- sd = ssp_print_mcu_debug(dataframe, &idx, len);
- if (sd) {
- dev_err(SSP_DEV,
- "Mcu data frame3 error %d\n", sd);
- return sd;
- }
- break;
- case SSP_MSG2AP_INST_LIBRARY_DATA:
- idx += len;
- break;
- case SSP_MSG2AP_INST_BIG_DATA:
- ssp_handle_big_data(data, dataframe, &idx);
- break;
- case SSP_MSG2AP_INST_TIME_SYNC:
- data->time_syncing = true;
- break;
- case SSP_MSG2AP_INST_RESET:
- ssp_queue_ssp_refresh_task(data, 0);
- break;
- }
- }
- if (data->time_syncing)
- data->timestamp = ts.tv_sec * 1000000000ULL + ts.tv_nsec;
- return 0;
- }
- /* threaded irq */
- int ssp_irq_msg(struct ssp_data *data)
- {
- bool found = false;
- char *buffer;
- u8 msg_type;
- int ret;
- u16 length, msg_options;
- struct ssp_msg *msg, *n;
- ret = spi_read(data->spi, data->header_buffer, SSP_HEADER_BUFFER_SIZE);
- if (ret < 0) {
- dev_err(SSP_DEV, "header read fail\n");
- return ret;
- }
- length = le16_to_cpu(data->header_buffer[1]);
- msg_options = le16_to_cpu(data->header_buffer[0]);
- if (length == 0) {
- dev_err(SSP_DEV, "length received from mcu is 0\n");
- return -EINVAL;
- }
- msg_type = SSP_GET_MESSAGE_TYPE(msg_options);
- switch (msg_type) {
- case SSP_AP2HUB_READ:
- case SSP_AP2HUB_WRITE:
- /*
- * this is a small list, a few elements - the packets can be
- * received with no order
- */
- mutex_lock(&data->pending_lock);
- list_for_each_entry_safe(msg, n, &data->pending_list, list) {
- if (msg->options == msg_options) {
- list_del(&msg->list);
- found = true;
- break;
- }
- }
- if (!found) {
- /*
- * here can be implemented dead messages handling
- * but the slave should not send such ones - it is to
- * check but let's handle this
- */
- buffer = kmalloc(length, GFP_KERNEL | GFP_DMA);
- if (!buffer) {
- ret = -ENOMEM;
- goto _unlock;
- }
- /* got dead packet so it is always an error */
- ret = spi_read(data->spi, buffer, length);
- if (ret >= 0)
- ret = -EPROTO;
- kfree(buffer);
- dev_err(SSP_DEV, "No match error %x\n",
- msg_options);
- goto _unlock;
- }
- if (msg_type == SSP_AP2HUB_READ)
- ret = spi_read(data->spi,
- &msg->buffer[SSP_HEADER_SIZE_ALIGNED],
- msg->length);
- if (msg_type == SSP_AP2HUB_WRITE) {
- ret = spi_write(data->spi,
- &msg->buffer[SSP_HEADER_SIZE_ALIGNED],
- msg->length);
- if (msg_options & SSP_AP2HUB_RETURN) {
- msg->options =
- SSP_AP2HUB_READ | SSP_AP2HUB_RETURN;
- msg->length = 1;
- list_add_tail(&msg->list, &data->pending_list);
- goto _unlock;
- }
- }
- if (msg->done)
- if (!completion_done(msg->done))
- complete(msg->done);
- _unlock:
- mutex_unlock(&data->pending_lock);
- break;
- case SSP_HUB2AP_WRITE:
- buffer = kzalloc(length, GFP_KERNEL | GFP_DMA);
- if (!buffer)
- return -ENOMEM;
- ret = spi_read(data->spi, buffer, length);
- if (ret < 0) {
- dev_err(SSP_DEV, "spi read fail\n");
- kfree(buffer);
- break;
- }
- ret = ssp_parse_dataframe(data, buffer, length);
- kfree(buffer);
- break;
- default:
- dev_err(SSP_DEV, "unknown msg type\n");
- return -EPROTO;
- }
- return ret;
- }
- void ssp_clean_pending_list(struct ssp_data *data)
- {
- struct ssp_msg *msg, *n;
- mutex_lock(&data->pending_lock);
- list_for_each_entry_safe(msg, n, &data->pending_list, list) {
- list_del(&msg->list);
- if (msg->done)
- if (!completion_done(msg->done))
- complete(msg->done);
- }
- mutex_unlock(&data->pending_lock);
- }
- int ssp_command(struct ssp_data *data, char command, int arg)
- {
- int ret;
- struct ssp_msg *msg;
- msg = ssp_create_msg(command, 0, SSP_AP2HUB_WRITE, arg);
- if (!msg)
- return -ENOMEM;
- ssp_dbg("%s - command 0x%x %d\n", __func__, command, arg);
- ret = ssp_spi_sync_command(data, msg);
- ssp_clean_msg(msg);
- return ret;
- }
- int ssp_send_instruction(struct ssp_data *data, u8 inst, u8 sensor_type,
- u8 *send_buf, u8 length)
- {
- int ret;
- struct ssp_msg *msg;
- if (data->fw_dl_state == SSP_FW_DL_STATE_DOWNLOADING) {
- dev_err(SSP_DEV, "%s - Skip Inst! DL state = %d\n",
- __func__, data->fw_dl_state);
- return -EBUSY;
- } else if (!(data->available_sensors & BIT(sensor_type)) &&
- (inst <= SSP_MSG2SSP_INST_CHANGE_DELAY)) {
- dev_err(SSP_DEV, "%s - Bypass Inst Skip! - %u\n",
- __func__, sensor_type);
- return -EIO; /* just fail */
- }
- msg = ssp_create_msg(inst, length + 2, SSP_AP2HUB_WRITE, 0);
- if (!msg)
- return -ENOMEM;
- ssp_fill_buffer(msg, 0, &sensor_type, 1);
- ssp_fill_buffer(msg, 1, send_buf, length);
- ssp_dbg("%s - Inst = 0x%x, Sensor Type = 0x%x, data = %u\n",
- __func__, inst, sensor_type, send_buf[1]);
- ret = ssp_spi_sync(data, msg, 1000);
- ssp_clean_msg(msg);
- return ret;
- }
- int ssp_get_chipid(struct ssp_data *data)
- {
- int ret;
- char buffer;
- struct ssp_msg *msg;
- msg = ssp_create_msg(SSP_MSG2SSP_AP_WHOAMI, 1, SSP_AP2HUB_READ, 0);
- if (!msg)
- return -ENOMEM;
- ret = ssp_spi_sync(data, msg, 1000);
- buffer = SSP_GET_BUFFER_AT_INDEX(msg, 0);
- ssp_clean_msg(msg);
- return ret < 0 ? ret : buffer;
- }
- int ssp_set_magnetic_matrix(struct ssp_data *data)
- {
- int ret;
- struct ssp_msg *msg;
- msg = ssp_create_msg(SSP_MSG2SSP_AP_SET_MAGNETIC_STATIC_MATRIX,
- data->sensorhub_info->mag_length, SSP_AP2HUB_WRITE,
- 0);
- if (!msg)
- return -ENOMEM;
- ssp_fill_buffer(msg, 0, data->sensorhub_info->mag_table,
- data->sensorhub_info->mag_length);
- ret = ssp_spi_sync(data, msg, 1000);
- ssp_clean_msg(msg);
- return ret;
- }
- unsigned int ssp_get_sensor_scanning_info(struct ssp_data *data)
- {
- int ret;
- __le32 result;
- u32 cpu_result = 0;
- struct ssp_msg *msg = ssp_create_msg(SSP_MSG2SSP_AP_SENSOR_SCANNING, 4,
- SSP_AP2HUB_READ, 0);
- if (!msg)
- return 0;
- ret = ssp_spi_sync(data, msg, 1000);
- if (ret < 0) {
- dev_err(SSP_DEV, "%s - spi read fail %d\n", __func__, ret);
- goto _exit;
- }
- ssp_get_buffer(msg, 0, &result, 4);
- cpu_result = le32_to_cpu(result);
- dev_info(SSP_DEV, "%s state: 0x%08x\n", __func__, cpu_result);
- _exit:
- ssp_clean_msg(msg);
- return cpu_result;
- }
- unsigned int ssp_get_firmware_rev(struct ssp_data *data)
- {
- int ret;
- __le32 result;
- struct ssp_msg *msg = ssp_create_msg(SSP_MSG2SSP_AP_FIRMWARE_REV, 4,
- SSP_AP2HUB_READ, 0);
- if (!msg)
- return SSP_INVALID_REVISION;
- ret = ssp_spi_sync(data, msg, 1000);
- if (ret < 0) {
- dev_err(SSP_DEV, "%s - transfer fail %d\n", __func__, ret);
- ret = SSP_INVALID_REVISION;
- goto _exit;
- }
- ssp_get_buffer(msg, 0, &result, 4);
- ret = le32_to_cpu(result);
- _exit:
- ssp_clean_msg(msg);
- return ret;
- }
|