Files
2026-08-24 10:03:24 +08:00

821 lines
20 KiB
C

// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
* Copyright (c) 2019 Amlogic, Inc. All rights reserved.
*/
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/sched/task_stack.h>
#include <linux/dma-mapping.h>
#include <linux/regmap.h>
#include <linux/spi/spi.h>
#include <linux/types.h>
#include <linux/atomic.h>
#include <linux/mtd/spinand.h>
#include <linux/spi/spi-mem.h>
#include <linux/mtd/mtd.h>
#include <linux/amlogic/aml_spi_mem.h>
#include "linux/amlogic/aml_pageinfo.h"
#include "nfc.h"
//#define __SPI_NFC_DEBUG__
#ifdef __SPI_NFC_DEBUG__
#define SPI_NFC_DEBUG(...) pr_info(__VA_ARGS__)
#else
#define SPI_NFC_DEBUG(...)
#endif
#define REG_MAX 0x80
#define CMD_PROG_LOAD 0x02
#define CMD_PROG_LOAD_RDM_DATA 0x84
#define CMD_PROG_LOAD_X4 0x32
#define CMD_PROG_LOAD_RDM_DATA_X4 0x34
#define CMD_READ 0x03
#define CMD_READ_FAST 0x0b
#define CMD_READ_DUAL_OUT 0x3b
#define CMD_READ_QUAD_OUT 0x6b
#define CMD_READ_DUAL_IO 0xbb
#define CMD_READ_QUAD_IO 0xeb
#define IS_READ_CACHE_CMD(cmd) ({ \
__typeof__(cmd) _cmd = (cmd); \
(_cmd) == CMD_READ || \
(_cmd) == CMD_READ_FAST || \
(_cmd) == CMD_READ_DUAL_OUT || \
(_cmd) == CMD_READ_QUAD_OUT || \
(_cmd) == CMD_READ_DUAL_IO || \
(_cmd) == CMD_READ_QUAD_IO; \
})
#define IS_WRITE_CACHE_CMD(cmd) ({ \
__typeof__(cmd) _cmd = (cmd); \
(_cmd) == CMD_PROG_LOAD || \
(_cmd) == CMD_PROG_LOAD_RDM_DATA || \
(_cmd) == CMD_PROG_LOAD_X4 || \
(_cmd) == CMD_PROG_LOAD_RDM_DATA_X4; \
})
#define DATA_BUF_SIZE (4096)
#define OOB_BUF_SIZE (128)
#define SPI_NFC_BUF_SIZE (DATA_BUF_SIZE + OOB_BUF_SIZE)
struct spi_nfc {
struct spi_controller *master;
struct regmap *regmap[3];
struct clk *clk_gate;
struct clk *fix_div2_pll;
struct clk_divider divider;
struct clk *div_clk;
struct device *dev;
void __iomem *nand_clk_reg;
u8 disable_host_ecc;
u8 infopage_force_hostecc;
u8 *data_buf;
u8 *info_buf;
dma_addr_t daddr;
dma_addr_t iaddr;
};
struct spi_nand_id {
u8 mfr_id;
u8 dev_id;
};
struct spi_nfc *spi_nfc;
static u8 spi_nfc_need_infopage_force_hostecc(void)
{
if (spi_nfc)
return spi_nfc->infopage_force_hostecc;
return 0;
}
static inline bool spi_nfc_disable_host_ecc(void)
{
if (spi_nfc && !spi_nfc->disable_host_ecc)
return false;
if (spi_nfc && spi_nfc->infopage_force_hostecc &&
page_info_get_state() == PAGE_INFO_DOING)
return false;
return true;
}
static struct spi_nand_id host_ecc_list[] = {
{0xc2, 0x14}, /* MX35LF1G24AD */
};
static const struct regmap_config spi_nfc_regmap_config = {
.reg_bits = 32,
.val_bits = 32,
.reg_stride = 4,
.max_register = REG_MAX,
};
static int spi_nfc_select_ecc(struct spi_nfc *spi_nfc)
{
u8 addr = 0, value[3] = {0};
u32 ret, i;
static int init_flags;
if (init_flags++)
return 0;
spi_nfc->disable_host_ecc = 1;
ret = NFC_SEND_CMD_ADDR_DATA_RD(0x9F, &addr, 1, value, 3);
if (ret)
return ret;
for (i = 0; i < (sizeof(host_ecc_list) / sizeof(struct spi_nand_id)); i++)
if (host_ecc_list[i].mfr_id == value[0] && host_ecc_list[i].dev_id == value[1])
spi_nfc->disable_host_ecc = 0;
if (spi_nfc->disable_host_ecc &&
device_property_read_bool(spi_nfc->dev, "infopage_force_hostecc"))
spi_nfc->infopage_force_hostecc = 1;
pr_info("spinand use %s ecc!\n", spi_nfc->disable_host_ecc ? "buildin" : "host");
return 0;
}
static int spi_nfc_buffer_init(struct spi_nfc *spi_nfc)
{
spi_nfc->data_buf = kzalloc(SPI_NFC_BUF_SIZE, GFP_KERNEL);
if (!spi_nfc->data_buf)
return -1;
spi_nfc->info_buf = spi_nfc->data_buf + DATA_BUF_SIZE;
return 0;
}
static void spi_nfc_buffer_free(struct spi_nfc *spi_nfc)
{
kfree(spi_nfc->data_buf);
kfree(spi_nfc->info_buf);
}
static void spi_nfc_auto_oob_ops(u8 *info, u8 *oob_pos, int ecc_steps, bool set)
{
u8 *src, *dst, i;
for (i = 0; i < ecc_steps; i++) {
src = (set) ? (info + 2 * i) : (oob_pos + i * (2 + 14));
dst = (set) ? (oob_pos + i * (2 + 14)) : (info + 2 * i);
memcpy(dst, src, 2);
}
}
static void spi_nfc_covert_buf_to_user(struct spi_nfc *spi_nfc,
u8 *user_buf,
u8 *oob_pos, u8 ecc_steps,
bool oob_only, bool auto_oob,
bool oob_required)
{
u8 *buf = spi_nfc->data_buf, *info_buf = spi_nfc->info_buf;
u8 oob_temp[OOB_BUF_SIZE] = {0};
u32 page_size = page_info_get_page_size();
SPI_NFC_DEBUG("oob only %s\n", (oob_only) ? "yes" : "no");
SPI_NFC_DEBUG("oob is %s\n", (oob_required) ? "required" : "not required");
SPI_NFC_DEBUG("auto_oob is %s\n", (auto_oob) ? "set" : "not set");
if (oob_only || oob_required) {
nfc_get_user_byte(oob_temp, (u64 *)info_buf, ecc_steps);
DUMP_BUFFER(info_buf, 64, 4, 16);
if (auto_oob)
spi_nfc_auto_oob_ops(oob_temp, oob_pos,
ecc_steps, true);
else
memcpy(oob_pos, oob_temp, ecc_steps * 2);
}
if (oob_only)
return;
memcpy((u8 *)user_buf, buf, page_size);
DUMP_BUFFER(user_buf, page_size, page_size / 512, 16);
}
static void spi_nfc_covert_buf_to_host(struct spi_nfc *spi_nfc,
u8 *user_buf,
u8 *oob_pos, u8 ecc_steps,
bool oob_only, bool auto_oob,
bool oob_required)
{
u8 *buf = spi_nfc->data_buf, *info_buf = spi_nfc->info_buf;
u8 oob_temp[OOB_BUF_SIZE] = {0};
u32 page_size = page_info_get_page_size();
SPI_NFC_DEBUG("oob only %s\n", (oob_only) ? "yes" : "no");
SPI_NFC_DEBUG("oob is %s\n", (oob_required) ? "required" : "not required");
SPI_NFC_DEBUG("auto_oob is %s\n", (auto_oob) ? "set" : "not set");
DUMP_BUFFER(oob_pos, OOB_BUF_SIZE, 4, 16);
if (oob_only || oob_required) {
if (auto_oob)
spi_nfc_auto_oob_ops(oob_temp, oob_pos,
ecc_steps, false);
else
memcpy(oob_temp, oob_pos, ecc_steps * 2);
}
memset(info_buf, 0, 64);
nfc_set_user_byte(oob_temp, (u64 *)info_buf, ecc_steps);
if (oob_only)
return;
memcpy(buf, user_buf, page_size);
DUMP_BUFFER(buf, page_size, page_size / 512, 16);
}
static int spi_nfc_ooblayout_ecc(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion)
{
if (section >= 8)
return -ERANGE;
oobregion->offset = 2 + (section * (2 + 14));
oobregion->length = 14;
return 0;
}
static int spi_nfc_ooblayout_free(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion)
{
if (section >= 8)
return -ERANGE;
oobregion->offset = section * (2 + 14);
oobregion->length = 2;
return 0;
}
static const struct mtd_ooblayout_ops spi_nfc_ecc_ooblayout = {
.ecc = spi_nfc_ooblayout_ecc,
.free = spi_nfc_ooblayout_free,
};
static void spi_nfc_mtd_info_prepare(struct spi_nfc *spi_nfc)
{
struct mtd_info *mtd = spi_mem_get_mtd();
unsigned int n2m_cmd;
if (!mtd)
return;
page_info_set_page_size(mtd->writesize);
if (spi_nfc_disable_host_ecc()) {
n2m_cmd = N2M_RAW | mtd->writesize;
page_info_set_n2m_command(n2m_cmd);
return;
}
if (!page_info_get_block_size() ||
!GET_BCH_MODE(page_info_get_n2m_command())) {
n2m_cmd = (DEFAULT_ECC_MODE & (~0x3F)) | mtd->writesize >> 9;
page_info_set_n2m_command(n2m_cmd);
if (!spi_nfc_need_infopage_force_hostecc()) {
mtd_set_ooblayout(mtd, &spi_nfc_ecc_ooblayout);
mtd->oobavail = mtd_ooblayout_count_freebytes(mtd);
}
}
SPI_NFC_DEBUG("page_size = 0x%x\n", page_info_get_page_size());
}
static bool spi_nfc_is_buffer_dma_safe(const void *buffer)
{
if ((uintptr_t)buffer % 8)
return false;
if (virt_addr_valid(buffer) && (!object_is_on_stack(buffer)))
return true;
return false;
}
static u8 *spi_nfc_get_dma_safe_buf(u8 *buf, u32 len, bool read)
{
u8 *new_buf;
if (spi_nfc_is_buffer_dma_safe(buf))
return buf;
new_buf = kzalloc(len, GFP_KERNEL);
if (!read && new_buf)
memcpy(new_buf, buf, len);
return new_buf;
}
static void spi_nfc_put_dma_safe_buf(u8 *user_buf, u8 *new_buf,
u32 len, bool read)
{
if (user_buf == new_buf)
return;
if (read)
memcpy(user_buf, new_buf, len);
kfree(new_buf);
}
static int spi_nfc_dma_buffer_setup(struct spi_nfc *spi_nfc, void *databuf,
int datalen, void *infobuf, int infolen,
enum dma_data_direction dir)
{
int ret = 0;
spi_nfc->daddr = dma_map_single(spi_nfc->dev, databuf, datalen, dir);
ret = dma_mapping_error(spi_nfc->dev, spi_nfc->daddr);
if (ret) {
dev_err(spi_nfc->dev, "DMA mapping error\n");
return ret;
}
if (infobuf) {
spi_nfc->iaddr = dma_map_single(spi_nfc->dev, infobuf, infolen, dir);
ret = dma_mapping_error(spi_nfc->dev, spi_nfc->iaddr);
if (ret) {
dev_err(spi_nfc->dev, "DMA mapping error\n");
dma_unmap_single(spi_nfc->dev,
spi_nfc->daddr, datalen, dir);
return ret;
}
}
return ret;
}
static void spi_nfc_dma_buffer_release(struct spi_nfc *spi_nfc,
int datalen, int infolen,
enum dma_data_direction dir)
{
dma_unmap_single(spi_nfc->dev, spi_nfc->daddr, datalen, dir);
if (infolen)
dma_unmap_single(spi_nfc->dev, spi_nfc->iaddr, infolen, dir);
}
static int spi_nfc_dma_xfer(struct spi_nfc *spi_nfc,
struct spi_device *spi,
unsigned long flags,
u8 *user_buf, int len, bool read)
{
u32 page_size, n2m_cmd;
bool raw, oob_only, auto_oob, oob_required;
u8 *buf, *oob_pos, ecc_steps, *temp_buf;
int ret = 0;
oob_only = ((flags & SPI_XFER_OOB_ONLY) != 0);
oob_required = ((flags & SPI_XFER_OOB) != 0);
raw = ((flags & SPI_XFER_RAW) != 0);
auto_oob = ((flags & SPI_XFER_AUTO_OOB) != 0);
spi_nfc_mtd_info_prepare(spi_nfc);
SPI_NFC_DEBUG("flags = %lx user_buf = %p len = 0x%x disable_host_ecc = %d %s\n",
flags, user_buf, len, spi_nfc_disable_host_ecc(),
(read) ? "read" : "write");
page_size = page_info_get_page_size();
oob_pos = (oob_only) ? user_buf : (user_buf + page_size);
if (read ? (spi->mode & SPI_RX_OCTAL) : (spi->mode & SPI_TX_OCTAL))
nfc_set_data_bus_width(3);
else if (read ? (spi->mode & SPI_RX_QUAD) : (spi->mode & SPI_TX_QUAD))
nfc_set_data_bus_width(2);
else if (read ? (spi->mode & SPI_RX_DUAL) : (spi->mode & SPI_TX_DUAL))
nfc_set_data_bus_width(1);
else
nfc_set_data_bus_width(0);
if (raw || spi_nfc_disable_host_ecc()) {
buf = spi_nfc_get_dma_safe_buf(user_buf, len, read);
temp_buf = user_buf;
nfc_raw_size_ext_convert(len);
n2m_cmd = (read) ? N2M_RAW : M2N_RAW;
n2m_cmd |= (len & ((1 << NFC_RAW_CHUNK_SHIFT) - 1));
ecc_steps = 1;
} else {
buf = spi_nfc->data_buf;
temp_buf = spi_nfc->data_buf;
n2m_cmd = nfc_recalculate_n2m_command(len, 1);
ecc_steps = n2m_cmd & 0x3F;
len = DATA_BUF_SIZE;
if (!read) {
n2m_cmd &= ~(N2M_RAW ^ M2N_RAW);
spi_nfc_covert_buf_to_host(spi_nfc, user_buf,
oob_pos, ecc_steps,
oob_only, auto_oob,
oob_required);
}
}
SPI_NFC_DEBUG("n2m_cmd = %x ecc_steps = %x\n", n2m_cmd, ecc_steps);
spi_nfc_dma_buffer_setup(spi_nfc, buf, len,
spi_nfc->info_buf, OOB_BUF_SIZE,
(read) ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
nfc_set_dma_mem_and_info((unsigned long)spi_nfc->daddr,
(unsigned long)spi_nfc->iaddr);
ret = nfc_start_dma_and_wait_done(n2m_cmd);
if (ret || !read) {
spi_nfc_put_dma_safe_buf(temp_buf, buf, len, read);
spi_nfc_dma_buffer_release(spi_nfc, len,
OOB_BUF_SIZE,
DMA_TO_DEVICE);
return ret;
}
ret = nfc_wait_data_and_ecc_engine_done(spi_nfc->dev, spi_nfc->iaddr,
(u64 *)spi_nfc->info_buf,
ecc_steps, raw);
if (ret) {
SPI_NFC_DEBUG("ecc error dump:\n");
DUMP_BUFFER(spi_nfc->info_buf, 64, 4, 16);
DUMP_BUFFER(buf, page_size, page_size / 16, 16);
return ret;
}
spi_nfc_put_dma_safe_buf(temp_buf, buf, len, read);
spi_nfc_dma_buffer_release(spi_nfc, len,
OOB_BUF_SIZE,
DMA_FROM_DEVICE);
if (raw || spi_nfc_disable_host_ecc()) {
DUMP_BUFFER(buf, len, len / 16, 16);
return 0;
}
spi_nfc_covert_buf_to_user(spi_nfc, user_buf,
oob_pos, ecc_steps,
oob_only, auto_oob,
oob_required);
return 0;
}
enum TRANSFER_STATE spi_nfc_get_current_handle_state(struct spi_transfer *xfer)
{
enum TRANSFER_STATE xfer_state = TRANSFER_STATE_NONE;
if (xfer->rx_nbits && !xfer->rx_buf)
xfer_state = xfer->rx_nbits;
else if (xfer->rx_buf)
xfer_state = TRANSFER_STATE_DATAIN;
else
xfer_state = TRANSFER_STATE_DATAOUT;
return xfer_state;
}
static int spi_nfc_transfer_one(struct spi_controller *master,
struct spi_device *spi,
struct spi_transfer *xfer)
{
struct spi_nfc *spi_nfc = spi_controller_get_devdata(master);
u32 page_size = page_info_get_page_size();
unsigned long flags;
enum TRANSFER_STATE xfer_state;
u8 *p = (u8 *)xfer->tx_buf;
u8 filp_addr[16] = {0};
static u8 cache_op_running;
int i;
xfer_state = spi_nfc_get_current_handle_state(xfer);
switch (xfer_state) {
case TRANSFER_STATE_CMD:
if (IS_READ_CACHE_CMD(p[0]) || IS_WRITE_CACHE_CMD(p[0]))
cache_op_running = 1;
if (p[0] == 0x9f)
spi_nfc_select_ecc(spi_nfc);
return NFC_SEND_CMD(p[0]);
case TRANSFER_STATE_DUMMY:
case TRANSFER_STATE_ADDR:
if (cache_op_running && !spi_nfc_disable_host_ecc()) {
p[0] = 0;
p[1] &= ~(1 << (fls(page_size) - 8));
cache_op_running = 0;
}
p += xfer->len;
for (i = 0; i < xfer->len; i++)
filp_addr[i] = *(--p);
return NFC_SEND_ADDR(filp_addr, xfer->len);
case TRANSFER_STATE_DATAOUT:
if (xfer->len <= SPINAND_MAX_ID_LEN + 2 && xfer->tx_nbits == 1) {
nfc_set_data_bus_width(0);
return NFC_SEND_DATA_OUT(p, xfer->len);
}
flags = spi_mem_get_xfer_flag();
return spi_nfc_dma_xfer(spi_nfc, spi, flags, p, xfer->len, false);
case TRANSFER_STATE_DATAIN:
p = (u8 *)xfer->rx_buf;
if (xfer->len <= SPINAND_MAX_ID_LEN + 2 && xfer->rx_nbits == 1) {
nfc_set_data_bus_width(0);
return NFC_SEND_DATA_IN(p, xfer->len);
}
flags = spi_mem_get_xfer_flag();
return spi_nfc_dma_xfer(spi_nfc, spi, flags, p, xfer->len, true);
default:
pr_info("%s %d\n", __func__, __LINE__);
break;
}
return 0;
}
static int spi_nfc_clk_init(struct spi_nfc *spi_nfc,
struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
struct clk_init_data init = {0};
const char *fix_div2_pll_name[1];
u32 nand_clk_ctrl;
int ret;
spi_nfc->clk_gate = devm_clk_get(spi_nfc->dev, "gate");
if (IS_ERR(spi_nfc->clk_gate)) {
dev_err(spi_nfc->dev, "failed to get gate\n");
return PTR_ERR(spi_nfc->clk_gate);
}
ret = clk_prepare_enable(spi_nfc->clk_gate);
if (ret) {
dev_err(spi_nfc->dev, "failed to enable gate\n");
return ret;
}
spi_nfc->fix_div2_pll = devm_clk_get(spi_nfc->dev, "fdiv2pll");
if (IS_ERR(spi_nfc->fix_div2_pll)) {
dev_err(spi_nfc->dev, "failed to get fix pll\n");
return PTR_ERR(spi_nfc->fix_div2_pll);
}
ret = clk_prepare_enable(spi_nfc->fix_div2_pll);
if (ret) {
dev_err(spi_nfc->dev, "failed to enable fix pll\n");
return ret;
}
ret = of_property_read_u32(np, "nand_clk_ctrl", &nand_clk_ctrl);
if (ret) {
pr_info("%s %d,please config para item nand_clk_ctrl in dts\n",
__func__, __LINE__);
return ret;
}
spi_nfc->nand_clk_reg = devm_ioremap(&pdev->dev,
nand_clk_ctrl,
sizeof(int));
writel(CLK_SELECT_NAND | readl(spi_nfc->nand_clk_reg), spi_nfc->nand_clk_reg);
init.name = devm_kstrdup(spi_nfc->dev, "nfc#div", GFP_KERNEL);
init.ops = &clk_divider_ops;
fix_div2_pll_name[0] = __clk_get_name(spi_nfc->fix_div2_pll);
init.parent_names = fix_div2_pll_name;
init.num_parents = 1;
spi_nfc->divider.reg = spi_nfc->nand_clk_reg;
spi_nfc->divider.shift = CLK_DIV_SHIFT;
spi_nfc->divider.width = CLK_DIV_WIDTH;
spi_nfc->divider.hw.init = &init;
spi_nfc->divider.flags = CLK_DIVIDER_ONE_BASED |
CLK_DIVIDER_ROUND_CLOSEST | CLK_DIVIDER_ALLOW_ZERO;
spi_nfc->div_clk = devm_clk_register(spi_nfc->dev,
&spi_nfc->divider.hw);
if (IS_ERR(spi_nfc->div_clk))
return PTR_ERR(spi_nfc->div_clk);
ret = clk_prepare_enable(spi_nfc->div_clk);
if (ret)
dev_err(spi_nfc->dev, "pre enable spi nfc divider fail\n");
return ret;
}
static void spi_nfc_disable_clk(struct spi_nfc *spi_nfc)
{
clk_disable_unprepare(spi_nfc->div_clk);
clk_disable_unprepare(spi_nfc->fix_div2_pll);
clk_disable_unprepare(spi_nfc->clk_gate);
}
static int spi_nfc_prepare(struct spi_nfc *spi_nfc,
struct platform_device *pdev)
{
unsigned long clk_rate;
int ret;
#if IS_ENABLED(CONFIG_AMLOGIC_MTD_COMMON)
u8 *boot_info;
#endif
ret = spi_nfc_clk_init(spi_nfc, pdev);
if (ret)
return ret;
ret = spi_nfc_buffer_init(spi_nfc);
if (ret)
return ret;
#if IS_ENABLED(CONFIG_AMLOGIC_MTD_COMMON)
boot_info = devm_kzalloc(spi_nfc->dev,
MAX_BYTES_IN_BOOTINFO,
GFP_KERNEL);
if (!boot_info)
return -ENOMEM;
page_info_pre_init(boot_info, PAGE_INFO_V3);
page_info_initialize(DEFAULT_ECC_MODE, 0, 0);
#endif
nfc_set_clock_and_timing(&clk_rate);
ret = clk_set_rate(spi_nfc->div_clk, clk_rate);
if (ret) {
dev_err(spi_nfc->dev, "failed to set device clock\n");
spi_nfc_disable_clk(spi_nfc);
}
return ret;
}
static int spi_nfc_probe(struct platform_device *pdev)
{
struct spi_controller *master;
void __iomem *base;
int ret = 0, i;
master = spi_alloc_master(&pdev->dev, sizeof(struct spi_nfc));
if (!master)
return -ENOMEM;
platform_set_drvdata(pdev, master);
spi_nfc = spi_controller_get_devdata(master);
spi_nfc->dev = &pdev->dev;
for (i = 0; i < 3; i++) {
base = devm_platform_ioremap_resource(pdev, i);
if (IS_ERR(base)) {
ret = PTR_ERR(base);
goto out_err;
}
spi_nfc->regmap[i] = devm_regmap_init_mmio(spi_nfc->dev, base,
&spi_nfc_regmap_config);
if (IS_ERR(spi_nfc->regmap[i])) {
ret = PTR_ERR(spi_nfc->regmap[i]);
goto out_err;
}
nfc_regmap[i] = spi_nfc->regmap[i];
}
ret = spi_nfc_prepare(spi_nfc, pdev);
if (ret)
goto out_err;
master->num_chipselect = 1;
master->dev.of_node = pdev->dev.of_node;
master->mode_bits = SPI_TX_DUAL | SPI_TX_QUAD | SPI_RX_DUAL | SPI_RX_QUAD |
SPI_TX_OCTAL | SPI_RX_OCTAL;
master->bits_per_word_mask = SPI_BPW_MASK(8);
master->auto_runtime_pm = true;
master->transfer_one = spi_nfc_transfer_one;
master->min_speed_hz = 1000000;
master->max_speed_hz = 200000000;
pm_runtime_set_autosuspend_delay(spi_nfc->dev, 500);
pm_runtime_use_autosuspend(spi_nfc->dev);
pm_runtime_enable(spi_nfc->dev);
ret = devm_spi_register_controller(spi_nfc->dev, master);
if (ret) {
dev_err(spi_nfc->dev, "failed to register spi master\n");
goto out_clk;
}
return 0;
out_clk:
spi_nfc_disable_clk(spi_nfc);
pm_runtime_disable(spi_nfc->dev);
out_err:
spi_controller_put(master);
return ret;
}
static void spi_nfc_remove(struct platform_device *pdev)
{
struct spi_nfc *spi_nfc = platform_get_drvdata(pdev);
pm_runtime_get_sync(&pdev->dev);
spi_nfc_disable_clk(spi_nfc);
spi_nfc_buffer_free(spi_nfc);
pm_runtime_disable(&pdev->dev);
}
#ifdef CONFIG_PM_SLEEP
static int spi_nfc_suspend(struct device *dev)
{
struct spi_nfc *spi_nfc = dev_get_drvdata(dev);
int ret = 0;
if (spi_nfc->master) {
ret = spi_controller_suspend(spi_nfc->master);
if (ret)
return ret;
}
if (!pm_runtime_suspended(dev))
clk_disable_unprepare(spi_nfc->clk_gate);
return 0;
}
static int spi_nfc_resume(struct device *dev)
{
struct spi_nfc *spi_nfc = dev_get_drvdata(dev);
int ret = 0;
if (!pm_runtime_suspended(dev)) {
ret = clk_prepare_enable(spi_nfc->clk_gate);
if (ret)
return ret;
}
if (spi_nfc->master) {
ret = spi_controller_resume(spi_nfc->master);
if (ret)
clk_disable_unprepare(spi_nfc->clk_gate);
}
return ret;
}
#endif /* CONFIG_PM_SLEEP */
#ifdef CONFIG_PM
static int spi_nfc_runtime_suspend(struct device *dev)
{
struct spi_nfc *spi_nfc = dev_get_drvdata(dev);
clk_disable_unprepare(spi_nfc->clk_gate);
return 0;
}
static int spi_nfc_runtime_resume(struct device *dev)
{
struct spi_nfc *spi_nfc = dev_get_drvdata(dev);
int ret;
ret = clk_prepare_enable(spi_nfc->clk_gate);
return ret;
}
#endif /* CONFIG_PM */
static const struct dev_pm_ops spi_nfc_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(spi_nfc_suspend, spi_nfc_resume)
SET_RUNTIME_PM_OPS(spi_nfc_runtime_suspend,
spi_nfc_runtime_resume,
NULL)
};
static const struct of_device_id spi_nfc_dt_match[] = {
{ .compatible = "amlogic,spi-nfc", },
{ },
};
MODULE_DEVICE_TABLE(of, spi_nfc_dt_match);
struct platform_driver spi_nfc_driver = {
.probe = spi_nfc_probe,
.remove = spi_nfc_remove,
.driver = {
.name = "spi-nfc",
.of_match_table = of_match_ptr(spi_nfc_dt_match),
//.pm = &spi_nfc_pm_ops,
},
};
#ifndef MODULE
module_platform_driver(spi_nfc_driver);
#endif
MODULE_AUTHOR("Amlogic R&D");
MODULE_DESCRIPTION("Amlogic SPI NFC driver");
MODULE_LICENSE("GPL v2");