/* * The interrupt status register indicates whether an error occurs * after per operation. Check it, and clear the interrupts for * next time judgement.
*/ staticint hisi_sfc_v3xx_handle_completion(struct hisi_sfc_v3xx_host *host)
{
u32 reg;
if (reg & HISI_SFC_V3XX_INT_MASK_IACCES) {
dev_err(host->dev, "fail to access protected address\n"); return -EIO;
}
if (reg & HISI_SFC_V3XX_INT_MASK_PP_ERR) {
dev_err(host->dev, "page program operation failed\n"); return -EIO;
}
/* * The other bits of the interrupt registers is not currently * used and probably not be triggered in this driver. When it * happens, we regard it as an unsupported error here.
*/ if (!(reg & HISI_SFC_V3XX_INT_MASK_CPLT)) {
dev_err(host->dev, "unsupported error occurred, status=0x%x\n", reg); return -EIO;
}
if (op->addr.nbytes != host->address_mode && op->addr.nbytes) returnfalse;
return spi_mem_default_supports_op(mem, op);
}
/* * memcpy_{to,from}io doesn't gurantee 32b accesses - which we require for the * DATABUF registers -so use __io{read,write}32_copy when possible. For * trailing bytes, copy them byte-by-byte from the DATABUF register, as we * can't clobber outside the source/dest buffer. * * For efficient data read/write, we try to put any start 32b unaligned data * into a separate transaction in hisi_sfc_v3xx_adjust_op_size().
*/ staticvoid hisi_sfc_v3xx_read_databuf(struct hisi_sfc_v3xx_host *host,
u8 *to, unsignedint len)
{ void __iomem *from; int i;
from = host->regbase + HISI_SFC_V3XX_CMD_DATABUF0;
if (IS_ALIGNED((uintptr_t)to, 4)) { int words = len / 4;
__ioread32_copy(to, from, words);
len -= words * 4; if (len) {
u32 val;
to += words * 4;
from += words * 4;
val = __raw_readl(from);
for (i = 0; i < len; i++, val >>= 8, to++)
*to = (u8)val;
}
} else { for (i = 0; i < DIV_ROUND_UP(len, 4); i++, from += 4) {
u32 val = __raw_readl(from); int j;
for (j = 0; j < 4 && (j + (i * 4) < len);
to++, val >>= 8, j++)
*to = (u8)val;
}
}
}
if (host->irq) {
host->completion = &done;
hisi_sfc_v3xx_enable_int(host);
}
if (op->data.dir == SPI_MEM_DATA_OUT)
hisi_sfc_v3xx_write_databuf(host, op->data.buf.out, op->data.nbytes);
ret = hisi_sfc_v3xx_start_bus(host, op, chip_select); if (ret) return ret;
if (host->irq) {
ret = wait_for_completion_timeout(host->completion,
usecs_to_jiffies(HISI_SFC_V3XX_WAIT_TIMEOUT_US)); if (!ret)
ret = -ETIMEDOUT; else
ret = 0;
hisi_sfc_v3xx_disable_int(host);
synchronize_irq(host->irq);
host->completion = NULL;
} else {
ret = hisi_sfc_v3xx_wait_cmd_idle(host);
} if (hisi_sfc_v3xx_handle_completion(host) || ret) return -EIO;
if (op->data.dir == SPI_MEM_DATA_IN)
hisi_sfc_v3xx_read_databuf(host, op->data.buf.in, op->data.nbytes);
reg = readl(host->regbase + HISI_SFC_V3XX_INT_STAT); if (!reg) return IRQ_NONE;
hisi_sfc_v3xx_disable_int(host);
complete(host->completion);
return IRQ_HANDLED;
}
staticint hisi_sfc_v3xx_buswidth_override_bits;
/* * ACPI FW does not allow us to currently set the device buswidth, so quirk it * depending on the board.
*/ staticint __init hisi_sfc_v3xx_dmi_quirk(conststruct dmi_system_id *d)
{
hisi_sfc_v3xx_buswidth_override_bits = SPI_RX_QUAD | SPI_TX_QUAD;
/* * The address mode of the controller is either 3 or 4, * which is indicated by the address mode bit in * the global config register. The register is read only * for the OS driver.
*/
glb_config = readl(host->regbase + HISI_SFC_V3XX_GLB_CFG); if (glb_config & HISI_SFC_V3XX_GLB_CFG_CS0_ADDR_MODE)
host->address_mode = 4; else
host->address_mode = 3;
version = readl(host->regbase + HISI_SFC_V3XX_VERSION);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.