Ai
45 Star 128 Fork 110

嵌入式软件部/Phytium-Linux-Kernel

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
spi-phytium-v2.c 12.30 KB
一键复制 编辑 原始数据 按行查看 历史
Peng Min 提交于 2025-03-04 10:35 +08:00 . spi-v2: Phytium: Add phytium spi-v2 driver
// SPDX-License-Identifier: GPL-2.0
/*
* Phytium SPI core controller driver.
*
* Copyright (c) 2023-2024, Phytium Technology Co., Ltd..
*/
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/gpio.h>
#include <linux/highmem.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/spi/spi.h>
#include <linux/scatterlist.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/of_platform.h>
#include <linux/property.h>
#include <linux/acpi.h>
#include <linux/mtd/spi-nor.h>
#include "spi-phytium.h"
static inline void spi_phyt_enable_chip(struct phytium_spi *fts, u8 enable)
{
u8 val = enable ? 1 : 2;
spi_phytium_set_cmd8(fts, PHYTSPI_MSG_CMD_SET_MODULE_EN, val);
}
static inline void spi_phyt_set_clk(struct phytium_spi *fts, u16 div)
{
u32 new_div = div;
spi_phytium_set_cmd32(fts, PHYTSPI_MSG_CMD_SET_BAUDR, new_div);
}
static inline void spi_phyt_dma_reset(struct phytium_spi *fts, u8 enable)
{
spi_phytium_set_cmd8(fts, PHYTSPI_MSG_CMD_SET_DMA_RESET, enable);
}
static inline void spi_phyt_global_cs(struct phytium_spi *fts)
{
u32 global_cs_en;
u16 cs;
global_cs_en = GENMASK(fts->num_cs-1, 0) << fts->num_cs;
cs = (u16)((0x1 << 8) | global_cs_en);
spi_phytium_set_cmd16(fts, PHYTSPI_MSG_CMD_SET_CS, cs);
}
static inline void spi_phyt_reset_chip(struct phytium_spi *fts)
{
spi_phyt_dma_reset(fts, 1);
spi_phyt_enable_chip(fts, 0);
if (fts->global_cs)
spi_phyt_global_cs(fts);
spi_phyt_enable_chip(fts, 1);
}
static inline void spi_phyt_shutdown_chip(struct phytium_spi *fts)
{
spi_phyt_enable_chip(fts, 0);
spi_phyt_set_clk(fts, 0);
}
struct phytium_spi_chip {
u8 poll_mode;
u8 type;
void (*cs_control)(u32 command);
};
struct chip_data {
u8 cs;
u8 tmode;
u8 type;
u8 poll_mode;
u16 clk_div;
u32 speed_hz;
void (*cs_control)(u32 command);
};
static void spi_phyt_set_cs(struct spi_device *spi, bool enable)
{
struct phytium_spi *fts = spi_master_get_devdata(spi->master);
struct chip_data *chip = spi_get_ctldata(spi);
u32 origin;
u16 cs;
if (fts->tx || fts->rx)
return;
if (fts->msg->cmd_id == PHYTSPI_MSG_CMD_DATA &&
fts->msg->cmd_subid == PHYTSPI_MSG_CMD_DATA_TX)
return;
if (chip && chip->cs_control)
chip->cs_control(!enable);
if (!enable) {
cs = BIT(spi->chip_select);
spi_phytium_set_cmd16(fts, PHYTSPI_MSG_CMD_SET_CS, cs);
if (fts->global_cs) {
origin = (GENMASK(fts->num_cs-1, 0) << fts->num_cs)
| (1 << spi->chip_select);
cs = (0x1 << 8) | origin;
spi_phytium_set_cmd16(fts, PHYTSPI_MSG_CMD_SET_CS, cs);
}
} else {
if (fts->global_cs) {
origin = (GENMASK(fts->num_cs-1, 0) << fts->num_cs)
& ~(1 << spi->chip_select);
cs = (0x1 << 8) | origin;
spi_phytium_set_cmd16(fts, PHYTSPI_MSG_CMD_SET_CS, cs);
}
}
}
static irqreturn_t spi_phyt_irq(int irq, void *dev_id)
{
struct spi_master *master = dev_id;
struct phytium_spi *fts = spi_master_get_devdata(master);
complete(&fts->cmd_completion);
writel_relaxed(0, fts->regfile + SPI_REGFILE_RV2AP_INTR_STATE);
writel_relaxed(0x10, fts->regfile + SPI_REGFILE_RV2AP_INT_CLEAN);
return IRQ_HANDLED;
}
static int spi_phyt_transfer_one(struct spi_master *master,
struct spi_device *spi, struct spi_transfer *transfer)
{
struct phytium_spi *fts = spi_master_get_devdata(master);
struct chip_data *chip = spi_get_ctldata(spi);
int ret = 0;
fts->tx = (void *)transfer->tx_buf;
fts->tx_end = fts->tx + transfer->len;
fts->rx = transfer->rx_buf;
fts->rx_end = fts->rx + transfer->len;
fts->len = transfer->len;
if (chip->cs_control) {
if (fts->rx && fts->tx)
chip->tmode = TMOD_TR;
else if (fts->rx)
chip->tmode = TMOD_RO;
else
chip->tmode = TMOD_TO;
}
if (fts->tx) {
if ((*(u8 *)fts->tx == SPINOR_OP_WREN) && fts->spi_write_flag == 0) {
spi_phytium_write_pre(fts, spi->chip_select,
transfer->bits_per_word, spi->mode,
chip->tmode, 3, fts->spi_write_flag);
fts->spi_write_flag++;
return 0;
}
if ((*(u8 *)fts->tx == SPINOR_OP_BE_4K) && (fts->spi_write_flag == 1) &&
fts->flash_read == 0 && fts->flash_erase != 1) {
fts->spi_write_flag++;
fts->flash_erase = 1;
return 0;
}
if ((*(u8 *)fts->tx == SPINOR_OP_CHIP_ERASE) && (fts->spi_write_flag == 1) &&
fts->flash_read == 0 && fts->flash_erase == 0) {
ret = spi_phytium_flash_erase(fts, spi->chip_select,
transfer->bits_per_word,
spi->mode, chip->tmode, 3, SPINOR_OP_CHIP_ERASE);
fts->spi_write_flag = 0;
fts->flash_erase = 2;
}
if ((*(u8 *)fts->tx == SPINOR_OP_READ || *(u8 *)fts->tx == SPINOR_OP_READ_FAST ||
*(u8 *)fts->tx == SPINOR_OP_READ_4B ||
*(u8 *)fts->tx == SPINOR_OP_READ_FAST_4B) &&
fts->spi_write_flag == 0 && fts->flash_read == 0 &&
fts->flash_erase == 0) {
fts->flash_cmd = *(u8 *)fts->tx;
fts->spi_write_flag++;
fts->flash_read = 1;
return 0;
}
if ((fts->spi_write_flag == 1) && fts->flash_read == 0 &&
fts->flash_write == 0 && ((*(u8 *)fts->tx == SPINOR_OP_PP) ||
(*(u8 *)fts->tx == SPINOR_OP_PP_4B))) {
fts->flash_cmd = *(u8 *)fts->tx;
fts->spi_write_flag++;
fts->flash_write = 1;
return 0;
}
if ((*(u8 *)fts->tx == SPINOR_OP_RDSR) && fts->flash_write == 3) {
fts->read_sr = 1;
fts->flash_write = 0;
return 0;
}
if ((*(u8 *)fts->tx == SPINOR_OP_RDSR) && fts->flash_erase == 2) {
fts->read_sr = 1;
return 0;
}
}
if (fts->read_sr) {
*(u8 *)(fts->rx) = 0;
fts->read_sr = 0;
return 0;
}
if (fts->tx) {
if (fts->flash_erase == 1) {
ret = spi_phytium_flash_erase(fts, spi->chip_select,
transfer->bits_per_word,
spi->mode, chip->tmode, 3, SPINOR_OP_BE_4K);
if (ret) {
dev_err(&master->dev, "flash erase failed\n");
return ret;
}
fts->spi_write_flag = 0;
fts->flash_erase++;
} else if (fts->flash_read) {
ret = spi_phytium_flash_erase(fts, spi->chip_select,
transfer->bits_per_word,
spi->mode, chip->tmode, 1, fts->flash_cmd);
if (ret) {
dev_err(&master->dev, "transfer read-command failed\n");
return ret;
}
fts->spi_write_flag = 0;
fts->flash_read = 0;
} else if (fts->flash_write == 1) {
fts->flash_write++;
ret = spi_phytium_flash_write(fts, fts->flash_cmd);
if (ret) {
dev_err(&master->dev, "flash write failed\n");
return ret;
}
} else if (fts->flash_erase == 2 && (*(u8 *)fts->tx == SPINOR_OP_WRDI)) {
ret = spi_phytium_write(fts, spi->chip_select, transfer->bits_per_word,
spi->mode, chip->tmode, 3, fts->spi_write_flag);
if (ret) {
dev_err(&master->dev, "transfer disable-command failed\n");
return ret;
}
fts->flash_erase = 0;
} else {
ret = spi_phytium_write(fts, spi->chip_select, transfer->bits_per_word,
spi->mode, chip->tmode, 1, fts->spi_write_flag);
if (ret) {
dev_err(&master->dev, "write command failed\n");
return ret;
}
if (fts->flash_write == 2)
fts->flash_write++;
fts->spi_write_flag = 0;
}
}
if (fts->rx) {
ret = spi_phytium_read(fts, spi->chip_select, transfer->bits_per_word,
spi->mode, chip->tmode, 2);
if (ret) {
dev_err(&master->dev, "read data failed\n");
return ret;
}
}
return ret;
}
static void spi_phyt_handle_err(struct spi_master *master,
struct spi_message *msg)
{
struct phytium_spi *fts = spi_master_get_devdata(master);
spi_phyt_reset_chip(fts);
}
static int spi_phyt_setup(struct spi_device *spi)
{
struct phytium_spi_chip *chip_info = NULL;
struct chip_data *chip;
struct spi_master *master = spi->master;
struct phytium_spi *fts = spi_master_get_devdata(master);
u8 data_width, scph, scpol, tmode;
u16 mode;
u16 clk_div;
spi_phyt_enable_chip(fts, 0);
clk_div = (fts->max_freq / spi->max_speed_hz + 1) & 0xfffe;
spi_phyt_set_clk(fts, clk_div);
fts->clk_div = clk_div;
chip = spi_get_ctldata(spi);
if (!chip) {
chip = kzalloc(sizeof(struct chip_data), GFP_KERNEL);
if (!chip)
return -ENOMEM;
spi_set_ctldata(spi, chip);
}
chip_info = spi->controller_data;
if (chip_info) {
if (chip_info->cs_control)
chip->cs_control = chip_info->cs_control;
chip->poll_mode = chip_info->poll_mode;
chip->type = chip_info->type;
}
chip->tmode = 0;
data_width = spi->bits_per_word;
spi_phytium_set_cmd8(fts, PHYTSPI_MSG_CMD_SET_DATA_WIDTH, data_width);
scph = spi->mode & (0x1);
scpol = spi->mode >> 1;
mode = (scph << 8) | scpol;
spi_phytium_set_cmd16(fts, PHYTSPI_MSG_CMD_SET_MODE, mode);
tmode = chip->tmode;
spi_phytium_set_cmd8(fts, PHYTSPI_MSG_CMD_SET_TMOD, tmode);
spi_phyt_enable_chip(fts, 1);
return 0;
}
static void spi_phyt_cleanup(struct spi_device *spi)
{
struct chip_data *chip = spi_get_ctldata(spi);
kfree(chip);
spi_set_ctldata(spi, NULL);
}
void spi_phyt_enable_debug(struct phytium_spi *fts)
{
u32 reg;
reg = phytium_read_regfile(fts, SPI_REGFILE_DEBUG);
phytium_write_regfile(fts, SPI_REGFILE_DEBUG,
reg | SPI_REGFILE_DEBUG_VAL);
}
void spi_phyt_disable_debug(struct phytium_spi *fts)
{
u32 reg;
reg = phytium_read_regfile(fts, SPI_REGFILE_DEBUG);
reg &= ~SPI_REGFILE_DEBUG_VAL;
phytium_write_regfile(fts, SPI_REGFILE_DEBUG, reg);
}
void spi_phyt_disable_alive(struct phytium_spi *fts)
{
u32 reg;
reg = phytium_read_regfile(fts, SPI_REGFILE_DEBUG);
reg &= ~SPI_REGFILE_ALIVE_VAL;
phytium_write_regfile(fts, SPI_REGFILE_DEBUG, reg);
}
void spi_watchdog(struct phytium_spi *fts)
{
u32 reg;
reg = phytium_read_regfile(fts, SPI_REGFILE_DEBUG);
phytium_write_regfile(fts, SPI_REGFILE_DEBUG,
reg | SPI_REGFILE_HEARTBIT_VAL);
}
static void spi_phyt_timer_handle(struct timer_list *t)
{
struct phytium_spi *fts = from_timer(fts, t, timer);
if (fts->alive_enabled && fts->watchdog) {
if (fts->runtimes < 20)
fts->watchdog(fts);
fts->runtimes++;
}
mod_timer(&fts->timer, jiffies + msecs_to_jiffies(10));
}
static void spi_phyt_hw_init(struct device *dev, struct phytium_spi *fts)
{
spi_phytium_default(fts);
}
int spi_phyt_add_host(struct device *dev, struct phytium_spi *fts)
{
struct spi_master *master;
int ret;
WARN_ON(fts == NULL);
master = spi_alloc_master(dev, 0);
if (!master)
return -ENOMEM;
fts->master = master;
snprintf(fts->name, sizeof(fts->name), "phytium_spi%d", fts->bus_num);
init_completion(&fts->cmd_completion);
ret = devm_request_irq(dev, fts->irq, spi_phyt_irq, IRQF_SHARED, fts->name, master);
if (ret < 0) {
dev_err(dev, "can not get IRQ\n");
goto err_free_master;
}
master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LOOP;
master->bits_per_word_mask = SPI_BPW_MASK(8) | SPI_BPW_MASK(16);
master->bus_num = fts->bus_num;
master->num_chipselect = fts->num_cs;
master->setup = spi_phyt_setup;
master->cleanup = spi_phyt_cleanup;
master->set_cs = spi_phyt_set_cs;
master->transfer_one = spi_phyt_transfer_one;
master->handle_err = spi_phyt_handle_err;
master->max_speed_hz = fts->max_freq;
master->dev.of_node = dev->of_node;
master->dev.fwnode = dev->fwnode;
master->flags = SPI_MASTER_GPIO_SS;
spi_master_set_devdata(master, fts);
spi_phyt_disable_debug(fts);
spi_phyt_disable_alive(fts);
fts->runtimes = 0;
fts->debug_enabled = false;
fts->alive_enabled = false;
fts->watchdog = spi_watchdog;
fts->timer.expires = jiffies + msecs_to_jiffies(50);
timer_setup(&fts->timer, spi_phyt_timer_handle, 0);
add_timer(&fts->timer);
spi_phyt_hw_init(dev, fts);
ret = devm_spi_register_master(dev, master);
if (ret) {
dev_err(&master->dev, "problem registering spi master\n");
goto err_exit;
}
return 0;
err_exit:
spi_phyt_enable_chip(fts, 0);
err_free_master:
spi_master_put(master);
return ret;
}
EXPORT_SYMBOL_GPL(spi_phyt_add_host);
void spi_phyt_remove_host(struct phytium_spi *fts)
{
del_timer(&fts->timer);
spi_phyt_shutdown_chip(fts);
}
EXPORT_SYMBOL_GPL(spi_phyt_remove_host);
int spi_phyt_suspend_host(struct phytium_spi *fts)
{
int ret;
ret = spi_controller_suspend(fts->master);
if (ret)
return ret;
spi_phyt_shutdown_chip(fts);
return 0;
}
EXPORT_SYMBOL_GPL(spi_phyt_suspend_host);
int spi_phyt_resume_host(struct phytium_spi *fts)
{
int ret;
spi_phyt_hw_init(&fts->master->dev, fts);
spi_phyt_enable_chip(fts, 0);
spi_phyt_set_clk(fts, fts->clk_div);
spi_phyt_enable_chip(fts, 1);
ret = spi_controller_resume(fts->master);
if (ret)
dev_err(&fts->master->dev, "fail to start queue (%d)\n", ret);
return ret;
}
EXPORT_SYMBOL_GPL(spi_phyt_resume_host);
MODULE_AUTHOR("Peng Min <pengmin1540@phytium.com.cn>");
MODULE_DESCRIPTION("Driver for Phytium SPI controller core");
MODULE_LICENSE("GPL");
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/phytium_embedded/phytium-linux-kernel.git
git@gitee.com:phytium_embedded/phytium-linux-kernel.git
phytium_embedded
phytium-linux-kernel
Phytium-Linux-Kernel
linux-5.10

搜索帮助