LinuxPPC-Dev Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2 3/6] mtd: m25p80: add support to parse the SPI flash's partitions
From: Mingkai Hu @ 2010-08-02  7:52 UTC (permalink / raw)
  To: linuxppc-dev, spi-devel-general; +Cc: kumar.gala, Mingkai Hu
In-Reply-To: <1280735524-17547-3-git-send-email-Mingkai.hu@freescale.com>

Signed-off-by: Mingkai Hu <Mingkai.hu@freescale.com>
---

v2:
 - Move the flash partition function from of_spi.c to MTD driver

 drivers/mtd/devices/m25p80.c |   29 +++++++++++++++++++++++++++++
 1 files changed, 29 insertions(+), 0 deletions(-)

diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c
index 81e49a9..5f00075 100644
--- a/drivers/mtd/devices/m25p80.c
+++ b/drivers/mtd/devices/m25p80.c
@@ -752,6 +752,31 @@ static const struct spi_device_id *__devinit jedec_probe(struct spi_device *spi)
 	return NULL;
 }
 
+/*
+ * parse_flash_partition - Parse the flash partition on the SPI bus
+ * @spi: Pointer to spi_device device
+ */
+void parse_flash_partition(struct spi_device *spi)
+{
+	struct mtd_partition *parts;
+	struct flash_platform_data *pdata;
+	int nr_parts = 0;
+	struct device_node *np = spi->dev.of_node;
+
+	nr_parts = of_mtd_parse_partitions(&spi->dev, np, &parts);
+	if (!nr_parts)
+		return;
+
+	pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
+	if (!pdata)
+		return;
+
+	pdata->parts = parts;
+	pdata->nr_parts = nr_parts;
+	spi->dev.platform_data = pdata;
+
+	return;
+}
 
 /*
  * board specific setup should have ensured the SPI clock used here
@@ -771,6 +796,10 @@ static int __devinit m25p_probe(struct spi_device *spi)
 	 * a chip ID, try the JEDEC id commands; they'll work for most
 	 * newer chips, even if we don't recognize the particular chip.
 	 */
