All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] i2c: vt8500: Add support for Wondermedia I2C master-mode
From: Tony Prisk @ 2012-12-28  1:29 UTC (permalink / raw)
  To: linux-arm-kernel

This patch adds support for the I2C controller found on Wondermedia
SoCs.

Due to the lack of pinmux support, GPIO pin alternate functions are
configured by machine's compatible property, as are pullups.

Signed-off-by: Tony Prisk <linux@prisktech.co.nz>
---
 .../devicetree/bindings/i2c/i2c-vt8500.txt         |   24 +
 drivers/i2c/busses/Kconfig                         |    7 +
 drivers/i2c/busses/Makefile                        |    1 +
 drivers/i2c/busses/i2c-wmt.c                       |  636 ++++++++++++++++++++
 4 files changed, 668 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/i2c/i2c-vt8500.txt
 create mode 100644 drivers/i2c/busses/i2c-wmt.c

diff --git a/Documentation/devicetree/bindings/i2c/i2c-vt8500.txt b/Documentation/devicetree/bindings/i2c/i2c-vt8500.txt
new file mode 100644
index 0000000..94a425e
--- /dev/null
+++ b/Documentation/devicetree/bindings/i2c/i2c-vt8500.txt
@@ -0,0 +1,24 @@
+* Wondermedia I2C Controller
+
+Required properties :
+
+ - compatible : should be "wm,wm8505-i2c"
+ - reg : Offset and length of the register set for the device
+ - interrupts : <IRQ> where IRQ is the interrupt number
+ - clocks : phandle to the I2C clock source
+
+Optional properties :
+
+ - clock-frequency : desired I2C bus clock frequency in Hz.
+	Valid values are 100000 and 400000.
+	Default to 100000 if not specified, or invalid value.
+
+Example :
+
+	i2c_0: i2c at d8280000 {
+		compatible = "wm,wm8505-i2c";
+		reg = <0xd8280000 0x1000>;
+		interrupts = <19>;
+		clocks = <&clki2c0>;
+		clock-frequency = <400000>;
+	};
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index bdca511..41270fa 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -700,6 +700,13 @@ config I2C_VERSATILE
 	  This driver can also be built as a module.  If so, the module
 	  will be called i2c-versatile.
 
+config I2C_WMT
+	tristate "Wondermedia I2C Controller support"
+	depends on ARCH_VT8500
+	help
+	  If you say yes to this option, support will be included for the
+	  I2C controllers found in Wondermedia SoCs.
+
 config I2C_OCTEON
 	tristate "Cavium OCTEON I2C bus support"
 	depends on CPU_CAVIUM_OCTEON
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index 6181f3f..dce6299 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -69,6 +69,7 @@ obj-$(CONFIG_I2C_SIRF)		+= i2c-sirf.o
 obj-$(CONFIG_I2C_STU300)	+= i2c-stu300.o
 obj-$(CONFIG_I2C_TEGRA)		+= i2c-tegra.o
 obj-$(CONFIG_I2C_VERSATILE)	+= i2c-versatile.o
+obj-$(CONFIG_I2C_WMT)		+= i2c-wmt.o
 obj-$(CONFIG_I2C_OCTEON)	+= i2c-octeon.o
 obj-$(CONFIG_I2C_XILINX)	+= i2c-xiic.o
 obj-$(CONFIG_I2C_XLR)		+= i2c-xlr.o
