RISC-V SoC drivers for v6.4
Microchip: Mailbox controller & client changes for the system controller on PolarFire SoC. The controller bits have been acked by Jassi. Primarily the changes work around a "hardware" bug (really the system controller's software, but it may as well be hardware as customers cannot change it) where interrupts are not generated if a service fails. The mailbox controller driver is tweaked to use polling, rather than interrupt, mode and there are some changes to timeout code required in the client driver as a result. There's some opportunistic cleanup that I performed while doing the swap too. Canaan: A single fix for some randconfig issues that crop up when !mmu is enabled for 32-bit kernels, due to my changes in a previous release that swapped out select based entablement of the driver. Signed-off-by: Conor Dooley <conor.dooley@microchip.com> -----BEGIN PGP SIGNATURE----- iHUEABYIAB0WIQRh246EGq/8RLhDjO14tDGHoIJi0gUCZC8mSgAKCRB4tDGHoIJi 0pA9AP4hFbaP41qbMdXb4mX4bxCwZxZt9M2+6JZoa1zmNZ7CIAD+Ip8fgHJzpIjC m0hZ3fmZh/8F+GuOYY1qytEpCkK3Cw0= =xY/y -----END PGP SIGNATURE----- gpgsig -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEiK/NIGsWEZVxh/FrYKtH/8kJUicFAmQ5R1cACgkQYKtH/8kJ Uic/8Q//XzDaRG+IR6IcaYtv8PQd37L83FCYvNCqIBdkWzKa40wTadPzgCnRtaL/ 2dUjvNGdDBNPL+EVGiVXEHbDYfQzyjfKZ+gaaVpdGrpY7OnZ7/E1AItFGn1ICeac OpCai9a05bfm2QCmDt1c9tbHoxzT21Lt2oGnpEmdJa505JUmYUsRrrJ88l71u/9b ZaoHAwlmE2WaZkYF2fgN6KAWKHHyvDc0ha1E08qZ13Rv8L1n6JblnmJK8PdFeRRN 3/J8cFGOSb+gqjFCGXbYmWhlGmdg+hSxaTRN3jqlKg0Vm4Ck9LNj2+C5FoUl8Va/ vkH2ZBH9ciA2pMBPtilB9mf98IkgeqLwT3M8tQf0NZ2rzkcGBWVQ+9a66rxWWZBf dq5ML/MtznPsUljJcjR7Jlun8DBpDe01ICpZHR+YVa6jsa+Quepnc0/pyzqnT55Z 8E0L07cktBUvXbIKi+q/GgKV1wbr+0z4x5u63dUPksJxDsM/Fazuxv3wxv7ZcVKK Ii0Fl9O1SLDA1lXK69rGLF5zKbwulPnA1q2GC5vrfolJWShw0FLO2KSu/mT3s2Ri tg7m8uIkvEKCVoFlSeX93n/mUEfmFONBPS9HH+rE+BtK00cIoRdJET2IQjkIssN8 Rv+YTIPT1skNAbLPlzE88UZ7Pd3IqpKssKYybt0X2qrISmZo5Yg= =vLWl -----END PGP SIGNATURE----- Merge tag 'riscv-soc-for-v6.4' of https://git.kernel.org/pub/scm/linux/kernel/git/conor/linux into soc/drivers RISC-V SoC drivers for v6.4 Microchip: Mailbox controller & client changes for the system controller on PolarFire SoC. The controller bits have been acked by Jassi. Primarily the changes work around a "hardware" bug (really the system controller's software, but it may as well be hardware as customers cannot change it) where interrupts are not generated if a service fails. The mailbox controller driver is tweaked to use polling, rather than interrupt, mode and there are some changes to timeout code required in the client driver as a result. There's some opportunistic cleanup that I performed while doing the swap too. Canaan: A single fix for some randconfig issues that crop up when !mmu is enabled for 32-bit kernels, due to my changes in a previous release that swapped out select based entablement of the driver. Signed-off-by: Conor Dooley <conor.dooley@microchip.com> * tag 'riscv-soc-for-v6.4' of https://git.kernel.org/pub/scm/linux/kernel/git/conor/linux: soc: microchip: mpfs: add a prefix to rx_callback() soc: microchip: mpfs: handle timeouts and failed services differently soc: microchip: mpfs: simplify error handling in mpfs_blocking_transaction() soc: microchip: mpfs: use a consistent completion timeout soc: microchip: mpfs: fix some horrible alignment mailbox: mpfs: check the service status in .tx_done() mailbox: mpfs: ditch a useless busy check mailbox: mpfs: switch to txdone_poll mailbox: mpfs: fix an incorrect mask width soc: canaan: Make K210_SYSCTL depend on CLK_K210 Link: https://lore.kernel.org/r/20230406-islamist-mop-81d651b8830d@spud Signed-off-by: Arnd Bergmann <arnd@arndb.de>
This commit is contained in:
commit
3d3b32a6dd
@ -39,7 +39,7 @@
|
||||
#define SCB_CTRL_NOTIFY_MASK BIT(SCB_CTRL_NOTIFY)
|
||||
|
||||
#define SCB_CTRL_POS (16)
|
||||
#define SCB_CTRL_MASK GENMASK_ULL(SCB_CTRL_POS + SCB_MASK_WIDTH, SCB_CTRL_POS)
|
||||
#define SCB_CTRL_MASK GENMASK(SCB_CTRL_POS + SCB_MASK_WIDTH - 1, SCB_CTRL_POS)
|
||||
|
||||
/* SCBCTRL service status register */
|
||||
|
||||
@ -79,6 +79,27 @@ static bool mpfs_mbox_busy(struct mpfs_mbox *mbox)
|
||||
return status & SCB_STATUS_BUSY_MASK;
|
||||
}
|
||||
|
||||
static bool mpfs_mbox_last_tx_done(struct mbox_chan *chan)
|
||||
{
|
||||
struct mpfs_mbox *mbox = (struct mpfs_mbox *)chan->con_priv;
|
||||
struct mpfs_mss_response *response = mbox->response;
|
||||
u32 val;
|
||||
|
||||
if (mpfs_mbox_busy(mbox))
|
||||
return false;
|
||||
|
||||
/*
|
||||
* The service status is stored in bits 31:16 of the SERVICES_SR
|
||||
* register & is only valid when the system controller is not busy.
|
||||
* Failed services are intended to generated interrupts, but in reality
|
||||
* this does not happen, so the status must be checked here.
|
||||
*/
|
||||
val = readl_relaxed(mbox->ctrl_base + SERVICES_SR_OFFSET);
|
||||
response->resp_status = (val & SCB_STATUS_MASK) >> SCB_STATUS_POS;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int mpfs_mbox_send_data(struct mbox_chan *chan, void *data)
|
||||
{
|
||||
struct mpfs_mbox *mbox = (struct mpfs_mbox *)chan->con_priv;
|
||||
@ -118,6 +139,7 @@ static int mpfs_mbox_send_data(struct mbox_chan *chan, void *data)
|
||||
}
|
||||
|
||||
opt_sel = ((msg->mbox_offset << 7u) | (msg->cmd_opcode & 0x7fu));
|
||||
|
||||
tx_trigger = (opt_sel << SCB_CTRL_POS) & SCB_CTRL_MASK;
|
||||
tx_trigger |= SCB_CTRL_REQ_MASK | SCB_STATUS_NOTIFY_MASK;
|
||||
writel_relaxed(tx_trigger, mbox->ctrl_base + SERVICES_CR_OFFSET);
|
||||
@ -130,7 +152,7 @@ static void mpfs_mbox_rx_data(struct mbox_chan *chan)
|
||||
struct mpfs_mbox *mbox = (struct mpfs_mbox *)chan->con_priv;
|
||||
struct mpfs_mss_response *response = mbox->response;
|
||||
u16 num_words = ALIGN((response->resp_size), (4)) / 4U;
|
||||
u32 i, status;
|
||||
u32 i;
|
||||
|
||||
if (!response->resp_msg) {
|
||||
dev_err(mbox->dev, "failed to assign memory for response %d\n", -ENOMEM);
|
||||
@ -138,8 +160,6 @@ static void mpfs_mbox_rx_data(struct mbox_chan *chan)
|
||||
}
|
||||
|
||||
/*
|
||||
* The status is stored in bits 31:16 of the SERVICES_SR register.
|
||||
* It is only valid when BUSY == 0.
|
||||
* We should *never* get an interrupt while the controller is
|
||||
* still in the busy state. If we do, something has gone badly
|
||||
* wrong & the content of the mailbox would not be valid.
|
||||
@ -150,24 +170,10 @@ static void mpfs_mbox_rx_data(struct mbox_chan *chan)
|
||||
return;
|
||||
}
|
||||
|
||||
status = readl_relaxed(mbox->ctrl_base + SERVICES_SR_OFFSET);
|
||||
|
||||
/*
|
||||
* If the status of the individual servers is non-zero, the service has
|
||||
* failed. The contents of the mailbox at this point are not be valid,
|
||||
* so don't bother reading them. Set the status so that the driver
|
||||
* implementing the service can handle the result.
|
||||
*/
|
||||
response->resp_status = (status & SCB_STATUS_MASK) >> SCB_STATUS_POS;
|
||||
if (response->resp_status)
|
||||
return;
|
||||
|
||||
if (!mpfs_mbox_busy(mbox)) {
|
||||
for (i = 0; i < num_words; i++) {
|
||||
response->resp_msg[i] =
|
||||
readl_relaxed(mbox->mbox_base
|
||||
+ mbox->resp_offset + i * 0x4);
|
||||
}
|
||||
for (i = 0; i < num_words; i++) {
|
||||
response->resp_msg[i] =
|
||||
readl_relaxed(mbox->mbox_base
|
||||
+ mbox->resp_offset + i * 0x4);
|
||||
}
|
||||
|
||||
mbox_chan_received_data(chan, response);
|
||||
@ -182,7 +188,6 @@ static irqreturn_t mpfs_mbox_inbox_isr(int irq, void *data)
|
||||
|
||||
mpfs_mbox_rx_data(chan);
|
||||
|
||||
mbox_chan_txdone(chan, 0);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
@ -212,6 +217,7 @@ static const struct mbox_chan_ops mpfs_mbox_ops = {
|
||||
.send_data = mpfs_mbox_send_data,
|
||||
.startup = mpfs_mbox_startup,
|
||||
.shutdown = mpfs_mbox_shutdown,
|
||||
.last_tx_done = mpfs_mbox_last_tx_done,
|
||||
};
|
||||
|
||||
static int mpfs_mbox_probe(struct platform_device *pdev)
|
||||
@ -247,7 +253,8 @@ static int mpfs_mbox_probe(struct platform_device *pdev)
|
||||
mbox->controller.num_chans = 1;
|
||||
mbox->controller.chans = mbox->chans;
|
||||
mbox->controller.ops = &mpfs_mbox_ops;
|
||||
mbox->controller.txdone_irq = true;
|
||||
mbox->controller.txdone_poll = true;
|
||||
mbox->controller.txpoll_period = 10u;
|
||||
|
||||
ret = devm_mbox_controller_register(&pdev->dev, &mbox->controller);
|
||||
if (ret) {
|
||||
|
@ -3,8 +3,9 @@
|
||||
config SOC_K210_SYSCTL
|
||||
bool "Canaan Kendryte K210 SoC system controller"
|
||||
depends on RISCV && SOC_CANAAN && OF
|
||||
depends on COMMON_CLK_K210
|
||||
default SOC_CANAAN
|
||||
select PM
|
||||
select MFD_SYSCON
|
||||
select PM
|
||||
select MFD_SYSCON
|
||||
help
|
||||
Canaan Kendryte K210 SoC system controller driver.
|
||||
|
@ -11,12 +11,19 @@
|
||||
#include <linux/slab.h>
|
||||
#include <linux/kref.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/mailbox_client.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <soc/microchip/mpfs.h>
|
||||
|
||||
/*
|
||||
* This timeout must be long, as some services (example: image authentication)
|
||||
* take significant time to complete
|
||||
*/
|
||||
#define MPFS_SYS_CTRL_TIMEOUT_MS 30000
|
||||
|
||||
static DEFINE_MUTEX(transaction_lock);
|
||||
|
||||
struct mpfs_sys_controller {
|
||||
@ -28,35 +35,47 @@ struct mpfs_sys_controller {
|
||||
|
||||
int mpfs_blocking_transaction(struct mpfs_sys_controller *sys_controller, struct mpfs_mss_msg *msg)
|
||||
{
|
||||
int ret, err;
|
||||
unsigned long timeout = msecs_to_jiffies(MPFS_SYS_CTRL_TIMEOUT_MS);
|
||||
int ret;
|
||||
|
||||
err = mutex_lock_interruptible(&transaction_lock);
|
||||
if (err)
|
||||
return err;
|
||||
ret = mutex_lock_interruptible(&transaction_lock);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
reinit_completion(&sys_controller->c);
|
||||
|
||||
ret = mbox_send_message(sys_controller->chan, msg);
|
||||
if (ret >= 0) {
|
||||
if (wait_for_completion_timeout(&sys_controller->c, HZ)) {
|
||||
ret = 0;
|
||||
} else {
|
||||
ret = -ETIMEDOUT;
|
||||
dev_warn(sys_controller->client.dev,
|
||||
"MPFS sys controller transaction timeout\n");
|
||||
}
|
||||
} else {
|
||||
dev_err(sys_controller->client.dev,
|
||||
"mpfs sys controller transaction returned %d\n", ret);
|
||||
if (ret < 0) {
|
||||
dev_warn(sys_controller->client.dev, "MPFS sys controller service timeout\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* Unfortunately, the system controller will only deliver an interrupt
|
||||
* if a service succeeds. mbox_send_message() will block until the busy
|
||||
* flag is gone. If the busy flag is gone but no interrupt has arrived
|
||||
* to trigger the rx callback then the service can be deemed to have
|
||||
* failed.
|
||||
* The caller can then interrogate msg::response::resp_status to
|
||||
* determine the cause of the failure.
|
||||
* mbox_send_message() returns positive integers in the success path, so
|
||||
* ret needs to be cleared if we do get an interrupt.
|
||||
*/
|
||||
if (!wait_for_completion_timeout(&sys_controller->c, timeout)) {
|
||||
ret = -EBADMSG;
|
||||
dev_warn(sys_controller->client.dev, "MPFS sys controller service failed\n");
|
||||
} else {
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
out:
|
||||
mutex_unlock(&transaction_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(mpfs_blocking_transaction);
|
||||
|
||||
static void rx_callback(struct mbox_client *client, void *msg)
|
||||
static void mpfs_sys_controller_rx_callback(struct mbox_client *client, void *msg)
|
||||
{
|
||||
struct mpfs_sys_controller *sys_controller =
|
||||
container_of(client, struct mpfs_sys_controller, client);
|
||||
@ -66,8 +85,8 @@ static void rx_callback(struct mbox_client *client, void *msg)
|
||||
|
||||
static void mpfs_sys_controller_delete(struct kref *kref)
|
||||
{
|
||||
struct mpfs_sys_controller *sys_controller = container_of(kref, struct mpfs_sys_controller,
|
||||
consumers);
|
||||
struct mpfs_sys_controller *sys_controller =
|
||||
container_of(kref, struct mpfs_sys_controller, consumers);
|
||||
|
||||
mbox_free_channel(sys_controller->chan);
|
||||
kfree(sys_controller);
|
||||
@ -102,8 +121,9 @@ static int mpfs_sys_controller_probe(struct platform_device *pdev)
|
||||
return -ENOMEM;
|
||||
|
||||
sys_controller->client.dev = dev;
|
||||
sys_controller->client.rx_callback = rx_callback;
|
||||
sys_controller->client.rx_callback = mpfs_sys_controller_rx_callback;
|
||||
sys_controller->client.tx_block = 1U;
|
||||
sys_controller->client.tx_tout = msecs_to_jiffies(MPFS_SYS_CTRL_TIMEOUT_MS);
|
||||
|
||||
sys_controller->chan = mbox_request_channel(&sys_controller->client, 0);
|
||||
if (IS_ERR(sys_controller->chan)) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user