diff options
Diffstat (limited to 'drivers')
90 files changed, 4629 insertions, 4674 deletions
diff --git a/drivers/base/soc.c b/drivers/base/soc.c index 10b280f30217..7c0c5ca5953d 100644 --- a/drivers/base/soc.c +++ b/drivers/base/soc.c @@ -33,6 +33,7 @@ static struct bus_type soc_bus_type = { static DEVICE_ATTR(machine, S_IRUGO, soc_info_get, NULL); static DEVICE_ATTR(family, S_IRUGO, soc_info_get, NULL); +static DEVICE_ATTR(serial_number, S_IRUGO, soc_info_get, NULL); static DEVICE_ATTR(soc_id, S_IRUGO, soc_info_get, NULL); static DEVICE_ATTR(revision, S_IRUGO, soc_info_get, NULL); @@ -57,6 +58,9 @@ static umode_t soc_attribute_mode(struct kobject *kobj, if ((attr == &dev_attr_revision.attr) && (soc_dev->attr->revision != NULL)) return attr->mode; + if ((attr == &dev_attr_serial_number.attr) + && (soc_dev->attr->serial_number != NULL)) + return attr->mode; if ((attr == &dev_attr_soc_id.attr) && (soc_dev->attr->soc_id != NULL)) return attr->mode; @@ -77,6 +81,8 @@ static ssize_t soc_info_get(struct device *dev, return sprintf(buf, "%s\n", soc_dev->attr->family); if (attr == &dev_attr_revision) return sprintf(buf, "%s\n", soc_dev->attr->revision); + if (attr == &dev_attr_serial_number) + return sprintf(buf, "%s\n", soc_dev->attr->serial_number); if (attr == &dev_attr_soc_id) return sprintf(buf, "%s\n", soc_dev->attr->soc_id); @@ -87,6 +93,7 @@ static ssize_t soc_info_get(struct device *dev, static struct attribute *soc_attr[] = { &dev_attr_machine.attr, &dev_attr_family.attr, + &dev_attr_serial_number.attr, &dev_attr_soc_id.attr, &dev_attr_revision.attr, NULL, @@ -157,6 +164,7 @@ out2: out1: return ERR_PTR(ret); } +EXPORT_SYMBOL_GPL(soc_device_register); /* Ensure soc_dev->attr is freed prior to calling soc_device_unregister. */ void soc_device_unregister(struct soc_device *soc_dev) @@ -166,6 +174,7 @@ void soc_device_unregister(struct soc_device *soc_dev) device_unregister(&soc_dev->dev); early_soc_dev_attr = NULL; } +EXPORT_SYMBOL_GPL(soc_device_unregister); static int __init soc_bus_register(void) { diff --git a/drivers/bus/Kconfig b/drivers/bus/Kconfig index 1851112ccc29..6b331061d34b 100644 --- a/drivers/bus/Kconfig +++ b/drivers/bus/Kconfig @@ -29,6 +29,16 @@ config BRCMSTB_GISB_ARB arbiter. This driver provides timeout and target abort error handling and internal bus master decoding. +config MOXTET + tristate "CZ.NIC Turris Mox module configuration bus" + depends on SPI_MASTER && OF + help + Say yes here to add support for the module configuration bus found + on CZ.NIC's Turris Mox. This is needed for the ability to discover + the order in which the modules are connected and to get/set some of + their settings. For example the GPIOs on Mox SFP module are + configured through this bus. + config HISILICON_LPC bool "Support for ISA I/O space on HiSilicon Hip06/7" depends on ARM64 && (ARCH_HISI || COMPILE_TEST) diff --git a/drivers/bus/Makefile b/drivers/bus/Makefile index ca300b1914ce..16b43d3468c6 100644 --- a/drivers/bus/Makefile +++ b/drivers/bus/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_ARM_CCI) += arm-cci.o obj-$(CONFIG_HISILICON_LPC) += hisi_lpc.o obj-$(CONFIG_BRCMSTB_GISB_ARB) += brcmstb_gisb.o +obj-$(CONFIG_MOXTET) += moxtet.o # DPAA2 fsl-mc bus obj-$(CONFIG_FSL_MC_BUS) += fsl-mc/ diff --git a/drivers/bus/fsl-mc/fsl-mc-allocator.c b/drivers/bus/fsl-mc/fsl-mc-allocator.c index 8ad77246f322..cc7bb900f524 100644 --- a/drivers/bus/fsl-mc/fsl-mc-allocator.c +++ b/drivers/bus/fsl-mc/fsl-mc-allocator.c @@ -330,7 +330,6 @@ void fsl_mc_object_free(struct fsl_mc_device *mc_adev) fsl_mc_resource_free(resource); - device_link_del(mc_adev->consumer_link); mc_adev->consumer_link = NULL; } EXPORT_SYMBOL_GPL(fsl_mc_object_free); diff --git a/drivers/bus/fsl-mc/mc-io.c b/drivers/bus/fsl-mc/mc-io.c index 3ae574a58cce..d9629fc13a15 100644 --- a/drivers/bus/fsl-mc/mc-io.c +++ b/drivers/bus/fsl-mc/mc-io.c @@ -255,7 +255,6 @@ void fsl_mc_portal_free(struct fsl_mc_io *mc_io) fsl_destroy_mc_io(mc_io); fsl_mc_resource_free(resource); - device_link_del(dpmcp_dev->consumer_link); dpmcp_dev->consumer_link = NULL; } EXPORT_SYMBOL_GPL(fsl_mc_portal_free); diff --git a/drivers/bus/imx-weim.c b/drivers/bus/imx-weim.c index db74334ca5ef..28bb65a5613f 100644 --- a/drivers/bus/imx-weim.c +++ b/drivers/bus/imx-weim.c @@ -19,6 +19,8 @@ struct imx_weim_devtype { unsigned int cs_count; unsigned int cs_regs_count; unsigned int cs_stride; + unsigned int wcr_offset; + unsigned int wcr_bcm; }; static const struct imx_weim_devtype imx1_weim_devtype = { @@ -37,6 +39,8 @@ static const struct imx_weim_devtype imx50_weim_devtype = { .cs_count = 4, .cs_regs_count = 6, .cs_stride = 0x18, + .wcr_offset = 0x90, + .wcr_bcm = BIT(0), }; static const struct imx_weim_devtype imx51_weim_devtype = { @@ -72,7 +76,7 @@ static const struct of_device_id weim_id_table[] = { }; MODULE_DEVICE_TABLE(of, weim_id_table); -static int __init imx_weim_gpr_setup(struct platform_device *pdev) +static int imx_weim_gpr_setup(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; struct property *prop; @@ -122,10 +126,10 @@ err: } /* Parse and set the timing for this device. */ -static int __init weim_timing_setup(struct device *dev, - struct device_node *np, void __iomem *base, - const struct imx_weim_devtype *devtype, - struct cs_timing_state *ts) +static int weim_timing_setup(struct device *dev, + struct device_node *np, void __iomem *base, + const struct imx_weim_devtype *devtype, + struct cs_timing_state *ts) { u32 cs_idx, value[MAX_CS_REGS_COUNT]; int i, ret; @@ -183,8 +187,7 @@ static int __init weim_timing_setup(struct device *dev, return 0; } -static int __init weim_parse_dt(struct platform_device *pdev, - void __iomem *base) +static int weim_parse_dt(struct platform_device *pdev, void __iomem *base) { const struct of_device_id *of_id = of_match_device(weim_id_table, &pdev->dev); @@ -192,6 +195,7 @@ static int __init weim_parse_dt(struct platform_device *pdev, struct device_node *child; int ret, have_child = 0; struct cs_timing_state ts = {}; + u32 reg; if (devtype == &imx50_weim_devtype) { ret = imx_weim_gpr_setup(pdev); @@ -199,6 +203,17 @@ static int __init weim_parse_dt(struct platform_device *pdev, return ret; } + if (of_property_read_bool(pdev->dev.of_node, "fsl,burst-clk-enable")) { + if (devtype->wcr_bcm) { + reg = readl(base + devtype->wcr_offset); + writel(reg | devtype->wcr_bcm, + base + devtype->wcr_offset); + } else { + dev_err(&pdev->dev, "burst clk mode not supported.\n"); + return -EINVAL; + } + } + for_each_available_child_of_node(pdev->dev.of_node, child) { ret = weim_timing_setup(&pdev->dev, child, base, devtype, &ts); if (ret) @@ -217,7 +232,7 @@ static int __init weim_parse_dt(struct platform_device *pdev, return ret; } -static int __init weim_probe(struct platform_device *pdev) +static int weim_probe(struct platform_device *pdev) { struct resource *res; struct clk *clk; @@ -254,8 +269,9 @@ static struct platform_driver weim_driver = { .name = "imx-weim", .of_match_table = weim_id_table, }, + .probe = weim_probe, }; -module_platform_driver_probe(weim_driver, weim_probe); +module_platform_driver(weim_driver); MODULE_AUTHOR("Freescale Semiconductor Inc."); MODULE_DESCRIPTION("i.MX EIM Controller Driver"); diff --git a/drivers/bus/moxtet.c b/drivers/bus/moxtet.c new file mode 100644 index 000000000000..36cf13eee6b8 --- /dev/null +++ b/drivers/bus/moxtet.c @@ -0,0 +1,885 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Turris Mox module configuration bus driver + * + * Copyright (C) 2019 Marek Behun <marek.behun@nic.cz> + */ + +#include <dt-bindings/bus/moxtet.h> +#include <linux/bitops.h> +#include <linux/debugfs.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/moxtet.h> +#include <linux/mutex.h> +#include <linux/of_device.h> +#include <linux/of_irq.h> +#include <linux/spi/spi.h> + +/* + * @name: module name for sysfs + * @hwirq_base: base index for IRQ for this module (-1 if no IRQs) + * @nirqs: how many interrupts does the shift register provide + * @desc: module description for kernel log + */ +static const struct { + const char *name; + int hwirq_base; + int nirqs; + const char *desc; +} mox_module_table[] = { + /* do not change order of this array! */ + { NULL, 0, 0, NULL }, + { "sfp", -1, 0, "MOX D (SFP cage)" }, + { "pci", MOXTET_IRQ_PCI, 1, "MOX B (Mini-PCIe)" }, + { "topaz", MOXTET_IRQ_TOPAZ, 1, "MOX C (4 port switch)" }, + { "peridot", MOXTET_IRQ_PERIDOT(0), 1, "MOX E (8 port switch)" }, + { "usb3", MOXTET_IRQ_USB3, 2, "MOX F (USB 3.0)" }, + { "pci-bridge", -1, 0, "MOX G (Mini-PCIe bridge)" }, +}; + +static inline bool mox_module_known(unsigned int id) +{ + return id >= TURRIS_MOX_MODULE_FIRST && id <= TURRIS_MOX_MODULE_LAST; +} + +static inline const char *mox_module_name(unsigned int id) +{ + if (mox_module_known(id)) + return mox_module_table[id].name; + else + return "unknown"; +} + +#define DEF_MODULE_ATTR(name, fmt, ...) \ +static ssize_t \ +module_##name##_show(struct device *dev, struct device_attribute *a, \ + char *buf) \ +{ \ + struct moxtet_device *mdev = to_moxtet_device(dev); \ + return sprintf(buf, (fmt), __VA_ARGS__); \ +} \ +static DEVICE_ATTR_RO(module_##name) + +DEF_MODULE_ATTR(id, "0x%x\n", mdev->id); +DEF_MODULE_ATTR(name, "%s\n", mox_module_name(mdev->id)); +DEF_MODULE_ATTR(description, "%s\n", + mox_module_known(mdev->id) ? mox_module_table[mdev->id].desc + : ""); + +static struct attribute *moxtet_dev_attrs[] = { + &dev_attr_module_id.attr, + &dev_attr_module_name.attr, + &dev_attr_module_description.attr, + NULL, +}; + +static const struct attribute_group moxtet_dev_group = { + .attrs = moxtet_dev_attrs, +}; + +static const struct attribute_group *moxtet_dev_groups[] = { + &moxtet_dev_group, + NULL, +}; + +static int moxtet_match(struct device *dev, struct device_driver *drv) +{ + struct moxtet_device *mdev = to_moxtet_device(dev); + struct moxtet_driver *tdrv = to_moxtet_driver(drv); + const enum turris_mox_module_id *t; + + if (of_driver_match_device(dev, drv)) + return 1; + + if (!tdrv->id_table) + return 0; + + for (t = tdrv->id_table; *t; ++t) + if (*t == mdev->id) + return 1; + + return 0; +} + +struct bus_type moxtet_bus_type = { + .name = "moxtet", + .dev_groups = moxtet_dev_groups, + .match = moxtet_match, +}; +EXPORT_SYMBOL_GPL(moxtet_bus_type); + +int __moxtet_register_driver(struct module *owner, + struct moxtet_driver *mdrv) +{ + mdrv->driver.owner = owner; + mdrv->driver.bus = &moxtet_bus_type; + return driver_register(&mdrv->driver); +} +EXPORT_SYMBOL_GPL(__moxtet_register_driver); + +static int moxtet_dev_check(struct device *dev, void *data) +{ + struct moxtet_device *mdev = to_moxtet_device(dev); + struct moxtet_device *new_dev = data; + + if (mdev->moxtet == new_dev->moxtet && mdev->id == new_dev->id && + mdev->idx == new_dev->idx) + return -EBUSY; + return 0; +} + +static void moxtet_dev_release(struct device *dev) +{ + struct moxtet_device *mdev = to_moxtet_device(dev); + + put_device(mdev->moxtet->dev); + kfree(mdev); +} + +static struct moxtet_device * +moxtet_alloc_device(struct moxtet *moxtet) +{ + struct moxtet_device *dev; + + if (!get_device(moxtet->dev)) + return NULL; + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) { + put_device(moxtet->dev); + return NULL; + } + + dev->moxtet = moxtet; + dev->dev.parent = moxtet->dev; + dev->dev.bus = &moxtet_bus_type; + dev->dev.release = moxtet_dev_release; + + device_initialize(&dev->dev); + + return dev; +} + +static int moxtet_add_device(struct moxtet_device *dev) +{ + static DEFINE_MUTEX(add_mutex); + int ret; + + if (dev->idx >= TURRIS_MOX_MAX_MODULES || dev->id > 0xf) + return -EINVAL; + + dev_set_name(&dev->dev, "moxtet-%s.%u", mox_module_name(dev->id), + dev->idx); + + mutex_lock(&add_mutex); + + ret = bus_for_each_dev(&moxtet_bus_type, NULL, dev, + moxtet_dev_check); + if (ret) + goto done; + + ret = device_add(&dev->dev); + if (ret < 0) + dev_err(dev->moxtet->dev, "can't add %s, status %d\n", + dev_name(dev->moxtet->dev), ret); + +done: + mutex_unlock(&add_mutex); + return ret; +} + +static int __unregister(struct device *dev, void *null) +{ + if (dev->of_node) { + of_node_clear_flag(dev->of_node, OF_POPULATED); + of_node_put(dev->of_node); + } + + device_unregister(dev); + + return 0; +} + +static struct moxtet_device * +of_register_moxtet_device(struct moxtet *moxtet, struct device_node *nc) +{ + struct moxtet_device *dev; + u32 val; + int ret; + + dev = moxtet_alloc_device(moxtet); + if (!dev) { + dev_err(moxtet->dev, + "Moxtet device alloc error for %pOF\n", nc); + return ERR_PTR(-ENOMEM); + } + + ret = of_property_read_u32(nc, "reg", &val); + if (ret) { + dev_err(moxtet->dev, "%pOF has no valid 'reg' property (%d)\n", + nc, ret); + goto err_put; + } + + dev->idx = val; + + if (dev->idx >= TURRIS_MOX_MAX_MODULES) { + dev_err(moxtet->dev, "%pOF Moxtet address 0x%x out of range\n", + nc, dev->idx); + ret = -EINVAL; + goto err_put; + } + + dev->id = moxtet->modules[dev->idx]; + + if (!dev->id) { + dev_err(moxtet->dev, "%pOF Moxtet address 0x%x is empty\n", nc, + dev->idx); + ret = -ENODEV; + goto err_put; + } + + of_node_get(nc); + dev->dev.of_node = nc; + + ret = moxtet_add_device(dev); + if (ret) { + dev_err(moxtet->dev, + "Moxtet device register error for %pOF\n", nc); + of_node_put(nc); + goto err_put; + } + + return dev; + +err_put: + put_device(&dev->dev); + return ERR_PTR(ret); +} + +static void of_register_moxtet_devices(struct moxtet *moxtet) +{ + struct moxtet_device *dev; + struct device_node *nc; + + if (!moxtet->dev->of_node) + return; + + for_each_available_child_of_node(moxtet->dev->of_node, nc) { + if (of_node_test_and_set_flag(nc, OF_POPULATED)) + continue; + dev = of_register_moxtet_device(moxtet, nc); + if (IS_ERR(dev)) { + dev_warn(moxtet->dev, + "Failed to create Moxtet device for %pOF\n", + nc); + of_node_clear_flag(nc, OF_POPULATED); + } + } +} + +static void +moxtet_register_devices_from_topology(struct moxtet *moxtet) +{ + struct moxtet_device *dev; + int i, ret; + + for (i = 0; i < moxtet->count; ++i) { + dev = moxtet_alloc_device(moxtet); + if (!dev) { + dev_err(moxtet->dev, "Moxtet device %u alloc error\n", + i); + continue; + } + + dev->idx = i; + dev->id = moxtet->modules[i]; + + ret = moxtet_add_device(dev); + if (ret && ret != -EBUSY) { + put_device(&dev->dev); + dev_err(moxtet->dev, + "Moxtet device %u register error: %i\n", i, + ret); + } + } +} + +/* + * @nsame: how many modules with same id are already in moxtet->modules + */ +static int moxtet_set_irq(struct moxtet *moxtet, int idx, int id, int nsame) +{ + int i, first; + struct moxtet_irqpos *pos; + + first = mox_module_table[id].hwirq_base + + nsame * mox_module_table[id].nirqs; + + if (first + mox_module_table[id].nirqs > MOXTET_NIRQS) + return -EINVAL; + + for (i = 0; i < mox_module_table[id].nirqs; ++i) { + pos = &moxtet->irq.position[first + i]; + pos->idx = idx; + pos->bit = i; + moxtet->irq.exists |= BIT(first + i); + } + + return 0; +} + +static int moxtet_find_topology(struct moxtet *moxtet) +{ + u8 buf[TURRIS_MOX_MAX_MODULES]; + int cnts[TURRIS_MOX_MODULE_LAST]; + int i, ret; + + memset(cnts, 0, sizeof(cnts)); + + ret = spi_read(to_spi_device(moxtet->dev), buf, TURRIS_MOX_MAX_MODULES); + if (ret < 0) + return ret; + + if (buf[0] == TURRIS_MOX_CPU_ID_EMMC) { + dev_info(moxtet->dev, "Found MOX A (eMMC CPU) module\n"); + } else if (buf[0] == TURRIS_MOX_CPU_ID_SD) { + dev_info(moxtet->dev, "Found MOX A (CPU) module\n"); + } else { + dev_err(moxtet->dev, "Invalid Turris MOX A CPU module 0x%02x\n", + buf[0]); + return -ENODEV; + } + + moxtet->count = 0; + + for (i = 1; i < TURRIS_MOX_MAX_MODULES; ++i) { + int id; + + if (buf[i] == 0xff) + break; + + id = buf[i] & 0xf; + + moxtet->modules[i-1] = id; + ++moxtet->count; + + if (mox_module_known(id)) { + dev_info(moxtet->dev, "Found %s module\n", + mox_module_table[id].desc); + + if (moxtet_set_irq(moxtet, i-1, id, cnts[id]++) < 0) + dev_err(moxtet->dev, + " Cannot set IRQ for module %s\n", + mox_module_table[id].desc); + } else { + dev_warn(moxtet->dev, + "Unknown Moxtet module found (ID 0x%02x)\n", + id); + } + } + + return 0; +} + +static int moxtet_spi_read(struct moxtet *moxtet, u8 *buf) +{ + struct spi_transfer xfer = { + .rx_buf = buf, + .tx_buf = moxtet->tx, + .len = moxtet->count + 1 + }; + int ret; + + mutex_lock(&moxtet->lock); + + ret = spi_sync_transfer(to_spi_device(moxtet->dev), &xfer, 1); + + mutex_unlock(&moxtet->lock); + + return ret; +} + +int moxtet_device_read(struct device *dev) +{ + struct moxtet_device *mdev = to_moxtet_device(dev); + struct moxtet *moxtet = mdev->moxtet; + u8 buf[TURRIS_MOX_MAX_MODULES]; + int ret; + + if (mdev->idx >= moxtet->count) + return -EINVAL; + + ret = moxtet_spi_read(moxtet, buf); + if (ret < 0) + return ret; + + return buf[mdev->idx + 1] >> 4; +} +EXPORT_SYMBOL_GPL(moxtet_device_read); + +int moxtet_device_write(struct device *dev, u8 val) +{ + struct moxtet_device *mdev = to_moxtet_device(dev); + struct moxtet *moxtet = mdev->moxtet; + int ret; + + if (mdev->idx >= moxtet->count) + return -EINVAL; + + mutex_lock(&moxtet->lock); + + moxtet->tx[moxtet->count - mdev->idx] = val; + + ret = spi_write(to_spi_device(moxtet->dev), moxtet->tx, + moxtet->count + 1); + + mutex_unlock(&moxtet->lock); + + return ret; +} +EXPORT_SYMBOL_GPL(moxtet_device_write); + +int moxtet_device_written(struct device *dev) +{ + struct moxtet_device *mdev = to_moxtet_device(dev); + struct moxtet *moxtet = mdev->moxtet; + + if (mdev->idx >= moxtet->count) + return -EINVAL; + + return moxtet->tx[moxtet->count - mdev->idx]; +} +EXPORT_SYMBOL_GPL(moxtet_device_written); + +#ifdef CONFIG_DEBUG_FS +static int moxtet_debug_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + + return nonseekable_open(inode, file); +} + +static ssize_t input_read(struct file *file, char __user *buf, size_t len, + loff_t *ppos) +{ + struct moxtet *moxtet = file->private_data; + u8 bin[TURRIS_MOX_MAX_MODULES]; + u8 hex[sizeof(buf) * 2 + 1]; + int ret, n; + + ret = moxtet_spi_read(moxtet, bin); + if (ret < 0) + return ret; + + n = moxtet->count + 1; + bin2hex(hex, bin, n); + + hex[2*n] = '\n'; + + return simple_read_from_buffer(buf, len, ppos, hex, 2*n + 1); +} + +static const struct file_operations input_fops = { + .owner = THIS_MODULE, + .open = moxtet_debug_open, + .read = input_read, + .llseek = no_llseek, +}; + +static ssize_t output_read(struct file *file, char __user *buf, size_t len, + loff_t *ppos) +{ + struct moxtet *moxtet = file->private_data; + u8 hex[TURRIS_MOX_MAX_MODULES * 2 + 1]; + u8 *p = hex; + int i; + + mutex_lock(&moxtet->lock); + + for (i = 0; i < moxtet->count; ++i) + p = hex_byte_pack(p, moxtet->tx[moxtet->count - i]); + + mutex_unlock(&moxtet->lock); + + *p++ = '\n'; + + return simple_read_from_buffer(buf, len, ppos, hex, p - hex); +} + +static ssize_t output_write(struct file *file, const char __user *buf, + size_t len, loff_t *ppos) +{ + struct moxtet *moxtet = file->private_data; + u8 bin[TURRIS_MOX_MAX_MODULES]; + u8 hex[sizeof(bin) * 2 + 1]; + ssize_t res; + loff_t dummy = 0; + int err, i; + + if (len > 2 * moxtet->count + 1 || len < 2 * moxtet->count) + return -EINVAL; + + res = simple_write_to_buffer(hex, sizeof(hex), &dummy, buf, len); + if (res < 0) + return res; + + if (len % 2 == 1 && hex[len - 1] != '\n') + return -EINVAL; + + err = hex2bin(bin, hex, moxtet->count); + if (err < 0) + return -EINVAL; + + mutex_lock(&moxtet->lock); + + for (i = 0; i < moxtet->count; ++i) + moxtet->tx[moxtet->count - i] = bin[i]; + + err = spi_write(to_spi_device(moxtet->dev), moxtet->tx, + moxtet->count + 1); + + mutex_unlock(&moxtet->lock); + + return err < 0 ? err : len; +} + +static const struct file_operations output_fops = { + .owner = THIS_MODULE, + .open = moxtet_debug_open, + .read = output_read, + .write = output_write, + .llseek = no_llseek, +}; + +static int moxtet_register_debugfs(struct moxtet *moxtet) +{ + struct dentry *root, *entry; + + root = debugfs_create_dir("moxtet", NULL); + + if (IS_ERR(root)) + return PTR_ERR(root); + + entry = debugfs_create_file_unsafe("input", 0444, root, moxtet, + &input_fops); + if (IS_ERR(entry)) + goto err_remove; + + entry = debugfs_create_file_unsafe("output", 0644, root, moxtet, + &output_fops); + if (IS_ERR(entry)) + goto err_remove; + + moxtet->debugfs_root = root; + + return 0; +err_remove: + debugfs_remove_recursive(root); + return PTR_ERR(entry); +} + +static void moxtet_unregister_debugfs(struct moxtet *moxtet) +{ + debugfs_remove_recursive(moxtet->debugfs_root); +} +#else +static inline int moxtet_register_debugfs(struct moxtet *moxtet) +{ + return 0; +} + +static inline void moxtet_unregister_debugfs(struct moxtet *moxtet) +{ +} +#endif + +static int moxtet_irq_domain_map(struct irq_domain *d, unsigned int irq, + irq_hw_number_t hw) +{ + struct moxtet *moxtet = d->host_data; + + if (hw >= MOXTET_NIRQS || !(moxtet->irq.exists & BIT(hw))) { + dev_err(moxtet->dev, "Invalid hw irq number\n"); + return -EINVAL; + } + + irq_set_chip_data(irq, d->host_data); + irq_set_chip_and_handler(irq, &moxtet->irq.chip, handle_level_irq); + + return 0; +} + +static int moxtet_irq_domain_xlate(struct irq_domain *d, + struct device_node *ctrlr, + const u32 *intspec, unsigned int intsize, + unsigned long *out_hwirq, + unsigned int *out_type) +{ + struct moxtet *moxtet = d->host_data; + int irq; + + if (WARN_ON(intsize < 1)) + return -EINVAL; + + irq = intspec[0]; + + if (irq >= MOXTET_NIRQS || !(moxtet->irq.exists & BIT(irq))) + return -EINVAL; + + *out_hwirq = irq; + *out_type = IRQ_TYPE_NONE; + return 0; +} + +static const struct irq_domain_ops moxtet_irq_domain = { + .map = moxtet_irq_domain_map, + .xlate = moxtet_irq_domain_xlate, +}; + +static void moxtet_irq_mask(struct irq_data *d) +{ + struct moxtet *moxtet = irq_data_get_irq_chip_data(d); + + moxtet->irq.masked |= BIT(d->hwirq); +} + +static void moxtet_irq_unmask(struct irq_data *d) +{ + struct moxtet *moxtet = irq_data_get_irq_chip_data(d); + + moxtet->irq.masked &= ~BIT(d->hwirq); +} + +static void moxtet_irq_print_chip(struct irq_data *d, struct seq_file *p) +{ + struct moxtet *moxtet = irq_data_get_irq_chip_data(d); + struct moxtet_irqpos *pos = &moxtet->irq.position[d->hwirq]; + int id; + + id = moxtet->modules[pos->idx]; + + seq_printf(p, " moxtet-%s.%i#%i", mox_module_name(id), pos->idx, + pos->bit); +} + +static const struct irq_chip moxtet_irq_chip = { + .name = "moxtet", + .irq_mask = moxtet_irq_mask, + .irq_unmask = moxtet_irq_unmask, + .irq_print_chip = moxtet_irq_print_chip, +}; + +static int moxtet_irq_read(struct moxtet *moxtet, unsigned long *map) +{ + struct moxtet_irqpos *pos = moxtet->irq.position; + u8 buf[TURRIS_MOX_MAX_MODULES]; + int i, ret; + + ret = moxtet_spi_read(moxtet, buf); + if (ret < 0) + return ret; + + *map = 0; + + for_each_set_bit(i, &moxtet->irq.exists, MOXTET_NIRQS) { + if (!(buf[pos[i].idx + 1] & BIT(4 + pos[i].bit))) + set_bit(i, map); + } + + return 0; +} + +static irqreturn_t moxtet_irq_thread_fn(int irq, void *data) +{ + struct moxtet *moxtet = data; + unsigned long set; + int nhandled = 0, i, sub_irq, ret; + + ret = moxtet_irq_read(moxtet, &set); + if (ret < 0) + goto out; + + set &= ~moxtet->irq.masked; + + do { + for_each_set_bit(i, &set, MOXTET_NIRQS) { + sub_irq = irq_find_mapping(moxtet->irq.domain, i); + handle_nested_irq(sub_irq); + dev_dbg(moxtet->dev, "%i irq\n", i); + ++nhandled; + } + + ret = moxtet_irq_read(moxtet, &set); + if (ret < 0) + goto out; + + set &= ~moxtet->irq.masked; + } while (set); + +out: + return (nhandled > 0 ? IRQ_HANDLED : IRQ_NONE); +} + +static void moxtet_irq_free(struct moxtet *moxtet) +{ + int i, irq; + + for (i = 0; i < MOXTET_NIRQS; ++i) { + if (moxtet->irq.exists & BIT(i)) { + irq = irq_find_mapping(moxtet->irq.domain, i); + irq_dispose_mapping(irq); + } + } + + irq_domain_remove(moxtet->irq.domain); +} + +static int moxtet_irq_setup(struct moxtet *moxtet) +{ + int i, ret; + + moxtet->irq.domain = irq_domain_add_simple(moxtet->dev->of_node, + MOXTET_NIRQS, 0, + &moxtet_irq_domain, moxtet); + if (moxtet->irq.domain == NULL) { + dev_err(moxtet->dev, "Could not add IRQ domain\n"); + return -ENOMEM; + } + + for (i = 0; i < MOXTET_NIRQS; ++i) + if (moxtet->irq.exists & BIT(i)) + irq_create_mapping(moxtet->irq.domain, i); + + moxtet->irq.chip = moxtet_irq_chip; + moxtet->irq.masked = ~0; + + ret = request_threaded_irq(moxtet->dev_irq, NULL, moxtet_irq_thread_fn, + IRQF_ONESHOT, "moxtet", moxtet); + if (ret < 0) + goto err_free; + + return 0; + +err_free: + moxtet_irq_free(moxtet); + return ret; +} + +static int moxtet_probe(struct spi_device *spi) +{ + struct moxtet *moxtet; + int ret; + + ret = spi_setup(spi); + if (ret < 0) + return ret; + + moxtet = devm_kzalloc(&spi->dev, sizeof(struct moxtet), + GFP_KERNEL); + if (!moxtet) + return -ENOMEM; + + moxtet->dev = &spi->dev; + spi_set_drvdata(spi, moxtet); + + mutex_init(&moxtet->lock); + + moxtet->dev_irq = of_irq_get(moxtet->dev->of_node, 0); + if (moxtet->dev_irq == -EPROBE_DEFER) + return -EPROBE_DEFER; + + if (moxtet->dev_irq <= 0) { + dev_err(moxtet->dev, "No IRQ resource found\n"); + return -ENXIO; + } + + ret = moxtet_find_topology(moxtet); + if (ret < 0) + return ret; + + if (moxtet->irq.exists) { + ret = moxtet_irq_setup(moxtet); + if (ret < 0) + return ret; + } + + of_register_moxtet_devices(moxtet); + moxtet_register_devices_from_topology(moxtet); + + ret = moxtet_register_debugfs(moxtet); + if (ret < 0) + dev_warn(moxtet->dev, "Failed creating debugfs entries: %i\n", + ret); + + return 0; +} + +static int moxtet_remove(struct spi_device *spi) +{ + struct moxtet *moxtet = spi_get_drvdata(spi); + + free_irq(moxtet->dev_irq, moxtet); + + moxtet_irq_free(moxtet); + + moxtet_unregister_debugfs(moxtet); + + device_for_each_child(moxtet->dev, NULL, __unregister); + + mutex_destroy(&moxtet->lock); + + return 0; +} + +static const struct of_device_id moxtet_dt_ids[] = { + { .compatible = "cznic,moxtet" }, + {}, +}; +MODULE_DEVICE_TABLE(of, moxtet_dt_ids); + +static struct spi_driver moxtet_spi_driver = { + .driver = { + .name = "moxtet", + .of_match_table = moxtet_dt_ids, + }, + .probe = moxtet_probe, + .remove = moxtet_remove, +}; + +static int __init moxtet_init(void) +{ + int ret; + + ret = bus_register(&moxtet_bus_type); + if (ret < 0) { + pr_err("moxtet bus registration failed: %d\n", ret); + goto error; + } + + ret = spi_register_driver(&moxtet_spi_driver); + if (ret < 0) { + pr_err("moxtet spi driver registration failed: %d\n", ret); + goto error_bus; + } + + return 0; + +error_bus: + bus_unregister(&moxtet_bus_type); +error: + return ret; +} +postcore_initcall_sync(moxtet_init); + +static void __exit moxtet_exit(void) +{ + spi_unregister_driver(&moxtet_spi_driver); + bus_unregister(&moxtet_bus_type); +} +module_exit(moxtet_exit); + +MODULE_AUTHOR("Marek Behun <marek.behun@nic.cz>"); +MODULE_DESCRIPTION("CZ.NIC's Turris Mox module configuration bus"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/bus/sunxi-rsb.c b/drivers/bus/sunxi-rsb.c index 1b76d9585902..be79d6c6a4e4 100644 --- a/drivers/bus/sunxi-rsb.c +++ b/drivers/bus/sunxi-rsb.c @@ -651,10 +651,8 @@ static int sunxi_rsb_probe(struct platform_device *pdev) return PTR_ERR(rsb->regs); irq = platform_get_irq(pdev, 0); - if (irq < 0) { - dev_err(dev, "failed to retrieve irq: %d\n", irq); + if (irq < 0) return irq; - } rsb->clk = devm_clk_get(dev, NULL); if (IS_ERR(rsb->clk)) { diff --git a/drivers/bus/uniphier-system-bus.c b/drivers/bus/uniphier-system-bus.c index e845c1a93f21..f70dedace20b 100644 --- a/drivers/bus/uniphier-system-bus.c +++ b/drivers/bus/uniphier-system-bus.c @@ -176,7 +176,6 @@ static int uniphier_system_bus_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct uniphier_system_bus_priv *priv; - struct resource *regs; const __be32 *ranges; u32 cells, addr, size; u64 paddr; @@ -186,8 +185,7 @@ static int uniphier_system_bus_probe(struct platform_device *pdev) if (!priv) return -ENOMEM; - regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); - priv->membase = devm_ioremap_resource(dev, regs); + priv->membase = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(priv->membase)) return PTR_ERR(priv->membase); diff --git a/drivers/clk/clk-scmi.c b/drivers/clk/clk-scmi.c index a2287c770d5c..886f7c5df51a 100644 --- a/drivers/clk/clk-scmi.c +++ b/drivers/clk/clk-scmi.c @@ -69,7 +69,7 @@ static int scmi_clk_set_rate(struct clk_hw *hw, unsigned long rate, { struct scmi_clk *clk = to_scmi_clk(hw); - return clk->handle->clk_ops->rate_set(clk->handle, clk->id, 0, rate); + return clk->handle->clk_ops->rate_set(clk->handle, clk->id, rate); } static int scmi_clk_enable(struct clk_hw *hw) diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig index ba8d3d0ef32c..c9a827bffe1c 100644 --- a/drivers/firmware/Kconfig +++ b/drivers/firmware/Kconfig @@ -271,6 +271,20 @@ config TRUSTED_FOUNDATIONS Choose N if you don't know what this is about. +config TURRIS_MOX_RWTM + tristate "Turris Mox rWTM secure firmware driver" + depends on ARCH_MVEBU || COMPILE_TEST + depends on HAS_DMA && OF + depends on MAILBOX + select HW_RANDOM + select ARMADA_37XX_RWTM_MBOX + help + This driver communicates with the firmware on the Cortex-M3 secure + processor of the Turris Mox router. Enable if you are building for + Turris Mox, and you will be able to read the device serial number and + other manufacturing data and also utilize the Entropy Bit Generator + for hardware random number generation. + config HAVE_ARM_SMCCC bool diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile index 3fa0b34eb72f..2b6e3a0be595 100644 --- a/drivers/firmware/Makefile +++ b/drivers/firmware/Makefile @@ -22,6 +22,7 @@ obj-$(CONFIG_QCOM_SCM_32) += qcom_scm-32.o CFLAGS_qcom_scm-32.o :=$(call as-instr,.arch armv7-a\n.arch_extension sec,-DREQUIRES_SEC=1) -march=armv7-a obj-$(CONFIG_TI_SCI_PROTOCOL) += ti_sci.o obj-$(CONFIG_TRUSTED_FOUNDATIONS) += trusted_foundations.o +obj-$(CONFIG_TURRIS_MOX_RWTM) += turris-mox-rwtm.o obj-$(CONFIG_ARM_SCMI_PROTOCOL) += arm_scmi/ obj-y += psci/ diff --git a/drivers/firmware/arm_scmi/Makefile b/drivers/firmware/arm_scmi/Makefile index c47d28d556b6..5f298f00a82e 100644 --- a/drivers/firmware/arm_scmi/Makefile +++ b/drivers/firmware/arm_scmi/Makefile @@ -2,5 +2,5 @@ obj-y = scmi-bus.o scmi-driver.o scmi-protocols.o scmi-bus-y = bus.o scmi-driver-y = driver.o -scmi-protocols-y = base.o clock.o perf.o power.o sensors.o +scmi-protocols-y = base.o clock.o perf.o power.o reset.o sensors.o obj-$(CONFIG_ARM_SCMI_POWER_DOMAIN) += scmi_pm_domain.o diff --git a/drivers/firmware/arm_scmi/base.c b/drivers/firmware/arm_scmi/base.c index 204390297f4b..f804e8af6521 100644 --- a/drivers/firmware/arm_scmi/base.c +++ b/drivers/firmware/arm_scmi/base.c @@ -204,7 +204,7 @@ static int scmi_base_discover_agent_get(const struct scmi_handle *handle, if (ret) return ret; - *(__le32 *)t->tx.buf = cpu_to_le32(id); + put_unaligned_le32(id, t->tx.buf); ret = scmi_do_xfer(handle, t); if (!ret) diff --git a/drivers/firmware/arm_scmi/clock.c b/drivers/firmware/arm_scmi/clock.c index 0a194af92438..32526a793f3a 100644 --- a/drivers/firmware/arm_scmi/clock.c +++ b/drivers/firmware/arm_scmi/clock.c @@ -56,7 +56,7 @@ struct scmi_msg_resp_clock_describe_rates { struct scmi_clock_set_rate { __le32 flags; #define CLOCK_SET_ASYNC BIT(0) -#define CLOCK_SET_DELAYED BIT(1) +#define CLOCK_SET_IGNORE_RESP BIT(1) #define CLOCK_SET_ROUND_UP BIT(2) #define CLOCK_SET_ROUND_AUTO BIT(3) __le32 id; @@ -67,6 +67,7 @@ struct scmi_clock_set_rate { struct clock_info { int num_clocks; int max_async_req; + atomic_t cur_async_req; struct scmi_clock_info *clk; }; @@ -106,7 +107,7 @@ static int scmi_clock_attributes_get(const struct scmi_handle *handle, if (ret) return ret; - *(__le32 *)t->tx.buf = cpu_to_le32(clk_id); + put_unaligned_le32(clk_id, t->tx.buf); attr = t->rx.buf; ret = scmi_do_xfer(handle, t); @@ -203,39 +204,47 @@ scmi_clock_rate_get(const struct scmi_handle *handle, u32 clk_id, u64 *value) if (ret) return ret; - *(__le32 *)t->tx.buf = cpu_to_le32(clk_id); + put_unaligned_le32(clk_id, t->tx.buf); ret = scmi_do_xfer(handle, t); - if (!ret) { - __le32 *pval = t->rx.buf; - - *value = le32_to_cpu(*pval); - *value |= (u64)le32_to_cpu(*(pval + 1)) << 32; - } + if (!ret) + *value = get_unaligned_le64(t->rx.buf); scmi_xfer_put(handle, t); return ret; } static int scmi_clock_rate_set(const struct scmi_handle *handle, u32 clk_id, - u32 config, u64 rate) + u64 rate) { int ret; + u32 flags = 0; struct scmi_xfer *t; struct scmi_clock_set_rate *cfg; + struct clock_info *ci = handle->clk_priv; ret = scmi_xfer_get_init(handle, CLOCK_RATE_SET, SCMI_PROTOCOL_CLOCK, sizeof(*cfg), 0, &t); if (ret) return ret; + if (ci->max_async_req && + atomic_inc_return(&ci->cur_async_req) < ci->max_async_req) + flags |= CLOCK_SET_ASYNC; + cfg = t->tx.buf; - cfg->flags = cpu_to_le32(config); + cfg->flags = cpu_to_le32(flags); cfg->id = cpu_to_le32(clk_id); cfg->value_low = cpu_to_le32(rate & 0xffffffff); cfg->value_high = cpu_to_le32(rate >> 32); - ret = scmi_do_xfer(handle, t); + if (flags & CLOCK_SET_ASYNC) + ret = scmi_do_xfer_with_response(handle, t); + else + ret = scmi_do_xfer(handle, t); + + if (ci->max_async_req) + atomic_dec(&ci->cur_async_req); scmi_xfer_put(handle, t); return ret; diff --git a/drivers/firmware/arm_scmi/common.h b/drivers/firmware/arm_scmi/common.h index 44fd4f9404a9..5237c2ff79fe 100644 --- a/drivers/firmware/arm_scmi/common.h +++ b/drivers/firmware/arm_scmi/common.h @@ -15,6 +15,8 @@ #include <linux/scmi_protocol.h> #include <linux/types.h> +#include <asm/unaligned.h> + #define PROTOCOL_REV_MINOR_MASK GENMASK(15, 0) #define PROTOCOL_REV_MAJOR_MASK GENMASK(31, 16) #define PROTOCOL_REV_MAJOR(x) (u16)(FIELD_GET(PROTOCOL_REV_MAJOR_MASK, (x))) @@ -48,11 +50,11 @@ struct scmi_msg_resp_prot_version { /** * struct scmi_msg_hdr - Message(Tx/Rx) header * - * @id: The identifier of the command being sent - * @protocol_id: The identifier of the protocol used to send @id command - * @seq: The token to identify the message. when a message/command returns, - * the platform returns the whole message header unmodified including - * the token + * @id: The identifier of the message being sent + * @protocol_id: The identifier of the protocol used to send @id message + * @seq: The token to identify the message. When a message returns, the + * platform returns the whole message header unmodified including the + * token * @status: Status of the transfer once it's complete * @poll_completion: Indicate if the transfer needs to be polled for * completion or interrupt mode is used @@ -84,17 +86,21 @@ struct scmi_msg { * @rx: Receive message, the buffer should be pre-allocated to store * message. If request-ACK protocol is used, we can reuse the same * buffer for the rx path as we use for the tx path. - * @done: completion event + * @done: command message transmit completion event + * @async: pointer to delayed response message received event completion */ struct scmi_xfer { struct scmi_msg_hdr hdr; struct scmi_msg tx; struct scmi_msg rx; struct completion done; + struct completion *async_done; }; void scmi_xfer_put(const struct scmi_handle *h, struct scmi_xfer *xfer); int scmi_do_xfer(const struct scmi_handle *h, struct scmi_xfer *xfer); +int scmi_do_xfer_with_response(const struct scmi_handle *h, + struct scmi_xfer *xfer); int scmi_xfer_get_init(const struct scmi_handle *h, u8 msg_id, u8 prot_id, size_t tx_size, size_t rx_size, struct scmi_xfer **p); int scmi_handle_put(const struct scmi_handle *handle); diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c index b5bc4c7a8fab..3eb0382491ce 100644 --- a/drivers/firmware/arm_scmi/driver.c +++ b/drivers/firmware/arm_scmi/driver.c @@ -30,8 +30,14 @@ #include "common.h" #define MSG_ID_MASK GENMASK(7, 0) +#define MSG_XTRACT_ID(hdr) FIELD_GET(MSG_ID_MASK, (hdr)) #define MSG_TYPE_MASK GENMASK(9, 8) +#define MSG_XTRACT_TYPE(hdr) FIELD_GET(MSG_TYPE_MASK, (hdr)) +#define MSG_TYPE_COMMAND 0 +#define MSG_TYPE_DELAYED_RESP 2 +#define MSG_TYPE_NOTIFICATION 3 #define MSG_PROTOCOL_ID_MASK GENMASK(17, 10) +#define MSG_XTRACT_PROT_ID(hdr) FIELD_GET(MSG_PROTOCOL_ID_MASK, (hdr)) #define MSG_TOKEN_ID_MASK GENMASK(27, 18) #define MSG_XTRACT_TOKEN(hdr) FIELD_GET(MSG_TOKEN_ID_MASK, (hdr)) #define MSG_TOKEN_MAX (MSG_XTRACT_TOKEN(MSG_TOKEN_ID_MASK) + 1) @@ -86,7 +92,7 @@ struct scmi_desc { }; /** - * struct scmi_chan_info - Structure representing a SCMI channel informfation + * struct scmi_chan_info - Structure representing a SCMI channel information * * @cl: Mailbox Client * @chan: Transmit/Receive mailbox channel @@ -111,8 +117,9 @@ struct scmi_chan_info { * @handle: Instance of SCMI handle to send to clients * @version: SCMI revision information containing protocol version, * implementation version and (sub-)vendor identification. - * @minfo: Message info - * @tx_idr: IDR object to map protocol id to channel info pointer + * @tx_minfo: Universal Transmit Message management info + * @tx_idr: IDR object to map protocol id to Tx channel info pointer + * @rx_idr: IDR object to map protocol id to Rx channel info pointer * @protocols_imp: List of protocols implemented, currently maximum of * MAX_PROTOCOLS_IMP elements allocated by the base protocol * @node: List head @@ -123,8 +130,9 @@ struct scmi_info { const struct scmi_desc *desc; struct scmi_revision_info version; struct scmi_handle handle; - struct scmi_xfers_info minfo; + struct scmi_xfers_info tx_minfo; struct idr tx_idr; + struct idr rx_idr; u8 *protocols_imp; struct list_head node; int users; @@ -182,7 +190,7 @@ static inline int scmi_to_linux_errno(int errno) static inline void scmi_dump_header_dbg(struct device *dev, struct scmi_msg_hdr *hdr) { - dev_dbg(dev, "Command ID: %x Sequence ID: %x Protocol: %x\n", + dev_dbg(dev, "Message ID: %x Sequence ID: %x Protocol: %x\n", hdr->id, hdr->seq, hdr->protocol_id); } @@ -190,7 +198,7 @@ static void scmi_fetch_response(struct scmi_xfer *xfer, struct scmi_shared_mem __iomem *mem) { xfer->hdr.status = ioread32(mem->msg_payload); - /* Skip the length of header and statues in payload area i.e 8 bytes*/ + /* Skip the length of header and status in payload area i.e 8 bytes */ xfer->rx.len = min_t(size_t, xfer->rx.len, ioread32(&mem->length) - 8); /* Take a copy to the rx buffer.. */ @@ -198,56 +206,12 @@ static void scmi_fetch_response(struct scmi_xfer *xfer, } /** - * scmi_rx_callback() - mailbox client callback for receive messages - * - * @cl: client pointer - * @m: mailbox message - * - * Processes one received message to appropriate transfer information and - * signals completion of the transfer. - * - * NOTE: This function will be invoked in IRQ context, hence should be - * as optimal as possible. - */ -static void scmi_rx_callback(struct mbox_client *cl, void *m) -{ - u16 xfer_id; - struct scmi_xfer *xfer; - struct scmi_chan_info *cinfo = client_to_scmi_chan_info(cl); - struct device *dev = cinfo->dev; - struct scmi_info *info = handle_to_scmi_info(cinfo->handle); - struct scmi_xfers_info *minfo = &info->minfo; - struct scmi_shared_mem __iomem *mem = cinfo->payload; - - xfer_id = MSG_XTRACT_TOKEN(ioread32(&mem->msg_header)); - - /* Are we even expecting this? */ - if (!test_bit(xfer_id, minfo->xfer_alloc_table)) { - dev_err(dev, "message for %d is not expected!\n", xfer_id); - return; - } - - xfer = &minfo->xfer_block[xfer_id]; - - scmi_dump_header_dbg(dev, &xfer->hdr); - /* Is the message of valid length? */ - if (xfer->rx.len > info->desc->max_msg_size) { - dev_err(dev, "unable to handle %zu xfer(max %d)\n", - xfer->rx.len, info->desc->max_msg_size); - return; - } - - scmi_fetch_response(xfer, mem); - complete(&xfer->done); -} - -/** * pack_scmi_header() - packs and returns 32-bit header * * @hdr: pointer to header containing all the information on message id, * protocol id and sequence id. * - * Return: 32-bit packed command header to be sent to the platform. + * Return: 32-bit packed message header to be sent to the platform. */ static inline u32 pack_scmi_header(struct scmi_msg_hdr *hdr) { @@ -257,6 +221,18 @@ static inline u32 pack_scmi_header(struct scmi_msg_hdr *hdr) } /** + * unpack_scmi_header() - unpacks and records message and protocol id + * + * @msg_hdr: 32-bit packed message header sent from the platform + * @hdr: pointer to header to fetch message and protocol id. + */ +static inline void unpack_scmi_header(u32 msg_hdr, struct scmi_msg_hdr *hdr) +{ + hdr->id = MSG_XTRACT_ID(msg_hdr); + hdr->protocol_id = MSG_XTRACT_PROT_ID(msg_hdr); +} + +/** * scmi_tx_prepare() - mailbox client callback to prepare for the transfer * * @cl: client pointer @@ -271,6 +247,14 @@ static void scmi_tx_prepare(struct mbox_client *cl, void *m) struct scmi_chan_info *cinfo = client_to_scmi_chan_info(cl); struct scmi_shared_mem __iomem *mem = cinfo->payload; + /* + * Ideally channel must be free by now unless OS timeout last + * request and platform continued to process the same, wait + * until it releases the shared memory, otherwise we may endup + * overwriting its response with new message payload or vice-versa + */ + spin_until_cond(ioread32(&mem->channel_status) & + SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE); /* Mark channel busy + clear error */ iowrite32(0x0, &mem->channel_status); iowrite32(t->hdr.poll_completion ? 0 : SCMI_SHMEM_FLAG_INTR_ENABLED, @@ -285,8 +269,9 @@ static void scmi_tx_prepare(struct mbox_client *cl, void *m) * scmi_xfer_get() - Allocate one message * * @handle: Pointer to SCMI entity handle + * @minfo: Pointer to Tx/Rx Message management info based on channel type * - * Helper function which is used by various command functions that are + * Helper function which is used by various message functions that are * exposed to clients of this driver for allocating a message traffic event. * * This function can sleep depending on pending requests already in the system @@ -295,13 +280,13 @@ static void scmi_tx_prepare(struct mbox_client *cl, void *m) * * Return: 0 if all went fine, else corresponding error. */ -static struct scmi_xfer *scmi_xfer_get(const struct scmi_handle *handle) +static struct scmi_xfer *scmi_xfer_get(const struct scmi_handle *handle, + struct scmi_xfers_info *minfo) { u16 xfer_id; struct scmi_xfer *xfer; unsigned long flags, bit_pos; struct scmi_info *info = handle_to_scmi_info(handle); - struct scmi_xfers_info *minfo = &info->minfo; /* Keep the locked section as small as possible */ spin_lock_irqsave(&minfo->xfer_lock, flags); @@ -324,18 +309,17 @@ static struct scmi_xfer *scmi_xfer_get(const struct scmi_handle *handle) } /** - * scmi_xfer_put() - Release a message + * __scmi_xfer_put() - Release a message * - * @handle: Pointer to SCMI entity handle + * @minfo: Pointer to Tx/Rx Message management info based on channel type * @xfer: message that was reserved by scmi_xfer_get * * This holds a spinlock to maintain integrity of internal data structures. */ -void scmi_xfer_put(const struct scmi_handle *handle, struct scmi_xfer *xfer) +static void +__scmi_xfer_put(struct scmi_xfers_info *minfo, struct scmi_xfer *xfer) { unsigned long flags; - struct scmi_info *info = handle_to_scmi_info(handle); - struct scmi_xfers_info *minfo = &info->minfo; /* * Keep the locked section as small as possible @@ -347,6 +331,68 @@ void scmi_xfer_put(const struct scmi_handle *handle, struct scmi_xfer *xfer) spin_unlock_irqrestore(&minfo->xfer_lock, flags); } +/** + * scmi_rx_callback() - mailbox client callback for receive messages + * + * @cl: client pointer + * @m: mailbox message + * + * Processes one received message to appropriate transfer information and + * signals completion of the transfer. + * + * NOTE: This function will be invoked in IRQ context, hence should be + * as optimal as possible. + */ +static void scmi_rx_callback(struct mbox_client *cl, void *m) +{ + u8 msg_type; + u32 msg_hdr; + u16 xfer_id; + struct scmi_xfer *xfer; + struct scmi_chan_info *cinfo = client_to_scmi_chan_info(cl); + struct device *dev = cinfo->dev; + struct scmi_info *info = handle_to_scmi_info(cinfo->handle); + struct scmi_xfers_info *minfo = &info->tx_minfo; + struct scmi_shared_mem __iomem *mem = cinfo->payload; + + msg_hdr = ioread32(&mem->msg_header); + msg_type = MSG_XTRACT_TYPE(msg_hdr); + xfer_id = MSG_XTRACT_TOKEN(msg_hdr); + + if (msg_type == MSG_TYPE_NOTIFICATION) + return; /* Notifications not yet supported */ + + /* Are we even expecting this? */ + if (!test_bit(xfer_id, minfo->xfer_alloc_table)) { + dev_err(dev, "message for %d is not expected!\n", xfer_id); + return; + } + + xfer = &minfo->xfer_block[xfer_id]; + + scmi_dump_header_dbg(dev, &xfer->hdr); + + scmi_fetch_response(xfer, mem); + + if (msg_type == MSG_TYPE_DELAYED_RESP) + complete(xfer->async_done); + else + complete(&xfer->done); +} + +/** + * scmi_xfer_put() - Release a transmit message + * + * @handle: Pointer to SCMI entity handle + * @xfer: message that was reserved by scmi_xfer_get + */ +void scmi_xfer_put(const struct scmi_handle *handle, struct scmi_xfer *xfer) +{ + struct scmi_info *info = handle_to_scmi_info(handle); + + __scmi_xfer_put(&info->tx_minfo, xfer); +} + static bool scmi_xfer_poll_done(const struct scmi_chan_info *cinfo, struct scmi_xfer *xfer) { @@ -435,8 +481,36 @@ int scmi_do_xfer(const struct scmi_handle *handle, struct scmi_xfer *xfer) return ret; } +#define SCMI_MAX_RESPONSE_TIMEOUT (2 * MSEC_PER_SEC) + +/** + * scmi_do_xfer_with_response() - Do one transfer and wait until the delayed + * response is received + * + * @handle: Pointer to SCMI entity handle + * @xfer: Transfer to initiate and wait for response + * + * Return: -ETIMEDOUT in case of no delayed response, if transmit error, + * return corresponding error, else if all goes well, return 0. + */ +int scmi_do_xfer_with_response(const struct scmi_handle *handle, + struct scmi_xfer *xfer) +{ + int ret, timeout = msecs_to_jiffies(SCMI_MAX_RESPONSE_TIMEOUT); + DECLARE_COMPLETION_ONSTACK(async_response); + + xfer->async_done = &async_response; + + ret = scmi_do_xfer(handle, xfer); + if (!ret && !wait_for_completion_timeout(xfer->async_done, timeout)) + ret = -ETIMEDOUT; + + xfer->async_done = NULL; + return ret; +} + /** - * scmi_xfer_get_init() - Allocate and initialise one message + * scmi_xfer_get_init() - Allocate and initialise one message for transmit * * @handle: Pointer to SCMI entity handle * @msg_id: Message identifier @@ -457,6 +531,7 @@ int scmi_xfer_get_init(const struct scmi_handle *handle, u8 msg_id, u8 prot_id, int ret; struct scmi_xfer *xfer; struct scmi_info *info = handle_to_scmi_info(handle); + struct scmi_xfers_info *minfo = &info->tx_minfo; struct device *dev = info->dev; /* Ensure we have sane transfer sizes */ @@ -464,7 +539,7 @@ int scmi_xfer_get_init(const struct scmi_handle *handle, u8 msg_id, u8 prot_id, tx_size > info->desc->max_msg_size) return -ERANGE; - xfer = scmi_xfer_get(handle); + xfer = scmi_xfer_get(handle, minfo); if (IS_ERR(xfer)) { ret = PTR_ERR(xfer); dev_err(dev, "failed to get free message slot(%d)\n", ret); @@ -597,27 +672,13 @@ int scmi_handle_put(const struct scmi_handle *handle) return 0; } -static const struct scmi_desc scmi_generic_desc = { - .max_rx_timeout_ms = 30, /* We may increase this if required */ - .max_msg = 20, /* Limited by MBOX_TX_QUEUE_LEN */ - .max_msg_size = 128, -}; - -/* Each compatible listed below must have descriptor associated with it */ -static const struct of_device_id scmi_of_match[] = { - { .compatible = "arm,scmi", .data = &scmi_generic_desc }, - { /* Sentinel */ }, -}; - -MODULE_DEVICE_TABLE(of, scmi_of_match); - static int scmi_xfer_info_init(struct scmi_info *sinfo) { int i; struct scmi_xfer *xfer; struct device *dev = sinfo->dev; const struct scmi_desc *desc = sinfo->desc; - struct scmi_xfers_info *info = &sinfo->minfo; + struct scmi_xfers_info *info = &sinfo->tx_minfo; /* Pre-allocated messages, no more than what hdr.seq can support */ if (WARN_ON(desc->max_msg >= MSG_TOKEN_MAX)) { @@ -652,61 +713,32 @@ static int scmi_xfer_info_init(struct scmi_info *sinfo) return 0; } -static int scmi_mailbox_check(struct device_node *np) +static int scmi_mailbox_check(struct device_node *np, int idx) { - return of_parse_phandle_with_args(np, "mboxes", "#mbox-cells", 0, NULL); + return of_parse_phandle_with_args(np, "mboxes", "#mbox-cells", + idx, NULL); } -static int scmi_mbox_free_channel(int id, void *p, void *data) +static int scmi_mbox_chan_setup(struct scmi_info *info, struct device *dev, + int prot_id, bool tx) { - struct scmi_chan_info *cinfo = p; - struct idr *idr = data; - - if (!IS_ERR_OR_NULL(cinfo->chan)) { - mbox_free_channel(cinfo->chan); - cinfo->chan = NULL; - } - - idr_remove(idr, id); - - return 0; -} - -static int scmi_remove(struct platform_device *pdev) -{ - int ret = 0; - struct scmi_info *info = platform_get_drvdata(pdev); - struct idr *idr = &info->tx_idr; - - mutex_lock(&scmi_list_mutex); - if (info->users) - ret = -EBUSY; - else - list_del(&info->node); - mutex_unlock(&scmi_list_mutex); - - if (ret) - return ret; - - /* Safe to free channels since no more users */ - ret = idr_for_each(idr, scmi_mbox_free_channel, idr); - idr_destroy(&info->tx_idr); - - return ret; -} - -static inline int -scmi_mbox_chan_setup(struct scmi_info *info, struct device *dev, int prot_id) -{ - int ret; + int ret, idx; struct resource res; resource_size_t size; struct device_node *shmem, *np = dev->of_node; struct scmi_chan_info *cinfo; struct mbox_client *cl; + struct idr *idr; + const char *desc = tx ? "Tx" : "Rx"; - if (scmi_mailbox_check(np)) { - cinfo = idr_find(&info->tx_idr, SCMI_PROTOCOL_BASE); + /* Transmit channel is first entry i.e. index 0 */ + idx = tx ? 0 : 1; + idr = tx ? &info->tx_idr : &info->rx_idr; + + if (scmi_mailbox_check(np, idx)) { + cinfo = idr_find(idr, SCMI_PROTOCOL_BASE); + if (unlikely(!cinfo)) /* Possible only if platform has no Rx */ + return -EINVAL; goto idr_alloc; } @@ -719,36 +751,36 @@ scmi_mbox_chan_setup(struct scmi_info *info, struct device *dev, int prot_id) cl = &cinfo->cl; cl->dev = dev; cl->rx_callback = scmi_rx_callback; - cl->tx_prepare = scmi_tx_prepare; + cl->tx_prepare = tx ? scmi_tx_prepare : NULL; cl->tx_block = false; - cl->knows_txdone = true; + cl->knows_txdone = tx; - shmem = of_parse_phandle(np, "shmem", 0); + shmem = of_parse_phandle(np, "shmem", idx); ret = of_address_to_resource(shmem, 0, &res); of_node_put(shmem); if (ret) { - dev_err(dev, "failed to get SCMI Tx payload mem resource\n"); + dev_err(dev, "failed to get SCMI %s payload memory\n", desc); return ret; } size = resource_size(&res); cinfo->payload = devm_ioremap(info->dev, res.start, size); if (!cinfo->payload) { - dev_err(dev, "failed to ioremap SCMI Tx payload\n"); + dev_err(dev, "failed to ioremap SCMI %s payload\n", desc); return -EADDRNOTAVAIL; } - /* Transmit channel is first entry i.e. index 0 */ - cinfo->chan = mbox_request_channel(cl, 0); + cinfo->chan = mbox_request_channel(cl, idx); if (IS_ERR(cinfo->chan)) { ret = PTR_ERR(cinfo->chan); if (ret != -EPROBE_DEFER) - dev_err(dev, "failed to request SCMI Tx mailbox\n"); + dev_err(dev, "failed to request SCMI %s mailbox\n", + desc); return ret; } idr_alloc: - ret = idr_alloc(&info->tx_idr, cinfo, prot_id, prot_id + 1, GFP_KERNEL); + ret = idr_alloc(idr, cinfo, prot_id, prot_id + 1, GFP_KERNEL); if (ret != prot_id) { dev_err(dev, "unable to allocate SCMI idr slot err %d\n", ret); return ret; @@ -758,6 +790,17 @@ idr_alloc: return 0; } +static inline int +scmi_mbox_txrx_setup(struct scmi_info *info, struct device *dev, int prot_id) +{ + int ret = scmi_mbox_chan_setup(info, dev, prot_id, true); + + if (!ret) /* Rx is optional, hence no error check */ + scmi_mbox_chan_setup(info, dev, prot_id, false); + + return ret; +} + static inline void scmi_create_protocol_device(struct device_node *np, struct scmi_info *info, int prot_id) @@ -771,7 +814,7 @@ scmi_create_protocol_device(struct device_node *np, struct scmi_info *info, return; } - if (scmi_mbox_chan_setup(info, &sdev->dev, prot_id)) { + if (scmi_mbox_txrx_setup(info, &sdev->dev, prot_id)) { dev_err(&sdev->dev, "failed to setup transport\n"); scmi_device_destroy(sdev); return; @@ -791,7 +834,7 @@ static int scmi_probe(struct platform_device *pdev) struct device_node *child, *np = dev->of_node; /* Only mailbox method supported, check for the presence of one */ - if (scmi_mailbox_check(np)) { + if (scmi_mailbox_check(np, 0)) { dev_err(dev, "no mailbox found in %pOF\n", np); return -EINVAL; } @@ -814,12 +857,13 @@ static int scmi_probe(struct platform_device *pdev) platform_set_drvdata(pdev, info); idr_init(&info->tx_idr); + idr_init(&info->rx_idr); handle = &info->handle; handle->dev = info->dev; handle->version = &info->version; - ret = scmi_mbox_chan_setup(info, dev, SCMI_PROTOCOL_BASE); + ret = scmi_mbox_txrx_setup(info, dev, SCMI_PROTOCOL_BASE); if (ret) return ret; @@ -854,6 +898,62 @@ static int scmi_probe(struct platform_device *pdev) return 0; } +static int scmi_mbox_free_channel(int id, void *p, void *data) +{ + struct scmi_chan_info *cinfo = p; + struct idr *idr = data; + + if (!IS_ERR_OR_NULL(cinfo->chan)) { + mbox_free_channel(cinfo->chan); + cinfo->chan = NULL; + } + + idr_remove(idr, id); + + return 0; +} + +static int scmi_remove(struct platform_device *pdev) +{ + int ret = 0; + struct scmi_info *info = platform_get_drvdata(pdev); + struct idr *idr = &info->tx_idr; + + mutex_lock(&scmi_list_mutex); + if (info->users) + ret = -EBUSY; + else + list_del(&info->node); + mutex_unlock(&scmi_list_mutex); + + if (ret) + return ret; + + /* Safe to free channels since no more users */ + ret = idr_for_each(idr, scmi_mbox_free_channel, idr); + idr_destroy(&info->tx_idr); + + idr = &info->rx_idr; + ret = idr_for_each(idr, scmi_mbox_free_channel, idr); + idr_destroy(&info->rx_idr); + + return ret; +} + +static const struct scmi_desc scmi_generic_desc = { + .max_rx_timeout_ms = 30, /* We may increase this if required */ + .max_msg = 20, /* Limited by MBOX_TX_QUEUE_LEN */ + .max_msg_size = 128, +}; + +/* Each compatible listed below must have descriptor associated with it */ +static const struct of_device_id scmi_of_match[] = { + { .compatible = "arm,scmi", .data = &scmi_generic_desc }, + { /* Sentinel */ }, +}; + +MODULE_DEVICE_TABLE(of, scmi_of_match); + static struct platform_driver scmi_driver = { .driver = { .name = "arm-scmi", diff --git a/drivers/firmware/arm_scmi/perf.c b/drivers/firmware/arm_scmi/perf.c index 3c8ae7cc35de..4a8012e3cb8c 100644 --- a/drivers/firmware/arm_scmi/perf.c +++ b/drivers/firmware/arm_scmi/perf.c @@ -5,7 +5,10 @@ * Copyright (C) 2018 ARM Ltd. */ +#include <linux/bits.h> #include <linux/of.h> +#include <linux/io.h> +#include <linux/io-64-nonatomic-hi-lo.h> #include <linux/platform_device.h> #include <linux/pm_opp.h> #include <linux/sort.h> @@ -21,6 +24,7 @@ enum scmi_performance_protocol_cmd { PERF_LEVEL_GET = 0x8, PERF_NOTIFY_LIMITS = 0x9, PERF_NOTIFY_LEVEL = 0xa, + PERF_DESCRIBE_FASTCHANNEL = 0xb, }; struct scmi_opp { @@ -44,6 +48,7 @@ struct scmi_msg_resp_perf_domain_attributes { #define SUPPORTS_SET_PERF_LVL(x) ((x) & BIT(30)) #define SUPPORTS_PERF_LIMIT_NOTIFY(x) ((x) & BIT(29)) #define SUPPORTS_PERF_LEVEL_NOTIFY(x) ((x) & BIT(28)) +#define SUPPORTS_PERF_FASTCHANNELS(x) ((x) & BIT(27)) __le32 rate_limit_us; __le32 sustained_freq_khz; __le32 sustained_perf_level; @@ -87,17 +92,56 @@ struct scmi_msg_resp_perf_describe_levels { } opp[0]; }; +struct scmi_perf_get_fc_info { + __le32 domain; + __le32 message_id; +}; + +struct scmi_msg_resp_perf_desc_fc { + __le32 attr; +#define SUPPORTS_DOORBELL(x) ((x) & BIT(0)) +#define DOORBELL_REG_WIDTH(x) FIELD_GET(GENMASK(2, 1), (x)) + __le32 rate_limit; + __le32 chan_addr_low; + __le32 chan_addr_high; + __le32 chan_size; + __le32 db_addr_low; + __le32 db_addr_high; + __le32 db_set_lmask; + __le32 db_set_hmask; + __le32 db_preserve_lmask; + __le32 db_preserve_hmask; +}; + +struct scmi_fc_db_info { + int width; + u64 set; + u64 mask; + void __iomem *addr; +}; + +struct scmi_fc_info { + void __iomem *level_set_addr; + void __iomem *limit_set_addr; + void __iomem *level_get_addr; + void __iomem *limit_get_addr; + struct scmi_fc_db_info *level_set_db; + struct scmi_fc_db_info *limit_set_db; +}; + struct perf_dom_info { bool set_limits; bool set_perf; bool perf_limit_notify; bool perf_level_notify; + bool perf_fastchannels; u32 opp_count; u32 sustained_freq_khz; u32 sustained_perf_level; u32 mult_factor; char name[SCMI_MAX_STR_SIZE]; struct scmi_opp opp[MAX_OPPS]; + struct scmi_fc_info *fc_info; }; struct scmi_perf_info { @@ -151,7 +195,7 @@ scmi_perf_domain_attributes_get(const struct scmi_handle *handle, u32 domain, if (ret) return ret; - *(__le32 *)t->tx.buf = cpu_to_le32(domain); + put_unaligned_le32(domain, t->tx.buf); attr = t->rx.buf; ret = scmi_do_xfer(handle, t); @@ -162,6 +206,7 @@ scmi_perf_domain_attributes_get(const struct scmi_handle *handle, u32 domain, dom_info->set_perf = SUPPORTS_SET_PERF_LVL(flags); dom_info->perf_limit_notify = SUPPORTS_PERF_LIMIT_NOTIFY(flags); dom_info->perf_level_notify = SUPPORTS_PERF_LEVEL_NOTIFY(flags); + dom_info->perf_fastchannels = SUPPORTS_PERF_FASTCHANNELS(flags); dom_info->sustained_freq_khz = le32_to_cpu(attr->sustained_freq_khz); dom_info->sustained_perf_level = @@ -249,8 +294,42 @@ scmi_perf_describe_levels_get(const struct scmi_handle *handle, u32 domain, return ret; } -static int scmi_perf_limits_set(const struct scmi_handle *handle, u32 domain, - u32 max_perf, u32 min_perf) +#define SCMI_PERF_FC_RING_DB(w) \ +do { \ + u##w val = 0; \ + \ + if (db->mask) \ + val = ioread##w(db->addr) & db->mask; \ + iowrite##w((u##w)db->set | val, db->addr); \ +} while (0) + +static void scmi_perf_fc_ring_db(struct scmi_fc_db_info *db) +{ + if (!db || !db->addr) + return; + + if (db->width == 1) + SCMI_PERF_FC_RING_DB(8); + else if (db->width == 2) + SCMI_PERF_FC_RING_DB(16); + else if (db->width == 4) + SCMI_PERF_FC_RING_DB(32); + else /* db->width == 8 */ +#ifdef CONFIG_64BIT + SCMI_PERF_FC_RING_DB(64); +#else + { + u64 val = 0; + + if (db->mask) + val = ioread64_hi_lo(db->addr) & db->mask; + iowrite64_hi_lo(db->set, db->addr); + } +#endif +} + +static int scmi_perf_mb_limits_set(const struct scmi_handle *handle, u32 domain, + u32 max_perf, u32 min_perf) { int ret; struct scmi_xfer *t; @@ -272,8 +351,24 @@ static int scmi_perf_limits_set(const struct scmi_handle *handle, u32 domain, return ret; } -static int scmi_perf_limits_get(const struct scmi_handle *handle, u32 domain, - u32 *max_perf, u32 *min_perf) +static int scmi_perf_limits_set(const struct scmi_handle *handle, u32 domain, + u32 max_perf, u32 min_perf) +{ + struct scmi_perf_info *pi = handle->perf_priv; + struct perf_dom_info *dom = pi->dom_info + domain; + + if (dom->fc_info && dom->fc_info->limit_set_addr) { + iowrite32(max_perf, dom->fc_info->limit_set_addr); + iowrite32(min_perf, dom->fc_info->limit_set_addr + 4); + scmi_perf_fc_ring_db(dom->fc_info->limit_set_db); + return 0; + } + + return scmi_perf_mb_limits_set(handle, domain, max_perf, min_perf); +} + +static int scmi_perf_mb_limits_get(const struct scmi_handle *handle, u32 domain, + u32 *max_perf, u32 *min_perf) { int ret; struct scmi_xfer *t; @@ -284,7 +379,7 @@ static int scmi_perf_limits_get(const struct scmi_handle *handle, u32 domain, if (ret) return ret; - *(__le32 *)t->tx.buf = cpu_to_le32(domain); + put_unaligned_le32(domain, t->tx.buf); ret = scmi_do_xfer(handle, t); if (!ret) { @@ -298,8 +393,23 @@ static int scmi_perf_limits_get(const struct scmi_handle *handle, u32 domain, return ret; } -static int scmi_perf_level_set(const struct scmi_handle *handle, u32 domain, - u32 level, bool poll) +static int scmi_perf_limits_get(const struct scmi_handle *handle, u32 domain, + u32 *max_perf, u32 *min_perf) +{ + struct scmi_perf_info *pi = handle->perf_priv; + struct perf_dom_info *dom = pi->dom_info + domain; + + if (dom->fc_info && dom->fc_info->limit_get_addr) { + *max_perf = ioread32(dom->fc_info->limit_get_addr); + *min_perf = ioread32(dom->fc_info->limit_get_addr + 4); + return 0; + } + + return scmi_perf_mb_limits_get(handle, domain, max_perf, min_perf); +} + +static int scmi_perf_mb_level_set(const struct scmi_handle *handle, u32 domain, + u32 level, bool poll) { int ret; struct scmi_xfer *t; @@ -321,8 +431,23 @@ static int scmi_perf_level_set(const struct scmi_handle *handle, u32 domain, return ret; } -static int scmi_perf_level_get(const struct scmi_handle *handle, u32 domain, - u32 *level, bool poll) +static int scmi_perf_level_set(const struct scmi_handle *handle, u32 domain, + u32 level, bool poll) +{ + struct scmi_perf_info *pi = handle->perf_priv; + struct perf_dom_info *dom = pi->dom_info + domain; + + if (dom->fc_info && dom->fc_info->level_set_addr) { + iowrite32(level, dom->fc_info->level_set_addr); + scmi_perf_fc_ring_db(dom->fc_info->level_set_db); + return 0; + } + + return scmi_perf_mb_level_set(handle, domain, level, poll); +} + +static int scmi_perf_mb_level_get(const struct scmi_handle *handle, u32 domain, + u32 *level, bool poll) { int ret; struct scmi_xfer *t; @@ -333,16 +458,128 @@ static int scmi_perf_level_get(const struct scmi_handle *handle, u32 domain, return ret; t->hdr.poll_completion = poll; - *(__le32 *)t->tx.buf = cpu_to_le32(domain); + put_unaligned_le32(domain, t->tx.buf); ret = scmi_do_xfer(handle, t); if (!ret) - *level = le32_to_cpu(*(__le32 *)t->rx.buf); + *level = get_unaligned_le32(t->rx.buf); scmi_xfer_put(handle, t); return ret; } +static int scmi_perf_level_get(const struct scmi_handle *handle, u32 domain, + u32 *level, bool poll) +{ + struct scmi_perf_info *pi = handle->perf_priv; + struct perf_dom_info *dom = pi->dom_info + domain; + + if (dom->fc_info && dom->fc_info->level_get_addr) { + *level = ioread32(dom->fc_info->level_get_addr); + return 0; + } + + return scmi_perf_mb_level_get(handle, domain, level, poll); +} + +static bool scmi_perf_fc_size_is_valid(u32 msg, u32 size) +{ + if ((msg == PERF_LEVEL_GET || msg == PERF_LEVEL_SET) && size == 4) + return true; + if ((msg == PERF_LIMITS_GET || msg == PERF_LIMITS_SET) && size == 8) + return true; + return false; +} + +static void +scmi_perf_domain_desc_fc(const struct scmi_handle *handle, u32 domain, + u32 message_id, void __iomem **p_addr, + struct scmi_fc_db_info **p_db) +{ + int ret; + u32 flags; + u64 phys_addr; + u8 size; + void __iomem *addr; + struct scmi_xfer *t; + struct scmi_fc_db_info *db; + struct scmi_perf_get_fc_info *info; + struct scmi_msg_resp_perf_desc_fc *resp; + + if (!p_addr) + return; + + ret = scmi_xfer_get_init(handle, PERF_DESCRIBE_FASTCHANNEL, + SCMI_PROTOCOL_PERF, + sizeof(*info), sizeof(*resp), &t); + if (ret) + return; + + info = t->tx.buf; + info->domain = cpu_to_le32(domain); + info->message_id = cpu_to_le32(message_id); + + ret = scmi_do_xfer(handle, t); + if (ret) + goto err_xfer; + + resp = t->rx.buf; + flags = le32_to_cpu(resp->attr); + size = le32_to_cpu(resp->chan_size); + if (!scmi_perf_fc_size_is_valid(message_id, size)) + goto err_xfer; + + phys_addr = le32_to_cpu(resp->chan_addr_low); + phys_addr |= (u64)le32_to_cpu(resp->chan_addr_high) << 32; + addr = devm_ioremap(handle->dev, phys_addr, size); + if (!addr) + goto err_xfer; + *p_addr = addr; + + if (p_db && SUPPORTS_DOORBELL(flags)) { + db = devm_kzalloc(handle->dev, sizeof(*db), GFP_KERNEL); + if (!db) + goto err_xfer; + + size = 1 << DOORBELL_REG_WIDTH(flags); + phys_addr = le32_to_cpu(resp->db_addr_low); + phys_addr |= (u64)le32_to_cpu(resp->db_addr_high) << 32; + addr = devm_ioremap(handle->dev, phys_addr, size); + if (!addr) + goto err_xfer; + + db->addr = addr; + db->width = size; + db->set = le32_to_cpu(resp->db_set_lmask); + db->set |= (u64)le32_to_cpu(resp->db_set_hmask) << 32; + db->mask = le32_to_cpu(resp->db_preserve_lmask); + db->mask |= (u64)le32_to_cpu(resp->db_preserve_hmask) << 32; + *p_db = db; + } +err_xfer: + scmi_xfer_put(handle, t); +} + +static void scmi_perf_domain_init_fc(const struct scmi_handle *handle, + u32 domain, struct scmi_fc_info **p_fc) +{ + struct scmi_fc_info *fc; + + fc = devm_kzalloc(handle->dev, sizeof(*fc), GFP_KERNEL); + if (!fc) + return; + + scmi_perf_domain_desc_fc(handle, domain, PERF_LEVEL_SET, + &fc->level_set_addr, &fc->level_set_db); + scmi_perf_domain_desc_fc(handle, domain, PERF_LEVEL_GET, + &fc->level_get_addr, NULL); + scmi_perf_domain_desc_fc(handle, domain, PERF_LIMITS_SET, + &fc->limit_set_addr, &fc->limit_set_db); + scmi_perf_domain_desc_fc(handle, domain, PERF_LIMITS_GET, + &fc->limit_get_addr, NULL); + *p_fc = fc; +} + /* Device specific ops */ static int scmi_dev_domain_id(struct device *dev) { @@ -494,6 +731,9 @@ static int scmi_perf_protocol_init(struct scmi_handle *handle) scmi_perf_domain_attributes_get(handle, domain, dom); scmi_perf_describe_levels_get(handle, domain, dom); + + if (dom->perf_fastchannels) + scmi_perf_domain_init_fc(handle, domain, &dom->fc_info); } handle->perf_ops = &perf_ops; diff --git a/drivers/firmware/arm_scmi/power.c b/drivers/firmware/arm_scmi/power.c index 62f3401a1f01..5abef7079c0a 100644 --- a/drivers/firmware/arm_scmi/power.c +++ b/drivers/firmware/arm_scmi/power.c @@ -96,7 +96,7 @@ scmi_power_domain_attributes_get(const struct scmi_handle *handle, u32 domain, if (ret) return ret; - *(__le32 *)t->tx.buf = cpu_to_le32(domain); + put_unaligned_le32(domain, t->tx.buf); attr = t->rx.buf; ret = scmi_do_xfer(handle, t); @@ -147,11 +147,11 @@ scmi_power_state_get(const struct scmi_handle *handle, u32 domain, u32 *state) if (ret) return ret; - *(__le32 *)t->tx.buf = cpu_to_le32(domain); + put_unaligned_le32(domain, t->tx.buf); ret = scmi_do_xfer(handle, t); if (!ret) - *state = le32_to_cpu(*(__le32 *)t->rx.buf); + *state = get_unaligned_le32(t->rx.buf); scmi_xfer_put(handle, t); return ret; diff --git a/drivers/firmware/arm_scmi/reset.c b/drivers/firmware/arm_scmi/reset.c new file mode 100644 index 000000000000..64cc81915581 --- /dev/null +++ b/drivers/firmware/arm_scmi/reset.c @@ -0,0 +1,231 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * System Control and Management Interface (SCMI) Reset Protocol + * + * Copyright (C) 2019 ARM Ltd. + */ + +#include "common.h" + +enum scmi_reset_protocol_cmd { + RESET_DOMAIN_ATTRIBUTES = 0x3, + RESET = 0x4, + RESET_NOTIFY = 0x5, +}; + +enum scmi_reset_protocol_notify { + RESET_ISSUED = 0x0, +}; + +#define NUM_RESET_DOMAIN_MASK 0xffff +#define RESET_NOTIFY_ENABLE BIT(0) + +struct scmi_msg_resp_reset_domain_attributes { + __le32 attributes; +#define SUPPORTS_ASYNC_RESET(x) ((x) & BIT(31)) +#define SUPPORTS_NOTIFY_RESET(x) ((x) & BIT(30)) + __le32 latency; + u8 name[SCMI_MAX_STR_SIZE]; +}; + +struct scmi_msg_reset_domain_reset { + __le32 domain_id; + __le32 flags; +#define AUTONOMOUS_RESET BIT(0) +#define EXPLICIT_RESET_ASSERT BIT(1) +#define ASYNCHRONOUS_RESET BIT(2) + __le32 reset_state; +#define ARCH_RESET_TYPE BIT(31) +#define COLD_RESET_STATE BIT(0) +#define ARCH_COLD_RESET (ARCH_RESET_TYPE | COLD_RESET_STATE) +}; + +struct reset_dom_info { + bool async_reset; + bool reset_notify; + u32 latency_us; + char name[SCMI_MAX_STR_SIZE]; +}; + +struct scmi_reset_info { + int num_domains; + struct reset_dom_info *dom_info; +}; + +static int scmi_reset_attributes_get(const struct scmi_handle *handle, + struct scmi_reset_info *pi) +{ + int ret; + struct scmi_xfer *t; + u32 attr; + + ret = scmi_xfer_get_init(handle, PROTOCOL_ATTRIBUTES, + SCMI_PROTOCOL_RESET, 0, sizeof(attr), &t); + if (ret) + return ret; + + ret = scmi_do_xfer(handle, t); + if (!ret) { + attr = get_unaligned_le32(t->rx.buf); + pi->num_domains = attr & NUM_RESET_DOMAIN_MASK; + } + + scmi_xfer_put(handle, t); + return ret; +} + +static int +scmi_reset_domain_attributes_get(const struct scmi_handle *handle, u32 domain, + struct reset_dom_info *dom_info) +{ + int ret; + struct scmi_xfer *t; + struct scmi_msg_resp_reset_domain_attributes *attr; + + ret = scmi_xfer_get_init(handle, RESET_DOMAIN_ATTRIBUTES, + SCMI_PROTOCOL_RESET, sizeof(domain), + sizeof(*attr), &t); + if (ret) + return ret; + + put_unaligned_le32(domain, t->tx.buf); + attr = t->rx.buf; + + ret = scmi_do_xfer(handle, t); + if (!ret) { + u32 attributes = le32_to_cpu(attr->attributes); + + dom_info->async_reset = SUPPORTS_ASYNC_RESET(attributes); + dom_info->reset_notify = SUPPORTS_NOTIFY_RESET(attributes); + dom_info->latency_us = le32_to_cpu(attr->latency); + if (dom_info->latency_us == U32_MAX) + dom_info->latency_us = 0; + strlcpy(dom_info->name, attr->name, SCMI_MAX_STR_SIZE); + } + + scmi_xfer_put(handle, t); + return ret; +} + +static int scmi_reset_num_domains_get(const struct scmi_handle *handle) +{ + struct scmi_reset_info *pi = handle->reset_priv; + + return pi->num_domains; +} + +static char *scmi_reset_name_get(const struct scmi_handle *handle, u32 domain) +{ + struct scmi_reset_info *pi = handle->reset_priv; + struct reset_dom_info *dom = pi->dom_info + domain; + + return dom->name; +} + +static int scmi_reset_latency_get(const struct scmi_handle *handle, u32 domain) +{ + struct scmi_reset_info *pi = handle->reset_priv; + struct reset_dom_info *dom = pi->dom_info + domain; + + return dom->latency_us; +} + +static int scmi_domain_reset(const struct scmi_handle *handle, u32 domain, + u32 flags, u32 state) +{ + int ret; + struct scmi_xfer *t; + struct scmi_msg_reset_domain_reset *dom; + struct scmi_reset_info *pi = handle->reset_priv; + struct reset_dom_info *rdom = pi->dom_info + domain; + + if (rdom->async_reset) + flags |= ASYNCHRONOUS_RESET; + + ret = scmi_xfer_get_init(handle, RESET, SCMI_PROTOCOL_RESET, + sizeof(*dom), 0, &t); + if (ret) + return ret; + + dom = t->tx.buf; + dom->domain_id = cpu_to_le32(domain); + dom->flags = cpu_to_le32(flags); + dom->domain_id = cpu_to_le32(state); + + if (rdom->async_reset) + ret = scmi_do_xfer_with_response(handle, t); + else + ret = scmi_do_xfer(handle, t); + + scmi_xfer_put(handle, t); + return ret; +} + +static int scmi_reset_domain_reset(const struct scmi_handle *handle, u32 domain) +{ + return scmi_domain_reset(handle, domain, AUTONOMOUS_RESET, + ARCH_COLD_RESET); +} + +static int +scmi_reset_domain_assert(const struct scmi_handle *handle, u32 domain) +{ + return scmi_domain_reset(handle, domain, EXPLICIT_RESET_ASSERT, + ARCH_COLD_RESET); +} + +static int +scmi_reset_domain_deassert(const struct scmi_handle *handle, u32 domain) +{ + return scmi_domain_reset(handle, domain, 0, ARCH_COLD_RESET); +} + +static struct scmi_reset_ops reset_ops = { + .num_domains_get = scmi_reset_num_domains_get, + .name_get = scmi_reset_name_get, + .latency_get = scmi_reset_latency_get, + .reset = scmi_reset_domain_reset, + .assert = scmi_reset_domain_assert, + .deassert = scmi_reset_domain_deassert, +}; + +static int scmi_reset_protocol_init(struct scmi_handle *handle) +{ + int domain; + u32 version; + struct scmi_reset_info *pinfo; + + scmi_version_get(handle, SCMI_PROTOCOL_RESET, &version); + + dev_dbg(handle->dev, "Reset Version %d.%d\n", + PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version)); + + pinfo = devm_kzalloc(handle->dev, sizeof(*pinfo), GFP_KERNEL); + if (!pinfo) + return -ENOMEM; + + scmi_reset_attributes_get(handle, pinfo); + + pinfo->dom_info = devm_kcalloc(handle->dev, pinfo->num_domains, + sizeof(*pinfo->dom_info), GFP_KERNEL); + if (!pinfo->dom_info) + return -ENOMEM; + + for (domain = 0; domain < pinfo->num_domains; domain++) { + struct reset_dom_info *dom = pinfo->dom_info + domain; + + scmi_reset_domain_attributes_get(handle, domain, dom); + } + + handle->reset_ops = &reset_ops; + handle->reset_priv = pinfo; + + return 0; +} + +static int __init scmi_reset_init(void) +{ + return scmi_protocol_register(SCMI_PROTOCOL_RESET, + &scmi_reset_protocol_init); +} +subsys_initcall(scmi_reset_init); diff --git a/drivers/firmware/arm_scmi/sensors.c b/drivers/firmware/arm_scmi/sensors.c index 0e94ab56f679..a400ea805fc2 100644 --- a/drivers/firmware/arm_scmi/sensors.c +++ b/drivers/firmware/arm_scmi/sensors.c @@ -9,8 +9,8 @@ enum scmi_sensor_protocol_cmd { SENSOR_DESCRIPTION_GET = 0x3, - SENSOR_CONFIG_SET = 0x4, - SENSOR_TRIP_POINT_SET = 0x5, + SENSOR_TRIP_POINT_NOTIFY = 0x4, + SENSOR_TRIP_POINT_CONFIG = 0x5, SENSOR_READING_GET = 0x6, }; @@ -42,9 +42,10 @@ struct scmi_msg_resp_sensor_description { } desc[0]; }; -struct scmi_msg_set_sensor_config { +struct scmi_msg_sensor_trip_point_notify { __le32 id; __le32 event_control; +#define SENSOR_TP_NOTIFY_ALL BIT(0) }; struct scmi_msg_set_sensor_trip_point { @@ -119,7 +120,7 @@ static int scmi_sensor_description_get(const struct scmi_handle *handle, do { /* Set the number of sensors to be skipped/already read */ - *(__le32 *)t->tx.buf = cpu_to_le32(desc_index); + put_unaligned_le32(desc_index, t->tx.buf); ret = scmi_do_xfer(handle, t); if (ret) @@ -135,9 +136,10 @@ static int scmi_sensor_description_get(const struct scmi_handle *handle, } for (cnt = 0; cnt < num_returned; cnt++) { - u32 attrh; + u32 attrh, attrl; struct scmi_sensor_info *s; + attrl = le32_to_cpu(buf->desc[cnt].attributes_low); attrh = le32_to_cpu(buf->desc[cnt].attributes_high); s = &si->sensors[desc_index + cnt]; s->id = le32_to_cpu(buf->desc[cnt].id); @@ -146,6 +148,8 @@ static int scmi_sensor_description_get(const struct scmi_handle *handle, /* Sign extend to a full s8 */ if (s->scale & SENSOR_SCALE_SIGN) s->scale |= SENSOR_SCALE_EXTEND; + s->async = SUPPORTS_ASYNC_READ(attrl); + s->num_trip_points = NUM_TRIP_POINTS(attrl); strlcpy(s->name, buf->desc[cnt].name, SCMI_MAX_STR_SIZE); } @@ -160,15 +164,15 @@ static int scmi_sensor_description_get(const struct scmi_handle *handle, return ret; } -static int -scmi_sensor_configuration_set(const struct scmi_handle *handle, u32 sensor_id) +static int scmi_sensor_trip_point_notify(const struct scmi_handle *handle, + u32 sensor_id, bool enable) { int ret; - u32 evt_cntl = BIT(0); + u32 evt_cntl = enable ? SENSOR_TP_NOTIFY_ALL : 0; struct scmi_xfer *t; - struct scmi_msg_set_sensor_config *cfg; + struct scmi_msg_sensor_trip_point_notify *cfg; - ret = scmi_xfer_get_init(handle, SENSOR_CONFIG_SET, + ret = scmi_xfer_get_init(handle, SENSOR_TRIP_POINT_NOTIFY, SCMI_PROTOCOL_SENSOR, sizeof(*cfg), 0, &t); if (ret) return ret; @@ -183,15 +187,16 @@ scmi_sensor_configuration_set(const struct scmi_handle *handle, u32 sensor_id) return ret; } -static int scmi_sensor_trip_point_set(const struct scmi_handle *handle, - u32 sensor_id, u8 trip_id, u64 trip_value) +static int +scmi_sensor_trip_point_config(const struct scmi_handle *handle, u32 sensor_id, + u8 trip_id, u64 trip_value) { int ret; u32 evt_cntl = SENSOR_TP_BOTH; struct scmi_xfer *t; struct scmi_msg_set_sensor_trip_point *trip; - ret = scmi_xfer_get_init(handle, SENSOR_TRIP_POINT_SET, + ret = scmi_xfer_get_init(handle, SENSOR_TRIP_POINT_CONFIG, SCMI_PROTOCOL_SENSOR, sizeof(*trip), 0, &t); if (ret) return ret; @@ -209,11 +214,13 @@ static int scmi_sensor_trip_point_set(const struct scmi_handle *handle, } static int scmi_sensor_reading_get(const struct scmi_handle *handle, - u32 sensor_id, bool async, u64 *value) + u32 sensor_id, u64 *value) { int ret; struct scmi_xfer *t; struct scmi_msg_sensor_reading_get *sensor; + struct sensors_info *si = handle->sensor_priv; + struct scmi_sensor_info *s = si->sensors + sensor_id; ret = scmi_xfer_get_init(handle, SENSOR_READING_GET, SCMI_PROTOCOL_SENSOR, sizeof(*sensor), @@ -223,14 +230,18 @@ static int scmi_sensor_reading_get(const struct scmi_handle *handle, sensor = t->tx.buf; sensor->id = cpu_to_le32(sensor_id); - sensor->flags = cpu_to_le32(async ? SENSOR_READ_ASYNC : 0); - ret = scmi_do_xfer(handle, t); - if (!ret) { - __le32 *pval = t->rx.buf; - - *value = le32_to_cpu(*pval); - *value |= (u64)le32_to_cpu(*(pval + 1)) << 32; + if (s->async) { + sensor->flags = cpu_to_le32(SENSOR_READ_ASYNC); + ret = scmi_do_xfer_with_response(handle, t); + if (!ret) + *value = get_unaligned_le64((void *) + ((__le32 *)t->rx.buf + 1)); + } else { + sensor->flags = cpu_to_le32(0); + ret = scmi_do_xfer(handle, t); + if (!ret) + *value = get_unaligned_le64(t->rx.buf); } scmi_xfer_put(handle, t); @@ -255,8 +266,8 @@ static int scmi_sensor_count_get(const struct scmi_handle *handle) static struct scmi_sensor_ops sensor_ops = { .count_get = scmi_sensor_count_get, .info_get = scmi_sensor_info_get, - .configuration_set = scmi_sensor_configuration_set, - .trip_point_set = scmi_sensor_trip_point_set, + .trip_point_notify = scmi_sensor_trip_point_notify, + .trip_point_config = scmi_sensor_trip_point_config, .reading_get = scmi_sensor_reading_get, }; diff --git a/drivers/firmware/imx/Kconfig b/drivers/firmware/imx/Kconfig index 42b566f8903f..0dbee32da4c6 100644 --- a/drivers/firmware/imx/Kconfig +++ b/drivers/firmware/imx/Kconfig @@ -1,4 +1,15 @@ # SPDX-License-Identifier: GPL-2.0-only +config IMX_DSP + bool "IMX DSP Protocol driver" + depends on IMX_MBOX + help + This enables DSP IPC protocol between host AP (Linux) + and the firmware running on DSP. + DSP exists on some i.MX8 processors (e.g i.MX8QM, i.MX8QXP). + + It acts like a doorbell. Client might use shared memory to + exchange information with DSP side. + config IMX_SCU bool "IMX SCU Protocol driver" depends on IMX_MBOX diff --git a/drivers/firmware/imx/Makefile b/drivers/firmware/imx/Makefile index 802c4ad8e8f9..08bc9ddfbdfb 100644 --- a/drivers/firmware/imx/Makefile +++ b/drivers/firmware/imx/Makefile @@ -1,3 +1,4 @@ # SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_IMX_DSP) += imx-dsp.o obj-$(CONFIG_IMX_SCU) += imx-scu.o misc.o imx-scu-irq.o obj-$(CONFIG_IMX_SCU_PD) += scu-pd.o diff --git a/drivers/firmware/imx/imx-dsp.c b/drivers/firmware/imx/imx-dsp.c new file mode 100644 index 000000000000..a43d2db5cbdb --- /dev/null +++ b/drivers/firmware/imx/imx-dsp.c @@ -0,0 +1,155 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2019 NXP + * Author: Daniel Baluta <daniel.baluta@nxp.com> + * + * Implementation of the DSP IPC interface (host side) + */ + +#include <linux/firmware/imx/dsp.h> +#include <linux/kernel.h> +#include <linux/mailbox_client.h> +#include <linux/module.h> +#include <linux/of_platform.h> +#include <linux/platform_device.h> +#include <linux/slab.h> + +/* + * imx_dsp_ring_doorbell - triggers an interrupt on the other side (DSP) + * + * @dsp: DSP IPC handle + * @chan_idx: index of the channel where to trigger the interrupt + * + * Returns non-negative value for success, negative value for error + */ +int imx_dsp_ring_doorbell(struct imx_dsp_ipc *ipc, unsigned int idx) +{ + int ret; + struct imx_dsp_chan *dsp_chan; + + if (idx >= DSP_MU_CHAN_NUM) + return -EINVAL; + + dsp_chan = &ipc->chans[idx]; + ret = mbox_send_message(dsp_chan->ch, NULL); + if (ret < 0) + return ret; + + return 0; +} +EXPORT_SYMBOL(imx_dsp_ring_doorbell); + +/* + * imx_dsp_handle_rx - rx callback used by imx mailbox + * + * @c: mbox client + * @msg: message received + * + * Users of DSP IPC will need to privde handle_reply and handle_request + * callbacks. + */ +static void imx_dsp_handle_rx(struct mbox_client *c, void *msg) +{ + struct imx_dsp_chan *chan = container_of(c, struct imx_dsp_chan, cl); + + if (chan->idx == 0) { + chan->ipc->ops->handle_reply(chan->ipc); + } else { + chan->ipc->ops->handle_request(chan->ipc); + imx_dsp_ring_doorbell(chan->ipc, 1); + } +} + +static int imx_dsp_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct imx_dsp_ipc *dsp_ipc; + struct imx_dsp_chan *dsp_chan; + struct mbox_client *cl; + char *chan_name; + int ret; + int i, j; + + device_set_of_node_from_dev(&pdev->dev, pdev->dev.parent); + + dsp_ipc = devm_kzalloc(dev, sizeof(*dsp_ipc), GFP_KERNEL); + if (!dsp_ipc) + return -ENOMEM; + + for (i = 0; i < DSP_MU_CHAN_NUM; i++) { + if (i < 2) + chan_name = kasprintf(GFP_KERNEL, "txdb%d", i); + else + chan_name = kasprintf(GFP_KERNEL, "rxdb%d", i - 2); + + if (!chan_name) + return -ENOMEM; + + dsp_chan = &dsp_ipc->chans[i]; + cl = &dsp_chan->cl; + cl->dev = dev; + cl->tx_block = false; + cl->knows_txdone = true; + cl->rx_callback = imx_dsp_handle_rx; + + dsp_chan->ipc = dsp_ipc; + dsp_chan->idx = i % 2; + dsp_chan->ch = mbox_request_channel_byname(cl, chan_name); + if (IS_ERR(dsp_chan->ch)) { + ret = PTR_ERR(dsp_chan->ch); + if (ret != -EPROBE_DEFER) + dev_err(dev, "Failed to request mbox chan %s ret %d\n", + chan_name, ret); + goto out; + } + + dev_dbg(dev, "request mbox chan %s\n", chan_name); + /* chan_name is not used anymore by framework */ + kfree(chan_name); + } + + dsp_ipc->dev = dev; + + dev_set_drvdata(dev, dsp_ipc); + + dev_info(dev, "NXP i.MX DSP IPC initialized\n"); + + return devm_of_platform_populate(dev); +out: + kfree(chan_name); + for (j = 0; j < i; j++) { + dsp_chan = &dsp_ipc->chans[j]; + mbox_free_channel(dsp_chan->ch); + } + + return ret; +} + +static int imx_dsp_remove(struct platform_device *pdev) +{ + struct imx_dsp_chan *dsp_chan; + struct imx_dsp_ipc *dsp_ipc; + int i; + + dsp_ipc = dev_get_drvdata(&pdev->dev); + + for (i = 0; i < DSP_MU_CHAN_NUM; i++) { + dsp_chan = &dsp_ipc->chans[i]; + mbox_free_channel(dsp_chan->ch); + } + + return 0; +} + +static struct platform_driver imx_dsp_driver = { + .driver = { + .name = "imx-dsp", + }, + .probe = imx_dsp_probe, + .remove = imx_dsp_remove, +}; +builtin_platform_driver(imx_dsp_driver); + +MODULE_AUTHOR("Daniel Baluta <daniel.baluta@nxp.com>"); +MODULE_DESCRIPTION("IMX DSP IPC protocol driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/firmware/imx/scu-pd.c b/drivers/firmware/imx/scu-pd.c index 480cec69e2c9..b556612207e5 100644 --- a/drivers/firmware/imx/scu-pd.c +++ b/drivers/firmware/imx/scu-pd.c @@ -92,7 +92,8 @@ static const struct imx_sc_pd_range imx8qxp_scu_pd_ranges[] = { { "gpt", IMX_SC_R_GPT_0, 5, true, 0 }, { "kpp", IMX_SC_R_KPP, 1, false, 0 }, { "fspi", IMX_SC_R_FSPI_0, 2, true, 0 }, - { "mu", IMX_SC_R_MU_0A, 14, true, 0 }, + { "mu_a", IMX_SC_R_MU_0A, 14, true, 0 }, + { "mu_b", IMX_SC_R_MU_13B, 1, true, 13 }, /* CONN SS */ { "usb", IMX_SC_R_USB_0, 2, true, 0 }, @@ -130,6 +131,7 @@ static const struct imx_sc_pd_range imx8qxp_scu_pd_ranges[] = { { "lcd0-pwm", IMX_SC_R_LCD_0_PWM_0, 1, true, 0 }, { "lpuart", IMX_SC_R_UART_0, 4, true, 0 }, { "lpspi", IMX_SC_R_SPI_0, 4, true, 0 }, + { "irqstr_dsp", IMX_SC_R_IRQSTR_DSP, 1, false, 0 }, /* VPU SS */ { "vpu", IMX_SC_R_VPU, 1, false, 0 }, diff --git a/drivers/firmware/qcom_scm.c b/drivers/firmware/qcom_scm.c index 2ddc118dba1b..4802ab170fe5 100644 --- a/drivers/firmware/qcom_scm.c +++ b/drivers/firmware/qcom_scm.c @@ -9,6 +9,7 @@ #include <linux/init.h> #include <linux/cpumask.h> #include <linux/export.h> +#include <linux/dma-direct.h> #include <linux/dma-mapping.h> #include <linux/module.h> #include <linux/types.h> @@ -425,21 +426,23 @@ EXPORT_SYMBOL(qcom_scm_set_remote_state); * @mem_sz: size of the region. * @srcvm: vmid for current set of owners, each set bit in * flag indicate a unique owner - * @newvm: array having new owners and corrsponding permission + * @newvm: array having new owners and corresponding permission * flags * @dest_cnt: number of owners in next set. * - * Return negative errno on failure, 0 on success, with @srcvm updated. + * Return negative errno on failure or 0 on success with @srcvm updated. */ int qcom_scm_assign_mem(phys_addr_t mem_addr, size_t mem_sz, unsigned int *srcvm, - struct qcom_scm_vmperm *newvm, int dest_cnt) + const struct qcom_scm_vmperm *newvm, + unsigned int dest_cnt) { struct qcom_scm_current_perm_info *destvm; struct qcom_scm_mem_map_info *mem_to_map; phys_addr_t mem_to_map_phys; phys_addr_t dest_phys; phys_addr_t ptr_phys; + dma_addr_t ptr_dma; size_t mem_to_map_sz; size_t dest_sz; size_t src_sz; @@ -447,52 +450,50 @@ int qcom_scm_assign_mem(phys_addr_t mem_addr, size_t mem_sz, int next_vm; __le32 *src; void *ptr; - int ret; - int len; - int i; + int ret, i, b; + unsigned long srcvm_bits = *srcvm; - src_sz = hweight_long(*srcvm) * sizeof(*src); + src_sz = hweight_long(srcvm_bits) * sizeof(*src); mem_to_map_sz = sizeof(*mem_to_map); dest_sz = dest_cnt * sizeof(*destvm); ptr_sz = ALIGN(src_sz, SZ_64) + ALIGN(mem_to_map_sz, SZ_64) + ALIGN(dest_sz, SZ_64); - ptr = dma_alloc_coherent(__scm->dev, ptr_sz, &ptr_phys, GFP_KERNEL); + ptr = dma_alloc_coherent(__scm->dev, ptr_sz, &ptr_dma, GFP_KERNEL); if (!ptr) return -ENOMEM; + ptr_phys = dma_to_phys(__scm->dev, ptr_dma); /* Fill source vmid detail */ src = ptr; - len = hweight_long(*srcvm); - for (i = 0; i < len; i++) { - src[i] = cpu_to_le32(ffs(*srcvm) - 1); - *srcvm ^= 1 << (ffs(*srcvm) - 1); - } + i = 0; + for_each_set_bit(b, &srcvm_bits, BITS_PER_LONG) + src[i++] = cpu_to_le32(b); /* Fill details of mem buff to map */ mem_to_map = ptr + ALIGN(src_sz, SZ_64); mem_to_map_phys = ptr_phys + ALIGN(src_sz, SZ_64); - mem_to_map[0].mem_addr = cpu_to_le64(mem_addr); - mem_to_map[0].mem_size = cpu_to_le64(mem_sz); + mem_to_map->mem_addr = cpu_to_le64(mem_addr); + mem_to_map->mem_size = cpu_to_le64(mem_sz); next_vm = 0; /* Fill details of next vmid detail */ destvm = ptr + ALIGN(mem_to_map_sz, SZ_64) + ALIGN(src_sz, SZ_64); dest_phys = ptr_phys + ALIGN(mem_to_map_sz, SZ_64) + ALIGN(src_sz, SZ_64); - for (i = 0; i < dest_cnt; i++) { - destvm[i].vmid = cpu_to_le32(newvm[i].vmid); - destvm[i].perm = cpu_to_le32(newvm[i].perm); - destvm[i].ctx = 0; - destvm[i].ctx_size = 0; - next_vm |= BIT(newvm[i].vmid); + for (i = 0; i < dest_cnt; i++, destvm++, newvm++) { + destvm->vmid = cpu_to_le32(newvm->vmid); + destvm->perm = cpu_to_le32(newvm->perm); + destvm->ctx = 0; + destvm->ctx_size = 0; + next_vm |= BIT(newvm->vmid); } ret = __qcom_scm_assign_mem(__scm->dev, mem_to_map_phys, mem_to_map_sz, ptr_phys, src_sz, dest_phys, dest_sz); - dma_free_coherent(__scm->dev, ALIGN(ptr_sz, SZ_64), ptr, ptr_phys); + dma_free_coherent(__scm->dev, ptr_sz, ptr, ptr_dma); if (ret) { dev_err(__scm->dev, - "Assign memory protection call failed %d.\n", ret); + "Assign memory protection call failed %d\n", ret); return -EINVAL; } diff --git a/drivers/firmware/ti_sci.c b/drivers/firmware/ti_sci.c index cdee0b45943d..4126be9e3216 100644 --- a/drivers/firmware/ti_sci.c +++ b/drivers/firmware/ti_sci.c @@ -635,6 +635,7 @@ fail: /** * ti_sci_cmd_get_device() - command to request for device managed by TISCI + * that can be shared with other hosts. * @handle: Pointer to TISCI handle as retrieved by *ti_sci_get_handle * @id: Device Identifier * @@ -642,12 +643,30 @@ fail: * usage count by balancing get_device with put_device. No refcounting is * managed by driver for that purpose. * - * NOTE: The request is for exclusive access for the processor. - * * Return: 0 if all went fine, else return appropriate error. */ static int ti_sci_cmd_get_device(const struct ti_sci_handle *handle, u32 id) { + return ti_sci_set_device_state(handle, id, 0, + MSG_DEVICE_SW_STATE_ON); +} + +/** + * ti_sci_cmd_get_device_exclusive() - command to request for device managed by + * TISCI that is exclusively owned by the + * requesting host. + * @handle: Pointer to TISCI handle as retrieved by *ti_sci_get_handle + * @id: Device Identifier + * + * Request for the device - NOTE: the client MUST maintain integrity of + * usage count by balancing get_device with put_device. No refcounting is + * managed by driver for that purpose. + * + * Return: 0 if all went fine, else return appropriate error. + */ +static int ti_sci_cmd_get_device_exclusive(const struct ti_sci_handle *handle, + u32 id) +{ return ti_sci_set_device_state(handle, id, MSG_FLAG_DEVICE_EXCLUSIVE, MSG_DEVICE_SW_STATE_ON); @@ -666,6 +685,26 @@ static int ti_sci_cmd_get_device(const struct ti_sci_handle *handle, u32 id) */ static int ti_sci_cmd_idle_device(const struct ti_sci_handle *handle, u32 id) { + return ti_sci_set_device_state(handle, id, 0, + MSG_DEVICE_SW_STATE_RETENTION); +} + +/** + * ti_sci_cmd_idle_device_exclusive() - Command to idle a device managed by + * TISCI that is exclusively owned by + * requesting host. + * @handle: Pointer to TISCI handle as retrieved by *ti_sci_get_handle + * @id: Device Identifier + * + * Request for the device - NOTE: the client MUST maintain integrity of + * usage count by balancing get_device with put_device. No refcounting is + * managed by driver for that purpose. + * + * Return: 0 if all went fine, else return appropriate error. + */ +static int ti_sci_cmd_idle_device_exclusive(const struct ti_sci_handle *handle, + u32 id) +{ return ti_sci_set_device_state(handle, id, MSG_FLAG_DEVICE_EXCLUSIVE, MSG_DEVICE_SW_STATE_RETENTION); @@ -2894,7 +2933,9 @@ static void ti_sci_setup_ops(struct ti_sci_info *info) core_ops->reboot_device = ti_sci_cmd_core_reboot; dops->get_device = ti_sci_cmd_get_device; + dops->get_device_exclusive = ti_sci_cmd_get_device_exclusive; dops->idle_device = ti_sci_cmd_idle_device; + dops->idle_device_exclusive = ti_sci_cmd_idle_device_exclusive; dops->put_device = ti_sci_cmd_put_device; dops->is_valid = ti_sci_cmd_dev_is_valid; diff --git a/drivers/firmware/turris-mox-rwtm.c b/drivers/firmware/turris-mox-rwtm.c new file mode 100644 index 000000000000..72be58960e54 --- /dev/null +++ b/drivers/firmware/turris-mox-rwtm.c @@ -0,0 +1,384 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Turris Mox rWTM firmware driver + * + * Copyright (C) 2019 Marek Behun <marek.behun@nic.cz> + */ + +#include <linux/armada-37xx-rwtm-mailbox.h> +#include <linux/completion.h> +#include <linux/dma-mapping.h> +#include <linux/hw_random.h> +#include <linux/mailbox_client.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/slab.h> + +#define DRIVER_NAME "turris-mox-rwtm" + +/* + * The macros and constants below come from Turris Mox's rWTM firmware code. + * This firmware is open source and it's sources can be found at + * https://gitlab.labs.nic.cz/turris/mox-boot-builder/tree/master/wtmi. + */ + +#define MBOX_STS_SUCCESS (0 << 30) +#define MBOX_STS_FAIL (1 << 30) +#define MBOX_STS_BADCMD (2 << 30) +#define MBOX_STS_ERROR(s) ((s) & (3 << 30)) +#define MBOX_STS_VALUE(s) (((s) >> 10) & 0xfffff) +#define MBOX_STS_CMD(s) ((s) & 0x3ff) + +enum mbox_cmd { + MBOX_CMD_GET_RANDOM = 1, + MBOX_CMD_BOARD_INFO = 2, + MBOX_CMD_ECDSA_PUB_KEY = 3, + MBOX_CMD_HASH = 4, + MBOX_CMD_SIGN = 5, + MBOX_CMD_VERIFY = 6, + + MBOX_CMD_OTP_READ = 7, + MBOX_CMD_OTP_WRITE = 8, +}; + +struct mox_kobject; + +struct mox_rwtm { + struct device *dev; + struct mbox_client mbox_client; + struct mbox_chan *mbox; + struct mox_kobject *kobj; + struct hwrng hwrng; + + struct armada_37xx_rwtm_rx_msg reply; + + void *buf; + dma_addr_t buf_phys; + + struct mutex busy; + struct completion cmd_done; + + /* board information */ + int has_board_info; + u64 serial_number; + int board_version, ram_size; + u8 mac_address1[6], mac_address2[6]; + + /* public key burned in eFuse */ + int has_pubkey; + u8 pubkey[135]; +}; + +struct mox_kobject { + struct kobject kobj; + struct mox_rwtm *rwtm; +}; + +static inline struct kobject *rwtm_to_kobj(struct mox_rwtm *rwtm) +{ + return &rwtm->kobj->kobj; +} + +static inline struct mox_rwtm *to_rwtm(struct kobject *kobj) +{ + return container_of(kobj, struct mox_kobject, kobj)->rwtm; +} + +static void mox_kobj_release(struct kobject *kobj) +{ + kfree(to_rwtm(kobj)->kobj); +} + +static struct kobj_type mox_kobj_ktype = { + .release = mox_kobj_release, + .sysfs_ops = &kobj_sysfs_ops, +}; + +static int mox_kobj_create(struct mox_rwtm *rwtm) +{ + rwtm->kobj = kzalloc(sizeof(*rwtm->kobj), GFP_KERNEL); + if (!rwtm->kobj) + return -ENOMEM; + + kobject_init(rwtm_to_kobj(rwtm), &mox_kobj_ktype); + if (kobject_add(rwtm_to_kobj(rwtm), firmware_kobj, "turris-mox-rwtm")) { + kobject_put(rwtm_to_kobj(rwtm)); + return -ENXIO; + } + + rwtm->kobj->rwtm = rwtm; + + return 0; +} + +#define MOX_ATTR_RO(name, format, cat) \ +static ssize_t \ +name##_show(struct kobject *kobj, struct kobj_attribute *a, \ + char *buf) \ +{ \ + struct mox_rwtm *rwtm = to_rwtm(kobj); \ + if (!rwtm->has_##cat) \ + return -ENODATA; \ + return sprintf(buf, format, rwtm->name); \ +} \ +static struct kobj_attribute mox_attr_##name = __ATTR_RO(name) + +MOX_ATTR_RO(serial_number, "%016llX\n", board_info); +MOX_ATTR_RO(board_version, "%i\n", board_info); +MOX_ATTR_RO(ram_size, "%i\n", board_info); +MOX_ATTR_RO(mac_address1, "%pM\n", board_info); +MOX_ATTR_RO(mac_address2, "%pM\n", board_info); +MOX_ATTR_RO(pubkey, "%s\n", pubkey); + +static int mox_get_status(enum mbox_cmd cmd, u32 retval) +{ + if (MBOX_STS_CMD(retval) != cmd || + MBOX_STS_ERROR(retval) != MBOX_STS_SUCCESS) + return -EIO; + else if (MBOX_STS_ERROR(retval) == MBOX_STS_FAIL) + return -(int)MBOX_STS_VALUE(retval); + else + return MBOX_STS_VALUE(retval); +} + +static const struct attribute *mox_rwtm_attrs[] = { + &mox_attr_serial_number.attr, + &mox_attr_board_version.attr, + &mox_attr_ram_size.attr, + &mox_attr_mac_address1.attr, + &mox_attr_mac_address2.attr, + &mox_attr_pubkey.attr, + NULL +}; + +static void mox_rwtm_rx_callback(struct mbox_client *cl, void *data) +{ + struct mox_rwtm *rwtm = dev_get_drvdata(cl->dev); + struct armada_37xx_rwtm_rx_msg *msg = data; + + rwtm->reply = *msg; + complete(&rwtm->cmd_done); +} + +static void reply_to_mac_addr(u8 *mac, u32 t1, u32 t2) +{ + mac[0] = t1 >> 8; + mac[1] = t1; + mac[2] = t2 >> 24; + mac[3] = t2 >> 16; + mac[4] = t2 >> 8; + mac[5] = t2; +} + +static int mox_get_board_info(struct mox_rwtm *rwtm) +{ + struct armada_37xx_rwtm_tx_msg msg; + struct armada_37xx_rwtm_rx_msg *reply = &rwtm->reply; + int ret; + + msg.command = MBOX_CMD_BOARD_INFO; + ret = mbox_send_message(rwtm->mbox, &msg); + if (ret < 0) + return ret; + + ret = wait_for_completion_timeout(&rwtm->cmd_done, HZ / 2); + if (ret < 0) + return ret; + + ret = mox_get_status(MBOX_CMD_BOARD_INFO, reply->retval); + if (ret < 0 && ret != -ENODATA) { + return ret; + } else if (ret == -ENODATA) { + dev_warn(rwtm->dev, + "Board does not have manufacturing information burned!\n"); + } else { + rwtm->serial_number = reply->status[1]; + rwtm->serial_number <<= 32; + rwtm->serial_number |= reply->status[0]; + rwtm->board_version = reply->status[2]; + rwtm->ram_size = reply->status[3]; + reply_to_mac_addr(rwtm->mac_address1, reply->status[4], + reply->status[5]); + reply_to_mac_addr(rwtm->mac_address2, reply->status[6], + reply->status[7]); + rwtm->has_board_info = 1; + + pr_info("Turris Mox serial number %016llX\n", + rwtm->serial_number); + pr_info(" board version %i\n", rwtm->board_version); + pr_info(" burned RAM size %i MiB\n", rwtm->ram_size); + } + + msg.command = MBOX_CMD_ECDSA_PUB_KEY; + ret = mbox_send_message(rwtm->mbox, &msg); + if (ret < 0) + return ret; + + ret = wait_for_completion_timeout(&rwtm->cmd_done, HZ / 2); + if (ret < 0) + return ret; + + ret = mox_get_status(MBOX_CMD_ECDSA_PUB_KEY, reply->retval); + if (ret < 0 && ret != -ENODATA) { + return ret; + } else if (ret == -ENODATA) { + dev_warn(rwtm->dev, "Board has no public key burned!\n"); + } else { + u32 *s = reply->status; + + rwtm->has_pubkey = 1; + sprintf(rwtm->pubkey, + "%06x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x", + ret, s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7], + s[8], s[9], s[10], s[11], s[12], s[13], s[14], s[15]); + } + + return 0; +} + +static int mox_hwrng_read(struct hwrng *rng, void *data, size_t max, bool wait) +{ + struct mox_rwtm *rwtm = (struct mox_rwtm *) rng->priv; + struct armada_37xx_rwtm_tx_msg msg; + int ret; + + if (max > 4096) + max = 4096; + + msg.command = MBOX_CMD_GET_RANDOM; + msg.args[0] = 1; + msg.args[1] = rwtm->buf_phys; + msg.args[2] = (max + 3) & ~3; + + if (!wait) { + if (!mutex_trylock(&rwtm->busy)) + return -EBUSY; + } else { + mutex_lock(&rwtm->busy); + } + + ret = mbox_send_message(rwtm->mbox, &msg); + if (ret < 0) + goto unlock_mutex; + + ret = wait_for_completion_interruptible(&rwtm->cmd_done); + if (ret < 0) + goto unlock_mutex; + + ret = mox_get_status(MBOX_CMD_GET_RANDOM, rwtm->reply.retval); + if (ret < 0) + goto unlock_mutex; + + memcpy(data, rwtm->buf, max); + ret = max; + +unlock_mutex: + mutex_unlock(&rwtm->busy); + return ret; +} + +static int turris_mox_rwtm_probe(struct platform_device *pdev) +{ + struct mox_rwtm *rwtm; + struct device *dev = &pdev->dev; + int ret; + + rwtm = devm_kzalloc(dev, sizeof(*rwtm), GFP_KERNEL); + if (!rwtm) + return -ENOMEM; + + rwtm->dev = dev; + rwtm->buf = dmam_alloc_coherent(dev, PAGE_SIZE, &rwtm->buf_phys, + GFP_KERNEL); + if (!rwtm->buf) + return -ENOMEM; + + ret = mox_kobj_create(rwtm); + if (ret < 0) { + dev_err(dev, "Cannot create turris-mox-rwtm kobject!\n"); + return ret; + } + + ret = sysfs_create_files(rwtm_to_kobj(rwtm), mox_rwtm_attrs); + if (ret < 0) { + dev_err(dev, "Cannot create sysfs files!\n"); + goto put_kobj; + } + + platform_set_drvdata(pdev, rwtm); + + mutex_init(&rwtm->busy); + + rwtm->mbox_client.dev = dev; + rwtm->mbox_client.rx_callback = mox_rwtm_rx_callback; + + rwtm->mbox = mbox_request_channel(&rwtm->mbox_client, 0); + if (IS_ERR(rwtm->mbox)) { + ret = PTR_ERR(rwtm->mbox); + if (ret != -EPROBE_DEFER) + dev_err(dev, "Cannot request mailbox channel: %i\n", + ret); + goto remove_files; + } + + init_completion(&rwtm->cmd_done); + + ret = mox_get_board_info(rwtm); + if (ret < 0) + dev_warn(dev, "Cannot read board information: %i\n", ret); + + rwtm->hwrng.name = DRIVER_NAME "_hwrng"; + rwtm->hwrng.read = mox_hwrng_read; + rwtm->hwrng.priv = (unsigned long) rwtm; + rwtm->hwrng.quality = 1024; + + ret = devm_hwrng_register(dev, &rwtm->hwrng); + if (ret < 0) { + dev_err(dev, "Cannot register HWRNG: %i\n", ret); + goto free_channel; + } + + return 0; + +free_channel: + mbox_free_channel(rwtm->mbox); +remove_files: + sysfs_remove_files(rwtm_to_kobj(rwtm), mox_rwtm_attrs); +put_kobj: + kobject_put(rwtm_to_kobj(rwtm)); + return ret; +} + +static int turris_mox_rwtm_remove(struct platform_device *pdev) +{ + struct mox_rwtm *rwtm = platform_get_drvdata(pdev); + + sysfs_remove_files(rwtm_to_kobj(rwtm), mox_rwtm_attrs); + kobject_put(rwtm_to_kobj(rwtm)); + mbox_free_channel(rwtm->mbox); + + return 0; +} + +static const struct of_device_id turris_mox_rwtm_match[] = { + { .compatible = "cznic,turris-mox-rwtm", }, + { }, +}; + +MODULE_DEVICE_TABLE(of, turris_mox_rwtm_match); + +static struct platform_driver turris_mox_rwtm_driver = { + .probe = turris_mox_rwtm_probe, + .remove = turris_mox_rwtm_remove, + .driver = { + .name = DRIVER_NAME, + .of_match_table = turris_mox_rwtm_match, + }, +}; +module_platform_driver(turris_mox_rwtm_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Turris Mox rWTM firmware driver"); +MODULE_AUTHOR("Marek Behun <marek.behun@nic.cz>"); diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 305b47ed4532..38e096e6925f 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -1453,6 +1453,15 @@ config GPIO_XRA1403 help GPIO driver for EXAR XRA1403 16-bit SPI-based GPIO expander. +config GPIO_MOXTET + tristate "Turris Mox Moxtet bus GPIO expander" + depends on MOXTET + help + Say yes here if you are building for the Turris Mox router. + This is the driver needed for configuring the GPIOs via the Moxtet + bus. For example the Mox module with SFP cage needs this driver + so that phylink can use corresponding GPIOs. + endmenu menu "USB GPIO expanders" diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index f3e051fb50e6..d2fd19c15bae 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -92,6 +92,7 @@ obj-$(CONFIG_GPIO_ML_IOH) += gpio-ml-ioh.o obj-$(CONFIG_GPIO_MLXBF) += gpio-mlxbf.o obj-$(CONFIG_GPIO_MM_LANTIQ) += gpio-mm-lantiq.o obj-$(CONFIG_GPIO_MOCKUP) += gpio-mockup.o +obj-$(CONFIG_GPIO_MOXTET) += gpio-moxtet.o obj-$(CONFIG_GPIO_MPC5200) += gpio-mpc5200.o obj-$(CONFIG_GPIO_MPC8XXX) += gpio-mpc8xxx.o obj-$(CONFIG_GPIO_MSIC) += gpio-msic.o diff --git a/drivers/gpio/gpio-moxtet.c b/drivers/gpio/gpio-moxtet.c new file mode 100644 index 000000000000..3fd729994a38 --- /dev/null +++ b/drivers/gpio/gpio-moxtet.c @@ -0,0 +1,179 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Turris Mox Moxtet GPIO expander + * + * Copyright (C) 2018 Marek Behun <marek.behun@nic.cz> + */ + +#include <linux/bitops.h> +#include <linux/gpio/driver.h> +#include <linux/moxtet.h> +#include <linux/module.h> + +#define MOXTET_GPIO_NGPIOS 12 +#define MOXTET_GPIO_INPUTS 4 + +struct moxtet_gpio_desc { + u16 in_mask; + u16 out_mask; +}; + +static const struct moxtet_gpio_desc descs[] = { + [TURRIS_MOX_MODULE_SFP] = { + .in_mask = GENMASK(2, 0), + .out_mask = GENMASK(5, 4), + }, +}; + +struct moxtet_gpio_chip { + struct device *dev; + struct gpio_chip gpio_chip; + const struct moxtet_gpio_desc *desc; +}; + +static int moxtet_gpio_get_value(struct gpio_chip *gc, unsigned int offset) +{ + struct moxtet_gpio_chip *chip = gpiochip_get_data(gc); + int ret; + + if (chip->desc->in_mask & BIT(offset)) { + ret = moxtet_device_read(chip->dev); + } else if (chip->desc->out_mask & BIT(offset)) { + ret = moxtet_device_written(chip->dev); + if (ret >= 0) + ret <<= MOXTET_GPIO_INPUTS; + } else { + return -EINVAL; + } + + if (ret < 0) + return ret; + + return !!(ret & BIT(offset)); +} + +static void moxtet_gpio_set_value(struct gpio_chip *gc, unsigned int offset, + int val) +{ + struct moxtet_gpio_chip *chip = gpiochip_get_data(gc); + int state; + + state = moxtet_device_written(chip->dev); + if (state < 0) + return; + + offset -= MOXTET_GPIO_INPUTS; + + if (val) + state |= BIT(offset); + else + state &= ~BIT(offset); + + moxtet_device_write(chip->dev, state); +} + +static int moxtet_gpio_get_direction(struct gpio_chip *gc, unsigned int offset) +{ + struct moxtet_gpio_chip *chip = gpiochip_get_data(gc); + + /* All lines are hard wired to be either input or output, not both. */ + if (chip->desc->in_mask & BIT(offset)) + return 1; + else if (chip->desc->out_mask & BIT(offset)) + return 0; + else + return -EINVAL; +} + +static int moxtet_gpio_direction_input(struct gpio_chip *gc, + unsigned int offset) +{ + struct moxtet_gpio_chip *chip = gpiochip_get_data(gc); + + if (chip->desc->in_mask & BIT(offset)) + return 0; + else if (chip->desc->out_mask & BIT(offset)) + return -ENOTSUPP; + else + return -EINVAL; +} + +static int moxtet_gpio_direction_output(struct gpio_chip *gc, + unsigned int offset, int val) +{ + struct moxtet_gpio_chip *chip = gpiochip_get_data(gc); + + if (chip->desc->out_mask & BIT(offset)) + moxtet_gpio_set_value(gc, offset, val); + else if (chip->desc->in_mask & BIT(offset)) + return -ENOTSUPP; + else + return -EINVAL; + + return 0; +} + +static int moxtet_gpio_probe(struct device *dev) +{ + struct moxtet_gpio_chip *chip; + struct device_node *nc = dev->of_node; + int id; + + id = to_moxtet_device(dev)->id; + + if (id >= ARRAY_SIZE(descs)) { + dev_err(dev, "%pOF Moxtet device id 0x%x is not supported by gpio-moxtet driver\n", + nc, id); + return -ENOTSUPP; + } + + chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + chip->dev = dev; + chip->gpio_chip.parent = dev; + chip->desc = &descs[id]; + + dev_set_drvdata(dev, chip); + + chip->gpio_chip.label = dev_name(dev); + chip->gpio_chip.get_direction = moxtet_gpio_get_direction; + chip->gpio_chip.direction_input = moxtet_gpio_direction_input; + chip->gpio_chip.direction_output = moxtet_gpio_direction_output; + chip->gpio_chip.get = moxtet_gpio_get_value; + chip->gpio_chip.set = moxtet_gpio_set_value; + chip->gpio_chip.base = -1; + + chip->gpio_chip.ngpio = MOXTET_GPIO_NGPIOS; + + chip->gpio_chip.can_sleep = true; + chip->gpio_chip.owner = THIS_MODULE; + + return devm_gpiochip_add_data(dev, &chip->gpio_chip, chip); +} + +static const struct of_device_id moxtet_gpio_dt_ids[] = { + { .compatible = "cznic,moxtet-gpio", }, + {}, +}; +MODULE_DEVICE_TABLE(of, moxtet_gpio_dt_ids); + +static const enum turris_mox_module_id moxtet_gpio_module_table[] = { + TURRIS_MOX_MODULE_SFP, + 0, +}; + +static struct moxtet_driver moxtet_gpio_driver = { + .driver = { + .name = "moxtet-gpio", + .of_match_table = moxtet_gpio_dt_ids, + .probe = moxtet_gpio_probe, + }, + .id_table = moxtet_gpio_module_table, +}; +module_moxtet_driver(moxtet_gpio_driver); + +MODULE_AUTHOR("Marek Behun <marek.behun@nic.cz>"); +MODULE_DESCRIPTION("Turris Mox Moxtet GPIO expander"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/hwmon/scmi-hwmon.c b/drivers/hwmon/scmi-hwmon.c index 0c93fc5ca762..8a7732c0bef3 100644 --- a/drivers/hwmon/scmi-hwmon.c +++ b/drivers/hwmon/scmi-hwmon.c @@ -72,7 +72,7 @@ static int scmi_hwmon_read(struct device *dev, enum hwmon_sensor_types type, const struct scmi_handle *h = scmi_sensors->handle; sensor = *(scmi_sensors->info[type] + channel); - ret = h->sensor_ops->reading_get(h, sensor->id, false, &value); + ret = h->sensor_ops->reading_get(h, sensor->id, &value); if (ret) return ret; diff --git a/drivers/net/ethernet/Kconfig b/drivers/net/ethernet/Kconfig index 93a2d4deb27c..dc9dee55976b 100644 --- a/drivers/net/ethernet/Kconfig +++ b/drivers/net/ethernet/Kconfig @@ -151,7 +151,6 @@ config NET_NETX To compile this driver as a module, choose M here. The module will be called netx-eth. -source "drivers/net/ethernet/nuvoton/Kconfig" source "drivers/net/ethernet/nvidia/Kconfig" source "drivers/net/ethernet/nxp/Kconfig" source "drivers/net/ethernet/oki-semi/Kconfig" diff --git a/drivers/net/ethernet/Makefile b/drivers/net/ethernet/Makefile index fb9155cffcff..4bc3c95562bf 100644 --- a/drivers/net/ethernet/Makefile +++ b/drivers/net/ethernet/Makefile @@ -65,7 +65,6 @@ obj-$(CONFIG_NET_VENDOR_NETERION) += neterion/ obj-$(CONFIG_NET_VENDOR_NETRONOME) += netronome/ obj-$(CONFIG_NET_VENDOR_NI) += ni/ obj-$(CONFIG_NET_NETX) += netx-eth.o -obj-$(CONFIG_NET_VENDOR_NUVOTON) += nuvoton/ obj-$(CONFIG_NET_VENDOR_NVIDIA) += nvidia/ obj-$(CONFIG_LPC_ENET) += nxp/ obj-$(CONFIG_NET_VENDOR_OKI) += oki-semi/ diff --git a/drivers/net/ethernet/micrel/Kconfig b/drivers/net/ethernet/micrel/Kconfig index 90a8c6bead56..b9c4d48e28e4 100644 --- a/drivers/net/ethernet/micrel/Kconfig +++ b/drivers/net/ethernet/micrel/Kconfig @@ -6,8 +6,7 @@ config NET_VENDOR_MICREL bool "Micrel devices" default y - depends on (HAS_IOMEM && DMA_ENGINE) || SPI || PCI || HAS_IOMEM || \ - (ARM && ARCH_KS8695) + depends on (HAS_IOMEM && DMA_ENGINE) || SPI || PCI || HAS_IOMEM ---help--- If you have a network (Ethernet) card belonging to this class, say Y. @@ -18,14 +17,6 @@ config NET_VENDOR_MICREL if NET_VENDOR_MICREL -config ARM_KS8695_ETHER - tristate "KS8695 Ethernet support" - depends on ARM && ARCH_KS8695 - select MII - ---help--- - If you wish to compile a kernel for the KS8695 and want to - use the internal ethernet then you should answer Y to this. - config KS8842 tristate "Micrel KSZ8841/42 with generic bus interface" depends on HAS_IOMEM && DMA_ENGINE diff --git a/drivers/net/ethernet/micrel/Makefile b/drivers/net/ethernet/micrel/Makefile index 848fc1c5a5dc..6d8ac5527aef 100644 --- a/drivers/net/ethernet/micrel/Makefile +++ b/drivers/net/ethernet/micrel/Makefile @@ -3,7 +3,6 @@ # Makefile for the Micrel network device drivers. # -obj-$(CONFIG_ARM_KS8695_ETHER) += ks8695net.o obj-$(CONFIG_KS8842) += ks8842.o obj-$(CONFIG_KS8851) += ks8851.o obj-$(CONFIG_KS8851_MLL) += ks8851_mll.o diff --git a/drivers/net/ethernet/micrel/ks8695net.c b/drivers/net/ethernet/micrel/ks8695net.c deleted file mode 100644 index 1390ef5323a2..000000000000 --- a/drivers/net/ethernet/micrel/ks8695net.c +++ /dev/null @@ -1,1632 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Micrel KS8695 (Centaur) Ethernet. - * - * Copyright 2008 Simtec Electronics - * Daniel Silverstone <dsilvers@simtec.co.uk> - * Vincent Sanders <vince@simtec.co.uk> - */ - -#include <linux/dma-mapping.h> -#include <linux/module.h> -#include <linux/ioport.h> -#include <linux/netdevice.h> -#include <linux/etherdevice.h> -#include <linux/interrupt.h> -#include <linux/skbuff.h> -#include <linux/spinlock.h> -#include <linux/crc32.h> -#include <linux/mii.h> -#include <linux/ethtool.h> -#include <linux/delay.h> -#include <linux/platform_device.h> -#include <linux/irq.h> -#include <linux/io.h> -#include <linux/slab.h> - -#include <asm/irq.h> - -#include <mach/regs-switch.h> -#include <mach/regs-misc.h> -#include <asm/mach/irq.h> -#include <mach/regs-irq.h> - -#include "ks8695net.h" - -#define MODULENAME "ks8695_ether" -#define MODULEVERSION "1.02" - -/* - * Transmit and device reset timeout, default 5 seconds. - */ -static int watchdog = 5000; - -/* Hardware structures */ - -/** - * struct rx_ring_desc - Receive descriptor ring element - * @status: The status of the descriptor element (E.g. who owns it) - * @length: The number of bytes in the block pointed to by data_ptr - * @data_ptr: The physical address of the data block to receive into - * @next_desc: The physical address of the next descriptor element. - */ -struct rx_ring_desc { - __le32 status; - __le32 length; - __le32 data_ptr; - __le32 next_desc; -}; - -/** - * struct tx_ring_desc - Transmit descriptor ring element - * @owner: Who owns the descriptor - * @status: The number of bytes in the block pointed to by data_ptr - * @data_ptr: The physical address of the data block to receive into - * @next_desc: The physical address of the next descriptor element. - */ -struct tx_ring_desc { - __le32 owner; - __le32 status; - __le32 data_ptr; - __le32 next_desc; -}; - -/** - * struct ks8695_skbuff - sk_buff wrapper for rx/tx rings. - * @skb: The buffer in the ring - * @dma_ptr: The mapped DMA pointer of the buffer - * @length: The number of bytes mapped to dma_ptr - */ -struct ks8695_skbuff { - struct sk_buff *skb; - dma_addr_t dma_ptr; - u32 length; -}; - -/* Private device structure */ - -#define MAX_TX_DESC 8 -#define MAX_TX_DESC_MASK 0x7 -#define MAX_RX_DESC 16 -#define MAX_RX_DESC_MASK 0xf - -/*napi_weight have better more than rx DMA buffers*/ -#define NAPI_WEIGHT 64 - -#define MAX_RXBUF_SIZE 0x700 - -#define TX_RING_DMA_SIZE (sizeof(struct tx_ring_desc) * MAX_TX_DESC) -#define RX_RING_DMA_SIZE (sizeof(struct rx_ring_desc) * MAX_RX_DESC) -#define RING_DMA_SIZE (TX_RING_DMA_SIZE + RX_RING_DMA_SIZE) - -/** - * enum ks8695_dtype - Device type - * @KS8695_DTYPE_WAN: This device is a WAN interface - * @KS8695_DTYPE_LAN: This device is a LAN interface - * @KS8695_DTYPE_HPNA: This device is an HPNA interface - */ -enum ks8695_dtype { - KS8695_DTYPE_WAN, - KS8695_DTYPE_LAN, - KS8695_DTYPE_HPNA, -}; - -/** - * struct ks8695_priv - Private data for the KS8695 Ethernet - * @in_suspend: Flag to indicate if we're suspending/resuming - * @ndev: The net_device for this interface - * @dev: The platform device object for this interface - * @dtype: The type of this device - * @io_regs: The ioremapped registers for this interface - * @napi : Add support NAPI for Rx - * @rx_irq_name: The textual name of the RX IRQ from the platform data - * @tx_irq_name: The textual name of the TX IRQ from the platform data - * @link_irq_name: The textual name of the link IRQ from the - * platform data if available - * @rx_irq: The IRQ number for the RX IRQ - * @tx_irq: The IRQ number for the TX IRQ - * @link_irq: The IRQ number for the link IRQ if available - * @regs_req: The resource request for the registers region - * @phyiface_req: The resource request for the phy/switch region - * if available - * @phyiface_regs: The ioremapped registers for the phy/switch if available - * @ring_base: The base pointer of the dma coherent memory for the rings - * @ring_base_dma: The DMA mapped equivalent of ring_base - * @tx_ring: The pointer in ring_base of the TX ring - * @tx_ring_used: The number of slots in the TX ring which are occupied - * @tx_ring_next_slot: The next slot to fill in the TX ring - * @tx_ring_dma: The DMA mapped equivalent of tx_ring - * @tx_buffers: The sk_buff mappings for the TX ring - * @txq_lock: A lock to protect the tx_buffers tx_ring_used etc variables - * @rx_ring: The pointer in ring_base of the RX ring - * @rx_ring_dma: The DMA mapped equivalent of rx_ring - * @rx_buffers: The sk_buff mappings for the RX ring - * @next_rx_desc_read: The next RX descriptor to read from on IRQ - * @rx_lock: A lock to protect Rx irq function - * @msg_enable: The flags for which messages to emit - */ -struct ks8695_priv { - int in_suspend; - struct net_device *ndev; - struct device *dev; - enum ks8695_dtype dtype; - void __iomem *io_regs; - - struct napi_struct napi; - - const char *rx_irq_name, *tx_irq_name, *link_irq_name; - int rx_irq, tx_irq, link_irq; - - struct resource *regs_req, *phyiface_req; - void __iomem *phyiface_regs; - - void *ring_base; - dma_addr_t ring_base_dma; - - struct tx_ring_desc *tx_ring; - int tx_ring_used; - int tx_ring_next_slot; - dma_addr_t tx_ring_dma; - struct ks8695_skbuff tx_buffers[MAX_TX_DESC]; - spinlock_t txq_lock; - - struct rx_ring_desc *rx_ring; - dma_addr_t rx_ring_dma; - struct ks8695_skbuff rx_buffers[MAX_RX_DESC]; - int next_rx_desc_read; - spinlock_t rx_lock; - - int msg_enable; -}; - -/* Register access */ - -/** - * ks8695_readreg - Read from a KS8695 ethernet register - * @ksp: The device to read from - * @reg: The register to read - */ -static inline u32 -ks8695_readreg(struct ks8695_priv *ksp, int reg) -{ - return readl(ksp->io_regs + reg); -} - -/** - * ks8695_writereg - Write to a KS8695 ethernet register - * @ksp: The device to write to - * @reg: The register to write - * @value: The value to write to the register - */ -static inline void -ks8695_writereg(struct ks8695_priv *ksp, int reg, u32 value) -{ - writel(value, ksp->io_regs + reg); -} - -/* Utility functions */ - -/** - * ks8695_port_type - Retrieve port-type as user-friendly string - * @ksp: The device to return the type for - * - * Returns a string indicating which of the WAN, LAN or HPNA - * ports this device is likely to represent. - */ -static const char * -ks8695_port_type(struct ks8695_priv *ksp) -{ - switch (ksp->dtype) { - case KS8695_DTYPE_LAN: - return "LAN"; - case KS8695_DTYPE_WAN: - return "WAN"; - case KS8695_DTYPE_HPNA: - return "HPNA"; - } - - return "UNKNOWN"; -} - -/** - * ks8695_update_mac - Update the MAC registers in the device - * @ksp: The device to update - * - * Updates the MAC registers in the KS8695 device from the address in the - * net_device structure associated with this interface. - */ -static void -ks8695_update_mac(struct ks8695_priv *ksp) -{ - /* Update the HW with the MAC from the net_device */ - struct net_device *ndev = ksp->ndev; - u32 machigh, maclow; - - maclow = ((ndev->dev_addr[2] << 24) | (ndev->dev_addr[3] << 16) | - (ndev->dev_addr[4] << 8) | (ndev->dev_addr[5] << 0)); - machigh = ((ndev->dev_addr[0] << 8) | (ndev->dev_addr[1] << 0)); - - ks8695_writereg(ksp, KS8695_MAL, maclow); - ks8695_writereg(ksp, KS8695_MAH, machigh); - -} - -/** - * ks8695_refill_rxbuffers - Re-fill the RX buffer ring - * @ksp: The device to refill - * - * Iterates the RX ring of the device looking for empty slots. - * For each empty slot, we allocate and map a new SKB and give it - * to the hardware. - * This can be called from interrupt context safely. - */ -static void -ks8695_refill_rxbuffers(struct ks8695_priv *ksp) -{ - /* Run around the RX ring, filling in any missing sk_buff's */ - int buff_n; - - for (buff_n = 0; buff_n < MAX_RX_DESC; ++buff_n) { - if (!ksp->rx_buffers[buff_n].skb) { - struct sk_buff *skb = - netdev_alloc_skb(ksp->ndev, MAX_RXBUF_SIZE); - dma_addr_t mapping; - - ksp->rx_buffers[buff_n].skb = skb; - if (skb == NULL) { - /* Failed to allocate one, perhaps - * we'll try again later. - */ - break; - } - - mapping = dma_map_single(ksp->dev, skb->data, - MAX_RXBUF_SIZE, - DMA_FROM_DEVICE); - if (unlikely(dma_mapping_error(ksp->dev, mapping))) { - /* Failed to DMA map this SKB, try later */ - dev_kfree_skb_irq(skb); - ksp->rx_buffers[buff_n].skb = NULL; - break; - } - ksp->rx_buffers[buff_n].dma_ptr = mapping; - ksp->rx_buffers[buff_n].length = MAX_RXBUF_SIZE; - - /* Record this into the DMA ring */ - ksp->rx_ring[buff_n].data_ptr = cpu_to_le32(mapping); - ksp->rx_ring[buff_n].length = - cpu_to_le32(MAX_RXBUF_SIZE); - - wmb(); - - /* And give ownership over to the hardware */ - ksp->rx_ring[buff_n].status = cpu_to_le32(RDES_OWN); - } - } -} - -/* Maximum number of multicast addresses which the KS8695 HW supports */ -#define KS8695_NR_ADDRESSES 16 - -/** - * ks8695_init_partial_multicast - Init the mcast addr registers - * @ksp: The device to initialise - * @addr: The multicast address list to use - * @nr_addr: The number of addresses in the list - * - * This routine is a helper for ks8695_set_multicast - it writes - * the additional-address registers in the KS8695 ethernet device - * and cleans up any others left behind. - */ -static void -ks8695_init_partial_multicast(struct ks8695_priv *ksp, - struct net_device *ndev) -{ - u32 low, high; - int i; - struct netdev_hw_addr *ha; - - i = 0; - netdev_for_each_mc_addr(ha, ndev) { - /* Ran out of space in chip? */ - BUG_ON(i == KS8695_NR_ADDRESSES); - - low = (ha->addr[2] << 24) | (ha->addr[3] << 16) | - (ha->addr[4] << 8) | (ha->addr[5]); - high = (ha->addr[0] << 8) | (ha->addr[1]); - - ks8695_writereg(ksp, KS8695_AAL_(i), low); - ks8695_writereg(ksp, KS8695_AAH_(i), AAH_E | high); - i++; - } - - /* Clear the remaining Additional Station Addresses */ - for (; i < KS8695_NR_ADDRESSES; i++) { - ks8695_writereg(ksp, KS8695_AAL_(i), 0); - ks8695_writereg(ksp, KS8695_AAH_(i), 0); - } -} - -/* Interrupt handling */ - -/** - * ks8695_tx_irq - Transmit IRQ handler - * @irq: The IRQ which went off (ignored) - * @dev_id: The net_device for the interrupt - * - * Process the TX ring, clearing out any transmitted slots. - * Allows the net_device to pass us new packets once slots are - * freed. - */ -static irqreturn_t -ks8695_tx_irq(int irq, void *dev_id) -{ - struct net_device *ndev = (struct net_device *)dev_id; - struct ks8695_priv *ksp = netdev_priv(ndev); - int buff_n; - - for (buff_n = 0; buff_n < MAX_TX_DESC; ++buff_n) { - if (ksp->tx_buffers[buff_n].skb && - !(ksp->tx_ring[buff_n].owner & cpu_to_le32(TDES_OWN))) { - rmb(); - /* An SKB which is not owned by HW is present */ - /* Update the stats for the net_device */ - ndev->stats.tx_packets++; - ndev->stats.tx_bytes += ksp->tx_buffers[buff_n].length; - - /* Free the packet from the ring */ - ksp->tx_ring[buff_n].data_ptr = 0; - - /* Free the sk_buff */ - dma_unmap_single(ksp->dev, - ksp->tx_buffers[buff_n].dma_ptr, - ksp->tx_buffers[buff_n].length, - DMA_TO_DEVICE); - dev_consume_skb_irq(ksp->tx_buffers[buff_n].skb); - ksp->tx_buffers[buff_n].skb = NULL; - ksp->tx_ring_used--; - } - } - - netif_wake_queue(ndev); - - return IRQ_HANDLED; -} - -/** - * ks8695_get_rx_enable_bit - Get rx interrupt enable/status bit - * @ksp: Private data for the KS8695 Ethernet - * - * For KS8695 document: - * Interrupt Enable Register (offset 0xE204) - * Bit29 : WAN MAC Receive Interrupt Enable - * Bit16 : LAN MAC Receive Interrupt Enable - * Interrupt Status Register (Offset 0xF208) - * Bit29: WAN MAC Receive Status - * Bit16: LAN MAC Receive Status - * So, this Rx interrupt enable/status bit number is equal - * as Rx IRQ number. - */ -static inline u32 ks8695_get_rx_enable_bit(struct ks8695_priv *ksp) -{ - return ksp->rx_irq; -} - -/** - * ks8695_rx_irq - Receive IRQ handler - * @irq: The IRQ which went off (ignored) - * @dev_id: The net_device for the interrupt - * - * Inform NAPI that packet reception needs to be scheduled - */ - -static irqreturn_t -ks8695_rx_irq(int irq, void *dev_id) -{ - struct net_device *ndev = (struct net_device *)dev_id; - struct ks8695_priv *ksp = netdev_priv(ndev); - - spin_lock(&ksp->rx_lock); - - if (napi_schedule_prep(&ksp->napi)) { - unsigned long status = readl(KS8695_IRQ_VA + KS8695_INTEN); - unsigned long mask_bit = 1 << ks8695_get_rx_enable_bit(ksp); - /*disable rx interrupt*/ - status &= ~mask_bit; - writel(status , KS8695_IRQ_VA + KS8695_INTEN); - __napi_schedule(&ksp->napi); - } - - spin_unlock(&ksp->rx_lock); - return IRQ_HANDLED; -} - -/** - * ks8695_rx - Receive packets called by NAPI poll method - * @ksp: Private data for the KS8695 Ethernet - * @budget: Number of packets allowed to process - */ -static int ks8695_rx(struct ks8695_priv *ksp, int budget) -{ - struct net_device *ndev = ksp->ndev; - struct sk_buff *skb; - int buff_n; - u32 flags; - int pktlen; - int received = 0; - - buff_n = ksp->next_rx_desc_read; - while (received < budget - && ksp->rx_buffers[buff_n].skb - && (!(ksp->rx_ring[buff_n].status & - cpu_to_le32(RDES_OWN)))) { - rmb(); - flags = le32_to_cpu(ksp->rx_ring[buff_n].status); - - /* Found an SKB which we own, this means we - * received a packet - */ - if ((flags & (RDES_FS | RDES_LS)) != - (RDES_FS | RDES_LS)) { - /* This packet is not the first and - * the last segment. Therefore it is - * a "spanning" packet and we can't - * handle it - */ - goto rx_failure; - } - - if (flags & (RDES_ES | RDES_RE)) { - /* It's an error packet */ - ndev->stats.rx_errors++; - if (flags & RDES_TL) - ndev->stats.rx_length_errors++; - if (flags & RDES_RF) - ndev->stats.rx_length_errors++; - if (flags & RDES_CE) - ndev->stats.rx_crc_errors++; - if (flags & RDES_RE) - ndev->stats.rx_missed_errors++; - - goto rx_failure; - } - - pktlen = flags & RDES_FLEN; - pktlen -= 4; /* Drop the CRC */ - - /* Retrieve the sk_buff */ - skb = ksp->rx_buffers[buff_n].skb; - - /* Clear it from the ring */ - ksp->rx_buffers[buff_n].skb = NULL; - ksp->rx_ring[buff_n].data_ptr = 0; - - /* Unmap the SKB */ - dma_unmap_single(ksp->dev, - ksp->rx_buffers[buff_n].dma_ptr, - ksp->rx_buffers[buff_n].length, - DMA_FROM_DEVICE); - - /* Relinquish the SKB to the network layer */ - skb_put(skb, pktlen); - skb->protocol = eth_type_trans(skb, ndev); - napi_gro_receive(&ksp->napi, skb); - - /* Record stats */ - ndev->stats.rx_packets++; - ndev->stats.rx_bytes += pktlen; - goto rx_finished; - -rx_failure: - /* This ring entry is an error, but we can - * re-use the skb - */ - /* Give the ring entry back to the hardware */ - ksp->rx_ring[buff_n].status = cpu_to_le32(RDES_OWN); -rx_finished: - received++; - buff_n = (buff_n + 1) & MAX_RX_DESC_MASK; - } - - /* And note which RX descriptor we last did */ - ksp->next_rx_desc_read = buff_n; - - /* And refill the buffers */ - ks8695_refill_rxbuffers(ksp); - - /* Kick the RX DMA engine, in case it became suspended */ - ks8695_writereg(ksp, KS8695_DRSC, 0); - - return received; -} - - -/** - * ks8695_poll - Receive packet by NAPI poll method - * @ksp: Private data for the KS8695 Ethernet - * @budget: The remaining number packets for network subsystem - * - * Invoked by the network core when it requests for new - * packets from the driver - */ -static int ks8695_poll(struct napi_struct *napi, int budget) -{ - struct ks8695_priv *ksp = container_of(napi, struct ks8695_priv, napi); - unsigned long isr = readl(KS8695_IRQ_VA + KS8695_INTEN); - unsigned long mask_bit = 1 << ks8695_get_rx_enable_bit(ksp); - int work_done; - - work_done = ks8695_rx(ksp, budget); - - if (work_done < budget && napi_complete_done(napi, work_done)) { - unsigned long flags; - - spin_lock_irqsave(&ksp->rx_lock, flags); - /* enable rx interrupt */ - writel(isr | mask_bit, KS8695_IRQ_VA + KS8695_INTEN); - spin_unlock_irqrestore(&ksp->rx_lock, flags); - } - return work_done; -} - -/** - * ks8695_link_irq - Link change IRQ handler - * @irq: The IRQ which went off (ignored) - * @dev_id: The net_device for the interrupt - * - * The WAN interface can generate an IRQ when the link changes, - * report this to the net layer and the user. - */ -static irqreturn_t -ks8695_link_irq(int irq, void *dev_id) -{ - struct net_device *ndev = (struct net_device *)dev_id; - struct ks8695_priv *ksp = netdev_priv(ndev); - u32 ctrl; - - ctrl = readl(ksp->phyiface_regs + KS8695_WMC); - if (ctrl & WMC_WLS) { - netif_carrier_on(ndev); - if (netif_msg_link(ksp)) - dev_info(ksp->dev, - "%s: Link is now up (10%sMbps/%s-duplex)\n", - ndev->name, - (ctrl & WMC_WSS) ? "0" : "", - (ctrl & WMC_WDS) ? "Full" : "Half"); - } else { - netif_carrier_off(ndev); - if (netif_msg_link(ksp)) - dev_info(ksp->dev, "%s: Link is now down.\n", - ndev->name); - } - - return IRQ_HANDLED; -} - - -/* KS8695 Device functions */ - -/** - * ks8695_reset - Reset a KS8695 ethernet interface - * @ksp: The interface to reset - * - * Perform an engine reset of the interface and re-program it - * with sensible defaults. - */ -static void -ks8695_reset(struct ks8695_priv *ksp) -{ - int reset_timeout = watchdog; - /* Issue the reset via the TX DMA control register */ - ks8695_writereg(ksp, KS8695_DTXC, DTXC_TRST); - while (reset_timeout--) { - if (!(ks8695_readreg(ksp, KS8695_DTXC) & DTXC_TRST)) - break; - msleep(1); - } - - if (reset_timeout < 0) { - dev_crit(ksp->dev, - "Timeout waiting for DMA engines to reset\n"); - /* And blithely carry on */ - } - - /* Definitely wait long enough before attempting to program - * the engines - */ - msleep(10); - - /* RX: unicast and broadcast */ - ks8695_writereg(ksp, KS8695_DRXC, DRXC_RU | DRXC_RB); - /* TX: pad and add CRC */ - ks8695_writereg(ksp, KS8695_DTXC, DTXC_TEP | DTXC_TAC); -} - -/** - * ks8695_shutdown - Shut down a KS8695 ethernet interface - * @ksp: The interface to shut down - * - * This disables packet RX/TX, cleans up IRQs, drains the rings, - * and basically places the interface into a clean shutdown - * state. - */ -static void -ks8695_shutdown(struct ks8695_priv *ksp) -{ - u32 ctrl; - int buff_n; - - /* Disable packet transmission */ - ctrl = ks8695_readreg(ksp, KS8695_DTXC); - ks8695_writereg(ksp, KS8695_DTXC, ctrl & ~DTXC_TE); - - /* Disable packet reception */ - ctrl = ks8695_readreg(ksp, KS8695_DRXC); - ks8695_writereg(ksp, KS8695_DRXC, ctrl & ~DRXC_RE); - - /* Release the IRQs */ - free_irq(ksp->rx_irq, ksp->ndev); - free_irq(ksp->tx_irq, ksp->ndev); - if (ksp->link_irq != -1) - free_irq(ksp->link_irq, ksp->ndev); - - /* Throw away any pending TX packets */ - for (buff_n = 0; buff_n < MAX_TX_DESC; ++buff_n) { - if (ksp->tx_buffers[buff_n].skb) { - /* Remove this SKB from the TX ring */ - ksp->tx_ring[buff_n].owner = 0; - ksp->tx_ring[buff_n].status = 0; - ksp->tx_ring[buff_n].data_ptr = 0; - - /* Unmap and bin this SKB */ - dma_unmap_single(ksp->dev, - ksp->tx_buffers[buff_n].dma_ptr, - ksp->tx_buffers[buff_n].length, - DMA_TO_DEVICE); - dev_kfree_skb_irq(ksp->tx_buffers[buff_n].skb); - ksp->tx_buffers[buff_n].skb = NULL; - } - } - - /* Purge the RX buffers */ - for (buff_n = 0; buff_n < MAX_RX_DESC; ++buff_n) { - if (ksp->rx_buffers[buff_n].skb) { - /* Remove the SKB from the RX ring */ - ksp->rx_ring[buff_n].status = 0; - ksp->rx_ring[buff_n].data_ptr = 0; - - /* Unmap and bin the SKB */ - dma_unmap_single(ksp->dev, - ksp->rx_buffers[buff_n].dma_ptr, - ksp->rx_buffers[buff_n].length, - DMA_FROM_DEVICE); - dev_kfree_skb_irq(ksp->rx_buffers[buff_n].skb); - ksp->rx_buffers[buff_n].skb = NULL; - } - } -} - - -/** - * ks8695_setup_irq - IRQ setup helper function - * @irq: The IRQ number to claim - * @irq_name: The name to give the IRQ claimant - * @handler: The function to call to handle the IRQ - * @ndev: The net_device to pass in as the dev_id argument to the handler - * - * Return 0 on success. - */ -static int -ks8695_setup_irq(int irq, const char *irq_name, - irq_handler_t handler, struct net_device *ndev) -{ - int ret; - - ret = request_irq(irq, handler, IRQF_SHARED, irq_name, ndev); - - if (ret) { - dev_err(&ndev->dev, "failure to request IRQ %d\n", irq); - return ret; - } - - return 0; -} - -/** - * ks8695_init_net - Initialise a KS8695 ethernet interface - * @ksp: The interface to initialise - * - * This routine fills the RX ring, initialises the DMA engines, - * allocates the IRQs and then starts the packet TX and RX - * engines. - */ -static int -ks8695_init_net(struct ks8695_priv *ksp) -{ - int ret; - u32 ctrl; - - ks8695_refill_rxbuffers(ksp); - - /* Initialise the DMA engines */ - ks8695_writereg(ksp, KS8695_RDLB, (u32) ksp->rx_ring_dma); - ks8695_writereg(ksp, KS8695_TDLB, (u32) ksp->tx_ring_dma); - - /* Request the IRQs */ - ret = ks8695_setup_irq(ksp->rx_irq, ksp->rx_irq_name, - ks8695_rx_irq, ksp->ndev); - if (ret) - return ret; - ret = ks8695_setup_irq(ksp->tx_irq, ksp->tx_irq_name, - ks8695_tx_irq, ksp->ndev); - if (ret) - return ret; - if (ksp->link_irq != -1) { - ret = ks8695_setup_irq(ksp->link_irq, ksp->link_irq_name, - ks8695_link_irq, ksp->ndev); - if (ret) - return ret; - } - - /* Set up the ring indices */ - ksp->next_rx_desc_read = 0; - ksp->tx_ring_next_slot = 0; - ksp->tx_ring_used = 0; - - /* Bring up transmission */ - ctrl = ks8695_readreg(ksp, KS8695_DTXC); - /* Enable packet transmission */ - ks8695_writereg(ksp, KS8695_DTXC, ctrl | DTXC_TE); - - /* Bring up the reception */ - ctrl = ks8695_readreg(ksp, KS8695_DRXC); - /* Enable packet reception */ - ks8695_writereg(ksp, KS8695_DRXC, ctrl | DRXC_RE); - /* And start the DMA engine */ - ks8695_writereg(ksp, KS8695_DRSC, 0); - - /* All done */ - return 0; -} - -/** - * ks8695_release_device - HW resource release for KS8695 e-net - * @ksp: The device to be freed - * - * This unallocates io memory regions, dma-coherent regions etc - * which were allocated in ks8695_probe. - */ -static void -ks8695_release_device(struct ks8695_priv *ksp) -{ - /* Unmap the registers */ - iounmap(ksp->io_regs); - if (ksp->phyiface_regs) - iounmap(ksp->phyiface_regs); - - /* And release the request */ - release_resource(ksp->regs_req); - kfree(ksp->regs_req); - if (ksp->phyiface_req) { - release_resource(ksp->phyiface_req); - kfree(ksp->phyiface_req); - } - - /* Free the ring buffers */ - dma_free_coherent(ksp->dev, RING_DMA_SIZE, - ksp->ring_base, ksp->ring_base_dma); -} - -/* Ethtool support */ - -/** - * ks8695_get_msglevel - Get the messages enabled for emission - * @ndev: The network device to read from - */ -static u32 -ks8695_get_msglevel(struct net_device *ndev) -{ - struct ks8695_priv *ksp = netdev_priv(ndev); - - return ksp->msg_enable; -} - -/** - * ks8695_set_msglevel - Set the messages enabled for emission - * @ndev: The network device to configure - * @value: The messages to set for emission - */ -static void -ks8695_set_msglevel(struct net_device *ndev, u32 value) -{ - struct ks8695_priv *ksp = netdev_priv(ndev); - - ksp->msg_enable = value; -} - -/** - * ks8695_wan_get_link_ksettings - Get device-specific settings. - * @ndev: The network device to read settings from - * @cmd: The ethtool structure to read into - */ -static int -ks8695_wan_get_link_ksettings(struct net_device *ndev, - struct ethtool_link_ksettings *cmd) -{ - struct ks8695_priv *ksp = netdev_priv(ndev); - u32 ctrl; - u32 supported, advertising; - - /* All ports on the KS8695 support these... */ - supported = (SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full | - SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full | - SUPPORTED_TP | SUPPORTED_MII); - - advertising = ADVERTISED_TP | ADVERTISED_MII; - cmd->base.port = PORT_MII; - supported |= (SUPPORTED_Autoneg | SUPPORTED_Pause); - cmd->base.phy_address = 0; - - ctrl = readl(ksp->phyiface_regs + KS8695_WMC); - if ((ctrl & WMC_WAND) == 0) { - /* auto-negotiation is enabled */ - advertising |= ADVERTISED_Autoneg; - if (ctrl & WMC_WANA100F) - advertising |= ADVERTISED_100baseT_Full; - if (ctrl & WMC_WANA100H) - advertising |= ADVERTISED_100baseT_Half; - if (ctrl & WMC_WANA10F) - advertising |= ADVERTISED_10baseT_Full; - if (ctrl & WMC_WANA10H) - advertising |= ADVERTISED_10baseT_Half; - if (ctrl & WMC_WANAP) - advertising |= ADVERTISED_Pause; - cmd->base.autoneg = AUTONEG_ENABLE; - - cmd->base.speed = (ctrl & WMC_WSS) ? SPEED_100 : SPEED_10; - cmd->base.duplex = (ctrl & WMC_WDS) ? - DUPLEX_FULL : DUPLEX_HALF; - } else { - /* auto-negotiation is disabled */ - cmd->base.autoneg = AUTONEG_DISABLE; - - cmd->base.speed = (ctrl & WMC_WANF100) ? - SPEED_100 : SPEED_10; - cmd->base.duplex = (ctrl & WMC_WANFF) ? - DUPLEX_FULL : DUPLEX_HALF; - } - - ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported, - supported); - ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.advertising, - advertising); - - return 0; -} - -/** - * ks8695_wan_set_link_ksettings - Set device-specific settings. - * @ndev: The network device to configure - * @cmd: The settings to configure - */ -static int -ks8695_wan_set_link_ksettings(struct net_device *ndev, - const struct ethtool_link_ksettings *cmd) -{ - struct ks8695_priv *ksp = netdev_priv(ndev); - u32 ctrl; - u32 advertising; - - ethtool_convert_link_mode_to_legacy_u32(&advertising, - cmd->link_modes.advertising); - - if ((cmd->base.speed != SPEED_10) && (cmd->base.speed != SPEED_100)) - return -EINVAL; - if ((cmd->base.duplex != DUPLEX_HALF) && - (cmd->base.duplex != DUPLEX_FULL)) - return -EINVAL; - if (cmd->base.port != PORT_MII) - return -EINVAL; - if ((cmd->base.autoneg != AUTONEG_DISABLE) && - (cmd->base.autoneg != AUTONEG_ENABLE)) - return -EINVAL; - - if (cmd->base.autoneg == AUTONEG_ENABLE) { - if ((advertising & (ADVERTISED_10baseT_Half | - ADVERTISED_10baseT_Full | - ADVERTISED_100baseT_Half | - ADVERTISED_100baseT_Full)) == 0) - return -EINVAL; - - ctrl = readl(ksp->phyiface_regs + KS8695_WMC); - - ctrl &= ~(WMC_WAND | WMC_WANA100F | WMC_WANA100H | - WMC_WANA10F | WMC_WANA10H); - if (advertising & ADVERTISED_100baseT_Full) - ctrl |= WMC_WANA100F; - if (advertising & ADVERTISED_100baseT_Half) - ctrl |= WMC_WANA100H; - if (advertising & ADVERTISED_10baseT_Full) - ctrl |= WMC_WANA10F; - if (advertising & ADVERTISED_10baseT_Half) - ctrl |= WMC_WANA10H; - - /* force a re-negotiation */ - ctrl |= WMC_WANR; - writel(ctrl, ksp->phyiface_regs + KS8695_WMC); - } else { - ctrl = readl(ksp->phyiface_regs + KS8695_WMC); - - /* disable auto-negotiation */ - ctrl |= WMC_WAND; - ctrl &= ~(WMC_WANF100 | WMC_WANFF); - - if (cmd->base.speed == SPEED_100) - ctrl |= WMC_WANF100; - if (cmd->base.duplex == DUPLEX_FULL) - ctrl |= WMC_WANFF; - - writel(ctrl, ksp->phyiface_regs + KS8695_WMC); - } - - return 0; -} - -/** - * ks8695_wan_nwayreset - Restart the autonegotiation on the port. - * @ndev: The network device to restart autoneotiation on - */ -static int -ks8695_wan_nwayreset(struct net_device *ndev) -{ - struct ks8695_priv *ksp = netdev_priv(ndev); - u32 ctrl; - - ctrl = readl(ksp->phyiface_regs + KS8695_WMC); - - if ((ctrl & WMC_WAND) == 0) - writel(ctrl | WMC_WANR, - ksp->phyiface_regs + KS8695_WMC); - else - /* auto-negotiation not enabled */ - return -EINVAL; - - return 0; -} - -/** - * ks8695_wan_get_pause - Retrieve network pause/flow-control advertising - * @ndev: The device to retrieve settings from - * @param: The structure to fill out with the information - */ -static void -ks8695_wan_get_pause(struct net_device *ndev, struct ethtool_pauseparam *param) -{ - struct ks8695_priv *ksp = netdev_priv(ndev); - u32 ctrl; - - ctrl = readl(ksp->phyiface_regs + KS8695_WMC); - - /* advertise Pause */ - param->autoneg = (ctrl & WMC_WANAP); - - /* current Rx Flow-control */ - ctrl = ks8695_readreg(ksp, KS8695_DRXC); - param->rx_pause = (ctrl & DRXC_RFCE); - - /* current Tx Flow-control */ - ctrl = ks8695_readreg(ksp, KS8695_DTXC); - param->tx_pause = (ctrl & DTXC_TFCE); -} - -/** - * ks8695_get_drvinfo - Retrieve driver information - * @ndev: The network device to retrieve info about - * @info: The info structure to fill out. - */ -static void -ks8695_get_drvinfo(struct net_device *ndev, struct ethtool_drvinfo *info) -{ - strlcpy(info->driver, MODULENAME, sizeof(info->driver)); - strlcpy(info->version, MODULEVERSION, sizeof(info->version)); - strlcpy(info->bus_info, dev_name(ndev->dev.parent), - sizeof(info->bus_info)); -} - -static const struct ethtool_ops ks8695_ethtool_ops = { - .get_msglevel = ks8695_get_msglevel, - .set_msglevel = ks8695_set_msglevel, - .get_drvinfo = ks8695_get_drvinfo, -}; - -static const struct ethtool_ops ks8695_wan_ethtool_ops = { - .get_msglevel = ks8695_get_msglevel, - .set_msglevel = ks8695_set_msglevel, - .nway_reset = ks8695_wan_nwayreset, - .get_link = ethtool_op_get_link, - .get_pauseparam = ks8695_wan_get_pause, - .get_drvinfo = ks8695_get_drvinfo, - .get_link_ksettings = ks8695_wan_get_link_ksettings, - .set_link_ksettings = ks8695_wan_set_link_ksettings, -}; - -/* Network device interface functions */ - -/** - * ks8695_set_mac - Update MAC in net dev and HW - * @ndev: The network device to update - * @addr: The new MAC address to set - */ -static int -ks8695_set_mac(struct net_device *ndev, void *addr) -{ - struct ks8695_priv *ksp = netdev_priv(ndev); - struct sockaddr *address = addr; - - if (!is_valid_ether_addr(address->sa_data)) - return -EADDRNOTAVAIL; - - memcpy(ndev->dev_addr, address->sa_data, ndev->addr_len); - - ks8695_update_mac(ksp); - - dev_dbg(ksp->dev, "%s: Updated MAC address to %pM\n", - ndev->name, ndev->dev_addr); - - return 0; -} - -/** - * ks8695_set_multicast - Set up the multicast behaviour of the interface - * @ndev: The net_device to configure - * - * This routine, called by the net layer, configures promiscuity - * and multicast reception behaviour for the interface. - */ -static void -ks8695_set_multicast(struct net_device *ndev) -{ - struct ks8695_priv *ksp = netdev_priv(ndev); - u32 ctrl; - - ctrl = ks8695_readreg(ksp, KS8695_DRXC); - - if (ndev->flags & IFF_PROMISC) { - /* enable promiscuous mode */ - ctrl |= DRXC_RA; - } else if (ndev->flags & ~IFF_PROMISC) { - /* disable promiscuous mode */ - ctrl &= ~DRXC_RA; - } - - if (ndev->flags & IFF_ALLMULTI) { - /* enable all multicast mode */ - ctrl |= DRXC_RM; - } else if (netdev_mc_count(ndev) > KS8695_NR_ADDRESSES) { - /* more specific multicast addresses than can be - * handled in hardware - */ - ctrl |= DRXC_RM; - } else { - /* enable specific multicasts */ - ctrl &= ~DRXC_RM; - ks8695_init_partial_multicast(ksp, ndev); - } - - ks8695_writereg(ksp, KS8695_DRXC, ctrl); -} - -/** - * ks8695_timeout - Handle a network tx/rx timeout. - * @ndev: The net_device which timed out. - * - * A network transaction timed out, reset the device. - */ -static void -ks8695_timeout(struct net_device *ndev) -{ - struct ks8695_priv *ksp = netdev_priv(ndev); - - netif_stop_queue(ndev); - ks8695_shutdown(ksp); - - ks8695_reset(ksp); - - ks8695_update_mac(ksp); - - /* We ignore the return from this since it managed to init - * before it probably will be okay to init again. - */ - ks8695_init_net(ksp); - - /* Reconfigure promiscuity etc */ - ks8695_set_multicast(ndev); - - /* And start the TX queue once more */ - netif_start_queue(ndev); -} - -/** - * ks8695_start_xmit - Start a packet transmission - * @skb: The packet to transmit - * @ndev: The network device to send the packet on - * - * This routine, called by the net layer, takes ownership of the - * sk_buff and adds it to the TX ring. It then kicks the TX DMA - * engine to ensure transmission begins. - */ -static netdev_tx_t -ks8695_start_xmit(struct sk_buff *skb, struct net_device *ndev) -{ - struct ks8695_priv *ksp = netdev_priv(ndev); - int buff_n; - dma_addr_t dmap; - - spin_lock_irq(&ksp->txq_lock); - - if (ksp->tx_ring_used == MAX_TX_DESC) { - /* Somehow we got entered when we have no room */ - spin_unlock_irq(&ksp->txq_lock); - return NETDEV_TX_BUSY; - } - - buff_n = ksp->tx_ring_next_slot; - - BUG_ON(ksp->tx_buffers[buff_n].skb); - - dmap = dma_map_single(ksp->dev, skb->data, skb->len, DMA_TO_DEVICE); - if (unlikely(dma_mapping_error(ksp->dev, dmap))) { - /* Failed to DMA map this SKB, give it back for now */ - spin_unlock_irq(&ksp->txq_lock); - dev_dbg(ksp->dev, "%s: Could not map DMA memory for "\ - "transmission, trying later\n", ndev->name); - return NETDEV_TX_BUSY; - } - - ksp->tx_buffers[buff_n].dma_ptr = dmap; - /* Mapped okay, store the buffer pointer and length for later */ - ksp->tx_buffers[buff_n].skb = skb; - ksp->tx_buffers[buff_n].length = skb->len; - - /* Fill out the TX descriptor */ - ksp->tx_ring[buff_n].data_ptr = - cpu_to_le32(ksp->tx_buffers[buff_n].dma_ptr); - ksp->tx_ring[buff_n].status = - cpu_to_le32(TDES_IC | TDES_FS | TDES_LS | - (skb->len & TDES_TBS)); - - wmb(); - - /* Hand it over to the hardware */ - ksp->tx_ring[buff_n].owner = cpu_to_le32(TDES_OWN); - - if (++ksp->tx_ring_used == MAX_TX_DESC) - netif_stop_queue(ndev); - - /* Kick the TX DMA in case it decided to go IDLE */ - ks8695_writereg(ksp, KS8695_DTSC, 0); - - /* And update the next ring slot */ - ksp->tx_ring_next_slot = (buff_n + 1) & MAX_TX_DESC_MASK; - - spin_unlock_irq(&ksp->txq_lock); - return NETDEV_TX_OK; -} - -/** - * ks8695_stop - Stop (shutdown) a KS8695 ethernet interface - * @ndev: The net_device to stop - * - * This disables the TX queue and cleans up a KS8695 ethernet - * device. - */ -static int -ks8695_stop(struct net_device *ndev) -{ - struct ks8695_priv *ksp = netdev_priv(ndev); - - netif_stop_queue(ndev); - napi_disable(&ksp->napi); - - ks8695_shutdown(ksp); - - return 0; -} - -/** - * ks8695_open - Open (bring up) a KS8695 ethernet interface - * @ndev: The net_device to open - * - * This resets, configures the MAC, initialises the RX ring and - * DMA engines and starts the TX queue for a KS8695 ethernet - * device. - */ -static int -ks8695_open(struct net_device *ndev) -{ - struct ks8695_priv *ksp = netdev_priv(ndev); - int ret; - - ks8695_reset(ksp); - - ks8695_update_mac(ksp); - - ret = ks8695_init_net(ksp); - if (ret) { - ks8695_shutdown(ksp); - return ret; - } - - napi_enable(&ksp->napi); - netif_start_queue(ndev); - - return 0; -} - -/* Platform device driver */ - -/** - * ks8695_init_switch - Init LAN switch to known good defaults. - * @ksp: The device to initialise - * - * This initialises the LAN switch in the KS8695 to a known-good - * set of defaults. - */ -static void -ks8695_init_switch(struct ks8695_priv *ksp) -{ - u32 ctrl; - - /* Default value for SEC0 according to datasheet */ - ctrl = 0x40819e00; - - /* LED0 = Speed LED1 = Link/Activity */ - ctrl &= ~(SEC0_LLED1S | SEC0_LLED0S); - ctrl |= (LLED0S_LINK | LLED1S_LINK_ACTIVITY); - - /* Enable Switch */ - ctrl |= SEC0_ENABLE; - - writel(ctrl, ksp->phyiface_regs + KS8695_SEC0); - - /* Defaults for SEC1 */ - writel(0x9400100, ksp->phyiface_regs + KS8695_SEC1); -} - -/** - * ks8695_init_wan_phy - Initialise the WAN PHY to sensible defaults - * @ksp: The device to initialise - * - * This initialises a KS8695's WAN phy to sensible values for - * autonegotiation etc. - */ -static void -ks8695_init_wan_phy(struct ks8695_priv *ksp) -{ - u32 ctrl; - - /* Support auto-negotiation */ - ctrl = (WMC_WANAP | WMC_WANA100F | WMC_WANA100H | - WMC_WANA10F | WMC_WANA10H); - - /* LED0 = Activity , LED1 = Link */ - ctrl |= (WLED0S_ACTIVITY | WLED1S_LINK); - - /* Restart Auto-negotiation */ - ctrl |= WMC_WANR; - - writel(ctrl, ksp->phyiface_regs + KS8695_WMC); - - writel(0, ksp->phyiface_regs + KS8695_WPPM); - writel(0, ksp->phyiface_regs + KS8695_PPS); -} - -static const struct net_device_ops ks8695_netdev_ops = { - .ndo_open = ks8695_open, - .ndo_stop = ks8695_stop, - .ndo_start_xmit = ks8695_start_xmit, - .ndo_tx_timeout = ks8695_timeout, - .ndo_set_mac_address = ks8695_set_mac, - .ndo_validate_addr = eth_validate_addr, - .ndo_set_rx_mode = ks8695_set_multicast, -}; - -/** - * ks8695_probe - Probe and initialise a KS8695 ethernet interface - * @pdev: The platform device to probe - * - * Initialise a KS8695 ethernet device from platform data. - * - * This driver requires at least one IORESOURCE_MEM for the - * registers and two IORESOURCE_IRQ for the RX and TX IRQs - * respectively. It can optionally take an additional - * IORESOURCE_MEM for the switch or phy in the case of the lan or - * wan ports, and an IORESOURCE_IRQ for the link IRQ for the wan - * port. - */ -static int -ks8695_probe(struct platform_device *pdev) -{ - struct ks8695_priv *ksp; - struct net_device *ndev; - struct resource *regs_res, *phyiface_res; - struct resource *rxirq_res, *txirq_res, *linkirq_res; - int ret = 0; - int buff_n; - bool inv_mac_addr = false; - u32 machigh, maclow; - - /* Initialise a net_device */ - ndev = alloc_etherdev(sizeof(struct ks8695_priv)); - if (!ndev) - return -ENOMEM; - - SET_NETDEV_DEV(ndev, &pdev->dev); - - dev_dbg(&pdev->dev, "ks8695_probe() called\n"); - - /* Configure our private structure a little */ - ksp = netdev_priv(ndev); - - ksp->dev = &pdev->dev; - ksp->ndev = ndev; - ksp->msg_enable = NETIF_MSG_LINK; - - /* Retrieve resources */ - regs_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - phyiface_res = platform_get_resource(pdev, IORESOURCE_MEM, 1); - - rxirq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - txirq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 1); - linkirq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 2); - - if (!(regs_res && rxirq_res && txirq_res)) { - dev_err(ksp->dev, "insufficient resources\n"); - ret = -ENOENT; - goto failure; - } - - ksp->regs_req = request_mem_region(regs_res->start, - resource_size(regs_res), - pdev->name); - - if (!ksp->regs_req) { - dev_err(ksp->dev, "cannot claim register space\n"); - ret = -EIO; - goto failure; - } - - ksp->io_regs = ioremap(regs_res->start, resource_size(regs_res)); - - if (!ksp->io_regs) { - dev_err(ksp->dev, "failed to ioremap registers\n"); - ret = -EINVAL; - goto failure; - } - - if (phyiface_res) { - ksp->phyiface_req = - request_mem_region(phyiface_res->start, - resource_size(phyiface_res), - phyiface_res->name); - - if (!ksp->phyiface_req) { - dev_err(ksp->dev, - "cannot claim switch register space\n"); - ret = -EIO; - goto failure; - } - - ksp->phyiface_regs = ioremap(phyiface_res->start, - resource_size(phyiface_res)); - - if (!ksp->phyiface_regs) { - dev_err(ksp->dev, - "failed to ioremap switch registers\n"); - ret = -EINVAL; - goto failure; - } - } - - ksp->rx_irq = rxirq_res->start; - ksp->rx_irq_name = rxirq_res->name ? rxirq_res->name : "Ethernet RX"; - ksp->tx_irq = txirq_res->start; - ksp->tx_irq_name = txirq_res->name ? txirq_res->name : "Ethernet TX"; - ksp->link_irq = (linkirq_res ? linkirq_res->start : -1); - ksp->link_irq_name = (linkirq_res && linkirq_res->name) ? - linkirq_res->name : "Ethernet Link"; - - /* driver system setup */ - ndev->netdev_ops = &ks8695_netdev_ops; - ndev->watchdog_timeo = msecs_to_jiffies(watchdog); - - netif_napi_add(ndev, &ksp->napi, ks8695_poll, NAPI_WEIGHT); - - /* Retrieve the default MAC addr from the chip. */ - /* The bootloader should have left it in there for us. */ - - machigh = ks8695_readreg(ksp, KS8695_MAH); - maclow = ks8695_readreg(ksp, KS8695_MAL); - - ndev->dev_addr[0] = (machigh >> 8) & 0xFF; - ndev->dev_addr[1] = machigh & 0xFF; - ndev->dev_addr[2] = (maclow >> 24) & 0xFF; - ndev->dev_addr[3] = (maclow >> 16) & 0xFF; - ndev->dev_addr[4] = (maclow >> 8) & 0xFF; - ndev->dev_addr[5] = maclow & 0xFF; - - if (!is_valid_ether_addr(ndev->dev_addr)) - inv_mac_addr = true; - - /* In order to be efficient memory-wise, we allocate both - * rings in one go. - */ - ksp->ring_base = dma_alloc_coherent(&pdev->dev, RING_DMA_SIZE, - &ksp->ring_base_dma, GFP_KERNEL); - if (!ksp->ring_base) { - ret = -ENOMEM; - goto failure; - } - - /* Specify the TX DMA ring buffer */ - ksp->tx_ring = ksp->ring_base; - ksp->tx_ring_dma = ksp->ring_base_dma; - - /* And initialise the queue's lock */ - spin_lock_init(&ksp->txq_lock); - spin_lock_init(&ksp->rx_lock); - - /* Specify the RX DMA ring buffer */ - ksp->rx_ring = ksp->ring_base + TX_RING_DMA_SIZE; - ksp->rx_ring_dma = ksp->ring_base_dma + TX_RING_DMA_SIZE; - - /* Zero the descriptor rings */ - memset(ksp->tx_ring, 0, TX_RING_DMA_SIZE); - memset(ksp->rx_ring, 0, RX_RING_DMA_SIZE); - - /* Build the rings */ - for (buff_n = 0; buff_n < MAX_TX_DESC; ++buff_n) { - ksp->tx_ring[buff_n].next_desc = - cpu_to_le32(ksp->tx_ring_dma + - (sizeof(struct tx_ring_desc) * - ((buff_n + 1) & MAX_TX_DESC_MASK))); - } - - for (buff_n = 0; buff_n < MAX_RX_DESC; ++buff_n) { - ksp->rx_ring[buff_n].next_desc = - cpu_to_le32(ksp->rx_ring_dma + - (sizeof(struct rx_ring_desc) * - ((buff_n + 1) & MAX_RX_DESC_MASK))); - } - - /* Initialise the port (physically) */ - if (ksp->phyiface_regs && ksp->link_irq == -1) { - ks8695_init_switch(ksp); - ksp->dtype = KS8695_DTYPE_LAN; - ndev->ethtool_ops = &ks8695_ethtool_ops; - } else if (ksp->phyiface_regs && ksp->link_irq != -1) { - ks8695_init_wan_phy(ksp); - ksp->dtype = KS8695_DTYPE_WAN; - ndev->ethtool_ops = &ks8695_wan_ethtool_ops; - } else { - /* No initialisation since HPNA does not have a PHY */ - ksp->dtype = KS8695_DTYPE_HPNA; - ndev->ethtool_ops = &ks8695_ethtool_ops; - } - - /* And bring up the net_device with the net core */ - platform_set_drvdata(pdev, ndev); - ret = register_netdev(ndev); - - if (ret == 0) { - if (inv_mac_addr) - dev_warn(ksp->dev, "%s: Invalid ethernet MAC address. Please set using ip\n", - ndev->name); - dev_info(ksp->dev, "ks8695 ethernet (%s) MAC: %pM\n", - ks8695_port_type(ksp), ndev->dev_addr); - } else { - /* Report the failure to register the net_device */ - dev_err(ksp->dev, "ks8695net: failed to register netdev.\n"); - goto failure; - } - - /* All is well */ - return 0; - - /* Error exit path */ -failure: - ks8695_release_device(ksp); - free_netdev(ndev); - - return ret; -} - -/** - * ks8695_drv_suspend - Suspend a KS8695 ethernet platform device. - * @pdev: The device to suspend - * @state: The suspend state - * - * This routine detaches and shuts down a KS8695 ethernet device. - */ -static int -ks8695_drv_suspend(struct platform_device *pdev, pm_message_t state) -{ - struct net_device *ndev = platform_get_drvdata(pdev); - struct ks8695_priv *ksp = netdev_priv(ndev); - - ksp->in_suspend = 1; - - if (netif_running(ndev)) { - netif_device_detach(ndev); - ks8695_shutdown(ksp); - } - - return 0; -} - -/** - * ks8695_drv_resume - Resume a KS8695 ethernet platform device. - * @pdev: The device to resume - * - * This routine re-initialises and re-attaches a KS8695 ethernet - * device. - */ -static int -ks8695_drv_resume(struct platform_device *pdev) -{ - struct net_device *ndev = platform_get_drvdata(pdev); - struct ks8695_priv *ksp = netdev_priv(ndev); - - if (netif_running(ndev)) { - ks8695_reset(ksp); - ks8695_init_net(ksp); - ks8695_set_multicast(ndev); - netif_device_attach(ndev); - } - - ksp->in_suspend = 0; - - return 0; -} - -/** - * ks8695_drv_remove - Remove a KS8695 net device on driver unload. - * @pdev: The platform device to remove - * - * This unregisters and releases a KS8695 ethernet device. - */ -static int -ks8695_drv_remove(struct platform_device *pdev) -{ - struct net_device *ndev = platform_get_drvdata(pdev); - struct ks8695_priv *ksp = netdev_priv(ndev); - - netif_napi_del(&ksp->napi); - - unregister_netdev(ndev); - ks8695_release_device(ksp); - free_netdev(ndev); - - dev_dbg(&pdev->dev, "released and freed device\n"); - return 0; -} - -static struct platform_driver ks8695_driver = { - .driver = { - .name = MODULENAME, - }, - .probe = ks8695_probe, - .remove = ks8695_drv_remove, - .suspend = ks8695_drv_suspend, - .resume = ks8695_drv_resume, -}; - -module_platform_driver(ks8695_driver); - -MODULE_AUTHOR("Simtec Electronics"); -MODULE_DESCRIPTION("Micrel KS8695 (Centaur) Ethernet driver"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:" MODULENAME); - -module_param(watchdog, int, 0400); -MODULE_PARM_DESC(watchdog, "transmit timeout in milliseconds"); diff --git a/drivers/net/ethernet/micrel/ks8695net.h b/drivers/net/ethernet/micrel/ks8695net.h deleted file mode 100644 index b18fad4ad5fd..000000000000 --- a/drivers/net/ethernet/micrel/ks8695net.h +++ /dev/null @@ -1,108 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * Micrel KS8695 (Centaur) Ethernet. - * - * Copyright 2008 Simtec Electronics - * Daniel Silverstone <dsilvers@simtec.co.uk> - * Vincent Sanders <vince@simtec.co.uk> - */ - -#ifndef KS8695NET_H -#define KS8695NET_H - -/* Receive descriptor flags */ -#define RDES_OWN (1 << 31) /* Ownership */ -#define RDES_FS (1 << 30) /* First Descriptor */ -#define RDES_LS (1 << 29) /* Last Descriptor */ -#define RDES_IPE (1 << 28) /* IP Checksum error */ -#define RDES_TCPE (1 << 27) /* TCP Checksum error */ -#define RDES_UDPE (1 << 26) /* UDP Checksum error */ -#define RDES_ES (1 << 25) /* Error summary */ -#define RDES_MF (1 << 24) /* Multicast Frame */ -#define RDES_RE (1 << 19) /* MII Error reported */ -#define RDES_TL (1 << 18) /* Frame too Long */ -#define RDES_RF (1 << 17) /* Runt Frame */ -#define RDES_CE (1 << 16) /* CRC error */ -#define RDES_FT (1 << 15) /* Frame Type */ -#define RDES_FLEN (0x7ff) /* Frame Length */ - -#define RDES_RER (1 << 25) /* Receive End of Ring */ -#define RDES_RBS (0x7ff) /* Receive Buffer Size */ - -/* Transmit descriptor flags */ - -#define TDES_OWN (1 << 31) /* Ownership */ - -#define TDES_IC (1 << 31) /* Interrupt on Completion */ -#define TDES_FS (1 << 30) /* First Segment */ -#define TDES_LS (1 << 29) /* Last Segment */ -#define TDES_IPCKG (1 << 28) /* IP Checksum generate */ -#define TDES_TCPCKG (1 << 27) /* TCP Checksum generate */ -#define TDES_UDPCKG (1 << 26) /* UDP Checksum generate */ -#define TDES_TER (1 << 25) /* Transmit End of Ring */ -#define TDES_TBS (0x7ff) /* Transmit Buffer Size */ - -/* - * Network controller register offsets - */ -#define KS8695_DTXC (0x00) /* DMA Transmit Control */ -#define KS8695_DRXC (0x04) /* DMA Receive Control */ -#define KS8695_DTSC (0x08) /* DMA Transmit Start Command */ -#define KS8695_DRSC (0x0c) /* DMA Receive Start Command */ -#define KS8695_TDLB (0x10) /* Transmit Descriptor List - * Base Address - */ -#define KS8695_RDLB (0x14) /* Receive Descriptor List - * Base Address - */ -#define KS8695_MAL (0x18) /* MAC Station Address Low */ -#define KS8695_MAH (0x1c) /* MAC Station Address High */ -#define KS8695_AAL_(n) (0x80 + ((n)*8)) /* MAC Additional - * Station Address - * (0..15) Low - */ -#define KS8695_AAH_(n) (0x84 + ((n)*8)) /* MAC Additional - * Station Address - * (0..15) High - */ - - -/* DMA Transmit Control Register */ -#define DTXC_TRST (1 << 31) /* Soft Reset */ -#define DTXC_TBS (0x3f << 24) /* Transmit Burst Size */ -#define DTXC_TUCG (1 << 18) /* Transmit UDP - * Checksum Generate - */ -#define DTXC_TTCG (1 << 17) /* Transmit TCP - * Checksum Generate - */ -#define DTXC_TICG (1 << 16) /* Transmit IP - * Checksum Generate - */ -#define DTXC_TFCE (1 << 9) /* Transmit Flow - * Control Enable - */ -#define DTXC_TLB (1 << 8) /* Loopback mode */ -#define DTXC_TEP (1 << 2) /* Transmit Enable Padding */ -#define DTXC_TAC (1 << 1) /* Transmit Add CRC */ -#define DTXC_TE (1 << 0) /* TX Enable */ - -/* DMA Receive Control Register */ -#define DRXC_RBS (0x3f << 24) /* Receive Burst Size */ -#define DRXC_RUCC (1 << 18) /* Receive UDP Checksum check */ -#define DRXC_RTCG (1 << 17) /* Receive TCP Checksum check */ -#define DRXC_RICG (1 << 16) /* Receive IP Checksum check */ -#define DRXC_RFCE (1 << 9) /* Receive Flow Control - * Enable - */ -#define DRXC_RB (1 << 6) /* Receive Broadcast */ -#define DRXC_RM (1 << 5) /* Receive Multicast */ -#define DRXC_RU (1 << 4) /* Receive Unicast */ -#define DRXC_RERR (1 << 3) /* Receive Error Frame */ -#define DRXC_RA (1 << 2) /* Receive All */ -#define DRXC_RE (1 << 0) /* RX Enable */ - -/* Additional Station Address High */ -#define AAH_E (1 << 31) /* Address Enabled */ - -#endif /* KS8695NET_H */ diff --git a/drivers/net/ethernet/nuvoton/Kconfig b/drivers/net/ethernet/nuvoton/Kconfig deleted file mode 100644 index 325e26c549f8..000000000000 --- a/drivers/net/ethernet/nuvoton/Kconfig +++ /dev/null @@ -1,29 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -# -# Nuvoton network device configuration -# - -config NET_VENDOR_NUVOTON - bool "Nuvoton devices" - default y - depends on ARM && ARCH_W90X900 - ---help--- - If you have a network (Ethernet) card belonging to this class, say Y. - - Note that the answer to this question doesn't directly affect the - kernel: saying N will just cause the configurator to skip all - the questions about Nuvoton cards. If you say Y, you will be asked - for your specific card in the following questions. - -if NET_VENDOR_NUVOTON - -config W90P910_ETH - tristate "Nuvoton w90p910 Ethernet support" - depends on ARM && ARCH_W90X900 - select PHYLIB - select MII - ---help--- - Say Y here if you want to use built-in Ethernet ports - on w90p910 processor. - -endif # NET_VENDOR_NUVOTON diff --git a/drivers/net/ethernet/nuvoton/Makefile b/drivers/net/ethernet/nuvoton/Makefile deleted file mode 100644 index 66f6e728d54b..000000000000 --- a/drivers/net/ethernet/nuvoton/Makefile +++ /dev/null @@ -1,6 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -# -# Makefile for the Nuvoton network device drivers. -# - -obj-$(CONFIG_W90P910_ETH) += w90p910_ether.o diff --git a/drivers/net/ethernet/nuvoton/w90p910_ether.c b/drivers/net/ethernet/nuvoton/w90p910_ether.c deleted file mode 100644 index 3d73970b3a2e..000000000000 --- a/drivers/net/ethernet/nuvoton/w90p910_ether.c +++ /dev/null @@ -1,1082 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (c) 2008-2009 Nuvoton technology corporation. - * - * Wan ZongShun <mcuos.com@gmail.com> - */ - -#include <linux/module.h> -#include <linux/init.h> -#include <linux/interrupt.h> -#include <linux/mii.h> -#include <linux/netdevice.h> -#include <linux/etherdevice.h> -#include <linux/skbuff.h> -#include <linux/ethtool.h> -#include <linux/platform_device.h> -#include <linux/clk.h> -#include <linux/gfp.h> - -#define DRV_MODULE_NAME "w90p910-emc" -#define DRV_MODULE_VERSION "0.1" - -/* Ethernet MAC Registers */ -#define REG_CAMCMR 0x00 -#define REG_CAMEN 0x04 -#define REG_CAMM_BASE 0x08 -#define REG_CAML_BASE 0x0c -#define REG_TXDLSA 0x88 -#define REG_RXDLSA 0x8C -#define REG_MCMDR 0x90 -#define REG_MIID 0x94 -#define REG_MIIDA 0x98 -#define REG_FFTCR 0x9C -#define REG_TSDR 0xa0 -#define REG_RSDR 0xa4 -#define REG_DMARFC 0xa8 -#define REG_MIEN 0xac -#define REG_MISTA 0xb0 -#define REG_CTXDSA 0xcc -#define REG_CTXBSA 0xd0 -#define REG_CRXDSA 0xd4 -#define REG_CRXBSA 0xd8 - -/* mac controller bit */ -#define MCMDR_RXON 0x01 -#define MCMDR_ACP (0x01 << 3) -#define MCMDR_SPCRC (0x01 << 5) -#define MCMDR_TXON (0x01 << 8) -#define MCMDR_FDUP (0x01 << 18) -#define MCMDR_ENMDC (0x01 << 19) -#define MCMDR_OPMOD (0x01 << 20) -#define SWR (0x01 << 24) - -/* cam command regiser */ -#define CAMCMR_AUP 0x01 -#define CAMCMR_AMP (0x01 << 1) -#define CAMCMR_ABP (0x01 << 2) -#define CAMCMR_CCAM (0x01 << 3) -#define CAMCMR_ECMP (0x01 << 4) -#define CAM0EN 0x01 - -/* mac mii controller bit */ -#define MDCCR (0x0a << 20) -#define PHYAD (0x01 << 8) -#define PHYWR (0x01 << 16) -#define PHYBUSY (0x01 << 17) -#define PHYPRESP (0x01 << 18) -#define CAM_ENTRY_SIZE 0x08 - -/* rx and tx status */ -#define TXDS_TXCP (0x01 << 19) -#define RXDS_CRCE (0x01 << 17) -#define RXDS_PTLE (0x01 << 19) -#define RXDS_RXGD (0x01 << 20) -#define RXDS_ALIE (0x01 << 21) -#define RXDS_RP (0x01 << 22) - -/* mac interrupt status*/ -#define MISTA_EXDEF (0x01 << 19) -#define MISTA_TXBERR (0x01 << 24) -#define MISTA_TDU (0x01 << 23) -#define MISTA_RDU (0x01 << 10) -#define MISTA_RXBERR (0x01 << 11) - -#define ENSTART 0x01 -#define ENRXINTR 0x01 -#define ENRXGD (0x01 << 4) -#define ENRXBERR (0x01 << 11) -#define ENTXINTR (0x01 << 16) -#define ENTXCP (0x01 << 18) -#define ENTXABT (0x01 << 21) -#define ENTXBERR (0x01 << 24) -#define ENMDC (0x01 << 19) -#define PHYBUSY (0x01 << 17) -#define MDCCR_VAL 0xa00000 - -/* rx and tx owner bit */ -#define RX_OWEN_DMA (0x01 << 31) -#define RX_OWEN_CPU (~(0x03 << 30)) -#define TX_OWEN_DMA (0x01 << 31) -#define TX_OWEN_CPU (~(0x01 << 31)) - -/* tx frame desc controller bit */ -#define MACTXINTEN 0x04 -#define CRCMODE 0x02 -#define PADDINGMODE 0x01 - -/* fftcr controller bit */ -#define TXTHD (0x03 << 8) -#define BLENGTH (0x01 << 20) - -/* global setting for driver */ -#define RX_DESC_SIZE 50 -#define TX_DESC_SIZE 10 -#define MAX_RBUFF_SZ 0x600 -#define MAX_TBUFF_SZ 0x600 -#define TX_TIMEOUT (HZ/2) -#define DELAY 1000 -#define CAM0 0x0 - -static int w90p910_mdio_read(struct net_device *dev, int phy_id, int reg); - -struct w90p910_rxbd { - unsigned int sl; - unsigned int buffer; - unsigned int reserved; - unsigned int next; -}; - -struct w90p910_txbd { - unsigned int mode; - unsigned int buffer; - unsigned int sl; - unsigned int next; -}; - -struct recv_pdesc { - struct w90p910_rxbd desclist[RX_DESC_SIZE]; - char recv_buf[RX_DESC_SIZE][MAX_RBUFF_SZ]; -}; - -struct tran_pdesc { - struct w90p910_txbd desclist[TX_DESC_SIZE]; - char tran_buf[TX_DESC_SIZE][MAX_TBUFF_SZ]; -}; - -struct w90p910_ether { - struct recv_pdesc *rdesc; - struct tran_pdesc *tdesc; - dma_addr_t rdesc_phys; - dma_addr_t tdesc_phys; - struct platform_device *pdev; - struct resource *res; - struct sk_buff *skb; - struct clk *clk; - struct clk *rmiiclk; - struct mii_if_info mii; - struct timer_list check_timer; - void __iomem *reg; - int rxirq; - int txirq; - unsigned int cur_tx; - unsigned int cur_rx; - unsigned int finish_tx; - unsigned int rx_packets; - unsigned int rx_bytes; - unsigned int start_tx_ptr; - unsigned int start_rx_ptr; - unsigned int linkflag; -}; - -static void update_linkspeed_register(struct net_device *dev, - unsigned int speed, unsigned int duplex) -{ - struct w90p910_ether *ether = netdev_priv(dev); - unsigned int val; - - val = __raw_readl(ether->reg + REG_MCMDR); - - if (speed == SPEED_100) { - /* 100 full/half duplex */ - if (duplex == DUPLEX_FULL) { - val |= (MCMDR_OPMOD | MCMDR_FDUP); - } else { - val |= MCMDR_OPMOD; - val &= ~MCMDR_FDUP; - } - } else { - /* 10 full/half duplex */ - if (duplex == DUPLEX_FULL) { - val |= MCMDR_FDUP; - val &= ~MCMDR_OPMOD; - } else { - val &= ~(MCMDR_FDUP | MCMDR_OPMOD); - } - } - - __raw_writel(val, ether->reg + REG_MCMDR); -} - -static void update_linkspeed(struct net_device *dev) -{ - struct w90p910_ether *ether = netdev_priv(dev); - struct platform_device *pdev; - unsigned int bmsr, bmcr, lpa, speed, duplex; - - pdev = ether->pdev; - - if (!mii_link_ok(ðer->mii)) { - ether->linkflag = 0x0; - netif_carrier_off(dev); - dev_warn(&pdev->dev, "%s: Link down.\n", dev->name); - return; - } - - if (ether->linkflag == 1) - return; - - bmsr = w90p910_mdio_read(dev, ether->mii.phy_id, MII_BMSR); - bmcr = w90p910_mdio_read(dev, ether->mii.phy_id, MII_BMCR); - - if (bmcr & BMCR_ANENABLE) { - if (!(bmsr & BMSR_ANEGCOMPLETE)) - return; - - lpa = w90p910_mdio_read(dev, ether->mii.phy_id, MII_LPA); - - if ((lpa & LPA_100FULL) || (lpa & LPA_100HALF)) - speed = SPEED_100; - else - speed = SPEED_10; - - if ((lpa & LPA_100FULL) || (lpa & LPA_10FULL)) - duplex = DUPLEX_FULL; - else - duplex = DUPLEX_HALF; - - } else { - speed = (bmcr & BMCR_SPEED100) ? SPEED_100 : SPEED_10; - duplex = (bmcr & BMCR_FULLDPLX) ? DUPLEX_FULL : DUPLEX_HALF; - } - - update_linkspeed_register(dev, speed, duplex); - - dev_info(&pdev->dev, "%s: Link now %i-%s\n", dev->name, speed, - (duplex == DUPLEX_FULL) ? "FullDuplex" : "HalfDuplex"); - ether->linkflag = 0x01; - - netif_carrier_on(dev); -} - -static void w90p910_check_link(struct timer_list *t) -{ - struct w90p910_ether *ether = from_timer(ether, t, check_timer); - struct net_device *dev = ether->mii.dev; - - update_linkspeed(dev); - mod_timer(ðer->check_timer, jiffies + msecs_to_jiffies(1000)); -} - -static void w90p910_write_cam(struct net_device *dev, - unsigned int x, unsigned char *pval) -{ - struct w90p910_ether *ether = netdev_priv(dev); - unsigned int msw, lsw; - - msw = (pval[0] << 24) | (pval[1] << 16) | (pval[2] << 8) | pval[3]; - - lsw = (pval[4] << 24) | (pval[5] << 16); - - __raw_writel(lsw, ether->reg + REG_CAML_BASE + x * CAM_ENTRY_SIZE); - __raw_writel(msw, ether->reg + REG_CAMM_BASE + x * CAM_ENTRY_SIZE); -} - -static int w90p910_init_desc(struct net_device *dev) -{ - struct w90p910_ether *ether; - struct w90p910_txbd *tdesc; - struct w90p910_rxbd *rdesc; - struct platform_device *pdev; - unsigned int i; - - ether = netdev_priv(dev); - pdev = ether->pdev; - - ether->tdesc = dma_alloc_coherent(&pdev->dev, sizeof(struct tran_pdesc), - ðer->tdesc_phys, GFP_KERNEL); - if (!ether->tdesc) - return -ENOMEM; - - ether->rdesc = dma_alloc_coherent(&pdev->dev, sizeof(struct recv_pdesc), - ðer->rdesc_phys, GFP_KERNEL); - if (!ether->rdesc) { - dma_free_coherent(&pdev->dev, sizeof(struct tran_pdesc), - ether->tdesc, ether->tdesc_phys); - return -ENOMEM; - } - - for (i = 0; i < TX_DESC_SIZE; i++) { - unsigned int offset; - - tdesc = &(ether->tdesc->desclist[i]); - - if (i == TX_DESC_SIZE - 1) - offset = offsetof(struct tran_pdesc, desclist[0]); - else - offset = offsetof(struct tran_pdesc, desclist[i + 1]); - - tdesc->next = ether->tdesc_phys + offset; - tdesc->buffer = ether->tdesc_phys + - offsetof(struct tran_pdesc, tran_buf[i]); - tdesc->sl = 0; - tdesc->mode = 0; - } - - ether->start_tx_ptr = ether->tdesc_phys; - - for (i = 0; i < RX_DESC_SIZE; i++) { - unsigned int offset; - - rdesc = &(ether->rdesc->desclist[i]); - - if (i == RX_DESC_SIZE - 1) - offset = offsetof(struct recv_pdesc, desclist[0]); - else - offset = offsetof(struct recv_pdesc, desclist[i + 1]); - - rdesc->next = ether->rdesc_phys + offset; - rdesc->sl = RX_OWEN_DMA; - rdesc->buffer = ether->rdesc_phys + - offsetof(struct recv_pdesc, recv_buf[i]); - } - - ether->start_rx_ptr = ether->rdesc_phys; - - return 0; -} - -static void w90p910_set_fifo_threshold(struct net_device *dev) -{ - struct w90p910_ether *ether = netdev_priv(dev); - unsigned int val; - - val = TXTHD | BLENGTH; - __raw_writel(val, ether->reg + REG_FFTCR); -} - -static void w90p910_return_default_idle(struct net_device *dev) -{ - struct w90p910_ether *ether = netdev_priv(dev); - unsigned int val; - - val = __raw_readl(ether->reg + REG_MCMDR); - val |= SWR; - __raw_writel(val, ether->reg + REG_MCMDR); -} - -static void w90p910_trigger_rx(struct net_device *dev) -{ - struct w90p910_ether *ether = netdev_priv(dev); - - __raw_writel(ENSTART, ether->reg + REG_RSDR); -} - -static void w90p910_trigger_tx(struct net_device *dev) -{ - struct w90p910_ether *ether = netdev_priv(dev); - - __raw_writel(ENSTART, ether->reg + REG_TSDR); -} - -static void w90p910_enable_mac_interrupt(struct net_device *dev) -{ - struct w90p910_ether *ether = netdev_priv(dev); - unsigned int val; - - val = ENTXINTR | ENRXINTR | ENRXGD | ENTXCP; - val |= ENTXBERR | ENRXBERR | ENTXABT; - - __raw_writel(val, ether->reg + REG_MIEN); -} - -static void w90p910_get_and_clear_int(struct net_device *dev, - unsigned int *val) -{ - struct w90p910_ether *ether = netdev_priv(dev); - - *val = __raw_readl(ether->reg + REG_MISTA); - __raw_writel(*val, ether->reg + REG_MISTA); -} - -static void w90p910_set_global_maccmd(struct net_device *dev) -{ - struct w90p910_ether *ether = netdev_priv(dev); - unsigned int val; - - val = __raw_readl(ether->reg + REG_MCMDR); - val |= MCMDR_SPCRC | MCMDR_ENMDC | MCMDR_ACP | ENMDC; - __raw_writel(val, ether->reg + REG_MCMDR); -} - -static void w90p910_enable_cam(struct net_device *dev) -{ - struct w90p910_ether *ether = netdev_priv(dev); - unsigned int val; - - w90p910_write_cam(dev, CAM0, dev->dev_addr); - - val = __raw_readl(ether->reg + REG_CAMEN); - val |= CAM0EN; - __raw_writel(val, ether->reg + REG_CAMEN); -} - -static void w90p910_enable_cam_command(struct net_device *dev) -{ - struct w90p910_ether *ether = netdev_priv(dev); - unsigned int val; - - val = CAMCMR_ECMP | CAMCMR_ABP | CAMCMR_AMP; - __raw_writel(val, ether->reg + REG_CAMCMR); -} - -static void w90p910_enable_tx(struct net_device *dev, unsigned int enable) -{ - struct w90p910_ether *ether = netdev_priv(dev); - unsigned int val; - - val = __raw_readl(ether->reg + REG_MCMDR); - - if (enable) - val |= MCMDR_TXON; - else - val &= ~MCMDR_TXON; - - __raw_writel(val, ether->reg + REG_MCMDR); -} - -static void w90p910_enable_rx(struct net_device *dev, unsigned int enable) -{ - struct w90p910_ether *ether = netdev_priv(dev); - unsigned int val; - - val = __raw_readl(ether->reg + REG_MCMDR); - - if (enable) - val |= MCMDR_RXON; - else - val &= ~MCMDR_RXON; - - __raw_writel(val, ether->reg + REG_MCMDR); -} - -static void w90p910_set_curdest(struct net_device *dev) -{ - struct w90p910_ether *ether = netdev_priv(dev); - - __raw_writel(ether->start_rx_ptr, ether->reg + REG_RXDLSA); - __raw_writel(ether->start_tx_ptr, ether->reg + REG_TXDLSA); -} - -static void w90p910_reset_mac(struct net_device *dev) -{ - struct w90p910_ether *ether = netdev_priv(dev); - - w90p910_enable_tx(dev, 0); - w90p910_enable_rx(dev, 0); - w90p910_set_fifo_threshold(dev); - w90p910_return_default_idle(dev); - - if (!netif_queue_stopped(dev)) - netif_stop_queue(dev); - - w90p910_init_desc(dev); - - netif_trans_update(dev); /* prevent tx timeout */ - ether->cur_tx = 0x0; - ether->finish_tx = 0x0; - ether->cur_rx = 0x0; - - w90p910_set_curdest(dev); - w90p910_enable_cam(dev); - w90p910_enable_cam_command(dev); - w90p910_enable_mac_interrupt(dev); - w90p910_enable_tx(dev, 1); - w90p910_enable_rx(dev, 1); - w90p910_trigger_tx(dev); - w90p910_trigger_rx(dev); - - netif_trans_update(dev); /* prevent tx timeout */ - - if (netif_queue_stopped(dev)) - netif_wake_queue(dev); -} - -static void w90p910_mdio_write(struct net_device *dev, - int phy_id, int reg, int data) -{ - struct w90p910_ether *ether = netdev_priv(dev); - struct platform_device *pdev; - unsigned int val, i; - - pdev = ether->pdev; - - __raw_writel(data, ether->reg + REG_MIID); - - val = (phy_id << 0x08) | reg; - val |= PHYBUSY | PHYWR | MDCCR_VAL; - __raw_writel(val, ether->reg + REG_MIIDA); - - for (i = 0; i < DELAY; i++) { - if ((__raw_readl(ether->reg + REG_MIIDA) & PHYBUSY) == 0) - break; - } - - if (i == DELAY) - dev_warn(&pdev->dev, "mdio write timed out\n"); -} - -static int w90p910_mdio_read(struct net_device *dev, int phy_id, int reg) -{ - struct w90p910_ether *ether = netdev_priv(dev); - struct platform_device *pdev; - unsigned int val, i, data; - - pdev = ether->pdev; - - val = (phy_id << 0x08) | reg; - val |= PHYBUSY | MDCCR_VAL; - __raw_writel(val, ether->reg + REG_MIIDA); - - for (i = 0; i < DELAY; i++) { - if ((__raw_readl(ether->reg + REG_MIIDA) & PHYBUSY) == 0) - break; - } - - if (i == DELAY) { - dev_warn(&pdev->dev, "mdio read timed out\n"); - data = 0xffff; - } else { - data = __raw_readl(ether->reg + REG_MIID); - } - - return data; -} - -static int w90p910_set_mac_address(struct net_device *dev, void *addr) -{ - struct sockaddr *address = addr; - - if (!is_valid_ether_addr(address->sa_data)) - return -EADDRNOTAVAIL; - - memcpy(dev->dev_addr, address->sa_data, dev->addr_len); - w90p910_write_cam(dev, CAM0, dev->dev_addr); - - return 0; -} - -static int w90p910_ether_close(struct net_device *dev) -{ - struct w90p910_ether *ether = netdev_priv(dev); - struct platform_device *pdev; - - pdev = ether->pdev; - - dma_free_coherent(&pdev->dev, sizeof(struct recv_pdesc), - ether->rdesc, ether->rdesc_phys); - dma_free_coherent(&pdev->dev, sizeof(struct tran_pdesc), - ether->tdesc, ether->tdesc_phys); - - netif_stop_queue(dev); - - del_timer_sync(ðer->check_timer); - clk_disable(ether->rmiiclk); - clk_disable(ether->clk); - - free_irq(ether->txirq, dev); - free_irq(ether->rxirq, dev); - - return 0; -} - -static int w90p910_send_frame(struct net_device *dev, - unsigned char *data, int length) -{ - struct w90p910_ether *ether; - struct w90p910_txbd *txbd; - struct platform_device *pdev; - unsigned char *buffer; - - ether = netdev_priv(dev); - pdev = ether->pdev; - - txbd = ðer->tdesc->desclist[ether->cur_tx]; - buffer = ether->tdesc->tran_buf[ether->cur_tx]; - - if (length > 1514) { - dev_err(&pdev->dev, "send data %d bytes, check it\n", length); - length = 1514; - } - - txbd->sl = length & 0xFFFF; - - memcpy(buffer, data, length); - - txbd->mode = TX_OWEN_DMA | PADDINGMODE | CRCMODE | MACTXINTEN; - - w90p910_enable_tx(dev, 1); - - w90p910_trigger_tx(dev); - - if (++ether->cur_tx >= TX_DESC_SIZE) - ether->cur_tx = 0; - - txbd = ðer->tdesc->desclist[ether->cur_tx]; - - if (txbd->mode & TX_OWEN_DMA) - netif_stop_queue(dev); - - return 0; -} - -static int w90p910_ether_start_xmit(struct sk_buff *skb, struct net_device *dev) -{ - struct w90p910_ether *ether = netdev_priv(dev); - - if (!(w90p910_send_frame(dev, skb->data, skb->len))) { - ether->skb = skb; - dev_consume_skb_irq(skb); - return 0; - } - return -EAGAIN; -} - -static irqreturn_t w90p910_tx_interrupt(int irq, void *dev_id) -{ - struct w90p910_ether *ether; - struct w90p910_txbd *txbd; - struct platform_device *pdev; - struct net_device *dev; - unsigned int cur_entry, entry, status; - - dev = dev_id; - ether = netdev_priv(dev); - pdev = ether->pdev; - - w90p910_get_and_clear_int(dev, &status); - - cur_entry = __raw_readl(ether->reg + REG_CTXDSA); - - entry = ether->tdesc_phys + - offsetof(struct tran_pdesc, desclist[ether->finish_tx]); - - while (entry != cur_entry) { - txbd = ðer->tdesc->desclist[ether->finish_tx]; - - if (++ether->finish_tx >= TX_DESC_SIZE) - ether->finish_tx = 0; - - if (txbd->sl & TXDS_TXCP) { - dev->stats.tx_packets++; - dev->stats.tx_bytes += txbd->sl & 0xFFFF; - } else { - dev->stats.tx_errors++; - } - - txbd->sl = 0x0; - txbd->mode = 0x0; - - if (netif_queue_stopped(dev)) - netif_wake_queue(dev); - - entry = ether->tdesc_phys + - offsetof(struct tran_pdesc, desclist[ether->finish_tx]); - } - - if (status & MISTA_EXDEF) { - dev_err(&pdev->dev, "emc defer exceed interrupt\n"); - } else if (status & MISTA_TXBERR) { - dev_err(&pdev->dev, "emc bus error interrupt\n"); - w90p910_reset_mac(dev); - } else if (status & MISTA_TDU) { - if (netif_queue_stopped(dev)) - netif_wake_queue(dev); - } - - return IRQ_HANDLED; -} - -static void netdev_rx(struct net_device *dev) -{ - struct w90p910_ether *ether; - struct w90p910_rxbd *rxbd; - struct platform_device *pdev; - struct sk_buff *skb; - unsigned char *data; - unsigned int length, status, val, entry; - - ether = netdev_priv(dev); - pdev = ether->pdev; - - rxbd = ðer->rdesc->desclist[ether->cur_rx]; - - do { - val = __raw_readl(ether->reg + REG_CRXDSA); - - entry = ether->rdesc_phys + - offsetof(struct recv_pdesc, desclist[ether->cur_rx]); - - if (val == entry) - break; - - status = rxbd->sl; - length = status & 0xFFFF; - - if (status & RXDS_RXGD) { - data = ether->rdesc->recv_buf[ether->cur_rx]; - skb = netdev_alloc_skb(dev, length + 2); - if (!skb) { - dev->stats.rx_dropped++; - return; - } - - skb_reserve(skb, 2); - skb_put(skb, length); - skb_copy_to_linear_data(skb, data, length); - skb->protocol = eth_type_trans(skb, dev); - dev->stats.rx_packets++; - dev->stats.rx_bytes += length; - netif_rx(skb); - } else { - dev->stats.rx_errors++; - - if (status & RXDS_RP) { - dev_err(&pdev->dev, "rx runt err\n"); - dev->stats.rx_length_errors++; - } else if (status & RXDS_CRCE) { - dev_err(&pdev->dev, "rx crc err\n"); - dev->stats.rx_crc_errors++; - } else if (status & RXDS_ALIE) { - dev_err(&pdev->dev, "rx alignment err\n"); - dev->stats.rx_frame_errors++; - } else if (status & RXDS_PTLE) { - dev_err(&pdev->dev, "rx longer err\n"); - dev->stats.rx_over_errors++; - } - } - - rxbd->sl = RX_OWEN_DMA; - rxbd->reserved = 0x0; - - if (++ether->cur_rx >= RX_DESC_SIZE) - ether->cur_rx = 0; - - rxbd = ðer->rdesc->desclist[ether->cur_rx]; - - } while (1); -} - -static irqreturn_t w90p910_rx_interrupt(int irq, void *dev_id) -{ - struct net_device *dev; - struct w90p910_ether *ether; - struct platform_device *pdev; - unsigned int status; - - dev = dev_id; - ether = netdev_priv(dev); - pdev = ether->pdev; - - w90p910_get_and_clear_int(dev, &status); - - if (status & MISTA_RDU) { - netdev_rx(dev); - w90p910_trigger_rx(dev); - - return IRQ_HANDLED; - } else if (status & MISTA_RXBERR) { - dev_err(&pdev->dev, "emc rx bus error\n"); - w90p910_reset_mac(dev); - } - - netdev_rx(dev); - return IRQ_HANDLED; -} - -static int w90p910_ether_open(struct net_device *dev) -{ - struct w90p910_ether *ether; - struct platform_device *pdev; - - ether = netdev_priv(dev); - pdev = ether->pdev; - - w90p910_reset_mac(dev); - w90p910_set_fifo_threshold(dev); - w90p910_set_curdest(dev); - w90p910_enable_cam(dev); - w90p910_enable_cam_command(dev); - w90p910_enable_mac_interrupt(dev); - w90p910_set_global_maccmd(dev); - w90p910_enable_rx(dev, 1); - - clk_enable(ether->rmiiclk); - clk_enable(ether->clk); - - ether->rx_packets = 0x0; - ether->rx_bytes = 0x0; - - if (request_irq(ether->txirq, w90p910_tx_interrupt, - 0x0, pdev->name, dev)) { - dev_err(&pdev->dev, "register irq tx failed\n"); - return -EAGAIN; - } - - if (request_irq(ether->rxirq, w90p910_rx_interrupt, - 0x0, pdev->name, dev)) { - dev_err(&pdev->dev, "register irq rx failed\n"); - free_irq(ether->txirq, dev); - return -EAGAIN; - } - - mod_timer(ðer->check_timer, jiffies + msecs_to_jiffies(1000)); - netif_start_queue(dev); - w90p910_trigger_rx(dev); - - dev_info(&pdev->dev, "%s is OPENED\n", dev->name); - - return 0; -} - -static void w90p910_ether_set_multicast_list(struct net_device *dev) -{ - struct w90p910_ether *ether; - unsigned int rx_mode; - - ether = netdev_priv(dev); - - if (dev->flags & IFF_PROMISC) - rx_mode = CAMCMR_AUP | CAMCMR_AMP | CAMCMR_ABP | CAMCMR_ECMP; - else if ((dev->flags & IFF_ALLMULTI) || !netdev_mc_empty(dev)) - rx_mode = CAMCMR_AMP | CAMCMR_ABP | CAMCMR_ECMP; - else - rx_mode = CAMCMR_ECMP | CAMCMR_ABP; - __raw_writel(rx_mode, ether->reg + REG_CAMCMR); -} - -static int w90p910_ether_ioctl(struct net_device *dev, - struct ifreq *ifr, int cmd) -{ - struct w90p910_ether *ether = netdev_priv(dev); - struct mii_ioctl_data *data = if_mii(ifr); - - return generic_mii_ioctl(ðer->mii, data, cmd, NULL); -} - -static void w90p910_get_drvinfo(struct net_device *dev, - struct ethtool_drvinfo *info) -{ - strlcpy(info->driver, DRV_MODULE_NAME, sizeof(info->driver)); - strlcpy(info->version, DRV_MODULE_VERSION, sizeof(info->version)); -} - -static int w90p910_get_link_ksettings(struct net_device *dev, - struct ethtool_link_ksettings *cmd) -{ - struct w90p910_ether *ether = netdev_priv(dev); - - mii_ethtool_get_link_ksettings(ðer->mii, cmd); - - return 0; -} - -static int w90p910_set_link_ksettings(struct net_device *dev, - const struct ethtool_link_ksettings *cmd) -{ - struct w90p910_ether *ether = netdev_priv(dev); - return mii_ethtool_set_link_ksettings(ðer->mii, cmd); -} - -static int w90p910_nway_reset(struct net_device *dev) -{ - struct w90p910_ether *ether = netdev_priv(dev); - return mii_nway_restart(ðer->mii); -} - -static u32 w90p910_get_link(struct net_device *dev) -{ - struct w90p910_ether *ether = netdev_priv(dev); - return mii_link_ok(ðer->mii); -} - -static const struct ethtool_ops w90p910_ether_ethtool_ops = { - .get_drvinfo = w90p910_get_drvinfo, - .nway_reset = w90p910_nway_reset, - .get_link = w90p910_get_link, - .get_link_ksettings = w90p910_get_link_ksettings, - .set_link_ksettings = w90p910_set_link_ksettings, -}; - -static const struct net_device_ops w90p910_ether_netdev_ops = { - .ndo_open = w90p910_ether_open, - .ndo_stop = w90p910_ether_close, - .ndo_start_xmit = w90p910_ether_start_xmit, - .ndo_set_rx_mode = w90p910_ether_set_multicast_list, - .ndo_set_mac_address = w90p910_set_mac_address, - .ndo_do_ioctl = w90p910_ether_ioctl, - .ndo_validate_addr = eth_validate_addr, -}; - -static void get_mac_address(struct net_device *dev) -{ - struct w90p910_ether *ether = netdev_priv(dev); - struct platform_device *pdev; - char addr[ETH_ALEN]; - - pdev = ether->pdev; - - addr[0] = 0x00; - addr[1] = 0x02; - addr[2] = 0xac; - addr[3] = 0x55; - addr[4] = 0x88; - addr[5] = 0xa8; - - if (is_valid_ether_addr(addr)) - memcpy(dev->dev_addr, &addr, ETH_ALEN); - else - dev_err(&pdev->dev, "invalid mac address\n"); -} - -static int w90p910_ether_setup(struct net_device *dev) -{ - struct w90p910_ether *ether = netdev_priv(dev); - - dev->netdev_ops = &w90p910_ether_netdev_ops; - dev->ethtool_ops = &w90p910_ether_ethtool_ops; - - dev->tx_queue_len = 16; - dev->dma = 0x0; - dev->watchdog_timeo = TX_TIMEOUT; - - get_mac_address(dev); - - ether->cur_tx = 0x0; - ether->cur_rx = 0x0; - ether->finish_tx = 0x0; - ether->linkflag = 0x0; - ether->mii.phy_id = 0x01; - ether->mii.phy_id_mask = 0x1f; - ether->mii.reg_num_mask = 0x1f; - ether->mii.dev = dev; - ether->mii.mdio_read = w90p910_mdio_read; - ether->mii.mdio_write = w90p910_mdio_write; - - timer_setup(ðer->check_timer, w90p910_check_link, 0); - - return 0; -} - -static int w90p910_ether_probe(struct platform_device *pdev) -{ - struct w90p910_ether *ether; - struct net_device *dev; - int error; - - dev = alloc_etherdev(sizeof(struct w90p910_ether)); - if (!dev) - return -ENOMEM; - - ether = netdev_priv(dev); - - ether->res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (ether->res == NULL) { - dev_err(&pdev->dev, "failed to get I/O memory\n"); - error = -ENXIO; - goto failed_free; - } - - if (!request_mem_region(ether->res->start, - resource_size(ether->res), pdev->name)) { - dev_err(&pdev->dev, "failed to request I/O memory\n"); - error = -EBUSY; - goto failed_free; - } - - ether->reg = ioremap(ether->res->start, resource_size(ether->res)); - if (ether->reg == NULL) { - dev_err(&pdev->dev, "failed to remap I/O memory\n"); - error = -ENXIO; - goto failed_free_mem; - } - - ether->txirq = platform_get_irq(pdev, 0); - if (ether->txirq < 0) { - dev_err(&pdev->dev, "failed to get ether tx irq\n"); - error = -ENXIO; - goto failed_free_io; - } - - ether->rxirq = platform_get_irq(pdev, 1); - if (ether->rxirq < 0) { - dev_err(&pdev->dev, "failed to get ether rx irq\n"); - error = -ENXIO; - goto failed_free_io; - } - - platform_set_drvdata(pdev, dev); - - ether->clk = clk_get(&pdev->dev, NULL); - if (IS_ERR(ether->clk)) { - dev_err(&pdev->dev, "failed to get ether clock\n"); - error = PTR_ERR(ether->clk); - goto failed_free_io; - } - - ether->rmiiclk = clk_get(&pdev->dev, "RMII"); - if (IS_ERR(ether->rmiiclk)) { - dev_err(&pdev->dev, "failed to get ether clock\n"); - error = PTR_ERR(ether->rmiiclk); - goto failed_put_clk; - } - - ether->pdev = pdev; - - w90p910_ether_setup(dev); - - error = register_netdev(dev); - if (error != 0) { - dev_err(&pdev->dev, "Register EMC w90p910 FAILED\n"); - error = -ENODEV; - goto failed_put_rmiiclk; - } - - return 0; -failed_put_rmiiclk: - clk_put(ether->rmiiclk); -failed_put_clk: - clk_put(ether->clk); -failed_free_io: - iounmap(ether->reg); -failed_free_mem: - release_mem_region(ether->res->start, resource_size(ether->res)); -failed_free: - free_netdev(dev); - return error; -} - -static int w90p910_ether_remove(struct platform_device *pdev) -{ - struct net_device *dev = platform_get_drvdata(pdev); - struct w90p910_ether *ether = netdev_priv(dev); - - unregister_netdev(dev); - - clk_put(ether->rmiiclk); - clk_put(ether->clk); - - iounmap(ether->reg); - release_mem_region(ether->res->start, resource_size(ether->res)); - - del_timer_sync(ðer->check_timer); - - free_netdev(dev); - return 0; -} - -static struct platform_driver w90p910_ether_driver = { - .probe = w90p910_ether_probe, - .remove = w90p910_ether_remove, - .driver = { - .name = "nuc900-emc", - }, -}; - -module_platform_driver(w90p910_ether_driver); - -MODULE_AUTHOR("Wan ZongShun <mcuos.com@gmail.com>"); -MODULE_DESCRIPTION("w90p910 MAC driver!"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:nuc900-emc"); - diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig index 21efb7d39d62..7b07281aa0ae 100644 --- a/drivers/reset/Kconfig +++ b/drivers/reset/Kconfig @@ -116,9 +116,20 @@ config RESET_QCOM_PDC to control reset signals provided by PDC for Modem, Compute, Display, GPU, Debug, AOP, Sensors, Audio, SP and APPS. +config RESET_SCMI + tristate "Reset driver controlled via ARM SCMI interface" + depends on ARM_SCMI_PROTOCOL || COMPILE_TEST + default ARM_SCMI_PROTOCOL + help + This driver provides support for reset signal/domains that are + controlled by firmware that implements the SCMI interface. + + This driver uses SCMI Message Protocol to interact with the + firmware controlling all the reset signals. + config RESET_SIMPLE bool "Simple Reset Controller Driver" if COMPILE_TEST - default ARCH_STM32 || ARCH_STRATIX10 || ARCH_SUNXI || ARCH_ZX || ARCH_ASPEED || ARCH_BITMAIN + default ARCH_STM32 || ARCH_STRATIX10 || ARCH_SUNXI || ARCH_ZX || ARCH_ASPEED || ARCH_BITMAIN || ARC help This enables a simple reset controller driver for reset lines that that can be asserted and deasserted by toggling bits in a contiguous, diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile index 61456b8f659c..cf60ce526064 100644 --- a/drivers/reset/Makefile +++ b/drivers/reset/Makefile @@ -18,6 +18,7 @@ obj-$(CONFIG_RESET_OXNAS) += reset-oxnas.o obj-$(CONFIG_RESET_PISTACHIO) += reset-pistachio.o obj-$(CONFIG_RESET_QCOM_AOSS) += reset-qcom-aoss.o obj-$(CONFIG_RESET_QCOM_PDC) += reset-qcom-pdc.o +obj-$(CONFIG_RESET_SCMI) += reset-scmi.o obj-$(CONFIG_RESET_SIMPLE) += reset-simple.o obj-$(CONFIG_RESET_STM32MP157) += reset-stm32mp1.o obj-$(CONFIG_RESET_SOCFPGA) += reset-socfpga.o diff --git a/drivers/reset/reset-imx7.c b/drivers/reset/reset-imx7.c index 3ecd770f910b..1443a55a0c29 100644 --- a/drivers/reset/reset-imx7.c +++ b/drivers/reset/reset-imx7.c @@ -169,9 +169,9 @@ static const struct imx7_src_signal imx8mq_src_signals[IMX8MQ_RESET_NUM] = { [IMX8MQ_RESET_OTG2_PHY_RESET] = { SRC_USBOPHY2_RCR, BIT(0) }, [IMX8MQ_RESET_MIPI_DSI_RESET_BYTE_N] = { SRC_MIPIPHY_RCR, BIT(1) }, [IMX8MQ_RESET_MIPI_DSI_RESET_N] = { SRC_MIPIPHY_RCR, BIT(2) }, - [IMX8MQ_RESET_MIPI_DIS_DPI_RESET_N] = { SRC_MIPIPHY_RCR, BIT(3) }, - [IMX8MQ_RESET_MIPI_DIS_ESC_RESET_N] = { SRC_MIPIPHY_RCR, BIT(4) }, - [IMX8MQ_RESET_MIPI_DIS_PCLK_RESET_N] = { SRC_MIPIPHY_RCR, BIT(5) }, + [IMX8MQ_RESET_MIPI_DSI_DPI_RESET_N] = { SRC_MIPIPHY_RCR, BIT(3) }, + [IMX8MQ_RESET_MIPI_DSI_ESC_RESET_N] = { SRC_MIPIPHY_RCR, BIT(4) }, + [IMX8MQ_RESET_MIPI_DSI_PCLK_RESET_N] = { SRC_MIPIPHY_RCR, BIT(5) }, [IMX8MQ_RESET_PCIEPHY] = { SRC_PCIEPHY_RCR, BIT(2) | BIT(1) }, [IMX8MQ_RESET_PCIEPHY_PERST] = { SRC_PCIEPHY_RCR, BIT(3) }, @@ -220,9 +220,9 @@ static int imx8mq_reset_set(struct reset_controller_dev *rcdev, case IMX8MQ_RESET_PCIE_CTRL_APPS_EN: case IMX8MQ_RESET_PCIE2_CTRL_APPS_EN: /* fallthrough */ - case IMX8MQ_RESET_MIPI_DIS_PCLK_RESET_N: /* fallthrough */ - case IMX8MQ_RESET_MIPI_DIS_ESC_RESET_N: /* fallthrough */ - case IMX8MQ_RESET_MIPI_DIS_DPI_RESET_N: /* fallthrough */ + case IMX8MQ_RESET_MIPI_DSI_PCLK_RESET_N: /* fallthrough */ + case IMX8MQ_RESET_MIPI_DSI_ESC_RESET_N: /* fallthrough */ + case IMX8MQ_RESET_MIPI_DSI_DPI_RESET_N: /* fallthrough */ case IMX8MQ_RESET_MIPI_DSI_RESET_N: /* fallthrough */ case IMX8MQ_RESET_MIPI_DSI_RESET_BYTE_N: /* fallthrough */ value = assert ? 0 : bit; diff --git a/drivers/reset/reset-meson.c b/drivers/reset/reset-meson.c index 5242e0679df7..7d05d766e1ea 100644 --- a/drivers/reset/reset-meson.c +++ b/drivers/reset/reset-meson.c @@ -1,58 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* * Amlogic Meson Reset Controller driver * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright (c) 2016 BayLibre, SAS. - * Author: Neil Armstrong <narmstrong@baylibre.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see <http://www.gnu.org/licenses/>. - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * BSD LICENSE - * * Copyright (c) 2016 BayLibre, SAS. * Author: Neil Armstrong <narmstrong@baylibre.com> - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include <linux/err.h> #include <linux/init.h> diff --git a/drivers/reset/reset-scmi.c b/drivers/reset/reset-scmi.c new file mode 100644 index 000000000000..c6d3c8427f14 --- /dev/null +++ b/drivers/reset/reset-scmi.c @@ -0,0 +1,124 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ARM System Control and Management Interface (ARM SCMI) reset driver + * + * Copyright (C) 2019 ARM Ltd. + */ + +#include <linux/module.h> +#include <linux/of.h> +#include <linux/device.h> +#include <linux/reset-controller.h> +#include <linux/scmi_protocol.h> + +/** + * struct scmi_reset_data - reset controller information structure + * @rcdev: reset controller entity + * @handle: ARM SCMI handle used for communication with system controller + */ +struct scmi_reset_data { + struct reset_controller_dev rcdev; + const struct scmi_handle *handle; +}; + +#define to_scmi_reset_data(p) container_of((p), struct scmi_reset_data, rcdev) +#define to_scmi_handle(p) (to_scmi_reset_data(p)->handle) + +/** + * scmi_reset_assert() - assert device reset + * @rcdev: reset controller entity + * @id: ID of the reset to be asserted + * + * This function implements the reset driver op to assert a device's reset + * using the ARM SCMI protocol. + * + * Return: 0 for successful request, else a corresponding error value + */ +static int +scmi_reset_assert(struct reset_controller_dev *rcdev, unsigned long id) +{ + const struct scmi_handle *handle = to_scmi_handle(rcdev); + + return handle->reset_ops->assert(handle, id); +} + +/** + * scmi_reset_deassert() - deassert device reset + * @rcdev: reset controller entity + * @id: ID of the reset to be deasserted + * + * This function implements the reset driver op to deassert a device's reset + * using the ARM SCMI protocol. + * + * Return: 0 for successful request, else a corresponding error value + */ +static int +scmi_reset_deassert(struct reset_controller_dev *rcdev, unsigned long id) +{ + const struct scmi_handle *handle = to_scmi_handle(rcdev); + + return handle->reset_ops->deassert(handle, id); +} + +/** + * scmi_reset_reset() - reset the device + * @rcdev: reset controller entity + * @id: ID of the reset signal to be reset(assert + deassert) + * + * This function implements the reset driver op to trigger a device's + * reset signal using the ARM SCMI protocol. + * + * Return: 0 for successful request, else a corresponding error value + */ +static int +scmi_reset_reset(struct reset_controller_dev *rcdev, unsigned long id) +{ + const struct scmi_handle *handle = to_scmi_handle(rcdev); + + return handle->reset_ops->reset(handle, id); +} + +static const struct reset_control_ops scmi_reset_ops = { + .assert = scmi_reset_assert, + .deassert = scmi_reset_deassert, + .reset = scmi_reset_reset, +}; + +static int scmi_reset_probe(struct scmi_device *sdev) +{ + struct scmi_reset_data *data; + struct device *dev = &sdev->dev; + struct device_node *np = dev->of_node; + const struct scmi_handle *handle = sdev->handle; + + if (!handle || !handle->reset_ops) + return -ENODEV; + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->rcdev.ops = &scmi_reset_ops; + data->rcdev.owner = THIS_MODULE; + data->rcdev.of_node = np; + data->rcdev.nr_resets = handle->reset_ops->num_domains_get(handle); + + return devm_reset_controller_register(dev, &data->rcdev); +} + +static const struct scmi_device_id scmi_id_table[] = { + { SCMI_PROTOCOL_RESET }, + { }, +}; +MODULE_DEVICE_TABLE(scmi, scmi_id_table); + +static struct scmi_driver scmi_reset_driver = { + .name = "scmi-reset", + .probe = scmi_reset_probe, + .id_table = scmi_id_table, +}; +module_scmi_driver(scmi_reset_driver); + +MODULE_AUTHOR("Sudeep Holla <sudeep.holla@arm.com>"); +MODULE_DESCRIPTION("ARM SCMI reset controller driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/reset/reset-simple.c b/drivers/reset/reset-simple.c index 1154f7b1f4dd..067e7e7b34f1 100644 --- a/drivers/reset/reset-simple.c +++ b/drivers/reset/reset-simple.c @@ -127,6 +127,9 @@ static const struct of_device_id reset_simple_dt_ids[] = { { .compatible = "aspeed,ast2500-lpc-reset" }, { .compatible = "bitmain,bm1880-reset", .data = &reset_simple_active_low }, + { .compatible = "snps,dw-high-reset" }, + { .compatible = "snps,dw-low-reset", + .data = &reset_simple_active_low }, { /* sentinel */ }, }; diff --git a/drivers/soc/amlogic/Kconfig b/drivers/soc/amlogic/Kconfig index 23bfb8ef4fdb..bc2c912949bd 100644 --- a/drivers/soc/amlogic/Kconfig +++ b/drivers/soc/amlogic/Kconfig @@ -37,6 +37,17 @@ config MESON_GX_PM_DOMAINS Say yes to expose Amlogic Meson GX Power Domains as Generic Power Domains. +config MESON_EE_PM_DOMAINS + bool "Amlogic Meson Everything-Else Power Domains driver" + depends on ARCH_MESON || COMPILE_TEST + depends on PM && OF + default ARCH_MESON + select PM_GENERIC_DOMAINS + select PM_GENERIC_DOMAINS_OF + help + Say yes to expose Amlogic Meson Everything-Else Power Domains as + Generic Power Domains. + config MESON_MX_SOCINFO bool "Amlogic Meson MX SoC Information driver" depends on ARCH_MESON || COMPILE_TEST diff --git a/drivers/soc/amlogic/Makefile b/drivers/soc/amlogic/Makefile index f2e4ed171297..de79d044b545 100644 --- a/drivers/soc/amlogic/Makefile +++ b/drivers/soc/amlogic/Makefile @@ -4,3 +4,4 @@ obj-$(CONFIG_MESON_CLK_MEASURE) += meson-clk-measure.o obj-$(CONFIG_MESON_GX_SOCINFO) += meson-gx-socinfo.o obj-$(CONFIG_MESON_GX_PM_DOMAINS) += meson-gx-pwrc-vpu.o obj-$(CONFIG_MESON_MX_SOCINFO) += meson-mx-socinfo.o +obj-$(CONFIG_MESON_EE_PM_DOMAINS) += meson-ee-pwrc.o diff --git a/drivers/soc/amlogic/meson-clk-measure.c b/drivers/soc/amlogic/meson-clk-measure.c index 19d4cbc93a17..0fa47d77577d 100644 --- a/drivers/soc/amlogic/meson-clk-measure.c +++ b/drivers/soc/amlogic/meson-clk-measure.c @@ -11,6 +11,8 @@ #include <linux/debugfs.h> #include <linux/regmap.h> +static DEFINE_MUTEX(measure_lock); + #define MSR_CLK_DUTY 0x0 #define MSR_CLK_REG0 0x4 #define MSR_CLK_REG1 0x8 @@ -322,6 +324,8 @@ static struct meson_msr_id clk_msr_g12a[CLK_MSR_MAX] = { CLK_MSR_ID(84, "co_tx"), CLK_MSR_ID(89, "hdmi_todig"), CLK_MSR_ID(90, "hdmitx_sys"), + CLK_MSR_ID(91, "sys_cpub_div16"), + CLK_MSR_ID(92, "sys_pll_cpub_div16"), CLK_MSR_ID(94, "eth_phy_rx"), CLK_MSR_ID(95, "eth_phy_pll"), CLK_MSR_ID(96, "vpu_b"), @@ -353,6 +357,136 @@ static struct meson_msr_id clk_msr_g12a[CLK_MSR_MAX] = { CLK_MSR_ID(122, "audio_pdm_dclk"), }; +static struct meson_msr_id clk_msr_sm1[CLK_MSR_MAX] = { + CLK_MSR_ID(0, "ring_osc_out_ee_0"), + CLK_MSR_ID(1, "ring_osc_out_ee_1"), + CLK_MSR_ID(2, "ring_osc_out_ee_2"), + CLK_MSR_ID(3, "ring_osc_out_ee_3"), + CLK_MSR_ID(4, "gp0_pll"), + CLK_MSR_ID(5, "gp1_pll"), + CLK_MSR_ID(6, "enci"), + CLK_MSR_ID(7, "clk81"), + CLK_MSR_ID(8, "encp"), + CLK_MSR_ID(9, "encl"), + CLK_MSR_ID(10, "vdac"), + CLK_MSR_ID(11, "eth_tx"), + CLK_MSR_ID(12, "hifi_pll"), + CLK_MSR_ID(13, "mod_tcon"), + CLK_MSR_ID(14, "fec_0"), + CLK_MSR_ID(15, "fec_1"), + CLK_MSR_ID(16, "fec_2"), + CLK_MSR_ID(17, "sys_pll_div16"), + CLK_MSR_ID(18, "sys_cpu_div16"), + CLK_MSR_ID(19, "lcd_an_ph2"), + CLK_MSR_ID(20, "rtc_osc_out"), + CLK_MSR_ID(21, "lcd_an_ph3"), + CLK_MSR_ID(22, "eth_phy_ref"), + CLK_MSR_ID(23, "mpll_50m"), + CLK_MSR_ID(24, "eth_125m"), + CLK_MSR_ID(25, "eth_rmii"), + CLK_MSR_ID(26, "sc_int"), + CLK_MSR_ID(27, "in_mac"), + CLK_MSR_ID(28, "sar_adc"), + CLK_MSR_ID(29, "pcie_inp"), + CLK_MSR_ID(30, "pcie_inn"), + CLK_MSR_ID(31, "mpll_test_out"), + CLK_MSR_ID(32, "vdec"), + CLK_MSR_ID(34, "eth_mpll_50m"), + CLK_MSR_ID(35, "mali"), + CLK_MSR_ID(36, "hdmi_tx_pixel"), + CLK_MSR_ID(37, "cdac"), + CLK_MSR_ID(38, "vdin_meas"), + CLK_MSR_ID(39, "bt656"), + CLK_MSR_ID(40, "arm_ring_osc_out_4"), + CLK_MSR_ID(41, "eth_rx_or_rmii"), + CLK_MSR_ID(42, "mp0_out"), + CLK_MSR_ID(43, "fclk_div5"), + CLK_MSR_ID(44, "pwm_b"), + CLK_MSR_ID(45, "pwm_a"), + CLK_MSR_ID(46, "vpu"), + CLK_MSR_ID(47, "ddr_dpll_pt"), + CLK_MSR_ID(48, "mp1_out"), + CLK_MSR_ID(49, "mp2_out"), + CLK_MSR_ID(50, "mp3_out"), + CLK_MSR_ID(51, "sd_emmc_c"), + CLK_MSR_ID(52, "sd_emmc_b"), + CLK_MSR_ID(53, "sd_emmc_a"), + CLK_MSR_ID(54, "vpu_clkc"), + CLK_MSR_ID(55, "vid_pll_div_out"), + CLK_MSR_ID(56, "wave420l_a"), + CLK_MSR_ID(57, "wave420l_c"), + CLK_MSR_ID(58, "wave420l_b"), + CLK_MSR_ID(59, "hcodec"), + CLK_MSR_ID(60, "arm_ring_osc_out_5"), + CLK_MSR_ID(61, "gpio_msr"), + CLK_MSR_ID(62, "hevcb"), + CLK_MSR_ID(63, "dsi_meas"), + CLK_MSR_ID(64, "spicc_1"), + CLK_MSR_ID(65, "spicc_0"), + CLK_MSR_ID(66, "vid_lock"), + CLK_MSR_ID(67, "dsi_phy"), + CLK_MSR_ID(68, "hdcp22_esm"), + CLK_MSR_ID(69, "hdcp22_skp"), + CLK_MSR_ID(70, "pwm_f"), + CLK_MSR_ID(71, "pwm_e"), + CLK_MSR_ID(72, "pwm_d"), + CLK_MSR_ID(73, "pwm_c"), + CLK_MSR_ID(74, "arm_ring_osc_out_6"), + CLK_MSR_ID(75, "hevcf"), + CLK_MSR_ID(76, "arm_ring_osc_out_7"), + CLK_MSR_ID(77, "rng_ring_osc_0"), + CLK_MSR_ID(78, "rng_ring_osc_1"), + CLK_MSR_ID(79, "rng_ring_osc_2"), + CLK_MSR_ID(80, "rng_ring_osc_3"), + CLK_MSR_ID(81, "vapb"), + CLK_MSR_ID(82, "ge2d"), + CLK_MSR_ID(83, "co_rx"), + CLK_MSR_ID(84, "co_tx"), + CLK_MSR_ID(85, "arm_ring_osc_out_8"), + CLK_MSR_ID(86, "arm_ring_osc_out_9"), + CLK_MSR_ID(87, "mipi_dsi_phy"), + CLK_MSR_ID(88, "cis2_adapt"), + CLK_MSR_ID(89, "hdmi_todig"), + CLK_MSR_ID(90, "hdmitx_sys"), + CLK_MSR_ID(91, "nna_core"), + CLK_MSR_ID(92, "nna_axi"), + CLK_MSR_ID(93, "vad"), + CLK_MSR_ID(94, "eth_phy_rx"), + CLK_MSR_ID(95, "eth_phy_pll"), + CLK_MSR_ID(96, "vpu_b"), + CLK_MSR_ID(97, "cpu_b_tmp"), + CLK_MSR_ID(98, "ts"), + CLK_MSR_ID(99, "arm_ring_osc_out_10"), + CLK_MSR_ID(100, "arm_ring_osc_out_11"), + CLK_MSR_ID(101, "arm_ring_osc_out_12"), + CLK_MSR_ID(102, "arm_ring_osc_out_13"), + CLK_MSR_ID(103, "arm_ring_osc_out_14"), + CLK_MSR_ID(104, "arm_ring_osc_out_15"), + CLK_MSR_ID(105, "arm_ring_osc_out_16"), + CLK_MSR_ID(106, "ephy_test"), + CLK_MSR_ID(107, "au_dac_g128x"), + CLK_MSR_ID(108, "audio_locker_out"), + CLK_MSR_ID(109, "audio_locker_in"), + CLK_MSR_ID(110, "audio_tdmout_c_sclk"), + CLK_MSR_ID(111, "audio_tdmout_b_sclk"), + CLK_MSR_ID(112, "audio_tdmout_a_sclk"), + CLK_MSR_ID(113, "audio_tdmin_lb_sclk"), + CLK_MSR_ID(114, "audio_tdmin_c_sclk"), + CLK_MSR_ID(115, "audio_tdmin_b_sclk"), + CLK_MSR_ID(116, "audio_tdmin_a_sclk"), + CLK_MSR_ID(117, "audio_resample"), + CLK_MSR_ID(118, "audio_pdm_sys"), + CLK_MSR_ID(119, "audio_spdifout_b"), + CLK_MSR_ID(120, "audio_spdifout"), + CLK_MSR_ID(121, "audio_spdifin"), + CLK_MSR_ID(122, "audio_pdm_dclk"), + CLK_MSR_ID(123, "audio_resampled"), + CLK_MSR_ID(124, "earcrx_pll"), + CLK_MSR_ID(125, "earcrx_pll_test"), + CLK_MSR_ID(126, "csi_phy0"), + CLK_MSR_ID(127, "csi2_data"), +}; + static int meson_measure_id(struct meson_msr_id *clk_msr_id, unsigned int duration) { @@ -360,6 +494,10 @@ static int meson_measure_id(struct meson_msr_id *clk_msr_id, unsigned int val; int ret; + ret = mutex_lock_interruptible(&measure_lock); + if (ret) + return ret; + regmap_write(priv->regmap, MSR_CLK_REG0, 0); /* Set measurement duration */ @@ -377,8 +515,10 @@ static int meson_measure_id(struct meson_msr_id *clk_msr_id, ret = regmap_read_poll_timeout(priv->regmap, MSR_CLK_REG0, val, !(val & MSR_BUSY), 10, 10000); - if (ret) + if (ret) { + mutex_unlock(&measure_lock); return ret; + } /* Disable */ regmap_update_bits(priv->regmap, MSR_CLK_REG0, MSR_ENABLE, 0); @@ -386,6 +526,8 @@ static int meson_measure_id(struct meson_msr_id *clk_msr_id, /* Get the value in multiple of gate time counts */ regmap_read(priv->regmap, MSR_CLK_REG2, &val); + mutex_unlock(&measure_lock); + if (val >= MSR_VAL_MASK) return -EINVAL; @@ -533,6 +675,10 @@ static const struct of_device_id meson_msr_match_table[] = { .compatible = "amlogic,meson-g12a-clk-measure", .data = (void *)clk_msr_g12a, }, + { + .compatible = "amlogic,meson-sm1-clk-measure", + .data = (void *)clk_msr_sm1, + }, { /* sentinel */ } }; diff --git a/drivers/soc/amlogic/meson-ee-pwrc.c b/drivers/soc/amlogic/meson-ee-pwrc.c new file mode 100644 index 000000000000..5823f5b67d16 --- /dev/null +++ b/drivers/soc/amlogic/meson-ee-pwrc.c @@ -0,0 +1,492 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2019 BayLibre, SAS + * Author: Neil Armstrong <narmstrong@baylibre.com> + */ + +#include <linux/of_address.h> +#include <linux/platform_device.h> +#include <linux/pm_domain.h> +#include <linux/bitfield.h> +#include <linux/regmap.h> +#include <linux/mfd/syscon.h> +#include <linux/of_device.h> +#include <linux/reset-controller.h> +#include <linux/reset.h> +#include <linux/clk.h> +#include <dt-bindings/power/meson-g12a-power.h> +#include <dt-bindings/power/meson-sm1-power.h> + +/* AO Offsets */ + +#define AO_RTI_GEN_PWR_SLEEP0 (0x3a << 2) +#define AO_RTI_GEN_PWR_ISO0 (0x3b << 2) + +/* HHI Offsets */ + +#define HHI_MEM_PD_REG0 (0x40 << 2) +#define HHI_VPU_MEM_PD_REG0 (0x41 << 2) +#define HHI_VPU_MEM_PD_REG1 (0x42 << 2) +#define HHI_VPU_MEM_PD_REG3 (0x43 << 2) +#define HHI_VPU_MEM_PD_REG4 (0x44 << 2) +#define HHI_AUDIO_MEM_PD_REG0 (0x45 << 2) +#define HHI_NANOQ_MEM_PD_REG0 (0x46 << 2) +#define HHI_NANOQ_MEM_PD_REG1 (0x47 << 2) +#define HHI_VPU_MEM_PD_REG2 (0x4d << 2) + +struct meson_ee_pwrc; +struct meson_ee_pwrc_domain; + +struct meson_ee_pwrc_mem_domain { + unsigned int reg; + unsigned int mask; +}; + +struct meson_ee_pwrc_top_domain { + unsigned int sleep_reg; + unsigned int sleep_mask; + unsigned int iso_reg; + unsigned int iso_mask; +}; + +struct meson_ee_pwrc_domain_desc { + char *name; + unsigned int reset_names_count; + unsigned int clk_names_count; + struct meson_ee_pwrc_top_domain *top_pd; + unsigned int mem_pd_count; + struct meson_ee_pwrc_mem_domain *mem_pd; + bool (*get_power)(struct meson_ee_pwrc_domain *pwrc_domain); +}; + +struct meson_ee_pwrc_domain_data { + unsigned int count; + struct meson_ee_pwrc_domain_desc *domains; +}; + +/* TOP Power Domains */ + +static struct meson_ee_pwrc_top_domain g12a_pwrc_vpu = { + .sleep_reg = AO_RTI_GEN_PWR_SLEEP0, + .sleep_mask = BIT(8), + .iso_reg = AO_RTI_GEN_PWR_SLEEP0, + .iso_mask = BIT(9), +}; + +#define SM1_EE_PD(__bit) \ + { \ + .sleep_reg = AO_RTI_GEN_PWR_SLEEP0, \ + .sleep_mask = BIT(__bit), \ + .iso_reg = AO_RTI_GEN_PWR_ISO0, \ + .iso_mask = BIT(__bit), \ + } + +static struct meson_ee_pwrc_top_domain sm1_pwrc_vpu = SM1_EE_PD(8); +static struct meson_ee_pwrc_top_domain sm1_pwrc_nna = SM1_EE_PD(16); +static struct meson_ee_pwrc_top_domain sm1_pwrc_usb = SM1_EE_PD(17); +static struct meson_ee_pwrc_top_domain sm1_pwrc_pci = SM1_EE_PD(18); +static struct meson_ee_pwrc_top_domain sm1_pwrc_ge2d = SM1_EE_PD(19); + +/* Memory PD Domains */ + +#define VPU_MEMPD(__reg) \ + { __reg, GENMASK(1, 0) }, \ + { __reg, GENMASK(3, 2) }, \ + { __reg, GENMASK(5, 4) }, \ + { __reg, GENMASK(7, 6) }, \ + { __reg, GENMASK(9, 8) }, \ + { __reg, GENMASK(11, 10) }, \ + { __reg, GENMASK(13, 12) }, \ + { __reg, GENMASK(15, 14) }, \ + { __reg, GENMASK(17, 16) }, \ + { __reg, GENMASK(19, 18) }, \ + { __reg, GENMASK(21, 20) }, \ + { __reg, GENMASK(23, 22) }, \ + { __reg, GENMASK(25, 24) }, \ + { __reg, GENMASK(27, 26) }, \ + { __reg, GENMASK(29, 28) }, \ + { __reg, GENMASK(31, 30) } + +#define VPU_HHI_MEMPD(__reg) \ + { __reg, BIT(8) }, \ + { __reg, BIT(9) }, \ + { __reg, BIT(10) }, \ + { __reg, BIT(11) }, \ + { __reg, BIT(12) }, \ + { __reg, BIT(13) }, \ + { __reg, BIT(14) }, \ + { __reg, BIT(15) } + +static struct meson_ee_pwrc_mem_domain g12a_pwrc_mem_vpu[] = { + VPU_MEMPD(HHI_VPU_MEM_PD_REG0), + VPU_MEMPD(HHI_VPU_MEM_PD_REG1), + VPU_MEMPD(HHI_VPU_MEM_PD_REG2), + VPU_HHI_MEMPD(HHI_MEM_PD_REG0), +}; + +static struct meson_ee_pwrc_mem_domain g12a_pwrc_mem_eth[] = { + { HHI_MEM_PD_REG0, GENMASK(3, 2) }, +}; + +static struct meson_ee_pwrc_mem_domain sm1_pwrc_mem_vpu[] = { + VPU_MEMPD(HHI_VPU_MEM_PD_REG0), + VPU_MEMPD(HHI_VPU_MEM_PD_REG1), + VPU_MEMPD(HHI_VPU_MEM_PD_REG2), + VPU_MEMPD(HHI_VPU_MEM_PD_REG3), + { HHI_VPU_MEM_PD_REG4, GENMASK(1, 0) }, + { HHI_VPU_MEM_PD_REG4, GENMASK(3, 2) }, + { HHI_VPU_MEM_PD_REG4, GENMASK(5, 4) }, + { HHI_VPU_MEM_PD_REG4, GENMASK(7, 6) }, + VPU_HHI_MEMPD(HHI_MEM_PD_REG0), +}; + +static struct meson_ee_pwrc_mem_domain sm1_pwrc_mem_nna[] = { + { HHI_NANOQ_MEM_PD_REG0, 0xff }, + { HHI_NANOQ_MEM_PD_REG1, 0xff }, +}; + +static struct meson_ee_pwrc_mem_domain sm1_pwrc_mem_usb[] = { + { HHI_MEM_PD_REG0, GENMASK(31, 30) }, +}; + +static struct meson_ee_pwrc_mem_domain sm1_pwrc_mem_pcie[] = { + { HHI_MEM_PD_REG0, GENMASK(29, 26) }, +}; + +static struct meson_ee_pwrc_mem_domain sm1_pwrc_mem_ge2d[] = { + { HHI_MEM_PD_REG0, GENMASK(25, 18) }, +}; + +static struct meson_ee_pwrc_mem_domain sm1_pwrc_mem_audio[] = { + { HHI_MEM_PD_REG0, GENMASK(5, 4) }, + { HHI_AUDIO_MEM_PD_REG0, GENMASK(1, 0) }, + { HHI_AUDIO_MEM_PD_REG0, GENMASK(3, 2) }, + { HHI_AUDIO_MEM_PD_REG0, GENMASK(5, 4) }, + { HHI_AUDIO_MEM_PD_REG0, GENMASK(7, 6) }, + { HHI_AUDIO_MEM_PD_REG0, GENMASK(13, 12) }, + { HHI_AUDIO_MEM_PD_REG0, GENMASK(15, 14) }, + { HHI_AUDIO_MEM_PD_REG0, GENMASK(17, 16) }, + { HHI_AUDIO_MEM_PD_REG0, GENMASK(19, 18) }, + { HHI_AUDIO_MEM_PD_REG0, GENMASK(21, 20) }, + { HHI_AUDIO_MEM_PD_REG0, GENMASK(23, 22) }, + { HHI_AUDIO_MEM_PD_REG0, GENMASK(25, 24) }, + { HHI_AUDIO_MEM_PD_REG0, GENMASK(27, 26) }, +}; + +#define VPU_PD(__name, __top_pd, __mem, __get_power, __resets, __clks) \ + { \ + .name = __name, \ + .reset_names_count = __resets, \ + .clk_names_count = __clks, \ + .top_pd = __top_pd, \ + .mem_pd_count = ARRAY_SIZE(__mem), \ + .mem_pd = __mem, \ + .get_power = __get_power, \ + } + +#define TOP_PD(__name, __top_pd, __mem, __get_power) \ + { \ + .name = __name, \ + .top_pd = __top_pd, \ + .mem_pd_count = ARRAY_SIZE(__mem), \ + .mem_pd = __mem, \ + .get_power = __get_power, \ + } + +#define MEM_PD(__name, __mem) \ + TOP_PD(__name, NULL, __mem, NULL) + +static bool pwrc_ee_get_power(struct meson_ee_pwrc_domain *pwrc_domain); + +static struct meson_ee_pwrc_domain_desc g12a_pwrc_domains[] = { + [PWRC_G12A_VPU_ID] = VPU_PD("VPU", &g12a_pwrc_vpu, g12a_pwrc_mem_vpu, + pwrc_ee_get_power, 11, 2), + [PWRC_G12A_ETH_ID] = MEM_PD("ETH", g12a_pwrc_mem_eth), +}; + +static struct meson_ee_pwrc_domain_desc sm1_pwrc_domains[] = { + [PWRC_SM1_VPU_ID] = VPU_PD("VPU", &sm1_pwrc_vpu, sm1_pwrc_mem_vpu, + pwrc_ee_get_power, 11, 2), + [PWRC_SM1_NNA_ID] = TOP_PD("NNA", &sm1_pwrc_nna, sm1_pwrc_mem_nna, + pwrc_ee_get_power), + [PWRC_SM1_USB_ID] = TOP_PD("USB", &sm1_pwrc_usb, sm1_pwrc_mem_usb, + pwrc_ee_get_power), + [PWRC_SM1_PCIE_ID] = TOP_PD("PCI", &sm1_pwrc_pci, sm1_pwrc_mem_pcie, + pwrc_ee_get_power), + [PWRC_SM1_GE2D_ID] = TOP_PD("GE2D", &sm1_pwrc_ge2d, sm1_pwrc_mem_ge2d, + pwrc_ee_get_power), + [PWRC_SM1_AUDIO_ID] = MEM_PD("AUDIO", sm1_pwrc_mem_audio), + [PWRC_SM1_ETH_ID] = MEM_PD("ETH", g12a_pwrc_mem_eth), +}; + +struct meson_ee_pwrc_domain { + struct generic_pm_domain base; + bool enabled; + struct meson_ee_pwrc *pwrc; + struct meson_ee_pwrc_domain_desc desc; + struct clk_bulk_data *clks; + int num_clks; + struct reset_control *rstc; + int num_rstc; +}; + +struct meson_ee_pwrc { + struct regmap *regmap_ao; + struct regmap *regmap_hhi; + struct meson_ee_pwrc_domain *domains; + struct genpd_onecell_data xlate; +}; + +static bool pwrc_ee_get_power(struct meson_ee_pwrc_domain *pwrc_domain) +{ + u32 reg; + + regmap_read(pwrc_domain->pwrc->regmap_ao, + pwrc_domain->desc.top_pd->sleep_reg, ®); + + return (reg & pwrc_domain->desc.top_pd->sleep_mask); +} + +static int meson_ee_pwrc_off(struct generic_pm_domain *domain) +{ + struct meson_ee_pwrc_domain *pwrc_domain = + container_of(domain, struct meson_ee_pwrc_domain, base); + int i; + + if (pwrc_domain->desc.top_pd) + regmap_update_bits(pwrc_domain->pwrc->regmap_ao, + pwrc_domain->desc.top_pd->sleep_reg, + pwrc_domain->desc.top_pd->sleep_mask, + pwrc_domain->desc.top_pd->sleep_mask); + udelay(20); + + for (i = 0 ; i < pwrc_domain->desc.mem_pd_count ; ++i) + regmap_update_bits(pwrc_domain->pwrc->regmap_hhi, + pwrc_domain->desc.mem_pd[i].reg, + pwrc_domain->desc.mem_pd[i].mask, + pwrc_domain->desc.mem_pd[i].mask); + + udelay(20); + + if (pwrc_domain->desc.top_pd) + regmap_update_bits(pwrc_domain->pwrc->regmap_ao, + pwrc_domain->desc.top_pd->iso_reg, + pwrc_domain->desc.top_pd->iso_mask, + pwrc_domain->desc.top_pd->iso_mask); + + if (pwrc_domain->num_clks) { + msleep(20); + clk_bulk_disable_unprepare(pwrc_domain->num_clks, + pwrc_domain->clks); + } + + return 0; +} + +static int meson_ee_pwrc_on(struct generic_pm_domain *domain) +{ + struct meson_ee_pwrc_domain *pwrc_domain = + container_of(domain, struct meson_ee_pwrc_domain, base); + int i, ret; + + if (pwrc_domain->desc.top_pd) + regmap_update_bits(pwrc_domain->pwrc->regmap_ao, + pwrc_domain->desc.top_pd->sleep_reg, + pwrc_domain->desc.top_pd->sleep_mask, 0); + udelay(20); + + for (i = 0 ; i < pwrc_domain->desc.mem_pd_count ; ++i) + regmap_update_bits(pwrc_domain->pwrc->regmap_hhi, + pwrc_domain->desc.mem_pd[i].reg, + pwrc_domain->desc.mem_pd[i].mask, 0); + + udelay(20); + + ret = reset_control_assert(pwrc_domain->rstc); + if (ret) + return ret; + + if (pwrc_domain->desc.top_pd) + regmap_update_bits(pwrc_domain->pwrc->regmap_ao, + pwrc_domain->desc.top_pd->iso_reg, + pwrc_domain->desc.top_pd->iso_mask, 0); + + ret = reset_control_deassert(pwrc_domain->rstc); + if (ret) + return ret; + + return clk_bulk_prepare_enable(pwrc_domain->num_clks, + pwrc_domain->clks); +} + +static int meson_ee_pwrc_init_domain(struct platform_device *pdev, + struct meson_ee_pwrc *pwrc, + struct meson_ee_pwrc_domain *dom) +{ + dom->pwrc = pwrc; + dom->num_rstc = dom->desc.reset_names_count; + dom->num_clks = dom->desc.clk_names_count; + + if (dom->num_rstc) { + int count = reset_control_get_count(&pdev->dev); + + if (count != dom->num_rstc) + dev_warn(&pdev->dev, "Invalid resets count %d for domain %s\n", + count, dom->desc.name); + + dom->rstc = devm_reset_control_array_get(&pdev->dev, false, + false); + if (IS_ERR(dom->rstc)) + return PTR_ERR(dom->rstc); + } + + if (dom->num_clks) { + int ret = devm_clk_bulk_get_all(&pdev->dev, &dom->clks); + if (ret < 0) + return ret; + + if (dom->num_clks != ret) { + dev_warn(&pdev->dev, "Invalid clocks count %d for domain %s\n", + ret, dom->desc.name); + dom->num_clks = ret; + } + } + + dom->base.name = dom->desc.name; + dom->base.power_on = meson_ee_pwrc_on; + dom->base.power_off = meson_ee_pwrc_off; + + /* + * TOFIX: This is a special case for the VPU power domain, which can + * be enabled previously by the bootloader. In this case the VPU + * pipeline may be functional but no driver maybe never attach + * to this power domain, and if the domain is disabled it could + * cause system errors. This is why the pm_domain_always_on_gov + * is used here. + * For the same reason, the clocks should be enabled in case + * we need to power the domain off, otherwise the internal clocks + * prepare/enable counters won't be in sync. + */ + if (dom->num_clks && dom->desc.get_power && !dom->desc.get_power(dom)) { + int ret = clk_bulk_prepare_enable(dom->num_clks, dom->clks); + if (ret) + return ret; + + pm_genpd_init(&dom->base, &pm_domain_always_on_gov, false); + } else + pm_genpd_init(&dom->base, NULL, + (dom->desc.get_power ? + dom->desc.get_power(dom) : true)); + + return 0; +} + +static int meson_ee_pwrc_probe(struct platform_device *pdev) +{ + const struct meson_ee_pwrc_domain_data *match; + struct regmap *regmap_ao, *regmap_hhi; + struct meson_ee_pwrc *pwrc; + int i, ret; + + match = of_device_get_match_data(&pdev->dev); + if (!match) { + dev_err(&pdev->dev, "failed to get match data\n"); + return -ENODEV; + } + + pwrc = devm_kzalloc(&pdev->dev, sizeof(*pwrc), GFP_KERNEL); + if (!pwrc) + return -ENOMEM; + + pwrc->xlate.domains = devm_kcalloc(&pdev->dev, match->count, + sizeof(*pwrc->xlate.domains), + GFP_KERNEL); + if (!pwrc->xlate.domains) + return -ENOMEM; + + pwrc->domains = devm_kcalloc(&pdev->dev, match->count, + sizeof(*pwrc->domains), GFP_KERNEL); + if (!pwrc->domains) + return -ENOMEM; + + pwrc->xlate.num_domains = match->count; + + regmap_hhi = syscon_node_to_regmap(of_get_parent(pdev->dev.of_node)); + if (IS_ERR(regmap_hhi)) { + dev_err(&pdev->dev, "failed to get HHI regmap\n"); + return PTR_ERR(regmap_hhi); + } + + regmap_ao = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, + "amlogic,ao-sysctrl"); + if (IS_ERR(regmap_ao)) { + dev_err(&pdev->dev, "failed to get AO regmap\n"); + return PTR_ERR(regmap_ao); + } + + pwrc->regmap_ao = regmap_ao; + pwrc->regmap_hhi = regmap_hhi; + + platform_set_drvdata(pdev, pwrc); + + for (i = 0 ; i < match->count ; ++i) { + struct meson_ee_pwrc_domain *dom = &pwrc->domains[i]; + + memcpy(&dom->desc, &match->domains[i], sizeof(dom->desc)); + + ret = meson_ee_pwrc_init_domain(pdev, pwrc, dom); + if (ret) + return ret; + + pwrc->xlate.domains[i] = &dom->base; + } + + of_genpd_add_provider_onecell(pdev->dev.of_node, &pwrc->xlate); + + return 0; +} + +static void meson_ee_pwrc_shutdown(struct platform_device *pdev) +{ + struct meson_ee_pwrc *pwrc = platform_get_drvdata(pdev); + int i; + + for (i = 0 ; i < pwrc->xlate.num_domains ; ++i) { + struct meson_ee_pwrc_domain *dom = &pwrc->domains[i]; + + if (dom->desc.get_power && !dom->desc.get_power(dom)) + meson_ee_pwrc_off(&dom->base); + } +} + +static struct meson_ee_pwrc_domain_data meson_ee_g12a_pwrc_data = { + .count = ARRAY_SIZE(g12a_pwrc_domains), + .domains = g12a_pwrc_domains, +}; + +static struct meson_ee_pwrc_domain_data meson_ee_sm1_pwrc_data = { + .count = ARRAY_SIZE(sm1_pwrc_domains), + .domains = sm1_pwrc_domains, +}; + +static const struct of_device_id meson_ee_pwrc_match_table[] = { + { + .compatible = "amlogic,meson-g12a-pwrc", + .data = &meson_ee_g12a_pwrc_data, + }, + { + .compatible = "amlogic,meson-sm1-pwrc", + .data = &meson_ee_sm1_pwrc_data, + }, + { /* sentinel */ } +}; + +static struct platform_driver meson_ee_pwrc_driver = { + .probe = meson_ee_pwrc_probe, + .shutdown = meson_ee_pwrc_shutdown, + .driver = { + .name = "meson_ee_pwrc", + .of_match_table = meson_ee_pwrc_match_table, + }, +}; +builtin_platform_driver(meson_ee_pwrc_driver); diff --git a/drivers/soc/amlogic/meson-gx-socinfo.c b/drivers/soc/amlogic/meson-gx-socinfo.c index bca34954518e..6d0d04f163cb 100644 --- a/drivers/soc/amlogic/meson-gx-socinfo.c +++ b/drivers/soc/amlogic/meson-gx-socinfo.c @@ -39,6 +39,7 @@ static const struct meson_gx_soc_id { { "TXHD", 0x27 }, { "G12A", 0x28 }, { "G12B", 0x29 }, + { "SM1", 0x2b }, }; static const struct meson_gx_package_id { @@ -65,6 +66,8 @@ static const struct meson_gx_package_id { { "S905D2", 0x28, 0x10, 0xf0 }, { "S905X2", 0x28, 0x40, 0xf0 }, { "S922X", 0x29, 0x40, 0xf0 }, + { "A311D", 0x29, 0x10, 0xf0 }, + { "S905X3", 0x2b, 0x5, 0xf }, }; static inline unsigned int socinfo_to_major(u32 socinfo) @@ -138,8 +141,10 @@ static int __init meson_gx_socinfo_init(void) } /* check if chip-id is available */ - if (!of_property_read_bool(np, "amlogic,has-chip-id")) + if (!of_property_read_bool(np, "amlogic,has-chip-id")) { + of_node_put(np); return -ENODEV; + } /* node should be a syscon */ regmap = syscon_node_to_regmap(np); diff --git a/drivers/soc/fsl/dpaa2-console.c b/drivers/soc/fsl/dpaa2-console.c index 9168d8ddc932..27243f706f37 100644 --- a/drivers/soc/fsl/dpaa2-console.c +++ b/drivers/soc/fsl/dpaa2-console.c @@ -73,7 +73,7 @@ static u64 get_mc_fw_base_address(void) mcfbaregs = ioremap(mc_base_addr.start, resource_size(&mc_base_addr)); if (!mcfbaregs) { - pr_err("could not map MC Firmaware Base registers\n"); + pr_err("could not map MC Firmware Base registers\n"); return 0; } diff --git a/drivers/soc/fsl/dpio/dpio-service.c b/drivers/soc/fsl/dpio/dpio-service.c index b9539ef2c3cd..518a8e081b49 100644 --- a/drivers/soc/fsl/dpio/dpio-service.c +++ b/drivers/soc/fsl/dpio/dpio-service.c @@ -305,8 +305,6 @@ void dpaa2_io_service_deregister(struct dpaa2_io *service, list_del(&ctx->node); spin_unlock_irqrestore(&d->lock_notifications, irqflags); - if (dev) - device_link_remove(dev, d->dev); } EXPORT_SYMBOL_GPL(dpaa2_io_service_deregister); diff --git a/drivers/soc/fsl/guts.c b/drivers/soc/fsl/guts.c index 1ef8068c8dd3..34810f9bb2ee 100644 --- a/drivers/soc/fsl/guts.c +++ b/drivers/soc/fsl/guts.c @@ -102,6 +102,11 @@ static const struct fsl_soc_die_attr fsl_soc_die[] = { .svr = 0x87360000, .mask = 0xff3f0000, }, + /* Die: LS1028A, SoC: LS1028A */ + { .die = "LS1028A", + .svr = 0x870b0000, + .mask = 0xff3f0000, + }, { }, }; @@ -224,6 +229,7 @@ static const struct of_device_id fsl_guts_of_match[] = { { .compatible = "fsl,ls1012a-dcfg", }, { .compatible = "fsl,ls1046a-dcfg", }, { .compatible = "fsl,lx2160a-dcfg", }, + { .compatible = "fsl,ls1028a-dcfg", }, {} }; MODULE_DEVICE_TABLE(of, fsl_guts_of_match); diff --git a/drivers/soc/fsl/qbman/bman.c b/drivers/soc/fsl/qbman/bman.c index f84ab596bde8..f4fb527d8301 100644 --- a/drivers/soc/fsl/qbman/bman.c +++ b/drivers/soc/fsl/qbman/bman.c @@ -635,30 +635,31 @@ int bman_p_irqsource_add(struct bman_portal *p, u32 bits) return 0; } -static int bm_shutdown_pool(u32 bpid) +int bm_shutdown_pool(u32 bpid) { + int err = 0; struct bm_mc_command *bm_cmd; union bm_mc_result *bm_res; + + struct bman_portal *p = get_affine_portal(); while (1) { - struct bman_portal *p = get_affine_portal(); /* Acquire buffers until empty */ bm_cmd = bm_mc_start(&p->p); bm_cmd->bpid = bpid; bm_mc_commit(&p->p, BM_MCC_VERB_CMD_ACQUIRE | 1); if (!bm_mc_result_timeout(&p->p, &bm_res)) { - put_affine_portal(); pr_crit("BMan Acquire Command timedout\n"); - return -ETIMEDOUT; + err = -ETIMEDOUT; + goto done; } if (!(bm_res->verb & BM_MCR_VERB_ACQUIRE_BUFCOUNT)) { - put_affine_portal(); /* Pool is empty */ - return 0; + goto done; } - put_affine_portal(); } - +done: + put_affine_portal(); return 0; } diff --git a/drivers/soc/fsl/qbman/bman_ccsr.c b/drivers/soc/fsl/qbman/bman_ccsr.c index 7c3cc968053c..cb24a08be084 100644 --- a/drivers/soc/fsl/qbman/bman_ccsr.c +++ b/drivers/soc/fsl/qbman/bman_ccsr.c @@ -97,17 +97,40 @@ static void bm_get_version(u16 *id, u8 *major, u8 *minor) /* signal transactions for FBPRs with higher priority */ #define FBPR_AR_RPRIO_HI BIT(30) -static void bm_set_memory(u64 ba, u32 size) +/* Track if probe has occurred and if cleanup is required */ +static int __bman_probed; +static int __bman_requires_cleanup; + + +static int bm_set_memory(u64 ba, u32 size) { + u32 bar, bare; u32 exp = ilog2(size); /* choke if size isn't within range */ DPAA_ASSERT(size >= 4096 && size <= 1024*1024*1024 && is_power_of_2(size)); /* choke if '[e]ba' has lower-alignment than 'size' */ DPAA_ASSERT(!(ba & (size - 1))); + + /* Check to see if BMan has already been initialized */ + bar = bm_ccsr_in(REG_FBPR_BAR); + if (bar) { + /* Maker sure ba == what was programmed) */ + bare = bm_ccsr_in(REG_FBPR_BARE); + if (bare != upper_32_bits(ba) || bar != lower_32_bits(ba)) { + pr_err("Attempted to reinitialize BMan with different BAR, got 0x%llx read BARE=0x%x BAR=0x%x\n", + ba, bare, bar); + return -ENOMEM; + } + pr_info("BMan BAR already configured\n"); + __bman_requires_cleanup = 1; + return 1; + } + bm_ccsr_out(REG_FBPR_BARE, upper_32_bits(ba)); bm_ccsr_out(REG_FBPR_BAR, lower_32_bits(ba)); bm_ccsr_out(REG_FBPR_AR, exp - 1); + return 0; } /* @@ -120,7 +143,6 @@ static void bm_set_memory(u64 ba, u32 size) */ static dma_addr_t fbpr_a; static size_t fbpr_sz; -static int __bman_probed; static int bman_fbpr(struct reserved_mem *rmem) { @@ -173,6 +195,16 @@ int bman_is_probed(void) } EXPORT_SYMBOL_GPL(bman_is_probed); +int bman_requires_cleanup(void) +{ + return __bman_requires_cleanup; +} + +void bman_done_cleanup(void) +{ + __bman_requires_cleanup = 0; +} + static int fsl_bman_probe(struct platform_device *pdev) { int ret, err_irq; diff --git a/drivers/soc/fsl/qbman/bman_portal.c b/drivers/soc/fsl/qbman/bman_portal.c index cf4f10d6f590..923c44063a9a 100644 --- a/drivers/soc/fsl/qbman/bman_portal.c +++ b/drivers/soc/fsl/qbman/bman_portal.c @@ -100,7 +100,7 @@ static int bman_portal_probe(struct platform_device *pdev) struct device_node *node = dev->of_node; struct bm_portal_config *pcfg; struct resource *addr_phys[2]; - int irq, cpu, err; + int irq, cpu, err, i; err = bman_is_probed(); if (!err) @@ -135,10 +135,8 @@ static int bman_portal_probe(struct platform_device *pdev) pcfg->cpu = -1; irq = platform_get_irq(pdev, 0); - if (irq <= 0) { - dev_err(dev, "Can't get %pOF IRQ'\n", node); + if (irq <= 0) goto err_ioremap1; - } pcfg->irq = irq; pcfg->addr_virt_ce = memremap(addr_phys[0]->start, @@ -178,6 +176,22 @@ static int bman_portal_probe(struct platform_device *pdev) if (!cpu_online(cpu)) bman_offline_cpu(cpu); + if (__bman_portals_probed == 1 && bman_requires_cleanup()) { + /* + * BMan wasn't reset prior to boot (Kexec for example) + * Empty all the buffer pools so they are in reset state + */ + for (i = 0; i < BM_POOL_MAX; i++) { + err = bm_shutdown_pool(i); + if (err) { + dev_err(dev, "Failed to shutdown bpool %d\n", + i); + goto err_portal_init; + } + } + bman_done_cleanup(); + } + return 0; err_portal_init: diff --git a/drivers/soc/fsl/qbman/bman_priv.h b/drivers/soc/fsl/qbman/bman_priv.h index 751ce90383b7..aa3981e04965 100644 --- a/drivers/soc/fsl/qbman/bman_priv.h +++ b/drivers/soc/fsl/qbman/bman_priv.h @@ -76,3 +76,8 @@ int bman_p_irqsource_add(struct bman_portal *p, u32 bits); const struct bm_portal_config * bman_get_bm_portal_config(const struct bman_portal *portal); + +int bman_requires_cleanup(void); +void bman_done_cleanup(void); + +int bm_shutdown_pool(u32 bpid); diff --git a/drivers/soc/fsl/qbman/dpaa_sys.c b/drivers/soc/fsl/qbman/dpaa_sys.c index e6d48dccb8d5..9dd8bb571dbc 100644 --- a/drivers/soc/fsl/qbman/dpaa_sys.c +++ b/drivers/soc/fsl/qbman/dpaa_sys.c @@ -37,42 +37,53 @@ int qbman_init_private_mem(struct device *dev, int idx, dma_addr_t *addr, size_t *size) { - int ret; struct device_node *mem_node; - u64 size64; + struct reserved_mem *rmem; + struct property *prop; + int len, err; + __be32 *res_array; - ret = of_reserved_mem_device_init_by_idx(dev, dev->of_node, idx); - if (ret) { - dev_err(dev, - "of_reserved_mem_device_init_by_idx(%d) failed 0x%x\n", - idx, ret); - return -ENODEV; - } - mem_node = of_parse_phandle(dev->of_node, "memory-region", 0); - if (mem_node) { - ret = of_property_read_u64(mem_node, "size", &size64); - if (ret) { - dev_err(dev, "of_address_to_resource fails 0x%x\n", - ret); - return -ENODEV; - } - *size = size64; - } else { + mem_node = of_parse_phandle(dev->of_node, "memory-region", idx); + if (!mem_node) { dev_err(dev, "No memory-region found for index %d\n", idx); return -ENODEV; } - if (!dma_alloc_coherent(dev, *size, addr, 0)) { - dev_err(dev, "DMA Alloc memory failed\n"); + rmem = of_reserved_mem_lookup(mem_node); + if (!rmem) { + dev_err(dev, "of_reserved_mem_lookup() returned NULL\n"); return -ENODEV; } + *addr = rmem->base; + *size = rmem->size; /* - * Disassociate the reserved memory area from the device - * because a device can only have one DMA memory area. This - * should be fine since the memory is allocated and initialized - * and only ever accessed by the QBMan device from now on + * Check if the reg property exists - if not insert the node + * so upon kexec() the same memory region address will be preserved. + * This is needed because QBMan HW does not allow the base address/ + * size to be modified once set. */ - of_reserved_mem_device_release(dev); + prop = of_find_property(mem_node, "reg", &len); + if (!prop) { + prop = devm_kzalloc(dev, sizeof(*prop), GFP_KERNEL); + if (!prop) + return -ENOMEM; + prop->value = res_array = devm_kzalloc(dev, sizeof(__be32) * 4, + GFP_KERNEL); + if (!prop->value) + return -ENOMEM; + res_array[0] = cpu_to_be32(upper_32_bits(*addr)); + res_array[1] = cpu_to_be32(lower_32_bits(*addr)); + res_array[2] = cpu_to_be32(upper_32_bits(*size)); + res_array[3] = cpu_to_be32(lower_32_bits(*size)); + prop->length = sizeof(__be32) * 4; + prop->name = devm_kstrdup(dev, "reg", GFP_KERNEL); + if (!prop->name) + return -ENOMEM; + err = of_add_property(mem_node, prop); + if (err) + return err; + } + return 0; } diff --git a/drivers/soc/fsl/qbman/qman.c b/drivers/soc/fsl/qbman/qman.c index 636f83f781f5..bf68d86d80ee 100644 --- a/drivers/soc/fsl/qbman/qman.c +++ b/drivers/soc/fsl/qbman/qman.c @@ -1018,6 +1018,20 @@ static inline void put_affine_portal(void) put_cpu_var(qman_affine_portal); } + +static inline struct qman_portal *get_portal_for_channel(u16 channel) +{ + int i; + + for (i = 0; i < num_possible_cpus(); i++) { + if (affine_portals[i] && + affine_portals[i]->config->channel == channel) + return affine_portals[i]; + } + + return NULL; +} + static struct workqueue_struct *qm_portal_wq; int qman_dqrr_set_ithresh(struct qman_portal *portal, u8 ithresh) @@ -1070,6 +1084,20 @@ int qman_wq_alloc(void) return 0; } + +void qman_enable_irqs(void) +{ + int i; + + for (i = 0; i < num_possible_cpus(); i++) { + if (affine_portals[i]) { + qm_out(&affine_portals[i]->p, QM_REG_ISR, 0xffffffff); + qm_out(&affine_portals[i]->p, QM_REG_IIR, 0); + } + + } +} + /* * This is what everything can wait on, even if it migrates to a different cpu * to the one whose affine portal it is waiting on. @@ -1164,6 +1192,7 @@ static int drain_mr_fqrni(struct qm_portal *p) { const union qm_mr_entry *msg; loop: + qm_mr_pvb_update(p); msg = qm_mr_current(p); if (!msg) { /* @@ -1180,7 +1209,8 @@ loop: * entries well before the ring has been fully consumed, so * we're being *really* paranoid here. */ - msleep(1); + mdelay(1); + qm_mr_pvb_update(p); msg = qm_mr_current(p); if (!msg) return 0; @@ -1267,8 +1297,8 @@ static int qman_create_portal(struct qman_portal *portal, qm_out(p, QM_REG_ISDR, isdr); portal->irq_sources = 0; qm_out(p, QM_REG_IER, 0); - qm_out(p, QM_REG_ISR, 0xffffffff); snprintf(portal->irqname, MAX_IRQNAME, IRQNAME, c->cpu); + qm_out(p, QM_REG_IIR, 1); if (request_irq(c->irq, portal_isr, 0, portal->irqname, portal)) { dev_err(c->dev, "request_irq() failed\n"); goto fail_irq; @@ -1288,7 +1318,7 @@ static int qman_create_portal(struct qman_portal *portal, isdr &= ~(QM_PIRQ_DQRI | QM_PIRQ_MRI); qm_out(p, QM_REG_ISDR, isdr); if (qm_dqrr_current(p)) { - dev_err(c->dev, "DQRR unclean\n"); + dev_dbg(c->dev, "DQRR unclean\n"); qm_dqrr_cdc_consume_n(p, 0xffff); } if (qm_mr_current(p) && drain_mr_fqrni(p)) { @@ -1301,8 +1331,10 @@ static int qman_create_portal(struct qman_portal *portal, } /* Success */ portal->config = c; + qm_out(p, QM_REG_ISR, 0xffffffff); qm_out(p, QM_REG_ISDR, 0); - qm_out(p, QM_REG_IIR, 0); + if (!qman_requires_cleanup()) + qm_out(p, QM_REG_IIR, 0); /* Write a sane SDQCR */ qm_dqrr_sdqcr_set(p, portal->sdqcr); return 0; @@ -2581,9 +2613,9 @@ static int _qm_dqrr_consume_and_match(struct qm_portal *p, u32 fqid, int s, #define qm_dqrr_drain_nomatch(p) \ _qm_dqrr_consume_and_match(p, 0, 0, false) -static int qman_shutdown_fq(u32 fqid) +int qman_shutdown_fq(u32 fqid) { - struct qman_portal *p; + struct qman_portal *p, *channel_portal; struct device *dev; union qm_mc_command *mcc; union qm_mc_result *mcr; @@ -2623,17 +2655,28 @@ static int qman_shutdown_fq(u32 fqid) channel = qm_fqd_get_chan(&mcr->queryfq.fqd); wq = qm_fqd_get_wq(&mcr->queryfq.fqd); + if (channel < qm_channel_pool1) { + channel_portal = get_portal_for_channel(channel); + if (channel_portal == NULL) { + dev_err(dev, "Can't find portal for dedicated channel 0x%x\n", + channel); + ret = -EIO; + goto out; + } + } else + channel_portal = p; + switch (state) { case QM_MCR_NP_STATE_TEN_SCHED: case QM_MCR_NP_STATE_TRU_SCHED: case QM_MCR_NP_STATE_ACTIVE: case QM_MCR_NP_STATE_PARKED: orl_empty = 0; - mcc = qm_mc_start(&p->p); + mcc = qm_mc_start(&channel_portal->p); qm_fqid_set(&mcc->fq, fqid); - qm_mc_commit(&p->p, QM_MCC_VERB_ALTER_RETIRE); - if (!qm_mc_result_timeout(&p->p, &mcr)) { - dev_err(dev, "QUERYFQ_NP timeout\n"); + qm_mc_commit(&channel_portal->p, QM_MCC_VERB_ALTER_RETIRE); + if (!qm_mc_result_timeout(&channel_portal->p, &mcr)) { + dev_err(dev, "ALTER_RETIRE timeout\n"); ret = -ETIMEDOUT; goto out; } @@ -2641,6 +2684,9 @@ static int qman_shutdown_fq(u32 fqid) QM_MCR_VERB_ALTER_RETIRE); res = mcr->result; /* Make a copy as we reuse MCR below */ + if (res == QM_MCR_RESULT_OK) + drain_mr_fqrni(&channel_portal->p); + if (res == QM_MCR_RESULT_PENDING) { /* * Need to wait for the FQRN in the message ring, which @@ -2670,21 +2716,25 @@ static int qman_shutdown_fq(u32 fqid) } /* Set the sdqcr to drain this channel */ if (channel < qm_channel_pool1) - qm_dqrr_sdqcr_set(&p->p, + qm_dqrr_sdqcr_set(&channel_portal->p, QM_SDQCR_TYPE_ACTIVE | QM_SDQCR_CHANNELS_DEDICATED); else - qm_dqrr_sdqcr_set(&p->p, + qm_dqrr_sdqcr_set(&channel_portal->p, QM_SDQCR_TYPE_ACTIVE | QM_SDQCR_CHANNELS_POOL_CONV (channel)); do { /* Keep draining DQRR while checking the MR*/ - qm_dqrr_drain_nomatch(&p->p); + qm_dqrr_drain_nomatch(&channel_portal->p); /* Process message ring too */ - found_fqrn = qm_mr_drain(&p->p, FQRN); + found_fqrn = qm_mr_drain(&channel_portal->p, + FQRN); cpu_relax(); } while (!found_fqrn); + /* Restore SDQCR */ + qm_dqrr_sdqcr_set(&channel_portal->p, + channel_portal->sdqcr); } if (res != QM_MCR_RESULT_OK && @@ -2715,9 +2765,8 @@ static int qman_shutdown_fq(u32 fqid) * Wait for a dequeue and process the dequeues, * making sure to empty the ring completely */ - } while (qm_dqrr_drain_wait(&p->p, fqid, FQ_EMPTY)); + } while (!qm_dqrr_drain_wait(&p->p, fqid, FQ_EMPTY)); } - qm_dqrr_sdqcr_set(&p->p, 0); while (!orl_empty) { /* Wait for the ORL to have been completely drained */ @@ -2754,7 +2803,7 @@ static int qman_shutdown_fq(u32 fqid) DPAA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) == QM_MCR_VERB_ALTER_OOS); - if (mcr->result) { + if (mcr->result != QM_MCR_RESULT_OK) { dev_err(dev, "OOS fail: FQ 0x%x (0x%x)\n", fqid, mcr->result); ret = -EIO; diff --git a/drivers/soc/fsl/qbman/qman_ccsr.c b/drivers/soc/fsl/qbman/qman_ccsr.c index a6bb43007d03..157659fd033a 100644 --- a/drivers/soc/fsl/qbman/qman_ccsr.c +++ b/drivers/soc/fsl/qbman/qman_ccsr.c @@ -274,6 +274,7 @@ static u32 __iomem *qm_ccsr_start; /* A SDQCR mask comprising all the available/visible pool channels */ static u32 qm_pools_sdqcr; static int __qman_probed; +static int __qman_requires_cleanup; static inline u32 qm_ccsr_in(u32 offset) { @@ -340,19 +341,55 @@ static void qm_get_version(u16 *id, u8 *major, u8 *minor) } #define PFDR_AR_EN BIT(31) -static void qm_set_memory(enum qm_memory memory, u64 ba, u32 size) +static int qm_set_memory(enum qm_memory memory, u64 ba, u32 size) { + void *ptr; u32 offset = (memory == qm_memory_fqd) ? REG_FQD_BARE : REG_PFDR_BARE; u32 exp = ilog2(size); + u32 bar, bare; /* choke if size isn't within range */ DPAA_ASSERT((size >= 4096) && (size <= 1024*1024*1024) && is_power_of_2(size)); /* choke if 'ba' has lower-alignment than 'size' */ DPAA_ASSERT(!(ba & (size - 1))); + + /* Check to see if QMan has already been initialized */ + bar = qm_ccsr_in(offset + REG_offset_BAR); + if (bar) { + /* Maker sure ba == what was programmed) */ + bare = qm_ccsr_in(offset); + if (bare != upper_32_bits(ba) || bar != lower_32_bits(ba)) { + pr_err("Attempted to reinitialize QMan with different BAR, got 0x%llx read BARE=0x%x BAR=0x%x\n", + ba, bare, bar); + return -ENOMEM; + } + __qman_requires_cleanup = 1; + /* Return 1 to indicate memory was previously programmed */ + return 1; + } + /* Need to temporarily map the area to make sure it is zeroed */ + ptr = memremap(ba, size, MEMREMAP_WB); + if (!ptr) { + pr_crit("memremap() of QMan private memory failed\n"); + return -ENOMEM; + } + memset(ptr, 0, size); + +#ifdef CONFIG_PPC + /* + * PPC doesn't appear to flush the cache on memunmap() but the + * cache must be flushed since QMan does non coherent accesses + * to this memory + */ + flush_dcache_range((unsigned long) ptr, (unsigned long) ptr+size); +#endif + memunmap(ptr); + qm_ccsr_out(offset, upper_32_bits(ba)); qm_ccsr_out(offset + REG_offset_BAR, lower_32_bits(ba)); qm_ccsr_out(offset + REG_offset_AR, PFDR_AR_EN | (exp - 1)); + return 0; } static void qm_set_pfdr_threshold(u32 th, u8 k) @@ -455,7 +492,7 @@ RESERVEDMEM_OF_DECLARE(qman_pfdr, "fsl,qman-pfdr", qman_pfdr); #endif -static unsigned int qm_get_fqid_maxcnt(void) +unsigned int qm_get_fqid_maxcnt(void) { return fqd_sz / 64; } @@ -571,12 +608,19 @@ static int qman_init_ccsr(struct device *dev) int i, err; /* FQD memory */ - qm_set_memory(qm_memory_fqd, fqd_a, fqd_sz); + err = qm_set_memory(qm_memory_fqd, fqd_a, fqd_sz); + if (err < 0) + return err; /* PFDR memory */ - qm_set_memory(qm_memory_pfdr, pfdr_a, pfdr_sz); - err = qm_init_pfdr(dev, 8, pfdr_sz / 64 - 8); - if (err) + err = qm_set_memory(qm_memory_pfdr, pfdr_a, pfdr_sz); + if (err < 0) return err; + /* Only initialize PFDRs if the QMan was not initialized before */ + if (err == 0) { + err = qm_init_pfdr(dev, 8, pfdr_sz / 64 - 8); + if (err) + return err; + } /* thresholds */ qm_set_pfdr_threshold(512, 64); qm_set_sfdr_threshold(128); @@ -693,6 +737,18 @@ int qman_is_probed(void) } EXPORT_SYMBOL_GPL(qman_is_probed); +int qman_requires_cleanup(void) +{ + return __qman_requires_cleanup; +} + +void qman_done_cleanup(void) +{ + qman_enable_irqs(); + __qman_requires_cleanup = 0; +} + + static int fsl_qman_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; diff --git a/drivers/soc/fsl/qbman/qman_portal.c b/drivers/soc/fsl/qbman/qman_portal.c index e2186b681d87..5685b6706893 100644 --- a/drivers/soc/fsl/qbman/qman_portal.c +++ b/drivers/soc/fsl/qbman/qman_portal.c @@ -233,7 +233,7 @@ static int qman_portal_probe(struct platform_device *pdev) struct device_node *node = dev->of_node; struct qm_portal_config *pcfg; struct resource *addr_phys[2]; - int irq, cpu, err; + int irq, cpu, err, i; u32 val; err = qman_is_probed(); @@ -275,10 +275,8 @@ static int qman_portal_probe(struct platform_device *pdev) pcfg->channel = val; pcfg->cpu = -1; irq = platform_get_irq(pdev, 0); - if (irq <= 0) { - dev_err(dev, "Can't get %pOF IRQ\n", node); + if (irq <= 0) goto err_ioremap1; - } pcfg->irq = irq; pcfg->addr_virt_ce = memremap(addr_phys[0]->start, @@ -325,6 +323,22 @@ static int qman_portal_probe(struct platform_device *pdev) if (!cpu_online(cpu)) qman_offline_cpu(cpu); + if (__qman_portals_probed == 1 && qman_requires_cleanup()) { + /* + * QMan wasn't reset prior to boot (Kexec for example) + * Empty all the frame queues so they are in reset state + */ + for (i = 0; i < qm_get_fqid_maxcnt(); i++) { + err = qman_shutdown_fq(i); + if (err) { + dev_err(dev, "Failed to shutdown frame queue %d\n", + i); + goto err_portal_init; + } + } + qman_done_cleanup(); + } + return 0; err_portal_init: diff --git a/drivers/soc/fsl/qbman/qman_priv.h b/drivers/soc/fsl/qbman/qman_priv.h index 04515718cfd9..fd1cf543fb81 100644 --- a/drivers/soc/fsl/qbman/qman_priv.h +++ b/drivers/soc/fsl/qbman/qman_priv.h @@ -272,3 +272,11 @@ extern struct qman_portal *affine_portals[NR_CPUS]; extern struct qman_portal *qman_dma_portal; const struct qm_portal_config *qman_get_qm_portal_config( struct qman_portal *portal); + +unsigned int qm_get_fqid_maxcnt(void); + +int qman_shutdown_fq(u32 fqid); + +int qman_requires_cleanup(void); +void qman_done_cleanup(void); +void qman_enable_irqs(void); diff --git a/drivers/soc/fsl/qe/qe.c b/drivers/soc/fsl/qe/qe.c index c9519e62308c..417df7e19281 100644 --- a/drivers/soc/fsl/qe/qe.c +++ b/drivers/soc/fsl/qe/qe.c @@ -10,6 +10,7 @@ * General Purpose functions for the global management of the * QUICC Engine (QE). */ +#include <linux/bitmap.h> #include <linux/errno.h> #include <linux/sched.h> #include <linux/kernel.h> @@ -39,29 +40,32 @@ static DEFINE_SPINLOCK(qe_lock); DEFINE_SPINLOCK(cmxgcr_lock); EXPORT_SYMBOL(cmxgcr_lock); -/* QE snum state */ -enum qe_snum_state { - QE_SNUM_STATE_USED, - QE_SNUM_STATE_FREE -}; - -/* QE snum */ -struct qe_snum { - u8 num; - enum qe_snum_state state; -}; - /* We allocate this here because it is used almost exclusively for * the communication processor devices. */ struct qe_immap __iomem *qe_immr; EXPORT_SYMBOL(qe_immr); -static struct qe_snum snums[QE_NUM_OF_SNUM]; /* Dynamically allocated SNUMs */ +static u8 snums[QE_NUM_OF_SNUM]; /* Dynamically allocated SNUMs */ +static DECLARE_BITMAP(snum_state, QE_NUM_OF_SNUM); static unsigned int qe_num_of_snum; static phys_addr_t qebase = -1; +static struct device_node *qe_get_device_node(void) +{ + struct device_node *qe; + + /* + * Newer device trees have an "fsl,qe" compatible property for the QE + * node, but we still need to support older device trees. + */ + qe = of_find_compatible_node(NULL, NULL, "fsl,qe"); + if (qe) + return qe; + return of_find_node_by_type(NULL, "qe"); +} + static phys_addr_t get_qe_base(void) { struct device_node *qe; @@ -71,12 +75,9 @@ static phys_addr_t get_qe_base(void) if (qebase != -1) return qebase; - qe = of_find_compatible_node(NULL, NULL, "fsl,qe"); - if (!qe) { - qe = of_find_node_by_type(NULL, "qe"); - if (!qe) - return qebase; - } + qe = qe_get_device_node(); + if (!qe) + return qebase; ret = of_address_to_resource(qe, 0, &res); if (!ret) @@ -170,12 +171,9 @@ unsigned int qe_get_brg_clk(void) if (brg_clk) return brg_clk; - qe = of_find_compatible_node(NULL, NULL, "fsl,qe"); - if (!qe) { - qe = of_find_node_by_type(NULL, "qe"); - if (!qe) - return brg_clk; - } + qe = qe_get_device_node(); + if (!qe) + return brg_clk; prop = of_get_property(qe, "brg-frequency", &size); if (prop && size == sizeof(*prop)) @@ -281,7 +279,6 @@ EXPORT_SYMBOL(qe_clock_source); */ static void qe_snums_init(void) { - int i; static const u8 snum_init_76[] = { 0x04, 0x05, 0x0C, 0x0D, 0x14, 0x15, 0x1C, 0x1D, 0x24, 0x25, 0x2C, 0x2D, 0x34, 0x35, 0x88, 0x89, @@ -302,19 +299,39 @@ static void qe_snums_init(void) 0x28, 0x29, 0x38, 0x39, 0x48, 0x49, 0x58, 0x59, 0x68, 0x69, 0x78, 0x79, 0x80, 0x81, }; - static const u8 *snum_init; + struct device_node *qe; + const u8 *snum_init; + int i; - qe_num_of_snum = qe_get_num_of_snums(); + bitmap_zero(snum_state, QE_NUM_OF_SNUM); + qe_num_of_snum = 28; /* The default number of snum for threads is 28 */ + qe = qe_get_device_node(); + if (qe) { + i = of_property_read_variable_u8_array(qe, "fsl,qe-snums", + snums, 1, QE_NUM_OF_SNUM); + if (i > 0) { + of_node_put(qe); + qe_num_of_snum = i; + return; + } + /* + * Fall back to legacy binding of using the value of + * fsl,qe-num-snums to choose one of the static arrays + * above. + */ + of_property_read_u32(qe, "fsl,qe-num-snums", &qe_num_of_snum); + of_node_put(qe); + } - if (qe_num_of_snum == 76) + if (qe_num_of_snum == 76) { snum_init = snum_init_76; - else + } else if (qe_num_of_snum == 28 || qe_num_of_snum == 46) { snum_init = snum_init_46; - - for (i = 0; i < qe_num_of_snum; i++) { - snums[i].num = snum_init[i]; - snums[i].state = QE_SNUM_STATE_FREE; + } else { + pr_err("QE: unsupported value of fsl,qe-num-snums: %u\n", qe_num_of_snum); + return; } + memcpy(snums, snum_init, qe_num_of_snum); } int qe_get_snum(void) @@ -324,12 +341,10 @@ int qe_get_snum(void) int i; spin_lock_irqsave(&qe_lock, flags); - for (i = 0; i < qe_num_of_snum; i++) { - if (snums[i].state == QE_SNUM_STATE_FREE) { - snums[i].state = QE_SNUM_STATE_USED; - snum = snums[i].num; - break; - } + i = find_first_zero_bit(snum_state, qe_num_of_snum); + if (i < qe_num_of_snum) { + set_bit(i, snum_state); + snum = snums[i]; } spin_unlock_irqrestore(&qe_lock, flags); @@ -339,14 +354,10 @@ EXPORT_SYMBOL(qe_get_snum); void qe_put_snum(u8 snum) { - int i; + const u8 *p = memchr(snums, snum, qe_num_of_snum); - for (i = 0; i < qe_num_of_snum; i++) { - if (snums[i].num == snum) { - snums[i].state = QE_SNUM_STATE_FREE; - break; - } - } + if (p) + clear_bit(p - snums, snum_state); } EXPORT_SYMBOL(qe_put_snum); @@ -572,16 +583,9 @@ struct qe_firmware_info *qe_get_firmware_info(void) initialized = 1; - /* - * Newer device trees have an "fsl,qe" compatible property for the QE - * node, but we still need to support older device trees. - */ - qe = of_find_compatible_node(NULL, NULL, "fsl,qe"); - if (!qe) { - qe = of_find_node_by_type(NULL, "qe"); - if (!qe) - return NULL; - } + qe = qe_get_device_node(); + if (!qe) + return NULL; /* Find the 'firmware' child node */ fw = of_get_child_by_name(qe, "firmware"); @@ -627,16 +631,9 @@ unsigned int qe_get_num_of_risc(void) unsigned int num_of_risc = 0; const u32 *prop; - qe = of_find_compatible_node(NULL, NULL, "fsl,qe"); - if (!qe) { - /* Older devices trees did not have an "fsl,qe" - * compatible property, so we need to look for - * the QE node by name. - */ - qe = of_find_node_by_type(NULL, "qe"); - if (!qe) - return num_of_risc; - } + qe = qe_get_device_node(); + if (!qe) + return num_of_risc; prop = of_get_property(qe, "fsl,qe-num-riscs", &size); if (prop && size == sizeof(*prop)) @@ -650,37 +647,7 @@ EXPORT_SYMBOL(qe_get_num_of_risc); unsigned int qe_get_num_of_snums(void) { - struct device_node *qe; - int size; - unsigned int num_of_snums; - const u32 *prop; - - num_of_snums = 28; /* The default number of snum for threads is 28 */ - qe = of_find_compatible_node(NULL, NULL, "fsl,qe"); - if (!qe) { - /* Older devices trees did not have an "fsl,qe" - * compatible property, so we need to look for - * the QE node by name. - */ - qe = of_find_node_by_type(NULL, "qe"); - if (!qe) - return num_of_snums; - } - - prop = of_get_property(qe, "fsl,qe-num-snums", &size); - if (prop && size == sizeof(*prop)) { - num_of_snums = *prop; - if ((num_of_snums < 28) || (num_of_snums > QE_NUM_OF_SNUM)) { - /* No QE ever has fewer than 28 SNUMs */ - pr_err("QE: number of snum is invalid\n"); - of_node_put(qe); - return -EINVAL; - } - } - - of_node_put(qe); - - return num_of_snums; + return qe_num_of_snum; } EXPORT_SYMBOL(qe_get_num_of_snums); diff --git a/drivers/soc/imx/gpcv2.c b/drivers/soc/imx/gpcv2.c index 31b8d002d855..b0dffb06c05d 100644 --- a/drivers/soc/imx/gpcv2.c +++ b/drivers/soc/imx/gpcv2.c @@ -198,7 +198,7 @@ static int imx_gpc_pu_pgc_sw_pxx_req(struct generic_pm_domain *genpd, err = regulator_disable(domain->regulator); if (err) dev_err(domain->dev, - "failed to disable regulator: %d\n", ret); + "failed to disable regulator: %d\n", err); /* Preserve earlier error code */ ret = ret ?: err; } diff --git a/drivers/soc/imx/soc-imx-scu.c b/drivers/soc/imx/soc-imx-scu.c index 676f612f6488..50831ebf126a 100644 --- a/drivers/soc/imx/soc-imx-scu.c +++ b/drivers/soc/imx/soc-imx-scu.c @@ -27,6 +27,40 @@ struct imx_sc_msg_misc_get_soc_id { } data; } __packed; +struct imx_sc_msg_misc_get_soc_uid { + struct imx_sc_rpc_msg hdr; + u32 uid_low; + u32 uid_high; +} __packed; + +static ssize_t soc_uid_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct imx_sc_msg_misc_get_soc_uid msg; + struct imx_sc_rpc_msg *hdr = &msg.hdr; + u64 soc_uid; + int ret; + + hdr->ver = IMX_SC_RPC_VERSION; + hdr->svc = IMX_SC_RPC_SVC_MISC; + hdr->func = IMX_SC_MISC_FUNC_UNIQUE_ID; + hdr->size = 1; + + ret = imx_scu_call_rpc(soc_ipc_handle, &msg, false); + if (ret) { + pr_err("%s: get soc uid failed, ret %d\n", __func__, ret); + return ret; + } + + soc_uid = msg.uid_high; + soc_uid <<= 32; + soc_uid |= msg.uid_low; + + return sprintf(buf, "%016llX\n", soc_uid); +} + +static DEVICE_ATTR_RO(soc_uid); + static int imx_scu_soc_id(void) { struct imx_sc_msg_misc_get_soc_id msg; @@ -102,6 +136,11 @@ static int imx_scu_soc_probe(struct platform_device *pdev) goto free_revision; } + ret = device_create_file(soc_device_to_device(soc_dev), + &dev_attr_soc_uid); + if (ret) + goto free_revision; + return 0; free_revision: diff --git a/drivers/soc/imx/soc-imx8.c b/drivers/soc/imx/soc-imx8.c index f924ae8c6514..b9831576dd25 100644 --- a/drivers/soc/imx/soc-imx8.c +++ b/drivers/soc/imx/soc-imx8.c @@ -16,6 +16,9 @@ #define IMX8MQ_SW_INFO_B1 0x40 #define IMX8MQ_SW_MAGIC_B1 0xff0055aa +#define OCOTP_UID_LOW 0x410 +#define OCOTP_UID_HIGH 0x420 + /* Same as ANADIG_DIGPROG_IMX7D */ #define ANADIG_DIGPROG_IMX8MM 0x800 @@ -24,6 +27,16 @@ struct imx8_soc_data { u32 (*soc_revision)(void); }; +static u64 soc_uid; + +static ssize_t soc_uid_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%016llX\n", soc_uid); +} + +static DEVICE_ATTR_RO(soc_uid); + static u32 __init imx8mq_soc_revision(void) { struct device_node *np; @@ -42,6 +55,10 @@ static u32 __init imx8mq_soc_revision(void) if (magic == IMX8MQ_SW_MAGIC_B1) rev = REV_B1; + soc_uid = readl_relaxed(ocotp_base + OCOTP_UID_HIGH); + soc_uid <<= 32; + soc_uid |= readl_relaxed(ocotp_base + OCOTP_UID_LOW); + iounmap(ocotp_base); out: @@ -49,6 +66,26 @@ out: return rev; } +static void __init imx8mm_soc_uid(void) +{ + void __iomem *ocotp_base; + struct device_node *np; + + np = of_find_compatible_node(NULL, NULL, "fsl,imx8mm-ocotp"); + if (!np) + return; + + ocotp_base = of_iomap(np, 0); + WARN_ON(!ocotp_base); + + soc_uid = readl_relaxed(ocotp_base + OCOTP_UID_HIGH); + soc_uid <<= 32; + soc_uid |= readl_relaxed(ocotp_base + OCOTP_UID_LOW); + + iounmap(ocotp_base); + of_node_put(np); +} + static u32 __init imx8mm_soc_revision(void) { struct device_node *np; @@ -66,6 +103,9 @@ static u32 __init imx8mm_soc_revision(void) iounmap(anatop_base); of_node_put(np); + + imx8mm_soc_uid(); + return rev; } @@ -140,6 +180,11 @@ static int __init imx8_soc_init(void) goto free_rev; } + ret = device_create_file(soc_device_to_device(soc_dev), + &dev_attr_soc_uid); + if (ret) + goto free_rev; + if (IS_ENABLED(CONFIG_ARM_IMX_CPUFREQ_DT)) platform_device_register_simple("imx-cpufreq-dt", -1, NULL, 0); diff --git a/drivers/soc/mediatek/mtk-cmdq-helper.c b/drivers/soc/mediatek/mtk-cmdq-helper.c index ff9fef5a032b..7aa0517ff2f3 100644 --- a/drivers/soc/mediatek/mtk-cmdq-helper.c +++ b/drivers/soc/mediatek/mtk-cmdq-helper.c @@ -136,7 +136,7 @@ static int cmdq_pkt_append_command(struct cmdq_pkt *pkt, enum cmdq_code code, return 0; } -int cmdq_pkt_write(struct cmdq_pkt *pkt, u32 value, u32 subsys, u32 offset) +int cmdq_pkt_write(struct cmdq_pkt *pkt, u8 subsys, u16 offset, u32 value) { u32 arg_a = (offset & CMDQ_ARG_A_WRITE_MASK) | (subsys << CMDQ_SUBSYS_SHIFT); @@ -145,8 +145,8 @@ int cmdq_pkt_write(struct cmdq_pkt *pkt, u32 value, u32 subsys, u32 offset) } EXPORT_SYMBOL(cmdq_pkt_write); -int cmdq_pkt_write_mask(struct cmdq_pkt *pkt, u32 value, - u32 subsys, u32 offset, u32 mask) +int cmdq_pkt_write_mask(struct cmdq_pkt *pkt, u8 subsys, + u16 offset, u32 value, u32 mask) { u32 offset_mask = offset; int err = 0; @@ -161,7 +161,7 @@ int cmdq_pkt_write_mask(struct cmdq_pkt *pkt, u32 value, } EXPORT_SYMBOL(cmdq_pkt_write_mask); -int cmdq_pkt_wfe(struct cmdq_pkt *pkt, u32 event) +int cmdq_pkt_wfe(struct cmdq_pkt *pkt, u16 event) { u32 arg_b; @@ -181,7 +181,7 @@ int cmdq_pkt_wfe(struct cmdq_pkt *pkt, u32 event) } EXPORT_SYMBOL(cmdq_pkt_wfe); -int cmdq_pkt_clear_event(struct cmdq_pkt *pkt, u32 event) +int cmdq_pkt_clear_event(struct cmdq_pkt *pkt, u16 event) { if (event >= CMDQ_MAX_EVENT) return -EINVAL; diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index a6d1bfb17279..661e47acc354 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -175,6 +175,14 @@ config QCOM_SMSM Say yes here to support the Qualcomm Shared Memory State Machine. The state machine is represented by bits in shared memory. +config QCOM_SOCINFO + tristate "Qualcomm socinfo driver" + depends on QCOM_SMEM + select SOC_BUS + help + Say yes here to support the Qualcomm socinfo driver, providing + information about the SoC to user space. + config QCOM_WCNSS_CTRL tristate "Qualcomm WCNSS control driver" depends on ARCH_QCOM || COMPILE_TEST diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile index eeb088beb15f..162788701a77 100644 --- a/drivers/soc/qcom/Makefile +++ b/drivers/soc/qcom/Makefile @@ -18,6 +18,7 @@ obj-$(CONFIG_QCOM_SMEM) += smem.o obj-$(CONFIG_QCOM_SMEM_STATE) += smem_state.o obj-$(CONFIG_QCOM_SMP2P) += smp2p.o obj-$(CONFIG_QCOM_SMSM) += smsm.o +obj-$(CONFIG_QCOM_SOCINFO) += socinfo.o obj-$(CONFIG_QCOM_WCNSS_CTRL) += wcnss_ctrl.o obj-$(CONFIG_QCOM_APR) += apr.o obj-$(CONFIG_QCOM_LLCC) += llcc-slice.o diff --git a/drivers/soc/qcom/qcom_aoss.c b/drivers/soc/qcom/qcom_aoss.c index 5f885196f4d0..33a27e6c6d67 100644 --- a/drivers/soc/qcom/qcom_aoss.c +++ b/drivers/soc/qcom/qcom_aoss.c @@ -10,6 +10,8 @@ #include <linux/module.h> #include <linux/platform_device.h> #include <linux/pm_domain.h> +#include <linux/thermal.h> +#include <linux/slab.h> #define QMP_DESC_MAGIC 0x0 #define QMP_DESC_VERSION 0x4 @@ -40,6 +42,17 @@ /* 64 bytes is enough to store the requests and provides padding to 4 bytes */ #define QMP_MSG_LEN 64 +#define QMP_NUM_COOLING_RESOURCES 2 + +static bool qmp_cdev_init_state = 1; + +struct qmp_cooling_device { + struct thermal_cooling_device *cdev; + struct qmp *qmp; + char *name; + bool state; +}; + /** * struct qmp - driver state for QMP implementation * @msgram: iomem referencing the message RAM used for communication @@ -69,6 +82,7 @@ struct qmp { struct clk_hw qdss_clk; struct genpd_onecell_data pd_data; + struct qmp_cooling_device *cooling_devs; }; struct qmp_pd { @@ -385,6 +399,118 @@ static void qmp_pd_remove(struct qmp *qmp) pm_genpd_remove(data->domains[i]); } +static int qmp_cdev_get_max_state(struct thermal_cooling_device *cdev, + unsigned long *state) +{ + *state = qmp_cdev_init_state; + return 0; +} + +static int qmp_cdev_get_cur_state(struct thermal_cooling_device *cdev, + unsigned long *state) +{ + struct qmp_cooling_device *qmp_cdev = cdev->devdata; + + *state = qmp_cdev->state; + return 0; +} + +static int qmp_cdev_set_cur_state(struct thermal_cooling_device *cdev, + unsigned long state) +{ + struct qmp_cooling_device *qmp_cdev = cdev->devdata; + char buf[QMP_MSG_LEN] = {}; + bool cdev_state; + int ret; + + /* Normalize state */ + cdev_state = !!state; + + if (qmp_cdev->state == state) + return 0; + + snprintf(buf, sizeof(buf), + "{class: volt_flr, event:zero_temp, res:%s, value:%s}", + qmp_cdev->name, + cdev_state ? "off" : "on"); + + ret = qmp_send(qmp_cdev->qmp, buf, sizeof(buf)); + + if (!ret) + qmp_cdev->state = cdev_state; + + return ret; +} + +static struct thermal_cooling_device_ops qmp_cooling_device_ops = { + .get_max_state = qmp_cdev_get_max_state, + .get_cur_state = qmp_cdev_get_cur_state, + .set_cur_state = qmp_cdev_set_cur_state, +}; + +static int qmp_cooling_device_add(struct qmp *qmp, + struct qmp_cooling_device *qmp_cdev, + struct device_node *node) +{ + char *cdev_name = (char *)node->name; + + qmp_cdev->qmp = qmp; + qmp_cdev->state = qmp_cdev_init_state; + qmp_cdev->name = cdev_name; + qmp_cdev->cdev = devm_thermal_of_cooling_device_register + (qmp->dev, node, + cdev_name, + qmp_cdev, &qmp_cooling_device_ops); + + if (IS_ERR(qmp_cdev->cdev)) + dev_err(qmp->dev, "unable to register %s cooling device\n", + cdev_name); + + return PTR_ERR_OR_ZERO(qmp_cdev->cdev); +} + +static int qmp_cooling_devices_register(struct qmp *qmp) +{ + struct device_node *np, *child; + int count = QMP_NUM_COOLING_RESOURCES; + int ret; + + np = qmp->dev->of_node; + + qmp->cooling_devs = devm_kcalloc(qmp->dev, count, + sizeof(*qmp->cooling_devs), + GFP_KERNEL); + + if (!qmp->cooling_devs) + return -ENOMEM; + + for_each_available_child_of_node(np, child) { + if (!of_find_property(child, "#cooling-cells", NULL)) + continue; + ret = qmp_cooling_device_add(qmp, &qmp->cooling_devs[count++], + child); + if (ret) + goto unroll; + } + + return 0; + +unroll: + while (--count >= 0) + thermal_cooling_device_unregister + (qmp->cooling_devs[count].cdev); + + return ret; +} + +static void qmp_cooling_devices_remove(struct qmp *qmp) +{ + int i; + + for (i = 0; i < QMP_NUM_COOLING_RESOURCES; i++) + thermal_cooling_device_unregister(qmp->cooling_devs[i].cdev); +} + static int qmp_probe(struct platform_device *pdev) { struct resource *res; @@ -433,6 +559,10 @@ static int qmp_probe(struct platform_device *pdev) if (ret) goto err_remove_qdss_clk; + ret = qmp_cooling_devices_register(qmp); + if (ret) + dev_err(&pdev->dev, "failed to register aoss cooling devices\n"); + platform_set_drvdata(pdev, qmp); return 0; @@ -453,6 +583,7 @@ static int qmp_remove(struct platform_device *pdev) qmp_qdss_clk_remove(qmp); qmp_pd_remove(qmp); + qmp_cooling_devices_remove(qmp); qmp_close(qmp); mbox_free_channel(qmp->mbox_chan); @@ -461,7 +592,9 @@ static int qmp_remove(struct platform_device *pdev) } static const struct of_device_id qmp_dt_match[] = { + { .compatible = "qcom,sc7180-aoss-qmp", }, { .compatible = "qcom,sdm845-aoss-qmp", }, + { .compatible = "qcom,sm8150-aoss-qmp", }, {} }; MODULE_DEVICE_TABLE(of, qmp_dt_match); diff --git a/drivers/soc/qcom/smem.c b/drivers/soc/qcom/smem.c index f27c00d82ae4..28c19bcb2f20 100644 --- a/drivers/soc/qcom/smem.c +++ b/drivers/soc/qcom/smem.c @@ -84,7 +84,7 @@ #define SMEM_GLOBAL_HOST 0xfffe /* Max number of processors/hosts in a system */ -#define SMEM_HOST_COUNT 10 +#define SMEM_HOST_COUNT 11 /** * struct smem_proc_comm - proc_comm communication struct (legacy) @@ -268,6 +268,7 @@ struct qcom_smem { struct smem_partition_header *partitions[SMEM_HOST_COUNT]; size_t cacheline[SMEM_HOST_COUNT]; u32 item_count; + struct platform_device *socinfo; unsigned num_regions; struct smem_region regions[]; @@ -963,11 +964,19 @@ static int qcom_smem_probe(struct platform_device *pdev) __smem = smem; + smem->socinfo = platform_device_register_data(&pdev->dev, "qcom-socinfo", + PLATFORM_DEVID_NONE, NULL, + 0); + if (IS_ERR(smem->socinfo)) + dev_dbg(&pdev->dev, "failed to register socinfo device\n"); + return 0; } static int qcom_smem_remove(struct platform_device *pdev) { + platform_device_unregister(__smem->socinfo); + hwspin_lock_free(__smem->hwlock); __smem = NULL; diff --git a/drivers/soc/qcom/socinfo.c b/drivers/soc/qcom/socinfo.c new file mode 100644 index 000000000000..a39ea5061dc5 --- /dev/null +++ b/drivers/soc/qcom/socinfo.c @@ -0,0 +1,476 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2009-2017, The Linux Foundation. All rights reserved. + * Copyright (c) 2017-2019, Linaro Ltd. + */ + +#include <linux/debugfs.h> +#include <linux/err.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/random.h> +#include <linux/slab.h> +#include <linux/soc/qcom/smem.h> +#include <linux/string.h> +#include <linux/sys_soc.h> +#include <linux/types.h> + +/* + * SoC version type with major number in the upper 16 bits and minor + * number in the lower 16 bits. + */ +#define SOCINFO_MAJOR(ver) (((ver) >> 16) & 0xffff) +#define SOCINFO_MINOR(ver) ((ver) & 0xffff) +#define SOCINFO_VERSION(maj, min) ((((maj) & 0xffff) << 16)|((min) & 0xffff)) + +#define SMEM_SOCINFO_BUILD_ID_LENGTH 32 + +/* + * SMEM item id, used to acquire handles to respective + * SMEM region. + */ +#define SMEM_HW_SW_BUILD_ID 137 + +#ifdef CONFIG_DEBUG_FS +#define SMEM_IMAGE_VERSION_BLOCKS_COUNT 32 +#define SMEM_IMAGE_VERSION_SIZE 4096 +#define SMEM_IMAGE_VERSION_NAME_SIZE 75 +#define SMEM_IMAGE_VERSION_VARIANT_SIZE 20 +#define SMEM_IMAGE_VERSION_OEM_SIZE 32 + +/* + * SMEM Image table indices + */ +#define SMEM_IMAGE_TABLE_BOOT_INDEX 0 +#define SMEM_IMAGE_TABLE_TZ_INDEX 1 +#define SMEM_IMAGE_TABLE_RPM_INDEX 3 +#define SMEM_IMAGE_TABLE_APPS_INDEX 10 +#define SMEM_IMAGE_TABLE_MPSS_INDEX 11 +#define SMEM_IMAGE_TABLE_ADSP_INDEX 12 +#define SMEM_IMAGE_TABLE_CNSS_INDEX 13 +#define SMEM_IMAGE_TABLE_VIDEO_INDEX 14 +#define SMEM_IMAGE_VERSION_TABLE 469 + +/* + * SMEM Image table names + */ +static const char *const socinfo_image_names[] = { + [SMEM_IMAGE_TABLE_ADSP_INDEX] = "adsp", + [SMEM_IMAGE_TABLE_APPS_INDEX] = "apps", + [SMEM_IMAGE_TABLE_BOOT_INDEX] = "boot", + [SMEM_IMAGE_TABLE_CNSS_INDEX] = "cnss", + [SMEM_IMAGE_TABLE_MPSS_INDEX] = "mpss", + [SMEM_IMAGE_TABLE_RPM_INDEX] = "rpm", + [SMEM_IMAGE_TABLE_TZ_INDEX] = "tz", + [SMEM_IMAGE_TABLE_VIDEO_INDEX] = "video", +}; + +static const char *const pmic_models[] = { + [0] = "Unknown PMIC model", + [9] = "PM8994", + [11] = "PM8916", + [13] = "PM8058", + [14] = "PM8028", + [15] = "PM8901", + [16] = "PM8027", + [17] = "ISL9519", + [18] = "PM8921", + [19] = "PM8018", + [20] = "PM8015", + [21] = "PM8014", + [22] = "PM8821", + [23] = "PM8038", + [24] = "PM8922", + [25] = "PM8917", +}; +#endif /* CONFIG_DEBUG_FS */ + +/* Socinfo SMEM item structure */ +struct socinfo { + __le32 fmt; + __le32 id; + __le32 ver; + char build_id[SMEM_SOCINFO_BUILD_ID_LENGTH]; + /* Version 2 */ + __le32 raw_id; + __le32 raw_ver; + /* Version 3 */ + __le32 hw_plat; + /* Version 4 */ + __le32 plat_ver; + /* Version 5 */ + __le32 accessory_chip; + /* Version 6 */ + __le32 hw_plat_subtype; + /* Version 7 */ + __le32 pmic_model; + __le32 pmic_die_rev; + /* Version 8 */ + __le32 pmic_model_1; + __le32 pmic_die_rev_1; + __le32 pmic_model_2; + __le32 pmic_die_rev_2; + /* Version 9 */ + __le32 foundry_id; + /* Version 10 */ + __le32 serial_num; + /* Version 11 */ + __le32 num_pmics; + __le32 pmic_array_offset; + /* Version 12 */ + __le32 chip_family; + __le32 raw_device_family; + __le32 raw_device_num; +}; + +#ifdef CONFIG_DEBUG_FS +struct socinfo_params { + u32 raw_device_family; + u32 hw_plat_subtype; + u32 accessory_chip; + u32 raw_device_num; + u32 chip_family; + u32 foundry_id; + u32 plat_ver; + u32 raw_ver; + u32 hw_plat; + u32 fmt; +}; + +struct smem_image_version { + char name[SMEM_IMAGE_VERSION_NAME_SIZE]; + char variant[SMEM_IMAGE_VERSION_VARIANT_SIZE]; + char pad; + char oem[SMEM_IMAGE_VERSION_OEM_SIZE]; +}; +#endif /* CONFIG_DEBUG_FS */ + +struct qcom_socinfo { + struct soc_device *soc_dev; + struct soc_device_attribute attr; +#ifdef CONFIG_DEBUG_FS + struct dentry *dbg_root; + struct socinfo_params info; +#endif /* CONFIG_DEBUG_FS */ +}; + +struct soc_id { + unsigned int id; + const char *name; +}; + +static const struct soc_id soc_id[] = { + { 87, "MSM8960" }, + { 109, "APQ8064" }, + { 122, "MSM8660A" }, + { 123, "MSM8260A" }, + { 124, "APQ8060A" }, + { 126, "MSM8974" }, + { 130, "MPQ8064" }, + { 138, "MSM8960AB" }, + { 139, "APQ8060AB" }, + { 140, "MSM8260AB" }, + { 141, "MSM8660AB" }, + { 178, "APQ8084" }, + { 184, "APQ8074" }, + { 185, "MSM8274" }, + { 186, "MSM8674" }, + { 194, "MSM8974PRO" }, + { 206, "MSM8916" }, + { 208, "APQ8074-AA" }, + { 209, "APQ8074-AB" }, + { 210, "APQ8074PRO" }, + { 211, "MSM8274-AA" }, + { 212, "MSM8274-AB" }, + { 213, "MSM8274PRO" }, + { 214, "MSM8674-AA" }, + { 215, "MSM8674-AB" }, + { 216, "MSM8674PRO" }, + { 217, "MSM8974-AA" }, + { 218, "MSM8974-AB" }, + { 246, "MSM8996" }, + { 247, "APQ8016" }, + { 248, "MSM8216" }, + { 249, "MSM8116" }, + { 250, "MSM8616" }, + { 291, "APQ8096" }, + { 305, "MSM8996SG" }, + { 310, "MSM8996AU" }, + { 311, "APQ8096AU" }, + { 312, "APQ8096SG" }, +}; + +static const char *socinfo_machine(struct device *dev, unsigned int id) +{ + int idx; + + for (idx = 0; idx < ARRAY_SIZE(soc_id); idx++) { + if (soc_id[idx].id == id) + return soc_id[idx].name; + } + + return NULL; +} + +#ifdef CONFIG_DEBUG_FS + +#define QCOM_OPEN(name, _func) \ +static int qcom_open_##name(struct inode *inode, struct file *file) \ +{ \ + return single_open(file, _func, inode->i_private); \ +} \ + \ +static const struct file_operations qcom_ ##name## _ops = { \ + .open = qcom_open_##name, \ + .read = seq_read, \ + .llseek = seq_lseek, \ + .release = single_release, \ +} + +#define DEBUGFS_ADD(info, name) \ + debugfs_create_file(__stringify(name), 0400, \ + qcom_socinfo->dbg_root, \ + info, &qcom_ ##name## _ops) + + +static int qcom_show_build_id(struct seq_file *seq, void *p) +{ + struct socinfo *socinfo = seq->private; + + seq_printf(seq, "%s\n", socinfo->build_id); + + return 0; +} + +static int qcom_show_pmic_model(struct seq_file *seq, void *p) +{ + struct socinfo *socinfo = seq->private; + int model = SOCINFO_MINOR(le32_to_cpu(socinfo->pmic_model)); + + if (model < 0) + return -EINVAL; + + seq_printf(seq, "%s\n", pmic_models[model]); + + return 0; +} + +static int qcom_show_pmic_die_revision(struct seq_file *seq, void *p) +{ + struct socinfo *socinfo = seq->private; + + seq_printf(seq, "%u.%u\n", + SOCINFO_MAJOR(le32_to_cpu(socinfo->pmic_die_rev)), + SOCINFO_MINOR(le32_to_cpu(socinfo->pmic_die_rev))); + + return 0; +} + +QCOM_OPEN(build_id, qcom_show_build_id); +QCOM_OPEN(pmic_model, qcom_show_pmic_model); +QCOM_OPEN(pmic_die_rev, qcom_show_pmic_die_revision); + +#define DEFINE_IMAGE_OPS(type) \ +static int show_image_##type(struct seq_file *seq, void *p) \ +{ \ + struct smem_image_version *image_version = seq->private; \ + seq_puts(seq, image_version->type); \ + seq_puts(seq, "\n"); \ + return 0; \ +} \ +static int open_image_##type(struct inode *inode, struct file *file) \ +{ \ + return single_open(file, show_image_##type, inode->i_private); \ +} \ + \ +static const struct file_operations qcom_image_##type##_ops = { \ + .open = open_image_##type, \ + .read = seq_read, \ + .llseek = seq_lseek, \ + .release = single_release, \ +} + +DEFINE_IMAGE_OPS(name); +DEFINE_IMAGE_OPS(variant); +DEFINE_IMAGE_OPS(oem); + +static void socinfo_debugfs_init(struct qcom_socinfo *qcom_socinfo, + struct socinfo *info) +{ + struct smem_image_version *versions; + struct dentry *dentry; + size_t size; + int i; + + qcom_socinfo->dbg_root = debugfs_create_dir("qcom_socinfo", NULL); + + qcom_socinfo->info.fmt = __le32_to_cpu(info->fmt); + + switch (qcom_socinfo->info.fmt) { + case SOCINFO_VERSION(0, 12): + qcom_socinfo->info.chip_family = + __le32_to_cpu(info->chip_family); + qcom_socinfo->info.raw_device_family = + __le32_to_cpu(info->raw_device_family); + qcom_socinfo->info.raw_device_num = + __le32_to_cpu(info->raw_device_num); + + debugfs_create_x32("chip_family", 0400, qcom_socinfo->dbg_root, + &qcom_socinfo->info.chip_family); + debugfs_create_x32("raw_device_family", 0400, + qcom_socinfo->dbg_root, + &qcom_socinfo->info.raw_device_family); + debugfs_create_x32("raw_device_number", 0400, + qcom_socinfo->dbg_root, + &qcom_socinfo->info.raw_device_num); + /* Fall through */ + case SOCINFO_VERSION(0, 11): + case SOCINFO_VERSION(0, 10): + case SOCINFO_VERSION(0, 9): + qcom_socinfo->info.foundry_id = __le32_to_cpu(info->foundry_id); + + debugfs_create_u32("foundry_id", 0400, qcom_socinfo->dbg_root, + &qcom_socinfo->info.foundry_id); + /* Fall through */ + case SOCINFO_VERSION(0, 8): + case SOCINFO_VERSION(0, 7): + DEBUGFS_ADD(info, pmic_model); + DEBUGFS_ADD(info, pmic_die_rev); + /* Fall through */ + case SOCINFO_VERSION(0, 6): + qcom_socinfo->info.hw_plat_subtype = + __le32_to_cpu(info->hw_plat_subtype); + + debugfs_create_u32("hardware_platform_subtype", 0400, + qcom_socinfo->dbg_root, + &qcom_socinfo->info.hw_plat_subtype); + /* Fall through */ + case SOCINFO_VERSION(0, 5): + qcom_socinfo->info.accessory_chip = + __le32_to_cpu(info->accessory_chip); + + debugfs_create_u32("accessory_chip", 0400, + qcom_socinfo->dbg_root, + &qcom_socinfo->info.accessory_chip); + /* Fall through */ + case SOCINFO_VERSION(0, 4): + qcom_socinfo->info.plat_ver = __le32_to_cpu(info->plat_ver); + + debugfs_create_u32("platform_version", 0400, + qcom_socinfo->dbg_root, + &qcom_socinfo->info.plat_ver); + /* Fall through */ + case SOCINFO_VERSION(0, 3): + qcom_socinfo->info.hw_plat = __le32_to_cpu(info->hw_plat); + + debugfs_create_u32("hardware_platform", 0400, + qcom_socinfo->dbg_root, + &qcom_socinfo->info.hw_plat); + /* Fall through */ + case SOCINFO_VERSION(0, 2): + qcom_socinfo->info.raw_ver = __le32_to_cpu(info->raw_ver); + + debugfs_create_u32("raw_version", 0400, qcom_socinfo->dbg_root, + &qcom_socinfo->info.raw_ver); + /* Fall through */ + case SOCINFO_VERSION(0, 1): + DEBUGFS_ADD(info, build_id); + break; + } + + versions = qcom_smem_get(QCOM_SMEM_HOST_ANY, SMEM_IMAGE_VERSION_TABLE, + &size); + + for (i = 0; i < ARRAY_SIZE(socinfo_image_names); i++) { + if (!socinfo_image_names[i]) + continue; + + dentry = debugfs_create_dir(socinfo_image_names[i], + qcom_socinfo->dbg_root); + debugfs_create_file("name", 0400, dentry, &versions[i], + &qcom_image_name_ops); + debugfs_create_file("variant", 0400, dentry, &versions[i], + &qcom_image_variant_ops); + debugfs_create_file("oem", 0400, dentry, &versions[i], + &qcom_image_oem_ops); + } +} + +static void socinfo_debugfs_exit(struct qcom_socinfo *qcom_socinfo) +{ + debugfs_remove_recursive(qcom_socinfo->dbg_root); +} +#else +static void socinfo_debugfs_init(struct qcom_socinfo *qcom_socinfo, + struct socinfo *info) +{ +} +static void socinfo_debugfs_exit(struct qcom_socinfo *qcom_socinfo) { } +#endif /* CONFIG_DEBUG_FS */ + +static int qcom_socinfo_probe(struct platform_device *pdev) +{ + struct qcom_socinfo *qs; + struct socinfo *info; + size_t item_size; + + info = qcom_smem_get(QCOM_SMEM_HOST_ANY, SMEM_HW_SW_BUILD_ID, + &item_size); + if (IS_ERR(info)) { + dev_err(&pdev->dev, "Couldn't find socinfo\n"); + return PTR_ERR(info); + } + + qs = devm_kzalloc(&pdev->dev, sizeof(*qs), GFP_KERNEL); + if (!qs) + return -ENOMEM; + + qs->attr.family = "Snapdragon"; + qs->attr.machine = socinfo_machine(&pdev->dev, + le32_to_cpu(info->id)); + qs->attr.revision = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%u.%u", + SOCINFO_MAJOR(le32_to_cpu(info->ver)), + SOCINFO_MINOR(le32_to_cpu(info->ver))); + if (offsetof(struct socinfo, serial_num) <= item_size) + qs->attr.serial_number = devm_kasprintf(&pdev->dev, GFP_KERNEL, + "%u", + le32_to_cpu(info->serial_num)); + + qs->soc_dev = soc_device_register(&qs->attr); + if (IS_ERR(qs->soc_dev)) + return PTR_ERR(qs->soc_dev); + + socinfo_debugfs_init(qs, info); + + /* Feed the soc specific unique data into entropy pool */ + add_device_randomness(info, item_size); + + platform_set_drvdata(pdev, qs->soc_dev); + + return 0; +} + +static int qcom_socinfo_remove(struct platform_device *pdev) +{ + struct qcom_socinfo *qs = platform_get_drvdata(pdev); + + soc_device_unregister(qs->soc_dev); + + socinfo_debugfs_exit(qs); + + return 0; +} + +static struct platform_driver qcom_socinfo_driver = { + .probe = qcom_socinfo_probe, + .remove = qcom_socinfo_remove, + .driver = { + .name = "qcom-socinfo", + }, +}; + +module_platform_driver(qcom_socinfo_driver); + +MODULE_DESCRIPTION("Qualcomm SoCinfo driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:qcom-socinfo"); diff --git a/drivers/soc/renesas/Kconfig b/drivers/soc/renesas/Kconfig index 2bbf49e5d441..3c5e017bacba 100644 --- a/drivers/soc/renesas/Kconfig +++ b/drivers/soc/renesas/Kconfig @@ -55,6 +55,7 @@ config ARCH_EMEV2 config ARCH_R7S72100 bool "RZ/A1H (R7S72100)" + select ARM_ERRATA_754322 select PM select PM_GENERIC_DOMAINS select RENESAS_OSTM @@ -72,12 +73,14 @@ config ARCH_R8A73A4 bool "R-Mobile APE6 (R8A73A40)" select ARCH_RMOBILE select ARM_ERRATA_798181 if SMP + select ARM_ERRATA_814220 select HAVE_ARM_ARCH_TIMER select RENESAS_IRQC config ARCH_R8A7740 bool "R-Mobile A1 (R8A77400)" select ARCH_RMOBILE + select ARM_ERRATA_754322 select RENESAS_INTC_IRQPIN config ARCH_R8A7743 @@ -95,20 +98,24 @@ config ARCH_R8A7744 config ARCH_R8A7745 bool "RZ/G1E (R8A77450)" select ARCH_RCAR_GEN2 + select ARM_ERRATA_814220 select SYSC_R8A7745 config ARCH_R8A77470 bool "RZ/G1C (R8A77470)" select ARCH_RCAR_GEN2 + select ARM_ERRATA_814220 select SYSC_R8A77470 config ARCH_R8A7778 bool "R-Car M1A (R8A77781)" select ARCH_RCAR_GEN1 + select ARM_ERRATA_754322 config ARCH_R8A7779 bool "R-Car H1 (R8A77790)" select ARCH_RCAR_GEN1 + select ARM_ERRATA_754322 select HAVE_ARM_SCU if SMP select HAVE_ARM_TWD if SMP select SYSC_R8A7779 @@ -117,6 +124,7 @@ config ARCH_R8A7790 bool "R-Car H2 (R8A77900)" select ARCH_RCAR_GEN2 select ARM_ERRATA_798181 if SMP + select ARM_ERRATA_814220 select I2C select SYSC_R8A7790 @@ -143,15 +151,18 @@ config ARCH_R8A7793 config ARCH_R8A7794 bool "R-Car E2 (R8A77940)" select ARCH_RCAR_GEN2 + select ARM_ERRATA_814220 select SYSC_R8A7794 config ARCH_R9A06G032 bool "RZ/N1D (R9A06G032)" select ARCH_RZN1 + select ARM_ERRATA_814220 config ARCH_SH73A0 bool "SH-Mobile AG5 (R8A73A00)" select ARCH_RMOBILE + select ARM_ERRATA_754322 select HAVE_ARM_SCU if SMP select HAVE_ARM_TWD if SMP select RENESAS_INTC_IRQPIN diff --git a/drivers/soc/renesas/rcar-sysc.c b/drivers/soc/renesas/rcar-sysc.c index 0c80fab4f8de..59b5e6b10272 100644 --- a/drivers/soc/renesas/rcar-sysc.c +++ b/drivers/soc/renesas/rcar-sysc.c @@ -170,7 +170,7 @@ struct rcar_sysc_pd { struct generic_pm_domain genpd; struct rcar_sysc_ch ch; unsigned int flags; - char name[0]; + char name[]; }; static inline struct rcar_sysc_pd *to_rcar_pd(struct generic_pm_domain *d) @@ -200,7 +200,6 @@ static int __init rcar_sysc_pd_setup(struct rcar_sysc_pd *pd) { struct generic_pm_domain *genpd = &pd->genpd; const char *name = pd->genpd.name; - struct dev_power_governor *gov = &simple_qos_governor; int error; if (pd->flags & PD_CPU) { @@ -254,7 +253,7 @@ static int __init rcar_sysc_pd_setup(struct rcar_sysc_pd *pd) rcar_sysc_power(&pd->ch, true); finalize: - error = pm_genpd_init(genpd, gov, false); + error = pm_genpd_init(genpd, &simple_qos_governor, false); if (error) pr_err("Failed to init PM domain %s: %d\n", name, error); @@ -346,7 +345,7 @@ static int __init rcar_sysc_pd_init(void) if (info->init) { error = info->init(); if (error) - return error; + goto out_put; } has_cpg_mstp = of_find_compatible_node(NULL, NULL, diff --git a/drivers/soc/renesas/rmobile-sysc.c b/drivers/soc/renesas/rmobile-sysc.c index 421ae1c887d8..54b616ad4a62 100644 --- a/drivers/soc/renesas/rmobile-sysc.c +++ b/drivers/soc/renesas/rmobile-sysc.c @@ -48,12 +48,8 @@ struct rmobile_pm_domain *to_rmobile_pd(struct generic_pm_domain *d) static int rmobile_pd_power_down(struct generic_pm_domain *genpd) { struct rmobile_pm_domain *rmobile_pd = to_rmobile_pd(genpd); - unsigned int mask; + unsigned int mask = BIT(rmobile_pd->bit_shift); - if (rmobile_pd->bit_shift == ~0) - return -EBUSY; - - mask = BIT(rmobile_pd->bit_shift); if (rmobile_pd->suspend) { int ret = rmobile_pd->suspend(); @@ -80,14 +76,10 @@ static int rmobile_pd_power_down(struct generic_pm_domain *genpd) static int __rmobile_pd_power_up(struct rmobile_pm_domain *rmobile_pd) { - unsigned int mask; + unsigned int mask = BIT(rmobile_pd->bit_shift); unsigned int retry_count; int ret = 0; - if (rmobile_pd->bit_shift == ~0) - return 0; - - mask = BIT(rmobile_pd->bit_shift); if (__raw_readl(rmobile_pd->base + PSTR) & mask) return ret; @@ -122,11 +114,15 @@ static void rmobile_init_pm_domain(struct rmobile_pm_domain *rmobile_pd) struct dev_power_governor *gov = rmobile_pd->gov; genpd->flags |= GENPD_FLAG_PM_CLK | GENPD_FLAG_ACTIVE_WAKEUP; - genpd->power_off = rmobile_pd_power_down; - genpd->power_on = rmobile_pd_power_up; - genpd->attach_dev = cpg_mstp_attach_dev; - genpd->detach_dev = cpg_mstp_detach_dev; - __rmobile_pd_power_up(rmobile_pd); + genpd->attach_dev = cpg_mstp_attach_dev; + genpd->detach_dev = cpg_mstp_detach_dev; + + if (!(genpd->flags & GENPD_FLAG_ALWAYS_ON)) { + genpd->power_off = rmobile_pd_power_down; + genpd->power_on = rmobile_pd_power_up; + __rmobile_pd_power_up(rmobile_pd); + } + pm_genpd_init(genpd, gov ? : &simple_qos_governor, false); } @@ -270,6 +266,11 @@ static void __init rmobile_setup_pm_domain(struct device_node *np, break; case PD_NORMAL: + if (pd->bit_shift == ~0) { + /* Top-level always-on domain */ + pr_debug("PM domain %s is always-on domain\n", name); + pd->genpd.flags |= GENPD_FLAG_ALWAYS_ON; + } break; } diff --git a/drivers/soc/samsung/Kconfig b/drivers/soc/samsung/Kconfig index 2186285fda92..33ad0de2de3c 100644 --- a/drivers/soc/samsung/Kconfig +++ b/drivers/soc/samsung/Kconfig @@ -7,6 +7,12 @@ menuconfig SOC_SAMSUNG if SOC_SAMSUNG +config EXYNOS_CHIPID + bool "Exynos Chipid controller driver" if COMPILE_TEST + depends on ARCH_EXYNOS || COMPILE_TEST + select MFD_SYSCON + select SOC_BUS + config EXYNOS_PMU bool "Exynos PMU controller driver" if COMPILE_TEST depends on ARCH_EXYNOS || ((ARM || ARM64) && COMPILE_TEST) diff --git a/drivers/soc/samsung/Makefile b/drivers/soc/samsung/Makefile index 29f294baac6e..3b6a8797416c 100644 --- a/drivers/soc/samsung/Makefile +++ b/drivers/soc/samsung/Makefile @@ -1,4 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 + +obj-$(CONFIG_EXYNOS_CHIPID) += exynos-chipid.o obj-$(CONFIG_EXYNOS_PMU) += exynos-pmu.o obj-$(CONFIG_EXYNOS_PMU_ARM_DRIVERS) += exynos3250-pmu.o exynos4-pmu.o \ diff --git a/drivers/soc/samsung/exynos-chipid.c b/drivers/soc/samsung/exynos-chipid.c new file mode 100644 index 000000000000..c55a47cfe617 --- /dev/null +++ b/drivers/soc/samsung/exynos-chipid.c @@ -0,0 +1,105 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * EXYNOS - CHIP ID support + * Author: Pankaj Dubey <pankaj.dubey@samsung.com> + * Author: Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com> + */ + +#include <linux/io.h> +#include <linux/mfd/syscon.h> +#include <linux/of.h> +#include <linux/regmap.h> +#include <linux/slab.h> +#include <linux/soc/samsung/exynos-chipid.h> +#include <linux/sys_soc.h> + +static const struct exynos_soc_id { + const char *name; + unsigned int id; +} soc_ids[] = { + { "EXYNOS3250", 0xE3472000 }, + { "EXYNOS4210", 0x43200000 }, /* EVT0 revision */ + { "EXYNOS4210", 0x43210000 }, + { "EXYNOS4212", 0x43220000 }, + { "EXYNOS4412", 0xE4412000 }, + { "EXYNOS5250", 0x43520000 }, + { "EXYNOS5260", 0xE5260000 }, + { "EXYNOS5410", 0xE5410000 }, + { "EXYNOS5420", 0xE5420000 }, + { "EXYNOS5440", 0xE5440000 }, + { "EXYNOS5800", 0xE5422000 }, + { "EXYNOS7420", 0xE7420000 }, + { "EXYNOS5433", 0xE5433000 }, +}; + +static const char * __init product_id_to_soc_id(unsigned int product_id) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(soc_ids); i++) + if ((product_id & EXYNOS_MASK) == soc_ids[i].id) + return soc_ids[i].name; + return NULL; +} + +int __init exynos_chipid_early_init(void) +{ + struct soc_device_attribute *soc_dev_attr; + struct soc_device *soc_dev; + struct device_node *root; + struct regmap *regmap; + u32 product_id; + u32 revision; + int ret; + + regmap = syscon_regmap_lookup_by_compatible("samsung,exynos4210-chipid"); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + ret = regmap_read(regmap, EXYNOS_CHIPID_REG_PRO_ID, &product_id); + if (ret < 0) + return ret; + + revision = product_id & EXYNOS_REV_MASK; + + soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL); + if (!soc_dev_attr) + return -ENOMEM; + + soc_dev_attr->family = "Samsung Exynos"; + + root = of_find_node_by_path("/"); + of_property_read_string(root, "model", &soc_dev_attr->machine); + of_node_put(root); + + soc_dev_attr->revision = kasprintf(GFP_KERNEL, "%x", revision); + soc_dev_attr->soc_id = product_id_to_soc_id(product_id); + if (!soc_dev_attr->soc_id) { + pr_err("Unknown SoC\n"); + ret = -ENODEV; + goto err; + } + + /* please note that the actual registration will be deferred */ + soc_dev = soc_device_register(soc_dev_attr); + if (IS_ERR(soc_dev)) { + ret = PTR_ERR(soc_dev); + goto err; + } + + /* it is too early to use dev_info() here (soc_dev is NULL) */ + pr_info("soc soc0: Exynos: CPU[%s] PRO_ID[0x%x] REV[0x%x] Detected\n", + soc_dev_attr->soc_id, product_id, revision); + + return 0; + +err: + kfree(soc_dev_attr->revision); + kfree(soc_dev_attr); + return ret; +} + +early_initcall(exynos_chipid_early_init); diff --git a/drivers/soc/ti/ti_sci_pm_domains.c b/drivers/soc/ti/ti_sci_pm_domains.c index 97817dd7ba24..8c2a2f23982c 100644 --- a/drivers/soc/ti/ti_sci_pm_domains.c +++ b/drivers/soc/ti/ti_sci_pm_domains.c @@ -15,15 +15,19 @@ #include <linux/pm_domain.h> #include <linux/slab.h> #include <linux/soc/ti/ti_sci_protocol.h> +#include <dt-bindings/soc/ti,sci_pm_domain.h> /** * struct ti_sci_genpd_dev_data: holds data needed for every device attached * to this genpd * @idx: index of the device that identifies it with the system * control processor. + * @exclusive: Permissions for exclusive request or shared request of the + * device. */ struct ti_sci_genpd_dev_data { int idx; + u8 exclusive; }; /** @@ -55,6 +59,14 @@ static int ti_sci_dev_id(struct device *dev) return sci_dev_data->idx; } +static u8 is_ti_sci_dev_exclusive(struct device *dev) +{ + struct generic_pm_domain_data *genpd_data = dev_gpd_data(dev); + struct ti_sci_genpd_dev_data *sci_dev_data = genpd_data->data; + + return sci_dev_data->exclusive; +} + /** * ti_sci_dev_to_sci_handle(): get pointer to ti_sci_handle * @dev: pointer to device associated with this genpd @@ -79,7 +91,10 @@ static int ti_sci_dev_start(struct device *dev) const struct ti_sci_handle *ti_sci = ti_sci_dev_to_sci_handle(dev); int idx = ti_sci_dev_id(dev); - return ti_sci->ops.dev_ops.get_device(ti_sci, idx); + if (is_ti_sci_dev_exclusive(dev)) + return ti_sci->ops.dev_ops.get_device_exclusive(ti_sci, idx); + else + return ti_sci->ops.dev_ops.get_device(ti_sci, idx); } /** @@ -110,7 +125,7 @@ static int ti_sci_pd_attach_dev(struct generic_pm_domain *domain, if (ret < 0) return ret; - if (pd_args.args_count != 1) + if (pd_args.args_count != 1 && pd_args.args_count != 2) return -EINVAL; idx = pd_args.args[0]; @@ -128,6 +143,10 @@ static int ti_sci_pd_attach_dev(struct generic_pm_domain *domain, return -ENOMEM; sci_dev_data->idx = idx; + /* Enable the exclusive permissions by default */ + sci_dev_data->exclusive = TI_SCI_PD_EXCLUSIVE; + if (pd_args.args_count == 2) + sci_dev_data->exclusive = pd_args.args[1] & 0x1; genpd_data = dev_gpd_data(dev); genpd_data->data = sci_dev_data; diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 6ee514fd0920..0f0fdb57198f 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -450,13 +450,6 @@ config SPI_NPCM_PSPI This driver provides support for Nuvoton NPCM BMC Peripheral SPI controller in master mode. -config SPI_NUC900 - tristate "Nuvoton NUC900 series SPI" - depends on ARCH_W90X900 - select SPI_BITBANG - help - SPI driver for Nuvoton NUC900 series ARM SoCs - config SPI_LANTIQ_SSC tristate "Lantiq SSC SPI controller" depends on LANTIQ || COMPILE_TEST diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index adbebee93a75..bb49c9e6d0a0 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -65,7 +65,6 @@ obj-$(CONFIG_SPI_MXIC) += spi-mxic.o obj-$(CONFIG_SPI_MXS) += spi-mxs.o obj-$(CONFIG_SPI_NPCM_FIU) += spi-npcm-fiu.o obj-$(CONFIG_SPI_NPCM_PSPI) += spi-npcm-pspi.o -obj-$(CONFIG_SPI_NUC900) += spi-nuc900.o obj-$(CONFIG_SPI_NXP_FLEXSPI) += spi-nxp-fspi.o obj-$(CONFIG_SPI_OC_TINY) += spi-oc-tiny.o spi-octeon-objs := spi-cavium.o spi-cavium-octeon.o diff --git a/drivers/spi/spi-nuc900.c b/drivers/spi/spi-nuc900.c deleted file mode 100644 index 61400358f4be..000000000000 --- a/drivers/spi/spi-nuc900.c +++ /dev/null @@ -1,426 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (c) 2009 Nuvoton technology. - * Wan ZongShun <mcuos.com@gmail.com> - */ - -#include <linux/module.h> -#include <linux/spinlock.h> -#include <linux/interrupt.h> -#include <linux/delay.h> -#include <linux/errno.h> -#include <linux/err.h> -#include <linux/clk.h> -#include <linux/device.h> -#include <linux/platform_device.h> -#include <linux/gpio.h> -#include <linux/io.h> -#include <linux/slab.h> - -#include <linux/spi/spi.h> -#include <linux/spi/spi_bitbang.h> - -#include <linux/platform_data/spi-nuc900.h> - -/* usi registers offset */ -#define USI_CNT 0x00 -#define USI_DIV 0x04 -#define USI_SSR 0x08 -#define USI_RX0 0x10 -#define USI_TX0 0x10 - -/* usi register bit */ -#define ENINT (0x01 << 17) -#define ENFLG (0x01 << 16) -#define SLEEP (0x0f << 12) -#define TXNUM (0x03 << 8) -#define TXBITLEN (0x1f << 3) -#define TXNEG (0x01 << 2) -#define RXNEG (0x01 << 1) -#define LSB (0x01 << 10) -#define SELECTLEV (0x01 << 2) -#define SELECTPOL (0x01 << 31) -#define SELECTSLAVE 0x01 -#define GOBUSY 0x01 - -struct nuc900_spi { - struct spi_bitbang bitbang; - struct completion done; - void __iomem *regs; - int irq; - int len; - int count; - const unsigned char *tx; - unsigned char *rx; - struct clk *clk; - struct spi_master *master; - struct nuc900_spi_info *pdata; - spinlock_t lock; -}; - -static inline struct nuc900_spi *to_hw(struct spi_device *sdev) -{ - return spi_master_get_devdata(sdev->master); -} - -static void nuc900_slave_select(struct spi_device *spi, unsigned int ssr) -{ - struct nuc900_spi *hw = to_hw(spi); - unsigned int val; - unsigned int cs = spi->mode & SPI_CS_HIGH ? 1 : 0; - unsigned int cpol = spi->mode & SPI_CPOL ? 1 : 0; - unsigned long flags; - - spin_lock_irqsave(&hw->lock, flags); - - val = __raw_readl(hw->regs + USI_SSR); - - if (!cs) - val &= ~SELECTLEV; - else - val |= SELECTLEV; - - if (!ssr) - val &= ~SELECTSLAVE; - else - val |= SELECTSLAVE; - - __raw_writel(val, hw->regs + USI_SSR); - - val = __raw_readl(hw->regs + USI_CNT); - - if (!cpol) - val &= ~SELECTPOL; - else - val |= SELECTPOL; - - __raw_writel(val, hw->regs + USI_CNT); - - spin_unlock_irqrestore(&hw->lock, flags); -} - -static void nuc900_spi_chipsel(struct spi_device *spi, int value) -{ - switch (value) { - case BITBANG_CS_INACTIVE: - nuc900_slave_select(spi, 0); - break; - - case BITBANG_CS_ACTIVE: - nuc900_slave_select(spi, 1); - break; - } -} - -static void nuc900_spi_setup_txnum(struct nuc900_spi *hw, unsigned int txnum) -{ - unsigned int val; - unsigned long flags; - - spin_lock_irqsave(&hw->lock, flags); - - val = __raw_readl(hw->regs + USI_CNT) & ~TXNUM; - - if (txnum) - val |= txnum << 0x08; - - __raw_writel(val, hw->regs + USI_CNT); - - spin_unlock_irqrestore(&hw->lock, flags); - -} - -static void nuc900_spi_setup_txbitlen(struct nuc900_spi *hw, - unsigned int txbitlen) -{ - unsigned int val; - unsigned long flags; - - spin_lock_irqsave(&hw->lock, flags); - - val = __raw_readl(hw->regs + USI_CNT) & ~TXBITLEN; - - val |= (txbitlen << 0x03); - - __raw_writel(val, hw->regs + USI_CNT); - - spin_unlock_irqrestore(&hw->lock, flags); -} - -static void nuc900_spi_gobusy(struct nuc900_spi *hw) -{ - unsigned int val; - unsigned long flags; - - spin_lock_irqsave(&hw->lock, flags); - - val = __raw_readl(hw->regs + USI_CNT); - - val |= GOBUSY; - - __raw_writel(val, hw->regs + USI_CNT); - - spin_unlock_irqrestore(&hw->lock, flags); -} - -static inline unsigned int hw_txbyte(struct nuc900_spi *hw, int count) -{ - return hw->tx ? hw->tx[count] : 0; -} - -static int nuc900_spi_txrx(struct spi_device *spi, struct spi_transfer *t) -{ - struct nuc900_spi *hw = to_hw(spi); - - hw->tx = t->tx_buf; - hw->rx = t->rx_buf; - hw->len = t->len; - hw->count = 0; - - __raw_writel(hw_txbyte(hw, 0x0), hw->regs + USI_TX0); - - nuc900_spi_gobusy(hw); - - wait_for_completion(&hw->done); - - return hw->count; -} - -static irqreturn_t nuc900_spi_irq(int irq, void *dev) -{ - struct nuc900_spi *hw = dev; - unsigned int status; - unsigned int count = hw->count; - - status = __raw_readl(hw->regs + USI_CNT); - __raw_writel(status, hw->regs + USI_CNT); - - if (status & ENFLG) { - hw->count++; - - if (hw->rx) - hw->rx[count] = __raw_readl(hw->regs + USI_RX0); - count++; - - if (count < hw->len) { - __raw_writel(hw_txbyte(hw, count), hw->regs + USI_TX0); - nuc900_spi_gobusy(hw); - } else { - complete(&hw->done); - } - - return IRQ_HANDLED; - } - - complete(&hw->done); - return IRQ_HANDLED; -} - -static void nuc900_tx_edge(struct nuc900_spi *hw, unsigned int edge) -{ - unsigned int val; - unsigned long flags; - - spin_lock_irqsave(&hw->lock, flags); - - val = __raw_readl(hw->regs + USI_CNT); - - if (edge) - val |= TXNEG; - else - val &= ~TXNEG; - __raw_writel(val, hw->regs + USI_CNT); - - spin_unlock_irqrestore(&hw->lock, flags); -} - -static void nuc900_rx_edge(struct nuc900_spi *hw, unsigned int edge) -{ - unsigned int val; - unsigned long flags; - - spin_lock_irqsave(&hw->lock, flags); - - val = __raw_readl(hw->regs + USI_CNT); - - if (edge) - val |= RXNEG; - else - val &= ~RXNEG; - __raw_writel(val, hw->regs + USI_CNT); - - spin_unlock_irqrestore(&hw->lock, flags); -} - -static void nuc900_send_first(struct nuc900_spi *hw, unsigned int lsb) -{ - unsigned int val; - unsigned long flags; - - spin_lock_irqsave(&hw->lock, flags); - - val = __raw_readl(hw->regs + USI_CNT); - - if (lsb) - val |= LSB; - else - val &= ~LSB; - __raw_writel(val, hw->regs + USI_CNT); - - spin_unlock_irqrestore(&hw->lock, flags); -} - -static void nuc900_set_sleep(struct nuc900_spi *hw, unsigned int sleep) -{ - unsigned int val; - unsigned long flags; - - spin_lock_irqsave(&hw->lock, flags); - - val = __raw_readl(hw->regs + USI_CNT) & ~SLEEP; - - if (sleep) - val |= (sleep << 12); - - __raw_writel(val, hw->regs + USI_CNT); - - spin_unlock_irqrestore(&hw->lock, flags); -} - -static void nuc900_enable_int(struct nuc900_spi *hw) -{ - unsigned int val; - unsigned long flags; - - spin_lock_irqsave(&hw->lock, flags); - - val = __raw_readl(hw->regs + USI_CNT); - - val |= ENINT; - - __raw_writel(val, hw->regs + USI_CNT); - - spin_unlock_irqrestore(&hw->lock, flags); -} - -static void nuc900_set_divider(struct nuc900_spi *hw) -{ - __raw_writel(hw->pdata->divider, hw->regs + USI_DIV); -} - -static void nuc900_init_spi(struct nuc900_spi *hw) -{ - clk_enable(hw->clk); - spin_lock_init(&hw->lock); - - nuc900_tx_edge(hw, hw->pdata->txneg); - nuc900_rx_edge(hw, hw->pdata->rxneg); - nuc900_send_first(hw, hw->pdata->lsb); - nuc900_set_sleep(hw, hw->pdata->sleep); - nuc900_spi_setup_txbitlen(hw, hw->pdata->txbitlen); - nuc900_spi_setup_txnum(hw, hw->pdata->txnum); - nuc900_set_divider(hw); - nuc900_enable_int(hw); -} - -static int nuc900_spi_probe(struct platform_device *pdev) -{ - struct nuc900_spi *hw; - struct spi_master *master; - int err = 0; - - master = spi_alloc_master(&pdev->dev, sizeof(struct nuc900_spi)); - if (master == NULL) { - dev_err(&pdev->dev, "No memory for spi_master\n"); - return -ENOMEM; - } - - hw = spi_master_get_devdata(master); - hw->master = master; - hw->pdata = dev_get_platdata(&pdev->dev); - - if (hw->pdata == NULL) { - dev_err(&pdev->dev, "No platform data supplied\n"); - err = -ENOENT; - goto err_pdata; - } - - platform_set_drvdata(pdev, hw); - init_completion(&hw->done); - - master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; - if (hw->pdata->lsb) - master->mode_bits |= SPI_LSB_FIRST; - master->num_chipselect = hw->pdata->num_cs; - master->bus_num = hw->pdata->bus_num; - hw->bitbang.master = hw->master; - hw->bitbang.chipselect = nuc900_spi_chipsel; - hw->bitbang.txrx_bufs = nuc900_spi_txrx; - - hw->regs = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(hw->regs)) { - err = PTR_ERR(hw->regs); - goto err_pdata; - } - - hw->irq = platform_get_irq(pdev, 0); - if (hw->irq < 0) { - err = -ENOENT; - goto err_pdata; - } - - err = devm_request_irq(&pdev->dev, hw->irq, nuc900_spi_irq, 0, - pdev->name, hw); - if (err) { - dev_err(&pdev->dev, "Cannot claim IRQ\n"); - goto err_pdata; - } - - hw->clk = devm_clk_get(&pdev->dev, "spi"); - if (IS_ERR(hw->clk)) { - dev_err(&pdev->dev, "No clock for device\n"); - err = PTR_ERR(hw->clk); - goto err_pdata; - } - - mfp_set_groupg(&pdev->dev, NULL); - nuc900_init_spi(hw); - - err = spi_bitbang_start(&hw->bitbang); - if (err) { - dev_err(&pdev->dev, "Failed to register SPI master\n"); - goto err_register; - } - - return 0; - -err_register: - clk_disable(hw->clk); -err_pdata: - spi_master_put(hw->master); - return err; -} - -static int nuc900_spi_remove(struct platform_device *dev) -{ - struct nuc900_spi *hw = platform_get_drvdata(dev); - - spi_bitbang_stop(&hw->bitbang); - clk_disable(hw->clk); - spi_master_put(hw->master); - return 0; -} - -static struct platform_driver nuc900_spi_driver = { - .probe = nuc900_spi_probe, - .remove = nuc900_spi_remove, - .driver = { - .name = "nuc900-spi", - }, -}; -module_platform_driver(nuc900_spi_driver); - -MODULE_AUTHOR("Wan ZongShun <mcuos.com@gmail.com>"); -MODULE_DESCRIPTION("nuc900 spi driver!"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:nuc900-spi"); diff --git a/drivers/tee/optee/call.c b/drivers/tee/optee/call.c index aa942703ae65..13b0269a0abc 100644 --- a/drivers/tee/optee/call.c +++ b/drivers/tee/optee/call.c @@ -148,6 +148,7 @@ u32 optee_do_call_with_arg(struct tee_context *ctx, phys_addr_t parg) */ optee_cq_wait_for_completion(&optee->call_queue, &w); } else if (OPTEE_SMC_RETURN_IS_RPC(res.a0)) { + might_sleep(); param.a0 = res.a0; param.a1 = res.a1; param.a2 = res.a2; diff --git a/drivers/video/fbdev/Kconfig b/drivers/video/fbdev/Kconfig index 6b2de93bd302..5f83cd715387 100644 --- a/drivers/video/fbdev/Kconfig +++ b/drivers/video/fbdev/Kconfig @@ -1924,20 +1924,6 @@ config FB_S3C2410_DEBUG Turn on debugging messages. Note that you can set/unset at run time through sysfs -config FB_NUC900 - tristate "NUC900 LCD framebuffer support" - depends on FB && ARCH_W90X900 - select FB_CFB_FILLRECT - select FB_CFB_COPYAREA - select FB_CFB_IMAGEBLIT - ---help--- - Frame buffer driver for the built-in LCD controller in the Nuvoton - NUC900 processor - -config GPM1040A0_320X240 - bool "Giantplus Technology GPM1040A0 320x240 Color TFT LCD" - depends on FB_NUC900 - config FB_SM501 tristate "Silicon Motion SM501 framebuffer support" depends on FB && MFD_SM501 diff --git a/drivers/video/fbdev/Makefile b/drivers/video/fbdev/Makefile index 7dc4861a93e6..aab7155884ea 100644 --- a/drivers/video/fbdev/Makefile +++ b/drivers/video/fbdev/Makefile @@ -116,7 +116,6 @@ obj-y += omap2/ obj-$(CONFIG_XEN_FBDEV_FRONTEND) += xen-fbfront.o obj-$(CONFIG_FB_CARMINE) += carminefb.o obj-$(CONFIG_FB_MB862XX) += mb862xx/ -obj-$(CONFIG_FB_NUC900) += nuc900fb.o obj-$(CONFIG_FB_JZ4740) += jz4740_fb.o obj-$(CONFIG_FB_PUV3_UNIGFX) += fb-puv3.o obj-$(CONFIG_FB_HYPERV) += hyperv_fb.o diff --git a/drivers/video/fbdev/da8xx-fb.c b/drivers/video/fbdev/da8xx-fb.c index b1cf248f3291..2d3dcc52fcf3 100644 --- a/drivers/video/fbdev/da8xx-fb.c +++ b/drivers/video/fbdev/da8xx-fb.c @@ -19,6 +19,7 @@ #include <linux/clk.h> #include <linux/cpufreq.h> #include <linux/console.h> +#include <linux/regulator/consumer.h> #include <linux/spinlock.h> #include <linux/slab.h> #include <linux/delay.h> @@ -164,7 +165,7 @@ struct da8xx_fb_par { struct notifier_block freq_transition; #endif unsigned int lcdc_clk_rate; - void (*panel_power_ctrl)(int); + struct regulator *lcd_supply; u32 pseudo_palette[16]; struct fb_videomode mode; struct lcd_ctrl_config cfg; @@ -1066,33 +1067,30 @@ static void lcd_da8xx_cpufreq_deregister(struct da8xx_fb_par *par) static int fb_remove(struct platform_device *dev) { struct fb_info *info = dev_get_drvdata(&dev->dev); - - if (info) { - struct da8xx_fb_par *par = info->par; + struct da8xx_fb_par *par = info->par; + int ret; #ifdef CONFIG_CPU_FREQ - lcd_da8xx_cpufreq_deregister(par); + lcd_da8xx_cpufreq_deregister(par); #endif - if (par->panel_power_ctrl) - par->panel_power_ctrl(0); + if (par->lcd_supply) { + ret = regulator_disable(par->lcd_supply); + if (ret) + return ret; + } - lcd_disable_raster(DA8XX_FRAME_WAIT); - lcdc_write(0, LCD_RASTER_CTRL_REG); + lcd_disable_raster(DA8XX_FRAME_WAIT); + lcdc_write(0, LCD_RASTER_CTRL_REG); - /* disable DMA */ - lcdc_write(0, LCD_DMA_CTRL_REG); + /* disable DMA */ + lcdc_write(0, LCD_DMA_CTRL_REG); - unregister_framebuffer(info); - fb_dealloc_cmap(&info->cmap); - dma_free_coherent(par->dev, PALETTE_SIZE, par->v_palette_base, - par->p_palette_base); - dma_free_coherent(par->dev, par->vram_size, par->vram_virt, - par->vram_phys); - pm_runtime_put_sync(&dev->dev); - pm_runtime_disable(&dev->dev); - framebuffer_release(info); + unregister_framebuffer(info); + fb_dealloc_cmap(&info->cmap); + pm_runtime_put_sync(&dev->dev); + pm_runtime_disable(&dev->dev); + framebuffer_release(info); - } return 0; } @@ -1179,15 +1177,21 @@ static int cfb_blank(int blank, struct fb_info *info) case FB_BLANK_UNBLANK: lcd_enable_raster(); - if (par->panel_power_ctrl) - par->panel_power_ctrl(1); + if (par->lcd_supply) { + ret = regulator_enable(par->lcd_supply); + if (ret) + return ret; + } break; case FB_BLANK_NORMAL: case FB_BLANK_VSYNC_SUSPEND: case FB_BLANK_HSYNC_SUSPEND: case FB_BLANK_POWERDOWN: - if (par->panel_power_ctrl) - par->panel_power_ctrl(0); + if (par->lcd_supply) { + ret = regulator_disable(par->lcd_supply); + if (ret) + return ret; + } lcd_disable_raster(DA8XX_FRAME_WAIT); break; @@ -1328,7 +1332,6 @@ static int fb_probe(struct platform_device *device) { struct da8xx_lcdc_platform_data *fb_pdata = dev_get_platdata(&device->dev); - struct resource *lcdc_regs; struct lcd_ctrl_config *lcd_cfg; struct fb_videomode *lcdc_info; struct fb_info *da8xx_fb_info; @@ -1346,8 +1349,7 @@ static int fb_probe(struct platform_device *device) if (lcdc_info == NULL) return -ENODEV; - lcdc_regs = platform_get_resource(device, IORESOURCE_MEM, 0); - da8xx_fb_reg_base = devm_ioremap_resource(&device->dev, lcdc_regs); + da8xx_fb_reg_base = devm_platform_ioremap_resource(device, 0); if (IS_ERR(da8xx_fb_reg_base)) return PTR_ERR(da8xx_fb_reg_base); @@ -1395,9 +1397,19 @@ static int fb_probe(struct platform_device *device) par->dev = &device->dev; par->lcdc_clk = tmp_lcdc_clk; par->lcdc_clk_rate = clk_get_rate(par->lcdc_clk); - if (fb_pdata->panel_power_ctrl) { - par->panel_power_ctrl = fb_pdata->panel_power_ctrl; - par->panel_power_ctrl(1); + + par->lcd_supply = devm_regulator_get_optional(&device->dev, "lcd"); + if (IS_ERR(par->lcd_supply)) { + if (PTR_ERR(par->lcd_supply) == -EPROBE_DEFER) { + ret = -EPROBE_DEFER; + goto err_pm_runtime_disable; + } + + par->lcd_supply = NULL; + } else { + ret = regulator_enable(par->lcd_supply); + if (ret) + goto err_pm_runtime_disable; } fb_videomode_to_var(&da8xx_fb_var, lcdc_info); @@ -1411,10 +1423,10 @@ static int fb_probe(struct platform_device *device) par->vram_size = roundup(par->vram_size/8, ulcm); par->vram_size = par->vram_size * LCD_NUM_BUFFERS; - par->vram_virt = dma_alloc_coherent(par->dev, - par->vram_size, - &par->vram_phys, - GFP_KERNEL | GFP_DMA); + par->vram_virt = dmam_alloc_coherent(par->dev, + par->vram_size, + &par->vram_phys, + GFP_KERNEL | GFP_DMA); if (!par->vram_virt) { dev_err(&device->dev, "GLCD: kmalloc for frame buffer failed\n"); @@ -1432,20 +1444,20 @@ static int fb_probe(struct platform_device *device) da8xx_fb_fix.line_length - 1; /* allocate palette buffer */ - par->v_palette_base = dma_alloc_coherent(par->dev, PALETTE_SIZE, - &par->p_palette_base, - GFP_KERNEL | GFP_DMA); + par->v_palette_base = dmam_alloc_coherent(par->dev, PALETTE_SIZE, + &par->p_palette_base, + GFP_KERNEL | GFP_DMA); if (!par->v_palette_base) { dev_err(&device->dev, "GLCD: kmalloc for palette buffer failed\n"); ret = -EINVAL; - goto err_release_fb_mem; + goto err_release_fb; } par->irq = platform_get_irq(device, 0); if (par->irq < 0) { ret = -ENOENT; - goto err_release_pl_mem; + goto err_release_fb; } da8xx_fb_var.grayscale = @@ -1463,7 +1475,7 @@ static int fb_probe(struct platform_device *device) ret = fb_alloc_cmap(&da8xx_fb_info->cmap, PALETTE_SIZE, 0); if (ret) - goto err_release_pl_mem; + goto err_release_fb; da8xx_fb_info->cmap.len = par->palette_sz; /* initialize var_screeninfo */ @@ -1517,14 +1529,6 @@ err_cpu_freq: err_dealloc_cmap: fb_dealloc_cmap(&da8xx_fb_info->cmap); -err_release_pl_mem: - dma_free_coherent(par->dev, PALETTE_SIZE, par->v_palette_base, - par->p_palette_base); - -err_release_fb_mem: - dma_free_coherent(par->dev, par->vram_size, par->vram_virt, - par->vram_phys); - err_release_fb: framebuffer_release(da8xx_fb_info); @@ -1603,10 +1607,14 @@ static int fb_suspend(struct device *dev) { struct fb_info *info = dev_get_drvdata(dev); struct da8xx_fb_par *par = info->par; + int ret; console_lock(); - if (par->panel_power_ctrl) - par->panel_power_ctrl(0); + if (par->lcd_supply) { + ret = regulator_disable(par->lcd_supply); + if (ret) + return ret; + } fb_set_suspend(info, 1); lcd_disable_raster(DA8XX_FRAME_WAIT); @@ -1620,6 +1628,7 @@ static int fb_resume(struct device *dev) { struct fb_info *info = dev_get_drvdata(dev); struct da8xx_fb_par *par = info->par; + int ret; console_lock(); pm_runtime_get_sync(dev); @@ -1627,8 +1636,11 @@ static int fb_resume(struct device *dev) if (par->blank == FB_BLANK_UNBLANK) { lcd_enable_raster(); - if (par->panel_power_ctrl) - par->panel_power_ctrl(1); + if (par->lcd_supply) { + ret = regulator_enable(par->lcd_supply); + if (ret) + return ret; + } } fb_set_suspend(info, 0); diff --git a/drivers/video/fbdev/nuc900fb.c b/drivers/video/fbdev/nuc900fb.c deleted file mode 100644 index 4fd851598584..000000000000 --- a/drivers/video/fbdev/nuc900fb.c +++ /dev/null @@ -1,760 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * - * Copyright (c) 2009 Nuvoton technology corporation - * All rights reserved. - * - * Description: - * Nuvoton LCD Controller Driver - * Author: - * Wang Qiang (rurality.linux@gmail.com) 2009/12/11 - */ -#include <linux/module.h> -#include <linux/kernel.h> -#include <linux/err.h> -#include <linux/errno.h> -#include <linux/string.h> -#include <linux/mm.h> -#include <linux/tty.h> -#include <linux/slab.h> -#include <linux/delay.h> -#include <linux/fb.h> -#include <linux/init.h> -#include <linux/dma-mapping.h> -#include <linux/interrupt.h> -#include <linux/workqueue.h> -#include <linux/wait.h> -#include <linux/platform_device.h> -#include <linux/clk.h> -#include <linux/cpufreq.h> -#include <linux/io.h> -#include <linux/pm.h> -#include <linux/device.h> - -#include <mach/map.h> -#include <mach/regs-clock.h> -#include <mach/regs-ldm.h> -#include <linux/platform_data/video-nuc900fb.h> - -#include "nuc900fb.h" - - -/* - * Initialize the nuc900 video (dual) buffer address - */ -static void nuc900fb_set_lcdaddr(struct fb_info *info) -{ - struct nuc900fb_info *fbi = info->par; - void __iomem *regs = fbi->io; - unsigned long vbaddr1, vbaddr2; - - vbaddr1 = info->fix.smem_start; - vbaddr2 = info->fix.smem_start; - vbaddr2 += info->fix.line_length * info->var.yres; - - /* set frambuffer start phy addr*/ - writel(vbaddr1, regs + REG_LCM_VA_BADDR0); - writel(vbaddr2, regs + REG_LCM_VA_BADDR1); - - writel(fbi->regs.lcd_va_fbctrl, regs + REG_LCM_VA_FBCTRL); - writel(fbi->regs.lcd_va_scale, regs + REG_LCM_VA_SCALE); -} - -/* - * calculate divider for lcd div - */ -static unsigned int nuc900fb_calc_pixclk(struct nuc900fb_info *fbi, - unsigned long pixclk) -{ - unsigned long clk = fbi->clk_rate; - unsigned long long div; - - /* pixclk is in picseconds. our clock is in Hz*/ - /* div = (clk * pixclk)/10^12 */ - div = (unsigned long long)clk * pixclk; - div >>= 12; - do_div(div, 625 * 625UL * 625); - - dev_dbg(fbi->dev, "pixclk %ld, divisor is %lld\n", pixclk, div); - - return div; -} - -/* - * Check the video params of 'var'. - */ -static int nuc900fb_check_var(struct fb_var_screeninfo *var, - struct fb_info *info) -{ - struct nuc900fb_info *fbi = info->par; - struct nuc900fb_mach_info *mach_info = dev_get_platdata(fbi->dev); - struct nuc900fb_display *display = NULL; - struct nuc900fb_display *default_display = mach_info->displays + - mach_info->default_display; - int i; - - dev_dbg(fbi->dev, "check_var(var=%p, info=%p)\n", var, info); - - /* validate x/y resolution */ - /* choose default mode if possible */ - if (var->xres == default_display->xres && - var->yres == default_display->yres && - var->bits_per_pixel == default_display->bpp) - display = default_display; - else - for (i = 0; i < mach_info->num_displays; i++) - if (var->xres == mach_info->displays[i].xres && - var->yres == mach_info->displays[i].yres && - var->bits_per_pixel == mach_info->displays[i].bpp) { - display = mach_info->displays + i; - break; - } - - if (display == NULL) { - printk(KERN_ERR "wrong resolution or depth %dx%d at %d bit per pixel\n", - var->xres, var->yres, var->bits_per_pixel); - return -EINVAL; - } - - /* it should be the same size as the display */ - var->xres_virtual = display->xres; - var->yres_virtual = display->yres; - var->height = display->height; - var->width = display->width; - - /* copy lcd settings */ - var->pixclock = display->pixclock; - var->left_margin = display->left_margin; - var->right_margin = display->right_margin; - var->upper_margin = display->upper_margin; - var->lower_margin = display->lower_margin; - var->vsync_len = display->vsync_len; - var->hsync_len = display->hsync_len; - - var->transp.offset = 0; - var->transp.length = 0; - - fbi->regs.lcd_dccs = display->dccs; - fbi->regs.lcd_device_ctrl = display->devctl; - fbi->regs.lcd_va_fbctrl = display->fbctrl; - fbi->regs.lcd_va_scale = display->scale; - - /* set R/G/B possions */ - switch (var->bits_per_pixel) { - case 1: - case 2: - case 4: - case 8: - default: - var->red.offset = 0; - var->red.length = var->bits_per_pixel; - var->green = var->red; - var->blue = var->red; - break; - case 12: - var->red.length = 4; - var->green.length = 4; - var->blue.length = 4; - var->red.offset = 8; - var->green.offset = 4; - var->blue.offset = 0; - break; - case 16: - var->red.length = 5; - var->green.length = 6; - var->blue.length = 5; - var->red.offset = 11; - var->green.offset = 5; - var->blue.offset = 0; - break; - case 18: - var->red.length = 6; - var->green.length = 6; - var->blue.length = 6; - var->red.offset = 12; - var->green.offset = 6; - var->blue.offset = 0; - break; - case 32: - var->red.length = 8; - var->green.length = 8; - var->blue.length = 8; - var->red.offset = 16; - var->green.offset = 8; - var->blue.offset = 0; - break; - } - - return 0; -} - -/* - * Calculate lcd register values from var setting & save into hw - */ -static void nuc900fb_calculate_lcd_regs(const struct fb_info *info, - struct nuc900fb_hw *regs) -{ - const struct fb_var_screeninfo *var = &info->var; - int vtt = var->height + var->upper_margin + var->lower_margin; - int htt = var->width + var->left_margin + var->right_margin; - int hsync = var->width + var->right_margin; - int vsync = var->height + var->lower_margin; - - regs->lcd_crtc_size = LCM_CRTC_SIZE_VTTVAL(vtt) | - LCM_CRTC_SIZE_HTTVAL(htt); - regs->lcd_crtc_dend = LCM_CRTC_DEND_VDENDVAL(var->height) | - LCM_CRTC_DEND_HDENDVAL(var->width); - regs->lcd_crtc_hr = LCM_CRTC_HR_EVAL(var->width + 5) | - LCM_CRTC_HR_SVAL(var->width + 1); - regs->lcd_crtc_hsync = LCM_CRTC_HSYNC_EVAL(hsync + var->hsync_len) | - LCM_CRTC_HSYNC_SVAL(hsync); - regs->lcd_crtc_vr = LCM_CRTC_VR_EVAL(vsync + var->vsync_len) | - LCM_CRTC_VR_SVAL(vsync); - -} - -/* - * Activate (set) the controller from the given framebuffer - * information - */ -static void nuc900fb_activate_var(struct fb_info *info) -{ - struct nuc900fb_info *fbi = info->par; - void __iomem *regs = fbi->io; - struct fb_var_screeninfo *var = &info->var; - int clkdiv; - - clkdiv = nuc900fb_calc_pixclk(fbi, var->pixclock) - 1; - if (clkdiv < 0) - clkdiv = 0; - - nuc900fb_calculate_lcd_regs(info, &fbi->regs); - - /* set the new lcd registers*/ - - dev_dbg(fbi->dev, "new lcd register set:\n"); - dev_dbg(fbi->dev, "dccs = 0x%08x\n", fbi->regs.lcd_dccs); - dev_dbg(fbi->dev, "dev_ctl = 0x%08x\n", fbi->regs.lcd_device_ctrl); - dev_dbg(fbi->dev, "crtc_size = 0x%08x\n", fbi->regs.lcd_crtc_size); - dev_dbg(fbi->dev, "crtc_dend = 0x%08x\n", fbi->regs.lcd_crtc_dend); - dev_dbg(fbi->dev, "crtc_hr = 0x%08x\n", fbi->regs.lcd_crtc_hr); - dev_dbg(fbi->dev, "crtc_hsync = 0x%08x\n", fbi->regs.lcd_crtc_hsync); - dev_dbg(fbi->dev, "crtc_vr = 0x%08x\n", fbi->regs.lcd_crtc_vr); - - writel(fbi->regs.lcd_device_ctrl, regs + REG_LCM_DEV_CTRL); - writel(fbi->regs.lcd_crtc_size, regs + REG_LCM_CRTC_SIZE); - writel(fbi->regs.lcd_crtc_dend, regs + REG_LCM_CRTC_DEND); - writel(fbi->regs.lcd_crtc_hr, regs + REG_LCM_CRTC_HR); - writel(fbi->regs.lcd_crtc_hsync, regs + REG_LCM_CRTC_HSYNC); - writel(fbi->regs.lcd_crtc_vr, regs + REG_LCM_CRTC_VR); - - /* set lcd address pointers */ - nuc900fb_set_lcdaddr(info); - - writel(fbi->regs.lcd_dccs, regs + REG_LCM_DCCS); -} - -/* - * Alters the hardware state. - * - */ -static int nuc900fb_set_par(struct fb_info *info) -{ - struct fb_var_screeninfo *var = &info->var; - - switch (var->bits_per_pixel) { - case 32: - case 24: - case 18: - case 16: - case 12: - info->fix.visual = FB_VISUAL_TRUECOLOR; - break; - case 1: - info->fix.visual = FB_VISUAL_MONO01; - break; - default: - info->fix.visual = FB_VISUAL_PSEUDOCOLOR; - break; - } - - info->fix.line_length = (var->xres_virtual * var->bits_per_pixel) / 8; - - /* activate this new configuration */ - nuc900fb_activate_var(info); - return 0; -} - -static inline unsigned int chan_to_field(unsigned int chan, - struct fb_bitfield *bf) -{ - chan &= 0xffff; - chan >>= 16 - bf->length; - return chan << bf->offset; -} - -static int nuc900fb_setcolreg(unsigned regno, - unsigned red, unsigned green, unsigned blue, - unsigned transp, struct fb_info *info) -{ - unsigned int val; - - switch (info->fix.visual) { - case FB_VISUAL_TRUECOLOR: - /* true-colour, use pseuo-palette */ - if (regno < 16) { - u32 *pal = info->pseudo_palette; - - val = chan_to_field(red, &info->var.red); - val |= chan_to_field(green, &info->var.green); - val |= chan_to_field(blue, &info->var.blue); - pal[regno] = val; - } - break; - - default: - return 1; /* unknown type */ - } - return 0; -} - -/** - * nuc900fb_blank - * - */ -static int nuc900fb_blank(int blank_mode, struct fb_info *info) -{ - - return 0; -} - -static struct fb_ops nuc900fb_ops = { - .owner = THIS_MODULE, - .fb_check_var = nuc900fb_check_var, - .fb_set_par = nuc900fb_set_par, - .fb_blank = nuc900fb_blank, - .fb_setcolreg = nuc900fb_setcolreg, - .fb_fillrect = cfb_fillrect, - .fb_copyarea = cfb_copyarea, - .fb_imageblit = cfb_imageblit, -}; - - -static inline void modify_gpio(void __iomem *reg, - unsigned long set, unsigned long mask) -{ - unsigned long tmp; - tmp = readl(reg) & ~mask; - writel(tmp | set, reg); -} - -/* - * Initialise LCD-related registers - */ -static int nuc900fb_init_registers(struct fb_info *info) -{ - struct nuc900fb_info *fbi = info->par; - struct nuc900fb_mach_info *mach_info = dev_get_platdata(fbi->dev); - void __iomem *regs = fbi->io; - - /*reset the display engine*/ - writel(0, regs + REG_LCM_DCCS); - writel(readl(regs + REG_LCM_DCCS) | LCM_DCCS_ENG_RST, - regs + REG_LCM_DCCS); - ndelay(100); - writel(readl(regs + REG_LCM_DCCS) & (~LCM_DCCS_ENG_RST), - regs + REG_LCM_DCCS); - ndelay(100); - - writel(0, regs + REG_LCM_DEV_CTRL); - - /* config gpio output */ - modify_gpio(W90X900_VA_GPIO + 0x54, mach_info->gpio_dir, - mach_info->gpio_dir_mask); - modify_gpio(W90X900_VA_GPIO + 0x58, mach_info->gpio_data, - mach_info->gpio_data_mask); - - return 0; -} - - -/* - * Alloc the SDRAM region of NUC900 for the frame buffer. - * The buffer should be a non-cached, non-buffered, memory region - * to allow palette and pixel writes without flushing the cache. - */ -static int nuc900fb_map_video_memory(struct fb_info *info) -{ - struct nuc900fb_info *fbi = info->par; - dma_addr_t map_dma; - unsigned long map_size = PAGE_ALIGN(info->fix.smem_len); - - dev_dbg(fbi->dev, "nuc900fb_map_video_memory(fbi=%p) map_size %lu\n", - fbi, map_size); - - info->screen_base = dma_alloc_wc(fbi->dev, map_size, &map_dma, - GFP_KERNEL); - - if (!info->screen_base) - return -ENOMEM; - - memset(info->screen_base, 0x00, map_size); - info->fix.smem_start = map_dma; - - return 0; -} - -static inline void nuc900fb_unmap_video_memory(struct fb_info *info) -{ - struct nuc900fb_info *fbi = info->par; - dma_free_wc(fbi->dev, PAGE_ALIGN(info->fix.smem_len), - info->screen_base, info->fix.smem_start); -} - -static irqreturn_t nuc900fb_irqhandler(int irq, void *dev_id) -{ - struct nuc900fb_info *fbi = dev_id; - void __iomem *regs = fbi->io; - void __iomem *irq_base = fbi->irq_base; - unsigned long lcdirq = readl(regs + REG_LCM_INT_CS); - - if (lcdirq & LCM_INT_CS_DISP_F_STATUS) { - writel(readl(irq_base) | 1<<30, irq_base); - - /* wait VA_EN low */ - if ((readl(regs + REG_LCM_DCCS) & - LCM_DCCS_SINGLE) == LCM_DCCS_SINGLE) - while ((readl(regs + REG_LCM_DCCS) & - LCM_DCCS_VA_EN) == LCM_DCCS_VA_EN) - ; - /* display_out-enable */ - writel(readl(regs + REG_LCM_DCCS) | LCM_DCCS_DISP_OUT_EN, - regs + REG_LCM_DCCS); - /* va-enable*/ - writel(readl(regs + REG_LCM_DCCS) | LCM_DCCS_VA_EN, - regs + REG_LCM_DCCS); - } else if (lcdirq & LCM_INT_CS_UNDERRUN_INT) { - writel(readl(irq_base) | LCM_INT_CS_UNDERRUN_INT, irq_base); - } else if (lcdirq & LCM_INT_CS_BUS_ERROR_INT) { - writel(readl(irq_base) | LCM_INT_CS_BUS_ERROR_INT, irq_base); - } - - return IRQ_HANDLED; -} - -#ifdef CONFIG_CPU_FREQ - -static int nuc900fb_cpufreq_transition(struct notifier_block *nb, - unsigned long val, void *data) -{ - struct nuc900fb_info *info; - struct fb_info *fbinfo; - long delta_f; - info = container_of(nb, struct nuc900fb_info, freq_transition); - fbinfo = dev_get_drvdata(info->dev); - - delta_f = info->clk_rate - clk_get_rate(info->clk); - - if ((val == CPUFREQ_POSTCHANGE && delta_f > 0) || - (val == CPUFREQ_PRECHANGE && delta_f < 0)) { - info->clk_rate = clk_get_rate(info->clk); - nuc900fb_activate_var(fbinfo); - } - - return 0; -} - -static inline int nuc900fb_cpufreq_register(struct nuc900fb_info *fbi) -{ - fbi->freq_transition.notifier_call = nuc900fb_cpufreq_transition; - return cpufreq_register_notifier(&fbi->freq_transition, - CPUFREQ_TRANSITION_NOTIFIER); -} - -static inline void nuc900fb_cpufreq_deregister(struct nuc900fb_info *fbi) -{ - cpufreq_unregister_notifier(&fbi->freq_transition, - CPUFREQ_TRANSITION_NOTIFIER); -} -#else -static inline int nuc900fb_cpufreq_transition(struct notifier_block *nb, - unsigned long val, void *data) -{ - return 0; -} - -static inline int nuc900fb_cpufreq_register(struct nuc900fb_info *fbi) -{ - return 0; -} - -static inline void nuc900fb_cpufreq_deregister(struct nuc900fb_info *info) -{ -} -#endif - -static char driver_name[] = "nuc900fb"; - -static int nuc900fb_probe(struct platform_device *pdev) -{ - struct nuc900fb_info *fbi; - struct nuc900fb_display *display; - struct fb_info *fbinfo; - struct nuc900fb_mach_info *mach_info; - struct resource *res; - int ret; - int irq; - int i; - int size; - - dev_dbg(&pdev->dev, "devinit\n"); - mach_info = dev_get_platdata(&pdev->dev); - if (mach_info == NULL) { - dev_err(&pdev->dev, - "no platform data for lcd, cannot attach\n"); - return -EINVAL; - } - - if (mach_info->default_display > mach_info->num_displays) { - dev_err(&pdev->dev, - "default display No. is %d but only %d displays \n", - mach_info->default_display, mach_info->num_displays); - return -EINVAL; - } - - - display = mach_info->displays + mach_info->default_display; - - irq = platform_get_irq(pdev, 0); - if (irq < 0) { - dev_err(&pdev->dev, "no irq for device\n"); - return -ENOENT; - } - - fbinfo = framebuffer_alloc(sizeof(struct nuc900fb_info), &pdev->dev); - if (!fbinfo) - return -ENOMEM; - - platform_set_drvdata(pdev, fbinfo); - - fbi = fbinfo->par; - fbi->dev = &pdev->dev; - -#ifdef CONFIG_CPU_NUC950 - fbi->drv_type = LCDDRV_NUC950; -#endif - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - - size = resource_size(res); - fbi->mem = request_mem_region(res->start, size, pdev->name); - if (fbi->mem == NULL) { - dev_err(&pdev->dev, "failed to alloc memory region\n"); - ret = -ENOENT; - goto free_fb; - } - - fbi->io = ioremap(res->start, size); - if (fbi->io == NULL) { - dev_err(&pdev->dev, "ioremap() of lcd registers failed\n"); - ret = -ENXIO; - goto release_mem_region; - } - - fbi->irq_base = fbi->io + REG_LCM_INT_CS; - - - /* Stop the LCD */ - writel(0, fbi->io + REG_LCM_DCCS); - - /* fill the fbinfo*/ - strcpy(fbinfo->fix.id, driver_name); - fbinfo->fix.type = FB_TYPE_PACKED_PIXELS; - fbinfo->fix.type_aux = 0; - fbinfo->fix.xpanstep = 0; - fbinfo->fix.ypanstep = 0; - fbinfo->fix.ywrapstep = 0; - fbinfo->fix.accel = FB_ACCEL_NONE; - fbinfo->var.nonstd = 0; - fbinfo->var.activate = FB_ACTIVATE_NOW; - fbinfo->var.accel_flags = 0; - fbinfo->var.vmode = FB_VMODE_NONINTERLACED; - fbinfo->fbops = &nuc900fb_ops; - fbinfo->flags = FBINFO_FLAG_DEFAULT; - fbinfo->pseudo_palette = &fbi->pseudo_pal; - - ret = request_irq(irq, nuc900fb_irqhandler, 0, pdev->name, fbi); - if (ret) { - dev_err(&pdev->dev, "cannot register irq handler %d -err %d\n", - irq, ret); - ret = -EBUSY; - goto release_regs; - } - - fbi->clk = clk_get(&pdev->dev, NULL); - if (IS_ERR(fbi->clk)) { - printk(KERN_ERR "nuc900-lcd:failed to get lcd clock source\n"); - ret = PTR_ERR(fbi->clk); - goto release_irq; - } - - clk_enable(fbi->clk); - dev_dbg(&pdev->dev, "got and enabled clock\n"); - - fbi->clk_rate = clk_get_rate(fbi->clk); - - /* calutate the video buffer size */ - for (i = 0; i < mach_info->num_displays; i++) { - unsigned long smem_len = mach_info->displays[i].xres; - smem_len *= mach_info->displays[i].yres; - smem_len *= mach_info->displays[i].bpp; - smem_len >>= 3; - if (fbinfo->fix.smem_len < smem_len) - fbinfo->fix.smem_len = smem_len; - } - - /* Initialize Video Memory */ - ret = nuc900fb_map_video_memory(fbinfo); - if (ret) { - printk(KERN_ERR "Failed to allocate video RAM: %x\n", ret); - goto release_clock; - } - - dev_dbg(&pdev->dev, "got video memory\n"); - - fbinfo->var.xres = display->xres; - fbinfo->var.yres = display->yres; - fbinfo->var.bits_per_pixel = display->bpp; - - nuc900fb_init_registers(fbinfo); - - nuc900fb_check_var(&fbinfo->var, fbinfo); - - ret = nuc900fb_cpufreq_register(fbi); - if (ret < 0) { - dev_err(&pdev->dev, "Failed to register cpufreq\n"); - goto free_video_memory; - } - - ret = register_framebuffer(fbinfo); - if (ret) { - printk(KERN_ERR "failed to register framebuffer device: %d\n", - ret); - goto free_cpufreq; - } - - fb_info(fbinfo, "%s frame buffer device\n", fbinfo->fix.id); - - return 0; - -free_cpufreq: - nuc900fb_cpufreq_deregister(fbi); -free_video_memory: - nuc900fb_unmap_video_memory(fbinfo); -release_clock: - clk_disable(fbi->clk); - clk_put(fbi->clk); -release_irq: - free_irq(irq, fbi); -release_regs: - iounmap(fbi->io); -release_mem_region: - release_mem_region(res->start, size); -free_fb: - framebuffer_release(fbinfo); - return ret; -} - -/* - * shutdown the lcd controller - */ -static void nuc900fb_stop_lcd(struct fb_info *info) -{ - struct nuc900fb_info *fbi = info->par; - void __iomem *regs = fbi->io; - - writel((~LCM_DCCS_DISP_INT_EN) | (~LCM_DCCS_VA_EN) | (~LCM_DCCS_OSD_EN), - regs + REG_LCM_DCCS); -} - -/* - * Cleanup - */ -static int nuc900fb_remove(struct platform_device *pdev) -{ - struct fb_info *fbinfo = platform_get_drvdata(pdev); - struct nuc900fb_info *fbi = fbinfo->par; - int irq; - - nuc900fb_stop_lcd(fbinfo); - msleep(1); - - unregister_framebuffer(fbinfo); - nuc900fb_cpufreq_deregister(fbi); - nuc900fb_unmap_video_memory(fbinfo); - - iounmap(fbi->io); - - irq = platform_get_irq(pdev, 0); - free_irq(irq, fbi); - - release_resource(fbi->mem); - kfree(fbi->mem); - - framebuffer_release(fbinfo); - - return 0; -} - -#ifdef CONFIG_PM - -/* - * suspend and resume support for the lcd controller - */ - -static int nuc900fb_suspend(struct platform_device *dev, pm_message_t state) -{ - struct fb_info *fbinfo = platform_get_drvdata(dev); - struct nuc900fb_info *info = fbinfo->par; - - nuc900fb_stop_lcd(fbinfo); - msleep(1); - clk_disable(info->clk); - return 0; -} - -static int nuc900fb_resume(struct platform_device *dev) -{ - struct fb_info *fbinfo = platform_get_drvdata(dev); - struct nuc900fb_info *fbi = fbinfo->par; - - printk(KERN_INFO "nuc900fb resume\n"); - - clk_enable(fbi->clk); - msleep(1); - - nuc900fb_init_registers(fbinfo); - nuc900fb_activate_var(fbinfo); - - return 0; -} - -#else -#define nuc900fb_suspend NULL -#define nuc900fb_resume NULL -#endif - -static struct platform_driver nuc900fb_driver = { - .probe = nuc900fb_probe, - .remove = nuc900fb_remove, - .suspend = nuc900fb_suspend, - .resume = nuc900fb_resume, - .driver = { - .name = "nuc900-lcd", - }, -}; - -module_platform_driver(nuc900fb_driver); - -MODULE_DESCRIPTION("Framebuffer driver for the NUC900"); -MODULE_LICENSE("GPL"); diff --git a/drivers/video/fbdev/nuc900fb.h b/drivers/video/fbdev/nuc900fb.h deleted file mode 100644 index 055ae9297931..000000000000 --- a/drivers/video/fbdev/nuc900fb.h +++ /dev/null @@ -1,51 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * - * Copyright (c) 2009 Nuvoton technology corporation - * All rights reserved. - * - * Author: - * Wang Qiang(rurality.linux@gmail.com) 2009/12/16 - */ - -#ifndef __NUC900FB_H -#define __NUC900FB_H - -#include <mach/map.h> -#include <linux/platform_data/video-nuc900fb.h> - -enum nuc900_lcddrv_type { - LCDDRV_NUC910, - LCDDRV_NUC930, - LCDDRV_NUC932, - LCDDRV_NUC950, - LCDDRV_NUC960, -}; - - -#define PALETTE_BUFFER_SIZE 256 -#define PALETTE_BUFF_CLEAR (0x80000000) /* entry is clear/invalid */ - -struct nuc900fb_info { - struct device *dev; - struct clk *clk; - - struct resource *mem; - void __iomem *io; - void __iomem *irq_base; - int drv_type; - struct nuc900fb_hw regs; - unsigned long clk_rate; - -#ifdef CONFIG_CPU_FREQ - struct notifier_block freq_transition; -#endif - - /* keep these registers in case we need to re-write palette */ - u32 palette_buffer[PALETTE_BUFFER_SIZE]; - u32 pseudo_pal[16]; -}; - -int nuc900fb_init(void); - -#endif /* __NUC900FB_H */ |