// SPDX-License-Identifier: (GPL-2.0+ OR MIT) /* * Copyright (c) 2019 Amlogic, Inc. All rights reserved. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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");