/* Copyright (c) 2014-2015, 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 "mhi_sys.h" #include "mhi_hwio.h" #include "mhi.h" enum MHI_STATUS mhi_test_for_device_reset(struct mhi_device_ctxt *mhi_dev_ctxt) { u32 pcie_word_val = 0; u32 expiry_counter; mhi_log(MHI_MSG_INFO, "Waiting for MMIO RESET bit to be cleared.\n"); pcie_word_val = mhi_reg_read(mhi_dev_ctxt->mmio_info.mmio_addr, MHISTATUS); MHI_READ_FIELD(pcie_word_val, MHICTRL_RESET_MASK, MHICTRL_RESET_SHIFT); if (pcie_word_val == 0xFFFFFFFF) return MHI_STATUS_LINK_DOWN; while (MHI_STATE_RESET != pcie_word_val && expiry_counter < 100) { expiry_counter++; mhi_log(MHI_MSG_ERROR, "Device is not RESET, sleeping and retrying.\n"); msleep(MHI_READY_STATUS_TIMEOUT_MS); pcie_word_val = mhi_reg_read(mhi_dev_ctxt->mmio_info.mmio_addr, MHICTRL); MHI_READ_FIELD(pcie_word_val, MHICTRL_RESET_MASK, MHICTRL_RESET_SHIFT); } if (MHI_STATE_READY != pcie_word_val) return MHI_STATUS_DEVICE_NOT_READY; return MHI_STATUS_SUCCESS; } enum MHI_STATUS mhi_test_for_device_ready(struct mhi_device_ctxt *mhi_dev_ctxt) { u32 pcie_word_val = 0; u32 expiry_counter; mhi_log(MHI_MSG_INFO, "Waiting for MMIO Ready bit to be set\n"); /* Read MMIO and poll for READY bit to be set */ pcie_word_val = mhi_reg_read( mhi_dev_ctxt->mmio_info.mmio_addr, MHISTATUS); MHI_READ_FIELD(pcie_word_val, MHISTATUS_READY_MASK, MHISTATUS_READY_SHIFT); if (pcie_word_val == 0xFFFFFFFF) return MHI_STATUS_LINK_DOWN; expiry_counter = 0; while (MHI_STATE_READY != pcie_word_val && expiry_counter < 50) { expiry_counter++; mhi_log(MHI_MSG_ERROR, "Device is not ready, sleeping and retrying.\n"); msleep(MHI_READY_STATUS_TIMEOUT_MS); pcie_word_val = mhi_reg_read(mhi_dev_ctxt->mmio_info.mmio_addr, MHISTATUS); MHI_READ_FIELD(pcie_word_val, MHISTATUS_READY_MASK, MHISTATUS_READY_SHIFT); } if (pcie_word_val != MHI_STATE_READY) return MHI_STATUS_DEVICE_NOT_READY; return MHI_STATUS_SUCCESS; } enum MHI_STATUS mhi_init_mmio(struct mhi_device_ctxt *mhi_dev_ctxt) { u64 pcie_dword_val = 0; u32 pcie_word_val = 0; u32 i = 0; enum MHI_STATUS ret_val; mhi_log(MHI_MSG_INFO, "~~~ Initializing MMIO ~~~\n"); mhi_dev_ctxt->mmio_info.mmio_addr = mhi_dev_ctxt->dev_props->bar0_base; mhi_log(MHI_MSG_INFO, "Bar 0 address is at: 0x%p\n", mhi_dev_ctxt->mmio_info.mmio_addr); mhi_dev_ctxt->mmio_info.mmio_len = mhi_reg_read( mhi_dev_ctxt->mmio_info.mmio_addr, MHIREGLEN); if (0 == mhi_dev_ctxt->mmio_info.mmio_len) { mhi_log(MHI_MSG_ERROR, "Received mmio length as zero\n"); return MHI_STATUS_ERROR; } mhi_log(MHI_MSG_INFO, "Testing MHI Ver\n"); mhi_dev_ctxt->dev_props->mhi_ver = mhi_reg_read( mhi_dev_ctxt->mmio_info.mmio_addr, MHIVER); if (MHI_VERSION != mhi_dev_ctxt->dev_props->mhi_ver) { mhi_log(MHI_MSG_CRITICAL, "Bad MMIO version, 0x%x\n", mhi_dev_ctxt->dev_props->mhi_ver); if (mhi_dev_ctxt->dev_props->mhi_ver == 0xFFFFFFFF) ret_val = mhi_wait_for_mdm(mhi_dev_ctxt); if (ret_val) return MHI_STATUS_ERROR; } /* Enable the channels */ for (i = 0; i < MHI_MAX_CHANNELS; ++i) { struct mhi_chan_ctxt *chan_ctxt = &mhi_dev_ctxt->dev_space.ring_ctxt.cc_list[i]; if (VALID_CHAN_NR(i)) chan_ctxt->mhi_chan_state = MHI_CHAN_STATE_ENABLED; else chan_ctxt->mhi_chan_state = MHI_CHAN_STATE_DISABLED; } mhi_log(MHI_MSG_INFO, "Read back MMIO Ready bit successfully. Moving on..\n"); mhi_log(MHI_MSG_INFO, "Reading channel doorbell offset\n"); mhi_dev_ctxt->mmio_info.chan_db_addr = mhi_dev_ctxt->mmio_info.mmio_addr; mhi_dev_ctxt->mmio_info.event_db_addr = mhi_dev_ctxt->mmio_info.mmio_addr; mhi_dev_ctxt->mmio_info.chan_db_addr += mhi_reg_read_field( mhi_dev_ctxt->mmio_info.mmio_addr, CHDBOFF, CHDBOFF_CHDBOFF_MASK, CHDBOFF_CHDBOFF_SHIFT); mhi_log(MHI_MSG_INFO, "Reading event doorbell offset\n"); mhi_dev_ctxt->mmio_info.event_db_addr += mhi_reg_read_field( mhi_dev_ctxt->mmio_info.mmio_addr, ERDBOFF, ERDBOFF_ERDBOFF_MASK, ERDBOFF_ERDBOFF_SHIFT); mhi_log(MHI_MSG_INFO, "Setting all MMIO values.\n"); mhi_reg_write_field(mhi_dev_ctxt, mhi_dev_ctxt->mmio_info.mmio_addr, MHICFG, MHICFG_NER_MASK, MHICFG_NER_SHIFT, mhi_dev_ctxt->mmio_info.nr_event_rings); pcie_dword_val = mhi_dev_ctxt->dev_space.ring_ctxt.dma_cc_list; pcie_word_val = HIGH_WORD(pcie_dword_val); mhi_reg_write_field(mhi_dev_ctxt, mhi_dev_ctxt->mmio_info.mmio_addr, CCABAP_HIGHER, CCABAP_HIGHER_CCABAP_HIGHER_MASK, CCABAP_HIGHER_CCABAP_HIGHER_SHIFT, pcie_word_val); pcie_word_val = LOW_WORD(pcie_dword_val); mhi_reg_write_field(mhi_dev_ctxt, mhi_dev_ctxt->mmio_info.mmio_addr, CCABAP_LOWER, CCABAP_LOWER_CCABAP_LOWER_MASK, CCABAP_LOWER_CCABAP_LOWER_SHIFT, pcie_word_val); /* Write the Event Context Base Address Register High and Low parts */ pcie_dword_val = mhi_dev_ctxt->dev_space.ring_ctxt.dma_ec_list; pcie_word_val = HIGH_WORD(pcie_dword_val); mhi_reg_write_field(mhi_dev_ctxt, mhi_dev_ctxt->mmio_info.mmio_addr, ECABAP_HIGHER, ECABAP_HIGHER_ECABAP_HIGHER_MASK, ECABAP_HIGHER_ECABAP_HIGHER_SHIFT, pcie_word_val); pcie_word_val = LOW_WORD(pcie_dword_val); mhi_reg_write_field(mhi_dev_ctxt, mhi_dev_ctxt->mmio_info.mmio_addr, ECABAP_LOWER, ECABAP_LOWER_ECABAP_LOWER_MASK, ECABAP_LOWER_ECABAP_LOWER_SHIFT, pcie_word_val); /* Write the Command Ring Control Register High and Low parts */ pcie_dword_val = mhi_dev_ctxt->dev_space.ring_ctxt.dma_cmd_ctxt; pcie_word_val = HIGH_WORD(pcie_dword_val); mhi_reg_write_field(mhi_dev_ctxt, mhi_dev_ctxt->mmio_info.mmio_addr, CRCBAP_HIGHER, CRCBAP_HIGHER_CRCBAP_HIGHER_MASK, CRCBAP_HIGHER_CRCBAP_HIGHER_SHIFT, pcie_word_val); pcie_word_val = LOW_WORD(pcie_dword_val); mhi_reg_write_field(mhi_dev_ctxt, mhi_dev_ctxt->mmio_info.mmio_addr, CRCBAP_LOWER, CRCBAP_LOWER_CRCBAP_LOWER_MASK, CRCBAP_LOWER_CRCBAP_LOWER_SHIFT, pcie_word_val); mhi_dev_ctxt->mmio_info.cmd_db_addr = mhi_dev_ctxt->mmio_info.mmio_addr + CRDB_LOWER; /* Set the control and data segments device MMIO */ pcie_dword_val = mhi_dev_ctxt->dev_space.start_win_addr; pcie_word_val = HIGH_WORD(pcie_dword_val); mhi_reg_write_field(mhi_dev_ctxt, mhi_dev_ctxt->mmio_info.mmio_addr, MHICTRLBASE_HIGHER, MHICTRLBASE_HIGHER_MHICTRLBASE_HIGHER_MASK, MHICTRLBASE_HIGHER_MHICTRLBASE_HIGHER_SHIFT, pcie_word_val); mhi_reg_write_field(mhi_dev_ctxt, mhi_dev_ctxt->mmio_info.mmio_addr, MHIDATABASE_HIGHER, MHIDATABASE_HIGHER_MHIDATABASE_HIGHER_MASK, MHIDATABASE_HIGHER_MHIDATABASE_HIGHER_SHIFT, pcie_word_val); pcie_word_val = LOW_WORD(pcie_dword_val); mhi_reg_write_field(mhi_dev_ctxt, mhi_dev_ctxt->mmio_info.mmio_addr, MHICTRLBASE_LOWER, MHICTRLBASE_LOWER_MHICTRLBASE_LOWER_MASK, MHICTRLBASE_LOWER_MHICTRLBASE_LOWER_SHIFT, pcie_word_val); mhi_reg_write_field(mhi_dev_ctxt, mhi_dev_ctxt->mmio_info.mmio_addr, MHIDATABASE_LOWER, MHIDATABASE_LOWER_MHIDATABASE_LOWER_MASK, MHIDATABASE_LOWER_MHIDATABASE_LOWER_SHIFT, pcie_word_val); pcie_dword_val = mhi_dev_ctxt->dev_space.end_win_addr; pcie_word_val = HIGH_WORD(pcie_dword_val); mhi_reg_write_field(mhi_dev_ctxt, mhi_dev_ctxt->mmio_info.mmio_addr, MHICTRLLIMIT_HIGHER, MHICTRLLIMIT_HIGHER_MHICTRLLIMIT_HIGHER_MASK, MHICTRLLIMIT_HIGHER_MHICTRLLIMIT_HIGHER_SHIFT, pcie_word_val); mhi_reg_write_field(mhi_dev_ctxt, mhi_dev_ctxt->mmio_info.mmio_addr, MHIDATALIMIT_HIGHER, MHIDATALIMIT_HIGHER_MHIDATALIMIT_HIGHER_MASK, MHIDATALIMIT_HIGHER_MHIDATALIMIT_HIGHER_SHIFT, pcie_word_val); pcie_word_val = LOW_WORD(pcie_dword_val); mhi_reg_write_field(mhi_dev_ctxt, mhi_dev_ctxt->mmio_info.mmio_addr, MHICTRLLIMIT_LOWER, MHICTRLLIMIT_LOWER_MHICTRLLIMIT_LOWER_MASK, MHICTRLLIMIT_LOWER_MHICTRLLIMIT_LOWER_SHIFT, pcie_word_val); mhi_reg_write_field(mhi_dev_ctxt, mhi_dev_ctxt->mmio_info.mmio_addr, MHIDATALIMIT_LOWER, MHIDATALIMIT_LOWER_MHIDATALIMIT_LOWER_MASK, MHIDATALIMIT_LOWER_MHIDATALIMIT_LOWER_SHIFT, pcie_word_val); mhi_log(MHI_MSG_INFO, "Done..\n"); return MHI_STATUS_SUCCESS; }