mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/
synced 2025-04-19 20:58:31 +09:00
spi-nand/spi-mem DTR support
Merge series from Miquel Raynal <miquel.raynal@bootlin.com>: Here is a (big) series supposed to bring DTR support in SPI-NAND.
This commit is contained in:
commit
89b37e4992
@ -1214,6 +1214,8 @@ spinand_select_op_variant(struct spinand_device *spinand,
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
spi_mem_adjust_op_freq(spinand->spimem, &op);
|
||||
|
||||
if (!spi_mem_supports_op(spinand->spimem, &op))
|
||||
break;
|
||||
|
||||
|
@ -298,19 +298,16 @@ static const struct amd_spi_freq amd_spi_freq[] = {
|
||||
{ AMD_SPI_MIN_HZ, F_800KHz, 0},
|
||||
};
|
||||
|
||||
static int amd_set_spi_freq(struct amd_spi *amd_spi, u32 speed_hz)
|
||||
static void amd_set_spi_freq(struct amd_spi *amd_spi, u32 speed_hz)
|
||||
{
|
||||
unsigned int i, spd7_val, alt_spd;
|
||||
|
||||
if (speed_hz < AMD_SPI_MIN_HZ)
|
||||
return -EINVAL;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(amd_spi_freq); i++)
|
||||
if (speed_hz >= amd_spi_freq[i].speed_hz)
|
||||
break;
|
||||
|
||||
if (amd_spi->speed_hz == amd_spi_freq[i].speed_hz)
|
||||
return 0;
|
||||
return;
|
||||
|
||||
amd_spi->speed_hz = amd_spi_freq[i].speed_hz;
|
||||
|
||||
@ -329,8 +326,6 @@ static int amd_set_spi_freq(struct amd_spi *amd_spi, u32 speed_hz)
|
||||
amd_spi_setclear_reg32(amd_spi, AMD_SPI_SPEED_REG, spd7_val,
|
||||
AMD_SPI_SPD7_MASK);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int amd_spi_fifo_xfer(struct amd_spi *amd_spi,
|
||||
@ -479,6 +474,9 @@ static bool amd_spi_supports_op(struct spi_mem *mem,
|
||||
return false;
|
||||
}
|
||||
|
||||
if (op->max_freq < mem->spi->controller->min_speed_hz)
|
||||
return false;
|
||||
|
||||
return spi_mem_default_supports_op(mem, op);
|
||||
}
|
||||
|
||||
@ -676,9 +674,7 @@ static int amd_spi_exec_mem_op(struct spi_mem *mem,
|
||||
|
||||
amd_spi = spi_controller_get_devdata(mem->spi->controller);
|
||||
|
||||
ret = amd_set_spi_freq(amd_spi, mem->spi->max_speed_hz);
|
||||
if (ret)
|
||||
return ret;
|
||||
amd_set_spi_freq(amd_spi, op->max_freq);
|
||||
|
||||
if (amd_spi->version == AMD_SPI_V2)
|
||||
amd_set_spi_addr_mode(amd_spi, op);
|
||||
@ -705,6 +701,10 @@ static const struct spi_controller_mem_ops amd_spi_mem_ops = {
|
||||
.supports_op = amd_spi_supports_op,
|
||||
};
|
||||
|
||||
static const struct spi_controller_mem_caps amd_spi_mem_caps = {
|
||||
.per_op_freq = true,
|
||||
};
|
||||
|
||||
static int amd_spi_host_transfer(struct spi_controller *host,
|
||||
struct spi_message *msg)
|
||||
{
|
||||
@ -782,6 +782,7 @@ static int amd_spi_probe(struct platform_device *pdev)
|
||||
host->setup = amd_spi_host_setup;
|
||||
host->transfer_one_message = amd_spi_host_transfer;
|
||||
host->mem_ops = &amd_spi_mem_ops;
|
||||
host->mem_caps = &amd_spi_mem_caps;
|
||||
host->max_transfer_size = amd_spi_max_transfer_size;
|
||||
host->max_message_size = amd_spi_max_transfer_size;
|
||||
|
||||
|
@ -259,7 +259,7 @@ static int amlogic_spifc_a1_exec_op(struct spi_mem *mem,
|
||||
size_t data_size = op->data.nbytes;
|
||||
int ret;
|
||||
|
||||
ret = amlogic_spifc_a1_set_freq(spifc, mem->spi->max_speed_hz);
|
||||
ret = amlogic_spifc_a1_set_freq(spifc, op->max_freq);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@ -320,6 +320,10 @@ static const struct spi_controller_mem_ops amlogic_spifc_a1_mem_ops = {
|
||||
.adjust_op_size = amlogic_spifc_a1_adjust_op_size,
|
||||
};
|
||||
|
||||
static const struct spi_controller_mem_caps amlogic_spifc_a1_mem_caps = {
|
||||
.per_op_freq = true,
|
||||
};
|
||||
|
||||
static int amlogic_spifc_a1_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct spi_controller *ctrl;
|
||||
@ -356,6 +360,7 @@ static int amlogic_spifc_a1_probe(struct platform_device *pdev)
|
||||
ctrl->bits_per_word_mask = SPI_BPW_MASK(8);
|
||||
ctrl->auto_runtime_pm = true;
|
||||
ctrl->mem_ops = &amlogic_spifc_a1_mem_ops;
|
||||
ctrl->mem_caps = &amlogic_spifc_a1_mem_caps;
|
||||
ctrl->min_speed_hz = SPIFC_A1_MIN_HZ;
|
||||
ctrl->max_speed_hz = SPIFC_A1_MAX_HZ;
|
||||
ctrl->mode_bits = (SPI_RX_DUAL | SPI_TX_DUAL |
|
||||
|
@ -1433,7 +1433,7 @@ static int cqspi_mem_process(struct spi_mem *mem, const struct spi_mem_op *op)
|
||||
struct cqspi_flash_pdata *f_pdata;
|
||||
|
||||
f_pdata = &cqspi->f_pdata[spi_get_chipselect(mem->spi, 0)];
|
||||
cqspi_configure(f_pdata, mem->spi->max_speed_hz);
|
||||
cqspi_configure(f_pdata, op->max_freq);
|
||||
|
||||
if (op->data.dir == SPI_MEM_DATA_IN && op->data.buf.in) {
|
||||
/*
|
||||
@ -1682,6 +1682,7 @@ static const struct spi_controller_mem_ops cqspi_mem_ops = {
|
||||
|
||||
static const struct spi_controller_mem_caps cqspi_mem_caps = {
|
||||
.dtr = true,
|
||||
.per_op_freq = true,
|
||||
};
|
||||
|
||||
static int cqspi_setup_flash(struct cqspi_st *cqspi)
|
||||
|
@ -677,7 +677,7 @@ static int dw_spi_exec_mem_op(struct spi_mem *mem, const struct spi_mem_op *op)
|
||||
* operation. Transmit-only mode is suitable for the rest of them.
|
||||
*/
|
||||
cfg.dfs = 8;
|
||||
cfg.freq = clamp(mem->spi->max_speed_hz, 0U, dws->max_mem_freq);
|
||||
cfg.freq = clamp(op->max_freq, 0U, dws->max_mem_freq);
|
||||
if (op->data.dir == SPI_MEM_DATA_IN) {
|
||||
cfg.tmode = DW_SPI_CTRLR0_TMOD_EPROMREAD;
|
||||
cfg.ndf = op->data.nbytes;
|
||||
@ -894,6 +894,10 @@ static void dw_spi_hw_init(struct device *dev, struct dw_spi *dws)
|
||||
dw_writel(dws, DW_SPI_CS_OVERRIDE, 0xF);
|
||||
}
|
||||
|
||||
static const struct spi_controller_mem_caps dw_spi_mem_caps = {
|
||||
.per_op_freq = true,
|
||||
};
|
||||
|
||||
int dw_spi_add_host(struct device *dev, struct dw_spi *dws)
|
||||
{
|
||||
struct spi_controller *host;
|
||||
@ -941,8 +945,10 @@ int dw_spi_add_host(struct device *dev, struct dw_spi *dws)
|
||||
host->set_cs = dw_spi_set_cs;
|
||||
host->transfer_one = dw_spi_transfer_one;
|
||||
host->handle_err = dw_spi_handle_err;
|
||||
if (dws->mem_ops.exec_op)
|
||||
if (dws->mem_ops.exec_op) {
|
||||
host->mem_ops = &dws->mem_ops;
|
||||
host->mem_caps = &dw_spi_mem_caps;
|
||||
}
|
||||
host->max_speed_hz = dws->max_freq;
|
||||
host->flags = SPI_CONTROLLER_GPIO_SS;
|
||||
host->auto_runtime_pm = true;
|
||||
|
@ -522,9 +522,10 @@ static void fsl_qspi_invalidate(struct fsl_qspi *q)
|
||||
qspi_writel(q, reg, q->iobase + QUADSPI_MCR);
|
||||
}
|
||||
|
||||
static void fsl_qspi_select_mem(struct fsl_qspi *q, struct spi_device *spi)
|
||||
static void fsl_qspi_select_mem(struct fsl_qspi *q, struct spi_device *spi,
|
||||
const struct spi_mem_op *op)
|
||||
{
|
||||
unsigned long rate = spi->max_speed_hz;
|
||||
unsigned long rate = op->max_freq;
|
||||
int ret;
|
||||
|
||||
if (q->selected == spi_get_chipselect(spi, 0))
|
||||
@ -652,7 +653,7 @@ static int fsl_qspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
|
||||
fsl_qspi_readl_poll_tout(q, base + QUADSPI_SR, (QUADSPI_SR_IP_ACC_MASK |
|
||||
QUADSPI_SR_AHB_ACC_MASK), 10, 1000);
|
||||
|
||||
fsl_qspi_select_mem(q, mem->spi);
|
||||
fsl_qspi_select_mem(q, mem->spi, op);
|
||||
|
||||
if (needs_amba_base_offset(q))
|
||||
addr_offset = q->memmap_phy;
|
||||
@ -839,6 +840,10 @@ static const struct spi_controller_mem_ops fsl_qspi_mem_ops = {
|
||||
.get_name = fsl_qspi_get_name,
|
||||
};
|
||||
|
||||
static const struct spi_controller_mem_caps fsl_qspi_mem_caps = {
|
||||
.per_op_freq = true,
|
||||
};
|
||||
|
||||
static int fsl_qspi_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct spi_controller *ctlr;
|
||||
@ -923,6 +928,7 @@ static int fsl_qspi_probe(struct platform_device *pdev)
|
||||
ctlr->bus_num = -1;
|
||||
ctlr->num_chipselect = 4;
|
||||
ctlr->mem_ops = &fsl_qspi_mem_ops;
|
||||
ctlr->mem_caps = &fsl_qspi_mem_caps;
|
||||
|
||||
fsl_qspi_default_setup(q);
|
||||
|
||||
|
@ -187,6 +187,16 @@ bool spi_mem_default_supports_op(struct spi_mem *mem,
|
||||
return false;
|
||||
}
|
||||
|
||||
if (op->max_freq && mem->spi->controller->min_speed_hz &&
|
||||
op->max_freq < mem->spi->controller->min_speed_hz)
|
||||
return false;
|
||||
|
||||
if (op->max_freq &&
|
||||
op->max_freq < mem->spi->max_speed_hz) {
|
||||
if (!spi_mem_controller_is_capable(ctlr, per_op_freq))
|
||||
return false;
|
||||
}
|
||||
|
||||
return spi_mem_check_buswidth(mem, op);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(spi_mem_default_supports_op);
|
||||
@ -364,6 +374,9 @@ int spi_mem_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
|
||||
u8 *tmpbuf;
|
||||
int ret;
|
||||
|
||||
/* Make sure the operation frequency is correct before going futher */
|
||||
spi_mem_adjust_op_freq(mem, (struct spi_mem_op *)op);
|
||||
|
||||
ret = spi_mem_check_op(op);
|
||||
if (ret)
|
||||
return ret;
|
||||
@ -410,6 +423,7 @@ int spi_mem_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
|
||||
xfers[xferpos].tx_buf = tmpbuf;
|
||||
xfers[xferpos].len = op->cmd.nbytes;
|
||||
xfers[xferpos].tx_nbits = op->cmd.buswidth;
|
||||
xfers[xferpos].speed_hz = op->max_freq;
|
||||
spi_message_add_tail(&xfers[xferpos], &msg);
|
||||
xferpos++;
|
||||
totalxferlen++;
|
||||
@ -424,6 +438,7 @@ int spi_mem_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
|
||||
xfers[xferpos].tx_buf = tmpbuf + 1;
|
||||
xfers[xferpos].len = op->addr.nbytes;
|
||||
xfers[xferpos].tx_nbits = op->addr.buswidth;
|
||||
xfers[xferpos].speed_hz = op->max_freq;
|
||||
spi_message_add_tail(&xfers[xferpos], &msg);
|
||||
xferpos++;
|
||||
totalxferlen += op->addr.nbytes;
|
||||
@ -435,6 +450,7 @@ int spi_mem_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
|
||||
xfers[xferpos].len = op->dummy.nbytes;
|
||||
xfers[xferpos].tx_nbits = op->dummy.buswidth;
|
||||
xfers[xferpos].dummy_data = 1;
|
||||
xfers[xferpos].speed_hz = op->max_freq;
|
||||
spi_message_add_tail(&xfers[xferpos], &msg);
|
||||
xferpos++;
|
||||
totalxferlen += op->dummy.nbytes;
|
||||
@ -450,6 +466,7 @@ int spi_mem_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
|
||||
}
|
||||
|
||||
xfers[xferpos].len = op->data.nbytes;
|
||||
xfers[xferpos].speed_hz = op->max_freq;
|
||||
spi_message_add_tail(&xfers[xferpos], &msg);
|
||||
xferpos++;
|
||||
totalxferlen += op->data.nbytes;
|
||||
@ -528,6 +545,23 @@ int spi_mem_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(spi_mem_adjust_op_size);
|
||||
|
||||
/**
|
||||
* spi_mem_adjust_op_freq() - Adjust the frequency of a SPI mem operation to
|
||||
* match controller, PCB and chip limitations
|
||||
* @mem: the SPI memory
|
||||
* @op: the operation to adjust
|
||||
*
|
||||
* Some chips have per-op frequency limitations and must adapt the maximum
|
||||
* speed. This function allows SPI mem drivers to set @op->max_freq to the
|
||||
* maximum supported value.
|
||||
*/
|
||||
void spi_mem_adjust_op_freq(struct spi_mem *mem, struct spi_mem_op *op)
|
||||
{
|
||||
if (!op->max_freq || op->max_freq > mem->spi->max_speed_hz)
|
||||
op->max_freq = mem->spi->max_speed_hz;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(spi_mem_adjust_op_freq);
|
||||
|
||||
static ssize_t spi_mem_no_dirmap_read(struct spi_mem_dirmap_desc *desc,
|
||||
u64 offs, size_t len, void *buf)
|
||||
{
|
||||
|
@ -265,7 +265,8 @@ static irqreturn_t mchp_coreqspi_isr(int irq, void *dev_id)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mchp_coreqspi_setup_clock(struct mchp_coreqspi *qspi, struct spi_device *spi)
|
||||
static int mchp_coreqspi_setup_clock(struct mchp_coreqspi *qspi, struct spi_device *spi,
|
||||
const struct spi_mem_op *op)
|
||||
{
|
||||
unsigned long clk_hz;
|
||||
u32 control, baud_rate_val = 0;
|
||||
@ -274,11 +275,11 @@ static int mchp_coreqspi_setup_clock(struct mchp_coreqspi *qspi, struct spi_devi
|
||||
if (!clk_hz)
|
||||
return -EINVAL;
|
||||
|
||||
baud_rate_val = DIV_ROUND_UP(clk_hz, 2 * spi->max_speed_hz);
|
||||
baud_rate_val = DIV_ROUND_UP(clk_hz, 2 * op->max_freq);
|
||||
if (baud_rate_val > MAX_DIVIDER || baud_rate_val < MIN_DIVIDER) {
|
||||
dev_err(&spi->dev,
|
||||
"could not configure the clock for spi clock %d Hz & system clock %ld Hz\n",
|
||||
spi->max_speed_hz, clk_hz);
|
||||
op->max_freq, clk_hz);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@ -399,7 +400,7 @@ static int mchp_coreqspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *o
|
||||
if (err)
|
||||
goto error;
|
||||
|
||||
err = mchp_coreqspi_setup_clock(qspi, mem->spi);
|
||||
err = mchp_coreqspi_setup_clock(qspi, mem->spi, op);
|
||||
if (err)
|
||||
goto error;
|
||||
|
||||
@ -457,6 +458,10 @@ error:
|
||||
|
||||
static bool mchp_coreqspi_supports_op(struct spi_mem *mem, const struct spi_mem_op *op)
|
||||
{
|
||||
struct mchp_coreqspi *qspi = spi_controller_get_devdata(mem->spi->controller);
|
||||
unsigned long clk_hz;
|
||||
u32 baud_rate_val;
|
||||
|
||||
if (!spi_mem_default_supports_op(mem, op))
|
||||
return false;
|
||||
|
||||
@ -479,6 +484,14 @@ static bool mchp_coreqspi_supports_op(struct spi_mem *mem, const struct spi_mem_
|
||||
return false;
|
||||
}
|
||||
|
||||
clk_hz = clk_get_rate(qspi->clk);
|
||||
if (!clk_hz)
|
||||
return false;
|
||||
|
||||
baud_rate_val = DIV_ROUND_UP(clk_hz, 2 * op->max_freq);
|
||||
if (baud_rate_val > MAX_DIVIDER || baud_rate_val < MIN_DIVIDER)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -498,6 +511,10 @@ static const struct spi_controller_mem_ops mchp_coreqspi_mem_ops = {
|
||||
.exec_op = mchp_coreqspi_exec_op,
|
||||
};
|
||||
|
||||
static const struct spi_controller_mem_caps mchp_coreqspi_mem_caps = {
|
||||
.per_op_freq = true,
|
||||
};
|
||||
|
||||
static int mchp_coreqspi_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct spi_controller *ctlr;
|
||||
@ -540,6 +557,7 @@ static int mchp_coreqspi_probe(struct platform_device *pdev)
|
||||
|
||||
ctlr->bits_per_word_mask = SPI_BPW_MASK(8);
|
||||
ctlr->mem_ops = &mchp_coreqspi_mem_ops;
|
||||
ctlr->mem_caps = &mchp_coreqspi_mem_caps;
|
||||
ctlr->setup = mchp_coreqspi_setup_op;
|
||||
ctlr->mode_bits = SPI_CPOL | SPI_CPHA | SPI_RX_DUAL | SPI_RX_QUAD |
|
||||
SPI_TX_DUAL | SPI_TX_QUAD;
|
||||
|
@ -961,7 +961,7 @@ static int mtk_spi_mem_exec_op(struct spi_mem *mem,
|
||||
|
||||
mtk_spi_reset(mdata);
|
||||
mtk_spi_hw_init(mem->spi->controller, mem->spi);
|
||||
mtk_spi_prepare_transfer(mem->spi->controller, mem->spi->max_speed_hz);
|
||||
mtk_spi_prepare_transfer(mem->spi->controller, op->max_freq);
|
||||
|
||||
reg_val = readl(mdata->base + SPI_CFG3_IPM_REG);
|
||||
/* opcode byte len */
|
||||
@ -1122,6 +1122,10 @@ static const struct spi_controller_mem_ops mtk_spi_mem_ops = {
|
||||
.exec_op = mtk_spi_mem_exec_op,
|
||||
};
|
||||
|
||||
static const struct spi_controller_mem_caps mtk_spi_mem_caps = {
|
||||
.per_op_freq = true,
|
||||
};
|
||||
|
||||
static int mtk_spi_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
@ -1160,6 +1164,7 @@ static int mtk_spi_probe(struct platform_device *pdev)
|
||||
if (mdata->dev_comp->ipm_design) {
|
||||
mdata->dev = dev;
|
||||
host->mem_ops = &mtk_spi_mem_ops;
|
||||
host->mem_caps = &mtk_spi_mem_caps;
|
||||
init_completion(&mdata->spimem_done);
|
||||
}
|
||||
|
||||
|
@ -522,7 +522,7 @@ static int mxic_spi_mem_exec_op(struct spi_mem *mem,
|
||||
int i, ret;
|
||||
u8 addr[8], cmd[2];
|
||||
|
||||
ret = mxic_spi_set_freq(mxic, mem->spi->max_speed_hz);
|
||||
ret = mxic_spi_set_freq(mxic, op->max_freq);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@ -582,6 +582,7 @@ static const struct spi_controller_mem_caps mxic_spi_mem_caps = {
|
||||
.dtr = true,
|
||||
.ecc = true,
|
||||
.swap16 = true,
|
||||
.per_op_freq = true,
|
||||
};
|
||||
|
||||
static void mxic_spi_set_cs(struct spi_device *spi, bool lvl)
|
||||
|
@ -705,9 +705,10 @@ static void nxp_fspi_dll_calibration(struct nxp_fspi *f)
|
||||
* Value for rest of the CS FLSHxxCR0 register would be zero.
|
||||
*
|
||||
*/
|
||||
static void nxp_fspi_select_mem(struct nxp_fspi *f, struct spi_device *spi)
|
||||
static void nxp_fspi_select_mem(struct nxp_fspi *f, struct spi_device *spi,
|
||||
const struct spi_mem_op *op)
|
||||
{
|
||||
unsigned long rate = spi->max_speed_hz;
|
||||
unsigned long rate = op->max_freq;
|
||||
int ret;
|
||||
uint64_t size_kb;
|
||||
|
||||
@ -931,7 +932,7 @@ static int nxp_fspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
|
||||
FSPI_STS0_ARB_IDLE, 1, POLL_TOUT, true);
|
||||
WARN_ON(err);
|
||||
|
||||
nxp_fspi_select_mem(f, mem->spi);
|
||||
nxp_fspi_select_mem(f, mem->spi, op);
|
||||
|
||||
nxp_fspi_prepare_lut(f, op);
|
||||
/*
|
||||
@ -1149,6 +1150,10 @@ static const struct spi_controller_mem_ops nxp_fspi_mem_ops = {
|
||||
.get_name = nxp_fspi_get_name,
|
||||
};
|
||||
|
||||
static const struct spi_controller_mem_caps nxp_fspi_mem_caps = {
|
||||
.per_op_freq = true,
|
||||
};
|
||||
|
||||
static int nxp_fspi_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct spi_controller *ctlr;
|
||||
@ -1246,6 +1251,7 @@ static int nxp_fspi_probe(struct platform_device *pdev)
|
||||
ctlr->bus_num = -1;
|
||||
ctlr->num_chipselect = NXP_FSPI_MAX_CHIPSELECT;
|
||||
ctlr->mem_ops = &nxp_fspi_mem_ops;
|
||||
ctlr->mem_caps = &nxp_fspi_mem_caps;
|
||||
|
||||
nxp_fspi_default_setup(f);
|
||||
|
||||
|
@ -534,12 +534,12 @@ static int rockchip_sfc_exec_mem_op(struct spi_mem *mem, const struct spi_mem_op
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (unlikely(mem->spi->max_speed_hz != sfc->speed[cs]) &&
|
||||
if (unlikely(op->max_freq != sfc->speed[cs]) &&
|
||||
!has_acpi_companion(sfc->dev)) {
|
||||
ret = rockchip_sfc_clk_set_rate(sfc, mem->spi->max_speed_hz);
|
||||
ret = rockchip_sfc_clk_set_rate(sfc, op->max_freq);
|
||||
if (ret)
|
||||
goto out;
|
||||
sfc->speed[cs] = mem->spi->max_speed_hz;
|
||||
sfc->speed[cs] = op->max_freq;
|
||||
dev_dbg(sfc->dev, "set_freq=%dHz real_freq=%ldHz\n",
|
||||
sfc->speed[cs], rockchip_sfc_clk_get_rate(sfc));
|
||||
}
|
||||
@ -585,6 +585,10 @@ static const struct spi_controller_mem_ops rockchip_sfc_mem_ops = {
|
||||
.adjust_op_size = rockchip_sfc_adjust_op_size,
|
||||
};
|
||||
|
||||
static const struct spi_controller_mem_caps rockchip_sfc_mem_caps = {
|
||||
.per_op_freq = true,
|
||||
};
|
||||
|
||||
static irqreturn_t rockchip_sfc_irq_handler(int irq, void *dev_id)
|
||||
{
|
||||
struct rockchip_sfc *sfc = dev_id;
|
||||
@ -618,6 +622,7 @@ static int rockchip_sfc_probe(struct platform_device *pdev)
|
||||
|
||||
host->flags = SPI_CONTROLLER_HALF_DUPLEX;
|
||||
host->mem_ops = &rockchip_sfc_mem_ops;
|
||||
host->mem_caps = &rockchip_sfc_mem_caps;
|
||||
host->dev.of_node = pdev->dev.of_node;
|
||||
host->mode_bits = SPI_TX_QUAD | SPI_TX_DUAL | SPI_RX_QUAD | SPI_RX_DUAL;
|
||||
host->max_speed_hz = SFC_MAX_SPEED;
|
||||
|
@ -335,7 +335,6 @@ static void f_ospi_config_indir_protocol(struct f_ospi *ospi,
|
||||
static int f_ospi_indir_prepare_op(struct f_ospi *ospi, struct spi_mem *mem,
|
||||
const struct spi_mem_op *op)
|
||||
{
|
||||
struct spi_device *spi = mem->spi;
|
||||
u32 irq_stat_en;
|
||||
int ret;
|
||||
|
||||
@ -343,7 +342,7 @@ static int f_ospi_indir_prepare_op(struct f_ospi *ospi, struct spi_mem *mem,
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
f_ospi_config_clk(ospi, spi->max_speed_hz);
|
||||
f_ospi_config_clk(ospi, op->max_freq);
|
||||
|
||||
f_ospi_config_indir_protocol(ospi, mem, op);
|
||||
|
||||
@ -577,6 +576,10 @@ static const struct spi_controller_mem_ops f_ospi_mem_ops = {
|
||||
.exec_op = f_ospi_exec_op,
|
||||
};
|
||||
|
||||
static const struct spi_controller_mem_caps f_ospi_mem_caps = {
|
||||
.per_op_freq = true,
|
||||
};
|
||||
|
||||
static int f_ospi_init(struct f_ospi *ospi)
|
||||
{
|
||||
int ret;
|
||||
@ -614,6 +617,7 @@ static int f_ospi_probe(struct platform_device *pdev)
|
||||
| SPI_RX_DUAL | SPI_RX_QUAD | SPI_RX_OCTAL
|
||||
| SPI_MODE_0 | SPI_MODE_1 | SPI_LSB_FIRST;
|
||||
ctlr->mem_ops = &f_ospi_mem_ops;
|
||||
ctlr->mem_caps = &f_ospi_mem_caps;
|
||||
ctlr->bus_num = -1;
|
||||
of_property_read_u32(dev->of_node, "num-cs", &num_cs);
|
||||
if (num_cs > OSPI_NUM_CS) {
|
||||
|
@ -623,7 +623,7 @@ static int ti_qspi_exec_mem_op(struct spi_mem *mem,
|
||||
mutex_lock(&qspi->list_lock);
|
||||
|
||||
if (!qspi->mmap_enabled || qspi->current_cs != spi_get_chipselect(mem->spi, 0)) {
|
||||
ti_qspi_setup_clk(qspi, mem->spi->max_speed_hz);
|
||||
ti_qspi_setup_clk(qspi, op->max_freq);
|
||||
ti_qspi_enable_memory_map(mem->spi);
|
||||
}
|
||||
ti_qspi_setup_mmap_read(mem->spi, op->cmd.opcode, op->data.buswidth,
|
||||
@ -658,6 +658,10 @@ static const struct spi_controller_mem_ops ti_qspi_mem_ops = {
|
||||
.adjust_op_size = ti_qspi_adjust_op_size,
|
||||
};
|
||||
|
||||
static const struct spi_controller_mem_caps ti_qspi_mem_caps = {
|
||||
.per_op_freq = true,
|
||||
};
|
||||
|
||||
static int ti_qspi_start_transfer_one(struct spi_controller *host,
|
||||
struct spi_message *m)
|
||||
{
|
||||
@ -777,6 +781,7 @@ static int ti_qspi_probe(struct platform_device *pdev)
|
||||
host->bits_per_word_mask = SPI_BPW_MASK(32) | SPI_BPW_MASK(16) |
|
||||
SPI_BPW_MASK(8);
|
||||
host->mem_ops = &ti_qspi_mem_ops;
|
||||
host->mem_caps = &ti_qspi_mem_caps;
|
||||
|
||||
if (!of_property_read_u32(np, "num-cs", &num_cs))
|
||||
host->num_chipselect = num_cs;
|
||||
|
@ -318,6 +318,7 @@ static void zynq_qspi_chipselect(struct spi_device *spi, bool assert)
|
||||
* zynq_qspi_config_op - Configure QSPI controller for specified transfer
|
||||
* @xqspi: Pointer to the zynq_qspi structure
|
||||
* @spi: Pointer to the spi_device structure
|
||||
* @op: The memory operation to execute
|
||||
*
|
||||
* Sets the operational mode of QSPI controller for the next QSPI transfer and
|
||||
* sets the requested clock frequency.
|
||||
@ -331,7 +332,8 @@ static void zynq_qspi_chipselect(struct spi_device *spi, bool assert)
|
||||
* controller the driver will set the highest or lowest frequency supported by
|
||||
* controller.
|
||||
*/
|
||||
static int zynq_qspi_config_op(struct zynq_qspi *xqspi, struct spi_device *spi)
|
||||
static int zynq_qspi_config_op(struct zynq_qspi *xqspi, struct spi_device *spi,
|
||||
const struct spi_mem_op *op)
|
||||
{
|
||||
u32 config_reg, baud_rate_val = 0;
|
||||
|
||||
@ -346,7 +348,7 @@ static int zynq_qspi_config_op(struct zynq_qspi *xqspi, struct spi_device *spi)
|
||||
*/
|
||||
while ((baud_rate_val < ZYNQ_QSPI_CONFIG_BAUD_DIV_MAX) &&
|
||||
(clk_get_rate(xqspi->refclk) / (2 << baud_rate_val)) >
|
||||
spi->max_speed_hz)
|
||||
op->max_freq)
|
||||
baud_rate_val++;
|
||||
|
||||
config_reg = zynq_qspi_read(xqspi, ZYNQ_QSPI_CONFIG_OFFSET);
|
||||
@ -543,7 +545,7 @@ static int zynq_qspi_exec_mem_op(struct spi_mem *mem,
|
||||
op->dummy.buswidth, op->data.buswidth);
|
||||
|
||||
zynq_qspi_chipselect(mem->spi, true);
|
||||
zynq_qspi_config_op(xqspi, mem->spi);
|
||||
zynq_qspi_config_op(xqspi, mem->spi, op);
|
||||
|
||||
if (op->cmd.opcode) {
|
||||
reinit_completion(&xqspi->data_completion);
|
||||
@ -629,6 +631,10 @@ static const struct spi_controller_mem_ops zynq_qspi_mem_ops = {
|
||||
.exec_op = zynq_qspi_exec_mem_op,
|
||||
};
|
||||
|
||||
static const struct spi_controller_mem_caps zynq_qspi_mem_caps = {
|
||||
.per_op_freq = true,
|
||||
};
|
||||
|
||||
/**
|
||||
* zynq_qspi_probe - Probe method for the QSPI driver
|
||||
* @pdev: Pointer to the platform_device structure
|
||||
@ -715,6 +721,7 @@ static int zynq_qspi_probe(struct platform_device *pdev)
|
||||
ctlr->mode_bits = SPI_RX_DUAL | SPI_RX_QUAD |
|
||||
SPI_TX_DUAL | SPI_TX_QUAD;
|
||||
ctlr->mem_ops = &zynq_qspi_mem_ops;
|
||||
ctlr->mem_caps = &zynq_qspi_mem_caps;
|
||||
ctlr->setup = zynq_qspi_setup_op;
|
||||
ctlr->max_speed_hz = clk_get_rate(xqspi->refclk) / 2;
|
||||
ctlr->dev.of_node = np;
|
||||
|
@ -535,7 +535,7 @@ static inline u32 zynqmp_qspi_selectspimode(struct zynqmp_qspi *xqspi,
|
||||
* zynqmp_qspi_config_op - Configure QSPI controller for specified
|
||||
* transfer
|
||||
* @xqspi: Pointer to the zynqmp_qspi structure
|
||||
* @qspi: Pointer to the spi_device structure
|
||||
* @op: The memory operation to execute
|
||||
*
|
||||
* Sets the operational mode of QSPI controller for the next QSPI transfer and
|
||||
* sets the requested clock frequency.
|
||||
@ -553,12 +553,12 @@ static inline u32 zynqmp_qspi_selectspimode(struct zynqmp_qspi *xqspi,
|
||||
* frequency supported by controller.
|
||||
*/
|
||||
static int zynqmp_qspi_config_op(struct zynqmp_qspi *xqspi,
|
||||
struct spi_device *qspi)
|
||||
const struct spi_mem_op *op)
|
||||
{
|
||||
ulong clk_rate;
|
||||
u32 config_reg, req_speed_hz, baud_rate_val = 0;
|
||||
|
||||
req_speed_hz = qspi->max_speed_hz;
|
||||
req_speed_hz = op->max_freq;
|
||||
|
||||
if (xqspi->speed_hz != req_speed_hz) {
|
||||
xqspi->speed_hz = req_speed_hz;
|
||||
@ -1072,7 +1072,7 @@ static int zynqmp_qspi_exec_op(struct spi_mem *mem,
|
||||
op->dummy.buswidth, op->data.buswidth);
|
||||
|
||||
mutex_lock(&xqspi->op_lock);
|
||||
zynqmp_qspi_config_op(xqspi, mem->spi);
|
||||
zynqmp_qspi_config_op(xqspi, op);
|
||||
zynqmp_qspi_chipselect(mem->spi, false);
|
||||
genfifoentry |= xqspi->genfifocs;
|
||||
genfifoentry |= xqspi->genfifobus;
|
||||
@ -1224,6 +1224,10 @@ static const struct spi_controller_mem_ops zynqmp_qspi_mem_ops = {
|
||||
.exec_op = zynqmp_qspi_exec_op,
|
||||
};
|
||||
|
||||
static const struct spi_controller_mem_caps zynqmp_qspi_mem_caps = {
|
||||
.per_op_freq = true,
|
||||
};
|
||||
|
||||
/**
|
||||
* zynqmp_qspi_probe - Probe method for the QSPI driver
|
||||
* @pdev: Pointer to the platform_device structure
|
||||
@ -1333,6 +1337,7 @@ static int zynqmp_qspi_probe(struct platform_device *pdev)
|
||||
|
||||
ctlr->bits_per_word_mask = SPI_BPW_MASK(8);
|
||||
ctlr->mem_ops = &zynqmp_qspi_mem_ops;
|
||||
ctlr->mem_caps = &zynqmp_qspi_mem_caps;
|
||||
ctlr->setup = zynqmp_qspi_setup_op;
|
||||
ctlr->bits_per_word_mask = SPI_BPW_MASK(8);
|
||||
ctlr->dev.of_node = np;
|
||||
|
@ -15,16 +15,32 @@
|
||||
|
||||
#define SPI_MEM_OP_CMD(__opcode, __buswidth) \
|
||||
{ \
|
||||
.nbytes = 1, \
|
||||
.buswidth = __buswidth, \
|
||||
.opcode = __opcode, \
|
||||
}
|
||||
|
||||
#define SPI_MEM_DTR_OP_CMD(__opcode, __buswidth) \
|
||||
{ \
|
||||
.nbytes = 1, \
|
||||
.opcode = __opcode, \
|
||||
.buswidth = __buswidth, \
|
||||
.dtr = true, \
|
||||
}
|
||||
|
||||
#define SPI_MEM_OP_ADDR(__nbytes, __val, __buswidth) \
|
||||
{ \
|
||||
.nbytes = __nbytes, \
|
||||
.buswidth = __buswidth, \
|
||||
.val = __val, \
|
||||
}
|
||||
|
||||
#define SPI_MEM_DTR_OP_ADDR(__nbytes, __val, __buswidth) \
|
||||
{ \
|
||||
.nbytes = __nbytes, \
|
||||
.val = __val, \
|
||||
.buswidth = __buswidth, \
|
||||
.dtr = true, \
|
||||
}
|
||||
|
||||
#define SPI_MEM_OP_NO_ADDR { }
|
||||
@ -35,22 +51,47 @@
|
||||
.buswidth = __buswidth, \
|
||||
}
|
||||
|
||||
#define SPI_MEM_DTR_OP_DUMMY(__nbytes, __buswidth) \
|
||||
{ \
|
||||
.nbytes = __nbytes, \
|
||||
.buswidth = __buswidth, \
|
||||
.dtr = true, \
|
||||
}
|
||||
|
||||
#define SPI_MEM_OP_NO_DUMMY { }
|
||||
|
||||
#define SPI_MEM_OP_DATA_IN(__nbytes, __buf, __buswidth) \
|
||||
{ \
|
||||
.buswidth = __buswidth, \
|
||||
.dir = SPI_MEM_DATA_IN, \
|
||||
.nbytes = __nbytes, \
|
||||
.buf.in = __buf, \
|
||||
}
|
||||
|
||||
#define SPI_MEM_DTR_OP_DATA_IN(__nbytes, __buf, __buswidth) \
|
||||
{ \
|
||||
.dir = SPI_MEM_DATA_IN, \
|
||||
.nbytes = __nbytes, \
|
||||
.buf.in = __buf, \
|
||||
.buswidth = __buswidth, \
|
||||
.dtr = true, \
|
||||
}
|
||||
|
||||
#define SPI_MEM_OP_DATA_OUT(__nbytes, __buf, __buswidth) \
|
||||
{ \
|
||||
.buswidth = __buswidth, \
|
||||
.dir = SPI_MEM_DATA_OUT, \
|
||||
.nbytes = __nbytes, \
|
||||
.buf.out = __buf, \
|
||||
}
|
||||
|
||||
#define SPI_MEM_DTR_OP_DATA_OUT(__nbytes, __buf, __buswidth) \
|
||||
{ \
|
||||
.dir = SPI_MEM_DATA_OUT, \
|
||||
.nbytes = __nbytes, \
|
||||
.buf.out = __buf, \
|
||||
.buswidth = __buswidth, \
|
||||
.dtr = true, \
|
||||
}
|
||||
|
||||
#define SPI_MEM_OP_NO_DATA { }
|
||||
@ -68,6 +109,9 @@ enum spi_mem_data_dir {
|
||||
SPI_MEM_DATA_OUT,
|
||||
};
|
||||
|
||||
#define SPI_MEM_OP_MAX_FREQ(__freq) \
|
||||
.max_freq = __freq
|
||||
|
||||
/**
|
||||
* struct spi_mem_op - describes a SPI memory operation
|
||||
* @cmd.nbytes: number of opcode bytes (only 1 or 2 are valid). The opcode is
|
||||
@ -97,6 +141,9 @@ enum spi_mem_data_dir {
|
||||
* operation does not involve transferring data
|
||||
* @data.buf.in: input buffer (must be DMA-able)
|
||||
* @data.buf.out: output buffer (must be DMA-able)
|
||||
* @max_freq: frequency limitation wrt this operation. 0 means there is no
|
||||
* specific constraint and the highest achievable frequency can be
|
||||
* attempted.
|
||||
*/
|
||||
struct spi_mem_op {
|
||||
struct {
|
||||
@ -135,14 +182,17 @@ struct spi_mem_op {
|
||||
const void *out;
|
||||
} buf;
|
||||
} data;
|
||||
|
||||
unsigned int max_freq;
|
||||
};
|
||||
|
||||
#define SPI_MEM_OP(__cmd, __addr, __dummy, __data) \
|
||||
#define SPI_MEM_OP(__cmd, __addr, __dummy, __data, ...) \
|
||||
{ \
|
||||
.cmd = __cmd, \
|
||||
.addr = __addr, \
|
||||
.dummy = __dummy, \
|
||||
.data = __data, \
|
||||
__VA_ARGS__ \
|
||||
}
|
||||
|
||||
/**
|
||||
@ -302,11 +352,13 @@ struct spi_controller_mem_ops {
|
||||
* @ecc: Supports operations with error correction
|
||||
* @swap16: Supports swapping bytes on a 16 bit boundary when configured in
|
||||
* Octal DTR
|
||||
* @per_op_freq: Supports per operation frequency switching
|
||||
*/
|
||||
struct spi_controller_mem_caps {
|
||||
bool dtr;
|
||||
bool ecc;
|
||||
bool swap16;
|
||||
bool per_op_freq;
|
||||
};
|
||||
|
||||
#define spi_mem_controller_is_capable(ctlr, cap) \
|
||||
@ -371,6 +423,7 @@ bool spi_mem_default_supports_op(struct spi_mem *mem,
|
||||
#endif /* CONFIG_SPI_MEM */
|
||||
|
||||
int spi_mem_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op);
|
||||
void spi_mem_adjust_op_freq(struct spi_mem *mem, struct spi_mem_op *op);
|
||||
|
||||
bool spi_mem_supports_op(struct spi_mem *mem,
|
||||
const struct spi_mem_op *op);
|
||||
|
Loading…
x
Reference in New Issue
Block a user