+
+	/* Parse the flash partition */
+	parse_flash_partition(spi);
+
 	data = spi->dev.platform_data;
 	if (data && data->type) {
 		const struct spi_device_id *plat_id;
-- 
1.6.4

^ permalink raw reply related

* [PATCH v2 2/6] eSPI: add eSPI controller support
From: Mingkai Hu @ 2010-08-02  7:52 UTC (permalink / raw)
  To: linuxppc-dev, spi-devel-general; +Cc: kumar.gala, Mingkai Hu
In-Reply-To: <1280735524-17547-2-git-send-email-Mingkai.hu@freescale.com>

Add eSPI controller support based on the library code spi_fsl_lib.c.

The eSPI controller is newer controller 85xx/Pxxx devices supported.
There're some differences comparing to the SPI controller:

1. Has different register map and different bit definition
   So leave the code operated the register to the driver code, not
   the common code.

2. Support 4 dedicated chip selects
   The software can't controll the chip selects directly, The SPCOM[CS]
   field is used to select which chip selects is used, and the
   SPCOM[TRANLEN] field is set to tell the controller how long the CS
   signal need to be asserted. So the driver doesn't need the chipselect
   related function when transfering data, just set corresponding register
   fields to controll the chipseclect.

3. Different Transmit/Receive FIFO access register behavior
   For SPI controller, the Tx/Rx FIFO access register can hold only
   one character regardless of the character length, but for eSPI
   controller, the register can hold 4 or 2 characters according to
   the character lengths. Access the Tx/Rx FIFO access register of the
   eSPI controller will shift out/in 4/2 characters one time. For SPI
   subsystem, the command and data are put into different transfers, so
   we need to combine all the transfers to one transfer in order to pass
   the transfer to eSPI controller.

Signed-off-by: Mingkai Hu <Mingkai.hu@freescale.com>
---

v2:
 - Rename fsl_espi.c to spi_fsl_espi.c, also the config name
 - Move register map definiton from spi_fsl_lib.c to spi_fsl_espi.c
 - Break some funcions line in the arguments instead of the declaration
 - Inconsistent whitespacing in the macro definition
 - Init bits_per_word to 0 to eliminate the else clause
 - Add brace for the else clause to match if clause
 - Add chip name mpc8536 to the compatible value
 - Drop the last entry's comma in the match table
 - move module_init() immediately after the init fsl_espi_init() function

 drivers/spi/Kconfig        |    9 +
 drivers/spi/Makefile       |    1 +
 drivers/spi/spi_fsl_espi.c |  633 ++++++++++++++++++++++++++++++++++++++++++++
 drivers/spi/spi_fsl_lib.h  |    2 +
 4 files changed, 645 insertions(+), 0 deletions(-)
 create mode 100644 drivers/spi/spi_fsl_espi.c

diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index cd7f13b..a379363 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -140,6 +140,15 @@ config SPI_FSL_SPI
 	  MPC83xx platform uses the controller in cpu mode or CPM/QE mode.
 	  MPC8569 uses the controller in QE mode, MPC8610 in cpu mode.
 
+config SPI_FSL_ESPI
+	tristate "Freescale eSPI controller"
+	depends on FSL_SOC
+	select SPI_FSL_LIB
+	help
+	  This enables using the Freescale eSPI controllers in master mode.
+	  From MPC8536, 85xx platform uses the controller, and all P10xx,
+	  P20xx, P40xx uses this controller.
+
 config SPI_GPIO
 	tristate "GPIO-based bitbanging SPI Master"
 	depends on GENERIC_GPIO
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index cf8d9be..dd86ba7 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -24,6 +24,7 @@ obj-$(CONFIG_SPI_DW_MMIO)		+= dw_spi_mmio.o
 obj-$(CONFIG_SPI_EP93XX)		+= ep93xx_spi.o
 obj-$(CONFIG_SPI_FSL_LIB)		+= spi_fsl_lib.o
 obj-$(CONFIG_SPI_FSL_SPI)		+= spi_fsl_spi.o
+obj-$(CONFIG_SPI_FSL_ESPI)		+= spi_fsl_espi.o
 obj-$(CONFIG_SPI_GPIO)			+= spi_gpio.o
 obj-$(CONFIG_SPI_IMX)			+= spi_imx.o
 obj-$(CONFIG_SPI_LM70_LLP)		+= spi_lm70llp.o
diff --git a/drivers/spi/spi_fsl_espi.c b/drivers/spi/spi_fsl_espi.c
new file mode 100644
index 0000000..61987cf
--- /dev/null
+++ b/drivers/spi/spi_fsl_espi.c
@@ -0,0 +1,633 @@
+/*
+ * Freescale eSPI controller driver.
+ *
+ * Copyright 2010 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ */
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/irq.h>
+#include <linux/spi/spi.h>
+#include <linux/platform_device.h>
+#include <linux/fsl_devices.h>
+#include <linux/mm.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/of_spi.h>
+#include <sysdev/fsl_soc.h>
+#include <linux/interrupt.h>
+
+#include "spi_fsl_lib.h"
+
+/* eSPI Controller registers */
+struct fsl_espi_reg {
+	__be32 mode;		/* 0x000 - eSPI mode register */
+	__be32 event;		/* 0x004 - eSPI event register */
+	__be32 mask;		/* 0x008 - eSPI mask register */
+	__be32 command;		/* 0x00c - eSPI command register */
+	__be32 transmit;	/* 0x010 - eSPI transmit FIFO access register*/
+	__be32 receive;		/* 0x014 - eSPI receive FIFO access register*/
+	u8 res[8];		/* 0x018 - 0x01c reserved */
+	__be32 csmode[4];	/* 0x020 - 0x02c eSPI cs mode register */
+};
+
+/* eSPI Controller mode register definitions */
+#define SPMODE_ENABLE		(1 << 31)
+#define SPMODE_LOOP		(1 << 30)
+#define SPMODE_TXTHR(x)		((x) << 8)
+#define SPMODE_RXTHR(x)		((x) << 0)
+
+/* eSPI Controller CS mode register definitions */
+#define CSMODE_CI_INACTIVEHIGH	(1 << 31)
+#define CSMODE_CP_BEGIN_EDGECLK	(1 << 30)
+#define CSMODE_REV		(1 << 29)
+#define CSMODE_DIV16		(1 << 28)
+#define CSMODE_PM(x)		((x) << 24)
+#define CSMODE_POL_1		(1 << 20)
+#define CSMODE_LEN(x)		((x) << 16)
+#define CSMODE_BEF(x)		((x) << 12)
+#define CSMODE_AFT(x)		((x) << 8)
+#define CSMODE_CG(x)		((x) << 3)
+
+/* Default mode/csmode for eSPI controller */
+#define SPMODE_INIT_VAL (SPMODE_TXTHR(4) | SPMODE_RXTHR(3))
+#define CSMODE_INIT_VAL (CSMODE_POL_1 | CSMODE_BEF(0) \
+		| CSMODE_AFT(0) | CSMODE_CG(1))
+
+/* SPIE register values */
+#define	SPIE_NE		0x00000200	/* Not empty */
+#define	SPIE_NF		0x00000100	/* Not full */
+
+/* SPIM register values */
+#define	SPIM_NE		0x00000200	/* Not empty */
+#define	SPIM_NF		0x00000100	/* Not full */
+#define SPIE_RXCNT(reg)     ((reg >> 24) & 0x3F)
+#define SPIE_TXCNT(reg)     ((reg >> 16) & 0x3F)
+
+/* SPCOM register values */
+#define SPCOM_CS(x)		((x) << 30)
+#define SPCOM_TRANLEN(x)	((x) << 0)
+#define	SPCOM_TRANLEN_MAX	0xFFFF	/* Max transaction length */
+
+static void fsl_espi_change_mode(struct spi_device *spi)
+{
+	struct mpc8xxx_spi *mspi = spi_master_get_devdata(spi->master);
+	struct spi_mpc8xxx_cs *cs = spi->controller_state;
+	__be32 __iomem *mode;
+	__be32 __iomem *espi_mode = NULL;
+	u32 tmp;
+	unsigned long flags;
+
+	espi_mode = &mspi->espi_base->mode;
+	mode = &mspi->espi_base->csmode[spi->chip_select];
+
+	/* Turn off IRQs locally to minimize time that SPI is disabled. */
+	local_irq_save(flags);
+
+	/* Turn off SPI unit prior changing mode */
+	tmp = mpc8xxx_spi_read_reg(espi_mode);
+	mpc8xxx_spi_write_reg(espi_mode, tmp & ~SPMODE_ENABLE);
+	mpc8xxx_spi_write_reg(mode, cs->hw_mode);
+	mpc8xxx_spi_write_reg(espi_mode, tmp);
+
+	local_irq_restore(flags);
+}
+
+static u32 fsl_espi_tx_buf_lsb(struct mpc8xxx_spi *mpc8xxx_spi)
+{
+	u32 data;
+	u16 data_h, data_l;
+
+	const u32 *tx = mpc8xxx_spi->tx;
+	if (!tx)
+		return 0;
+
+	data = *tx++ << mpc8xxx_spi->tx_shift;
+	data_l = data & 0xffff;
+	data_h = (data >> 16) & 0xffff;
+	swab16s(&data_l);
+	swab16s(&data_h);
+	data = data_h | data_l;
+
+	mpc8xxx_spi->tx = tx;
+	return data;
+}
+
+static int fsl_espi_setup_transfer(struct spi_device *spi,
+					struct spi_transfer *t)
+{
+	struct mpc8xxx_spi *mpc8xxx_spi;
+	int bits_per_word = 0;
+	u8 pm;
+	u32 hz = 0;
+	struct spi_mpc8xxx_cs	*cs = spi->controller_state;
+
+	mpc8xxx_spi = spi_master_get_devdata(spi->master);
+
+	if (t) {
+		bits_per_word = t->bits_per_word;
+		hz = t->speed_hz;
+	}
+
+	/* spi_transfer level calls that work per-word */
+	if (!bits_per_word)
+		bits_per_word = spi->bits_per_word;
+
+	/* Make sure its a bit width we support [4..16] */
+	if ((bits_per_word < 4) || (bits_per_word > 16))
+		return -EINVAL;
+
+	if (!hz)
+		hz = spi->max_speed_hz;
+
+	cs->rx_shift = 0;
+	cs->tx_shift = 0;
+	cs->get_rx = mpc8xxx_spi_rx_buf_u32;
+	cs->get_tx = mpc8xxx_spi_tx_buf_u32;
+	if (bits_per_word <= 8) {
+		cs->rx_shift = 8 - bits_per_word;
+	} else if (bits_per_word <= 16) {
+		cs->rx_shift = 16 - bits_per_word;
+		if (spi->mode & SPI_LSB_FIRST)
+			cs->get_tx = fsl_espi_tx_buf_lsb;
+	} else
+		return -EINVAL;
+
+	mpc8xxx_spi->rx_shift = cs->rx_shift;
+	mpc8xxx_spi->tx_shift = cs->tx_shift;
+	mpc8xxx_spi->get_rx = cs->get_rx;
+	mpc8xxx_spi->get_tx = cs->get_tx;
+
+	bits_per_word = bits_per_word - 1;
+
+	/* mask out bits we are going to set */
+	cs->hw_mode &= ~(CSMODE_LEN(0xF) | CSMODE_DIV16
+				  | CSMODE_PM(0xF));
+
+	cs->hw_mode |= CSMODE_LEN(bits_per_word);
+
+	if ((mpc8xxx_spi->spibrg / hz) > 64) {
+		cs->hw_mode |= CSMODE_DIV16;
+		pm = (mpc8xxx_spi->spibrg - 1) / (hz * 64) + 1;
+
+		WARN_ONCE(pm > 16, "%s: Requested speed is too low: %d Hz. "
+			  "Will use %d Hz instead.\n", dev_name(&spi->dev),
+			  hz, mpc8xxx_spi->spibrg / 1024);
+		if (pm > 16)
+			pm = 16;
+	} else {
+		pm = (mpc8xxx_spi->spibrg - 1) / (hz * 4) + 1;
+	}
+	if (pm)
+		pm--;
+
+	cs->hw_mode |= CSMODE_PM(pm);
+
+	fsl_espi_change_mode(spi);
+	return 0;
+}
+
+int fsl_espi_cpu_bufs(struct mpc8xxx_spi *mspi, struct spi_transfer *t,
+		unsigned int len)
+{
+	u32 word;
+
+	mspi->count = len;
+
+	/* enable rx ints */
+	mpc8xxx_spi_write_reg(&mspi->espi_base->mask, SPIM_NE);
+
+	/* transmit word */
+	word = mspi->get_tx(mspi);
+	mpc8xxx_spi_write_reg(&mspi->espi_base->transmit, word);
+
+	return 0;
+}
+
+static int fsl_espi_bufs(struct spi_device *spi, struct spi_transfer *t,
+			    bool is_dma_mapped)
+{
+	struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(spi->master);
+	unsigned int len = t->len;
+	u8 bits_per_word;
+	int ret;
+
+	bits_per_word = spi->bits_per_word;
+	if (t->bits_per_word)
+		bits_per_word = t->bits_per_word;
+
+	mpc8xxx_spi->len = t->len;
+	len = roundup(len, 4) / 4;
+
+	mpc8xxx_spi->tx = t->tx_buf;
+	mpc8xxx_spi->rx = t->rx_buf;
+
+	INIT_COMPLETION(mpc8xxx_spi->done);
+
+	/* Set SPCOM[CS] and SPCOM[TRANLEN] field */
+	if ((t->len - 1) > SPCOM_TRANLEN_MAX) {
+		dev_err(mpc8xxx_spi->dev, "Transaction length (%d)"
+				" beyond the SPCOM[TRANLEN] field\n", t->len);
+		return -EINVAL;
+	}
+	mpc8xxx_spi_write_reg(&mpc8xxx_spi->espi_base->command,
+		(SPCOM_CS(spi->chip_select) | SPCOM_TRANLEN(t->len - 1)));
+
+	ret = fsl_espi_cpu_bufs(mpc8xxx_spi, t, len);
+	if (ret)
+		return ret;
+
+	wait_for_completion(&mpc8xxx_spi->done);
+
+	/* disable rx ints */
+	mpc8xxx_spi_write_reg(&mpc8xxx_spi->espi_base->mask, 0);
+
+	return mpc8xxx_spi->count;
+}
+
+static void fsl_espi_do_one_msg(struct spi_message *m)
+{
+	struct spi_device *spi = m->spi;
+	struct mpc8xxx_spi *mspi = spi_master_get_devdata(spi->master);
+	struct spi_message message;
+	struct spi_transfer *t, *first, trans;
+	u8 *local_buf, *rx_buf = NULL;
+	unsigned int n_tx = 0;
+	unsigned int n_rx = 0;
+	int status = 0;
+	int i = 0;
+
+	spi_message_init(&message);
+	memset(&trans, 0, sizeof(trans));
+
+	first = list_first_entry(&m->transfers, struct spi_transfer,
+			transfer_list);
+	list_for_each_entry(t, &m->transfers, transfer_list) {
+		if ((first->bits_per_word != t->bits_per_word) ||
+			(first->speed_hz != t->speed_hz)) {
+			status = -EINVAL;
+			dev_err(mspi->dev, "bits_per_word/speed_hz should be"
+					" same for the same SPI transfer\n");
+			return;
+		}
+
+		trans.speed_hz = t->speed_hz;
+		trans.bits_per_word = t->bits_per_word;
+		trans.delay_usecs = max(first->delay_usecs, t->delay_usecs);
+
+		if (t->tx_buf)
+			n_tx += t->len;
+
+		if (t->rx_buf) {
+			n_rx += t->len;
+			rx_buf = t->rx_buf;
+		}
+	}
+
+	local_buf = kzalloc(n_tx * 2 + roundup(n_rx + n_tx, 4), GFP_KERNEL);
+	if (!local_buf) {
+		status = -ENOMEM;
+		return;
+	}
+
+	list_for_each_entry(t, &m->transfers, transfer_list) {
+		if (t->tx_buf) {
+			memcpy(local_buf + i, t->tx_buf, t->len);
+			i += t->len;
+		}
+	}
+
+	trans.len = n_tx + n_rx;
+	trans.tx_buf = local_buf;
+	trans.rx_buf = local_buf + n_tx;
+	spi_message_add_tail(&trans, &message);
+
+	list_for_each_entry(t, &message.transfers, transfer_list) {
+		if (t->bits_per_word || t->speed_hz) {
+			status = -EINVAL;
+
+			status = fsl_espi_setup_transfer(spi, t);
+			if (status < 0)
+				break;
+		}
+
+		if (t->len)
+			status = fsl_espi_bufs(spi, t, 0);
+		if (status) {
+			status = -EMSGSIZE;
+			break;
+		}
+		m->actual_length += t->len;
+
+		if (rx_buf)
+			memcpy(rx_buf, t->rx_buf + n_tx, n_rx);
+
+		if (t->delay_usecs)
+			udelay(t->delay_usecs);
+	}
+
+	m->status = status;
+	m->complete(m->context);
+
+	fsl_espi_setup_transfer(spi, NULL);
+	kfree(local_buf);
+}
+
+static int fsl_espi_setup(struct spi_device *spi)
+{
+	struct mpc8xxx_spi *mpc8xxx_spi;
+	int retval;
+	u32 hw_mode;
+	u32 loop_mode;
+	struct spi_mpc8xxx_cs	*cs = spi->controller_state;
+
+	if (!spi->max_speed_hz)
+		return -EINVAL;
+
+	if (!cs) {
+		cs = kzalloc(sizeof *cs, GFP_KERNEL);
+		if (!cs)
+			return -ENOMEM;
+		spi->controller_state = cs;
+	}
+
+	mpc8xxx_spi = spi_master_get_devdata(spi->master);
+
+	hw_mode = cs->hw_mode; /* Save orginal settings */
+	cs->hw_mode = mpc8xxx_spi_read_reg(
+			&mpc8xxx_spi->espi_base->csmode[spi->chip_select]);
+	/* mask out bits we are going to set */
+	cs->hw_mode &= ~(CSMODE_CP_BEGIN_EDGECLK | CSMODE_CI_INACTIVEHIGH
+			 | CSMODE_REV);
+
+	if (spi->mode & SPI_CPHA)
+		cs->hw_mode |= CSMODE_CP_BEGIN_EDGECLK;
+	if (spi->mode & SPI_CPOL)
+		cs->hw_mode |= CSMODE_CI_INACTIVEHIGH;
+	if (!(spi->mode & SPI_LSB_FIRST))
+		cs->hw_mode |= CSMODE_REV;
+
+	/* Handle the loop mode */
+	loop_mode = mpc8xxx_spi_read_reg(&mpc8xxx_spi->espi_base->mode);
+	loop_mode &= ~SPMODE_LOOP;
+	if (spi->mode & SPI_LOOP)
+		loop_mode |= SPMODE_LOOP;
+	mpc8xxx_spi_write_reg(&mpc8xxx_spi->espi_base->mode, loop_mode);
+
+	retval = fsl_espi_setup_transfer(spi, NULL);
+	if (retval < 0) {
+		cs->hw_mode = hw_mode; /* Restore settings */
+		return retval;
+	}
+	return 0;
+}
+
+static void fsl_espi_cpu_irq(struct mpc8xxx_spi *mspi, u32 events)
+{
+	/* We need handle RX first */
+	if (events & SPIE_NE) {
+		u32 rx_data;
+
+		/* Spin until RX is done */
+		while (SPIE_RXCNT(events) < min(4, mspi->len)) {
+			cpu_relax();
+			events = mpc8xxx_spi_read_reg(&mspi->espi_base->event);
+		}
+		mspi->len -= 4;
+
+		rx_data = mpc8xxx_spi_read_reg(&mspi->espi_base->receive);
+
+		if (mspi->rx)
+			mspi->get_rx(rx_data, mspi);
+	}
+
+	if ((events & SPIE_NF) == 0)
+		/* spin until TX is done */
+		while (((events =
+			mpc8xxx_spi_read_reg(&mspi->espi_base->event)) &
+						SPIE_NF) == 0)
+			cpu_relax();
+
+	/* Clear the events */
+	mpc8xxx_spi_write_reg(&mspi->espi_base->event, events);
+
+	mspi->count -= 1;
+	if (mspi->count) {
+		u32 word = mspi->get_tx(mspi);
+
+		mpc8xxx_spi_write_reg(&mspi->espi_base->transmit, word);
+	} else {
+		complete(&mspi->done);
+	}
+}
+
+irqreturn_t fsl_espi_irq(s32 irq, void *context_data)
+{
+	struct mpc8xxx_spi *mspi = context_data;
+	irqreturn_t ret = IRQ_NONE;
+	u32 events;
+
+	/* Get interrupt events(tx/rx) */
+	events = mpc8xxx_spi_read_reg(&mspi->espi_base->event);
+	if (events)
+		ret = IRQ_HANDLED;
+
+	dev_dbg(mspi->dev, "%s: events %x\n", __func__, events);
+
+	fsl_espi_cpu_irq(mspi, events);
+
+	return ret;
+}
+
+static void fsl_espi_remove(struct mpc8xxx_spi *mspi)
+{
+	iounmap(mspi->espi_base);
+}
+
+static struct spi_master * __devinit fsl_espi_probe(struct device *dev,
+		struct resource *mem, unsigned int irq)
+{
+	struct fsl_spi_platform_data *pdata = dev->platform_data;
+	struct spi_master *master;
+	struct mpc8xxx_spi *mpc8xxx_spi;
+	u32 regval;
+	int i, ret = 0;
+
+	master = spi_alloc_master(dev, sizeof(struct mpc8xxx_spi));
+	if (master == NULL) {
+		ret = -ENOMEM;
+		goto err;
+	}
+
+	dev_set_drvdata(dev, master);
+
+	ret = mpc8xxx_spi_probe(dev, mem, irq);
+	if (ret)
+		goto err_probe;
+
+	master->setup = fsl_espi_setup;
+
+	mpc8xxx_spi = spi_master_get_devdata(master);
+	mpc8xxx_spi->spi_do_one_msg = fsl_espi_do_one_msg;
+	mpc8xxx_spi->spi_remove = fsl_espi_remove;
+
+	mpc8xxx_spi->espi_base = ioremap(mem->start, resource_size(mem));
+	if (mpc8xxx_spi->espi_base == NULL) {
+		ret = -ENOMEM;
+		goto err_probe;
+	}
+
+	/* Register for SPI Interrupt */
+	ret = request_irq(mpc8xxx_spi->irq, fsl_espi_irq,
+			  0, "fsl_espi", mpc8xxx_spi);
+
+	if (ret != 0)
+		goto err_free_irq;
+
+	if (mpc8xxx_spi->flags & SPI_QE_CPU_MODE) {
+		mpc8xxx_spi->rx_shift = 16;
+		mpc8xxx_spi->tx_shift = 24;
+	}
+
+	/* SPI controller initializations */
+	mpc8xxx_spi_write_reg(&mpc8xxx_spi->espi_base->mode, 0);
+	mpc8xxx_spi_write_reg(&mpc8xxx_spi->espi_base->mask, 0);
+	mpc8xxx_spi_write_reg(&mpc8xxx_spi->espi_base->command, 0);
+	mpc8xxx_spi_write_reg(&mpc8xxx_spi->espi_base->event, 0xffffffff);
+
+	/* Init eSPI CS mode register */
+	for (i = 0; i < pdata->max_chipselect; i++)
+		mpc8xxx_spi_write_reg(&mpc8xxx_spi->espi_base->csmode[i],
+				CSMODE_INIT_VAL);
+
+	/* Enable SPI interface */
+	regval = pdata->initial_spmode | SPMODE_INIT_VAL | SPMODE_ENABLE;
+
+	mpc8xxx_spi_write_reg(&mpc8xxx_spi->espi_base->mode, regval);
+
+	ret = spi_register_master(master);
+	if (ret < 0)
+		goto unreg_master;
+
+	dev_info(dev, "at 0x%p (irq = %d)\n", mpc8xxx_spi->espi_base,
+			mpc8xxx_spi->irq);
+
+	return master;
+
+unreg_master:
+	free_irq(mpc8xxx_spi->irq, mpc8xxx_spi);
+err_free_irq:
+	iounmap(mpc8xxx_spi->espi_base);
+err_probe:
+	spi_master_put(master);
+err:
+	return ERR_PTR(ret);
+}
+
+static int of_fsl_espi_get_chipselects(struct device *dev)
+{
+	struct device_node *np = dev->of_node;
+	struct fsl_spi_platform_data *pdata = dev->platform_data;
+	const u32 *prop;
+	int len;
+
+	prop = of_get_property(np, "fsl,espi-num-chipselects", &len);
+	if (!prop || len < sizeof(*prop)) {
+		dev_err(dev, "No 'fsl,espi-num-chipselects' property\n");
+		return -EINVAL;
+	}
+
+	pdata->max_chipselect = *prop;
+	pdata->cs_control = NULL;
+
+	return 0;
+}
+
+static int __devinit of_fsl_espi_probe(struct of_device *ofdev,
+					  const struct of_device_id *ofid)
+{
+	struct device *dev = &ofdev->dev;
+	struct device_node *np = ofdev->dev.of_node;
+	struct spi_master *master;
+	struct resource mem;
+	struct resource irq;
+	int ret = -ENOMEM;
+
+	ret = of_mpc8xxx_spi_probe(ofdev, ofid);
+	if (ret)
+		return ret;
+
+	ret = of_fsl_espi_get_chipselects(dev);
+	if (ret)
+		goto err;
+
+	ret = of_address_to_resource(np, 0, &mem);
+	if (ret)
+		goto err;
+
+	ret = of_irq_to_resource(np, 0, &irq);
+	if (!ret) {
+		ret = -EINVAL;
+		goto err;
+	}
+
+	master = fsl_espi_probe(dev, &mem, irq.start);
+	if (IS_ERR(master)) {
+		ret = PTR_ERR(master);
+		goto err;
+	}
+
+	of_register_spi_devices(master, np);
+
+	return 0;
+
+err:
+	return ret;
+}
+
+static int __devexit of_fsl_espi_remove(struct of_device *ofdev)
+{
+	int ret;
+
+	ret = mpc8xxx_spi_remove(&ofdev->dev);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static const struct of_device_id of_fsl_espi_match[] = {
+	{ .compatible = "fsl,mpc8536-espi" },
+	{}
+};
+MODULE_DEVICE_TABLE(of, of_fsl_espi_match);
+
+static struct of_platform_driver of_fsl_espi_driver = {
+	.driver = {
+		.name = "fsl_espi",
+		.owner = THIS_MODULE,
+		.of_match_table = of_fsl_espi_match,
+	},
+	.probe		= of_fsl_espi_probe,
+	.remove		= __devexit_p(of_fsl_espi_remove),
+};
+
+static int __init fsl_espi_init(void)
+{
+	return of_register_platform_driver(&of_fsl_espi_driver);
+}
+module_init(fsl_espi_init);
+
+static void __exit fsl_espi_exit(void)
+{
+	of_unregister_platform_driver(&of_fsl_espi_driver);
+}
+module_exit(fsl_espi_exit);
+
+MODULE_AUTHOR("Mingkai Hu");
+MODULE_DESCRIPTION("Enhanced Freescale SPI Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/spi/spi_fsl_lib.h b/drivers/spi/spi_fsl_lib.h
index 774e1c8..0772c98 100644
--- a/drivers/spi/spi_fsl_lib.h
+++ b/drivers/spi/spi_fsl_lib.h
@@ -22,10 +22,12 @@
 struct mpc8xxx_spi {
 	struct device *dev;
 	struct fsl_spi_reg __iomem *base;
+	struct fsl_espi_reg __iomem *espi_base;
 
 	/* rx & tx bufs from the spi_transfer */
 	const void *tx;
 	void *rx;
+	int len;
 
 	int subblock;
 	struct spi_pram __iomem *pram;
-- 
1.6.4

^ permalink raw reply related

* [PATCH v2 5/6] powerpc/of: add eSPI controller dts bindings
From: Mingkai Hu @ 2010-08-02  7:52 UTC (permalink / raw)
  To: linuxppc-dev, spi-devel-general; +Cc: kumar.gala, Mingkai Hu
In-Reply-To: <1280735524-17547-5-git-send-email-Mingkai.hu@freescale.com>

Also modifiy the document of cell-index in SPI controller.

Signed-off-by: Mingkai Hu <Mingkai.hu@freescale.com>
---

v2:
 - Add cell-index clarification
 - Add mpc8536 chip name to the compatible value

 Documentation/powerpc/dts-bindings/fsl/spi.txt |   24 +++++++++++++++++++++++-
 1 files changed, 23 insertions(+), 1 deletions(-)

diff --git a/Documentation/powerpc/dts-bindings/fsl/spi.txt b/Documentation/powerpc/dts-bindings/fsl/spi.txt
index 80510c0..01543ca 100644
--- a/Documentation/powerpc/dts-bindings/fsl/spi.txt
+++ b/Documentation/powerpc/dts-bindings/fsl/spi.txt
@@ -1,7 +1,9 @@
 * SPI (Serial Peripheral Interface)
 
 Required properties:
-- cell-index : SPI controller index.
+- cell-index : QE SPI subblock index.
+ 	       0: QE subblock SPI1
+ 	       1: QE subblock SPI2
 - compatible : should be "fsl,spi".
 - mode : the SPI operation mode, it can be "cpu" or "cpu-qe".
 - reg : Offset and length of the register set for the device
@@ -29,3 +31,23 @@ Example:
 		gpios = <&gpio 18 1	// device reg=<0>
 			 &gpio 19 1>;	// device reg=<1>
 	};
+
+
+* eSPI (Enhanced Serial Peripheral Interface)
+
+Required properties:
+- compatible : should be "fsl,mpc8536-espi".
+- reg : Offset and length of the register set for the device.
+- interrupts : should contain eSPI interrupt, the device has one interrupt.
+- fsl,espi-num-chipselects : the number of the chipselect signals.
+
+Example:
+	spi@110000 {
+		#address-cells = <1>;
+		#size-cells = <0>;
+		compatible = "fsl,espi";
+		reg = <0x110000 0x1000>;
+		interrupts = <53 0x2>;
+		interrupt-parent = <&mpic>;
+		fsl,espi-num-chipselects = <4>;
+	};
-- 
1.6.4

^ permalink raw reply related

* [PATCH v2 6/6] DTS: add SPI flash(s25fl128p01) support on p4080ds and mpc8536ds board
From: Mingkai Hu @ 2010-08-02  7:52 UTC (permalink / raw)
  To: linuxppc-dev, spi-devel-general; +Cc: kumar.gala, Mingkai Hu
In-Reply-To: <1280735524-17547-6-git-send-email-Mingkai.hu@freescale.com>

Signed-off-by: Mingkai Hu <Mingkai.hu@freescale.com>
---

v2:
 - Remove the whitespace inconsitencies

 arch/powerpc/boot/dts/mpc8536ds.dts |   52 +++++++++++++++++++++++++++++++++++
 arch/powerpc/boot/dts/p4080ds.dts   |   11 +++-----
 2 files changed, 56 insertions(+), 7 deletions(-)

diff --git a/arch/powerpc/boot/dts/mpc8536ds.dts b/arch/powerpc/boot/dts/mpc8536ds.dts
index 815cebb..a75c10e 100644
--- a/arch/powerpc/boot/dts/mpc8536ds.dts
+++ b/arch/powerpc/boot/dts/mpc8536ds.dts
@@ -108,6 +108,58 @@
 			};
 		};
 
