lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite for Android: free password hash cracker in your pocket
[<prev] [next>] [<thread-prev] [day] [month] [year] [list]
Message-ID: <452de3e6-face-623d-4997-26b35d0733e7@opensource.cirrus.com>
Date:   Fri, 14 Sep 2018 16:29:24 +0100
From:   Richard Fitzgerald <rf@...nsource.cirrus.com>
To:     <tglx@...utronix.de>, <jason@...edaemon.net>,
        <marc.zyngier@....com>
CC:     <patches@...nsource.cirrus.com>, <linux-kernel@...r.kernel.org>
Subject: Re: [PATCH v12] irqchip: Add driver for Cirrus Logic Madera codecs

On 14/09/18 16:20, Richard Fitzgerald wrote:
> The Cirrus Logic Madera codecs (Cirrus Logic CS47L35/85/90/91 and WM1840)
> are highly complex devices containing up to 7 programmable DSPs and many
> other internal sources of interrupts plus a number of GPIOs that can be
> used as interrupt inputs. The large number (>150) of internal interrupt
> sources are managed by an on-board interrupt controller.
> 
> This driver provides the handling for the interrupt controller. As the
> codec is accessed via regmap, we can make use of the generic IRQ
> functionality from regmap to do most of the work. Only around half of
> the possible interrupt source are currently of interest from the driver
> so only this subset is defined. Others can be added in future if needed.
> 
> The KConfig options are not user-configurable because this driver is
> mandatory so is automatically included when the parent MFD driver is
> selected.
> 
> Signed-off-by: Richard Fitzgerald <rf@...nsource.cirrus.com>
> Signed-off-by: Charles Keepax <ckeepax@...nsource.cirrus.com>
> ---
> Only difference from v11 is the copyright header.

Sorry everyone, ignore this version and use v13.
I forgot to change the copyright on the .h file.
Friday afternoon is a bad time to send patches.

