// SPDX-License-Identifier: GPL-2.0+
/*
* Driver for Realtek PCI-Express card reader
*
* Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved.
*
* Author:
* Wei WANG (wei_wang@realsil.com.cn)
* Micky Ching (micky_ching@realsil.com.cn)
*/
#include <linux/blkdev.h>
#include <linux/kthread.h>
#include <linux/sched.h>
#include "rtsx.h"
#include "spi.h"
static inline void spi_set_err_code(struct rtsx_chip *chip, u8 err_code)
{
struct spi_info *spi = &chip->spi;
spi->err_code = err_code;
}
static int spi_init(struct rtsx_chip *chip)
{
int retval;
retval = rtsx_write_register(chip, SPI_CONTROL, 0xFF,
CS_POLARITY_LOW | DTO_MSB_FIRST
| SPI_MASTER | SPI_MODE0 | SPI_AUTO);
if (retval)
return retval;
retval = rtsx_write_register(chip, SPI_TCTL, EDO_TIMING_MASK,
SAMPLE_DELAY_HALF);
if (retval)
return retval;
return STATUS_SUCCESS;
}
static int spi_set_init_para(struct rtsx_chip *chip)
{
struct spi_info *spi = &chip->spi;
int retval;
retval = rtsx_write_register(chip, SPI_CLK_DIVIDER1, 0xFF,
(u8)(spi->clk_div >> 8));
if (retval)
return retval;
retval = rtsx_write_register(chip, SPI_CLK_DIVIDER0, 0xFF,
(u8)(spi->clk_div));
if (retval)
return retval;
retval = switch_clock(chip, spi->spi_clock);
if (retval != STATUS_SUCCESS)
return STATUS_FAIL;
retval = select_card(chip, SPI_CARD);
if (retval != STATUS_SUCCESS)
return STATUS_FAIL;
retval = rtsx_write_register(chip, CARD_CLK_EN, SPI_CLK_EN,
SPI_CLK_EN);
if (retval)
return retval;
retval = rtsx_write_register(chip, CARD_OE, SPI_OUTPUT_EN,
SPI_OUTPUT_EN);
if (retval)
return retval;
wait_timeout(10);
retval = spi_init(chip);
if (retval != STATUS_SUCCESS)
return STATUS_FAIL;
return STATUS_SUCCESS;
}
static int sf_polling_status(struct rtsx_chip *chip, int msec)
{
int retval;
rtsx_init_cmd(chip);
rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_COMMAND, 0xFF, SPI_RDSR);
rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_TRANSFER0, 0xFF,
SPI_TRANSFER0_START | SPI_POLLING_MODE0);
rtsx_add_cmd(chip, CHECK_REG_CMD, SPI_TRANSFER0, SPI_TRANSFER0_END,
SPI_TRANSFER0_END);
retval = rtsx_send_cmd(chip, 0, msec);
if (retval < 0) {
rtsx_clear_spi_error(chip);
spi_set_err_code(chip, SPI_BUSY_ERR);
return STATUS_FAIL;
}
return STATUS_SUCCESS;
}
static int sf_enable_write(struct rtsx_chip *chip, u8 ins)
{
struct spi_info *spi = &chip->spi;
int retval;
if (!spi->write_en)
return STATUS_SUCCESS;
rtsx_init_cmd(chip);
rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_COMMAND, 0xFF, ins);
rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_CA_NUMBER, 0xFF,
SPI_COMMAND_BIT_8 | SPI_ADDRESS_BIT_24);
rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_TRANSFER0, 0xFF,
SPI_TRANSFER0_START | SPI_C_MODE0);
rtsx_add_cmd(chip, CHECK_REG_CMD, SPI_TRANSFER0, SPI_TRANSFER0_END,
SPI_TRANSFER0_END);
retval = rtsx_send_cmd(chip, 0, 100);
if (retval < 0) {
rtsx_clear_spi_error(chip);
spi_set_err_code(chip, SPI_HW_ERR);
return STATUS_FAIL;
}
return STATUS_SUCCESS;
}
static int sf_disable_write(struct rtsx_chip *chip, u8 ins)
{
struct spi_info *spi = &chip->spi;
int retval;
if (!spi->write_en)
return STATUS_SUCCESS;
rtsx_init_cmd(chip);
rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_COMMAND, 0xFF, ins);
rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_CA_NUMBER, 0xFF,
SPI_COMMAND_BIT_8 | SPI_ADDRESS_BIT_24);
rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_TRANSFER0, 0xFF,
SPI_TRANSFER0_START | SPI_C_MODE0);
rtsx_add_cmd(chip, CHECK_REG_CMD, SPI_TRANSFER0, SPI_TRANSFER0_END,
SPI_TRANSFER0_END);
retval = rtsx_send_cmd(chip, 0, 100);
if (retval < 0) {
rtsx_clear_spi_error(chip);
spi_set_err_code(chip, SPI_HW_ERR);
return STATUS_FAIL;
}
return STATUS_SUCCESS;
}
static void sf_program(struct rtsx_chip *chip, u8 ins, u8