root/drivers/net/ethernet/asix/ax88796c_spi.c
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (c) 2010 ASIX Electronics Corporation
 * Copyright (c) 2020 Samsung Electronics Co., Ltd.
 *
 * ASIX AX88796C SPI Fast Ethernet Linux driver
 */

#define pr_fmt(fmt)     "ax88796c: " fmt

#include <linux/string.h>
#include <linux/spi/spi.h>

#include "ax88796c_spi.h"

const u8 ax88796c_rx_cmd_buf[5] = {AX_SPICMD_READ_RXQ, 0xFF, 0xFF, 0xFF, 0xFF};
const u8 ax88796c_tx_cmd_buf[4] = {AX_SPICMD_WRITE_TXQ, 0xFF, 0xFF, 0xFF};

/* driver bus management functions */
int axspi_wakeup(struct axspi_data *ax_spi)
{
        int ret;

        ax_spi->cmd_buf[0] = AX_SPICMD_EXIT_PWD;        /* OP */
        ret = spi_write(ax_spi->spi, ax_spi->cmd_buf, 1);
        if (ret)
                dev_err(&ax_spi->spi->dev, "%s() failed: ret = %d\n", __func__, ret);
        return ret;
}

int axspi_read_status(struct axspi_data *ax_spi, struct spi_status *status)
{
        int ret;

        /* OP */
        ax_spi->cmd_buf[0] = AX_SPICMD_READ_STATUS;
        ret = spi_write_then_read(ax_spi->spi, ax_spi->cmd_buf, 1, (u8 *)status, 3);
        if (ret)
                dev_err(&ax_spi->spi->dev, "%s() failed: ret = %d\n", __func__, ret);
        else
                le16_to_cpus(&status->isr);

        return ret;
}

int axspi_read_rxq(struct axspi_data *ax_spi, void *data, int len)
{
        struct spi_transfer *xfer = ax_spi->spi_rx_xfer;
        int ret;

        memcpy(ax_spi->cmd_buf, ax88796c_rx_cmd_buf, 5);

        xfer->tx_buf = ax_spi->cmd_buf;
        xfer->rx_buf = NULL;
        xfer->len = ax_spi->comp ? 2 : 5;
        xfer->bits_per_word = 8;
        spi_message_add_tail(xfer, &ax_spi->rx_msg);

        xfer++;
        xfer->rx_buf = data;
        xfer->tx_buf = NULL;
        xfer->len = len;
        xfer->bits_per_word = 8;
        spi_message_add_tail(xfer, &ax_spi->rx_msg);
        ret = spi_sync(ax_spi->spi, &ax_spi->rx_msg);
        if (ret)
                dev_err(&ax_spi->spi->dev, "%s() failed: ret = %d\n", __func__, ret);

        return ret;
}

int axspi_write_txq(const struct axspi_data *ax_spi, void *data, int len)
{
        return spi_write(ax_spi->spi, data, len);
}

u16 axspi_read_reg(struct axspi_data *ax_spi, u8 reg)
{
        int ret;
        int len = ax_spi->comp ? 3 : 4;

        ax_spi->cmd_buf[0] = 0x03;      /* OP code read register */
        ax_spi->cmd_buf[1] = reg;       /* register address */
        ax_spi->cmd_buf[2] = 0xFF;      /* dumy cycle */
        ax_spi->cmd_buf[3] = 0xFF;      /* dumy cycle */
        ret = spi_write_then_read(ax_spi->spi,
                                  ax_spi->cmd_buf, len,
                                  ax_spi->rx_buf, 2);
        if (ret) {
                dev_err(&ax_spi->spi->dev,
                        "%s() failed: ret = %d\n", __func__, ret);
                return 0xFFFF;
        }

        le16_to_cpus((u16 *)ax_spi->rx_buf);

        return *(u16 *)ax_spi->rx_buf;
}

int axspi_write_reg(struct axspi_data *ax_spi, u8 reg, u16 value)
{
        int ret;

        memset(ax_spi->cmd_buf, 0, sizeof(ax_spi->cmd_buf));
        ax_spi->cmd_buf[0] = AX_SPICMD_WRITE_REG;       /* OP code read register */
        ax_spi->cmd_buf[1] = reg;                       /* register address */
        ax_spi->cmd_buf[2] = value;
        ax_spi->cmd_buf[3] = value >> 8;

        ret = spi_write(ax_spi->spi, ax_spi->cmd_buf, 4);
        if (ret)
                dev_err(&ax_spi->spi->dev, "%s() failed: ret = %d\n", __func__, ret);
        return ret;
}