diff --git a/drivers/i2c/busses/i2c-wmt.c b/drivers/i2c/busses/i2c-wmt.c
new file mode 100644
index 0000000..07d86e3
--- /dev/null
+++ b/drivers/i2c/busses/i2c-wmt.c
@@ -0,0 +1,636 @@
+/*
+ *  Wondermedia I2C Master Mode Driver
+ *
+ *  Copyright (C) 2012 Tony Prisk <linux@prisktech.co.nz>
+ *
+ *  Derived from GPL licensed source:
+ *  - Copyright (C) 2008 WonderMedia Technologies, Inc.
+ *
+ *  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
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_i2c.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+
+#define REG_CR		0x00
+#define REG_TCR		0x02
+#define REG_CSR		0x04
+#define REG_ISR		0x06
+#define REG_IMR		0x08
+#define REG_CDR		0x0A
+#define REG_TR		0x0C
+#define REG_MCR		0x0E
+#define REG_SLAVE_CR	0x10
+#define REG_SLAVE_SR	0x12
+#define REG_SLAVE_ISR	0x14
+#define REG_SLAVE_IMR	0x16
+#define REG_SLAVE_DR	0x18
+#define REG_SLAVE_TR	0x1A
+
+/* REG_CR Bit fields */
+#define CR_TX_NEXT_ACK		0x0000
+#define CR_ENABLE		0x0001
+#define CR_TX_NEXT_NO_ACK	0x0002
+#define CR_TX_END		0x0004
+#define CR_CPU_RDY		0x0008
+#define SLAV_MODE_SEL		0x8000
+
+/* REG_TCR Bit fields */
+#define TCR_STANDARD_MODE	0x0000
+#define TCR_MASTER_WRITE	0x0000
+#define TCR_HS_MODE		0x2000
+#define TCR_MASTER_READ		0x4000
+#define TCR_FAST_MODE		0x8000
+#define TCR_SLAVE_ADDR_MASK	0x007F
+
+/* REG_ISR Bit fields */
+#define ISR_NACK_ADDR		0x0001
+#define ISR_BYTE_END		0x0002
+#define ISR_SCL_TIMEOUT		0x0004
+#define ISR_WRITE_ALL		0x0007
+
+/* REG_IMR Bit fields */
+#define IMR_ENABLE_ALL		0x0007
+
+/* REG_CSR Bit fields */
+#define CSR_RCV_NOT_ACK		0x0001
+#define CSR_RCV_ACK_MASK	0x0001
+#define CSR_READY_MASK		0x0002
+
+#define I2C_MODE_STANDARD	0
+#define I2C_MODE_FAST		1
+
+
+
+struct wmt_i2c_dev {
+	struct i2c_adapter	adapter;
+	struct completion	complete;
+	struct device		*dev;
+	void __iomem		*base;
+	struct clk		*clk;
+	int			mode;
+	int			irq;
+	u16			cmd_status;
+};
+
+static int wmt_i2c_wait_bus_not_busy(struct wmt_i2c_dev *i2c_dev)
+{
+	u16 val;
+	int i;
+	int ret = 0;
+
+	for (i = 0; i < 10000000; i++) {
+		val = readw(i2c_dev->base + REG_CSR);
+		if (val & CSR_READY_MASK)
+			break;
+
+		udelay(1);
+	}
+	if (i >= 9999999)
+		ret = -EBUSY;
+
+	return ret;
+}
+
+static int wmt_check_status(struct wmt_i2c_dev *i2c_dev)
+{
+	int ret = 0;
+
+	if (i2c_dev->cmd_status & ISR_NACK_ADDR)
+		ret = -EIO;
+
+	if (i2c_dev->cmd_status & ISR_SCL_TIMEOUT)
+		ret = -ETIMEDOUT;
+
+	return ret;
+}
+
+static int wmt_i2c_write(struct i2c_adapter *adap, struct i2c_msg *pmsg,
+						   int restart, int last)
+{
+	struct wmt_i2c_dev *i2c_dev = i2c_get_adapdata(adap);
+	u16 val;
+	u16 tcr_val;
+	int ret;
+	int wait_result;
+	u32 xfer_len = 0;
+
+	if (pmsg->len < 0)
+		return -EINVAL;
+
+	if (restart == 0) {
+		ret = wmt_i2c_wait_bus_not_busy(i2c_dev);
+		if (ret < 0)
+			return ret;
+	}
+
+	if (pmsg->len == 0)
+		writew(0, i2c_dev->base + REG_CDR);
+	else
+		writew(pmsg->buf[0] & 0xFF, i2c_dev->base + REG_CDR);
+
+	if (restart == 0) {
+		val = readw(i2c_dev->base + REG_CR);
+		val &= ~CR_TX_END;
+		writew(val, i2c_dev->base + REG_CR);
+
+		val = readw(i2c_dev->base + REG_CR);
+		val |= CR_CPU_RDY;
+		writew(val, i2c_dev->base + REG_CR);
+	}
+
+	init_completion(&i2c_dev->complete);
+
+	if (i2c_dev->mode == I2C_MODE_STANDARD)
+		tcr_val = TCR_STANDARD_MODE;
+	else
+		tcr_val = TCR_FAST_MODE;
+
+	tcr_val |= (TCR_MASTER_WRITE | (pmsg->addr & TCR_SLAVE_ADDR_MASK));
+
+	writew(tcr_val, i2c_dev->base + REG_TCR);
+
+	if (restart == 1) {
+		val = readw(i2c_dev->base + REG_CR);
+		val |= CR_CPU_RDY;
+		writew(val, i2c_dev->base + REG_CR);
+	}
+
+	ret = 0;
+
+	for (;;) {
+		wait_result = wait_for_completion_interruptible_timeout(
+				&i2c_dev->complete, 500 * HZ / 1000);
+
+		if (wait_result == 0) {
+			dev_dbg(i2c_dev->dev, "wait timeout (tx)\n");
+			ret = -ETIMEDOUT;
+			break;
+		}
+
+		ret = wmt_check_status(i2c_dev);
+		if (ret)
+			break;
+
+		xfer_len++;
+
+		val = readw(i2c_dev->base + REG_CSR);
+		if ((val & CSR_RCV_ACK_MASK) == CSR_RCV_NOT_ACK) {
+			dev_dbg(i2c_dev->dev, "write RCV NACK error\n");
+			ret = -EIO;
+			break;
+		}
+
+		if (pmsg->len == 0) {
+			val = CR_TX_END | CR_CPU_RDY | CR_ENABLE;
+			writew(val, i2c_dev->base + REG_CR);
+			break;
+		}
+
+		if (pmsg->len > xfer_len) {
+			writew(pmsg->buf[xfer_len] & 0xFF, i2c_dev->base +
+								REG_CDR);
+			writew(CR_CPU_RDY | CR_ENABLE, i2c_dev->base + REG_CR);
+		} else if (pmsg->len == xfer_len) {
+			if (last != 1)
+				writew(CR_ENABLE, i2c_dev->base + REG_CR);
+			break;
+		} else {
+			dev_dbg(i2c_dev->dev, "unknown error (tx)\n");
+			ret = -EIO;
+			break;
+		}
+	}
+
+	return ret;
+}
+
+static int wmt_i2c_read(struct i2c_adapter *adap, struct i2c_msg *pmsg,
+						  int restart, int last)
+{
+	struct wmt_i2c_dev *i2c_dev = i2c_get_adapdata(adap);
+	u16 val;
+	u16 tcr_val;
+	int ret;
+	int wait_result;
+	u32 xfer_len = 0;
+
+	if (pmsg->len <= 0)
+		return -EINVAL;
+
+	if (restart == 0) {
+		ret = wmt_i2c_wait_bus_not_busy(i2c_dev);
+		if (ret < 0)
+			return ret;
+	}
+
+	val = readw(i2c_dev->base + REG_CR);
+	val &= ~CR_TX_END;
+	writew(val, i2c_dev->base + REG_CR);
+
+	val = readw(i2c_dev->base + REG_CR);
+	val &= ~CR_TX_NEXT_NO_ACK;
+	writew(val, i2c_dev->base + REG_CR);
+
+	if (restart == 0) {
+		val = readw(i2c_dev->base + REG_CR);
+		val |= CR_CPU_RDY;
+		writew(val, i2c_dev->base + REG_CR);
+	}
+
+	if (pmsg->len == 1) {
+		val = readw(i2c_dev->base + REG_CR);
+		val |= CR_TX_NEXT_NO_ACK;
+		writew(val, i2c_dev->base + REG_CR);
+	}
+
+	init_completion(&i2c_dev->complete);
+
+	if (i2c_dev->mode == I2C_MODE_STANDARD)
+		tcr_val = TCR_STANDARD_MODE;
+	else
+		tcr_val = TCR_FAST_MODE;
+
+	tcr_val |= TCR_MASTER_READ | (pmsg->addr & TCR_SLAVE_ADDR_MASK);
+
+	writew(tcr_val, i2c_dev->base + REG_TCR);
+
+	if (restart == 1) {
+		val = readw(i2c_dev->base + REG_CR);
+		val |= CR_CPU_RDY;
+		writew(val, i2c_dev->base + REG_CR);
+	}
+
+	ret = 0;
+
+	for (;;) {
+		wait_result = wait_for_completion_interruptible_timeout(
+				&i2c_dev->complete, 500 * HZ / 1000);
+
+		if (wait_result == 0) {
+			dev_dbg(i2c_dev->dev, "wait timeout (tx)\n");
+			ret = -ETIMEDOUT;
+			break;
+		}
+
+		ret = wmt_check_status(i2c_dev);
+		if (ret)
+			break;
+
+		pmsg->buf[xfer_len] = readw(i2c_dev->base + REG_CDR) >> 8;
+		xfer_len++;
+
+		if (pmsg->len > xfer_len) {
+			if (pmsg->len - 1 == xfer_len) {
+				val = readw(i2c_dev->base + REG_CR);
+				val |= (CR_TX_NEXT_NO_ACK | CR_CPU_RDY);
+				writew(val, i2c_dev->base + REG_CR);
+			} else {
+				val = readw(i2c_dev->base + REG_CR);
+				val |= CR_CPU_RDY;
+				writew(val, i2c_dev->base + REG_CR);
+			}
+		} else if (pmsg->len == xfer_len) {
+			break;
+		} else {
+			ret = -EIO;
+			break;
+		}
+	}
+
+	return ret;
+}
+
+static int wmt_i2c_xfer(struct i2c_adapter *adap,
+			struct i2c_msg msgs[],
+			int num)
+{
+	struct i2c_msg *pmsg;
+	int i;
+	int ret = 0;
+	int is_last;
+	int restart;
+
+	for (i = 0; ret >= 0 && i < num; i++) {
+		is_last = ((i + 1) == num);
+		restart = (i != 0);
+
+		pmsg = &msgs[i];
+		if (pmsg->flags & I2C_M_NOSTART)
+			restart = 1;
+		if (pmsg->flags & I2C_M_RD)
+			ret = wmt_i2c_read(adap, pmsg, restart, is_last);
+		else
+			ret = wmt_i2c_write(adap, pmsg, restart, is_last);
+	}
+
+	if (ret < 0)
+		return ret;
+	else
+		return i;
+}
+
+static u32 wmt_i2c_func(struct i2c_adapter *adap)
+{
+	return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+static const struct i2c_algorithm wmt_i2c_algo = {
+	.master_xfer	= wmt_i2c_xfer,
+	.functionality	= wmt_i2c_func,
+};
+
+static irqreturn_t wmt_i2c_isr(int irq, void *data)
+{
+	struct wmt_i2c_dev *i2c_dev = data;
+
+	/* save the status and write-clear it */
+	i2c_dev->cmd_status = readw(i2c_dev->base + REG_ISR);
+	writew(i2c_dev->cmd_status, i2c_dev->base + REG_ISR);
+
+	complete(&i2c_dev->complete);
+
+	return IRQ_HANDLED;
+}
+
+/*
+ * Due to a lack of pinmux functionality we need to manually configure
+ * the GPIO pullup. Once pinmux is implemented, this function should be
+ * removed. Pullup's are not available on vt8500 or wm8505 so we skip.
+ */
+static int wmt_i2c_setup_gpio(struct wmt_i2c_dev *i2c_dev)
+{
+	struct device_node *np;
+	void __iomem *gpio_base;
+	int nr = i2c_dev->adapter.nr;
+	u8 val;
+	u8 mask;
+
+	/* VT8500 - has external pullups so no configuration required. */
+	if (of_machine_is_compatible("via,vt8500"))
+		return 0;
+
+	/*
+	 * WM8505 - has external pullups but we need to config the alt func
+	 * WM8650 - has programmable pullups + alt func
+	 * WM8850 - has programmable pullups + alt func
+	 */
+	if (of_machine_is_compatible("wm,wm8505")) {
+		np = of_find_compatible_node(NULL, NULL, "wm,wm8505-gpio");
+		if (!np) {
+			dev_err(i2c_dev->dev, "GPIO required for WM8505 i2c\n");
+			return -EINVAL;
+		}
+	} else if (of_machine_is_compatible("wm,wm8650") ||
+		   of_machine_is_compatible("wm,wm8850")) {
+		np = of_find_compatible_node(NULL, NULL, "wm,wm8650-gpio");
+		if (!np) {
+			dev_err(i2c_dev->dev, "GPIO required for WM8650 i2c\n");
+			return -EINVAL;
+		}
+	} else {
+		/* If we don't know what machine, assume its configured */
+		dev_warn(i2c_dev->dev, "unrecognised machine\n");
+		return 0;
+	}
+
+	gpio_base = of_iomap(np, 0);
+	if (!gpio_base) {
+		dev_err(i2c_dev->dev, "failed to map gpio memory\n");
+		return -ENOMEM;
+	}
+
+	if (of_machine_is_compatible("wm,wm8505")) {
+		if (nr == 0) {
+			/* Set gpio pins to alt function */
+			val = readb(gpio_base + 0x500);
+			writeb(val & 0xFC, gpio_base + 0x500);
+		}
+	} else if (of_machine_is_compatible("wm,wm8650")) {
+		if (nr == 0) {
+			/* set gpio pins to alt function */
+			val = readb(gpio_base + 0x55);
+			val &= ~(BIT(0) | BIT(1));
+			writeb(val, gpio_base + 0x55);
+
+			/* enable pull */
+			val = readb(gpio_base + 0x495);
+			val |= (BIT(0) | BIT(1));
+			writeb(val, gpio_base + 0x495);
+
+			/* set pulled-up */
+			val = readb(gpio_base + 0x4D5);
+			val |= (BIT(0) | BIT(1));
+			writeb(val, gpio_base + 0x4D5);
+		} else if (nr == 1) {
+			/* set gpio pins to alt function */
+			val = readb(gpio_base + 0x49);
+			val &= ~(BIT(4) | BIT(5));
+			writeb(val, gpio_base + 0x49);
+
+			/* enable pull */
+			val = readb(gpio_base + 0x489);
+			val |= (BIT(4) | BIT(5));
+			writeb(val, gpio_base + 0x489);
+
+			/* set pulled-up */
+			val = readb(gpio_base + 0x4C9);
+			val |= (BIT(4) | BIT(5));
+			writeb(val, gpio_base + 0x4C9);
+		}
+	} else if (of_machine_is_compatible("wm,wm8850")) {
+		if ((nr >= 0) && (nr <= 2)) {
+			mask = 0x03 << (nr << 1);
+	
+			/* set gpio pins to alt function */
+			val = readb(gpio_base + 0x55);
+			val &= ~mask;
+			writeb(val, gpio_base + 0x55);
+
+			/* enable pull */
+			val = readb(gpio_base + 0x495);
+			val |= mask;
+			writeb(val, gpio_base + 0x495);
+
+			/* set pulled-up */
+			val = readb(gpio_base + 0x4D5);
+			val |= mask;
+			writeb(val, gpio_base + 0x4D5);
+		}
+	}
+
+
+	iounmap(gpio_base);
+
+	return 0;
+}
+
+static int wmt_i2c_reset_hardware(struct wmt_i2c_dev *i2c_dev)
+{
+	int err;
+
+	err = clk_prepare_enable(i2c_dev->clk);
+	if (err) {
+		dev_err(i2c_dev->dev, "failed to enable clock\n");
+		return err;
+	}
+
+	err = clk_set_rate(i2c_dev->clk, 20000000);
+	if (err) {
+		dev_err(i2c_dev->dev, "failed to set clock = 20Mhz\n");
+		return err;
+	}
+
+	err = wmt_i2c_setup_gpio(i2c_dev);
+	if (err)
+		return err;
+
+	writew(0, i2c_dev->base + REG_CR);
+	writew(12, i2c_dev->base + REG_MCR);
+	writew(ISR_WRITE_ALL, i2c_dev->base + REG_ISR);
+	writew(IMR_ENABLE_ALL, i2c_dev->base + REG_IMR);
+	writew(CR_ENABLE, i2c_dev->base + REG_CR);
+	readw(i2c_dev->base + REG_CSR);		/* read clear */
+	writew(ISR_WRITE_ALL, i2c_dev->base + REG_ISR);
+
+	if (i2c_dev->mode == I2C_MODE_STANDARD)
+		writew(0x8064, i2c_dev->base + REG_TR);
+	else
+		writew(0x8019, i2c_dev->base + REG_TR);
+
+	return 0;
+}
+
+static int wmt_i2c_probe(struct platform_device *pdev)
+{
+	struct device_node	*np = pdev->dev.of_node;
+	struct wmt_i2c_dev	*i2c_dev;
+	struct i2c_adapter	*adap;
+	struct resource		*res;
+	int err;
+	u32 clk_rate;
+
+	if (!np) {
+		dev_err(&pdev->dev, "device node not found\n");
+		return -ENODEV;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(&pdev->dev, "no memory resource defined\n");
+		return -ENODEV;
+	}
+
+	i2c_dev = devm_kzalloc(&pdev->dev, sizeof(*i2c_dev), GFP_KERNEL);
+	if (!i2c_dev) {
+		dev_err(&pdev->dev, "device memory allocation failed\n");
+		return -ENOMEM;
+	}
+
+	i2c_dev->base = devm_request_and_ioremap(&pdev->dev, res);
+	if (!i2c_dev->base) {
+		dev_err(&pdev->dev, "memory region unavailable\n");
+		return -ENOMEM;
+	}
+
+	i2c_dev->irq = irq_of_parse_and_map(np, 0);
+	if (!i2c_dev->irq) {
+		dev_err(&pdev->dev, "irq missing or invalid\n");
+		return -EINVAL;
+	}
+
+	i2c_dev->clk = of_clk_get(np, 0);
+	if (IS_ERR(i2c_dev->clk)) {
+		dev_err(&pdev->dev, "unable to request clock\n");
+		return PTR_ERR(i2c_dev->clk);
+	}
+
+	i2c_dev->mode = I2C_MODE_STANDARD;
+	err = of_property_read_u32(np, "clock-frequency", &clk_rate);
+	if ((!err) && (clk_rate == 400000))
+		i2c_dev->mode = I2C_MODE_FAST;
+
+	i2c_dev->dev = &pdev->dev;
+
+	err = devm_request_irq(&pdev->dev, i2c_dev->irq, wmt_i2c_isr, 0,
+							"i2c", i2c_dev);
+	if (err) {
+		dev_err(&pdev->dev, "failed to request irq %i\n", i2c_dev->irq);
+		return err;
+	}
+
+	adap = &i2c_dev->adapter;
+	i2c_set_adapdata(adap, i2c_dev);
+	strlcpy(adap->name, "WMT I2C adapter", sizeof(adap->name));
+	adap->owner		= THIS_MODULE;
+	adap->class		= I2C_CLASS_HWMON;
+	adap->algo		= &wmt_i2c_algo;
+	adap->dev.parent	= &pdev->dev;
+	adap->dev.of_node	= pdev->dev.of_node;
+	adap->nr		= of_alias_get_id(pdev->dev.of_node, "i2c");
+
+	err = wmt_i2c_reset_hardware(i2c_dev);
+	if (err) {
+		dev_err(&pdev->dev, "error initializing hardware\n");
+		return err;
+	}
+
+	if (adap->nr < 0)
+		err = i2c_add_adapter(adap);
+	else
+		err = i2c_add_numbered_adapter(adap);
+
+	if (err) {
+		dev_err(&pdev->dev, "failed to add adapter\n");
+		return err;
+	}
+
+	platform_set_drvdata(pdev, i2c_dev);
+
+	of_i2c_register_devices(adap);
+
+	return 0;
+}
+
+static int wmt_i2c_remove(struct platform_device *pdev)
+{
+	struct wmt_i2c_dev *i2c_dev = platform_get_drvdata(pdev);
+
+	i2c_del_adapter(&i2c_dev->adapter);
+
+	return 0;
+}
+
+static struct of_device_id wmt_i2c_dt_ids[] = {
+	{ .compatible = "wm,wm8505-i2c" },
+	{ /* Sentinel */ },
+};
+
+static struct platform_driver wmt_i2c_driver = {
+	.probe		= wmt_i2c_probe,
+	.remove		= wmt_i2c_remove,
+	.driver		= {
+		.name	= "wmt-i2c",
+		.owner	= THIS_MODULE,
+		.of_match_table = wmt_i2c_dt_ids,
+	},
+};
+
+module_platform_driver(wmt_i2c_driver);
+
+MODULE_DESCRIPTION("Wondermedia I2C master-mode bus adapter");
+MODULE_AUTHOR("Tony Prisk <linux@prisktech.co.nz>");
+MODULE_LICENSE("GPL v2");
+MODULE_DEVICE_TABLE(of, wmt_i2c_dt_ids);
-- 
1.7.9.5

^ permalink raw reply related

* [PATCH] i2c: vt8500: Add support for Wondermedia I2C master-mode
From: Tony Prisk @ 2012-12-28  1:29 UTC (permalink / raw)
  To: Ben Dooks, Wolfram Sang
  Cc: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-i2c-u79uwXL29TY76Z2rM5mHXA, Tony Prisk

This patch adds support for the I2C controller found on Wondermedia
SoCs.

Due to the lack of pinmux support, GPIO pin alternate functions are
configured by machine's compatible property, as are pullups.

Signed-off-by: Tony Prisk <linux-ci5G2KO2hbZ+pU9mqzGVBQ@public.gmane.org>
---
 .../devicetree/bindings/i2c/i2c-vt8500.txt         |   24 +
 drivers/i2c/busses/Kconfig                         |    7 +
 drivers/i2c/busses/Makefile                        |    1 +
 drivers/i2c/busses/i2c-wmt.c                       |  636 ++++++++++++++++++++
 4 files changed, 668 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/i2c/i2c-vt8500.txt
 create mode 100644 drivers/i2c/busses/i2c-wmt.c

diff --git a/Documentation/devicetree/bindings/i2c/i2c-vt8500.txt b/Documentation/devicetree/bindings/i2c/i2c-vt8500.txt
new file mode 100644
index 0000000..94a425e
--- /dev/null
+++ b/Documentation/devicetree/bindings/i2c/i2c-vt8500.txt
@@ -0,0 +1,24 @@
+* Wondermedia I2C Controller
+
+Required properties :
+
+ - compatible : should be "wm,wm8505-i2c"
+ - reg : Offset and length of the register set for the device
+ - interrupts : <IRQ> where IRQ is the interrupt number
+ - clocks : phandle to the I2C clock source
+
+Optional properties :
+
+ - clock-frequency : desired I2C bus clock frequency in Hz.
+	Valid values are 100000 and 400000.
+	Default to 100000 if not specified, or invalid value.
+
+Example :
+
+	i2c_0: i2c@d8280000 {
+		compatible = "wm,wm8505-i2c";
+		reg = <0xd8280000 0x1000>;
+		interrupts = <19>;
+		clocks = <&clki2c0>;
+		clock-frequency = <400000>;
+	};
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index bdca511..41270fa 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -700,6 +700,13 @@ config I2C_VERSATILE
 	  This driver can also be built as a module.  If so, the module
 	  will be called i2c-versatile.
 
+config I2C_WMT
+	tristate "Wondermedia I2C Controller support"
+	depends on ARCH_VT8500
+	help
+	  If you say yes to this option, support will be included for the
+	  I2C controllers found in Wondermedia SoCs.
+
 config I2C_OCTEON
 	tristate "Cavium OCTEON I2C bus support"
 	depends on CPU_CAVIUM_OCTEON
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index 6181f3f..dce6299 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -69,6 +69,7 @@ obj-$(CONFIG_I2C_SIRF)		+= i2c-sirf.o
 obj-$(CONFIG_I2C_STU300)	+= i2c-stu300.o
 obj-$(CONFIG_I2C_TEGRA)		+= i2c-tegra.o
 obj-$(CONFIG_I2C_VERSATILE)	+= i2c-versatile.o
+obj-$(CONFIG_I2C_WMT)		+= i2c-wmt.o
 obj-$(CONFIG_I2C_OCTEON)	+= i2c-octeon.o
 obj-$(CONFIG_I2C_XILINX)	+= i2c-xiic.o
 obj-$(CONFIG_I2C_XLR)		+= i2c-xlr.o
diff --git a/drivers/i2c/busses/i2c-wmt.c b/drivers/i2c/busses/i2c-wmt.c
new file mode 100644
index 0000000..07d86e3
--- /dev/null
+++ b/drivers/i2c/busses/i2c-wmt.c
@@ -0,0 +1,636 @@
+/*
+ *  Wondermedia I2C Master Mode Driver
+ *
+ *  Copyright (C) 2012 Tony Prisk <linux-ci5G2KO2hbZ+pU9mqzGVBQ@public.gmane.org>
+ *
+ *  Derived from GPL licensed source:
+ *  - Copyright (C) 2008 WonderMedia Technologies, Inc.
+ *
+ *  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
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_i2c.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+
+#define REG_CR		0x00
+#define REG_TCR		0x02
+#define REG_CSR		0x04
+#define REG_ISR		0x06
+#define REG_IMR		0x08
+#define REG_CDR		0x0A
+#define REG_TR		0x0C
+#define REG_MCR		0x0E
+#define REG_SLAVE_CR	0x10
+#define REG_SLAVE_SR	0x12
+#define REG_SLAVE_ISR	0x14
+#define REG_SLAVE_IMR	0x16
+#define REG_SLAVE_DR	0x18
+#define REG_SLAVE_TR	0x1A
+
+/* REG_CR Bit fields */
+#define CR_TX_NEXT_ACK		0x0000
+#define CR_ENABLE		0x0001
+#define CR_TX_NEXT_NO_ACK	0x0002
+#define CR_TX_END		0x0004
+#define CR_CPU_RDY		0x0008
+#define SLAV_MODE_SEL		0x8000
+
+/* REG_TCR Bit fields */
+#define TCR_STANDARD_MODE	0x0000
+#define TCR_MASTER_WRITE	0x0000
+#define TCR_HS_MODE		0x2000
+#define TCR_MASTER_READ		0x4000
+#define TCR_FAST_MODE		0x8000
+#define TCR_SLAVE_ADDR_MASK	0x007F
+
+/* REG_ISR Bit fields */
+#define ISR_NACK_ADDR		0x0001
+#define ISR_BYTE_END		0x0002
+#define ISR_SCL_TIMEOUT		0x0004
+#define ISR_WRITE_ALL		0x0007
+
+/* REG_IMR Bit fields */
+#define IMR_ENABLE_ALL		0x0007
+
+/* REG_CSR Bit fields */
+#define CSR_RCV_NOT_ACK		0x0001
+#define CSR_RCV_ACK_MASK	0x0001
+#define CSR_READY_MASK		0x0002
+
+#define I2C_MODE_STANDARD	0
+#define I2C_MODE_FAST		1
+
+
+
+struct wmt_i2c_dev {
+	struct i2c_adapter	adapter;
+	struct completion	complete;
+	struct device		*dev;
+	void __iomem		*base;
+	struct clk		*clk;
+	int			mode;
+	int			irq;
+	u16			cmd_status;
+};
+
+static int wmt_i2c_wait_bus_not_busy(struct wmt_i2c_dev *i2c_dev)
+{
+	u16 val;
+	int i;
+	int ret = 0;
+
+	for (i = 0; i < 10000000; i++) {
+		val = readw(i2c_dev->base + REG_CSR);
+		if (val & CSR_READY_MASK)
+			break;
+
+		udelay(1);
+	}
+	if (i >= 9999999)
+		ret = -EBUSY;
+
+	return ret;
+}
+
+static int wmt_check_status(struct wmt_i2c_dev *i2c_dev)
+{
+	int ret = 0;
+
+	if (i2c_dev->cmd_status & ISR_NACK_ADDR)
+		ret = -EIO;
+
+	if (i2c_dev->cmd_status & ISR_SCL_TIMEOUT)
+		ret = -ETIMEDOUT;
+
+	return ret;
+}
+
+static int wmt_i2c_write(struct i2c_adapter *adap, struct i2c_msg *pmsg,
+						   int restart, int last)
+{
+	struct wmt_i2c_dev *i2c_dev = i2c_get_adapdata(adap);
+	u16 val;
+	u16 tcr_val;
+	int ret;
+	int wait_result;
+	u32 xfer_len = 0;
+
+	if (pmsg->len < 0)
+		return -EINVAL;
+
+	if (restart == 0) {
+		ret = wmt_i2c_wait_bus_not_busy(i2c_dev);
+		if (ret < 0)
+			return ret;
+	}
+
+	if (pmsg->len == 0)
+		writew(0, i2c_dev->base + REG_CDR);
+	else
+		writew(pmsg->buf[0] & 0xFF, i2c_dev->base + REG_CDR);
+
+	if (restart == 0) {
+		val = readw(i2c_dev->base + REG_CR);
+		val &= ~CR_TX_END;
+		writew(val, i2c_dev->base + REG_CR);
+
+		val = readw(i2c_dev->base + REG_CR);
+		val |= CR_CPU_RDY;
+		writew(val, i2c_dev->base + REG_CR);
+	}
+
+	init_completion(&i2c_dev->complete);
+
+	if (i2c_dev->mode == I2C_MODE_STANDARD)
+		tcr_val = TCR_STANDARD_MODE;
+	else
+		tcr_val = TCR_FAST_MODE;
+
+	tcr_val |= (TCR_MASTER_WRITE | (pmsg->addr & TCR_SLAVE_ADDR_MASK));
+
+	writew(tcr_val, i2c_dev->base + REG_TCR);
+
+	if (restart == 1) {
+		val = readw(i2c_dev->base + REG_CR);
+		val |= CR_CPU_RDY;
+		writew(val, i2c_dev->base + REG_CR);
+	}
+
+	ret = 0;
+
+	for (;;) {
+		wait_result = wait_for_completion_interruptible_timeout(
+				&i2c_dev->complete, 500 * HZ / 1000);
+
+		if (wait_result == 0) {
+			dev_dbg(i2c_dev->dev, "wait timeout (tx)\n");
+			ret = -ETIMEDOUT;
+			break;
+		}
+
+		ret = wmt_check_status(i2c_dev);
+		if (ret)
+			break;
+
+		xfer_len++;
+
+		val = readw(i2c_dev->base + REG_CSR);
+		if ((val & CSR_RCV_ACK_MASK) == CSR_RCV_NOT_ACK) {
+			dev_dbg(i2c_dev->dev, "write RCV NACK error\n");
+			ret = -EIO;
+			break;
+		}
+
+		if (pmsg->len == 0) {
+			val = CR_TX_END | CR_CPU_RDY | CR_ENABLE;
+			writew(val, i2c_dev->base + REG_CR);
+			break;
+		}
+
+		if (pmsg->len > xfer_len) {
+			writew(pmsg->buf[xfer_len] & 0xFF, i2c_dev->base +
+								REG_CDR);
+			writew(CR_CPU_RDY | CR_ENABLE, i2c_dev->base + REG_CR);
+		} else if (pmsg->len == xfer_len) {
+			if (last != 1)
+				writew(CR_ENABLE, i2c_dev->base + REG_CR);
+			break;
+		} else {
+			dev_dbg(i2c_dev->dev, "unknown error (tx)\n");
+			ret = -EIO;
+			break;
+		}
+	}
+
+	return ret;
+}
+
+static int wmt_i2c_read(struct i2c_adapter *adap, struct i2c_msg *pmsg,
+						  int restart, int last)
+{
+	struct wmt_i2c_dev *i2c_dev = i2c_get_adapdata(adap);
+	u16 val;
+	u16 tcr_val;
+	int ret;
+	int wait_result;
+	u32 xfer_len = 0;
+
+	if (pmsg->len <= 0)
+		return -EINVAL;
+
+	if (restart == 0) {
+		ret = wmt_i2c_wait_bus_not_busy(i2c_dev);
+		if (ret < 0)
+			return ret;
+	}
+
+	val = readw(i2c_dev->base + REG_CR);
+	val &= ~CR_TX_END;
+	writew(val, i2c_dev->base + REG_CR);
+
+	val = readw(i2c_dev->base + REG_CR);
+	val &= ~CR_TX_NEXT_NO_ACK;
+	writew(val, i2c_dev->base + REG_CR);
+
+	if (restart == 0) {
+		val = readw(i2c_dev->base + REG_CR);
+		val |= CR_CPU_RDY;
+		writew(val, i2c_dev->base + REG_CR);
+	}
+
+	if (pmsg->len == 1) {
+		val = readw(i2c_dev->base + REG_CR);
+		val |= CR_TX_NEXT_NO_ACK;
+		writew(val, i2c_dev->base + REG_CR);
+	}
+
+	init_completion(&i2c_dev->complete);
+
+	if (i2c_dev->mode == I2C_MODE_STANDARD)
+		tcr_val = TCR_STANDARD_MODE;
+	else
+		tcr_val = TCR_FAST_MODE;
+
+	tcr_val |= TCR_MASTER_READ | (pmsg->addr & TCR_SLAVE_ADDR_MASK);
+
+	writew(tcr_val, i2c_dev->base + REG_TCR);
+
+	if (restart == 1) {
+		val = readw(i2c_dev->base + REG_CR);
+		val |= CR_CPU_RDY;
+		writew(val, i2c_dev->base + REG_CR);
+	}
+
+	ret = 0;
+
+	for (;;) {
+		wait_result = wait_for_completion_interruptible_timeout(
+				&i2c_dev->complete, 500 * HZ / 1000);
+
+		if (wait_result == 0) {
+			dev_dbg(i2c_dev->dev, "wait timeout (tx)\n");
+			ret = -ETIMEDOUT;
+			break;
+		}
+
+		ret = wmt_check_status(i2c_dev);
+		if (ret)
+			break;
+
+		pmsg->buf[xfer_len] = readw(i2c_dev->base + REG_CDR) >> 8;
+		xfer_len++;
+
+		if (pmsg->len > xfer_len) {
+			if (pmsg->len - 1 == xfer_len) {
+				val = readw(i2c_dev->base + REG_CR);
+				val |= (CR_TX_NEXT_NO_ACK | CR_CPU_RDY);
+				writew(val, i2c_dev->base + REG_CR);
+			} else {
+				val = readw(i2c_dev->base + REG_CR);
+				val |= CR_CPU_RDY;
+				writew(val, i2c_dev->base + REG_CR);
+			}
+		} else if (pmsg->len == xfer_len) {
+			break;
+		} else {
+			ret = -EIO;
+			break;
+		}
+	}
+
+	return ret;
+}
+
+static int wmt_i2c_xfer(struct i2c_adapter *adap,
+			struct i2c_msg msgs[],
+			int num)
+{
+	struct i2c_msg *pmsg;
+	int i;
+	int ret = 0;
+	int is_last;
+	int restart;
+
+	for (i = 0; ret >= 0 && i < num; i++) {
+		is_last = ((i + 1) == num);
+		restart = (i != 0);
+
+		pmsg = &msgs[i];
+		if (pmsg->flags & I2C_M_NOSTART)
+			restart = 1;
+		if (pmsg->flags & I2C_M_RD)
+			ret = wmt_i2c_read(adap, pmsg, restart, is_last);
+		else
+			ret = wmt_i2c_write(adap, pmsg, restart, is_last);
+	}
+
+	if (ret < 0)
+		return ret;
+	else
+		return i;
+}
+
+static u32 wmt_i2c_func(struct i2c_adapter *adap)
+{
+	return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+static const struct i2c_algorithm wmt_i2c_algo = {
+	.master_xfer	= wmt_i2c_xfer,
+	.functionality	= wmt_i2c_func,
+};
+
+static irqreturn_t wmt_i2c_isr(int irq, void *data)
+{
+	struct wmt_i2c_dev *i2c_dev = data;
+
+	/* save the status and write-clear it */
+	i2c_dev->cmd_status = readw(i2c_dev->base + REG_ISR);
+	writew(i2c_dev->cmd_status, i2c_dev->base + REG_ISR);
+
+	complete(&i2c_dev->complete);
+
+	return IRQ_HANDLED;
+}
+
+/*
+ * Due to a lack of pinmux functionality we need to manually configure
+ * the GPIO pullup. Once pinmux is implemented, this function should be
+ * removed. Pullup's are not available on vt8500 or wm8505 so we skip.
+ */
+static int wmt_i2c_setup_gpio(struct wmt_i2c_dev *i2c_dev)
+{
+	struct device_node *np;
+	void __iomem *gpio_base;
+	int nr = i2c_dev->adapter.nr;
+	u8 val;
+	u8 mask;
+
+	/* VT8500 - has external pullups so no configuration required. */
+	if (of_machine_is_compatible("via,vt8500"))
+		return 0;
+
+	/*
+	 * WM8505 - has external pullups but we need to config the alt func
+	 * WM8650 - has programmable pullups + alt func
+	 * WM8850 - has programmable pullups + alt func
+	 */
+	if (of_machine_is_compatible("wm,wm8505")) {
+		np = of_find_compatible_node(NULL, NULL, "wm,wm8505-gpio");
+		if (!np) {
+			dev_err(i2c_dev->dev, "GPIO required for WM8505 i2c\n");
+			return -EINVAL;
+		}
+	} else if (of_machine_is_compatible("wm,wm8650") ||
+		   of_machine_is_compatible("wm,wm8850")) {
+		np = of_find_compatible_node(NULL, NULL, "wm,wm8650-gpio");
+		if (!np) {
+			dev_err(i2c_dev->dev, "GPIO required for WM8650 i2c\n");
+			return -EINVAL;
+		}
+	} else {
+		/* If we don't know what machine, assume its configured */
+		dev_warn(i2c_dev->dev, "unrecognised machine\n");
+		return 0;
+	}
+
+	gpio_base = of_iomap(np, 0);
+	if (!gpio_base) {
+		dev_err(i2c_dev->dev, "failed to map gpio memory\n");
+		return -ENOMEM;
+	}
+
+	if (of_machine_is_compatible("wm,wm8505")) {
+		if (nr == 0) {
+			/* Set gpio pins to alt function */
+			val = readb(gpio_base + 0x500);
+			writeb(val & 0xFC, gpio_base + 0x500);
+		}
+	} else if (of_machine_is_compatible("wm,wm8650")) {
+		if (nr == 0) {
+			/* set gpio pins to alt function */
+			val = readb(gpio_base + 0x55);
+			val &= ~(BIT(0) | BIT(1));
+			writeb(val, gpio_base + 0x55);
+
+			/* enable pull */
+			val = readb(gpio_base + 0x495);
+			val |= (BIT(0) | BIT(1));
+			writeb(val, gpio_base + 0x495);
+
+			/* set pulled-up */
+			val = readb(gpio_base + 0x4D5);
+			val |= (BIT(0) | BIT(1));
+			writeb(val, gpio_base + 0x4D5);
+		} else if (nr == 1) {
+			/* set gpio pins to alt function */
+			val = readb(gpio_base + 0x49);
+			val &= ~(BIT(4) | BIT(5));
+			writeb(val, gpio_base + 0x49);
+
+			/* enable pull */
+			val = readb(gpio_base + 0x489);
+			val |= (BIT(4) | BIT(5));
+			writeb(val, gpio_base + 0x489);
+
+			/* set pulled-up */
+			val = readb(gpio_base + 0x4C9);
+			val |= (BIT(4) | BIT(5));
+			writeb(val, gpio_base + 0x4C9);
+		}
+	} else if (of_machine_is_compatible("wm,wm8850")) {
+		if ((nr >= 0) && (nr <= 2)) {
+			mask = 0x03 << (nr << 1);
+	
+			/* set gpio pins to alt function */
+			val = readb(gpio_base + 0x55);
+			val &= ~mask;
+			writeb(val, gpio_base + 0x55);
+
+			/* enable pull */
+			val = readb(gpio_base + 0x495);
+			val |= mask;
+			writeb(val, gpio_base + 0x495);
+
+			/* set pulled-up */
+			val = readb(gpio_base + 0x4D5);
+			val |= mask;
+			writeb(val, gpio_base + 0x4D5);
+		}
+	}
+
+
+	iounmap(gpio_base);
+
+	return 0;
+}
+
+static int wmt_i2c_reset_hardware(struct wmt_i2c_dev *i2c_dev)
+{
+	int err;
+
+	err = clk_prepare_enable(i2c_dev->clk);
+	if (err) {
+		dev_err(i2c_dev->dev, "failed to enable clock\n");
+		return err;
+	}
+
+	err = clk_set_rate(i2c_dev->clk, 20000000);
+	if (err) {
+		dev_err(i2c_dev->dev, "failed to set clock = 20Mhz\n");
+		return err;
+	}
+
+	err = wmt_i2c_setup_gpio(i2c_dev);
+	if (err)
+		return err;
+
+	writew(0, i2c_dev->base + REG_CR);
+	writew(12, i2c_dev->base + REG_MCR);
+	writew(ISR_WRITE_ALL, i2c_dev->base + REG_ISR);
+	writew(IMR_ENABLE_ALL, i2c_dev->base + REG_IMR);
+	writew(CR_ENABLE, i2c_dev->base + REG_CR);
+	readw(i2c_dev->base + REG_CSR);		/* read clear */
+	writew(ISR_WRITE_ALL, i2c_dev->base + REG_ISR);
+
+	if (i2c_dev->mode == I2C_MODE_STANDARD)
+		writew(0x8064, i2c_dev->base + REG_TR);
+	else
+		writew(0x8019, i2c_dev->base + REG_TR);
+
+	return 0;
+}
+
+static int wmt_i2c_probe(struct platform_device *pdev)
+{
+	struct device_node	*np = pdev->dev.of_node;
+	struct wmt_i2c_dev	*i2c_dev;
+	struct i2c_adapter	*adap;
+	struct resource		*res;
+	int err;
+	u32 clk_rate;
+
+	if (!np) {
+		dev_err(&pdev->dev, "device node not found\n");
+		return -ENODEV;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(&pdev->dev, "no memory resource defined\n");
+		return -ENODEV;
+	}
+
+	i2c_dev = devm_kzalloc(&pdev->dev, sizeof(*i2c_dev), GFP_KERNEL);
+	if (!i2c_dev) {
+		dev_err(&pdev->dev, "device memory allocation failed\n");
+		return -ENOMEM;
+	}
+
+	i2c_dev->base = devm_request_and_ioremap(&pdev->dev, res);
+	if (!i2c_dev->base) {
+		dev_err(&pdev->dev, "memory region unavailable\n");
+		return -ENOMEM;
+	}
+
+	i2c_dev->irq = irq_of_parse_and_map(np, 0);
+	if (!i2c_dev->irq) {
+		dev_err(&pdev->dev, "irq missing or invalid\n");
+		return -EINVAL;
+	}
+
+	i2c_dev->clk = of_clk_get(np, 0);
+	if (IS_ERR(i2c_dev->clk)) {
+		dev_err(&pdev->dev, "unable to request clock\n");
+		return PTR_ERR(i2c_dev->clk);
+	}
+
+	i2c_dev->mode = I2C_MODE_STANDARD;
+	err = of_property_read_u32(np, "clock-frequency", &clk_rate);
+	if ((!err) && (clk_rate == 400000))
+		i2c_dev->mode = I2C_MODE_FAST;
+
+	i2c_dev->dev = &pdev->dev;
+
+	err = devm_request_irq(&pdev->dev, i2c_dev->irq, wmt_i2c_isr, 0,
+							"i2c", i2c_dev);
+	if (err) {
+		dev_err(&pdev->dev, "failed to request irq %i\n", i2c_dev->irq);
+		return err;
+	}
+
+	adap = &i2c_dev->adapter;
+	i2c_set_adapdata(adap, i2c_dev);
+	strlcpy(adap->name, "WMT I2C adapter", sizeof(adap->name));
+	adap->owner		= THIS_MODULE;
+	adap->class		= I2C_CLASS_HWMON;
+	adap->algo		= &wmt_i2c_algo;
+	adap->dev.parent	= &pdev->dev;
+	adap->dev.of_node	= pdev->dev.of_node;
+	adap->nr		= of_alias_get_id(pdev->dev.of_node, "i2c");
+
+	err = wmt_i2c_reset_hardware(i2c_dev);
+	if (err) {
+		dev_err(&pdev->dev, "error initializing hardware\n");
+		return err;
+	}
+
+	if (adap->nr < 0)
+		err = i2c_add_adapter(adap);
+	else
+		err = i2c_add_numbered_adapter(adap);
+
+	if (err) {
+		dev_err(&pdev->dev, "failed to add adapter\n");
+		return err;
+	}
+
+	platform_set_drvdata(pdev, i2c_dev);
+
+	of_i2c_register_devices(adap);
+
+	return 0;
+}
+
+static int wmt_i2c_remove(struct platform_device *pdev)
+{
+	struct wmt_i2c_dev *i2c_dev = platform_get_drvdata(pdev);
+
+	i2c_del_adapter(&i2c_dev->adapter);
+
+	return 0;
+}
+
+static struct of_device_id wmt_i2c_dt_ids[] = {
+	{ .compatible = "wm,wm8505-i2c" },
+	{ /* Sentinel */ },
+};
+
+static struct platform_driver wmt_i2c_driver = {
+	.probe		= wmt_i2c_probe,
+	.remove		= wmt_i2c_remove,
+	.driver		= {
+		.name	= "wmt-i2c",
+		.owner	= THIS_MODULE,
+		.of_match_table = wmt_i2c_dt_ids,
+	},
+};
+
+module_platform_driver(wmt_i2c_driver);
+
+MODULE_DESCRIPTION("Wondermedia I2C master-mode bus adapter");
+MODULE_AUTHOR("Tony Prisk <linux-ci5G2KO2hbZ+pU9mqzGVBQ@public.gmane.org>");
+MODULE_LICENSE("GPL v2");
+MODULE_DEVICE_TABLE(of, wmt_i2c_dt_ids);
-- 
1.7.9.5

^ permalink raw reply related

* [PATCH] clk: vt8500: Add support for WM8750/WM8850 PLL clocks
From: Tony Prisk @ 2012-12-28  1:24 UTC (permalink / raw)
  To: Mike Turquette; +Cc: linux-arm-kernel, linux-kernel, Tony Prisk

This patch adds support for the new PLL module found in WM8750 and
WM8850 SoCs.

Signed-off-by: Tony Prisk <linux@prisktech.co.nz>
---
 drivers/clk/clk-vt8500.c |  102 +++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 100 insertions(+), 2 deletions(-)

diff --git a/drivers/clk/clk-vt8500.c b/drivers/clk/clk-vt8500.c
index fe25570..d3fefa4 100644
--- a/drivers/clk/clk-vt8500.c
+++ b/drivers/clk/clk-vt8500.c
@@ -41,6 +41,7 @@ struct clk_device {
 
 #define PLL_TYPE_VT8500		0
 #define PLL_TYPE_WM8650		1
+#define PLL_TYPE_WM8750		2
 
 struct clk_pll {
 	struct clk_hw	hw;
@@ -298,6 +299,16 @@ static __init void vtwm_device_clk_init(struct device_node *node)
 #define WM8650_BITS_TO_VAL(m, d1, d2)					\
 				((d2 << 13) | (d1 << 10) | (m & 0x3FF))
 
+/* Helper macros for PLL_WM8750 */
+#define WM8750_PLL_MUL(x)	(((x >> 16) & 0xFF) + 1)
+#define WM8750_PLL_DIV(x)	((((x >> 8) & 1) + 1) * (1 << (x & 7)))
+
+#define WM8750_BITS_TO_FREQ(r, m, d1, d2)				\
+				(r * (m+1) / ((d1+1) * (1 << d2)))
+
+#define WM8750_BITS_TO_VAL(f, m, d1, d2)				\
+		((f << 24) | ((m - 1) << 16) | ((d1 - 1) << 8) | d2)
+
 
 static void vt8500_find_pll_bits(unsigned long rate, unsigned long parent_rate,
 				u32 *multiplier, u32 *prediv)
@@ -366,11 +377,82 @@ static void wm8650_find_pll_bits(unsigned long rate, unsigned long parent_rate,
 	*divisor2 = div2;
 }
 
+static u32 wm8750_get_filter(u32 parent_rate, u32 divisor1)
+{
+	/* calculate frequency (MHz) after pre-divisor */
+	u32 freq = (parent_rate / 1000000) / (divisor1 + 1);
+
+	if ((freq < 10) || (freq > 200))
+		pr_warn("%s: PLL recommended input frequency 10..200Mhz (requested %d Mhz)\n",
+				__func__, freq);
+
+	if (freq >= 166)
+		return 7;
+	else if (freq >= 104)
+		return 6;
+	else if (freq >= 65)
+		return 5;
+	else if (freq >= 42)
+		return 4;
+	else if (freq >= 26)
+		return 3;
+	else if (freq >= 16)
+		return 2;
+	else if (freq >= 10)
+		return 1;
+
+	return 0;
+}
+
+static void wm8750_find_pll_bits(unsigned long rate, unsigned long parent_rate,
+				u32 *filter, u32 *multiplier, u32 *divisor1, u32 *divisor2)
+{
+	u32 mul, div1, div2;
+	u32 best_mul, best_div1, best_div2;
+	unsigned long tclk, rate_err, best_err;
+
+	best_err = (unsigned long)-1;
+
+	/* Find the closest match (lower or equal to requested) */
+	for (div1 = 1; div1 >= 0; div1--)
+		for (div2 = 7; div2 >= 0; div2--)
+			for (mul = 0; mul <= 255; mul++) {
+				tclk = parent_rate * (mul + 1) / ((div1 + 1) * (1 << div2));
+				if (tclk > rate)
+					continue;
+				/* error will always be +ve */
+				rate_err = rate - tclk;
+				if (rate_err == 0) {
+					*filter = wm8750_get_filter(parent_rate, div1);
+					*multiplier = mul;
+					*divisor1 = div1;
+					*divisor2 = div2;
+					return;
+				}
+
+				if (rate_err < best_err) {
+					best_err = rate_err;
+					best_mul = mul;
+					best_div1 = div1;
+					best_div2 = div2;
+				}
+			}
+
+	/* if we got here, it wasn't an exact match */
+	pr_warn("%s: requested rate %lu, found rate %lu\n", __func__, rate,
+							rate - best_err);
+
+	*filter = wm8750_get_filter(parent_rate, best_div1);
+	*multiplier = best_mul;
+	*divisor1 = best_div1;
+	*divisor2 = best_div2;
+}
+
 static int vtwm_pll_set_rate(struct clk_hw *hw, unsigned long rate,
 				unsigned long parent_rate)
 {
 	struct clk_pll *pll = to_clk_pll(hw);
-	u32 mul, div1, div2;
+	u32 filter, mul, div1, div2;
 	u32 pll_val;
 	unsigned long flags = 0;
 
@@ -385,6 +467,9 @@ static int vtwm_pll_set_rate(struct clk_hw *hw, unsigned long rate,
 		wm8650_find_pll_bits(rate, parent_rate, &mul, &div1, &div2);
 		pll_val = WM8650_BITS_TO_VAL(mul, div1, div2);
 		break;
+	case PLL_TYPE_WM8750:
+		wm8750_find_pll_bits(rate, parent_rate, &filter, &mul, &div1, &div2);
+		pll_val = WM8750_BITS_TO_VAL(filter, mul, div1, div2);
 	default:
 		pr_err("%s: invalid pll type\n", __func__);
 		return 0;
@@ -405,7 +490,7 @@ static long vtwm_pll_round_rate(struct clk_hw *hw, unsigned long rate,
 				unsigned long *prate)
 {
 	struct clk_pll *pll = to_clk_pll(hw);
-	u32 mul, div1, div2;
+	u32 filter, mul, div1, div2;
 	long round_rate;
 
 	switch (pll->type) {
@@ -417,6 +502,9 @@ static long vtwm_pll_round_rate(struct clk_hw *hw, unsigned long rate,
 		wm8650_find_pll_bits(rate, *prate, &mul, &div1, &div2);
 		round_rate = WM8650_BITS_TO_FREQ(*prate, mul, div1, div2);
 		break;
+	case PLL_TYPE_WM8750:
+		wm8750_find_pll_bits(rate, *prate, &filter, &mul, &div1, &div2);
+		round_rate = WM8750_BITS_TO_FREQ(*prate, mul, div1, div2);
 	default:
 		round_rate = 0;
 	}
@@ -440,6 +528,10 @@ static unsigned long vtwm_pll_recalc_rate(struct clk_hw *hw,
 		pll_freq = parent_rate * WM8650_PLL_MUL(pll_val);
 		pll_freq /= WM8650_PLL_DIV(pll_val);
 		break;
+	case PLL_TYPE_WM8750:
+		pll_freq = parent_rate * WM8750_PLL_MUL(pll_val);
+		pll_freq /= WM8750_PLL_DIV(pll_val);
+		break;
 	default:
 		pll_freq = 0;
 	}
@@ -508,10 +600,16 @@ static void __init wm8650_pll_init(struct device_node *node)
 	vtwm_pll_clk_init(node, PLL_TYPE_WM8650);
 }
 
+static void __init wm8750_pll_init(struct device_node *node)
+{
+	vtwm_pll_clk_init(node, PLL_TYPE_WM8750);
+}
+
 static const __initconst struct of_device_id clk_match[] = {
 	{ .compatible = "fixed-clock", .data = of_fixed_clk_setup, },
 	{ .compatible = "via,vt8500-pll-clock", .data = vt8500_pll_init, },
 	{ .compatible = "wm,wm8650-pll-clock", .data = wm8650_pll_init, },
+	{ .compatible = "wm,wm8750-pll-clock", .data = wm8750_pll_init, },
 	{ .compatible = "via,vt8500-device-clock",
 					.data = vtwm_device_clk_init, },
 	{ /* sentinel */ }
-- 
1.7.9.5


^ permalink raw reply related

* [PATCH] clk: vt8500: Add support for WM8750/WM8850 PLL clocks
From: Tony Prisk @ 2012-12-28  1:24 UTC (permalink / raw)
  To: linux-arm-kernel

This patch adds support for the new PLL module found in WM8750 and
WM8850 SoCs.

Signed-off-by: Tony Prisk <linux@prisktech.co.nz>
---
 drivers/clk/clk-vt8500.c |  102 +++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 100 insertions(+), 2 deletions(-)

diff --git a/drivers/clk/clk-vt8500.c b/drivers/clk/clk-vt8500.c
index fe25570..d3fefa4 100644
--- a/drivers/clk/clk-vt8500.c
+++ b/drivers/clk/clk-vt8500.c
@@ -41,6 +41,7 @@ struct clk_device {
 
 #define PLL_TYPE_VT8500		0
 #define PLL_TYPE_WM8650		1
+#define PLL_TYPE_WM8750		2
 
 struct clk_pll {
 	struct clk_hw	hw;
@@ -298,6 +299,16 @@ static __init void vtwm_device_clk_init(struct device_node *node)
 #define WM8650_BITS_TO_VAL(m, d1, d2)					\
 				((d2 << 13) | (d1 << 10) | (m & 0x3FF))
 
+/* Helper macros for PLL_WM8750 */
+#define WM8750_PLL_MUL(x)	(((x >> 16) & 0xFF) + 1)
+#define WM8750_PLL_DIV(x)	((((x >> 8) & 1) + 1) * (1 << (x & 7)))
+
+#define WM8750_BITS_TO_FREQ(r, m, d1, d2)				\
+				(r * (m+1) / ((d1+1) * (1 << d2)))
+
+#define WM8750_BITS_TO_VAL(f, m, d1, d2)				\
+		((f << 24) | ((m - 1) << 16) | ((d1 - 1) << 8) | d2)
+
 
 static void vt8500_find_pll_bits(unsigned long rate, unsigned long parent_rate,
 				u32 *multiplier, u32 *prediv)
@@ -366,11 +377,82 @@ static void wm8650_find_pll_bits(unsigned long rate, unsigned long parent_rate,
 	*divisor2 = div2;
 }
 
+static u32 wm8750_get_filter(u32 parent_rate, u32 divisor1)
+{
+	/* calculate frequency (MHz) after pre-divisor */
+	u32 freq = (parent_rate / 1000000) / (divisor1 + 1);
+
+	if ((freq < 10) || (freq > 200))
+		pr_warn("%s: PLL recommended input frequency 10..200Mhz (requested %d Mhz)\n",
+				__func__, freq);
+
+	if (freq >= 166)
+		return 7;
+	else if (freq >= 104)
+		return 6;
+	else if (freq >= 65)
+		return 5;
+	else if (freq >= 42)
+		return 4;
+	else if (freq >= 26)
+		return 3;
+	else if (freq >= 16)
+		return 2;
+	else if (freq >= 10)
+		return 1;
+
+	return 0;
+}
+
+static void wm8750_find_pll_bits(unsigned long rate, unsigned long parent_rate,
+				u32 *filter, u32 *multiplier, u32 *divisor1, u32 *divisor2)
+{
+	u32 mul, div1, div2;
+	u32 best_mul, best_div1, best_div2;
+	unsigned long tclk, rate_err, best_err;
+
+	best_err = (unsigned long)-1;
+
+	/* Find the closest match (lower or equal to requested) */
+	for (div1 = 1; div1 >= 0; div1--)
+		for (div2 = 7; div2 >= 0; div2--)
+			for (mul = 0; mul <= 255; mul++) {
+				tclk = parent_rate * (mul + 1) / ((div1 + 1) * (1 << div2));
+				if (tclk > rate)
+					continue;
+				/* error will always be +ve */
+				rate_err = rate - tclk;
+				if (rate_err == 0) {
+					*filter = wm8750_get_filter(parent_rate, div1);
+					*multiplier = mul;
+					*divisor1 = div1;
+					*divisor2 = div2;
+					return;
+				}
+
+				if (rate_err < best_err) {
+					best_err = rate_err;
+					best_mul = mul;
+					best_div1 = div1;
+					best_div2 = div2;
+				}
+			}
+
+	/* if we got here, it wasn't an exact match */
+	pr_warn("%s: requested rate %lu, found rate %lu\n", __func__, rate,
+							rate - best_err);
+
+	*filter = wm8750_get_filter(parent_rate, best_div1);
+	*multiplier = best_mul;
+	*divisor1 = best_div1;
+	*divisor2 = best_div2;
+}
+
 static int vtwm_pll_set_rate(struct clk_hw *hw, unsigned long rate,
 				unsigned long parent_rate)
 {
 	struct clk_pll *pll = to_clk_pll(hw);
-	u32 mul, div1, div2;
+	u32 filter, mul, div1, div2;
 	u32 pll_val;
 	unsigned long flags = 0;
 
@@ -385,6 +467,9 @@ static int vtwm_pll_set_rate(struct clk_hw *hw, unsigned long rate,
 		wm8650_find_pll_bits(rate, parent_rate, &mul, &div1, &div2);
 		pll_val = WM8650_BITS_TO_VAL(mul, div1, div2);
 		break;
+	case PLL_TYPE_WM8750:
+		wm8750_find_pll_bits(rate, parent_rate, &filter, &mul, &div1, &div2);
+		pll_val = WM8750_BITS_TO_VAL(filter, mul, div1, div2);
 	default:
 		pr_err("%s: invalid pll type\n", __func__);
 		return 0;
@@ -405,7 +490,7 @@ static long vtwm_pll_round_rate(struct clk_hw *hw, unsigned long rate,
 				unsigned long *prate)
 {
 	struct clk_pll *pll = to_clk_pll(hw);
-	u32 mul, div1, div2;
+	u32 filter, mul, div1, div2;
 	long round_rate;
 
 	switch (pll->type) {
@@ -417,6 +502,9 @@ static long vtwm_pll_round_rate(struct clk_hw *hw, unsigned long rate,
 		wm8650_find_pll_bits(rate, *prate, &mul, &div1, &div2);
 		round_rate = WM8650_BITS_TO_FREQ(*prate, mul, div1, div2);
 		break;
+	case PLL_TYPE_WM8750:
+		wm8750_find_pll_bits(rate, *prate, &filter, &mul, &div1, &div2);
+		round_rate = WM8750_BITS_TO_FREQ(*prate, mul, div1, div2);
 	default:
 		round_rate = 0;
 	}
@@ -440,6 +528,10 @@ static unsigned long vtwm_pll_recalc_rate(struct clk_hw *hw,
 		pll_freq = parent_rate * WM8650_PLL_MUL(pll_val);
 		pll_freq /= WM8650_PLL_DIV(pll_val);
 		break;
+	case PLL_TYPE_WM8750:
+		pll_freq = parent_rate * WM8750_PLL_MUL(pll_val);
+		pll_freq /= WM8750_PLL_DIV(pll_val);
+		break;
 	default:
 		pll_freq = 0;
 	}
@@ -508,10 +600,16 @@ static void __init wm8650_pll_init(struct device_node *node)
 	vtwm_pll_clk_init(node, PLL_TYPE_WM8650);
 }
 
+static void __init wm8750_pll_init(struct device_node *node)
+{
+	vtwm_pll_clk_init(node, PLL_TYPE_WM8750);
+}
+
 static const __initconst struct of_device_id clk_match[] = {
 	{ .compatible = "fixed-clock", .data = of_fixed_clk_setup, },
 	{ .compatible = "via,vt8500-pll-clock", .data = vt8500_pll_init, },
 	{ .compatible = "wm,wm8650-pll-clock", .data = wm8650_pll_init, },
+	{ .compatible = "wm,wm8750-pll-clock", .data = wm8750_pll_init, },
 	{ .compatible = "via,vt8500-device-clock",
 					.data = vtwm_device_clk_init, },
 	{ /* sentinel */ }
-- 
1.7.9.5

^ permalink raw reply related

* Re: problems with dracut mounting subvols
From: dima @ 2012-12-28  1:13 UTC (permalink / raw)
  To: linux-btrfs Mailing list
In-Reply-To: <5FA2C0D0-740D-4425-A099-46382311E2A3@colorremedies.com>

> I've just tried putting usr in a subvol. Installation proceeds normally, no errors, but I'm dropped to a dracut prompt which indicates mount of /usr failed. dmesg follows:
>
>
> [   14.025215] systemd[1]: Starting dracut initqueue hook...
> [   14.077890] Btrfs loaded
> [   14.129987] device label fedora_f18v devid 1 transid 31 /dev/sda1
> [   14.313607] device label fedora_f18v devid 1 transid 31 /dev/sda1
> [   14.446523] systemd[1]: Started Show Plymouth Boot Screen.
> [^[[1;32m  OK  ^[[[   14.533662] btrfs: disk space caching is enabled
> 0m] Started Show Plymouth Boot Screen.
> [   14.534404] systemd[1]: Started Dispatch Password Requests to Console Directory Watch.
> [   14.534886] systemd[1]: Starting Forward Password Requests to Plymouth Directory Watch.
> [   14.535001] systemd[1]: Started Forward Password Requests to Plymouth Directory Watch.
> [   14.535067] systemd[1]: Starting Basic System.
> [^[[1;32m  OK  ^[[0m] Reached target Basic System.[   15.053733] systemd[1]: Reached target Basic System.
> [   15.094806] device label fedora_f18v devid 1 transid 31 /dev/sda1
> [   15.251736] btrfs: disk space caching is enabled
> [   15.430129] systemd[1]: Started dracut initqueue hook.
> [   15.574280] systemd[1]: Starting dracut pre-pivot and cleanup hook...
> [   15.735181] device label fedora_f18v devid 1 transid 31 /dev/sda1
> [   15.876950] btrfs: 'root' is not a valid subvolume
> dracut-pre-pivot[228]: Warning: Mounting /usr to /sysroot/usr failed
> dracut-pre-pivot[228]: Warning: *** Dropping you to a shell; the system will continue
> dracut-pre-pivot[228]: Warning: *** when you leave the shell.
> dracut-pre-pivot[228]: Warning:
> [^[[1;32m  OK  ^[[0m] Started Show Plymouth Boot Screen.
> [^[[1;32m  OK  ^[[0m] Reached target Basic System.
> dracut-pre-pivot[228]: Warning: Mounting /usr to /sysroot/usr failed
> dracut-pre-pivot[228]: Warning: *** Dropping you to a shell; the system will continue
> dracut-pre-pivot[228]: Warning: *** when you leave the shell.
> dracut-pre-pivot[228]: Warning:
> [   16.314849] systemd[1]: Starting Emergency Shell…
>
>
> After a bit of snooping I found the dracut fstab suspicious:
>
>
> dracut:/# cat /etc/fstab
> /dev/disk/by-uuid/64383cfe-c31d-4d25-97c4-4e6b7e788b26 /sysroot btrfs subvol=root,ro 0
> /dev/disk/by-uuid/64383cfe-c31d-4d25-97c4-4e6b7e788b26 /sysroot/usr btrfs subvol=usr,subvol=root,ro 1 2
>
>
> Two mount options, mounting two subvols to the same mount point? Seems problematic.  But even when I try what I think is correct from a dracut prompt:
>
>
> dracut:/# mount -o subvol=usr /dev/sda1 /sysroot/usr
> mount: /dev/sda1 is already mounted or /sysroot/usr busy
>         /dev/sda1 is already mounted on /sysroot
>
>
> The only sda device mounted at this time is the root subvol to /sysroot. So the additional question is if dracut maybe has a problem with multiple physical devices being mounted more than once, which is what happens when mounting multiple subvols? This is dracut-024-17.git20121220.fc18.
>
> Seems like maybe two bugs?



> /dev/disk/by-uuid/64383cfe-c31d-4d25-97c4-4e6b7e788b26 /sysroot/usr btrfs subvol=usr,subvol=root,ro 1 2

I'd say that the problem is definitely with this line having two 
subvolumes listed.
Maybe you should boot from a live CD, mount your subvolid=1 and check 
out what subvolumes you really have in there. And then re-write fstab 
manually.
If you have /sysroot subvolume, you can create /sysroot/usr as a child 
subvolume and you won't even need then to specify /sysroot/usr in your 
fstab because it will be mounted automatically when the parent subvolume 
gets mounted.

^ permalink raw reply

* Re: [PATCH V3 8/8] memcg: Document cgroup dirty/writeback memory statistics
From: Kamezawa Hiroyuki @ 2012-12-28  1:10 UTC (permalink / raw)
  To: Sha Zhengju
  Cc: linux-kernel, cgroups, linux-mm, mhocko, akpm, gthelen,
	fengguang.wu, glommer, Sha Zhengju
In-Reply-To: <1356456501-14818-1-git-send-email-handai.szj@taobao.com>

(2012/12/26 2:28), Sha Zhengju wrote:
> From: Sha Zhengju <handai.szj@taobao.com>
> 
> Signed-off-by: Sha Zhengju <handai.szj@taobao.com>

I don't think your words are bad but it may be better to sync with meminfo's text.

> ---
>   Documentation/cgroups/memory.txt |    2 ++
>   1 file changed, 2 insertions(+)
> 
> diff --git a/Documentation/cgroups/memory.txt b/Documentation/cgroups/memory.txt
> index addb1f1..2828164 100644
> --- a/Documentation/cgroups/memory.txt
> +++ b/Documentation/cgroups/memory.txt
> @@ -487,6 +487,8 @@ pgpgin		- # of charging events to the memory cgroup. The charging
>   pgpgout		- # of uncharging events to the memory cgroup. The uncharging
>   		event happens each time a page is unaccounted from the cgroup.
>   swap		- # of bytes of swap usage
> +dirty          - # of bytes of file cache that are not in sync with the disk copy.
> +writeback      - # of bytes of file/anon cache that are queued for syncing to disk.
>   inactive_anon	- # of bytes of anonymous memory and swap cache memory on
>   		LRU list.
>   active_anon	- # of bytes of anonymous and swap cache memory on active
> 

Documentation/filesystems/proc.txt

       Dirty: Memory which is waiting to get written back to the disk
   Writeback: Memory which is actively being written back to the disk

even if others are not ;(

Thanks,
-Kame






^ permalink raw reply

* Re: [PATCH V3 8/8] memcg: Document cgroup dirty/writeback memory statistics
From: Kamezawa Hiroyuki @ 2012-12-28  1:10 UTC (permalink / raw)
  To: Sha Zhengju
  Cc: linux-kernel, cgroups, linux-mm, mhocko, akpm, gthelen,
	fengguang.wu, glommer, Sha Zhengju
In-Reply-To: <1356456501-14818-1-git-send-email-handai.szj@taobao.com>

(2012/12/26 2:28), Sha Zhengju wrote:
> From: Sha Zhengju <handai.szj@taobao.com>
> 
> Signed-off-by: Sha Zhengju <handai.szj@taobao.com>

I don't think your words are bad but it may be better to sync with meminfo's text.

> ---
>   Documentation/cgroups/memory.txt |    2 ++
>   1 file changed, 2 insertions(+)
> 
> diff --git a/Documentation/cgroups/memory.txt b/Documentation/cgroups/memory.txt
> index addb1f1..2828164 100644
> --- a/Documentation/cgroups/memory.txt
> +++ b/Documentation/cgroups/memory.txt
> @@ -487,6 +487,8 @@ pgpgin		- # of charging events to the memory cgroup. The charging
>   pgpgout		- # of uncharging events to the memory cgroup. The uncharging
>   		event happens each time a page is unaccounted from the cgroup.
>   swap		- # of bytes of swap usage
> +dirty          - # of bytes of file cache that are not in sync with the disk copy.
> +writeback      - # of bytes of file/anon cache that are queued for syncing to disk.
>   inactive_anon	- # of bytes of anonymous memory and swap cache memory on
>   		LRU list.
>   active_anon	- # of bytes of anonymous and swap cache memory on active
> 

Documentation/filesystems/proc.txt

       Dirty: Memory which is waiting to get written back to the disk
   Writeback: Memory which is actively being written back to the disk

even if others are not ;(

Thanks,
-Kame





--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply

* Re: [PATCH V3 8/8] memcg: Document cgroup dirty/writeback memory statistics
From: Kamezawa Hiroyuki @ 2012-12-28  1:10 UTC (permalink / raw)
  To: Sha Zhengju
  Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	cgroups-u79uwXL29TY76Z2rM5mHXA, linux-mm-Bw31MaZKKs3YtjvyW6yDsg,
	mhocko-AlSwsSmVLrQ, akpm-de/tnXTf+JLsfHDXvbKv3WD2FQJk+8+b,
	gthelen-hpIqsD4AKlfQT0dZR+AlfA,
	fengguang.wu-ral2JQCrhuEAvxtiuMwx3w,
	glommer-bzQdu9zFT3WakBO8gow8eQ, Sha Zhengju
In-Reply-To: <1356456501-14818-1-git-send-email-handai.szj-3b8fjiQLQpfQT0dZR+AlfA@public.gmane.org>

(2012/12/26 2:28), Sha Zhengju wrote:
> From: Sha Zhengju <handai.szj-3b8fjiQLQpfQT0dZR+AlfA@public.gmane.org>
> 
> Signed-off-by: Sha Zhengju <handai.szj-3b8fjiQLQpfQT0dZR+AlfA@public.gmane.org>

I don't think your words are bad but it may be better to sync with meminfo's text.

> ---
>   Documentation/cgroups/memory.txt |    2 ++
>   1 file changed, 2 insertions(+)
> 
> diff --git a/Documentation/cgroups/memory.txt b/Documentation/cgroups/memory.txt
> index addb1f1..2828164 100644
> --- a/Documentation/cgroups/memory.txt
> +++ b/Documentation/cgroups/memory.txt
> @@ -487,6 +487,8 @@ pgpgin		- # of charging events to the memory cgroup. The charging
>   pgpgout		- # of uncharging events to the memory cgroup. The uncharging
>   		event happens each time a page is unaccounted from the cgroup.
>   swap		- # of bytes of swap usage
> +dirty          - # of bytes of file cache that are not in sync with the disk copy.
> +writeback      - # of bytes of file/anon cache that are queued for syncing to disk.
>   inactive_anon	- # of bytes of anonymous memory and swap cache memory on
>   		LRU list.
>   active_anon	- # of bytes of anonymous and swap cache memory on active
> 

Documentation/filesystems/proc.txt

       Dirty: Memory which is waiting to get written back to the disk
   Writeback: Memory which is actively being written back to the disk

even if others are not ;(

Thanks,
-Kame





^ permalink raw reply

* Re: [PATCH] x86, realmode: set real_mode permissions early
From: H. Peter Anvin @ 2012-12-28  1:09 UTC (permalink / raw)
  To: Yinghai Lu; +Cc: Borislav Petkov, LKML
In-Reply-To: <CAE9FiQV2+AaGjyQBD=f8kKWDucbcnrk_UNHPi3Y=HtQCuYcYAQ@mail.gmail.com>

Seems ok.

Yinghai Lu <yinghai@kernel.org> wrote:

>On Thu, Dec 27, 2012 at 4:01 PM, H. Peter Anvin <hpa@zytor.com> wrote:
>> On 12/27/2012 03:56 PM, Yinghai Lu wrote:
>>>>
>>>>
>>>> I'm looking at this patch in the -v8 branch: you need to update the
>>>> comment above that function to say that trampoline code needs to be
>>>> marked X now earlier.
>>>
>>>
>>> Not sure if we really need to add such comment in the code.
>>>
>>> Peter, what is your idea about this?
>>>
>>
>> It would be good to note why the early_initcall is the right phase.
>>
>
>How about:
>
>---
>Trampoline will be executed by APs with SMP,
>So we need to set it EXEC in do_pre_smp_initcalls and it is
>early_initcall.
>---

-- 
Sent from my mobile phone. Please excuse brevity and lack of formatting.

^ permalink raw reply

* Re: Bad magic number and
From: Mulyadi Santosa @ 2012-12-28  1:07 UTC (permalink / raw)
  To: sham pavman; +Cc: kernelnewbies, linux-kernel
In-Reply-To: <CAD0Gp_YYu+LQr1rKResm_v3pYd+1M5G9kee7e4RJ1TUML7kq_g@mail.gmail.com>

Hi...

On Wed, Dec 26, 2012 at 12:19 PM, sham pavman <shampavman.cg@gmail.com> wrote:
> <SNIP>
>  mkfs -t ext4 /dev/sdb
> mke2fs 1.42 (29-Nov-2011)

try using mkfs.ext4 instead....

-- 
regards,

Mulyadi Santosa
Freelance Linux trainer and consultant

blog: the-hydra.blogspot.com
training: mulyaditraining.blogspot.com

^ permalink raw reply

* Bad magic number and
From: Mulyadi Santosa @ 2012-12-28  1:07 UTC (permalink / raw)
  To: kernelnewbies
In-Reply-To: <CAD0Gp_YYu+LQr1rKResm_v3pYd+1M5G9kee7e4RJ1TUML7kq_g@mail.gmail.com>

Hi...

On Wed, Dec 26, 2012 at 12:19 PM, sham pavman <shampavman.cg@gmail.com> wrote:
> <SNIP>
>  mkfs -t ext4 /dev/sdb
> mke2fs 1.42 (29-Nov-2011)

try using mkfs.ext4 instead....

-- 
regards,

Mulyadi Santosa
Freelance Linux trainer and consultant

blog: the-hydra.blogspot.com
training: mulyaditraining.blogspot.com

^ permalink raw reply

* Re: [PATCH V3 7/8] memcg: disable memcg page stat accounting code when not in use
From: Kamezawa Hiroyuki @ 2012-12-28  1:06 UTC (permalink / raw)
  To: Sha Zhengju
  Cc: linux-kernel, cgroups, linux-mm, mhocko, akpm, gthelen,
	fengguang.wu, glommer, Sha Zhengju
In-Reply-To: <1356456477-14780-1-git-send-email-handai.szj@taobao.com>

(2012/12/26 2:27), Sha Zhengju wrote:
> From: Sha Zhengju <handai.szj@taobao.com>
> 
> It's inspired by a similar optimization from Glauber Costa
> (memcg: make it suck faster; https://lkml.org/lkml/2012/9/25/154).
> Here we use jump label to patch the memcg page stat accounting code
> in or out when not used. when the first non-root memcg comes to
> life the code is patching in otherwise it is out.
> 
> Signed-off-by: Sha Zhengju <handai.szj@taobao.com>

Acked-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>



^ permalink raw reply

* Re: [PATCH V3 7/8] memcg: disable memcg page stat accounting code when not in use
From: Kamezawa Hiroyuki @ 2012-12-28  1:06 UTC (permalink / raw)
  To: Sha Zhengju
  Cc: linux-kernel, cgroups, linux-mm, mhocko, akpm, gthelen,
	fengguang.wu, glommer, Sha Zhengju
In-Reply-To: <1356456477-14780-1-git-send-email-handai.szj@taobao.com>

(2012/12/26 2:27), Sha Zhengju wrote:
> From: Sha Zhengju <handai.szj@taobao.com>
> 
> It's inspired by a similar optimization from Glauber Costa
> (memcg: make it suck faster; https://lkml.org/lkml/2012/9/25/154).
> Here we use jump label to patch the memcg page stat accounting code
> in or out when not used. when the first non-root memcg comes to
> life the code is patching in otherwise it is out.
> 
> Signed-off-by: Sha Zhengju <handai.szj@taobao.com>

Acked-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>


--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply

* Re: [PATCH V3 7/8] memcg: disable memcg page stat accounting code when not in use
From: Kamezawa Hiroyuki @ 2012-12-28  1:06 UTC (permalink / raw)
  To: Sha Zhengju
  Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	cgroups-u79uwXL29TY76Z2rM5mHXA, linux-mm-Bw31MaZKKs3YtjvyW6yDsg,
	mhocko-AlSwsSmVLrQ, akpm-de/tnXTf+JLsfHDXvbKv3WD2FQJk+8+b,
	gthelen-hpIqsD4AKlfQT0dZR+AlfA,
	fengguang.wu-ral2JQCrhuEAvxtiuMwx3w,
	glommer-bzQdu9zFT3WakBO8gow8eQ, Sha Zhengju
In-Reply-To: <1356456477-14780-1-git-send-email-handai.szj-3b8fjiQLQpfQT0dZR+AlfA@public.gmane.org>

(2012/12/26 2:27), Sha Zhengju wrote:
> From: Sha Zhengju <handai.szj-3b8fjiQLQpfQT0dZR+AlfA@public.gmane.org>
> 
> It's inspired by a similar optimization from Glauber Costa
> (memcg: make it suck faster; https://lkml.org/lkml/2012/9/25/154).
> Here we use jump label to patch the memcg page stat accounting code
> in or out when not used. when the first non-root memcg comes to
> life the code is patching in otherwise it is out.
> 
> Signed-off-by: Sha Zhengju <handai.szj-3b8fjiQLQpfQT0dZR+AlfA@public.gmane.org>

Acked-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu-+CUm20s59erQFUHtdCDX3A@public.gmane.org>


^ permalink raw reply

* Re: [PATCH V3 6/8] memcg: Don't account root_mem_cgroup page statistics
From: Kamezawa Hiroyuki @ 2012-12-28  1:04 UTC (permalink / raw)
  To: Sha Zhengju
  Cc: linux-kernel, cgroups, linux-mm, mhocko, akpm, gthelen,
	fengguang.wu, glommer, Sha Zhengju
In-Reply-To: <1356456447-14740-1-git-send-email-handai.szj@taobao.com>

(2012/12/26 2:27), Sha Zhengju wrote:
> From: Sha Zhengju <handai.szj@taobao.com>
> 
> If memcg is enabled and no non-root memcg exists, all allocated pages
> belongs to root_mem_cgroup and go through root memcg statistics routines
> which brings some overheads. So for the sake of performance, we can give
> up accounting stats of root memcg for MEM_CGROUP_STAT_FILE_MAPPED/FILE_DIRTY
> /WRITEBACK and instead we pay special attention while showing root
> memcg numbers in memcg_stat_show(): as we don't account root memcg stats
> anymore, the root_mem_cgroup->stat numbers are actually 0. But because of
> hierachy, figures of root_mem_cgroup may just represent numbers of pages
> used by its own tasks(not belonging to any other child cgroup). So here we
> fake these root numbers by using stats of global state and all other memcg.
> That is for root memcg:
> 	nr(MEM_CGROUP_STAT_FILE_MAPPED) = global_page_state(NR_FILE_MAPPED) -
>                                sum_of_all_memcg(MEM_CGROUP_STAT_FILE_MAPPED);
> Dirty/Writeback pages accounting are in the similar way.
> 
> Signed-off-by: Sha Zhengju <handai.szj@taobao.com>

isn't it better to use mem_cgroup_is_root() call rather than
direct comparison (memcg == root_mem_cgroup) ?

Anyway, Ack to this approach. 

Thanks,
-Kame


^ permalink raw reply

* Re: Linux 3.8-rc1 (mei driver BROKEN)
From: Sedat Dilek @ 2012-12-28  1:04 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: LKML, Greg Kroah-Hartman, Arnd Bergmann, khoroshilov

Hi Linus,

I am not sure if Greg is on holidays as his char-misc GIT tree didn't
get updated the last days.

The MEI driver causes here a serious regression requiring a hard cold
start of the machine I am working on.
A fix "mei: fix mismatch in mutex unlock-lock in mei_amthif_read()"
was already sent to LKML [1] and fixes the issue for me.

Can you push that a bit?
Feel free to add any credits.

Regards,
- Sedat -

[1] https://patchwork.kernel.org/patch/1904801/

^ permalink raw reply

* Re: [PATCH V3 6/8] memcg: Don't account root_mem_cgroup page statistics
From: Kamezawa Hiroyuki @ 2012-12-28  1:04 UTC (permalink / raw)
  To: Sha Zhengju
  Cc: linux-kernel, cgroups, linux-mm, mhocko, akpm, gthelen,
	fengguang.wu, glommer, Sha Zhengju
In-Reply-To: <1356456447-14740-1-git-send-email-handai.szj@taobao.com>

(2012/12/26 2:27), Sha Zhengju wrote:
> From: Sha Zhengju <handai.szj@taobao.com>
> 
> If memcg is enabled and no non-root memcg exists, all allocated pages
> belongs to root_mem_cgroup and go through root memcg statistics routines
> which brings some overheads. So for the sake of performance, we can give
> up accounting stats of root memcg for MEM_CGROUP_STAT_FILE_MAPPED/FILE_DIRTY
> /WRITEBACK and instead we pay special attention while showing root
> memcg numbers in memcg_stat_show(): as we don't account root memcg stats
> anymore, the root_mem_cgroup->stat numbers are actually 0. But because of
> hierachy, figures of root_mem_cgroup may just represent numbers of pages
> used by its own tasks(not belonging to any other child cgroup). So here we
> fake these root numbers by using stats of global state and all other memcg.
> That is for root memcg:
> 	nr(MEM_CGROUP_STAT_FILE_MAPPED) = global_page_state(NR_FILE_MAPPED) -
>                                sum_of_all_memcg(MEM_CGROUP_STAT_FILE_MAPPED);
> Dirty/Writeback pages accounting are in the similar way.
> 
> Signed-off-by: Sha Zhengju <handai.szj@taobao.com>

isn't it better to use mem_cgroup_is_root() call rather than
direct comparison (memcg == root_mem_cgroup) ?

Anyway, Ack to this approach. 

Thanks,
-Kame

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply

* [RFC] ARM: DMA-Mapping: add a new attribute to clear buffer
From: daeinki @ 2012-12-28  1:00 UTC (permalink / raw)
  To: linux-mm, linaro-mm-sig; +Cc: m.szyprowski, kyungmin.park, Inki Dae

From: Inki Dae <inki.dae@samsung.com>

This patch adds a new attribute, DMA_ATTR_SKIP_BUFFER_CLEAR
to skip buffer clearing. The buffer clearing also flushes CPU cache
so this operation has performance deterioration a little bit.

With this patch, allocated buffer region is cleared as default.
So if you want to skip the buffer clearing, just set this attribute.

But this flag should be used carefully because this use might get
access to some vulnerable content such as security data. So with this
patch, we make sure that all pages will be somehow cleared before
exposing to userspace.

For example, let's say that the security data had been stored
in some memory and freed without clearing it.
And then malicious process allocated the region though some buffer
allocator such as gem and ion without clearing it, and requested blit
operation with cleared another buffer though gpu or other drivers.
At this time, the malicious process could access the security data.

Signed-off-by: Inki Dae <inki.dae@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
---
 arch/arm/mm/dma-mapping.c |    6 ++++--
 include/linux/dma-attrs.h |    1 +
 2 files changed, 5 insertions(+), 2 deletions(-)

diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c
index 6b2fb87..fbe9dff 100644
--- a/arch/arm/mm/dma-mapping.c
+++ b/arch/arm/mm/dma-mapping.c
@@ -1058,7 +1058,8 @@ static struct page **__iommu_alloc_buffer(struct device *dev, size_t size,
 		if (!page)
 			goto error;
 
-		__dma_clear_buffer(page, size);
+		if (!dma_get_attr(DMA_ATTR_SKIP_BUFFER_CLEAR, attrs))
+			__dma_clear_buffer(page, size);
 
 		for (i = 0; i < count; i++)
 			pages[i] = page + i;
@@ -1082,7 +1083,8 @@ static struct page **__iommu_alloc_buffer(struct device *dev, size_t size,
 				pages[i + j] = pages[i] + j;
 		}
 
-		__dma_clear_buffer(pages[i], PAGE_SIZE << order);
+		if (!dma_get_attr(DMA_ATTR_SKIP_BUFFER_CLEAR, attrs))
+			__dma_clear_buffer(pages[i], PAGE_SIZE << order);
 		i += 1 << order;
 		count -= 1 << order;
 	}
diff --git a/include/linux/dma-attrs.h b/include/linux/dma-attrs.h
index c8e1831..2592c05 100644
--- a/include/linux/dma-attrs.h
+++ b/include/linux/dma-attrs.h
@@ -18,6 +18,7 @@ enum dma_attr {
 	DMA_ATTR_NO_KERNEL_MAPPING,
 	DMA_ATTR_SKIP_CPU_SYNC,
 	DMA_ATTR_FORCE_CONTIGUOUS,
+	DMA_ATTR_SKIP_BUFFER_CLEAR,
 	DMA_ATTR_MAX,
 };
 
-- 
1.7.4.1

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply related

* Re: [PATCH] x86, realmode: set real_mode permissions early
From: Yinghai Lu @ 2012-12-28  0:57 UTC (permalink / raw)
  To: H. Peter Anvin; +Cc: Borislav Petkov, LKML
In-Reply-To: <50DCE160.2040707@zytor.com>

On Thu, Dec 27, 2012 at 4:01 PM, H. Peter Anvin <hpa@zytor.com> wrote:
> On 12/27/2012 03:56 PM, Yinghai Lu wrote:
>>>
>>>
>>> I'm looking at this patch in the -v8 branch: you need to update the
>>> comment above that function to say that trampoline code needs to be
>>> marked X now earlier.
>>
>>
>> Not sure if we really need to add such comment in the code.
>>
>> Peter, what is your idea about this?
>>
>
> It would be good to note why the early_initcall is the right phase.
>

How about:

---
Trampoline will be executed by APs with SMP,
So we need to set it EXEC in do_pre_smp_initcalls and it is early_initcall.
---

^ permalink raw reply

* Re: [PATCH 3/3] libceph: WARN, don't BUG on unexpected connection states
From: Sage Weil @ 2012-12-28  0:57 UTC (permalink / raw)
  To: Alex Elder; +Cc: ceph-devel
In-Reply-To: <50DCD720.4070909@inktank.com>

I agree that we should do BUG -> WARN on con->state everywhere.

But I don't think we should drop any of them, yet.  For Ugis's particular 
crash, it was fail_protocol()'s fault... see my other patch.  The rest of 
the time, a socket close should be caught at the top of con_work().

For any other cases where we see con->state changing when we don't expect 
it to, let's look at them on a case-by-case basis and address them in 
separate patches?

sage


On Thu, 27 Dec 2012, Alex Elder wrote:

> A number of assertions in the ceph messenger are implemented with
> BUG_ON(), killing the system if connection's state doesn't match
> what's expected.  At this point our state model is (evidently) not
> well understood enough for these assertions to trigger a BUG().
> Convert all BUG_ON(con->state...) calls to be WARN_ON(con->state...)
> so we learn about these issues without killing the machine.
> 
> We now recognize that a connection fault can occur due to a socket
> closure at any time, regardless of the state of the connection.  So
> there is really nothing we can assert about the state of the
> connection at that point so eliminate that assertion.
> 
> Reported-by: Ugis <ugis22@gmail.com>
> Tested-by: Ugis <ugis22@gmail.com>
> Signed-off-by: Alex Elder <elder@inktank.com>
> ---
>  net/ceph/messenger.c |   13 ++++---------
>  1 file changed, 4 insertions(+), 9 deletions(-)
> 
> diff --git a/net/ceph/messenger.c b/net/ceph/messenger.c
> index 4d111fd..075b9fd 100644
> --- a/net/ceph/messenger.c
> +++ b/net/ceph/messenger.c
> @@ -561,7 +561,7 @@ void ceph_con_open(struct ceph_connection *con,
>  	mutex_lock(&con->mutex);
>  	dout("con_open %p %s\n", con, ceph_pr_addr(&addr->in_addr));
> 
> -	BUG_ON(con->state != CON_STATE_CLOSED);
> +	WARN_ON(con->state != CON_STATE_CLOSED);
>  	con->state = CON_STATE_PREOPEN;
> 
>  	con->peer_name.type = (__u8) entity_type;
> @@ -1509,7 +1509,7 @@ static int process_banner(struct ceph_connection *con)
>  static void fail_protocol(struct ceph_connection *con)
>  {
>  	reset_connection(con);
> -	BUG_ON(con->state != CON_STATE_NEGOTIATING);
> +	WARN_ON(con->state != CON_STATE_NEGOTIATING);
>  	con->state = CON_STATE_CLOSED;
>  }
> 
> @@ -1635,7 +1635,7 @@ static int process_connect(struct ceph_connection
> *con)
>  			return -1;
>  		}
> 
> -		BUG_ON(con->state != CON_STATE_NEGOTIATING);
> +		WARN_ON(con->state != CON_STATE_NEGOTIATING);
>  		con->state = CON_STATE_OPEN;
> 
>  		con->peer_global_seq = le32_to_cpu(con->in_reply.global_seq);
> @@ -2132,7 +2132,6 @@ more:
>  		if (ret < 0)
>  			goto out;
> 
> -		BUG_ON(con->state != CON_STATE_CONNECTING);
>  		con->state = CON_STATE_NEGOTIATING;
> 
>  		/*
> @@ -2160,7 +2159,7 @@ more:
>  		goto more;
>  	}
> 
> -	BUG_ON(con->state != CON_STATE_OPEN);
> +	WARN_ON(con->state != CON_STATE_OPEN);
> 
>  	if (con->in_base_pos < 0) {
>  		/*
> @@ -2382,10 +2381,6 @@ static void ceph_fault(struct ceph_connection *con)
>  	dout("fault %p state %lu to peer %s\n",
>  	     con, con->state, ceph_pr_addr(&con->peer_addr.in_addr));
> 
> -	BUG_ON(con->state != CON_STATE_CONNECTING &&
> -	       con->state != CON_STATE_NEGOTIATING &&
> -	       con->state != CON_STATE_OPEN);
> -
>  	con_close_socket(con);
> 
>  	if (test_bit(CON_FLAG_LOSSYTX, &con->flags)) {
> -- 
> 1.7.9.5
> 
> --
> To unsubscribe from this list: send the line "unsubscribe ceph-devel" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 
> 

^ permalink raw reply

* Re: BUG: unable to handle kernel NULL pointer dereference at 0000000000000500
From: Sedat Dilek @ 2012-12-28  0:56 UTC (permalink / raw)
  To: Zlatko Calusic; +Cc: LKML, linux-mm
In-Reply-To: <50DCEB01.3060903@iskon.hr>

On Fri, Dec 28, 2012 at 1:42 AM, Zlatko Calusic <zlatko.calusic@iskon.hr> wrote:
> On 28.12.2012 01:37, Sedat Dilek wrote:
>>
>> On Fri, Dec 28, 2012 at 1:33 AM, Zlatko Calusic <zlatko.calusic@iskon.hr>
>> wrote:
>>>
>>> On 28.12.2012 01:24, Sedat Dilek wrote:
>>>>
>>>>
>>>> On Fri, Dec 28, 2012 at 12:51 AM, Zlatko Calusic
>>>> <zlatko.calusic@iskon.hr> wrote:
>>>>>
>>>>>
>>>>> On 28.12.2012 00:42, Sedat Dilek wrote:
>>>>>>
>>>>>>
>>>>>>
>>>>>> On Fri, Dec 28, 2012 at 12:39 AM, Zlatko Calusic
>>>>>> <zlatko.calusic@iskon.hr> wrote:
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>> On 28.12.2012 00:30, Sedat Dilek wrote:
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>> Hi Zlatko,
>>>>>>>>
>>>>>>>> I am not sure if I hit the same problem as described in this thread.
>>>>>>>>
>>>>>>>> Under heavy load, while building a customized toolchain for the
>>>>>>>> Freetz
>>>>>>>> router project I got a BUG || NULL pointer derefence || kswapd ||
>>>>>>>> zone_balanced || pgdat_balanced() etc. (details see my screenshot).
>>>>>>>>
>>>>>>>> I will try your patch from [1] ***only*** on top of my last
>>>>>>>> Linux-v3.8-rc1 GIT setup (post-v3.8-rc1 mainline + some net-fixes).
>>>>>>>>
>>>>>>>
>>>>>>> Yes, that's the same bug. It should be fixed with my latest patch, so
>>>>>>> I'd
>>>>>>> appreciate you testing it, to be on the safe side this time. There
>>>>>>> should
>>>>>>> be
>>>>>>> no difference if you apply it to anything newer than 3.8-rc1, so go
>>>>>>> for
>>>>>>> it.
>>>>>>> Thanks!
>>>>>>>
>>>>>>
>>>>>> Not sure how I can really reproduce this bug as one build worked fine
>>>>>> within my last v3.8-rc1 kernel.
>>>>>> I increased the parallel-make-jobs-number from "4" to "8" to stress a
>>>>>> bit harder.
>>>>>> Just building right now... and will report.
>>>>>>
>>>>>> If you have any test-case (script or whatever), please let me/us know.
>>>>>>
>>>>>
>>>>> Unfortunately not, I haven't reproduced it yet on my machines. But it
>>>>> seems
>>>>> that bug will hit only under heavy memory pressure. When close to OOM,
>>>>> or
>>>>> possibly with lots of writing to disk. It's also possible that
>>>>> fragmentation
>>>>> of memory zones could provoke it, that means testing it for a longer
>>>>> time.
>>>>>
>>>>
>>>> I tested successfully by doing simultaneously...
>>>> - building Freetz with 8 parallel make-jobs
>>>> - building Linux GIT with 1 make-job
>>>> - 9 tabs open in firefox
>>>> - In one tab I ran YouTube music video
>>>> - etc.
>>>>
>>>> I am reading [1] and [2] where another user reports success by reverting
>>>> this...
>>>>
>>>> commit cda73a10eb3f493871ed39f468db50a65ebeddce
>>>> "mm: do not sleep in balance_pgdat if there's no i/o congestion"
>>>>
>>>> BTW, this machine has also 4GiB RAM (Ubuntu/precise AMD64).
>>>>
>>>> Feel free to add a "Reported-by/Tested-by" if you think this is a
>>>> positive report.
>>>>
>>>
>>> Thanks for the testing! And keep running it in case something interesting
>>> pops up. ;)
>>>
>>> No need to revert cda73a10eb because it fixes another bug. And the patch
>>> you're now running fixes the new bug I introduced with a combination of
>>> my
>>> latest 2 patches. Nah, it gets complicated... :)
>>>
>>> But, at least I found the culprit and as soon as Linus applies the fix,
>>> everything will be hunky dory again, at least on this front. :P
>>>
>>
>> I am not subscribed to LKML and linux-mm,,,
>> Do you have a patch with a proper subject and descriptive text? URL?
>>
>
> Soon to follow. I'd appreciate Zhouping Liu testing it too, though.

I understand, but I want to push your patch with a proper subject-line
as a pending patch with reference to patchwork, so...

- Sedat -

> --
> Zlatko

^ permalink raw reply

* Re: BUG: unable to handle kernel NULL pointer dereference at 0000000000000500
From: Sedat Dilek @ 2012-12-28  0:56 UTC (permalink / raw)
  To: Zlatko Calusic; +Cc: LKML, linux-mm
In-Reply-To: <50DCEB01.3060903@iskon.hr>

On Fri, Dec 28, 2012 at 1:42 AM, Zlatko Calusic <zlatko.calusic@iskon.hr> wrote:
> On 28.12.2012 01:37, Sedat Dilek wrote:
>>
>> On Fri, Dec 28, 2012 at 1:33 AM, Zlatko Calusic <zlatko.calusic@iskon.hr>
>> wrote:
>>>
>>> On 28.12.2012 01:24, Sedat Dilek wrote:
>>>>
>>>>
>>>> On Fri, Dec 28, 2012 at 12:51 AM, Zlatko Calusic
>>>> <zlatko.calusic@iskon.hr> wrote:
>>>>>
>>>>>
>>>>> On 28.12.2012 00:42, Sedat Dilek wrote:
>>>>>>
>>>>>>
>>>>>>
>>>>>> On Fri, Dec 28, 2012 at 12:39 AM, Zlatko Calusic
>>>>>> <zlatko.calusic@iskon.hr> wrote:
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>> On 28.12.2012 00:30, Sedat Dilek wrote:
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>> Hi Zlatko,
>>>>>>>>
>>>>>>>> I am not sure if I hit the same problem as described in this thread.
>>>>>>>>
>>>>>>>> Under heavy load, while building a customized toolchain for the
>>>>>>>> Freetz
>>>>>>>> router project I got a BUG || NULL pointer derefence || kswapd ||
>>>>>>>> zone_balanced || pgdat_balanced() etc. (details see my screenshot).
>>>>>>>>
>>>>>>>> I will try your patch from [1] ***only*** on top of my last
>>>>>>>> Linux-v3.8-rc1 GIT setup (post-v3.8-rc1 mainline + some net-fixes).
>>>>>>>>
>>>>>>>
>>>>>>> Yes, that's the same bug. It should be fixed with my latest patch, so
>>>>>>> I'd
>>>>>>> appreciate you testing it, to be on the safe side this time. There
>>>>>>> should
>>>>>>> be
>>>>>>> no difference if you apply it to anything newer than 3.8-rc1, so go
>>>>>>> for
>>>>>>> it.
>>>>>>> Thanks!
>>>>>>>
>>>>>>
>>>>>> Not sure how I can really reproduce this bug as one build worked fine
>>>>>> within my last v3.8-rc1 kernel.
>>>>>> I increased the parallel-make-jobs-number from "4" to "8" to stress a
>>>>>> bit harder.
>>>>>> Just building right now... and will report.
>>>>>>
>>>>>> If you have any test-case (script or whatever), please let me/us know.
>>>>>>
>>>>>
>>>>> Unfortunately not, I haven't reproduced it yet on my machines. But it
>>>>> seems
>>>>> that bug will hit only under heavy memory pressure. When close to OOM,
>>>>> or
>>>>> possibly with lots of writing to disk. It's also possible that
>>>>> fragmentation
>>>>> of memory zones could provoke it, that means testing it for a longer
>>>>> time.
>>>>>
>>>>
>>>> I tested successfully by doing simultaneously...
>>>> - building Freetz with 8 parallel make-jobs
>>>> - building Linux GIT with 1 make-job
>>>> - 9 tabs open in firefox
>>>> - In one tab I ran YouTube music video
>>>> - etc.
>>>>
>>>> I am reading [1] and [2] where another user reports success by reverting
>>>> this...
>>>>
>>>> commit cda73a10eb3f493871ed39f468db50a65ebeddce
>>>> "mm: do not sleep in balance_pgdat if there's no i/o congestion"
>>>>
>>>> BTW, this machine has also 4GiB RAM (Ubuntu/precise AMD64).
>>>>
>>>> Feel free to add a "Reported-by/Tested-by" if you think this is a
>>>> positive report.
>>>>
>>>
>>> Thanks for the testing! And keep running it in case something interesting
>>> pops up. ;)
>>>
>>> No need to revert cda73a10eb because it fixes another bug. And the patch
>>> you're now running fixes the new bug I introduced with a combination of
>>> my
>>> latest 2 patches. Nah, it gets complicated... :)
>>>
>>> But, at least I found the culprit and as soon as Linus applies the fix,
>>> everything will be hunky dory again, at least on this front. :P
>>>
>>
>> I am not subscribed to LKML and linux-mm,,,
>> Do you have a patch with a proper subject and descriptive text? URL?
>>
>
> Soon to follow. I'd appreciate Zhouping Liu testing it too, though.

I understand, but I want to push your patch with a proper subject-line
as a pending patch with reference to patchwork, so...

- Sedat -

> --
> Zlatko

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply

* Re: BUG: unable to handle kernel NULL pointer dereference at 0000000000000500
From: Zlatko Calusic @ 2012-12-28  0:42 UTC (permalink / raw)
  To: sedat.dilek; +Cc: LKML, linux-mm
In-Reply-To: <CA+icZUWDPux3QAzQ85ntfUTHMr5m3Ueo-zjfnoGGxi=VrB9_7A@mail.gmail.com>

On 28.12.2012 01:37, Sedat Dilek wrote:
> On Fri, Dec 28, 2012 at 1:33 AM, Zlatko Calusic <zlatko.calusic@iskon.hr> wrote:
>> On 28.12.2012 01:24, Sedat Dilek wrote:
>>>
>>> On Fri, Dec 28, 2012 at 12:51 AM, Zlatko Calusic
>>> <zlatko.calusic@iskon.hr> wrote:
>>>>
>>>> On 28.12.2012 00:42, Sedat Dilek wrote:
>>>>>
>>>>>
>>>>> On Fri, Dec 28, 2012 at 12:39 AM, Zlatko Calusic
>>>>> <zlatko.calusic@iskon.hr> wrote:
>>>>>>
>>>>>>
>>>>>> On 28.12.2012 00:30, Sedat Dilek wrote:
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>> Hi Zlatko,
>>>>>>>
>>>>>>> I am not sure if I hit the same problem as described in this thread.
>>>>>>>
>>>>>>> Under heavy load, while building a customized toolchain for the Freetz
>>>>>>> router project I got a BUG || NULL pointer derefence || kswapd ||
>>>>>>> zone_balanced || pgdat_balanced() etc. (details see my screenshot).
>>>>>>>
>>>>>>> I will try your patch from [1] ***only*** on top of my last
>>>>>>> Linux-v3.8-rc1 GIT setup (post-v3.8-rc1 mainline + some net-fixes).
>>>>>>>
>>>>>>
>>>>>> Yes, that's the same bug. It should be fixed with my latest patch, so
>>>>>> I'd
>>>>>> appreciate you testing it, to be on the safe side this time. There
>>>>>> should
>>>>>> be
>>>>>> no difference if you apply it to anything newer than 3.8-rc1, so go for
>>>>>> it.
>>>>>> Thanks!
>>>>>>
>>>>>
>>>>> Not sure how I can really reproduce this bug as one build worked fine
>>>>> within my last v3.8-rc1 kernel.
>>>>> I increased the parallel-make-jobs-number from "4" to "8" to stress a
>>>>> bit harder.
>>>>> Just building right now... and will report.
>>>>>
>>>>> If you have any test-case (script or whatever), please let me/us know.
>>>>>
>>>>
>>>> Unfortunately not, I haven't reproduced it yet on my machines. But it
>>>> seems
>>>> that bug will hit only under heavy memory pressure. When close to OOM, or
>>>> possibly with lots of writing to disk. It's also possible that
>>>> fragmentation
>>>> of memory zones could provoke it, that means testing it for a longer
>>>> time.
>>>>
>>>
>>> I tested successfully by doing simultaneously...
>>> - building Freetz with 8 parallel make-jobs
>>> - building Linux GIT with 1 make-job
>>> - 9 tabs open in firefox
>>> - In one tab I ran YouTube music video
>>> - etc.
>>>
>>> I am reading [1] and [2] where another user reports success by reverting
>>> this...
>>>
>>> commit cda73a10eb3f493871ed39f468db50a65ebeddce
>>> "mm: do not sleep in balance_pgdat if there's no i/o congestion"
>>>
>>> BTW, this machine has also 4GiB RAM (Ubuntu/precise AMD64).
>>>
>>> Feel free to add a "Reported-by/Tested-by" if you think this is a
>>> positive report.
>>>
>>
>> Thanks for the testing! And keep running it in case something interesting
>> pops up. ;)
>>
>> No need to revert cda73a10eb because it fixes another bug. And the patch
>> you're now running fixes the new bug I introduced with a combination of my
>> latest 2 patches. Nah, it gets complicated... :)
>>
>> But, at least I found the culprit and as soon as Linus applies the fix,
>> everything will be hunky dory again, at least on this front. :P
>>
>
> I am not subscribed to LKML and linux-mm,,,
> Do you have a patch with a proper subject and descriptive text? URL?
>

Soon to follow. I'd appreciate Zhouping Liu testing it too, though.
-- 
Zlatko

^ permalink raw reply

* Re: [PATCH 2/3] libceph: always reset osds when kicking
From: Sage Weil @ 2012-12-28  0:55 UTC (permalink / raw)
  To: Alex Elder; +Cc: ceph-devel
In-Reply-To: <50DCD70B.9020008@inktank.com>

On Thu, 27 Dec 2012, Alex Elder wrote:
> When ceph_osdc_handle_map() is called to process a new osd map,
> kick_requests() is called to ensure all affected requests are
> updated if necessary to reflect changes in the osd map.  This
> happens in two cases:  whenever an incremental map update is
> processed; and when a full map update (or the last one if there is
> more than one) gets processed.
> 
> In the former case, the kick_requests() call is followed immediately
> by a call to reset_changed_osds() to ensure any connections to osds
> affected by the map change are reset.  But for full map updates
> this isn't done.
> 
> Both cases should be doint this osd reset.
> 
> Rather than duplicating the reset_changed_osds() call, move it into
> the end of kick_requests().
> 
> Signed-off-by: Alex Elder <elder@inktank.com>

Reviewed-by: Sage Weil <sage@inktank.com>

> ---
>  net/ceph/osd_client.c |    4 ++--
>  1 file changed, 2 insertions(+), 2 deletions(-)
> 
> diff --git a/net/ceph/osd_client.c b/net/ceph/osd_client.c
> index 616c6cf..0be8f0d 100644
> --- a/net/ceph/osd_client.c
> +++ b/net/ceph/osd_client.c
> @@ -1270,7 +1270,7 @@ static void reset_changed_osds(struct
> ceph_osd_client *osdc)
>   * Requeue requests whose mapping to an OSD has changed.  If requests
> map to
>   * no osd, request a new map.
>   *
> - * Caller should hold map_sem for read and request_mutex.
> + * Caller should hold map_sem for read.
>   */
>  static void kick_requests(struct ceph_osd_client *osdc, int force_resend)
>  {
> @@ -1346,6 +1346,7 @@ static void kick_requests(struct ceph_osd_client
> *osdc, int force_resend)
>  		dout("%d requests for down osds, need new map\n", needmap);
>  		ceph_monc_request_next_osdmap(&osdc->client->monc);
>  	}
> +	reset_changed_osds(osdc);
>  }
> 
> 
> @@ -1402,7 +1403,6 @@ void ceph_osdc_handle_map(struct ceph_osd_client
> *osdc, struct ceph_msg *msg)
>  				osdc->osdmap = newmap;
>  			}
>  			kick_requests(osdc, 0);
> -			reset_changed_osds(osdc);
>  		} else {
>  			dout("ignoring incremental map %u len %d\n",
>  			     epoch, maplen);
> -- 
> 1.7.9.5
> 
> --
> To unsubscribe from this list: send the line "unsubscribe ceph-devel" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 
> 

^ permalink raw reply

* Re: [PATCH 1/3] libceph: move linger requests sooner in kick_requests()
From: Sage Weil @ 2012-12-28  0:54 UTC (permalink / raw)
  To: Alex Elder; +Cc: ceph-devel
In-Reply-To: <50DCD706.9030601@inktank.com>

On Thu, 27 Dec 2012, Alex Elder wrote:
> The kick_requests() function is called by ceph_osdc_handle_map()
> when an osd map change has been indicated.  Its purpose is to
> re-queue any request whose target osd is different from what it
> was when it was originally sent.
> 
> It is structured as two loops, one for incomplete but registered
> requests, and a second for handling completed linger requests.
> As a special case, in the first loop if a request marked to linger
> has not yet completed, it is moved from the request list to the
> linger list.  This is as a quick and dirty way to have the second
> loop handle sending the request along with all the other linger
> requests.
> 
> Because of the way it's done now, however, this quick and dirty
> solution can result in these incomplete linger requests never
> getting re-sent as desired.  The problem lies in the fact that
> the second loop only arranges for a linger request to be sent
> if it appears its target osd has changed.  This is the proper
> handling for *completed* linger requests (it avoids issuing
> the same linger request twice to the same osd).
> 
> But although the linger requests added to the list in the first loop
> may have been sent, they have not yet completed, so they need to be
> re-sent regardless of whether their target osd has changed.
> 
> The first required fix is we need to avoid calling __map_request()
> on any incomplete linger request.  Otherwise the subsequent
> __map_request() call in the second loop will find the target osd
> has not changed and will therefore not re-send the request.
> 
> Second, we need to be sure that a sent but incomplete linger request
> gets re-sent.  If the target osd is the same with the new osd map as
> it was when the request was originally sent, this won't happen.
> This can be fixed through careful handling when we move these
> requests from the request list to the linger list, by unregistering
> the request *before* it is registered as a linger request.  This
> works because a side-effect of unregistering the request is to make
> the request's r_osd pointer be NULL, and *that* will ensure the
> second loop actually re-sends the linger request.
> 
> Processing of such a request is done at that point, so continue with
> the next one once it's been moved.
> 
> Signed-off-by: Alex Elder <elder@inktank.com>
> ---
>  net/ceph/osd_client.c |   31 ++++++++++++++++++++-----------
>  1 file changed, 20 insertions(+), 11 deletions(-)
> 
> diff --git a/net/ceph/osd_client.c b/net/ceph/osd_client.c
> index 780caf6..616c6cf 100644
> --- a/net/ceph/osd_client.c
> +++ b/net/ceph/osd_client.c
> @@ -1284,6 +1284,25 @@ static void kick_requests(struct ceph_osd_client
> *osdc, int force_resend)
>  	for (p = rb_first(&osdc->requests); p; ) {
>  		req = rb_entry(p, struct ceph_osd_request, r_node);
>  		p = rb_next(p);
> +
> +		/*
> +		 * For linger requests that have not yet been
> +		 * registered, move them to the linger list; they'll
> +		 * be sent to the osd in the loop below.  Unregister
> +		 * the request before re-registering it as a linger
> +		 * request to ensure the __map_request() below
> +		 * will decide it needs to be sent.
> +		 */
> +		if (req->r_linger && list_empty(&req->r_linger_item)) {
> +			dout("%p tid %llu restart on osd%d\n",
> +			     req, req->r_tid,
> +			     req->r_osd ? req->r_osd->o_osd : -1);
> +			__unregister_request(osdc, req);
> +			__register_linger_request(osdc, req);
> +

Drop the newline?

Reviewed-by: Sage Weil <sage@inktank.com>


> +			continue;
> +		}
> +
>  		err = __map_request(osdc, req, force_resend);
>  		if (err < 0)
>  			continue;  /* error */
> @@ -1298,17 +1317,6 @@ static void kick_requests(struct ceph_osd_client
> *osdc, int force_resend)
>  				req->r_flags |= CEPH_OSD_FLAG_RETRY;
>  			}
>  		}
> -		if (req->r_linger && list_empty(&req->r_linger_item)) {
> -			/*
> -			 * register as a linger so that we will
> -			 * re-submit below and get a new tid
> -			 */
> -			dout("%p tid %llu restart on osd%d\n",
> -			     req, req->r_tid,
> -			     req->r_osd ? req->r_osd->o_osd : -1);
> -			__register_linger_request(osdc, req);
> -			__unregister_request(osdc, req);
> -		}
>  	}
> 
>  	list_for_each_entry_safe(req, nreq, &osdc->req_linger,
> @@ -1316,6 +1324,7 @@ static void kick_requests(struct ceph_osd_client
> *osdc, int force_resend)
>  		dout("linger req=%p req->r_osd=%p\n", req, req->r_osd);
> 
>  		err = __map_request(osdc, req, force_resend);
> +		dout("__map_request returned %d\n", err);
>  		if (err == 0)
>  			continue;  /* no change and no osd was specified */
>  		if (err < 0)
> -- 
> 1.7.9.5
> 
> --
> To unsubscribe from this list: send the line "unsubscribe ceph-devel" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 
> 

^ permalink raw reply


This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.