diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2022-03-23 18:20:09 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2022-03-23 18:20:09 -0700 |
commit | baaa68a9796ef2cadfe5caaf4c730412eda0f31c (patch) | |
tree | 73a8daadf72b5a80268222b863fc9f8f286b3c3d /drivers/soc/microchip/mpfs-sys-controller.c | |
parent | 8ffa5709e577385a1c8d20fb434cb02732f1d991 (diff) | |
parent | 2f618d5ef5dd05e2380ee627814de90d5a33c3f2 (diff) | |
download | linux-baaa68a9796ef2cadfe5caaf4c730412eda0f31c.tar.gz linux-baaa68a9796ef2cadfe5caaf4c730412eda0f31c.tar.bz2 linux-baaa68a9796ef2cadfe5caaf4c730412eda0f31c.zip |
Merge tag 'arm-soc-5.18' of git://git.kernel.org/pub/scm/linux/kernel/git/soc/soc
Pull ARM SoC updates from Arnd Bergmann:
"SoC specific code is generally used for older platforms that don't
(yet) use device tree to do the same things.
- Support is added for i.MXRT10xx, a Cortex-M7 based microcontroller
from NXP. At the moment this is still incomplete as other portions
are merged through different trees.
- Long abandoned support for running NOMMU ARMv4 or ARMv5 platforms
gets removed, now the Arm NOMMU platforms are limited to the
Cortex-M family of microcontrollers
- Two old PXA boards get removed, along with corresponding driver
bits.
- Continued cleanup of the Intel IXP4xx platforms, removing some
remnants of the old board files.
- Minor Cleanups and fixes for Orion, PXA, MMP, Mstar, Samsung
- CPU idle support for AT91
- A system controller driver for Polarfire"
* tag 'arm-soc-5.18' of git://git.kernel.org/pub/scm/linux/kernel/git/soc/soc: (29 commits)
ARM: remove support for NOMMU ARMv4/v5
ARM: PXA: fix up decompressor code
soc: microchip: make mpfs_sys_controller_put static
ARM: pxa: remove Intel Imote2 and Stargate 2 boards
ARM: mmp: Fix failure to remove sram device
ARM: mstar: Select ARM_ERRATA_814220
soc: add microchip polarfire soc system controller
ARM: at91: Kconfig: select PM_OPP
ARM: at91: PM: add cpu idle support for sama7g5
ARM: at91: ddr: fix typo to align with datasheet naming
ARM: at91: ddr: align macro definitions
ARM: at91: ddr: remove CONFIG_SOC_SAMA7 dependency
ARM: ixp4xx: Convert to SPARSE_IRQ and P2V
ARM: ixp4xx: Drop all common code
ARM: ixp4xx: Drop custom DMA coherency and bouncing
ARM: ixp4xx: Remove feature bit accessors
net: ixp4xx_hss: Check features using syscon
net: ixp4xx_eth: Drop platform data support
soc: ixp4xx-npe: Access syscon regs using regmap
soc: ixp4xx: Add features from regmap helper
...
Diffstat (limited to 'drivers/soc/microchip/mpfs-sys-controller.c')
-rw-r--r-- | drivers/soc/microchip/mpfs-sys-controller.c | 193 |
1 files changed, 193 insertions, 0 deletions
diff --git a/drivers/soc/microchip/mpfs-sys-controller.c b/drivers/soc/microchip/mpfs-sys-controller.c new file mode 100644 index 000000000000..31f3f29fc1ae --- /dev/null +++ b/drivers/soc/microchip/mpfs-sys-controller.c @@ -0,0 +1,193 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Microchip PolarFire SoC (MPFS) system controller driver + * + * Copyright (c) 2020-2021 Microchip Corporation. All rights reserved. + * + * Author: Conor Dooley <conor.dooley@microchip.com> + * + */ + +#include <linux/slab.h> +#include <linux/kref.h> +#include <linux/module.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> + +static DEFINE_MUTEX(transaction_lock); + +struct mpfs_sys_controller { + struct mbox_client client; + struct mbox_chan *chan; + struct completion c; + struct kref consumers; +}; + +int mpfs_blocking_transaction(struct mpfs_sys_controller *sys_controller, struct mpfs_mss_msg *msg) +{ + int ret, err; + + err = mutex_lock_interruptible(&transaction_lock); + if (err) + return err; + + 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); + } + + mutex_unlock(&transaction_lock); + + return ret; +} +EXPORT_SYMBOL(mpfs_blocking_transaction); + +static void rx_callback(struct mbox_client *client, void *msg) +{ + struct mpfs_sys_controller *sys_controller = + container_of(client, struct mpfs_sys_controller, client); + + complete(&sys_controller->c); +} + +static void mpfs_sys_controller_delete(struct kref *kref) +{ + struct mpfs_sys_controller *sys_controller = container_of(kref, struct mpfs_sys_controller, + consumers); + + mbox_free_channel(sys_controller->chan); + kfree(sys_controller); +} + +static void mpfs_sys_controller_put(void *data) +{ + struct mpfs_sys_controller *sys_controller = data; + + kref_put(&sys_controller->consumers, mpfs_sys_controller_delete); +} + +static struct platform_device subdevs[] = { + { + .name = "mpfs-rng", + .id = -1, + }, + { + .name = "mpfs-generic-service", + .id = -1, + } +}; + +static int mpfs_sys_controller_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct mpfs_sys_controller *sys_controller; + int i; + + sys_controller = devm_kzalloc(dev, sizeof(*sys_controller), GFP_KERNEL); + if (!sys_controller) + return -ENOMEM; + + sys_controller->client.dev = dev; + sys_controller->client.rx_callback = rx_callback; + sys_controller->client.tx_block = 1U; + + sys_controller->chan = mbox_request_channel(&sys_controller->client, 0); + if (IS_ERR(sys_controller->chan)) + return dev_err_probe(dev, PTR_ERR(sys_controller->chan), + "Failed to get mbox channel\n"); + + init_completion(&sys_controller->c); + kref_init(&sys_controller->consumers); + + platform_set_drvdata(pdev, sys_controller); + + dev_info(&pdev->dev, "Registered MPFS system controller\n"); + + for (i = 0; i < ARRAY_SIZE(subdevs); i++) { + subdevs[i].dev.parent = dev; + if (platform_device_register(&subdevs[i])) + dev_warn(dev, "Error registering sub device %s\n", subdevs[i].name); + } + + return 0; +} + +static int mpfs_sys_controller_remove(struct platform_device *pdev) +{ + struct mpfs_sys_controller *sys_controller = platform_get_drvdata(pdev); + + mpfs_sys_controller_put(sys_controller); + + return 0; +} + +static const struct of_device_id mpfs_sys_controller_of_match[] = { + {.compatible = "microchip,mpfs-sys-controller", }, + {}, +}; +MODULE_DEVICE_TABLE(of, mpfs_sys_controller_of_match); + +struct mpfs_sys_controller *mpfs_sys_controller_get(struct device *dev) +{ + const struct of_device_id *match; + struct mpfs_sys_controller *sys_controller; + int ret; + + if (!dev->parent) + goto err_no_device; + + match = of_match_node(mpfs_sys_controller_of_match, dev->parent->of_node); + of_node_put(dev->parent->of_node); + if (!match) + goto err_no_device; + + sys_controller = dev_get_drvdata(dev->parent); + if (!sys_controller) + goto err_bad_device; + + if (!kref_get_unless_zero(&sys_controller->consumers)) + goto err_bad_device; + + ret = devm_add_action_or_reset(dev, mpfs_sys_controller_put, sys_controller); + if (ret) + return ERR_PTR(ret); + + return sys_controller; + +err_no_device: + dev_dbg(dev, "Parent device was not an MPFS system controller\n"); + return ERR_PTR(-ENODEV); + +err_bad_device: + dev_dbg(dev, "MPFS system controller found but could not register as a sub device\n"); + return ERR_PTR(-EPROBE_DEFER); +} +EXPORT_SYMBOL(mpfs_sys_controller_get); + +static struct platform_driver mpfs_sys_controller_driver = { + .driver = { + .name = "mpfs-sys-controller", + .of_match_table = mpfs_sys_controller_of_match, + }, + .probe = mpfs_sys_controller_probe, + .remove = mpfs_sys_controller_remove, +}; +module_platform_driver(mpfs_sys_controller_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Conor Dooley <conor.dooley@microchip.com>"); +MODULE_DESCRIPTION("MPFS system controller driver"); |