[RFC PATCH] drivers: bus: add ARM CCI support
Lorenzo Pieralisi
lorenzo.pieralisi at arm.com
Wed Apr 24 00:27:41 EST 2013
On Tue, Apr 23, 2013 at 02:52:08PM +0100, Jon Medhurst (Tixy) wrote:
> On Thu, 2013-04-11 at 15:47 +0100, Lorenzo Pieralisi wrote:
> [...]
> > diff --git a/drivers/bus/arm-cci.c b/drivers/bus/arm-cci.c
> > new file mode 100644
> > index 0000000..81953de
> > --- /dev/null
> > +++ b/drivers/bus/arm-cci.c
> > @@ -0,0 +1,344 @@
> > +/*
> > + * CCI cache coherent interconnect driver
> > + *
> > + * Copyright (C) 2013 ARM Ltd.
> > + * Author: Lorenzo Pieralisi <lorenzo.pieralisi at arm.com>
> > + *
> > + * This program is free software; you can redistribute it and/or modify
> > + * it under the terms of the GNU General Public License version 2 as
> > + * published by the Free Software Foundation.
> > + *
> > + * This program is distributed "as is" WITHOUT ANY WARRANTY of any
> > + * kind, whether express or implied; without even the implied warranty
> > + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> > + * GNU General Public License for more details.
> > + */
> > +
> > +#include <linux/arm-cci.h>
> > +#include <linux/device.h>
> > +#include <linux/io.h>
> > +#include <linux/module.h>
> > +#include <linux/of_address.h>
> > +#include <linux/of_device.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/slab.h>
> > +
> > +#include <asm/cacheflush.h>
> > +#include <asm/dt_affinity.h>
> > +#include <asm/outercache.h>
> > +#include <asm/smp_plat.h>
> > +
> > +#define DRIVER_NAME "CCI"
> > +#define CCI_CTRL_STATUS 0xc
> > +
> > +#define CCI_PORT_CTRL 0x0
> > +
> > +struct cci_nb_ports {
> > + unsigned int nb_ace;
> > + unsigned int nb_ace_lite;
> > +};
> > +
> > +enum cci_ace_port_type {
> > + ACE_INVALID_PORT = 0x0,
> > + ACE_PORT,
> > + ACE_LITE_PORT,
> > +};
> > +
> > +struct cci_ace_port {
> > + void __iomem *base;
> > + int type;
> > + union {
> > + cpumask_t match_mask;
> > + struct device_node *np;
> > + } match;
> > +};
> > +
> > +static struct cci_ace_port *ports;
> > +static unsigned int nb_cci_ports;
> > +
> > +static void __iomem *cci_ctrl_base;
> > +#define INVALID_PORT_IDX -1
> > +static int cpu_port[NR_CPUS] = { [0 ... NR_CPUS-1] = INVALID_PORT_IDX };
> > +
> > +static void __init cci_ace_init_ports(void)
> > +{
> > + bool is_ace;
> > + int port, cpu;
> > + /*
> > + * Port index look-up speeds up the function disabling ports by CPU,
> > + * since the logical to port index mapping is done once and does
> > + * not change after system boot.
> > + * The stashed index array is initialized for all possible CPUs
> > + * at probe time.
> > + */
> > + for_each_possible_cpu(cpu) {
> > + for (port = 0; port < nb_cci_ports; port++) {
> > + is_ace = ports[port].type == ACE_PORT;
> > + if (is_ace && cpu_isset(cpu,
> > + ports[port].match.match_mask)) {
> > + cpu_port[cpu] = port;
> > + break;
> > + }
> > + }
> > + WARN(cpu_port[cpu] == INVALID_PORT_IDX, "CPU %d has an invalid"
> > + " CCI port index\n",
>
> I think the convention is to not split user visible messages across
> multiple source lines as this makes grepping for them difficult.
Ok, I will address this issue on the entire patch.
>
> > + cpu);
> > + }
> > +}
> > +
> > +/*
> > + * cci_ace_lite_port - Function to retrieve the port index corresponding to
> > + * a device tree node
> > + *
> > + * @np = device tree node to match
> > + *
> > + * Return value:
> > + * - ACE LITE port index if success
> > + * - -EINVAL if failure
> > + *
> > + */
> > +static int cci_ace_lite_port(struct device_node *np)
> > +{
> > + int i;
> > + bool is_ace_lite;
> > +
> > + for (i = 0; i < nb_cci_ports; i++) {
> > + is_ace_lite = ports[i].type == ACE_LITE_PORT;
> > + if (is_ace_lite && np == ports[i].match.np)
> > + return i;
> > + }
> > + return -ENODEV;
> > +}
> > +
> > +/*
> > + * Functions to enable/disable a CCI interconnect slave port
> > + *
> > + * They are called by low-level power management code to disable slave
> > + * interfaces snoops and DVM broadcast.
> > + * Since they may execute with cache data allocation disabled and
> > + * after the caches have been cleaned and invalidated the functions provide
> > + * no explicit locking since they may run with D-cache disabled, so normal
> > + * cacheable kernel locks based on ldrex/strex may not work.
> > + * Locking has to be provided by BSP implementations to ensure proper
> > + * operations.
> > + */
> > +
> > +/*
> > + * cci_port_control()
> > + * @port = index of the port to setup
> > + * @enable = if true enables the port, if false disables it
> > + */
> > +static void notrace cci_port_control(unsigned int port, bool enable)
> > +{
> > + void __iomem *base = ports[port].base;
> > +
> > + if (!base)
> > + return;
> > +
> > + writel_relaxed(enable, base + CCI_PORT_CTRL);
>
> If enable is bool (0 or 1) then this is going to set snoops as specified
> but is always going to clear DVM broadcast as that is controlled by bit1
> of the register. So, does the API need specifying different to allow the
> caller to choose DVM state as well, or does this function need to assume
> DVM and snoops state should be equal? I.e.
>
> writel_relaxed(enable ? 0x3 : 0x0, base + CCI_PORT_CTRL);
Well spotted. I will have to think about this, but you are spot-on, the
current interface is plain wrong.
[...]
> > +ioremap_err:
> > + for (j = 0; j < nb_cci_ports; j++) {
> > + if (j == i)
> > + break;
> > + iounmap(ports[j].base);
> > + }
>
> That loop would be simpler as:
>
> while (--i >= 0)
> iounmap(ports[i].base);
>
> and declaration of 'j' can dropped from the function start.
Yes, absolutely.
> > +
> > + iounmap(cci_ctrl_base);
> > +memalloc_err:
> > +
> > + kfree(ports);
> > + return ret;
> > +}
> > +
> > +static int __exit cci_driver_remove(struct platform_device *pdev)
>
> Under what circumstances would we want to remove the driver?
Consider this function gone, so 0 circumstances.
Thanks a lot for the review,
Lorenzo
More information about the devicetree-discuss
mailing list