+		spi@7000 {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			compatible = "fsl,mpc8536-espi";
+			reg = <0x7000 0x1000>;
+			interrupts = <59 0x2>;
+			interrupt-parent = <&mpic>;
+			fsl,espi-num-chipselects = <4>;
+
+			flash@0 {
+				#address-cells = <1>;
+				#size-cells = <1>;
+				compatible = "spansion,s25sl12801";
+				reg = <0>;
+				spi-max-frequency = <40000000>;
+				partition@u-boot {
+					label = "u-boot";
+					reg = <0x00000000 0x00100000>;
+					read-only;
+				};
+				partition@kernel {
+					label = "kernel";
+					reg = <0x00100000 0x00500000>;
+					read-only;
+				};
+				partition@dtb {
+					label = "dtb";
+					reg = <0x00600000 0x00100000>;
+					read-only;
+				};
+				partition@fs {
+					label = "file system";
+					reg = <0x00700000 0x00900000>;
+				};
+			};
+			flash@1 {
+				compatible = "spansion,s25sl12801";
+				reg = <1>;
+				spi-max-frequency = <40000000>;
+			};
+			flash@2 {
+				compatible = "spansion,s25sl12801";
+				reg = <2>;
+				spi-max-frequency = <40000000>;
+			};
+			flash@3 {
+				compatible = "spansion,s25sl12801";
+				reg = <3>;
+				spi-max-frequency = <40000000>;
+			};
+		};
+
 		dma@21300 {
 			#address-cells = <1>;
 			#size-cells = <1>;
diff --git a/arch/powerpc/boot/dts/p4080ds.dts b/arch/powerpc/boot/dts/p4080ds.dts
index 6b29eab..48437ad 100644
--- a/arch/powerpc/boot/dts/p4080ds.dts
+++ b/arch/powerpc/boot/dts/p4080ds.dts
@@ -236,22 +236,19 @@
 		};
 
 		spi@110000 {
-			cell-index = <0>;
 			#address-cells = <1>;
 			#size-cells = <0>;
-			compatible = "fsl,espi";
+			compatible = "fsl,mpc8536-espi";
 			reg = <0x110000 0x1000>;
 			interrupts = <53 0x2>;
 			interrupt-parent = <&mpic>;
-			espi,num-ss-bits = <4>;
-			mode = "cpu";
+			fsl,espi-num-chipselects = <4>;
 
-			fsl_m25p80@0 {
+			flash@0 {
 				#address-cells = <1>;
 				#size-cells = <1>;
-				compatible = "fsl,espi-flash";
+				compatible = "spansion,s25sl12801";
 				reg = <0>;
-				linux,modalias = "fsl_m25p80";
 				spi-max-frequency = <40000000>; /* input clock */
 				partition@u-boot {
 					label = "u-boot";
-- 
1.6.4

^ permalink raw reply related

* [PATCH v2 0/6] refactor spi_mpc8xxx.c and add eSPI controller support
From: Mingkai Hu @ 2010-08-02  7:51 UTC (permalink / raw)
  To: linuxppc-dev, spi-devel-general; +Cc: kumar.gala

In-Reply-To: 

This patchset refactor the file spi_mpc8xxx.c to abstract some common
code as a lib used by the SPI/eSPI controller driver, move the SPI 
controller driver code to spi_fsl_spi.c, and add the eSPI controller
support with spi_fsl_espi.c.

Tested on P4080DS and MPC8536DS board based on latest Linux tree.

[PATCH v2 1/6] spi/mpc8xxx: refactor the common code for SPI/eSPI controller
[PATCH v2 2/6] eSPI: add eSPI controller support
[PATCH v2 3/6] mtd: m25p80: add support to parse the SPI flash's partitions
[PATCH v2 4/6] mtd: m25p80: add a read function to read page by page
[PATCH v2 5/6] powerpc/of: add eSPI controller dts bindings
[PATCH v2 6/6] DTS: add SPI flash(s25fl128p01) support on p4080ds and mpc8536ds board

Thanks,
Mingkai

^ permalink raw reply

* Re: [PATCH 02/11] powerpc/nvram: More flexible nvram_create_partition()
From: Benjamin Herrenschmidt @ 2010-08-02  7:02 UTC (permalink / raw)
  To: linuxppc-dev
In-Reply-To: <1280710522-6362-3-git-send-email-benh@kernel.crashing.org>

On Mon, 2010-08-02 at 10:55 +1000, Benjamin Herrenschmidt wrote:
> Replace nvram_create_os_partition() with a variant that takes
> the partition name, signature and size as arguments.
> 
> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
> ---
>  arch/powerpc/kernel/nvram_64.c         |   46 +++++++++++++++++++------------
>  arch/powerpc/platforms/pseries/nvram.c |    6 ++--
>  2 files changed, 31 insertions(+), 21 deletions(-)
> 
> diff --git a/arch/powerpc/kernel/nvram_64.c b/arch/powerpc/kernel/nvram_64.c
> index a8154f1..974b3ec 100644
> --- a/arch/powerpc/kernel/nvram_64.c
> +++ b/arch/powerpc/kernel/nvram_64.c
> @@ -307,13 +307,15 @@ static int __init nvram_remove_os_partition(void)
>  	return 0;
>  }
>  
> -/* nvram_create_os_partition
> - *
> - * Create a OS linux partition to buffer error logs.
> - * Will create a partition starting at the first free
> - * space found if space has enough room.
> +/**
> + * nvram_create_partition - Create a partition in nvram
> + * @name: name of the partition to create
> + * @sig: signature of the partition to create
> + * @req_size: size to allocate preferrably
> + * @min_size: minimum acceptable size (0 means req_size)
>   */
> -static int __init nvram_create_os_partition(void)
> +static int __init nvram_create_partition(const char *name, int sig,
> +					 int req_size, int min_size)
>  {

Order of args is req , min but...

> +	rc = 	nvram_create_partition("ppc64,linux", NVRAM_SIG_OS,
> +				       NVRAM_MIN_REQ, NVRAM_MAX_REQ);
>  	if (rc) {

I call it the other way around :-)

I should have tested this more before sending for RFC :-) Anyways, I
found more crap, like the way the list head is allocated etc...

I'll do another pass this week after I've dealt with the merge window.

Cheers,
Ben.

^ permalink raw reply

* Re: [PATCH 3/3 v2] mmc: Add ESDHC weird voltage bits workaround
From: Anton Vorontsov @ 2010-08-02  6:52 UTC (permalink / raw)
  To: Zang Roy-R61911; +Cc: linuxppc-dev, akpm, linux-mmc
In-Reply-To: <83024545B35D6445A690EFD603E7869A07ED05@zch01exm23.fsl.freescale.net>

On Mon, Aug 02, 2010 at 02:19:58PM +0800, Zang Roy-R61911 wrote:
[...]
> > For p4080 it will be 'voltage-ranges = <3200 3400>;'. So, with
> > voltage-ranges we can do fine grained VDD control without
> > introducing anything new.
> why not
>                voltage-ranges = <3300 3300>;

Right you are, both will be 3300.

Thanks,

-- 
Anton Vorontsov
email: cbouatmailru@gmail.com
irc://irc.freenode.net/bd2

^ permalink raw reply

* Re: ramdisk size is larger than 4MB
From: Shawn Jin @ 2010-08-02  6:34 UTC (permalink / raw)
  To: Scott Wood; +Cc: ppcdev
In-Reply-To: <AANLkTim0pD9LWEgWjX4QvqUkZkYO3YOeELz_XjNJPDft@mail.gmail.com>

>> It should be fine to just change it locally. =A0It would be a problem to
>> change it upstream for all boards, since some supported boards have
>> only 16MB (or even 8MB) of RAM.
>
> I'll definitely try to change it locally first. Would a configurable
> base address for the bootwrapper an acceptable solution?

I found the link_address in the wrapper shell script sets the _start
address. But after changing it to 0x800000, the kernel failed to boot,
shown below. There must be something also needs proper adjustment.
What would that be?

=3D> bootm 4000000
## Booting image at 04000000 ...
   Image Name:   Linux-2.6.33.5
   Image Type:   PowerPC Linux Kernel Image (gzip compressed)
   Data Size:    1757356 Bytes =3D  1.7 MB
   Load Address: 00800000
   Entry Point:  00800554
   Verifying Checksum ... OK
   Uncompressing Kernel Image ... OK
Memory <- <0x0 0x8000000> (128MB)
ENET0: local-mac-address <- 00:09:9b:01:58:64
CPU clock-frequency <- 0x7270e00 (120MHz)
CPU timebase-frequency <- 0x7270e0 (8MHz)
CPU bus-frequency <- 0x3938700 (60MHz)

zImage starting: loaded at 0x00800000 (sp: 0x07d1cbd0)
Allocating 0x3a15a4 bytes for kernel ...
gunzipping (0x00000000 <- 0x0080c000:0x00bd702c)...done 0x3886ec bytes

Linux/PowerPC load: root=3D/dev/ram
Finalizing device tree... flat tree at 0xbe4300

Thanks a lot,
-Shawn.

^ permalink raw reply

* RE: [PATCH 3/3 v2] mmc: Add ESDHC weird voltage bits workaround
From: Zang Roy-R61911 @ 2010-08-02  6:19 UTC (permalink / raw)
  To: Anton Vorontsov; +Cc: linuxppc-dev, akpm, linux-mmc
In-Reply-To: <20100730070624.GB20351@oksana.dev.rtsoft.ru>

=20

> -----Original Message-----
> From: Anton Vorontsov [mailto:cbouatmailru@gmail.com]=20
> Sent: Friday, July 30, 2010 15:06 PM
> To: Zang Roy-R61911
> Cc: linux-mmc@vger.kernel.org; linuxppc-dev@ozlabs.org;=20
> akpm@linux-foundation.org
> Subject: Re: [PATCH 3/3 v2] mmc: Add ESDHC weird voltage bits=20
> workaround
>=20
> On Fri, Jul 30, 2010 at 11:52:57AM +0800, Roy Zang wrote:
> > P4080 ESDHC controller does not support 1.8V and 3.0V=20
> voltage. but the
> > host controller capabilities register wrongly set the bits.
> > This patch adds the workaround to correct the weird voltage=20
> setting bits.
> >=20
> > Signed-off-by: Roy Zang <tie-fei.zang@freescale.com>
> > ---
> [...]
> > diff --git a/drivers/mmc/host/sdhci-of-core.c=20
> b/drivers/mmc/host/sdhci-of-core.c
> > index 0c30242..1f3913d 100644
> > --- a/drivers/mmc/host/sdhci-of-core.c
> > +++ b/drivers/mmc/host/sdhci-of-core.c
> > @@ -164,6 +164,10 @@ static int __devinit=20
> sdhci_of_probe(struct of_device *ofdev,
> >  	if (sdhci_of_wp_inverted(np))
> >  		host->quirks |=3D SDHCI_QUIRK_INVERTED_WRITE_PROTECT;
> > =20
> > +	if (of_device_is_compatible(np, "fsl,p4080-esdhc"))
> > +		host->quirks |=3D (SDHCI_QUIRK_QORIQ_NO_VDD_180
> > +				|SDHCI_QUIRK_QORIQ_NO_VDD_300);
> > +
>=20
> It should be two properties, something like sdhci,no-vdd-180
> and sdhci,no-vdd-300. But it might be even better: we have
> voltage-ranges for mmc-spi case, see
> Documentation/powerpc/dts-bindings/mmc-spi-slot.txt.
>=20
> If voltage-ranges specified, then we use it, not capabilities
> register.
>=20
> For p4080 it will be 'voltage-ranges =3D <3200 3400>;'. So, with
> voltage-ranges we can do fine grained VDD control without
> introducing anything new.
why not
               voltage-ranges =3D <3300 3300>;
?
Roy

^ permalink raw reply

* [PATCH] perf, powerpc: Convert the FSL driver to use local64_t
From: Paul Mackerras @ 2010-08-02  6:18 UTC (permalink / raw)
  To: linuxppc-dev; +Cc: Peter Zijlstra

From: Peter Zijlstra <a.p.zijlstra@chello.nl>

For some reason the FSL driver got left out when we converted perf
to use local64_t instead of atomic64_t.

Signed-off-by: Peter Zijlstra <a.p.zijlstra@chello.nl>
---
This is against the perf/core branch in the tip tree at
git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip
Posting here for comment.

 arch/powerpc/kernel/perf_event_fsl_emb.c |   28 ++++++++++++++--------------
 1 file changed, 14 insertions(+), 14 deletions(-)

Index: linux-2.6/arch/powerpc/kernel/perf_event_fsl_emb.c
===================================================================
--- linux-2.6.orig/arch/powerpc/kernel/perf_event_fsl_emb.c
+++ linux-2.6/arch/powerpc/kernel/perf_event_fsl_emb.c
@@ -162,15 +162,15 @@ static void fsl_emb_pmu_read(struct perf
 	 * Therefore we treat them like NMIs.
 	 */
 	do {
-		prev = atomic64_read(&event->hw.prev_count);
+		prev = local64_read(&event->hw.prev_count);
 		barrier();
 		val = read_pmc(event->hw.idx);
-	} while (atomic64_cmpxchg(&event->hw.prev_count, prev, val) != prev);
+	} while (local64_cmpxchg(&event->hw.prev_count, prev, val) != prev);
 
 	/* The counters are only 32 bits wide */
 	delta = (val - prev) & 0xfffffffful;
-	atomic64_add(delta, &event->count);
-	atomic64_sub(delta, &event->hw.period_left);
+	local64_add(delta, &event->count);
+	local64_sub(delta, &event->hw.period_left);
 }
 
 /*
@@ -296,11 +296,11 @@ static int fsl_emb_pmu_enable(struct per
 
 	val = 0;
 	if (event->hw.sample_period) {
-		s64 left = atomic64_read(&event->hw.period_left);
+		s64 left = local64_read(&event->hw.period_left);
 		if (left < 0x80000000L)
 			val = 0x80000000L - left;
 	}
-	atomic64_set(&event->hw.prev_count, val);
+	local64_set(&event->hw.prev_count, val);
 	write_pmc(i, val);
 	perf_event_update_userpage(event);
 
@@ -371,8 +371,8 @@ static void fsl_emb_pmu_unthrottle(struc
 	if (left < 0x80000000L)
 		val = 0x80000000L - left;
 	write_pmc(event->hw.idx, val);
-	atomic64_set(&event->hw.prev_count, val);
-	atomic64_set(&event->hw.period_left, left);
+	local64_set(&event->hw.prev_count, val);
+	local64_set(&event->hw.period_left, left);
 	perf_event_update_userpage(event);
 	perf_enable();
 	local_irq_restore(flags);
@@ -500,7 +500,7 @@ const struct pmu *hw_perf_event_init(str
 		return ERR_PTR(-ENOTSUPP);
 
 	event->hw.last_period = event->hw.sample_period;
-	atomic64_set(&event->hw.period_left, event->hw.last_period);
+	local64_set(&event->hw.period_left, event->hw.last_period);
 
 	/*
 	 * See if we need to reserve the PMU.
@@ -541,16 +541,16 @@ static void record_and_restart(struct pe
 	int record = 0;
 
 	/* we don't have to worry about interrupts here */
-	prev = atomic64_read(&event->hw.prev_count);
+	prev = local64_read(&event->hw.prev_count);
 	delta = (val - prev) & 0xfffffffful;
-	atomic64_add(delta, &event->count);
+	local64_add(delta, &event->count);
 
 	/*
 	 * See if the total period for this event has expired,
 	 * and update for the next period.
 	 */
 	val = 0;
-	left = atomic64_read(&event->hw.period_left) - delta;
+	left = local64_read(&event->hw.period_left) - delta;
 	if (period) {
 		if (left <= 0) {
 			left += period;
@@ -584,8 +584,8 @@ static void record_and_restart(struct pe
 	}
 
 	write_pmc(event->hw.idx, val);
-	atomic64_set(&event->hw.prev_count, val);
-	atomic64_set(&event->hw.period_left, left);
+	local64_set(&event->hw.prev_count, val);
+	local64_set(&event->hw.period_left, left);
 	perf_event_update_userpage(event);
 }
 

^ permalink raw reply

* Re: [PATCH 02/11] powerpc/nvram: More flexible nvram_create_partition()
From: Benjamin Herrenschmidt @ 2010-08-02  3:54 UTC (permalink / raw)
  To: michael; +Cc: linuxppc-dev
In-Reply-To: <1280720607.2491.175.camel@concordia>

On Mon, 2010-08-02 at 13:43 +1000, Michael Ellerman wrote:

> >  	
> >  	/* try creating a partition with the free space we have */
> > -	rc = nvram_create_partition("ppc64,linux", );
> 
> That looks odd? Trailing comma.

Yeah, that and...

> > diff --git a/arch/powerpc/platforms/pseries/nvram.c b/arch/powerpc/platforms/pseries/nvram.c
> > index f4e4c06..2a1ef5c 100644
> > --- a/arch/powerpc/platforms/pseries/nvram.c
> > +++ b/arch/powerpc/platforms/pseries/nvram.c
> > @@ -22,14 +22,14 @@
> >  #include <asm/prom.h>
> >  #include <asm/machdep.h>
> >  
> > +/* Max bytes to read/write in one go */
> > +#define NVRW_CNT 0x20
> > +
> >  static unsigned int nvram_size;
> >  static int nvram_fetch, nvram_store;
> >  static char nvram_buf[NVRW_CNT];	/* assume this is in the first 4GB */
> >  static DEFINE_SPINLOCK(nvram_lock);
> >  
> > -/* Max bytes to read/write in one go */
> > -#define NVRW_CNT 0x20
> > -
> 
> Churn alert, you just moved that hunk there in the previous patch.

 ... that are local rebasing fu*kage.

I'll sort all that out in the next version.

Thanks,
Ben.

^ permalink raw reply

* Re: [PATCH 05/11] powerpc/nvram: Completely clear a new partition
From: Michael Ellerman @ 2010-08-02  3:50 UTC (permalink / raw)
  To: Benjamin Herrenschmidt; +Cc: linuxppc-dev
In-Reply-To: <1280710522-6362-6-git-send-email-benh@kernel.crashing.org>

[-- Attachment #1: Type: text/plain, Size: 1714 bytes --]

On Mon, 2010-08-02 at 10:55 +1000, Benjamin Herrenschmidt wrote:
> When creating a partition, we clear it entirely rather than
> just the first two words since the previous code was rather
> specific to the pseries log partition format.
> 
> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
> ---
>  arch/powerpc/kernel/nvram_64.c |   19 ++++++++++---------
>  1 files changed, 10 insertions(+), 9 deletions(-)
> 
> diff --git a/arch/powerpc/kernel/nvram_64.c b/arch/powerpc/kernel/nvram_64.c
> index dd18a5a..7ddeaa6 100644
> --- a/arch/powerpc/kernel/nvram_64.c
> +++ b/arch/powerpc/kernel/nvram_64.c
> @@ -320,7 +320,7 @@ static int __init nvram_create_partition(const char *name, int sig,
>  	struct nvram_partition *part;
>  	struct nvram_partition *new_part;
>  	struct nvram_partition *free_part = NULL;
> -	int seq_init[2] = { 0, 0 };
> +	static char nv_init_vals[16];

Or should we just grab a zeroed page?

>  	loff_t tmp_index;
>  	long size = 0;
>  	int rc;
> @@ -379,14 +379,15 @@ static int __init nvram_create_partition(const char *name, int sig,
>  		return rc;
>  	}
>  
> -	/* make sure and initialize to zero the sequence number and the error
> -	   type logged */
> -	tmp_index = new_part->index + NVRAM_HEADER_LEN;
> -	rc = ppc_md.nvram_write((char *)&seq_init, sizeof(seq_init), &tmp_index);
> -	if (rc <= 0) {
> -		printk(KERN_ERR "nvram_create_os_partition: nvram_write "
> -		       "failed (%d)\n", rc);
> -		return rc;
> +	/* Clear the partition */
> +	for (tmp_index = new_part->index + NVRAM_HEADER_LEN;
> +	     tmp_index <  ((size - 1) * NVRAM_BLOCK_LEN);
                          ^
Extra whitespace          |

cheers


[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 198 bytes --]

^ permalink raw reply

* Re: [PATCH 03/11] powerpc/nvram: nvram_create_partitions() now uses bytes
From: Michael Ellerman @ 2010-08-02  3:47 UTC (permalink / raw)
  To: Benjamin Herrenschmidt; +Cc: linuxppc-dev
In-Reply-To: <1280710522-6362-4-git-send-email-benh@kernel.crashing.org>

[-- Attachment #1: Type: text/plain, Size: 1194 bytes --]

On Mon, 2010-08-02 at 10:55 +1000, Benjamin Herrenschmidt wrote:
> This converts nvram_create_partition() to use a size in bytes
> rather than blocks. It does the appropriate alignment internally
> 
> The size passed is also the data size (ie. doesn't include the
> header anymore).
> 
> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
> ---
>  arch/powerpc/kernel/nvram_64.c |   20 ++++++++++++++------
>  1 files changed, 14 insertions(+), 6 deletions(-)
> 
> diff --git a/arch/powerpc/kernel/nvram_64.c b/arch/powerpc/kernel/nvram_64.c
> index 974b3ec..b0f2114 100644
> --- a/arch/powerpc/kernel/nvram_64.c
> +++ b/arch/powerpc/kernel/nvram_64.c
> @@ -34,10 +34,10 @@
>  
>  #undef DEBUG_NVRAM
>  
> -#define NVRAM_HEADER_LEN 16 /* sizeof(struct nvram_header) */
> -#define NVRAM_BLOCK_LEN 16
> -#define NVRAM_MAX_REQ (2080/NVRAM_BLOCK_LEN)
> -#define NVRAM_MIN_REQ (1056/NVRAM_BLOCK_LEN)
> +#define NVRAM_HEADER_LEN	sizeof(struct nvram_header)
> +#define NVRAM_BLOCK_LEN		NVRAM_HEADER_LEN
> +#define NVRAM_MAX_REQ		2079
> +#define NVRAM_MIN_REQ		1055

You don't change any callers, but that's because they use these #defines
right.

cheers

[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 198 bytes --]

^ permalink raw reply

* Re: [PATCH 02/11] powerpc/nvram: More flexible nvram_create_partition()
From: Michael Ellerman @ 2010-08-02  3:43 UTC (permalink / raw)
  To: Benjamin Herrenschmidt; +Cc: linuxppc-dev
In-Reply-To: <1280710522-6362-3-git-send-email-benh@kernel.crashing.org>

[-- Attachment #1: Type: text/plain, Size: 4888 bytes --]

On Mon, 2010-08-02 at 10:55 +1000, Benjamin Herrenschmidt wrote:
> Replace nvram_create_os_partition() with a variant that takes
> the partition name, signature and size as arguments.
> 
> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
> ---
>  arch/powerpc/kernel/nvram_64.c         |   46 +++++++++++++++++++------------
>  arch/powerpc/platforms/pseries/nvram.c |    6 ++--
>  2 files changed, 31 insertions(+), 21 deletions(-)
> 
> diff --git a/arch/powerpc/kernel/nvram_64.c b/arch/powerpc/kernel/nvram_64.c
> index a8154f1..974b3ec 100644
> --- a/arch/powerpc/kernel/nvram_64.c
> +++ b/arch/powerpc/kernel/nvram_64.c
> @@ -307,13 +307,15 @@ static int __init nvram_remove_os_partition(void)
>  	return 0;
>  }
>  
> -/* nvram_create_os_partition
> - *
> - * Create a OS linux partition to buffer error logs.
> - * Will create a partition starting at the first free
> - * space found if space has enough room.
> +/**
> + * nvram_create_partition - Create a partition in nvram
> + * @name: name of the partition to create
> + * @sig: signature of the partition to create
> + * @req_size: size to allocate preferrably
> + * @min_size: minimum acceptable size (0 means req_size)
>   */
> -static int __init nvram_create_os_partition(void)
> +static int __init nvram_create_partition(const char *name, int sig,
> +					 int req_size, int min_size)
>  {
>  	struct nvram_partition *part;
>  	struct nvram_partition *new_part;
> @@ -322,20 +324,27 @@ static int __init nvram_create_os_partition(void)
>  	loff_t tmp_index;
>  	long size = 0;
>  	int rc;
> -	
> +
> +	/* If no minimum size specified, make it the same as the
> +	 * requested size
> +	 */
> +	if (min_size == 0)
> +		min_size = req_size;
> +
>  	/* Find a free partition that will give us the maximum needed size 
>  	   If can't find one that will give us the minimum size needed */
>  	list_for_each_entry(part, &nvram_part->partition, partition) {
>  		if (part->header.signature != NVRAM_SIG_FREE)
>  			continue;
>  
> -		if (part->header.length >= NVRAM_MAX_REQ) {
> -			size = NVRAM_MAX_REQ;
> +		if (part->header.length >= req_size) {
> +			size = req_size;
>  			free_part = part;
>  			break;
>  		}
> -		if (!size && part->header.length >= NVRAM_MIN_REQ) {
> -			size = NVRAM_MIN_REQ;
> +		if (part->header.length > size &&
> +		    part->header.length >= min_size) {
> +			size = part->header.length;
>  			free_part = part;
>  		}

I can't quite convince myself that this is right, but perhaps I just
need a coffee.

> @@ -350,9 +359,9 @@ static int __init nvram_create_os_partition(void)
>  	}
>  
>  	new_part->index = free_part->index;
> -	new_part->header.signature = NVRAM_SIG_OS;
> +	new_part->header.signature = sig;
>  	new_part->header.length = size;
> -	strcpy(new_part->header.name, "ppc64,linux");
> +	strncpy(new_part->header.name, name, 12);

Should we be checking the name fits?

>  	new_part->header.checksum = nvram_checksum(&new_part->header);
>  
>  	rc = nvram_write_header(new_part);
> @@ -451,10 +460,10 @@ static int __init nvram_setup_partition(void)
>  	}
>  	
>  	/* try creating a partition with the free space we have */
> -	rc = nvram_create_partition("ppc64,linux", );

That looks odd? Trailing comma.

> -	if (!rc) {
> +	rc = 	nvram_create_partition("ppc64,linux", NVRAM_SIG_OS,
> +				       NVRAM_MIN_REQ, NVRAM_MAX_REQ);

Odd whitespace.

> +	if (!rc)
>  		return 0;
> -	}
>  		
>  	/* need to free up some space */
>  	rc = nvram_remove_os_partition();
> @@ -463,9 +472,10 @@ static int __init nvram_setup_partition(void)
>  	}
>  	
>  	/* create a partition in this new space */
> -	rc = nvram_create_os_partition();
> +	rc = 	nvram_create_partition("ppc64,linux", NVRAM_SIG_OS,
> +				       NVRAM_MIN_REQ, NVRAM_MAX_REQ);

And again.

>  	if (rc) {
> -		printk(KERN_ERR "nvram_create_os_partition: Could not find a "
> +		printk(KERN_ERR "nvram_create_partition: Could not find a "
>  		       "NVRAM partition large enough\n");

pr_err("%s: ..", __func__, ..) if you're touching it anyway?

> diff --git a/arch/powerpc/platforms/pseries/nvram.c b/arch/powerpc/platforms/pseries/nvram.c
> index f4e4c06..2a1ef5c 100644
> --- a/arch/powerpc/platforms/pseries/nvram.c
> +++ b/arch/powerpc/platforms/pseries/nvram.c
> @@ -22,14 +22,14 @@
>  #include <asm/prom.h>
>  #include <asm/machdep.h>
>  
> +/* Max bytes to read/write in one go */
> +#define NVRW_CNT 0x20
> +
>  static unsigned int nvram_size;
>  static int nvram_fetch, nvram_store;
>  static char nvram_buf[NVRW_CNT];	/* assume this is in the first 4GB */
>  static DEFINE_SPINLOCK(nvram_lock);
>  
> -/* Max bytes to read/write in one go */
> -#define NVRW_CNT 0x20
> -

Churn alert, you just moved that hunk there in the previous patch.

cheers



[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 198 bytes --]

^ permalink raw reply

* Re: [PATCH 10/11] powerpc/nvram: Move the log partition stuff to pseries
From: Benjamin Herrenschmidt @ 2010-08-02  1:40 UTC (permalink / raw)
  To: linuxppc-dev
In-Reply-To: <1280710522-6362-11-git-send-email-benh@kernel.crashing.org>

On Mon, 2010-08-02 at 10:55 +1000, Benjamin Herrenschmidt wrote:
> The nvram log partition stuff currently in nvram_64.c is really
> pseries specific. It isn't actually used on anything else (despite
> the fact that we ran the code to setup the partition on anything
> except powermac) and the log format is specific to pseries RTAS
> implementation. So move it where it belongs

Some glitches in this one, like the wrong init order. I'll move
the nvram scan to be explicitely called by archs who want that stuff.

In fact, I might move the whole partition handling to a separate
file to be selected by archs who want it, and try to merge that with
powermac, but at least we have a first step here.

I'll send an updated patch later, in case there are other comments.

Cheers,
Ben.

> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
> ---
>  arch/powerpc/include/asm/nvram.h       |    9 ++-
>  arch/powerpc/kernel/nvram_64.c         |  216 +-------------------------------
>  arch/powerpc/platforms/pseries/nvram.c |  196 +++++++++++++++++++++++++++++
>  3 files changed, 208 insertions(+), 213 deletions(-)
> 
> diff --git a/arch/powerpc/include/asm/nvram.h b/arch/powerpc/include/asm/nvram.h
> index 459dc09..457a1a5 100644
> --- a/arch/powerpc/include/asm/nvram.h
> +++ b/arch/powerpc/include/asm/nvram.h
> @@ -30,13 +30,14 @@
>  #include <linux/errno.h>
>  #include <linux/list.h>
>  
> +#ifdef CONFIG_PPC_PSERIES
>  extern int nvram_write_error_log(char * buff, int length,
>  					 unsigned int err_type, unsigned int err_seq);
>  extern int nvram_read_error_log(char * buff, int length,
>  					 unsigned int * err_type, unsigned int *err_seq);
>  extern int nvram_clear_error_log(void);
> -
>  extern int pSeries_nvram_init(void);
> +#endif /* CONFIG_PPC_PSERIES */
>  
>  #ifdef CONFIG_MMIO_NVRAM
>  extern int mmio_nvram_init(void);
> @@ -47,6 +48,12 @@ static inline int mmio_nvram_init(void)
>  }
>  #endif
>  
> +extern loff_t nvram_create_partition(const char *name, int sig,
> +				     int req_size, int min_size);
> +extern int nvram_remove_partition(const char *name, int sig);
> +extern int nvram_get_partition_size(loff_t data_index);
> +extern loff_t nvram_find_partition(const char *name, int sig, int *out_size);
> +
>  #endif /* __KERNEL__ */
>  
>  /* PowerMac specific nvram stuffs */
> diff --git a/arch/powerpc/kernel/nvram_64.c b/arch/powerpc/kernel/nvram_64.c
> index ddc94cf..d467f8c 100644
> --- a/arch/powerpc/kernel/nvram_64.c
> +++ b/arch/powerpc/kernel/nvram_64.c
> @@ -36,8 +36,6 @@
>  
>  #define NVRAM_HEADER_LEN	sizeof(struct nvram_header)
>  #define NVRAM_BLOCK_LEN		NVRAM_HEADER_LEN
> -#define NVRAM_MAX_REQ		2079
> -#define NVRAM_MIN_REQ		1055
>  
>  /* If change this size, then change the size of NVNAME_LEN */
>  struct nvram_header {
> @@ -54,13 +52,6 @@ struct nvram_partition {
>  };
>  
>  static struct nvram_partition * nvram_part;
> -static long nvram_error_log_index = -1;
> -static long nvram_error_log_size = 0;
> -
> -struct err_log_info {
> -	int error_type;
> -	unsigned int seq_num;
> -};
>  
>  static loff_t dev_nvram_llseek(struct file *file, loff_t offset, int origin)
>  {
> @@ -254,7 +245,7 @@ static unsigned char __init nvram_checksum(struct nvram_header *p)
>   * @sig: signature of the partition(s) to remove
>   */
>  
> -static int __init nvram_remove_partition(const char *name, int sig)
> +int __init nvram_remove_partition(const char *name, int sig)
>  {
>  	struct nvram_partition *part, *prev, *tmp;
>  	int rc;
> @@ -313,8 +304,8 @@ static int __init nvram_remove_partition(const char *name, int sig)
>   * you need to query for the actual size yourself after the
>   * call using nvram_partition_get_size().
>   */
> -static loff_t __init nvram_create_partition(const char *name, int sig,
> -					    int req_size, int min_size)
> +loff_t __init nvram_create_partition(const char *name, int sig,
> +				     int req_size, int min_size)
>  {
>  	struct nvram_partition *part;
>  	struct nvram_partition *new_part;
> @@ -417,7 +408,7 @@ static loff_t __init nvram_create_partition(const char *name, int sig,
>   *              the partition. The same value that is returned by
>   *              nvram_create_partition().
>   */
> -static int nvram_get_partition_size(loff_t data_index)
> +int nvram_get_partition_size(loff_t data_index)
>  {
>  	struct nvram_partition *part;
>  	
> @@ -451,74 +442,6 @@ loff_t nvram_find_partition(const char *name, int sig, int *out_size)
>  	return 0;
>  }
>  
> -/* nvram_setup_partition
> - *
> - * This will setup the partition we need for buffering the
> - * error logs and cleanup partitions if needed.
> - *
> - * The general strategy is the following:
> - * 1.) If there is ppc64,linux partition large enough then use it.
> - * 2.) If there is not a ppc64,linux partition large enough, search
> - * for a free partition that is large enough.
> - * 3.) If there is not a free partition large enough remove 
> - * _all_ OS partitions and consolidate the space.
> - * 4.) Will first try getting a chunk that will satisfy the maximum
> - * error log size (NVRAM_MAX_REQ).
> - * 5.) If the max chunk cannot be allocated then try finding a chunk
> - * that will satisfy the minum needed (NVRAM_MIN_REQ).
> - */
> -static int __init nvram_setup_partition(void)
> -{
> -	loff_t p;
> -	int size;
> -
> -	/* For now, we don't do any of this on pmac, until I
> -	 * have figured out if it's worth killing some unused stuffs
> -	 * in our nvram, as Apple defined partitions use pretty much
> -	 * all of the space
> -	 */
> -	if (machine_is(powermac))
> -		return -ENOSPC;
> -
> -	p = nvram_find_partition("ppc64,linux", NVRAM_SIG_OS, &size);
> -
> -	/* Found one but too small, remove it */
> -	if (p && size < NVRAM_MIN_REQ) {
> -		pr_info("nvram: Found too small ppc64,linux partition"
> -			",removing it...");
> -		nvram_remove_partition("ppc64,linux", NVRAM_SIG_OS);
> -		p = 0;
> -	}
> -
> -	/* Create one if we didn't find */
> -	if (!p) {
> -		p = nvram_create_partition("ppc64,linux", NVRAM_SIG_OS,
> -					   NVRAM_MIN_REQ, NVRAM_MAX_REQ);
> -		/* No room for it, try to get rid of any OS partition
> -		 * and try again
> -		 */
> -		if (p == -ENOSPC) {
> -			pr_info("nvram: No room to create ppc64,linux"
> -				" partition, deleting all OS partitions...");
> -			nvram_remove_partition(NULL, NVRAM_SIG_OS);
> -			p = nvram_create_partition("ppc64,linux", NVRAM_SIG_OS,
> -						   NVRAM_MIN_REQ, NVRAM_MAX_REQ);
> -		}
> -	}
> -
> -	if (p <= 0) {
> -		pr_err("nvram: Failed to find or create ppc64,linux"
> -		       " partition, err %d\n", (int)p);
> -		return 0;
> -	}
> -
> -	nvram_error_log_index = p;
> -	nvram_error_log_size = nvram_get_partition_size(p) -
> -		sizeof(struct err_log_info);
> -	
> -	return 0;
> -}
> -
>  static int __init nvram_scan_partitions(void)
>  {
>  	loff_t cur_index = 0;
> @@ -617,10 +540,6 @@ static int __init nvram_init(void)
>    		printk(KERN_ERR "nvram_init: Failed nvram_scan_partitions\n");
>    		return error;
>    	}
> -  		
> -  	if(nvram_setup_partition()) 
> -  		printk(KERN_WARNING "nvram_init: Could not find nvram partition"
> -  		       " for nvram buffered error logging.\n");
>    
>  #ifdef DEBUG_NVRAM
>  	nvram_print_partitions("NVRAM Partitions");
> @@ -635,133 +554,6 @@ void __exit nvram_cleanup(void)
>  }
>  
> 
> -#ifdef CONFIG_PPC_PSERIES
> -
> -/* nvram_write_error_log
> - *
> - * We need to buffer the error logs into nvram to ensure that we have
> - * the failure information to decode.  If we have a severe error there
> - * is no way to guarantee that the OS or the machine is in a state to
> - * get back to user land and write the error to disk.  For example if
> - * the SCSI device driver causes a Machine Check by writing to a bad
> - * IO address, there is no way of guaranteeing that the device driver
> - * is in any state that is would also be able to write the error data
> - * captured to disk, thus we buffer it in NVRAM for analysis on the
> - * next boot.
> - *
> - * In NVRAM the partition containing the error log buffer will looks like:
> - * Header (in bytes):
> - * +-----------+----------+--------+------------+------------------+
> - * | signature | checksum | length | name       | data             |
> - * |0          |1         |2      3|4         15|16        length-1|
> - * +-----------+----------+--------+------------+------------------+
> - *
> - * The 'data' section would look like (in bytes):
> - * +--------------+------------+-----------------------------------+
> - * | event_logged | sequence # | error log                         |
> - * |0            3|4          7|8            nvram_error_log_size-1|
> - * +--------------+------------+-----------------------------------+
> - *
> - * event_logged: 0 if event has not been logged to syslog, 1 if it has
> - * sequence #: The unique sequence # for each event. (until it wraps)
> - * error log: The error log from event_scan
> - */
> -int nvram_write_error_log(char * buff, int length,
> -                          unsigned int err_type, unsigned int error_log_cnt)
> -{
> -	int rc;
> -	loff_t tmp_index;
> -	struct err_log_info info;
> -	
> -	if (nvram_error_log_index == -1) {
> -		return -ESPIPE;
> -	}
> -
> -	if (length > nvram_error_log_size) {
> -		length = nvram_error_log_size;
> -	}
> -
> -	info.error_type = err_type;
> -	info.seq_num = error_log_cnt;
> -
> -	tmp_index = nvram_error_log_index;
> -
> -	rc = ppc_md.nvram_write((char *)&info, sizeof(struct err_log_info), &tmp_index);
> -	if (rc <= 0) {
> -		printk(KERN_ERR "nvram_write_error_log: Failed nvram_write (%d)\n", rc);
> -		return rc;
> -	}
> -
> -	rc = ppc_md.nvram_write(buff, length, &tmp_index);
> -	if (rc <= 0) {
> -		printk(KERN_ERR "nvram_write_error_log: Failed nvram_write (%d)\n", rc);
> -		return rc;
> -	}
> -	
> -	return 0;
> -}
> -
> -/* nvram_read_error_log
> - *
> - * Reads nvram for error log for at most 'length'
> - */
> -int nvram_read_error_log(char * buff, int length,
> -                         unsigned int * err_type, unsigned int * error_log_cnt)
> -{
> -	int rc;
> -	loff_t tmp_index;
> -	struct err_log_info info;
> -	
> -	if (nvram_error_log_index == -1)
> -		return -1;
> -
> -	if (length > nvram_error_log_size)
> -		length = nvram_error_log_size;
> -
> -	tmp_index = nvram_error_log_index;
> -
> -	rc = ppc_md.nvram_read((char *)&info, sizeof(struct err_log_info), &tmp_index);
> -	if (rc <= 0) {
> -		printk(KERN_ERR "nvram_read_error_log: Failed nvram_read (%d)\n", rc);
> -		return rc;
> -	}
> -
> -	rc = ppc_md.nvram_read(buff, length, &tmp_index);
> -	if (rc <= 0) {
> -		printk(KERN_ERR "nvram_read_error_log: Failed nvram_read (%d)\n", rc);
> -		return rc;
> -	}
> -
> -	*error_log_cnt = info.seq_num;
> -	*err_type = info.error_type;
> -
> -	return 0;
> -}
> -
> -/* This doesn't actually zero anything, but it sets the event_logged
> - * word to tell that this event is safely in syslog.
> - */
> -int nvram_clear_error_log(void)
> -{
> -	loff_t tmp_index;
> -	int clear_word = ERR_FLAG_ALREADY_LOGGED;
> -	int rc;
> -
> -	if (nvram_error_log_index == -1)
> -		return -1;
> -
> -	tmp_index = nvram_error_log_index;
> -	
> -	rc = ppc_md.nvram_write((char *)&clear_word, sizeof(int), &tmp_index);
> -	if (rc <= 0) {
> -		printk(KERN_ERR "nvram_clear_error_log: Failed nvram_write (%d)\n", rc);
> -		return rc;
> -	}
> -
> -	return 0;
> -}
> -
> -#endif /* CONFIG_PPC_PSERIES */
>  
>  module_init(nvram_init);
>  module_exit(nvram_cleanup);
> diff --git a/arch/powerpc/platforms/pseries/nvram.c b/arch/powerpc/platforms/pseries/nvram.c
> index 2a1ef5c..c8b3f0f 100644
> --- a/arch/powerpc/platforms/pseries/nvram.c
> +++ b/arch/powerpc/platforms/pseries/nvram.c
> @@ -30,6 +30,16 @@ static int nvram_fetch, nvram_store;
>  static char nvram_buf[NVRW_CNT];	/* assume this is in the first 4GB */
>  static DEFINE_SPINLOCK(nvram_lock);
>  
> +static long nvram_error_log_index = -1;
> +static long nvram_error_log_size = 0;
> +
> +struct err_log_info {
> +	int error_type;
> +	unsigned int seq_num;
> +};
> +#define NVRAM_MAX_REQ		2079
> +#define NVRAM_MIN_REQ		1055
> +
>  static ssize_t pSeries_nvram_read(char *buf, size_t count, loff_t *index)
>  {
>  	unsigned int i;
> @@ -121,6 +131,192 @@ static ssize_t pSeries_nvram_get_size(void)
>  	return nvram_size ? nvram_size : -ENODEV;
>  }
>  
> +
> +/* nvram_write_error_log
> + *
> + * We need to buffer the error logs into nvram to ensure that we have
> + * the failure information to decode.  If we have a severe error there
> + * is no way to guarantee that the OS or the machine is in a state to
> + * get back to user land and write the error to disk.  For example if
> + * the SCSI device driver causes a Machine Check by writing to a bad
> + * IO address, there is no way of guaranteeing that the device driver
> + * is in any state that is would also be able to write the error data
> + * captured to disk, thus we buffer it in NVRAM for analysis on the
> + * next boot.
> + *
> + * In NVRAM the partition containing the error log buffer will looks like:
> + * Header (in bytes):
> + * +-----------+----------+--------+------------+------------------+
> + * | signature | checksum | length | name       | data             |
> + * |0          |1         |2      3|4         15|16        length-1|
> + * +-----------+----------+--------+------------+------------------+
> + *
> + * The 'data' section would look like (in bytes):
> + * +--------------+------------+-----------------------------------+
> + * | event_logged | sequence # | error log                         |
> + * |0            3|4          7|8            nvram_error_log_size-1|
> + * +--------------+------------+-----------------------------------+
> + *
> + * event_logged: 0 if event has not been logged to syslog, 1 if it has
> + * sequence #: The unique sequence # for each event. (until it wraps)
> + * error log: The error log from event_scan
> + */
> +int nvram_write_error_log(char * buff, int length,
> +                          unsigned int err_type, unsigned int error_log_cnt)
> +{
> +	int rc;
> +	loff_t tmp_index;
> +	struct err_log_info info;
> +	
> +	if (nvram_error_log_index == -1) {
> +		return -ESPIPE;
> +	}
> +
> +	if (length > nvram_error_log_size) {
> +		length = nvram_error_log_size;
> +	}
> +
> +	info.error_type = err_type;
> +	info.seq_num = error_log_cnt;
> +
> +	tmp_index = nvram_error_log_index;
> +
> +	rc = ppc_md.nvram_write((char *)&info, sizeof(struct err_log_info), &tmp_index);
> +	if (rc <= 0) {
> +		printk(KERN_ERR "nvram_write_error_log: Failed nvram_write (%d)\n", rc);
> +		return rc;
> +	}
> +
> +	rc = ppc_md.nvram_write(buff, length, &tmp_index);
> +	if (rc <= 0) {
> +		printk(KERN_ERR "nvram_write_error_log: Failed nvram_write (%d)\n", rc);
> +		return rc;
> +	}
> +	
> +	return 0;
> +}
> +
> +/* nvram_read_error_log
> + *
> + * Reads nvram for error log for at most 'length'
> + */
> +int nvram_read_error_log(char * buff, int length,
> +                         unsigned int * err_type, unsigned int * error_log_cnt)
> +{
> +	int rc;
> +	loff_t tmp_index;
> +	struct err_log_info info;
> +	
> +	if (nvram_error_log_index == -1)
> +		return -1;
> +
> +	if (length > nvram_error_log_size)
> +		length = nvram_error_log_size;
> +
> +	tmp_index = nvram_error_log_index;
> +
> +	rc = ppc_md.nvram_read((char *)&info, sizeof(struct err_log_info), &tmp_index);
> +	if (rc <= 0) {
> +		printk(KERN_ERR "nvram_read_error_log: Failed nvram_read (%d)\n", rc);
> +		return rc;
> +	}
> +
> +	rc = ppc_md.nvram_read(buff, length, &tmp_index);
> +	if (rc <= 0) {
> +		printk(KERN_ERR "nvram_read_error_log: Failed nvram_read (%d)\n", rc);
> +		return rc;
> +	}
> +
> +	*error_log_cnt = info.seq_num;
> +	*err_type = info.error_type;
> +
> +	return 0;
> +}
> +
> +/* This doesn't actually zero anything, but it sets the event_logged
> + * word to tell that this event is safely in syslog.
> + */
> +int nvram_clear_error_log(void)
> +{
> +	loff_t tmp_index;
> +	int clear_word = ERR_FLAG_ALREADY_LOGGED;
> +	int rc;
> +
> +	if (nvram_error_log_index == -1)
> +		return -1;
> +
> +	tmp_index = nvram_error_log_index;
> +	
> +	rc = ppc_md.nvram_write((char *)&clear_word, sizeof(int), &tmp_index);
> +	if (rc <= 0) {
> +		printk(KERN_ERR "nvram_clear_error_log: Failed nvram_write (%d)\n", rc);
> +		return rc;
> +	}
> +
> +	return 0;
> +}
> +
> +/* pseries_nvram_init_log_partition
> + *
> + * This will setup the partition we need for buffering the
> + * error logs and cleanup partitions if needed.
> + *
> + * The general strategy is the following:
> + * 1.) If there is ppc64,linux partition large enough then use it.
> + * 2.) If there is not a ppc64,linux partition large enough, search
> + * for a free partition that is large enough.
> + * 3.) If there is not a free partition large enough remove 
> + * _all_ OS partitions and consolidate the space.
> + * 4.) Will first try getting a chunk that will satisfy the maximum
> + * error log size (NVRAM_MAX_REQ).
> + * 5.) If the max chunk cannot be allocated then try finding a chunk
> + * that will satisfy the minum needed (NVRAM_MIN_REQ).
> + */
> +static int __init pseries_nvram_init_log_partition(void)
> +{
> +	loff_t p;
> +	int size;
> +
> +	p = nvram_find_partition("ppc64,linux", NVRAM_SIG_OS, &size);
> +
> +	/* Found one but too small, remove it */
> +	if (p && size < NVRAM_MIN_REQ) {
> +		pr_info("nvram: Found too small ppc64,linux partition"
> +			",removing it...");
> +		nvram_remove_partition("ppc64,linux", NVRAM_SIG_OS);
> +		p = 0;
> +	}
> +
> +	/* Create one if we didn't find */
> +	if (!p) {
> +		p = nvram_create_partition("ppc64,linux", NVRAM_SIG_OS,
> +					   NVRAM_MIN_REQ, NVRAM_MAX_REQ);
> +		/* No room for it, try to get rid of any OS partition
> +		 * and try again
> +		 */
> +		if (p == -ENOSPC) {
> +			pr_info("nvram: No room to create ppc64,linux"
> +				" partition, deleting all OS partitions...");
> +			nvram_remove_partition(NULL, NVRAM_SIG_OS);
> +			p = nvram_create_partition("ppc64,linux", NVRAM_SIG_OS,
> +						   NVRAM_MIN_REQ, NVRAM_MAX_REQ);
> +		}
> +	}
> +
> +	if (p <= 0) {
> +		pr_err("nvram: Failed to find or create ppc64,linux"
> +		       " partition, err %d\n", (int)p);
> +		return 0;
> +	}
> +
> +	nvram_error_log_index = p;
> +	nvram_error_log_size = nvram_get_partition_size(p) -
> +		sizeof(struct err_log_info);
> +	
> +	return 0;
> +}
> +machine_arch_initcall(pseries, pseries_nvram_init_log_partition);
> +
>  int __init pSeries_nvram_init(void)
>  {
>  	struct device_node *nvram;

^ permalink raw reply

* [PATCH 11/11] powerpc/nvram: Rename ppc64, linux partition to ibm, rtas-log
From: Benjamin Herrenschmidt @ 2010-08-02  0:55 UTC (permalink / raw)
  To: linuxppc-dev
In-Reply-To: <1280710522-6362-11-git-send-email-benh@kernel.crashing.org>

I'm not aware of any userspace tool accessing it by its name anyways,
it's read back by the kernel itself on the next boot to get back
older log entries

Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
---
 arch/powerpc/platforms/pseries/nvram.c |   23 +++++++++++++----------
 1 files changed, 13 insertions(+), 10 deletions(-)

diff --git a/arch/powerpc/platforms/pseries/nvram.c b/arch/powerpc/platforms/pseries/nvram.c
index c8b3f0f..e3acb0b 100644
--- a/arch/powerpc/platforms/pseries/nvram.c
+++ b/arch/powerpc/platforms/pseries/nvram.c
@@ -40,6 +40,8 @@ struct err_log_info {
 #define NVRAM_MAX_REQ		2079
 #define NVRAM_MIN_REQ		1055
 
+#define NVRAM_LOG_PART_NAME	"ibm,rtas-log"
+
 static ssize_t pSeries_nvram_read(char *buf, size_t count, loff_t *index)
 {
 	unsigned int i;
@@ -262,8 +264,8 @@ int nvram_clear_error_log(void)
  * error logs and cleanup partitions if needed.
  *
  * The general strategy is the following:
- * 1.) If there is ppc64,linux partition large enough then use it.
- * 2.) If there is not a ppc64,linux partition large enough, search
+ * 1.) If there is log partition large enough then use it.
+ * 2.) If there is none large enough, search
  * for a free partition that is large enough.
  * 3.) If there is not a free partition large enough remove 
  * _all_ OS partitions and consolidate the space.
@@ -277,34 +279,35 @@ static int __init pseries_nvram_init_log_partition(void)
 	loff_t p;
 	int size;
 
-	p = nvram_find_partition("ppc64,linux", NVRAM_SIG_OS, &size);
+	p = nvram_find_partition(NVRAM_LOG_PART_NAME, NVRAM_SIG_OS, &size);
 
 	/* Found one but too small, remove it */
 	if (p && size < NVRAM_MIN_REQ) {
-		pr_info("nvram: Found too small ppc64,linux partition"
+		pr_info("nvram: Found too small "NVRAM_LOG_PART_NAME" partition"
 			",removing it...");
-		nvram_remove_partition("ppc64,linux", NVRAM_SIG_OS);
+		nvram_remove_partition(NVRAM_LOG_PART_NAME, NVRAM_SIG_OS);
 		p = 0;
 	}
 
 	/* Create one if we didn't find */
 	if (!p) {
-		p = nvram_create_partition("ppc64,linux", NVRAM_SIG_OS,
+		p = nvram_create_partition(NVRAM_LOG_PART_NAME, NVRAM_SIG_OS,
 					   NVRAM_MIN_REQ, NVRAM_MAX_REQ);
 		/* No room for it, try to get rid of any OS partition
 		 * and try again
 		 */
 		if (p == -ENOSPC) {
-			pr_info("nvram: No room to create ppc64,linux"
+			pr_info("nvram: No room to create "NVRAM_LOG_PART_NAME
 				" partition, deleting all OS partitions...");
 			nvram_remove_partition(NULL, NVRAM_SIG_OS);
-			p = nvram_create_partition("ppc64,linux", NVRAM_SIG_OS,
-						   NVRAM_MIN_REQ, NVRAM_MAX_REQ);
+			p = nvram_create_partition(NVRAM_LOG_PART_NAME,
+						   NVRAM_SIG_OS, NVRAM_MIN_REQ,
+						   NVRAM_MAX_REQ);
 		}
 	}
 
 	if (p <= 0) {
-		pr_err("nvram: Failed to find or create ppc64,linux"
+		pr_err("nvram: Failed to find or create "NVRAM_LOG_PART_NAME
 		       " partition, err %d\n", (int)p);
 		return 0;
 	}
-- 
1.6.3.3

^ permalink raw reply related

* [PATCH 10/11] powerpc/nvram: Move the log partition stuff to pseries
From: Benjamin Herrenschmidt @ 2010-08-02  0:55 UTC (permalink / raw)
  To: linuxppc-dev
In-Reply-To: <1280710522-6362-10-git-send-email-benh@kernel.crashing.org>

The nvram log partition stuff currently in nvram_64.c is really
pseries specific. It isn't actually used on anything else (despite
the fact that we ran the code to setup the partition on anything
except powermac) and the log format is specific to pseries RTAS
implementation. So move it where it belongs

Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
---
 arch/powerpc/include/asm/nvram.h       |    9 ++-
 arch/powerpc/kernel/nvram_64.c         |  216 +-------------------------------
 arch/powerpc/platforms/pseries/nvram.c |  196 +++++++++++++++++++++++++++++
 3 files changed, 208 insertions(+), 213 deletions(-)

diff --git a/arch/powerpc/include/asm/nvram.h b/arch/powerpc/include/asm/nvram.h
index 459dc09..457a1a5 100644
--- a/arch/powerpc/include/asm/nvram.h
+++ b/arch/powerpc/include/asm/nvram.h
@@ -30,13 +30,14 @@
 #include <linux/errno.h>
 #include <linux/list.h>
 
+#ifdef CONFIG_PPC_PSERIES
 extern int nvram_write_error_log(char * buff, int length,
 					 unsigned int err_type, unsigned int err_seq);
 extern int nvram_read_error_log(char * buff, int length,
 					 unsigned int * err_type, unsigned int *err_seq);
 extern int nvram_clear_error_log(void);
-
 extern int pSeries_nvram_init(void);
+#endif /* CONFIG_PPC_PSERIES */
 
 #ifdef CONFIG_MMIO_NVRAM
 extern int mmio_nvram_init(void);
@@ -47,6 +48,12 @@ static inline int mmio_nvram_init(void)
 }
 #endif
 
+extern loff_t nvram_create_partition(const char *name, int sig,
+				     int req_size, int min_size);
+extern int nvram_remove_partition(const char *name, int sig);
+extern int nvram_get_partition_size(loff_t data_index);
+extern loff_t nvram_find_partition(const char *name, int sig, int *out_size);
+
 #endif /* __KERNEL__ */
 
 /* PowerMac specific nvram stuffs */
diff --git a/arch/powerpc/kernel/nvram_64.c b/arch/powerpc/kernel/nvram_64.c
index ddc94cf..d467f8c 100644
--- a/arch/powerpc/kernel/nvram_64.c
+++ b/arch/powerpc/kernel/nvram_64.c
@@ -36,8 +36,6 @@
 
 #define NVRAM_HEADER_LEN	sizeof(struct nvram_header)
 #define NVRAM_BLOCK_LEN		NVRAM_HEADER_LEN
-#define NVRAM_MAX_REQ		2079
-#define NVRAM_MIN_REQ		1055
 
 /* If change this size, then change the size of NVNAME_LEN */
 struct nvram_header {
@@ -54,13 +52,6 @@ struct nvram_partition {
 };
 
 static struct nvram_partition * nvram_part;
-static long nvram_error_log_index = -1;
-static long nvram_error_log_size = 0;
-
-struct err_log_info {
-	int error_type;
-	unsigned int seq_num;
-};
 
 static loff_t dev_nvram_llseek(struct file *file, loff_t offset, int origin)
 {
@@ -254,7 +245,7 @@ static unsigned char __init nvram_checksum(struct nvram_header *p)
  * @sig: signature of the partition(s) to remove
  */
 
-static int __init nvram_remove_partition(const char *name, int sig)
+int __init nvram_remove_partition(const char *name, int sig)
 {
 	struct nvram_partition *part, *prev, *tmp;
 	int rc;
@@ -313,8 +304,8 @@ static int __init nvram_remove_partition(const char *name, int sig)
  * you need to query for the actual size yourself after the
  * call using nvram_partition_get_size().
  */
-static loff_t __init nvram_create_partition(const char *name, int sig,
-					    int req_size, int min_size)
+loff_t __init nvram_create_partition(const char *name, int sig,
+				     int req_size, int min_size)
 {
 	struct nvram_partition *part;
 	struct nvram_partition *new_part;
@@ -417,7 +408,7 @@ static loff_t __init nvram_create_partition(const char *name, int sig,
  *              the partition. The same value that is returned by
  *              nvram_create_partition().
  */
-static int nvram_get_partition_size(loff_t data_index)
+int nvram_get_partition_size(loff_t data_index)
 {
 	struct nvram_partition *part;
 	
@@ -451,74 +442,6 @@ loff_t nvram_find_partition(const char *name, int sig, int *out_size)
 	return 0;
 }
 
-/* nvram_setup_partition
- *
- * This will setup the partition we need for buffering the
- * error logs and cleanup partitions if needed.
- *
- * The general strategy is the following:
- * 1.) If there is ppc64,linux partition large enough then use it.
- * 2.) If there is not a ppc64,linux partition large enough, search
- * for a free partition that is large enough.
- * 3.) If there is not a free partition large enough remove 
- * _all_ OS partitions and consolidate the space.
- * 4.) Will first try getting a chunk that will satisfy the maximum
- * error log size (NVRAM_MAX_REQ).
- * 5.) If the max chunk cannot be allocated then try finding a chunk
- * that will satisfy the minum needed (NVRAM_MIN_REQ).
- */
-static int __init nvram_setup_partition(void)
-{
-	loff_t p;
-	int size;
-
-	/* For now, we don't do any of this on pmac, until I
-	 * have figured out if it's worth killing some unused stuffs
-	 * in our nvram, as Apple defined partitions use pretty much
-	 * all of the space
-	 */
-	if (machine_is(powermac))
-		return -ENOSPC;
-
-	p = nvram_find_partition("ppc64,linux", NVRAM_SIG_OS, &size);
-
-	/* Found one but too small, remove it */
-	if (p && size < NVRAM_MIN_REQ) {
-		pr_info("nvram: Found too small ppc64,linux partition"
-			",removing it...");
-		nvram_remove_partition("ppc64,linux", NVRAM_SIG_OS);
-		p = 0;
-	}
-
-	/* Create one if we didn't find */
-	if (!p) {
-		p = nvram_create_partition("ppc64,linux", NVRAM_SIG_OS,
-					   NVRAM_MIN_REQ, NVRAM_MAX_REQ);
-		/* No room for it, try to get rid of any OS partition
-		 * and try again
-		 */
-		if (p == -ENOSPC) {
-			pr_info("nvram: No room to create ppc64,linux"
-				" partition, deleting all OS partitions...");
-			nvram_remove_partition(NULL, NVRAM_SIG_OS);
-			p = nvram_create_partition("ppc64,linux", NVRAM_SIG_OS,
-						   NVRAM_MIN_REQ, NVRAM_MAX_REQ);
-		}
-	}
-
-	if (p <= 0) {
-		pr_err("nvram: Failed to find or create ppc64,linux"
-		       " partition, err %d\n", (int)p);
-		return 0;
-	}
-
-	nvram_error_log_index = p;
-	nvram_error_log_size = nvram_get_partition_size(p) -
-		sizeof(struct err_log_info);
-	
-	return 0;
-}
-
 static int __init nvram_scan_partitions(void)
 {
 	loff_t cur_index = 0;
@@ -617,10 +540,6 @@ static int __init nvram_init(void)
   		printk(KERN_ERR "nvram_init: Failed nvram_scan_partitions\n");
   		return error;
   	}
-  		
-  	if(nvram_setup_partition()) 
-  		printk(KERN_WARNING "nvram_init: Could not find nvram partition"
-  		       " for nvram buffered error logging.\n");
   
 #ifdef DEBUG_NVRAM
 	nvram_print_partitions("NVRAM Partitions");
@@ -635,133 +554,6 @@ void __exit nvram_cleanup(void)
 }
 
 
-#ifdef CONFIG_PPC_PSERIES
-
-/* nvram_write_error_log
- *
- * We need to buffer the error logs into nvram to ensure that we have
- * the failure information to decode.  If we have a severe error there
- * is no way to guarantee that the OS or the machine is in a state to
- * get back to user land and write the error to disk.  For example if
- * the SCSI device driver causes a Machine Check by writing to a bad
- * IO address, there is no way of guaranteeing that the device driver
- * is in any state that is would also be able to write the error data
- * captured to disk, thus we buffer it in NVRAM for analysis on the
- * next boot.
- *
- * In NVRAM the partition containing the error log buffer will looks like:
- * Header (in bytes):
- * +-----------+----------+--------+------------+------------------+
- * | signature | checksum | length | name       | data             |
- * |0          |1         |2      3|4         15|16        length-1|
- * +-----------+----------+--------+------------+------------------+
- *
- * The 'data' section would look like (in bytes):
- * +--------------+------------+-----------------------------------+
- * | event_logged | sequence # | error log                         |
- * |0            3|4          7|8            nvram_error_log_size-1|
- * +--------------+------------+-----------------------------------+
- *
- * event_logged: 0 if event has not been logged to syslog, 1 if it has
- * sequence #: The unique sequence # for each event. (until it wraps)
- * error log: The error log from event_scan
- */
-int nvram_write_error_log(char * buff, int length,
-                          unsigned int err_type, unsigned int error_log_cnt)
-{
-	int rc;
-	loff_t tmp_index;
-	struct err_log_info info;
-	
-	if (nvram_error_log_index == -1) {
-		return -ESPIPE;
-	}
-
-	if (length > nvram_error_log_size) {
-		length = nvram_error_log_size;
-	}
-
-	info.error_type = err_type;
-	info.seq_num = error_log_cnt;
-
-	tmp_index = nvram_error_log_index;
-
-	rc = ppc_md.nvram_write((char *)&info, sizeof(struct err_log_info), &tmp_index);
-	if (rc <= 0) {
-		printk(KERN_ERR "nvram_write_error_log: Failed nvram_write (%d)\n", rc);
-		return rc;
-	}
-
-	rc = ppc_md.nvram_write(buff, length, &tmp_index);
-	if (rc <= 0) {
-		printk(KERN_ERR "nvram_write_error_log: Failed nvram_write (%d)\n", rc);
-		return rc;
-	}
-	
-	return 0;
-}
-
-/* nvram_read_error_log
- *
- * Reads nvram for error log for at most 'length'
- */
-int nvram_read_error_log(char * buff, int length,
-                         unsigned int * err_type, unsigned int * error_log_cnt)
-{
-	int rc;
-	loff_t tmp_index;
-	struct err_log_info info;
-	
-	if (nvram_error_log_index == -1)
-		return -1;
-
-	if (length > nvram_error_log_size)
-		length = nvram_error_log_size;
-
-	tmp_index = nvram_error_log_index;
-
-	rc = ppc_md.nvram_read((char *)&info, sizeof(struct err_log_info), &tmp_index);
-	if (rc <= 0) {
-		printk(KERN_ERR "nvram_read_error_log: Failed nvram_read (%d)\n", rc);
-		return rc;
-	}
-
-	rc = ppc_md.nvram_read(buff, length, &tmp_index);
-	if (rc <= 0) {
-		printk(KERN_ERR "nvram_read_error_log: Failed nvram_read (%d)\n", rc);
-		return rc;
-	}
-
-	*error_log_cnt = info.seq_num;
-	*err_type = info.error_type;
-
-	return 0;
-}
-
-/* This doesn't actually zero anything, but it sets the event_logged
- * word to tell that this event is safely in syslog.
- */
-int nvram_clear_error_log(void)
-{
-	loff_t tmp_index;
-	int clear_word = ERR_FLAG_ALREADY_LOGGED;
-	int rc;
-
-	if (nvram_error_log_index == -1)
-		return -1;
-
-	tmp_index = nvram_error_log_index;
-	
-	rc = ppc_md.nvram_write((char *)&clear_word, sizeof(int), &tmp_index);
-	if (rc <= 0) {
-		printk(KERN_ERR "nvram_clear_error_log: Failed nvram_write (%d)\n", rc);
-		return rc;
-	}
-
-	return 0;
-}
-
-#endif /* CONFIG_PPC_PSERIES */
 
 module_init(nvram_init);
 module_exit(nvram_cleanup);
diff --git a/arch/powerpc/platforms/pseries/nvram.c b/arch/powerpc/platforms/pseries/nvram.c
index 2a1ef5c..c8b3f0f 100644
--- a/arch/powerpc/platforms/pseries/nvram.c
+++ b/arch/powerpc/platforms/pseries/nvram.c
@@ -30,6 +30,16 @@ static int nvram_fetch, nvram_store;
 static char nvram_buf[NVRW_CNT];	/* assume this is in the first 4GB */
 static DEFINE_SPINLOCK(nvram_lock);
 
+static long nvram_error_log_index = -1;
+static long nvram_error_log_size = 0;
+
+struct err_log_info {
+	int error_type;
+	unsigned int seq_num;
+};
+#define NVRAM_MAX_REQ		2079
+#define NVRAM_MIN_REQ		1055
+
 static ssize_t pSeries_nvram_read(char *buf, size_t count, loff_t *index)
 {
 	unsigned int i;
@@ -121,6 +131,192 @@ static ssize_t pSeries_nvram_get_size(void)
 	return nvram_size ? nvram_size : -ENODEV;
 }
 
+
+/* nvram_write_error_log
+ *
+ * We need to buffer the error logs into nvram to ensure that we have
+ * the failure information to decode.  If we have a severe error there
+ * is no way to guarantee that the OS or the machine is in a state to
+ * get back to user land and write the error to disk.  For example if
+ * the SCSI device driver causes a Machine Check by writing to a bad
+ * IO address, there is no way of guaranteeing that the device driver
+ * is in any state that is would also be able to write the error data
+ * captured to disk, thus we buffer it in NVRAM for analysis on the
+ * next boot.
+ *
+ * In NVRAM the partition containing the error log buffer will looks like:
+ * Header (in bytes):
+ * +-----------+----------+--------+------------+------------------+
+ * | signature | checksum | length | name       | data             |
+ * |0          |1         |2      3|4         15|16        length-1|
+ * +-----------+----------+--------+------------+------------------+
+ *
+ * The 'data' section would look like (in bytes):
+ * +--------------+------------+-----------------------------------+
+ * | event_logged | sequence # | error log                         |
+ * |0            3|4          7|8            nvram_error_log_size-1|
+ * +--------------+------------+-----------------------------------+
+ *
+ * event_logged: 0 if event has not been logged to syslog, 1 if it has
+ * sequence #: The unique sequence # for each event. (until it wraps)
+ * error log: The error log from event_scan
+ */
+int nvram_write_error_log(char * buff, int length,
+                          unsigned int err_type, unsigned int error_log_cnt)
+{
+	int rc;
+	loff_t tmp_index;
+	struct err_log_info info;
+	
+	if (nvram_error_log_index == -1) {
+		return -ESPIPE;
+	}
+
+	if (length > nvram_error_log_size) {
+		length = nvram_error_log_size;
+	}
+
+	info.error_type = err_type;
+	info.seq_num = error_log_cnt;
+
+	tmp_index = nvram_error_log_index;
+
+	rc = ppc_md.nvram_write((char *)&info, sizeof(struct err_log_info), &tmp_index);
+	if (rc <= 0) {
+		printk(KERN_ERR "nvram_write_error_log: Failed nvram_write (%d)\n", rc);
+		return rc;
+	}
+
+	rc = ppc_md.nvram_write(buff, length, &tmp_index);
+	if (rc <= 0) {
+		printk(KERN_ERR "nvram_write_error_log: Failed nvram_write (%d)\n", rc);
+		return rc;
+	}
+	
+	return 0;
+}
+
+/* nvram_read_error_log
+ *
+ * Reads nvram for error log for at most 'length'
+ */
+int nvram_read_error_log(char * buff, int length,
+                         unsigned int * err_type, unsigned int * error_log_cnt)
+{
+	int rc;
+	loff_t tmp_index;
+	struct err_log_info info;
+	
+	if (nvram_error_log_index == -1)
+		return -1;
+
+	if (length > nvram_error_log_size)
+		length = nvram_error_log_size;
+
+	tmp_index = nvram_error_log_index;
+
+	rc = ppc_md.nvram_read((char *)&info, sizeof(struct err_log_info), &tmp_index);
+	if (rc <= 0) {
+		printk(KERN_ERR "nvram_read_error_log: Failed nvram_read (%d)\n", rc);
+		return rc;
+	}
+
+	rc = ppc_md.nvram_read(buff, length, &tmp_index);
+	if (rc <= 0) {
+		printk(KERN_ERR "nvram_read_error_log: Failed nvram_read (%d)\n", rc);
+		return rc;
+	}
+
+	*error_log_cnt = info.seq_num;
+	*err_type = info.error_type;
+
+	return 0;
+}
+
+/* This doesn't actually zero anything, but it sets the event_logged
+ * word to tell that this event is safely in syslog.
+ */
+int nvram_clear_error_log(void)
+{
+	loff_t tmp_index;
+	int clear_word = ERR_FLAG_ALREADY_LOGGED;
+	int rc;
+
+	if (nvram_error_log_index == -1)
+		return -1;
+
+	tmp_index = nvram_error_log_index;
+	
+	rc = ppc_md.nvram_write((char *)&clear_word, sizeof(int), &tmp_index);
+	if (rc <= 0) {
+		printk(KERN_ERR "nvram_clear_error_log: Failed nvram_write (%d)\n", rc);
+		return rc;
+	}
+
+	return 0;
+}
+
+/* pseries_nvram_init_log_partition
+ *
+ * This will setup the partition we need for buffering the
+ * error logs and cleanup partitions if needed.
+ *
+ * The general strategy is the following:
+ * 1.) If there is ppc64,linux partition large enough then use it.
+ * 2.) If there is not a ppc64,linux partition large enough, search
+ * for a free partition that is large enough.
+ * 3.) If there is not a free partition large enough remove 
+ * _all_ OS partitions and consolidate the space.
+ * 4.) Will first try getting a chunk that will satisfy the maximum
+ * error log size (NVRAM_MAX_REQ).
+ * 5.) If the max chunk cannot be allocated then try finding a chunk
+ * that will satisfy the minum needed (NVRAM_MIN_REQ).
+ */
+static int __init pseries_nvram_init_log_partition(void)
+{
+	loff_t p;
+	int size;
+
+	p = nvram_find_partition("ppc64,linux", NVRAM_SIG_OS, &size);
+
+	/* Found one but too small, remove it */
+	if (p && size < NVRAM_MIN_REQ) {
+		pr_info("nvram: Found too small ppc64,linux partition"
+			",removing it...");
+		nvram_remove_partition("ppc64,linux", NVRAM_SIG_OS);
+		p = 0;
+	}
+
+	/* Create one if we didn't find */
+	if (!p) {
+		p = nvram_create_partition("ppc64,linux", NVRAM_SIG_OS,
+					   NVRAM_MIN_REQ, NVRAM_MAX_REQ);
+		/* No room for it, try to get rid of any OS partition
+		 * and try again
+		 */
+		if (p == -ENOSPC) {
+			pr_info("nvram: No room to create ppc64,linux"
+				" partition, deleting all OS partitions...");
+			nvram_remove_partition(NULL, NVRAM_SIG_OS);
+			p = nvram_create_partition("ppc64,linux", NVRAM_SIG_OS,
+						   NVRAM_MIN_REQ, NVRAM_MAX_REQ);
+		}
+	}
+
+	if (p <= 0) {
+		pr_err("nvram: Failed to find or create ppc64,linux"
+		       " partition, err %d\n", (int)p);
+		return 0;
+	}
+
+	nvram_error_log_index = p;
+	nvram_error_log_size = nvram_get_partition_size(p) -
+		sizeof(struct err_log_info);
+	
+	return 0;
+}
+machine_arch_initcall(pseries, pseries_nvram_init_log_partition);
+
 int __init pSeries_nvram_init(void)
 {
 	struct device_node *nvram;
-- 
1.6.3.3

^ permalink raw reply related

* [PATCH 09/11] powerpc/nvram: Change nvram_setup_partition() to use new helper
From: Benjamin Herrenschmidt @ 2010-08-02  0:55 UTC (permalink / raw)
  To: linuxppc-dev
In-Reply-To: <1280710522-6362-9-git-send-email-benh@kernel.crashing.org>

This changes the function to use nvram_find_partition() instead
of doing the lookup "by hand". It also makes some of the logic
clearer and prints out more useful diagnostic information.

Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
---
 arch/powerpc/kernel/nvram_64.c |   72 ++++++++++++++++++----------------------
 1 files changed, 32 insertions(+), 40 deletions(-)

diff --git a/arch/powerpc/kernel/nvram_64.c b/arch/powerpc/kernel/nvram_64.c
index fd0d12d..ddc94cf 100644
--- a/arch/powerpc/kernel/nvram_64.c
+++ b/arch/powerpc/kernel/nvram_64.c
@@ -469,9 +469,8 @@ loff_t nvram_find_partition(const char *name, int sig, int *out_size)
  */
 static int __init nvram_setup_partition(void)
 {
-	struct list_head * p;
-	struct nvram_partition * part;
-	int rc;
+	loff_t p;
+	int size;
 
 	/* For now, we don't do any of this on pmac, until I
 	 * have figured out if it's worth killing some unused stuffs
@@ -481,48 +480,41 @@ static int __init nvram_setup_partition(void)
 	if (machine_is(powermac))
 		return -ENOSPC;
 
-	/* see if we have an OS partition that meets our needs.
-	   will try getting the max we need.  If not we'll delete
-	   partitions and try again. */
-	list_for_each(p, &nvram_part->partition) {
-		part = list_entry(p, struct nvram_partition, partition);
-		if (part->header.signature != NVRAM_SIG_OS)
-			continue;
-
-		if (strcmp(part->header.name, "ppc64,linux"))
-			continue;
-
-		if ((part->header.length - 1) * NVRAM_BLOCK_LEN >= NVRAM_MIN_REQ) {
-			/* found our partition */
-			nvram_error_log_index = part->index + NVRAM_HEADER_LEN;
-			nvram_error_log_size = ((part->header.length - 1) *
-						NVRAM_BLOCK_LEN) - sizeof(struct err_log_info);
-			return 0;
-		}
+	p = nvram_find_partition("ppc64,linux", NVRAM_SIG_OS, &size);
 
-		/* Found one but it's too small, remove it */
+	/* Found one but too small, remove it */
+	if (p && size < NVRAM_MIN_REQ) {
+		pr_info("nvram: Found too small ppc64,linux partition"
+			",removing it...");
 		nvram_remove_partition("ppc64,linux", NVRAM_SIG_OS);
+		p = 0;
 	}
-	
-	/* try creating a partition with the free space we have */
-	rc = nvram_create_partition("ppc64,linux", NVRAM_SIG_OS,
-				       NVRAM_MIN_REQ, NVRAM_MAX_REQ);
-	if (rc < 0) {
-		/* need to free up some space, remove any "OS" partition */
-		nvram_remove_partition(NULL, NVRAM_SIG_OS);
-	
-		/* Try again */
-		rc = nvram_create_partition("ppc64,linux", NVRAM_SIG_OS,
-					    NVRAM_MIN_REQ, NVRAM_MAX_REQ);
-		if (rc < 0) {
-			pr_err("nvram_create_partition: Could not find"
-			       " enough space in NVRAM for partition\n");
-			return rc;
+
+	/* Create one if we didn't find */
+	if (!p) {
+		p = nvram_create_partition("ppc64,linux", NVRAM_SIG_OS,
+					   NVRAM_MIN_REQ, NVRAM_MAX_REQ);
+		/* No room for it, try to get rid of any OS partition
+		 * and try again
+		 */
+		if (p == -ENOSPC) {
+			pr_info("nvram: No room to create ppc64,linux"
+				" partition, deleting all OS partitions...");
+			nvram_remove_partition(NULL, NVRAM_SIG_OS);
+			p = nvram_create_partition("ppc64,linux", NVRAM_SIG_OS,
+						   NVRAM_MIN_REQ, NVRAM_MAX_REQ);
 		}
 	}
-	
-	nvram_error_log_index = rc;	
-	nvram_error_log_size = nvram_get_partition_size(rc) - sizeof(struct err_log_info);
+
+	if (p <= 0) {
+		pr_err("nvram: Failed to find or create ppc64,linux"
+		       " partition, err %d\n", (int)p);
+		return 0;
+	}
+
+	nvram_error_log_index = p;
+	nvram_error_log_size = nvram_get_partition_size(p) -
+		sizeof(struct err_log_info);
 	
 	return 0;
 }
-- 
1.6.3.3

^ permalink raw reply related

* [PATCH 08/11] powerpc/nvram: Add nvram_find_partition()
From: Benjamin Herrenschmidt @ 2010-08-02  0:55 UTC (permalink / raw)
  To: linuxppc-dev
In-Reply-To: <1280710522-6362-8-git-send-email-benh@kernel.crashing.org>

Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
---
 arch/powerpc/kernel/nvram_64.c |   22 ++++++++++++++++++++++
 1 files changed, 22 insertions(+), 0 deletions(-)

diff --git a/arch/powerpc/kernel/nvram_64.c b/arch/powerpc/kernel/nvram_64.c
index c934b5a..fd0d12d 100644
--- a/arch/powerpc/kernel/nvram_64.c
+++ b/arch/powerpc/kernel/nvram_64.c
@@ -429,6 +429,28 @@ static int nvram_get_partition_size(loff_t data_index)
 }
 
 
+/**
+ * nvram_find_partition - Find an nvram partition by signature and name
+ * @name: Name of the partition or NULL for any name
+ * @sig: Signature to test against
+ * @out_size: if non-NULL, returns the size of the data part of the partition
+ */
+loff_t nvram_find_partition(const char *name, int sig, int *out_size)
+{
+	struct nvram_partition *p;
+
+	list_for_each_entry(p, &nvram_part->partition, partition) {
+		if (p->header.signature == sig &&
+		    (!name || !strncmp(p->header.name, name, 12))) {
+			if (out_size)
+				*out_size = (p->header.length - 1) *
+					NVRAM_BLOCK_LEN;
+			return p->index + NVRAM_HEADER_LEN;
+		}
+	}
+	return 0;
+}
+
 /* nvram_setup_partition
  *
  * This will setup the partition we need for buffering the
-- 
1.6.3.3

^ permalink raw reply related

* [PATCH 06/11] powerpc/nvram: Shuffle code around in nvram_create_partition()
From: Benjamin Herrenschmidt @ 2010-08-02  0:55 UTC (permalink / raw)
  To: linuxppc-dev
In-Reply-To: <1280710522-6362-6-git-send-email-benh@kernel.crashing.org>

This error log stuff is really pseries specific. As a first step we move
the initialization of these variables to the caller of
nvram_create_partition(), which is also slightly reorganized so we
setup the free partition before we clear the new partition, so the
chance of an error during clear leaving us with invalid headers
is lessened.

Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
---
 arch/powerpc/kernel/nvram_64.c |  107 +++++++++++++++++++++++-----------------
 1 files changed, 62 insertions(+), 45 deletions(-)

diff --git a/arch/powerpc/kernel/nvram_64.c b/arch/powerpc/kernel/nvram_64.c
index 7ddeaa6..10f4b82 100644
--- a/arch/powerpc/kernel/nvram_64.c
+++ b/arch/powerpc/kernel/nvram_64.c
@@ -313,9 +313,15 @@ static int __init nvram_remove_os_partition(void)
  * @sig: signature of the partition to create
  * @req_size: size of data to allocate in bytes
  * @min_size: minimum acceptable size (0 means req_size)
+ *
+ * Returns a negative error code or a positive nvram index
+ * of the beginning of the data area of the newly created
+ * partition. If you provided a min_size smaller than req_size
+ * you need to query for the actual size yourself after the
+ * call using nvram_partition_get_size().
  */
-static int __init nvram_create_partition(const char *name, int sig,
-					 int req_size, int min_size)
+static loff_t __init nvram_create_partition(const char *name, int sig,
+					    int req_size, int min_size)
 {
 	struct nvram_partition *part;
 	struct nvram_partition *new_part;
@@ -334,6 +340,8 @@ static int __init nvram_create_partition(const char *name, int sig,
 	 */
 	if (min_size == 0)
 		min_size = req_size;
+	if (min_size > req_size)
+		return -EINVAL;
 
 	/* Now add one block to each for the header */
 	req_size += 1;
@@ -362,7 +370,7 @@ static int __init nvram_create_partition(const char *name, int sig,
 	/* Create our OS partition */
 	new_part = kmalloc(sizeof(*new_part), GFP_KERNEL);
 	if (!new_part) {
-		printk(KERN_ERR "nvram_create_os_partition: kmalloc failed\n");
+		pr_err("nvram_create_os_partition: kmalloc failed\n");
 		return -ENOMEM;
 	}
 
@@ -374,12 +382,29 @@ static int __init nvram_create_partition(const char *name, int sig,
 
 	rc = nvram_write_header(new_part);
 	if (rc <= 0) {
-		printk(KERN_ERR "nvram_create_os_partition: nvram_write_header "
-				"failed (%d)\n", rc);
+		pr_err("nvram_create_os_partition: nvram_write_header "
+		       "failed (%d)\n", rc);
 		return rc;
 	}
+	list_add_tail(&new_part->partition, &free_part->partition);
 
-	/* Clear the partition */
+	/* Adjust or remove the partition we stole the space from */
+	if (free_part->header.length > size) {
+		free_part->index += size * NVRAM_BLOCK_LEN;
+		free_part->header.length -= size;
+		free_part->header.checksum = nvram_checksum(&free_part->header);
+		rc = nvram_write_header(free_part);
+		if (rc <= 0) {
+			pr_err("nvram_create_os_partition: nvram_write_header "
+			       "failed (%d)\n", rc);
+			return rc;
+		}
+	} else {
+		list_del(&free_part->partition);
+		kfree(free_part);
+	} 
+
+	/* Clear the new partition */
 	for (tmp_index = new_part->index + NVRAM_HEADER_LEN;
 	     tmp_index <  ((size - 1) * NVRAM_BLOCK_LEN);
 	     tmp_index += NVRAM_BLOCK_LEN) {
@@ -390,31 +415,24 @@ static int __init nvram_create_partition(const char *name, int sig,
 		}
 	}
 	
-	nvram_error_log_index = new_part->index + NVRAM_HEADER_LEN;
-	nvram_error_log_size = ((part->header.length - 1) *
-				NVRAM_BLOCK_LEN) - sizeof(struct err_log_info);
-	
-	list_add_tail(&new_part->partition, &free_part->partition);
-
-	if (free_part->header.length <= size) {
-		list_del(&free_part->partition);
-		kfree(free_part);
-		return 0;
-	} 
+	return new_part->index + NVRAM_HEADER_LEN;
+}
 
-	/* Adjust the partition we stole the space from */
-	free_part->index += size * NVRAM_BLOCK_LEN;
-	free_part->header.length -= size;
-	free_part->header.checksum = nvram_checksum(&free_part->header);
+/**
+ * nvram_get_partition_size - Get the data size of an nvram partition
+ * @data_index: This is the offset of the start of the data of
+ *              the partition. The same value that is returned by
+ *              nvram_create_partition().
+ */
+static int nvram_get_partition_size(loff_t data_index)
+{
+	struct nvram_partition *part;
 	
-	rc = nvram_write_header(free_part);
-	if (rc <= 0) {
-		printk(KERN_ERR "nvram_create_os_partition: nvram_write_header "
-		       "failed (%d)\n", rc);
-		return rc;
+	list_for_each_entry(part, &nvram_part->partition, partition) {
+		if (part->index + NVRAM_HEADER_LEN == data_index)
+			return (part->header.length - 1) * NVRAM_BLOCK_LEN;
 	}
-
-	return 0;
+	return -1;
 }
 
 
@@ -469,30 +487,29 @@ static int __init nvram_setup_partition(void)
 	}
 	
 	/* try creating a partition with the free space we have */
-	rc = 	nvram_create_partition("ppc64,linux", NVRAM_SIG_OS,
+	rc = nvram_create_partition("ppc64,linux", NVRAM_SIG_OS,
 				       NVRAM_MIN_REQ, NVRAM_MAX_REQ);
-	if (!rc)
-		return 0;
-		
-	/* need to free up some space */
-	rc = nvram_remove_os_partition();
-	if (rc) {
-		return rc;
+	if (rc < 0) {
+		/* need to free up some space */
+		rc = nvram_remove_os_partition();
+		if (rc)
+			return rc;	
+		/* create a partition in this new space */
+		rc = nvram_create_partition("ppc64,linux", NVRAM_SIG_OS,
+					    NVRAM_MIN_REQ, NVRAM_MAX_REQ);
+		if (rc < 0) {
+			pr_err("nvram_create_partition: Could not find"
+			       " enough space in NVRAM for partition\n");
+			return rc;
+		}
 	}
 	
-	/* create a partition in this new space */
-	rc = 	nvram_create_partition("ppc64,linux", NVRAM_SIG_OS,
-				       NVRAM_MIN_REQ, NVRAM_MAX_REQ);
-	if (rc) {
-		printk(KERN_ERR "nvram_create_partition: Could not find a "
-		       "NVRAM partition large enough\n");
-		return rc;
-	}
+	nvram_error_log_index = rc;	
+	nvram_error_log_size = nvram_get_partition_size(rc) - sizeof(struct err_log_info);
 	
 	return 0;
 }
 
-
 static int __init nvram_scan_partitions(void)
 {
 	loff_t cur_index = 0;
-- 
1.6.3.3

^ permalink raw reply related

* [PATCH 07/11] powerpc/nvram: Improve partition removal
From: Benjamin Herrenschmidt @ 2010-08-02  0:55 UTC (permalink / raw)
  To: linuxppc-dev
In-Reply-To: <1280710522-6362-7-git-send-email-benh@kernel.crashing.org>

Existing code is nasty, has bugs etc... rewrite the function
more simply, and make it take the signature and optional
name of the partitions to remove as arguments, thus making
it a more generic utility.

We also try to remove a log partition that we find and is too
small rather than creating a duplicate.

Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
---
 arch/powerpc/kernel/nvram_64.c |   91 +++++++++++++++++++---------------------
 1 files changed, 43 insertions(+), 48 deletions(-)

diff --git a/arch/powerpc/kernel/nvram_64.c b/arch/powerpc/kernel/nvram_64.c
index 10f4b82..c934b5a 100644
--- a/arch/powerpc/kernel/nvram_64.c
+++ b/arch/powerpc/kernel/nvram_64.c
@@ -247,61 +247,54 @@ static unsigned char __init nvram_checksum(struct nvram_header *p)
 	return c_sum;
 }
 
-static int __init nvram_remove_os_partition(void)
+/**
+ * nvram_remove_partition - Remove one or more partitions in nvram
+ * @name: name of the partition to remove, or NULL for a
+ *        signature only match
+ * @sig: signature of the partition(s) to remove
+ */
+
+static int __init nvram_remove_partition(const char *name, int sig)
 {
-	struct list_head *i;
-	struct list_head *j;
-	struct nvram_partition * part;
-	struct nvram_partition * cur_part;
+	struct nvram_partition *part, *prev, *tmp;
 	int rc;
 
-	list_for_each(i, &nvram_part->partition) {
-		part = list_entry(i, struct nvram_partition, partition);
-		if (part->header.signature != NVRAM_SIG_OS)
+	list_for_each_entry(part, &nvram_part->partition, partition) {
+		if (part->header.signature != sig)
 			continue;
-		
-		/* Make os partition a free partition */
+		if (name && strncmp(name, part->header.name, 12))
+			continue;
+
+		/* Make partition a free partition */
 		part->header.signature = NVRAM_SIG_FREE;
 		sprintf(part->header.name, "wwwwwwwwwwww");
 		part->header.checksum = nvram_checksum(&part->header);
-
-		/* Merge contiguous free partitions backwards */
-		list_for_each_prev(j, &part->partition) {
-			cur_part = list_entry(j, struct nvram_partition, partition);
-			if (cur_part == nvram_part || cur_part->header.signature != NVRAM_SIG_FREE) {
-				break;
-			}
-			
-			part->header.length += cur_part->header.length;
-			part->header.checksum = nvram_checksum(&part->header);
-			part->index = cur_part->index;
-
-			list_del(&cur_part->partition);
-			kfree(cur_part);
-			j = &part->partition; /* fixup our loop */
-		}
-		
-		/* Merge contiguous free partitions forwards */
-		list_for_each(j, &part->partition) {
-			cur_part = list_entry(j, struct nvram_partition, partition);
-			if (cur_part == nvram_part || cur_part->header.signature != NVRAM_SIG_FREE) {
-				break;
-			}
-
-			part->header.length += cur_part->header.length;
-			part->header.checksum = nvram_checksum(&part->header);
-
-			list_del(&cur_part->partition);
-			kfree(cur_part);
-			j = &part->partition; /* fixup our loop */
-		}
-		
 		rc = nvram_write_header(part);
 		if (rc <= 0) {
-			printk(KERN_ERR "nvram_remove_os_partition: nvram_write failed (%d)\n", rc);
+			printk(KERN_ERR "nvram_remove_partition: nvram_write failed (%d)\n", rc);
 			return rc;
 		}
+	}
 
+	/* Merge contiguous ones */
+	prev = NULL;
+	list_for_each_entry_safe(part, tmp, &nvram_part->partition, partition) {
+		if (part->header.signature != NVRAM_SIG_FREE) {
+			prev = NULL;
+			continue;
+		}
+		if (prev) {
+			prev->header.length += part->header.length;
+			prev->header.checksum = nvram_checksum(&part->header);
+			rc = nvram_write_header(part);
+			if (rc <= 0) {
+				printk(KERN_ERR "nvram_remove_partition: nvram_write failed (%d)\n", rc);
+				return rc;
+			}
+			list_del(&part->partition);
+			kfree(part);
+		} else
+			prev = part;
 	}
 	
 	return 0;
@@ -484,17 +477,19 @@ static int __init nvram_setup_partition(void)
 						NVRAM_BLOCK_LEN) - sizeof(struct err_log_info);
 			return 0;
 		}
+
+		/* Found one but it's too small, remove it */
+		nvram_remove_partition("ppc64,linux", NVRAM_SIG_OS);
 	}
 	
 	/* try creating a partition with the free space we have */
 	rc = nvram_create_partition("ppc64,linux", NVRAM_SIG_OS,
 				       NVRAM_MIN_REQ, NVRAM_MAX_REQ);
 	if (rc < 0) {
-		/* need to free up some space */
-		rc = nvram_remove_os_partition();
-		if (rc)
-			return rc;	
-		/* create a partition in this new space */
+		/* need to free up some space, remove any "OS" partition */
+		nvram_remove_partition(NULL, NVRAM_SIG_OS);
+	
+		/* Try again */
 		rc = nvram_create_partition("ppc64,linux", NVRAM_SIG_OS,
 					    NVRAM_MIN_REQ, NVRAM_MAX_REQ);
 		if (rc < 0) {
-- 
1.6.3.3

^ permalink raw reply related

* [PATCH 03/11] powerpc/nvram: nvram_create_partitions() now uses bytes
From: Benjamin Herrenschmidt @ 2010-08-02  0:55 UTC (permalink / raw)
  To: linuxppc-dev
In-Reply-To: <1280710522-6362-3-git-send-email-benh@kernel.crashing.org>

This converts nvram_create_partition() to use a size in bytes
rather than blocks. It does the appropriate alignment internally

The size passed is also the data size (ie. doesn't include the
header anymore).

Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
---
 arch/powerpc/kernel/nvram_64.c |   20 ++++++++++++++------
 1 files changed, 14 insertions(+), 6 deletions(-)

diff --git a/arch/powerpc/kernel/nvram_64.c b/arch/powerpc/kernel/nvram_64.c
index 974b3ec..b0f2114 100644
--- a/arch/powerpc/kernel/nvram_64.c
+++ b/arch/powerpc/kernel/nvram_64.c
@@ -34,10 +34,10 @@
 
 #undef DEBUG_NVRAM
 
-#define NVRAM_HEADER_LEN 16 /* sizeof(struct nvram_header) */
-#define NVRAM_BLOCK_LEN 16
-#define NVRAM_MAX_REQ (2080/NVRAM_BLOCK_LEN)
-#define NVRAM_MIN_REQ (1056/NVRAM_BLOCK_LEN)
+#define NVRAM_HEADER_LEN	sizeof(struct nvram_header)
+#define NVRAM_BLOCK_LEN		NVRAM_HEADER_LEN
+#define NVRAM_MAX_REQ		2079
+#define NVRAM_MIN_REQ		1055
 
 /* If change this size, then change the size of NVNAME_LEN */
 struct nvram_header {
@@ -311,7 +311,7 @@ static int __init nvram_remove_os_partition(void)
  * nvram_create_partition - Create a partition in nvram
  * @name: name of the partition to create
  * @sig: signature of the partition to create
- * @req_size: size to allocate preferrably
+ * @req_size: size of data to allocate in bytes
  * @min_size: minimum acceptable size (0 means req_size)
  */
 static int __init nvram_create_partition(const char *name, int sig,
@@ -325,12 +325,20 @@ static int __init nvram_create_partition(const char *name, int sig,
 	long size = 0;
 	int rc;
 
+	/* Convert sizes from bytes to blocks */
+	req_size = _ALIGN_UP(req_size, NVRAM_BLOCK_LEN) / NVRAM_BLOCK_LEN;
+	min_size = _ALIGN_UP(min_size, NVRAM_BLOCK_LEN) / NVRAM_BLOCK_LEN;
+
 	/* If no minimum size specified, make it the same as the
 	 * requested size
 	 */
 	if (min_size == 0)
 		min_size = req_size;
 
+	/* Now add one block to each for the header */
+	req_size += 1;
+	min_size += 1;
+
 	/* Find a free partition that will give us the maximum needed size 
 	   If can't find one that will give us the minimum size needed */
 	list_for_each_entry(part, &nvram_part->partition, partition) {
@@ -450,7 +458,7 @@ static int __init nvram_setup_partition(void)
 		if (strcmp(part->header.name, "ppc64,linux"))
 			continue;
 
-		if (part->header.length >= NVRAM_MIN_REQ) {
+		if ((part->header.length - 1) * NVRAM_BLOCK_LEN >= NVRAM_MIN_REQ) {
 			/* found our partition */
 			nvram_error_log_index = part->index + NVRAM_HEADER_LEN;
 			nvram_error_log_size = ((part->header.length - 1) *
-- 
1.6.3.3

^ permalink raw reply related

* [PATCH 05/11] powerpc/nvram: Completely clear a new partition
From: Benjamin Herrenschmidt @ 2010-08-02  0:55 UTC (permalink / raw)
  To: linuxppc-dev
In-Reply-To: <1280710522-6362-5-git-send-email-benh@kernel.crashing.org>

When creating a partition, we clear it entirely rather than
just the first two words since the previous code was rather
specific to the pseries log partition format.

Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
---
 arch/powerpc/kernel/nvram_64.c |   19 ++++++++++---------
 1 files changed, 10 insertions(+), 9 deletions(-)

diff --git a/arch/powerpc/kernel/nvram_64.c b/arch/powerpc/kernel/nvram_64.c
index dd18a5a..7ddeaa6 100644
--- a/arch/powerpc/kernel/nvram_64.c
+++ b/arch/powerpc/kernel/nvram_64.c
@@ -320,7 +320,7 @@ static int __init nvram_create_partition(const char *name, int sig,
 	struct nvram_partition *part;
 	struct nvram_partition *new_part;
 	struct nvram_partition *free_part = NULL;
-	int seq_init[2] = { 0, 0 };
+	static char nv_init_vals[16];
 	loff_t tmp_index;
 	long size = 0;
 	int rc;
@@ -379,14 +379,15 @@ static int __init nvram_create_partition(const char *name, int sig,
 		return rc;
 	}
 
-	/* make sure and initialize to zero the sequence number and the error
-	   type logged */
-	tmp_index = new_part->index + NVRAM_HEADER_LEN;
-	rc = ppc_md.nvram_write((char *)&seq_init, sizeof(seq_init), &tmp_index);
-	if (rc <= 0) {
-		printk(KERN_ERR "nvram_create_os_partition: nvram_write "
-		       "failed (%d)\n", rc);
-		return rc;
+	/* Clear the partition */
+	for (tmp_index = new_part->index + NVRAM_HEADER_LEN;
+	     tmp_index <  ((size - 1) * NVRAM_BLOCK_LEN);
+	     tmp_index += NVRAM_BLOCK_LEN) {
+		rc = ppc_md.nvram_write(nv_init_vals, NVRAM_BLOCK_LEN, &tmp_index);
+		if (rc <= 0) {
+			pr_err("nvram_create_partition: nvram_write failed (%d)\n", rc);
+			return rc;
+		}
 	}
 	
 	nvram_error_log_index = new_part->index + NVRAM_HEADER_LEN;
-- 
1.6.3.3

^ permalink raw reply related

* [PATCH 04/11] powerpc/nvram: Ensure that the partition header/block size is right
From: Benjamin Herrenschmidt @ 2010-08-02  0:55 UTC (permalink / raw)
  To: linuxppc-dev
In-Reply-To: <1280710522-6362-4-git-send-email-benh@kernel.crashing.org>

Use BUILD_BUG_ON to ensure the structure representing a partition
header have the right size.

Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
---
 arch/powerpc/kernel/nvram_64.c |    2 ++
 1 files changed, 2 insertions(+), 0 deletions(-)

diff --git a/arch/powerpc/kernel/nvram_64.c b/arch/powerpc/kernel/nvram_64.c
index b0f2114..dd18a5a 100644
--- a/arch/powerpc/kernel/nvram_64.c
+++ b/arch/powerpc/kernel/nvram_64.c
@@ -565,6 +565,8 @@ static int __init nvram_init(void)
 	int error;
 	int rc;
 	
+	BUILD_BUG_ON(NVRAM_BLOCK_LEN != 16);
+
 	if (ppc_md.nvram_size == NULL || ppc_md.nvram_size() <= 0)
 		return  -ENODEV;
 
-- 
1.6.3.3

^ permalink raw reply related

* [RFC] Clean up ppc64 nvram code
From: Benjamin Herrenschmidt @ 2010-08-02  0:55 UTC (permalink / raw)
  To: linuxppc-dev

This series is a first go at cleaning up some cruft in nvram_64.c for
handling nvram partitions. The current ppc64,linux partition is really
pseries specific, so we change the code for finding/creating it gets
turned into a more generic API for manipulating nvram partitions, and
the specifics to that partition are moved to pseries. We also rename
it to better represent what it's for

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox