blob: 33ec7dfb12de9c3bd883ff3c935d49f1fd2d5c9f [file] [log] [blame]
/*
* Copyright (C) 2018 Samsung Electronics Co., Ltd.
*
* Authors: Shaik Ameer Basha <shaik.ameer@samsung.com>
*
* 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.
*/
#include <linux/types.h>
#include <linux/atomic.h>
#include <linux/device.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/ktime.h>
#include <linux/kthread.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/gpio/consumer.h>
#include <linux/gpio/machine.h>
#include <linux/slab.h>
#include <linux/stat.h>
#include <linux/string.h>
#include <linux/sysfs.h>
#include <linux/interrupt.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <asm/segment.h>
#include <linux/firmware.h>
#include <linux/pci.h>
#include <linux/airbrush-sm-ctrl.h>
#include <linux/clk.h>
#include "airbrush-ddr.h"
#include "airbrush-pmic-ctrl.h"
#include "airbrush-regs.h"
#include "airbrush-spi.h"
#include "airbrush-thermal.h"
#define REG_AON_PCI_OTP 0x10b3000c
#define REG_SRAM_ADDR 0x10b30374
#define REG_DDR_INIT 0x10b30378
#define REG_UPL_CMPL 0x10b30384
#define RAM_CRC_CLR 0x10b30390
#define RAM_CRC_En 0x10b30394
#define RAM_CRC_VAL 0x10b30398
#define GPB0_DRV 0x10b4006c
#define ROM_BL_ADDR 0
#define SRAM_PCI_OTP 0x20414
#define SRAM_PCI_BAR (SRAM_PCI_OTP + 4*253)
#define SOC_PWRGOOD_WAIT_TIMEOUT msecs_to_jiffies(100)
#define AB_READY_WAIT_TIMEOUT msecs_to_jiffies(100)
#define POLL_USLEEP_MIN (100)
#define M0_FIRMWARE_PATH "ab.fw"
static int enable_ref_clk(struct device *dev)
{
struct clk *ref_clk = clk_get(dev, "ab_ref");
if (!IS_ERR(ref_clk))
return clk_prepare_enable(ref_clk);
else
return PTR_ERR(ref_clk);
}
void parse_fw(uint32_t *image_dw_buf, const unsigned char *image_buf,
int image_size_dw)
{
int dw, byte;
for (dw = 0; dw < image_size_dw; dw++) {
byte = dw * 4;
image_dw_buf[dw] = (image_buf[byte + 3] << 24 |
image_buf[byte + 2] << 16 | image_buf[byte + 1] << 8 |
image_buf[byte]);
}
}
/* Caller must hold ab_ctx->state_lock */
int ab_bootsequence(struct ab_state_context *ab_ctx, enum chip_state prev_state)
{
/* Number of attempts to flash SRAM bootcode when CRC error happens */
int num_attempts = 1;
int crc_ok = 1;
unsigned int data;
struct airbrush_spi_packet spi_packet;
int ret;
struct platform_device *plat_dev = ab_ctx->pdev;
unsigned long timeout;
uint32_t dummy[3] = { 0 };
enum ab_chip_id saved_chip_id, raw_chip_id;
if (!ab_ctx)
return -EINVAL;
if (ab_ctx->cold_boot) {
ab_disable_pgood(ab_ctx);
msm_pcie_assert_perst(1);
/* Disable PCIe equalization only for Airbrush A0 parts.
* A0 => PCIe EQ disable
* B0 => PCIe EQ enable
*/
saved_chip_id = ab_get_chip_id(ab_ctx);
if (saved_chip_id == CHIP_ID_B0) {
dev_info(ab_ctx->dev,
"AB version is B0\n");
msm_pcie_eq_ctrl(1, /*enable=*/true);
} else if (saved_chip_id == CHIP_ID_A0) {
dev_err(ab_ctx->dev,
"AB version is A0, which is not fully supported\n");
msm_pcie_eq_ctrl(1, /*enable=*/false);
} else {
dev_warn(ab_ctx->dev,
"WARNING: AB version is unknown\n");
msm_pcie_eq_ctrl(1, /*enable=*/false);
}
}
ret = ab_pmic_on(ab_ctx);
if (ret) {
dev_err(ab_ctx->dev, "ERROR!!! PMIC failure during ABC Boot");
return ret;
}
ret = enable_ref_clk(ab_ctx->dev);
if (ret) {
dev_err(ab_ctx->dev,
"Unable to enable reference clock (err %d)",
ret);
return ret;
}
if (prev_state == CHIP_STATE_100) {
/* M0 samples DDR_SR for its ddr_train sequence */
ab_gpio_enable_ddr_sr(ab_ctx);
/*
* DDRCKE_ISO stays on during suspend in production.
* Disable it during resume before asserting pgood
* (b/128545111).
*/
if (!ab_ctx->ddrcke_iso_clamp_wr)
ab_gpio_disable_ddr_iso(ab_ctx);
}
if (ab_ctx->alternate_boot)
ab_gpio_enable_fw_patch(ab_ctx);
msm_pcie_deassert_perst(1);
ab_enable_pgood(ab_ctx);
timeout = jiffies + SOC_PWRGOOD_WAIT_TIMEOUT;
/* Wait until soc_pwrgood is set by PMIC */
while (!gpiod_get_value_cansleep(ab_ctx->soc_pwrgood) &&
time_before(jiffies, timeout))
usleep_range(POLL_USLEEP_MIN, POLL_USLEEP_MIN + 1);
if (!gpiod_get_value_cansleep(ab_ctx->soc_pwrgood)) {
dev_err(&plat_dev->dev, "ABC PWRGOOD is not enabled");
return -EIO;
}
ab_sm_start_ts(AB_SM_TS_ALT_BOOT);
if (ab_ctx->alternate_boot) {
dev_info(ab_ctx->dev, "alternate boot\n");
/* Wait for AB_READY = 1,
* this ensures the SPI FSM is initialized to flash the
* alternate bootcode to SRAM.
*/
timeout = jiffies + AB_READY_WAIT_TIMEOUT;
while (!gpiod_get_value_cansleep(ab_ctx->ab_ready) &&
time_before(jiffies, timeout))
usleep_range(POLL_USLEEP_MIN, POLL_USLEEP_MIN + 1);
if (!gpiod_get_value_cansleep(ab_ctx->ab_ready)) {
dev_err(&plat_dev->dev,
"ab_ready is not high before fw load");
return -EIO;
}
/* Enable CRC via SPI-FSM */
while (num_attempts) {
/* [TBD] Reset CRC Register via SPI-FSM */
/* PCIe PHY OTP patch count */
spi_packet.command = AB_SPI_CMD_FSM_WRITE_SINGLE;
spi_packet.granularity = FOUR_BYTE;
spi_packet.base_address = SRAM_PCI_OTP;
spi_packet.data_length = 1;
spi_packet.data = dummy;
if (airbrush_spi_run_cmd(&spi_packet))
return -EIO;
/* BAR0, 2, and 4 sizes */
spi_packet.command = AB_SPI_CMD_FSM_BURST_WRITE;
spi_packet.granularity = FOUR_BYTE;
spi_packet.base_address = SRAM_PCI_BAR;
spi_packet.data_length = 3;
spi_packet.data = dummy;
if (airbrush_spi_run_cmd(&spi_packet))
return -EIO;
/* Stop OTP read of PCIe configuration */
dummy[0] = 2;
spi_packet.command = AB_SPI_CMD_FSM_WRITE_SINGLE;
spi_packet.granularity = FOUR_BYTE;
spi_packet.base_address = REG_AON_PCI_OTP;
spi_packet.data_length = 1;
spi_packet.data = dummy;
if (airbrush_spi_run_cmd(&spi_packet))
return -EIO;
/* if CRC is OK, break the loop */
if (crc_ok)
break;
/* Decrement the number of attempts and retry */
num_attempts--;
if (!num_attempts)
return -EIO;
}
/* set REG_SRAM_ADDR=SRAM_BL_ADDR via SPI-FSM */
data = ROM_BL_ADDR;
spi_packet.command = AB_SPI_CMD_FSM_WRITE_SINGLE;
spi_packet.granularity = FOUR_BYTE;
spi_packet.base_address = REG_SRAM_ADDR;
spi_packet.data_length = 1;
spi_packet.data = &data;
if (airbrush_spi_run_cmd(&spi_packet))
return -EIO;
/* set REG_UPL_CMPL=1 via SPI-FSM */
data = 0x1; // used to write REG_UPL_CMPL = 0x1
spi_packet.command = AB_SPI_CMD_FSM_WRITE_SINGLE;
spi_packet.granularity = FOUR_BYTE;
spi_packet.base_address = REG_UPL_CMPL;
spi_packet.data_length = 1;
spi_packet.data = &data;
if (airbrush_spi_run_cmd(&spi_packet))
return -EIO;
/* Read the REG_UPL_COMPL register to make sure the
* AB_READY is deasserted by the Airbrush.
* Note: Assumption is that, by the time host reads the
* REG_UPL_CMPL register, Airbrush would have deasserted
* the AB_READY gpio.
*/
spi_packet.command = AB_SPI_CMD_FSM_READ_SINGLE;
spi_packet.granularity = FOUR_BYTE;
spi_packet.base_address = REG_UPL_CMPL;
spi_packet.data_length = 1;
spi_packet.data = NULL;
if (airbrush_spi_run_cmd(&spi_packet))
return -EIO;
}
ab_sm_record_ts(AB_SM_TS_ALT_BOOT);
if (ab_ctx->cold_boot) {
ab_sm_start_ts(AB_SM_TS_PCIE_ENUM);
ret = ab_sm_enumerate_pcie(ab_ctx);
ab_sm_record_ts(AB_SM_TS_PCIE_ENUM);
if (ret)
return ret;
ab_ctx->cold_boot = false;
/* Cross-check chip id after cold boot. */
raw_chip_id = ab_get_raw_chip_id(ab_ctx);
if (saved_chip_id != raw_chip_id) {
dev_warn(ab_ctx->dev,
"saved_chip_id %d does not match raw_chip_id %d\n",
saved_chip_id, raw_chip_id);
if (raw_chip_id == CHIP_ID_UNKNOWN) {
dev_warn(ab_ctx->dev,
"Keep %d as chip_id\n",
ab_ctx->chip_id);
} else {
dev_warn(ab_ctx->dev,
"Use %d as chip_id\n",
raw_chip_id);
ab_ctx->chip_id = raw_chip_id;
}
}
ab_sm_start_ts(AB_SM_TS_LVCC_INIT);
ab_lvcc_init(&ab_ctx->asv_info);
ab_sm_record_ts(AB_SM_TS_LVCC_INIT);
} else {
ab_sm_start_ts(AB_SM_TS_PCIE_ENUM);
if (ab_ctx->debug_skip_pcie_link_init) {
dev_warn(ab_ctx->dev,
"Skip PCIe link resume for testing\n");
ab_sm_record_ts(AB_SM_TS_PCIE_ENUM);
return -ENODEV;
}
ret = ab_sm_enable_pcie(ab_ctx);
ab_sm_record_ts(AB_SM_TS_PCIE_ENUM);
if (ret)
return ret;
}
/* Wait for AB_READY = 1,
* this ensures the SPI FSM is initialized to flash the
* alternate bootcode to SRAM.
*/
timeout = jiffies + AB_READY_WAIT_TIMEOUT;
while (!gpiod_get_value_cansleep(ab_ctx->ab_ready) &&
time_before(jiffies, timeout)) {
usleep_range(POLL_USLEEP_MIN, POLL_USLEEP_MIN + 1);
}
if (!gpiod_get_value_cansleep(ab_ctx->ab_ready)) {
dev_err(&plat_dev->dev,
"ab_ready is not high after fw load\n");
return -EIO;
}
ab_sm_start_ts(AB_SM_TS_AB_READY_NOTIFY);
mutex_lock(&ab_ctx->mfd_lock);
ret = ab_ctx->mfd_ops->ab_ready(ab_ctx->mfd_ops->ctx);
mutex_unlock(&ab_ctx->mfd_lock);
ab_sm_record_ts(AB_SM_TS_AB_READY_NOTIFY);
/* b/129788388: configure PCIe PCS block to disable PLL at the start of
* P1.CPM state
*/
ABC_WRITE(PCS_OUT_VEC_4, 0x700DD);
/* Enable schmitt trigger mode for SPI clk pad.
* This is to filter out any noise on SPI clk line.
* Also reset the SPI controller in case it's already
* in a glitched state.
*/
ABC_WRITE(GPB0_DRV, 0x22222262);
ABC_WRITE(SYSREG_AON_SPI0_AHB_ENABLE, 0x0);
ABC_WRITE(SYSREG_AON_SPI0_AHB_ENABLE, 0x1);
/* Set clocks to usable states */
ab_sm_start_ts(AB_SM_TS_CLK_INIT);
ab_ctx->clk_ops->init(ab_ctx->clk_ops->ctx);
ab_sm_record_ts(AB_SM_TS_CLK_INIT);
ab_sm_start_ts(AB_SM_TS_DDR_INIT);
/* Setup the function pointer to read DDR OTPs */
ret = ab_ctx->dram_ops->setup(ab_ctx->dram_ops->ctx, ab_ctx);
if (ret) {
dev_err(ab_ctx->dev, "ddr setup failed\n");
return ret;
}
/* Wait till the ddr init & training is completed in case of ddr
* initialization is done by BootROM
*/
if (IS_M0_DDR_INIT()) {
if (ab_ctx->dram_ops->wait_for_m0_ddr_init(
ab_ctx->dram_ops->ctx))
return -EIO;
}
ret = ab_ctx->dram_ops->init(ab_ctx->dram_ops->ctx);
if (ret) {
dev_err(ab_ctx->dev, "ddr init failed\n");
return ret;
}
ab_sm_record_ts(AB_SM_TS_DDR_INIT);
/*
* Enable thermal after boot sequence finished successfully. Do not
* let thermal throttle intefere the bootsequence.
*/
ab_thermal_enable(ab_ctx->thermal);
return 0;
}
EXPORT_SYMBOL(ab_bootsequence);