diff options
Diffstat (limited to 'drivers')
216 files changed, 12887 insertions, 1040 deletions
diff --git a/drivers/char/adsprpc.c b/drivers/char/adsprpc.c index ed0226131b90..a84172106e0f 100644 --- a/drivers/char/adsprpc.c +++ b/drivers/char/adsprpc.c @@ -212,6 +212,7 @@ struct fastrpc_channel_ctx { struct device *dev; struct fastrpc_session_ctx session[NUM_SESSIONS]; struct completion work; + struct completion workport; struct notifier_block nb; struct kref kref; int channel; @@ -318,7 +319,6 @@ static struct fastrpc_channel_ctx gcinfo[NUM_CHANNELS] = { .channel = SMD_APPS_DSPS, .link.link_info.edge = "dsps", .link.link_info.transport = "smem", - .vmid = VMID_SSC_Q6, }, { .name = "cdsprpc-smd", @@ -1140,6 +1140,9 @@ static int get_args(uint32_t kernel, struct smq_invoke_ctx *ctx) if (err) goto bail; } + if (ctx->buf->virt && metalen <= copylen) + memset(ctx->buf->virt, 0, metalen); + /* copy metadata */ rpra = ctx->buf->virt; ctx->rpra = rpra; @@ -1472,6 +1475,7 @@ static void fastrpc_init(struct fastrpc_apps *me) me->channel = &gcinfo[0]; for (i = 0; i < NUM_CHANNELS; i++) { init_completion(&me->channel[i].work); + init_completion(&me->channel[i].workport); me->channel[i].sesscount = 0; } } @@ -2133,7 +2137,7 @@ void fastrpc_glink_notify_state(void *handle, const void *priv, unsigned event) switch (event) { case GLINK_CONNECTED: link->port_state = FASTRPC_LINK_CONNECTED; - complete(&me->channel[cid].work); + complete(&me->channel[cid].workport); break; case GLINK_LOCAL_DISCONNECTED: link->port_state = FASTRPC_LINK_DISCONNECTED; @@ -2283,8 +2287,7 @@ static void fastrpc_glink_close(void *chan, int cid) return; link = &gfa.channel[cid].link; - if (link->port_state == FASTRPC_LINK_CONNECTED || - link->port_state == FASTRPC_LINK_CONNECTING) { + if (link->port_state == FASTRPC_LINK_CONNECTED) { link->port_state = FASTRPC_LINK_DISCONNECTING; glink_close(chan); } @@ -2482,8 +2485,9 @@ static int fastrpc_channel_open(struct fastrpc_file *fl) if (err) goto bail; - VERIFY(err, wait_for_completion_timeout(&me->channel[cid].work, - RPC_TIMEOUT)); + VERIFY(err, + wait_for_completion_timeout(&me->channel[cid].workport, + RPC_TIMEOUT)); if (err) { me->channel[cid].chan = 0; goto bail; diff --git a/drivers/char/diag/diag_masks.c b/drivers/char/diag/diag_masks.c index 20e617ed0770..e37609abf7bf 100644 --- a/drivers/char/diag/diag_masks.c +++ b/drivers/char/diag/diag_masks.c @@ -796,7 +796,9 @@ static int diag_cmd_set_msg_mask(unsigned char *src_buf, int src_len, for (i = 0; i < NUM_PERIPHERALS; i++) { if (!diag_check_update(i)) continue; + mutex_lock(&driver->md_session_lock); diag_send_msg_mask_update(i, req->ssid_first, req->ssid_last); + mutex_unlock(&driver->md_session_lock); } end: return write_len; @@ -856,7 +858,9 @@ static int diag_cmd_set_all_msg_mask(unsigned char *src_buf, int src_len, for (i = 0; i < NUM_PERIPHERALS; i++) { if (!diag_check_update(i)) continue; + mutex_lock(&driver->md_session_lock); diag_send_msg_mask_update(i, ALL_SSID, ALL_SSID); + mutex_unlock(&driver->md_session_lock); } return write_len; @@ -950,7 +954,9 @@ static int diag_cmd_update_event_mask(unsigned char *src_buf, int src_len, for (i = 0; i < NUM_PERIPHERALS; i++) { if (!diag_check_update(i)) continue; + mutex_lock(&driver->md_session_lock); diag_send_event_mask_update(i); + mutex_unlock(&driver->md_session_lock); } return write_len; @@ -997,7 +1003,9 @@ static int diag_cmd_toggle_events(unsigned char *src_buf, int src_len, for (i = 0; i < NUM_PERIPHERALS; i++) { if (!diag_check_update(i)) continue; + mutex_lock(&driver->md_session_lock); diag_send_event_mask_update(i); + mutex_unlock(&driver->md_session_lock); } memcpy(dest_buf, &header, sizeof(header)); write_len += sizeof(header); @@ -1251,7 +1259,9 @@ static int diag_cmd_set_log_mask(unsigned char *src_buf, int src_len, for (i = 0; i < NUM_PERIPHERALS; i++) { if (!diag_check_update(i)) continue; + mutex_lock(&driver->md_session_lock); diag_send_log_mask_update(i, req->equip_id); + mutex_unlock(&driver->md_session_lock); } end: return write_len; @@ -1302,7 +1312,9 @@ static int diag_cmd_disable_log_mask(unsigned char *src_buf, int src_len, for (i = 0; i < NUM_PERIPHERALS; i++) { if (!diag_check_update(i)) continue; + mutex_lock(&driver->md_session_lock); diag_send_log_mask_update(i, ALL_EQUIP_ID); + mutex_unlock(&driver->md_session_lock); } return write_len; @@ -1966,9 +1978,11 @@ void diag_send_updates_peripheral(uint8_t peripheral) diag_send_feature_mask_update(peripheral); if (driver->time_sync_enabled) diag_send_time_sync_update(peripheral); + mutex_lock(&driver->md_session_lock); diag_send_msg_mask_update(peripheral, ALL_SSID, ALL_SSID); diag_send_log_mask_update(peripheral, ALL_EQUIP_ID); diag_send_event_mask_update(peripheral); + mutex_unlock(&driver->md_session_lock); diag_send_real_time_update(peripheral, driver->real_time_mode[DIAG_LOCAL_PROC]); diag_send_peripheral_buffering_mode( diff --git a/drivers/char/diag/diag_memorydevice.c b/drivers/char/diag/diag_memorydevice.c index 06b83f5230bf..a27f12883c8d 100644 --- a/drivers/char/diag/diag_memorydevice.c +++ b/drivers/char/diag/diag_memorydevice.c @@ -129,37 +129,6 @@ void diag_md_close_all() diag_ws_reset(DIAG_WS_MUX); } -static int diag_md_get_peripheral(int ctxt) -{ - int peripheral; - - if (driver->num_pd_session) { - peripheral = GET_PD_CTXT(ctxt); - switch (peripheral) { - case UPD_WLAN: - case UPD_AUDIO: - case UPD_SENSORS: - break; - case DIAG_ID_MPSS: - case DIAG_ID_LPASS: - case DIAG_ID_CDSP: - default: - peripheral = - GET_BUF_PERIPHERAL(ctxt); - if (peripheral > NUM_PERIPHERALS) - peripheral = -EINVAL; - break; - } - } else { - /* Account for Apps data as well */ - peripheral = GET_BUF_PERIPHERAL(ctxt); - if (peripheral > NUM_PERIPHERALS) - peripheral = -EINVAL; - } - - return peripheral; -} - int diag_md_write(int id, unsigned char *buf, int len, int ctx) { int i; diff --git a/drivers/char/diag/diag_mux.c b/drivers/char/diag/diag_mux.c index d6f6ea7af8ea..8cc803eef552 100644 --- a/drivers/char/diag/diag_mux.c +++ b/drivers/char/diag/diag_mux.c @@ -153,12 +153,15 @@ int diag_mux_write(int proc, unsigned char *buf, int len, int ctx) upd = PERIPHERAL_CDSP; break; case UPD_WLAN: - if (!driver->num_pd_session) + if (!driver->pd_logging_mode[0]) upd = PERIPHERAL_MODEM; break; case UPD_AUDIO: + if (!driver->pd_logging_mode[1]) + upd = PERIPHERAL_LPASS; + break; case UPD_SENSORS: - if (!driver->num_pd_session) + if (!driver->pd_logging_mode[2]) upd = PERIPHERAL_LPASS; break; default: diff --git a/drivers/char/diag/diagchar.h b/drivers/char/diag/diagchar.h index 92cf24dcab5e..cc56d68217b3 100644 --- a/drivers/char/diag/diagchar.h +++ b/drivers/char/diag/diagchar.h @@ -578,6 +578,7 @@ struct diagchar_dev { unsigned char *buf_feature_mask_update; uint8_t hdlc_disabled; struct mutex hdlc_disable_mutex; + struct mutex hdlc_recovery_mutex; struct timer_list hdlc_reset_timer; struct mutex diag_hdlc_mutex; unsigned char *hdlc_buf; diff --git a/drivers/char/diag/diagchar_core.c b/drivers/char/diag/diagchar_core.c index 0bc23199b92e..afaedc99a4e7 100644 --- a/drivers/char/diag/diagchar_core.c +++ b/drivers/char/diag/diagchar_core.c @@ -456,6 +456,7 @@ static void diag_close_logging_process(const int pid) { int i, j; int session_mask; + uint32_t p_mask; struct diag_md_session_t *session_info = NULL; struct diag_logging_mode_param_t params; @@ -475,6 +476,9 @@ static void diag_close_logging_process(const int pid) session_mask = session_info->peripheral_mask; diag_md_session_close(session_info); + p_mask = + diag_translate_kernel_to_user_mask(session_mask); + for (i = 0; i < NUM_MD_SESSIONS; i++) if (MD_PERIPHERAL_MASK(i) & session_mask) diag_mux_close_peripheral(DIAG_LOCAL_PROC, i); @@ -482,19 +486,17 @@ static void diag_close_logging_process(const int pid) params.req_mode = USB_MODE; params.mode_param = 0; params.pd_mask = 0; - params.peripheral_mask = - diag_translate_kernel_to_user_mask(session_mask); + params.peripheral_mask = p_mask; if (driver->num_pd_session > 0) { - for (i = UPD_WLAN; ((i < NUM_MD_SESSIONS) && - (session_mask & MD_PERIPHERAL_MASK(i))); - i++) { - j = i - UPD_WLAN; - driver->pd_session_clear[j] = 1; - driver->pd_logging_mode[j] = 0; - driver->num_pd_session -= 1; - params.pd_mask = - diag_translate_kernel_to_user_mask(session_mask); + for (i = UPD_WLAN; (i < NUM_MD_SESSIONS); i++) { + if (session_mask & MD_PERIPHERAL_MASK(i)) { + j = i - UPD_WLAN; + driver->pd_session_clear[j] = 1; + driver->pd_logging_mode[j] = 0; + driver->num_pd_session -= 1; + params.pd_mask = p_mask; + } } } @@ -1020,6 +1022,11 @@ static int diag_send_raw_data_remote(int proc, void *buf, int len, else hdlc_disabled = driver->hdlc_disabled; if (hdlc_disabled) { + if (len < 4) { + pr_err("diag: In %s, invalid len: %d of non_hdlc pkt", + __func__, len); + return -EBADMSG; + } payload = *(uint16_t *)(buf + 2); if (payload > DIAG_MAX_HDLC_BUF_SIZE) { pr_err("diag: Dropping packet, payload size is %d\n", @@ -1028,11 +1035,21 @@ static int diag_send_raw_data_remote(int proc, void *buf, int len, } driver->hdlc_encode_buf_len = payload; /* - * Adding 4 bytes for start (1 byte), version (1 byte) and - * payload (2 bytes) + * Adding 5 bytes for start (1 byte), version (1 byte), + * payload (2 bytes) and end (1 byte) */ - memcpy(driver->hdlc_encode_buf, buf + 4, payload); - goto send_data; + if (len == (payload + 5)) { + /* + * Adding 4 bytes for start (1 byte), version (1 byte) + * and payload (2 bytes) + */ + memcpy(driver->hdlc_encode_buf, buf + 4, payload); + goto send_data; + } else { + pr_err("diag: In %s, invalid len: %d of non_hdlc pkt", + __func__, len); + return -EBADMSG; + } } if (hdlc_flag) { @@ -3624,6 +3641,7 @@ static int __init diagchar_init(void) mutex_init(&driver->delayed_rsp_mutex); mutex_init(&apps_data_mutex); mutex_init(&driver->msg_mask_lock); + mutex_init(&driver->hdlc_recovery_mutex); for (i = 0; i < NUM_PERIPHERALS; i++) mutex_init(&driver->diagfwd_channel_mutex[i]); init_waitqueue_head(&driver->wait_q); diff --git a/drivers/char/diag/diagfwd.c b/drivers/char/diag/diagfwd.c index 019bf1946ac3..7dc2eabf1bb9 100644 --- a/drivers/char/diag/diagfwd.c +++ b/drivers/char/diag/diagfwd.c @@ -1405,7 +1405,9 @@ static void diag_hdlc_start_recovery(unsigned char *buf, int len, if (start_ptr) { /* Discard any partial packet reads */ + mutex_lock(&driver->hdlc_recovery_mutex); driver->incoming_pkt.processing = 0; + mutex_unlock(&driver->hdlc_recovery_mutex); diag_process_non_hdlc_pkt(start_ptr, len - i, info); } } @@ -1419,18 +1421,24 @@ void diag_process_non_hdlc_pkt(unsigned char *buf, int len, const uint32_t header_len = sizeof(struct diag_pkt_frame_t); struct diag_pkt_frame_t *actual_pkt = NULL; unsigned char *data_ptr = NULL; - struct diag_partial_pkt_t *partial_pkt = &driver->incoming_pkt; + struct diag_partial_pkt_t *partial_pkt = NULL; - if (!buf || len <= 0) + mutex_lock(&driver->hdlc_recovery_mutex); + if (!buf || len <= 0) { + mutex_unlock(&driver->hdlc_recovery_mutex); return; - - if (!partial_pkt->processing) + } + partial_pkt = &driver->incoming_pkt; + if (!partial_pkt->processing) { + mutex_unlock(&driver->hdlc_recovery_mutex); goto start; + } if (partial_pkt->remaining > len) { if ((partial_pkt->read_len + len) > partial_pkt->capacity) { pr_err("diag: Invalid length %d, %d received in %s\n", partial_pkt->read_len, len, __func__); + mutex_unlock(&driver->hdlc_recovery_mutex); goto end; } memcpy(partial_pkt->data + partial_pkt->read_len, buf, len); @@ -1444,6 +1452,7 @@ void diag_process_non_hdlc_pkt(unsigned char *buf, int len, pr_err("diag: Invalid length during partial read %d, %d received in %s\n", partial_pkt->read_len, partial_pkt->remaining, __func__); + mutex_unlock(&driver->hdlc_recovery_mutex); goto end; } memcpy(partial_pkt->data + partial_pkt->read_len, buf, @@ -1457,20 +1466,27 @@ void diag_process_non_hdlc_pkt(unsigned char *buf, int len, if (partial_pkt->remaining == 0) { actual_pkt = (struct diag_pkt_frame_t *)(partial_pkt->data); data_ptr = partial_pkt->data + header_len; - if (*(uint8_t *)(data_ptr + actual_pkt->length) != CONTROL_CHAR) + if (*(uint8_t *)(data_ptr + actual_pkt->length) != + CONTROL_CHAR) { + mutex_unlock(&driver->hdlc_recovery_mutex); diag_hdlc_start_recovery(buf, len, info); + mutex_lock(&driver->hdlc_recovery_mutex); + } err = diag_process_apps_pkt(data_ptr, actual_pkt->length, info); if (err) { pr_err("diag: In %s, unable to process incoming data packet, err: %d\n", __func__, err); + mutex_unlock(&driver->hdlc_recovery_mutex); goto end; } partial_pkt->read_len = 0; partial_pkt->total_len = 0; partial_pkt->processing = 0; + mutex_unlock(&driver->hdlc_recovery_mutex); goto start; } + mutex_unlock(&driver->hdlc_recovery_mutex); goto end; start: @@ -1483,14 +1499,14 @@ start: diag_send_error_rsp(buf, len, info); goto end; } - + mutex_lock(&driver->hdlc_recovery_mutex); if (pkt_len + header_len > partial_pkt->capacity) { pr_err("diag: In %s, incoming data is too large for the request buffer %d\n", __func__, pkt_len); + mutex_unlock(&driver->hdlc_recovery_mutex); diag_hdlc_start_recovery(buf, len, info); break; } - if ((pkt_len + header_len) > (len - read_bytes)) { partial_pkt->read_len = len - read_bytes; partial_pkt->total_len = pkt_len + header_len; @@ -1498,19 +1514,27 @@ start: partial_pkt->read_len; partial_pkt->processing = 1; memcpy(partial_pkt->data, buf, partial_pkt->read_len); + mutex_unlock(&driver->hdlc_recovery_mutex); break; } data_ptr = buf + header_len; - if (*(uint8_t *)(data_ptr + actual_pkt->length) != CONTROL_CHAR) + if (*(uint8_t *)(data_ptr + actual_pkt->length) != + CONTROL_CHAR) { + mutex_unlock(&driver->hdlc_recovery_mutex); diag_hdlc_start_recovery(buf, len, info); + mutex_lock(&driver->hdlc_recovery_mutex); + } else hdlc_reset = 0; err = diag_process_apps_pkt(data_ptr, actual_pkt->length, info); - if (err) + if (err) { + mutex_unlock(&driver->hdlc_recovery_mutex); break; + } read_bytes += header_len + pkt_len + 1; buf += header_len + pkt_len + 1; /* advance to next pkt */ + mutex_unlock(&driver->hdlc_recovery_mutex); } end: return; diff --git a/drivers/char/diag/diagfwd_peripheral.c b/drivers/char/diag/diagfwd_peripheral.c index e209039bed5a..9ac1ad62ffe0 100644 --- a/drivers/char/diag/diagfwd_peripheral.c +++ b/drivers/char/diag/diagfwd_peripheral.c @@ -216,6 +216,45 @@ static int check_bufsize_for_encoding(struct diagfwd_buf_t *buf, uint32_t len) return buf->len; } +int diag_md_get_peripheral(int ctxt) +{ + int peripheral; + + if (driver->num_pd_session) { + peripheral = GET_PD_CTXT(ctxt); + switch (peripheral) { + case UPD_WLAN: + if (!driver->pd_logging_mode[0]) + peripheral = PERIPHERAL_MODEM; + break; + case UPD_AUDIO: + if (!driver->pd_logging_mode[1]) + peripheral = PERIPHERAL_LPASS; + break; + case UPD_SENSORS: + if (!driver->pd_logging_mode[2]) + peripheral = PERIPHERAL_LPASS; + break; + case DIAG_ID_MPSS: + case DIAG_ID_LPASS: + case DIAG_ID_CDSP: + default: + peripheral = + GET_BUF_PERIPHERAL(ctxt); + if (peripheral > NUM_PERIPHERALS) + peripheral = -EINVAL; + break; + } + } else { + /* Account for Apps data as well */ + peripheral = GET_BUF_PERIPHERAL(ctxt); + if (peripheral > NUM_PERIPHERALS) + peripheral = -EINVAL; + } + + return peripheral; +} + static void diagfwd_data_process_done(struct diagfwd_info *fwd_info, struct diagfwd_buf_t *buf, int len) { @@ -245,13 +284,15 @@ static void diagfwd_data_process_done(struct diagfwd_info *fwd_info, mutex_lock(&driver->hdlc_disable_mutex); mutex_lock(&fwd_info->data_mutex); - peripheral = GET_PD_CTXT(buf->ctxt); - if (peripheral == DIAG_ID_MPSS) - peripheral = PERIPHERAL_MODEM; - if (peripheral == DIAG_ID_LPASS) - peripheral = PERIPHERAL_LPASS; - if (peripheral == DIAG_ID_CDSP) - peripheral = PERIPHERAL_CDSP; + peripheral = diag_md_get_peripheral(buf->ctxt); + if (peripheral < 0) { + pr_err("diag:%s:%d invalid peripheral = %d\n", + __func__, __LINE__, peripheral); + mutex_unlock(&fwd_info->data_mutex); + mutex_unlock(&driver->hdlc_disable_mutex); + diag_ws_release(); + return; + } session_info = diag_md_session_get_peripheral(peripheral); diff --git a/drivers/char/diag/diagfwd_peripheral.h b/drivers/char/diag/diagfwd_peripheral.h index 037eeebdeb35..eda70dcfdcd9 100644 --- a/drivers/char/diag/diagfwd_peripheral.h +++ b/drivers/char/diag/diagfwd_peripheral.h @@ -105,6 +105,9 @@ void diagfwd_early_open(uint8_t peripheral); void diagfwd_late_open(struct diagfwd_info *fwd_info); void diagfwd_close(uint8_t peripheral, uint8_t type); + +int diag_md_get_peripheral(int ctxt); + int diagfwd_register(uint8_t transport, uint8_t peripheral, uint8_t type, void *ctxt, struct diag_peripheral_ops *ops, struct diagfwd_info **fwd_ctxt); diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index 31e8ae916ba0..be0b09a0fb44 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -1864,7 +1864,7 @@ static void config_work_handler(struct work_struct *work) { struct ports_device *portdev; - portdev = container_of(work, struct ports_device, control_work); + portdev = container_of(work, struct ports_device, config_work); if (!use_multiport(portdev)) { struct virtio_device *vdev; struct port *port; diff --git a/drivers/cpufreq/s3c2416-cpufreq.c b/drivers/cpufreq/s3c2416-cpufreq.c index d6d425773fa4..5b2db3c6568f 100644 --- a/drivers/cpufreq/s3c2416-cpufreq.c +++ b/drivers/cpufreq/s3c2416-cpufreq.c @@ -400,7 +400,6 @@ static int s3c2416_cpufreq_driver_init(struct cpufreq_policy *policy) rate = clk_get_rate(s3c_freq->hclk); if (rate < 133 * 1000 * 1000) { pr_err("cpufreq: HCLK not at 133MHz\n"); - clk_put(s3c_freq->hclk); ret = -EINVAL; goto err_armclk; } diff --git a/drivers/cpuidle/lpm-levels.c b/drivers/cpuidle/lpm-levels.c index d6aee7188516..584a1857624a 100644 --- a/drivers/cpuidle/lpm-levels.c +++ b/drivers/cpuidle/lpm-levels.c @@ -749,8 +749,8 @@ static int cpu_power_select(struct cpuidle_device *dev, if (next_wakeup_us > max_residency[i]) { predicted = lpm_cpuidle_predict(dev, cpu, &idx_restrict, &idx_restrict_time); - if (predicted < min_residency[i]) - predicted = 0; + if (predicted && (predicted < min_residency[i])) + predicted = min_residency[i]; } else invalidate_predict_history(dev); } diff --git a/drivers/crypto/msm/compat_qcedev.c b/drivers/crypto/msm/compat_qcedev.c index c69dc2b86a68..90e5fa804e47 100644 --- a/drivers/crypto/msm/compat_qcedev.c +++ b/drivers/crypto/msm/compat_qcedev.c @@ -1,7 +1,7 @@ /* * QTI CE 32-bit compatibility syscall for 64-bit systems * - * Copyright (c) 2014-2015, The Linux Foundation. All rights reserved. + * Copyright (c) 2014-2015, 2017, The Linux Foundation. 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 version 2 and @@ -97,7 +97,6 @@ static int compat_get_qcedev_vbuf_info( for (i = 0; i < QCEDEV_MAX_BUFFERS; i++) { err |= get_user(vaddr, &vbuf32->src[i].vaddr); - vbuf->src[i].vaddr = NULL; err |= put_user(vaddr, (compat_uptr_t *)&vbuf->src[i].vaddr); err |= get_user(len, &vbuf32->src[i].len); err |= put_user(len, &vbuf->src[i].len); @@ -105,7 +104,6 @@ static int compat_get_qcedev_vbuf_info( for (i = 0; i < QCEDEV_MAX_BUFFERS; i++) { err |= get_user(vaddr, &vbuf32->dst[i].vaddr); - vbuf->dst[i].vaddr = NULL; err |= put_user(vaddr, (compat_uptr_t *)&vbuf->dst[i].vaddr); err |= get_user(len, &vbuf32->dst[i].len); err |= put_user(len, &vbuf->dst[i].len); @@ -123,7 +121,6 @@ static int compat_put_qcedev_vbuf_info( for (i = 0; i < QCEDEV_MAX_BUFFERS; i++) { err |= get_user(vaddr, (compat_uptr_t *)&vbuf->src[i].vaddr); - vbuf32->src[i].vaddr = 0; err |= put_user(vaddr, &vbuf32->src[i].vaddr); err |= get_user(len, &vbuf->src[i].len); err |= put_user(len, &vbuf32->src[i].len); @@ -131,7 +128,6 @@ static int compat_put_qcedev_vbuf_info( for (i = 0; i < QCEDEV_MAX_BUFFERS; i++) { err |= get_user(vaddr, (compat_uptr_t *)&vbuf->dst[i].vaddr); - vbuf32->dst[i].vaddr = 0; err |= put_user(vaddr, &vbuf32->dst[i].vaddr); err |= get_user(len, &vbuf->dst[i].len); err |= put_user(len, &vbuf32->dst[i].len); @@ -276,7 +272,6 @@ static int compat_get_qcedev_sha_op_req( for (i = 0; i < QCEDEV_MAX_BUFFERS; i++) { err |= get_user(vaddr, &data32->data[i].vaddr); - data->data[i].vaddr = 0; err |= put_user(vaddr, (compat_uptr_t *)&data->data[i].vaddr); err |= get_user(len, &data32->data[i].len); err |= put_user(len, &data->data[i].len); @@ -295,7 +290,6 @@ static int compat_get_qcedev_sha_op_req( err |= get_user(diglen, &data32->diglen); err |= put_user(diglen, &data->diglen); err |= get_user(authkey, &data32->authkey); - data->authkey = NULL; err |= put_user(authkey, (compat_uptr_t *)&data->authkey); err |= get_user(authklen, &data32->authklen); err |= put_user(authklen, &data->authklen); @@ -322,7 +316,6 @@ static int compat_put_qcedev_sha_op_req( for (i = 0; i < QCEDEV_MAX_BUFFERS; i++) { err |= get_user(vaddr, (compat_uptr_t *)&data->data[i].vaddr); - data32->data[i].vaddr = 0; err |= put_user(vaddr, &data32->data[i].vaddr); err |= get_user(len, &data->data[i].len); err |= put_user(len, &data32->data[i].len); @@ -341,7 +334,6 @@ static int compat_put_qcedev_sha_op_req( err |= get_user(diglen, &data->diglen); err |= put_user(diglen, &data32->diglen); err |= get_user(authkey, (compat_uptr_t *)&data->authkey); - data32->authkey = 0; err |= put_user(authkey, &data32->authkey); err |= get_user(authklen, &data->authklen); err |= put_user(authklen, &data32->authklen); diff --git a/drivers/crypto/msm/ice.c b/drivers/crypto/msm/ice.c index f99a421c0ee4..7dd8d3d3f6ad 100644 --- a/drivers/crypto/msm/ice.c +++ b/drivers/crypto/msm/ice.c @@ -441,7 +441,7 @@ static int qcom_ice_enable(struct ice_device *ice_dev) (ICE_REV(ice_dev->ice_hw_version, MINOR) >= 1))) { reg = qcom_ice_readl(ice_dev, QCOM_ICE_REGS_BYPASS_STATUS); if ((reg & 0x80000000) != 0x0) { - pr_err("%s: Bypass failed for ice = %p", + pr_err("%s: Bypass failed for ice = %pK", __func__, (void *)ice_dev); BUG(); } @@ -467,7 +467,7 @@ static int qcom_ice_verify_ice(struct ice_device *ice_dev) } ice_dev->ice_hw_version = rev; - dev_info(ice_dev->pdev, "QC ICE %d.%d.%d device found @0x%p\n", + dev_info(ice_dev->pdev, "QC ICE %d.%d.%d device found @0x%pK\n", maj_rev, min_rev, step_rev, ice_dev->mmio); @@ -1256,7 +1256,7 @@ static void qcom_ice_debug(struct platform_device *pdev) goto out; } - pr_err("%s: =========== REGISTER DUMP (%p)===========\n", + pr_err("%s: =========== REGISTER DUMP (%pK)===========\n", ice_dev->ice_instance_type, ice_dev); pr_err("%s: ICE Control: 0x%08x | ICE Reset: 0x%08x\n", @@ -1570,7 +1570,7 @@ struct platform_device *qcom_ice_get_pdevice(struct device_node *node) struct ice_device *ice_dev = NULL; if (!node) { - pr_err("%s: invalid node %p", __func__, node); + pr_err("%s: invalid node %pK", __func__, node); goto out; } @@ -1587,13 +1587,14 @@ struct platform_device *qcom_ice_get_pdevice(struct device_node *node) list_for_each_entry(ice_dev, &ice_devices, list) { if (ice_dev->pdev->of_node == node) { - pr_info("%s: found ice device %p\n", __func__, ice_dev); + pr_info("%s: found ice device %pK\n", __func__, + ice_dev); break; } } ice_pdev = to_platform_device(ice_dev->pdev); - pr_info("%s: matching platform device %p\n", __func__, ice_pdev); + pr_info("%s: matching platform device %pK\n", __func__, ice_pdev); out: return ice_pdev; } @@ -1632,7 +1633,7 @@ static int enable_ice_setup(struct ice_device *ice_dev) } ret = regulator_enable(ice_dev->reg); if (ret) { - pr_err("%s:%p: Could not enable regulator\n", + pr_err("%s:%pK: Could not enable regulator\n", __func__, ice_dev); goto out; } @@ -1640,7 +1641,7 @@ static int enable_ice_setup(struct ice_device *ice_dev) /* Setup Clocks */ if (qcom_ice_enable_clocks(ice_dev, true)) { - pr_err("%s:%p:%s Could not enable clocks\n", __func__, + pr_err("%s:%pK:%s Could not enable clocks\n", __func__, ice_dev, ice_dev->ice_instance_type); goto out_reg; } @@ -1652,7 +1653,7 @@ static int enable_ice_setup(struct ice_device *ice_dev) ret = qcom_ice_set_bus_vote(ice_dev, vote); if (ret) { - pr_err("%s:%p: failed %d\n", __func__, ice_dev, ret); + pr_err("%s:%pK: failed %d\n", __func__, ice_dev, ret); goto out_clocks; } @@ -1684,19 +1685,19 @@ static int disable_ice_setup(struct ice_device *ice_dev) /* Setup Bus Vote */ vote = qcom_ice_get_bus_vote(ice_dev, "MIN"); if (vote < 0) { - pr_err("%s:%p: Unable to get bus vote\n", __func__, ice_dev); + pr_err("%s:%pK: Unable to get bus vote\n", __func__, ice_dev); goto out_disable_clocks; } ret = qcom_ice_set_bus_vote(ice_dev, vote); if (ret) - pr_err("%s:%p: failed %d\n", __func__, ice_dev, ret); + pr_err("%s:%pK: failed %d\n", __func__, ice_dev, ret); out_disable_clocks: /* Setup Clocks */ if (qcom_ice_enable_clocks(ice_dev, false)) - pr_err("%s:%p:%s Could not disable clocks\n", __func__, + pr_err("%s:%pK:%s Could not disable clocks\n", __func__, ice_dev, ice_dev->ice_instance_type); /* Setup Regulator */ @@ -1707,7 +1708,7 @@ out_disable_clocks: } ret = regulator_disable(ice_dev->reg); if (ret) { - pr_err("%s:%p: Could not disable regulator\n", + pr_err("%s:%pK: Could not disable regulator\n", __func__, ice_dev); goto out; } diff --git a/drivers/crypto/msm/ota_crypto.c b/drivers/crypto/msm/ota_crypto.c index a568bf46f09f..96297fe7eaad 100644 --- a/drivers/crypto/msm/ota_crypto.c +++ b/drivers/crypto/msm/ota_crypto.c @@ -172,7 +172,7 @@ static int qcota_release(struct inode *inode, struct file *file) podev = file->private_data; if (podev != NULL && podev->magic != OTA_MAGIC) { - pr_err("%s: invalid handle %p\n", + pr_err("%s: invalid handle %pK\n", __func__, podev); } @@ -444,7 +444,7 @@ static long qcota_ioctl(struct file *file, podev = file->private_data; if (podev == NULL || podev->magic != OTA_MAGIC) { - pr_err("%s: invalid handle %p\n", + pr_err("%s: invalid handle %pK\n", __func__, podev); return -ENOENT; } diff --git a/drivers/crypto/msm/qce50.c b/drivers/crypto/msm/qce50.c index ee7e735761e2..4ab8ca143f6c 100644 --- a/drivers/crypto/msm/qce50.c +++ b/drivers/crypto/msm/qce50.c @@ -1,6 +1,6 @@ /* Qualcomm Crypto Engine driver. * - * Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. + * Copyright (c) 2012-2017, The Linux Foundation. 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 version 2 and @@ -294,11 +294,11 @@ static int _probe_ce_engine(struct qce_device *pce_dev) pce_dev->ce_bam_info.ce_burst_size = MAX_CE_BAM_BURST_SIZE; dev_info(pce_dev->pdev, - "CE device = 0x%x\n, " - "IO base, CE = 0x%p\n, " + "CE device = 0x%x\n" + "IO base, CE = 0x%pK\n" "Consumer (IN) PIPE %d, " "Producer (OUT) PIPE %d\n" - "IO base BAM = 0x%p\n" + "IO base BAM = 0x%pK\n" "BAM IRQ %d\n" "Engines Availability = 0x%x\n", pce_dev->ce_bam_info.ce_device, @@ -1160,7 +1160,7 @@ static void _qce_dump_descr_fifos_dbg(struct qce_device *pce_dev, int req_info) #define QCE_WRITE_REG(val, addr) \ { \ - pr_info(" [0x%p] 0x%x\n", addr, (uint32_t)val); \ + pr_info(" [0x%pK] 0x%x\n", addr, (uint32_t)val); \ writel_relaxed(val, addr); \ } @@ -2730,7 +2730,7 @@ static int qce_sps_init_ep_conn(struct qce_device *pce_dev, sps_event->callback = NULL; } - pr_debug("success, %s : pipe_handle=0x%lx, desc fifo base (phy) = 0x%p\n", + pr_debug("success, %s : pipe_handle=0x%lx, desc fifo base (phy) = 0x%pK\n", is_producer ? "PRODUCER(RX/OUT)" : "CONSUMER(TX/IN)", (uintptr_t)sps_pipe_info, &sps_connect_info->desc.phys_base); goto out; @@ -2895,7 +2895,7 @@ static int qce_sps_get_bam(struct qce_device *pce_dev) bam.ipc_loglevel = QCE_BAM_DEFAULT_IPC_LOGLVL; bam.options |= SPS_BAM_CACHED_WP; pr_debug("bam physical base=0x%lx\n", (uintptr_t)bam.phys_addr); - pr_debug("bam virtual base=0x%p\n", bam.virt_addr); + pr_debug("bam virtual base=0x%pK\n", bam.virt_addr); /* Register CE Peripheral BAM device to SPS driver */ rc = sps_register_bam_device(&bam, &pbam->handle); @@ -2998,7 +2998,7 @@ static void print_notify_debug(struct sps_event_notify *notify) phys_addr_t addr = DESC_FULL_ADDR((phys_addr_t) notify->data.transfer.iovec.flags, notify->data.transfer.iovec.addr); - pr_debug("sps ev_id=%d, addr=0x%pa, size=0x%x, flags=0x%x user=0x%p\n", + pr_debug("sps ev_id=%d, addr=0x%pa, size=0x%x, flags=0x%x user=0x%pK\n", notify->event_id, &addr, notify->data.transfer.iovec.size, notify->data.transfer.iovec.flags, diff --git a/drivers/crypto/msm/qcedev.c b/drivers/crypto/msm/qcedev.c index beeb99e479c7..20bf034bb193 100644 --- a/drivers/crypto/msm/qcedev.c +++ b/drivers/crypto/msm/qcedev.c @@ -201,7 +201,7 @@ static int qcedev_release(struct inode *inode, struct file *file) handle = file->private_data; podev = handle->cntl; if (podev != NULL && podev->magic != QCEDEV_MAGIC) { - pr_err("%s: invalid handle %p\n", + pr_err("%s: invalid handle %pK\n", __func__, podev); } kzfree(handle); @@ -1607,7 +1607,7 @@ long qcedev_ioctl(struct file *file, unsigned cmd, unsigned long arg) podev = handle->cntl; qcedev_areq.handle = handle; if (podev == NULL || podev->magic != QCEDEV_MAGIC) { - pr_err("%s: invalid handle %p\n", + pr_err("%s: invalid handle %pK\n", __func__, podev); return -ENOENT; } diff --git a/drivers/crypto/msm/qcrypto.c b/drivers/crypto/msm/qcrypto.c index b5c5dc035c66..5b364f053b1b 100644 --- a/drivers/crypto/msm/qcrypto.c +++ b/drivers/crypto/msm/qcrypto.c @@ -262,7 +262,7 @@ static void qcrypto_free_req_control(struct crypto_engine *pce, preq->arsp = NULL; /* free req */ if (xchg(&preq->in_use, false) == false) { - pr_warn("request info %p free already\n", preq); + pr_warn("request info %pK free already\n", preq); } else { atomic_dec(&pce->req_count); } @@ -1720,7 +1720,7 @@ static void _qce_ahash_complete(void *cookie, unsigned char *digest, } #ifdef QCRYPTO_DEBUG - dev_info(&pengine->pdev->dev, "_qce_ahash_complete: %p ret %d\n", + dev_info(&pengine->pdev->dev, "_qce_ahash_complete: %pK ret %d\n", areq, ret); #endif if (digest) { @@ -1779,7 +1779,7 @@ static void _qce_ablk_cipher_complete(void *cookie, unsigned char *icb, } #ifdef QCRYPTO_DEBUG - dev_info(&pengine->pdev->dev, "_qce_ablk_cipher_complete: %p ret %d\n", + dev_info(&pengine->pdev->dev, "_qce_ablk_cipher_complete: %pK ret %d\n", areq, ret); #endif if (iv) @@ -2479,7 +2479,7 @@ static int _qcrypto_enc_aes_ecb(struct ablkcipher_request *req) BUG_ON(crypto_tfm_alg_type(req->base.tfm) != CRYPTO_ALG_TYPE_ABLKCIPHER); #ifdef QCRYPTO_DEBUG - dev_info(&ctx->pengine->pdev->dev, "_qcrypto_enc_aes_ecb: %p\n", req); + dev_info(&ctx->pengine->pdev->dev, "_qcrypto_enc_aes_ecb: %pK\n", req); #endif if ((ctx->enc_key_len == AES_KEYSIZE_192) && @@ -2509,7 +2509,7 @@ static int _qcrypto_enc_aes_cbc(struct ablkcipher_request *req) BUG_ON(crypto_tfm_alg_type(req->base.tfm) != CRYPTO_ALG_TYPE_ABLKCIPHER); #ifdef QCRYPTO_DEBUG - dev_info(&ctx->pengine->pdev->dev, "_qcrypto_enc_aes_cbc: %p\n", req); + dev_info(&ctx->pengine->pdev->dev, "_qcrypto_enc_aes_cbc: %pK\n", req); #endif if ((ctx->enc_key_len == AES_KEYSIZE_192) && @@ -2539,7 +2539,7 @@ static int _qcrypto_enc_aes_ctr(struct ablkcipher_request *req) BUG_ON(crypto_tfm_alg_type(req->base.tfm) != CRYPTO_ALG_TYPE_ABLKCIPHER); #ifdef QCRYPTO_DEBUG - dev_info(&ctx->pengine->pdev->dev, "_qcrypto_enc_aes_ctr: %p\n", req); + dev_info(&ctx->pengine->pdev->dev, "_qcrypto_enc_aes_ctr: %pK\n", req); #endif if ((ctx->enc_key_len == AES_KEYSIZE_192) && @@ -2727,7 +2727,7 @@ static int _qcrypto_dec_aes_ecb(struct ablkcipher_request *req) BUG_ON(crypto_tfm_alg_type(req->base.tfm) != CRYPTO_ALG_TYPE_ABLKCIPHER); #ifdef QCRYPTO_DEBUG - dev_info(&ctx->pengine->pdev->dev, "_qcrypto_dec_aes_ecb: %p\n", req); + dev_info(&ctx->pengine->pdev->dev, "_qcrypto_dec_aes_ecb: %pK\n", req); #endif if ((ctx->enc_key_len == AES_KEYSIZE_192) && @@ -2757,7 +2757,7 @@ static int _qcrypto_dec_aes_cbc(struct ablkcipher_request *req) BUG_ON(crypto_tfm_alg_type(req->base.tfm) != CRYPTO_ALG_TYPE_ABLKCIPHER); #ifdef QCRYPTO_DEBUG - dev_info(&ctx->pengine->pdev->dev, "_qcrypto_dec_aes_cbc: %p\n", req); + dev_info(&ctx->pengine->pdev->dev, "_qcrypto_dec_aes_cbc: %pK\n", req); #endif if ((ctx->enc_key_len == AES_KEYSIZE_192) && @@ -2787,7 +2787,7 @@ static int _qcrypto_dec_aes_ctr(struct ablkcipher_request *req) BUG_ON(crypto_tfm_alg_type(req->base.tfm) != CRYPTO_ALG_TYPE_ABLKCIPHER); #ifdef QCRYPTO_DEBUG - dev_info(&ctx->pengine->pdev->dev, "_qcrypto_dec_aes_ctr: %p\n", req); + dev_info(&ctx->pengine->pdev->dev, "_qcrypto_dec_aes_ctr: %pK\n", req); #endif if ((ctx->enc_key_len == AES_KEYSIZE_192) && @@ -3353,7 +3353,7 @@ static int _qcrypto_aead_encrypt_aes_cbc(struct aead_request *req) #ifdef QCRYPTO_DEBUG dev_info(&ctx->pengine->pdev->dev, - "_qcrypto_aead_encrypt_aes_cbc: %p\n", req); + "_qcrypto_aead_encrypt_aes_cbc: %pK\n", req); #endif rctx = aead_request_ctx(req); @@ -3384,7 +3384,7 @@ static int _qcrypto_aead_decrypt_aes_cbc(struct aead_request *req) #ifdef QCRYPTO_DEBUG dev_info(&ctx->pengine->pdev->dev, - "_qcrypto_aead_decrypt_aes_cbc: %p\n", req); + "_qcrypto_aead_decrypt_aes_cbc: %pK\n", req); #endif rctx = aead_request_ctx(req); rctx->aead = 1; diff --git a/drivers/firmware/qcom/tz_log.c b/drivers/firmware/qcom/tz_log.c index fa90330db93b..c893681f3bf3 100644 --- a/drivers/firmware/qcom/tz_log.c +++ b/drivers/firmware/qcom/tz_log.c @@ -477,10 +477,10 @@ static int _disp_tz_reset_stats(void) static int _disp_tz_interrupt_stats(void) { - int i, j, int_info_size; + int i, j; int len = 0; int *num_int; - unsigned char *ptr; + void *ptr; struct tzdbg_int_t *tzdbg_ptr; struct tzdbg_int_t_tz40 *tzdbg_ptr_tz40; @@ -488,14 +488,12 @@ static int _disp_tz_interrupt_stats(void) (tzdbg.diag_buf->int_info_off - sizeof(uint32_t))); ptr = ((unsigned char *)tzdbg.diag_buf + tzdbg.diag_buf->int_info_off); - int_info_size = ((tzdbg.diag_buf->ring_off - - tzdbg.diag_buf->int_info_off)/(*num_int)); pr_info("qsee_version = 0x%x\n", tzdbg.tz_version); if (tzdbg.tz_version < QSEE_VERSION_TZ_4_X) { + tzdbg_ptr = ptr; for (i = 0; i < (*num_int); i++) { - tzdbg_ptr = (struct tzdbg_int_t *)ptr; len += snprintf(tzdbg.disp_buf + len, (debug_rw_buf_size - 1) - len, " Interrupt Number : 0x%x\n" @@ -519,11 +517,11 @@ static int _disp_tz_interrupt_stats(void) __func__); break; } - ptr += int_info_size; + tzdbg_ptr++; } } else { + tzdbg_ptr_tz40 = ptr; for (i = 0; i < (*num_int); i++) { - tzdbg_ptr_tz40 = (struct tzdbg_int_t_tz40 *)ptr; len += snprintf(tzdbg.disp_buf + len, (debug_rw_buf_size - 1) - len, " Interrupt Number : 0x%x\n" @@ -547,7 +545,7 @@ static int _disp_tz_interrupt_stats(void) __func__); break; } - ptr += int_info_size; + tzdbg_ptr_tz40++; } } diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_atombios.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_atombios.c index 51a9942cdb40..f4cae5357e40 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_atombios.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_atombios.c @@ -681,6 +681,10 @@ int amdgpu_atombios_get_clock_info(struct amdgpu_device *adev) DRM_INFO("Changing default dispclk from %dMhz to 600Mhz\n", adev->clock.default_dispclk / 100); adev->clock.default_dispclk = 60000; + } else if (adev->clock.default_dispclk <= 60000) { + DRM_INFO("Changing default dispclk from %dMhz to 625Mhz\n", + adev->clock.default_dispclk / 100); + adev->clock.default_dispclk = 62500; } adev->clock.dp_extclk = le16_to_cpu(firmware_info->info_21.usUniphyDPModeExtClkFreq); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c index 25a3e2485cc2..2bc17a907ecf 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c @@ -124,6 +124,13 @@ int amdgpu_cs_get_ring(struct amdgpu_device *adev, u32 ip_type, } break; } + + if (!(*out_ring && (*out_ring)->adev)) { + DRM_ERROR("Ring %d is not initialized on IP %d\n", + ring, ip_type); + return -EINVAL; + } + return 0; } diff --git a/drivers/gpu/drm/amd/amdgpu/atombios_crtc.c b/drivers/gpu/drm/amd/amdgpu/atombios_crtc.c index 49aa35016653..247b088990dc 100644 --- a/drivers/gpu/drm/amd/amdgpu/atombios_crtc.c +++ b/drivers/gpu/drm/amd/amdgpu/atombios_crtc.c @@ -164,7 +164,7 @@ void amdgpu_atombios_crtc_powergate(struct drm_crtc *crtc, int state) struct drm_device *dev = crtc->dev; struct amdgpu_device *adev = dev->dev_private; int index = GetIndexIntoMasterTable(COMMAND, EnableDispPowerGating); - ENABLE_DISP_POWER_GATING_PARAMETERS_V2_1 args; + ENABLE_DISP_POWER_GATING_PS_ALLOCATION args; memset(&args, 0, sizeof(args)); @@ -177,7 +177,7 @@ void amdgpu_atombios_crtc_powergate(struct drm_crtc *crtc, int state) void amdgpu_atombios_crtc_powergate_init(struct amdgpu_device *adev) { int index = GetIndexIntoMasterTable(COMMAND, EnableDispPowerGating); - ENABLE_DISP_POWER_GATING_PARAMETERS_V2_1 args; + ENABLE_DISP_POWER_GATING_PS_ALLOCATION args; memset(&args, 0, sizeof(args)); diff --git a/drivers/gpu/drm/ast/ast_drv.h b/drivers/gpu/drm/ast/ast_drv.h index b92139e9b9d8..b5c64edeb668 100644 --- a/drivers/gpu/drm/ast/ast_drv.h +++ b/drivers/gpu/drm/ast/ast_drv.h @@ -113,7 +113,11 @@ struct ast_private { struct ttm_bo_kmap_obj cache_kmap; int next_cursor; bool support_wide_screen; - bool DisableP2A; + enum { + ast_use_p2a, + ast_use_dt, + ast_use_defaults + } config_mode; enum ast_tx_chip tx_chip_type; u8 dp501_maxclk; diff --git a/drivers/gpu/drm/ast/ast_main.c b/drivers/gpu/drm/ast/ast_main.c index 6c021165ca67..498a94069e6b 100644 --- a/drivers/gpu/drm/ast/ast_main.c +++ b/drivers/gpu/drm/ast/ast_main.c @@ -62,13 +62,84 @@ uint8_t ast_get_index_reg_mask(struct ast_private *ast, return ret; } +static void ast_detect_config_mode(struct drm_device *dev, u32 *scu_rev) +{ + struct device_node *np = dev->pdev->dev.of_node; + struct ast_private *ast = dev->dev_private; + uint32_t data, jregd0, jregd1; + + /* Defaults */ + ast->config_mode = ast_use_defaults; + *scu_rev = 0xffffffff; + + /* Check if we have device-tree properties */ + if (np && !of_property_read_u32(np, "aspeed,scu-revision-id", + scu_rev)) { + /* We do, disable P2A access */ + ast->config_mode = ast_use_dt; + DRM_INFO("Using device-tree for configuration\n"); + return; + } + + /* Not all families have a P2A bridge */ + if (dev->pdev->device != PCI_CHIP_AST2000) + return; + + /* + * The BMC will set SCU 0x40 D[12] to 1 if the P2 bridge + * is disabled. We force using P2A if VGA only mode bit + * is set D[7] + */ + jregd0 = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xd0, 0xff); + jregd1 = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xd1, 0xff); + if (!(jregd0 & 0x80) || !(jregd1 & 0x10)) { + /* Double check it's actually working */ + data = ast_read32(ast, 0xf004); + if (data != 0xFFFFFFFF) { + /* P2A works, grab silicon revision */ + ast->config_mode = ast_use_p2a; + + DRM_INFO("Using P2A bridge for configuration\n"); + + /* Read SCU7c (silicon revision register) */ + ast_write32(ast, 0xf004, 0x1e6e0000); + ast_write32(ast, 0xf000, 0x1); + *scu_rev = ast_read32(ast, 0x1207c); + return; + } + } + + /* We have a P2A bridge but it's disabled */ + DRM_INFO("P2A bridge disabled, using default configuration\n"); +} static int ast_detect_chip(struct drm_device *dev, bool *need_post) { struct ast_private *ast = dev->dev_private; - uint32_t data, jreg; + uint32_t jreg, scu_rev; + + /* + * If VGA isn't enabled, we need to enable now or subsequent + * access to the scratch registers will fail. We also inform + * our caller that it needs to POST the chip + * (Assumption: VGA not enabled -> need to POST) + */ + if (!ast_is_vga_enabled(dev)) { + ast_enable_vga(dev); + DRM_INFO("VGA not enabled on entry, requesting chip POST\n"); + *need_post = true; + } else + *need_post = false; + + + /* Enable extended register access */ + ast_enable_mmio(dev); ast_open_key(ast); + /* Find out whether P2A works or whether to use device-tree */ + ast_detect_config_mode(dev, &scu_rev); + + /* Identify chipset */ if (dev->pdev->device == PCI_CHIP_AST1180) { ast->chip = AST1100; DRM_INFO("AST 1180 detected\n"); @@ -80,12 +151,7 @@ static int ast_detect_chip(struct drm_device *dev, bool *need_post) ast->chip = AST2300; DRM_INFO("AST 2300 detected\n"); } else if (dev->pdev->revision >= 0x10) { - uint32_t data; - ast_write32(ast, 0xf004, 0x1e6e0000); - ast_write32(ast, 0xf000, 0x1); - - data = ast_read32(ast, 0x1207c); - switch (data & 0x0300) { + switch (scu_rev & 0x0300) { case 0x0200: ast->chip = AST1100; DRM_INFO("AST 1100 detected\n"); @@ -110,26 +176,6 @@ static int ast_detect_chip(struct drm_device *dev, bool *need_post) } } - /* - * If VGA isn't enabled, we need to enable now or subsequent - * access to the scratch registers will fail. We also inform - * our caller that it needs to POST the chip - * (Assumption: VGA not enabled -> need to POST) - */ - if (!ast_is_vga_enabled(dev)) { - ast_enable_vga(dev); - ast_enable_mmio(dev); - DRM_INFO("VGA not enabled on entry, requesting chip POST\n"); - *need_post = true; - } else - *need_post = false; - - /* Check P2A Access */ - ast->DisableP2A = true; - data = ast_read32(ast, 0xf004); - if (data != 0xFFFFFFFF) - ast->DisableP2A = false; - /* Check if we support wide screen */ switch (ast->chip) { case AST1180: @@ -146,17 +192,12 @@ static int ast_detect_chip(struct drm_device *dev, bool *need_post) ast->support_wide_screen = true; else { ast->support_wide_screen = false; - if (ast->DisableP2A == false) { - /* Read SCU7c (silicon revision register) */ - ast_write32(ast, 0xf004, 0x1e6e0000); - ast_write32(ast, 0xf000, 0x1); - data = ast_read32(ast, 0x1207c); - data &= 0x300; - if (ast->chip == AST2300 && data == 0x0) /* ast1300 */ - ast->support_wide_screen = true; - if (ast->chip == AST2400 && data == 0x100) /* ast1400 */ - ast->support_wide_screen = true; - } + if (ast->chip == AST2300 && + (scu_rev & 0x300) == 0x0) /* ast1300 */ + ast->support_wide_screen = true; + if (ast->chip == AST2400 && + (scu_rev & 0x300) == 0x100) /* ast1400 */ + ast->support_wide_screen = true; } break; } @@ -220,85 +261,102 @@ static int ast_detect_chip(struct drm_device *dev, bool *need_post) static int ast_get_dram_info(struct drm_device *dev) { + struct device_node *np = dev->pdev->dev.of_node; struct ast_private *ast = dev->dev_private; - uint32_t data, data2; - uint32_t denum, num, div, ref_pll; + uint32_t mcr_cfg, mcr_scu_mpll, mcr_scu_strap; + uint32_t denum, num, div, ref_pll, dsel; - if (ast->DisableP2A) - { + switch (ast->config_mode) { + case ast_use_dt: + /* + * If some properties are missing, use reasonable + * defaults for AST2400 + */ + if (of_property_read_u32(np, "aspeed,mcr-configuration", + &mcr_cfg)) + mcr_cfg = 0x00000577; + if (of_property_read_u32(np, "aspeed,mcr-scu-mpll", + &mcr_scu_mpll)) + mcr_scu_mpll = 0x000050C0; + if (of_property_read_u32(np, "aspeed,mcr-scu-strap", + &mcr_scu_strap)) + mcr_scu_strap = 0; + break; + case ast_use_p2a: + ast_write32(ast, 0xf004, 0x1e6e0000); + ast_write32(ast, 0xf000, 0x1); + mcr_cfg = ast_read32(ast, 0x10004); + mcr_scu_mpll = ast_read32(ast, 0x10120); + mcr_scu_strap = ast_read32(ast, 0x10170); + break; + case ast_use_defaults: + default: ast->dram_bus_width = 16; ast->dram_type = AST_DRAM_1Gx16; ast->mclk = 396; + return 0; } - else - { - ast_write32(ast, 0xf004, 0x1e6e0000); - ast_write32(ast, 0xf000, 0x1); - data = ast_read32(ast, 0x10004); - - if (data & 0x40) - ast->dram_bus_width = 16; - else - ast->dram_bus_width = 32; - if (ast->chip == AST2300 || ast->chip == AST2400) { - switch (data & 0x03) { - case 0: - ast->dram_type = AST_DRAM_512Mx16; - break; - default: - case 1: - ast->dram_type = AST_DRAM_1Gx16; - break; - case 2: - ast->dram_type = AST_DRAM_2Gx16; - break; - case 3: - ast->dram_type = AST_DRAM_4Gx16; - break; - } - } else { - switch (data & 0x0c) { - case 0: - case 4: - ast->dram_type = AST_DRAM_512Mx16; - break; - case 8: - if (data & 0x40) - ast->dram_type = AST_DRAM_1Gx16; - else - ast->dram_type = AST_DRAM_512Mx32; - break; - case 0xc: - ast->dram_type = AST_DRAM_1Gx32; - break; - } - } + if (mcr_cfg & 0x40) + ast->dram_bus_width = 16; + else + ast->dram_bus_width = 32; - data = ast_read32(ast, 0x10120); - data2 = ast_read32(ast, 0x10170); - if (data2 & 0x2000) - ref_pll = 14318; - else - ref_pll = 12000; - - denum = data & 0x1f; - num = (data & 0x3fe0) >> 5; - data = (data & 0xc000) >> 14; - switch (data) { - case 3: - div = 0x4; + if (ast->chip == AST2300 || ast->chip == AST2400) { + switch (mcr_cfg & 0x03) { + case 0: + ast->dram_type = AST_DRAM_512Mx16; break; - case 2: + default: case 1: - div = 0x2; + ast->dram_type = AST_DRAM_1Gx16; break; - default: - div = 0x1; + case 2: + ast->dram_type = AST_DRAM_2Gx16; + break; + case 3: + ast->dram_type = AST_DRAM_4Gx16; + break; + } + } else { + switch (mcr_cfg & 0x0c) { + case 0: + case 4: + ast->dram_type = AST_DRAM_512Mx16; + break; + case 8: + if (mcr_cfg & 0x40) + ast->dram_type = AST_DRAM_1Gx16; + else + ast->dram_type = AST_DRAM_512Mx32; + break; + case 0xc: + ast->dram_type = AST_DRAM_1Gx32; break; } - ast->mclk = ref_pll * (num + 2) / (denum + 2) * (div * 1000); } + + if (mcr_scu_strap & 0x2000) + ref_pll = 14318; + else + ref_pll = 12000; + + denum = mcr_scu_mpll & 0x1f; + num = (mcr_scu_mpll & 0x3fe0) >> 5; + dsel = (mcr_scu_mpll & 0xc000) >> 14; + switch (dsel) { + case 3: + div = 0x4; + break; + case 2: + case 1: + div = 0x2; + break; + default: + div = 0x1; + break; + } + ast->mclk = ref_pll * (num + 2) / (denum + 2) * (div * 1000); return 0; } diff --git a/drivers/gpu/drm/ast/ast_post.c b/drivers/gpu/drm/ast/ast_post.c index 270e8fb2803f..c7c58becb25d 100644 --- a/drivers/gpu/drm/ast/ast_post.c +++ b/drivers/gpu/drm/ast/ast_post.c @@ -375,17 +375,14 @@ void ast_post_gpu(struct drm_device *dev) ast_enable_mmio(dev); ast_set_def_ext_reg(dev); - if (ast->DisableP2A == false) - { + if (ast->config_mode == ast_use_p2a) { if (ast->chip == AST2300 || ast->chip == AST2400) ast_init_dram_2300(dev); else ast_init_dram_reg(dev); ast_init_3rdtx(dev); - } - else - { + } else { if (ast->tx_chip_type != AST_TX_NONE) ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xa3, 0xcf, 0x80); /* Enable DVO */ } diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile index 999d5e45e5c5..84125b3d1f95 100644 --- a/drivers/gpu/drm/msm/Makefile +++ b/drivers/gpu/drm/msm/Makefile @@ -149,6 +149,7 @@ msm_drm-$(CONFIG_DRM_MSM) += \ msm_ringbuffer.o \ msm_prop.o \ msm_snapshot.o \ - msm_submitqueue.o + msm_submitqueue.o \ + msm_trace_points.o obj-$(CONFIG_DRM_MSM) += msm_drm.o diff --git a/drivers/gpu/drm/msm/adreno/a3xx_gpu.c b/drivers/gpu/drm/msm/adreno/a3xx_gpu.c index a417e42944fc..e24827590b7c 100644 --- a/drivers/gpu/drm/msm/adreno/a3xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a3xx_gpu.c @@ -439,7 +439,6 @@ static const struct adreno_gpu_funcs funcs = { .pm_suspend = msm_gpu_pm_suspend, .pm_resume = msm_gpu_pm_resume, .recover = a3xx_recover, - .last_fence = adreno_last_fence, .submitted_fence = adreno_submitted_fence, .submit = adreno_submit, .flush = adreno_flush, diff --git a/drivers/gpu/drm/msm/adreno/a4xx_gpu.c b/drivers/gpu/drm/msm/adreno/a4xx_gpu.c index 069823f054f7..b612c9a18faf 100644 --- a/drivers/gpu/drm/msm/adreno/a4xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a4xx_gpu.c @@ -522,7 +522,6 @@ static const struct adreno_gpu_funcs funcs = { .pm_suspend = a4xx_pm_suspend, .pm_resume = a4xx_pm_resume, .recover = a4xx_recover, - .last_fence = adreno_last_fence, .submitted_fence = adreno_submitted_fence, .submit = adreno_submit, .flush = adreno_flush, diff --git a/drivers/gpu/drm/msm/adreno/a5xx_counters.c b/drivers/gpu/drm/msm/adreno/a5xx_counters.c index f1fac5535359..bc442039c308 100644 --- a/drivers/gpu/drm/msm/adreno/a5xx_counters.c +++ b/drivers/gpu/drm/msm/adreno/a5xx_counters.c @@ -106,7 +106,7 @@ static void a5xx_counter_enable_pm4(struct msm_gpu *gpu, { struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu); struct a5xx_gpu *a5xx_gpu = to_a5xx_gpu(adreno_gpu); - struct msm_ringbuffer *ring = gpu->rb[MSM_GPU_MAX_RINGS - 1]; + struct msm_ringbuffer *ring = gpu->rb[0]; struct adreno_counter *counter = &group->counters[counterid]; mutex_lock(&gpu->dev->struct_mutex); diff --git a/drivers/gpu/drm/msm/adreno/a5xx_gpu.c b/drivers/gpu/drm/msm/adreno/a5xx_gpu.c index f8dbc843f852..37323e962c2c 100644 --- a/drivers/gpu/drm/msm/adreno/a5xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a5xx_gpu.c @@ -13,6 +13,7 @@ #include "msm_gem.h" #include "msm_iommu.h" +#include "msm_trace.h" #include "a5xx_gpu.h" #define SECURE_VA_START 0xc0000000 @@ -100,12 +101,32 @@ static void a5xx_set_pagetable(struct msm_gpu *gpu, struct msm_ringbuffer *ring, OUT_RING(ring, 1); } +/* Inline PM4 code to get the current value of the 19.2 Mhz always on counter */ +static void a5xx_get_ticks(struct msm_ringbuffer *ring, uint64_t iova) +{ + /* + * Set bit[30] to make this command a 64 bit write operation. + * bits[18-29] is to specify number of consecutive registers + * to copy, so set this space with 2, since we want to copy + * data from REG_A5XX_RBBM_ALWAYSON_COUNTER_LO and [HI]. + */ + + OUT_PKT7(ring, CP_REG_TO_MEM, 3); + OUT_RING(ring, REG_A5XX_RBBM_ALWAYSON_COUNTER_LO | + (1 << 30) | (2 << 18)); + OUT_RING(ring, lower_32_bits(iova)); + OUT_RING(ring, upper_32_bits(iova)); +} + static void a5xx_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit) { struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu); struct a5xx_gpu *a5xx_gpu = to_a5xx_gpu(adreno_gpu); struct msm_ringbuffer *ring = gpu->rb[submit->ring]; unsigned int i, ibs = 0; + unsigned long flags; + u64 ticks; + ktime_t time; a5xx_set_pagetable(gpu, ring, submit->aspace); @@ -139,24 +160,15 @@ static void a5xx_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit) OUT_RING(ring, 1); } - /* Record the always on counter before command execution */ - if (submit->profile_buf_iova) { - uint64_t gpuaddr = submit->profile_buf_iova + - offsetof(struct drm_msm_gem_submit_profile_buffer, - ticks_submitted); + /* Record the GPU ticks at command start for kernel side profiling */ + a5xx_get_ticks(ring, + RING_TICKS_IOVA(ring, submit->tick_index, started)); - /* - * Set bit[30] to make this command a 64 bit write operation. - * bits[18-29] is to specify number of consecutive registers - * to copy, so set this space with 2, since we want to copy - * data from REG_A5XX_RBBM_ALWAYSON_COUNTER_LO and [HI]. - */ - OUT_PKT7(ring, CP_REG_TO_MEM, 3); - OUT_RING(ring, REG_A5XX_RBBM_ALWAYSON_COUNTER_LO | - (1 << 30) | (2 << 18)); - OUT_RING(ring, lower_32_bits(gpuaddr)); - OUT_RING(ring, upper_32_bits(gpuaddr)); - } + /* And for the user profiling too if it is enabled */ + if (submit->profile_buf_iova) + a5xx_get_ticks(ring, submit->profile_buf_iova + + offsetof(struct drm_msm_gem_submit_profile_buffer, + ticks_submitted)); /* Submit the commands */ for (i = 0; i < submit->nr_cmds; i++) { @@ -190,18 +202,15 @@ static void a5xx_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit) OUT_PKT7(ring, CP_YIELD_ENABLE, 1); OUT_RING(ring, 0x01); + /* Record the GPU ticks at command retire for kernel side profiling */ + a5xx_get_ticks(ring, + RING_TICKS_IOVA(ring, submit->tick_index, retired)); + /* Record the always on counter after command execution */ - if (submit->profile_buf_iova) { - uint64_t gpuaddr = submit->profile_buf_iova + + if (submit->profile_buf_iova) + a5xx_get_ticks(ring, submit->profile_buf_iova + offsetof(struct drm_msm_gem_submit_profile_buffer, - ticks_retired); - - OUT_PKT7(ring, CP_REG_TO_MEM, 3); - OUT_RING(ring, REG_A5XX_RBBM_ALWAYSON_COUNTER_LO | - (1 << 30) | (2 << 18)); - OUT_RING(ring, lower_32_bits(gpuaddr)); - OUT_RING(ring, upper_32_bits(gpuaddr)); - } + ticks_retired)); /* Write the fence to the scratch register */ OUT_PKT4(ring, REG_A5XX_CP_SCRATCH_REG(2), 1); @@ -237,33 +246,28 @@ static void a5xx_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit) /* Set bit 0 to trigger an interrupt on preempt complete */ OUT_RING(ring, 0x01); - if (submit->profile_buf_iova) { - unsigned long flags; - uint64_t ktime; - struct drm_msm_gem_submit_profile_buffer *profile_buf = - submit->profile_buf_vaddr; - - /* - * With this profiling, we are trying to create closest - * possible mapping between the CPU time domain(monotonic clock) - * and the GPU time domain(ticks). In order to make this - * happen, we need to briefly turn off interrupts to make sure - * interrupts do not run between collecting these two samples. - */ - local_irq_save(flags); - - profile_buf->ticks_queued = gpu_read64(gpu, - REG_A5XX_RBBM_ALWAYSON_COUNTER_LO, - REG_A5XX_RBBM_ALWAYSON_COUNTER_HI); + /* + * Get the current kernel time and ticks with interrupts off so we don't + * get interrupted between the operations and skew the numbers + */ - ktime = ktime_get_raw_ns(); + local_irq_save(flags); + ticks = gpu_read64(gpu, REG_A5XX_RBBM_ALWAYSON_COUNTER_LO, + REG_A5XX_RBBM_ALWAYSON_COUNTER_HI); + time = ktime_get_raw(); + local_irq_restore(flags); - local_irq_restore(flags); + if (submit->profile_buf) { + struct timespec64 ts = ktime_to_timespec64(time); - profile_buf->queue_time = ktime; - profile_buf->submit_time = ktime; + /* Write the data into the user-specified profile buffer */ + submit->profile_buf->time.tv_sec = ts.tv_sec; + submit->profile_buf->time.tv_nsec = ts.tv_nsec; + submit->profile_buf->ticks_queued = ticks; } + trace_msm_submitted(submit, ticks, ktime_to_ns(time)); + a5xx_flush(gpu, ring); /* Check to see if we need to start preemption */ @@ -770,6 +774,9 @@ static int a5xx_hw_init(struct msm_gpu *gpu) gpu_write(gpu, REG_A5XX_TPL1_ADDR_MODE_CNTL, 0x1); gpu_write(gpu, REG_A5XX_RBBM_SECVID_TSB_ADDR_MODE_CNTL, 0x1); + a5xx_gpu->timestamp_counter = adreno_get_counter(gpu, + MSM_COUNTER_GROUP_CP, 0, NULL, NULL); + /* Load the GPMU firmware before starting the HW init */ a5xx_gpmu_ucode_init(gpu); @@ -1214,8 +1221,11 @@ static int a5xx_pm_suspend(struct msm_gpu *gpu) static int a5xx_get_timestamp(struct msm_gpu *gpu, uint64_t *value) { - *value = gpu_read64(gpu, REG_A5XX_RBBM_PERFCTR_CP_0_LO, - REG_A5XX_RBBM_PERFCTR_CP_0_HI); + struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu); + struct a5xx_gpu *a5xx_gpu = to_a5xx_gpu(adreno_gpu); + + *value = adreno_read_counter(gpu, MSM_COUNTER_GROUP_CP, + a5xx_gpu->timestamp_counter); return 0; } @@ -1248,7 +1258,6 @@ static const struct adreno_gpu_funcs funcs = { .pm_suspend = a5xx_pm_suspend, .pm_resume = a5xx_pm_resume, .recover = a5xx_recover, - .last_fence = adreno_last_fence, .submitted_fence = adreno_submitted_fence, .submit = a5xx_submit, .flush = a5xx_flush, diff --git a/drivers/gpu/drm/msm/adreno/a5xx_gpu.h b/drivers/gpu/drm/msm/adreno/a5xx_gpu.h index 8eb3838ffe90..f8b00982fe86 100644 --- a/drivers/gpu/drm/msm/adreno/a5xx_gpu.h +++ b/drivers/gpu/drm/msm/adreno/a5xx_gpu.h @@ -56,6 +56,8 @@ struct a5xx_gpu { struct a5xx_smmu_info *smmu_info; struct drm_gem_object *smmu_info_bo; uint64_t smmu_info_iova; + + int timestamp_counter; }; #define to_a5xx_gpu(x) container_of(x, struct a5xx_gpu, base) diff --git a/drivers/gpu/drm/msm/adreno/a5xx_preempt.c b/drivers/gpu/drm/msm/adreno/a5xx_preempt.c index 57ef366cf82c..6ab3ba076c2f 100644 --- a/drivers/gpu/drm/msm/adreno/a5xx_preempt.c +++ b/drivers/gpu/drm/msm/adreno/a5xx_preempt.c @@ -68,7 +68,12 @@ static struct msm_ringbuffer *get_next_ring(struct msm_gpu *gpu) unsigned long flags; int i; - for (i = gpu->nr_rings - 1; i >= 0; i--) { + /* + * Find the highest prority ringbuffer that isn't empty and jump + * to it (0 being the highest and gpu->nr_rings - 1 being the + * lowest) + */ + for (i = 0; i < gpu->nr_rings; i++) { bool empty; struct msm_ringbuffer *ring = gpu->rb[i]; diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.c b/drivers/gpu/drm/msm/adreno/adreno_gpu.c index 9f3d957499d3..2273b06b59a6 100644 --- a/drivers/gpu/drm/msm/adreno/adreno_gpu.c +++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.c @@ -51,6 +51,9 @@ int adreno_get_param(struct msm_gpu *gpu, uint32_t param, uint64_t *value) if (adreno_gpu->funcs->get_timestamp) return adreno_gpu->funcs->get_timestamp(gpu, value); return -EINVAL; + case MSM_PARAM_NR_RINGS: + *value = gpu->nr_rings; + return 0; default: DBG("%s: invalid param: %u", gpu->name, param); return -EINVAL; @@ -127,11 +130,6 @@ uint32_t adreno_submitted_fence(struct msm_gpu *gpu, return ring->submitted_fence; } -uint32_t adreno_last_fence(struct msm_gpu *gpu, struct msm_ringbuffer *ring) -{ - return ring ? ring->memptrs->fence : 0; -} - void adreno_recover(struct msm_gpu *gpu) { struct drm_device *dev = gpu->dev; @@ -291,7 +289,7 @@ void adreno_show(struct msm_gpu *gpu, struct seq_file *m) continue; seq_printf(m, "rb %d: fence: %d/%d\n", i, - adreno_last_fence(gpu, ring), + ring->memptrs->fence, adreno_submitted_fence(gpu, ring)); seq_printf(m, " rptr: %d\n", @@ -341,7 +339,7 @@ void adreno_dump_info(struct msm_gpu *gpu) continue; dev_err(dev->dev, " ring %d: fence %d/%d rptr/wptr %x/%x\n", i, - adreno_last_fence(gpu, ring), + ring->memptrs->fence, adreno_submitted_fence(gpu, ring), get_rptr(adreno_gpu, ring), get_wptr(ring)); @@ -600,7 +598,7 @@ static void adreno_snapshot_ringbuffer(struct msm_gpu *gpu, header.rptr = get_rptr(adreno_gpu, ring); header.wptr = get_wptr(ring); header.timestamp_queued = adreno_submitted_fence(gpu, ring); - header.timestamp_retired = adreno_last_fence(gpu, ring); + header.timestamp_retired = ring->memptrs->fence; /* Write the header even if the ringbuffer data is empty */ if (!SNAPSHOT_HEADER(snapshot, header, SNAPSHOT_SECTION_RB_V2, diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.h b/drivers/gpu/drm/msm/adreno/adreno_gpu.h index c894956fb5e8..c96189fb805b 100644 --- a/drivers/gpu/drm/msm/adreno/adreno_gpu.h +++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.h @@ -217,7 +217,6 @@ static inline int adreno_is_a540(struct adreno_gpu *gpu) int adreno_get_param(struct msm_gpu *gpu, uint32_t param, uint64_t *value); int adreno_hw_init(struct msm_gpu *gpu); -uint32_t adreno_last_fence(struct msm_gpu *gpu, struct msm_ringbuffer *ring); uint32_t adreno_submitted_fence(struct msm_gpu *gpu, struct msm_ringbuffer *ring); void adreno_recover(struct msm_gpu *gpu); diff --git a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c index 2ee75a2c1039..1b295949122b 100644 --- a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c +++ b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c @@ -556,13 +556,13 @@ static const struct file_operations sde_hdmi_hdcp_state_fops = { .read = _sde_hdmi_hdcp_state_read, }; -static u64 _sde_hdmi_clip_valid_pclk(struct drm_display_mode *mode, u64 pclk_in) +static u64 _sde_hdmi_clip_valid_pclk(struct hdmi *hdmi, u64 pclk_in) { u32 pclk_delta, pclk; u64 pclk_clip = pclk_in; /* as per standard, 0.5% of deviation is allowed */ - pclk = mode->clock * HDMI_KHZ_TO_HZ; + pclk = hdmi->pixclock; pclk_delta = pclk * 5 / 1000; if (pclk_in < (pclk - pclk_delta)) @@ -700,7 +700,6 @@ static void sde_hdmi_tx_hdcp_cb_work(struct work_struct *work) static int _sde_hdmi_update_pll_delta(struct sde_hdmi *display, s32 ppm) { struct hdmi *hdmi = display->ctrl.ctrl; - struct drm_display_mode *current_mode = &display->mode; u64 cur_pclk, dst_pclk; u64 clip_pclk; int rc = 0; @@ -725,7 +724,7 @@ static int _sde_hdmi_update_pll_delta(struct sde_hdmi *display, s32 ppm) dst_pclk = cur_pclk * (1000000000 + ppm); do_div(dst_pclk, 1000000000); - clip_pclk = _sde_hdmi_clip_valid_pclk(current_mode, dst_pclk); + clip_pclk = _sde_hdmi_clip_valid_pclk(hdmi, dst_pclk); /* update pclk */ if (clip_pclk != cur_pclk) { @@ -1893,11 +1892,6 @@ struct drm_msm_ext_panel_hdr_metadata *hdr_meta) return; } - if (!connector->hdr_supported) { - SDE_ERROR("Sink does not support HDR\n"); - return; - } - /* Setup Packet header and payload */ packet_header = type_code | (version << 8) | (length << 16); hdmi_write(hdmi, HDMI_GENERIC0_HDR, packet_header); @@ -2131,9 +2125,13 @@ static int sde_hdmi_tx_check_capability(struct sde_hdmi *sde_hdmi) } } - SDE_DEBUG("%s: Features <HDMI:%s, HDCP:%s>\n", __func__, + if (sde_hdmi->hdmi_tx_major_version >= HDMI_TX_VERSION_4) + sde_hdmi->dc_feature_supported = true; + + SDE_DEBUG("%s: Features <HDMI:%s, HDCP:%s, Deep Color:%s>\n", __func__, hdmi_disabled ? "OFF" : "ON", - hdcp_disabled ? "OFF" : "ON"); + hdcp_disabled ? "OFF" : "ON", + sde_hdmi->dc_feature_supported ? "ON" : "OFF"); if (hdmi_disabled) { DEV_ERR("%s: HDMI disabled\n", __func__); @@ -2308,8 +2306,14 @@ int sde_hdmi_pre_kickoff(struct drm_connector *connector, struct msm_display_kickoff_params *params) { - sde_hdmi_panel_set_hdr_infoframe(display, - params->hdr_metadata); + if (!connector || !display || !params) { + pr_err("Invalid params\n"); + return -EINVAL; + } + + if (connector->hdr_supported) + sde_hdmi_panel_set_hdr_infoframe(display, + params->hdr_metadata); return 0; } diff --git a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h index 6b9bcfec031b..743d34d05e57 100644 --- a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h +++ b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h @@ -33,6 +33,9 @@ #include "sde_hdmi_util.h" #include "sde_hdcp.h" +#ifndef MIN +#define MIN(x, y) (((x) < (y)) ? (x) : (y)) +#endif #ifdef HDMI_DEBUG_ENABLE #define SDE_HDMI_DEBUG(fmt, args...) SDE_ERROR(fmt, ##args) #else @@ -110,6 +113,8 @@ enum hdmi_tx_feature_type { * @client_notify_pending: If there is client notification pending. * @irq_domain: IRQ domain structure. * @pll_update_enable: if it's allowed to update HDMI PLL ppm. + * @dc_enable: If deep color is enabled. Only DC_30 so far. + * @dc_feature_supported: If deep color feature is supported. * @notifier: CEC notifider to convey physical address information. * @root: Debug fs root entry. */ @@ -161,6 +166,8 @@ struct sde_hdmi { struct irq_domain *irq_domain; struct cec_notifier *notifier; bool pll_update_enable; + bool dc_enable; + bool dc_feature_supported; struct delayed_work hdcp_cb_work; struct dss_io_data io[HDMI_TX_MAX_IO]; @@ -188,6 +195,8 @@ enum hdmi_tx_scdc_access_type { #define HDMI_KHZ_TO_HZ 1000 #define HDMI_MHZ_TO_HZ 1000000 +#define HDMI_YUV420_24BPP_PCLK_TMDS_CH_RATE_RATIO 2 +#define HDMI_RGB_24BPP_PCLK_TMDS_CH_RATE_RATIO 1 /* Maximum pixel clock rates for hdmi tx */ #define HDMI_DEFAULT_MAX_PCLK_RATE 148500 diff --git a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_bridge.c b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_bridge.c index 24d2320683e4..a4c3c2e7ce46 100644 --- a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_bridge.c +++ b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_bridge.c @@ -345,7 +345,9 @@ static int _sde_hdmi_bridge_setup_scrambler(struct hdmi *hdmi, return 0; } - if (mode->clock > HDMI_TX_SCRAMBLER_THRESHOLD_RATE_KHZ) { + /* use actual clock instead of mode clock */ + if (hdmi->pixclock > + HDMI_TX_SCRAMBLER_THRESHOLD_RATE_KHZ * HDMI_KHZ_TO_HZ) { scrambler_on = true; tmds_clock_ratio = 1; } else { @@ -402,6 +404,38 @@ static int _sde_hdmi_bridge_setup_scrambler(struct hdmi *hdmi, return rc; } +static void _sde_hdmi_bridge_setup_deep_color(struct hdmi *hdmi) +{ + struct drm_connector *connector = hdmi->connector; + struct sde_connector *c_conn = to_sde_connector(connector); + struct sde_hdmi *display = (struct sde_hdmi *)c_conn->display; + u32 hdmi_ctrl_reg, vbi_pkt_reg; + + SDE_DEBUG("Deep Color: %s\n", display->dc_enable ? "On" : "Off"); + + if (display->dc_enable) { + hdmi_ctrl_reg = hdmi_read(hdmi, REG_HDMI_CTRL); + + /* GC CD override */ + hdmi_ctrl_reg |= BIT(27); + + /* enable deep color for RGB888/YUV444/YUV420 30 bits */ + hdmi_ctrl_reg |= BIT(24); + hdmi_write(hdmi, REG_HDMI_CTRL, hdmi_ctrl_reg); + /* Enable GC_CONT and GC_SEND in General Control Packet + * (GCP) register so that deep color data is + * transmitted to the sink on every frame, allowing + * the sink to decode the data correctly. + * + * GC_CONT: 0x1 - Send GCP on every frame + * GC_SEND: 0x1 - Enable GCP Transmission + */ + vbi_pkt_reg = hdmi_read(hdmi, REG_HDMI_VBI_PKT_CTRL); + vbi_pkt_reg |= BIT(5) | BIT(4); + hdmi_write(hdmi, REG_HDMI_VBI_PKT_CTRL, vbi_pkt_reg); + } +} + static void _sde_hdmi_bridge_pre_enable(struct drm_bridge *bridge) { struct sde_hdmi_bridge *sde_hdmi_bridge = to_hdmi_bridge(bridge); @@ -543,6 +577,10 @@ static void _sde_hdmi_bridge_set_avi_infoframe(struct hdmi *hdmi, struct hdmi_avi_infoframe info; drm_hdmi_avi_infoframe_from_display_mode(&info, mode); + + if (mode->private_flags & MSM_MODE_FLAG_COLOR_FORMAT_YCBCR420) + info.colorspace = HDMI_COLORSPACE_YUV420; + hdmi_avi_infoframe_pack(&info, avi_iframe, sizeof(avi_iframe)); checksum = avi_iframe[HDMI_INFOFRAME_HEADER_SIZE - 1]; @@ -660,31 +698,77 @@ static inline void _sde_hdmi_save_mode(struct hdmi *hdmi, drm_mode_copy(&display->mode, mode); } +static u32 _sde_hdmi_choose_best_format(struct hdmi *hdmi, + struct drm_display_mode *mode) +{ + /* + * choose priority: + * 1. DC + RGB + * 2. DC + YUV + * 3. RGB + * 4. YUV + */ + int dc_format; + struct drm_connector *connector = hdmi->connector; + + dc_format = sde_hdmi_sink_dc_support(connector, mode); + if (dc_format & MSM_MODE_FLAG_RGB444_DC_ENABLE) + return (MSM_MODE_FLAG_COLOR_FORMAT_RGB444 + | MSM_MODE_FLAG_RGB444_DC_ENABLE); + else if (dc_format & MSM_MODE_FLAG_YUV420_DC_ENABLE) + return (MSM_MODE_FLAG_COLOR_FORMAT_YCBCR420 + | MSM_MODE_FLAG_YUV420_DC_ENABLE); + else if (mode->flags & DRM_MODE_FLAG_SUPPORTS_RGB) + return MSM_MODE_FLAG_COLOR_FORMAT_RGB444; + else if (mode->flags & DRM_MODE_FLAG_SUPPORTS_YUV) + return MSM_MODE_FLAG_COLOR_FORMAT_YCBCR420; + + SDE_ERROR("Can't get available best display format\n"); + + return MSM_MODE_FLAG_COLOR_FORMAT_RGB444; +} + static void _sde_hdmi_bridge_mode_set(struct drm_bridge *bridge, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) { struct sde_hdmi_bridge *sde_hdmi_bridge = to_hdmi_bridge(bridge); struct hdmi *hdmi = sde_hdmi_bridge->hdmi; + struct drm_connector *connector = hdmi->connector; + struct sde_connector *c_conn = to_sde_connector(connector); + struct sde_hdmi *display = (struct sde_hdmi *)c_conn->display; int hstart, hend, vstart, vend; uint32_t frame_ctrl; + u32 div = 0; mode = adjusted_mode; - hdmi->pixclock = mode->clock * 1000; + display->dc_enable = mode->private_flags & + (MSM_MODE_FLAG_RGB444_DC_ENABLE | + MSM_MODE_FLAG_YUV420_DC_ENABLE); + /* compute pixclock as per color format and bit depth */ + hdmi->pixclock = sde_hdmi_calc_pixclk( + mode->clock * HDMI_KHZ_TO_HZ, + mode->private_flags, + display->dc_enable); + SDE_DEBUG("Actual PCLK: %lu, Mode PCLK: %d\n", + hdmi->pixclock, mode->clock); + + if (mode->private_flags & MSM_MODE_FLAG_COLOR_FORMAT_YCBCR420) + div = 1; - hstart = mode->htotal - mode->hsync_start; - hend = mode->htotal - mode->hsync_start + mode->hdisplay; + hstart = (mode->htotal - mode->hsync_start) >> div; + hend = (mode->htotal - mode->hsync_start + mode->hdisplay) >> div; vstart = mode->vtotal - mode->vsync_start - 1; vend = mode->vtotal - mode->vsync_start + mode->vdisplay - 1; - DRM_DEBUG( + SDE_DEBUG( "htotal=%d, vtotal=%d, hstart=%d, hend=%d, vstart=%d, vend=%d", mode->htotal, mode->vtotal, hstart, hend, vstart, vend); hdmi_write(hdmi, REG_HDMI_TOTAL, - SDE_HDMI_TOTAL_H_TOTAL(mode->htotal - 1) | + SDE_HDMI_TOTAL_H_TOTAL((mode->htotal >> div) - 1) | SDE_HDMI_TOTAL_V_TOTAL(mode->vtotal - 1)); hdmi_write(hdmi, REG_HDMI_ACTIVE_HSYNC, @@ -734,6 +818,22 @@ static void _sde_hdmi_bridge_mode_set(struct drm_bridge *bridge, _sde_hdmi_save_mode(hdmi, mode); _sde_hdmi_bridge_setup_scrambler(hdmi, mode); + _sde_hdmi_bridge_setup_deep_color(hdmi); +} + +static bool _sde_hdmi_bridge_mode_fixup(struct drm_bridge *bridge, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct sde_hdmi_bridge *sde_hdmi_bridge = to_hdmi_bridge(bridge); + struct hdmi *hdmi = sde_hdmi_bridge->hdmi; + + adjusted_mode->private_flags |= + _sde_hdmi_choose_best_format(hdmi, adjusted_mode); + SDE_DEBUG("Adjusted mode private flags: 0x%x\n", + adjusted_mode->private_flags); + + return true; } static const struct drm_bridge_funcs _sde_hdmi_bridge_funcs = { @@ -742,6 +842,7 @@ static const struct drm_bridge_funcs _sde_hdmi_bridge_funcs = { .disable = _sde_hdmi_bridge_disable, .post_disable = _sde_hdmi_bridge_post_disable, .mode_set = _sde_hdmi_bridge_mode_set, + .mode_fixup = _sde_hdmi_bridge_mode_fixup, }; diff --git a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_util.c b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_util.c index a7887d2c84b0..ba47b7702efd 100644 --- a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_util.c +++ b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_util.c @@ -825,3 +825,76 @@ int sde_hdmi_hdcp2p2_read_rxstatus(void *hdmi_display) } return rc; } + +unsigned long sde_hdmi_calc_pixclk(unsigned long pixel_freq, + u32 out_format, bool dc_enable) +{ + u32 rate_ratio = HDMI_RGB_24BPP_PCLK_TMDS_CH_RATE_RATIO; + + if (out_format & MSM_MODE_FLAG_COLOR_FORMAT_YCBCR420) + rate_ratio = HDMI_YUV420_24BPP_PCLK_TMDS_CH_RATE_RATIO; + + pixel_freq /= rate_ratio; + + if (dc_enable) + pixel_freq += pixel_freq >> 2; + + return pixel_freq; + +} + +bool sde_hdmi_validate_pixclk(struct drm_connector *connector, + unsigned long pclk) +{ + struct sde_connector *c_conn = to_sde_connector(connector); + struct sde_hdmi *display = (struct sde_hdmi *)c_conn->display; + unsigned long max_pclk = display->max_pclk_khz * HDMI_KHZ_TO_HZ; + + if (connector->max_tmds_char) + max_pclk = MIN(max_pclk, + connector->max_tmds_char * HDMI_MHZ_TO_HZ); + else if (connector->max_tmds_clock) + max_pclk = MIN(max_pclk, + connector->max_tmds_clock * HDMI_MHZ_TO_HZ); + + SDE_DEBUG("MAX PCLK = %ld, PCLK = %ld\n", max_pclk, pclk); + + return pclk < max_pclk; +} + +static bool sde_hdmi_check_dc_clock(struct drm_connector *connector, + struct drm_display_mode *mode, u32 format) +{ + struct sde_connector *c_conn = to_sde_connector(connector); + struct sde_hdmi *display = (struct sde_hdmi *)c_conn->display; + + u32 tmds_clk_with_dc = sde_hdmi_calc_pixclk( + mode->clock * HDMI_KHZ_TO_HZ, + format, + true); + + return (display->dc_feature_supported && + sde_hdmi_validate_pixclk(connector, tmds_clk_with_dc)); +} + +int sde_hdmi_sink_dc_support(struct drm_connector *connector, + struct drm_display_mode *mode) +{ + int dc_format = 0; + + if ((mode->flags & DRM_MODE_FLAG_SUPPORTS_YUV) && + (connector->display_info.edid_hdmi_dc_modes + & DRM_EDID_YCBCR420_DC_30)) + if (sde_hdmi_check_dc_clock(connector, mode, + MSM_MODE_FLAG_COLOR_FORMAT_YCBCR420)) + dc_format |= MSM_MODE_FLAG_YUV420_DC_ENABLE; + + if ((mode->flags & DRM_MODE_FLAG_SUPPORTS_RGB) && + (connector->display_info.edid_hdmi_dc_modes + & DRM_EDID_HDMI_DC_30)) + if (sde_hdmi_check_dc_clock(connector, mode, + MSM_MODE_FLAG_COLOR_FORMAT_RGB444)) + dc_format |= MSM_MODE_FLAG_RGB444_DC_ENABLE; + + return dc_format; +} diff --git a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_util.h b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_util.h index 8b69dd31f637..10effed54a14 100644 --- a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_util.h +++ b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_util.h @@ -164,4 +164,10 @@ int sde_hdmi_hdcp2p2_read_rxstatus(void *hdmi_display); void sde_hdmi_ddc_config(void *hdmi_display); int sde_hdmi_ddc_hdcp2p2_isr(void *hdmi_display); void sde_hdmi_dump_regs(void *hdmi_display); +unsigned long sde_hdmi_calc_pixclk(unsigned long pixel_freq, + u32 out_format, bool dc_enable); +bool sde_hdmi_validate_pixclk(struct drm_connector *connector, + unsigned long pclk); +int sde_hdmi_sink_dc_support(struct drm_connector *connector, + struct drm_display_mode *mode); #endif /* _SDE_HDMI_UTIL_H_ */ diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c index 367e701b59cb..924ce64206f4 100644 --- a/drivers/gpu/drm/msm/msm_drv.c +++ b/drivers/gpu/drm/msm/msm_drv.c @@ -316,8 +316,8 @@ static int get_mdp_ver(struct platform_device *pdev) static const struct of_device_id match_types[] = { { .compatible = "qcom,sde-kms", .data = (void *)KMS_SDE, - /* end node */ - } }; + }, + {} }; struct device *dev = &pdev->dev; const struct of_device_id *match; match = of_match_node(match_types, dev->of_node); @@ -1760,7 +1760,8 @@ static int msm_ioctl_submitqueue_new(struct drm_device *dev, void *data, if (args->flags & ~MSM_SUBMITQUEUE_FLAGS) return -EINVAL; - if (!file->is_master && args->prio >= gpu->nr_rings - 1) { + if ((gpu->nr_rings > 1) && + (!file->is_master && args->prio == 0)) { DRM_ERROR("Only DRM master can set highest priority ringbuffer\n"); return -EPERM; } diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h index d696f05e0459..ac15f399df7d 100644 --- a/drivers/gpu/drm/msm/msm_drv.h +++ b/drivers/gpu/drm/msm/msm_drv.h @@ -297,7 +297,6 @@ struct msm_drm_private { struct drm_fb_helper *fbdev; - uint32_t next_fence[MSM_GPU_MAX_RINGS]; uint32_t completed_fence[MSM_GPU_MAX_RINGS]; wait_queue_head_t fence_event; diff --git a/drivers/gpu/drm/msm/msm_gem.h b/drivers/gpu/drm/msm/msm_gem.h index e8528892939f..df9ddadc5c5c 100644 --- a/drivers/gpu/drm/msm/msm_gem.h +++ b/drivers/gpu/drm/msm/msm_gem.h @@ -151,9 +151,10 @@ struct msm_gem_submit { u32 flags; bool valid; uint64_t profile_buf_iova; - void *profile_buf_vaddr; + struct drm_msm_gem_submit_profile_buffer *profile_buf; bool secure; struct msm_gpu_submitqueue *queue; + int tick_index; unsigned int nr_cmds; unsigned int nr_bos; struct { diff --git a/drivers/gpu/drm/msm/msm_gem_submit.c b/drivers/gpu/drm/msm/msm_gem_submit.c index 7ccc146f3ae1..b73379aa9ed7 100644 --- a/drivers/gpu/drm/msm/msm_gem_submit.c +++ b/drivers/gpu/drm/msm/msm_gem_submit.c @@ -18,6 +18,7 @@ #include "msm_drv.h" #include "msm_gpu.h" #include "msm_gem.h" +#include "msm_trace.h" /* * Cmdstream submission: @@ -55,7 +56,7 @@ static struct msm_gem_submit *submit_create(struct drm_device *dev, submit->nr_bos = 0; submit->nr_cmds = 0; - submit->profile_buf_vaddr = NULL; + submit->profile_buf = NULL; submit->profile_buf_iova = 0; submit->cmd = (void *)&submit->bos[nr_bos]; @@ -510,9 +511,8 @@ int msm_ioctl_gem_submit(struct drm_device *dev, void *data, if (submit_cmd.type == MSM_SUBMIT_CMD_PROFILE_BUF) { submit->profile_buf_iova = submit->cmd[i].iova; - submit->profile_buf_vaddr = - msm_gem_vaddr(&msm_obj->base) + - submit_cmd.submit_offset; + submit->profile_buf = msm_gem_vaddr(&msm_obj->base) + + submit_cmd.submit_offset; } if (submit->valid) diff --git a/drivers/gpu/drm/msm/msm_gpu.c b/drivers/gpu/drm/msm/msm_gpu.c index d896e436251f..8073898e4275 100644 --- a/drivers/gpu/drm/msm/msm_gpu.c +++ b/drivers/gpu/drm/msm/msm_gpu.c @@ -18,7 +18,7 @@ #include "msm_gpu.h" #include "msm_gem.h" #include "msm_mmu.h" - +#include "msm_trace.h" /* * Power Management: @@ -310,8 +310,7 @@ static void recover_worker(struct work_struct *work) /* Retire all events that have already passed */ FOR_EACH_RING(gpu, ring, i) - retire_submits(gpu, ring, - gpu->funcs->last_fence(gpu, ring)); + retire_submits(gpu, ring, ring->memptrs->fence); retire_guilty_submit(gpu, gpu->funcs->active_ring(gpu)); @@ -319,7 +318,7 @@ static void recover_worker(struct work_struct *work) gpu->funcs->recover(gpu); /* Replay the remaining on all rings, highest priority first */ - for (i = gpu->nr_rings - 1; i >= 0; i--) { + for (i = 0; i < gpu->nr_rings; i++) { struct msm_ringbuffer *ring = gpu->rb[i]; list_for_each_entry(submit, &ring->submits, node) @@ -344,16 +343,16 @@ static void hangcheck_handler(unsigned long data) struct drm_device *dev = gpu->dev; struct msm_drm_private *priv = dev->dev_private; struct msm_ringbuffer *ring = gpu->funcs->active_ring(gpu); - uint32_t fence = gpu->funcs->last_fence(gpu, ring); + uint32_t fence = ring->memptrs->fence; uint32_t submitted = gpu->funcs->submitted_fence(gpu, ring); - if (fence != gpu->hangcheck_fence[ring->id]) { + if (fence != ring->hangcheck_fence) { /* some progress has been made.. ya! */ - gpu->hangcheck_fence[ring->id] = fence; + ring->hangcheck_fence = fence; } else if (fence < submitted) { struct msm_gem_submit *submit; - gpu->hangcheck_fence[ring->id] = fence; + ring->hangcheck_fence = fence; /* * No progress done, but see if the current submit is @@ -379,7 +378,7 @@ static void hangcheck_handler(unsigned long data) out: /* if still more pending work, reset the hangcheck timer: */ - if (submitted > gpu->hangcheck_fence[ring->id]) + if (submitted > ring->hangcheck_fence) hangcheck_timer_reset(gpu); /* workaround for missing irq: */ @@ -494,9 +493,18 @@ static void retire_submits(struct msm_gpu *gpu, struct msm_ringbuffer *ring, WARN_ON(!mutex_is_locked(&dev->struct_mutex)); list_for_each_entry_safe(submit, tmp, &ring->submits, node) { + struct msm_memptr_ticks *ticks; + if (submit->fence > fence) break; + ticks = &(ring->memptrs->ticks[submit->tick_index]); + + /* Add memory barrier to ensure the timer ticks are posted */ + rmb(); + + trace_msm_retired(submit, ticks->started, ticks->retired); + msm_gem_submit_free(submit); } } @@ -533,16 +541,13 @@ static void retire_worker(struct work_struct *work) int i; FOR_EACH_RING(gpu, ring, i) { - uint32_t fence; - if (!ring) continue; - fence = gpu->funcs->last_fence(gpu, ring); - msm_update_fence(gpu->dev, fence); + msm_update_fence(gpu->dev, ring->memptrs->fence); mutex_lock(&dev->struct_mutex); - _retire_ring(gpu, ring, fence); + _retire_ring(gpu, ring, ring->memptrs->fence); mutex_unlock(&dev->struct_mutex); } @@ -562,13 +567,12 @@ void msm_gpu_retire(struct msm_gpu *gpu) int msm_gpu_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit) { struct drm_device *dev = gpu->dev; - struct msm_drm_private *priv = dev->dev_private; struct msm_ringbuffer *ring = gpu->rb[submit->ring]; int i; WARN_ON(!mutex_is_locked(&dev->struct_mutex)); - submit->fence = FENCE(submit->ring, ++priv->next_fence[submit->ring]); + submit->fence = FENCE(submit->ring, ++ring->seqno); inactive_cancel(gpu); @@ -578,6 +582,12 @@ int msm_gpu_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit) ring->submitted_fence = submit->fence; + submit->tick_index = ring->tick_index; + ring->tick_index = (ring->tick_index + 1) % + ARRAY_SIZE(ring->memptrs->ticks); + + trace_msm_queued(submit); + update_sw_cntrs(gpu); for (i = 0; i < submit->nr_bos; i++) { diff --git a/drivers/gpu/drm/msm/msm_gpu.h b/drivers/gpu/drm/msm/msm_gpu.h index 306139bcd103..eeebfb746f7f 100644 --- a/drivers/gpu/drm/msm/msm_gpu.h +++ b/drivers/gpu/drm/msm/msm_gpu.h @@ -64,8 +64,6 @@ struct msm_gpu_funcs { void (*submit)(struct msm_gpu *gpu, struct msm_gem_submit *submit); void (*flush)(struct msm_gpu *gpu, struct msm_ringbuffer *ring); irqreturn_t (*irq)(struct msm_gpu *irq); - uint32_t (*last_fence)(struct msm_gpu *gpu, - struct msm_ringbuffer *ring); uint32_t (*submitted_fence)(struct msm_gpu *gpu, struct msm_ringbuffer *ring); struct msm_ringbuffer *(*active_ring)(struct msm_gpu *gpu); @@ -147,7 +145,6 @@ struct msm_gpu { #define DRM_MSM_HANGCHECK_PERIOD 500 /* in ms */ #define DRM_MSM_HANGCHECK_JIFFIES msecs_to_jiffies(DRM_MSM_HANGCHECK_PERIOD) struct timer_list hangcheck_timer; - uint32_t hangcheck_fence[MSM_GPU_MAX_RINGS]; struct work_struct recover_work; struct msm_snapshot *snapshot; }; @@ -186,7 +183,7 @@ static inline bool msm_gpu_active(struct msm_gpu *gpu) FOR_EACH_RING(gpu, ring, i) { if (gpu->funcs->submitted_fence(gpu, ring) > - gpu->funcs->last_fence(gpu, ring)) + ring->memptrs->fence) return true; } diff --git a/drivers/gpu/drm/msm/msm_kms.h b/drivers/gpu/drm/msm/msm_kms.h index 2ab50919f514..ed0ba928f170 100644 --- a/drivers/gpu/drm/msm/msm_kms.h +++ b/drivers/gpu/drm/msm/msm_kms.h @@ -34,6 +34,24 @@ #define MSM_MODE_FLAG_SEAMLESS_DYNAMIC_FPS (1<<0) /* Transition to new mode requires a wait-for-vblank before the modeset */ #define MSM_MODE_FLAG_VBLANK_PRE_MODESET (1<<1) +/* + * We need setting some flags in bridge, and using them in encoder. Add them in + * private_flags would be better for use. DRM_MODE_FLAG_SUPPORTS_RGB/YUV are + * flags that indicating the SINK supported color formats read from EDID. While, + * these flags defined here indicate the best color/bit depth foramt we choosed + * that would be better for display. For example the best mode display like: + * RGB+RGB_DC,YUV+YUV_DC, RGB,YUV. And we could not set RGB and YUV format at + * the same time. And also RGB_DC only set when RGB format is set,the same for + * YUV_DC. + */ +/* Enable RGB444 30 bit deep color */ +#define MSM_MODE_FLAG_RGB444_DC_ENABLE (1<<2) +/* Enable YUV420 30 bit deep color */ +#define MSM_MODE_FLAG_YUV420_DC_ENABLE (1<<3) +/* Choose RGB444 format to display */ +#define MSM_MODE_FLAG_COLOR_FORMAT_RGB444 (1<<4) +/* Choose YUV420 format to display */ +#define MSM_MODE_FLAG_COLOR_FORMAT_YCBCR420 (1<<5) /* As there are different display controller blocks depending on the * snapdragon version, the kms support is split out and the appropriate diff --git a/drivers/gpu/drm/msm/msm_prop.c b/drivers/gpu/drm/msm/msm_prop.c index 5f3d1b6356aa..10f89de25831 100644 --- a/drivers/gpu/drm/msm/msm_prop.c +++ b/drivers/gpu/drm/msm/msm_prop.c @@ -304,7 +304,7 @@ void msm_property_install_rotation(struct msm_property_info *info, void msm_property_install_enum(struct msm_property_info *info, const char *name, int flags, int is_bitmask, const struct drm_prop_enum_list *values, int num_values, - uint32_t property_idx) + uint32_t property_idx, uint64_t default_value) { struct drm_property **prop; @@ -337,7 +337,7 @@ void msm_property_install_enum(struct msm_property_info *info, } /* save init value for later */ - info->property_data[property_idx].default_value = 0; + info->property_data[property_idx].default_value = default_value; info->property_data[property_idx].force_dirty = false; /* always attach property, if created */ diff --git a/drivers/gpu/drm/msm/msm_prop.h b/drivers/gpu/drm/msm/msm_prop.h index 1430551700c7..6e600c4fd02f 100644 --- a/drivers/gpu/drm/msm/msm_prop.h +++ b/drivers/gpu/drm/msm/msm_prop.h @@ -267,6 +267,7 @@ void msm_property_install_rotation(struct msm_property_info *info, * @values: Array of allowable enumeration/bitmask values * @num_values: Size of values array * @property_idx: Property index + * @default_value: Default value of current property */ void msm_property_install_enum(struct msm_property_info *info, const char *name, @@ -274,7 +275,8 @@ void msm_property_install_enum(struct msm_property_info *info, int is_bitmask, const struct drm_prop_enum_list *values, int num_values, - uint32_t property_idx); + uint32_t property_idx, + uint64_t default_value); /** * msm_property_install_blob - install standard drm blob property diff --git a/drivers/gpu/drm/msm/msm_ringbuffer.h b/drivers/gpu/drm/msm/msm_ringbuffer.h index 3eb9a86b2a2e..e9678d57fffd 100644 --- a/drivers/gpu/drm/msm/msm_ringbuffer.h +++ b/drivers/gpu/drm/msm/msm_ringbuffer.h @@ -23,25 +23,39 @@ #define rbmemptr(ring, member) \ ((ring)->memptrs_iova + offsetof(struct msm_memptrs, member)) +struct msm_memptr_ticks { + uint64_t started; + uint64_t retired; +}; + struct msm_memptrs { volatile uint32_t rptr; volatile uint32_t fence; volatile uint64_t ttbr0; volatile unsigned int contextidr; + struct msm_memptr_ticks ticks[128]; }; +#define RING_TICKS_IOVA(ring, index, field) \ + ((ring)->memptrs_iova + offsetof(struct msm_memptrs, ticks) + \ + ((index) * sizeof(struct msm_memptr_ticks)) + \ + offsetof(struct msm_memptr_ticks, field)) + struct msm_ringbuffer { struct msm_gpu *gpu; int id; struct drm_gem_object *bo; uint32_t *start, *end, *cur, *next; uint64_t iova; + uint32_t seqno; uint32_t submitted_fence; spinlock_t lock; struct list_head submits; + uint32_t hangcheck_fence; struct msm_memptrs *memptrs; uint64_t memptrs_iova; + int tick_index; }; struct msm_ringbuffer *msm_ringbuffer_new(struct msm_gpu *gpu, int id, diff --git a/drivers/gpu/drm/msm/msm_submitqueue.c b/drivers/gpu/drm/msm/msm_submitqueue.c index 4f2af876db49..f79e74071c79 100644 --- a/drivers/gpu/drm/msm/msm_submitqueue.c +++ b/drivers/gpu/drm/msm/msm_submitqueue.c @@ -92,10 +92,10 @@ int msm_submitqueue_init(struct msm_file_private *ctx) /* * Add the "default" submitqueue with id 0 - * "medium" priority (3) and no flags + * "low" priority (2) and no flags */ - return msm_submitqueue_create(ctx, 3, 0, NULL); + return msm_submitqueue_create(ctx, 2, 0, NULL); } int msm_submitqueue_query(struct msm_file_private *ctx, u32 id, u32 param, diff --git a/drivers/gpu/drm/msm/msm_trace.h b/drivers/gpu/drm/msm/msm_trace.h new file mode 100644 index 000000000000..68c7ff78ffc2 --- /dev/null +++ b/drivers/gpu/drm/msm/msm_trace.h @@ -0,0 +1,98 @@ +/* Copyright (c) 2017 The Linux Foundation. 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 version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#if !defined(_MSM_TRACE_H_) || defined(TRACE_HEADER_MULTI_READ) +#define _MSM_TRACE_H_ + +#include <linux/tracepoint.h> + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM msm_drm +#define TRACE_INCLUDE_FILE msm_trace + +TRACE_EVENT(msm_queued, + TP_PROTO(struct msm_gem_submit *submit), + TP_ARGS(submit), + TP_STRUCT__entry( + __field(uint32_t, queue_id) + __field(uint32_t, fence_id) + __field(int, ring) + ), + TP_fast_assign( + __entry->queue_id = submit->queue->id; + __entry->fence_id = submit->fence; + __entry->ring = submit->ring; + ), + TP_printk( + "queue=%u fence=%u ring=%d", + __entry->queue_id, __entry->fence_id, __entry->ring + ) +); + +TRACE_EVENT(msm_submitted, + TP_PROTO(struct msm_gem_submit *submit, uint64_t ticks, uint64_t nsecs), + TP_ARGS(submit, ticks, nsecs), + TP_STRUCT__entry( + __field(uint32_t, queue_id) + __field(uint32_t, fence_id) + __field(int, ring) + __field(uint64_t, ticks) + __field(uint64_t, nsecs) + ), + TP_fast_assign( + __entry->queue_id = submit->queue->id; + __entry->fence_id = submit->fence; + __entry->ring = submit->ring; + __entry->ticks = ticks; + __entry->nsecs = nsecs; + ), + TP_printk( + "queue=%u fence=%u ring=%d ticks=%lld nsecs=%llu", + __entry->queue_id, __entry->fence_id, __entry->ring, + __entry->ticks, __entry->nsecs + ) +); + +TRACE_EVENT(msm_retired, + TP_PROTO(struct msm_gem_submit *submit, uint64_t start_ticks, + uint64_t retire_ticks), + TP_ARGS(submit, start_ticks, retire_ticks), + TP_STRUCT__entry( + __field(uint32_t, queue_id) + __field(uint32_t, fence_id) + __field(int, ring) + __field(uint64_t, start_ticks) + __field(uint64_t, retire_ticks) + ), + TP_fast_assign( + __entry->queue_id = submit->queue->id; + __entry->fence_id = submit->fence; + __entry->ring = submit->ring; + __entry->start_ticks = start_ticks; + __entry->retire_ticks = retire_ticks; + ), + TP_printk( + "queue=%u fence=%u ring=%d started=%lld retired=%lld", + __entry->queue_id, __entry->fence_id, __entry->ring, + __entry->start_ticks, __entry->retire_ticks + ) +); + + +#endif + +/* This part must be outside protection */ +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . +#include <trace/define_trace.h> + diff --git a/drivers/gpu/drm/msm/msm_trace_points.c b/drivers/gpu/drm/msm/msm_trace_points.c new file mode 100644 index 000000000000..41d9a975ac92 --- /dev/null +++ b/drivers/gpu/drm/msm/msm_trace_points.c @@ -0,0 +1,18 @@ +/* Copyright (c) 2017 The Linux Foundation. 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 version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT 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 "msm_gem.h" +#include "msm_gpu.h" + +#define CREATE_TRACE_POINTS +#include "msm_trace.h" diff --git a/drivers/gpu/drm/msm/sde/sde_connector.c b/drivers/gpu/drm/msm/sde/sde_connector.c index 0bb8298c1013..76e6e4ef6e7d 100644 --- a/drivers/gpu/drm/msm/sde/sde_connector.c +++ b/drivers/gpu/drm/msm/sde/sde_connector.c @@ -736,11 +736,11 @@ struct drm_connector *sde_connector_init(struct drm_device *dev, msm_property_install_enum(&c_conn->property_info, "topology_name", DRM_MODE_PROP_IMMUTABLE, 0, e_topology_name, ARRAY_SIZE(e_topology_name), - CONNECTOR_PROP_TOPOLOGY_NAME); + CONNECTOR_PROP_TOPOLOGY_NAME, 0); msm_property_install_enum(&c_conn->property_info, "topology_control", 0, 1, e_topology_control, ARRAY_SIZE(e_topology_control), - CONNECTOR_PROP_TOPOLOGY_CONTROL); + CONNECTOR_PROP_TOPOLOGY_CONTROL, 0); rc = msm_property_install_get_status(&c_conn->property_info); if (rc) { diff --git a/drivers/gpu/drm/msm/sde/sde_crtc.c b/drivers/gpu/drm/msm/sde/sde_crtc.c index 238d34419bf6..5323c0194594 100644 --- a/drivers/gpu/drm/msm/sde/sde_crtc.c +++ b/drivers/gpu/drm/msm/sde/sde_crtc.c @@ -368,12 +368,6 @@ void sde_crtc_prepare_commit(struct drm_crtc *crtc, cstate->is_rt = true; } - if (cstate->num_connectors > 0 && cstate->connectors[0]->encoder) - cstate->intf_mode = sde_encoder_get_intf_mode( - cstate->connectors[0]->encoder); - else - cstate->intf_mode = INTF_MODE_NONE; - /* prepare main output fence */ sde_fence_prepare(&sde_crtc->output_fence); } @@ -424,6 +418,22 @@ static void _sde_crtc_complete_flip(struct drm_crtc *crtc, spin_unlock_irqrestore(&dev->event_lock, flags); } +enum sde_intf_mode sde_crtc_get_intf_mode(struct drm_crtc *crtc) +{ + struct drm_encoder *encoder; + + if (!crtc || !crtc->dev) { + SDE_ERROR("invalid crtc\n"); + return INTF_MODE_NONE; + } + + drm_for_each_encoder(encoder, crtc->dev) + if (encoder->crtc == crtc) + return sde_encoder_get_intf_mode(encoder); + + return INTF_MODE_NONE; +} + static void sde_crtc_vblank_cb(void *data) { struct drm_crtc *crtc = (struct drm_crtc *)data; @@ -1361,7 +1371,7 @@ static void sde_crtc_install_properties(struct drm_crtc *crtc, msm_property_install_enum(&sde_crtc->property_info, "security_level", 0x0, 0, e_secure_level, ARRAY_SIZE(e_secure_level), - CRTC_PROP_SECURITY_LEVEL); + CRTC_PROP_SECURITY_LEVEL, SDE_DRM_SEC_NON_SEC); sde_kms_info_reset(info); @@ -1375,6 +1385,7 @@ static void sde_crtc_install_properties(struct drm_crtc *crtc, if (catalog->qseed_type == SDE_SSPP_SCALER_QSEED3) sde_kms_info_add_keystr(info, "qseed_type", "qseed3"); sde_kms_info_add_keyint(info, "has_src_split", catalog->has_src_split); + sde_kms_info_add_keyint(info, "has_hdr", catalog->has_hdr); if (catalog->perf.max_bw_low) sde_kms_info_add_keyint(info, "max_bandwidth_low", catalog->perf.max_bw_low); @@ -1665,7 +1676,7 @@ static int sde_crtc_debugfs_state_show(struct seq_file *s, void *v) seq_printf(s, "num_connectors: %d\n", cstate->num_connectors); seq_printf(s, "is_rt: %d\n", cstate->is_rt); - seq_printf(s, "intf_mode: %d\n", cstate->intf_mode); + seq_printf(s, "intf_mode: %d\n", sde_crtc_get_intf_mode(crtc)); seq_printf(s, "bw_ctl: %llu\n", cstate->cur_perf.bw_ctl); seq_printf(s, "core_clk_rate: %u\n", cstate->cur_perf.core_clk_rate); seq_printf(s, "max_per_pipe_ib: %llu\n", diff --git a/drivers/gpu/drm/msm/sde/sde_crtc.h b/drivers/gpu/drm/msm/sde/sde_crtc.h index 97a20b987ef5..aaa815c76c4e 100644 --- a/drivers/gpu/drm/msm/sde/sde_crtc.h +++ b/drivers/gpu/drm/msm/sde/sde_crtc.h @@ -246,16 +246,10 @@ void sde_crtc_cancel_pending_flip(struct drm_crtc *crtc, struct drm_file *file); bool sde_crtc_is_rt(struct drm_crtc *crtc); /** - * sde_crtc_get_intf_mode - get interface mode of the given crtc + * sde_crtc_get_intf_mode - get primary interface mode of the given crtc * @crtc: Pointert to crtc */ -static inline enum sde_intf_mode sde_crtc_get_intf_mode(struct drm_crtc *crtc) -{ - struct sde_crtc_state *cstate = - crtc ? to_sde_crtc_state(crtc->state) : NULL; - - return cstate ? cstate->intf_mode : INTF_MODE_NONE; -} +enum sde_intf_mode sde_crtc_get_intf_mode(struct drm_crtc *crtc); /** * sde_core_perf_crtc_is_wb - check if writeback is primary output of this crtc diff --git a/drivers/gpu/drm/msm/sde/sde_encoder.c b/drivers/gpu/drm/msm/sde/sde_encoder.c index 29444a83cf02..97c9f8baea6d 100644 --- a/drivers/gpu/drm/msm/sde/sde_encoder.c +++ b/drivers/gpu/drm/msm/sde/sde_encoder.c @@ -55,6 +55,33 @@ #define MAX_CHANNELS_PER_ENC 2 +/* rgb to yuv color space conversion matrix */ +static struct sde_csc_cfg sde_csc_10bit_convert[SDE_MAX_CSC] = { + [SDE_CSC_RGB2YUV_601L] = { + { + TO_S15D16(0x0083), TO_S15D16(0x0102), TO_S15D16(0x0032), + TO_S15D16(0xffb4), TO_S15D16(0xff6b), TO_S15D16(0x00e1), + TO_S15D16(0x00e1), TO_S15D16(0xff44), TO_S15D16(0xffdb), + }, + { 0x0, 0x0, 0x0,}, + { 0x0040, 0x0200, 0x0200,}, + { 0x0, 0x3ff, 0x0, 0x3ff, 0x0, 0x3ff,}, + { 0x0040, 0x03ac, 0x0040, 0x03c0, 0x0040, 0x03c0,}, + }, + + [SDE_CSC_RGB2YUV_601FR] = { + { + TO_S15D16(0x0099), TO_S15D16(0x012d), TO_S15D16(0x003a), + TO_S15D16(0xffaa), TO_S15D16(0xff56), TO_S15D16(0x0100), + TO_S15D16(0x0100), TO_S15D16(0xff2a), TO_S15D16(0xffd6), + }, + { 0x0, 0x0, 0x0,}, + { 0x0000, 0x0200, 0x0200,}, + { 0x0, 0x3ff, 0x0, 0x3ff, 0x0, 0x3ff,}, + { 0x0, 0x3ff, 0x0, 0x3ff, 0x0, 0x3ff,}, + }, +}; + /** * struct sde_encoder_virt - virtual encoder. Container of one or more physical * encoders. Virtual encoder manages one "logical" display. Physical @@ -1374,3 +1401,108 @@ enum sde_intf_mode sde_encoder_get_intf_mode(struct drm_encoder *encoder) return INTF_MODE_NONE; } + +/** + * sde_encoder_phys_setup_cdm - setup chroma down block + * @phys_enc: Pointer to physical encoder + * @output_type: HDMI/WB + * @format: Output format + * @roi: Output size + */ +void sde_encoder_phys_setup_cdm(struct sde_encoder_phys *phys_enc, + const struct sde_format *format, u32 output_type, + struct sde_rect *roi) +{ + struct drm_encoder *encoder = phys_enc->parent; + struct sde_encoder_virt *sde_enc = NULL; + struct sde_hw_cdm *hw_cdm = phys_enc->hw_cdm; + struct sde_hw_cdm_cfg *cdm_cfg = &phys_enc->cdm_cfg; + int ret; + u32 csc_type = 0; + + if (!encoder) { + SDE_ERROR("invalid encoder\n"); + return; + } + sde_enc = to_sde_encoder_virt(encoder); + + if (!SDE_FORMAT_IS_YUV(format)) { + SDE_DEBUG_ENC(sde_enc, "[cdm_disable fmt:%x]\n", + format->base.pixel_format); + + if (hw_cdm && hw_cdm->ops.disable) + hw_cdm->ops.disable(hw_cdm); + + return; + } + + memset(cdm_cfg, 0, sizeof(struct sde_hw_cdm_cfg)); + + cdm_cfg->output_width = roi->w; + cdm_cfg->output_height = roi->h; + cdm_cfg->output_fmt = format; + cdm_cfg->output_type = output_type; + cdm_cfg->output_bit_depth = SDE_FORMAT_IS_DX(format) ? + CDM_CDWN_OUTPUT_10BIT : CDM_CDWN_OUTPUT_8BIT; + + /* enable 10 bit logic */ + switch (cdm_cfg->output_fmt->chroma_sample) { + case SDE_CHROMA_RGB: + cdm_cfg->h_cdwn_type = CDM_CDWN_DISABLE; + cdm_cfg->v_cdwn_type = CDM_CDWN_DISABLE; + break; + case SDE_CHROMA_H2V1: + cdm_cfg->h_cdwn_type = CDM_CDWN_COSITE; + cdm_cfg->v_cdwn_type = CDM_CDWN_DISABLE; + break; + case SDE_CHROMA_420: + cdm_cfg->h_cdwn_type = CDM_CDWN_COSITE; + cdm_cfg->v_cdwn_type = CDM_CDWN_OFFSITE; + break; + case SDE_CHROMA_H1V2: + default: + SDE_ERROR("unsupported chroma sampling type\n"); + cdm_cfg->h_cdwn_type = CDM_CDWN_DISABLE; + cdm_cfg->v_cdwn_type = CDM_CDWN_DISABLE; + break; + } + + SDE_DEBUG_ENC(sde_enc, "[cdm_enable:%d,%d,%X,%d,%d,%d,%d]\n", + cdm_cfg->output_width, + cdm_cfg->output_height, + cdm_cfg->output_fmt->base.pixel_format, + cdm_cfg->output_type, + cdm_cfg->output_bit_depth, + cdm_cfg->h_cdwn_type, + cdm_cfg->v_cdwn_type); + + if (output_type == CDM_CDWN_OUTPUT_HDMI) + csc_type = SDE_CSC_RGB2YUV_601FR; + else if (output_type == CDM_CDWN_OUTPUT_WB) + csc_type = SDE_CSC_RGB2YUV_601L; + + if (hw_cdm && hw_cdm->ops.setup_csc_data) { + ret = hw_cdm->ops.setup_csc_data(hw_cdm, + &sde_csc_10bit_convert[csc_type]); + if (ret < 0) { + SDE_ERROR("failed to setup CSC %d\n", ret); + return; + } + } + + if (hw_cdm && hw_cdm->ops.setup_cdwn) { + ret = hw_cdm->ops.setup_cdwn(hw_cdm, cdm_cfg); + if (ret < 0) { + SDE_ERROR("failed to setup CDM %d\n", ret); + return; + } + } + + if (hw_cdm && hw_cdm->ops.enable) { + ret = hw_cdm->ops.enable(hw_cdm, cdm_cfg); + if (ret < 0) { + SDE_ERROR("failed to enable CDM %d\n", ret); + return; + } + } +} diff --git a/drivers/gpu/drm/msm/sde/sde_encoder_phys.h b/drivers/gpu/drm/msm/sde/sde_encoder_phys.h index 2205dd98a927..20f125155de3 100644 --- a/drivers/gpu/drm/msm/sde/sde_encoder_phys.h +++ b/drivers/gpu/drm/msm/sde/sde_encoder_phys.h @@ -349,8 +349,8 @@ struct sde_encoder_phys *sde_encoder_phys_wb_init( #endif void sde_encoder_phys_setup_cdm(struct sde_encoder_phys *phys_enc, - struct drm_framebuffer *fb, const struct sde_format *format, - struct sde_rect *wb_roi); + const struct sde_format *format, u32 output_type, + struct sde_rect *roi); /** * sde_encoder_helper_trigger_start - control start helper function diff --git a/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c b/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c index 0b6ee302e231..78a8b732b0de 100644 --- a/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c +++ b/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2017, The Linux Foundation. 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 version 2 and @@ -242,17 +242,20 @@ static void sde_encoder_phys_vid_setup_timing_engine( SDE_DEBUG_VIDENC(vid_enc, "enabling mode:\n"); drm_mode_debug_printmodeline(&mode); - if (phys_enc->split_role != ENC_ROLE_SOLO) { + if (phys_enc->split_role != ENC_ROLE_SOLO || + (mode.private_flags & MSM_MODE_FLAG_COLOR_FORMAT_YCBCR420)) { mode.hdisplay >>= 1; mode.htotal >>= 1; mode.hsync_start >>= 1; mode.hsync_end >>= 1; + mode.hskew >>= 1; SDE_DEBUG_VIDENC(vid_enc, - "split_role %d, halve horizontal %d %d %d %d\n", + "split_role %d, halve horizontal %d %d %d %d %d\n", phys_enc->split_role, mode.hdisplay, mode.htotal, - mode.hsync_start, mode.hsync_end); + mode.hsync_start, mode.hsync_end, + mode.hskew); } drm_mode_to_intf_timing_params(vid_enc, &mode, &timing_params); @@ -407,6 +410,9 @@ static void sde_encoder_phys_vid_mode_set( return; } + phys_enc->hw_ctl = NULL; + phys_enc->hw_cdm = NULL; + rm = &phys_enc->sde_kms->rm; vid_enc = to_sde_encoder_phys_vid(phys_enc); phys_enc->cached_mode = *adj_mode; @@ -427,6 +433,20 @@ static void sde_encoder_phys_vid_mode_set( phys_enc->hw_ctl = NULL; return; } + + /* CDM is optional */ + sde_rm_init_hw_iter(&iter, phys_enc->parent->base.id, SDE_HW_BLK_CDM); + for (i = 0; i <= instance; i++) { + sde_rm_get_hw(rm, &iter); + if (i == instance) + phys_enc->hw_cdm = (struct sde_hw_cdm *) iter.hw; + } + + if (IS_ERR(phys_enc->hw_cdm)) { + SDE_ERROR("CDM required but not allocated: %ld\n", + PTR_ERR(phys_enc->hw_cdm)); + phys_enc->hw_cdm = NULL; + } } static int sde_encoder_phys_vid_control_vblank_irq( @@ -477,6 +497,9 @@ static void sde_encoder_phys_vid_enable(struct sde_encoder_phys *phys_enc) struct sde_encoder_phys_vid *vid_enc; struct sde_hw_intf *intf; struct sde_hw_ctl *ctl; + struct sde_hw_cdm *hw_cdm = NULL; + struct drm_display_mode mode; + const struct sde_format *fmt = NULL; u32 flush_mask = 0; int ret; @@ -485,7 +508,9 @@ static void sde_encoder_phys_vid_enable(struct sde_encoder_phys *phys_enc) SDE_ERROR("invalid encoder/device\n"); return; } + hw_cdm = phys_enc->hw_cdm; priv = phys_enc->parent->dev->dev_private; + mode = phys_enc->cached_mode; vid_enc = to_sde_encoder_phys_vid(phys_enc); intf = vid_enc->hw_intf; @@ -520,7 +545,21 @@ static void sde_encoder_phys_vid_enable(struct sde_encoder_phys *phys_enc) goto end; } + if (mode.private_flags & MSM_MODE_FLAG_COLOR_FORMAT_YCBCR420) + fmt = sde_get_sde_format(DRM_FORMAT_YUV420); + + if (fmt) { + struct sde_rect hdmi_roi; + + hdmi_roi.w = mode.hdisplay; + hdmi_roi.h = mode.vdisplay; + sde_encoder_phys_setup_cdm(phys_enc, fmt, + CDM_CDWN_OUTPUT_HDMI, &hdmi_roi); + } + ctl->ops.get_bitmask_intf(ctl, &flush_mask, intf->idx); + if (ctl->ops.get_bitmask_cdm && hw_cdm) + ctl->ops.get_bitmask_cdm(ctl, &flush_mask, hw_cdm->idx); ctl->ops.update_pending_flush(ctl, flush_mask); SDE_DEBUG_VIDENC(vid_enc, "update pending flush ctl %d flush_mask %x\n", @@ -569,6 +608,8 @@ static void sde_encoder_phys_vid_get_hw_resources( SDE_DEBUG_VIDENC(vid_enc, "\n"); hw_res->intfs[vid_enc->hw_intf->idx - INTF_0] = INTF_MODE_VIDEO; + hw_res->needs_cdm = true; + SDE_DEBUG_DRIVER("[vid] needs_cdm=%d\n", hw_res->needs_cdm); } static int sde_encoder_phys_vid_wait_for_vblank( @@ -713,6 +754,11 @@ static void sde_encoder_phys_vid_disable(struct sde_encoder_phys *phys_enc) SDE_ERROR_VIDENC(vid_enc, "invalid vblank refcount %d\n", atomic_read(&phys_enc->vblank_refcount)); + if (phys_enc->hw_cdm && phys_enc->hw_cdm->ops.disable) { + SDE_DEBUG_DRIVER("[cdm_disable]\n"); + phys_enc->hw_cdm->ops.disable(phys_enc->hw_cdm); + } + phys_enc->enable_state = SDE_ENC_DISABLED; } diff --git a/drivers/gpu/drm/msm/sde/sde_encoder_phys_wb.c b/drivers/gpu/drm/msm/sde/sde_encoder_phys_wb.c index 9368c4974126..65b16419fcec 100644 --- a/drivers/gpu/drm/msm/sde/sde_encoder_phys_wb.c +++ b/drivers/gpu/drm/msm/sde/sde_encoder_phys_wb.c @@ -87,87 +87,6 @@ static void sde_encoder_phys_wb_set_traffic_shaper( } /** - * sde_encoder_phys_setup_cdm - setup chroma down block - * @phys_enc: Pointer to physical encoder - * @fb: Pointer to output framebuffer - * @format: Output format - */ -void sde_encoder_phys_setup_cdm(struct sde_encoder_phys *phys_enc, - struct drm_framebuffer *fb, const struct sde_format *format, - struct sde_rect *wb_roi) -{ - struct sde_hw_cdm *hw_cdm = phys_enc->hw_cdm; - struct sde_hw_cdm_cfg *cdm_cfg = &phys_enc->cdm_cfg; - int ret; - - if (!SDE_FORMAT_IS_YUV(format)) { - SDE_DEBUG("[cdm_disable fmt:%x]\n", - format->base.pixel_format); - - if (hw_cdm && hw_cdm->ops.disable) - hw_cdm->ops.disable(hw_cdm); - - return; - } - - memset(cdm_cfg, 0, sizeof(struct sde_hw_cdm_cfg)); - - cdm_cfg->output_width = wb_roi->w; - cdm_cfg->output_height = wb_roi->h; - cdm_cfg->output_fmt = format; - cdm_cfg->output_type = CDM_CDWN_OUTPUT_WB; - cdm_cfg->output_bit_depth = SDE_FORMAT_IS_DX(format) ? - CDM_CDWN_OUTPUT_10BIT : CDM_CDWN_OUTPUT_8BIT; - - /* enable 10 bit logic */ - switch (cdm_cfg->output_fmt->chroma_sample) { - case SDE_CHROMA_RGB: - cdm_cfg->h_cdwn_type = CDM_CDWN_DISABLE; - cdm_cfg->v_cdwn_type = CDM_CDWN_DISABLE; - break; - case SDE_CHROMA_H2V1: - cdm_cfg->h_cdwn_type = CDM_CDWN_COSITE; - cdm_cfg->v_cdwn_type = CDM_CDWN_DISABLE; - break; - case SDE_CHROMA_420: - cdm_cfg->h_cdwn_type = CDM_CDWN_COSITE; - cdm_cfg->v_cdwn_type = CDM_CDWN_OFFSITE; - break; - case SDE_CHROMA_H1V2: - default: - SDE_ERROR("unsupported chroma sampling type\n"); - cdm_cfg->h_cdwn_type = CDM_CDWN_DISABLE; - cdm_cfg->v_cdwn_type = CDM_CDWN_DISABLE; - break; - } - - SDE_DEBUG("[cdm_enable:%d,%d,%X,%d,%d,%d,%d]\n", - cdm_cfg->output_width, - cdm_cfg->output_height, - cdm_cfg->output_fmt->base.pixel_format, - cdm_cfg->output_type, - cdm_cfg->output_bit_depth, - cdm_cfg->h_cdwn_type, - cdm_cfg->v_cdwn_type); - - if (hw_cdm && hw_cdm->ops.setup_cdwn) { - ret = hw_cdm->ops.setup_cdwn(hw_cdm, cdm_cfg); - if (ret < 0) { - SDE_ERROR("failed to setup CDM %d\n", ret); - return; - } - } - - if (hw_cdm && hw_cdm->ops.enable) { - ret = hw_cdm->ops.enable(hw_cdm, cdm_cfg); - if (ret < 0) { - SDE_ERROR("failed to enable CDM %d\n", ret); - return; - } - } -} - -/** * sde_encoder_phys_wb_setup_fb - setup output framebuffer * @phys_enc: Pointer to physical encoder * @fb: Pointer to output framebuffer @@ -493,7 +412,8 @@ static void sde_encoder_phys_wb_setup( sde_encoder_phys_wb_set_traffic_shaper(phys_enc); - sde_encoder_phys_setup_cdm(phys_enc, fb, wb_enc->wb_fmt, wb_roi); + sde_encoder_phys_setup_cdm(phys_enc, wb_enc->wb_fmt, + CDM_CDWN_OUTPUT_WB, wb_roi); sde_encoder_phys_wb_setup_fb(phys_enc, fb, wb_roi); diff --git a/drivers/gpu/drm/msm/sde/sde_formats.c b/drivers/gpu/drm/msm/sde/sde_formats.c index 49b3d8e96938..2187d221a352 100644 --- a/drivers/gpu/drm/msm/sde/sde_formats.c +++ b/drivers/gpu/drm/msm/sde/sde_formats.c @@ -10,6 +10,8 @@ * GNU General Public License for more details. */ +#define pr_fmt(fmt) "[drm:%s:%d] " fmt, __func__, __LINE__ + #include <uapi/drm/drm_fourcc.h> #include <uapi/media/msm_media_info.h> @@ -408,6 +410,82 @@ static const struct sde_format sde_format_map[] = { }; /* + * A5x tile formats tables: + * These tables hold the A5x tile formats supported. + */ +static const struct sde_format sde_format_map_tile[] = { + INTERLEAVED_RGB_FMT(ARGB8888, + COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, + C3_ALPHA, C2_R_Cr, C0_G_Y, C1_B_Cb, 4, + true, 4, 0, + SDE_FETCH_UBWC, 1), + + INTERLEAVED_RGB_FMT(ABGR8888, + COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, + C3_ALPHA, C1_B_Cb, C0_G_Y, C2_R_Cr, 4, + true, 4, 0, + SDE_FETCH_UBWC, 1), + + INTERLEAVED_RGB_FMT(RGBA8888, + COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, + C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4, + true, 4, 0, + SDE_FETCH_UBWC, 1), + + INTERLEAVED_RGB_FMT(BGRA8888, + COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, + C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA, 4, + true, 4, 0, + SDE_FETCH_UBWC, 1), + + INTERLEAVED_RGB_FMT(BGRX8888, + COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, + C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA, 4, + false, 4, 0, + SDE_FETCH_UBWC, 1), + + INTERLEAVED_RGB_FMT(XRGB8888, + COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, + C3_ALPHA, C2_R_Cr, C0_G_Y, C1_B_Cb, 4, + false, 4, 0, + SDE_FETCH_UBWC, 1), + + INTERLEAVED_RGB_FMT(RGBX8888, + COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, + C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4, + false, 4, 0, + SDE_FETCH_UBWC, 1), + + PSEUDO_YUV_FMT(NV12, + 0, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, + C1_B_Cb, C2_R_Cr, + SDE_CHROMA_420, SDE_FORMAT_FLAG_YUV, + SDE_FETCH_UBWC, 2), + + PSEUDO_YUV_FMT(NV21, + 0, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, + C2_R_Cr, C1_B_Cb, + SDE_CHROMA_420, SDE_FORMAT_FLAG_YUV, + SDE_FETCH_UBWC, 2), +}; + +static const struct sde_format sde_format_map_p010_tile[] = { + PSEUDO_YUV_FMT_LOOSE(NV12, + 0, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, + C1_B_Cb, C2_R_Cr, + SDE_CHROMA_420, (SDE_FORMAT_FLAG_YUV | SDE_FORMAT_FLAG_DX), + SDE_FETCH_UBWC, 2), +}; + +static const struct sde_format sde_format_map_tp10_tile[] = { + PSEUDO_YUV_FMT(NV12, + 0, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, + C1_B_Cb, C2_R_Cr, + SDE_CHROMA_420, (SDE_FORMAT_FLAG_YUV | SDE_FORMAT_FLAG_DX), + SDE_FETCH_UBWC, 2), +}; + +/* * UBWC formats table: * This table holds the UBWC formats supported. * If a compression ratio needs to be used for this or any other format, @@ -417,37 +495,38 @@ static const struct sde_format sde_format_map_ubwc[] = { INTERLEAVED_RGB_FMT(BGR565, 0, COLOR_5BIT, COLOR_6BIT, COLOR_5BIT, C2_R_Cr, C0_G_Y, C1_B_Cb, 0, 3, - false, 2, 0, + false, 2, SDE_FORMAT_FLAG_COMPRESSED, SDE_FETCH_UBWC, 2), INTERLEAVED_RGB_FMT(ABGR8888, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4, - true, 4, 0, + true, 4, SDE_FORMAT_FLAG_COMPRESSED, SDE_FETCH_UBWC, 2), INTERLEAVED_RGB_FMT(XBGR8888, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4, - false, 4, 0, + false, 4, SDE_FORMAT_FLAG_COMPRESSED, SDE_FETCH_UBWC, 2), INTERLEAVED_RGB_FMT(ABGR2101010, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4, - true, 4, SDE_FORMAT_FLAG_DX, + true, 4, SDE_FORMAT_FLAG_DX | SDE_FORMAT_FLAG_COMPRESSED, SDE_FETCH_UBWC, 2), INTERLEAVED_RGB_FMT(XBGR2101010, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4, - true, 4, SDE_FORMAT_FLAG_DX, + true, 4, SDE_FORMAT_FLAG_DX | SDE_FORMAT_FLAG_COMPRESSED, SDE_FETCH_UBWC, 2), PSEUDO_YUV_FMT(NV12, 0, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, C1_B_Cb, C2_R_Cr, - SDE_CHROMA_420, SDE_FORMAT_FLAG_YUV, + SDE_CHROMA_420, SDE_FORMAT_FLAG_YUV | + SDE_FORMAT_FLAG_COMPRESSED, SDE_FETCH_UBWC, 4), }; @@ -463,7 +542,8 @@ static const struct sde_format sde_format_map_p010_ubwc[] = { PSEUDO_YUV_FMT_LOOSE(NV12, 0, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, C1_B_Cb, C2_R_Cr, - SDE_CHROMA_420, (SDE_FORMAT_FLAG_YUV | SDE_FORMAT_FLAG_DX), + SDE_CHROMA_420, (SDE_FORMAT_FLAG_YUV | SDE_FORMAT_FLAG_DX | + SDE_FORMAT_FLAG_COMPRESSED), SDE_FETCH_UBWC, 4), }; @@ -471,7 +551,8 @@ static const struct sde_format sde_format_map_tp10_ubwc[] = { PSEUDO_YUV_FMT(NV12, 0, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, C1_B_Cb, C2_R_Cr, - SDE_CHROMA_420, (SDE_FORMAT_FLAG_YUV | SDE_FORMAT_FLAG_DX), + SDE_CHROMA_420, (SDE_FORMAT_FLAG_YUV | SDE_FORMAT_FLAG_DX | + SDE_FORMAT_FLAG_COMPRESSED), SDE_FETCH_UBWC, 4), }; @@ -545,6 +626,7 @@ static int _sde_format_get_plane_sizes_ubwc( { int i; int color; + bool meta = SDE_FORMAT_IS_UBWC(fmt); memset(layout, 0, sizeof(struct sde_hw_fmt_layout)); layout->format = fmt; @@ -564,7 +646,7 @@ static int _sde_format_get_plane_sizes_ubwc( uint32_t y_meta_scanlines = 0; uint32_t uv_meta_scanlines = 0; - layout->num_planes = 4; + layout->num_planes = 2; layout->plane_pitch[0] = VENUS_Y_STRIDE(color, width); y_sclines = VENUS_Y_SCANLINES(color, height); layout->plane_size[0] = MSM_MEDIA_ALIGN(layout->plane_pitch[0] * @@ -575,6 +657,10 @@ static int _sde_format_get_plane_sizes_ubwc( layout->plane_size[1] = MSM_MEDIA_ALIGN(layout->plane_pitch[1] * uv_sclines, SDE_UBWC_PLANE_SIZE_ALIGNMENT); + if (!meta) + goto done; + + layout->num_planes += 2; layout->plane_pitch[2] = VENUS_Y_META_STRIDE(color, width); y_meta_scanlines = VENUS_Y_META_SCANLINES(color, height); layout->plane_size[2] = MSM_MEDIA_ALIGN(layout->plane_pitch[2] * @@ -588,19 +674,23 @@ static int _sde_format_get_plane_sizes_ubwc( } else { uint32_t rgb_scanlines, rgb_meta_scanlines; - layout->num_planes = 3; + layout->num_planes = 1; layout->plane_pitch[0] = VENUS_RGB_STRIDE(color, width); rgb_scanlines = VENUS_RGB_SCANLINES(color, height); layout->plane_size[0] = MSM_MEDIA_ALIGN(layout->plane_pitch[0] * rgb_scanlines, SDE_UBWC_PLANE_SIZE_ALIGNMENT); + if (!meta) + goto done; + layout->num_planes += 2; layout->plane_pitch[2] = VENUS_RGB_META_STRIDE(color, width); rgb_meta_scanlines = VENUS_RGB_META_SCANLINES(color, height); layout->plane_size[2] = MSM_MEDIA_ALIGN(layout->plane_pitch[2] * rgb_meta_scanlines, SDE_UBWC_PLANE_SIZE_ALIGNMENT); } +done: for (i = 0; i < SDE_MAX_PLANES; i++) layout->total_size += layout->plane_size[i]; @@ -667,7 +757,7 @@ static int _sde_format_get_plane_sizes_linear( return 0; } -static int _sde_format_get_plane_sizes( +int sde_format_get_plane_sizes( const struct sde_format *fmt, const uint32_t w, const uint32_t h, @@ -683,7 +773,7 @@ static int _sde_format_get_plane_sizes( return -ERANGE; } - if (SDE_FORMAT_IS_UBWC(fmt)) + if (SDE_FORMAT_IS_UBWC(fmt) || SDE_FORMAT_IS_TILE(fmt)) return _sde_format_get_plane_sizes_ubwc(fmt, w, h, layout); return _sde_format_get_plane_sizes_linear(fmt, w, h, layout); @@ -695,6 +785,7 @@ static int _sde_format_populate_addrs_ubwc( struct sde_hw_fmt_layout *layout) { uint32_t base_addr; + bool meta; if (!fb || !layout) { DRM_ERROR("invalid pointers\n"); @@ -707,6 +798,8 @@ static int _sde_format_populate_addrs_ubwc( return -EFAULT; } + meta = SDE_FORMAT_IS_UBWC(layout->format); + /* Per-format logic for verifying active planes */ if (SDE_FORMAT_IS_YUV(layout->format)) { /************************************************/ @@ -736,6 +829,9 @@ static int _sde_format_populate_addrs_ubwc( layout->plane_addr[1] = base_addr + layout->plane_size[0] + layout->plane_size[2] + layout->plane_size[3]; + if (!meta) + goto done; + /* configure Y metadata plane */ layout->plane_addr[2] = base_addr; @@ -763,10 +859,14 @@ static int _sde_format_populate_addrs_ubwc( layout->plane_addr[0] = base_addr + layout->plane_size[2]; layout->plane_addr[1] = 0; + + if (!meta) + goto done; + layout->plane_addr[2] = base_addr; layout->plane_addr[3] = 0; } - +done: return 0; } @@ -820,7 +920,7 @@ int sde_format_populate_layout( layout->format = to_sde_format(msm_framebuffer_format(fb)); /* Populate the plane sizes etc via get_format */ - ret = _sde_format_get_plane_sizes(layout->format, fb->width, fb->height, + ret = sde_format_get_plane_sizes(layout->format, fb->width, fb->height, layout); if (ret) return ret; @@ -829,7 +929,8 @@ int sde_format_populate_layout( plane_addr[i] = layout->plane_addr[i]; /* Populate the addresses given the fb */ - if (SDE_FORMAT_IS_UBWC(layout->format)) + if (SDE_FORMAT_IS_UBWC(layout->format) || + SDE_FORMAT_IS_TILE(layout->format)) ret = _sde_format_populate_addrs_ubwc(aspace, fb, layout); else ret = _sde_format_populate_addrs_linear(aspace, fb, layout); @@ -923,7 +1024,7 @@ int sde_format_check_modified_format( fmt = to_sde_format(msm_fmt); num_base_fmt_planes = drm_format_num_planes(fmt->base.pixel_format); - ret = _sde_format_get_plane_sizes(fmt, cmd->width, cmd->height, + ret = sde_format_get_plane_sizes(fmt, cmd->width, cmd->height, &layout); if (ret) return ret; @@ -962,14 +1063,14 @@ const struct sde_format *sde_get_sde_format_ext( * All planes used must specify the same modifier. */ if (modifiers_len && !modifiers) { - DRM_ERROR("invalid modifiers array\n"); + SDE_ERROR("invalid modifiers array\n"); return NULL; } else if (modifiers && modifiers_len && modifiers[0]) { mod0 = modifiers[0]; - DBG("plane format modifier 0x%llX", mod0); + SDE_DEBUG("plane format modifier 0x%llX\n", mod0); for (i = 1; i < modifiers_len; i++) { if (modifiers[i] != mod0) { - DRM_ERROR("bad fmt mod 0x%llX on plane %d\n", + SDE_ERROR("bad fmt mod 0x%llX on plane %d\n", modifiers[i], i); return NULL; } @@ -982,29 +1083,55 @@ const struct sde_format *sde_get_sde_format_ext( map_size = ARRAY_SIZE(sde_format_map); break; case DRM_FORMAT_MOD_QCOM_COMPRESSED: + case DRM_FORMAT_MOD_QCOM_COMPRESSED | DRM_FORMAT_MOD_QCOM_TILE: map = sde_format_map_ubwc; map_size = ARRAY_SIZE(sde_format_map_ubwc); - DBG("found fmt 0x%X DRM_FORMAT_MOD_QCOM_COMPRESSED", format); + SDE_DEBUG("found fmt 0x%X DRM_FORMAT_MOD_QCOM_COMPRESSED\n", + format); break; case DRM_FORMAT_MOD_QCOM_DX: map = sde_format_map_p010; map_size = ARRAY_SIZE(sde_format_map_p010); - DBG("found fmt 0x%X DRM_FORMAT_MOD_QCOM_DX", format); + SDE_DEBUG("found fmt 0x%X DRM_FORMAT_MOD_QCOM_DX\n", format); break; case (DRM_FORMAT_MOD_QCOM_DX | DRM_FORMAT_MOD_QCOM_COMPRESSED): + case (DRM_FORMAT_MOD_QCOM_DX | DRM_FORMAT_MOD_QCOM_COMPRESSED | + DRM_FORMAT_MOD_QCOM_TILE): map = sde_format_map_p010_ubwc; map_size = ARRAY_SIZE(sde_format_map_p010_ubwc); - DBG("found fmt 0x%X DRM_FORMAT_MOD_QCOM_COMPRESSED/DX", format); + SDE_DEBUG("found fmt 0x%X DRM_FORMAT_MOD_QCOM_COMPRESSED/DX\n", + format); break; case (DRM_FORMAT_MOD_QCOM_DX | DRM_FORMAT_MOD_QCOM_COMPRESSED | DRM_FORMAT_MOD_QCOM_TIGHT): + case (DRM_FORMAT_MOD_QCOM_DX | DRM_FORMAT_MOD_QCOM_COMPRESSED | + DRM_FORMAT_MOD_QCOM_TIGHT | DRM_FORMAT_MOD_QCOM_TILE): map = sde_format_map_tp10_ubwc; map_size = ARRAY_SIZE(sde_format_map_tp10_ubwc); - DBG("found fmt 0x%X DRM_FORMAT_MOD_QCOM_COMPRESSED/DX/TIGHT", + SDE_DEBUG( + "found fmt 0x%X DRM_FORMAT_MOD_QCOM_COMPRESSED/DX/TIGHT\n", format); break; + case DRM_FORMAT_MOD_QCOM_TILE: + map = sde_format_map_tile; + map_size = ARRAY_SIZE(sde_format_map_tile); + SDE_DEBUG("found fmt 0x%X DRM_FORMAT_MOD_QCOM_TILE\n", format); + break; + case (DRM_FORMAT_MOD_QCOM_TILE | DRM_FORMAT_MOD_QCOM_DX): + map = sde_format_map_p010_tile; + map_size = ARRAY_SIZE(sde_format_map_p010_tile); + SDE_DEBUG("found fmt 0x%X DRM_FORMAT_MOD_QCOM_TILE/DX\n", + format); + break; + case (DRM_FORMAT_MOD_QCOM_TILE | DRM_FORMAT_MOD_QCOM_DX | + DRM_FORMAT_MOD_QCOM_TIGHT): + map = sde_format_map_tp10_tile; + map_size = ARRAY_SIZE(sde_format_map_tp10_tile); + SDE_DEBUG("found fmt 0x%X DRM_FORMAT_MOD_QCOM_TILE/DX/TIGHT\n", + format); + break; default: - DRM_ERROR("unsupported format modifier %llX\n", mod0); + SDE_ERROR("unsupported format modifier %llX\n", mod0); return NULL; } @@ -1016,10 +1143,10 @@ const struct sde_format *sde_get_sde_format_ext( } if (fmt == NULL) - DRM_ERROR("unsupported fmt 0x%X modifier 0x%llX\n", + SDE_ERROR("unsupported fmt 0x%X modifier 0x%llX\n", format, mod0); else - DBG("fmt %s mod 0x%llX ubwc %d yuv %d", + SDE_DEBUG("fmt %s mod 0x%llX ubwc %d yuv %d\n", drm_get_format_name(format), mod0, SDE_FORMAT_IS_UBWC(fmt), SDE_FORMAT_IS_YUV(fmt)); diff --git a/drivers/gpu/drm/msm/sde/sde_formats.h b/drivers/gpu/drm/msm/sde/sde_formats.h index 0de081d619b7..ec8f97da4a41 100644 --- a/drivers/gpu/drm/msm/sde/sde_formats.h +++ b/drivers/gpu/drm/msm/sde/sde_formats.h @@ -59,6 +59,21 @@ uint32_t sde_populate_formats( uint32_t pixel_formats_max); /** + * sde_format_get_plane_sizes - calculate size and layout of given buffer format + * @fmt: pointer to sde_format + * @w: width of the buffer + * @h: height of the buffer + * @layout: layout of the buffer + * + * Return: size of the buffer + */ +int sde_format_get_plane_sizes( + const struct sde_format *fmt, + const uint32_t w, + const uint32_t h, + struct sde_hw_fmt_layout *layout); + +/** * sde_format_check_modified_format - validate format and buffers for * sde non-standard, i.e. modified format * @kms: kms driver diff --git a/drivers/gpu/drm/msm/sde/sde_hw_catalog.c b/drivers/gpu/drm/msm/sde/sde_hw_catalog.c index 4f84e31db5f6..635c2e4e954b 100644 --- a/drivers/gpu/drm/msm/sde/sde_hw_catalog.c +++ b/drivers/gpu/drm/msm/sde/sde_hw_catalog.c @@ -428,8 +428,8 @@ static uint32_t _sde_copy_formats( return 0; for (i = 0, cur_pos = dst_list_pos; - (cur_pos < (dst_list_size - 1)) && src_list[i].fourcc_format - && (i < src_list_size); ++i, ++cur_pos) + (cur_pos < (dst_list_size - 1)) && (i < src_list_size) + && src_list[i].fourcc_format; ++i, ++cur_pos) dst_list[cur_pos] = src_list[i]; dst_list[cur_pos].fourcc_format = 0; @@ -503,6 +503,7 @@ static int _validate_dt_entry(struct device_node *np, rc = -EINVAL; } *off_count = 0; + memset(prop_count, 0, sizeof(int) * prop_size); return rc; } } @@ -2074,6 +2075,11 @@ static int sde_hardware_format_caps(struct sde_mdss_cfg *sde_cfg, goto end; } + if (IS_SDE_MAJOR_MINOR_SAME((hw_rev), SDE_HW_VER_300) || + IS_SDE_MAJOR_MINOR_SAME((hw_rev), SDE_HW_VER_301)) { + sde_cfg->has_hdr = true; + } + index = _sde_copy_formats(sde_cfg->dma_formats, dma_list_size, 0, plane_formats, ARRAY_SIZE(plane_formats)); index += _sde_copy_formats(sde_cfg->dma_formats, dma_list_size, diff --git a/drivers/gpu/drm/msm/sde/sde_hw_catalog.h b/drivers/gpu/drm/msm/sde/sde_hw_catalog.h index 01204df48871..73bb77b7afa6 100644 --- a/drivers/gpu/drm/msm/sde/sde_hw_catalog.h +++ b/drivers/gpu/drm/msm/sde/sde_hw_catalog.h @@ -655,6 +655,7 @@ struct sde_vp_cfg { * @csc_type csc or csc_10bit support. * @has_src_split source split feature status * @has_cdp Client driver prefetch feature status + * @has_hdr HDR feature support * @dma_formats Supported formats for dma pipe * @cursor_formats Supported formats for cursor pipe * @vig_formats Supported formats for vig pipe @@ -672,7 +673,7 @@ struct sde_mdss_cfg { u32 csc_type; bool has_src_split; bool has_cdp; - + bool has_hdr; u32 mdss_count; struct sde_mdss_base_cfg mdss[MAX_BLOCKS]; diff --git a/drivers/gpu/drm/msm/sde/sde_hw_cdm.c b/drivers/gpu/drm/msm/sde/sde_hw_cdm.c index c7cbb93bece4..9ec81c227e60 100644 --- a/drivers/gpu/drm/msm/sde/sde_hw_cdm.c +++ b/drivers/gpu/drm/msm/sde/sde_hw_cdm.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2017, The Linux Foundation. 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 version 2 and @@ -88,50 +88,12 @@ static struct sde_cdm_cfg *_cdm_offset(enum sde_cdm cdm, return ERR_PTR(-EINVAL); } -static void sde_hw_cdm_setup_csc_10bit(struct sde_hw_cdm *ctx, +static int sde_hw_cdm_setup_csc_10bit(struct sde_hw_cdm *ctx, struct sde_csc_cfg *data) { - struct sde_hw_blk_reg_map *c = &ctx->hw; - u32 csc_reg_off = CDM_CSC_10_MATRIX_COEFF_0; - u32 val; - - /* matrix coeff */ - val = data->csc_mv[0] | (data->csc_mv[1] << 16); - SDE_REG_WRITE(c, csc_reg_off, val); - val = data->csc_mv[2] | (data->csc_mv[3] << 16); - SDE_REG_WRITE(c, csc_reg_off + 0x4, val); - val = data->csc_mv[4] | (data->csc_mv[5] << 16); - SDE_REG_WRITE(c, csc_reg_off + 0x8, val); - val = data->csc_mv[6] | (data->csc_mv[7] << 16); - SDE_REG_WRITE(c, csc_reg_off + 0xc, val); - val = data->csc_mv[8]; - SDE_REG_WRITE(c, csc_reg_off + 0x10, val); - - /* Pre clamp */ - val = (data->csc_pre_lv[0] << 16) | data->csc_pre_lv[1]; - SDE_REG_WRITE(c, csc_reg_off + 0x14, val); - val = (data->csc_pre_lv[2] << 16) | data->csc_pre_lv[3]; - SDE_REG_WRITE(c, csc_reg_off + 0x18, val); - val = (data->csc_pre_lv[4] << 16) | data->csc_pre_lv[5]; - SDE_REG_WRITE(c, csc_reg_off + 0x1c, val); - - /* Post clamp */ - val = (data->csc_post_lv[0] << 16) | data->csc_post_lv[1]; - SDE_REG_WRITE(c, csc_reg_off + 0x20, val); - val = (data->csc_post_lv[2] << 16) | data->csc_post_lv[3]; - SDE_REG_WRITE(c, csc_reg_off + 0x24, val); - val = (data->csc_post_lv[4] << 16) | data->csc_post_lv[5]; - SDE_REG_WRITE(c, csc_reg_off + 0x28, val); - - /* Pre-Bias */ - SDE_REG_WRITE(c, csc_reg_off + 0x2c, data->csc_pre_bv[0]); - SDE_REG_WRITE(c, csc_reg_off + 0x30, data->csc_pre_bv[1]); - SDE_REG_WRITE(c, csc_reg_off + 0x34, data->csc_pre_bv[2]); - - /* Post-Bias */ - SDE_REG_WRITE(c, csc_reg_off + 0x38, data->csc_post_bv[0]); - SDE_REG_WRITE(c, csc_reg_off + 0x3c, data->csc_post_bv[1]); - SDE_REG_WRITE(c, csc_reg_off + 0x40, data->csc_post_bv[2]); + sde_hw_csc_setup(&ctx->hw, CDM_CSC_10_MATRIX_COEFF_0, data, true); + + return 0; } static int sde_hw_cdm_setup_cdwn(struct sde_hw_cdm *ctx, @@ -265,7 +227,7 @@ int sde_hw_cdm_enable(struct sde_hw_cdm *ctx, return -EINVAL; if (cdm->output_type == CDM_CDWN_OUTPUT_HDMI) { - if (fmt->chroma_sample != SDE_CHROMA_H1V2) + if (fmt->chroma_sample == SDE_CHROMA_H1V2) return -EINVAL; /*unsupported format */ opmode = BIT(0); opmode |= (fmt->chroma_sample << 1); diff --git a/drivers/gpu/drm/msm/sde/sde_hw_cdm.h b/drivers/gpu/drm/msm/sde/sde_hw_cdm.h index 264b8a418573..a0afd897e867 100644 --- a/drivers/gpu/drm/msm/sde/sde_hw_cdm.h +++ b/drivers/gpu/drm/msm/sde/sde_hw_cdm.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2017, The Linux Foundation. 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 version 2 and @@ -65,8 +65,9 @@ struct sde_hw_cdm_ops { * to program a different matrix than default matrix. * @cdm: Pointer to the chroma down context structure * @data Pointer to CSC configuration data + * return: 0 if success; error code otherwise */ - void (*setup_csc_data)(struct sde_hw_cdm *cdm, + int (*setup_csc_data)(struct sde_hw_cdm *cdm, struct sde_csc_cfg *data); /** diff --git a/drivers/gpu/drm/msm/sde/sde_hw_interrupts.c b/drivers/gpu/drm/msm/sde/sde_hw_interrupts.c index 49930365d989..1535d1d1ade5 100644 --- a/drivers/gpu/drm/msm/sde/sde_hw_interrupts.c +++ b/drivers/gpu/drm/msm/sde/sde_hw_interrupts.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2016-2017, The Linux Foundation. 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 version 2 and @@ -357,37 +357,32 @@ static const struct sde_irq_type sde_irq_map[] = { SDE_INTR_HIST_VIG_1_RSTSEQ_DONE, 2}, { SDE_IRQ_TYPE_RESERVED, 0, 0, 2}, { SDE_IRQ_TYPE_RESERVED, 0, 0, 2}, - /* irq_idx: 68-71 */ + /* irq_idx: 72-75 */ { SDE_IRQ_TYPE_HIST_VIG_DONE, SSPP_VIG2, SDE_INTR_HIST_VIG_2_DONE, 2}, { SDE_IRQ_TYPE_HIST_VIG_RSTSEQ, SSPP_VIG2, SDE_INTR_HIST_VIG_2_RSTSEQ_DONE, 2}, { SDE_IRQ_TYPE_HIST_VIG_DONE, SSPP_VIG3, SDE_INTR_HIST_VIG_3_DONE, 2}, { SDE_IRQ_TYPE_HIST_VIG_RSTSEQ, SSPP_VIG3, SDE_INTR_HIST_VIG_3_RSTSEQ_DONE, 2}, - /* irq_idx: 72-75 */ + /* irq_idx: 76-79 */ { SDE_IRQ_TYPE_HIST_DSPP_DONE, DSPP_0, SDE_INTR_HIST_DSPP_0_DONE, 2}, { SDE_IRQ_TYPE_HIST_DSPP_RSTSEQ, DSPP_0, SDE_INTR_HIST_DSPP_0_RSTSEQ_DONE, 2}, { SDE_IRQ_TYPE_RESERVED, 0, 0, 2}, { SDE_IRQ_TYPE_RESERVED, 0, 0, 2}, - /* irq_idx: 76-79 */ + /* irq_idx: 80-83 */ { SDE_IRQ_TYPE_HIST_DSPP_DONE, DSPP_1, SDE_INTR_HIST_DSPP_1_DONE, 2}, { SDE_IRQ_TYPE_HIST_DSPP_RSTSEQ, DSPP_1, SDE_INTR_HIST_DSPP_1_RSTSEQ_DONE, 2}, { SDE_IRQ_TYPE_RESERVED, 0, 0, 2}, { SDE_IRQ_TYPE_RESERVED, 0, 0, 2}, - /* irq_idx: 80-83 */ + /* irq_idx: 84-87 */ { SDE_IRQ_TYPE_HIST_DSPP_DONE, DSPP_2, SDE_INTR_HIST_DSPP_2_DONE, 2}, { SDE_IRQ_TYPE_HIST_DSPP_RSTSEQ, DSPP_2, SDE_INTR_HIST_DSPP_2_RSTSEQ_DONE, 2}, { SDE_IRQ_TYPE_HIST_DSPP_DONE, DSPP_3, SDE_INTR_HIST_DSPP_3_DONE, 2}, { SDE_IRQ_TYPE_HIST_DSPP_RSTSEQ, DSPP_3, SDE_INTR_HIST_DSPP_3_RSTSEQ_DONE, 2}, - /* irq_idx: 84-87 */ - { SDE_IRQ_TYPE_RESERVED, 0, 0, 2}, - { SDE_IRQ_TYPE_RESERVED, 0, 0, 2}, - { SDE_IRQ_TYPE_RESERVED, 0, 0, 2}, - { SDE_IRQ_TYPE_RESERVED, 0, 0, 2}, /* irq_idx: 88-91 */ { SDE_IRQ_TYPE_RESERVED, 0, 0, 2}, { SDE_IRQ_TYPE_RESERVED, 0, 0, 2}, @@ -905,7 +900,7 @@ static u32 sde_hw_intr_get_interrupt_status(struct sde_hw_intr *intr, sde_intr_set[reg_idx].status_off) & sde_irq_map[irq_idx].irq_mask; if (intr_status && clear) - SDE_REG_WRITE(&intr->hw, sde_intr_set[irq_idx].clr_off, + SDE_REG_WRITE(&intr->hw, sde_intr_set[reg_idx].clr_off, intr_status); spin_unlock_irqrestore(&intr->mask_lock, irq_flags); diff --git a/drivers/gpu/drm/msm/sde/sde_hw_mdss.h b/drivers/gpu/drm/msm/sde/sde_hw_mdss.h index 7a5e7ad79f0f..1edeff6a7aec 100644 --- a/drivers/gpu/drm/msm/sde/sde_hw_mdss.h +++ b/drivers/gpu/drm/msm/sde/sde_hw_mdss.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2017, The Linux Foundation. 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 version 2 and @@ -44,17 +44,26 @@ enum sde_format_flags { SDE_FORMAT_FLAG_YUV_BIT, SDE_FORMAT_FLAG_DX_BIT, + SDE_FORMAT_FLAG_COMPRESSED_BIT, SDE_FORMAT_FLAG_BIT_MAX, }; #define SDE_FORMAT_FLAG_YUV BIT(SDE_FORMAT_FLAG_YUV_BIT) #define SDE_FORMAT_FLAG_DX BIT(SDE_FORMAT_FLAG_DX_BIT) +#define SDE_FORMAT_FLAG_COMPRESSED BIT(SDE_FORMAT_FLAG_COMPRESSED_BIT) #define SDE_FORMAT_IS_YUV(X) \ (test_bit(SDE_FORMAT_FLAG_YUV_BIT, (X)->flag)) #define SDE_FORMAT_IS_DX(X) \ (test_bit(SDE_FORMAT_FLAG_DX_BIT, (X)->flag)) #define SDE_FORMAT_IS_LINEAR(X) ((X)->fetch_mode == SDE_FETCH_LINEAR) -#define SDE_FORMAT_IS_UBWC(X) ((X)->fetch_mode == SDE_FETCH_UBWC) +#define SDE_FORMAT_IS_TILE(X) \ + (((X)->fetch_mode == SDE_FETCH_UBWC) && \ + !test_bit(SDE_FORMAT_FLAG_COMPRESSED_BIT, (X)->flag)) +#define SDE_FORMAT_IS_UBWC(X) \ + (((X)->fetch_mode == SDE_FETCH_UBWC) && \ + test_bit(SDE_FORMAT_FLAG_COMPRESSED_BIT, (X)->flag)) + +#define TO_S15D16(_x_) ((_x_) << 7) #define SDE_BLEND_FG_ALPHA_FG_CONST (0 << 0) #define SDE_BLEND_FG_ALPHA_BG_CONST (1 << 0) @@ -332,6 +341,12 @@ enum sde_3d_blend_mode { BLEND_3D_MAX }; +enum sde_csc_type { + SDE_CSC_RGB2YUV_601L, + SDE_CSC_RGB2YUV_601FR, + SDE_MAX_CSC +}; + /** struct sde_format - defines the format configuration which * allows SDE HW to correctly fetch and decode the format * @base: base msm_format struture containing fourcc code diff --git a/drivers/gpu/drm/msm/sde/sde_hw_sspp.c b/drivers/gpu/drm/msm/sde/sde_hw_sspp.c index 882a1c84e9a2..ea2890d776ae 100644 --- a/drivers/gpu/drm/msm/sde/sde_hw_sspp.c +++ b/drivers/gpu/drm/msm/sde/sde_hw_sspp.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2017, The Linux Foundation. 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 version 2 and @@ -755,14 +755,17 @@ static void sde_hw_sspp_setup_csc(struct sde_hw_pipe *ctx, struct sde_csc_cfg *data) { u32 idx; + bool csc10 = false; if (_sspp_subblk_offset(ctx, SDE_SSPP_CSC, &idx) || !data) return; - if (test_bit(SDE_SSPP_CSC_10BIT, &ctx->cap->features)) + if (test_bit(SDE_SSPP_CSC_10BIT, &ctx->cap->features)) { idx += CSC_10BIT_OFFSET; + csc10 = true; + } - sde_hw_csc_setup(&ctx->hw, idx, data); + sde_hw_csc_setup(&ctx->hw, idx, data, csc10); } static void sde_hw_sspp_setup_sharpening(struct sde_hw_pipe *ctx, diff --git a/drivers/gpu/drm/msm/sde/sde_hw_util.c b/drivers/gpu/drm/msm/sde/sde_hw_util.c index 6f52f31a7569..b899f0c2f71c 100644 --- a/drivers/gpu/drm/msm/sde/sde_hw_util.c +++ b/drivers/gpu/drm/msm/sde/sde_hw_util.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2017, The Linux Foundation. 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 version 2 and @@ -42,9 +42,10 @@ u32 *sde_hw_util_get_log_mask_ptr(void) void sde_hw_csc_setup(struct sde_hw_blk_reg_map *c, u32 csc_reg_off, - struct sde_csc_cfg *data) + struct sde_csc_cfg *data, bool csc10) { static const u32 matrix_shift = 7; + u32 clamp_shift = csc10 ? 16 : 8; u32 val; /* matrix coeff - convert S15.16 to S4.9 */ @@ -64,19 +65,19 @@ void sde_hw_csc_setup(struct sde_hw_blk_reg_map *c, SDE_REG_WRITE(c, csc_reg_off + 0x10, val); /* Pre clamp */ - val = (data->csc_pre_lv[0] << 8) | data->csc_pre_lv[1]; + val = (data->csc_pre_lv[0] << clamp_shift) | data->csc_pre_lv[1]; SDE_REG_WRITE(c, csc_reg_off + 0x14, val); - val = (data->csc_pre_lv[2] << 8) | data->csc_pre_lv[3]; + val = (data->csc_pre_lv[2] << clamp_shift) | data->csc_pre_lv[3]; SDE_REG_WRITE(c, csc_reg_off + 0x18, val); - val = (data->csc_pre_lv[4] << 8) | data->csc_pre_lv[5]; + val = (data->csc_pre_lv[4] << clamp_shift) | data->csc_pre_lv[5]; SDE_REG_WRITE(c, csc_reg_off + 0x1c, val); /* Post clamp */ - val = (data->csc_post_lv[0] << 8) | data->csc_post_lv[1]; + val = (data->csc_post_lv[0] << clamp_shift) | data->csc_post_lv[1]; SDE_REG_WRITE(c, csc_reg_off + 0x20, val); - val = (data->csc_post_lv[2] << 8) | data->csc_post_lv[3]; + val = (data->csc_post_lv[2] << clamp_shift) | data->csc_post_lv[3]; SDE_REG_WRITE(c, csc_reg_off + 0x24, val); - val = (data->csc_post_lv[4] << 8) | data->csc_post_lv[5]; + val = (data->csc_post_lv[4] << clamp_shift) | data->csc_post_lv[5]; SDE_REG_WRITE(c, csc_reg_off + 0x28, val); /* Pre-Bias */ diff --git a/drivers/gpu/drm/msm/sde/sde_hw_util.h b/drivers/gpu/drm/msm/sde/sde_hw_util.h index a4d8be9de907..c38c22237a57 100644 --- a/drivers/gpu/drm/msm/sde/sde_hw_util.h +++ b/drivers/gpu/drm/msm/sde/sde_hw_util.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2017, The Linux Foundation. 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 version 2 and @@ -49,7 +49,7 @@ void *sde_hw_util_get_dir(void); void sde_hw_csc_setup(struct sde_hw_blk_reg_map *c, u32 csc_reg_off, - struct sde_csc_cfg *data); + struct sde_csc_cfg *data, bool csc10); #endif /* _SDE_HW_UTIL_H */ diff --git a/drivers/gpu/drm/msm/sde/sde_plane.c b/drivers/gpu/drm/msm/sde/sde_plane.c index 85ebff08761b..6fe1d1629d22 100644 --- a/drivers/gpu/drm/msm/sde/sde_plane.c +++ b/drivers/gpu/drm/msm/sde/sde_plane.c @@ -1907,10 +1907,12 @@ static void _sde_plane_install_properties(struct drm_plane *plane, BIT(DRM_REFLECT_X) | BIT(DRM_REFLECT_Y), PLANE_PROP_ROTATION); msm_property_install_enum(&psde->property_info, "blend_op", 0x0, 0, - e_blend_op, ARRAY_SIZE(e_blend_op), PLANE_PROP_BLEND_OP); + e_blend_op, ARRAY_SIZE(e_blend_op), PLANE_PROP_BLEND_OP, + SDE_DRM_BLEND_OP_PREMULTIPLIED); msm_property_install_enum(&psde->property_info, "src_config", 0x0, 1, - e_src_config, ARRAY_SIZE(e_src_config), PLANE_PROP_SRC_CONFIG); + e_src_config, ARRAY_SIZE(e_src_config), PLANE_PROP_SRC_CONFIG, + 0); list_for_each_entry(pp, &psde->phy_plane_head, phy_plane_list) { if (pp->pipe_hw->ops.setup_solidfill) @@ -1973,7 +1975,7 @@ static void _sde_plane_install_properties(struct drm_plane *plane, 0x0, 0, e_fb_translation_mode, ARRAY_SIZE(e_fb_translation_mode), - PLANE_PROP_FB_TRANSLATION_MODE); + PLANE_PROP_FB_TRANSLATION_MODE, SDE_DRM_FB_NON_SEC); } static inline void _sde_plane_set_csc_v1(struct sde_phy_plane *pp, diff --git a/drivers/gpu/drm/msm/sde_edid_parser.c b/drivers/gpu/drm/msm/sde_edid_parser.c index 69ab367307ea..ca9229ede251 100644 --- a/drivers/gpu/drm/msm/sde_edid_parser.c +++ b/drivers/gpu/drm/msm/sde_edid_parser.c @@ -93,6 +93,21 @@ for ((i) = (start); \ (i) < (end) && (i) + sde_cea_db_payload_len(&(cea)[(i)]) < (end); \ (i) += sde_cea_db_payload_len(&(cea)[(i)]) + 1) +static bool sde_cea_db_is_hdmi_hf_vsdb(const u8 *db) +{ + int hdmi_id; + + if (sde_cea_db_tag(db) != VENDOR_SPECIFIC_DATA_BLOCK) + return false; + + if (sde_cea_db_payload_len(db) < 7) + return false; + + hdmi_id = db[1] | (db[2] << 8) | (db[3] << 16); + + return hdmi_id == HDMI_IEEE_OUI_HF; +} + static u8 *sde_edid_find_extended_tag_block(struct edid *edid, int blk_id) { u8 *db = NULL; @@ -339,6 +354,63 @@ struct drm_connector *connector, struct sde_edid_ctrl *edid_ctrl) SDE_EDID_DEBUG("%s -\n", __func__); } +static void _sde_edid_update_dc_modes( +struct drm_connector *connector, struct sde_edid_ctrl *edid_ctrl) +{ + int i, start, end; + u8 *edid_ext, *hdmi; + struct drm_display_info *disp_info; + u32 hdmi_dc_yuv_modes = 0; + + SDE_EDID_DEBUG("%s +\n", __func__); + + if (!connector || !edid_ctrl) { + SDE_ERROR("invalid input\n"); + return; + } + + disp_info = &connector->display_info; + + edid_ext = sde_find_cea_extension(edid_ctrl->edid); + + if (!edid_ext) { + SDE_ERROR("no cea extension\n"); + return; + } + + if (sde_cea_db_offsets(edid_ext, &start, &end)) + return; + + sde_for_each_cea_db(edid_ext, i, start, end) { + if (sde_cea_db_is_hdmi_hf_vsdb(&edid_ext[i])) { + + hdmi = &edid_ext[i]; + + if (sde_cea_db_payload_len(hdmi) < 7) + continue; + + if (hdmi[7] & DRM_EDID_YCBCR420_DC_30) { + hdmi_dc_yuv_modes |= DRM_EDID_YCBCR420_DC_30; + SDE_EDID_DEBUG("Y420 30-bit supported\n"); + } + + if (hdmi[7] & DRM_EDID_YCBCR420_DC_36) { + hdmi_dc_yuv_modes |= DRM_EDID_YCBCR420_DC_36; + SDE_EDID_DEBUG("Y420 36-bit supported\n"); + } + + if (hdmi[7] & DRM_EDID_YCBCR420_DC_48) { + hdmi_dc_yuv_modes |= DRM_EDID_YCBCR420_DC_36; + SDE_EDID_DEBUG("Y420 48-bit supported\n"); + } + } + } + + disp_info->edid_hdmi_dc_modes |= hdmi_dc_yuv_modes; + + SDE_EDID_DEBUG("%s -\n", __func__); +} + static void _sde_edid_extract_audio_data_blocks( struct sde_edid_ctrl *edid_ctrl) { @@ -476,6 +548,7 @@ int _sde_edid_update_modes(struct drm_connector *connector, rc = drm_add_edid_modes(connector, edid_ctrl->edid); sde_edid_set_mode_format(connector, edid_ctrl); + _sde_edid_update_dc_modes(connector, edid_ctrl); SDE_EDID_DEBUG("%s -", __func__); return rc; } diff --git a/drivers/gpu/drm/radeon/radeon_combios.c b/drivers/gpu/drm/radeon/radeon_combios.c index a9b01bcf7d0a..fcecaf5b5526 100644 --- a/drivers/gpu/drm/radeon/radeon_combios.c +++ b/drivers/gpu/drm/radeon/radeon_combios.c @@ -3394,6 +3394,13 @@ void radeon_combios_asic_init(struct drm_device *dev) rdev->pdev->subsystem_vendor == 0x103c && rdev->pdev->subsystem_device == 0x280a) return; + /* quirk for rs4xx Toshiba Sattellite L20-183 latop to make it resume + * - it hangs on resume inside the dynclk 1 table. + */ + if (rdev->family == CHIP_RS400 && + rdev->pdev->subsystem_vendor == 0x1179 && + rdev->pdev->subsystem_device == 0xff31) + return; /* DYN CLK 1 */ table = combios_get_table_offset(dev, COMBIOS_DYN_CLK_1_TABLE); diff --git a/drivers/gpu/drm/radeon/radeon_device.c b/drivers/gpu/drm/radeon/radeon_device.c index 4aa2cbe4c85f..a77521695c9a 100644 --- a/drivers/gpu/drm/radeon/radeon_device.c +++ b/drivers/gpu/drm/radeon/radeon_device.c @@ -127,6 +127,10 @@ static struct radeon_px_quirk radeon_px_quirk_list[] = { * https://bugzilla.kernel.org/show_bug.cgi?id=51381 */ { PCI_VENDOR_ID_ATI, 0x6840, 0x1043, 0x2122, RADEON_PX_QUIRK_DISABLE_PX }, + /* Asus K53TK laptop with AMD A6-3420M APU and Radeon 7670m GPU + * https://bugs.freedesktop.org/show_bug.cgi?id=101491 + */ + { PCI_VENDOR_ID_ATI, 0x6741, 0x1043, 0x2122, RADEON_PX_QUIRK_DISABLE_PX }, /* macbook pro 8.2 */ { PCI_VENDOR_ID_ATI, 0x6741, PCI_VENDOR_ID_APPLE, 0x00e2, RADEON_PX_QUIRK_LONG_WAKEUP }, { 0, 0, 0, 0, 0 }, diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_cmdbuf_res.c b/drivers/gpu/drm/vmwgfx/vmwgfx_cmdbuf_res.c index 13db8a2851ed..1f013d45c9e9 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_cmdbuf_res.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_cmdbuf_res.c @@ -321,6 +321,7 @@ void vmw_cmdbuf_res_man_destroy(struct vmw_cmdbuf_res_manager *man) list_for_each_entry_safe(entry, next, &man->list, head) vmw_cmdbuf_res_free(man, entry); + drm_ht_remove(&man->resources); kfree(man); } diff --git a/drivers/gpu/msm/adreno_a5xx_snapshot.c b/drivers/gpu/msm/adreno_a5xx_snapshot.c index bc7c0badf189..496fc6a9248e 100644 --- a/drivers/gpu/msm/adreno_a5xx_snapshot.c +++ b/drivers/gpu/msm/adreno_a5xx_snapshot.c @@ -358,8 +358,8 @@ static const unsigned int a5xx_registers[] = { 0x0000, 0x0002, 0x0004, 0x0020, 0x0022, 0x0026, 0x0029, 0x002B, 0x002E, 0x0035, 0x0038, 0x0042, 0x0044, 0x0044, 0x0047, 0x0095, 0x0097, 0x00BB, 0x03A0, 0x0464, 0x0469, 0x046F, 0x04D2, 0x04D3, - 0x04E0, 0x04F4, 0X04F6, 0x0533, 0x0540, 0x0555, 0xF400, 0xF400, - 0xF800, 0xF807, + 0x04E0, 0x04F4, 0X04F8, 0x0529, 0x0531, 0x0533, 0x0540, 0x0555, + 0xF400, 0xF400, 0xF800, 0xF807, /* CP */ 0x0800, 0x081A, 0x081F, 0x0841, 0x0860, 0x0860, 0x0880, 0x08A0, 0x0B00, 0x0B12, 0x0B15, 0X0B1C, 0X0B1E, 0x0B28, 0x0B78, 0x0B7F, @@ -420,8 +420,8 @@ static const unsigned int a5xx_registers[] = { * is the stop offset (inclusive) */ static const unsigned int a5xx_pre_crashdumper_registers[] = { - /* RBBM: RBBM_STATUS */ - 0x04F5, 0x04F5, + /* RBBM: RBBM_STATUS - RBBM_STATUS3 */ + 0x04F5, 0x04F7, 0x0530, 0x0530, /* CP: CP_STATUS_1 */ 0x0B1D, 0x0B1D, }; diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c index 990c9bca5127..afb489f10172 100644 --- a/drivers/gpu/msm/kgsl.c +++ b/drivers/gpu/msm/kgsl.c @@ -2210,21 +2210,23 @@ static int kgsl_setup_dmabuf_useraddr(struct kgsl_device *device, if (fd != 0) dmabuf = dma_buf_get(fd - 1); } - up_read(¤t->mm->mmap_sem); - if (IS_ERR_OR_NULL(dmabuf)) + if (IS_ERR_OR_NULL(dmabuf)) { + up_read(¤t->mm->mmap_sem); return dmabuf ? PTR_ERR(dmabuf) : -ENODEV; + } ret = kgsl_setup_dma_buf(device, pagetable, entry, dmabuf); if (ret) { dma_buf_put(dmabuf); + up_read(¤t->mm->mmap_sem); return ret; } /* Setup the user addr/cache mode for cache operations */ entry->memdesc.useraddr = hostptr; _setup_cache_mode(entry, vma); - + up_read(¤t->mm->mmap_sem); return 0; } #else diff --git a/drivers/gpu/msm/kgsl_pwrctrl.c b/drivers/gpu/msm/kgsl_pwrctrl.c index e42f92392e8d..ad8b0131bb46 100644 --- a/drivers/gpu/msm/kgsl_pwrctrl.c +++ b/drivers/gpu/msm/kgsl_pwrctrl.c @@ -1022,6 +1022,8 @@ static void __force_on(struct kgsl_device *device, int flag, int on) if (on) { switch (flag) { case KGSL_PWRFLAGS_CLK_ON: + /* make sure pwrrail is ON before enabling clocks */ + kgsl_pwrctrl_pwrrail(device, KGSL_PWRFLAGS_ON); kgsl_pwrctrl_clk(device, KGSL_PWRFLAGS_ON, KGSL_STATE_ACTIVE); break; @@ -1817,7 +1819,12 @@ static int kgsl_pwrctrl_pwrrail(struct kgsl_device *device, int state) struct kgsl_pwrctrl *pwr = &device->pwrctrl; int status = 0; - if (test_bit(KGSL_PWRFLAGS_POWER_ON, &pwr->ctrl_flags)) + /* + * Disabling the regulator means also disabling dependent clocks. + * Hence don't disable it if force clock ON is set. + */ + if (test_bit(KGSL_PWRFLAGS_POWER_ON, &pwr->ctrl_flags) || + test_bit(KGSL_PWRFLAGS_CLK_ON, &pwr->ctrl_flags)) return 0; if (state == KGSL_PWRFLAGS_OFF) { @@ -3097,7 +3104,7 @@ EXPORT_SYMBOL(kgsl_pwr_limits_add); void kgsl_pwr_limits_del(void *limit_ptr) { struct kgsl_pwr_limit *limit = limit_ptr; - if (IS_ERR(limit)) + if (IS_ERR_OR_NULL(limit)) return; _update_limits(limit, KGSL_PWR_DEL_LIMIT, 0); @@ -3118,7 +3125,7 @@ int kgsl_pwr_limits_set_freq(void *limit_ptr, unsigned int freq) struct kgsl_pwr_limit *limit = limit_ptr; int level; - if (IS_ERR(limit)) + if (IS_ERR_OR_NULL(limit)) return -EINVAL; pwr = &limit->device->pwrctrl; @@ -3140,7 +3147,7 @@ void kgsl_pwr_limits_set_default(void *limit_ptr) { struct kgsl_pwr_limit *limit = limit_ptr; - if (IS_ERR(limit)) + if (IS_ERR_OR_NULL(limit)) return; _update_limits(limit, KGSL_PWR_SET_LIMIT, 0); diff --git a/drivers/gpu/msm/kgsl_pwrscale.c b/drivers/gpu/msm/kgsl_pwrscale.c index 7f93ab8fa8d4..3e4cc4490792 100644 --- a/drivers/gpu/msm/kgsl_pwrscale.c +++ b/drivers/gpu/msm/kgsl_pwrscale.c @@ -522,7 +522,8 @@ int kgsl_devfreq_target(struct device *dev, unsigned long *freq, u32 flags) struct kgsl_device *device = dev_get_drvdata(dev); struct kgsl_pwrctrl *pwr; struct kgsl_pwrlevel *pwr_level; - int level, i; + int level; + unsigned int i; unsigned long cur_freq; if (device == NULL) @@ -550,7 +551,12 @@ int kgsl_devfreq_target(struct device *dev, unsigned long *freq, u32 flags) /* If the governor recommends a new frequency, update it here */ if (*freq != cur_freq) { level = pwr->max_pwrlevel; - for (i = pwr->min_pwrlevel; i >= pwr->max_pwrlevel; i--) + /* + * Array index of pwrlevels[] should be within the permitted + * power levels, i.e., from max_pwrlevel to min_pwrlevel. + */ + for (i = pwr->min_pwrlevel; (i >= pwr->max_pwrlevel + && i <= pwr->min_pwrlevel); i--) if (*freq <= pwr->pwrlevels[i].gpu_freq) { if (pwr->thermal_cycle == CYCLE_ACTIVE) level = _thermal_adjust(pwr, i); diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index e37030624165..c7f8b70d15ee 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -285,6 +285,9 @@ #define USB_VENDOR_ID_DEALEXTREAME 0x10c5 #define USB_DEVICE_ID_DEALEXTREAME_RADIO_SI4701 0x819a +#define USB_VENDOR_ID_DELL 0x413c +#define USB_DEVICE_ID_DELL_PIXART_USB_OPTICAL_MOUSE 0x301a + #define USB_VENDOR_ID_DELORME 0x1163 #define USB_DEVICE_ID_DELORME_EARTHMATE 0x0100 #define USB_DEVICE_ID_DELORME_EM_LT20 0x0200 diff --git a/drivers/hid/i2c-hid/i2c-hid.c b/drivers/hid/i2c-hid/i2c-hid.c index 0b80633bae91..d4d655a10df1 100644 --- a/drivers/hid/i2c-hid/i2c-hid.c +++ b/drivers/hid/i2c-hid/i2c-hid.c @@ -364,6 +364,15 @@ static int i2c_hid_hwreset(struct i2c_client *client) if (ret) return ret; + /* + * The HID over I2C specification states that if a DEVICE needs time + * after the PWR_ON request, it should utilise CLOCK stretching. + * However, it has been observered that the Windows driver provides a + * 1ms sleep between the PWR_ON and RESET requests and that some devices + * rely on this. + */ + usleep_range(1000, 5000); + i2c_hid_dbg(ihid, "resetting...\n"); ret = i2c_hid_command(client, &hid_reset_cmd, NULL, 0); diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c index 6ca6ab00fa93..ce1543d69acb 100644 --- a/drivers/hid/usbhid/hid-quirks.c +++ b/drivers/hid/usbhid/hid-quirks.c @@ -72,6 +72,7 @@ static const struct hid_blacklist { { USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_AXIS_295, HID_QUIRK_NOGET }, { USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_PIXART_USB_OPTICAL_MOUSE, HID_QUIRK_ALWAYS_POLL }, { USB_VENDOR_ID_CREATIVELABS, USB_DEVICE_ID_CREATIVE_SB_OMNI_SURROUND_51, HID_QUIRK_NOGET }, + { USB_VENDOR_ID_DELL, USB_DEVICE_ID_DELL_PIXART_USB_OPTICAL_MOUSE, HID_QUIRK_ALWAYS_POLL }, { USB_VENDOR_ID_DMI, USB_DEVICE_ID_DMI_ENC, HID_QUIRK_NOGET }, { USB_VENDOR_ID_DRAGONRISE, USB_DEVICE_ID_DRAGONRISE_WIIU, HID_QUIRK_MULTI_INPUT }, { USB_VENDOR_ID_ELAN, HID_ANY_ID, HID_QUIRK_ALWAYS_POLL }, diff --git a/drivers/hwtracing/coresight/coresight-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c index 316d8b783d94..691c7bb3afac 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.c +++ b/drivers/hwtracing/coresight/coresight-tmc.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012, 2016-2017 The Linux Foundation. All rights reserved. +/* Copyright (c) 2012, 2016-2017, The Linux Foundation. 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 version 2 and @@ -764,6 +764,16 @@ static int tmc_enable(struct tmc_drvdata *drvdata, enum tmc_mode mode) pm_runtime_get_sync(drvdata->dev); mutex_lock(&drvdata->mem_lock); + + spin_lock_irqsave(&drvdata->spinlock, flags); + if (drvdata->reading) { + spin_unlock_irqrestore(&drvdata->spinlock, flags); + mutex_unlock(&drvdata->mem_lock); + pm_runtime_put(drvdata->dev); + return -EBUSY; + } + spin_unlock_irqrestore(&drvdata->spinlock, flags); + if (drvdata->config_type == TMC_CONFIG_TYPE_ETR && drvdata->out_mode == TMC_ETR_OUT_MODE_MEM) { /* @@ -807,19 +817,8 @@ static int tmc_enable(struct tmc_drvdata *drvdata, enum tmc_mode mode) coresight_cti_map_trigout(drvdata->cti_flush, 1, 0); coresight_cti_map_trigin(drvdata->cti_reset, 2, 0); } - mutex_unlock(&drvdata->mem_lock); spin_lock_irqsave(&drvdata->spinlock, flags); - if (drvdata->reading) { - spin_unlock_irqrestore(&drvdata->spinlock, flags); - - if (drvdata->config_type == TMC_CONFIG_TYPE_ETR - && drvdata->out_mode == TMC_ETR_OUT_MODE_USB) - usb_qdss_close(drvdata->usbch); - pm_runtime_put(drvdata->dev); - - return -EBUSY; - } if (drvdata->config_type == TMC_CONFIG_TYPE_ETB) { tmc_etb_enable_hw(drvdata); @@ -845,6 +844,7 @@ static int tmc_enable(struct tmc_drvdata *drvdata, enum tmc_mode mode) */ drvdata->sticky_enable = true; spin_unlock_irqrestore(&drvdata->spinlock, flags); + mutex_unlock(&drvdata->mem_lock); dev_info(drvdata->dev, "TMC enabled\n"); return 0; @@ -1140,6 +1140,7 @@ static int tmc_read_prepare(struct tmc_drvdata *drvdata) unsigned long flags; enum tmc_mode mode; + mutex_lock(&drvdata->mem_lock); spin_lock_irqsave(&drvdata->spinlock, flags); if (!drvdata->sticky_enable) { dev_err(drvdata->dev, "enable tmc once before reading\n"); @@ -1172,11 +1173,13 @@ static int tmc_read_prepare(struct tmc_drvdata *drvdata) out: drvdata->reading = true; spin_unlock_irqrestore(&drvdata->spinlock, flags); + mutex_unlock(&drvdata->mem_lock); dev_info(drvdata->dev, "TMC read start\n"); return 0; err: spin_unlock_irqrestore(&drvdata->spinlock, flags); + mutex_unlock(&drvdata->mem_lock); return ret; } @@ -1353,7 +1356,11 @@ static ssize_t tmc_read(struct file *file, char __user *data, size_t len, { struct tmc_drvdata *drvdata = container_of(file->private_data, struct tmc_drvdata, miscdev); - char *bufp = drvdata->buf + *ppos; + char *bufp; + + mutex_lock(&drvdata->mem_lock); + + bufp = drvdata->buf + *ppos; if (*ppos + len > drvdata->size) len = drvdata->size - *ppos; @@ -1375,6 +1382,7 @@ static ssize_t tmc_read(struct file *file, char __user *data, size_t len, if (copy_to_user(data, bufp, len)) { dev_dbg(drvdata->dev, "%s: copy_to_user failed\n", __func__); + mutex_unlock(&drvdata->mem_lock); return -EFAULT; } @@ -1382,6 +1390,8 @@ static ssize_t tmc_read(struct file *file, char __user *data, size_t len, dev_dbg(drvdata->dev, "%s: %zu bytes copied, %d bytes left\n", __func__, len, (int)(drvdata->size - *ppos)); + + mutex_unlock(&drvdata->mem_lock); return len; } diff --git a/drivers/i2c/busses/i2c-msm-v2.c b/drivers/i2c/busses/i2c-msm-v2.c index 1f042fa56eea..f4ed71f9c1a7 100644 --- a/drivers/i2c/busses/i2c-msm-v2.c +++ b/drivers/i2c/busses/i2c-msm-v2.c @@ -2232,19 +2232,8 @@ static int i2c_msm_pm_clk_enable(struct i2c_msm_ctrl *ctrl) static int i2c_msm_pm_xfer_start(struct i2c_msm_ctrl *ctrl) { int ret; - struct i2c_msm_xfer *xfer = &ctrl->xfer; mutex_lock(&ctrl->xfer.mtx); - /* if system is suspended just bail out */ - if (ctrl->pwr_state == I2C_MSM_PM_SYS_SUSPENDED) { - struct i2c_msg *msgs = xfer->msgs + xfer->cur_buf.msg_idx; - dev_err(ctrl->dev, - "slave:0x%x is calling xfer when system is suspended\n", - msgs->addr); - mutex_unlock(&ctrl->xfer.mtx); - return -EIO; - } - i2c_msm_pm_pinctrl_state(ctrl, true); pm_runtime_get_sync(ctrl->dev); /* @@ -2330,6 +2319,14 @@ i2c_msm_frmwrk_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) return PTR_ERR(msgs); } + /* if system is suspended just bail out */ + if (ctrl->pwr_state == I2C_MSM_PM_SYS_SUSPENDED) { + dev_err(ctrl->dev, + "slave:0x%x is calling xfer when system is suspended\n", + msgs->addr); + return -EIO; + } + ret = i2c_msm_pm_xfer_start(ctrl); if (ret) return ret; diff --git a/drivers/iio/adc/qcom-rradc.c b/drivers/iio/adc/qcom-rradc.c index 537cca877f66..5c20970ccbbb 100644 --- a/drivers/iio/adc/qcom-rradc.c +++ b/drivers/iio/adc/qcom-rradc.c @@ -331,8 +331,8 @@ static int rradc_post_process_therm(struct rradc_chip *chip, int64_t temp; /* K = code/4 */ - temp = div64_s64(adc_code, FG_ADC_RR_BATT_THERM_LSB_K); - temp *= FG_ADC_SCALE_MILLI_FACTOR; + temp = ((int64_t)adc_code * FG_ADC_SCALE_MILLI_FACTOR); + temp = div64_s64(temp, FG_ADC_RR_BATT_THERM_LSB_K); *result_millidegc = temp - FG_ADC_KELVINMIL_CELSIUSMIL; return 0; diff --git a/drivers/input/serio/i8042-x86ia64io.h b/drivers/input/serio/i8042-x86ia64io.h index e7b96f1ac2c5..5be14ad29d46 100644 --- a/drivers/input/serio/i8042-x86ia64io.h +++ b/drivers/input/serio/i8042-x86ia64io.h @@ -788,6 +788,13 @@ static const struct dmi_system_id __initconst i8042_dmi_notimeout_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK U574"), }, }, + { + /* Fujitsu UH554 laptop */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), + DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK UH544"), + }, + }, { } }; diff --git a/drivers/iommu/amd_iommu_v2.c b/drivers/iommu/amd_iommu_v2.c index 4831eb910fc7..22160e481794 100644 --- a/drivers/iommu/amd_iommu_v2.c +++ b/drivers/iommu/amd_iommu_v2.c @@ -699,9 +699,9 @@ out_clear_state: out_unregister: mmu_notifier_unregister(&pasid_state->mn, mm); + mmput(mm); out_free: - mmput(mm); free_pasid_state(pasid_state); out: diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c index b92b8a724efb..f9711aceef54 100644 --- a/drivers/iommu/intel-iommu.c +++ b/drivers/iommu/intel-iommu.c @@ -1137,7 +1137,7 @@ static void dma_pte_free_level(struct dmar_domain *domain, int level, if (!dma_pte_present(pte) || dma_pte_superpage(pte)) goto next; - level_pfn = pfn & level_mask(level - 1); + level_pfn = pfn & level_mask(level); level_pte = phys_to_virt(dma_pte_addr(pte)); if (level > 2) diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c index 33176a4aa6ef..92e6ae48caf8 100644 --- a/drivers/iommu/iommu.c +++ b/drivers/iommu/iommu.c @@ -394,36 +394,30 @@ int iommu_group_add_device(struct iommu_group *group, struct device *dev) device->dev = dev; ret = sysfs_create_link(&dev->kobj, &group->kobj, "iommu_group"); - if (ret) { - kfree(device); - return ret; - } + if (ret) + goto err_free_device; device->name = kasprintf(GFP_KERNEL, "%s", kobject_name(&dev->kobj)); rename: if (!device->name) { - sysfs_remove_link(&dev->kobj, "iommu_group"); - kfree(device); - return -ENOMEM; + ret = -ENOMEM; + goto err_remove_link; } ret = sysfs_create_link_nowarn(group->devices_kobj, &dev->kobj, device->name); if (ret) { - kfree(device->name); if (ret == -EEXIST && i >= 0) { /* * Account for the slim chance of collision * and append an instance to the name. */ + kfree(device->name); device->name = kasprintf(GFP_KERNEL, "%s.%d", kobject_name(&dev->kobj), i++); goto rename; } - - sysfs_remove_link(&dev->kobj, "iommu_group"); - kfree(device); - return ret; + goto err_free_name; } kobject_get(group->devices_kobj); @@ -435,8 +429,10 @@ rename: mutex_lock(&group->mutex); list_add_tail(&device->list, &group->devices); if (group->domain) - __iommu_attach_device(group->domain, dev); + ret = __iommu_attach_device(group->domain, dev); mutex_unlock(&group->mutex); + if (ret) + goto err_put_group; /* Notify any listeners about change to group. */ blocking_notifier_call_chain(&group->notifier, @@ -447,6 +443,21 @@ rename: pr_info("Adding device %s to group %d\n", dev_name(dev), group->id); return 0; + +err_put_group: + mutex_lock(&group->mutex); + list_del(&device->list); + mutex_unlock(&group->mutex); + dev->iommu_group = NULL; + kobject_put(group->devices_kobj); +err_free_name: + kfree(device->name); +err_remove_link: + sysfs_remove_link(&dev->kobj, "iommu_group"); +err_free_device: + kfree(device); + pr_err("Failed to add device %s to group %d: %d\n", dev_name(dev), group->id, ret); + return ret; } EXPORT_SYMBOL_GPL(iommu_group_add_device); diff --git a/drivers/media/platform/msm/ais/isp/msm_isp_axi_util.c b/drivers/media/platform/msm/ais/isp/msm_isp_axi_util.c index 373a963f75aa..a85ee30769c4 100644 --- a/drivers/media/platform/msm/ais/isp/msm_isp_axi_util.c +++ b/drivers/media/platform/msm/ais/isp/msm_isp_axi_util.c @@ -3889,6 +3889,12 @@ int msm_isp_update_axi_stream(struct vfe_device *vfe_dev, void *arg) case UPDATE_STREAM_REQUEST_FRAMES_VER2: { struct msm_vfe_axi_stream_cfg_update_info_req_frm *req_frm = &update_cmd->req_frm_ver2; + if (HANDLE_TO_IDX(req_frm->stream_handle) >= VFE_AXI_SRC_MAX) { + pr_err("%s: Invalid stream handle\n", __func__); + rc = -EINVAL; + break; + } + stream_info = &axi_data->stream_info[HANDLE_TO_IDX( req_frm->stream_handle)]; rc = msm_isp_request_frame(vfe_dev, stream_info, diff --git a/drivers/media/platform/msm/ais/sensor/flash/msm_flash.c b/drivers/media/platform/msm/ais/sensor/flash/msm_flash.c index 6af589e5c230..a2a89b92c9f1 100644 --- a/drivers/media/platform/msm/ais/sensor/flash/msm_flash.c +++ b/drivers/media/platform/msm/ais/sensor/flash/msm_flash.c @@ -1022,13 +1022,13 @@ static long msm_flash_subdev_do_ioctl( sd = vdev_to_v4l2_subdev(vdev); u32 = (struct msm_flash_cfg_data_t32 *)arg; - flash_data.cfg_type = u32->cfg_type; - for (i = 0; i < MAX_LED_TRIGGERS; i++) { - flash_data.flash_current[i] = u32->flash_current[i]; - flash_data.flash_duration[i] = u32->flash_duration[i]; - } switch (cmd) { case VIDIOC_MSM_FLASH_CFG32: + flash_data.cfg_type = u32->cfg_type; + for (i = 0; i < MAX_LED_TRIGGERS; i++) { + flash_data.flash_current[i] = u32->flash_current[i]; + flash_data.flash_duration[i] = u32->flash_duration[i]; + } cmd = VIDIOC_MSM_FLASH_CFG; switch (flash_data.cfg_type) { case CFG_FLASH_OFF: diff --git a/drivers/media/platform/msm/ais/sensor/ois/msm_ois.c b/drivers/media/platform/msm/ais/sensor/ois/msm_ois.c index 28a5402a4359..236660dca3fb 100644 --- a/drivers/media/platform/msm/ais/sensor/ois/msm_ois.c +++ b/drivers/media/platform/msm/ais/sensor/ois/msm_ois.c @@ -781,11 +781,10 @@ static long msm_ois_subdev_do_ioctl( u32 = (struct msm_ois_cfg_data32 *)arg; parg = arg; - ois_data.cfgtype = u32->cfgtype; - switch (cmd) { case VIDIOC_MSM_OIS_CFG32: cmd = VIDIOC_MSM_OIS_CFG; + ois_data.cfgtype = u32->cfgtype; switch (u32->cfgtype) { case CFG_OIS_CONTROL: diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c index 0e0f10e7fbb5..63f5497e63b8 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c @@ -776,40 +776,6 @@ void msm_isp_check_for_output_error(struct vfe_device *vfe_dev, } } -static int msm_isp_check_sync_time(struct msm_vfe_src_info *src_info, - struct msm_isp_timestamp *ts, - struct master_slave_resource_info *ms_res) -{ - int i; - struct msm_vfe_src_info *master_src_info = NULL; - uint32_t master_time = 0, current_time; - - if (!ms_res->src_sof_mask) - return 0; - - for (i = 0; i < MAX_VFE * VFE_SRC_MAX; i++) { - if (ms_res->src_info[i] == NULL) - continue; - if (src_info == ms_res->src_info[i] || - ms_res->src_info[i]->active == 0) - continue; - if (ms_res->src_sof_mask & - (1 << ms_res->src_info[i]->dual_hw_ms_info.index)) { - master_src_info = ms_res->src_info[i]; - break; - } - } - if (!master_src_info) - return 0; - master_time = master_src_info-> - dual_hw_ms_info.sof_info.mono_timestamp_ms; - current_time = ts->buf_time.tv_sec * 1000 + - ts->buf_time.tv_usec / 1000; - if ((current_time - master_time) > ms_res->sof_delta_threshold) - return 1; - return 0; -} - static void msm_isp_sync_dual_cam_frame_id( struct vfe_device *vfe_dev, struct master_slave_resource_info *ms_res, @@ -824,24 +790,11 @@ static void msm_isp_sync_dual_cam_frame_id( if (src_info->dual_hw_ms_info.sync_state == ms_res->dual_sync_mode) { - if (msm_isp_check_sync_time(src_info, ts, ms_res) == 0) { - (frame_src == VFE_PIX_0) ? src_info->frame_id += + (frame_src == VFE_PIX_0) ? src_info->frame_id += vfe_dev->axi_data.src_info[frame_src]. sof_counter_step : src_info->frame_id++; - return; - } - ms_res->src_sof_mask = 0; - ms_res->active_src_mask = 0; - for (i = 0; i < MAX_VFE * VFE_SRC_MAX; i++) { - if (ms_res->src_info[i] == NULL) - continue; - if (ms_res->src_info[i]->active == 0) - continue; - ms_res->src_info[i]->dual_hw_ms_info. - sync_state = - MSM_ISP_DUAL_CAM_ASYNC; - } + return; } /* find highest frame id */ @@ -2616,6 +2569,7 @@ int msm_isp_axi_reset(struct vfe_device *vfe_dev, struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data; uint32_t bufq_handle = 0, bufq_id = 0; struct msm_isp_timestamp timestamp; + struct msm_vfe_frame_request_queue *queue_req; unsigned long flags; int vfe_idx; @@ -2652,8 +2606,18 @@ int msm_isp_axi_reset(struct vfe_device *vfe_dev, VFE_PING_FLAG); msm_isp_cfg_stream_scratch(stream_info, VFE_PONG_FLAG); + stream_info->undelivered_request_cnt = 0; spin_unlock_irqrestore(&stream_info->lock, flags); + while (!list_empty(&stream_info->request_q)) { + queue_req = list_first_entry_or_null( + &stream_info->request_q, + struct msm_vfe_frame_request_queue, list); + if (queue_req) { + queue_req->cmd_used = 0; + list_del(&queue_req->list); + } + } for (bufq_id = 0; bufq_id < VFE_BUF_QUEUE_MAX; bufq_id++) { bufq_handle = stream_info->bufq_handle[bufq_id]; @@ -3446,7 +3410,7 @@ static int msm_isp_request_frame(struct vfe_device *vfe_dev, } if ((vfe_dev->axi_data.src_info[frame_src].active && (frame_id != vfe_dev->axi_data.src_info[frame_src].frame_id + vfe_dev-> - axi_data.src_info[VFE_PIX_0].sof_counter_step)) || + axi_data.src_info[frame_src].sof_counter_step)) || ((!vfe_dev->axi_data.src_info[frame_src].active))) { pr_debug("%s:%d invalid frame id %d cur frame id %d pix %d\n", __func__, __LINE__, frame_id, diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c index d30d8022b7ab..e87f2414a879 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c @@ -511,6 +511,9 @@ static int msm_isp_cfg_rdi(struct vfe_device *vfe_dev, return -EINVAL; } + vfe_dev->axi_data. + src_info[input_cfg->input_src].sof_counter_step = 1; + vfe_dev->axi_data.src_info[input_cfg->input_src].pixel_clock = input_cfg->input_pix_clk; vfe_dev->hw_info->vfe_ops.core_ops.cfg_rdi_reg( diff --git a/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c b/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c index ab981f762dd2..3f900ded090a 100644 --- a/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c +++ b/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c @@ -86,6 +86,12 @@ static void msm_ispif_io_dump_reg(struct ispif_device *ispif) { if (!ispif->enb_dump_reg) return; + + if (!ispif->base) { + pr_err("%s: null pointer for the ispif base\n", __func__); + return; + } + msm_camera_io_dump(ispif->base, 0x250, 0); } diff --git a/drivers/media/platform/msm/camera_v2/msm_vb2/msm_vb2.c b/drivers/media/platform/msm/camera_v2/msm_vb2/msm_vb2.c index ba9b4df6bf22..719b14226067 100644 --- a/drivers/media/platform/msm/camera_v2/msm_vb2/msm_vb2.c +++ b/drivers/media/platform/msm/camera_v2/msm_vb2/msm_vb2.c @@ -374,6 +374,7 @@ static int msm_vb2_put_buf(struct vb2_v4l2_buffer *vb, int session_id, pr_err("VB buffer is INVALID vb=%pK, ses_id=%d, str_id=%d\n", vb, session_id, stream_id); spin_unlock_irqrestore(&stream->stream_lock, flags); + read_unlock(&session->stream_rwlock); return -EINVAL; } msm_vb2 = @@ -428,6 +429,7 @@ static int msm_vb2_buf_done(struct vb2_v4l2_buffer *vb, int session_id, pr_err("VB buffer is INVALID ses_id=%d, str_id=%d, vb=%pK\n", session_id, stream_id, vb); spin_unlock_irqrestore(&stream->stream_lock, flags); + read_unlock(&session->stream_rwlock); return -EINVAL; } msm_vb2 = diff --git a/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.c b/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.c index 75043e1b0427..7dda92510879 100644 --- a/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.c +++ b/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.c @@ -1602,6 +1602,12 @@ static int32_t msm_cci_write(struct v4l2_subdev *sd, return rc; } + if (cci_dev->cci_state != CCI_STATE_ENABLED) { + pr_err("%s invalid cci state %d\n", + __func__, cci_dev->cci_state); + return -EINVAL; + } + if (c_ctrl->cci_info->cci_i2c_master >= MASTER_MAX || c_ctrl->cci_info->cci_i2c_master < 0) { pr_err("%s:%d Invalid I2C master addr\n", __func__, __LINE__); diff --git a/drivers/media/platform/msm/camera_v2/sensor/flash/msm_flash.c b/drivers/media/platform/msm/camera_v2/sensor/flash/msm_flash.c index 491b8d31935a..cf7d1a8aa1f4 100644 --- a/drivers/media/platform/msm/camera_v2/sensor/flash/msm_flash.c +++ b/drivers/media/platform/msm/camera_v2/sensor/flash/msm_flash.c @@ -152,6 +152,12 @@ static int32_t msm_flash_i2c_write_table( conf_array.reg_setting = settings->reg_setting_a; conf_array.size = settings->size; + /* Validate the settings size */ + if ((!conf_array.size) || (conf_array.size > MAX_I2C_REG_SET)) { + pr_err("failed: invalid size %d", conf_array.size); + return -EINVAL; + } + return flash_ctrl->flash_i2c_client.i2c_func_tbl->i2c_write_table( &flash_ctrl->flash_i2c_client, &conf_array); } diff --git a/drivers/media/platform/msm/camera_v2/sensor/msm_sensor.c b/drivers/media/platform/msm/camera_v2/sensor/msm_sensor.c index e1143c356721..fcef05374098 100644 --- a/drivers/media/platform/msm/camera_v2/sensor/msm_sensor.c +++ b/drivers/media/platform/msm/camera_v2/sensor/msm_sensor.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2011-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2011-2017, The Linux Foundation. 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 version 2 and @@ -584,7 +584,12 @@ static int msm_sensor_config32(struct msm_sensor_ctrl_t *s_ctrl, pr_err("%s:%d: i2c_read failed\n", __func__, __LINE__); break; } - read_config_ptr->data = local_data; + if (copy_to_user(&read_config_ptr->data, + &local_data, sizeof(local_data))) { + pr_err("%s:%d failed\n", __func__, __LINE__); + rc = -EFAULT; + break; + } break; } case CFG_SLAVE_WRITE_I2C_ARRAY: { @@ -1098,7 +1103,12 @@ int msm_sensor_config(struct msm_sensor_ctrl_t *s_ctrl, void __user *argp) pr_err("%s:%d: i2c_read failed\n", __func__, __LINE__); break; } - read_config_ptr->data = local_data; + if (copy_to_user(&read_config_ptr->data, + &local_data, sizeof(local_data))) { + pr_err("%s:%d failed\n", __func__, __LINE__); + rc = -EFAULT; + break; + } break; } case CFG_SLAVE_WRITE_I2C_ARRAY: { diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c index acb697946e18..10f72a2155db 100644 --- a/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c +++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c @@ -814,6 +814,9 @@ static void sde_hw_rotator_setup_wbengine(struct sde_hw_rotator_context *ctx, bw /= TRAFFIC_SHAPE_CLKTICK_12MS; if (bw > 0xFF) bw = 0xFF; + else if (bw == 0) + bw = 1; + SDE_REGDMA_WRITE(wrptr, ROT_WB_TRAFFIC_SHAPER_WR_CLIENT, BIT(31) | bw); SDEROT_DBG("Enable ROT_WB Traffic Shaper:%d\n", bw); diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index 29c57d13744e..01e5502917f7 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -1701,7 +1701,7 @@ static int mmc_blk_cmd_recovery(struct mmc_card *card, struct request *req, mmc_retune_recheck(card->host); prev_cmd_status_valid = false; - pr_err("%s: error %d sending status command, %sing\n", + pr_err_ratelimited("%s: error %d sending status command, %sing\n", req->rq_disk->disk_name, err, retry ? "retry" : "abort"); } diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 79b5b3504ccd..0da9c5caea13 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -487,12 +487,17 @@ static int mmc_devfreq_set_target(struct device *dev, struct mmc_devfeq_clk_scaling *clk_scaling; int err = 0; int abort; + unsigned long pflags = current->flags; + + /* Ensure scaling would happen even in memory pressure conditions */ + current->flags |= PF_MEMALLOC; if (!(host && freq)) { pr_err("%s: unexpected host/freq parameter\n", __func__); err = -EINVAL; goto out; } + clk_scaling = &host->clk_scaling; if (!clk_scaling->enable) @@ -551,6 +556,7 @@ static int mmc_devfreq_set_target(struct device *dev, rel_host: mmc_release_host(host); out: + tsk_restore_flags(current, pflags, PF_MEMALLOC); return err; } diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index df60774b02af..a28d6b98a042 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -1323,10 +1323,6 @@ int mmc_hs400_to_hs200(struct mmc_card *card) if (host->caps & MMC_CAP_WAIT_WHILE_BUSY) send_status = false; - /* Reduce frequency to HS */ - max_dtr = card->ext_csd.hs_max_dtr; - mmc_set_clock(host, max_dtr); - /* Switch HS400 to HS DDR */ val = EXT_CSD_TIMING_HS; err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING, @@ -1337,6 +1333,10 @@ int mmc_hs400_to_hs200(struct mmc_card *card) mmc_set_timing(host, MMC_TIMING_MMC_DDR52); + /* Reduce frequency to HS */ + max_dtr = card->ext_csd.hs_max_dtr; + mmc_set_clock(host, max_dtr); + if (!send_status) { err = mmc_switch_status(card, false); if (err) @@ -2849,6 +2849,7 @@ static int mmc_runtime_suspend(struct mmc_host *host) return -EBUSY; } + MMC_TRACE(host, "%s\n", __func__); err = _mmc_suspend(host, true); if (err) pr_err("%s: error %d doing aggressive suspend\n", @@ -2870,6 +2871,7 @@ static int mmc_runtime_resume(struct mmc_host *host) if (!(host->caps & (MMC_CAP_AGGRESSIVE_PM | MMC_CAP_RUNTIME_RESUME))) return 0; + MMC_TRACE(host, "%s\n", __func__); err = _mmc_resume(host); if (err) pr_err("%s: error %d doing aggressive resume\n", diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index d908c3fed7c9..7aefeb037ef4 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -2494,15 +2494,30 @@ void sdhci_msm_dump_pwr_ctrl_regs(struct sdhci_host *host) struct sdhci_msm_host *msm_host = pltfm_host->priv; const struct sdhci_msm_offset *msm_host_offset = msm_host->offset; + unsigned int irq_flags = 0; + struct irq_desc *pwr_irq_desc = irq_to_desc(msm_host->pwr_irq); - pr_err("%s: PWRCTL_STATUS: 0x%08x | PWRCTL_MASK: 0x%08x | PWRCTL_CTL: 0x%08x\n", + if (pwr_irq_desc) + irq_flags = pwr_irq_desc->irq_data.common->state_use_accessors; + + pr_err("%s: PWRCTL_STATUS: 0x%08x | PWRCTL_MASK: 0x%08x | PWRCTL_CTL: 0x%08x, pwr isr state=0x%x\n", mmc_hostname(host->mmc), sdhci_msm_readl_relaxed(host, msm_host_offset->CORE_PWRCTL_STATUS), sdhci_msm_readl_relaxed(host, msm_host_offset->CORE_PWRCTL_MASK), sdhci_msm_readl_relaxed(host, - msm_host_offset->CORE_PWRCTL_CTL)); + msm_host_offset->CORE_PWRCTL_CTL), irq_flags); + + MMC_TRACE(host->mmc, + "%s: Sts: 0x%08x | Mask: 0x%08x | Ctrl: 0x%08x, pwr isr state=0x%x\n", + __func__, + sdhci_msm_readb_relaxed(host, + msm_host_offset->CORE_PWRCTL_STATUS), + sdhci_msm_readb_relaxed(host, + msm_host_offset->CORE_PWRCTL_MASK), + sdhci_msm_readb_relaxed(host, + msm_host_offset->CORE_PWRCTL_CTL), irq_flags); } static irqreturn_t sdhci_msm_pwr_irq(int irq, void *data) @@ -2775,10 +2790,14 @@ static void sdhci_msm_check_power_status(struct sdhci_host *host, u32 req_type) if (done) init_completion(&msm_host->pwr_irq_completion); else if (!wait_for_completion_timeout(&msm_host->pwr_irq_completion, - msecs_to_jiffies(MSM_PWR_IRQ_TIMEOUT_MS))) + msecs_to_jiffies(MSM_PWR_IRQ_TIMEOUT_MS))) { __WARN_printf("%s: request(%d) timed out waiting for pwr_irq\n", mmc_hostname(host->mmc), req_type); - + MMC_TRACE(host->mmc, + "%s: request(%d) timed out waiting for pwr_irq\n", + __func__, req_type); + sdhci_msm_dump_pwr_ctrl_regs(host); + } pr_debug("%s: %s: request %d done\n", mmc_hostname(host->mmc), __func__, req_type); } diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index ddb9947ce298..a5ff9f73dfbc 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -255,6 +255,8 @@ retry_reset: if (timeout == 0) { pr_err("%s: Reset 0x%x never completed.\n", mmc_hostname(host->mmc), (int)mask); + MMC_TRACE(host->mmc, "%s: Reset 0x%x never completed\n", + __func__, (int)mask); if ((host->quirks2 & SDHCI_QUIRK2_USE_RESET_WORKAROUND) && host->ops->reset_workaround) { if (!host->reset_wa_applied) { @@ -1178,6 +1180,9 @@ void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd) if (timeout == 0) { pr_err("%s: Controller never released " "inhibit bit(s).\n", mmc_hostname(host->mmc)); + MMC_TRACE(host->mmc, + "%s :Controller never released inhibit bit(s)\n", + __func__); sdhci_dumpregs(host); cmd->error = -EIO; tasklet_schedule(&host->finish_tasklet); @@ -1233,12 +1238,12 @@ void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd) if (cmd->data) host->data_start_time = ktime_get(); trace_mmc_cmd_rw_start(cmd->opcode, cmd->arg, cmd->flags); + sdhci_writew(host, SDHCI_MAKE_CMD(cmd->opcode, flags), SDHCI_COMMAND); MMC_TRACE(host->mmc, "%s: updated 0x8=0x%08x 0xC=0x%08x 0xE=0x%08x\n", __func__, sdhci_readl(host, SDHCI_ARGUMENT), sdhci_readw(host, SDHCI_TRANSFER_MODE), sdhci_readw(host, SDHCI_COMMAND)); - sdhci_writew(host, SDHCI_MAKE_CMD(cmd->opcode, flags), SDHCI_COMMAND); } EXPORT_SYMBOL_GPL(sdhci_send_command); @@ -1434,6 +1439,8 @@ clock_set: if (timeout == 0) { pr_err("%s: Internal clock never " "stabilised.\n", mmc_hostname(host->mmc)); + MMC_TRACE(host->mmc, + "%s: Internal clock never stabilised.\n", __func__); sdhci_dumpregs(host); return; } @@ -1777,6 +1784,7 @@ end_req: if (mrq->data) mrq->data->error = -EIO; host->mrq = NULL; + MMC_TRACE(host->mmc, "Request failed due to ice config\n"); sdhci_dumpregs(host); mmc_request_done(host->mmc, mrq); sdhci_runtime_pm_put(host); @@ -2846,6 +2854,7 @@ static void sdhci_timeout_timer(unsigned long data) if (host->mrq) { pr_err("%s: Timeout waiting for hardware " "interrupt.\n", mmc_hostname(host->mmc)); + MMC_TRACE(host->mmc, "Timeout waiting for h/w interrupt\n"); sdhci_dumpregs(host); if (host->data) { @@ -2885,6 +2894,9 @@ static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask, u32 *mask) pr_err("%s: Got command interrupt 0x%08x even " "though no command operation was in progress.\n", mmc_hostname(host->mmc), (unsigned)intmask); + MMC_TRACE(host->mmc, + "Got command interrupt 0x%08x even though no command operation was in progress.\n", + (unsigned)intmask); sdhci_dumpregs(host); return; } @@ -3054,6 +3066,9 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask) pr_err("%s: Got data interrupt 0x%08x even " "though no data operation was in progress.\n", mmc_hostname(host->mmc), (unsigned)intmask); + MMC_TRACE(host->mmc, + "Got data interrupt 0x%08x even though no data operation was in progress.\n", + (unsigned)intmask); sdhci_dumpregs(host); return; @@ -3089,6 +3104,11 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask) mmc_hostname(host->mmc), intmask, host->data->error, ktime_to_ms(ktime_sub( ktime_get(), host->data_start_time))); + MMC_TRACE(host->mmc, + "data txfr (0x%08x) error: %d after %lld ms\n", + intmask, host->data->error, + ktime_to_ms(ktime_sub(ktime_get(), + host->data_start_time))); if (!host->mmc->sdr104_wa || (host->mmc->ios.timing != MMC_TIMING_UHS_SDR104)) @@ -3340,6 +3360,8 @@ out: if (unexpected) { pr_err("%s: Unexpected interrupt 0x%08x.\n", mmc_hostname(host->mmc), unexpected); + MMC_TRACE(host->mmc, "Unexpected interrupt 0x%08x.\n", + unexpected); sdhci_dumpregs(host); } diff --git a/drivers/mtd/bcm47xxpart.c b/drivers/mtd/bcm47xxpart.c index 5abab8800891..9190057535e6 100644 --- a/drivers/mtd/bcm47xxpart.c +++ b/drivers/mtd/bcm47xxpart.c @@ -66,11 +66,13 @@ static const char *bcm47xxpart_trx_data_part_name(struct mtd_info *master, { uint32_t buf; size_t bytes_read; + int err; - if (mtd_read(master, offset, sizeof(buf), &bytes_read, - (uint8_t *)&buf) < 0) { - pr_err("mtd_read error while parsing (offset: 0x%X)!\n", - offset); + err = mtd_read(master, offset, sizeof(buf), &bytes_read, + (uint8_t *)&buf); + if (err && !mtd_is_bitflip(err)) { + pr_err("mtd_read error while parsing (offset: 0x%X): %d\n", + offset, err); goto out_default; } @@ -95,6 +97,7 @@ static int bcm47xxpart_parse(struct mtd_info *master, int trx_part = -1; int last_trx_part = -1; int possible_nvram_sizes[] = { 0x8000, 0xF000, 0x10000, }; + int err; /* * Some really old flashes (like AT45DB*) had smaller erasesize-s, but @@ -118,8 +121,8 @@ static int bcm47xxpart_parse(struct mtd_info *master, /* Parse block by block looking for magics */ for (offset = 0; offset <= master->size - blocksize; offset += blocksize) { - /* Nothing more in higher memory */ - if (offset >= 0x2000000) + /* Nothing more in higher memory on BCM47XX (MIPS) */ + if (config_enabled(CONFIG_BCM47XX) && offset >= 0x2000000) break; if (curr_part >= BCM47XXPART_MAX_PARTS) { @@ -128,10 +131,11 @@ static int bcm47xxpart_parse(struct mtd_info *master, } /* Read beginning of the block */ - if (mtd_read(master, offset, BCM47XXPART_BYTES_TO_READ, - &bytes_read, (uint8_t *)buf) < 0) { - pr_err("mtd_read error while parsing (offset: 0x%X)!\n", - offset); + err = mtd_read(master, offset, BCM47XXPART_BYTES_TO_READ, + &bytes_read, (uint8_t *)buf); + if (err && !mtd_is_bitflip(err)) { + pr_err("mtd_read error while parsing (offset: 0x%X): %d\n", + offset, err); continue; } @@ -252,10 +256,11 @@ static int bcm47xxpart_parse(struct mtd_info *master, } /* Read middle of the block */ - if (mtd_read(master, offset + 0x8000, 0x4, - &bytes_read, (uint8_t *)buf) < 0) { - pr_err("mtd_read error while parsing (offset: 0x%X)!\n", - offset); + err = mtd_read(master, offset + 0x8000, 0x4, &bytes_read, + (uint8_t *)buf); + if (err && !mtd_is_bitflip(err)) { + pr_err("mtd_read error while parsing (offset: 0x%X): %d\n", + offset, err); continue; } @@ -275,10 +280,11 @@ static int bcm47xxpart_parse(struct mtd_info *master, } offset = master->size - possible_nvram_sizes[i]; - if (mtd_read(master, offset, 0x4, &bytes_read, - (uint8_t *)buf) < 0) { - pr_err("mtd_read error while reading at offset 0x%X!\n", - offset); + err = mtd_read(master, offset, 0x4, &bytes_read, + (uint8_t *)buf); + if (err && !mtd_is_bitflip(err)) { + pr_err("mtd_read error while reading (offset 0x%X): %d\n", + offset, err); continue; } diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index 37e4135ab213..64d6f053c2a5 100644 --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -1057,6 +1057,13 @@ static int spansion_quad_enable(struct spi_nor *nor) return -EINVAL; } + ret = spi_nor_wait_till_ready(nor); + if (ret) { + dev_err(nor->dev, + "timeout while writing configuration register\n"); + return ret; + } + /* read back and check it */ ret = read_cr(nor); if (!(ret > 0 && (ret & CR_QUAD_EN_SPAN))) { diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-dev.c b/drivers/net/ethernet/amd/xgbe/xgbe-dev.c index 5e6238e0b2bd..75e6e7e6baed 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-dev.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-dev.c @@ -2732,8 +2732,10 @@ static int xgbe_init(struct xgbe_prv_data *pdata) /* Flush Tx queues */ ret = xgbe_flush_tx_queues(pdata); - if (ret) + if (ret) { + netdev_err(pdata->netdev, "error flushing TX queues\n"); return ret; + } /* * Initialize DMA related features diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c index 865b7e0b133b..64034ff081a0 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c @@ -877,7 +877,9 @@ static int xgbe_start(struct xgbe_prv_data *pdata) DBGPR("-->xgbe_start\n"); - hw_if->init(pdata); + ret = hw_if->init(pdata); + if (ret) + return ret; ret = phy_if->phy_start(pdata); if (ret) diff --git a/drivers/net/ethernet/broadcom/bgmac.c b/drivers/net/ethernet/broadcom/bgmac.c index b56c9c581359..70da30095b89 100644 --- a/drivers/net/ethernet/broadcom/bgmac.c +++ b/drivers/net/ethernet/broadcom/bgmac.c @@ -255,15 +255,16 @@ static void bgmac_dma_tx_free(struct bgmac *bgmac, struct bgmac_dma_ring *ring) while (ring->start != ring->end) { int slot_idx = ring->start % BGMAC_TX_RING_SLOTS; struct bgmac_slot_info *slot = &ring->slots[slot_idx]; - u32 ctl1; + u32 ctl0, ctl1; int len; if (slot_idx == empty_slot) break; + ctl0 = le32_to_cpu(ring->cpu_base[slot_idx].ctl0); ctl1 = le32_to_cpu(ring->cpu_base[slot_idx].ctl1); len = ctl1 & BGMAC_DESC_CTL1_LEN; - if (ctl1 & BGMAC_DESC_CTL0_SOF) + if (ctl0 & BGMAC_DESC_CTL0_SOF) /* Unmap no longer used buffer */ dma_unmap_single(dma_dev, slot->dma_addr, len, DMA_TO_DEVICE); @@ -469,6 +470,11 @@ static int bgmac_dma_rx_read(struct bgmac *bgmac, struct bgmac_dma_ring *ring, len -= ETH_FCS_LEN; skb = build_skb(buf, BGMAC_RX_ALLOC_SIZE); + if (unlikely(!skb)) { + bgmac_err(bgmac, "build_skb failed\n"); + put_page(virt_to_head_page(buf)); + break; + } skb_put(skb, BGMAC_RX_FRAME_OFFSET + BGMAC_RX_BUF_OFFSET + len); skb_pull(skb, BGMAC_RX_FRAME_OFFSET + @@ -1302,7 +1308,8 @@ static int bgmac_open(struct net_device *net_dev) phy_start(bgmac->phy_dev); - netif_carrier_on(net_dev); + netif_start_queue(net_dev); + return 0; } diff --git a/drivers/net/ethernet/emulex/benet/be_cmds.c b/drivers/net/ethernet/emulex/benet/be_cmds.c index 1795c935ff02..7b8638ddb673 100644 --- a/drivers/net/ethernet/emulex/benet/be_cmds.c +++ b/drivers/net/ethernet/emulex/benet/be_cmds.c @@ -1052,7 +1052,7 @@ int be_cmd_pmac_add(struct be_adapter *adapter, u8 *mac_addr, err: spin_unlock_bh(&adapter->mcc_lock); - if (status == MCC_STATUS_UNAUTHORIZED_REQUEST) + if (base_status(status) == MCC_STATUS_UNAUTHORIZED_REQUEST) status = -EPERM; return status; diff --git a/drivers/net/ethernet/freescale/gianfar.c b/drivers/net/ethernet/freescale/gianfar.c index 6a061f17a44f..4cd2a7d0124f 100644 --- a/drivers/net/ethernet/freescale/gianfar.c +++ b/drivers/net/ethernet/freescale/gianfar.c @@ -2939,7 +2939,7 @@ static bool gfar_add_rx_frag(struct gfar_rx_buff *rxb, u32 lstatus, size, GFAR_RXB_TRUESIZE); /* try reuse page */ - if (unlikely(page_count(page) != 1)) + if (unlikely(page_count(page) != 1 || page_is_pfmemalloc(page))) return false; /* change offset to the other half */ diff --git a/drivers/net/ethernet/ibm/ibmveth.c b/drivers/net/ethernet/ibm/ibmveth.c index f9e4988ea30e..2f9b12cf9ee5 100644 --- a/drivers/net/ethernet/ibm/ibmveth.c +++ b/drivers/net/ethernet/ibm/ibmveth.c @@ -1602,8 +1602,11 @@ static int ibmveth_probe(struct vio_dev *dev, const struct vio_device_id *id) netdev->netdev_ops = &ibmveth_netdev_ops; netdev->ethtool_ops = &netdev_ethtool_ops; SET_NETDEV_DEV(netdev, &dev->dev); - netdev->hw_features = NETIF_F_SG | NETIF_F_RXCSUM | - NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM; + netdev->hw_features = NETIF_F_SG; + if (vio_get_attribute(dev, "ibm,illan-options", NULL) != NULL) { + netdev->hw_features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | + NETIF_F_RXCSUM; + } netdev->features |= netdev->hw_features; diff --git a/drivers/net/ethernet/korina.c b/drivers/net/ethernet/korina.c index d74f5f4e5782..07eabf72c480 100644 --- a/drivers/net/ethernet/korina.c +++ b/drivers/net/ethernet/korina.c @@ -900,10 +900,10 @@ static void korina_restart_task(struct work_struct *work) DMA_STAT_DONE | DMA_STAT_HALT | DMA_STAT_ERR, &lp->rx_dma_regs->dmasm); - korina_free_ring(dev); - napi_disable(&lp->napi); + korina_free_ring(dev); + if (korina_init(dev) < 0) { printk(KERN_ERR "%s: cannot restart device\n", dev->name); return; @@ -1064,12 +1064,12 @@ static int korina_close(struct net_device *dev) tmp = tmp | DMA_STAT_DONE | DMA_STAT_HALT | DMA_STAT_ERR; writel(tmp, &lp->rx_dma_regs->dmasm); - korina_free_ring(dev); - napi_disable(&lp->napi); cancel_work_sync(&lp->restart_task); + korina_free_ring(dev); + free_irq(lp->rx_irq, dev); free_irq(lp->tx_irq, dev); free_irq(lp->ovr_irq, dev); diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c index 71ec9cb08e06..15056f06754a 100644 --- a/drivers/net/ethernet/marvell/mvneta.c +++ b/drivers/net/ethernet/marvell/mvneta.c @@ -2446,7 +2446,7 @@ static void mvneta_start_dev(struct mvneta_port *pp) mvneta_port_enable(pp); /* Enable polling on the port */ - for_each_present_cpu(cpu) { + for_each_online_cpu(cpu) { struct mvneta_pcpu_port *port = per_cpu_ptr(pp->ports, cpu); napi_enable(&port->napi); @@ -2472,7 +2472,7 @@ static void mvneta_stop_dev(struct mvneta_port *pp) phy_stop(pp->phy_dev); - for_each_present_cpu(cpu) { + for_each_online_cpu(cpu) { struct mvneta_pcpu_port *port = per_cpu_ptr(pp->ports, cpu); napi_disable(&port->napi); @@ -2902,13 +2902,11 @@ err_cleanup_rxqs: static int mvneta_stop(struct net_device *dev) { struct mvneta_port *pp = netdev_priv(dev); - int cpu; mvneta_stop_dev(pp); mvneta_mdio_remove(pp); unregister_cpu_notifier(&pp->cpu_notifier); - for_each_present_cpu(cpu) - smp_call_function_single(cpu, mvneta_percpu_disable, pp, true); + on_each_cpu(mvneta_percpu_disable, pp, true); free_percpu_irq(dev->irq, pp->ports); mvneta_cleanup_rxqs(pp); mvneta_cleanup_txqs(pp); diff --git a/drivers/net/ethernet/mellanox/mlx4/eq.c b/drivers/net/ethernet/mellanox/mlx4/eq.c index 603d1c3d3b2e..ff77b8b608bd 100644 --- a/drivers/net/ethernet/mellanox/mlx4/eq.c +++ b/drivers/net/ethernet/mellanox/mlx4/eq.c @@ -542,8 +542,9 @@ static int mlx4_eq_int(struct mlx4_dev *dev, struct mlx4_eq *eq) break; case MLX4_EVENT_TYPE_SRQ_LIMIT: - mlx4_dbg(dev, "%s: MLX4_EVENT_TYPE_SRQ_LIMIT\n", - __func__); + mlx4_dbg(dev, "%s: MLX4_EVENT_TYPE_SRQ_LIMIT. srq_no=0x%x, eq 0x%x\n", + __func__, be32_to_cpu(eqe->event.srq.srqn), + eq->eqn); case MLX4_EVENT_TYPE_SRQ_CATAS_ERROR: if (mlx4_is_master(dev)) { /* forward only to slave owning the SRQ */ @@ -558,15 +559,19 @@ static int mlx4_eq_int(struct mlx4_dev *dev, struct mlx4_eq *eq) eq->eqn, eq->cons_index, ret); break; } - mlx4_warn(dev, "%s: slave:%d, srq_no:0x%x, event: %02x(%02x)\n", - __func__, slave, - be32_to_cpu(eqe->event.srq.srqn), - eqe->type, eqe->subtype); + if (eqe->type == + MLX4_EVENT_TYPE_SRQ_CATAS_ERROR) + mlx4_warn(dev, "%s: slave:%d, srq_no:0x%x, event: %02x(%02x)\n", + __func__, slave, + be32_to_cpu(eqe->event.srq.srqn), + eqe->type, eqe->subtype); if (!ret && slave != dev->caps.function) { - mlx4_warn(dev, "%s: sending event %02x(%02x) to slave:%d\n", - __func__, eqe->type, - eqe->subtype, slave); + if (eqe->type == + MLX4_EVENT_TYPE_SRQ_CATAS_ERROR) + mlx4_warn(dev, "%s: sending event %02x(%02x) to slave:%d\n", + __func__, eqe->type, + eqe->subtype, slave); mlx4_slave_event(dev, slave, eqe); break; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/main.c b/drivers/net/ethernet/mellanox/mlx5/core/main.c index 1e611980cf99..f5c1f4acc57b 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/main.c @@ -153,8 +153,9 @@ static struct mlx5_profile profile[] = { }, }; -#define FW_INIT_TIMEOUT_MILI 2000 -#define FW_INIT_WAIT_MS 2 +#define FW_INIT_TIMEOUT_MILI 2000 +#define FW_INIT_WAIT_MS 2 +#define FW_PRE_INIT_TIMEOUT_MILI 10000 static int wait_fw_init(struct mlx5_core_dev *dev, u32 max_wait_mili) { @@ -934,6 +935,15 @@ static int mlx5_load_one(struct mlx5_core_dev *dev, struct mlx5_priv *priv) */ dev->state = MLX5_DEVICE_STATE_UP; + /* wait for firmware to accept initialization segments configurations + */ + err = wait_fw_init(dev, FW_PRE_INIT_TIMEOUT_MILI); + if (err) { + dev_err(&dev->pdev->dev, "Firmware over %d MS in pre-initializing state, aborting\n", + FW_PRE_INIT_TIMEOUT_MILI); + goto out; + } + err = mlx5_cmd_init(dev); if (err) { dev_err(&pdev->dev, "Failed initializing command interface, aborting\n"); diff --git a/drivers/net/ethernet/renesas/ravb_main.c b/drivers/net/ethernet/renesas/ravb_main.c index 1e61d4da72db..585e90f8341d 100644 --- a/drivers/net/ethernet/renesas/ravb_main.c +++ b/drivers/net/ethernet/renesas/ravb_main.c @@ -221,18 +221,6 @@ static void ravb_ring_free(struct net_device *ndev, int q) int ring_size; int i; - /* Free RX skb ringbuffer */ - if (priv->rx_skb[q]) { - for (i = 0; i < priv->num_rx_ring[q]; i++) - dev_kfree_skb(priv->rx_skb[q][i]); - } - kfree(priv->rx_skb[q]); - priv->rx_skb[q] = NULL; - - /* Free aligned TX buffers */ - kfree(priv->tx_align[q]); - priv->tx_align[q] = NULL; - if (priv->rx_ring[q]) { for (i = 0; i < priv->num_rx_ring[q]; i++) { struct ravb_ex_rx_desc *desc = &priv->rx_ring[q][i]; @@ -261,6 +249,18 @@ static void ravb_ring_free(struct net_device *ndev, int q) priv->tx_ring[q] = NULL; } + /* Free RX skb ringbuffer */ + if (priv->rx_skb[q]) { + for (i = 0; i < priv->num_rx_ring[q]; i++) + dev_kfree_skb(priv->rx_skb[q][i]); + } + kfree(priv->rx_skb[q]); + priv->rx_skb[q] = NULL; + + /* Free aligned TX buffers */ + kfree(priv->tx_align[q]); + priv->tx_align[q] = NULL; + /* Free TX skb ringbuffer. * SKBs are freed by ravb_tx_free() call above. */ diff --git a/drivers/net/ethernet/sfc/falcon.c b/drivers/net/ethernet/sfc/falcon.c index d790cb8d9db3..8e832ba8ab24 100644 --- a/drivers/net/ethernet/sfc/falcon.c +++ b/drivers/net/ethernet/sfc/falcon.c @@ -2796,6 +2796,11 @@ const struct efx_nic_type falcon_a1_nic_type = { .timer_period_max = 1 << FRF_AB_TC_TIMER_VAL_WIDTH, .offload_features = NETIF_F_IP_CSUM, .mcdi_max_ver = -1, +#ifdef CONFIG_SFC_SRIOV + .vswitching_probe = efx_port_dummy_op_int, + .vswitching_restore = efx_port_dummy_op_int, + .vswitching_remove = efx_port_dummy_op_void, +#endif }; const struct efx_nic_type falcon_b0_nic_type = { @@ -2897,4 +2902,9 @@ const struct efx_nic_type falcon_b0_nic_type = { .offload_features = NETIF_F_IP_CSUM | NETIF_F_RXHASH | NETIF_F_NTUPLE, .mcdi_max_ver = -1, .max_rx_ip_filters = FR_BZ_RX_FILTER_TBL0_ROWS, +#ifdef CONFIG_SFC_SRIOV + .vswitching_probe = efx_port_dummy_op_int, + .vswitching_restore = efx_port_dummy_op_int, + .vswitching_remove = efx_port_dummy_op_void, +#endif }; diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c index d2701c53ed68..ebec2dceff45 100644 --- a/drivers/net/phy/marvell.c +++ b/drivers/net/phy/marvell.c @@ -822,8 +822,6 @@ static int marvell_read_status(struct phy_device *phydev) phydev->lp_advertising = mii_stat1000_to_ethtool_lpa_t(lpagb) | mii_lpa_to_ethtool_lpa_t(lpa); - lpa &= adv; - if (status & MII_M1011_PHY_STATUS_FULLDUPLEX) phydev->duplex = DUPLEX_FULL; else diff --git a/drivers/net/phy/mdio-bcm-iproc.c b/drivers/net/phy/mdio-bcm-iproc.c index c0b4e65267af..46fe1ae919a3 100644 --- a/drivers/net/phy/mdio-bcm-iproc.c +++ b/drivers/net/phy/mdio-bcm-iproc.c @@ -81,8 +81,6 @@ static int iproc_mdio_read(struct mii_bus *bus, int phy_id, int reg) if (rc) return rc; - iproc_mdio_config_clk(priv->base); - /* Prepare the read operation */ cmd = (MII_DATA_TA_VAL << MII_DATA_TA_SHIFT) | (reg << MII_DATA_RA_SHIFT) | @@ -112,8 +110,6 @@ static int iproc_mdio_write(struct mii_bus *bus, int phy_id, if (rc) return rc; - iproc_mdio_config_clk(priv->base); - /* Prepare the write operation */ cmd = (MII_DATA_TA_VAL << MII_DATA_TA_SHIFT) | (reg << MII_DATA_RA_SHIFT) | @@ -163,6 +159,8 @@ static int iproc_mdio_probe(struct platform_device *pdev) bus->read = iproc_mdio_read; bus->write = iproc_mdio_write; + iproc_mdio_config_clk(priv->base); + rc = of_mdiobus_register(bus, pdev->dev.of_node); if (rc) { dev_err(&pdev->dev, "MDIO bus registration failed\n"); diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index 7f7c87762bc6..8dfc75250583 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -47,8 +47,16 @@ module_param(gso, bool, 0444); */ DECLARE_EWMA(pkt_len, 1, 64) +/* With mergeable buffers we align buffer address and use the low bits to + * encode its true size. Buffer size is up to 1 page so we need to align to + * square root of page size to ensure we reserve enough bits to encode the true + * size. + */ +#define MERGEABLE_BUFFER_MIN_ALIGN_SHIFT ((PAGE_SHIFT + 1) / 2) + /* Minimum alignment for mergeable packet buffers. */ -#define MERGEABLE_BUFFER_ALIGN max(L1_CACHE_BYTES, 256) +#define MERGEABLE_BUFFER_ALIGN max(L1_CACHE_BYTES, \ + 1 << MERGEABLE_BUFFER_MIN_ALIGN_SHIFT) #define VIRTNET_DRIVER_VERSION "1.0.0" diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c index 9a986ccd42e5..dab3bf6649e6 100644 --- a/drivers/net/vxlan.c +++ b/drivers/net/vxlan.c @@ -2240,7 +2240,7 @@ static void vxlan_cleanup(unsigned long arg) = container_of(p, struct vxlan_fdb, hlist); unsigned long timeout; - if (f->state & NUD_PERMANENT) + if (f->state & (NUD_PERMANENT | NUD_NOARP)) continue; timeout = f->used + vxlan->cfg.age_interval * HZ; diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig index 486af5dac5df..7945b1cd6244 100644 --- a/drivers/net/wireless/Kconfig +++ b/drivers/net/wireless/Kconfig @@ -311,6 +311,14 @@ config CNSS_CRYPTO from cnss platform driver. This crypto APIs used to generate cipher key and add support for the WLAN driver module security protocol. +config CNSS_QCA6290 + bool "Enable CNSS QCA6290 chipset specific changes" + ---help--- + This enables the changes from WLAN host driver that are specific to + CNSS QCA6290 chipset. + These changes are needed to support the new hardware architecture + for CNSS QCA6290 chipset. + source "drivers/net/wireless/ath/Kconfig" source "drivers/net/wireless/b43/Kconfig" source "drivers/net/wireless/b43legacy/Kconfig" @@ -332,6 +340,8 @@ source "drivers/net/wireless/mwifiex/Kconfig" source "drivers/net/wireless/cw1200/Kconfig" source "drivers/net/wireless/rsi/Kconfig" source "drivers/net/wireless/cnss/Kconfig" +source "drivers/net/wireless/cnss2/Kconfig" source "drivers/net/wireless/cnss_genl/Kconfig" +source "drivers/net/wireless/cnss_utils/Kconfig" endif # WLAN diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile index 0204fc00f0c5..f23a2fbc3afa 100644 --- a/drivers/net/wireless/Makefile +++ b/drivers/net/wireless/Makefile @@ -64,6 +64,8 @@ obj-$(CONFIG_RSI_91X) += rsi/ obj-$(CONFIG_WCNSS_CORE) += wcnss/ obj-$(CONFIG_CNSS) += cnss/ +obj-$(CONFIG_CNSS2) += cnss2/ obj-$(CONFIG_WCNSS_MEM_PRE_ALLOC) += cnss_prealloc/ obj-$(CONFIG_CNSS_CRYPTO) += cnss_crypto/ obj-$(CONFIG_CNSS_GENL) += cnss_genl/ +obj-$(CONFIG_CNSS_UTILS) += cnss_utils/ diff --git a/drivers/net/wireless/cnss/Kconfig b/drivers/net/wireless/cnss/Kconfig index 8e69a2b469b9..6faf9f1ef5d0 100644 --- a/drivers/net/wireless/cnss/Kconfig +++ b/drivers/net/wireless/cnss/Kconfig @@ -1,5 +1,6 @@ config CNSS tristate "CNSS driver for wifi module" + select CNSS_UTILS select CRYPTO select CRYPTO_HASH select CRYPTO_BLKCIPHER @@ -55,6 +56,9 @@ config CLD_LL_CORE select WEXT_PRIV select WEXT_SPY select WIRELESS_EXT + select CRYPTO + select CRYPTO_HASH + select CRYPTO_BLKCIPHER ---help--- This section contains the necessary modules needed to enable the core WLAN driver for Qualcomm QCA6174 chipset. @@ -72,7 +76,7 @@ config CNSS_SECURE_FW config BUS_AUTO_SUSPEND bool "Enable/Disable Runtime PM support for PCIe based WLAN Drivers" - depends on CNSS + depends on CNSS || CNSS2 depends on PCI ---help--- Runtime Power Management is supported for PCIe based WLAN Drivers. diff --git a/drivers/net/wireless/cnss2/Kconfig b/drivers/net/wireless/cnss2/Kconfig new file mode 100644 index 000000000000..85d2a7b30a84 --- /dev/null +++ b/drivers/net/wireless/cnss2/Kconfig @@ -0,0 +1,17 @@ +config CNSS2 + tristate "CNSS2 Platform Driver for Wi-Fi Module" + depends on !CNSS && PCI_MSM + ---help--- + This module adds the support for Connectivity Subsystem (CNSS) used + for PCIe based Wi-Fi devices with QCA6174/QCA6290 chipsets. + This driver also adds support to integrate WLAN module to subsystem + restart framework. + +config CNSS2_DEBUG + bool "CNSS2 Platform Driver Debug Support" + depends on CNSS2 + ---help--- + This option is to enable CNSS2 platform driver debug support which + primarily includes providing additional verbose logs for certain + features, enabling kernel panic for certain cases to aid the + debugging, and enabling any other debug mechanisms. diff --git a/drivers/net/wireless/cnss2/Makefile b/drivers/net/wireless/cnss2/Makefile new file mode 100644 index 000000000000..9d383c8daa43 --- /dev/null +++ b/drivers/net/wireless/cnss2/Makefile @@ -0,0 +1,9 @@ +obj-$(CONFIG_CNSS2) += cnss2.o + +cnss2-y := main.o +cnss2-y += debug.o +cnss2-y += pci.o +cnss2-y += power.o +cnss2-y += qmi.o +cnss2-y += utils.o +cnss2-y += wlan_firmware_service_v01.o diff --git a/drivers/net/wireless/cnss2/debug.c b/drivers/net/wireless/cnss2/debug.c new file mode 100644 index 000000000000..360ab31c61dd --- /dev/null +++ b/drivers/net/wireless/cnss2/debug.c @@ -0,0 +1,174 @@ +/* Copyright (c) 2016-2017, The Linux Foundation. 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 version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/err.h> +#include <linux/seq_file.h> +#include <linux/debugfs.h> +#include "main.h" +#include "debug.h" + +#define CNSS_IPC_LOG_PAGES 32 + +void *cnss_ipc_log_context; + +static int cnss_pin_connect_show(struct seq_file *s, void *data) +{ + struct cnss_plat_data *cnss_priv = s->private; + + seq_puts(s, "Pin connect results\n"); + seq_printf(s, "FW power pin result: %04x\n", + cnss_priv->pin_result.fw_pwr_pin_result); + seq_printf(s, "FW PHY IO pin result: %04x\n", + cnss_priv->pin_result.fw_phy_io_pin_result); + seq_printf(s, "FW RF pin result: %04x\n", + cnss_priv->pin_result.fw_rf_pin_result); + seq_printf(s, "Host pin result: %04x\n", + cnss_priv->pin_result.host_pin_result); + seq_puts(s, "\n"); + + return 0; +} + +static int cnss_pin_connect_open(struct inode *inode, struct file *file) +{ + return single_open(file, cnss_pin_connect_show, inode->i_private); +} + +static const struct file_operations cnss_pin_connect_fops = { + .read = seq_read, + .release = single_release, + .open = cnss_pin_connect_open, + .owner = THIS_MODULE, + .llseek = seq_lseek, +}; + +static int cnss_stats_show_state(struct seq_file *s, + struct cnss_plat_data *plat_priv) +{ + enum cnss_driver_state i; + int skip = 0; + unsigned long state; + + seq_printf(s, "\nState: 0x%lx(", plat_priv->driver_state); + for (i = 0, state = plat_priv->driver_state; state != 0; + state >>= 1, i++) { + if (!(state & 0x1)) + continue; + + if (skip++) + seq_puts(s, " | "); + + switch (i) { + case CNSS_QMI_WLFW_CONNECTED: + seq_puts(s, "QMI_WLFW_CONNECTED"); + continue; + case CNSS_FW_MEM_READY: + seq_puts(s, "FW_MEM_READY"); + continue; + case CNSS_FW_READY: + seq_puts(s, "FW_READY"); + continue; + case CNSS_COLD_BOOT_CAL: + seq_puts(s, "COLD_BOOT_CAL"); + continue; + case CNSS_DRIVER_LOADING: + seq_puts(s, "DRIVER_LOADING"); + continue; + case CNSS_DRIVER_UNLOADING: + seq_puts(s, "DRIVER_UNLOADING"); + continue; + case CNSS_DRIVER_PROBED: + seq_puts(s, "DRIVER_PROBED"); + continue; + case CNSS_DRIVER_RECOVERY: + seq_puts(s, "DRIVER_RECOVERY"); + continue; + case CNSS_FW_BOOT_RECOVERY: + seq_puts(s, "FW_BOOT_RECOVERY"); + continue; + case CNSS_DEV_ERR_NOTIFY: + seq_puts(s, "DEV_ERR"); + } + + seq_printf(s, "UNKNOWN-%d", i); + } + seq_puts(s, ")\n"); + + return 0; +} + +static int cnss_stats_show(struct seq_file *s, void *data) +{ + struct cnss_plat_data *plat_priv = s->private; + + cnss_stats_show_state(s, plat_priv); + + return 0; +} + +static int cnss_stats_open(struct inode *inode, struct file *file) +{ + return single_open(file, cnss_stats_show, inode->i_private); +} + +static const struct file_operations cnss_stats_fops = { + .read = seq_read, + .release = single_release, + .open = cnss_stats_open, + .owner = THIS_MODULE, + .llseek = seq_lseek, +}; + +int cnss_debugfs_create(struct cnss_plat_data *plat_priv) +{ + int ret = 0; + struct dentry *root_dentry; + + root_dentry = debugfs_create_dir("cnss", 0); + if (IS_ERR(root_dentry)) { + ret = PTR_ERR(root_dentry); + cnss_pr_err("Unable to create debugfs %d\n", ret); + goto out; + } + plat_priv->root_dentry = root_dentry; + debugfs_create_file("pin_connect_result", 0644, root_dentry, plat_priv, + &cnss_pin_connect_fops); + debugfs_create_file("stats", 0644, root_dentry, plat_priv, + &cnss_stats_fops); +out: + return ret; +} + +void cnss_debugfs_destroy(struct cnss_plat_data *plat_priv) +{ + debugfs_remove_recursive(plat_priv->root_dentry); +} + +int cnss_debug_init(void) +{ + cnss_ipc_log_context = ipc_log_context_create(CNSS_IPC_LOG_PAGES, + "cnss", 0); + if (!cnss_ipc_log_context) { + cnss_pr_err("Unable to create IPC log context!\n"); + return -EINVAL; + } + + return 0; +} + +void cnss_debug_deinit(void) +{ + if (cnss_ipc_log_context) { + ipc_log_context_destroy(cnss_ipc_log_context); + cnss_ipc_log_context = NULL; + } +} diff --git a/drivers/net/wireless/cnss2/debug.h b/drivers/net/wireless/cnss2/debug.h new file mode 100644 index 000000000000..1621514eb5b2 --- /dev/null +++ b/drivers/net/wireless/cnss2/debug.h @@ -0,0 +1,73 @@ +/* Copyright (c) 2016-2017, The Linux Foundation. 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 version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _CNSS_DEBUG_H +#define _CNSS_DEBUG_H + +#include <linux/ipc_logging.h> +#include <linux/printk.h> + +extern void *cnss_ipc_log_context; + +#define cnss_ipc_log_string(_x...) do { \ + if (cnss_ipc_log_context) \ + ipc_log_string(cnss_ipc_log_context, _x); \ + } while (0) + +#define cnss_pr_err(_fmt, ...) do { \ + pr_err("cnss: " _fmt, ##__VA_ARGS__); \ + cnss_ipc_log_string("ERR: " pr_fmt(_fmt), \ + ##__VA_ARGS__); \ + } while (0) + +#define cnss_pr_warn(_fmt, ...) do { \ + pr_warn("cnss: " _fmt, ##__VA_ARGS__); \ + cnss_ipc_log_string("WRN: " pr_fmt(_fmt), \ + ##__VA_ARGS__); \ + } while (0) + +#define cnss_pr_info(_fmt, ...) do { \ + pr_info("cnss: " _fmt, ##__VA_ARGS__); \ + cnss_ipc_log_string("INF: " pr_fmt(_fmt), \ + ##__VA_ARGS__); \ + } while (0) + +#define cnss_pr_dbg(_fmt, ...) do { \ + pr_debug("cnss: " _fmt, ##__VA_ARGS__); \ + cnss_ipc_log_string("DBG: " pr_fmt(_fmt), \ + ##__VA_ARGS__); \ + } while (0) + +#ifdef CONFIG_CNSS2_DEBUG +#define CNSS_ASSERT(_condition) do { \ + if (!(_condition)) { \ + cnss_pr_err("ASSERT at line %d\n", \ + __LINE__); \ + WARN_ON(1); \ + } \ + } while (0) +#else +#define CNSS_ASSERT(_condition) do { \ + if (!(_condition)) { \ + cnss_pr_err("ASSERT at line %d\n", \ + __LINE__); \ + WARN_ON(1); \ + } \ + } while (0) +#endif + +int cnss_debug_init(void); +void cnss_debug_deinit(void); +int cnss_debugfs_create(struct cnss_plat_data *plat_priv); +void cnss_debugfs_destroy(struct cnss_plat_data *plat_priv); + +#endif /* _CNSS_DEBUG_H */ diff --git a/drivers/net/wireless/cnss2/main.c b/drivers/net/wireless/cnss2/main.c new file mode 100644 index 000000000000..29bfe1f4d6ed --- /dev/null +++ b/drivers/net/wireless/cnss2/main.c @@ -0,0 +1,2309 @@ +/* Copyright (c) 2016-2017, The Linux Foundation. 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 version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/delay.h> +#include <linux/jiffies.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/pm_wakeup.h> +#include <linux/rwsem.h> +#include <linux/suspend.h> +#include <linux/timer.h> +#include <soc/qcom/ramdump.h> +#include <soc/qcom/subsystem_notif.h> + +#include "main.h" +#include "debug.h" +#include "pci.h" + +#define CNSS_DUMP_FORMAT_VER 0x11 +#define CNSS_DUMP_FORMAT_VER_V2 0x22 +#define CNSS_DUMP_MAGIC_VER_V2 0x42445953 +#define CNSS_DUMP_NAME "CNSS_WLAN" +#define CNSS_DUMP_DESC_SIZE 0x1000 +#define CNSS_DUMP_SEG_VER 0x1 +#define WLAN_RECOVERY_DELAY 1000 +#define FILE_SYSTEM_READY 1 +#define FW_READY_TIMEOUT 20000 +#define FW_ASSERT_TIMEOUT 5000 +#define CNSS_EVENT_PENDING 2989 + +static struct cnss_plat_data *plat_env; + +static DECLARE_RWSEM(cnss_pm_sem); + +static bool qmi_bypass; +#ifdef CONFIG_CNSS2_DEBUG +module_param(qmi_bypass, bool, 0600); +MODULE_PARM_DESC(qmi_bypass, "Bypass QMI from platform driver"); +#endif + +static bool enable_waltest; +#ifdef CONFIG_CNSS2_DEBUG +module_param(enable_waltest, bool, 0600); +MODULE_PARM_DESC(enable_waltest, "Enable to handle firmware waltest"); +#endif + +enum cnss_debug_quirks { + LINK_DOWN_SELF_RECOVERY, +}; + +unsigned long quirks; +#ifdef CONFIG_CNSS2_DEBUG +module_param(quirks, ulong, 0600); +MODULE_PARM_DESC(quirks, "Debug quirks for the driver"); +#endif + +static struct cnss_fw_files FW_FILES_QCA6174_FW_3_0 = { + "qwlan30.bin", "bdwlan30.bin", "otp30.bin", "utf30.bin", + "utfbd30.bin", "epping30.bin", "evicted30.bin" +}; + +static struct cnss_fw_files FW_FILES_DEFAULT = { + "qwlan.bin", "bdwlan.bin", "otp.bin", "utf.bin", + "utfbd.bin", "epping.bin", "evicted.bin" +}; + +struct cnss_driver_event { + struct list_head list; + enum cnss_driver_event_type type; + bool sync; + struct completion complete; + int ret; + void *data; +}; + +static enum cnss_dev_bus_type cnss_get_dev_bus_type(struct device *dev) +{ + if (!dev) + return CNSS_BUS_NONE; + + if (!dev->bus) + return CNSS_BUS_NONE; + + if (memcmp(dev->bus->name, "pci", 3) == 0) + return CNSS_BUS_PCI; + else + return CNSS_BUS_NONE; +} + +static void cnss_set_plat_priv(struct platform_device *plat_dev, + struct cnss_plat_data *plat_priv) +{ + plat_env = plat_priv; +} + +static struct cnss_plat_data *cnss_get_plat_priv(struct platform_device + *plat_dev) +{ + return plat_env; +} + +void *cnss_bus_dev_to_bus_priv(struct device *dev) +{ + if (!dev) + return NULL; + + switch (cnss_get_dev_bus_type(dev)) { + case CNSS_BUS_PCI: + return cnss_get_pci_priv(to_pci_dev(dev)); + default: + return NULL; + } +} + +struct cnss_plat_data *cnss_bus_dev_to_plat_priv(struct device *dev) +{ + void *bus_priv; + + if (!dev) + return cnss_get_plat_priv(NULL); + + bus_priv = cnss_bus_dev_to_bus_priv(dev); + if (!bus_priv) + return NULL; + + switch (cnss_get_dev_bus_type(dev)) { + case CNSS_BUS_PCI: + return cnss_pci_priv_to_plat_priv(bus_priv); + default: + return NULL; + } +} + +static int cnss_pm_notify(struct notifier_block *b, + unsigned long event, void *p) +{ + switch (event) { + case PM_SUSPEND_PREPARE: + down_write(&cnss_pm_sem); + break; + case PM_POST_SUSPEND: + up_write(&cnss_pm_sem); + break; + } + + return NOTIFY_DONE; +} + +static struct notifier_block cnss_pm_notifier = { + .notifier_call = cnss_pm_notify, +}; + +static void cnss_pm_stay_awake(struct cnss_plat_data *plat_priv) +{ + if (atomic_inc_return(&plat_priv->pm_count) != 1) + return; + + cnss_pr_dbg("PM stay awake, state: 0x%lx, count: %d\n", + plat_priv->driver_state, + atomic_read(&plat_priv->pm_count)); + pm_stay_awake(&plat_priv->plat_dev->dev); +} + +static void cnss_pm_relax(struct cnss_plat_data *plat_priv) +{ + int r = atomic_dec_return(&plat_priv->pm_count); + + WARN_ON(r < 0); + + if (r != 0) + return; + + cnss_pr_dbg("PM relax, state: 0x%lx, count: %d\n", + plat_priv->driver_state, + atomic_read(&plat_priv->pm_count)); + pm_relax(&plat_priv->plat_dev->dev); +} + +void cnss_lock_pm_sem(void) +{ + down_read(&cnss_pm_sem); +} +EXPORT_SYMBOL(cnss_lock_pm_sem); + +void cnss_release_pm_sem(void) +{ + up_read(&cnss_pm_sem); +} +EXPORT_SYMBOL(cnss_release_pm_sem); + +int cnss_get_fw_files_for_target(struct cnss_fw_files *pfw_files, + u32 target_type, u32 target_version) +{ + if (!pfw_files) + return -ENODEV; + + switch (target_version) { + case QCA6174_REV3_VERSION: + case QCA6174_REV3_2_VERSION: + memcpy(pfw_files, &FW_FILES_QCA6174_FW_3_0, sizeof(*pfw_files)); + break; + default: + memcpy(pfw_files, &FW_FILES_DEFAULT, sizeof(*pfw_files)); + cnss_pr_err("Unknown target version, type: 0x%X, version: 0x%X", + target_type, target_version); + break; + } + + return 0; +} +EXPORT_SYMBOL(cnss_get_fw_files_for_target); + +int cnss_request_bus_bandwidth(int bandwidth) +{ + int ret = 0; + struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(NULL); + struct cnss_bus_bw_info *bus_bw_info; + + if (!plat_priv) + return -ENODEV; + + bus_bw_info = &plat_priv->bus_bw_info; + if (!bus_bw_info->bus_client) + return -EINVAL; + + switch (bandwidth) { + case CNSS_BUS_WIDTH_NONE: + case CNSS_BUS_WIDTH_LOW: + case CNSS_BUS_WIDTH_MEDIUM: + case CNSS_BUS_WIDTH_HIGH: + ret = msm_bus_scale_client_update_request( + bus_bw_info->bus_client, bandwidth); + if (!ret) + bus_bw_info->current_bw_vote = bandwidth; + else + cnss_pr_err("Could not set bus bandwidth: %d, err = %d\n", + bandwidth, ret); + break; + default: + cnss_pr_err("Invalid bus bandwidth: %d", bandwidth); + ret = -EINVAL; + } + + return ret; +} +EXPORT_SYMBOL(cnss_request_bus_bandwidth); + +int cnss_get_platform_cap(struct cnss_platform_cap *cap) +{ + struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(NULL); + + if (!plat_priv) + return -ENODEV; + + if (cap) + *cap = plat_priv->cap; + + return 0; +} +EXPORT_SYMBOL(cnss_get_platform_cap); + +int cnss_get_soc_info(struct device *dev, struct cnss_soc_info *info) +{ + int ret = 0; + struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(dev); + void *bus_priv = cnss_bus_dev_to_bus_priv(dev); + + if (!plat_priv) + return -ENODEV; + + ret = cnss_pci_get_bar_info(bus_priv, &info->va, &info->pa); + if (ret) + return ret; + + return 0; +} +EXPORT_SYMBOL(cnss_get_soc_info); + +void cnss_set_driver_status(enum cnss_driver_status driver_status) +{ + struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(NULL); + + if (!plat_priv) + return; + + plat_priv->driver_status = driver_status; +} +EXPORT_SYMBOL(cnss_set_driver_status); + +void cnss_request_pm_qos(u32 qos_val) +{ + struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(NULL); + + if (!plat_priv) + return; + + pm_qos_add_request(&plat_priv->qos_request, PM_QOS_CPU_DMA_LATENCY, + qos_val); +} +EXPORT_SYMBOL(cnss_request_pm_qos); + +void cnss_remove_pm_qos(void) +{ + struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(NULL); + + if (!plat_priv) + return; + + pm_qos_remove_request(&plat_priv->qos_request); +} +EXPORT_SYMBOL(cnss_remove_pm_qos); + +u8 *cnss_common_get_wlan_mac_address(struct device *dev, u32 *num) +{ + struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(dev); + struct cnss_wlan_mac_info *wlan_mac_info; + struct cnss_wlan_mac_addr *addr; + + if (!plat_priv) + goto out; + + wlan_mac_info = &plat_priv->wlan_mac_info; + if (!wlan_mac_info->is_wlan_mac_set) { + cnss_pr_info("Platform driver doesn't have any MAC address!\n"); + goto out; + } + + addr = &wlan_mac_info->wlan_mac_addr; + *num = addr->no_of_mac_addr_set; + + return &addr->mac_addr[0][0]; +out: + *num = 0; + return NULL; +} +EXPORT_SYMBOL(cnss_common_get_wlan_mac_address); + +int cnss_wlan_enable(struct device *dev, + struct cnss_wlan_enable_cfg *config, + enum cnss_driver_mode mode, + const char *host_version) +{ + struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(dev); + struct wlfw_wlan_cfg_req_msg_v01 req; + u32 i; + int ret = 0; + + if (plat_priv->device_id == QCA6174_DEVICE_ID) + return 0; + + if (qmi_bypass) + return 0; + + if (!config || !host_version) { + cnss_pr_err("Invalid config or host_version pointer\n"); + return -EINVAL; + } + + cnss_pr_dbg("Mode: %d, config: %pK, host_version: %s\n", + mode, config, host_version); + + if (mode == CNSS_WALTEST || mode == CNSS_CCPM) + goto skip_cfg; + + memset(&req, 0, sizeof(req)); + + req.host_version_valid = 1; + strlcpy(req.host_version, host_version, + QMI_WLFW_MAX_STR_LEN_V01 + 1); + + req.tgt_cfg_valid = 1; + if (config->num_ce_tgt_cfg > QMI_WLFW_MAX_NUM_CE_V01) + req.tgt_cfg_len = QMI_WLFW_MAX_NUM_CE_V01; + else + req.tgt_cfg_len = config->num_ce_tgt_cfg; + for (i = 0; i < req.tgt_cfg_len; i++) { + req.tgt_cfg[i].pipe_num = config->ce_tgt_cfg[i].pipe_num; + req.tgt_cfg[i].pipe_dir = config->ce_tgt_cfg[i].pipe_dir; + req.tgt_cfg[i].nentries = config->ce_tgt_cfg[i].nentries; + req.tgt_cfg[i].nbytes_max = config->ce_tgt_cfg[i].nbytes_max; + req.tgt_cfg[i].flags = config->ce_tgt_cfg[i].flags; + } + + req.svc_cfg_valid = 1; + if (config->num_ce_svc_pipe_cfg > QMI_WLFW_MAX_NUM_SVC_V01) + req.svc_cfg_len = QMI_WLFW_MAX_NUM_SVC_V01; + else + req.svc_cfg_len = config->num_ce_svc_pipe_cfg; + for (i = 0; i < req.svc_cfg_len; i++) { + req.svc_cfg[i].service_id = config->ce_svc_cfg[i].service_id; + req.svc_cfg[i].pipe_dir = config->ce_svc_cfg[i].pipe_dir; + req.svc_cfg[i].pipe_num = config->ce_svc_cfg[i].pipe_num; + } + + req.shadow_reg_v2_valid = 1; + if (config->num_shadow_reg_v2_cfg > + QMI_WLFW_MAX_NUM_SHADOW_REG_V2_V01) + req.shadow_reg_v2_len = QMI_WLFW_MAX_NUM_SHADOW_REG_V2_V01; + else + req.shadow_reg_v2_len = config->num_shadow_reg_v2_cfg; + + memcpy(req.shadow_reg_v2, config->shadow_reg_v2_cfg, + sizeof(struct wlfw_shadow_reg_v2_cfg_s_v01) + * req.shadow_reg_v2_len); + + ret = cnss_wlfw_wlan_cfg_send_sync(plat_priv, &req); + if (ret) + goto out; + +skip_cfg: + ret = cnss_wlfw_wlan_mode_send_sync(plat_priv, mode); +out: + return ret; +} +EXPORT_SYMBOL(cnss_wlan_enable); + +int cnss_wlan_disable(struct device *dev, enum cnss_driver_mode mode) +{ + struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(dev); + + if (plat_priv->device_id == QCA6174_DEVICE_ID) + return 0; + + if (qmi_bypass) + return 0; + + return cnss_wlfw_wlan_mode_send_sync(plat_priv, QMI_WLFW_OFF_V01); +} +EXPORT_SYMBOL(cnss_wlan_disable); + +#ifdef CONFIG_CNSS2_DEBUG +int cnss_athdiag_read(struct device *dev, u32 offset, u32 mem_type, + u32 data_len, u8 *output) +{ + struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(dev); + int ret = 0; + + if (!plat_priv) { + cnss_pr_err("plat_priv is NULL!\n"); + return -EINVAL; + } + + if (plat_priv->device_id == QCA6174_DEVICE_ID) + return 0; + + if (!output || data_len == 0 || data_len > QMI_WLFW_MAX_DATA_SIZE_V01) { + cnss_pr_err("Invalid parameters for athdiag read: output %p, data_len %u\n", + output, data_len); + ret = -EINVAL; + goto out; + } + + if (!test_bit(CNSS_FW_READY, &plat_priv->driver_state)) { + cnss_pr_err("Invalid state for athdiag read: 0x%lx\n", + plat_priv->driver_state); + ret = -EINVAL; + goto out; + } + + ret = cnss_wlfw_athdiag_read_send_sync(plat_priv, offset, mem_type, + data_len, output); + +out: + return ret; +} +EXPORT_SYMBOL(cnss_athdiag_read); + +int cnss_athdiag_write(struct device *dev, u32 offset, u32 mem_type, + u32 data_len, u8 *input) +{ + struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(dev); + int ret = 0; + + if (!plat_priv) { + cnss_pr_err("plat_priv is NULL!\n"); + return -EINVAL; + } + + if (plat_priv->device_id == QCA6174_DEVICE_ID) + return 0; + + if (!input || data_len == 0 || data_len > QMI_WLFW_MAX_DATA_SIZE_V01) { + cnss_pr_err("Invalid parameters for athdiag write: input %p, data_len %u\n", + input, data_len); + ret = -EINVAL; + goto out; + } + + if (!test_bit(CNSS_FW_READY, &plat_priv->driver_state)) { + cnss_pr_err("Invalid state for athdiag write: 0x%lx\n", + plat_priv->driver_state); + ret = -EINVAL; + goto out; + } + + ret = cnss_wlfw_athdiag_write_send_sync(plat_priv, offset, mem_type, + data_len, input); + +out: + return ret; +} +EXPORT_SYMBOL(cnss_athdiag_write); +#else +int cnss_athdiag_read(struct device *dev, u32 offset, u32 mem_type, + u32 data_len, u8 *output) +{ + return -EPERM; +} +EXPORT_SYMBOL(cnss_athdiag_read); + +int cnss_athdiag_write(struct device *dev, u32 offset, u32 mem_type, + u32 data_len, u8 *input) +{ + return -EPERM; +} +EXPORT_SYMBOL(cnss_athdiag_write); +#endif + +int cnss_set_fw_log_mode(struct device *dev, u8 fw_log_mode) +{ + struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(dev); + + if (plat_priv->device_id == QCA6174_DEVICE_ID) + return 0; + + return cnss_wlfw_ini_send_sync(plat_priv, fw_log_mode); +} +EXPORT_SYMBOL(cnss_set_fw_log_mode); + +static int cnss_fw_mem_ready_hdlr(struct cnss_plat_data *plat_priv) +{ + int ret = 0; + + if (!plat_priv) + return -ENODEV; + + set_bit(CNSS_FW_MEM_READY, &plat_priv->driver_state); + + ret = cnss_wlfw_tgt_cap_send_sync(plat_priv); + if (ret) + goto out; + + ret = cnss_wlfw_bdf_dnld_send_sync(plat_priv); + if (ret) + goto out; + + ret = cnss_pci_load_m3(plat_priv->bus_priv); + if (ret) + goto out; + + ret = cnss_wlfw_m3_dnld_send_sync(plat_priv); + if (ret) + goto out; + + return 0; +out: + return ret; +} + +static int cnss_driver_call_probe(struct cnss_plat_data *plat_priv) +{ + int ret; + struct cnss_pci_data *pci_priv = plat_priv->bus_priv; + + if (!plat_priv->driver_ops) { + cnss_pr_err("driver_ops is NULL!"); + ret = -EINVAL; + goto out; + } + + if (test_bit(CNSS_DRIVER_RECOVERY, &plat_priv->driver_state)) { + ret = plat_priv->driver_ops->reinit(pci_priv->pci_dev, + pci_priv->pci_device_id); + if (ret) { + cnss_pr_err("Failed to reinit host driver, err = %d\n", + ret); + goto out; + } + clear_bit(CNSS_DRIVER_RECOVERY, &plat_priv->driver_state); + } else { + ret = plat_priv->driver_ops->probe(pci_priv->pci_dev, + pci_priv->pci_device_id); + if (ret) { + cnss_pr_err("Failed to probe host driver, err = %d\n", + ret); + goto out; + } + clear_bit(CNSS_DRIVER_LOADING, &plat_priv->driver_state); + set_bit(CNSS_DRIVER_PROBED, &plat_priv->driver_state); + } + + return 0; + +out: + return ret; +} + +static int cnss_fw_ready_hdlr(struct cnss_plat_data *plat_priv) +{ + int ret = 0; + + if (!plat_priv) + return -ENODEV; + + del_timer(&plat_priv->fw_boot_timer); + set_bit(CNSS_FW_READY, &plat_priv->driver_state); + + if (test_bit(CNSS_FW_BOOT_RECOVERY, &plat_priv->driver_state)) { + clear_bit(CNSS_FW_BOOT_RECOVERY, &plat_priv->driver_state); + clear_bit(CNSS_DRIVER_RECOVERY, &plat_priv->driver_state); + } + + if (enable_waltest) { + ret = cnss_wlfw_wlan_mode_send_sync(plat_priv, + QMI_WLFW_WALTEST_V01); + } else if (test_bit(CNSS_COLD_BOOT_CAL, &plat_priv->driver_state)) { + ret = cnss_wlfw_wlan_mode_send_sync(plat_priv, + QMI_WLFW_CALIBRATION_V01); + } else { + ret = cnss_driver_call_probe(plat_priv); + } + + if (ret) + goto shutdown; + + return 0; + +shutdown: + cnss_pci_stop_mhi(plat_priv->bus_priv); + cnss_suspend_pci_link(plat_priv->bus_priv); + cnss_power_off_device(plat_priv); + + return ret; +} + +static char *cnss_driver_event_to_str(enum cnss_driver_event_type type) +{ + switch (type) { + case CNSS_DRIVER_EVENT_SERVER_ARRIVE: + return "SERVER_ARRIVE"; + case CNSS_DRIVER_EVENT_SERVER_EXIT: + return "SERVER_EXIT"; + case CNSS_DRIVER_EVENT_REQUEST_MEM: + return "REQUEST_MEM"; + case CNSS_DRIVER_EVENT_FW_MEM_READY: + return "FW_MEM_READY"; + case CNSS_DRIVER_EVENT_FW_READY: + return "FW_READY"; + case CNSS_DRIVER_EVENT_COLD_BOOT_CAL_START: + return "COLD_BOOT_CAL_START"; + case CNSS_DRIVER_EVENT_COLD_BOOT_CAL_DONE: + return "COLD_BOOT_CAL_DONE"; + case CNSS_DRIVER_EVENT_REGISTER_DRIVER: + return "REGISTER_DRIVER"; + case CNSS_DRIVER_EVENT_UNREGISTER_DRIVER: + return "UNREGISTER_DRIVER"; + case CNSS_DRIVER_EVENT_RECOVERY: + return "RECOVERY"; + case CNSS_DRIVER_EVENT_FORCE_FW_ASSERT: + return "FORCE_FW_ASSERT"; + case CNSS_DRIVER_EVENT_MAX: + return "EVENT_MAX"; + } + + return "UNKNOWN"; +}; + +int cnss_driver_event_post(struct cnss_plat_data *plat_priv, + enum cnss_driver_event_type type, + bool sync, void *data) +{ + struct cnss_driver_event *event; + unsigned long flags; + int gfp = GFP_KERNEL; + int ret = 0; + + if (!plat_priv) + return -ENODEV; + + cnss_pr_dbg("Posting event: %s(%d)%s, state: 0x%lx\n", + cnss_driver_event_to_str(type), type, + sync ? "-sync" : "", plat_priv->driver_state); + + if (type >= CNSS_DRIVER_EVENT_MAX) { + cnss_pr_err("Invalid Event type: %d, can't post", type); + return -EINVAL; + } + + if (in_interrupt() || irqs_disabled()) + gfp = GFP_ATOMIC; + + event = kzalloc(sizeof(*event), gfp); + if (!event) + return -ENOMEM; + + cnss_pm_stay_awake(plat_priv); + + event->type = type; + event->data = data; + init_completion(&event->complete); + event->ret = CNSS_EVENT_PENDING; + event->sync = sync; + + spin_lock_irqsave(&plat_priv->event_lock, flags); + list_add_tail(&event->list, &plat_priv->event_list); + spin_unlock_irqrestore(&plat_priv->event_lock, flags); + + queue_work(plat_priv->event_wq, &plat_priv->event_work); + + if (!sync) + goto out; + + ret = wait_for_completion_interruptible(&event->complete); + + cnss_pr_dbg("Completed event: %s(%d), state: 0x%lx, ret: %d/%d\n", + cnss_driver_event_to_str(type), type, + plat_priv->driver_state, ret, event->ret); + + spin_lock_irqsave(&plat_priv->event_lock, flags); + if (ret == -ERESTARTSYS && event->ret == CNSS_EVENT_PENDING) { + event->sync = false; + spin_unlock_irqrestore(&plat_priv->event_lock, flags); + ret = -EINTR; + goto out; + } + spin_unlock_irqrestore(&plat_priv->event_lock, flags); + + ret = event->ret; + kfree(event); + +out: + cnss_pm_relax(plat_priv); + return ret; +} + +int cnss_power_up(struct device *dev) +{ + int ret = 0; + void *bus_priv = cnss_bus_dev_to_bus_priv(dev); + struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(dev); + + if (!bus_priv || !plat_priv) + return -ENODEV; + + if (plat_priv->device_id != QCA6174_DEVICE_ID) { + cnss_pr_dbg("Power up is not supported for device ID 0x%lx\n", + plat_priv->device_id); + return 0; + } + + ret = cnss_power_on_device(plat_priv); + if (ret) { + cnss_pr_err("Failed to power on device, err = %d\n", ret); + goto err_power_on; + } + + ret = cnss_resume_pci_link(bus_priv); + if (ret) { + cnss_pr_err("Failed to resume PCI link, err = %d\n", ret); + goto err_resume_link; + } + + return 0; +err_resume_link: + cnss_power_off_device(plat_priv); +err_power_on: + return ret; +} +EXPORT_SYMBOL(cnss_power_up); + +int cnss_power_down(struct device *dev) +{ + int ret = 0; + void *bus_priv = cnss_bus_dev_to_bus_priv(dev); + struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(dev); + + if (!bus_priv || !plat_priv) + return -ENODEV; + + if (plat_priv->device_id != QCA6174_DEVICE_ID) { + cnss_pr_dbg("Power down is not supported for device ID 0x%lx\n", + plat_priv->device_id); + return 0; + } + + cnss_request_bus_bandwidth(CNSS_BUS_WIDTH_NONE); + cnss_pci_set_monitor_wake_intr(bus_priv, false); + cnss_pci_set_auto_suspended(bus_priv, 0); + + ret = cnss_suspend_pci_link(bus_priv); + if (ret) + cnss_pr_err("Failed to suspend PCI link, err = %d\n", ret); + + cnss_power_off_device(plat_priv); + + return 0; +} +EXPORT_SYMBOL(cnss_power_down); + +int cnss_wlan_register_driver(struct cnss_wlan_driver *driver_ops) +{ + int ret = 0; + struct cnss_plat_data *plat_priv = cnss_get_plat_priv(NULL); + + if (!plat_priv) { + cnss_pr_err("plat_priv is NULL!\n"); + return -ENODEV; + } + + if (plat_priv->driver_ops) { + cnss_pr_err("Driver has already registered!\n"); + return -EEXIST; + } + + ret = cnss_driver_event_post(plat_priv, + CNSS_DRIVER_EVENT_REGISTER_DRIVER, + true, driver_ops); + return ret; +} +EXPORT_SYMBOL(cnss_wlan_register_driver); + +void cnss_wlan_unregister_driver(struct cnss_wlan_driver *driver_ops) +{ + struct cnss_plat_data *plat_priv = cnss_get_plat_priv(NULL); + + if (!plat_priv) { + cnss_pr_err("plat_priv is NULL!\n"); + return; + } + + cnss_driver_event_post(plat_priv, + CNSS_DRIVER_EVENT_UNREGISTER_DRIVER, + true, NULL); +} +EXPORT_SYMBOL(cnss_wlan_unregister_driver); + +static int cnss_get_resources(struct cnss_plat_data *plat_priv) +{ + int ret = 0; + + ret = cnss_get_vreg(plat_priv); + if (ret) { + cnss_pr_err("Failed to get vreg, err = %d\n", ret); + goto out; + } + + ret = cnss_get_pinctrl(plat_priv); + if (ret) { + cnss_pr_err("Failed to get pinctrl, err = %d\n", ret); + goto out; + } + + return 0; +out: + return ret; +} + +static void cnss_put_resources(struct cnss_plat_data *plat_priv) +{ +} + +static int cnss_modem_notifier_nb(struct notifier_block *nb, + unsigned long code, + void *ss_handle) +{ + struct cnss_plat_data *plat_priv = + container_of(nb, struct cnss_plat_data, modem_nb); + struct cnss_pci_data *pci_priv = plat_priv->bus_priv; + struct cnss_esoc_info *esoc_info; + struct cnss_wlan_driver *driver_ops; + + cnss_pr_dbg("Modem notifier: event %lu\n", code); + + if (!pci_priv) + return NOTIFY_DONE; + + esoc_info = &plat_priv->esoc_info; + + if (code == SUBSYS_AFTER_POWERUP) + esoc_info->modem_current_status = 1; + else if (code == SUBSYS_BEFORE_SHUTDOWN) + esoc_info->modem_current_status = 0; + else + return NOTIFY_DONE; + + driver_ops = plat_priv->driver_ops; + if (!driver_ops || !driver_ops->modem_status) + return NOTIFY_DONE; + + driver_ops->modem_status(pci_priv->pci_dev, + esoc_info->modem_current_status); + + return NOTIFY_OK; +} + +static int cnss_register_esoc(struct cnss_plat_data *plat_priv) +{ + int ret = 0; + struct device *dev; + struct cnss_esoc_info *esoc_info; + struct esoc_desc *esoc_desc; + const char *client_desc; + + dev = &plat_priv->plat_dev->dev; + esoc_info = &plat_priv->esoc_info; + + esoc_info->notify_modem_status = + of_property_read_bool(dev->of_node, + "qcom,notify-modem-status"); + + if (esoc_info->notify_modem_status) + goto out; + + ret = of_property_read_string_index(dev->of_node, "esoc-names", 0, + &client_desc); + if (ret) { + cnss_pr_dbg("esoc-names is not defined in DT, skip!\n"); + } else { + esoc_desc = devm_register_esoc_client(dev, client_desc); + if (IS_ERR_OR_NULL(esoc_desc)) { + ret = PTR_RET(esoc_desc); + cnss_pr_err("Failed to register esoc_desc, err = %d\n", + ret); + goto out; + } + esoc_info->esoc_desc = esoc_desc; + } + + plat_priv->modem_nb.notifier_call = cnss_modem_notifier_nb; + esoc_info->modem_current_status = 0; + esoc_info->modem_notify_handler = + subsys_notif_register_notifier(esoc_info->esoc_desc ? + esoc_info->esoc_desc->name : + "modem", &plat_priv->modem_nb); + if (IS_ERR(esoc_info->modem_notify_handler)) { + ret = PTR_ERR(esoc_info->modem_notify_handler); + cnss_pr_err("Failed to register esoc notifier, err = %d\n", + ret); + goto unreg_esoc; + } + + return 0; +unreg_esoc: + if (esoc_info->esoc_desc) + devm_unregister_esoc_client(dev, esoc_info->esoc_desc); +out: + return ret; +} + +static void cnss_unregister_esoc(struct cnss_plat_data *plat_priv) +{ + struct device *dev; + struct cnss_esoc_info *esoc_info; + + dev = &plat_priv->plat_dev->dev; + esoc_info = &plat_priv->esoc_info; + + if (esoc_info->notify_modem_status) + subsys_notif_unregister_notifier(esoc_info-> + modem_notify_handler, + &plat_priv->modem_nb); + if (esoc_info->esoc_desc) + devm_unregister_esoc_client(dev, esoc_info->esoc_desc); +} + +static int cnss_qca6174_powerup(struct cnss_plat_data *plat_priv) +{ + int ret = 0; + struct cnss_pci_data *pci_priv = plat_priv->bus_priv; + + if (!pci_priv) { + cnss_pr_err("pci_priv is NULL!\n"); + return -ENODEV; + } + + if (!plat_priv->driver_ops) { + cnss_pr_err("driver_ops is NULL!\n"); + return -EINVAL; + } + + ret = cnss_power_on_device(plat_priv); + if (ret) { + cnss_pr_err("Failed to power on device, err = %d\n", ret); + goto out; + } + + ret = cnss_resume_pci_link(pci_priv); + if (ret) { + cnss_pr_err("Failed to resume PCI link, err = %d\n", ret); + goto power_off; + } + + ret = cnss_driver_call_probe(plat_priv); + if (ret) + goto suspend_link; + + return 0; +suspend_link: + cnss_suspend_pci_link(pci_priv); +power_off: + cnss_power_off_device(plat_priv); +out: + return ret; +} + +static int cnss_qca6174_shutdown(struct cnss_plat_data *plat_priv) +{ + int ret = 0; + struct cnss_pci_data *pci_priv = plat_priv->bus_priv; + + if (!pci_priv) + return -ENODEV; + + if (!plat_priv->driver_ops) + return -EINVAL; + + if (test_bit(CNSS_DRIVER_UNLOADING, &plat_priv->driver_state)) { + cnss_request_bus_bandwidth(CNSS_BUS_WIDTH_NONE); + plat_priv->driver_ops->remove(pci_priv->pci_dev); + cnss_pci_set_monitor_wake_intr(pci_priv, false); + cnss_pci_set_auto_suspended(pci_priv, 0); + } else { + plat_priv->driver_ops->shutdown(pci_priv->pci_dev); + } + + ret = cnss_suspend_pci_link(pci_priv); + if (ret) + cnss_pr_err("Failed to suspend PCI link, err = %d\n", ret); + + cnss_power_off_device(plat_priv); + + if (test_bit(CNSS_DRIVER_UNLOADING, &plat_priv->driver_state)) { + clear_bit(CNSS_DRIVER_UNLOADING, &plat_priv->driver_state); + clear_bit(CNSS_DRIVER_PROBED, &plat_priv->driver_state); + } + + return ret; +} + +static void cnss_qca6174_crash_shutdown(struct cnss_plat_data *plat_priv) +{ + struct cnss_pci_data *pci_priv = plat_priv->bus_priv; + + if (!plat_priv->driver_ops) + return; + + plat_priv->driver_ops->crash_shutdown(pci_priv->pci_dev); +} + +static int cnss_qca6290_powerup(struct cnss_plat_data *plat_priv) +{ + int ret = 0; + struct cnss_pci_data *pci_priv = plat_priv->bus_priv; + unsigned int timeout; + + if (!pci_priv) { + cnss_pr_err("pci_priv is NULL!\n"); + return -ENODEV; + } + + if (plat_priv->ramdump_info_v2.dump_data_valid) { + cnss_pci_set_mhi_state(pci_priv, CNSS_MHI_DEINIT); + cnss_pci_clear_dump_info(pci_priv); + } + + ret = cnss_power_on_device(plat_priv); + if (ret) { + cnss_pr_err("Failed to power on device, err = %d\n", ret); + goto out; + } + + ret = cnss_resume_pci_link(pci_priv); + if (ret) { + cnss_pr_err("Failed to resume PCI link, err = %d\n", ret); + goto power_off; + } + + timeout = cnss_get_qmi_timeout(); + + ret = cnss_pci_start_mhi(pci_priv); + if (ret) { + cnss_pr_err("Failed to start MHI, err = %d\n", ret); + if (!test_bit(CNSS_DEV_ERR_NOTIFY, &plat_priv->driver_state) && + !pci_priv->pci_link_down_ind && timeout) + mod_timer(&plat_priv->fw_boot_timer, + jiffies + msecs_to_jiffies(timeout >> 1)); + return 0; + } + + cnss_set_pin_connect_status(plat_priv); + + if (qmi_bypass) { + ret = cnss_driver_call_probe(plat_priv); + if (ret) + goto stop_mhi; + } else if (timeout) { + mod_timer(&plat_priv->fw_boot_timer, + jiffies + msecs_to_jiffies(timeout << 1)); + } + + return 0; + +stop_mhi: + cnss_pci_stop_mhi(pci_priv); + cnss_suspend_pci_link(pci_priv); +power_off: + cnss_power_off_device(plat_priv); +out: + return ret; +} + +static int cnss_qca6290_shutdown(struct cnss_plat_data *plat_priv) +{ + int ret = 0; + struct cnss_pci_data *pci_priv = plat_priv->bus_priv; + + if (!pci_priv) + return -ENODEV; + + if (test_bit(CNSS_COLD_BOOT_CAL, &plat_priv->driver_state) || + test_bit(CNSS_FW_BOOT_RECOVERY, &plat_priv->driver_state)) + goto skip_driver_remove; + + if (!plat_priv->driver_ops) + return -EINVAL; + + if (test_bit(CNSS_DRIVER_UNLOADING, &plat_priv->driver_state)) { + cnss_request_bus_bandwidth(CNSS_BUS_WIDTH_NONE); + plat_priv->driver_ops->remove(pci_priv->pci_dev); + cnss_pci_set_monitor_wake_intr(pci_priv, false); + cnss_pci_set_auto_suspended(pci_priv, 0); + } else { + plat_priv->driver_ops->shutdown(pci_priv->pci_dev); + } + +skip_driver_remove: + cnss_pci_stop_mhi(pci_priv); + + ret = cnss_suspend_pci_link(pci_priv); + if (ret) + cnss_pr_err("Failed to suspend PCI link, err = %d\n", ret); + + cnss_power_off_device(plat_priv); + + clear_bit(CNSS_FW_READY, &plat_priv->driver_state); + clear_bit(CNSS_FW_MEM_READY, &plat_priv->driver_state); + + if (test_bit(CNSS_DRIVER_UNLOADING, &plat_priv->driver_state)) { + clear_bit(CNSS_DRIVER_UNLOADING, &plat_priv->driver_state); + clear_bit(CNSS_DRIVER_PROBED, &plat_priv->driver_state); + } + + return ret; +} + +static void cnss_qca6290_crash_shutdown(struct cnss_plat_data *plat_priv) +{ + struct cnss_pci_data *pci_priv = plat_priv->bus_priv; + int ret = 0; + + cnss_pr_dbg("Crash shutdown with driver_state 0x%lx\n", + plat_priv->driver_state); + + if (test_bit(CNSS_DRIVER_RECOVERY, &plat_priv->driver_state) || + test_bit(CNSS_DRIVER_LOADING, &plat_priv->driver_state) || + test_bit(CNSS_DRIVER_UNLOADING, &plat_priv->driver_state)) + return; + + ret = cnss_pci_set_mhi_state(pci_priv, CNSS_MHI_RDDM_KERNEL_PANIC); + if (ret) { + cnss_pr_err("Fail to complete RDDM, err = %d\n", ret); + return; + } + + cnss_pci_collect_dump_info(pci_priv); +} + +static int cnss_powerup(const struct subsys_desc *subsys_desc) +{ + int ret = 0; + struct cnss_plat_data *plat_priv; + + if (!subsys_desc->dev) { + cnss_pr_err("dev from subsys_desc is NULL\n"); + return -ENODEV; + } + + plat_priv = dev_get_drvdata(subsys_desc->dev); + if (!plat_priv) { + cnss_pr_err("plat_priv is NULL!\n"); + return -ENODEV; + } + + if (!plat_priv->driver_state) { + cnss_pr_dbg("Powerup is ignored.\n"); + return 0; + } + + switch (plat_priv->device_id) { + case QCA6174_DEVICE_ID: + ret = cnss_qca6174_powerup(plat_priv); + break; + case QCA6290_EMULATION_DEVICE_ID: + case QCA6290_DEVICE_ID: + ret = cnss_qca6290_powerup(plat_priv); + break; + default: + cnss_pr_err("Unknown device_id found: 0x%lx\n", + plat_priv->device_id); + ret = -ENODEV; + } + + return ret; +} + +static int cnss_shutdown(const struct subsys_desc *subsys_desc, bool force_stop) +{ + int ret = 0; + struct cnss_plat_data *plat_priv; + + if (!subsys_desc->dev) { + cnss_pr_err("dev from subsys_desc is NULL\n"); + return -ENODEV; + } + + plat_priv = dev_get_drvdata(subsys_desc->dev); + if (!plat_priv) { + cnss_pr_err("plat_priv is NULL!\n"); + return -ENODEV; + } + + switch (plat_priv->device_id) { + case QCA6174_DEVICE_ID: + ret = cnss_qca6174_shutdown(plat_priv); + break; + case QCA6290_EMULATION_DEVICE_ID: + case QCA6290_DEVICE_ID: + ret = cnss_qca6290_shutdown(plat_priv); + break; + default: + cnss_pr_err("Unknown device_id found: 0x%lx\n", + plat_priv->device_id); + ret = -ENODEV; + } + + return ret; +} + +static int cnss_qca6290_ramdump(struct cnss_plat_data *plat_priv) +{ + struct cnss_ramdump_info_v2 *info_v2 = &plat_priv->ramdump_info_v2; + struct cnss_dump_data *dump_data = &info_v2->dump_data; + struct cnss_dump_seg *dump_seg = info_v2->dump_data_vaddr; + struct ramdump_segment *ramdump_segs, *s; + int i, ret = 0; + + if (!info_v2->dump_data_valid || + dump_data->nentries == 0) + return 0; + + ramdump_segs = kcalloc(dump_data->nentries, + sizeof(*ramdump_segs), + GFP_KERNEL); + if (!ramdump_segs) + return -ENOMEM; + + s = ramdump_segs; + for (i = 0; i < dump_data->nentries; i++) { + s->address = dump_seg->address; + s->v_address = dump_seg->v_address; + s->size = dump_seg->size; + s++; + dump_seg++; + } + + ret = do_elf_ramdump(info_v2->ramdump_dev, ramdump_segs, + dump_data->nentries); + kfree(ramdump_segs); + + cnss_pci_set_mhi_state(plat_priv->bus_priv, CNSS_MHI_DEINIT); + cnss_pci_clear_dump_info(plat_priv->bus_priv); + + return ret; +} + +static int cnss_qca6174_ramdump(struct cnss_plat_data *plat_priv) +{ + int ret = 0; + struct cnss_ramdump_info *ramdump_info; + struct ramdump_segment segment; + + ramdump_info = &plat_priv->ramdump_info; + if (!ramdump_info->ramdump_size) + return -EINVAL; + + memset(&segment, 0, sizeof(segment)); + segment.v_address = ramdump_info->ramdump_va; + segment.size = ramdump_info->ramdump_size; + ret = do_ramdump(ramdump_info->ramdump_dev, &segment, 1); + + return ret; +} + +static int cnss_ramdump(int enable, const struct subsys_desc *subsys_desc) +{ + int ret = 0; + struct cnss_plat_data *plat_priv = dev_get_drvdata(subsys_desc->dev); + + if (!plat_priv) { + cnss_pr_err("plat_priv is NULL!\n"); + return -ENODEV; + } + + if (!enable) + return 0; + + switch (plat_priv->device_id) { + case QCA6174_DEVICE_ID: + ret = cnss_qca6174_ramdump(plat_priv); + break; + case QCA6290_EMULATION_DEVICE_ID: + case QCA6290_DEVICE_ID: + ret = cnss_qca6290_ramdump(plat_priv); + break; + default: + cnss_pr_err("Unknown device_id found: 0x%lx\n", + plat_priv->device_id); + ret = -ENODEV; + } + + return ret; +} + +void *cnss_get_virt_ramdump_mem(unsigned long *size) +{ + struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(NULL); + struct cnss_ramdump_info *ramdump_info; + + if (!plat_priv) + return NULL; + + ramdump_info = &plat_priv->ramdump_info; + *size = ramdump_info->ramdump_size; + + return ramdump_info->ramdump_va; +} +EXPORT_SYMBOL(cnss_get_virt_ramdump_mem); + +void cnss_device_crashed(void) +{ + struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(NULL); + struct cnss_subsys_info *subsys_info; + + if (!plat_priv) + return; + + subsys_info = &plat_priv->subsys_info; + if (subsys_info->subsys_device) { + set_bit(CNSS_DRIVER_RECOVERY, &plat_priv->driver_state); + subsys_set_crash_status(subsys_info->subsys_device, true); + subsystem_restart_dev(subsys_info->subsys_device); + } +} +EXPORT_SYMBOL(cnss_device_crashed); + +static void cnss_crash_shutdown(const struct subsys_desc *subsys_desc) +{ + struct cnss_plat_data *plat_priv = dev_get_drvdata(subsys_desc->dev); + + if (!plat_priv) { + cnss_pr_err("plat_priv is NULL!\n"); + return; + } + + switch (plat_priv->device_id) { + case QCA6174_DEVICE_ID: + cnss_qca6174_crash_shutdown(plat_priv); + break; + case QCA6290_EMULATION_DEVICE_ID: + case QCA6290_DEVICE_ID: + cnss_qca6290_crash_shutdown(plat_priv); + break; + default: + cnss_pr_err("Unknown device_id found: 0x%lx\n", + plat_priv->device_id); + } +} + +static const char *cnss_recovery_reason_to_str(enum cnss_recovery_reason reason) +{ + switch (reason) { + case CNSS_REASON_DEFAULT: + return "DEFAULT"; + case CNSS_REASON_LINK_DOWN: + return "LINK_DOWN"; + case CNSS_REASON_RDDM: + return "RDDM"; + case CNSS_REASON_TIMEOUT: + return "TIMEOUT"; + } + + return "UNKNOWN"; +}; + +static int cnss_do_recovery(struct cnss_plat_data *plat_priv, + enum cnss_recovery_reason reason) +{ + struct cnss_pci_data *pci_priv = plat_priv->bus_priv; + struct cnss_subsys_info *subsys_info = + &plat_priv->subsys_info; + int ret = 0; + + plat_priv->recovery_count++; + + if (plat_priv->device_id == QCA6174_DEVICE_ID) + goto self_recovery; + + if (plat_priv->driver_ops && + test_bit(CNSS_DRIVER_PROBED, &plat_priv->driver_state)) + plat_priv->driver_ops->update_status(pci_priv->pci_dev, + CNSS_RECOVERY); + + switch (reason) { + case CNSS_REASON_LINK_DOWN: + if (test_bit(LINK_DOWN_SELF_RECOVERY, &quirks)) + goto self_recovery; + break; + case CNSS_REASON_RDDM: + clear_bit(CNSS_DEV_ERR_NOTIFY, &plat_priv->driver_state); + ret = cnss_pci_set_mhi_state(pci_priv, CNSS_MHI_RDDM); + if (ret) { + cnss_pr_err("Failed to complete RDDM, err = %d\n", ret); + break; + } + cnss_pci_collect_dump_info(pci_priv); + break; + case CNSS_REASON_DEFAULT: + case CNSS_REASON_TIMEOUT: + break; + default: + cnss_pr_err("Unsupported recovery reason: %s(%d)\n", + cnss_recovery_reason_to_str(reason), reason); + break; + } + + if (!subsys_info->subsys_device) + return 0; + + subsys_set_crash_status(subsys_info->subsys_device, true); + subsystem_restart_dev(subsys_info->subsys_device); + + return 0; + +self_recovery: + cnss_shutdown(&subsys_info->subsys_desc, false); + cnss_powerup(&subsys_info->subsys_desc); + + return 0; +} + +static int cnss_driver_recovery_hdlr(struct cnss_plat_data *plat_priv, + void *data) +{ + struct cnss_recovery_data *recovery_data = data; + int ret = 0; + + cnss_pr_dbg("Driver recovery is triggered with reason: %s(%d)\n", + cnss_recovery_reason_to_str(recovery_data->reason), + recovery_data->reason); + + if (test_bit(CNSS_DRIVER_RECOVERY, &plat_priv->driver_state)) { + cnss_pr_err("Recovery is already in progress!\n"); + ret = -EINVAL; + goto out; + } + + if (test_bit(CNSS_DRIVER_UNLOADING, &plat_priv->driver_state)) { + cnss_pr_err("Driver unload is in progress, ignore recovery\n"); + ret = -EINVAL; + goto out; + } + + switch (plat_priv->device_id) { + case QCA6174_DEVICE_ID: + if (test_bit(CNSS_DRIVER_LOADING, &plat_priv->driver_state)) { + cnss_pr_err("Driver load is in progress, ignore recovery\n"); + ret = -EINVAL; + goto out; + } + break; + default: + if (!test_bit(CNSS_FW_READY, &plat_priv->driver_state)) { + set_bit(CNSS_FW_BOOT_RECOVERY, + &plat_priv->driver_state); + } else if (test_bit(CNSS_DRIVER_LOADING, + &plat_priv->driver_state)) { + cnss_pr_err("Driver probe is in progress, ignore recovery\n"); + ret = -EINVAL; + goto out; + } + break; + } + + set_bit(CNSS_DRIVER_RECOVERY, &plat_priv->driver_state); + ret = cnss_do_recovery(plat_priv, recovery_data->reason); + +out: + kfree(data); + return ret; +} + +int cnss_self_recovery(struct device *dev, + enum cnss_recovery_reason reason) +{ + cnss_schedule_recovery(dev, reason); + return 0; +} +EXPORT_SYMBOL(cnss_self_recovery); + +void cnss_schedule_recovery(struct device *dev, + enum cnss_recovery_reason reason) +{ + struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(dev); + struct cnss_recovery_data *data; + int gfp = GFP_KERNEL; + + if (in_interrupt() || irqs_disabled()) + gfp = GFP_ATOMIC; + + data = kzalloc(sizeof(*data), gfp); + if (!data) + return; + + data->reason = reason; + cnss_driver_event_post(plat_priv, + CNSS_DRIVER_EVENT_RECOVERY, + false, data); +} +EXPORT_SYMBOL(cnss_schedule_recovery); + +static int cnss_force_fw_assert_hdlr(struct cnss_plat_data *plat_priv) +{ + struct cnss_pci_data *pci_priv = plat_priv->bus_priv; + int ret; + + ret = cnss_pci_set_mhi_state(plat_priv->bus_priv, + CNSS_MHI_TRIGGER_RDDM); + if (ret) { + cnss_pr_err("Failed to trigger RDDM, err = %d\n", ret); + cnss_schedule_recovery(&pci_priv->pci_dev->dev, + CNSS_REASON_DEFAULT); + return 0; + } + + if (!test_bit(CNSS_DEV_ERR_NOTIFY, &plat_priv->driver_state)) { + mod_timer(&plat_priv->fw_boot_timer, + jiffies + msecs_to_jiffies(FW_ASSERT_TIMEOUT)); + } + + return 0; +} + +int cnss_force_fw_assert(struct device *dev) +{ + struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(dev); + + if (!plat_priv) { + cnss_pr_err("plat_priv is NULL\n"); + return -ENODEV; + } + + if (plat_priv->device_id == QCA6174_DEVICE_ID) { + cnss_pr_info("Forced FW assert is not supported\n"); + return -EINVAL; + } + + if (test_bit(CNSS_DRIVER_RECOVERY, &plat_priv->driver_state)) { + cnss_pr_info("Recovery is already in progress, ignore forced FW assert\n"); + return 0; + } + + cnss_driver_event_post(plat_priv, + CNSS_DRIVER_EVENT_FORCE_FW_ASSERT, + false, NULL); + + return 0; +} +EXPORT_SYMBOL(cnss_force_fw_assert); + +void fw_boot_timeout(unsigned long data) +{ + struct cnss_plat_data *plat_priv = (struct cnss_plat_data *)data; + struct cnss_pci_data *pci_priv = plat_priv->bus_priv; + + cnss_pr_err("Timeout waiting for FW ready indication!\n"); + + cnss_schedule_recovery(&pci_priv->pci_dev->dev, + CNSS_REASON_TIMEOUT); +} + +static int cnss_register_driver_hdlr(struct cnss_plat_data *plat_priv, + void *data) +{ + int ret = 0; + struct cnss_subsys_info *subsys_info = &plat_priv->subsys_info; + + set_bit(CNSS_DRIVER_LOADING, &plat_priv->driver_state); + plat_priv->driver_ops = data; + + ret = cnss_powerup(&subsys_info->subsys_desc); + if (ret) { + clear_bit(CNSS_DRIVER_LOADING, &plat_priv->driver_state); + plat_priv->driver_ops = NULL; + } + + return ret; +} + +static int cnss_unregister_driver_hdlr(struct cnss_plat_data *plat_priv) +{ + struct cnss_subsys_info *subsys_info = &plat_priv->subsys_info; + + set_bit(CNSS_DRIVER_UNLOADING, &plat_priv->driver_state); + cnss_shutdown(&subsys_info->subsys_desc, false); + plat_priv->driver_ops = NULL; + + return 0; +} + +static int cnss_cold_boot_cal_start_hdlr(struct cnss_plat_data *plat_priv) +{ + int ret = 0; + struct cnss_subsys_info *subsys_info = &plat_priv->subsys_info; + + set_bit(CNSS_COLD_BOOT_CAL, &plat_priv->driver_state); + ret = cnss_powerup(&subsys_info->subsys_desc); + if (ret) + clear_bit(CNSS_COLD_BOOT_CAL, &plat_priv->driver_state); + + return ret; +} + +static int cnss_cold_boot_cal_done_hdlr(struct cnss_plat_data *plat_priv) +{ + struct cnss_subsys_info *subsys_info = &plat_priv->subsys_info; + + cnss_wlfw_wlan_mode_send_sync(plat_priv, QMI_WLFW_OFF_V01); + cnss_shutdown(&subsys_info->subsys_desc, false); + clear_bit(CNSS_COLD_BOOT_CAL, &plat_priv->driver_state); + + return 0; +} + +static void cnss_driver_event_work(struct work_struct *work) +{ + struct cnss_plat_data *plat_priv = + container_of(work, struct cnss_plat_data, event_work); + struct cnss_driver_event *event; + unsigned long flags; + int ret = 0; + + if (!plat_priv) { + cnss_pr_err("plat_priv is NULL!\n"); + return; + } + + cnss_pm_stay_awake(plat_priv); + + spin_lock_irqsave(&plat_priv->event_lock, flags); + + while (!list_empty(&plat_priv->event_list)) { + event = list_first_entry(&plat_priv->event_list, + struct cnss_driver_event, list); + list_del(&event->list); + spin_unlock_irqrestore(&plat_priv->event_lock, flags); + + cnss_pr_dbg("Processing driver event: %s%s(%d), state: 0x%lx\n", + cnss_driver_event_to_str(event->type), + event->sync ? "-sync" : "", event->type, + plat_priv->driver_state); + + switch (event->type) { + case CNSS_DRIVER_EVENT_SERVER_ARRIVE: + ret = cnss_wlfw_server_arrive(plat_priv); + break; + case CNSS_DRIVER_EVENT_SERVER_EXIT: + ret = cnss_wlfw_server_exit(plat_priv); + break; + case CNSS_DRIVER_EVENT_REQUEST_MEM: + ret = cnss_pci_alloc_fw_mem(plat_priv->bus_priv); + if (ret) + break; + ret = cnss_wlfw_respond_mem_send_sync(plat_priv); + break; + case CNSS_DRIVER_EVENT_FW_MEM_READY: + ret = cnss_fw_mem_ready_hdlr(plat_priv); + break; + case CNSS_DRIVER_EVENT_FW_READY: + ret = cnss_fw_ready_hdlr(plat_priv); + break; + case CNSS_DRIVER_EVENT_COLD_BOOT_CAL_START: + ret = cnss_cold_boot_cal_start_hdlr(plat_priv); + break; + case CNSS_DRIVER_EVENT_COLD_BOOT_CAL_DONE: + ret = cnss_cold_boot_cal_done_hdlr(plat_priv); + break; + case CNSS_DRIVER_EVENT_REGISTER_DRIVER: + ret = cnss_register_driver_hdlr(plat_priv, + event->data); + break; + case CNSS_DRIVER_EVENT_UNREGISTER_DRIVER: + ret = cnss_unregister_driver_hdlr(plat_priv); + break; + case CNSS_DRIVER_EVENT_RECOVERY: + ret = cnss_driver_recovery_hdlr(plat_priv, + event->data); + break; + case CNSS_DRIVER_EVENT_FORCE_FW_ASSERT: + ret = cnss_force_fw_assert_hdlr(plat_priv); + break; + default: + cnss_pr_err("Invalid driver event type: %d", + event->type); + kfree(event); + spin_lock_irqsave(&plat_priv->event_lock, flags); + continue; + } + + spin_lock_irqsave(&plat_priv->event_lock, flags); + if (event->sync) { + event->ret = ret; + complete(&event->complete); + continue; + } + spin_unlock_irqrestore(&plat_priv->event_lock, flags); + + kfree(event); + + spin_lock_irqsave(&plat_priv->event_lock, flags); + } + spin_unlock_irqrestore(&plat_priv->event_lock, flags); + + cnss_pm_relax(plat_priv); +} + +int cnss_register_subsys(struct cnss_plat_data *plat_priv) +{ + int ret = 0; + struct cnss_subsys_info *subsys_info; + + subsys_info = &plat_priv->subsys_info; + + switch (plat_priv->device_id) { + case QCA6174_DEVICE_ID: + subsys_info->subsys_desc.name = "AR6320"; + break; + case QCA6290_EMULATION_DEVICE_ID: + case QCA6290_DEVICE_ID: + subsys_info->subsys_desc.name = "QCA6290"; + break; + default: + cnss_pr_err("Unknown device ID: 0x%lx\n", plat_priv->device_id); + ret = -ENODEV; + goto out; + } + + subsys_info->subsys_desc.owner = THIS_MODULE; + subsys_info->subsys_desc.powerup = cnss_powerup; + subsys_info->subsys_desc.shutdown = cnss_shutdown; + subsys_info->subsys_desc.ramdump = cnss_ramdump; + subsys_info->subsys_desc.crash_shutdown = cnss_crash_shutdown; + subsys_info->subsys_desc.dev = &plat_priv->plat_dev->dev; + + subsys_info->subsys_device = subsys_register(&subsys_info->subsys_desc); + if (IS_ERR(subsys_info->subsys_device)) { + ret = PTR_ERR(subsys_info->subsys_device); + cnss_pr_err("Failed to register subsys, err = %d\n", ret); + goto out; + } + + subsys_info->subsys_handle = + subsystem_get(subsys_info->subsys_desc.name); + if (!subsys_info->subsys_handle) { + cnss_pr_err("Failed to get subsys_handle!\n"); + ret = -EINVAL; + goto unregister_subsys; + } else if (IS_ERR(subsys_info->subsys_handle)) { + ret = PTR_ERR(subsys_info->subsys_handle); + cnss_pr_err("Failed to do subsystem_get, err = %d\n", ret); + goto unregister_subsys; + } + + return 0; + +unregister_subsys: + subsys_unregister(subsys_info->subsys_device); +out: + return ret; +} + +void cnss_unregister_subsys(struct cnss_plat_data *plat_priv) +{ + struct cnss_subsys_info *subsys_info; + + subsys_info = &plat_priv->subsys_info; + subsystem_put(subsys_info->subsys_handle); + subsys_unregister(subsys_info->subsys_device); +} + +static int cnss_init_dump_entry(struct cnss_plat_data *plat_priv) +{ + struct cnss_ramdump_info *ramdump_info; + struct msm_dump_entry dump_entry; + + ramdump_info = &plat_priv->ramdump_info; + ramdump_info->dump_data.addr = ramdump_info->ramdump_pa; + ramdump_info->dump_data.len = ramdump_info->ramdump_size; + ramdump_info->dump_data.version = CNSS_DUMP_FORMAT_VER; + ramdump_info->dump_data.magic = CNSS_DUMP_MAGIC_VER_V2; + strlcpy(ramdump_info->dump_data.name, CNSS_DUMP_NAME, + sizeof(ramdump_info->dump_data.name)); + dump_entry.id = MSM_DUMP_DATA_CNSS_WLAN; + dump_entry.addr = virt_to_phys(&ramdump_info->dump_data); + + return msm_dump_data_register(MSM_DUMP_TABLE_APPS, &dump_entry); +} + +static int cnss_qca6174_register_ramdump(struct cnss_plat_data *plat_priv) +{ + int ret = 0; + struct device *dev; + struct cnss_subsys_info *subsys_info; + struct cnss_ramdump_info *ramdump_info; + u32 ramdump_size = 0; + + dev = &plat_priv->plat_dev->dev; + subsys_info = &plat_priv->subsys_info; + ramdump_info = &plat_priv->ramdump_info; + + if (of_property_read_u32(dev->of_node, "qcom,wlan-ramdump-dynamic", + &ramdump_size) == 0) { + ramdump_info->ramdump_va = dma_alloc_coherent(dev, ramdump_size, + &ramdump_info->ramdump_pa, GFP_KERNEL); + + if (ramdump_info->ramdump_va) + ramdump_info->ramdump_size = ramdump_size; + } + + cnss_pr_dbg("ramdump va: %pK, pa: %pa\n", + ramdump_info->ramdump_va, &ramdump_info->ramdump_pa); + + if (ramdump_info->ramdump_size == 0) { + cnss_pr_info("Ramdump will not be collected"); + goto out; + } + + ret = cnss_init_dump_entry(plat_priv); + if (ret) { + cnss_pr_err("Failed to setup dump table, err = %d\n", ret); + goto free_ramdump; + } + + ramdump_info->ramdump_dev = create_ramdump_device( + subsys_info->subsys_desc.name, subsys_info->subsys_desc.dev); + if (!ramdump_info->ramdump_dev) { + cnss_pr_err("Failed to create ramdump device!"); + ret = -ENOMEM; + goto free_ramdump; + } + + return 0; +free_ramdump: + dma_free_coherent(dev, ramdump_info->ramdump_size, + ramdump_info->ramdump_va, ramdump_info->ramdump_pa); +out: + return ret; +} + +static void cnss_qca6174_unregister_ramdump(struct cnss_plat_data *plat_priv) +{ + struct device *dev; + struct cnss_ramdump_info *ramdump_info; + + dev = &plat_priv->plat_dev->dev; + ramdump_info = &plat_priv->ramdump_info; + + if (ramdump_info->ramdump_dev) + destroy_ramdump_device(ramdump_info->ramdump_dev); + + if (ramdump_info->ramdump_va) + dma_free_coherent(dev, ramdump_info->ramdump_size, + ramdump_info->ramdump_va, + ramdump_info->ramdump_pa); +} + +static int cnss_qca6290_register_ramdump(struct cnss_plat_data *plat_priv) +{ + int ret = 0; + struct cnss_subsys_info *subsys_info; + struct cnss_ramdump_info_v2 *info_v2; + struct cnss_dump_data *dump_data; + struct msm_dump_entry dump_entry; + struct device *dev = &plat_priv->plat_dev->dev; + u32 ramdump_size = 0; + + subsys_info = &plat_priv->subsys_info; + info_v2 = &plat_priv->ramdump_info_v2; + dump_data = &info_v2->dump_data; + + if (of_property_read_u32(dev->of_node, "qcom,wlan-ramdump-dynamic", + &ramdump_size) == 0) + info_v2->ramdump_size = ramdump_size; + + cnss_pr_dbg("Ramdump size 0x%lx\n", info_v2->ramdump_size); + + info_v2->dump_data_vaddr = kzalloc(CNSS_DUMP_DESC_SIZE, GFP_KERNEL); + if (!info_v2->dump_data_vaddr) + return -ENOMEM; + + dump_data->paddr = virt_to_phys(info_v2->dump_data_vaddr); + dump_data->version = CNSS_DUMP_FORMAT_VER_V2; + dump_data->magic = CNSS_DUMP_MAGIC_VER_V2; + dump_data->seg_version = CNSS_DUMP_SEG_VER; + strlcpy(dump_data->name, CNSS_DUMP_NAME, + sizeof(dump_data->name)); + dump_entry.id = MSM_DUMP_DATA_CNSS_WLAN; + dump_entry.addr = virt_to_phys(dump_data); + + ret = msm_dump_data_register(MSM_DUMP_TABLE_APPS, &dump_entry); + if (ret) { + cnss_pr_err("Failed to setup dump table, err = %d\n", ret); + goto free_ramdump; + } + + info_v2->ramdump_dev = + create_ramdump_device(subsys_info->subsys_desc.name, + subsys_info->subsys_desc.dev); + if (!info_v2->ramdump_dev) { + cnss_pr_err("Failed to create ramdump device!\n"); + ret = -ENOMEM; + goto free_ramdump; + } + + return 0; + +free_ramdump: + kfree(info_v2->dump_data_vaddr); + info_v2->dump_data_vaddr = NULL; + return ret; +} + +static void cnss_qca6290_unregister_ramdump(struct cnss_plat_data *plat_priv) +{ + struct cnss_ramdump_info_v2 *info_v2; + + info_v2 = &plat_priv->ramdump_info_v2; + + if (info_v2->ramdump_dev) + destroy_ramdump_device(info_v2->ramdump_dev); + + kfree(info_v2->dump_data_vaddr); + info_v2->dump_data_vaddr = NULL; + info_v2->dump_data_valid = false; +} + +int cnss_register_ramdump(struct cnss_plat_data *plat_priv) +{ + int ret = 0; + + switch (plat_priv->device_id) { + case QCA6174_DEVICE_ID: + ret = cnss_qca6174_register_ramdump(plat_priv); + break; + case QCA6290_EMULATION_DEVICE_ID: + case QCA6290_DEVICE_ID: + ret = cnss_qca6290_register_ramdump(plat_priv); + break; + default: + cnss_pr_err("Unknown device ID: 0x%lx\n", plat_priv->device_id); + ret = -ENODEV; + break; + } + return ret; +} + +void cnss_unregister_ramdump(struct cnss_plat_data *plat_priv) +{ + switch (plat_priv->device_id) { + case QCA6174_DEVICE_ID: + cnss_qca6174_unregister_ramdump(plat_priv); + break; + case QCA6290_EMULATION_DEVICE_ID: + case QCA6290_DEVICE_ID: + cnss_qca6290_unregister_ramdump(plat_priv); + break; + default: + cnss_pr_err("Unknown device ID: 0x%lx\n", plat_priv->device_id); + break; + } +} + +static int cnss_register_bus_scale(struct cnss_plat_data *plat_priv) +{ + int ret = 0; + struct cnss_bus_bw_info *bus_bw_info; + + bus_bw_info = &plat_priv->bus_bw_info; + + bus_bw_info->bus_scale_table = + msm_bus_cl_get_pdata(plat_priv->plat_dev); + if (bus_bw_info->bus_scale_table) { + bus_bw_info->bus_client = + msm_bus_scale_register_client( + bus_bw_info->bus_scale_table); + if (!bus_bw_info->bus_client) { + cnss_pr_err("Failed to register bus scale client!\n"); + ret = -EINVAL; + goto out; + } + } + + return 0; +out: + return ret; +} + +static void cnss_unregister_bus_scale(struct cnss_plat_data *plat_priv) +{ + struct cnss_bus_bw_info *bus_bw_info; + + bus_bw_info = &plat_priv->bus_bw_info; + + if (bus_bw_info->bus_client) + msm_bus_scale_unregister_client(bus_bw_info->bus_client); +} + +static ssize_t cnss_fs_ready_store(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + int fs_ready = 0; + struct cnss_plat_data *plat_priv = dev_get_drvdata(dev); + + if (sscanf(buf, "%du", &fs_ready) != 1) + return -EINVAL; + + cnss_pr_dbg("File system is ready, fs_ready is %d, count is %zu\n", + fs_ready, count); + + if (qmi_bypass) { + cnss_pr_dbg("QMI is bypassed.\n"); + return count; + } + + if (!plat_priv) { + cnss_pr_err("plat_priv is NULL!\n"); + return count; + } + + switch (plat_priv->device_id) { + case QCA6290_EMULATION_DEVICE_ID: + case QCA6290_DEVICE_ID: + break; + default: + cnss_pr_err("Not supported for device ID 0x%lx\n", + plat_priv->device_id); + return count; + } + + if (fs_ready == FILE_SYSTEM_READY) { + cnss_driver_event_post(plat_priv, + CNSS_DRIVER_EVENT_COLD_BOOT_CAL_START, + true, NULL); + } + + return count; +} + +static DEVICE_ATTR(fs_ready, 0220, NULL, cnss_fs_ready_store); + +static int cnss_create_sysfs(struct cnss_plat_data *plat_priv) +{ + int ret = 0; + + ret = device_create_file(&plat_priv->plat_dev->dev, &dev_attr_fs_ready); + if (ret) { + cnss_pr_err("Failed to create device file, err = %d\n", ret); + goto out; + } + + return 0; +out: + return ret; +} + +static void cnss_remove_sysfs(struct cnss_plat_data *plat_priv) +{ + device_remove_file(&plat_priv->plat_dev->dev, &dev_attr_fs_ready); +} + +static int cnss_event_work_init(struct cnss_plat_data *plat_priv) +{ + spin_lock_init(&plat_priv->event_lock); + plat_priv->event_wq = alloc_workqueue("cnss_driver_event", + WQ_UNBOUND, 1); + if (!plat_priv->event_wq) { + cnss_pr_err("Failed to create event workqueue!\n"); + return -EFAULT; + } + + INIT_WORK(&plat_priv->event_work, cnss_driver_event_work); + INIT_LIST_HEAD(&plat_priv->event_list); + + return 0; +} + +static void cnss_event_work_deinit(struct cnss_plat_data *plat_priv) +{ + destroy_workqueue(plat_priv->event_wq); +} + +static const struct platform_device_id cnss_platform_id_table[] = { + { .name = "qca6174", .driver_data = QCA6174_DEVICE_ID, }, + { .name = "qca6290", .driver_data = QCA6290_DEVICE_ID, }, +}; + +static const struct of_device_id cnss_of_match_table[] = { + { + .compatible = "qcom,cnss", + .data = (void *)&cnss_platform_id_table[0]}, + { + .compatible = "qcom,cnss-qca6290", + .data = (void *)&cnss_platform_id_table[1]}, + { }, +}; +MODULE_DEVICE_TABLE(of, cnss_of_match_table); + +static int cnss_probe(struct platform_device *plat_dev) +{ + int ret = 0; + struct cnss_plat_data *plat_priv; + const struct of_device_id *of_id; + const struct platform_device_id *device_id; + + if (cnss_get_plat_priv(plat_dev)) { + cnss_pr_err("Driver is already initialized!\n"); + ret = -EEXIST; + goto out; + } + + of_id = of_match_device(cnss_of_match_table, &plat_dev->dev); + if (!of_id || !of_id->data) { + cnss_pr_err("Failed to find of match device!\n"); + ret = -ENODEV; + goto out; + } + + device_id = of_id->data; + + plat_priv = devm_kzalloc(&plat_dev->dev, sizeof(*plat_priv), + GFP_KERNEL); + if (!plat_priv) { + ret = -ENOMEM; + goto out; + } + + plat_priv->plat_dev = plat_dev; + plat_priv->device_id = device_id->driver_data; + cnss_set_plat_priv(plat_dev, plat_priv); + platform_set_drvdata(plat_dev, plat_priv); + + ret = cnss_get_resources(plat_priv); + if (ret) + goto reset_ctx; + + ret = cnss_power_on_device(plat_priv); + if (ret) + goto free_res; + + ret = cnss_pci_init(plat_priv); + if (ret) + goto power_off; + + ret = cnss_register_esoc(plat_priv); + if (ret) + goto deinit_pci; + + ret = cnss_register_bus_scale(plat_priv); + if (ret) + goto unreg_esoc; + + ret = cnss_create_sysfs(plat_priv); + if (ret) + goto unreg_bus_scale; + + ret = cnss_event_work_init(plat_priv); + if (ret) + goto remove_sysfs; + + ret = cnss_qmi_init(plat_priv); + if (ret) + goto deinit_event_work; + + ret = cnss_debugfs_create(plat_priv); + if (ret) + goto deinit_qmi; + + setup_timer(&plat_priv->fw_boot_timer, + fw_boot_timeout, (unsigned long)plat_priv); + + register_pm_notifier(&cnss_pm_notifier); + + ret = device_init_wakeup(&plat_dev->dev, true); + if (ret) + cnss_pr_err("Failed to init platform device wakeup source, err = %d\n", + ret); + + cnss_pr_info("Platform driver probed successfully.\n"); + + return 0; + +deinit_qmi: + cnss_qmi_deinit(plat_priv); +deinit_event_work: + cnss_event_work_deinit(plat_priv); +remove_sysfs: + cnss_remove_sysfs(plat_priv); +unreg_bus_scale: + cnss_unregister_bus_scale(plat_priv); +unreg_esoc: + cnss_unregister_esoc(plat_priv); +deinit_pci: + cnss_pci_deinit(plat_priv); +power_off: + cnss_power_off_device(plat_priv); +free_res: + cnss_put_resources(plat_priv); +reset_ctx: + platform_set_drvdata(plat_dev, NULL); + cnss_set_plat_priv(plat_dev, NULL); +out: + return ret; +} + +static int cnss_remove(struct platform_device *plat_dev) +{ + struct cnss_plat_data *plat_priv = platform_get_drvdata(plat_dev); + + device_init_wakeup(&plat_dev->dev, false); + unregister_pm_notifier(&cnss_pm_notifier); + del_timer(&plat_priv->fw_boot_timer); + cnss_debugfs_destroy(plat_priv); + cnss_qmi_deinit(plat_priv); + cnss_event_work_deinit(plat_priv); + cnss_remove_sysfs(plat_priv); + cnss_unregister_bus_scale(plat_priv); + cnss_unregister_esoc(plat_priv); + cnss_pci_deinit(plat_priv); + cnss_put_resources(plat_priv); + platform_set_drvdata(plat_dev, NULL); + plat_env = NULL; + + return 0; +} + +static struct platform_driver cnss_platform_driver = { + .probe = cnss_probe, + .remove = cnss_remove, + .driver = { + .name = "cnss2", + .owner = THIS_MODULE, + .of_match_table = cnss_of_match_table, + }, +}; + +static int __init cnss_initialize(void) +{ + int ret = 0; + + cnss_debug_init(); + ret = platform_driver_register(&cnss_platform_driver); + if (ret) + cnss_debug_deinit(); + + return ret; +} + +static void __exit cnss_exit(void) +{ + platform_driver_unregister(&cnss_platform_driver); + cnss_debug_deinit(); +} + +module_init(cnss_initialize); +module_exit(cnss_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("CNSS2 Platform Driver"); diff --git a/drivers/net/wireless/cnss2/main.h b/drivers/net/wireless/cnss2/main.h new file mode 100644 index 000000000000..e3a8d0cccd52 --- /dev/null +++ b/drivers/net/wireless/cnss2/main.h @@ -0,0 +1,221 @@ +/* Copyright (c) 2016-2017, The Linux Foundation. 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 version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _CNSS_MAIN_H +#define _CNSS_MAIN_H + +#include <linux/esoc_client.h> +#include <linux/etherdevice.h> +#include <linux/msm-bus.h> +#include <linux/pm_qos.h> +#include <net/cnss2.h> +#include <soc/qcom/memory_dump.h> +#include <soc/qcom/subsystem_restart.h> + +#include "qmi.h" + +#define MAX_NO_OF_MAC_ADDR 4 + +enum cnss_dev_bus_type { + CNSS_BUS_NONE = -1, + CNSS_BUS_PCI, +}; + +struct cnss_vreg_info { + struct regulator *reg; + const char *name; + u32 min_uv; + u32 max_uv; + u32 load_ua; + u32 delay_us; +}; + +struct cnss_pinctrl_info { + struct pinctrl *pinctrl; + struct pinctrl_state *bootstrap_active; + struct pinctrl_state *wlan_en_active; + struct pinctrl_state *wlan_en_sleep; +}; + +struct cnss_subsys_info { + struct subsys_device *subsys_device; + struct subsys_desc subsys_desc; + void *subsys_handle; +}; + +struct cnss_ramdump_info { + struct ramdump_device *ramdump_dev; + unsigned long ramdump_size; + void *ramdump_va; + phys_addr_t ramdump_pa; + struct msm_dump_data dump_data; +}; + +struct cnss_dump_seg { + unsigned long address; + void *v_address; + unsigned long size; + u32 type; +}; + +struct cnss_dump_data { + u32 version; + u32 magic; + char name[32]; + phys_addr_t paddr; + int nentries; + u32 seg_version; +}; + +struct cnss_ramdump_info_v2 { + struct ramdump_device *ramdump_dev; + unsigned long ramdump_size; + void *dump_data_vaddr; + bool dump_data_valid; + struct cnss_dump_data dump_data; +}; + +struct cnss_esoc_info { + struct esoc_desc *esoc_desc; + bool notify_modem_status; + void *modem_notify_handler; + int modem_current_status; +}; + +struct cnss_bus_bw_info { + struct msm_bus_scale_pdata *bus_scale_table; + u32 bus_client; + int current_bw_vote; +}; + +struct cnss_wlan_mac_addr { + u8 mac_addr[MAX_NO_OF_MAC_ADDR][ETH_ALEN]; + u32 no_of_mac_addr_set; +}; + +struct cnss_wlan_mac_info { + struct cnss_wlan_mac_addr wlan_mac_addr; + bool is_wlan_mac_set; +}; + +struct cnss_fw_mem { + size_t size; + void *va; + phys_addr_t pa; + bool valid; +}; + +enum cnss_driver_event_type { + CNSS_DRIVER_EVENT_SERVER_ARRIVE, + CNSS_DRIVER_EVENT_SERVER_EXIT, + CNSS_DRIVER_EVENT_REQUEST_MEM, + CNSS_DRIVER_EVENT_FW_MEM_READY, + CNSS_DRIVER_EVENT_FW_READY, + CNSS_DRIVER_EVENT_COLD_BOOT_CAL_START, + CNSS_DRIVER_EVENT_COLD_BOOT_CAL_DONE, + CNSS_DRIVER_EVENT_REGISTER_DRIVER, + CNSS_DRIVER_EVENT_UNREGISTER_DRIVER, + CNSS_DRIVER_EVENT_RECOVERY, + CNSS_DRIVER_EVENT_FORCE_FW_ASSERT, + CNSS_DRIVER_EVENT_MAX, +}; + +enum cnss_driver_state { + CNSS_QMI_WLFW_CONNECTED, + CNSS_FW_MEM_READY, + CNSS_FW_READY, + CNSS_COLD_BOOT_CAL, + CNSS_DRIVER_LOADING, + CNSS_DRIVER_UNLOADING, + CNSS_DRIVER_PROBED, + CNSS_DRIVER_RECOVERY, + CNSS_FW_BOOT_RECOVERY, + CNSS_DEV_ERR_NOTIFY, +}; + +struct cnss_recovery_data { + enum cnss_recovery_reason reason; +}; + +enum cnss_pins { + CNSS_WLAN_EN, + CNSS_PCIE_TXP, + CNSS_PCIE_TXN, + CNSS_PCIE_RXP, + CNSS_PCIE_RXN, + CNSS_PCIE_REFCLKP, + CNSS_PCIE_REFCLKN, + CNSS_PCIE_RST, + CNSS_PCIE_WAKE, +}; + +struct cnss_pin_connect_result { + u32 fw_pwr_pin_result; + u32 fw_phy_io_pin_result; + u32 fw_rf_pin_result; + u32 host_pin_result; +}; + +struct cnss_plat_data { + struct platform_device *plat_dev; + void *bus_priv; + struct cnss_vreg_info *vreg_info; + struct cnss_pinctrl_info pinctrl_info; + struct cnss_subsys_info subsys_info; + struct cnss_ramdump_info ramdump_info; + struct cnss_ramdump_info_v2 ramdump_info_v2; + struct cnss_esoc_info esoc_info; + struct cnss_bus_bw_info bus_bw_info; + struct notifier_block modem_nb; + struct cnss_platform_cap cap; + struct pm_qos_request qos_request; + unsigned long device_id; + struct cnss_wlan_driver *driver_ops; + enum cnss_driver_status driver_status; + u32 recovery_count; + struct cnss_wlan_mac_info wlan_mac_info; + unsigned long driver_state; + struct list_head event_list; + spinlock_t event_lock; /* spinlock for driver work event handling */ + struct work_struct event_work; + struct workqueue_struct *event_wq; + struct qmi_handle *qmi_wlfw_clnt; + struct work_struct qmi_recv_msg_work; + struct notifier_block qmi_wlfw_clnt_nb; + struct wlfw_rf_chip_info_s_v01 chip_info; + struct wlfw_rf_board_info_s_v01 board_info; + struct wlfw_soc_info_s_v01 soc_info; + struct wlfw_fw_version_info_s_v01 fw_version_info; + struct cnss_fw_mem fw_mem; + struct cnss_fw_mem m3_mem; + struct cnss_pin_connect_result pin_result; + struct dentry *root_dentry; + atomic_t pm_count; + struct timer_list fw_boot_timer; +}; + +void *cnss_bus_dev_to_bus_priv(struct device *dev); +struct cnss_plat_data *cnss_bus_dev_to_plat_priv(struct device *dev); +int cnss_driver_event_post(struct cnss_plat_data *plat_priv, + enum cnss_driver_event_type type, + bool sync, void *data); +int cnss_get_vreg(struct cnss_plat_data *plat_priv); +int cnss_get_pinctrl(struct cnss_plat_data *plat_priv); +int cnss_power_on_device(struct cnss_plat_data *plat_priv); +void cnss_power_off_device(struct cnss_plat_data *plat_priv); +int cnss_register_subsys(struct cnss_plat_data *plat_priv); +void cnss_unregister_subsys(struct cnss_plat_data *plat_priv); +int cnss_register_ramdump(struct cnss_plat_data *plat_priv); +void cnss_unregister_ramdump(struct cnss_plat_data *plat_priv); +void cnss_set_pin_connect_status(struct cnss_plat_data *plat_priv); + +#endif /* _CNSS_MAIN_H */ diff --git a/drivers/net/wireless/cnss2/pci.c b/drivers/net/wireless/cnss2/pci.c new file mode 100644 index 000000000000..a17b72ce03ba --- /dev/null +++ b/drivers/net/wireless/cnss2/pci.c @@ -0,0 +1,1610 @@ +/* Copyright (c) 2016-2017, The Linux Foundation. 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 version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/firmware.h> +#include <linux/irq.h> +#include <linux/module.h> +#include <linux/msi.h> +#include <linux/of.h> +#include <linux/pm_runtime.h> + +#include "main.h" +#include "debug.h" +#include "pci.h" + +#define PCI_LINK_UP 1 +#define PCI_LINK_DOWN 0 + +#define SAVE_PCI_CONFIG_SPACE 1 +#define RESTORE_PCI_CONFIG_SPACE 0 + +#define PM_OPTIONS_DEFAULT 0 +#define PM_OPTIONS_LINK_DOWN \ + (MSM_PCIE_CONFIG_NO_CFG_RESTORE | MSM_PCIE_CONFIG_LINKDOWN) + +#define PCI_BAR_NUM 0 + +#ifdef CONFIG_ARM_LPAE +#define PCI_DMA_MASK 64 +#else +#define PCI_DMA_MASK 32 +#endif + +#define MHI_NODE_NAME "qcom,mhi" + +#define MAX_M3_FILE_NAME_LENGTH 13 +#define DEFAULT_M3_FILE_NAME "m3.bin" + +static DEFINE_SPINLOCK(pci_link_down_lock); + +static unsigned int pci_link_down_panic; +module_param(pci_link_down_panic, uint, 0600); +MODULE_PARM_DESC(pci_link_down_panic, + "Trigger kernel panic when PCI link down is detected"); + +static bool fbc_bypass; +#ifdef CONFIG_CNSS2_DEBUG +module_param(fbc_bypass, bool, 0600); +MODULE_PARM_DESC(fbc_bypass, + "Bypass firmware download when loading WLAN driver"); +#endif + +static int cnss_set_pci_config_space(struct cnss_pci_data *pci_priv, bool save) +{ + int ret = 0; + struct pci_dev *pci_dev = pci_priv->pci_dev; + struct cnss_plat_data *plat_priv = pci_priv->plat_priv; + bool link_down_or_recovery; + + if (!plat_priv) + return -ENODEV; + + link_down_or_recovery = pci_priv->pci_link_down_ind || + (test_bit(CNSS_DRIVER_RECOVERY, &plat_priv->driver_state)); + + if (save) { + if (link_down_or_recovery) { + pci_priv->saved_state = NULL; + } else { + pci_save_state(pci_dev); + pci_priv->saved_state = pci_store_saved_state(pci_dev); + } + } else { + if (link_down_or_recovery) { + ret = msm_pcie_recover_config(pci_dev); + if (ret) { + cnss_pr_err("Failed to recover PCI config space, err = %d\n", + ret); + return ret; + } + } else if (pci_priv->saved_state) { + pci_load_and_free_saved_state(pci_dev, + &pci_priv->saved_state); + pci_restore_state(pci_dev); + } + } + + return 0; +} + +static int cnss_set_pci_link(struct cnss_pci_data *pci_priv, bool link_up) +{ + int ret = 0; + struct pci_dev *pci_dev = pci_priv->pci_dev; + struct cnss_plat_data *plat_priv = pci_priv->plat_priv; + bool link_down_or_recovery; + + if (!plat_priv) + return -ENODEV; + + link_down_or_recovery = pci_priv->pci_link_down_ind || + (test_bit(CNSS_DRIVER_RECOVERY, &plat_priv->driver_state)); + + ret = msm_pcie_pm_control(link_up ? MSM_PCIE_RESUME : + MSM_PCIE_SUSPEND, + pci_dev->bus->number, + pci_dev, NULL, + link_down_or_recovery ? + PM_OPTIONS_LINK_DOWN : + PM_OPTIONS_DEFAULT); + if (ret) { + cnss_pr_err("Failed to %s PCI link with %s option, err = %d\n", + link_up ? "resume" : "suspend", + link_down_or_recovery ? "link down" : "default", + ret); + return ret; + } + + return 0; +} + +int cnss_suspend_pci_link(struct cnss_pci_data *pci_priv) +{ + int ret = 0; + + if (!pci_priv) + return -ENODEV; + + if (!pci_priv->pci_link_state) { + cnss_pr_info("PCI link is already suspended!\n"); + goto out; + } + + ret = cnss_set_pci_config_space(pci_priv, SAVE_PCI_CONFIG_SPACE); + if (ret) + goto out; + + pci_disable_device(pci_priv->pci_dev); + + ret = pci_set_power_state(pci_priv->pci_dev, PCI_D3hot); + if (ret) + cnss_pr_err("Failed to set D3Hot, err = %d\n", ret); + + ret = cnss_set_pci_link(pci_priv, PCI_LINK_DOWN); + if (ret) + goto out; + + pci_priv->pci_link_state = PCI_LINK_DOWN; + + return 0; +out: + return ret; +} + +int cnss_resume_pci_link(struct cnss_pci_data *pci_priv) +{ + int ret = 0; + + if (!pci_priv) + return -ENODEV; + + if (pci_priv->pci_link_state) { + cnss_pr_info("PCI link is already resumed!\n"); + goto out; + } + + ret = cnss_set_pci_link(pci_priv, PCI_LINK_UP); + if (ret) + goto out; + + pci_priv->pci_link_state = PCI_LINK_UP; + + ret = pci_enable_device(pci_priv->pci_dev); + if (ret) { + cnss_pr_err("Failed to enable PCI device, err = %d\n", ret); + goto out; + } + + ret = cnss_set_pci_config_space(pci_priv, RESTORE_PCI_CONFIG_SPACE); + if (ret) + goto out; + + pci_set_master(pci_priv->pci_dev); + + if (pci_priv->pci_link_down_ind) + pci_priv->pci_link_down_ind = false; + + return 0; +out: + return ret; +} + +int cnss_pci_link_down(struct device *dev) +{ + unsigned long flags; + struct pci_dev *pci_dev = to_pci_dev(dev); + struct cnss_pci_data *pci_priv = cnss_get_pci_priv(pci_dev); + + if (!pci_priv) { + cnss_pr_err("pci_priv is NULL!\n"); + return -EINVAL; + } + + if (pci_link_down_panic) + panic("cnss: PCI link is down!\n"); + + spin_lock_irqsave(&pci_link_down_lock, flags); + if (pci_priv->pci_link_down_ind) { + cnss_pr_dbg("PCI link down recovery is in progress, ignore!\n"); + spin_unlock_irqrestore(&pci_link_down_lock, flags); + return -EINVAL; + } + pci_priv->pci_link_down_ind = true; + spin_unlock_irqrestore(&pci_link_down_lock, flags); + + cnss_pr_err("PCI link down is detected by host driver, schedule recovery!\n"); + + cnss_pci_set_mhi_state(pci_priv, CNSS_MHI_NOTIFY_LINK_ERROR); + cnss_schedule_recovery(dev, CNSS_REASON_LINK_DOWN); + + return 0; +} +EXPORT_SYMBOL(cnss_pci_link_down); + +static int cnss_pci_init_smmu(struct cnss_pci_data *pci_priv) +{ + int ret = 0; + struct device *dev; + struct dma_iommu_mapping *mapping; + int atomic_ctx = 1; + int s1_bypass = 1; + + dev = &pci_priv->pci_dev->dev; + + mapping = arm_iommu_create_mapping(&platform_bus_type, + pci_priv->smmu_iova_start, + pci_priv->smmu_iova_len); + if (IS_ERR(mapping)) { + ret = PTR_ERR(mapping); + cnss_pr_err("Failed to create SMMU mapping, err = %d\n", ret); + goto out; + } + + ret = iommu_domain_set_attr(mapping->domain, + DOMAIN_ATTR_ATOMIC, + &atomic_ctx); + if (ret) { + pr_err("Failed to set SMMU atomic_ctx attribute, err = %d\n", + ret); + goto release_mapping; + } + + ret = iommu_domain_set_attr(mapping->domain, + DOMAIN_ATTR_S1_BYPASS, + &s1_bypass); + if (ret) { + pr_err("Failed to set SMMU s1_bypass attribute, err = %d\n", + ret); + goto release_mapping; + } + + ret = arm_iommu_attach_device(dev, mapping); + if (ret) { + pr_err("Failed to attach SMMU device, err = %d\n", ret); + goto release_mapping; + } + + pci_priv->smmu_mapping = mapping; + + return ret; +release_mapping: + arm_iommu_release_mapping(mapping); +out: + return ret; +} + +static void cnss_pci_deinit_smmu(struct cnss_pci_data *pci_priv) +{ + arm_iommu_detach_device(&pci_priv->pci_dev->dev); + arm_iommu_release_mapping(pci_priv->smmu_mapping); + + pci_priv->smmu_mapping = NULL; +} + +static void cnss_pci_event_cb(struct msm_pcie_notify *notify) +{ + unsigned long flags; + struct pci_dev *pci_dev; + struct cnss_pci_data *pci_priv; + + if (!notify) + return; + + pci_dev = notify->user; + if (!pci_dev) + return; + + pci_priv = cnss_get_pci_priv(pci_dev); + if (!pci_priv) + return; + + switch (notify->event) { + case MSM_PCIE_EVENT_LINKDOWN: + if (pci_link_down_panic) + panic("cnss: PCI link is down!\n"); + + spin_lock_irqsave(&pci_link_down_lock, flags); + if (pci_priv->pci_link_down_ind) { + cnss_pr_dbg("PCI link down recovery is in progress, ignore!\n"); + spin_unlock_irqrestore(&pci_link_down_lock, flags); + return; + } + pci_priv->pci_link_down_ind = true; + spin_unlock_irqrestore(&pci_link_down_lock, flags); + + cnss_pr_err("PCI link down, schedule recovery!\n"); + cnss_pci_set_mhi_state(pci_priv, CNSS_MHI_NOTIFY_LINK_ERROR); + if (pci_dev->device == QCA6174_DEVICE_ID) + disable_irq(pci_dev->irq); + cnss_schedule_recovery(&pci_dev->dev, CNSS_REASON_LINK_DOWN); + break; + case MSM_PCIE_EVENT_WAKEUP: + if (cnss_pci_get_monitor_wake_intr(pci_priv) && + cnss_pci_get_auto_suspended(pci_priv)) { + cnss_pci_set_monitor_wake_intr(pci_priv, false); + pm_request_resume(&pci_dev->dev); + } + break; + default: + cnss_pr_err("Received invalid PCI event: %d\n", notify->event); + } +} + +static int cnss_reg_pci_event(struct cnss_pci_data *pci_priv) +{ + int ret = 0; + struct msm_pcie_register_event *pci_event; + + pci_event = &pci_priv->msm_pci_event; + pci_event->events = MSM_PCIE_EVENT_LINKDOWN | + MSM_PCIE_EVENT_WAKEUP; + pci_event->user = pci_priv->pci_dev; + pci_event->mode = MSM_PCIE_TRIGGER_CALLBACK; + pci_event->callback = cnss_pci_event_cb; + pci_event->options = MSM_PCIE_CONFIG_NO_RECOVERY; + + ret = msm_pcie_register_event(pci_event); + if (ret) + cnss_pr_err("Failed to register MSM PCI event, err = %d\n", + ret); + + return ret; +} + +static void cnss_dereg_pci_event(struct cnss_pci_data *pci_priv) +{ + msm_pcie_deregister_event(&pci_priv->msm_pci_event); +} + +static int cnss_pci_suspend(struct device *dev) +{ + int ret = 0; + struct pci_dev *pci_dev = to_pci_dev(dev); + struct cnss_pci_data *pci_priv = cnss_get_pci_priv(pci_dev); + struct cnss_plat_data *plat_priv; + struct cnss_wlan_driver *driver_ops; + + pm_message_t state = { .event = PM_EVENT_SUSPEND }; + + if (!pci_priv) + goto out; + + plat_priv = pci_priv->plat_priv; + if (!plat_priv) + goto out; + + driver_ops = plat_priv->driver_ops; + if (driver_ops && driver_ops->suspend) { + ret = driver_ops->suspend(pci_dev, state); + if (pci_priv->pci_link_state) { + if (cnss_pci_set_mhi_state(pci_priv, + CNSS_MHI_SUSPEND)) { + driver_ops->resume(pci_dev); + ret = -EAGAIN; + goto out; + } + + cnss_set_pci_config_space(pci_priv, + SAVE_PCI_CONFIG_SPACE); + pci_disable_device(pci_dev); + + ret = pci_set_power_state(pci_dev, PCI_D3hot); + if (ret) + cnss_pr_err("Failed to set D3Hot, err = %d\n", + ret); + } + } + + cnss_pci_set_monitor_wake_intr(pci_priv, false); + +out: + return ret; +} + +static int cnss_pci_resume(struct device *dev) +{ + int ret = 0; + struct pci_dev *pci_dev = to_pci_dev(dev); + struct cnss_pci_data *pci_priv = cnss_get_pci_priv(pci_dev); + struct cnss_plat_data *plat_priv; + struct cnss_wlan_driver *driver_ops; + + if (!pci_priv) + goto out; + + plat_priv = pci_priv->plat_priv; + if (!plat_priv) + goto out; + + driver_ops = plat_priv->driver_ops; + if (driver_ops && driver_ops->resume && !pci_priv->pci_link_down_ind) { + ret = pci_enable_device(pci_dev); + if (ret) + cnss_pr_err("Failed to enable PCI device, err = %d\n", + ret); + + if (pci_priv->saved_state) + cnss_set_pci_config_space(pci_priv, + RESTORE_PCI_CONFIG_SPACE); + + pci_set_master(pci_dev); + cnss_pci_set_mhi_state(pci_priv, CNSS_MHI_RESUME); + + ret = driver_ops->resume(pci_dev); + } + +out: + return ret; +} + +static int cnss_pci_suspend_noirq(struct device *dev) +{ + int ret = 0; + struct pci_dev *pci_dev = to_pci_dev(dev); + struct cnss_pci_data *pci_priv = cnss_get_pci_priv(pci_dev); + struct cnss_plat_data *plat_priv; + struct cnss_wlan_driver *driver_ops; + + if (!pci_priv) + goto out; + + plat_priv = pci_priv->plat_priv; + if (!plat_priv) + goto out; + + driver_ops = plat_priv->driver_ops; + if (driver_ops && driver_ops->suspend_noirq) + ret = driver_ops->suspend_noirq(pci_dev); + +out: + return ret; +} + +static int cnss_pci_resume_noirq(struct device *dev) +{ + int ret = 0; + struct pci_dev *pci_dev = to_pci_dev(dev); + struct cnss_pci_data *pci_priv = cnss_get_pci_priv(pci_dev); + struct cnss_plat_data *plat_priv; + struct cnss_wlan_driver *driver_ops; + + if (!pci_priv) + goto out; + + plat_priv = pci_priv->plat_priv; + if (!plat_priv) + goto out; + + driver_ops = plat_priv->driver_ops; + if (driver_ops && driver_ops->resume_noirq && + !pci_priv->pci_link_down_ind) + ret = driver_ops->resume_noirq(pci_dev); + +out: + return ret; +} + +static int cnss_pci_runtime_suspend(struct device *dev) +{ + int ret = 0; + struct pci_dev *pci_dev = to_pci_dev(dev); + struct cnss_pci_data *pci_priv = cnss_get_pci_priv(pci_dev); + struct cnss_plat_data *plat_priv; + struct cnss_wlan_driver *driver_ops; + + if (!pci_priv) + return -EAGAIN; + + plat_priv = pci_priv->plat_priv; + if (!plat_priv) + return -EAGAIN; + + if (pci_priv->pci_link_down_ind) { + cnss_pr_dbg("PCI link down recovery is in progress!\n"); + return -EAGAIN; + } + + cnss_pr_dbg("Runtime suspend start\n"); + + driver_ops = plat_priv->driver_ops; + if (driver_ops && driver_ops->runtime_ops && + driver_ops->runtime_ops->runtime_suspend) + ret = driver_ops->runtime_ops->runtime_suspend(pci_dev); + + cnss_pr_info("Runtime suspend status: %d\n", ret); + + return ret; +} + +static int cnss_pci_runtime_resume(struct device *dev) +{ + int ret = 0; + struct pci_dev *pci_dev = to_pci_dev(dev); + struct cnss_pci_data *pci_priv = cnss_get_pci_priv(pci_dev); + struct cnss_plat_data *plat_priv; + struct cnss_wlan_driver *driver_ops; + + if (!pci_priv) + return -EAGAIN; + + plat_priv = pci_priv->plat_priv; + if (!plat_priv) + return -EAGAIN; + + if (pci_priv->pci_link_down_ind) { + cnss_pr_dbg("PCI link down recovery is in progress!\n"); + return -EAGAIN; + } + + cnss_pr_dbg("Runtime resume start\n"); + + driver_ops = plat_priv->driver_ops; + if (driver_ops && driver_ops->runtime_ops && + driver_ops->runtime_ops->runtime_resume) + ret = driver_ops->runtime_ops->runtime_resume(pci_dev); + + cnss_pr_info("Runtime resume status: %d\n", ret); + + return ret; +} + +static int cnss_pci_runtime_idle(struct device *dev) +{ + cnss_pr_dbg("Runtime idle\n"); + + pm_request_autosuspend(dev); + + return -EBUSY; +} + +int cnss_wlan_pm_control(bool vote) +{ + struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(NULL); + struct cnss_pci_data *pci_priv; + struct pci_dev *pci_dev; + + if (!plat_priv) + return -ENODEV; + + pci_priv = plat_priv->bus_priv; + if (!pci_priv) + return -ENODEV; + + pci_dev = pci_priv->pci_dev; + + return msm_pcie_pm_control(vote ? MSM_PCIE_DISABLE_PC : + MSM_PCIE_ENABLE_PC, + pci_dev->bus->number, pci_dev, + NULL, PM_OPTIONS_DEFAULT); +} +EXPORT_SYMBOL(cnss_wlan_pm_control); + +int cnss_auto_suspend(void) +{ + int ret = 0; + struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(NULL); + struct pci_dev *pci_dev; + struct cnss_pci_data *pci_priv; + struct cnss_bus_bw_info *bus_bw_info; + + if (!plat_priv) + return -ENODEV; + + pci_priv = plat_priv->bus_priv; + if (!pci_priv) + return -ENODEV; + + pci_dev = pci_priv->pci_dev; + + if (pci_priv->pci_link_state) { + if (cnss_pci_set_mhi_state(pci_priv, CNSS_MHI_SUSPEND)) { + ret = -EAGAIN; + goto out; + } + + cnss_set_pci_config_space(pci_priv, SAVE_PCI_CONFIG_SPACE); + pci_disable_device(pci_dev); + + ret = pci_set_power_state(pci_dev, PCI_D3hot); + if (ret) + cnss_pr_err("Failed to set D3Hot, err = %d\n", ret); + if (cnss_set_pci_link(pci_priv, PCI_LINK_DOWN)) { + cnss_pr_err("Failed to shutdown PCI link!\n"); + ret = -EAGAIN; + goto resume_mhi; + } + } + + pci_priv->pci_link_state = PCI_LINK_DOWN; + cnss_pci_set_auto_suspended(pci_priv, 1); + cnss_pci_set_monitor_wake_intr(pci_priv, true); + + bus_bw_info = &plat_priv->bus_bw_info; + msm_bus_scale_client_update_request(bus_bw_info->bus_client, + CNSS_BUS_WIDTH_NONE); + + return 0; + +resume_mhi: + if (pci_enable_device(pci_dev)) + cnss_pr_err("Failed to enable PCI device!\n"); + cnss_pci_set_mhi_state(pci_priv, CNSS_MHI_RESUME); +out: + return ret; +} +EXPORT_SYMBOL(cnss_auto_suspend); + +int cnss_auto_resume(void) +{ + int ret = 0; + struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(NULL); + struct pci_dev *pci_dev; + struct cnss_pci_data *pci_priv; + struct cnss_bus_bw_info *bus_bw_info; + + if (!plat_priv) + return -ENODEV; + + pci_priv = plat_priv->bus_priv; + if (!pci_priv) + return -ENODEV; + + pci_dev = pci_priv->pci_dev; + if (!pci_priv->pci_link_state) { + if (cnss_set_pci_link(pci_priv, PCI_LINK_UP)) { + cnss_pr_err("Failed to resume PCI link!\n"); + ret = -EAGAIN; + goto out; + } + pci_priv->pci_link_state = PCI_LINK_UP; + ret = pci_enable_device(pci_dev); + if (ret) + cnss_pr_err("Failed to enable PCI device, err = %d\n", + ret); + } + + cnss_set_pci_config_space(pci_priv, RESTORE_PCI_CONFIG_SPACE); + pci_set_master(pci_dev); + cnss_pci_set_mhi_state(pci_priv, CNSS_MHI_RESUME); + cnss_pci_set_auto_suspended(pci_priv, 0); + + bus_bw_info = &plat_priv->bus_bw_info; + msm_bus_scale_client_update_request(bus_bw_info->bus_client, + bus_bw_info->current_bw_vote); +out: + return ret; +} +EXPORT_SYMBOL(cnss_auto_resume); + +int cnss_pci_alloc_fw_mem(struct cnss_pci_data *pci_priv) +{ + struct cnss_plat_data *plat_priv = pci_priv->plat_priv; + struct cnss_fw_mem *fw_mem = &plat_priv->fw_mem; + + if (!fw_mem->va && fw_mem->size) { + fw_mem->va = dma_alloc_coherent(&pci_priv->pci_dev->dev, + fw_mem->size, &fw_mem->pa, + GFP_KERNEL); + if (!fw_mem->va) { + cnss_pr_err("Failed to allocate memory for FW, size: 0x%zx\n", + fw_mem->size); + fw_mem->size = 0; + + return -ENOMEM; + } + } + + return 0; +} + +static void cnss_pci_free_fw_mem(struct cnss_pci_data *pci_priv) +{ + struct cnss_plat_data *plat_priv = pci_priv->plat_priv; + struct cnss_fw_mem *fw_mem = &plat_priv->fw_mem; + + if (fw_mem->va && fw_mem->size) { + cnss_pr_dbg("Freeing memory for FW, va: 0x%pK, pa: %pa, size: 0x%zx\n", + fw_mem->va, &fw_mem->pa, fw_mem->size); + dma_free_coherent(&pci_priv->pci_dev->dev, fw_mem->size, + fw_mem->va, fw_mem->pa); + fw_mem->va = NULL; + fw_mem->pa = 0; + fw_mem->size = 0; + } +} + +int cnss_pci_load_m3(struct cnss_pci_data *pci_priv) +{ + struct cnss_plat_data *plat_priv = pci_priv->plat_priv; + struct cnss_fw_mem *m3_mem = &plat_priv->m3_mem; + char filename[MAX_M3_FILE_NAME_LENGTH]; + const struct firmware *fw_entry; + int ret = 0; + + if (!m3_mem->va && !m3_mem->size) { + snprintf(filename, sizeof(filename), DEFAULT_M3_FILE_NAME); + + ret = request_firmware(&fw_entry, filename, + &pci_priv->pci_dev->dev); + if (ret) { + cnss_pr_err("Failed to load M3 image: %s\n", filename); + return ret; + } + + m3_mem->va = dma_alloc_coherent(&pci_priv->pci_dev->dev, + fw_entry->size, &m3_mem->pa, + GFP_KERNEL); + if (!m3_mem->va) { + cnss_pr_err("Failed to allocate memory for M3, size: 0x%zx\n", + fw_entry->size); + release_firmware(fw_entry); + return -ENOMEM; + } + + memcpy(m3_mem->va, fw_entry->data, fw_entry->size); + m3_mem->size = fw_entry->size; + release_firmware(fw_entry); + } + + return 0; +} + +static void cnss_pci_free_m3_mem(struct cnss_pci_data *pci_priv) +{ + struct cnss_plat_data *plat_priv = pci_priv->plat_priv; + struct cnss_fw_mem *m3_mem = &plat_priv->m3_mem; + + if (m3_mem->va && m3_mem->size) { + cnss_pr_dbg("Freeing memory for M3, va: 0x%pK, pa: %pa, size: 0x%zx\n", + m3_mem->va, &m3_mem->pa, m3_mem->size); + dma_free_coherent(&pci_priv->pci_dev->dev, m3_mem->size, + m3_mem->va, m3_mem->pa); + } + + m3_mem->va = NULL; + m3_mem->pa = 0; + m3_mem->size = 0; +} + +int cnss_pci_get_bar_info(struct cnss_pci_data *pci_priv, void __iomem **va, + phys_addr_t *pa) +{ + if (!pci_priv) + return -ENODEV; + + *va = pci_priv->bar; + *pa = pci_resource_start(pci_priv->pci_dev, PCI_BAR_NUM); + + return 0; +} + +#ifdef CONFIG_CNSS_QCA6290 +#define PCI_MAX_BAR_SIZE 0xD00000 + +static void __iomem *cnss_pci_iomap(struct pci_dev *dev, int bar, + unsigned long maxlen) +{ + resource_size_t start = pci_resource_start(dev, bar); + resource_size_t len = PCI_MAX_BAR_SIZE; + unsigned long flags = pci_resource_flags(dev, bar); + + if (!len || !start) + return NULL; + + if ((flags & IORESOURCE_IO) || (flags & IORESOURCE_MEM)) { + if (flags & IORESOURCE_CACHEABLE && !(flags & IORESOURCE_IO)) + return ioremap(start, len); + else + return ioremap_nocache(start, len); + } + + return NULL; +} +#else +static void __iomem *cnss_pci_iomap(struct pci_dev *dev, int bar, + unsigned long maxlen) +{ + return pci_iomap(dev, bar, maxlen); +} +#endif + +static struct cnss_msi_config msi_config = { + .total_vectors = 32, + .total_users = 4, + .users = (struct cnss_msi_user[]) { + { .name = "MHI", .num_vectors = 2, .base_vector = 0 }, + { .name = "CE", .num_vectors = 11, .base_vector = 2 }, + { .name = "WAKE", .num_vectors = 1, .base_vector = 13 }, + { .name = "DP", .num_vectors = 18, .base_vector = 14 }, + }, +}; + +static int cnss_pci_get_msi_assignment(struct cnss_pci_data *pci_priv) +{ + pci_priv->msi_config = &msi_config; + + return 0; +} + +static int cnss_pci_enable_msi(struct cnss_pci_data *pci_priv) +{ + int ret = 0; + struct pci_dev *pci_dev = pci_priv->pci_dev; + int num_vectors; + struct cnss_msi_config *msi_config; + struct msi_desc *msi_desc; + + ret = cnss_pci_get_msi_assignment(pci_priv); + if (ret) { + cnss_pr_err("Failed to get MSI assignment, err = %d\n", ret); + goto out; + } + + msi_config = pci_priv->msi_config; + if (!msi_config) { + cnss_pr_err("msi_config is NULL!\n"); + ret = -EINVAL; + goto out; + } + + num_vectors = pci_enable_msi_range(pci_dev, + msi_config->total_vectors, + msi_config->total_vectors); + if (num_vectors != msi_config->total_vectors) { + cnss_pr_err("Failed to get enough MSI vectors (%d), available vectors = %d", + msi_config->total_vectors, num_vectors); + ret = -EINVAL; + goto reset_msi_config; + } + + msi_desc = irq_get_msi_desc(pci_dev->irq); + if (!msi_desc) { + cnss_pr_err("msi_desc is NULL!\n"); + ret = -EINVAL; + goto disable_msi; + } + + pci_priv->msi_ep_base_data = msi_desc->msg.data; + if (!pci_priv->msi_ep_base_data) { + cnss_pr_err("Got 0 MSI base data!\n"); + CNSS_ASSERT(0); + } + + cnss_pr_dbg("MSI base data is %d\n", pci_priv->msi_ep_base_data); + + return 0; + +disable_msi: + pci_disable_msi(pci_priv->pci_dev); +reset_msi_config: + pci_priv->msi_config = NULL; +out: + return ret; +} + +static void cnss_pci_disable_msi(struct cnss_pci_data *pci_priv) +{ + pci_disable_msi(pci_priv->pci_dev); +} + +int cnss_get_user_msi_assignment(struct device *dev, char *user_name, + int *num_vectors, u32 *user_base_data, + u32 *base_vector) +{ + struct cnss_pci_data *pci_priv = dev_get_drvdata(dev); + struct cnss_msi_config *msi_config; + int idx; + + if (!pci_priv) + return -ENODEV; + + msi_config = pci_priv->msi_config; + if (!msi_config) { + cnss_pr_err("MSI is not supported.\n"); + return -EINVAL; + } + + for (idx = 0; idx < msi_config->total_users; idx++) { + if (strcmp(user_name, msi_config->users[idx].name) == 0) { + *num_vectors = msi_config->users[idx].num_vectors; + *user_base_data = msi_config->users[idx].base_vector + + pci_priv->msi_ep_base_data; + *base_vector = msi_config->users[idx].base_vector; + + cnss_pr_dbg("Assign MSI to user: %s, num_vectors: %d, user_base_data: %u, base_vector: %u\n", + user_name, *num_vectors, *user_base_data, + *base_vector); + + return 0; + } + } + + cnss_pr_err("Failed to find MSI assignment for %s!\n", user_name); + + return -EINVAL; +} +EXPORT_SYMBOL(cnss_get_user_msi_assignment); + +int cnss_get_msi_irq(struct device *dev, unsigned int vector) +{ + struct pci_dev *pci_dev = to_pci_dev(dev); + + return pci_dev->irq + vector; +} +EXPORT_SYMBOL(cnss_get_msi_irq); + +void cnss_get_msi_address(struct device *dev, u32 *msi_addr_low, + u32 *msi_addr_high) +{ + struct pci_dev *pci_dev = to_pci_dev(dev); + + pci_read_config_dword(pci_dev, pci_dev->msi_cap + PCI_MSI_ADDRESS_LO, + msi_addr_low); + + pci_read_config_dword(pci_dev, pci_dev->msi_cap + PCI_MSI_ADDRESS_HI, + msi_addr_high); +} +EXPORT_SYMBOL(cnss_get_msi_address); + +static int cnss_pci_enable_bus(struct cnss_pci_data *pci_priv) +{ + int ret = 0; + struct pci_dev *pci_dev = pci_priv->pci_dev; + u16 device_id; + + pci_read_config_word(pci_dev, PCI_DEVICE_ID, &device_id); + if (device_id != pci_priv->pci_device_id->device) { + cnss_pr_err("PCI device ID mismatch, config ID: 0x%x, probe ID: 0x%x\n", + device_id, pci_priv->pci_device_id->device); + ret = -EIO; + goto out; + } + + ret = pci_assign_resource(pci_dev, PCI_BAR_NUM); + if (ret) { + pr_err("Failed to assign PCI resource, err = %d\n", ret); + goto out; + } + + ret = pci_enable_device(pci_dev); + if (ret) { + cnss_pr_err("Failed to enable PCI device, err = %d\n", ret); + goto out; + } + + ret = pci_request_region(pci_dev, PCI_BAR_NUM, "cnss"); + if (ret) { + cnss_pr_err("Failed to request PCI region, err = %d\n", ret); + goto disable_device; + } + + ret = pci_set_dma_mask(pci_dev, DMA_BIT_MASK(PCI_DMA_MASK)); + if (ret) { + cnss_pr_err("Failed to set PCI DMA mask (%d), err = %d\n", + ret, PCI_DMA_MASK); + goto release_region; + } + + ret = pci_set_consistent_dma_mask(pci_dev, DMA_BIT_MASK(PCI_DMA_MASK)); + if (ret) { + cnss_pr_err("Failed to set PCI consistent DMA mask (%d), err = %d\n", + ret, PCI_DMA_MASK); + goto release_region; + } + + pci_set_master(pci_dev); + + pci_priv->bar = cnss_pci_iomap(pci_dev, PCI_BAR_NUM, 0); + if (!pci_priv->bar) { + cnss_pr_err("Failed to do PCI IO map!\n"); + ret = -EIO; + goto clear_master; + } + return 0; + +clear_master: + pci_clear_master(pci_dev); +release_region: + pci_release_region(pci_dev, PCI_BAR_NUM); +disable_device: + pci_disable_device(pci_dev); +out: + return ret; +} + +static void cnss_pci_disable_bus(struct cnss_pci_data *pci_priv) +{ + struct pci_dev *pci_dev = pci_priv->pci_dev; + + if (pci_priv->bar) { + pci_iounmap(pci_dev, pci_priv->bar); + pci_priv->bar = NULL; + } + + pci_clear_master(pci_dev); + pci_release_region(pci_dev, PCI_BAR_NUM); + pci_disable_device(pci_dev); +} + +static int cnss_mhi_pm_runtime_get(struct pci_dev *pci_dev) +{ + return pm_runtime_get(&pci_dev->dev); +} + +static void cnss_mhi_pm_runtime_put_noidle(struct pci_dev *pci_dev) +{ + pm_runtime_put_noidle(&pci_dev->dev); +} + +static char *cnss_mhi_state_to_str(enum cnss_mhi_state mhi_state) +{ + switch (mhi_state) { + case CNSS_MHI_INIT: + return "INIT"; + case CNSS_MHI_DEINIT: + return "DEINIT"; + case CNSS_MHI_POWER_ON: + return "POWER_ON"; + case CNSS_MHI_POWER_OFF: + return "POWER_OFF"; + case CNSS_MHI_SUSPEND: + return "SUSPEND"; + case CNSS_MHI_RESUME: + return "RESUME"; + case CNSS_MHI_TRIGGER_RDDM: + return "TRIGGER_RDDM"; + case CNSS_MHI_RDDM: + return "RDDM"; + case CNSS_MHI_RDDM_KERNEL_PANIC: + return "RDDM_KERNEL_PANIC"; + case CNSS_MHI_NOTIFY_LINK_ERROR: + return "NOTIFY_LINK_ERROR"; + default: + return "UNKNOWN"; + } +}; + +static void *cnss_pci_collect_dump_seg(struct cnss_pci_data *pci_priv, + enum mhi_rddm_segment type, + void *start_addr) +{ + int count; + struct scatterlist *sg_list, *s; + unsigned int i; + struct cnss_plat_data *plat_priv = pci_priv->plat_priv; + struct cnss_dump_data *dump_data = + &plat_priv->ramdump_info_v2.dump_data; + struct cnss_dump_seg *dump_seg = start_addr; + + count = mhi_xfer_rddm(&pci_priv->mhi_dev, type, &sg_list); + if (count <= 0 || !sg_list) { + cnss_pr_err("Invalid dump_seg for type %u, count %u, sg_list %pK\n", + type, count, sg_list); + return start_addr; + } + + cnss_pr_dbg("Collect dump seg: type %u, nentries %d\n", type, count); + + for_each_sg(sg_list, s, count, i) { + dump_seg->address = sg_dma_address(s); + dump_seg->v_address = sg_virt(s); + dump_seg->size = s->length; + dump_seg->type = type; + cnss_pr_dbg("seg-%d: address 0x%lx, v_address %pK, size 0x%lx\n", + i, dump_seg->address, + dump_seg->v_address, dump_seg->size); + dump_seg++; + } + + dump_data->nentries += count; + + return dump_seg; +} + +void cnss_pci_collect_dump_info(struct cnss_pci_data *pci_priv) +{ + struct cnss_plat_data *plat_priv = pci_priv->plat_priv; + struct cnss_dump_data *dump_data = + &plat_priv->ramdump_info_v2.dump_data; + void *start_addr, *end_addr; + + dump_data->nentries = 0; + + start_addr = plat_priv->ramdump_info_v2.dump_data_vaddr; + end_addr = cnss_pci_collect_dump_seg(pci_priv, + MHI_RDDM_FW_SEGMENT, start_addr); + + start_addr = end_addr; + end_addr = cnss_pci_collect_dump_seg(pci_priv, + MHI_RDDM_RD_SEGMENT, start_addr); + + if (dump_data->nentries > 0) + plat_priv->ramdump_info_v2.dump_data_valid = true; +} + +void cnss_pci_clear_dump_info(struct cnss_pci_data *pci_priv) +{ + struct cnss_plat_data *plat_priv = pci_priv->plat_priv; + + plat_priv->ramdump_info_v2.dump_data.nentries = 0; + plat_priv->ramdump_info_v2.dump_data_valid = false; +} + +static void cnss_mhi_notify_status(enum MHI_CB_REASON reason, void *priv) +{ + struct cnss_pci_data *pci_priv = priv; + struct cnss_plat_data *plat_priv = pci_priv->plat_priv; + enum cnss_recovery_reason cnss_reason = CNSS_REASON_RDDM; + + if (!pci_priv) + return; + + cnss_pr_dbg("MHI status cb is called with reason %d\n", reason); + + set_bit(CNSS_DEV_ERR_NOTIFY, &plat_priv->driver_state); + del_timer(&plat_priv->fw_boot_timer); + + if (reason == MHI_CB_SYS_ERROR) + cnss_reason = CNSS_REASON_TIMEOUT; + + cnss_schedule_recovery(&pci_priv->pci_dev->dev, + cnss_reason); +} + +static int cnss_pci_register_mhi(struct cnss_pci_data *pci_priv) +{ + int ret = 0; + struct pci_dev *pci_dev = pci_priv->pci_dev; + struct mhi_device *mhi_dev = &pci_priv->mhi_dev; + + mhi_dev->dev = &pci_priv->plat_priv->plat_dev->dev; + mhi_dev->pci_dev = pci_dev; + + mhi_dev->resources[0].start = (resource_size_t)pci_priv->bar; + mhi_dev->resources[0].end = (resource_size_t)pci_priv->bar + + pci_resource_len(pci_dev, PCI_BAR_NUM); + mhi_dev->resources[0].flags = + pci_resource_flags(pci_dev, PCI_BAR_NUM); + mhi_dev->resources[0].name = "BAR"; + cnss_pr_dbg("BAR start is %pa, BAR end is %pa\n", + &mhi_dev->resources[0].start, &mhi_dev->resources[0].end); + + if (!mhi_dev->resources[1].start) { + mhi_dev->resources[1].start = pci_dev->irq; + mhi_dev->resources[1].end = pci_dev->irq + 1; + mhi_dev->resources[1].flags = IORESOURCE_IRQ; + mhi_dev->resources[1].name = "IRQ"; + } + cnss_pr_dbg("IRQ start is %pa, IRQ end is %pa\n", + &mhi_dev->resources[1].start, &mhi_dev->resources[1].end); + + mhi_dev->pm_runtime_get = cnss_mhi_pm_runtime_get; + mhi_dev->pm_runtime_put_noidle = cnss_mhi_pm_runtime_put_noidle; + + mhi_dev->support_rddm = true; + mhi_dev->rddm_size = pci_priv->plat_priv->ramdump_info_v2.ramdump_size; + mhi_dev->status_cb = cnss_mhi_notify_status; + + ret = mhi_register_device(mhi_dev, MHI_NODE_NAME, pci_priv); + if (ret) { + cnss_pr_err("Failed to register as MHI device, err = %d\n", + ret); + return ret; + } + + return 0; +} + +static void cnss_pci_unregister_mhi(struct cnss_pci_data *pci_priv) +{ +} + +static enum mhi_dev_ctrl cnss_to_mhi_dev_state(enum cnss_mhi_state state) +{ + switch (state) { + case CNSS_MHI_INIT: + return MHI_DEV_CTRL_INIT; + case CNSS_MHI_DEINIT: + return MHI_DEV_CTRL_DE_INIT; + case CNSS_MHI_POWER_ON: + return MHI_DEV_CTRL_POWER_ON; + case CNSS_MHI_POWER_OFF: + return MHI_DEV_CTRL_POWER_OFF; + case CNSS_MHI_SUSPEND: + return MHI_DEV_CTRL_SUSPEND; + case CNSS_MHI_RESUME: + return MHI_DEV_CTRL_RESUME; + case CNSS_MHI_TRIGGER_RDDM: + return MHI_DEV_CTRL_TRIGGER_RDDM; + case CNSS_MHI_RDDM: + return MHI_DEV_CTRL_RDDM; + case CNSS_MHI_RDDM_KERNEL_PANIC: + return MHI_DEV_CTRL_RDDM_KERNEL_PANIC; + case CNSS_MHI_NOTIFY_LINK_ERROR: + return MHI_DEV_CTRL_NOTIFY_LINK_ERROR; + default: + cnss_pr_err("Unknown CNSS MHI state (%d)\n", state); + return -EINVAL; + } +} + +static int cnss_pci_check_mhi_state_bit(struct cnss_pci_data *pci_priv, + enum cnss_mhi_state mhi_state) +{ + switch (mhi_state) { + case CNSS_MHI_INIT: + if (!test_bit(CNSS_MHI_INIT, &pci_priv->mhi_state)) + return 0; + break; + case CNSS_MHI_DEINIT: + case CNSS_MHI_POWER_ON: + if (test_bit(CNSS_MHI_INIT, &pci_priv->mhi_state) && + !test_bit(CNSS_MHI_POWER_ON, &pci_priv->mhi_state)) + return 0; + break; + case CNSS_MHI_POWER_OFF: + case CNSS_MHI_SUSPEND: + if (test_bit(CNSS_MHI_POWER_ON, &pci_priv->mhi_state) && + !test_bit(CNSS_MHI_SUSPEND, &pci_priv->mhi_state)) + return 0; + break; + case CNSS_MHI_RESUME: + if (test_bit(CNSS_MHI_SUSPEND, &pci_priv->mhi_state)) + return 0; + break; + case CNSS_MHI_TRIGGER_RDDM: + case CNSS_MHI_RDDM: + case CNSS_MHI_RDDM_KERNEL_PANIC: + case CNSS_MHI_NOTIFY_LINK_ERROR: + return 0; + default: + cnss_pr_err("Unhandled MHI state: %s(%d)\n", + cnss_mhi_state_to_str(mhi_state), mhi_state); + } + + cnss_pr_err("Cannot set MHI state %s(%d) in current MHI state (0x%lx)\n", + cnss_mhi_state_to_str(mhi_state), mhi_state, + pci_priv->mhi_state); + + return -EINVAL; +} + +static void cnss_pci_set_mhi_state_bit(struct cnss_pci_data *pci_priv, + enum cnss_mhi_state mhi_state) +{ + switch (mhi_state) { + case CNSS_MHI_INIT: + set_bit(CNSS_MHI_INIT, &pci_priv->mhi_state); + break; + case CNSS_MHI_DEINIT: + clear_bit(CNSS_MHI_INIT, &pci_priv->mhi_state); + break; + case CNSS_MHI_POWER_ON: + set_bit(CNSS_MHI_POWER_ON, &pci_priv->mhi_state); + break; + case CNSS_MHI_POWER_OFF: + clear_bit(CNSS_MHI_POWER_ON, &pci_priv->mhi_state); + break; + case CNSS_MHI_SUSPEND: + set_bit(CNSS_MHI_SUSPEND, &pci_priv->mhi_state); + break; + case CNSS_MHI_RESUME: + clear_bit(CNSS_MHI_SUSPEND, &pci_priv->mhi_state); + break; + case CNSS_MHI_TRIGGER_RDDM: + case CNSS_MHI_RDDM: + case CNSS_MHI_RDDM_KERNEL_PANIC: + case CNSS_MHI_NOTIFY_LINK_ERROR: + break; + default: + cnss_pr_err("Unhandled MHI state (%d)\n", mhi_state); + } +} + +int cnss_pci_set_mhi_state(struct cnss_pci_data *pci_priv, + enum cnss_mhi_state mhi_state) +{ + int ret = 0; + enum mhi_dev_ctrl mhi_dev_state = cnss_to_mhi_dev_state(mhi_state); + + if (!pci_priv) { + cnss_pr_err("pci_priv is NULL!\n"); + return -ENODEV; + } + + if (pci_priv->device_id == QCA6174_DEVICE_ID) + return 0; + + if (mhi_dev_state < 0) { + cnss_pr_err("Invalid MHI DEV state (%d)\n", mhi_dev_state); + return -EINVAL; + } + + ret = cnss_pci_check_mhi_state_bit(pci_priv, mhi_state); + if (ret) + goto out; + + cnss_pr_dbg("Setting MHI state: %s(%d)\n", + cnss_mhi_state_to_str(mhi_state), mhi_state); + ret = mhi_pm_control_device(&pci_priv->mhi_dev, mhi_dev_state); + if (ret) { + cnss_pr_err("Failed to set MHI state: %s(%d)\n", + cnss_mhi_state_to_str(mhi_state), mhi_state); + goto out; + } + + cnss_pci_set_mhi_state_bit(pci_priv, mhi_state); + +out: + return ret; +} + +int cnss_pci_start_mhi(struct cnss_pci_data *pci_priv) +{ + int ret = 0; + + if (!pci_priv) { + cnss_pr_err("pci_priv is NULL!\n"); + return -ENODEV; + } + + if (fbc_bypass) + return 0; + + ret = cnss_pci_set_mhi_state(pci_priv, CNSS_MHI_INIT); + if (ret) + goto out; + + ret = cnss_pci_set_mhi_state(pci_priv, CNSS_MHI_POWER_ON); + if (ret) + goto deinit_mhi; + + return 0; + +deinit_mhi: + cnss_pci_set_mhi_state(pci_priv, CNSS_MHI_DEINIT); +out: + return ret; +} + +void cnss_pci_stop_mhi(struct cnss_pci_data *pci_priv) +{ + struct cnss_plat_data *plat_priv; + + if (!pci_priv) { + cnss_pr_err("pci_priv is NULL!\n"); + return; + } + + if (fbc_bypass) + return; + + plat_priv = pci_priv->plat_priv; + + cnss_pci_set_mhi_state_bit(pci_priv, CNSS_MHI_RESUME); + cnss_pci_set_mhi_state(pci_priv, CNSS_MHI_POWER_OFF); + if (!plat_priv->ramdump_info_v2.dump_data_valid) + cnss_pci_set_mhi_state(pci_priv, CNSS_MHI_DEINIT); +} + +static int cnss_pci_probe(struct pci_dev *pci_dev, + const struct pci_device_id *id) +{ + int ret = 0; + struct cnss_pci_data *pci_priv; + struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(NULL); + struct resource *res; + + cnss_pr_dbg("PCI is probing, vendor ID: 0x%x, device ID: 0x%x\n", + id->vendor, pci_dev->device); + + switch (pci_dev->device) { + case QCA6290_EMULATION_DEVICE_ID: + case QCA6290_DEVICE_ID: + if (!mhi_is_device_ready(&plat_priv->plat_dev->dev, + MHI_NODE_NAME)) { + cnss_pr_err("MHI driver is not ready, defer PCI probe!\n"); + ret = -EPROBE_DEFER; + goto out; + } + break; + default: + break; + } + + pci_priv = devm_kzalloc(&pci_dev->dev, sizeof(*pci_priv), + GFP_KERNEL); + if (!pci_priv) { + ret = -ENOMEM; + goto out; + } + + pci_priv->pci_link_state = PCI_LINK_UP; + pci_priv->plat_priv = plat_priv; + pci_priv->pci_dev = pci_dev; + pci_priv->pci_device_id = id; + pci_priv->device_id = pci_dev->device; + cnss_set_pci_priv(pci_dev, pci_priv); + plat_priv->device_id = pci_dev->device; + plat_priv->bus_priv = pci_priv; + + ret = cnss_register_subsys(plat_priv); + if (ret) + goto reset_ctx; + + ret = cnss_register_ramdump(plat_priv); + if (ret) + goto unregister_subsys; + + res = platform_get_resource_byname(plat_priv->plat_dev, IORESOURCE_MEM, + "smmu_iova_base"); + if (res) { + pci_priv->smmu_iova_start = res->start; + pci_priv->smmu_iova_len = resource_size(res); + cnss_pr_dbg("smmu_iova_start: %pa, smmu_iova_len: %zu\n", + &pci_priv->smmu_iova_start, + pci_priv->smmu_iova_len); + + ret = cnss_pci_init_smmu(pci_priv); + if (ret) { + cnss_pr_err("Failed to init SMMU, err = %d\n", ret); + goto unregister_ramdump; + } + } + + ret = cnss_reg_pci_event(pci_priv); + if (ret) { + cnss_pr_err("Failed to register PCI event, err = %d\n", ret); + goto deinit_smmu; + } + + ret = cnss_pci_enable_bus(pci_priv); + if (ret) + goto dereg_pci_event; + + switch (pci_dev->device) { + case QCA6174_DEVICE_ID: + pci_read_config_word(pci_dev, QCA6174_REV_ID_OFFSET, + &pci_priv->revision_id); + ret = cnss_suspend_pci_link(pci_priv); + if (ret) + cnss_pr_err("Failed to suspend PCI link, err = %d\n", + ret); + cnss_power_off_device(plat_priv); + break; + case QCA6290_EMULATION_DEVICE_ID: + case QCA6290_DEVICE_ID: + ret = cnss_pci_enable_msi(pci_priv); + if (ret) + goto disable_bus; + ret = cnss_pci_register_mhi(pci_priv); + if (ret) { + cnss_pci_disable_msi(pci_priv); + goto disable_bus; + } + break; + default: + cnss_pr_err("Unknown PCI device found: 0x%x\n", + pci_dev->device); + ret = -ENODEV; + goto disable_bus; + } + + return 0; + +disable_bus: + cnss_pci_disable_bus(pci_priv); +dereg_pci_event: + cnss_dereg_pci_event(pci_priv); +deinit_smmu: + if (pci_priv->smmu_mapping) + cnss_pci_deinit_smmu(pci_priv); +unregister_ramdump: + cnss_unregister_ramdump(plat_priv); +unregister_subsys: + cnss_unregister_subsys(plat_priv); +reset_ctx: + plat_priv->bus_priv = NULL; +out: + return ret; +} + +static void cnss_pci_remove(struct pci_dev *pci_dev) +{ + struct cnss_pci_data *pci_priv = cnss_get_pci_priv(pci_dev); + struct cnss_plat_data *plat_priv = + cnss_bus_dev_to_plat_priv(&pci_dev->dev); + + cnss_pci_free_m3_mem(pci_priv); + cnss_pci_free_fw_mem(pci_priv); + + switch (pci_dev->device) { + case QCA6290_EMULATION_DEVICE_ID: + case QCA6290_DEVICE_ID: + cnss_pci_unregister_mhi(pci_priv); + cnss_pci_disable_msi(pci_priv); + break; + default: + break; + } + + cnss_pci_disable_bus(pci_priv); + cnss_dereg_pci_event(pci_priv); + if (pci_priv->smmu_mapping) + cnss_pci_deinit_smmu(pci_priv); + cnss_unregister_ramdump(plat_priv); + cnss_unregister_subsys(plat_priv); + plat_priv->bus_priv = NULL; +} + +static const struct pci_device_id cnss_pci_id_table[] = { + { QCA6174_VENDOR_ID, QCA6174_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID }, + { QCA6290_EMULATION_VENDOR_ID, QCA6290_EMULATION_DEVICE_ID, + PCI_ANY_ID, PCI_ANY_ID }, + { QCA6290_VENDOR_ID, QCA6290_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID }, + { 0 } +}; +MODULE_DEVICE_TABLE(pci, cnss_pci_id_table); + +static const struct dev_pm_ops cnss_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(cnss_pci_suspend, cnss_pci_resume) + SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(cnss_pci_suspend_noirq, + cnss_pci_resume_noirq) + SET_RUNTIME_PM_OPS(cnss_pci_runtime_suspend, cnss_pci_runtime_resume, + cnss_pci_runtime_idle) +}; + +struct pci_driver cnss_pci_driver = { + .name = "cnss_pci", + .id_table = cnss_pci_id_table, + .probe = cnss_pci_probe, + .remove = cnss_pci_remove, + .driver = { + .pm = &cnss_pm_ops, + }, +}; + +int cnss_pci_init(struct cnss_plat_data *plat_priv) +{ + int ret = 0; + struct device *dev = &plat_priv->plat_dev->dev; + u32 rc_num; + + ret = of_property_read_u32(dev->of_node, "qcom,wlan-rc-num", &rc_num); + if (ret) { + cnss_pr_err("Failed to find PCIe RC number, err = %d\n", ret); + goto out; + } + + ret = msm_pcie_enumerate(rc_num); + if (ret) { + cnss_pr_err("Failed to enable PCIe RC%x, err = %d\n", + rc_num, ret); + goto out; + } + + ret = pci_register_driver(&cnss_pci_driver); + if (ret) { + cnss_pr_err("Failed to register to PCI framework, err = %d\n", + ret); + goto out; + } + + return 0; +out: + return ret; +} + +void cnss_pci_deinit(struct cnss_plat_data *plat_priv) +{ + pci_unregister_driver(&cnss_pci_driver); +} diff --git a/drivers/net/wireless/cnss2/pci.h b/drivers/net/wireless/cnss2/pci.h new file mode 100644 index 000000000000..4dc29c3c1f10 --- /dev/null +++ b/drivers/net/wireless/cnss2/pci.h @@ -0,0 +1,141 @@ +/* Copyright (c) 2016-2017, The Linux Foundation. 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 version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _CNSS_PCI_H +#define _CNSS_PCI_H + +#include <asm/dma-iommu.h> +#include <linux/iommu.h> +#include <linux/msm_mhi.h> +#include <linux/msm_pcie.h> +#include <linux/pci.h> + +#include "main.h" + +#define QCA6174_VENDOR_ID 0x168C +#define QCA6174_DEVICE_ID 0x003E +#define QCA6174_REV_ID_OFFSET 0x08 +#define QCA6174_REV3_VERSION 0x5020000 +#define QCA6174_REV3_2_VERSION 0x5030000 +#define QCA6290_VENDOR_ID 0x17CB +#define QCA6290_DEVICE_ID 0x1100 +#define QCA6290_EMULATION_VENDOR_ID 0x168C +#define QCA6290_EMULATION_DEVICE_ID 0xABCD + +enum cnss_mhi_state { + CNSS_MHI_INIT, + CNSS_MHI_DEINIT, + CNSS_MHI_SUSPEND, + CNSS_MHI_RESUME, + CNSS_MHI_POWER_OFF, + CNSS_MHI_POWER_ON, + CNSS_MHI_TRIGGER_RDDM, + CNSS_MHI_RDDM, + CNSS_MHI_RDDM_KERNEL_PANIC, + CNSS_MHI_NOTIFY_LINK_ERROR, +}; + +struct cnss_msi_user { + char *name; + int num_vectors; + u32 base_vector; +}; + +struct cnss_msi_config { + int total_vectors; + int total_users; + struct cnss_msi_user *users; +}; + +struct cnss_pci_data { + struct pci_dev *pci_dev; + struct cnss_plat_data *plat_priv; + const struct pci_device_id *pci_device_id; + u32 device_id; + u16 revision_id; + bool pci_link_state; + bool pci_link_down_ind; + struct pci_saved_state *saved_state; + struct msm_pcie_register_event msm_pci_event; + atomic_t auto_suspended; + bool monitor_wake_intr; + struct dma_iommu_mapping *smmu_mapping; + dma_addr_t smmu_iova_start; + size_t smmu_iova_len; + void __iomem *bar; + struct cnss_msi_config *msi_config; + u32 msi_ep_base_data; + struct mhi_device mhi_dev; + unsigned long mhi_state; +}; + +static inline void cnss_set_pci_priv(struct pci_dev *pci_dev, void *data) +{ + pci_set_drvdata(pci_dev, data); +} + +static inline struct cnss_pci_data *cnss_get_pci_priv(struct pci_dev *pci_dev) +{ + return pci_get_drvdata(pci_dev); +} + +static inline struct cnss_plat_data *cnss_pci_priv_to_plat_priv(void *bus_priv) +{ + struct cnss_pci_data *pci_priv = bus_priv; + + return pci_priv->plat_priv; +} + +static inline void cnss_pci_set_monitor_wake_intr(void *bus_priv, bool val) +{ + struct cnss_pci_data *pci_priv = bus_priv; + + pci_priv->monitor_wake_intr = val; +} + +static inline bool cnss_pci_get_monitor_wake_intr(void *bus_priv) +{ + struct cnss_pci_data *pci_priv = bus_priv; + + return pci_priv->monitor_wake_intr; +} + +static inline void cnss_pci_set_auto_suspended(void *bus_priv, int val) +{ + struct cnss_pci_data *pci_priv = bus_priv; + + atomic_set(&pci_priv->auto_suspended, val); +} + +static inline int cnss_pci_get_auto_suspended(void *bus_priv) +{ + struct cnss_pci_data *pci_priv = bus_priv; + + return atomic_read(&pci_priv->auto_suspended); +} + +int cnss_suspend_pci_link(struct cnss_pci_data *pci_priv); +int cnss_resume_pci_link(struct cnss_pci_data *pci_priv); +int cnss_pci_init(struct cnss_plat_data *plat_priv); +void cnss_pci_deinit(struct cnss_plat_data *plat_priv); +int cnss_pci_alloc_fw_mem(struct cnss_pci_data *pci_priv); +int cnss_pci_load_m3(struct cnss_pci_data *pci_priv); +int cnss_pci_get_bar_info(struct cnss_pci_data *pci_priv, void __iomem **va, + phys_addr_t *pa); +int cnss_pci_set_mhi_state(struct cnss_pci_data *pci_priv, + enum cnss_mhi_state state); +int cnss_pci_start_mhi(struct cnss_pci_data *pci_priv); +void cnss_pci_stop_mhi(struct cnss_pci_data *pci_priv); +void cnss_pci_collect_dump_info(struct cnss_pci_data *pci_priv); +void cnss_pci_clear_dump_info(struct cnss_pci_data *pci_priv); + +#endif /* _CNSS_PCI_H */ diff --git a/drivers/net/wireless/cnss2/power.c b/drivers/net/wireless/cnss2/power.c new file mode 100644 index 000000000000..8ed1507bde11 --- /dev/null +++ b/drivers/net/wireless/cnss2/power.c @@ -0,0 +1,386 @@ +/* Copyright (c) 2016-2017, The Linux Foundation. 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 version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/delay.h> +#include <linux/of.h> +#include <linux/pinctrl/consumer.h> +#include <linux/regulator/consumer.h> + +#include "main.h" +#include "debug.h" + +static struct cnss_vreg_info cnss_vreg_info[] = { + {NULL, "vdd-wlan-core", 1300000, 1300000, 0, 0}, + {NULL, "vdd-wlan-io", 1800000, 1800000, 0, 0}, + {NULL, "vdd-wlan-xtal-aon", 0, 0, 0, 0}, + {NULL, "vdd-wlan-xtal", 1800000, 1800000, 0, 2}, + {NULL, "vdd-wlan", 0, 0, 0, 0}, + {NULL, "vdd-wlan-sp2t", 2700000, 2700000, 0, 0}, + {NULL, "wlan-ant-switch", 2700000, 2700000, 20000, 0}, + {NULL, "wlan-soc-swreg", 1200000, 1200000, 0, 0}, + {NULL, "vdd-wlan-en", 0, 0, 0, 10}, +}; + +#define CNSS_VREG_INFO_SIZE ARRAY_SIZE(cnss_vreg_info) +#define MAX_PROP_SIZE 32 + +#define BOOTSTRAP_GPIO "qcom,enable-bootstrap-gpio" +#define BOOTSTRAP_ACTIVE "bootstrap_active" +#define WLAN_EN_GPIO "wlan-en-gpio" +#define WLAN_EN_ACTIVE "wlan_en_active" +#define WLAN_EN_SLEEP "wlan_en_sleep" + +#define BOOTSTRAP_DELAY 1000 +#define WLAN_ENABLE_DELAY 1000 + +int cnss_get_vreg(struct cnss_plat_data *plat_priv) +{ + int ret = 0; + int i; + struct cnss_vreg_info *vreg_info; + struct device *dev; + struct regulator *reg; + const __be32 *prop; + char prop_name[MAX_PROP_SIZE]; + int len; + + dev = &plat_priv->plat_dev->dev; + + plat_priv->vreg_info = devm_kzalloc(dev, sizeof(cnss_vreg_info), + GFP_KERNEL); + if (!plat_priv->vreg_info) { + ret = -ENOMEM; + goto out; + } + + memcpy(plat_priv->vreg_info, cnss_vreg_info, sizeof(cnss_vreg_info)); + + for (i = 0; i < CNSS_VREG_INFO_SIZE; i++) { + vreg_info = &plat_priv->vreg_info[i]; + reg = devm_regulator_get_optional(dev, vreg_info->name); + if (IS_ERR(reg)) { + ret = PTR_ERR(reg); + if (ret == -ENODEV) + continue; + else if (ret == -EPROBE_DEFER) + cnss_pr_info("EPROBE_DEFER for regulator: %s\n", + vreg_info->name); + else + cnss_pr_err("Failed to get regulator %s, err = %d\n", + vreg_info->name, ret); + goto out; + } + + vreg_info->reg = reg; + + snprintf(prop_name, MAX_PROP_SIZE, "qcom,%s-info", + vreg_info->name); + + prop = of_get_property(dev->of_node, prop_name, &len); + cnss_pr_dbg("Got regulator info, name: %s, len: %d\n", + prop_name, len); + + if (!prop || len != (4 * sizeof(__be32))) { + cnss_pr_dbg("Property %s %s, use default\n", prop_name, + prop ? "invalid format" : "doesn't exist"); + } else { + vreg_info->min_uv = be32_to_cpup(&prop[0]); + vreg_info->max_uv = be32_to_cpup(&prop[1]); + vreg_info->load_ua = be32_to_cpup(&prop[2]); + vreg_info->delay_us = be32_to_cpup(&prop[3]); + } + + cnss_pr_dbg("Got regulator: %s, min_uv: %u, max_uv: %u, load_ua: %u, delay_us: %u\n", + vreg_info->name, vreg_info->min_uv, + vreg_info->max_uv, vreg_info->load_ua, + vreg_info->delay_us); + } + + return 0; +out: + return ret; +} + +static int cnss_vreg_on(struct cnss_plat_data *plat_priv) +{ + int ret = 0; + struct cnss_vreg_info *vreg_info; + int i; + + if (!plat_priv) { + cnss_pr_err("plat_priv is NULL!\n"); + return -ENODEV; + } + + for (i = 0; i < CNSS_VREG_INFO_SIZE; i++) { + vreg_info = &plat_priv->vreg_info[i]; + + if (!vreg_info->reg) + continue; + + cnss_pr_dbg("Regulator %s is being enabled\n", vreg_info->name); + + if (vreg_info->min_uv != 0 && vreg_info->max_uv != 0) { + ret = regulator_set_voltage(vreg_info->reg, + vreg_info->min_uv, + vreg_info->max_uv); + + if (ret) { + cnss_pr_err("Failed to set voltage for regulator %s, min_uv: %u, max_uv: %u, err = %d\n", + vreg_info->name, vreg_info->min_uv, + vreg_info->max_uv, ret); + break; + } + } + + if (vreg_info->load_ua) { + ret = regulator_set_load(vreg_info->reg, + vreg_info->load_ua); + + if (ret < 0) { + cnss_pr_err("Failed to set load for regulator %s, load: %u, err = %d\n", + vreg_info->name, vreg_info->load_ua, + ret); + break; + } + } + + if (vreg_info->delay_us) + udelay(vreg_info->delay_us); + + ret = regulator_enable(vreg_info->reg); + if (ret) { + cnss_pr_err("Failed to enable regulator %s, err = %d\n", + vreg_info->name, ret); + break; + } + } + + if (ret) { + for (; i >= 0; i--) { + vreg_info = &plat_priv->vreg_info[i]; + + if (!vreg_info->reg) + continue; + + regulator_disable(vreg_info->reg); + if (vreg_info->load_ua) + regulator_set_load(vreg_info->reg, 0); + if (vreg_info->min_uv != 0 && vreg_info->max_uv != 0) + regulator_set_voltage(vreg_info->reg, 0, + vreg_info->max_uv); + } + + return ret; + } + + return 0; +} + +static int cnss_vreg_off(struct cnss_plat_data *plat_priv) +{ + int ret = 0; + struct cnss_vreg_info *vreg_info; + int i; + + if (!plat_priv) { + cnss_pr_err("plat_priv is NULL!\n"); + return -ENODEV; + } + + for (i = CNSS_VREG_INFO_SIZE - 1; i >= 0; i--) { + vreg_info = &plat_priv->vreg_info[i]; + + if (!vreg_info->reg) + continue; + + cnss_pr_dbg("Regulator %s is being disabled\n", + vreg_info->name); + + ret = regulator_disable(vreg_info->reg); + if (ret) + cnss_pr_err("Failed to disable regulator %s, err = %d\n", + vreg_info->name, ret); + + if (vreg_info->load_ua) { + ret = regulator_set_load(vreg_info->reg, 0); + if (ret < 0) + cnss_pr_err("Failed to set load for regulator %s, err = %d\n", + vreg_info->name, ret); + } + + if (vreg_info->min_uv != 0 && vreg_info->max_uv != 0) { + ret = regulator_set_voltage(vreg_info->reg, 0, + vreg_info->max_uv); + if (ret) + cnss_pr_err("Failed to set voltage for regulator %s, err = %d\n", + vreg_info->name, ret); + } + } + + return ret; +} + +int cnss_get_pinctrl(struct cnss_plat_data *plat_priv) +{ + int ret = 0; + struct device *dev; + struct cnss_pinctrl_info *pinctrl_info; + + dev = &plat_priv->plat_dev->dev; + pinctrl_info = &plat_priv->pinctrl_info; + + pinctrl_info->pinctrl = devm_pinctrl_get(dev); + if (IS_ERR_OR_NULL(pinctrl_info->pinctrl)) { + ret = PTR_ERR(pinctrl_info->pinctrl); + cnss_pr_err("Failed to get pinctrl, err = %d\n", ret); + goto out; + } + + if (of_find_property(dev->of_node, BOOTSTRAP_GPIO, NULL)) { + pinctrl_info->bootstrap_active = + pinctrl_lookup_state(pinctrl_info->pinctrl, + BOOTSTRAP_ACTIVE); + if (IS_ERR_OR_NULL(pinctrl_info->bootstrap_active)) { + ret = PTR_ERR(pinctrl_info->bootstrap_active); + cnss_pr_err("Failed to get bootstrap active state, err = %d\n", + ret); + goto out; + } + } + + if (of_find_property(dev->of_node, WLAN_EN_GPIO, NULL)) { + pinctrl_info->wlan_en_active = + pinctrl_lookup_state(pinctrl_info->pinctrl, + WLAN_EN_ACTIVE); + if (IS_ERR_OR_NULL(pinctrl_info->wlan_en_active)) { + ret = PTR_ERR(pinctrl_info->wlan_en_active); + cnss_pr_err("Failed to get wlan_en active state, err = %d\n", + ret); + goto out; + } + + pinctrl_info->wlan_en_sleep = + pinctrl_lookup_state(pinctrl_info->pinctrl, + WLAN_EN_SLEEP); + if (IS_ERR_OR_NULL(pinctrl_info->wlan_en_sleep)) { + ret = PTR_ERR(pinctrl_info->wlan_en_sleep); + cnss_pr_err("Failed to get wlan_en sleep state, err = %d\n", + ret); + goto out; + } + } + + return 0; +out: + return ret; +} + +static int cnss_select_pinctrl_state(struct cnss_plat_data *plat_priv, + bool state) +{ + int ret = 0; + struct cnss_pinctrl_info *pinctrl_info; + + if (!plat_priv) { + cnss_pr_err("plat_priv is NULL!\n"); + ret = -ENODEV; + goto out; + } + + pinctrl_info = &plat_priv->pinctrl_info; + + if (state) { + if (!IS_ERR_OR_NULL(pinctrl_info->bootstrap_active)) { + ret = pinctrl_select_state(pinctrl_info->pinctrl, + pinctrl_info-> + bootstrap_active); + if (ret) { + cnss_pr_err("Failed to select bootstrap active state, err = %d\n", + ret); + goto out; + } + udelay(BOOTSTRAP_DELAY); + } + + if (!IS_ERR_OR_NULL(pinctrl_info->wlan_en_active)) { + ret = pinctrl_select_state(pinctrl_info->pinctrl, + pinctrl_info-> + wlan_en_active); + if (ret) { + cnss_pr_err("Failed to select wlan_en active state, err = %d\n", + ret); + goto out; + } + udelay(WLAN_ENABLE_DELAY); + } + } else { + if (!IS_ERR_OR_NULL(pinctrl_info->wlan_en_sleep)) { + ret = pinctrl_select_state(pinctrl_info->pinctrl, + pinctrl_info->wlan_en_sleep); + if (ret) { + cnss_pr_err("Failed to select wlan_en sleep state, err = %d\n", + ret); + goto out; + } + } + } + + return 0; +out: + return ret; +} + +int cnss_power_on_device(struct cnss_plat_data *plat_priv) +{ + int ret = 0; + + ret = cnss_vreg_on(plat_priv); + if (ret) { + cnss_pr_err("Failed to turn on vreg, err = %d\n", ret); + goto out; + } + + ret = cnss_select_pinctrl_state(plat_priv, true); + if (ret) { + cnss_pr_err("Failed to select pinctrl state, err = %d\n", ret); + goto vreg_off; + } + + return 0; +vreg_off: + cnss_vreg_off(plat_priv); +out: + return ret; +} + +void cnss_power_off_device(struct cnss_plat_data *plat_priv) +{ + cnss_select_pinctrl_state(plat_priv, false); + cnss_vreg_off(plat_priv); +} + +void cnss_set_pin_connect_status(struct cnss_plat_data *plat_priv) +{ + unsigned long pin_status = 0; + + set_bit(CNSS_WLAN_EN, &pin_status); + set_bit(CNSS_PCIE_TXN, &pin_status); + set_bit(CNSS_PCIE_TXP, &pin_status); + set_bit(CNSS_PCIE_RXN, &pin_status); + set_bit(CNSS_PCIE_RXP, &pin_status); + set_bit(CNSS_PCIE_REFCLKN, &pin_status); + set_bit(CNSS_PCIE_REFCLKP, &pin_status); + set_bit(CNSS_PCIE_RST, &pin_status); + + plat_priv->pin_result.host_pin_result = pin_status; +} diff --git a/drivers/net/wireless/cnss2/qmi.c b/drivers/net/wireless/cnss2/qmi.c new file mode 100644 index 000000000000..db55d3350eb5 --- /dev/null +++ b/drivers/net/wireless/cnss2/qmi.c @@ -0,0 +1,1006 @@ +/* Copyright (c) 2015-2017, The Linux Foundation. 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 version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/firmware.h> +#include <linux/module.h> +#include <linux/qmi_encdec.h> +#include <soc/qcom/msm_qmi_interface.h> + +#include "main.h" +#include "debug.h" +#include "qmi.h" + +#define WLFW_SERVICE_INS_ID_V01 1 +#define WLFW_CLIENT_ID 0x4b4e454c +#define MAX_BDF_FILE_NAME 11 +#define DEFAULT_BDF_FILE_NAME "bdwlan.elf" +#define BDF_FILE_NAME_PREFIX "bdwlan.e" + +#ifdef CONFIG_CNSS2_DEBUG +static unsigned int qmi_timeout = 10000; +module_param(qmi_timeout, uint, 0600); +MODULE_PARM_DESC(qmi_timeout, "Timeout for QMI message in milliseconds"); + +#define QMI_WLFW_TIMEOUT_MS qmi_timeout +#else +#define QMI_WLFW_TIMEOUT_MS 10000 +#endif + +static bool daemon_support; +module_param(daemon_support, bool, 0600); +MODULE_PARM_DESC(daemon_support, "User space has cnss-daemon support or not"); + +static bool bdf_bypass = true; +#ifdef CONFIG_CNSS2_DEBUG +module_param(bdf_bypass, bool, 0600); +MODULE_PARM_DESC(bdf_bypass, "If BDF is not found, send dummy BDF to FW"); +#endif + +enum cnss_bdf_type { + CNSS_BDF_BIN, + CNSS_BDF_ELF, +}; + +static void cnss_wlfw_clnt_notifier_work(struct work_struct *work) +{ + struct cnss_plat_data *plat_priv = + container_of(work, struct cnss_plat_data, qmi_recv_msg_work); + int ret = 0; + + cnss_pr_dbg("Receiving QMI WLFW event in work queue context\n"); + + do { + ret = qmi_recv_msg(plat_priv->qmi_wlfw_clnt); + } while (ret == 0); + + if (ret != -ENOMSG) + cnss_pr_err("Error receiving message: %d\n", ret); + + cnss_pr_dbg("Receiving QMI event completed\n"); +} + +static void cnss_wlfw_clnt_notifier(struct qmi_handle *handle, + enum qmi_event_type event, + void *notify_priv) +{ + struct cnss_plat_data *plat_priv = notify_priv; + + cnss_pr_dbg("Received QMI WLFW event: %d\n", event); + + if (!plat_priv) { + cnss_pr_err("plat_priv is NULL!\n"); + return; + } + + switch (event) { + case QMI_RECV_MSG: + schedule_work(&plat_priv->qmi_recv_msg_work); + break; + case QMI_SERVER_EXIT: + break; + default: + cnss_pr_dbg("Unhandled QMI event: %d\n", event); + break; + } +} + +static int cnss_wlfw_clnt_svc_event_notifier(struct notifier_block *nb, + unsigned long code, void *_cmd) +{ + struct cnss_plat_data *plat_priv = + container_of(nb, struct cnss_plat_data, qmi_wlfw_clnt_nb); + int ret = 0; + + cnss_pr_dbg("Received QMI WLFW service event: %ld\n", code); + + switch (code) { + case QMI_SERVER_ARRIVE: + ret = cnss_driver_event_post(plat_priv, + CNSS_DRIVER_EVENT_SERVER_ARRIVE, + false, NULL); + break; + + case QMI_SERVER_EXIT: + ret = cnss_driver_event_post(plat_priv, + CNSS_DRIVER_EVENT_SERVER_EXIT, + false, NULL); + break; + default: + cnss_pr_dbg("Invalid QMI service event: %ld\n", code); + break; + } + + return ret; +} + +static int cnss_wlfw_host_cap_send_sync(struct cnss_plat_data *plat_priv) +{ + struct wlfw_host_cap_req_msg_v01 req; + struct wlfw_host_cap_resp_msg_v01 resp; + struct msg_desc req_desc, resp_desc; + int ret = 0; + + cnss_pr_dbg("Sending host capability message, state: 0x%lx\n", + plat_priv->driver_state); + + memset(&req, 0, sizeof(req)); + memset(&resp, 0, sizeof(resp)); + + req.daemon_support_valid = 1; + req.daemon_support = daemon_support; + + cnss_pr_dbg("daemon_support is %d\n", req.daemon_support); + + req_desc.max_msg_len = WLFW_HOST_CAP_REQ_MSG_V01_MAX_MSG_LEN; + req_desc.msg_id = QMI_WLFW_HOST_CAP_REQ_V01; + req_desc.ei_array = wlfw_host_cap_req_msg_v01_ei; + + resp_desc.max_msg_len = WLFW_HOST_CAP_RESP_MSG_V01_MAX_MSG_LEN; + resp_desc.msg_id = QMI_WLFW_HOST_CAP_RESP_V01; + resp_desc.ei_array = wlfw_host_cap_resp_msg_v01_ei; + + ret = qmi_send_req_wait(plat_priv->qmi_wlfw_clnt, &req_desc, &req, + sizeof(req), &resp_desc, &resp, sizeof(resp), + QMI_WLFW_TIMEOUT_MS); + if (ret < 0) { + cnss_pr_err("Failed to send host capability request, err = %d\n", + ret); + goto out; + } + + if (resp.resp.result != QMI_RESULT_SUCCESS_V01) { + cnss_pr_err("Host capability request failed, result: %d, err: %d\n", + resp.resp.result, resp.resp.error); + ret = resp.resp.result; + goto out; + } + + return 0; +out: + CNSS_ASSERT(0); + return ret; +} + +static int cnss_wlfw_ind_register_send_sync(struct cnss_plat_data *plat_priv) +{ + struct wlfw_ind_register_req_msg_v01 req; + struct wlfw_ind_register_resp_msg_v01 resp; + struct msg_desc req_desc, resp_desc; + int ret = 0; + + cnss_pr_dbg("Sending indication register message, state: 0x%lx\n", + plat_priv->driver_state); + + memset(&req, 0, sizeof(req)); + memset(&resp, 0, sizeof(resp)); + + req.client_id_valid = 1; + req.client_id = WLFW_CLIENT_ID; + req.fw_ready_enable_valid = 1; + req.fw_ready_enable = 1; + req.request_mem_enable_valid = 1; + req.request_mem_enable = 1; + req.fw_mem_ready_enable_valid = 1; + req.fw_mem_ready_enable = 1; + req.cold_boot_cal_done_enable_valid = 1; + req.cold_boot_cal_done_enable = 1; + req.pin_connect_result_enable_valid = 1; + req.pin_connect_result_enable = 1; + + req_desc.max_msg_len = WLFW_IND_REGISTER_REQ_MSG_V01_MAX_MSG_LEN; + req_desc.msg_id = QMI_WLFW_IND_REGISTER_REQ_V01; + req_desc.ei_array = wlfw_ind_register_req_msg_v01_ei; + + resp_desc.max_msg_len = WLFW_IND_REGISTER_RESP_MSG_V01_MAX_MSG_LEN; + resp_desc.msg_id = QMI_WLFW_IND_REGISTER_RESP_V01; + resp_desc.ei_array = wlfw_ind_register_resp_msg_v01_ei; + + ret = qmi_send_req_wait(plat_priv->qmi_wlfw_clnt, &req_desc, &req, + sizeof(req), &resp_desc, &resp, sizeof(resp), + QMI_WLFW_TIMEOUT_MS); + if (ret < 0) { + cnss_pr_err("Failed to send indication register request, err = %d\n", + ret); + goto out; + } + + if (resp.resp.result != QMI_RESULT_SUCCESS_V01) { + cnss_pr_err("Indication register request failed, result: %d, err: %d\n", + resp.resp.result, resp.resp.error); + ret = resp.resp.result; + goto out; + } + + return 0; +out: + CNSS_ASSERT(0); + return ret; +} + +static int cnss_wlfw_request_mem_ind_hdlr(struct cnss_plat_data *plat_priv, + void *msg, unsigned int msg_len) +{ + struct msg_desc ind_desc; + struct wlfw_request_mem_ind_msg_v01 ind_msg; + struct cnss_fw_mem *fw_mem = &plat_priv->fw_mem; + int ret = 0; + + ind_desc.msg_id = QMI_WLFW_REQUEST_MEM_IND_V01; + ind_desc.max_msg_len = WLFW_REQUEST_MEM_IND_MSG_V01_MAX_MSG_LEN; + ind_desc.ei_array = wlfw_request_mem_ind_msg_v01_ei; + + ret = qmi_kernel_decode(&ind_desc, &ind_msg, msg, msg_len); + if (ret < 0) { + cnss_pr_err("Failed to decode request memory indication, msg_len: %u, err = %d\n", + ret, msg_len); + return ret; + } + + fw_mem->size = ind_msg.size; + + cnss_driver_event_post(plat_priv, CNSS_DRIVER_EVENT_REQUEST_MEM, + false, NULL); + + return 0; +} + +static int cnss_qmi_pin_result_ind_hdlr(struct cnss_plat_data *plat_priv, + void *msg, unsigned int msg_len) +{ + struct msg_desc ind_desc; + struct wlfw_pin_connect_result_ind_msg_v01 ind_msg; + int ret = 0; + + ind_desc.msg_id = QMI_WLFW_PIN_CONNECT_RESULT_IND_V01; + ind_desc.max_msg_len = WLFW_PIN_CONNECT_RESULT_IND_MSG_V01_MAX_MSG_LEN; + ind_desc.ei_array = wlfw_pin_connect_result_ind_msg_v01_ei; + + ret = qmi_kernel_decode(&ind_desc, &ind_msg, msg, msg_len); + if (ret < 0) { + cnss_pr_err("Failed to decode pin connect result indication, msg_len: %u, err = %d\n", + msg_len, ret); + return ret; + } + if (ind_msg.pwr_pin_result_valid) + plat_priv->pin_result.fw_pwr_pin_result = + ind_msg.pwr_pin_result; + if (ind_msg.phy_io_pin_result_valid) + plat_priv->pin_result.fw_phy_io_pin_result = + ind_msg.phy_io_pin_result; + if (ind_msg.rf_pin_result_valid) + plat_priv->pin_result.fw_rf_pin_result = ind_msg.rf_pin_result; + + cnss_pr_dbg("Pin connect Result: pwr_pin: 0x%x phy_io_pin: 0x%x rf_io_pin: 0x%x\n", + ind_msg.pwr_pin_result, ind_msg.phy_io_pin_result, + ind_msg.rf_pin_result); + return ret; +} + +int cnss_wlfw_respond_mem_send_sync(struct cnss_plat_data *plat_priv) +{ + struct wlfw_respond_mem_req_msg_v01 req; + struct wlfw_respond_mem_resp_msg_v01 resp; + struct msg_desc req_desc, resp_desc; + struct cnss_fw_mem *fw_mem = &plat_priv->fw_mem; + int ret = 0; + + cnss_pr_dbg("Sending respond memory message, state: 0x%lx\n", + plat_priv->driver_state); + + if (!fw_mem->pa || !fw_mem->size) { + cnss_pr_err("Memory for FW is not available!\n"); + ret = -ENOMEM; + goto out; + } + + cnss_pr_dbg("Memory for FW, va: 0x%pK, pa: %pa, size: 0x%zx\n", + fw_mem->va, &fw_mem->pa, fw_mem->size); + + memset(&req, 0, sizeof(req)); + memset(&resp, 0, sizeof(resp)); + + req.addr = fw_mem->pa; + req.size = fw_mem->size; + + req_desc.max_msg_len = WLFW_RESPOND_MEM_REQ_MSG_V01_MAX_MSG_LEN; + req_desc.msg_id = QMI_WLFW_RESPOND_MEM_REQ_V01; + req_desc.ei_array = wlfw_respond_mem_req_msg_v01_ei; + + resp_desc.max_msg_len = WLFW_RESPOND_MEM_RESP_MSG_V01_MAX_MSG_LEN; + resp_desc.msg_id = QMI_WLFW_RESPOND_MEM_RESP_V01; + resp_desc.ei_array = wlfw_respond_mem_resp_msg_v01_ei; + + ret = qmi_send_req_wait(plat_priv->qmi_wlfw_clnt, &req_desc, &req, + sizeof(req), &resp_desc, &resp, sizeof(resp), + QMI_WLFW_TIMEOUT_MS); + if (ret < 0) { + cnss_pr_err("Failed to send respond memory request, err = %d\n", + ret); + goto out; + } + + if (resp.resp.result != QMI_RESULT_SUCCESS_V01) { + cnss_pr_err("Respond memory request failed, result: %d, err: %d\n", + resp.resp.result, resp.resp.error); + ret = resp.resp.result; + goto out; + } + + return 0; +out: + CNSS_ASSERT(0); + return ret; +} + +int cnss_wlfw_tgt_cap_send_sync(struct cnss_plat_data *plat_priv) +{ + struct wlfw_cap_req_msg_v01 req; + struct wlfw_cap_resp_msg_v01 resp; + struct msg_desc req_desc, resp_desc; + int ret = 0; + + cnss_pr_dbg("Sending target capability message, state: 0x%lx\n", + plat_priv->driver_state); + + memset(&req, 0, sizeof(req)); + memset(&resp, 0, sizeof(resp)); + + req_desc.max_msg_len = WLFW_CAP_REQ_MSG_V01_MAX_MSG_LEN; + req_desc.msg_id = QMI_WLFW_CAP_REQ_V01; + req_desc.ei_array = wlfw_cap_req_msg_v01_ei; + + resp_desc.max_msg_len = WLFW_CAP_RESP_MSG_V01_MAX_MSG_LEN; + resp_desc.msg_id = QMI_WLFW_CAP_RESP_V01; + resp_desc.ei_array = wlfw_cap_resp_msg_v01_ei; + + ret = qmi_send_req_wait(plat_priv->qmi_wlfw_clnt, &req_desc, &req, + sizeof(req), &resp_desc, &resp, sizeof(resp), + QMI_WLFW_TIMEOUT_MS); + if (ret < 0) { + cnss_pr_err("Failed to send target capability request, err = %d\n", + ret); + goto out; + } + + if (resp.resp.result != QMI_RESULT_SUCCESS_V01) { + cnss_pr_err("Target capability request failed, result: %d, err: %d\n", + resp.resp.result, resp.resp.error); + ret = resp.resp.result; + goto out; + } + + if (resp.chip_info_valid) + plat_priv->chip_info = resp.chip_info; + if (resp.board_info_valid) + plat_priv->board_info = resp.board_info; + else + plat_priv->board_info.board_id = 0xFF; + if (resp.soc_info_valid) + plat_priv->soc_info = resp.soc_info; + if (resp.fw_version_info_valid) + plat_priv->fw_version_info = resp.fw_version_info; + + cnss_pr_dbg("Target capability: chip_id: 0x%x, chip_family: 0x%x, board_id: 0x%x, soc_id: 0x%x, fw_version: 0x%x, fw_build_timestamp: %s", + plat_priv->chip_info.chip_id, + plat_priv->chip_info.chip_family, + plat_priv->board_info.board_id, plat_priv->soc_info.soc_id, + plat_priv->fw_version_info.fw_version, + plat_priv->fw_version_info.fw_build_timestamp); + + return 0; +out: + CNSS_ASSERT(0); + return ret; +} + +int cnss_wlfw_bdf_dnld_send_sync(struct cnss_plat_data *plat_priv) +{ + struct wlfw_bdf_download_req_msg_v01 *req; + struct wlfw_bdf_download_resp_msg_v01 resp; + struct msg_desc req_desc, resp_desc; + char filename[MAX_BDF_FILE_NAME]; + const struct firmware *fw_entry; + const u8 *temp; + unsigned int remaining; + int ret = 0; + + cnss_pr_dbg("Sending BDF download message, state: 0x%lx\n", + plat_priv->driver_state); + + req = kzalloc(sizeof(*req), GFP_KERNEL); + if (!req) { + ret = -ENOMEM; + goto out; + } + + if (plat_priv->board_info.board_id == 0xFF) + snprintf(filename, sizeof(filename), DEFAULT_BDF_FILE_NAME); + else + snprintf(filename, sizeof(filename), + BDF_FILE_NAME_PREFIX "%02x", + plat_priv->board_info.board_id); + + ret = request_firmware(&fw_entry, filename, &plat_priv->plat_dev->dev); + if (ret) { + cnss_pr_err("Failed to load BDF: %s\n", filename); + if (bdf_bypass) { + cnss_pr_info("bdf_bypass is enabled, sending dummy BDF\n"); + temp = filename; + remaining = MAX_BDF_FILE_NAME; + goto bypass_bdf; + } else { + goto err_req_fw; + } + } + + temp = fw_entry->data; + remaining = fw_entry->size; + +bypass_bdf: + cnss_pr_dbg("Downloading BDF: %s, size: %u\n", filename, remaining); + + memset(&resp, 0, sizeof(resp)); + + req_desc.max_msg_len = WLFW_BDF_DOWNLOAD_REQ_MSG_V01_MAX_MSG_LEN; + req_desc.msg_id = QMI_WLFW_BDF_DOWNLOAD_REQ_V01; + req_desc.ei_array = wlfw_bdf_download_req_msg_v01_ei; + + resp_desc.max_msg_len = WLFW_BDF_DOWNLOAD_RESP_MSG_V01_MAX_MSG_LEN; + resp_desc.msg_id = QMI_WLFW_BDF_DOWNLOAD_RESP_V01; + resp_desc.ei_array = wlfw_bdf_download_resp_msg_v01_ei; + + while (remaining) { + req->valid = 1; + req->file_id_valid = 1; + req->file_id = plat_priv->board_info.board_id; + req->total_size_valid = 1; + req->total_size = remaining; + req->seg_id_valid = 1; + req->data_valid = 1; + req->end_valid = 1; + req->bdf_type_valid = 1; + req->bdf_type = CNSS_BDF_ELF; + + if (remaining > QMI_WLFW_MAX_DATA_SIZE_V01) { + req->data_len = QMI_WLFW_MAX_DATA_SIZE_V01; + } else { + req->data_len = remaining; + req->end = 1; + } + + memcpy(req->data, temp, req->data_len); + + ret = qmi_send_req_wait(plat_priv->qmi_wlfw_clnt, &req_desc, + req, sizeof(*req), &resp_desc, &resp, + sizeof(resp), QMI_WLFW_TIMEOUT_MS); + if (ret < 0) { + cnss_pr_err("Failed to send BDF download request, err = %d\n", + ret); + goto err_send; + } + + if (resp.resp.result != QMI_RESULT_SUCCESS_V01) { + cnss_pr_err("BDF download request failed, result: %d, err: %d\n", + resp.resp.result, resp.resp.error); + ret = resp.resp.result; + goto err_send; + } + + remaining -= req->data_len; + temp += req->data_len; + req->seg_id++; + } + +err_send: + if (!bdf_bypass) + release_firmware(fw_entry); +err_req_fw: + kfree(req); +out: + if (ret) + CNSS_ASSERT(0); + return ret; +} + +int cnss_wlfw_m3_dnld_send_sync(struct cnss_plat_data *plat_priv) +{ + struct wlfw_m3_info_req_msg_v01 req; + struct wlfw_m3_info_resp_msg_v01 resp; + struct msg_desc req_desc, resp_desc; + struct cnss_fw_mem *m3_mem = &plat_priv->m3_mem; + int ret = 0; + + cnss_pr_dbg("Sending M3 information message, state: 0x%lx\n", + plat_priv->driver_state); + + if (!m3_mem->pa || !m3_mem->size) { + cnss_pr_err("Memory for M3 is not available!\n"); + ret = -ENOMEM; + goto out; + } + + cnss_pr_dbg("M3 memory, va: 0x%pK, pa: %pa, size: 0x%zx\n", + m3_mem->va, &m3_mem->pa, m3_mem->size); + + memset(&req, 0, sizeof(req)); + memset(&resp, 0, sizeof(resp)); + + req.addr = plat_priv->m3_mem.pa; + req.size = plat_priv->m3_mem.size; + + req_desc.max_msg_len = WLFW_M3_INFO_REQ_MSG_V01_MAX_MSG_LEN; + req_desc.msg_id = QMI_WLFW_M3_INFO_REQ_V01; + req_desc.ei_array = wlfw_m3_info_req_msg_v01_ei; + + resp_desc.max_msg_len = WLFW_M3_INFO_RESP_MSG_V01_MAX_MSG_LEN; + resp_desc.msg_id = QMI_WLFW_M3_INFO_RESP_V01; + resp_desc.ei_array = wlfw_m3_info_resp_msg_v01_ei; + + ret = qmi_send_req_wait(plat_priv->qmi_wlfw_clnt, &req_desc, &req, + sizeof(req), &resp_desc, &resp, sizeof(resp), + QMI_WLFW_TIMEOUT_MS); + if (ret < 0) { + cnss_pr_err("Failed to send M3 information request, err = %d\n", + ret); + goto out; + } + + if (resp.resp.result != QMI_RESULT_SUCCESS_V01) { + cnss_pr_err("M3 information request failed, result: %d, err: %d\n", + resp.resp.result, resp.resp.error); + ret = resp.resp.result; + goto out; + } + + return 0; + +out: + CNSS_ASSERT(0); + return ret; +} + +int cnss_wlfw_wlan_mode_send_sync(struct cnss_plat_data *plat_priv, + enum wlfw_driver_mode_enum_v01 mode) +{ + struct wlfw_wlan_mode_req_msg_v01 req; + struct wlfw_wlan_mode_resp_msg_v01 resp; + struct msg_desc req_desc, resp_desc; + int ret = 0; + + if (!plat_priv) + return -ENODEV; + + cnss_pr_dbg("Sending mode message, state: 0x%lx, mode: %d\n", + plat_priv->driver_state, mode); + + if (mode == QMI_WLFW_OFF_V01 && + test_bit(CNSS_DRIVER_RECOVERY, &plat_priv->driver_state)) { + cnss_pr_dbg("Recovery is in progress, ignore mode off request.\n"); + return 0; + } + + memset(&req, 0, sizeof(req)); + memset(&resp, 0, sizeof(resp)); + + req.mode = mode; + req.hw_debug_valid = 1; + req.hw_debug = 0; + + req_desc.max_msg_len = WLFW_WLAN_MODE_REQ_MSG_V01_MAX_MSG_LEN; + req_desc.msg_id = QMI_WLFW_WLAN_MODE_REQ_V01; + req_desc.ei_array = wlfw_wlan_mode_req_msg_v01_ei; + + resp_desc.max_msg_len = WLFW_WLAN_MODE_RESP_MSG_V01_MAX_MSG_LEN; + resp_desc.msg_id = QMI_WLFW_WLAN_MODE_RESP_V01; + resp_desc.ei_array = wlfw_wlan_mode_resp_msg_v01_ei; + + ret = qmi_send_req_wait(plat_priv->qmi_wlfw_clnt, &req_desc, &req, + sizeof(req), &resp_desc, &resp, sizeof(resp), + QMI_WLFW_TIMEOUT_MS); + if (ret < 0) { + if (mode == QMI_WLFW_OFF_V01 && ret == -ENETRESET) { + cnss_pr_dbg("WLFW service is disconnected while sending mode off request.\n"); + return 0; + } + cnss_pr_err("Failed to send mode request, mode: %d, err = %d\n", + mode, ret); + goto out; + } + + if (resp.resp.result != QMI_RESULT_SUCCESS_V01) { + cnss_pr_err("Mode request failed, mode: %d, result: %d err: %d\n", + mode, resp.resp.result, resp.resp.error); + ret = resp.resp.result; + goto out; + } + + return 0; +out: + if (mode != QMI_WLFW_OFF_V01) + CNSS_ASSERT(0); + return ret; +} + +int cnss_wlfw_wlan_cfg_send_sync(struct cnss_plat_data *plat_priv, + struct wlfw_wlan_cfg_req_msg_v01 *data) +{ + struct wlfw_wlan_cfg_req_msg_v01 req; + struct wlfw_wlan_cfg_resp_msg_v01 resp; + struct msg_desc req_desc, resp_desc; + int ret = 0; + + cnss_pr_dbg("Sending WLAN config message, state: 0x%lx\n", + plat_priv->driver_state); + + if (!plat_priv) + return -ENODEV; + + memset(&req, 0, sizeof(req)); + memset(&resp, 0, sizeof(resp)); + + memcpy(&req, data, sizeof(req)); + + req_desc.max_msg_len = WLFW_WLAN_CFG_REQ_MSG_V01_MAX_MSG_LEN; + req_desc.msg_id = QMI_WLFW_WLAN_CFG_REQ_V01; + req_desc.ei_array = wlfw_wlan_cfg_req_msg_v01_ei; + + resp_desc.max_msg_len = WLFW_WLAN_CFG_RESP_MSG_V01_MAX_MSG_LEN; + resp_desc.msg_id = QMI_WLFW_WLAN_CFG_RESP_V01; + resp_desc.ei_array = wlfw_wlan_cfg_resp_msg_v01_ei; + + ret = qmi_send_req_wait(plat_priv->qmi_wlfw_clnt, &req_desc, &req, + sizeof(req), &resp_desc, &resp, sizeof(resp), + QMI_WLFW_TIMEOUT_MS); + if (ret < 0) { + cnss_pr_err("Failed to send WLAN config request, err = %d\n", + ret); + goto out; + } + + if (resp.resp.result != QMI_RESULT_SUCCESS_V01) { + cnss_pr_err("WLAN config request failed, result: %d, err: %d\n", + resp.resp.result, resp.resp.error); + ret = resp.resp.result; + goto out; + } + + return 0; +out: + CNSS_ASSERT(0); + return ret; +} + +int cnss_wlfw_athdiag_read_send_sync(struct cnss_plat_data *plat_priv, + u32 offset, u32 mem_type, + u32 data_len, u8 *data) +{ + struct wlfw_athdiag_read_req_msg_v01 req; + struct wlfw_athdiag_read_resp_msg_v01 *resp; + struct msg_desc req_desc, resp_desc; + int ret = 0; + + if (!plat_priv) + return -ENODEV; + + if (!plat_priv->qmi_wlfw_clnt) + return -EINVAL; + + cnss_pr_dbg("athdiag read: state 0x%lx, offset %x, mem_type %x, data_len %u\n", + plat_priv->driver_state, offset, mem_type, data_len); + + resp = kzalloc(sizeof(*resp), GFP_KERNEL); + if (!resp) + return -ENOMEM; + + memset(&req, 0, sizeof(req)); + + req.offset = offset; + req.mem_type = mem_type; + req.data_len = data_len; + + req_desc.max_msg_len = WLFW_ATHDIAG_READ_REQ_MSG_V01_MAX_MSG_LEN; + req_desc.msg_id = QMI_WLFW_ATHDIAG_READ_REQ_V01; + req_desc.ei_array = wlfw_athdiag_read_req_msg_v01_ei; + + resp_desc.max_msg_len = WLFW_ATHDIAG_READ_RESP_MSG_V01_MAX_MSG_LEN; + resp_desc.msg_id = QMI_WLFW_ATHDIAG_READ_RESP_V01; + resp_desc.ei_array = wlfw_athdiag_read_resp_msg_v01_ei; + + ret = qmi_send_req_wait(plat_priv->qmi_wlfw_clnt, &req_desc, &req, + sizeof(req), &resp_desc, resp, sizeof(*resp), + QMI_WLFW_TIMEOUT_MS); + if (ret < 0) { + cnss_pr_err("Failed to send athdiag read request, err = %d\n", + ret); + goto out; + } + + if (resp->resp.result != QMI_RESULT_SUCCESS_V01) { + cnss_pr_err("athdiag read request failed, result: %d, err: %d\n", + resp->resp.result, resp->resp.error); + ret = resp->resp.result; + goto out; + } + + if (!resp->data_valid || resp->data_len != data_len) { + cnss_pr_err("athdiag read data is invalid, data_valid = %u, data_len = %u\n", + resp->data_valid, resp->data_len); + ret = -EINVAL; + goto out; + } + + memcpy(data, resp->data, resp->data_len); + +out: + kfree(resp); + return ret; +} + +int cnss_wlfw_athdiag_write_send_sync(struct cnss_plat_data *plat_priv, + u32 offset, u32 mem_type, + u32 data_len, u8 *data) +{ + struct wlfw_athdiag_write_req_msg_v01 *req; + struct wlfw_athdiag_write_resp_msg_v01 resp; + struct msg_desc req_desc, resp_desc; + int ret = 0; + + if (!plat_priv) + return -ENODEV; + + if (!plat_priv->qmi_wlfw_clnt) + return -EINVAL; + + cnss_pr_dbg("athdiag write: state 0x%lx, offset %x, mem_type %x, data_len %u, data %p\n", + plat_priv->driver_state, offset, mem_type, data_len, data); + + req = kzalloc(sizeof(*req), GFP_KERNEL); + if (!req) + return -ENOMEM; + + memset(&resp, 0, sizeof(resp)); + + req->offset = offset; + req->mem_type = mem_type; + req->data_len = data_len; + memcpy(req->data, data, data_len); + + req_desc.max_msg_len = WLFW_ATHDIAG_WRITE_REQ_MSG_V01_MAX_MSG_LEN; + req_desc.msg_id = QMI_WLFW_ATHDIAG_WRITE_REQ_V01; + req_desc.ei_array = wlfw_athdiag_write_req_msg_v01_ei; + + resp_desc.max_msg_len = WLFW_ATHDIAG_WRITE_RESP_MSG_V01_MAX_MSG_LEN; + resp_desc.msg_id = QMI_WLFW_ATHDIAG_WRITE_RESP_V01; + resp_desc.ei_array = wlfw_athdiag_write_resp_msg_v01_ei; + + ret = qmi_send_req_wait(plat_priv->qmi_wlfw_clnt, &req_desc, req, + sizeof(*req), &resp_desc, &resp, sizeof(resp), + QMI_WLFW_TIMEOUT_MS); + if (ret < 0) { + cnss_pr_err("Failed to send athdiag write request, err = %d\n", + ret); + goto out; + } + + if (resp.resp.result != QMI_RESULT_SUCCESS_V01) { + cnss_pr_err("athdiag write request failed, result: %d, err: %d\n", + resp.resp.result, resp.resp.error); + ret = resp.resp.result; + goto out; + } + +out: + kfree(req); + return ret; +} + +int cnss_wlfw_ini_send_sync(struct cnss_plat_data *plat_priv, + u8 fw_log_mode) +{ + int ret; + struct wlfw_ini_req_msg_v01 req; + struct wlfw_ini_resp_msg_v01 resp; + struct msg_desc req_desc, resp_desc; + + if (!plat_priv) + return -ENODEV; + + cnss_pr_dbg("Sending ini sync request, state: 0x%lx, fw_log_mode: %d\n", + plat_priv->driver_state, fw_log_mode); + + memset(&req, 0, sizeof(req)); + memset(&resp, 0, sizeof(resp)); + + req.enablefwlog_valid = 1; + req.enablefwlog = fw_log_mode; + + req_desc.max_msg_len = WLFW_INI_REQ_MSG_V01_MAX_MSG_LEN; + req_desc.msg_id = QMI_WLFW_INI_REQ_V01; + req_desc.ei_array = wlfw_ini_req_msg_v01_ei; + + resp_desc.max_msg_len = WLFW_INI_RESP_MSG_V01_MAX_MSG_LEN; + resp_desc.msg_id = QMI_WLFW_INI_RESP_V01; + resp_desc.ei_array = wlfw_ini_resp_msg_v01_ei; + + ret = qmi_send_req_wait(plat_priv->qmi_wlfw_clnt, + &req_desc, &req, sizeof(req), + &resp_desc, &resp, sizeof(resp), + QMI_WLFW_TIMEOUT_MS); + if (ret < 0) { + cnss_pr_err("Send INI req failed fw_log_mode: %d, ret: %d\n", + fw_log_mode, ret); + goto out; + } + + if (resp.resp.result != QMI_RESULT_SUCCESS_V01) { + cnss_pr_err("QMI INI request rejected, fw_log_mode:%d result:%d error:%d\n", + fw_log_mode, resp.resp.result, resp.resp.error); + ret = resp.resp.result; + goto out; + } + + return 0; + +out: + return ret; +} + +static void cnss_wlfw_clnt_ind(struct qmi_handle *handle, + unsigned int msg_id, void *msg, + unsigned int msg_len, void *ind_cb_priv) +{ + struct cnss_plat_data *plat_priv = ind_cb_priv; + + cnss_pr_dbg("Received QMI WLFW indication, msg_id: 0x%x, msg_len: %d\n", + msg_id, msg_len); + + if (!plat_priv) { + cnss_pr_err("plat_priv is NULL!\n"); + return; + } + + switch (msg_id) { + case QMI_WLFW_REQUEST_MEM_IND_V01: + cnss_wlfw_request_mem_ind_hdlr(plat_priv, msg, msg_len); + break; + case QMI_WLFW_FW_MEM_READY_IND_V01: + cnss_driver_event_post(plat_priv, + CNSS_DRIVER_EVENT_FW_MEM_READY, + false, NULL); + break; + case QMI_WLFW_COLD_BOOT_CAL_DONE_IND_V01: + cnss_driver_event_post(plat_priv, + CNSS_DRIVER_EVENT_COLD_BOOT_CAL_DONE, + false, NULL); + break; + case QMI_WLFW_FW_READY_IND_V01: + cnss_driver_event_post(plat_priv, + CNSS_DRIVER_EVENT_FW_READY, + false, NULL); + break; + case QMI_WLFW_PIN_CONNECT_RESULT_IND_V01: + cnss_qmi_pin_result_ind_hdlr(plat_priv, msg, msg_len); + break; + default: + cnss_pr_err("Invalid QMI WLFW indication, msg_id: 0x%x\n", + msg_id); + break; + } +} + +unsigned int cnss_get_qmi_timeout(void) +{ + cnss_pr_dbg("QMI timeout is %u ms\n", QMI_WLFW_TIMEOUT_MS); + + return QMI_WLFW_TIMEOUT_MS; +} +EXPORT_SYMBOL(cnss_get_qmi_timeout); + +int cnss_wlfw_server_arrive(struct cnss_plat_data *plat_priv) +{ + int ret = 0; + + if (!plat_priv) + return -ENODEV; + + plat_priv->qmi_wlfw_clnt = + qmi_handle_create(cnss_wlfw_clnt_notifier, plat_priv); + if (!plat_priv->qmi_wlfw_clnt) { + cnss_pr_err("Failed to create QMI client handle!\n"); + ret = -ENOMEM; + goto err_create_handle; + } + + ret = qmi_connect_to_service(plat_priv->qmi_wlfw_clnt, + WLFW_SERVICE_ID_V01, + WLFW_SERVICE_VERS_V01, + WLFW_SERVICE_INS_ID_V01); + if (ret < 0) { + cnss_pr_err("Failed to connect to QMI WLFW service, err = %d\n", + ret); + goto out; + } + + ret = qmi_register_ind_cb(plat_priv->qmi_wlfw_clnt, + cnss_wlfw_clnt_ind, plat_priv); + if (ret < 0) { + cnss_pr_err("Failed to register QMI WLFW service indication callback, err = %d\n", + ret); + goto out; + } + + set_bit(CNSS_QMI_WLFW_CONNECTED, &plat_priv->driver_state); + + cnss_pr_info("QMI WLFW service connected, state: 0x%lx\n", + plat_priv->driver_state); + + ret = cnss_wlfw_host_cap_send_sync(plat_priv); + if (ret < 0) + goto out; + + ret = cnss_wlfw_ind_register_send_sync(plat_priv); + if (ret < 0) + goto out; + + return 0; +out: + qmi_handle_destroy(plat_priv->qmi_wlfw_clnt); + plat_priv->qmi_wlfw_clnt = NULL; +err_create_handle: + CNSS_ASSERT(0); + return ret; +} + +int cnss_wlfw_server_exit(struct cnss_plat_data *plat_priv) +{ + if (!plat_priv) + return -ENODEV; + + qmi_handle_destroy(plat_priv->qmi_wlfw_clnt); + plat_priv->qmi_wlfw_clnt = NULL; + + clear_bit(CNSS_QMI_WLFW_CONNECTED, &plat_priv->driver_state); + + cnss_pr_info("QMI WLFW service disconnected, state: 0x%lx\n", + plat_priv->driver_state); + + return 0; +} + +int cnss_qmi_init(struct cnss_plat_data *plat_priv) +{ + int ret = 0; + + INIT_WORK(&plat_priv->qmi_recv_msg_work, + cnss_wlfw_clnt_notifier_work); + + plat_priv->qmi_wlfw_clnt_nb.notifier_call = + cnss_wlfw_clnt_svc_event_notifier; + + ret = qmi_svc_event_notifier_register(WLFW_SERVICE_ID_V01, + WLFW_SERVICE_VERS_V01, + WLFW_SERVICE_INS_ID_V01, + &plat_priv->qmi_wlfw_clnt_nb); + if (ret < 0) + cnss_pr_err("Failed to register QMI event notifier, err = %d\n", + ret); + + return ret; +} + +void cnss_qmi_deinit(struct cnss_plat_data *plat_priv) +{ + qmi_svc_event_notifier_unregister(WLFW_SERVICE_ID_V01, + WLFW_SERVICE_VERS_V01, + WLFW_SERVICE_INS_ID_V01, + &plat_priv->qmi_wlfw_clnt_nb); +} diff --git a/drivers/net/wireless/cnss2/qmi.h b/drivers/net/wireless/cnss2/qmi.h new file mode 100644 index 000000000000..70d8d404c48e --- /dev/null +++ b/drivers/net/wireless/cnss2/qmi.h @@ -0,0 +1,41 @@ +/* Copyright (c) 2015-2017, The Linux Foundation. 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 version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _CNSS_QMI_H +#define _CNSS_QMI_H + +#include "wlan_firmware_service_v01.h" + +struct cnss_plat_data; + +int cnss_qmi_init(struct cnss_plat_data *plat_priv); +void cnss_qmi_deinit(struct cnss_plat_data *plat_priv); +int cnss_wlfw_server_arrive(struct cnss_plat_data *plat_priv); +int cnss_wlfw_server_exit(struct cnss_plat_data *plat_priv); +int cnss_wlfw_respond_mem_send_sync(struct cnss_plat_data *plat_priv); +int cnss_wlfw_tgt_cap_send_sync(struct cnss_plat_data *plat_priv); +int cnss_wlfw_bdf_dnld_send_sync(struct cnss_plat_data *plat_priv); +int cnss_wlfw_m3_dnld_send_sync(struct cnss_plat_data *plat_priv); +int cnss_wlfw_wlan_mode_send_sync(struct cnss_plat_data *plat_priv, + enum wlfw_driver_mode_enum_v01 mode); +int cnss_wlfw_wlan_cfg_send_sync(struct cnss_plat_data *plat_priv, + struct wlfw_wlan_cfg_req_msg_v01 *data); +int cnss_wlfw_athdiag_read_send_sync(struct cnss_plat_data *plat_priv, + u32 offset, u32 mem_type, + u32 data_len, u8 *data); +int cnss_wlfw_athdiag_write_send_sync(struct cnss_plat_data *plat_priv, + u32 offset, u32 mem_type, + u32 data_len, u8 *data); +int cnss_wlfw_ini_send_sync(struct cnss_plat_data *plat_priv, + u8 fw_log_mode); + +#endif /* _CNSS_QMI_H */ diff --git a/drivers/net/wireless/cnss2/utils.c b/drivers/net/wireless/cnss2/utils.c new file mode 100644 index 000000000000..9ffe386e3677 --- /dev/null +++ b/drivers/net/wireless/cnss2/utils.c @@ -0,0 +1,129 @@ +/* Copyright (c) 2016-2017, The Linux Foundation. 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 version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define CNSS_MAX_CH_NUM 45 + +#include <linux/module.h> +#include <linux/slab.h> + +static DEFINE_MUTEX(unsafe_channel_list_lock); +static DEFINE_MUTEX(dfs_nol_info_lock); + +static struct cnss_unsafe_channel_list { + u16 unsafe_ch_count; + u16 unsafe_ch_list[CNSS_MAX_CH_NUM]; +} unsafe_channel_list; + +static struct cnss_dfs_nol_info { + void *dfs_nol_info; + u16 dfs_nol_info_len; +} dfs_nol_info; + +int cnss_set_wlan_unsafe_channel(u16 *unsafe_ch_list, u16 ch_count) +{ + mutex_lock(&unsafe_channel_list_lock); + if ((!unsafe_ch_list) || (ch_count > CNSS_MAX_CH_NUM)) { + mutex_unlock(&unsafe_channel_list_lock); + return -EINVAL; + } + + unsafe_channel_list.unsafe_ch_count = ch_count; + + if (ch_count != 0) { + memcpy((char *)unsafe_channel_list.unsafe_ch_list, + (char *)unsafe_ch_list, ch_count * sizeof(u16)); + } + mutex_unlock(&unsafe_channel_list_lock); + + return 0; +} +EXPORT_SYMBOL(cnss_set_wlan_unsafe_channel); + +int cnss_get_wlan_unsafe_channel(u16 *unsafe_ch_list, + u16 *ch_count, u16 buf_len) +{ + mutex_lock(&unsafe_channel_list_lock); + if (!unsafe_ch_list || !ch_count) { + mutex_unlock(&unsafe_channel_list_lock); + return -EINVAL; + } + + if (buf_len < (unsafe_channel_list.unsafe_ch_count * sizeof(u16))) { + mutex_unlock(&unsafe_channel_list_lock); + return -ENOMEM; + } + + *ch_count = unsafe_channel_list.unsafe_ch_count; + memcpy((char *)unsafe_ch_list, + (char *)unsafe_channel_list.unsafe_ch_list, + unsafe_channel_list.unsafe_ch_count * sizeof(u16)); + mutex_unlock(&unsafe_channel_list_lock); + + return 0; +} +EXPORT_SYMBOL(cnss_get_wlan_unsafe_channel); + +int cnss_wlan_set_dfs_nol(const void *info, u16 info_len) +{ + void *temp; + struct cnss_dfs_nol_info *dfs_info; + + mutex_lock(&dfs_nol_info_lock); + if (!info || !info_len) { + mutex_unlock(&dfs_nol_info_lock); + return -EINVAL; + } + + temp = kmalloc(info_len, GFP_KERNEL); + if (!temp) { + mutex_unlock(&dfs_nol_info_lock); + return -ENOMEM; + } + + memcpy(temp, info, info_len); + dfs_info = &dfs_nol_info; + kfree(dfs_info->dfs_nol_info); + + dfs_info->dfs_nol_info = temp; + dfs_info->dfs_nol_info_len = info_len; + mutex_unlock(&dfs_nol_info_lock); + + return 0; +} +EXPORT_SYMBOL(cnss_wlan_set_dfs_nol); + +int cnss_wlan_get_dfs_nol(void *info, u16 info_len) +{ + int len; + struct cnss_dfs_nol_info *dfs_info; + + mutex_lock(&dfs_nol_info_lock); + if (!info || !info_len) { + mutex_unlock(&dfs_nol_info_lock); + return -EINVAL; + } + + dfs_info = &dfs_nol_info; + + if (!dfs_info->dfs_nol_info || dfs_info->dfs_nol_info_len == 0) { + mutex_unlock(&dfs_nol_info_lock); + return -ENOENT; + } + + len = min(info_len, dfs_info->dfs_nol_info_len); + + memcpy(info, dfs_info->dfs_nol_info, len); + mutex_unlock(&dfs_nol_info_lock); + + return len; +} +EXPORT_SYMBOL(cnss_wlan_get_dfs_nol); diff --git a/drivers/net/wireless/cnss2/wlan_firmware_service_v01.c b/drivers/net/wireless/cnss2/wlan_firmware_service_v01.c new file mode 100644 index 000000000000..84a4707e9cc3 --- /dev/null +++ b/drivers/net/wireless/cnss2/wlan_firmware_service_v01.c @@ -0,0 +1,2168 @@ +/* Copyright (c) 2015-2017, The Linux Foundation. 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 version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT 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 "wlan_firmware_service_v01.h" + +static struct elem_info wlfw_ce_tgt_pipe_cfg_s_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .is_array = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct wlfw_ce_tgt_pipe_cfg_s_v01, + pipe_num), + }, + { + .data_type = QMI_SIGNED_4_BYTE_ENUM, + .elem_len = 1, + .elem_size = sizeof(enum wlfw_pipedir_enum_v01), + .is_array = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct wlfw_ce_tgt_pipe_cfg_s_v01, + pipe_dir), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .is_array = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct wlfw_ce_tgt_pipe_cfg_s_v01, + nentries), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .is_array = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct wlfw_ce_tgt_pipe_cfg_s_v01, + nbytes_max), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .is_array = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct wlfw_ce_tgt_pipe_cfg_s_v01, + flags), + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .is_array = QMI_COMMON_TLV_TYPE, + }, +}; + +static struct elem_info wlfw_ce_svc_pipe_cfg_s_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .is_array = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct wlfw_ce_svc_pipe_cfg_s_v01, + service_id), + }, + { + .data_type = QMI_SIGNED_4_BYTE_ENUM, + .elem_len = 1, + .elem_size = sizeof(enum wlfw_pipedir_enum_v01), + .is_array = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct wlfw_ce_svc_pipe_cfg_s_v01, + pipe_dir), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .is_array = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct wlfw_ce_svc_pipe_cfg_s_v01, + pipe_num), + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .is_array = QMI_COMMON_TLV_TYPE, + }, +}; + +static struct elem_info wlfw_shadow_reg_cfg_s_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_2_BYTE, + .elem_len = 1, + .elem_size = sizeof(u16), + .is_array = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct wlfw_shadow_reg_cfg_s_v01, + id), + }, + { + .data_type = QMI_UNSIGNED_2_BYTE, + .elem_len = 1, + .elem_size = sizeof(u16), + .is_array = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct wlfw_shadow_reg_cfg_s_v01, + offset), + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .is_array = QMI_COMMON_TLV_TYPE, + }, +}; + +static struct elem_info wlfw_shadow_reg_v2_cfg_s_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .is_array = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct wlfw_shadow_reg_v2_cfg_s_v01, + addr), + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .is_array = QMI_COMMON_TLV_TYPE, + }, +}; + +static struct elem_info wlfw_memory_region_info_s_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_8_BYTE, + .elem_len = 1, + .elem_size = sizeof(u64), + .is_array = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct wlfw_memory_region_info_s_v01, + region_addr), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .is_array = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct wlfw_memory_region_info_s_v01, + size), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct wlfw_memory_region_info_s_v01, + secure_flag), + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .is_array = QMI_COMMON_TLV_TYPE, + }, +}; + +static struct elem_info wlfw_rf_chip_info_s_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .is_array = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct wlfw_rf_chip_info_s_v01, + chip_id), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .is_array = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct wlfw_rf_chip_info_s_v01, + chip_family), + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .is_array = QMI_COMMON_TLV_TYPE, + }, +}; + +static struct elem_info wlfw_rf_board_info_s_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .is_array = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct wlfw_rf_board_info_s_v01, + board_id), + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .is_array = QMI_COMMON_TLV_TYPE, + }, +}; + +static struct elem_info wlfw_soc_info_s_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .is_array = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct wlfw_soc_info_s_v01, + soc_id), + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .is_array = QMI_COMMON_TLV_TYPE, + }, +}; + +static struct elem_info wlfw_fw_version_info_s_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .is_array = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct wlfw_fw_version_info_s_v01, + fw_version), + }, + { + .data_type = QMI_STRING, + .elem_len = QMI_WLFW_MAX_TIMESTAMP_LEN_V01 + 1, + .elem_size = sizeof(char), + .is_array = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct wlfw_fw_version_info_s_v01, + fw_build_timestamp), + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .is_array = QMI_COMMON_TLV_TYPE, + }, +}; + +struct elem_info wlfw_ind_register_req_msg_v01_ei[] = { + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct + wlfw_ind_register_req_msg_v01, + fw_ready_enable_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct wlfw_ind_register_req_msg_v01, + fw_ready_enable), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x11, + .offset = offsetof(struct wlfw_ind_register_req_msg_v01, + initiate_cal_download_enable_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x11, + .offset = offsetof(struct wlfw_ind_register_req_msg_v01, + initiate_cal_download_enable), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x12, + .offset = offsetof(struct wlfw_ind_register_req_msg_v01, + initiate_cal_update_enable_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x12, + .offset = offsetof(struct wlfw_ind_register_req_msg_v01, + initiate_cal_update_enable), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x13, + .offset = offsetof(struct wlfw_ind_register_req_msg_v01, + msa_ready_enable_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x13, + .offset = offsetof(struct wlfw_ind_register_req_msg_v01, + msa_ready_enable), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x14, + .offset = offsetof(struct wlfw_ind_register_req_msg_v01, + pin_connect_result_enable_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x14, + .offset = offsetof(struct wlfw_ind_register_req_msg_v01, + pin_connect_result_enable), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x15, + .offset = offsetof(struct wlfw_ind_register_req_msg_v01, + client_id_valid), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .is_array = NO_ARRAY, + .tlv_type = 0x15, + .offset = offsetof(struct wlfw_ind_register_req_msg_v01, + client_id), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x16, + .offset = offsetof(struct wlfw_ind_register_req_msg_v01, + request_mem_enable_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x16, + .offset = offsetof(struct wlfw_ind_register_req_msg_v01, + request_mem_enable), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x17, + .offset = offsetof(struct wlfw_ind_register_req_msg_v01, + fw_mem_ready_enable_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x17, + .offset = offsetof(struct wlfw_ind_register_req_msg_v01, + fw_mem_ready_enable), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x18, + .offset = offsetof(struct wlfw_ind_register_req_msg_v01, + cold_boot_cal_done_enable_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x18, + .offset = offsetof(struct wlfw_ind_register_req_msg_v01, + cold_boot_cal_done_enable), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x19, + .offset = offsetof(struct wlfw_ind_register_req_msg_v01, + rejuvenate_enable_valid), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .is_array = NO_ARRAY, + .tlv_type = 0x19, + .offset = offsetof(struct wlfw_ind_register_req_msg_v01, + rejuvenate_enable), + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .is_array = QMI_COMMON_TLV_TYPE, + }, +}; + +struct elem_info wlfw_ind_register_resp_msg_v01_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct qmi_response_type_v01), + .is_array = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct + wlfw_ind_register_resp_msg_v01, + resp), + .ei_array = get_qmi_response_type_v01_ei(), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct + wlfw_ind_register_resp_msg_v01, + fw_status_valid), + }, + { + .data_type = QMI_UNSIGNED_8_BYTE, + .elem_len = 1, + .elem_size = sizeof(u64), + .is_array = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct + wlfw_ind_register_resp_msg_v01, + fw_status), + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .is_array = QMI_COMMON_TLV_TYPE, + }, +}; + +struct elem_info wlfw_fw_ready_ind_msg_v01_ei[] = { + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .is_array = QMI_COMMON_TLV_TYPE, + }, +}; + +struct elem_info wlfw_msa_ready_ind_msg_v01_ei[] = { + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .is_array = QMI_COMMON_TLV_TYPE, + }, +}; + +struct elem_info wlfw_pin_connect_result_ind_msg_v01_ei[] = { + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct + wlfw_pin_connect_result_ind_msg_v01, + pwr_pin_result_valid), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .is_array = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct + wlfw_pin_connect_result_ind_msg_v01, + pwr_pin_result), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x11, + .offset = offsetof(struct + wlfw_pin_connect_result_ind_msg_v01, + phy_io_pin_result_valid), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .is_array = NO_ARRAY, + .tlv_type = 0x11, + .offset = offsetof(struct + wlfw_pin_connect_result_ind_msg_v01, + phy_io_pin_result), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x12, + .offset = offsetof(struct + wlfw_pin_connect_result_ind_msg_v01, + rf_pin_result_valid), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .is_array = NO_ARRAY, + .tlv_type = 0x12, + .offset = offsetof(struct + wlfw_pin_connect_result_ind_msg_v01, + rf_pin_result), + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .is_array = QMI_COMMON_TLV_TYPE, + }, +}; + +struct elem_info wlfw_wlan_mode_req_msg_v01_ei[] = { + { + .data_type = QMI_SIGNED_4_BYTE_ENUM, + .elem_len = 1, + .elem_size = sizeof(enum wlfw_driver_mode_enum_v01), + .is_array = NO_ARRAY, + .tlv_type = 0x01, + .offset = offsetof(struct wlfw_wlan_mode_req_msg_v01, + mode), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct wlfw_wlan_mode_req_msg_v01, + hw_debug_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct wlfw_wlan_mode_req_msg_v01, + hw_debug), + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .is_array = QMI_COMMON_TLV_TYPE, + }, +}; + +struct elem_info wlfw_wlan_mode_resp_msg_v01_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct qmi_response_type_v01), + .is_array = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct wlfw_wlan_mode_resp_msg_v01, + resp), + .ei_array = get_qmi_response_type_v01_ei(), + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .is_array = QMI_COMMON_TLV_TYPE, + }, +}; + +struct elem_info wlfw_wlan_cfg_req_msg_v01_ei[] = { + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct wlfw_wlan_cfg_req_msg_v01, + host_version_valid), + }, + { + .data_type = QMI_STRING, + .elem_len = QMI_WLFW_MAX_STR_LEN_V01 + 1, + .elem_size = sizeof(char), + .is_array = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct wlfw_wlan_cfg_req_msg_v01, + host_version), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x11, + .offset = offsetof(struct wlfw_wlan_cfg_req_msg_v01, + tgt_cfg_valid), + }, + { + .data_type = QMI_DATA_LEN, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x11, + .offset = offsetof(struct wlfw_wlan_cfg_req_msg_v01, + tgt_cfg_len), + }, + { + .data_type = QMI_STRUCT, + .elem_len = QMI_WLFW_MAX_NUM_CE_V01, + .elem_size = sizeof(struct wlfw_ce_tgt_pipe_cfg_s_v01), + .is_array = VAR_LEN_ARRAY, + .tlv_type = 0x11, + .offset = offsetof(struct wlfw_wlan_cfg_req_msg_v01, + tgt_cfg), + .ei_array = wlfw_ce_tgt_pipe_cfg_s_v01_ei, + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x12, + .offset = offsetof(struct wlfw_wlan_cfg_req_msg_v01, + svc_cfg_valid), + }, + { + .data_type = QMI_DATA_LEN, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x12, + .offset = offsetof(struct wlfw_wlan_cfg_req_msg_v01, + svc_cfg_len), + }, + { + .data_type = QMI_STRUCT, + .elem_len = QMI_WLFW_MAX_NUM_SVC_V01, + .elem_size = sizeof(struct wlfw_ce_svc_pipe_cfg_s_v01), + .is_array = VAR_LEN_ARRAY, + .tlv_type = 0x12, + .offset = offsetof(struct wlfw_wlan_cfg_req_msg_v01, + svc_cfg), + .ei_array = wlfw_ce_svc_pipe_cfg_s_v01_ei, + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x13, + .offset = offsetof(struct wlfw_wlan_cfg_req_msg_v01, + shadow_reg_valid), + }, + { + .data_type = QMI_DATA_LEN, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x13, + .offset = offsetof(struct wlfw_wlan_cfg_req_msg_v01, + shadow_reg_len), + }, + { + .data_type = QMI_STRUCT, + .elem_len = QMI_WLFW_MAX_NUM_SHADOW_REG_V01, + .elem_size = sizeof(struct wlfw_shadow_reg_cfg_s_v01), + .is_array = VAR_LEN_ARRAY, + .tlv_type = 0x13, + .offset = offsetof(struct wlfw_wlan_cfg_req_msg_v01, + shadow_reg), + .ei_array = wlfw_shadow_reg_cfg_s_v01_ei, + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x14, + .offset = offsetof(struct wlfw_wlan_cfg_req_msg_v01, + shadow_reg_v2_valid), + }, + { + .data_type = QMI_DATA_LEN, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x14, + .offset = offsetof(struct wlfw_wlan_cfg_req_msg_v01, + shadow_reg_v2_len), + }, + { + .data_type = QMI_STRUCT, + .elem_len = QMI_WLFW_MAX_NUM_SHADOW_REG_V2_V01, + .elem_size = sizeof(struct wlfw_shadow_reg_v2_cfg_s_v01), + .is_array = VAR_LEN_ARRAY, + .tlv_type = 0x14, + .offset = offsetof(struct wlfw_wlan_cfg_req_msg_v01, + shadow_reg_v2), + .ei_array = wlfw_shadow_reg_v2_cfg_s_v01_ei, + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .is_array = QMI_COMMON_TLV_TYPE, + }, +}; + +struct elem_info wlfw_wlan_cfg_resp_msg_v01_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct qmi_response_type_v01), + .is_array = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct wlfw_wlan_cfg_resp_msg_v01, + resp), + .ei_array = get_qmi_response_type_v01_ei(), + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .is_array = QMI_COMMON_TLV_TYPE, + }, +}; + +struct elem_info wlfw_cap_req_msg_v01_ei[] = { + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .is_array = QMI_COMMON_TLV_TYPE, + }, +}; + +struct elem_info wlfw_cap_resp_msg_v01_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct qmi_response_type_v01), + .is_array = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct wlfw_cap_resp_msg_v01, + resp), + .ei_array = get_qmi_response_type_v01_ei(), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct wlfw_cap_resp_msg_v01, + chip_info_valid), + }, + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct wlfw_rf_chip_info_s_v01), + .is_array = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct wlfw_cap_resp_msg_v01, + chip_info), + .ei_array = wlfw_rf_chip_info_s_v01_ei, + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x11, + .offset = offsetof(struct wlfw_cap_resp_msg_v01, + board_info_valid), + }, + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct wlfw_rf_board_info_s_v01), + .is_array = NO_ARRAY, + .tlv_type = 0x11, + .offset = offsetof(struct wlfw_cap_resp_msg_v01, + board_info), + .ei_array = wlfw_rf_board_info_s_v01_ei, + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x12, + .offset = offsetof(struct wlfw_cap_resp_msg_v01, + soc_info_valid), + }, + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct wlfw_soc_info_s_v01), + .is_array = NO_ARRAY, + .tlv_type = 0x12, + .offset = offsetof(struct wlfw_cap_resp_msg_v01, + soc_info), + .ei_array = wlfw_soc_info_s_v01_ei, + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x13, + .offset = offsetof(struct wlfw_cap_resp_msg_v01, + fw_version_info_valid), + }, + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct wlfw_fw_version_info_s_v01), + .is_array = NO_ARRAY, + .tlv_type = 0x13, + .offset = offsetof(struct wlfw_cap_resp_msg_v01, + fw_version_info), + .ei_array = wlfw_fw_version_info_s_v01_ei, + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x14, + .offset = offsetof(struct wlfw_cap_resp_msg_v01, + fw_build_id_valid), + }, + { + .data_type = QMI_STRING, + .elem_len = QMI_WLFW_MAX_BUILD_ID_LEN_V01 + 1, + .elem_size = sizeof(char), + .is_array = NO_ARRAY, + .tlv_type = 0x14, + .offset = offsetof(struct wlfw_cap_resp_msg_v01, + fw_build_id), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x15, + .offset = offsetof(struct wlfw_cap_resp_msg_v01, + num_macs_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x15, + .offset = offsetof(struct wlfw_cap_resp_msg_v01, + num_macs), + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .is_array = QMI_COMMON_TLV_TYPE, + }, +}; + +struct elem_info wlfw_bdf_download_req_msg_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x01, + .offset = offsetof(struct wlfw_bdf_download_req_msg_v01, + valid), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct wlfw_bdf_download_req_msg_v01, + file_id_valid), + }, + { + .data_type = QMI_SIGNED_4_BYTE_ENUM, + .elem_len = 1, + .elem_size = sizeof(enum wlfw_cal_temp_id_enum_v01), + .is_array = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct wlfw_bdf_download_req_msg_v01, + file_id), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x11, + .offset = offsetof(struct wlfw_bdf_download_req_msg_v01, + total_size_valid), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .is_array = NO_ARRAY, + .tlv_type = 0x11, + .offset = offsetof(struct wlfw_bdf_download_req_msg_v01, + total_size), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x12, + .offset = offsetof(struct wlfw_bdf_download_req_msg_v01, + seg_id_valid), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .is_array = NO_ARRAY, + .tlv_type = 0x12, + .offset = offsetof(struct wlfw_bdf_download_req_msg_v01, + seg_id), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x13, + .offset = offsetof(struct wlfw_bdf_download_req_msg_v01, + data_valid), + }, + { + .data_type = QMI_DATA_LEN, + .elem_len = 1, + .elem_size = sizeof(u16), + .is_array = NO_ARRAY, + .tlv_type = 0x13, + .offset = offsetof(struct wlfw_bdf_download_req_msg_v01, + data_len), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = QMI_WLFW_MAX_DATA_SIZE_V01, + .elem_size = sizeof(u8), + .is_array = VAR_LEN_ARRAY, + .tlv_type = 0x13, + .offset = offsetof(struct wlfw_bdf_download_req_msg_v01, + data), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x14, + .offset = offsetof(struct wlfw_bdf_download_req_msg_v01, + end_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x14, + .offset = offsetof(struct wlfw_bdf_download_req_msg_v01, + end), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x15, + .offset = offsetof(struct wlfw_bdf_download_req_msg_v01, + bdf_type_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x15, + .offset = offsetof(struct wlfw_bdf_download_req_msg_v01, + bdf_type), + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .is_array = QMI_COMMON_TLV_TYPE, + }, +}; + +struct elem_info wlfw_bdf_download_resp_msg_v01_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct qmi_response_type_v01), + .is_array = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct + wlfw_bdf_download_resp_msg_v01, + resp), + .ei_array = get_qmi_response_type_v01_ei(), + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .is_array = QMI_COMMON_TLV_TYPE, + }, +}; + +struct elem_info wlfw_cal_report_req_msg_v01_ei[] = { + { + .data_type = QMI_DATA_LEN, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x01, + .offset = offsetof(struct wlfw_cal_report_req_msg_v01, + meta_data_len), + }, + { + .data_type = QMI_SIGNED_4_BYTE_ENUM, + .elem_len = QMI_WLFW_MAX_NUM_CAL_V01, + .elem_size = sizeof(enum wlfw_cal_temp_id_enum_v01), + .is_array = VAR_LEN_ARRAY, + .tlv_type = 0x01, + .offset = offsetof(struct wlfw_cal_report_req_msg_v01, + meta_data), + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .is_array = QMI_COMMON_TLV_TYPE, + }, +}; + +struct elem_info wlfw_cal_report_resp_msg_v01_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct qmi_response_type_v01), + .is_array = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct wlfw_cal_report_resp_msg_v01, + resp), + .ei_array = get_qmi_response_type_v01_ei(), + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .is_array = QMI_COMMON_TLV_TYPE, + }, +}; + +struct elem_info wlfw_initiate_cal_download_ind_msg_v01_ei[] = { + { + .data_type = QMI_SIGNED_4_BYTE_ENUM, + .elem_len = 1, + .elem_size = sizeof(enum wlfw_cal_temp_id_enum_v01), + .is_array = NO_ARRAY, + .tlv_type = 0x01, + .offset = offsetof( + struct wlfw_initiate_cal_download_ind_msg_v01, + cal_id), + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .is_array = QMI_COMMON_TLV_TYPE, + }, +}; + +struct elem_info wlfw_cal_download_req_msg_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x01, + .offset = offsetof(struct wlfw_cal_download_req_msg_v01, + valid), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct wlfw_cal_download_req_msg_v01, + file_id_valid), + }, + { + .data_type = QMI_SIGNED_4_BYTE_ENUM, + .elem_len = 1, + .elem_size = sizeof(enum wlfw_cal_temp_id_enum_v01), + .is_array = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct wlfw_cal_download_req_msg_v01, + file_id), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x11, + .offset = offsetof(struct wlfw_cal_download_req_msg_v01, + total_size_valid), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .is_array = NO_ARRAY, + .tlv_type = 0x11, + .offset = offsetof(struct wlfw_cal_download_req_msg_v01, + total_size), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x12, + .offset = offsetof(struct wlfw_cal_download_req_msg_v01, + seg_id_valid), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .is_array = NO_ARRAY, + .tlv_type = 0x12, + .offset = offsetof(struct wlfw_cal_download_req_msg_v01, + seg_id), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x13, + .offset = offsetof(struct wlfw_cal_download_req_msg_v01, + data_valid), + }, + { + .data_type = QMI_DATA_LEN, + .elem_len = 1, + .elem_size = sizeof(u16), + .is_array = NO_ARRAY, + .tlv_type = 0x13, + .offset = offsetof(struct wlfw_cal_download_req_msg_v01, + data_len), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = QMI_WLFW_MAX_DATA_SIZE_V01, + .elem_size = sizeof(u8), + .is_array = VAR_LEN_ARRAY, + .tlv_type = 0x13, + .offset = offsetof(struct wlfw_cal_download_req_msg_v01, + data), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x14, + .offset = offsetof(struct wlfw_cal_download_req_msg_v01, + end_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x14, + .offset = offsetof(struct wlfw_cal_download_req_msg_v01, + end), + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .is_array = QMI_COMMON_TLV_TYPE, + }, +}; + +struct elem_info wlfw_cal_download_resp_msg_v01_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct qmi_response_type_v01), + .is_array = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct + wlfw_cal_download_resp_msg_v01, + resp), + .ei_array = get_qmi_response_type_v01_ei(), + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .is_array = QMI_COMMON_TLV_TYPE, + }, +}; + +struct elem_info wlfw_initiate_cal_update_ind_msg_v01_ei[] = { + { + .data_type = QMI_SIGNED_4_BYTE_ENUM, + .elem_len = 1, + .elem_size = sizeof(enum wlfw_cal_temp_id_enum_v01), + .is_array = NO_ARRAY, + .tlv_type = 0x01, + .offset = offsetof(struct + wlfw_initiate_cal_update_ind_msg_v01, + cal_id), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .is_array = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct + wlfw_initiate_cal_update_ind_msg_v01, + total_size), + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .is_array = QMI_COMMON_TLV_TYPE, + }, +}; + +struct elem_info wlfw_cal_update_req_msg_v01_ei[] = { + { + .data_type = QMI_SIGNED_4_BYTE_ENUM, + .elem_len = 1, + .elem_size = sizeof(enum wlfw_cal_temp_id_enum_v01), + .is_array = NO_ARRAY, + .tlv_type = 0x01, + .offset = offsetof(struct wlfw_cal_update_req_msg_v01, + cal_id), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .is_array = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct wlfw_cal_update_req_msg_v01, + seg_id), + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .is_array = QMI_COMMON_TLV_TYPE, + }, +}; + +struct elem_info wlfw_cal_update_resp_msg_v01_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct qmi_response_type_v01), + .is_array = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct wlfw_cal_update_resp_msg_v01, + resp), + .ei_array = get_qmi_response_type_v01_ei(), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct wlfw_cal_update_resp_msg_v01, + file_id_valid), + }, + { + .data_type = QMI_SIGNED_4_BYTE_ENUM, + .elem_len = 1, + .elem_size = sizeof(enum wlfw_cal_temp_id_enum_v01), + .is_array = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct wlfw_cal_update_resp_msg_v01, + file_id), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x11, + .offset = offsetof(struct wlfw_cal_update_resp_msg_v01, + total_size_valid), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .is_array = NO_ARRAY, + .tlv_type = 0x11, + .offset = offsetof(struct wlfw_cal_update_resp_msg_v01, + total_size), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x12, + .offset = offsetof(struct wlfw_cal_update_resp_msg_v01, + seg_id_valid), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .is_array = NO_ARRAY, + .tlv_type = 0x12, + .offset = offsetof(struct wlfw_cal_update_resp_msg_v01, + seg_id), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x13, + .offset = offsetof(struct wlfw_cal_update_resp_msg_v01, + data_valid), + }, + { + .data_type = QMI_DATA_LEN, + .elem_len = 1, + .elem_size = sizeof(u16), + .is_array = NO_ARRAY, + .tlv_type = 0x13, + .offset = offsetof(struct wlfw_cal_update_resp_msg_v01, + data_len), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = QMI_WLFW_MAX_DATA_SIZE_V01, + .elem_size = sizeof(u8), + .is_array = VAR_LEN_ARRAY, + .tlv_type = 0x13, + .offset = offsetof(struct wlfw_cal_update_resp_msg_v01, + data), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x14, + .offset = offsetof(struct wlfw_cal_update_resp_msg_v01, + end_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x14, + .offset = offsetof(struct wlfw_cal_update_resp_msg_v01, + end), + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .is_array = QMI_COMMON_TLV_TYPE, + }, +}; + +struct elem_info wlfw_msa_info_req_msg_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_8_BYTE, + .elem_len = 1, + .elem_size = sizeof(u64), + .is_array = NO_ARRAY, + .tlv_type = 0x01, + .offset = offsetof(struct wlfw_msa_info_req_msg_v01, + msa_addr), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .is_array = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct wlfw_msa_info_req_msg_v01, + size), + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .is_array = QMI_COMMON_TLV_TYPE, + }, +}; + +struct elem_info wlfw_msa_info_resp_msg_v01_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct qmi_response_type_v01), + .is_array = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct wlfw_msa_info_resp_msg_v01, + resp), + .ei_array = get_qmi_response_type_v01_ei(), + }, + { + .data_type = QMI_DATA_LEN, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x03, + .offset = offsetof(struct wlfw_msa_info_resp_msg_v01, + mem_region_info_len), + }, + { + .data_type = QMI_STRUCT, + .elem_len = QMI_WLFW_MAX_NUM_MEMORY_REGIONS_V01, + .elem_size = sizeof(struct wlfw_memory_region_info_s_v01), + .is_array = VAR_LEN_ARRAY, + .tlv_type = 0x03, + .offset = offsetof(struct wlfw_msa_info_resp_msg_v01, + mem_region_info), + .ei_array = wlfw_memory_region_info_s_v01_ei, + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .is_array = QMI_COMMON_TLV_TYPE, + }, +}; + +struct elem_info wlfw_msa_ready_req_msg_v01_ei[] = { + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .is_array = QMI_COMMON_TLV_TYPE, + }, +}; + +struct elem_info wlfw_msa_ready_resp_msg_v01_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct qmi_response_type_v01), + .is_array = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct wlfw_msa_ready_resp_msg_v01, + resp), + .ei_array = get_qmi_response_type_v01_ei(), + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .is_array = QMI_COMMON_TLV_TYPE, + }, +}; + +struct elem_info wlfw_ini_req_msg_v01_ei[] = { + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct wlfw_ini_req_msg_v01, + enablefwlog_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct wlfw_ini_req_msg_v01, + enablefwlog), + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .is_array = QMI_COMMON_TLV_TYPE, + }, +}; + +struct elem_info wlfw_ini_resp_msg_v01_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct qmi_response_type_v01), + .is_array = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct wlfw_ini_resp_msg_v01, + resp), + .ei_array = get_qmi_response_type_v01_ei(), + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .is_array = QMI_COMMON_TLV_TYPE, + }, +}; + +struct elem_info wlfw_athdiag_read_req_msg_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .is_array = NO_ARRAY, + .tlv_type = 0x01, + .offset = offsetof(struct wlfw_athdiag_read_req_msg_v01, + offset), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .is_array = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct wlfw_athdiag_read_req_msg_v01, + mem_type), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .is_array = NO_ARRAY, + .tlv_type = 0x03, + .offset = offsetof(struct wlfw_athdiag_read_req_msg_v01, + data_len), + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .is_array = QMI_COMMON_TLV_TYPE, + }, +}; + +struct elem_info wlfw_athdiag_read_resp_msg_v01_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct qmi_response_type_v01), + .is_array = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct + wlfw_athdiag_read_resp_msg_v01, + resp), + .ei_array = get_qmi_response_type_v01_ei(), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct + wlfw_athdiag_read_resp_msg_v01, + data_valid), + }, + { + .data_type = QMI_DATA_LEN, + .elem_len = 1, + .elem_size = sizeof(u16), + .is_array = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct + wlfw_athdiag_read_resp_msg_v01, + data_len), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = QMI_WLFW_MAX_ATHDIAG_DATA_SIZE_V01, + .elem_size = sizeof(u8), + .is_array = VAR_LEN_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct + wlfw_athdiag_read_resp_msg_v01, + data), + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .is_array = QMI_COMMON_TLV_TYPE, + }, +}; + +struct elem_info wlfw_athdiag_write_req_msg_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .is_array = NO_ARRAY, + .tlv_type = 0x01, + .offset = offsetof(struct + wlfw_athdiag_write_req_msg_v01, + offset), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .is_array = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct + wlfw_athdiag_write_req_msg_v01, + mem_type), + }, + { + .data_type = QMI_DATA_LEN, + .elem_len = 1, + .elem_size = sizeof(u16), + .is_array = NO_ARRAY, + .tlv_type = 0x03, + .offset = offsetof(struct + wlfw_athdiag_write_req_msg_v01, + data_len), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = QMI_WLFW_MAX_ATHDIAG_DATA_SIZE_V01, + .elem_size = sizeof(u8), + .is_array = VAR_LEN_ARRAY, + .tlv_type = 0x03, + .offset = offsetof(struct + wlfw_athdiag_write_req_msg_v01, + data), + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .is_array = QMI_COMMON_TLV_TYPE, + }, +}; + +struct elem_info wlfw_athdiag_write_resp_msg_v01_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct qmi_response_type_v01), + .is_array = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct + wlfw_athdiag_write_resp_msg_v01, + resp), + .ei_array = get_qmi_response_type_v01_ei(), + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .is_array = QMI_COMMON_TLV_TYPE, + }, +}; + +struct elem_info wlfw_vbatt_req_msg_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_8_BYTE, + .elem_len = 1, + .elem_size = sizeof(u64), + .is_array = NO_ARRAY, + .tlv_type = 0x01, + .offset = offsetof(struct wlfw_vbatt_req_msg_v01, + voltage_uv), + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .is_array = QMI_COMMON_TLV_TYPE, + }, +}; + +struct elem_info wlfw_vbatt_resp_msg_v01_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct qmi_response_type_v01), + .is_array = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct wlfw_vbatt_resp_msg_v01, + resp), + .ei_array = get_qmi_response_type_v01_ei(), + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .is_array = QMI_COMMON_TLV_TYPE, + }, +}; + +struct elem_info wlfw_mac_addr_req_msg_v01_ei[] = { + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct wlfw_mac_addr_req_msg_v01, + mac_addr_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = QMI_WLFW_MAC_ADDR_SIZE_V01, + .elem_size = sizeof(u8), + .is_array = STATIC_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct wlfw_mac_addr_req_msg_v01, + mac_addr), + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .is_array = QMI_COMMON_TLV_TYPE, + }, +}; + +struct elem_info wlfw_mac_addr_resp_msg_v01_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct qmi_response_type_v01), + .is_array = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct wlfw_mac_addr_resp_msg_v01, + resp), + .ei_array = get_qmi_response_type_v01_ei(), + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .is_array = QMI_COMMON_TLV_TYPE, + }, +}; + +struct elem_info wlfw_host_cap_req_msg_v01_ei[] = { + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct wlfw_host_cap_req_msg_v01, + daemon_support_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct wlfw_host_cap_req_msg_v01, + daemon_support), + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .is_array = QMI_COMMON_TLV_TYPE, + }, +}; + +struct elem_info wlfw_host_cap_resp_msg_v01_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct qmi_response_type_v01), + .is_array = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct wlfw_host_cap_resp_msg_v01, + resp), + .ei_array = get_qmi_response_type_v01_ei(), + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .is_array = QMI_COMMON_TLV_TYPE, + }, +}; + +struct elem_info wlfw_request_mem_ind_msg_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .is_array = NO_ARRAY, + .tlv_type = 0x01, + .offset = offsetof(struct wlfw_request_mem_ind_msg_v01, + size), + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .is_array = QMI_COMMON_TLV_TYPE, + }, +}; + +struct elem_info wlfw_respond_mem_req_msg_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_8_BYTE, + .elem_len = 1, + .elem_size = sizeof(u64), + .is_array = NO_ARRAY, + .tlv_type = 0x01, + .offset = offsetof(struct wlfw_respond_mem_req_msg_v01, + addr), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .is_array = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct wlfw_respond_mem_req_msg_v01, + size), + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .is_array = QMI_COMMON_TLV_TYPE, + }, +}; + +struct elem_info wlfw_respond_mem_resp_msg_v01_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct qmi_response_type_v01), + .is_array = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct wlfw_respond_mem_resp_msg_v01, + resp), + .ei_array = get_qmi_response_type_v01_ei(), + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .is_array = QMI_COMMON_TLV_TYPE, + }, +}; + +struct elem_info wlfw_fw_mem_ready_ind_msg_v01_ei[] = { + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .is_array = QMI_COMMON_TLV_TYPE, + }, +}; + +struct elem_info wlfw_cold_boot_cal_done_ind_msg_v01_ei[] = { + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .is_array = QMI_COMMON_TLV_TYPE, + }, +}; + +struct elem_info wlfw_rejuvenate_ind_msg_v01_ei[] = { + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct wlfw_rejuvenate_ind_msg_v01, + cause_for_rejuvenation_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct wlfw_rejuvenate_ind_msg_v01, + cause_for_rejuvenation), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x11, + .offset = offsetof(struct wlfw_rejuvenate_ind_msg_v01, + requesting_sub_system_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x11, + .offset = offsetof(struct wlfw_rejuvenate_ind_msg_v01, + requesting_sub_system), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x12, + .offset = offsetof(struct wlfw_rejuvenate_ind_msg_v01, + line_number_valid), + }, + { + .data_type = QMI_UNSIGNED_2_BYTE, + .elem_len = 1, + .elem_size = sizeof(u16), + .is_array = NO_ARRAY, + .tlv_type = 0x12, + .offset = offsetof(struct wlfw_rejuvenate_ind_msg_v01, + line_number), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x13, + .offset = offsetof(struct wlfw_rejuvenate_ind_msg_v01, + function_name_valid), + }, + { + .data_type = QMI_STRING, + .elem_len = QMI_WLFW_FUNCTION_NAME_LEN_V01 + 1, + .elem_size = sizeof(char), + .is_array = NO_ARRAY, + .tlv_type = 0x13, + .offset = offsetof(struct wlfw_rejuvenate_ind_msg_v01, + function_name), + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .is_array = QMI_COMMON_TLV_TYPE, + }, +}; + +struct elem_info wlfw_rejuvenate_ack_req_msg_v01_ei[] = { + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .is_array = QMI_COMMON_TLV_TYPE, + }, +}; + +struct elem_info wlfw_rejuvenate_ack_resp_msg_v01_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct qmi_response_type_v01), + .is_array = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct + wlfw_rejuvenate_ack_resp_msg_v01, + resp), + .ei_array = get_qmi_response_type_v01_ei(), + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .is_array = QMI_COMMON_TLV_TYPE, + }, +}; + +struct elem_info wlfw_dynamic_feature_mask_req_msg_v01_ei[] = { + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof( + struct wlfw_dynamic_feature_mask_req_msg_v01, + mask_valid), + }, + { + .data_type = QMI_UNSIGNED_8_BYTE, + .elem_len = 1, + .elem_size = sizeof(u64), + .is_array = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof( + struct wlfw_dynamic_feature_mask_req_msg_v01, + mask), + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .is_array = QMI_COMMON_TLV_TYPE, + }, +}; + +struct elem_info wlfw_dynamic_feature_mask_resp_msg_v01_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct qmi_response_type_v01), + .is_array = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof( + struct wlfw_dynamic_feature_mask_resp_msg_v01, + resp), + .ei_array = get_qmi_response_type_v01_ei(), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof( + struct wlfw_dynamic_feature_mask_resp_msg_v01, + prev_mask_valid), + }, + { + .data_type = QMI_UNSIGNED_8_BYTE, + .elem_len = 1, + .elem_size = sizeof(u64), + .is_array = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof( + struct wlfw_dynamic_feature_mask_resp_msg_v01, + prev_mask), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x11, + .offset = offsetof( + struct wlfw_dynamic_feature_mask_resp_msg_v01, + curr_mask_valid), + }, + { + .data_type = QMI_UNSIGNED_8_BYTE, + .elem_len = 1, + .elem_size = sizeof(u64), + .is_array = NO_ARRAY, + .tlv_type = 0x11, + .offset = offsetof( + struct wlfw_dynamic_feature_mask_resp_msg_v01, + curr_mask), + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .is_array = QMI_COMMON_TLV_TYPE, + }, +}; + +struct elem_info wlfw_m3_info_req_msg_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_8_BYTE, + .elem_len = 1, + .elem_size = sizeof(u64), + .is_array = NO_ARRAY, + .tlv_type = 0x01, + .offset = offsetof(struct wlfw_m3_info_req_msg_v01, + addr), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .is_array = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct wlfw_m3_info_req_msg_v01, + size), + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .is_array = QMI_COMMON_TLV_TYPE, + }, +}; + +struct elem_info wlfw_m3_info_resp_msg_v01_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct qmi_response_type_v01), + .is_array = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct wlfw_m3_info_resp_msg_v01, + resp), + .ei_array = get_qmi_response_type_v01_ei(), + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .is_array = QMI_COMMON_TLV_TYPE, + }, +}; diff --git a/drivers/net/wireless/cnss2/wlan_firmware_service_v01.h b/drivers/net/wireless/cnss2/wlan_firmware_service_v01.h new file mode 100644 index 000000000000..cb8225a7c3c3 --- /dev/null +++ b/drivers/net/wireless/cnss2/wlan_firmware_service_v01.h @@ -0,0 +1,645 @@ +/* Copyright (c) 2015-2017, The Linux Foundation. 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 version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef WLAN_FIRMWARE_SERVICE_V01_H +#define WLAN_FIRMWARE_SERVICE_V01_H + +#include <linux/qmi_encdec.h> +#include <soc/qcom/msm_qmi_interface.h> + +#define WLFW_SERVICE_ID_V01 0x45 +#define WLFW_SERVICE_VERS_V01 0x01 + +#define QMI_WLFW_BDF_DOWNLOAD_REQ_V01 0x0025 +#define QMI_WLFW_FW_MEM_READY_IND_V01 0x0037 +#define QMI_WLFW_INITIATE_CAL_UPDATE_IND_V01 0x002A +#define QMI_WLFW_HOST_CAP_REQ_V01 0x0034 +#define QMI_WLFW_DYNAMIC_FEATURE_MASK_RESP_V01 0x003B +#define QMI_WLFW_M3_INFO_REQ_V01 0x003C +#define QMI_WLFW_CAP_REQ_V01 0x0024 +#define QMI_WLFW_CAL_REPORT_REQ_V01 0x0026 +#define QMI_WLFW_M3_INFO_RESP_V01 0x003C +#define QMI_WLFW_CAL_UPDATE_RESP_V01 0x0029 +#define QMI_WLFW_CAL_DOWNLOAD_RESP_V01 0x0027 +#define QMI_WLFW_INI_RESP_V01 0x002F +#define QMI_WLFW_CAL_REPORT_RESP_V01 0x0026 +#define QMI_WLFW_MAC_ADDR_RESP_V01 0x0033 +#define QMI_WLFW_INITIATE_CAL_DOWNLOAD_IND_V01 0x0028 +#define QMI_WLFW_HOST_CAP_RESP_V01 0x0034 +#define QMI_WLFW_MSA_READY_IND_V01 0x002B +#define QMI_WLFW_ATHDIAG_WRITE_RESP_V01 0x0031 +#define QMI_WLFW_WLAN_MODE_REQ_V01 0x0022 +#define QMI_WLFW_IND_REGISTER_REQ_V01 0x0020 +#define QMI_WLFW_WLAN_CFG_RESP_V01 0x0023 +#define QMI_WLFW_COLD_BOOT_CAL_DONE_IND_V01 0x0038 +#define QMI_WLFW_REQUEST_MEM_IND_V01 0x0035 +#define QMI_WLFW_REJUVENATE_IND_V01 0x0039 +#define QMI_WLFW_DYNAMIC_FEATURE_MASK_REQ_V01 0x003B +#define QMI_WLFW_ATHDIAG_WRITE_REQ_V01 0x0031 +#define QMI_WLFW_WLAN_MODE_RESP_V01 0x0022 +#define QMI_WLFW_RESPOND_MEM_REQ_V01 0x0036 +#define QMI_WLFW_PIN_CONNECT_RESULT_IND_V01 0x002C +#define QMI_WLFW_FW_READY_IND_V01 0x0021 +#define QMI_WLFW_MSA_READY_RESP_V01 0x002E +#define QMI_WLFW_CAL_UPDATE_REQ_V01 0x0029 +#define QMI_WLFW_INI_REQ_V01 0x002F +#define QMI_WLFW_BDF_DOWNLOAD_RESP_V01 0x0025 +#define QMI_WLFW_REJUVENATE_ACK_RESP_V01 0x003A +#define QMI_WLFW_MSA_INFO_RESP_V01 0x002D +#define QMI_WLFW_MSA_READY_REQ_V01 0x002E +#define QMI_WLFW_CAP_RESP_V01 0x0024 +#define QMI_WLFW_REJUVENATE_ACK_REQ_V01 0x003A +#define QMI_WLFW_ATHDIAG_READ_RESP_V01 0x0030 +#define QMI_WLFW_VBATT_REQ_V01 0x0032 +#define QMI_WLFW_MAC_ADDR_REQ_V01 0x0033 +#define QMI_WLFW_RESPOND_MEM_RESP_V01 0x0036 +#define QMI_WLFW_VBATT_RESP_V01 0x0032 +#define QMI_WLFW_MSA_INFO_REQ_V01 0x002D +#define QMI_WLFW_CAL_DOWNLOAD_REQ_V01 0x0027 +#define QMI_WLFW_ATHDIAG_READ_REQ_V01 0x0030 +#define QMI_WLFW_WLAN_CFG_REQ_V01 0x0023 +#define QMI_WLFW_IND_REGISTER_RESP_V01 0x0020 + +#define QMI_WLFW_MAX_NUM_MEMORY_REGIONS_V01 2 +#define QMI_WLFW_MAX_NUM_CAL_V01 5 +#define QMI_WLFW_MAX_DATA_SIZE_V01 6144 +#define QMI_WLFW_FUNCTION_NAME_LEN_V01 128 +#define QMI_WLFW_MAX_NUM_CE_V01 12 +#define QMI_WLFW_MAX_TIMESTAMP_LEN_V01 32 +#define QMI_WLFW_MAX_ATHDIAG_DATA_SIZE_V01 512 +#define QMI_WLFW_MAX_BUILD_ID_LEN_V01 128 +#define QMI_WLFW_MAX_STR_LEN_V01 16 +#define QMI_WLFW_MAX_NUM_SHADOW_REG_V01 24 +#define QMI_WLFW_MAC_ADDR_SIZE_V01 6 +#define QMI_WLFW_MAX_NUM_SHADOW_REG_V2_V01 36 +#define QMI_WLFW_MAX_NUM_SVC_V01 24 + +enum wlfw_driver_mode_enum_v01 { + WLFW_DRIVER_MODE_ENUM_MIN_VAL_V01 = INT_MIN, + QMI_WLFW_MISSION_V01 = 0, + QMI_WLFW_FTM_V01 = 1, + QMI_WLFW_EPPING_V01 = 2, + QMI_WLFW_WALTEST_V01 = 3, + QMI_WLFW_OFF_V01 = 4, + QMI_WLFW_CCPM_V01 = 5, + QMI_WLFW_QVIT_V01 = 6, + QMI_WLFW_CALIBRATION_V01 = 7, + WLFW_DRIVER_MODE_ENUM_MAX_VAL_V01 = INT_MAX, +}; + +enum wlfw_cal_temp_id_enum_v01 { + WLFW_CAL_TEMP_ID_ENUM_MIN_VAL_V01 = INT_MIN, + QMI_WLFW_CAL_TEMP_IDX_0_V01 = 0, + QMI_WLFW_CAL_TEMP_IDX_1_V01 = 1, + QMI_WLFW_CAL_TEMP_IDX_2_V01 = 2, + QMI_WLFW_CAL_TEMP_IDX_3_V01 = 3, + QMI_WLFW_CAL_TEMP_IDX_4_V01 = 4, + WLFW_CAL_TEMP_ID_ENUM_MAX_VAL_V01 = INT_MAX, +}; + +enum wlfw_pipedir_enum_v01 { + WLFW_PIPEDIR_ENUM_MIN_VAL_V01 = INT_MIN, + QMI_WLFW_PIPEDIR_NONE_V01 = 0, + QMI_WLFW_PIPEDIR_IN_V01 = 1, + QMI_WLFW_PIPEDIR_OUT_V01 = 2, + QMI_WLFW_PIPEDIR_INOUT_V01 = 3, + WLFW_PIPEDIR_ENUM_MAX_VAL_V01 = INT_MAX, +}; + +#define QMI_WLFW_CE_ATTR_FLAGS_V01 ((u32)0x00) +#define QMI_WLFW_CE_ATTR_NO_SNOOP_V01 ((u32)0x01) +#define QMI_WLFW_CE_ATTR_BYTE_SWAP_DATA_V01 ((u32)0x02) +#define QMI_WLFW_CE_ATTR_SWIZZLE_DESCRIPTORS_V01 ((u32)0x04) +#define QMI_WLFW_CE_ATTR_DISABLE_INTR_V01 ((u32)0x08) +#define QMI_WLFW_CE_ATTR_ENABLE_POLL_V01 ((u32)0x10) + +#define QMI_WLFW_ALREADY_REGISTERED_V01 ((u64)0x01ULL) +#define QMI_WLFW_FW_READY_V01 ((u64)0x02ULL) +#define QMI_WLFW_MSA_READY_V01 ((u64)0x04ULL) +#define QMI_WLFW_FW_MEM_READY_V01 ((u64)0x08ULL) + +#define QMI_WLFW_FW_REJUVENATE_V01 ((u64)0x01ULL) + +struct wlfw_ce_tgt_pipe_cfg_s_v01 { + u32 pipe_num; + enum wlfw_pipedir_enum_v01 pipe_dir; + u32 nentries; + u32 nbytes_max; + u32 flags; +}; + +struct wlfw_ce_svc_pipe_cfg_s_v01 { + u32 service_id; + enum wlfw_pipedir_enum_v01 pipe_dir; + u32 pipe_num; +}; + +struct wlfw_shadow_reg_cfg_s_v01 { + u16 id; + u16 offset; +}; + +struct wlfw_shadow_reg_v2_cfg_s_v01 { + u32 addr; +}; + +struct wlfw_memory_region_info_s_v01 { + u64 region_addr; + u32 size; + u8 secure_flag; +}; + +struct wlfw_rf_chip_info_s_v01 { + u32 chip_id; + u32 chip_family; +}; + +struct wlfw_rf_board_info_s_v01 { + u32 board_id; +}; + +struct wlfw_soc_info_s_v01 { + u32 soc_id; +}; + +struct wlfw_fw_version_info_s_v01 { + u32 fw_version; + char fw_build_timestamp[QMI_WLFW_MAX_TIMESTAMP_LEN_V01 + 1]; +}; + +struct wlfw_ind_register_req_msg_v01 { + u8 fw_ready_enable_valid; + u8 fw_ready_enable; + u8 initiate_cal_download_enable_valid; + u8 initiate_cal_download_enable; + u8 initiate_cal_update_enable_valid; + u8 initiate_cal_update_enable; + u8 msa_ready_enable_valid; + u8 msa_ready_enable; + u8 pin_connect_result_enable_valid; + u8 pin_connect_result_enable; + u8 client_id_valid; + u32 client_id; + u8 request_mem_enable_valid; + u8 request_mem_enable; + u8 fw_mem_ready_enable_valid; + u8 fw_mem_ready_enable; + u8 cold_boot_cal_done_enable_valid; + u8 cold_boot_cal_done_enable; + u8 rejuvenate_enable_valid; + u32 rejuvenate_enable; +}; + +#define WLFW_IND_REGISTER_REQ_MSG_V01_MAX_MSG_LEN 46 +extern struct elem_info wlfw_ind_register_req_msg_v01_ei[]; + +struct wlfw_ind_register_resp_msg_v01 { + struct qmi_response_type_v01 resp; + u8 fw_status_valid; + u64 fw_status; +}; + +#define WLFW_IND_REGISTER_RESP_MSG_V01_MAX_MSG_LEN 18 +extern struct elem_info wlfw_ind_register_resp_msg_v01_ei[]; + +struct wlfw_fw_ready_ind_msg_v01 { + char placeholder; +}; + +#define WLFW_FW_READY_IND_MSG_V01_MAX_MSG_LEN 0 +extern struct elem_info wlfw_fw_ready_ind_msg_v01_ei[]; + +struct wlfw_msa_ready_ind_msg_v01 { + char placeholder; +}; + +#define WLFW_MSA_READY_IND_MSG_V01_MAX_MSG_LEN 0 +extern struct elem_info wlfw_msa_ready_ind_msg_v01_ei[]; + +struct wlfw_pin_connect_result_ind_msg_v01 { + u8 pwr_pin_result_valid; + u32 pwr_pin_result; + u8 phy_io_pin_result_valid; + u32 phy_io_pin_result; + u8 rf_pin_result_valid; + u32 rf_pin_result; +}; + +#define WLFW_PIN_CONNECT_RESULT_IND_MSG_V01_MAX_MSG_LEN 21 +extern struct elem_info wlfw_pin_connect_result_ind_msg_v01_ei[]; + +struct wlfw_wlan_mode_req_msg_v01 { + enum wlfw_driver_mode_enum_v01 mode; + u8 hw_debug_valid; + u8 hw_debug; +}; + +#define WLFW_WLAN_MODE_REQ_MSG_V01_MAX_MSG_LEN 11 +extern struct elem_info wlfw_wlan_mode_req_msg_v01_ei[]; + +struct wlfw_wlan_mode_resp_msg_v01 { + struct qmi_response_type_v01 resp; +}; + +#define WLFW_WLAN_MODE_RESP_MSG_V01_MAX_MSG_LEN 7 +extern struct elem_info wlfw_wlan_mode_resp_msg_v01_ei[]; + +struct wlfw_wlan_cfg_req_msg_v01 { + u8 host_version_valid; + char host_version[QMI_WLFW_MAX_STR_LEN_V01 + 1]; + u8 tgt_cfg_valid; + u32 tgt_cfg_len; + struct wlfw_ce_tgt_pipe_cfg_s_v01 tgt_cfg[QMI_WLFW_MAX_NUM_CE_V01]; + u8 svc_cfg_valid; + u32 svc_cfg_len; + struct wlfw_ce_svc_pipe_cfg_s_v01 svc_cfg[QMI_WLFW_MAX_NUM_SVC_V01]; + u8 shadow_reg_valid; + u32 shadow_reg_len; + struct wlfw_shadow_reg_cfg_s_v01 + shadow_reg[QMI_WLFW_MAX_NUM_SHADOW_REG_V01]; + u8 shadow_reg_v2_valid; + u32 shadow_reg_v2_len; + struct wlfw_shadow_reg_v2_cfg_s_v01 + shadow_reg_v2[QMI_WLFW_MAX_NUM_SHADOW_REG_V2_V01]; +}; + +#define WLFW_WLAN_CFG_REQ_MSG_V01_MAX_MSG_LEN 803 +extern struct elem_info wlfw_wlan_cfg_req_msg_v01_ei[]; + +struct wlfw_wlan_cfg_resp_msg_v01 { + struct qmi_response_type_v01 resp; +}; + +#define WLFW_WLAN_CFG_RESP_MSG_V01_MAX_MSG_LEN 7 +extern struct elem_info wlfw_wlan_cfg_resp_msg_v01_ei[]; + +struct wlfw_cap_req_msg_v01 { + char placeholder; +}; + +#define WLFW_CAP_REQ_MSG_V01_MAX_MSG_LEN 0 +extern struct elem_info wlfw_cap_req_msg_v01_ei[]; + +struct wlfw_cap_resp_msg_v01 { + struct qmi_response_type_v01 resp; + u8 chip_info_valid; + struct wlfw_rf_chip_info_s_v01 chip_info; + u8 board_info_valid; + struct wlfw_rf_board_info_s_v01 board_info; + u8 soc_info_valid; + struct wlfw_soc_info_s_v01 soc_info; + u8 fw_version_info_valid; + struct wlfw_fw_version_info_s_v01 fw_version_info; + u8 fw_build_id_valid; + char fw_build_id[QMI_WLFW_MAX_BUILD_ID_LEN_V01 + 1]; + u8 num_macs_valid; + u8 num_macs; +}; + +#define WLFW_CAP_RESP_MSG_V01_MAX_MSG_LEN 207 +extern struct elem_info wlfw_cap_resp_msg_v01_ei[]; + +struct wlfw_bdf_download_req_msg_v01 { + u8 valid; + u8 file_id_valid; + enum wlfw_cal_temp_id_enum_v01 file_id; + u8 total_size_valid; + u32 total_size; + u8 seg_id_valid; + u32 seg_id; + u8 data_valid; + u32 data_len; + u8 data[QMI_WLFW_MAX_DATA_SIZE_V01]; + u8 end_valid; + u8 end; + u8 bdf_type_valid; + u8 bdf_type; +}; + +#define WLFW_BDF_DOWNLOAD_REQ_MSG_V01_MAX_MSG_LEN 6182 +extern struct elem_info wlfw_bdf_download_req_msg_v01_ei[]; + +struct wlfw_bdf_download_resp_msg_v01 { + struct qmi_response_type_v01 resp; +}; + +#define WLFW_BDF_DOWNLOAD_RESP_MSG_V01_MAX_MSG_LEN 7 +extern struct elem_info wlfw_bdf_download_resp_msg_v01_ei[]; + +struct wlfw_cal_report_req_msg_v01 { + u32 meta_data_len; + enum wlfw_cal_temp_id_enum_v01 meta_data[QMI_WLFW_MAX_NUM_CAL_V01]; +}; + +#define WLFW_CAL_REPORT_REQ_MSG_V01_MAX_MSG_LEN 24 +extern struct elem_info wlfw_cal_report_req_msg_v01_ei[]; + +struct wlfw_cal_report_resp_msg_v01 { + struct qmi_response_type_v01 resp; +}; + +#define WLFW_CAL_REPORT_RESP_MSG_V01_MAX_MSG_LEN 7 +extern struct elem_info wlfw_cal_report_resp_msg_v01_ei[]; + +struct wlfw_initiate_cal_download_ind_msg_v01 { + enum wlfw_cal_temp_id_enum_v01 cal_id; +}; + +#define WLFW_INITIATE_CAL_DOWNLOAD_IND_MSG_V01_MAX_MSG_LEN 7 +extern struct elem_info wlfw_initiate_cal_download_ind_msg_v01_ei[]; + +struct wlfw_cal_download_req_msg_v01 { + u8 valid; + u8 file_id_valid; + enum wlfw_cal_temp_id_enum_v01 file_id; + u8 total_size_valid; + u32 total_size; + u8 seg_id_valid; + u32 seg_id; + u8 data_valid; + u32 data_len; + u8 data[QMI_WLFW_MAX_DATA_SIZE_V01]; + u8 end_valid; + u8 end; +}; + +#define WLFW_CAL_DOWNLOAD_REQ_MSG_V01_MAX_MSG_LEN 6178 +extern struct elem_info wlfw_cal_download_req_msg_v01_ei[]; + +struct wlfw_cal_download_resp_msg_v01 { + struct qmi_response_type_v01 resp; +}; + +#define WLFW_CAL_DOWNLOAD_RESP_MSG_V01_MAX_MSG_LEN 7 +extern struct elem_info wlfw_cal_download_resp_msg_v01_ei[]; + +struct wlfw_initiate_cal_update_ind_msg_v01 { + enum wlfw_cal_temp_id_enum_v01 cal_id; + u32 total_size; +}; + +#define WLFW_INITIATE_CAL_UPDATE_IND_MSG_V01_MAX_MSG_LEN 14 +extern struct elem_info wlfw_initiate_cal_update_ind_msg_v01_ei[]; + +struct wlfw_cal_update_req_msg_v01 { + enum wlfw_cal_temp_id_enum_v01 cal_id; + u32 seg_id; +}; + +#define WLFW_CAL_UPDATE_REQ_MSG_V01_MAX_MSG_LEN 14 +extern struct elem_info wlfw_cal_update_req_msg_v01_ei[]; + +struct wlfw_cal_update_resp_msg_v01 { + struct qmi_response_type_v01 resp; + u8 file_id_valid; + enum wlfw_cal_temp_id_enum_v01 file_id; + u8 total_size_valid; + u32 total_size; + u8 seg_id_valid; + u32 seg_id; + u8 data_valid; + u32 data_len; + u8 data[QMI_WLFW_MAX_DATA_SIZE_V01]; + u8 end_valid; + u8 end; +}; + +#define WLFW_CAL_UPDATE_RESP_MSG_V01_MAX_MSG_LEN 6181 +extern struct elem_info wlfw_cal_update_resp_msg_v01_ei[]; + +struct wlfw_msa_info_req_msg_v01 { + u64 msa_addr; + u32 size; +}; + +#define WLFW_MSA_INFO_REQ_MSG_V01_MAX_MSG_LEN 18 +extern struct elem_info wlfw_msa_info_req_msg_v01_ei[]; + +struct wlfw_msa_info_resp_msg_v01 { + struct qmi_response_type_v01 resp; + u32 mem_region_info_len; + struct wlfw_memory_region_info_s_v01 + mem_region_info[QMI_WLFW_MAX_NUM_MEMORY_REGIONS_V01]; +}; + +#define WLFW_MSA_INFO_RESP_MSG_V01_MAX_MSG_LEN 37 +extern struct elem_info wlfw_msa_info_resp_msg_v01_ei[]; + +struct wlfw_msa_ready_req_msg_v01 { + char placeholder; +}; + +#define WLFW_MSA_READY_REQ_MSG_V01_MAX_MSG_LEN 0 +extern struct elem_info wlfw_msa_ready_req_msg_v01_ei[]; + +struct wlfw_msa_ready_resp_msg_v01 { + struct qmi_response_type_v01 resp; +}; + +#define WLFW_MSA_READY_RESP_MSG_V01_MAX_MSG_LEN 7 +extern struct elem_info wlfw_msa_ready_resp_msg_v01_ei[]; + +struct wlfw_ini_req_msg_v01 { + u8 enablefwlog_valid; + u8 enablefwlog; +}; + +#define WLFW_INI_REQ_MSG_V01_MAX_MSG_LEN 4 +extern struct elem_info wlfw_ini_req_msg_v01_ei[]; + +struct wlfw_ini_resp_msg_v01 { + struct qmi_response_type_v01 resp; +}; + +#define WLFW_INI_RESP_MSG_V01_MAX_MSG_LEN 7 +extern struct elem_info wlfw_ini_resp_msg_v01_ei[]; + +struct wlfw_athdiag_read_req_msg_v01 { + u32 offset; + u32 mem_type; + u32 data_len; +}; + +#define WLFW_ATHDIAG_READ_REQ_MSG_V01_MAX_MSG_LEN 21 +extern struct elem_info wlfw_athdiag_read_req_msg_v01_ei[]; + +struct wlfw_athdiag_read_resp_msg_v01 { + struct qmi_response_type_v01 resp; + u8 data_valid; + u32 data_len; + u8 data[QMI_WLFW_MAX_ATHDIAG_DATA_SIZE_V01]; +}; + +#define WLFW_ATHDIAG_READ_RESP_MSG_V01_MAX_MSG_LEN 524 +extern struct elem_info wlfw_athdiag_read_resp_msg_v01_ei[]; + +struct wlfw_athdiag_write_req_msg_v01 { + u32 offset; + u32 mem_type; + u32 data_len; + u8 data[QMI_WLFW_MAX_ATHDIAG_DATA_SIZE_V01]; +}; + +#define WLFW_ATHDIAG_WRITE_REQ_MSG_V01_MAX_MSG_LEN 531 +extern struct elem_info wlfw_athdiag_write_req_msg_v01_ei[]; + +struct wlfw_athdiag_write_resp_msg_v01 { + struct qmi_response_type_v01 resp; +}; + +#define WLFW_ATHDIAG_WRITE_RESP_MSG_V01_MAX_MSG_LEN 7 +extern struct elem_info wlfw_athdiag_write_resp_msg_v01_ei[]; + +struct wlfw_vbatt_req_msg_v01 { + u64 voltage_uv; +}; + +#define WLFW_VBATT_REQ_MSG_V01_MAX_MSG_LEN 11 +extern struct elem_info wlfw_vbatt_req_msg_v01_ei[]; + +struct wlfw_vbatt_resp_msg_v01 { + struct qmi_response_type_v01 resp; +}; + +#define WLFW_VBATT_RESP_MSG_V01_MAX_MSG_LEN 7 +extern struct elem_info wlfw_vbatt_resp_msg_v01_ei[]; + +struct wlfw_mac_addr_req_msg_v01 { + u8 mac_addr_valid; + u8 mac_addr[QMI_WLFW_MAC_ADDR_SIZE_V01]; +}; + +#define WLFW_MAC_ADDR_REQ_MSG_V01_MAX_MSG_LEN 9 +extern struct elem_info wlfw_mac_addr_req_msg_v01_ei[]; + +struct wlfw_mac_addr_resp_msg_v01 { + struct qmi_response_type_v01 resp; +}; + +#define WLFW_MAC_ADDR_RESP_MSG_V01_MAX_MSG_LEN 7 +extern struct elem_info wlfw_mac_addr_resp_msg_v01_ei[]; + +struct wlfw_host_cap_req_msg_v01 { + u8 daemon_support_valid; + u8 daemon_support; +}; + +#define WLFW_HOST_CAP_REQ_MSG_V01_MAX_MSG_LEN 4 +extern struct elem_info wlfw_host_cap_req_msg_v01_ei[]; + +struct wlfw_host_cap_resp_msg_v01 { + struct qmi_response_type_v01 resp; +}; + +#define WLFW_HOST_CAP_RESP_MSG_V01_MAX_MSG_LEN 7 +extern struct elem_info wlfw_host_cap_resp_msg_v01_ei[]; + +struct wlfw_request_mem_ind_msg_v01 { + u32 size; +}; + +#define WLFW_REQUEST_MEM_IND_MSG_V01_MAX_MSG_LEN 7 +extern struct elem_info wlfw_request_mem_ind_msg_v01_ei[]; + +struct wlfw_respond_mem_req_msg_v01 { + u64 addr; + u32 size; +}; + +#define WLFW_RESPOND_MEM_REQ_MSG_V01_MAX_MSG_LEN 18 +extern struct elem_info wlfw_respond_mem_req_msg_v01_ei[]; + +struct wlfw_respond_mem_resp_msg_v01 { + struct qmi_response_type_v01 resp; +}; + +#define WLFW_RESPOND_MEM_RESP_MSG_V01_MAX_MSG_LEN 7 +extern struct elem_info wlfw_respond_mem_resp_msg_v01_ei[]; + +struct wlfw_fw_mem_ready_ind_msg_v01 { + char placeholder; +}; + +#define WLFW_FW_MEM_READY_IND_MSG_V01_MAX_MSG_LEN 0 +extern struct elem_info wlfw_fw_mem_ready_ind_msg_v01_ei[]; + +struct wlfw_cold_boot_cal_done_ind_msg_v01 { + char placeholder; +}; + +#define WLFW_COLD_BOOT_CAL_DONE_IND_MSG_V01_MAX_MSG_LEN 0 +extern struct elem_info wlfw_cold_boot_cal_done_ind_msg_v01_ei[]; + +struct wlfw_rejuvenate_ind_msg_v01 { + u8 cause_for_rejuvenation_valid; + u8 cause_for_rejuvenation; + u8 requesting_sub_system_valid; + u8 requesting_sub_system; + u8 line_number_valid; + u16 line_number; + u8 function_name_valid; + char function_name[QMI_WLFW_FUNCTION_NAME_LEN_V01 + 1]; +}; + +#define WLFW_REJUVENATE_IND_MSG_V01_MAX_MSG_LEN 144 +extern struct elem_info wlfw_rejuvenate_ind_msg_v01_ei[]; + +struct wlfw_rejuvenate_ack_req_msg_v01 { + char placeholder; +}; + +#define WLFW_REJUVENATE_ACK_REQ_MSG_V01_MAX_MSG_LEN 0 +extern struct elem_info wlfw_rejuvenate_ack_req_msg_v01_ei[]; + +struct wlfw_rejuvenate_ack_resp_msg_v01 { + struct qmi_response_type_v01 resp; +}; + +#define WLFW_REJUVENATE_ACK_RESP_MSG_V01_MAX_MSG_LEN 7 +extern struct elem_info wlfw_rejuvenate_ack_resp_msg_v01_ei[]; + +struct wlfw_dynamic_feature_mask_req_msg_v01 { + u8 mask_valid; + u64 mask; +}; + +#define WLFW_DYNAMIC_FEATURE_MASK_REQ_MSG_V01_MAX_MSG_LEN 11 +extern struct elem_info wlfw_dynamic_feature_mask_req_msg_v01_ei[]; + +struct wlfw_dynamic_feature_mask_resp_msg_v01 { + struct qmi_response_type_v01 resp; + u8 prev_mask_valid; + u64 prev_mask; + u8 curr_mask_valid; + u64 curr_mask; +}; + +#define WLFW_DYNAMIC_FEATURE_MASK_RESP_MSG_V01_MAX_MSG_LEN 29 +extern struct elem_info wlfw_dynamic_feature_mask_resp_msg_v01_ei[]; + +struct wlfw_m3_info_req_msg_v01 { + u64 addr; + u32 size; +}; + +#define WLFW_M3_INFO_REQ_MSG_V01_MAX_MSG_LEN 18 +extern struct elem_info wlfw_m3_info_req_msg_v01_ei[]; + +struct wlfw_m3_info_resp_msg_v01 { + struct qmi_response_type_v01 resp; +}; + +#define WLFW_M3_INFO_RESP_MSG_V01_MAX_MSG_LEN 7 +extern struct elem_info wlfw_m3_info_resp_msg_v01_ei[]; + +#endif diff --git a/drivers/net/wireless/cnss_utils/Kconfig b/drivers/net/wireless/cnss_utils/Kconfig new file mode 100644 index 000000000000..5f43e4872d65 --- /dev/null +++ b/drivers/net/wireless/cnss_utils/Kconfig @@ -0,0 +1,6 @@ +config CNSS_UTILS + bool "CNSS utilities support" + ---help--- + Add CNSS utilities support for the WLAN driver module. + This feature enable wlan driver to use CNSS utilities APIs to set + and get wlan related information.
\ No newline at end of file diff --git a/drivers/net/wireless/cnss_utils/Makefile b/drivers/net/wireless/cnss_utils/Makefile new file mode 100644 index 000000000000..0d1ed7ae939e --- /dev/null +++ b/drivers/net/wireless/cnss_utils/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_CNSS_UTILS) += cnss_utils.o diff --git a/drivers/net/wireless/cnss_utils/cnss_utils.c b/drivers/net/wireless/cnss_utils/cnss_utils.c new file mode 100644 index 000000000000..d73846efbc4c --- /dev/null +++ b/drivers/net/wireless/cnss_utils/cnss_utils.c @@ -0,0 +1,310 @@ +/* Copyright (c) 2017 The Linux Foundation. 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 version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define pr_fmt(fmt) "cnss_utils: " fmt + +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/etherdevice.h> +#include <net/cnss_utils.h> + +#define CNSS_MAX_CH_NUM 45 +struct cnss_unsafe_channel_list { + u16 unsafe_ch_count; + u16 unsafe_ch_list[CNSS_MAX_CH_NUM]; +}; + +struct cnss_dfs_nol_info { + void *dfs_nol_info; + u16 dfs_nol_info_len; +}; + +#define MAX_NO_OF_MAC_ADDR 4 +struct cnss_wlan_mac_addr { + u8 mac_addr[MAX_NO_OF_MAC_ADDR][ETH_ALEN]; + u32 no_of_mac_addr_set; +}; + +static struct cnss_utils_priv { + struct cnss_unsafe_channel_list unsafe_channel_list; + struct cnss_dfs_nol_info dfs_nol_info; + /* generic mutex for unsafe channel */ + struct mutex unsafe_channel_list_lock; + /* generic spin-lock for dfs_nol info */ + spinlock_t dfs_nol_info_lock; + int driver_load_cnt; + bool is_wlan_mac_set; + struct cnss_wlan_mac_addr wlan_mac_addr; + enum cnss_utils_cc_src cc_source; +} *cnss_utils_priv; + +int cnss_utils_set_wlan_unsafe_channel(struct device *dev, + u16 *unsafe_ch_list, u16 ch_count) +{ + struct cnss_utils_priv *priv = cnss_utils_priv; + + if (!priv) + return -EINVAL; + + mutex_lock(&priv->unsafe_channel_list_lock); + if ((!unsafe_ch_list) || (ch_count > CNSS_MAX_CH_NUM)) { + mutex_unlock(&priv->unsafe_channel_list_lock); + return -EINVAL; + } + + priv->unsafe_channel_list.unsafe_ch_count = ch_count; + + if (ch_count == 0) + goto end; + + memcpy(priv->unsafe_channel_list.unsafe_ch_list, + unsafe_ch_list, ch_count * sizeof(u16)); + +end: + mutex_unlock(&priv->unsafe_channel_list_lock); + + return 0; +} +EXPORT_SYMBOL(cnss_utils_set_wlan_unsafe_channel); + +int cnss_utils_get_wlan_unsafe_channel(struct device *dev, + u16 *unsafe_ch_list, + u16 *ch_count, u16 buf_len) +{ + struct cnss_utils_priv *priv = cnss_utils_priv; + + if (!priv) + return -EINVAL; + + mutex_lock(&priv->unsafe_channel_list_lock); + if (!unsafe_ch_list || !ch_count) { + mutex_unlock(&priv->unsafe_channel_list_lock); + return -EINVAL; + } + + if (buf_len < + (priv->unsafe_channel_list.unsafe_ch_count * sizeof(u16))) { + mutex_unlock(&priv->unsafe_channel_list_lock); + return -ENOMEM; + } + + *ch_count = priv->unsafe_channel_list.unsafe_ch_count; + memcpy(unsafe_ch_list, priv->unsafe_channel_list.unsafe_ch_list, + priv->unsafe_channel_list.unsafe_ch_count * sizeof(u16)); + mutex_unlock(&priv->unsafe_channel_list_lock); + + return 0; +} +EXPORT_SYMBOL(cnss_utils_get_wlan_unsafe_channel); + +int cnss_utils_wlan_set_dfs_nol(struct device *dev, + const void *info, u16 info_len) +{ + void *temp; + void *old_nol_info; + struct cnss_dfs_nol_info *dfs_info; + struct cnss_utils_priv *priv = cnss_utils_priv; + + if (!priv) + return -EINVAL; + + if (!info || !info_len) + return -EINVAL; + + temp = kmalloc(info_len, GFP_ATOMIC); + if (!temp) + return -ENOMEM; + + memcpy(temp, info, info_len); + spin_lock_bh(&priv->dfs_nol_info_lock); + dfs_info = &priv->dfs_nol_info; + old_nol_info = dfs_info->dfs_nol_info; + dfs_info->dfs_nol_info = temp; + dfs_info->dfs_nol_info_len = info_len; + spin_unlock_bh(&priv->dfs_nol_info_lock); + kfree(old_nol_info); + + return 0; +} +EXPORT_SYMBOL(cnss_utils_wlan_set_dfs_nol); + +int cnss_utils_wlan_get_dfs_nol(struct device *dev, + void *info, u16 info_len) +{ + int len; + struct cnss_dfs_nol_info *dfs_info; + struct cnss_utils_priv *priv = cnss_utils_priv; + + if (!priv) + return -EINVAL; + + if (!info || !info_len) + return -EINVAL; + + spin_lock_bh(&priv->dfs_nol_info_lock); + + dfs_info = &priv->dfs_nol_info; + if (!dfs_info->dfs_nol_info || + dfs_info->dfs_nol_info_len == 0) { + spin_unlock_bh(&priv->dfs_nol_info_lock); + return -ENOENT; + } + + len = min(info_len, dfs_info->dfs_nol_info_len); + memcpy(info, dfs_info->dfs_nol_info, len); + spin_unlock_bh(&priv->dfs_nol_info_lock); + + return len; +} +EXPORT_SYMBOL(cnss_utils_wlan_get_dfs_nol); + +void cnss_utils_increment_driver_load_cnt(struct device *dev) +{ + struct cnss_utils_priv *priv = cnss_utils_priv; + + if (!priv) + return; + + ++(priv->driver_load_cnt); +} +EXPORT_SYMBOL(cnss_utils_increment_driver_load_cnt); + +int cnss_utils_get_driver_load_cnt(struct device *dev) +{ + struct cnss_utils_priv *priv = cnss_utils_priv; + + if (!priv) + return -EINVAL; + + return priv->driver_load_cnt; +} +EXPORT_SYMBOL(cnss_utils_get_driver_load_cnt); + +int cnss_utils_set_wlan_mac_address(const u8 *in, const uint32_t len) +{ + struct cnss_utils_priv *priv = cnss_utils_priv; + u32 no_of_mac_addr; + struct cnss_wlan_mac_addr *addr = NULL; + int iter; + u8 *temp = NULL; + + if (!priv) + return -EINVAL; + + if (priv->is_wlan_mac_set) { + pr_debug("WLAN MAC address is already set\n"); + return 0; + } + + if (len == 0 || (len % ETH_ALEN) != 0) { + pr_err("Invalid length %d\n", len); + return -EINVAL; + } + + no_of_mac_addr = len / ETH_ALEN; + if (no_of_mac_addr > MAX_NO_OF_MAC_ADDR) { + pr_err("Exceed maximum supported MAC address %u %u\n", + MAX_NO_OF_MAC_ADDR, no_of_mac_addr); + return -EINVAL; + } + + priv->is_wlan_mac_set = true; + addr = &priv->wlan_mac_addr; + addr->no_of_mac_addr_set = no_of_mac_addr; + temp = &addr->mac_addr[0][0]; + + for (iter = 0; iter < no_of_mac_addr; + ++iter, temp += ETH_ALEN, in += ETH_ALEN) { + ether_addr_copy(temp, in); + pr_debug("MAC_ADDR:%02x:%02x:%02x:%02x:%02x:%02x\n", + temp[0], temp[1], temp[2], + temp[3], temp[4], temp[5]); + } + + return 0; +} +EXPORT_SYMBOL(cnss_utils_set_wlan_mac_address); + +u8 *cnss_utils_get_wlan_mac_address(struct device *dev, uint32_t *num) +{ + struct cnss_utils_priv *priv = cnss_utils_priv; + struct cnss_wlan_mac_addr *addr = NULL; + + if (!priv) + goto out; + + if (!priv->is_wlan_mac_set) { + pr_debug("WLAN MAC address is not set\n"); + goto out; + } + + addr = &priv->wlan_mac_addr; + *num = addr->no_of_mac_addr_set; + return &addr->mac_addr[0][0]; +out: + *num = 0; + return NULL; +} +EXPORT_SYMBOL(cnss_utils_get_wlan_mac_address); + +void cnss_utils_set_cc_source(struct device *dev, + enum cnss_utils_cc_src cc_source) +{ + struct cnss_utils_priv *priv = cnss_utils_priv; + + if (!priv) + return; + + priv->cc_source = cc_source; +} +EXPORT_SYMBOL(cnss_utils_set_cc_source); + +enum cnss_utils_cc_src cnss_utils_get_cc_source(struct device *dev) +{ + struct cnss_utils_priv *priv = cnss_utils_priv; + + if (!priv) + return -EINVAL; + + return priv->cc_source; +} +EXPORT_SYMBOL(cnss_utils_get_cc_source); + +static int __init cnss_utils_init(void) +{ + struct cnss_utils_priv *priv = NULL; + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->cc_source = CNSS_UTILS_SOURCE_CORE; + + mutex_init(&priv->unsafe_channel_list_lock); + spin_lock_init(&priv->dfs_nol_info_lock); + + cnss_utils_priv = priv; + + return 0; +} + +static void __exit cnss_utils_exit(void) +{ + kfree(cnss_utils_priv); + cnss_utils_priv = NULL; +} + +module_init(cnss_utils_init); +module_exit(cnss_utils_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION(DEVICE "CNSS Utilities Driver"); diff --git a/drivers/net/xen-netfront.c b/drivers/net/xen-netfront.c index 888e9cfef51a..34a062ccb11d 100644 --- a/drivers/net/xen-netfront.c +++ b/drivers/net/xen-netfront.c @@ -321,7 +321,7 @@ static void xennet_alloc_rx_buffers(struct netfront_queue *queue) queue->rx.req_prod_pvt = req_prod; /* Not enough requests? Try again later. */ - if (req_prod - queue->rx.rsp_cons < NET_RX_SLOTS_MIN) { + if (req_prod - queue->rx.sring->req_prod < NET_RX_SLOTS_MIN) { mod_timer(&queue->rx_refill_timer, jiffies + (HZ/10)); return; } diff --git a/drivers/nvme/host/nvme.h b/drivers/nvme/host/nvme.h index 044253dca30a..b8a5a8e8f57d 100644 --- a/drivers/nvme/host/nvme.h +++ b/drivers/nvme/host/nvme.h @@ -27,6 +27,13 @@ enum { NVME_NS_LIGHTNVM = 1, }; +/* The below value is the specific amount of delay needed before checking + * readiness in case of the PCI_DEVICE(0x1c58, 0x0003), which needs the + * NVME_QUIRK_DELAY_BEFORE_CHK_RDY quirk enabled. The value (in ms) was + * found empirically. + */ +#define NVME_QUIRK_DELAY_AMOUNT 2000 + /* * Represents an NVM Express device. Each nvme_dev is a PCI function. */ diff --git a/drivers/nvme/host/pci.c b/drivers/nvme/host/pci.c index c851bc53831c..4c673d45f1bd 100644 --- a/drivers/nvme/host/pci.c +++ b/drivers/nvme/host/pci.c @@ -1633,10 +1633,15 @@ static int nvme_wait_ready(struct nvme_dev *dev, u64 cap, bool enabled) */ static int nvme_disable_ctrl(struct nvme_dev *dev, u64 cap) { + struct pci_dev *pdev = to_pci_dev(dev->dev); + dev->ctrl_config &= ~NVME_CC_SHN_MASK; dev->ctrl_config &= ~NVME_CC_ENABLE; writel(dev->ctrl_config, &dev->bar->cc); + if (pdev->vendor == 0x1c58 && pdev->device == 0x0003) + msleep(NVME_QUIRK_DELAY_AMOUNT); + return nvme_wait_ready(dev, cap, false); } diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c index 0438512f4d69..ca175710c4c8 100644 --- a/drivers/of/fdt.c +++ b/drivers/of/fdt.c @@ -632,9 +632,12 @@ int __init of_scan_flat_dt(int (*it)(unsigned long node, const char *pathp; int offset, rc = 0, depth = -1; - for (offset = fdt_next_node(blob, -1, &depth); - offset >= 0 && depth >= 0 && !rc; - offset = fdt_next_node(blob, offset, &depth)) { + if (!blob) + return 0; + + for (offset = fdt_next_node(blob, -1, &depth); + offset >= 0 && depth >= 0 && !rc; + offset = fdt_next_node(blob, offset, &depth)) { pathp = fdt_get_name(blob, offset, NULL); if (*pathp == '/') diff --git a/drivers/platform/msm/Kconfig b/drivers/platform/msm/Kconfig index 66bdc593f811..333d28e64087 100644 --- a/drivers/platform/msm/Kconfig +++ b/drivers/platform/msm/Kconfig @@ -194,7 +194,7 @@ config MSM_11AD tristate "Platform driver for 11ad chip" depends on PCI depends on PCI_MSM - default y + default m ---help--- This module adds required platform support for wireless adapter based on Qualcomm Technologies, Inc. 11ad chip, integrated into MSM platform diff --git a/drivers/platform/msm/ipa/ipa_clients/ipa_mhi_client.c b/drivers/platform/msm/ipa/ipa_clients/ipa_mhi_client.c index 5aa39b699bd6..9b3b53dcba68 100644 --- a/drivers/platform/msm/ipa/ipa_clients/ipa_mhi_client.c +++ b/drivers/platform/msm/ipa/ipa_clients/ipa_mhi_client.c @@ -2046,6 +2046,8 @@ static int ipa_mhi_suspend_dl(bool force) if (ipa_get_transport_type() == IPA_TRANSPORT_TYPE_GSI) ipa_mhi_update_host_ch_state(true); + return 0; + fail_stop_event_update_dl_channel: ipa_mhi_resume_channels(true, ipa_mhi_client_ctx->dl_channels); diff --git a/drivers/platform/msm/ipa/ipa_common_i.h b/drivers/platform/msm/ipa/ipa_common_i.h index 981129eb9f3a..d5f102eaaac6 100644 --- a/drivers/platform/msm/ipa/ipa_common_i.h +++ b/drivers/platform/msm/ipa/ipa_common_i.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2017, The Linux Foundation. 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 version 2 and @@ -26,7 +26,8 @@ log_info.file = __FILENAME__; \ log_info.line = __LINE__; \ log_info.type = EP; \ - log_info.id_string = ipa_clients_strings[client] + log_info.id_string = (client < 0 || client >= IPA_CLIENT_MAX) \ + ? "Invalid Client" : ipa_clients_strings[client] #define IPA_ACTIVE_CLIENTS_PREP_SIMPLE(log_info) \ log_info.file = __FILENAME__; \ diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_flt.c b/drivers/platform/msm/ipa/ipa_v2/ipa_flt.c index 80514f6c738e..b7f451b7c6fd 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa_flt.c +++ b/drivers/platform/msm/ipa/ipa_v2/ipa_flt.c @@ -1027,7 +1027,7 @@ static int __ipa_add_flt_rule(struct ipa_flt_tbl *tbl, enum ipa_ip_type ip, goto error; } - if (rt_tbl->cookie != IPA_COOKIE) { + if (rt_tbl->cookie != IPA_RT_TBL_COOKIE) { IPAERR("RT table cookie is invalid\n"); goto error; } @@ -1048,7 +1048,7 @@ static int __ipa_add_flt_rule(struct ipa_flt_tbl *tbl, enum ipa_ip_type ip, } INIT_LIST_HEAD(&entry->link); entry->rule = *rule; - entry->cookie = IPA_COOKIE; + entry->cookie = IPA_FLT_COOKIE; entry->rt_tbl = rt_tbl; entry->tbl = tbl; if (add_rear) { @@ -1067,13 +1067,19 @@ static int __ipa_add_flt_rule(struct ipa_flt_tbl *tbl, enum ipa_ip_type ip, if (id < 0) { IPAERR("failed to add to tree\n"); WARN_ON(1); + goto ipa_insert_failed; } *rule_hdl = id; entry->id = id; IPADBG_LOW("add flt rule rule_cnt=%d\n", tbl->rule_cnt); return 0; - +ipa_insert_failed: + tbl->rule_cnt--; + if (entry->rt_tbl) + entry->rt_tbl->ref_cnt--; + list_del(&entry->link); + kmem_cache_free(ipa_ctx->flt_rule_cache, entry); error: return -EPERM; } @@ -1089,7 +1095,7 @@ static int __ipa_del_flt_rule(u32 rule_hdl) return -EINVAL; } - if (entry->cookie != IPA_COOKIE) { + if (entry->cookie != IPA_FLT_COOKIE) { IPAERR("bad params\n"); return -EINVAL; } @@ -1121,7 +1127,7 @@ static int __ipa_mdfy_flt_rule(struct ipa_flt_rule_mdfy *frule, goto error; } - if (entry->cookie != IPA_COOKIE) { + if (entry->cookie != IPA_FLT_COOKIE) { IPAERR("bad params\n"); goto error; } @@ -1142,7 +1148,7 @@ static int __ipa_mdfy_flt_rule(struct ipa_flt_rule_mdfy *frule, goto error; } - if (rt_tbl->cookie != IPA_COOKIE) { + if (rt_tbl->cookie != IPA_RT_TBL_COOKIE) { IPAERR("RT table cookie is invalid\n"); goto error; } diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_hdr.c b/drivers/platform/msm/ipa/ipa_v2/ipa_hdr.c index 10a49b1e75d8..75e4ff5c3c57 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa_hdr.c +++ b/drivers/platform/msm/ipa/ipa_v2/ipa_hdr.c @@ -547,7 +547,7 @@ static int __ipa_add_hdr_proc_ctx(struct ipa_hdr_proc_ctx_add *proc_ctx, { struct ipa_hdr_entry *hdr_entry; struct ipa_hdr_proc_ctx_entry *entry; - struct ipa_hdr_proc_ctx_offset_entry *offset; + struct ipa_hdr_proc_ctx_offset_entry *offset = NULL; u32 bin; struct ipa_hdr_proc_ctx_tbl *htbl = &ipa_ctx->hdr_proc_ctx_tbl; int id; @@ -563,7 +563,7 @@ static int __ipa_add_hdr_proc_ctx(struct ipa_hdr_proc_ctx_add *proc_ctx, } hdr_entry = ipa_id_find(proc_ctx->hdr_hdl); - if (!hdr_entry || (hdr_entry->cookie != IPA_COOKIE)) { + if (!hdr_entry || (hdr_entry->cookie != IPA_HDR_COOKIE)) { IPAERR("hdr_hdl is invalid\n"); return -EINVAL; } @@ -580,7 +580,7 @@ static int __ipa_add_hdr_proc_ctx(struct ipa_hdr_proc_ctx_add *proc_ctx, entry->hdr = hdr_entry; if (add_ref_hdr) hdr_entry->ref_cnt++; - entry->cookie = IPA_COOKIE; + entry->cookie = IPA_PROC_HDR_COOKIE; needed_len = (proc_ctx->type == IPA_HDR_PROC_NONE) ? sizeof(struct ipa_hdr_proc_ctx_add_hdr_seq) : @@ -640,6 +640,7 @@ static int __ipa_add_hdr_proc_ctx(struct ipa_hdr_proc_ctx_add *proc_ctx, if (id < 0) { IPAERR("failed to alloc id\n"); WARN_ON(1); + goto ipa_insert_failed; } entry->id = id; proc_ctx->proc_ctx_hdl = id; @@ -647,6 +648,14 @@ static int __ipa_add_hdr_proc_ctx(struct ipa_hdr_proc_ctx_add *proc_ctx, return 0; +ipa_insert_failed: + if (offset) + list_move(&offset->link, + &htbl->head_free_offset_list[offset->bin]); + entry->offset_entry = NULL; + list_del(&entry->link); + htbl->proc_ctx_cnt--; + bad_len: if (add_ref_hdr) hdr_entry->ref_cnt--; @@ -659,7 +668,7 @@ bad_len: static int __ipa_add_hdr(struct ipa_hdr_add *hdr) { struct ipa_hdr_entry *entry; - struct ipa_hdr_offset_entry *offset; + struct ipa_hdr_offset_entry *offset = NULL; u32 bin; struct ipa_hdr_tbl *htbl = &ipa_ctx->hdr_tbl; int id; @@ -691,7 +700,7 @@ static int __ipa_add_hdr(struct ipa_hdr_add *hdr) entry->type = hdr->type; entry->is_eth2_ofst_valid = hdr->is_eth2_ofst_valid; entry->eth2_ofst = hdr->eth2_ofst; - entry->cookie = IPA_COOKIE; + entry->cookie = IPA_HDR_COOKIE; if (hdr->hdr_len <= ipa_hdr_bin_sz[IPA_HDR_BIN0]) bin = IPA_HDR_BIN0; @@ -780,6 +789,7 @@ static int __ipa_add_hdr(struct ipa_hdr_add *hdr) if (id < 0) { IPAERR("failed to alloc id\n"); WARN_ON(1); + goto ipa_insert_failed; } entry->id = id; hdr->hdr_hdl = id; @@ -804,10 +814,19 @@ fail_add_proc_ctx: entry->ref_cnt--; hdr->hdr_hdl = 0; ipa_id_remove(id); +ipa_insert_failed: + if (entry->is_hdr_proc_ctx) { + dma_unmap_single(ipa_ctx->pdev, entry->phys_base, + entry->hdr_len, DMA_TO_DEVICE); + } else { + if (offset) + list_move(&offset->link, + &htbl->head_free_offset_list[offset->bin]); + entry->offset_entry = NULL; + } htbl->hdr_cnt--; list_del(&entry->link); - dma_unmap_single(ipa_ctx->pdev, entry->phys_base, - entry->hdr_len, DMA_TO_DEVICE); + fail_dma_mapping: entry->is_hdr_proc_ctx = false; bad_hdr_len: @@ -824,7 +843,7 @@ static int __ipa_del_hdr_proc_ctx(u32 proc_ctx_hdl, struct ipa_hdr_proc_ctx_tbl *htbl = &ipa_ctx->hdr_proc_ctx_tbl; entry = ipa_id_find(proc_ctx_hdl); - if (!entry || (entry->cookie != IPA_COOKIE)) { + if (!entry || (entry->cookie != IPA_PROC_HDR_COOKIE)) { IPAERR("bad parm\n"); return -EINVAL; } @@ -875,7 +894,7 @@ int __ipa_del_hdr(u32 hdr_hdl, bool by_user) return -EINVAL; } - if (entry->cookie != IPA_COOKIE) { + if (entry->cookie != IPA_HDR_COOKIE) { IPAERR("bad parm\n"); return -EINVAL; } @@ -1444,7 +1463,7 @@ int ipa2_put_hdr(u32 hdr_hdl) goto bail; } - if (entry->cookie != IPA_COOKIE) { + if (entry->cookie != IPA_HDR_COOKIE) { IPAERR("invalid header entry\n"); result = -EINVAL; goto bail; diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_i.h b/drivers/platform/msm/ipa/ipa_v2/ipa_i.h index bb11230c960a..70b2a2eeb53f 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa_i.h +++ b/drivers/platform/msm/ipa/ipa_v2/ipa_i.h @@ -37,7 +37,15 @@ #define DRV_NAME "ipa" #define NAT_DEV_NAME "ipaNatTable" + #define IPA_COOKIE 0x57831603 +#define IPA_RT_RULE_COOKIE 0x57831604 +#define IPA_RT_TBL_COOKIE 0x57831605 +#define IPA_FLT_COOKIE 0x57831606 +#define IPA_HDR_COOKIE 0x57831607 +#define IPA_PROC_HDR_COOKIE 0x57831608 + + #define MTU_BYTE 1500 #define IPA_MAX_NUM_PIPES 0x14 @@ -223,8 +231,8 @@ struct ipa_smmu_cb_ctx { */ struct ipa_flt_entry { struct list_head link; - struct ipa_flt_rule rule; u32 cookie; + struct ipa_flt_rule rule; struct ipa_flt_tbl *tbl; struct ipa_rt_tbl *rt_tbl; u32 hw_len; @@ -249,13 +257,13 @@ struct ipa_flt_entry { */ struct ipa_rt_tbl { struct list_head link; + u32 cookie; struct list_head head_rt_rule_list; char name[IPA_RESOURCE_NAME_MAX]; u32 idx; u32 rule_cnt; u32 ref_cnt; struct ipa_rt_tbl_set *set; - u32 cookie; bool in_sys; u32 sz; struct ipa_mem_buffer curr_mem; @@ -286,6 +294,7 @@ struct ipa_rt_tbl { */ struct ipa_hdr_entry { struct list_head link; + u32 cookie; u8 hdr[IPA_HDR_MAX_SIZE]; u32 hdr_len; char name[IPA_RESOURCE_NAME_MAX]; @@ -295,7 +304,6 @@ struct ipa_hdr_entry { dma_addr_t phys_base; struct ipa_hdr_proc_ctx_entry *proc_ctx; struct ipa_hdr_offset_entry *offset_entry; - u32 cookie; u32 ref_cnt; int id; u8 is_eth2_ofst_valid; @@ -368,10 +376,10 @@ struct ipa_hdr_proc_ctx_add_hdr_cmd_seq { */ struct ipa_hdr_proc_ctx_entry { struct list_head link; + u32 cookie; enum ipa_hdr_proc_type type; struct ipa_hdr_proc_ctx_offset_entry *offset_entry; struct ipa_hdr_entry *hdr; - u32 cookie; u32 ref_cnt; int id; bool user_deleted; @@ -427,8 +435,8 @@ struct ipa_flt_tbl { */ struct ipa_rt_entry { struct list_head link; - struct ipa_rt_rule rule; u32 cookie; + struct ipa_rt_rule rule; struct ipa_rt_tbl *tbl; struct ipa_hdr_entry *hdr; struct ipa_hdr_proc_ctx_entry *proc_ctx; diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_intf.c b/drivers/platform/msm/ipa/ipa_v2/ipa_intf.c index 5c07bc7d43b5..e3f987d47692 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa_intf.c +++ b/drivers/platform/msm/ipa/ipa_v2/ipa_intf.c @@ -541,15 +541,15 @@ ssize_t ipa_read(struct file *filp, char __user *buf, size_t count, char __user *start; struct ipa_push_msg *msg = NULL; int ret; - DEFINE_WAIT(wait); + DEFINE_WAIT_FUNC(wait, woken_wake_function); int locked; start = buf; + add_wait_queue(&ipa_ctx->msg_waitq, &wait); while (1) { mutex_lock(&ipa_ctx->msg_lock); locked = 1; - prepare_to_wait(&ipa_ctx->msg_waitq, &wait, TASK_INTERRUPTIBLE); if (!list_empty(&ipa_ctx->msg_list)) { msg = list_first_entry(&ipa_ctx->msg_list, struct ipa_push_msg, link); @@ -601,10 +601,10 @@ ssize_t ipa_read(struct file *filp, char __user *buf, size_t count, locked = 0; mutex_unlock(&ipa_ctx->msg_lock); - schedule(); + wait_woken(&wait, TASK_INTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT); } - finish_wait(&ipa_ctx->msg_waitq, &wait); + remove_wait_queue(&ipa_ctx->msg_waitq, &wait); if (start != buf && ret != -EFAULT) ret = buf - start; diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c b/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c index f2909110d09f..9ac7b3809895 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c +++ b/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c @@ -909,7 +909,7 @@ static struct ipa_rt_tbl *__ipa_add_rt_tbl(enum ipa_ip_type ip, INIT_LIST_HEAD(&entry->link); strlcpy(entry->name, name, IPA_RESOURCE_NAME_MAX); entry->set = set; - entry->cookie = IPA_COOKIE; + entry->cookie = IPA_RT_TBL_COOKIE; entry->in_sys = (ip == IPA_IP_v4) ? !ipa_ctx->ip4_rt_tbl_lcl : !ipa_ctx->ip6_rt_tbl_lcl; set->tbl_cnt++; @@ -922,12 +922,16 @@ static struct ipa_rt_tbl *__ipa_add_rt_tbl(enum ipa_ip_type ip, if (id < 0) { IPAERR("failed to add to tree\n"); WARN_ON(1); + goto ipa_insert_failed; } entry->id = id; } return entry; +ipa_insert_failed: + set->tbl_cnt--; + list_del(&entry->link); fail_rt_idx_alloc: entry->cookie = 0; kmem_cache_free(ipa_ctx->rt_tbl_cache, entry); @@ -940,7 +944,7 @@ static int __ipa_del_rt_tbl(struct ipa_rt_tbl *entry) enum ipa_ip_type ip = IPA_IP_MAX; u32 id; - if (entry == NULL || (entry->cookie != IPA_COOKIE)) { + if (entry == NULL || (entry->cookie != IPA_RT_TBL_COOKIE)) { IPAERR("bad parms\n"); return -EINVAL; } @@ -954,8 +958,11 @@ static int __ipa_del_rt_tbl(struct ipa_rt_tbl *entry) ip = IPA_IP_v4; else if (entry->set == &ipa_ctx->rt_tbl_set[IPA_IP_v6]) ip = IPA_IP_v6; - else + else { WARN_ON(1); + return -EPERM; + } + if (!entry->in_sys) { list_del(&entry->link); @@ -994,13 +1001,14 @@ static int __ipa_add_rt_rule(enum ipa_ip_type ip, const char *name, if (rule->hdr_hdl) { hdr = ipa_id_find(rule->hdr_hdl); - if ((hdr == NULL) || (hdr->cookie != IPA_COOKIE)) { + if ((hdr == NULL) || (hdr->cookie != IPA_HDR_COOKIE)) { IPAERR("rt rule does not point to valid hdr\n"); goto error; } } else if (rule->hdr_proc_ctx_hdl) { proc_ctx = ipa_id_find(rule->hdr_proc_ctx_hdl); - if ((proc_ctx == NULL) || (proc_ctx->cookie != IPA_COOKIE)) { + if ((proc_ctx == NULL) || + (proc_ctx->cookie != IPA_PROC_HDR_COOKIE)) { IPAERR("rt rule does not point to valid proc ctx\n"); goto error; } @@ -1008,7 +1016,7 @@ static int __ipa_add_rt_rule(enum ipa_ip_type ip, const char *name, tbl = __ipa_add_rt_tbl(ip, name); - if (tbl == NULL || (tbl->cookie != IPA_COOKIE)) { + if (tbl == NULL || (tbl->cookie != IPA_RT_TBL_COOKIE)) { IPAERR("bad params\n"); goto error; } @@ -1029,7 +1037,7 @@ static int __ipa_add_rt_rule(enum ipa_ip_type ip, const char *name, goto error; } INIT_LIST_HEAD(&entry->link); - entry->cookie = IPA_COOKIE; + entry->cookie = IPA_RT_RULE_COOKIE; entry->rule = *rule; entry->tbl = tbl; entry->hdr = hdr; @@ -1123,7 +1131,7 @@ int __ipa_del_rt_rule(u32 rule_hdl) return -EINVAL; } - if (entry->cookie != IPA_COOKIE) { + if (entry->cookie != IPA_RT_RULE_COOKIE) { IPAERR("bad params\n"); return -EINVAL; } @@ -1358,7 +1366,7 @@ int ipa2_get_rt_tbl(struct ipa_ioc_get_rt_tbl *lookup) } mutex_lock(&ipa_ctx->lock); entry = __ipa_find_rt_tbl(lookup->ip, lookup->name); - if (entry && entry->cookie == IPA_COOKIE) { + if (entry && entry->cookie == IPA_RT_TBL_COOKIE) { if (entry->ref_cnt == U32_MAX) { IPAERR("fail: ref count crossed limit\n"); goto ret; @@ -1401,7 +1409,7 @@ int ipa2_put_rt_tbl(u32 rt_tbl_hdl) goto ret; } - if ((entry->cookie != IPA_COOKIE) || entry->ref_cnt == 0) { + if ((entry->cookie != IPA_RT_TBL_COOKIE) || entry->ref_cnt == 0) { IPAERR("bad parms\n"); result = -EINVAL; goto ret; @@ -1411,8 +1419,11 @@ int ipa2_put_rt_tbl(u32 rt_tbl_hdl) ip = IPA_IP_v4; else if (entry->set == &ipa_ctx->rt_tbl_set[IPA_IP_v6]) ip = IPA_IP_v6; - else + else { WARN_ON(1); + result = -EINVAL; + goto ret; + } entry->ref_cnt--; if (entry->ref_cnt == 0 && entry->rule_cnt == 0) { @@ -1439,7 +1450,7 @@ static int __ipa_mdfy_rt_rule(struct ipa_rt_rule_mdfy *rtrule) if (rtrule->rule.hdr_hdl) { hdr = ipa_id_find(rtrule->rule.hdr_hdl); - if ((hdr == NULL) || (hdr->cookie != IPA_COOKIE)) { + if ((hdr == NULL) || (hdr->cookie != IPA_HDR_COOKIE)) { IPAERR("rt rule does not point to valid hdr\n"); goto error; } @@ -1451,7 +1462,7 @@ static int __ipa_mdfy_rt_rule(struct ipa_rt_rule_mdfy *rtrule) goto error; } - if (entry->cookie != IPA_COOKIE) { + if (entry->cookie != IPA_RT_RULE_COOKIE) { IPAERR("bad params\n"); goto error; } diff --git a/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c b/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c index c59c597f39bf..834712a71ac6 100644 --- a/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c +++ b/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c @@ -2618,6 +2618,9 @@ static int rmnet_ipa_set_data_quota_modem(struct wan_ioctl_set_data_quota *data) if (!data->set_quota) ipa_qmi_stop_data_qouta(); + /* prevent string buffer overflows */ + data->interface_name[IFNAMSIZ-1] = '\0'; + index = find_vchannel_name_index(data->interface_name); IPAWANERR("iface name %s, quota %lu\n", data->interface_name, diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_client.c b/drivers/platform/msm/ipa/ipa_v3/ipa_client.c index bc6622d4725b..8c6bd48cfb2c 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_client.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_client.c @@ -1254,6 +1254,12 @@ int ipa3_request_gsi_channel(struct ipa_request_gsi_channel_params *params, memset(gsi_ep_cfg_ptr, 0, sizeof(struct ipa_gsi_ep_config)); gsi_ep_cfg_ptr = ipa_get_gsi_ep_info(ipa_ep_idx); + if (gsi_ep_cfg_ptr == NULL) { + IPAERR("Error ipa_get_gsi_ep_info ret NULL\n"); + result = -EFAULT; + goto write_evt_scratch_fail; + } + params->chan_params.evt_ring_hdl = ep->gsi_evt_ring_hdl; params->chan_params.ch_id = gsi_ep_cfg_ptr->ipa_gsi_chan_num; gsi_res = gsi_alloc_channel(¶ms->chan_params, gsi_dev_hdl, diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c b/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c index 6a3a89e2cf8d..5fe425ea7655 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c @@ -1188,6 +1188,15 @@ static void ipa3_handle_rx(struct ipa3_sys_context *sys) } else { inactive_cycles = 0; } + + /* + * if pipe is out of buffers there is no point polling for + * completed descs; release the worker so delayed work can + * run in a timely manner + */ + if (sys->len == 0) + break; + } while (inactive_cycles <= POLLING_INACTIVITY_RX); trace_poll_to_intr3(sys->ep->client); diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_flt.c b/drivers/platform/msm/ipa/ipa_v3/ipa_flt.c index 41b29335d23b..42632c527bdd 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_flt.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_flt.c @@ -745,7 +745,7 @@ static int __ipa_validate_flt_rule(const struct ipa_flt_rule *rule, goto error; } - if ((*rt_tbl)->cookie != IPA_COOKIE) { + if ((*rt_tbl)->cookie != IPA_RT_TBL_COOKIE) { IPAERR("RT table cookie is invalid\n"); goto error; } @@ -787,7 +787,7 @@ static int __ipa_create_flt_entry(struct ipa3_flt_entry **entry, } INIT_LIST_HEAD(&((*entry)->link)); (*entry)->rule = *rule; - (*entry)->cookie = IPA_COOKIE; + (*entry)->cookie = IPA_FLT_COOKIE; (*entry)->rt_tbl = rt_tbl; (*entry)->tbl = tbl; if (rule->rule_id) { @@ -822,12 +822,18 @@ static int __ipa_finish_flt_rule_add(struct ipa3_flt_tbl *tbl, if (id < 0) { IPAERR("failed to add to tree\n"); WARN_ON(1); + goto ipa_insert_failed; } *rule_hdl = id; entry->id = id; IPADBG_LOW("add flt rule rule_cnt=%d\n", tbl->rule_cnt); return 0; +ipa_insert_failed: + if (entry->rt_tbl) + entry->rt_tbl->ref_cnt--; + tbl->rule_cnt--; + return -EPERM; } static int __ipa_add_flt_rule(struct ipa3_flt_tbl *tbl, enum ipa_ip_type ip, @@ -853,9 +859,16 @@ static int __ipa_add_flt_rule(struct ipa3_flt_tbl *tbl, enum ipa_ip_type ip, list_add(&entry->link, &tbl->head_flt_rule_list); } - __ipa_finish_flt_rule_add(tbl, entry, rule_hdl); + if (__ipa_finish_flt_rule_add(tbl, entry, rule_hdl)) + goto ipa_insert_failed; return 0; +ipa_insert_failed: + list_del(&entry->link); + /* if rule id was allocated from idr, remove it */ + if (!(entry->rule_id & ipahal_get_rule_id_hi_bit())) + idr_remove(&entry->tbl->rule_ids, entry->rule_id); + kmem_cache_free(ipa3_ctx->flt_rule_cache, entry); error: return -EPERM; @@ -887,7 +900,8 @@ static int __ipa_add_flt_rule_after(struct ipa3_flt_tbl *tbl, list_add(&entry->link, &((*add_after_entry)->link)); - __ipa_finish_flt_rule_add(tbl, entry, rule_hdl); + if (__ipa_finish_flt_rule_add(tbl, entry, rule_hdl)) + goto ipa_insert_failed; /* * prepare for next insertion @@ -896,6 +910,13 @@ static int __ipa_add_flt_rule_after(struct ipa3_flt_tbl *tbl, return 0; +ipa_insert_failed: + list_del(&entry->link); + /* if rule id was allocated from idr, remove it */ + if (!(entry->rule_id & ipahal_get_rule_id_hi_bit())) + idr_remove(&entry->tbl->rule_ids, entry->rule_id); + kmem_cache_free(ipa3_ctx->flt_rule_cache, entry); + error: *add_after_entry = NULL; return -EPERM; @@ -912,7 +933,7 @@ static int __ipa_del_flt_rule(u32 rule_hdl) return -EINVAL; } - if (entry->cookie != IPA_COOKIE) { + if (entry->cookie != IPA_FLT_COOKIE) { IPAERR("bad params\n"); return -EINVAL; } @@ -949,7 +970,7 @@ static int __ipa_mdfy_flt_rule(struct ipa_flt_rule_mdfy *frule, goto error; } - if (entry->cookie != IPA_COOKIE) { + if (entry->cookie != IPA_FLT_COOKIE) { IPAERR("bad params\n"); goto error; } @@ -970,7 +991,7 @@ static int __ipa_mdfy_flt_rule(struct ipa_flt_rule_mdfy *frule, goto error; } - if (rt_tbl->cookie != IPA_COOKIE) { + if (rt_tbl->cookie != IPA_RT_TBL_COOKIE) { IPAERR("RT table cookie is invalid\n"); goto error; } diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_hdr.c b/drivers/platform/msm/ipa/ipa_v3/ipa_hdr.c index a5186f1aff35..34bc7dcd63cf 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_hdr.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_hdr.c @@ -339,7 +339,7 @@ static int __ipa_add_hdr_proc_ctx(struct ipa_hdr_proc_ctx_add *proc_ctx, IPAERR("hdr_hdl is invalid\n"); return -EINVAL; } - if (hdr_entry->cookie != IPA_COOKIE) { + if (hdr_entry->cookie != IPA_HDR_COOKIE) { IPAERR("Invalid header cookie %u\n", hdr_entry->cookie); WARN_ON(1); return -EINVAL; @@ -359,7 +359,7 @@ static int __ipa_add_hdr_proc_ctx(struct ipa_hdr_proc_ctx_add *proc_ctx, entry->hdr = hdr_entry; if (add_ref_hdr) hdr_entry->ref_cnt++; - entry->cookie = IPA_COOKIE; + entry->cookie = IPA_HDR_COOKIE; needed_len = ipahal_get_proc_ctx_needed_len(proc_ctx->type); @@ -417,6 +417,7 @@ static int __ipa_add_hdr_proc_ctx(struct ipa_hdr_proc_ctx_add *proc_ctx, if (id < 0) { IPAERR("failed to alloc id\n"); WARN_ON(1); + goto ipa_insert_failed; } entry->id = id; proc_ctx->proc_ctx_hdl = id; @@ -424,6 +425,14 @@ static int __ipa_add_hdr_proc_ctx(struct ipa_hdr_proc_ctx_add *proc_ctx, return 0; +ipa_insert_failed: + if (offset) + list_move(&offset->link, + &htbl->head_free_offset_list[offset->bin]); + entry->offset_entry = NULL; + list_del(&entry->link); + htbl->proc_ctx_cnt--; + bad_len: if (add_ref_hdr) hdr_entry->ref_cnt--; @@ -436,7 +445,7 @@ bad_len: static int __ipa_add_hdr(struct ipa_hdr_add *hdr) { struct ipa3_hdr_entry *entry; - struct ipa_hdr_offset_entry *offset; + struct ipa_hdr_offset_entry *offset = NULL; u32 bin; struct ipa3_hdr_tbl *htbl = &ipa3_ctx->hdr_tbl; int id; @@ -467,7 +476,7 @@ static int __ipa_add_hdr(struct ipa_hdr_add *hdr) entry->type = hdr->type; entry->is_eth2_ofst_valid = hdr->is_eth2_ofst_valid; entry->eth2_ofst = hdr->eth2_ofst; - entry->cookie = IPA_COOKIE; + entry->cookie = IPA_HDR_COOKIE; if (hdr->hdr_len <= ipa_hdr_bin_sz[IPA_HDR_BIN0]) bin = IPA_HDR_BIN0; @@ -546,6 +555,7 @@ static int __ipa_add_hdr(struct ipa_hdr_add *hdr) if (id < 0) { IPAERR("failed to alloc id\n"); WARN_ON(1); + goto ipa_insert_failed; } entry->id = id; hdr->hdr_hdl = id; @@ -570,10 +580,19 @@ fail_add_proc_ctx: entry->ref_cnt--; hdr->hdr_hdl = 0; ipa3_id_remove(id); +ipa_insert_failed: + if (entry->is_hdr_proc_ctx) { + dma_unmap_single(ipa3_ctx->pdev, entry->phys_base, + entry->hdr_len, DMA_TO_DEVICE); + } else { + if (offset) + list_move(&offset->link, + &htbl->head_free_offset_list[offset->bin]); + entry->offset_entry = NULL; + } htbl->hdr_cnt--; list_del(&entry->link); - dma_unmap_single(ipa3_ctx->pdev, entry->phys_base, - entry->hdr_len, DMA_TO_DEVICE); + fail_dma_mapping: entry->is_hdr_proc_ctx = false; @@ -591,7 +610,7 @@ static int __ipa3_del_hdr_proc_ctx(u32 proc_ctx_hdl, struct ipa3_hdr_proc_ctx_tbl *htbl = &ipa3_ctx->hdr_proc_ctx_tbl; entry = ipa3_id_find(proc_ctx_hdl); - if (!entry || (entry->cookie != IPA_COOKIE)) { + if (!entry || (entry->cookie != IPA_HDR_COOKIE)) { IPAERR("bad parm\n"); return -EINVAL; } @@ -642,7 +661,7 @@ int __ipa3_del_hdr(u32 hdr_hdl, bool by_user) return -EINVAL; } - if (entry->cookie != IPA_COOKIE) { + if (entry->cookie != IPA_HDR_COOKIE) { IPAERR("bad parm\n"); return -EINVAL; } @@ -1188,7 +1207,7 @@ int ipa3_put_hdr(u32 hdr_hdl) goto bail; } - if (entry->cookie != IPA_COOKIE) { + if (entry->cookie != IPA_HDR_COOKIE) { IPAERR("invalid header entry\n"); result = -EINVAL; goto bail; diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h index ac7ef6a21952..cbc53fb6815d 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h @@ -40,6 +40,12 @@ #define DRV_NAME "ipa" #define NAT_DEV_NAME "ipaNatTable" #define IPA_COOKIE 0x57831603 +#define IPA_RT_RULE_COOKIE 0x57831604 +#define IPA_RT_TBL_COOKIE 0x57831605 +#define IPA_FLT_COOKIE 0x57831606 +#define IPA_HDR_COOKIE 0x57831607 +#define IPA_PROC_HDR_COOKIE 0x57831608 + #define MTU_BYTE 1500 #define IPA_EP_NOT_ALLOCATED (-1) @@ -217,8 +223,8 @@ struct ipa_smmu_cb_ctx { */ struct ipa3_flt_entry { struct list_head link; - struct ipa_flt_rule rule; u32 cookie; + struct ipa_flt_rule rule; struct ipa3_flt_tbl *tbl; struct ipa3_rt_tbl *rt_tbl; u32 hw_len; @@ -246,13 +252,13 @@ struct ipa3_flt_entry { */ struct ipa3_rt_tbl { struct list_head link; + u32 cookie; struct list_head head_rt_rule_list; char name[IPA_RESOURCE_NAME_MAX]; u32 idx; u32 rule_cnt; u32 ref_cnt; struct ipa3_rt_tbl_set *set; - u32 cookie; bool in_sys[IPA_RULE_TYPE_MAX]; u32 sz[IPA_RULE_TYPE_MAX]; struct ipa_mem_buffer curr_mem[IPA_RULE_TYPE_MAX]; @@ -284,6 +290,7 @@ struct ipa3_rt_tbl { */ struct ipa3_hdr_entry { struct list_head link; + u32 cookie; u8 hdr[IPA_HDR_MAX_SIZE]; u32 hdr_len; char name[IPA_RESOURCE_NAME_MAX]; @@ -293,7 +300,6 @@ struct ipa3_hdr_entry { dma_addr_t phys_base; struct ipa3_hdr_proc_ctx_entry *proc_ctx; struct ipa_hdr_offset_entry *offset_entry; - u32 cookie; u32 ref_cnt; int id; u8 is_eth2_ofst_valid; @@ -342,10 +348,10 @@ struct ipa3_hdr_proc_ctx_offset_entry { */ struct ipa3_hdr_proc_ctx_entry { struct list_head link; + u32 cookie; enum ipa_hdr_proc_type type; struct ipa3_hdr_proc_ctx_offset_entry *offset_entry; struct ipa3_hdr_entry *hdr; - u32 cookie; u32 ref_cnt; int id; bool user_deleted; @@ -407,8 +413,8 @@ struct ipa3_flt_tbl { */ struct ipa3_rt_entry { struct list_head link; - struct ipa_rt_rule rule; u32 cookie; + struct ipa_rt_rule rule; struct ipa3_rt_tbl *tbl; struct ipa3_hdr_entry *hdr; struct ipa3_hdr_proc_ctx_entry *proc_ctx; diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_intf.c b/drivers/platform/msm/ipa/ipa_v3/ipa_intf.c index 16a567644f79..38e8d4e9d639 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_intf.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_intf.c @@ -546,17 +546,15 @@ ssize_t ipa3_read(struct file *filp, char __user *buf, size_t count, char __user *start; struct ipa3_push_msg *msg = NULL; int ret; - DEFINE_WAIT(wait); + DEFINE_WAIT_FUNC(wait, woken_wake_function); int locked; start = buf; + add_wait_queue(&ipa3_ctx->msg_waitq, &wait); while (1) { mutex_lock(&ipa3_ctx->msg_lock); locked = 1; - prepare_to_wait(&ipa3_ctx->msg_waitq, - &wait, - TASK_INTERRUPTIBLE); if (!list_empty(&ipa3_ctx->msg_list)) { msg = list_first_entry(&ipa3_ctx->msg_list, @@ -609,10 +607,10 @@ ssize_t ipa3_read(struct file *filp, char __user *buf, size_t count, locked = 0; mutex_unlock(&ipa3_ctx->msg_lock); - schedule(); + wait_woken(&wait, TASK_INTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT); } - finish_wait(&ipa3_ctx->msg_waitq, &wait); + remove_wait_queue(&ipa3_ctx->msg_waitq, &wait); if (start != buf && ret != -EFAULT) ret = buf - start; diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_mhi.c b/drivers/platform/msm/ipa/ipa_v3/ipa_mhi.c index cd027c28e597..3267e0e83a82 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_mhi.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_mhi.c @@ -191,7 +191,7 @@ static int ipa3_mhi_get_ch_poll_cfg(enum ipa_client_type client, static int ipa_mhi_start_gsi_channel(enum ipa_client_type client, int ipa_ep_idx, struct start_gsi_channel *params) { - int res; + int res = 0; struct gsi_evt_ring_props ev_props; struct ipa_mhi_msi_info *msi; struct gsi_chan_props ch_props; @@ -241,7 +241,6 @@ static int ipa_mhi_start_gsi_channel(enum ipa_client_type client, if (res) { IPA_MHI_ERR("gsi_alloc_evt_ring failed %d\n", res); goto fail_alloc_evt; - return res; } IPA_MHI_DBG("client %d, caching event ring hdl %lu\n", client, @@ -259,7 +258,6 @@ static int ipa_mhi_start_gsi_channel(enum ipa_client_type client, IPA_MHI_ERR("event ring wp is not updated. base=wp=0x%llx\n", params->ev_ctx_host->wp); goto fail_alloc_ch; - return res; } IPA_MHI_DBG("Ring event db: evt_ring_hdl=%lu host_wp=0x%llx\n", @@ -270,7 +268,6 @@ static int ipa_mhi_start_gsi_channel(enum ipa_client_type client, IPA_MHI_ERR("fail to ring evt ring db %d. hdl=%lu wp=0x%llx\n", res, ep->gsi_evt_ring_hdl, params->ev_ctx_host->wp); goto fail_alloc_ch; - return res; } memset(&ch_props, 0, sizeof(ch_props)); diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.c b/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.c index 0269bfba993f..690a9db67ff0 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.c @@ -1177,10 +1177,13 @@ void ipa3_qmi_service_exit(void) } /* clean the QMI msg cache */ + mutex_lock(&ipa3_qmi_lock); if (ipa3_qmi_ctx != NULL) { vfree(ipa3_qmi_ctx); ipa3_qmi_ctx = NULL; } + mutex_unlock(&ipa3_qmi_lock); + ipa3_svc_handle = 0; ipa3_qmi_modem_init_fin = false; ipa3_qmi_indication_fin = false; diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c b/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c index 6197c9f64ca5..2ade584890fb 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c @@ -796,7 +796,7 @@ static struct ipa3_rt_tbl *__ipa_add_rt_tbl(enum ipa_ip_type ip, INIT_LIST_HEAD(&entry->link); strlcpy(entry->name, name, IPA_RESOURCE_NAME_MAX); entry->set = set; - entry->cookie = IPA_COOKIE; + entry->cookie = IPA_RT_TBL_COOKIE; entry->in_sys[IPA_RULE_HASHABLE] = (ip == IPA_IP_v4) ? !ipa3_ctx->ip4_rt_tbl_hash_lcl : !ipa3_ctx->ip6_rt_tbl_hash_lcl; @@ -814,12 +814,16 @@ static struct ipa3_rt_tbl *__ipa_add_rt_tbl(enum ipa_ip_type ip, if (id < 0) { IPAERR("failed to add to tree\n"); WARN_ON(1); + goto ipa_insert_failed; } entry->id = id; } return entry; - +ipa_insert_failed: + set->tbl_cnt--; + list_del(&entry->link); + idr_destroy(&entry->rule_ids); fail_rt_idx_alloc: entry->cookie = 0; kmem_cache_free(ipa3_ctx->rt_tbl_cache, entry); @@ -833,7 +837,7 @@ static int __ipa_del_rt_tbl(struct ipa3_rt_tbl *entry) u32 id; struct ipa3_rt_tbl_set *rset; - if (entry == NULL || (entry->cookie != IPA_COOKIE)) { + if (entry == NULL || (entry->cookie != IPA_RT_TBL_COOKIE)) { IPAERR("bad parms\n"); return -EINVAL; } @@ -847,8 +851,10 @@ static int __ipa_del_rt_tbl(struct ipa3_rt_tbl *entry) ip = IPA_IP_v4; else if (entry->set == &ipa3_ctx->rt_tbl_set[IPA_IP_v6]) ip = IPA_IP_v6; - else + else { WARN_ON(1); + return -EPERM; + } rset = &ipa3_ctx->reap_rt_tbl_set[ip]; @@ -885,14 +891,14 @@ static int __ipa_rt_validate_hndls(const struct ipa_rt_rule *rule, if (rule->hdr_hdl) { *hdr = ipa3_id_find(rule->hdr_hdl); - if ((*hdr == NULL) || ((*hdr)->cookie != IPA_COOKIE)) { + if ((*hdr == NULL) || ((*hdr)->cookie != IPA_HDR_COOKIE)) { IPAERR("rt rule does not point to valid hdr\n"); return -EPERM; } } else if (rule->hdr_proc_ctx_hdl) { *proc_ctx = ipa3_id_find(rule->hdr_proc_ctx_hdl); if ((*proc_ctx == NULL) || - ((*proc_ctx)->cookie != IPA_COOKIE)) { + ((*proc_ctx)->cookie != IPA_PROC_HDR_COOKIE)) { IPAERR("rt rule does not point to valid proc ctx\n"); return -EPERM; @@ -915,7 +921,7 @@ static int __ipa_create_rt_entry(struct ipa3_rt_entry **entry, goto error; } INIT_LIST_HEAD(&(*entry)->link); - (*(entry))->cookie = IPA_COOKIE; + (*(entry))->cookie = IPA_RT_RULE_COOKIE; (*(entry))->rule = *rule; (*(entry))->tbl = tbl; (*(entry))->hdr = hdr; @@ -983,7 +989,7 @@ static int __ipa_add_rt_rule(enum ipa_ip_type ip, const char *name, tbl = __ipa_add_rt_tbl(ip, name); - if (tbl == NULL || (tbl->cookie != IPA_COOKIE)) { + if (tbl == NULL || (tbl->cookie != IPA_RT_TBL_COOKIE)) { IPAERR("failed adding rt tbl name = %s\n", name ? name : ""); goto error; @@ -1118,7 +1124,7 @@ int ipa3_add_rt_rule_after(struct ipa_ioc_add_rt_rule_after *rules) mutex_lock(&ipa3_ctx->lock); tbl = __ipa3_find_rt_tbl(rules->ip, rules->rt_tbl_name); - if (tbl == NULL || (tbl->cookie != IPA_COOKIE)) { + if (tbl == NULL || (tbl->cookie != IPA_RT_TBL_COOKIE)) { IPAERR("failed finding rt tbl name = %s\n", rules->rt_tbl_name ? rules->rt_tbl_name : ""); ret = -EINVAL; @@ -1202,7 +1208,7 @@ int __ipa3_del_rt_rule(u32 rule_hdl) return -EINVAL; } - if (entry->cookie != IPA_COOKIE) { + if (entry->cookie != IPA_RT_RULE_COOKIE) { IPAERR("bad params\n"); return -EINVAL; } @@ -1440,7 +1446,7 @@ int ipa3_get_rt_tbl(struct ipa_ioc_get_rt_tbl *lookup) } mutex_lock(&ipa3_ctx->lock); entry = __ipa3_find_rt_tbl(lookup->ip, lookup->name); - if (entry && entry->cookie == IPA_COOKIE) { + if (entry && entry->cookie == IPA_RT_TBL_COOKIE) { if (entry->ref_cnt == U32_MAX) { IPAERR("fail: ref count crossed limit\n"); goto ret; @@ -1483,7 +1489,7 @@ int ipa3_put_rt_tbl(u32 rt_tbl_hdl) goto ret; } - if ((entry->cookie != IPA_COOKIE) || entry->ref_cnt == 0) { + if ((entry->cookie != IPA_RT_TBL_COOKIE) || entry->ref_cnt == 0) { IPAERR("bad parms\n"); result = -EINVAL; goto ret; @@ -1493,8 +1499,10 @@ int ipa3_put_rt_tbl(u32 rt_tbl_hdl) ip = IPA_IP_v4; else if (entry->set == &ipa3_ctx->rt_tbl_set[IPA_IP_v6]) ip = IPA_IP_v6; - else + else { WARN_ON(1); + goto ret; + } entry->ref_cnt--; if (entry->ref_cnt == 0 && entry->rule_cnt == 0) { @@ -1524,13 +1532,14 @@ static int __ipa_mdfy_rt_rule(struct ipa_rt_rule_mdfy *rtrule) if (rtrule->rule.hdr_hdl) { hdr = ipa3_id_find(rtrule->rule.hdr_hdl); - if ((hdr == NULL) || (hdr->cookie != IPA_COOKIE)) { + if ((hdr == NULL) || (hdr->cookie != IPA_HDR_COOKIE)) { IPAERR("rt rule does not point to valid hdr\n"); goto error; } } else if (rtrule->rule.hdr_proc_ctx_hdl) { proc_ctx = ipa3_id_find(rtrule->rule.hdr_proc_ctx_hdl); - if ((proc_ctx == NULL) || (proc_ctx->cookie != IPA_COOKIE)) { + if ((proc_ctx == NULL) || + (proc_ctx->cookie != IPA_PROC_HDR_COOKIE)) { IPAERR("rt rule does not point to valid proc ctx\n"); goto error; } @@ -1542,7 +1551,7 @@ static int __ipa_mdfy_rt_rule(struct ipa_rt_rule_mdfy *rtrule) goto error; } - if (entry->cookie != IPA_COOKIE) { + if (entry->cookie != IPA_RT_RULE_COOKIE) { IPAERR("bad params\n"); goto error; } diff --git a/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c index 5397b6c39419..039bc7da5153 100644 --- a/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c +++ b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c @@ -2746,6 +2746,9 @@ static int rmnet_ipa3_set_data_quota_modem( if (!data->set_quota) ipa3_qmi_stop_data_qouta(); + /* prevent string buffer overflows */ + data->interface_name[IFNAMSIZ-1] = '\0'; + index = find_vchannel_name_index(data->interface_name); IPAWANERR("iface name %s, quota %lu\n", data->interface_name, diff --git a/drivers/platform/msm/ipa/test/ipa_test_mhi.c b/drivers/platform/msm/ipa/test/ipa_test_mhi.c index 5a41d641de4f..3a89c7dffdb0 100644 --- a/drivers/platform/msm/ipa/test/ipa_test_mhi.c +++ b/drivers/platform/msm/ipa/test/ipa_test_mhi.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2016-2017, The Linux Foundation. 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 version 2 and @@ -326,6 +326,7 @@ struct ipa_test_mhi_context { struct ipa_mem_buffer out_buffer; u32 prod_hdl; u32 cons_hdl; + u32 test_prod_hdl; }; static struct ipa_test_mhi_context *test_mhi_ctx; @@ -774,6 +775,7 @@ fail_destroy_out_ch_ctx: static int ipa_test_mhi_suite_setup(void **ppriv) { int rc = 0; + struct ipa_sys_connect_params sys_in; IPA_UT_DBG("Start Setup\n"); @@ -815,9 +817,22 @@ static int ipa_test_mhi_suite_setup(void **ppriv) goto fail_free_mmio_spc; } + /* connect PROD pipe for remote wakeup */ + memset(&sys_in, 0, sizeof(struct ipa_sys_connect_params)); + sys_in.client = IPA_CLIENT_TEST_PROD; + sys_in.desc_fifo_sz = IPA_SYS_DESC_FIFO_SZ; + sys_in.ipa_ep_cfg.mode.mode = IPA_DMA; + sys_in.ipa_ep_cfg.mode.dst = IPA_CLIENT_MHI_CONS; + if (ipa_setup_sys_pipe(&sys_in, &test_mhi_ctx->test_prod_hdl)) { + IPA_UT_ERR("setup sys pipe failed.\n"); + goto fail_destroy_data_structures; + } + *ppriv = test_mhi_ctx; return 0; +fail_destroy_data_structures: + ipa_mhi_test_destroy_data_structures(); fail_free_mmio_spc: ipa_test_mhi_free_mmio_space(); fail_iounmap: @@ -838,6 +853,7 @@ static int ipa_test_mhi_suite_teardown(void *priv) if (!test_mhi_ctx) return 0; + ipa_teardown_sys_pipe(test_mhi_ctx->test_prod_hdl); ipa_mhi_test_destroy_data_structures(); ipa_test_mhi_free_mmio_space(); iounmap(test_mhi_ctx->gsi_mmio); @@ -1811,7 +1827,7 @@ static int ipa_mhi_test_create_aggr_open_frame(void) memset(test_mhi_ctx->out_buffer.base + i, i & 0xFF, 1); } - rc = ipa_tx_dp(IPA_CLIENT_MHI_CONS, skb, NULL); + rc = ipa_tx_dp(IPA_CLIENT_TEST_PROD, skb, NULL); if (rc) { IPA_UT_LOG("ipa_tx_dp failed %d\n", rc); IPA_UT_TEST_FAIL_REPORT("ipa tx dp fail"); @@ -1982,7 +1998,7 @@ static int ipa_mhi_test_suspend_host_wakeup(void) memset(test_mhi_ctx->out_buffer.base + i, i & 0xFF, 1); } - rc = ipa_tx_dp(IPA_CLIENT_MHI_CONS, skb, NULL); + rc = ipa_tx_dp(IPA_CLIENT_TEST_PROD, skb, NULL); if (rc) { IPA_UT_LOG("ipa_tx_dp failed %d\n", rc); IPA_UT_TEST_FAIL_REPORT("ipa tx dp fail"); diff --git a/drivers/platform/x86/ideapad-laptop.c b/drivers/platform/x86/ideapad-laptop.c index be3bc2f4edd4..09cc64b3b695 100644 --- a/drivers/platform/x86/ideapad-laptop.c +++ b/drivers/platform/x86/ideapad-laptop.c @@ -807,6 +807,7 @@ static void ideapad_acpi_notify(acpi_handle handle, u32 event, void *data) case 11: case 7: case 6: + case 1: ideapad_input_report(priv, vpc_bit); break; case 5: diff --git a/drivers/power/qcom/msm-core.c b/drivers/power/qcom/msm-core.c index 43ad33ea234b..825c27e7a4c1 100644 --- a/drivers/power/qcom/msm-core.c +++ b/drivers/power/qcom/msm-core.c @@ -190,10 +190,12 @@ static void core_temp_notify(enum thermal_trip_type type, struct cpu_activity_info *cpu_node = (struct cpu_activity_info *) data; + temp /= scaling_factor; + trace_temp_notification(cpu_node->sensor_id, type, temp, cpu_node->temp); - cpu_node->temp = temp / scaling_factor; + cpu_node->temp = temp; complete(&sampling_completion); } diff --git a/drivers/power/reset/msm-poweroff.c b/drivers/power/reset/msm-poweroff.c index e17c65c1e4e7..209263ccced7 100644 --- a/drivers/power/reset/msm-poweroff.c +++ b/drivers/power/reset/msm-poweroff.c @@ -389,6 +389,7 @@ static void do_msm_restart(enum reboot_mode reboot_mode, const char *cmd) msm_trigger_wdog_bite(); #endif + scm_disable_sdi(); halt_spmi_pmic_arbiter(); deassert_ps_hold(); diff --git a/drivers/power/supply/qcom/qpnp-smb2.c b/drivers/power/supply/qcom/qpnp-smb2.c index 0908eea3b186..7b7f991ecba9 100644 --- a/drivers/power/supply/qcom/qpnp-smb2.c +++ b/drivers/power/supply/qcom/qpnp-smb2.c @@ -1847,6 +1847,12 @@ static int smb2_post_init(struct smb2 *chip) struct smb_charger *chg = &chip->chg; int rc; + /* In case the usb path is suspended, we would have missed disabling + * the icl change interrupt because the interrupt could have been + * not requested + */ + rerun_election(chg->usb_icl_votable); + /* configure power role for dual-role */ rc = smblib_masked_write(chg, TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG, TYPEC_POWER_ROLE_CMD_MASK, 0); @@ -2196,6 +2202,8 @@ static int smb2_request_interrupts(struct smb2 *chip) return rc; } } + if (chg->irq_info[USBIN_ICL_CHANGE_IRQ].irq) + chg->usb_icl_change_irq_enabled = true; return rc; } diff --git a/drivers/power/supply/qcom/smb-lib.c b/drivers/power/supply/qcom/smb-lib.c index 16db425514c3..64ed53185e88 100644 --- a/drivers/power/supply/qcom/smb-lib.c +++ b/drivers/power/supply/qcom/smb-lib.c @@ -427,6 +427,14 @@ static int step_charge_soc_update(struct smb_charger *chg, int capacity) int smblib_set_usb_suspend(struct smb_charger *chg, bool suspend) { int rc = 0; + int irq = chg->irq_info[USBIN_ICL_CHANGE_IRQ].irq; + + if (suspend && irq) { + if (chg->usb_icl_change_irq_enabled) { + disable_irq_nosync(irq); + chg->usb_icl_change_irq_enabled = false; + } + } rc = smblib_masked_write(chg, USBIN_CMD_IL_REG, USBIN_SUSPEND_BIT, suspend ? USBIN_SUSPEND_BIT : 0); @@ -434,6 +442,13 @@ int smblib_set_usb_suspend(struct smb_charger *chg, bool suspend) smblib_err(chg, "Couldn't write %s to USBIN_SUSPEND_BIT rc=%d\n", suspend ? "suspend" : "resume", rc); + if (!suspend && irq) { + if (!chg->usb_icl_change_irq_enabled) { + enable_irq(irq); + chg->usb_icl_change_irq_enabled = true; + } + } + return rc; } @@ -885,7 +900,6 @@ int smblib_set_icl_current(struct smb_charger *chg, int icl_ua) if (icl_ua < USBIN_25MA) return smblib_set_usb_suspend(chg, true); - disable_irq_nosync(chg->irq_info[USBIN_ICL_CHANGE_IRQ].irq); if (icl_ua == INT_MAX) goto override_suspend_config; @@ -943,7 +957,6 @@ override_suspend_config: } enable_icl_changed_interrupt: - enable_irq(chg->irq_info[USBIN_ICL_CHANGE_IRQ].irq); return rc; } diff --git a/drivers/power/supply/qcom/smb-lib.h b/drivers/power/supply/qcom/smb-lib.h index c97b84c34084..18714827b8d2 100644 --- a/drivers/power/supply/qcom/smb-lib.h +++ b/drivers/power/supply/qcom/smb-lib.h @@ -328,6 +328,7 @@ struct smb_charger { int fake_input_current_limited; bool pr_swap_in_progress; int typec_mode; + int usb_icl_change_irq_enabled; /* workaround flag */ u32 wa_flags; diff --git a/drivers/pwm/pwm-qpnp.c b/drivers/pwm/pwm-qpnp.c index d57bf2f3b80c..e46b1d583f40 100644 --- a/drivers/pwm/pwm-qpnp.c +++ b/drivers/pwm/pwm-qpnp.c @@ -317,6 +317,7 @@ struct _qpnp_pwm_config { struct pwm_period_config period; int supported_sizes; int force_pwm_size; + bool update_period; }; /* Public facing structure */ @@ -1211,28 +1212,32 @@ static int _pwm_config(struct qpnp_pwm_chip *chip, rc = qpnp_lpg_save_pwm_value(chip); if (rc) goto out; - rc = qpnp_lpg_configure_pwm(chip); - if (rc) - goto out; - rc = qpnp_configure_pwm_control(chip); - if (rc) - goto out; - if (!rc && chip->enabled) { - rc = qpnp_lpg_configure_pwm_state(chip, QPNP_PWM_ENABLE); - if (rc) { - pr_err("Error in configuring pwm state, rc=%d\n", rc); - return rc; - } + if (pwm_config->update_period) { + rc = qpnp_lpg_configure_pwm(chip); + if (rc) + goto out; + rc = qpnp_configure_pwm_control(chip); + if (rc) + goto out; + if (!rc && chip->enabled) { + rc = qpnp_lpg_configure_pwm_state(chip, + QPNP_PWM_ENABLE); + if (rc) { + pr_err("Error in configuring pwm state, rc=%d\n", + rc); + return rc; + } - /* Enable the glitch removal after PWM is enabled */ - rc = qpnp_lpg_glitch_removal(chip, true); - if (rc) { - pr_err("Error in enabling glitch control, rc=%d\n", rc); - return rc; + /* Enable the glitch removal after PWM is enabled */ + rc = qpnp_lpg_glitch_removal(chip, true); + if (rc) { + pr_err("Error in enabling glitch control, rc=%d\n", + rc); + return rc; + } } } - pr_debug("duty/period=%u/%u %s: pwm_value=%d (of %d)\n", (unsigned int)duty_value, (unsigned int)period_value, (tm_lvl == LVL_USEC) ? "usec" : "nsec", @@ -1375,12 +1380,14 @@ static int qpnp_pwm_config(struct pwm_chip *pwm_chip, spin_lock_irqsave(&chip->lpg_lock, flags); + chip->pwm_config.update_period = false; if (prev_period_us > INT_MAX / NSEC_PER_USEC || prev_period_us * NSEC_PER_USEC != period_ns) { qpnp_lpg_calc_period(LVL_NSEC, period_ns, chip); qpnp_lpg_save_period(chip); pwm->period = period_ns; chip->pwm_config.pwm_period = period_ns / NSEC_PER_USEC; + chip->pwm_config.update_period = true; } rc = _pwm_config(chip, LVL_NSEC, duty_ns, period_ns); @@ -1619,6 +1626,7 @@ int pwm_config_us(struct pwm_device *pwm, int duty_us, int period_us) spin_lock_irqsave(&chip->lpg_lock, flags); + chip->pwm_config.update_period = false; if (chip->pwm_config.pwm_period != period_us) { qpnp_lpg_calc_period(LVL_USEC, period_us, chip); qpnp_lpg_save_period(chip); @@ -1628,6 +1636,7 @@ int pwm_config_us(struct pwm_device *pwm, int duty_us, int period_us) pwm->period = 0; else pwm->period = (unsigned int)period_us * NSEC_PER_USEC; + chip->pwm_config.update_period = true; } rc = _pwm_config(chip, LVL_USEC, duty_us, period_us); @@ -1734,6 +1743,7 @@ static int qpnp_parse_pwm_dt_config(struct device_node *of_pwm_node, qpnp_lpg_calc_period(LVL_USEC, period, chip); qpnp_lpg_save_period(chip); chip->pwm_config.pwm_period = period; + chip->pwm_config.update_period = true; rc = _pwm_config(chip, LVL_USEC, chip->pwm_config.pwm_duty, period); diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 4f9de7fda612..188920367e84 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -2361,6 +2361,14 @@ static void regulator_disable_work(struct work_struct *work) count = rdev->deferred_disables; rdev->deferred_disables = 0; + /* + * Workqueue functions queue the new work instance while the previous + * work instance is being processed. Cancel the queued work instance + * as the work instance under processing does the job of the queued + * work instance. + */ + cancel_delayed_work(&rdev->disable_work); + for (i = 0; i < count; i++) { ret = _regulator_disable(rdev); if (ret != 0) @@ -2395,7 +2403,6 @@ static void regulator_disable_work(struct work_struct *work) int regulator_disable_deferred(struct regulator *regulator, int ms) { struct regulator_dev *rdev = regulator->rdev; - int ret; if (regulator->always_on) return 0; @@ -2405,15 +2412,11 @@ int regulator_disable_deferred(struct regulator *regulator, int ms) mutex_lock(&rdev->mutex); rdev->deferred_disables++; + mod_delayed_work(system_power_efficient_wq, &rdev->disable_work, + msecs_to_jiffies(ms)); mutex_unlock(&rdev->mutex); - ret = queue_delayed_work(system_power_efficient_wq, - &rdev->disable_work, - msecs_to_jiffies(ms)); - if (ret < 0) - return ret; - else - return 0; + return 0; } EXPORT_SYMBOL_GPL(regulator_disable_deferred); diff --git a/drivers/scsi/lpfc/lpfc_els.c b/drivers/scsi/lpfc/lpfc_els.c index 59ced8864b2f..0e6aaef9a038 100644 --- a/drivers/scsi/lpfc/lpfc_els.c +++ b/drivers/scsi/lpfc/lpfc_els.c @@ -3563,12 +3563,14 @@ lpfc_els_free_iocb(struct lpfc_hba *phba, struct lpfc_iocbq *elsiocb) } else { buf_ptr1 = (struct lpfc_dmabuf *) elsiocb->context2; lpfc_els_free_data(phba, buf_ptr1); + elsiocb->context2 = NULL; } } if (elsiocb->context3) { buf_ptr = (struct lpfc_dmabuf *) elsiocb->context3; lpfc_els_free_bpl(phba, buf_ptr); + elsiocb->context3 = NULL; } lpfc_sli_release_iocbq(phba, elsiocb); return 0; diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c index f5aeda8f014f..38e90d9c2ced 100644 --- a/drivers/scsi/lpfc/lpfc_sli.c +++ b/drivers/scsi/lpfc/lpfc_sli.c @@ -5887,18 +5887,25 @@ lpfc_sli4_alloc_resource_identifiers(struct lpfc_hba *phba) free_vfi_bmask: kfree(phba->sli4_hba.vfi_bmask); + phba->sli4_hba.vfi_bmask = NULL; free_xri_ids: kfree(phba->sli4_hba.xri_ids); + phba->sli4_hba.xri_ids = NULL; free_xri_bmask: kfree(phba->sli4_hba.xri_bmask); + phba->sli4_hba.xri_bmask = NULL; free_vpi_ids: kfree(phba->vpi_ids); + phba->vpi_ids = NULL; free_vpi_bmask: kfree(phba->vpi_bmask); + phba->vpi_bmask = NULL; free_rpi_ids: kfree(phba->sli4_hba.rpi_ids); + phba->sli4_hba.rpi_ids = NULL; free_rpi_bmask: kfree(phba->sli4_hba.rpi_bmask); + phba->sli4_hba.rpi_bmask = NULL; err_exit: return rc; } diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c index 0e59731f95ad..1f6a3b86965f 100644 --- a/drivers/scsi/qla2xxx/qla_isr.c +++ b/drivers/scsi/qla2xxx/qla_isr.c @@ -2466,6 +2466,10 @@ qla2x00_error_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, sts_entry_t *pkt) if (pkt->entry_status & RF_BUSY) res = DID_BUS_BUSY << 16; + if (pkt->entry_type == NOTIFY_ACK_TYPE && + pkt->handle == QLA_TGT_SKIP_HANDLE) + return; + sp = qla2x00_get_sp_from_handle(vha, func, req, pkt); if (sp) { sp->done(ha, sp, res); diff --git a/drivers/scsi/qla2xxx/qla_target.c b/drivers/scsi/qla2xxx/qla_target.c index f57d96984ae4..e6faa0b050d1 100644 --- a/drivers/scsi/qla2xxx/qla_target.c +++ b/drivers/scsi/qla2xxx/qla_target.c @@ -2865,7 +2865,7 @@ static int __qlt_send_term_imm_notif(struct scsi_qla_host *vha, pkt->entry_type = NOTIFY_ACK_TYPE; pkt->entry_count = 1; - pkt->handle = QLA_TGT_SKIP_HANDLE | CTIO_COMPLETION_HANDLE_MARK; + pkt->handle = QLA_TGT_SKIP_HANDLE; nack = (struct nack_to_isp *)pkt; nack->ox_id = ntfy->ox_id; diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index 9b3af788376c..8aa202faafb5 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -2497,7 +2497,8 @@ sd_read_cache_type(struct scsi_disk *sdkp, unsigned char *buffer) if (sdp->broken_fua) { sd_first_printk(KERN_NOTICE, sdkp, "Disabling FUA\n"); sdkp->DPOFUA = 0; - } else if (sdkp->DPOFUA && !sdkp->device->use_10_for_rw) { + } else if (sdkp->DPOFUA && !sdkp->device->use_10_for_rw && + !sdkp->device->use_16_for_rw) { sd_first_printk(KERN_NOTICE, sdkp, "Uses READ/WRITE(6), disabling FUA\n"); sdkp->DPOFUA = 0; diff --git a/drivers/scsi/virtio_scsi.c b/drivers/scsi/virtio_scsi.c index 7dbbb29d24c6..03a2aadf0d3c 100644 --- a/drivers/scsi/virtio_scsi.c +++ b/drivers/scsi/virtio_scsi.c @@ -533,7 +533,9 @@ static int virtscsi_queuecommand(struct virtio_scsi *vscsi, { struct Scsi_Host *shost = virtio_scsi_host(vscsi->vdev); struct virtio_scsi_cmd *cmd = scsi_cmd_priv(sc); + unsigned long flags; int req_size; + int ret; BUG_ON(scsi_sg_count(sc) > shost->sg_tablesize); @@ -561,8 +563,15 @@ static int virtscsi_queuecommand(struct virtio_scsi *vscsi, req_size = sizeof(cmd->req.cmd); } - if (virtscsi_kick_cmd(req_vq, cmd, req_size, sizeof(cmd->resp.cmd)) != 0) + ret = virtscsi_kick_cmd(req_vq, cmd, req_size, sizeof(cmd->resp.cmd)); + if (ret == -EIO) { + cmd->resp.cmd.response = VIRTIO_SCSI_S_BAD_TARGET; + spin_lock_irqsave(&req_vq->vq_lock, flags); + virtscsi_complete_cmd(vscsi, cmd); + spin_unlock_irqrestore(&req_vq->vq_lock, flags); + } else if (ret != 0) { return SCSI_MLQUEUE_HOST_BUSY; + } return 0; } diff --git a/drivers/soc/Makefile b/drivers/soc/Makefile index 6358d1256bb1..fccbdf313e08 100644 --- a/drivers/soc/Makefile +++ b/drivers/soc/Makefile @@ -7,6 +7,7 @@ obj-$(CONFIG_MACH_DOVE) += dove/ obj-$(CONFIG_ARCH_MEDIATEK) += mediatek/ obj-$(CONFIG_ARCH_QCOM) += qcom/ obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/ +obj-$(CONFIG_QCOM_SCM_QCPE) += qcom/ obj-$(CONFIG_ARCH_SUNXI) += sunxi/ obj-$(CONFIG_ARCH_TEGRA) += tegra/ obj-$(CONFIG_SOC_TI) += ti/ diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index ea008ffbc856..907960cfa9d5 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -379,6 +379,10 @@ config QCOM_SCM bool "Secure Channel Manager (SCM) support" default n +config QCOM_SCM_QCPE + bool "Para-Virtualized Secure Channel Manager (SCM) support over QCPE" + default n + menuconfig QCOM_SCM_XPU bool "Qualcomm XPU configuration driver" depends on QCOM_SCM @@ -468,6 +472,7 @@ config MINIDUMP_MAX_ENTRIES config ICNSS tristate "Platform driver for Q6 integrated connectivity" + select CNSS_UTILS ---help--- This module adds support for Q6 integrated WLAN connectivity subsystem. This module is responsible for communicating WLAN on/off diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile index 5eeede23333d..0bf54bedd6ea 100644 --- a/drivers/soc/qcom/Makefile +++ b/drivers/soc/qcom/Makefile @@ -62,6 +62,7 @@ CFLAGS_scm.o :=$(call as-instr,.arch_extension sec,-DREQUIRES_SEC=1) obj-$(CONFIG_QCOM_SCM_ERRATA) += scm-errata.o obj-$(CONFIG_QCOM_SCM) += scm.o scm-boot.o +obj-$(CONFIG_QCOM_SCM_QCPE) += scm_qcpe.o obj-$(CONFIG_QCOM_SCM_XPU) += scm-xpu.o obj-$(CONFIG_QCOM_WATCHDOG_V2) += watchdog_v2.o obj-$(CONFIG_QCOM_MEMORY_DUMP) += memory_dump.o diff --git a/drivers/soc/qcom/icnss.c b/drivers/soc/qcom/icnss.c index 21be894414bc..c74cd3b814cd 100644 --- a/drivers/soc/qcom/icnss.c +++ b/drivers/soc/qcom/icnss.c @@ -13,6 +13,7 @@ #define pr_fmt(fmt) "icnss: " fmt #include <asm/dma-iommu.h> +#include <linux/of_address.h> #include <linux/clk.h> #include <linux/iommu.h> #include <linux/export.h> @@ -365,6 +366,7 @@ struct icnss_stats { uint32_t vbatt_req; uint32_t vbatt_resp; uint32_t vbatt_req_err; + u32 rejuvenate_ind; uint32_t rejuvenate_ack_req; uint32_t rejuvenate_ack_resp; uint32_t rejuvenate_ack_err; @@ -456,6 +458,10 @@ static struct icnss_priv { struct icnss_wlan_mac_addr wlan_mac_addr; bool bypass_s1_smmu; struct mutex dev_lock; + u8 cause_for_rejuvenation; + u8 requesting_sub_system; + u16 line_number; + char function_name[QMI_WLFW_FUNCTION_NAME_LEN_V01 + 1]; } *penv; #ifdef CONFIG_ICNSS_DEBUG @@ -1691,6 +1697,60 @@ out: return ret; } +static int icnss_decode_rejuvenate_ind(void *msg, unsigned int msg_len) +{ + struct msg_desc ind_desc; + struct wlfw_rejuvenate_ind_msg_v01 ind_msg; + int ret = 0; + + if (!penv || !penv->wlfw_clnt) { + ret = -ENODEV; + goto out; + } + + memset(&ind_msg, 0, sizeof(ind_msg)); + + ind_desc.msg_id = QMI_WLFW_REJUVENATE_IND_V01; + ind_desc.max_msg_len = WLFW_REJUVENATE_IND_MSG_V01_MAX_MSG_LEN; + ind_desc.ei_array = wlfw_rejuvenate_ind_msg_v01_ei; + + ret = qmi_kernel_decode(&ind_desc, &ind_msg, msg, msg_len); + if (ret < 0) { + icnss_pr_err("Failed to decode rejuvenate ind message: ret %d, msg_len %u\n", + ret, msg_len); + goto out; + } + + if (ind_msg.cause_for_rejuvenation_valid) + penv->cause_for_rejuvenation = ind_msg.cause_for_rejuvenation; + else + penv->cause_for_rejuvenation = 0; + if (ind_msg.requesting_sub_system_valid) + penv->requesting_sub_system = ind_msg.requesting_sub_system; + else + penv->requesting_sub_system = 0; + if (ind_msg.line_number_valid) + penv->line_number = ind_msg.line_number; + else + penv->line_number = 0; + if (ind_msg.function_name_valid) + memcpy(penv->function_name, ind_msg.function_name, + QMI_WLFW_FUNCTION_NAME_LEN_V01 + 1); + else + memset(penv->function_name, 0, + QMI_WLFW_FUNCTION_NAME_LEN_V01 + 1); + + icnss_pr_info("Cause for rejuvenation: 0x%x, requesting sub-system: 0x%x, line number: %u, function name: %s\n", + penv->cause_for_rejuvenation, + penv->requesting_sub_system, + penv->line_number, + penv->function_name); + + penv->stats.rejuvenate_ind++; +out: + return ret; +} + static int wlfw_rejuvenate_ack_send_sync_msg(struct icnss_priv *priv) { int ret; @@ -1884,6 +1944,7 @@ static void icnss_qmi_wlfw_clnt_ind(struct qmi_handle *handle, msg_id, penv->state); icnss_ignore_qmi_timeout(true); + icnss_decode_rejuvenate_ind(msg, msg_len); event_data = kzalloc(sizeof(*event_data), GFP_KERNEL); if (event_data == NULL) return; @@ -3786,6 +3847,26 @@ static int icnss_stats_show_capability(struct seq_file *s, return 0; } +static int icnss_stats_show_rejuvenate_info(struct seq_file *s, + struct icnss_priv *priv) +{ + if (priv->stats.rejuvenate_ind) { + seq_puts(s, "\n<---------------- Rejuvenate Info ----------------->\n"); + seq_printf(s, "Number of Rejuvenations: %u\n", + priv->stats.rejuvenate_ind); + seq_printf(s, "Cause for Rejuvenation: 0x%x\n", + priv->cause_for_rejuvenation); + seq_printf(s, "Requesting Sub-System: 0x%x\n", + priv->requesting_sub_system); + seq_printf(s, "Line Number: %u\n", + priv->line_number); + seq_printf(s, "Function Name: %s\n", + priv->function_name); + } + + return 0; +} + static int icnss_stats_show_events(struct seq_file *s, struct icnss_priv *priv) { int i; @@ -3851,6 +3932,7 @@ static int icnss_stats_show(struct seq_file *s, void *data) ICNSS_STATS_DUMP(s, priv, vbatt_req); ICNSS_STATS_DUMP(s, priv, vbatt_resp); ICNSS_STATS_DUMP(s, priv, vbatt_req_err); + ICNSS_STATS_DUMP(s, priv, rejuvenate_ind); ICNSS_STATS_DUMP(s, priv, rejuvenate_ack_req); ICNSS_STATS_DUMP(s, priv, rejuvenate_ack_resp); ICNSS_STATS_DUMP(s, priv, rejuvenate_ack_err); @@ -3875,6 +3957,8 @@ static int icnss_stats_show(struct seq_file *s, void *data) icnss_stats_show_capability(s, priv); + icnss_stats_show_rejuvenate_info(s, priv); + icnss_stats_show_events(s, priv); icnss_stats_show_state(s, priv); @@ -4215,6 +4299,9 @@ static int icnss_probe(struct platform_device *pdev) int i; struct device *dev = &pdev->dev; struct icnss_priv *priv; + const __be32 *addrp; + u64 prop_size = 0; + struct device_node *np; if (penv) { icnss_pr_err("Driver is already initialized\n"); @@ -4286,24 +4373,53 @@ static int icnss_probe(struct platform_device *pdev) } } - ret = of_property_read_u32(dev->of_node, "qcom,wlan-msa-memory", - &priv->msa_mem_size); + np = of_parse_phandle(dev->of_node, + "qcom,wlan-msa-fixed-region", 0); + if (np) { + addrp = of_get_address(np, 0, &prop_size, NULL); + if (!addrp) { + icnss_pr_err("Failed to get assigned-addresses or property\n"); + ret = -EINVAL; + goto out; + } - if (ret || priv->msa_mem_size == 0) { - icnss_pr_err("Fail to get MSA Memory Size: %u, ret: %d\n", - priv->msa_mem_size, ret); - goto out; - } + priv->msa_pa = of_translate_address(np, addrp); + if (priv->msa_pa == OF_BAD_ADDR) { + icnss_pr_err("Failed to translate MSA PA from device-tree\n"); + ret = -EINVAL; + goto out; + } - priv->msa_va = dmam_alloc_coherent(&pdev->dev, priv->msa_mem_size, - &priv->msa_pa, GFP_KERNEL); - if (!priv->msa_va) { - icnss_pr_err("DMA alloc failed for MSA\n"); - ret = -ENOMEM; - goto out; + priv->msa_va = memremap(priv->msa_pa, + (unsigned long)prop_size, MEMREMAP_WT); + if (!priv->msa_va) { + icnss_pr_err("MSA PA ioremap failed: phy addr: %pa\n", + &priv->msa_pa); + ret = -EINVAL; + goto out; + } + priv->msa_mem_size = prop_size; + } else { + ret = of_property_read_u32(dev->of_node, "qcom,wlan-msa-memory", + &priv->msa_mem_size); + if (ret || priv->msa_mem_size == 0) { + icnss_pr_err("Fail to get MSA Memory Size: %u ret: %d\n", + priv->msa_mem_size, ret); + goto out; + } + + priv->msa_va = dmam_alloc_coherent(&pdev->dev, + priv->msa_mem_size, &priv->msa_pa, GFP_KERNEL); + + if (!priv->msa_va) { + icnss_pr_err("DMA alloc failed for MSA\n"); + ret = -ENOMEM; + goto out; + } } - icnss_pr_dbg("MSA pa: %pa, MSA va: 0x%p\n", &priv->msa_pa, - priv->msa_va); + + icnss_pr_dbg("MSA pa: %pa, MSA va: 0x%p MSA Memory Size: 0x%x\n", + &priv->msa_pa, (void *)priv->msa_va, priv->msa_mem_size); res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "smmu_iova_base"); diff --git a/drivers/soc/qcom/ipc_router_mhi_xprt.c b/drivers/soc/qcom/ipc_router_mhi_xprt.c index e5f6104bd7de..adf4078818a5 100644 --- a/drivers/soc/qcom/ipc_router_mhi_xprt.c +++ b/drivers/soc/qcom/ipc_router_mhi_xprt.c @@ -132,12 +132,11 @@ struct ipc_router_mhi_xprt { struct ipc_router_mhi_xprt_work { struct ipc_router_mhi_xprt *mhi_xprtp; enum MHI_CLIENT_CHANNEL chan_id; - struct work_struct work; }; static void mhi_xprt_read_data(struct work_struct *work); -static void mhi_xprt_enable_event(struct work_struct *work); -static void mhi_xprt_disable_event(struct work_struct *work); +static void mhi_xprt_enable_event(struct ipc_router_mhi_xprt_work *xprt_work); +static void mhi_xprt_disable_event(struct ipc_router_mhi_xprt_work *xprt_work); /** * ipc_router_mhi_xprt_config - Config. Info. of each MHI XPRT @@ -574,8 +573,6 @@ static int ipc_router_mhi_close(struct msm_ipc_router_xprt *xprt) mhi_xprtp->ch_hndl.in_chan_enabled = false; mutex_unlock(&mhi_xprtp->ch_hndl.state_lock); flush_workqueue(mhi_xprtp->wq); - mhi_close_channel(mhi_xprtp->ch_hndl.in_handle); - mhi_close_channel(mhi_xprtp->ch_hndl.out_handle); return 0; } @@ -600,10 +597,8 @@ static void mhi_xprt_sft_close_done(struct msm_ipc_router_xprt *xprt) * * This work is scheduled when the MHI link to the peripheral is up. */ -static void mhi_xprt_enable_event(struct work_struct *work) +static void mhi_xprt_enable_event(struct ipc_router_mhi_xprt_work *xprt_work) { - struct ipc_router_mhi_xprt_work *xprt_work = - container_of(work, struct ipc_router_mhi_xprt_work, work); struct ipc_router_mhi_xprt *mhi_xprtp = xprt_work->mhi_xprtp; int rc; bool notify = false; @@ -613,7 +608,7 @@ static void mhi_xprt_enable_event(struct work_struct *work) if (rc) { IPC_RTR_ERR("%s Failed to open chan 0x%x, rc %d\n", __func__, mhi_xprtp->ch_hndl.out_chan_id, rc); - goto out_enable_event; + return; } mutex_lock(&mhi_xprtp->ch_hndl.state_lock); mhi_xprtp->ch_hndl.out_chan_enabled = true; @@ -625,7 +620,7 @@ static void mhi_xprt_enable_event(struct work_struct *work) if (rc) { IPC_RTR_ERR("%s Failed to open chan 0x%x, rc %d\n", __func__, mhi_xprtp->ch_hndl.in_chan_id, rc); - goto out_enable_event; + return; } mutex_lock(&mhi_xprtp->ch_hndl.state_lock); mhi_xprtp->ch_hndl.in_chan_enabled = true; @@ -643,11 +638,11 @@ static void mhi_xprt_enable_event(struct work_struct *work) } if (xprt_work->chan_id != mhi_xprtp->ch_hndl.in_chan_id) - goto out_enable_event; + return; rc = mhi_xprt_queue_in_buffers(mhi_xprtp, mhi_xprtp->ch_hndl.num_trbs); if (rc > 0) - goto out_enable_event; + return; IPC_RTR_ERR("%s: Could not queue one TRB atleast\n", __func__); mutex_lock(&mhi_xprtp->ch_hndl.state_lock); @@ -656,9 +651,6 @@ static void mhi_xprt_enable_event(struct work_struct *work) if (notify) msm_ipc_router_xprt_notify(&mhi_xprtp->xprt, IPC_ROUTER_XPRT_EVENT_CLOSE, NULL); - mhi_close_channel(mhi_xprtp->ch_hndl.in_handle); -out_enable_event: - kfree(xprt_work); } /** @@ -667,10 +659,8 @@ out_enable_event: * * This work is scheduled when the MHI link to the peripheral is down. */ -static void mhi_xprt_disable_event(struct work_struct *work) +static void mhi_xprt_disable_event(struct ipc_router_mhi_xprt_work *xprt_work) { - struct ipc_router_mhi_xprt_work *xprt_work = - container_of(work, struct ipc_router_mhi_xprt_work, work); struct ipc_router_mhi_xprt *mhi_xprtp = xprt_work->mhi_xprtp; bool notify = false; @@ -681,7 +671,6 @@ static void mhi_xprt_disable_event(struct work_struct *work) mhi_xprtp->ch_hndl.out_chan_enabled = false; mutex_unlock(&mhi_xprtp->ch_hndl.state_lock); wake_up(&mhi_xprtp->write_wait_q); - mhi_close_channel(mhi_xprtp->ch_hndl.out_handle); } else if (xprt_work->chan_id == mhi_xprtp->ch_hndl.in_chan_id) { mutex_lock(&mhi_xprtp->ch_hndl.state_lock); notify = mhi_xprtp->ch_hndl.out_chan_enabled && @@ -691,7 +680,6 @@ static void mhi_xprt_disable_event(struct work_struct *work) /* Queue a read work to remove any partially read packets */ queue_work(mhi_xprtp->wq, &mhi_xprtp->read_work); flush_workqueue(mhi_xprtp->wq); - mhi_close_channel(mhi_xprtp->ch_hndl.in_handle); } if (notify) { @@ -702,7 +690,6 @@ static void mhi_xprt_disable_event(struct work_struct *work) __func__, mhi_xprtp->xprt.name); wait_for_completion(&mhi_xprtp->sft_close_complete); } - kfree(xprt_work); } /** @@ -743,7 +730,7 @@ static void mhi_xprt_xfer_event(struct mhi_cb_info *cb_info) static void ipc_router_mhi_xprt_cb(struct mhi_cb_info *cb_info) { struct ipc_router_mhi_xprt *mhi_xprtp; - struct ipc_router_mhi_xprt_work *xprt_work; + struct ipc_router_mhi_xprt_work xprt_work; if (cb_info->result == NULL) { IPC_RTR_ERR("%s: Result not available in cb_info\n", __func__); @@ -751,23 +738,16 @@ static void ipc_router_mhi_xprt_cb(struct mhi_cb_info *cb_info) } mhi_xprtp = (struct ipc_router_mhi_xprt *)(cb_info->result->user_data); + xprt_work.mhi_xprtp = mhi_xprtp; + xprt_work.chan_id = cb_info->chan; switch (cb_info->cb_reason) { - case MHI_CB_MHI_ENABLED: + case MHI_CB_MHI_SHUTDOWN: + case MHI_CB_SYS_ERROR: case MHI_CB_MHI_DISABLED: - xprt_work = kmalloc(sizeof(*xprt_work), GFP_KERNEL); - if (!xprt_work) { - IPC_RTR_ERR("%s: Couldn't handle %d event on %s\n", - __func__, cb_info->cb_reason, - mhi_xprtp->xprt_name); - return; - } - xprt_work->mhi_xprtp = mhi_xprtp; - xprt_work->chan_id = cb_info->chan; - if (cb_info->cb_reason == MHI_CB_MHI_ENABLED) - INIT_WORK(&xprt_work->work, mhi_xprt_enable_event); - else - INIT_WORK(&xprt_work->work, mhi_xprt_disable_event); - queue_work(mhi_xprtp->wq, &xprt_work->work); + mhi_xprt_disable_event(&xprt_work); + break; + case MHI_CB_MHI_ENABLED: + mhi_xprt_enable_event(&xprt_work); break; case MHI_CB_XFER: mhi_xprt_xfer_event(cb_info); @@ -788,22 +768,37 @@ static void ipc_router_mhi_xprt_cb(struct mhi_cb_info *cb_info) * This function is called when a new XPRT is added. */ static int ipc_router_mhi_driver_register( - struct ipc_router_mhi_xprt *mhi_xprtp) + struct ipc_router_mhi_xprt *mhi_xprtp, struct device *dev) { - int rc_status; - - rc_status = mhi_register_channel(&mhi_xprtp->ch_hndl.out_handle, NULL); - if (rc_status) { + int rc; + const char *node_name = "qcom,mhi"; + struct mhi_client_info_t *mhi_info; + + if (!mhi_is_device_ready(dev, node_name)) + return -EPROBE_DEFER; + + mhi_info = &mhi_xprtp->ch_hndl.out_clnt_info; + mhi_info->chan = mhi_xprtp->ch_hndl.out_chan_id; + mhi_info->dev = dev; + mhi_info->node_name = node_name; + mhi_info->user_data = mhi_xprtp; + rc = mhi_register_channel(&mhi_xprtp->ch_hndl.out_handle, mhi_info); + if (rc) { IPC_RTR_ERR("%s: Error %d registering out_chan for %s\n", - __func__, rc_status, mhi_xprtp->xprt_name); + __func__, rc, mhi_xprtp->xprt_name); return -EFAULT; } - rc_status = mhi_register_channel(&mhi_xprtp->ch_hndl.in_handle, NULL); - if (rc_status) { + mhi_info = &mhi_xprtp->ch_hndl.in_clnt_info; + mhi_info->chan = mhi_xprtp->ch_hndl.in_chan_id; + mhi_info->dev = dev; + mhi_info->node_name = node_name; + mhi_info->user_data = mhi_xprtp; + rc = mhi_register_channel(&mhi_xprtp->ch_hndl.in_handle, mhi_info); + if (rc) { mhi_deregister_channel(mhi_xprtp->ch_hndl.out_handle); IPC_RTR_ERR("%s: Error %d registering in_chan for %s\n", - __func__, rc_status, mhi_xprtp->xprt_name); + __func__, rc, mhi_xprtp->xprt_name); return -EFAULT; } return 0; @@ -820,7 +815,8 @@ static int ipc_router_mhi_driver_register( * the MHI XPRT configurations from device tree. */ static int ipc_router_mhi_config_init( - struct ipc_router_mhi_xprt_config *mhi_xprt_config) + struct ipc_router_mhi_xprt_config *mhi_xprt_config, + struct device *dev) { struct ipc_router_mhi_xprt *mhi_xprtp; char wq_name[XPRT_NAME_LEN]; @@ -879,7 +875,7 @@ static int ipc_router_mhi_config_init( INIT_LIST_HEAD(&mhi_xprtp->rx_addr_map_list); spin_lock_init(&mhi_xprtp->rx_addr_map_list_lock); - rc = ipc_router_mhi_driver_register(mhi_xprtp); + rc = ipc_router_mhi_driver_register(mhi_xprtp, dev); return rc; } @@ -963,7 +959,7 @@ static int ipc_router_mhi_xprt_probe(struct platform_device *pdev) return rc; } - rc = ipc_router_mhi_config_init(&mhi_xprt_config); + rc = ipc_router_mhi_config_init(&mhi_xprt_config, &pdev->dev); if (rc) { IPC_RTR_ERR("%s: init failed\n", __func__); return rc; diff --git a/drivers/soc/qcom/scm_qcpe.c b/drivers/soc/qcom/scm_qcpe.c new file mode 100644 index 000000000000..54a978157bda --- /dev/null +++ b/drivers/soc/qcom/scm_qcpe.c @@ -0,0 +1,1137 @@ +/* Copyright (c) 2010-2017, The Linux Foundation. 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 version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/slab.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/errno.h> +#include <linux/err.h> +#include <linux/init.h> +#include <linux/delay.h> + +#include <asm/cacheflush.h> +#include <asm/compiler.h> + +#include <soc/qcom/scm.h> + +#define CREATE_TRACE_POINTS +#include <trace/events/scm.h> + +#include <uapi/linux/habmm.h> + +#define SCM_ENOMEM (-5) +#define SCM_EOPNOTSUPP (-4) +#define SCM_EINVAL_ADDR (-3) +#define SCM_EINVAL_ARG (-2) +#define SCM_ERROR (-1) +#define SCM_INTERRUPTED (1) +#define SCM_EBUSY (-55) +#define SCM_V2_EBUSY (-12) + +static DEFINE_MUTEX(scm_lock); + +/* + * MSM8996 V2 requires a lock to protect against + * concurrent accesses between the limits management + * driver and the clock controller + */ +DEFINE_MUTEX(scm_lmh_lock); + +#define SCM_EBUSY_WAIT_MS 30 +#define SCM_EBUSY_MAX_RETRY 67 + +#define N_EXT_SCM_ARGS 7 +#define FIRST_EXT_ARG_IDX 3 +#define SMC_ATOMIC_SYSCALL 31 +#define N_REGISTER_ARGS (MAX_SCM_ARGS - N_EXT_SCM_ARGS + 1) +#define SMC64_MASK 0x40000000 +#define SMC_ATOMIC_MASK 0x80000000 +#define IS_CALL_AVAIL_CMD 1 + +#define SCM_BUF_LEN(__cmd_size, __resp_size) ({ \ + size_t x = __cmd_size + __resp_size; \ + size_t y = sizeof(struct scm_command) + sizeof(struct scm_response); \ + size_t result; \ + if (x < __cmd_size || (x + y) < x) \ + result = 0; \ + else \ + result = x + y; \ + result; \ + }) +/** + * struct scm_command - one SCM command buffer + * @len: total available memory for command and response + * @buf_offset: start of command buffer + * @resp_hdr_offset: start of response buffer + * @id: command to be executed + * @buf: buffer returned from scm_get_command_buffer() + * + * An SCM command is laid out in memory as follows: + * + * ------------------- <--- struct scm_command + * | command header | + * ------------------- <--- scm_get_command_buffer() + * | command buffer | + * ------------------- <--- struct scm_response and + * | response header | scm_command_to_response() + * ------------------- <--- scm_get_response_buffer() + * | response buffer | + * ------------------- + * + * There can be arbitrary padding between the headers and buffers so + * you should always use the appropriate scm_get_*_buffer() routines + * to access the buffers in a safe manner. + */ +struct scm_command { + u32 len; + u32 buf_offset; + u32 resp_hdr_offset; + u32 id; + u32 buf[0]; +}; + +/** + * struct scm_response - one SCM response buffer + * @len: total available memory for response + * @buf_offset: start of response data relative to start of scm_response + * @is_complete: indicates if the command has finished processing + */ +struct scm_response { + u32 len; + u32 buf_offset; + u32 is_complete; +}; + +#ifdef CONFIG_ARM64 + +#define R0_STR "x0" +#define R1_STR "x1" +#define R2_STR "x2" +#define R3_STR "x3" +#define R4_STR "x4" +#define R5_STR "x5" +#define R6_STR "x6" + +/* Outer caches unsupported on ARM64 platforms */ +#define outer_inv_range(x, y) +#define outer_flush_range(x, y) + +#define __cpuc_flush_dcache_area __flush_dcache_area + +#else + +#define R0_STR "r0" +#define R1_STR "r1" +#define R2_STR "r2" +#define R3_STR "r3" +#define R4_STR "r4" +#define R5_STR "r5" +#define R6_STR "r6" + +#endif + +/** + * scm_command_to_response() - Get a pointer to a scm_response + * @cmd: command + * + * Returns a pointer to a response for a command. + */ +static inline struct scm_response *scm_command_to_response( + const struct scm_command *cmd) +{ + return (void *)cmd + cmd->resp_hdr_offset; +} + +/** + * scm_get_command_buffer() - Get a pointer to a command buffer + * @cmd: command + * + * Returns a pointer to the command buffer of a command. + */ +static inline void *scm_get_command_buffer(const struct scm_command *cmd) +{ + return (void *)cmd->buf; +} + +/** + * scm_get_response_buffer() - Get a pointer to a response buffer + * @rsp: response + * + * Returns a pointer to a response buffer of a response. + */ +static inline void *scm_get_response_buffer(const struct scm_response *rsp) +{ + return (void *)rsp + rsp->buf_offset; +} + +static int scm_remap_error(int err) +{ + switch (err) { + case SCM_ERROR: + return -EIO; + case SCM_EINVAL_ADDR: + case SCM_EINVAL_ARG: + return -EINVAL; + case SCM_EOPNOTSUPP: + return -EOPNOTSUPP; + case SCM_ENOMEM: + return -ENOMEM; + case SCM_EBUSY: + return SCM_EBUSY; + case SCM_V2_EBUSY: + return SCM_V2_EBUSY; + } + return -EINVAL; +} + +static int scm_call_qcpe(u32 fn_id, struct scm_desc *desc) +{ + static bool opened; + static u32 handle; + u32 ret; + u32 size_bytes; + + struct smc_params_s { + uint64_t x0; + uint64_t x1; + uint64_t x2; + uint64_t x3; + uint64_t x4; + uint64_t x5; + uint64_t sid; + } smc_params; + + pr_info("scm_call_qcpe: IN: 0x%x, 0x%x, 0x%llx, 0x%llx, 0x%llx, 0x%llx, 0x%llx, 0x%llx, 0x%llx", + fn_id, desc->arginfo, desc->args[0], desc->args[1], + desc->args[2], desc->args[3], desc->args[4], + desc->args[5], desc->args[6]); + + if (!opened) { + ret = habmm_socket_open(&handle, MM_QCPE_VM1, 0, 0); + if (ret != HAB_OK) { + pr_err("scm_call2: habmm_socket_open failed with ret = %d", + ret); + return ret; + } + opened = true; + } + + smc_params.x0 = fn_id | 0x40000000; /* SMC64_MASK */ + smc_params.x1 = desc->arginfo; + smc_params.x2 = desc->args[0]; + smc_params.x3 = desc->args[1]; + smc_params.x4 = desc->args[2]; + smc_params.x5 = desc->x5; + smc_params.sid = 0; + + ret = habmm_socket_send(handle, &smc_params, sizeof(smc_params), 0); + if (ret != HAB_OK) + return ret; + + size_bytes = sizeof(smc_params); + + ret = habmm_socket_recv(handle, &smc_params, &size_bytes, 0, 0); + if (ret != HAB_OK) + return ret; + + desc->ret[0] = smc_params.x1; + desc->ret[1] = smc_params.x2; + desc->ret[2] = smc_params.x3; + + pr_info("scm_call_qcpe: OUT: 0x%llx, 0x%llx, 0x%llx", + desc->ret[0], desc->ret[1], desc->ret[2]); + + return 0; +} + +static u32 smc(u32 cmd_addr) +{ + int context_id; + int ret; + uint64_t x0; + struct scm_desc desc = {0}; + + register u32 r0 asm("r0") = 1; + register u32 r1 asm("r1") = (uintptr_t)&context_id; + register u32 r2 asm("r2") = cmd_addr; + + x0 = r0; + desc.arginfo = r1; + desc.args[0] = r2; + + ret = scm_call_qcpe(x0, &desc); + + if (ret < 0) + return scm_remap_error(ret); + + return 0; +} + +static int __scm_call(const struct scm_command *cmd) +{ + int ret; + u32 cmd_addr = virt_to_phys(cmd); + + /* + * Flush the command buffer so that the secure world sees + * the correct data. + */ + __cpuc_flush_dcache_area((void *)cmd, cmd->len); + outer_flush_range(cmd_addr, cmd_addr + cmd->len); + + ret = smc(cmd_addr); + if (ret < 0) { + if (ret != SCM_EBUSY) + pr_err("scm_call failed with error code %d\n", ret); + ret = scm_remap_error(ret); + } + return ret; +} + +#ifndef CONFIG_ARM64 +static void scm_inv_range(unsigned long start, unsigned long end) +{ + u32 cacheline_size, ctr; + + asm volatile("mrc p15, 0, %0, c0, c0, 1" : "=r" (ctr)); + cacheline_size = 4 << ((ctr >> 16) & 0xf); + + start = round_down(start, cacheline_size); + end = round_up(end, cacheline_size); + outer_inv_range(start, end); + while (start < end) { + asm ("mcr p15, 0, %0, c7, c6, 1" : : "r" (start) + : "memory"); + start += cacheline_size; + } + mb(); /* Make sure memory is visible to TZ */ + isb(); +} +#else + +static void scm_inv_range(unsigned long start, unsigned long end) +{ + dmac_inv_range((void *)start, (void *)end); +} +#endif + +/** + * scm_call_common() - Send an SCM command + * @svc_id: service identifier + * @cmd_id: command identifier + * @cmd_buf: command buffer + * @cmd_len: length of the command buffer + * @resp_buf: response buffer + * @resp_len: length of the response buffer + * @scm_buf: internal scm structure used for passing data + * @scm_buf_len: length of the internal scm structure + * + * Core function to scm call. Initializes the given cmd structure with + * appropriate values and makes the actual scm call. Validation of cmd + * pointer and length must occur in the calling function. + * + * Returns the appropriate error code from the scm call + */ + +static int scm_call_common(u32 svc_id, u32 cmd_id, const void *cmd_buf, + size_t cmd_len, void *resp_buf, size_t resp_len, + struct scm_command *scm_buf, + size_t scm_buf_length) +{ + int ret; + struct scm_response *rsp; + unsigned long start, end; + + scm_buf->len = scm_buf_length; + scm_buf->buf_offset = offsetof(struct scm_command, buf); + scm_buf->resp_hdr_offset = scm_buf->buf_offset + cmd_len; + scm_buf->id = (svc_id << 10) | cmd_id; + + if (cmd_buf) + memcpy(scm_get_command_buffer(scm_buf), cmd_buf, cmd_len); + + mutex_lock(&scm_lock); + ret = __scm_call(scm_buf); + mutex_unlock(&scm_lock); + if (ret) + return ret; + + rsp = scm_command_to_response(scm_buf); + start = (unsigned long)rsp; + + do { + scm_inv_range(start, start + sizeof(*rsp)); + } while (!rsp->is_complete); + + end = (unsigned long)scm_get_response_buffer(rsp) + resp_len; + scm_inv_range(start, end); + + if (resp_buf) + memcpy(resp_buf, scm_get_response_buffer(rsp), resp_len); + + return ret; +} + +/* + * Sometimes the secure world may be busy waiting for a particular resource. + * In those situations, it is expected that the secure world returns a special + * error code (SCM_EBUSY). Retry any scm_call that fails with this error code, + * but with a timeout in place. Also, don't move this into scm_call_common, + * since we want the first attempt to be the "fastpath". + */ +static int _scm_call_retry(u32 svc_id, u32 cmd_id, const void *cmd_buf, + size_t cmd_len, void *resp_buf, size_t resp_len, + struct scm_command *cmd, + size_t len) +{ + int ret, retry_count = 0; + + do { + ret = scm_call_common(svc_id, cmd_id, cmd_buf, cmd_len, + resp_buf, resp_len, cmd, len); + if (ret == SCM_EBUSY) + msleep(SCM_EBUSY_WAIT_MS); + if (retry_count == 33) + pr_warn("scm: secure world has been busy for 1 second!\n"); + } while (ret == SCM_EBUSY && (retry_count++ < SCM_EBUSY_MAX_RETRY)); + + if (ret == SCM_EBUSY) + pr_err("scm: secure world busy (rc = SCM_EBUSY)\n"); + + return ret; +} + +/** + * scm_call_noalloc - Send an SCM command + * + * Same as scm_call except clients pass in a buffer (@scm_buf) to be used for + * scm internal structures. The buffer should be allocated with + * DEFINE_SCM_BUFFER to account for the proper alignment and size. + */ +int scm_call_noalloc(u32 svc_id, u32 cmd_id, const void *cmd_buf, + size_t cmd_len, void *resp_buf, size_t resp_len, + void *scm_buf, size_t scm_buf_len) +{ + int ret; + size_t len = SCM_BUF_LEN(cmd_len, resp_len); + + if (len == 0) + return -EINVAL; + + if (!IS_ALIGNED((unsigned long)scm_buf, PAGE_SIZE)) + return -EINVAL; + + memset(scm_buf, 0, scm_buf_len); + + ret = scm_call_common(svc_id, cmd_id, cmd_buf, cmd_len, resp_buf, + resp_len, scm_buf, len); + return ret; + +} + +struct scm_extra_arg { + union { + u32 args32[N_EXT_SCM_ARGS]; + u64 args64[N_EXT_SCM_ARGS]; + }; +}; + +static enum scm_interface_version { + SCM_UNKNOWN, + SCM_LEGACY, + SCM_ARMV8_32, + SCM_ARMV8_64, +} scm_version = SCM_UNKNOWN; + +/* This will be set to specify SMC32 or SMC64 */ +static u32 scm_version_mask; + +bool is_scm_armv8(void) +{ + int ret; + u64 ret1, x0; + + struct scm_desc desc = {0}; + + if (likely(scm_version != SCM_UNKNOWN)) + return (scm_version == SCM_ARMV8_32) || + (scm_version == SCM_ARMV8_64); + /* + * This is a one time check that runs on the first ever + * invocation of is_scm_armv8. We might be called in atomic + * context so no mutexes etc. Also, we can't use the scm_call2 + * or scm_call2_APIs directly since they depend on this init. + */ + + /* First try a SMC64 call */ + scm_version = SCM_ARMV8_64; + ret1 = 0; + x0 = SCM_SIP_FNID(SCM_SVC_INFO, IS_CALL_AVAIL_CMD) | SMC_ATOMIC_MASK; + + desc.arginfo = SCM_ARGS(1); + desc.args[0] = x0; + + ret = scm_call_qcpe(x0 | SMC64_MASK, &desc); + + ret1 = desc.arginfo; + + if (ret || !ret1) { + /* Try SMC32 call */ + ret1 = 0; + + desc.arginfo = SCM_ARGS(1); + desc.args[0] = x0; + + ret = scm_call_qcpe(x0, &desc); + + if (ret || !ret1) + scm_version = SCM_LEGACY; + else + scm_version = SCM_ARMV8_32; + } else + scm_version_mask = SMC64_MASK; + + pr_debug("scm_call: scm version is %x, mask is %x\n", scm_version, + scm_version_mask); + + return (scm_version == SCM_ARMV8_32) || + (scm_version == SCM_ARMV8_64); +} +EXPORT_SYMBOL(is_scm_armv8); + +/* + * If there are more than N_REGISTER_ARGS, allocate a buffer and place + * the additional arguments in it. The extra argument buffer will be + * pointed to by X5. + */ +static int allocate_extra_arg_buffer(struct scm_desc *desc, gfp_t flags) +{ + int i, j; + struct scm_extra_arg *argbuf; + int arglen = desc->arginfo & 0xf; + size_t argbuflen = PAGE_ALIGN(sizeof(struct scm_extra_arg)); + + desc->x5 = desc->args[FIRST_EXT_ARG_IDX]; + + if (likely(arglen <= N_REGISTER_ARGS)) { + desc->extra_arg_buf = NULL; + return 0; + } + + argbuf = kzalloc(argbuflen, flags); + if (!argbuf) + return -ENOMEM; + + desc->extra_arg_buf = argbuf; + + j = FIRST_EXT_ARG_IDX; + if (scm_version == SCM_ARMV8_64) + for (i = 0; i < N_EXT_SCM_ARGS; i++) + argbuf->args64[i] = desc->args[j++]; + else + for (i = 0; i < N_EXT_SCM_ARGS; i++) + argbuf->args32[i] = desc->args[j++]; + desc->x5 = virt_to_phys(argbuf); + __cpuc_flush_dcache_area(argbuf, argbuflen); + outer_flush_range(virt_to_phys(argbuf), + virt_to_phys(argbuf) + argbuflen); + + return 0; +} + +/** + * scm_call2() - Invoke a syscall in the secure world + * @fn_id: The function ID for this syscall + * @desc: Descriptor structure containing arguments and return values + * + * Sends a command to the SCM and waits for the command to finish processing. + * This should *only* be called in pre-emptible context. + * + * A note on cache maintenance: + * Note that any buffers that are expected to be accessed by the secure world + * must be flushed before invoking scm_call and invalidated in the cache + * immediately after scm_call returns. An important point that must be noted + * is that on ARMV8 architectures, invalidation actually also causes a dirty + * cache line to be cleaned (flushed + unset-dirty-bit). Therefore it is of + * paramount importance that the buffer be flushed before invoking scm_call2, + * even if you don't care about the contents of that buffer. + * + * Note that cache maintenance on the argument buffer (desc->args) is taken care + * of by scm_call2; however, callers are responsible for any other cached + * buffers passed over to the secure world. +*/ +int scm_call2(u32 fn_id, struct scm_desc *desc) +{ + int arglen = desc->arginfo & 0xf; + int ret; + u64 x0; + + if (unlikely(!is_scm_armv8())) + return -ENODEV; + + ret = allocate_extra_arg_buffer(desc, GFP_NOIO); + if (ret) + return ret; + + x0 = fn_id | scm_version_mask; + + mutex_lock(&scm_lock); + + if (SCM_SVC_ID(fn_id) == SCM_SVC_LMH) + mutex_lock(&scm_lmh_lock); + + desc->ret[0] = desc->ret[1] = desc->ret[2] = 0; + + trace_scm_call_start(x0, desc); + + ret = scm_call_qcpe(x0, desc); + + trace_scm_call_end(desc); + + if (SCM_SVC_ID(fn_id) == SCM_SVC_LMH) + mutex_unlock(&scm_lmh_lock); + + mutex_unlock(&scm_lock); + + if (ret < 0) + pr_err("scm_call failed: func id %#llx, ret: %d, syscall returns: %#llx, %#llx, %#llx\n", + x0, ret, desc->ret[0], desc->ret[1], desc->ret[2]); + + if (arglen > N_REGISTER_ARGS) + kfree(desc->extra_arg_buf); + if (ret < 0) + return scm_remap_error(ret); + return 0; +} +EXPORT_SYMBOL(scm_call2); + +/** + * scm_call2_atomic() - Invoke a syscall in the secure world + * + * Similar to scm_call2 except that this can be invoked in atomic context. + * There is also no retry mechanism implemented. Please ensure that the + * secure world syscall can be executed in such a context and can complete + * in a timely manner. + */ +int scm_call2_atomic(u32 fn_id, struct scm_desc *desc) +{ + int arglen = desc->arginfo & 0xf; + int ret; + u64 x0; + + if (unlikely(!is_scm_armv8())) + return -ENODEV; + + ret = allocate_extra_arg_buffer(desc, GFP_ATOMIC); + if (ret) + return ret; + + x0 = fn_id | BIT(SMC_ATOMIC_SYSCALL) | scm_version_mask; + + pr_debug("scm_call: func id %#llx, args: %#x, %#llx, %#llx, %#llx, %#llx\n", + x0, desc->arginfo, desc->args[0], desc->args[1], + desc->args[2], desc->x5); + + ret = scm_call_qcpe(x0, desc); + + if (ret < 0) + pr_err("scm_call failed: func id %#llx, arginfo: %#x, args: %#llx, %#llx, %#llx, %#llx, ret: %d, syscall returns: %#llx, %#llx, %#llx\n", + x0, desc->arginfo, desc->args[0], desc->args[1], + desc->args[2], desc->x5, ret, desc->ret[0], + desc->ret[1], desc->ret[2]); + + if (arglen > N_REGISTER_ARGS) + kfree(desc->extra_arg_buf); + if (ret < 0) + return scm_remap_error(ret); + return ret; +} + +/** + * scm_call() - Send an SCM command + * @svc_id: service identifier + * @cmd_id: command identifier + * @cmd_buf: command buffer + * @cmd_len: length of the command buffer + * @resp_buf: response buffer + * @resp_len: length of the response buffer + * + * Sends a command to the SCM and waits for the command to finish processing. + * + * A note on cache maintenance: + * Note that any buffers that are expected to be accessed by the secure world + * must be flushed before invoking scm_call and invalidated in the cache + * immediately after scm_call returns. Cache maintenance on the command and + * response buffers is taken care of by scm_call; however, callers are + * responsible for any other cached buffers passed over to the secure world. + */ +int scm_call(u32 svc_id, u32 cmd_id, const void *cmd_buf, size_t cmd_len, + void *resp_buf, size_t resp_len) +{ + struct scm_command *cmd; + int ret; + size_t len = SCM_BUF_LEN(cmd_len, resp_len); + + if (len == 0 || PAGE_ALIGN(len) < len) + return -EINVAL; + + cmd = kzalloc(PAGE_ALIGN(len), GFP_KERNEL); + if (!cmd) + return -ENOMEM; + + ret = scm_call_common(svc_id, cmd_id, cmd_buf, cmd_len, resp_buf, + resp_len, cmd, len); + if (unlikely(ret == SCM_EBUSY)) + ret = _scm_call_retry(svc_id, cmd_id, cmd_buf, cmd_len, + resp_buf, resp_len, cmd, PAGE_ALIGN(len)); + kfree(cmd); + return ret; +} +EXPORT_SYMBOL(scm_call); + +#define SCM_CLASS_REGISTER (0x2 << 8) +#define SCM_MASK_IRQS BIT(5) +#define SCM_ATOMIC(svc, cmd, n) (((((svc) << 10)|((cmd) & 0x3ff)) << 12) | \ + SCM_CLASS_REGISTER | \ + SCM_MASK_IRQS | \ + (n & 0xf)) + +/** + * scm_call_atomic1() - Send an atomic SCM command with one argument + * @svc_id: service identifier + * @cmd_id: command identifier + * @arg1: first argument + * + * This shall only be used with commands that are guaranteed to be + * uninterruptable, atomic and SMP safe. + */ +s32 scm_call_atomic1(u32 svc, u32 cmd, u32 arg1) +{ + int context_id; + int ret; + uint64_t x0; + struct scm_desc desc = {0}; + + register u32 r0 asm("r0") = SCM_ATOMIC(svc, cmd, 1); + register u32 r1 asm("r1") = (uintptr_t)&context_id; + register u32 r2 asm("r2") = arg1; + + x0 = r0; + desc.arginfo = r1; + desc.args[0] = r2; + + ret = scm_call_qcpe(x0, &desc); + + if (ret < 0) + return scm_remap_error(ret); + + return 0; +} +EXPORT_SYMBOL(scm_call_atomic1); + +/** + * scm_call_atomic1_1() - SCM command with one argument and one return value + * @svc_id: service identifier + * @cmd_id: command identifier + * @arg1: first argument + * @ret1: first return value + * + * This shall only be used with commands that are guaranteed to be + * uninterruptable, atomic and SMP safe. + */ +s32 scm_call_atomic1_1(u32 svc, u32 cmd, u32 arg1, u32 *ret1) +{ + int context_id; + int ret; + uint64_t x0; + struct scm_desc desc = {0}; + + register u32 r0 asm("r0") = SCM_ATOMIC(svc, cmd, 1); + register u32 r1 asm("r1") = (uintptr_t)&context_id; + register u32 r2 asm("r2") = arg1; + + x0 = r0; + desc.arginfo = r1; + desc.args[0] = r2; + + ret = scm_call_qcpe(x0, &desc); + + if (ret < 0) + return scm_remap_error(ret); + + *ret1 = desc.arginfo; + + return 0; +} +EXPORT_SYMBOL(scm_call_atomic1_1); + +/** + * scm_call_atomic2() - Send an atomic SCM command with two arguments + * @svc_id: service identifier + * @cmd_id: command identifier + * @arg1: first argument + * @arg2: second argument + * + * This shall only be used with commands that are guaranteed to be + * uninterruptable, atomic and SMP safe. + */ +s32 scm_call_atomic2(u32 svc, u32 cmd, u32 arg1, u32 arg2) +{ + int context_id; + int ret; + uint64_t x0; + struct scm_desc desc = {0}; + + register u32 r0 asm("r0") = SCM_ATOMIC(svc, cmd, 2); + register u32 r1 asm("r1") = (uintptr_t)&context_id; + register u32 r2 asm("r2") = arg1; + register u32 r3 asm("r3") = arg2; + + x0 = r0; + desc.arginfo = r1; + desc.args[0] = r2; + desc.args[1] = r3; + + ret = scm_call_qcpe(x0, &desc); + + if (ret < 0) + return scm_remap_error(ret); + + return 0; +} +EXPORT_SYMBOL(scm_call_atomic2); + +/** + * scm_call_atomic3() - Send an atomic SCM command with three arguments + * @svc_id: service identifier + * @cmd_id: command identifier + * @arg1: first argument + * @arg2: second argument + * @arg3: third argument + * + * This shall only be used with commands that are guaranteed to be + * uninterruptable, atomic and SMP safe. + */ +s32 scm_call_atomic3(u32 svc, u32 cmd, u32 arg1, u32 arg2, u32 arg3) +{ + int context_id; + int ret; + uint64_t x0; + struct scm_desc desc = {0}; + + register u32 r0 asm("r0") = SCM_ATOMIC(svc, cmd, 3); + register u32 r1 asm("r1") = (uintptr_t)&context_id; + register u32 r2 asm("r2") = arg1; + register u32 r3 asm("r3") = arg2; + register u32 r4 asm("r4") = arg3; + + x0 = r0; + desc.arginfo = r1; + desc.args[0] = r2; + desc.args[1] = r3; + desc.args[2] = r4; + + ret = scm_call_qcpe(x0, &desc); + + if (ret < 0) + return scm_remap_error(ret); + + return 0; +} +EXPORT_SYMBOL(scm_call_atomic3); + +s32 scm_call_atomic4_3(u32 svc, u32 cmd, u32 arg1, u32 arg2, + u32 arg3, u32 arg4, u32 *ret1, u32 *ret2) +{ + int ret; + int context_id; + uint64_t x0; + struct scm_desc desc = {0}; + + register u32 r0 asm("r0") = SCM_ATOMIC(svc, cmd, 4); + register u32 r1 asm("r1") = (uintptr_t)&context_id; + register u32 r2 asm("r2") = arg1; + register u32 r3 asm("r3") = arg2; + register u32 r4 asm("r4") = arg3; + register u32 r5 asm("r5") = arg4; + + x0 = r0; + desc.arginfo = r1; + desc.args[0] = r2; + desc.args[1] = r3; + desc.args[2] = r4; + desc.args[3] = r5; + + ret = scm_call_qcpe(x0, &desc); + + if (ret < 0) + return scm_remap_error(ret); + + *ret1 = desc.arginfo; + *ret2 = desc.args[0]; + + return 0; +} +EXPORT_SYMBOL(scm_call_atomic4_3); + +/** + * scm_call_atomic5_3() - SCM command with five argument and three return value + * @svc_id: service identifier + * @cmd_id: command identifier + * @arg1: first argument + * @arg2: second argument + * @arg3: third argument + * @arg4: fourth argument + * @arg5: fifth argument + * @ret1: first return value + * @ret2: second return value + * @ret3: third return value + * + * This shall only be used with commands that are guaranteed to be + * uninterruptable, atomic and SMP safe. + */ +s32 scm_call_atomic5_3(u32 svc, u32 cmd, u32 arg1, u32 arg2, + u32 arg3, u32 arg4, u32 arg5, u32 *ret1, u32 *ret2, u32 *ret3) +{ + int ret; + int context_id; + uint64_t x0; + struct scm_desc desc = {0}; + + register u32 r0 asm("r0") = SCM_ATOMIC(svc, cmd, 5); + register u32 r1 asm("r1") = (uintptr_t)&context_id; + register u32 r2 asm("r2") = arg1; + register u32 r3 asm("r3") = arg2; + register u32 r4 asm("r4") = arg3; + register u32 r5 asm("r5") = arg4; + register u32 r6 asm("r6") = arg5; + + x0 = r0; + desc.arginfo = r1; + desc.args[0] = r2; + desc.args[1] = r3; + desc.args[2] = r4; + desc.args[3] = r5; + desc.args[4] = r6; + + ret = scm_call_qcpe(x0, &desc); + + if (ret < 0) + return scm_remap_error(ret); + + *ret1 = desc.arginfo; + *ret2 = desc.args[0]; + *ret3 = desc.args[1]; + + return 0; +} +EXPORT_SYMBOL(scm_call_atomic5_3); + +u32 scm_get_version(void) +{ + int context_id; + static u32 version = -1; + int ret; + uint64_t x0; + struct scm_desc desc = {0}; + + register u32 r0 asm("r0"); + register u32 r1 asm("r1"); + + if (version != -1) + return version; + + mutex_lock(&scm_lock); + + r0 = 0x1 << 8; + r1 = (uintptr_t)&context_id; + + x0 = r0; + desc.arginfo = r1; + + ret = scm_call_qcpe(x0, &desc); + + version = desc.arginfo; + + mutex_unlock(&scm_lock); + + if (ret < 0) + return scm_remap_error(ret); + + return version; +} +EXPORT_SYMBOL(scm_get_version); + +#define SCM_IO_READ 0x1 +#define SCM_IO_WRITE 0x2 + +u32 scm_io_read(phys_addr_t address) +{ + struct scm_desc desc = { + .args[0] = address, + .arginfo = SCM_ARGS(1), + }; + + if (!is_scm_armv8()) + return scm_call_atomic1(SCM_SVC_IO, SCM_IO_READ, address); + + scm_call2_atomic(SCM_SIP_FNID(SCM_SVC_IO, SCM_IO_READ), &desc); + return desc.ret[0]; +} +EXPORT_SYMBOL(scm_io_read); + +int scm_io_write(phys_addr_t address, u32 val) +{ + int ret; + + if (!is_scm_armv8()) + ret = scm_call_atomic2(SCM_SVC_IO, SCM_IO_WRITE, address, val); + else { + struct scm_desc desc = { + .args[0] = address, + .args[1] = val, + .arginfo = SCM_ARGS(2), + }; + ret = scm_call2_atomic(SCM_SIP_FNID(SCM_SVC_IO, SCM_IO_WRITE), + &desc); + } + return ret; +} +EXPORT_SYMBOL(scm_io_write); + +int scm_is_call_available(u32 svc_id, u32 cmd_id) +{ + int ret; + struct scm_desc desc = {0}; + + if (!is_scm_armv8()) { + u32 ret_val = 0; + u32 svc_cmd = (svc_id << 10) | cmd_id; + + ret = scm_call(SCM_SVC_INFO, IS_CALL_AVAIL_CMD, &svc_cmd, + sizeof(svc_cmd), &ret_val, sizeof(ret_val)); + if (ret) + return ret; + + return ret_val; + } + desc.arginfo = SCM_ARGS(1); + desc.args[0] = SCM_SIP_FNID(svc_id, cmd_id); + ret = scm_call2(SCM_SIP_FNID(SCM_SVC_INFO, IS_CALL_AVAIL_CMD), &desc); + if (ret) + return ret; + + return desc.ret[0]; +} +EXPORT_SYMBOL(scm_is_call_available); + +#define GET_FEAT_VERSION_CMD 3 +int scm_get_feat_version(u32 feat) +{ + struct scm_desc desc = {0}; + int ret; + + if (!is_scm_armv8()) { + if (scm_is_call_available(SCM_SVC_INFO, GET_FEAT_VERSION_CMD)) { + u32 version; + + if (!scm_call(SCM_SVC_INFO, GET_FEAT_VERSION_CMD, &feat, + sizeof(feat), &version, sizeof(version))) + return version; + } + return 0; + } + + ret = scm_is_call_available(SCM_SVC_INFO, GET_FEAT_VERSION_CMD); + if (ret <= 0) + return 0; + + desc.args[0] = feat; + desc.arginfo = SCM_ARGS(1); + ret = scm_call2(SCM_SIP_FNID(SCM_SVC_INFO, GET_FEAT_VERSION_CMD), + &desc); + if (!ret) + return desc.ret[0]; + + return 0; +} +EXPORT_SYMBOL(scm_get_feat_version); + +#define RESTORE_SEC_CFG 2 +int scm_restore_sec_cfg(u32 device_id, u32 spare, int *scm_ret) +{ + struct scm_desc desc = {0}; + int ret; + struct restore_sec_cfg { + u32 device_id; + u32 spare; + } cfg; + + cfg.device_id = device_id; + cfg.spare = spare; + + if (IS_ERR_OR_NULL(scm_ret)) + return -EINVAL; + + if (!is_scm_armv8()) + return scm_call(SCM_SVC_MP, RESTORE_SEC_CFG, &cfg, sizeof(cfg), + scm_ret, sizeof(*scm_ret)); + + desc.args[0] = device_id; + desc.args[1] = spare; + desc.arginfo = SCM_ARGS(2); + + ret = scm_call2(SCM_SIP_FNID(SCM_SVC_MP, RESTORE_SEC_CFG), &desc); + if (ret) + return ret; + + *scm_ret = desc.ret[0]; + return 0; +} +EXPORT_SYMBOL(scm_restore_sec_cfg); + +/* + * SCM call command ID to check secure mode + * Return zero for secure device. + * Return one for non secure device or secure + * device with debug enabled device. + */ +#define TZ_INFO_GET_SECURE_STATE 0x4 +bool scm_is_secure_device(void) +{ + struct scm_desc desc = {0}; + int ret = 0, resp; + + desc.args[0] = 0; + desc.arginfo = 0; + if (!is_scm_armv8()) { + ret = scm_call(SCM_SVC_INFO, TZ_INFO_GET_SECURE_STATE, NULL, + 0, &resp, sizeof(resp)); + } else { + ret = scm_call2(SCM_SIP_FNID(SCM_SVC_INFO, + TZ_INFO_GET_SECURE_STATE), + &desc); + resp = desc.ret[0]; + } + + if (ret) { + pr_err("%s: SCM call failed\n", __func__); + return false; + } + + if ((resp & BIT(0)) || (resp & BIT(2))) + return true; + else + return false; +} +EXPORT_SYMBOL(scm_is_secure_device); diff --git a/drivers/soc/qcom/service-notifier.c b/drivers/soc/qcom/service-notifier.c index 221ae0c1fefb..f4c67f1cd9d9 100644 --- a/drivers/soc/qcom/service-notifier.c +++ b/drivers/soc/qcom/service-notifier.c @@ -84,6 +84,7 @@ static DEFINE_MUTEX(service_list_lock); struct ind_req_resp { char service_path[SERVREG_NOTIF_NAME_LENGTH]; int transaction_id; + int curr_state; }; /* @@ -200,8 +201,30 @@ static void send_ind_ack(struct work_struct *work) struct qmi_servreg_notif_set_ack_req_msg_v01 req; struct msg_desc req_desc, resp_desc; struct qmi_servreg_notif_set_ack_resp_msg_v01 resp = { { 0, 0 } }; + struct service_notif_info *service_notif; + enum pd_subsys_state state = USER_PD_STATE_CHANGE; int rc; + service_notif = _find_service_info(data->ind_msg.service_path); + if (!service_notif) + return; + if ((int)data->ind_msg.curr_state < QMI_STATE_MIN_VAL || + (int)data->ind_msg.curr_state > QMI_STATE_MAX_VAL) + pr_err("Unexpected indication notification state %d\n", + data->ind_msg.curr_state); + else { + mutex_lock(¬if_add_lock); + mutex_lock(&service_list_lock); + rc = service_notif_queue_notification(service_notif, + data->ind_msg.curr_state, &state); + if (rc & NOTIFY_STOP_MASK) + pr_err("Notifier callback aborted for %s with error %d\n", + data->ind_msg.service_path, rc); + service_notif->curr_state = data->ind_msg.curr_state; + mutex_unlock(&service_list_lock); + mutex_unlock(¬if_add_lock); + } + req.transaction_id = data->ind_msg.transaction_id; snprintf(req.service_name, ARRAY_SIZE(req.service_name), "%s", data->ind_msg.service_path); @@ -236,11 +259,9 @@ static void root_service_service_ind_cb(struct qmi_handle *handle, unsigned int msg_len, void *ind_cb_priv) { struct qmi_client_info *data = (struct qmi_client_info *)ind_cb_priv; - struct service_notif_info *service_notif; struct msg_desc ind_desc; struct qmi_servreg_notif_state_updated_ind_msg_v01 ind_msg = { QMI_STATE_MIN_VAL, "", 0xFFFF }; - enum pd_subsys_state state = USER_PD_STATE_CHANGE; int rc; ind_desc.msg_id = SERVREG_NOTIF_STATE_UPDATED_IND_MSG; @@ -256,27 +277,8 @@ static void root_service_service_ind_cb(struct qmi_handle *handle, ind_msg.service_name, ind_msg.curr_state, ind_msg.transaction_id); - service_notif = _find_service_info(ind_msg.service_name); - if (!service_notif) - return; - - if ((int)ind_msg.curr_state < QMI_STATE_MIN_VAL || - (int)ind_msg.curr_state > QMI_STATE_MAX_VAL) - pr_err("Unexpected indication notification state %d\n", - ind_msg.curr_state); - else { - mutex_lock(¬if_add_lock); - mutex_lock(&service_list_lock); - rc = service_notif_queue_notification(service_notif, - ind_msg.curr_state, &state); - if (rc & NOTIFY_STOP_MASK) - pr_err("Notifier callback aborted for %s with error %d\n", - ind_msg.service_name, rc); - service_notif->curr_state = ind_msg.curr_state; - mutex_unlock(&service_list_lock); - mutex_unlock(¬if_add_lock); - } data->ind_msg.transaction_id = ind_msg.transaction_id; + data->ind_msg.curr_state = ind_msg.curr_state; snprintf(data->ind_msg.service_path, ARRAY_SIZE(data->ind_msg.service_path), "%s", ind_msg.service_name); @@ -373,6 +375,12 @@ static void root_service_service_arrive(struct work_struct *work) mutex_unlock(&qmi_client_release_lock); pr_info("Connection established between QMI handle and %d service\n", data->instance_id); + /* Register for indication messages about service */ + rc = qmi_register_ind_cb(data->clnt_handle, + root_service_service_ind_cb, (void *)data); + if (rc < 0) + pr_err("Indication callback register failed(instance-id: %d) rc:%d\n", + data->instance_id, rc); mutex_lock(¬if_add_lock); mutex_lock(&service_list_lock); list_for_each_entry(service_notif, &service_list, list) { @@ -395,12 +403,6 @@ static void root_service_service_arrive(struct work_struct *work) } mutex_unlock(&service_list_lock); mutex_unlock(¬if_add_lock); - /* Register for indication messages about service */ - rc = qmi_register_ind_cb(data->clnt_handle, - root_service_service_ind_cb, (void *)data); - if (rc < 0) - pr_err("Indication callback register failed(instance-id: %d) rc:%d\n", - data->instance_id, rc); } static void root_service_service_exit(struct qmi_client_info *data, diff --git a/drivers/soc/qcom/spcom.c b/drivers/soc/qcom/spcom.c index 7f03aabf415e..e4afce02afc7 100644 --- a/drivers/soc/qcom/spcom.c +++ b/drivers/soc/qcom/spcom.c @@ -245,7 +245,7 @@ struct spcom_device { int channel_count; /* private */ - struct mutex lock; + struct mutex cmd_lock; /* Link state */ struct completion link_state_changed; @@ -1952,6 +1952,8 @@ static int spcom_handle_write(struct spcom_channel *ch, swap_id = htonl(cmd->cmd_id); memcpy(cmd_name, &swap_id, sizeof(int)); + mutex_lock(&spcom_dev->cmd_lock); + pr_debug("cmd_id [0x%x] cmd_name [%s].\n", cmd_id, cmd_name); switch (cmd_id) { @@ -1972,9 +1974,11 @@ static int spcom_handle_write(struct spcom_channel *ch, break; default: pr_err("Invalid Command Id [0x%x].\n", (int) cmd->cmd_id); - return -EINVAL; + ret = -EINVAL; } + mutex_unlock(&spcom_dev->cmd_lock); + return ret; } @@ -2675,7 +2679,7 @@ static int spcom_probe(struct platform_device *pdev) return -ENOMEM; spcom_dev = dev; - mutex_init(&dev->lock); + mutex_init(&spcom_dev->cmd_lock); init_completion(&dev->link_state_changed); spcom_dev->link_state = GLINK_LINK_STATE_DOWN; diff --git a/drivers/spi/spi-davinci.c b/drivers/spi/spi-davinci.c index 7d3af3eacf57..1ddba9ae8c0f 100644 --- a/drivers/spi/spi-davinci.c +++ b/drivers/spi/spi-davinci.c @@ -651,7 +651,7 @@ static int davinci_spi_bufs(struct spi_device *spi, struct spi_transfer *t) buf = t->rx_buf; t->rx_dma = dma_map_single(&spi->dev, buf, t->len, DMA_FROM_DEVICE); - if (!t->rx_dma) { + if (dma_mapping_error(&spi->dev, !t->rx_dma)) { ret = -EFAULT; goto err_rx_map; } @@ -665,7 +665,7 @@ static int davinci_spi_bufs(struct spi_device *spi, struct spi_transfer *t) buf = (void *)t->tx_buf; t->tx_dma = dma_map_single(&spi->dev, buf, t->len, DMA_TO_DEVICE); - if (!t->tx_dma) { + if (dma_mapping_error(&spi->dev, t->tx_dma)) { ret = -EFAULT; goto err_tx_map; } diff --git a/drivers/staging/android/sync_debug.c b/drivers/staging/android/sync_debug.c index aaa96c3df45b..5fbd3766b981 100644 --- a/drivers/staging/android/sync_debug.c +++ b/drivers/staging/android/sync_debug.c @@ -87,7 +87,7 @@ static void sync_print_pt(struct seq_file *s, struct sync_pt *pt, bool fence) int status = 1; struct sync_timeline *parent = sync_pt_parent(pt); - if (fence_is_signaled_locked(&pt->base)) + if (test_bit(FENCE_FLAG_SIGNALED_BIT, &pt->base.flags)) status = pt->base.status; seq_printf(s, " %s%spt %s", diff --git a/drivers/target/iscsi/iscsi_target.c b/drivers/target/iscsi/iscsi_target.c index 200d3de8bc1e..a180c000e246 100644 --- a/drivers/target/iscsi/iscsi_target.c +++ b/drivers/target/iscsi/iscsi_target.c @@ -1112,6 +1112,18 @@ iscsit_get_immediate_data(struct iscsi_cmd *cmd, struct iscsi_scsi_req *hdr, */ if (dump_payload) goto after_immediate_data; + /* + * Check for underflow case where both EDTL and immediate data payload + * exceeds what is presented by CDB's TRANSFER LENGTH, and what has + * already been set in target_cmd_size_check() as se_cmd->data_length. + * + * For this special case, fail the command and dump the immediate data + * payload. + */ + if (cmd->first_burst_len > cmd->se_cmd.data_length) { + cmd->sense_reason = TCM_INVALID_CDB_FIELD; + goto after_immediate_data; + } immed_ret = iscsit_handle_immediate_data(cmd, hdr, cmd->first_burst_len); diff --git a/drivers/target/target_core_internal.h b/drivers/target/target_core_internal.h index 253a91bff943..272e6f755322 100644 --- a/drivers/target/target_core_internal.h +++ b/drivers/target/target_core_internal.h @@ -132,7 +132,7 @@ int init_se_kmem_caches(void); void release_se_kmem_caches(void); u32 scsi_get_new_index(scsi_index_t); void transport_subsystem_check_init(void); -void transport_cmd_finish_abort(struct se_cmd *, int); +int transport_cmd_finish_abort(struct se_cmd *, int); unsigned char *transport_dump_cmd_direction(struct se_cmd *); void transport_dump_dev_state(struct se_device *, char *, int *); void transport_dump_dev_info(struct se_device *, struct se_lun *, diff --git a/drivers/target/target_core_tmr.c b/drivers/target/target_core_tmr.c index 46b1991fbb50..c9be953496ec 100644 --- a/drivers/target/target_core_tmr.c +++ b/drivers/target/target_core_tmr.c @@ -75,7 +75,7 @@ void core_tmr_release_req(struct se_tmr_req *tmr) kfree(tmr); } -static void core_tmr_handle_tas_abort(struct se_cmd *cmd, int tas) +static int core_tmr_handle_tas_abort(struct se_cmd *cmd, int tas) { unsigned long flags; bool remove = true, send_tas; @@ -91,7 +91,7 @@ static void core_tmr_handle_tas_abort(struct se_cmd *cmd, int tas) transport_send_task_abort(cmd); } - transport_cmd_finish_abort(cmd, remove); + return transport_cmd_finish_abort(cmd, remove); } static int target_check_cdb_and_preempt(struct list_head *list, @@ -185,8 +185,8 @@ void core_tmr_abort_task( cancel_work_sync(&se_cmd->work); transport_wait_for_tasks(se_cmd); - transport_cmd_finish_abort(se_cmd, true); - target_put_sess_cmd(se_cmd); + if (!transport_cmd_finish_abort(se_cmd, true)) + target_put_sess_cmd(se_cmd); printk("ABORT_TASK: Sending TMR_FUNCTION_COMPLETE for" " ref_tag: %llu\n", ref_tag); @@ -286,8 +286,8 @@ static void core_tmr_drain_tmr_list( cancel_work_sync(&cmd->work); transport_wait_for_tasks(cmd); - transport_cmd_finish_abort(cmd, 1); - target_put_sess_cmd(cmd); + if (!transport_cmd_finish_abort(cmd, 1)) + target_put_sess_cmd(cmd); } } @@ -385,8 +385,8 @@ static void core_tmr_drain_state_list( cancel_work_sync(&cmd->work); transport_wait_for_tasks(cmd); - core_tmr_handle_tas_abort(cmd, tas); - target_put_sess_cmd(cmd); + if (!core_tmr_handle_tas_abort(cmd, tas)) + target_put_sess_cmd(cmd); } } diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index 60743bf27f37..37c77db6e737 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -639,9 +639,10 @@ static void transport_lun_remove_cmd(struct se_cmd *cmd) percpu_ref_put(&lun->lun_ref); } -void transport_cmd_finish_abort(struct se_cmd *cmd, int remove) +int transport_cmd_finish_abort(struct se_cmd *cmd, int remove) { bool ack_kref = (cmd->se_cmd_flags & SCF_ACK_KREF); + int ret = 0; if (cmd->se_cmd_flags & SCF_SE_LUN_CMD) transport_lun_remove_cmd(cmd); @@ -653,9 +654,11 @@ void transport_cmd_finish_abort(struct se_cmd *cmd, int remove) cmd->se_tfo->aborted_task(cmd); if (transport_cmd_check_stop_to_fabric(cmd)) - return; + return 1; if (remove && ack_kref) - transport_put_cmd(cmd); + ret = transport_put_cmd(cmd); + + return ret; } static void target_complete_failure_work(struct work_struct *work) diff --git a/drivers/tty/serial/msm_serial_hs.c b/drivers/tty/serial/msm_serial_hs.c index 830ef92ffe80..fc9faaee3170 100644 --- a/drivers/tty/serial/msm_serial_hs.c +++ b/drivers/tty/serial/msm_serial_hs.c @@ -272,7 +272,7 @@ static struct of_device_id msm_hs_match_table[] = { #define UARTDM_TX_BUF_SIZE UART_XMIT_SIZE #define UARTDM_RX_BUF_SIZE 512 #define RETRY_TIMEOUT 5 -#define UARTDM_NR 256 +#define UARTDM_NR 4 #define BAM_PIPE_MIN 0 #define BAM_PIPE_MAX 11 #define BUS_SCALING 1 diff --git a/drivers/usb/pd/policy_engine.c b/drivers/usb/pd/policy_engine.c index 03aeec2e878c..7cdc95d2b085 100644 --- a/drivers/usb/pd/policy_engine.c +++ b/drivers/usb/pd/policy_engine.c @@ -182,7 +182,7 @@ static void *usbpd_ipc_log; #define PS_HARD_RESET_TIME 25 #define PS_SOURCE_ON 400 #define PS_SOURCE_OFF 750 -#define SWAP_SOURCE_START_TIME 20 +#define FIRST_SOURCE_CAP_TIME 200 #define VDM_BUSY_TIME 50 #define VCONN_ON_TIME 100 @@ -790,17 +790,27 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state) pd->pd_phy_opened = true; } - pd->current_state = PE_SRC_SEND_CAPABILITIES; if (pd->in_pr_swap) { - kick_sm(pd, SWAP_SOURCE_START_TIME); pd->in_pr_swap = false; val.intval = 0; power_supply_set_property(pd->usb_psy, POWER_SUPPLY_PROP_PR_SWAP, &val); - break; } - /* fall-through */ + /* + * A sink might remove its terminations (during some Type-C + * compliance tests or a sink attempting to do Try.SRC) + * at this point just after we enabled VBUS. Sending PD + * messages now would delay detecting the detach beyond the + * required timing. Instead, delay sending out the first + * source capabilities to allow for the other side to + * completely settle CC debounce and allow HW to detect detach + * sooner in the meantime. PD spec allows up to + * tFirstSourceCap (250ms). + */ + pd->current_state = PE_SRC_SEND_CAPABILITIES; + kick_sm(pd, FIRST_SOURCE_CAP_TIME); + break; case PE_SRC_SEND_CAPABILITIES: kick_sm(pd, 0); diff --git a/drivers/usb/phy/phy-msm-qusb-v2.c b/drivers/usb/phy/phy-msm-qusb-v2.c index de4f93afdc97..e5f38e42e165 100644 --- a/drivers/usb/phy/phy-msm-qusb-v2.c +++ b/drivers/usb/phy/phy-msm-qusb-v2.c @@ -652,13 +652,16 @@ static int qusb_phy_set_suspend(struct usb_phy *phy, int suspend) writel_relaxed(intr_mask, qphy->base + QUSB2PHY_INTR_CTRL); - /* enable phy auto-resume */ - writel_relaxed(0x91, + if (linestate & (LINESTATE_DP | LINESTATE_DM)) { + + /* enable phy auto-resume */ + writel_relaxed(0x91, qphy->base + QUSB2PHY_TEST1); - /* flush the previous write before next write */ - wmb(); - writel_relaxed(0x90, - qphy->base + QUSB2PHY_TEST1); + /* flush the previous write before next write */ + wmb(); + writel_relaxed(0x90, + qphy->base + QUSB2PHY_TEST1); + } dev_dbg(phy->dev, "%s: intr_mask = %x\n", __func__, intr_mask); diff --git a/drivers/usb/phy/phy-msm-qusb.c b/drivers/usb/phy/phy-msm-qusb.c index 6a2529ec1511..bd2722e8fc48 100644 --- a/drivers/usb/phy/phy-msm-qusb.c +++ b/drivers/usb/phy/phy-msm-qusb.c @@ -730,13 +730,15 @@ static int qusb_phy_set_suspend(struct usb_phy *phy, int suspend) writel_relaxed(intr_mask, qphy->base + QUSB2PHY_PORT_INTR_CTRL); - /* enable phy auto-resume */ - writel_relaxed(0x0C, + if (linestate & (LINESTATE_DP | LINESTATE_DM)) { + /* enable phy auto-resume */ + writel_relaxed(0x0C, qphy->base + QUSB2PHY_PORT_TEST_CTRL); - /* flush the previous write before next write */ - wmb(); - writel_relaxed(0x04, - qphy->base + QUSB2PHY_PORT_TEST_CTRL); + /* flush the previous write before next write */ + wmb(); + writel_relaxed(0x04, + qphy->base + QUSB2PHY_PORT_TEST_CTRL); + } dev_dbg(phy->dev, "%s: intr_mask = %x\n", diff --git a/drivers/usb/usbip/vhci_hcd.c b/drivers/usb/usbip/vhci_hcd.c index 7fbe19d5279e..81b2b9f808b5 100644 --- a/drivers/usb/usbip/vhci_hcd.c +++ b/drivers/usb/usbip/vhci_hcd.c @@ -215,14 +215,19 @@ done: static inline void hub_descriptor(struct usb_hub_descriptor *desc) { + int width; + memset(desc, 0, sizeof(*desc)); desc->bDescriptorType = USB_DT_HUB; - desc->bDescLength = 9; desc->wHubCharacteristics = cpu_to_le16( HUB_CHAR_INDV_PORT_LPSM | HUB_CHAR_COMMON_OCPM); + desc->bNbrPorts = VHCI_NPORTS; - desc->u.hs.DeviceRemovable[0] = 0xff; - desc->u.hs.DeviceRemovable[1] = 0xff; + BUILD_BUG_ON(VHCI_NPORTS > USB_MAXCHILDREN); + width = desc->bNbrPorts / 8 + 1; + desc->bDescLength = USB_DT_HUB_NONVAR_SIZE + 2 * width; + memset(&desc->u.hs.DeviceRemovable[0], 0, width); + memset(&desc->u.hs.DeviceRemovable[width], 0xff, width); } static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, diff --git a/drivers/vfio/vfio_iommu_spapr_tce.c b/drivers/vfio/vfio_iommu_spapr_tce.c index 1a9f18b40be6..34e4b3ad8b92 100644 --- a/drivers/vfio/vfio_iommu_spapr_tce.c +++ b/drivers/vfio/vfio_iommu_spapr_tce.c @@ -1163,6 +1163,10 @@ static int tce_iommu_attach_group(void *iommu_data, /* pr_debug("tce_vfio: Attaching group #%u to iommu %p\n", iommu_group_id(iommu_group), iommu_group); */ table_group = iommu_group_get_iommudata(iommu_group); + if (!table_group) { + ret = -ENODEV; + goto unlock_exit; + } if (tce_groups_attached(container) && (!table_group->ops || !table_group->ops->take_ownership || diff --git a/drivers/video/fbdev/msm/mdp3_ppp.c b/drivers/video/fbdev/msm/mdp3_ppp.c index eaacdd875747..4f7a4a1189f4 100644 --- a/drivers/video/fbdev/msm/mdp3_ppp.c +++ b/drivers/video/fbdev/msm/mdp3_ppp.c @@ -1,4 +1,5 @@ /* Copyright (c) 2007, 2013-2014, 2016, The Linux Foundation. All rights reserved. + * Copyright (c) 2017, The Linux Foundation. All rights reserved. * Copyright (C) 2007 Google Incorporated * * This software is licensed under the terms of the GNU General Public @@ -39,6 +40,7 @@ #define MDP_PPP_MAX_BPP 4 #define MDP_PPP_DYNAMIC_FACTOR 3 #define MDP_PPP_MAX_READ_WRITE 3 +#define MDP_PPP_MAX_WIDTH 0xFFF #define ENABLE_SOLID_FILL 0x2 #define DISABLE_SOLID_FILL 0x0 #define BLEND_LATENCY 3 @@ -152,6 +154,11 @@ int mdp3_ppp_get_img(struct mdp_img *img, struct mdp_blit_req *req, return -EINVAL; } + if (img->width > MDP_PPP_MAX_WIDTH) { + pr_err("%s incorrect width %d\n", __func__, img->width); + return -EINVAL; + } + fb_data.flags = img->priv; fb_data.memory_id = img->memory_id; fb_data.offset = 0; diff --git a/drivers/video/fbdev/msm/mdss.h b/drivers/video/fbdev/msm/mdss.h index d6e8a213c215..5548f0f09f8a 100644 --- a/drivers/video/fbdev/msm/mdss.h +++ b/drivers/video/fbdev/msm/mdss.h @@ -547,6 +547,7 @@ struct mdss_data_type { u32 sec_session_cnt; wait_queue_head_t secure_waitq; struct cx_ipeak_client *mdss_cx_ipeak; + struct mult_factor bus_throughput_factor; }; extern struct mdss_data_type *mdss_res; diff --git a/drivers/video/fbdev/msm/mdss_dp.c b/drivers/video/fbdev/msm/mdss_dp.c index c8afb3a244c3..2f169074261a 100644 --- a/drivers/video/fbdev/msm/mdss_dp.c +++ b/drivers/video/fbdev/msm/mdss_dp.c @@ -2176,6 +2176,10 @@ static int mdss_dp_process_hpd_high(struct mdss_dp_drv_pdata *dp) ret = mdss_dp_dpcd_cap_read(dp); if (ret || !mdss_dp_aux_is_link_rate_valid(dp->dpcd.max_link_rate) || !mdss_dp_aux_is_lane_count_valid(dp->dpcd.max_lane_count)) { + if (ret == EDP_AUX_ERR_TOUT) { + pr_err("DPCD read timedout, skip connect notification\n"); + goto end; + } /* * If there is an error in parsing DPCD or if DPCD reports * unsupported link parameters then set the default link diff --git a/drivers/video/fbdev/msm/mdss_dsi.c b/drivers/video/fbdev/msm/mdss_dsi.c index 82f6d4a123b5..4ac10ab494e5 100644 --- a/drivers/video/fbdev/msm/mdss_dsi.c +++ b/drivers/video/fbdev/msm/mdss_dsi.c @@ -909,10 +909,15 @@ static ssize_t mdss_dsi_cmd_write(struct file *file, const char __user *p, /* Writing in batches is possible */ ret = simple_write_to_buffer(string_buf, blen, ppos, p, count); + if (ret < 0) { + pr_err("%s: Failed to copy data\n", __func__); + mutex_unlock(&pcmds->dbg_mutex); + return -EINVAL; + } - string_buf[blen] = '\0'; + string_buf[ret] = '\0'; pcmds->string_buf = string_buf; - pcmds->sblen = blen; + pcmds->sblen = count; mutex_unlock(&pcmds->dbg_mutex); return ret; } diff --git a/drivers/video/fbdev/msm/mdss_fb.c b/drivers/video/fbdev/msm/mdss_fb.c index 42ea4b0b82b1..fe1289633291 100644 --- a/drivers/video/fbdev/msm/mdss_fb.c +++ b/drivers/video/fbdev/msm/mdss_fb.c @@ -1192,11 +1192,24 @@ static int mdss_fb_init_panel_modes(struct msm_fb_data_type *mfd, if (pdata->next) { spt = mdss_panel_get_timing_by_name(pdata->next, modedb[i].name); - if (!IS_ERR_OR_NULL(spt)) + /* for split config, recalculate xres and pixel clock */ + if (!IS_ERR_OR_NULL(spt)) { + unsigned long pclk, h_total, v_total; modedb[i].xres += spt->xres; - else + h_total = modedb[i].xres + + modedb[i].left_margin + + modedb[i].right_margin + + modedb[i].hsync_len; + v_total = modedb[i].yres + + modedb[i].lower_margin + + modedb[i].upper_margin + + modedb[i].vsync_len; + pclk = h_total * v_total * modedb[i].refresh; + modedb[i].pixclock = KHZ2PICOS(pclk / 1000); + } else { pr_debug("no matching split config for %s\n", modedb[i].name); + } /* * if no panel timing found for current, need to diff --git a/drivers/video/fbdev/msm/mdss_mdp.c b/drivers/video/fbdev/msm/mdss_mdp.c index 6936c4c1f3cc..6fb32761a767 100644 --- a/drivers/video/fbdev/msm/mdss_mdp.c +++ b/drivers/video/fbdev/msm/mdss_mdp.c @@ -4527,6 +4527,15 @@ static int mdss_mdp_parse_dt_misc(struct platform_device *pdev) mdss_mdp_parse_dt_fudge_factors(pdev, "qcom,mdss-clk-factor", &mdata->clk_factor); + /* + * Bus throughput factor will be used during high downscale cases. + * The recommended default factor is 1.1. + */ + mdata->bus_throughput_factor.numer = 11; + mdata->bus_throughput_factor.denom = 10; + mdss_mdp_parse_dt_fudge_factors(pdev, "qcom,mdss-bus-througput-factor", + &mdata->bus_throughput_factor); + rc = of_property_read_u32(pdev->dev.of_node, "qcom,max-bandwidth-low-kbps", &mdata->max_bw_low); if (rc) diff --git a/drivers/video/fbdev/msm/mdss_mdp_ctl.c b/drivers/video/fbdev/msm/mdss_mdp_ctl.c index efd681a5d954..0165c48a0467 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_ctl.c +++ b/drivers/video/fbdev/msm/mdss_mdp_ctl.c @@ -31,6 +31,7 @@ #define MDSS_MDP_QSEED3_VER_DOWNSCALE_LIM 2 #define NUM_MIXERCFG_REGS 3 #define MDSS_MDP_WB_OUTPUT_BPP 3 +#define MIN_BUS_THROUGHPUT_SCALE_FACTOR 35 struct mdss_mdp_mixer_cfg { u32 config_masks[NUM_MIXERCFG_REGS]; bool border_enabled; @@ -622,11 +623,21 @@ static u32 __calc_qseed3_mdp_clk_rate(struct mdss_mdp_pipe *pipe, } static inline bool __is_vert_downscaling(u32 src_h, - struct mdss_rect dst){ - + struct mdss_rect dst) +{ return (src_h > dst.h); } +static inline bool __is_bus_throughput_factor_required(u32 src_h, + struct mdss_rect dst) +{ + u32 scale_factor = src_h * 10; + + do_div(scale_factor, dst.h); + return (__is_vert_downscaling(src_h, dst) && + (scale_factor >= MIN_BUS_THROUGHPUT_SCALE_FACTOR)); +} + static u32 get_pipe_mdp_clk_rate(struct mdss_mdp_pipe *pipe, struct mdss_rect src, struct mdss_rect dst, u32 fps, u32 v_total, u32 flags) @@ -673,6 +684,15 @@ static u32 get_pipe_mdp_clk_rate(struct mdss_mdp_pipe *pipe, } } + /* + * If the downscale factor is >= 3.5 for a 32 BPP surface, + * it is recommended to add a 10% bus throughput factor to + * the clock rate. + */ + if ((pipe->src_fmt->bpp == 4) && + __is_bus_throughput_factor_required(src_h, dst)) + rate = apply_fudge_factor(rate, &mdata->bus_throughput_factor); + if (flags & PERF_CALC_PIPE_APPLY_CLK_FUDGE) rate = mdss_mdp_clk_fudge_factor(mixer, rate); diff --git a/drivers/video/fbdev/msm/mdss_mdp_intf_video.c b/drivers/video/fbdev/msm/mdss_mdp_intf_video.c index d9aaac4526ea..c31c0149866a 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_intf_video.c +++ b/drivers/video/fbdev/msm/mdss_mdp_intf_video.c @@ -1996,8 +1996,9 @@ static void mdss_mdp_handoff_programmable_fetch(struct mdss_mdp_ctl *ctl, MDSS_MDP_REG_INTF_HSYNC_CTL) >> 16; v_total_handoff = mdp_video_read(ctx, MDSS_MDP_REG_INTF_VSYNC_PERIOD_F0)/h_total_handoff; - pinfo->prg_fet = v_total_handoff - - ((fetch_start_handoff - 1)/h_total_handoff); + if (h_total_handoff) + pinfo->prg_fet = v_total_handoff - + ((fetch_start_handoff - 1)/h_total_handoff); pr_debug("programmable fetch lines %d start:%d\n", pinfo->prg_fet, fetch_start_handoff); MDSS_XLOG(pinfo->prg_fet, fetch_start_handoff, diff --git a/drivers/video/fbdev/msm/mdss_mdp_layer.c b/drivers/video/fbdev/msm/mdss_mdp_layer.c index e44edb8fbea7..3ccc09d58480 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_layer.c +++ b/drivers/video/fbdev/msm/mdss_mdp_layer.c @@ -3131,6 +3131,14 @@ int mdss_mdp_layer_pre_commit_wfd(struct msm_fb_data_type *mfd, sync_pt_data = &mfd->mdp_sync_pt_data; mutex_lock(&sync_pt_data->sync_mutex); count = sync_pt_data->acq_fen_cnt; + + if (count >= MDP_MAX_FENCE_FD) { + pr_err("Reached maximum possible value for fence count\n"); + mutex_unlock(&sync_pt_data->sync_mutex); + rc = -EINVAL; + goto input_layer_err; + } + sync_pt_data->acq_fen[count] = fence; sync_pt_data->acq_fen_cnt++; mutex_unlock(&sync_pt_data->sync_mutex); diff --git a/drivers/video/fbdev/msm/mdss_mdp_overlay.c b/drivers/video/fbdev/msm/mdss_mdp_overlay.c index 953aeb95332b..305fff6b5695 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_overlay.c +++ b/drivers/video/fbdev/msm/mdss_mdp_overlay.c @@ -5023,12 +5023,15 @@ static int mdss_fb_get_metadata(struct msm_fb_data_type *mfd, break; case metadata_op_get_ion_fd: if (mfd->fb_ion_handle && mfd->fb_ion_client) { + get_dma_buf(mfd->fbmem_buf); metadata->data.fbmem_ionfd = ion_share_dma_buf_fd(mfd->fb_ion_client, mfd->fb_ion_handle); - if (metadata->data.fbmem_ionfd < 0) + if (metadata->data.fbmem_ionfd < 0) { + dma_buf_put(mfd->fbmem_buf); pr_err("fd allocation failed. fd = %d\n", metadata->data.fbmem_ionfd); + } } break; case metadata_op_crc: diff --git a/drivers/video/fbdev/msm/mdss_mdp_pp_cache_config.c b/drivers/video/fbdev/msm/mdss_mdp_pp_cache_config.c index 017a2f10dfbc..a5ec7097e0f6 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_pp_cache_config.c +++ b/drivers/video/fbdev/msm/mdss_mdp_pp_cache_config.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2016, The Linux Foundation. All rights reserved. + * Copyright (c) 2014-2017, The Linux Foundation. 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 version 2 and @@ -194,8 +194,12 @@ static int pp_hist_lut_cache_params_pipe_v1_7(struct mdp_hist_lut_data *config, return -EINVAL; } - memcpy(&hist_lut_usr_config, config->cfg_payload, - sizeof(struct mdp_hist_lut_data_v1_7)); + if (copy_from_user(&hist_lut_usr_config, + (void __user *) config->cfg_payload, + sizeof(hist_lut_usr_config))) { + pr_err("failed to copy hist lut config\n"); + return -EFAULT; + } hist_lut_cache_data = pipe->pp_res.hist_lut_cfg_payload; if (!hist_lut_cache_data) { @@ -606,8 +610,12 @@ static int pp_pcc_cache_params_pipe_v1_7(struct mdp_pcc_cfg_data *config, return -EINVAL; } - memcpy(&v17_usr_config, config->cfg_payload, - sizeof(v17_usr_config)); + if (copy_from_user(&v17_usr_config, + (void __user *) config->cfg_payload, + sizeof(v17_usr_config))) { + pr_err("failed to copy pcc config\n"); + return -EFAULT; + } if (!(config->ops & MDP_PP_OPS_WRITE)) { pr_debug("write ops not set value of flag is %d\n", @@ -861,8 +869,12 @@ static int pp_igc_lut_cache_params_pipe_v1_7(struct mdp_igc_lut_data *config, goto igc_config_exit; } - memcpy(&v17_usr_config, config->cfg_payload, - sizeof(v17_usr_config)); + if (copy_from_user(&v17_usr_config, + (void __user *) config->cfg_payload, + sizeof(v17_usr_config))) { + pr_err("failed to copy igc usr config\n"); + return -EFAULT; + } if (!(config->ops & MDP_PP_OPS_WRITE)) { pr_debug("op for gamut %d\n", config->ops); @@ -1272,8 +1284,12 @@ static int pp_pa_cache_params_pipe_v1_7(struct mdp_pa_v2_cfg_data *config, return -EINVAL; } - memcpy(&pa_usr_config, config->cfg_payload, - sizeof(struct mdp_pa_data_v1_7)); + if (copy_from_user(&pa_usr_config, + (void __user *) config->cfg_payload, + sizeof(pa_usr_config))) { + pr_err("failed to copy pa usr config\n"); + return -EFAULT; + } pa_cache_data = pipe->pp_res.pa_cfg_payload; if (!pa_cache_data) { diff --git a/drivers/video/fbdev/msm/mdss_mdp_splash_logo.c b/drivers/video/fbdev/msm/mdss_mdp_splash_logo.c index d412a1c66e79..5df1ed7c89a5 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_splash_logo.c +++ b/drivers/video/fbdev/msm/mdss_mdp_splash_logo.c @@ -175,7 +175,7 @@ static int mdss_mdp_splash_iommu_attach(struct msm_fb_data_type *mfd) ret = mdss_smmu_set_attribute(MDSS_IOMMU_DOMAIN_UNSECURE, EARLY_MAP, 1); if (ret) { - pr_debug("mdss set attribute failed for early map\n"); + pr_err("mdss set attribute failed for early map\n"); goto end; } @@ -198,6 +198,9 @@ static int mdss_mdp_splash_iommu_attach(struct msm_fb_data_type *mfd) } ret = mdss_smmu_set_attribute(MDSS_IOMMU_DOMAIN_UNSECURE, EARLY_MAP, 0); + if (ret) + pr_err("mdss reset attribute failed for early map\n"); + end: mdata->handoff_pending = true; diff --git a/drivers/video/fbdev/msm/msm_mdss_io_8974.c b/drivers/video/fbdev/msm/msm_mdss_io_8974.c index 80dbe83972d7..bb3b4b3fa929 100644 --- a/drivers/video/fbdev/msm/msm_mdss_io_8974.c +++ b/drivers/video/fbdev/msm/msm_mdss_io_8974.c @@ -2587,7 +2587,8 @@ int mdss_dsi_post_clkon_cb(void *priv, } if (clk & MDSS_DSI_LINK_CLK) { /* toggle the resync FIFO everytime clock changes */ - if (ctrl->shared_data->phy_rev == DSI_PHY_REV_30) + if ((ctrl->shared_data->phy_rev == DSI_PHY_REV_30) && + !pdata->panel_info.cont_splash_enabled) mdss_dsi_phy_v3_toggle_resync_fifo(ctrl); if (ctrl->ulps) { diff --git a/drivers/watchdog/bcm_kona_wdt.c b/drivers/watchdog/bcm_kona_wdt.c index e0c98423f2c9..11a72bc2c71b 100644 --- a/drivers/watchdog/bcm_kona_wdt.c +++ b/drivers/watchdog/bcm_kona_wdt.c @@ -304,6 +304,8 @@ static int bcm_kona_wdt_probe(struct platform_device *pdev) if (!wdt) return -ENOMEM; + spin_lock_init(&wdt->lock); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); wdt->base = devm_ioremap_resource(dev, res); if (IS_ERR(wdt->base)) @@ -316,7 +318,6 @@ static int bcm_kona_wdt_probe(struct platform_device *pdev) return ret; } - spin_lock_init(&wdt->lock); platform_set_drvdata(pdev, wdt); watchdog_set_drvdata(&bcm_kona_wdt_wdd, wdt); bcm_kona_wdt_wdd.parent = &pdev->dev; diff --git a/drivers/xen/swiotlb-xen.c b/drivers/xen/swiotlb-xen.c index 7399782c0998..8a58bbc14de2 100644 --- a/drivers/xen/swiotlb-xen.c +++ b/drivers/xen/swiotlb-xen.c @@ -409,9 +409,9 @@ dma_addr_t xen_swiotlb_map_page(struct device *dev, struct page *page, if (map == SWIOTLB_MAP_ERROR) return DMA_ERROR_CODE; + dev_addr = xen_phys_to_bus(map); xen_dma_map_page(dev, pfn_to_page(map >> PAGE_SHIFT), dev_addr, map & ~PAGE_MASK, size, dir, attrs); - dev_addr = xen_phys_to_bus(map); /* * Ensure that the address returned is DMA'ble @@ -567,13 +567,14 @@ xen_swiotlb_map_sg_attrs(struct device *hwdev, struct scatterlist *sgl, sg_dma_len(sgl) = 0; return 0; } + dev_addr = xen_phys_to_bus(map); xen_dma_map_page(hwdev, pfn_to_page(map >> PAGE_SHIFT), dev_addr, map & ~PAGE_MASK, sg->length, dir, attrs); - sg->dma_address = xen_phys_to_bus(map); + sg->dma_address = dev_addr; } else { /* we are not interested in the dma_addr returned by * xen_dma_map_page, only in the potential cache flushes executed |