> ---
>   MAINTAINERS                        |   2 +
>   drivers/irqchip/Kconfig            |   3 +
>   drivers/irqchip/Makefile           |   1 +
>   drivers/irqchip/irq-madera.c       | 256 +++++++++++++++++++++++++++++++++++++
>   include/linux/irqchip/irq-madera.h | 135 +++++++++++++++++++
>   5 files changed, 397 insertions(+)
>   create mode 100644 drivers/irqchip/irq-madera.c
>   create mode 100644 include/linux/irqchip/irq-madera.h
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index bb65f0c1861c..faff2f72ab29 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -3595,8 +3595,10 @@ W:	https://github.com/CirrusLogic/linux-drivers/wiki
>   S:	Supported
>   F:	Documentation/devicetree/bindings/mfd/madera.txt
>   F:	Documentation/devicetree/bindings/pinctrl/cirrus,madera-pinctrl.txt
> +F:	include/linux/irqchip/irq-madera*
>   F:	include/linux/mfd/madera/*
>   F:	drivers/gpio/gpio-madera*
> +F:	drivers/irqchip/irq-madera*
>   F:	drivers/mfd/madera*
>   F:	drivers/mfd/cs47l*
>   F:	drivers/pinctrl/cirrus/*
> diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
> index 383e7b70221d..0afe6bdd2074 100644
> --- a/drivers/irqchip/Kconfig
> +++ b/drivers/irqchip/Kconfig
> @@ -150,6 +150,9 @@ config IMGPDC_IRQ
>   	select GENERIC_IRQ_CHIP
>   	select IRQ_DOMAIN
>   
> +config MADERA_IRQ
> +	tristate
> +
>   config IRQ_MIPS_CPU
>   	bool
>   	select GENERIC_IRQ_CHIP
> diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
> index fbd1ec8070ef..97838212d8e4 100644
> --- a/drivers/irqchip/Makefile
> +++ b/drivers/irqchip/Makefile
> @@ -88,3 +88,4 @@ obj-$(CONFIG_GOLDFISH_PIC) 		+= irq-goldfish-pic.o
>   obj-$(CONFIG_NDS32)			+= irq-ativic32.o
>   obj-$(CONFIG_QCOM_PDC)			+= qcom-pdc.o
>   obj-$(CONFIG_SIFIVE_PLIC)		+= irq-sifive-plic.o
> +obj-$(CONFIG_MADERA_IRQ)		+= irq-madera.o
> diff --git a/drivers/irqchip/irq-madera.c b/drivers/irqchip/irq-madera.c
> new file mode 100644
> index 000000000000..e9256dee1a45
> --- /dev/null
> +++ b/drivers/irqchip/irq-madera.c
> @@ -0,0 +1,256 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Interrupt support for Cirrus Logic Madera codecs
> + *
> + * Copyright (C) 2015-2018 Cirrus Logic, Inc. and
> + *                         Cirrus Logic International Semiconductor Ltd.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/gpio.h>
> +#include <linux/interrupt.h>
> +#include <linux/irq.h>
> +#include <linux/irqdomain.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/regmap.h>
> +#include <linux/slab.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
> +#include <linux/of_gpio.h>
> +#include <linux/of_irq.h>
> +#include <linux/irqchip/irq-madera.h>
> +#include <linux/mfd/madera/core.h>
> +#include <linux/mfd/madera/pdata.h>
> +#include <linux/mfd/madera/registers.h>
> +
> +#define MADERA_IRQ(_irq, _reg)					\
> +	[MADERA_IRQ_ ## _irq] = {				\
> +		.reg_offset = (_reg) - MADERA_IRQ1_STATUS_2,	\
> +		.mask = MADERA_ ## _irq ## _EINT1		\
> +	}
> +
> +/* Mappings are the same for all Madera codecs */
> +static const struct regmap_irq madera_irqs[MADERA_NUM_IRQ] = {
> +	MADERA_IRQ(FLL1_LOCK,		MADERA_IRQ1_STATUS_2),
> +	MADERA_IRQ(FLL2_LOCK,		MADERA_IRQ1_STATUS_2),
> +	MADERA_IRQ(FLL3_LOCK,		MADERA_IRQ1_STATUS_2),
> +	MADERA_IRQ(FLLAO_LOCK,		MADERA_IRQ1_STATUS_2),
> +
> +	MADERA_IRQ(MICDET1,		MADERA_IRQ1_STATUS_6),
> +	MADERA_IRQ(MICDET2,		MADERA_IRQ1_STATUS_6),
> +	MADERA_IRQ(HPDET,		MADERA_IRQ1_STATUS_6),
> +
> +	MADERA_IRQ(MICD_CLAMP_RISE,	MADERA_IRQ1_STATUS_7),
> +	MADERA_IRQ(MICD_CLAMP_FALL,	MADERA_IRQ1_STATUS_7),
> +	MADERA_IRQ(JD1_RISE,		MADERA_IRQ1_STATUS_7),
> +	MADERA_IRQ(JD1_FALL,		MADERA_IRQ1_STATUS_7),
> +
> +	MADERA_IRQ(ASRC2_IN1_LOCK,	MADERA_IRQ1_STATUS_9),
> +	MADERA_IRQ(ASRC2_IN2_LOCK,	MADERA_IRQ1_STATUS_9),
> +	MADERA_IRQ(ASRC1_IN1_LOCK,	MADERA_IRQ1_STATUS_9),
> +	MADERA_IRQ(ASRC1_IN2_LOCK,	MADERA_IRQ1_STATUS_9),
> +	MADERA_IRQ(DRC2_SIG_DET,	MADERA_IRQ1_STATUS_9),
> +	MADERA_IRQ(DRC1_SIG_DET,	MADERA_IRQ1_STATUS_9),
> +
> +	MADERA_IRQ(DSP_IRQ1,		MADERA_IRQ1_STATUS_11),
> +	MADERA_IRQ(DSP_IRQ2,		MADERA_IRQ1_STATUS_11),
> +	MADERA_IRQ(DSP_IRQ3,		MADERA_IRQ1_STATUS_11),
> +	MADERA_IRQ(DSP_IRQ4,		MADERA_IRQ1_STATUS_11),
> +	MADERA_IRQ(DSP_IRQ5,		MADERA_IRQ1_STATUS_11),
> +	MADERA_IRQ(DSP_IRQ6,		MADERA_IRQ1_STATUS_11),
> +	MADERA_IRQ(DSP_IRQ7,		MADERA_IRQ1_STATUS_11),
> +	MADERA_IRQ(DSP_IRQ8,		MADERA_IRQ1_STATUS_11),
> +	MADERA_IRQ(DSP_IRQ9,		MADERA_IRQ1_STATUS_11),
> +	MADERA_IRQ(DSP_IRQ10,		MADERA_IRQ1_STATUS_11),
> +	MADERA_IRQ(DSP_IRQ11,		MADERA_IRQ1_STATUS_11),
> +	MADERA_IRQ(DSP_IRQ12,		MADERA_IRQ1_STATUS_11),
> +	MADERA_IRQ(DSP_IRQ13,		MADERA_IRQ1_STATUS_11),
> +	MADERA_IRQ(DSP_IRQ14,		MADERA_IRQ1_STATUS_11),
> +	MADERA_IRQ(DSP_IRQ15,		MADERA_IRQ1_STATUS_11),
> +	MADERA_IRQ(DSP_IRQ16,		MADERA_IRQ1_STATUS_11),
> +
> +	MADERA_IRQ(HP3R_SC,		MADERA_IRQ1_STATUS_12),
> +	MADERA_IRQ(HP3L_SC,		MADERA_IRQ1_STATUS_12),
> +	MADERA_IRQ(HP2R_SC,		MADERA_IRQ1_STATUS_12),
> +	MADERA_IRQ(HP2L_SC,		MADERA_IRQ1_STATUS_12),
> +	MADERA_IRQ(HP1R_SC,		MADERA_IRQ1_STATUS_12),
> +	MADERA_IRQ(HP1L_SC,		MADERA_IRQ1_STATUS_12),
> +
> +	MADERA_IRQ(SPK_OVERHEAT_WARN,	MADERA_IRQ1_STATUS_15),
> +	MADERA_IRQ(SPK_OVERHEAT,	MADERA_IRQ1_STATUS_15),
> +
> +	MADERA_IRQ(DSP1_BUS_ERR,	MADERA_IRQ1_STATUS_33),
> +	MADERA_IRQ(DSP2_BUS_ERR,	MADERA_IRQ1_STATUS_33),
> +	MADERA_IRQ(DSP3_BUS_ERR,	MADERA_IRQ1_STATUS_33),
> +	MADERA_IRQ(DSP4_BUS_ERR,	MADERA_IRQ1_STATUS_33),
> +	MADERA_IRQ(DSP5_BUS_ERR,	MADERA_IRQ1_STATUS_33),
> +	MADERA_IRQ(DSP6_BUS_ERR,	MADERA_IRQ1_STATUS_33),
> +	MADERA_IRQ(DSP7_BUS_ERR,	MADERA_IRQ1_STATUS_33),
> +};
> +
> +static const struct regmap_irq_chip madera_irq_chip = {
> +	.name		= "madera IRQ",
> +	.status_base	= MADERA_IRQ1_STATUS_2,
> +	.mask_base	= MADERA_IRQ1_MASK_2,
> +	.ack_base	= MADERA_IRQ1_STATUS_2,
> +	.runtime_pm	= true,
> +	.num_regs	= 32,
> +	.irqs		= madera_irqs,
> +	.num_irqs	= ARRAY_SIZE(madera_irqs),
> +};
> +
> +#ifdef CONFIG_PM_SLEEP
> +static int madera_suspend(struct device *dev)
> +{
> +	struct madera *madera = dev_get_drvdata(dev->parent);
> +
> +	dev_dbg(madera->irq_dev, "Suspend, disabling IRQ\n");
> +
> +	/*
> +	 * A runtime resume would be needed to access the chip interrupt
> +	 * controller but runtime pm doesn't function during suspend.
> +	 * Temporarily disable interrupts until we reach suspend_noirq state.
> +	 */
> +	disable_irq(madera->irq);
> +
> +	return 0;
> +}
> +
> +static int madera_suspend_noirq(struct device *dev)
> +{
> +	struct madera *madera = dev_get_drvdata(dev->parent);
> +
> +	dev_dbg(madera->irq_dev, "No IRQ suspend, reenabling IRQ\n");
> +
> +	/* Re-enable interrupts to service wakeup interrupts from the chip */
> +	enable_irq(madera->irq);
> +
> +	return 0;
> +}
> +
> +static int madera_resume_noirq(struct device *dev)
> +{
> +	struct madera *madera = dev_get_drvdata(dev->parent);
> +
> +	dev_dbg(madera->irq_dev, "No IRQ resume, disabling IRQ\n");
> +
> +	/*
> +	 * We can't handle interrupts until runtime pm is available again.
> +	 * Disable them temporarily.
> +	 */
> +	disable_irq(madera->irq);
> +
> +	return 0;
> +}
> +
> +static int madera_resume(struct device *dev)
> +{
> +	struct madera *madera = dev_get_drvdata(dev->parent);
> +
> +	dev_dbg(madera->irq_dev, "Resume, reenabling IRQ\n");
> +
> +	/* Interrupts can now be handled */
> +	enable_irq(madera->irq);
> +
> +	return 0;
> +}
> +#endif
> +
> +static const struct dev_pm_ops madera_irq_pm_ops = {
> +	SET_SYSTEM_SLEEP_PM_OPS(madera_suspend, madera_resume)
> +	SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(madera_suspend_noirq,
> +				      madera_resume_noirq)
> +};
> +
> +static int madera_irq_probe(struct platform_device *pdev)
> +{
> +	struct madera *madera = dev_get_drvdata(pdev->dev.parent);
> +	struct irq_data *irq_data;
> +	unsigned int irq_flags = 0;
> +	int ret;
> +
> +	dev_dbg(&pdev->dev, "probe\n");
> +
> +	/*
> +	 * Read the flags from the interrupt controller if not specified
> +	 * by pdata
> +	 */
> +	irq_flags = madera->pdata.irq_flags;
> +	if (!irq_flags) {
> +		irq_data = irq_get_irq_data(madera->irq);
> +		if (!irq_data) {
> +			dev_err(&pdev->dev, "Invalid IRQ: %d\n", madera->irq);
> +			return -EINVAL;
> +		}
> +
> +		irq_flags = irqd_get_trigger_type(irq_data);
> +
> +		/* Codec defaults to trigger low, use this if no flags given */
> +		if (irq_flags == IRQ_TYPE_NONE)
> +			irq_flags = IRQF_TRIGGER_LOW;
> +	}
> +
> +	if (irq_flags & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) {
> +		dev_err(&pdev->dev, "Host interrupt not level-triggered\n");
> +		return -EINVAL;
> +	}
> +
> +	/*
> +	 * The silicon always starts at active-low, check if we need to
> +	 * switch to active-high.
> +	 */
> +	if (irq_flags & IRQF_TRIGGER_HIGH) {
> +		ret = regmap_update_bits(madera->regmap, MADERA_IRQ1_CTRL,
> +					 MADERA_IRQ_POL_MASK, 0);
> +		if (ret) {
> +			dev_err(&pdev->dev,
> +				"Failed to set IRQ polarity: %d\n", ret);
> +			return ret;
> +		}
> +	}
> +
> +	/*
> +	 * NOTE: regmap registers this against the OF node of the parent of
> +	 * the regmap - that is, against the mfd driver
> +	 */
> +	ret = regmap_add_irq_chip(madera->regmap, madera->irq, IRQF_ONESHOT, 0,
> +				  &madera_irq_chip, &madera->irq_data);
> +	if (ret) {
> +		dev_err(&pdev->dev, "add_irq_chip failed: %d\n", ret);
> +		return ret;
> +	}
> +
> +	/* Save dev in parent MFD struct so it is accessible to siblings */
> +	madera->irq_dev = &pdev->dev;
> +
> +	return 0;
> +}
> +
> +static int madera_irq_remove(struct platform_device *pdev)
> +{
> +	struct madera *madera = dev_get_drvdata(pdev->dev.parent);
> +
> +	/*
> +	 * The IRQ is disabled by the parent MFD driver before
> +	 * it starts cleaning up all child drivers
> +	 */
> +	madera->irq_dev = NULL;
> +	regmap_del_irq_chip(madera->irq, madera->irq_data);
> +
> +	return 0;
> +}
> +
> +static struct platform_driver madera_irq_driver = {
> +	.probe	= &madera_irq_probe,
> +	.remove = &madera_irq_remove,
> +	.driver = {
> +		.name	= "madera-irq",
> +		.pm	= &madera_irq_pm_ops,
> +	}
> +};
> +module_platform_driver(madera_irq_driver);
> +
> +MODULE_SOFTDEP("pre: madera");
> +MODULE_DESCRIPTION("Madera IRQ driver");
> +MODULE_AUTHOR("Richard Fitzgerald <rf@...nsource.cirrus.com>");
> +MODULE_LICENSE("GPL v2");
> diff --git a/include/linux/irqchip/irq-madera.h b/include/linux/irqchip/irq-madera.h
> new file mode 100644
> index 000000000000..5aeb7e6adc82
> --- /dev/null
> +++ b/include/linux/irqchip/irq-madera.h
> @@ -0,0 +1,135 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Interrupt support for Cirrus Logic Madera codecs
> + *
> + * Copyright 2016-2018 Cirrus Logic
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by the
> + * Free Software Foundation; version 2.
> + */
> +
> +#ifndef IRQCHIP_MADERA_H
> +#define IRQCHIP_MADERA_H
> +
> +#include <linux/interrupt.h>
> +#include <linux/mfd/madera/core.h>
> +
> +#define MADERA_IRQ_FLL1_LOCK		0
> +#define MADERA_IRQ_FLL2_LOCK		1
> +#define MADERA_IRQ_FLL3_LOCK		2
> +#define MADERA_IRQ_FLLAO_LOCK		3
> +#define MADERA_IRQ_CLK_SYS_ERR		4
> +#define MADERA_IRQ_CLK_ASYNC_ERR	5
> +#define MADERA_IRQ_CLK_DSP_ERR		6
> +#define MADERA_IRQ_HPDET		7
> +#define MADERA_IRQ_MICDET1		8
> +#define MADERA_IRQ_MICDET2		9
> +#define MADERA_IRQ_JD1_RISE		10
> +#define MADERA_IRQ_JD1_FALL		11
> +#define MADERA_IRQ_JD2_RISE		12
> +#define MADERA_IRQ_JD2_FALL		13
> +#define MADERA_IRQ_MICD_CLAMP_RISE	14
> +#define MADERA_IRQ_MICD_CLAMP_FALL	15
> +#define MADERA_IRQ_DRC2_SIG_DET		16
> +#define MADERA_IRQ_DRC1_SIG_DET		17
> +#define MADERA_IRQ_ASRC1_IN1_LOCK	18
> +#define MADERA_IRQ_ASRC1_IN2_LOCK	19
> +#define MADERA_IRQ_ASRC2_IN1_LOCK	20
> +#define MADERA_IRQ_ASRC2_IN2_LOCK	21
> +#define MADERA_IRQ_DSP_IRQ1		22
> +#define MADERA_IRQ_DSP_IRQ2		23
> +#define MADERA_IRQ_DSP_IRQ3		24
> +#define MADERA_IRQ_DSP_IRQ4		25
> +#define MADERA_IRQ_DSP_IRQ5		26
> +#define MADERA_IRQ_DSP_IRQ6		27
> +#define MADERA_IRQ_DSP_IRQ7		28
> +#define MADERA_IRQ_DSP_IRQ8		29
> +#define MADERA_IRQ_DSP_IRQ9		30
> +#define MADERA_IRQ_DSP_IRQ10		31
> +#define MADERA_IRQ_DSP_IRQ11		32
> +#define MADERA_IRQ_DSP_IRQ12		33
> +#define MADERA_IRQ_DSP_IRQ13		34
> +#define MADERA_IRQ_DSP_IRQ14		35
> +#define MADERA_IRQ_DSP_IRQ15		36
> +#define MADERA_IRQ_DSP_IRQ16		37
> +#define MADERA_IRQ_HP1L_SC		38
> +#define MADERA_IRQ_HP1R_SC		39
> +#define MADERA_IRQ_HP2L_SC		40
> +#define MADERA_IRQ_HP2R_SC		41
> +#define MADERA_IRQ_HP3L_SC		42
> +#define MADERA_IRQ_HP3R_SC		43
> +#define MADERA_IRQ_SPKOUTL_SC		44
> +#define MADERA_IRQ_SPKOUTR_SC		45
> +#define MADERA_IRQ_HP1L_ENABLE_DONE	46
> +#define MADERA_IRQ_HP1R_ENABLE_DONE	47
> +#define MADERA_IRQ_HP2L_ENABLE_DONE	48
> +#define MADERA_IRQ_HP2R_ENABLE_DONE	49
> +#define MADERA_IRQ_HP3L_ENABLE_DONE	50
> +#define MADERA_IRQ_HP3R_ENABLE_DONE	51
> +#define MADERA_IRQ_SPKOUTL_ENABLE_DONE	52
> +#define MADERA_IRQ_SPKOUTR_ENABLE_DONE	53
> +#define MADERA_IRQ_SPK_SHUTDOWN		54
> +#define MADERA_IRQ_SPK_OVERHEAT		55
> +#define MADERA_IRQ_SPK_OVERHEAT_WARN	56
> +#define MADERA_IRQ_GPIO1		57
> +#define MADERA_IRQ_GPIO2		58
> +#define MADERA_IRQ_GPIO3		59
> +#define MADERA_IRQ_GPIO4		60
> +#define MADERA_IRQ_GPIO5		61
> +#define MADERA_IRQ_GPIO6		62
> +#define MADERA_IRQ_GPIO7		63
> +#define MADERA_IRQ_GPIO8		64
> +#define MADERA_IRQ_DSP1_BUS_ERR		65
> +#define MADERA_IRQ_DSP2_BUS_ERR		66
> +#define MADERA_IRQ_DSP3_BUS_ERR		67
> +#define MADERA_IRQ_DSP4_BUS_ERR		68
> +#define MADERA_IRQ_DSP5_BUS_ERR		69
> +#define MADERA_IRQ_DSP6_BUS_ERR		70
> +#define MADERA_IRQ_DSP7_BUS_ERR		71
> +
> +#define MADERA_NUM_IRQ			72
> +
> +/*
> + * These wrapper functions are for use by other child drivers of the
> + * same parent MFD.
> + */
> +static inline int madera_get_irq_mapping(struct madera *madera, int irq)
> +{
> +	if (!madera->irq_dev)
> +		return -ENODEV;
> +
> +	return regmap_irq_get_virq(madera->irq_data, irq);
> +}
> +
> +static inline int madera_request_irq(struct madera *madera, int irq,
> +				     const char *name,
> +				     irq_handler_t handler, void *data)
> +{
> +	irq = madera_get_irq_mapping(madera, irq);
> +	if (irq < 0)
> +		return irq;
> +
> +	return request_threaded_irq(irq, NULL, handler, IRQF_ONESHOT, name,
> +				    data);
> +}
> +
> +static inline void madera_free_irq(struct madera *madera, int irq, void *data)
> +{
> +	irq = madera_get_irq_mapping(madera, irq);
> +	if (irq < 0)
> +		return;
> +
> +	free_irq(irq, data);
> +}
> +
> +static inline int madera_set_irq_wake(struct madera *madera, int irq, int on)
> +{
> +	irq = madera_get_irq_mapping(madera, irq);
> +	if (irq < 0)
> +		return irq;
> +
> +	return irq_set_irq_wake(irq, on);
> +}
> +
> +#endif
> 

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