* Re: [PATCH 2/4] xilinx_spi: Switch to iomem functions and support little endian.
From: Josh Boyer @ 2009-11-11 14:49 UTC (permalink / raw)
To: Richard Röjfors
Cc: spi-devel-general, Andrew Morton, dbrownell, John Linn,
linuxppc-dev
In-Reply-To: <4AFACC6A.304@mocean-labs.com>
On Wed, Nov 11, 2009 at 03:38:34PM +0100, Richard Röjfors wrote:
>This patch changes the out_(be)(8|16|32) and in_(be)(8|16|32) calls to iowrite(8|16|32)
>and ioread(8|16|32). This to be able to build on platforms not supporting the in/out calls
>for instance x86.
>
>Support is also added for little endian writes. In some systems the registers should be
>accessed little endian rather than big endian.
I wonder if you should make the endianness a config option. Right now you
have a conditional check for every read and write. Does that impact
performance at all?
josh
^ permalink raw reply
* [PATCH 4/4] xilinx_spi: add a platform driver using the xilinx_spi common module.
From: Richard Röjfors @ 2009-11-11 14:40 UTC (permalink / raw)
To: spi-devel-general; +Cc: linuxppc-dev, Andrew Morton, dbrownell, John Linn
This patch adds in a platform device driver using the xilinx_spi common module.
Signed-off-by: Richard Röjfors <richard.rojfors@mocean-labs.com>
---
diff --git a/drivers/spi/xilinx_spi_pltfm.c b/drivers/spi/xilinx_spi_pltfm.c
new file mode 100644
index 0000000..f28a48e
--- /dev/null
+++ b/drivers/spi/xilinx_spi_pltfm.c
@@ -0,0 +1,101 @@
+/*
+ * xilinx_spi_pltfm.c Support for Xilinx SPI platform devices
+ * Copyright (c) 2009 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* Supports:
+ * Xilinx SPI devices as platform devices
+ *
+ * Inspired by xilinx_spi.c, 2002-2007 (c) MontaVista Software, Inc.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+
+#include <linux/spi/xilinx_spi.h>
+#include "xilinx_spi.h"
+
+static int __devinit xilinx_spi_probe(struct platform_device *dev)
+{
+ struct xspi_platform_data *pdata;
+ struct resource *r;
+ int irq;
+ struct spi_master *master;
+ u8 i;
+
+ pdata = dev->dev.platform_data;
+ if (pdata == NULL)
+ return -ENODEV;
+
+ r = platform_get_resource(dev, IORESOURCE_MEM, 0);
+ if (r == NULL)
+ return -ENODEV;
+
+ irq = platform_get_irq(dev, 0);
+ if (irq < 0)
+ return -ENXIO;
+
+ master = xilinx_spi_init(&dev->dev, r, irq, dev->id,
+ pdata->num_chipselect, false, pdata->bits_per_word);
+ if (IS_ERR(master))
+ return PTR_ERR(master);
+
+ for (i = 0; i < pdata->num_devices; i++)
+ spi_new_device(master, pdata->devices + i);
+
+ platform_set_drvdata(dev, master);
+ return 0;
+}
+
+static int __devexit xilinx_spi_remove(struct platform_device *dev)
+{
+ xilinx_spi_deinit(platform_get_drvdata(dev));
+ platform_set_drvdata(dev, 0);
+
+ return 0;
+}
+
+/* work with hotplug and coldplug */
+MODULE_ALIAS("platform:" XILINX_SPI_NAME);
+
+static struct platform_driver xilinx_spi_driver = {
+ .probe = xilinx_spi_probe,
+ .remove = __devexit_p(xilinx_spi_remove),
+ .driver = {
+ .name = XILINX_SPI_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init xilinx_spi_pltfm_init(void)
+{
+ return platform_driver_register(&xilinx_spi_driver);
+}
+module_init(xilinx_spi_pltfm_init);
+
+static void __exit xilinx_spi_pltfm_exit(void)
+{
+ platform_driver_unregister(&xilinx_spi_driver);
+}
+module_exit(xilinx_spi_pltfm_exit);
+
+MODULE_AUTHOR("Mocean Laboratories <info@mocean-labs.com>");
+MODULE_DESCRIPTION("Xilinx SPI platform driver");
+MODULE_LICENSE("GPL v2");
+
diff --git a/include/linux/spi/xilinx_spi.h b/include/linux/spi/xilinx_spi.h
new file mode 100644
index 0000000..03e1301
--- /dev/null
+++ b/include/linux/spi/xilinx_spi.h
@@ -0,0 +1,19 @@
+#ifndef __LINUX_SPI_XILINX_SPI_H
+#define __LINUX_SPI_XILINX_SPI_H
+
+/**
+ * struct xspi_platform_data - Platform data of the Xilinx SPI driver
+ * @num_chipselect: Number of chip select by the IP
+ * @bits_per_word: Number of bits per word. 8/16/32, Note that the DS464
+ * only support 8bit SPI.
+ * @devices: Devices to add when the driver is probed.
+ * @num_devices: Number of devices in the devices array.
+ */
+struct xspi_platform_data {
+ u16 num_chipselect;
+ u8 bits_per_word;
+ struct spi_board_info *devices;
+ u8 num_devices;
+};
+
+#endif /* __LINUX_SPI_XILINX_SPI_H */
^ permalink raw reply related
* [PATCH 3/4] xilinx_spi: add support for the DS570 IP.
From: Richard Röjfors @ 2009-11-11 14:39 UTC (permalink / raw)
To: spi-devel-general; +Cc: linuxppc-dev, Andrew Morton, dbrownell, John Linn
This patch adds in support for the DS570 IP.
It's register compatible with the DS464, but adds support for 8/16/32 SPI.
Signed-off-by: Richard Röjfors <richard.rojfors@mocean-labs.com>
---
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 9667650..b956284 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -235,7 +235,7 @@ config SPI_TXX9
SPI driver for Toshiba TXx9 MIPS SoCs
config SPI_XILINX
- tristate "Xilinx SPI controller"
+ tristate "Xilinx SPI controller common module"
depends on HAS_IOMEM && EXPERIMENTAL
select SPI_BITBANG
help
@@ -244,6 +244,8 @@ config SPI_XILINX
See the "OPB Serial Peripheral Interface (SPI) (v1.00e)"
Product Specification document (DS464) for hardware details.
+ Or for the DS570, see "XPS Serial Peripheral Interface (SPI) (v2.00b)"
+
config SPI_XILINX_OF
tristate "Xilinx SPI controller OF device"
depends on SPI_XILINX && (XILINX_VIRTEX || MICROBLAZE)
diff --git a/drivers/spi/xilinx_spi.c b/drivers/spi/xilinx_spi.c
index b00dabc..ae744ba 100644
--- a/drivers/spi/xilinx_spi.c
+++ b/drivers/spi/xilinx_spi.c
@@ -24,7 +24,7 @@
/* Register definitions as per "OPB Serial Peripheral Interface (SPI) (v1.00e)
* Product Specification", DS464
*/
-#define XSPI_CR_OFFSET 0x60 /* 16-bit Control Register */
+#define XSPI_CR_OFFSET 0x60 /* Control Register */
#define XSPI_CR_ENABLE 0x02
#define XSPI_CR_MASTER_MODE 0x04
@@ -35,8 +35,9 @@
#define XSPI_CR_RXFIFO_RESET 0x40
#define XSPI_CR_MANUAL_SSELECT 0x80
#define XSPI_CR_TRANS_INHIBIT 0x100
+#define XSPI_CR_LSB_FIRST 0x200
-#define XSPI_SR_OFFSET 0x64 /* 8-bit Status Register */
+#define XSPI_SR_OFFSET 0x64 /* Status Register */
#define XSPI_SR_RX_EMPTY_MASK 0x01 /* Receive FIFO is empty */
#define XSPI_SR_RX_FULL_MASK 0x02 /* Receive FIFO is full */
@@ -44,8 +45,8 @@
#define XSPI_SR_TX_FULL_MASK 0x08 /* Transmit FIFO is full */
#define XSPI_SR_MODE_FAULT_MASK 0x10 /* Mode fault error */
-#define XSPI_TXD_OFFSET 0x68 /* 8-bit Data Transmit Register */
-#define XSPI_RXD_OFFSET 0x6c /* 8-bit Data Receive Register */
+#define XSPI_TXD_OFFSET 0x68 /* Data Transmit Register */
+#define XSPI_RXD_OFFSET 0x6c /* Data Receive Register */
#define XSPI_SSR_OFFSET 0x70 /* 32-bit Slave Select Register */
@@ -65,6 +66,7 @@
#define XSPI_INTR_TX_UNDERRUN 0x08 /* TxFIFO was underrun */
#define XSPI_INTR_RX_FULL 0x10 /* RxFIFO is full */
#define XSPI_INTR_RX_OVERRUN 0x20 /* RxFIFO was overrun */
+#define XSPI_INTR_TX_HALF_EMPTY 0x40 /* TxFIFO is half empty */
#define XIPIF_V123B_RESETR_OFFSET 0x40 /* IPIF reset register */
#define XIPIF_V123B_RESET_MASK 0x0a /* the value to write */
@@ -86,6 +88,7 @@ struct xilinx_spi {
bool big_endian; /* The device could be accessed big or little
* endian
*/
+ u8 bits_per_word;
};
/* to follow are some functions that does little of big endian read and
@@ -146,8 +149,9 @@ static void xspi_init_hw(struct xilinx_spi *xspi)
/* Disable the transmitter, enable Manual Slave Select Assertion,
* put SPI controller into master mode, and enable it */
xspi_write16(xspi, XSPI_CR_OFFSET,
- XSPI_CR_TRANS_INHIBIT | XSPI_CR_MANUAL_SSELECT
- | XSPI_CR_MASTER_MODE | XSPI_CR_ENABLE);
+ XSPI_CR_TRANS_INHIBIT | XSPI_CR_MANUAL_SSELECT |
+ XSPI_CR_MASTER_MODE | XSPI_CR_ENABLE | XSPI_CR_TXFIFO_RESET |
+ XSPI_CR_RXFIFO_RESET);
}
static void xilinx_spi_chipselect(struct spi_device *spi, int is_on)
@@ -179,17 +183,20 @@ static void xilinx_spi_chipselect(struct spi_device *spi, int is_on)
/* spi_bitbang requires custom setup_transfer() to be defined if there is a
* custom txrx_bufs(). We have nothing to setup here as the SPI IP block
- * supports just 8 bits per word, and SPI clock can't be changed in software.
- * Check for 8 bits per word. Chip select delay calculations could be
+ * supports 8 or 16 bits per word, which can not be changed in software.
+ * SPI clock can't be changed in software.
+ * Check for correct bits per word. Chip select delay calculations could be
* added here as soon as bitbang_work() can be made aware of the delay value.
*/
static int xilinx_spi_setup_transfer(struct spi_device *spi,
- struct spi_transfer *t)
+ struct spi_transfer *t)
{
u8 bits_per_word;
+ struct xilinx_spi *xspi = spi_master_get_devdata(spi->master);
- bits_per_word = (t) ? t->bits_per_word : spi->bits_per_word;
- if (bits_per_word != 8) {
+ bits_per_word = (t->bits_per_word) ? t->bits_per_word :
+ spi->bits_per_word;
+ if (bits_per_word != xspi->bits_per_word) {
dev_err(&spi->dev, "%s, unsupported bits_per_word=%d\n",
__func__, bits_per_word);
return -EINVAL;
@@ -200,33 +207,49 @@ static int xilinx_spi_setup_transfer(struct spi_device *spi,
static int xilinx_spi_setup(struct spi_device *spi)
{
- struct spi_bitbang *bitbang;
- struct xilinx_spi *xspi;
- int retval;
-
- xspi = spi_master_get_devdata(spi->master);
- bitbang = &xspi->bitbang;
-
- retval = xilinx_spi_setup_transfer(spi, NULL);
- if (retval < 0)
- return retval;
-
+ /* always return 0, we can not check the number of bits.
+ * There are cases when SPI setup is called before any driver is
+ * there, in that case the SPI core defaults to 8 bits, which we
+ * do not support in some cases. But if we return an error, the
+ * SPI device would not be registered and no driver can get hold of it
+ * When the driver is there, it will call SPI setup again with the
+ * correct number of bits per transfer.
+ * If a driver setups with the wrong bit number, it will fail when
+ * it tries to do a transfer
+ */
return 0;
}
static void xilinx_spi_fill_tx_fifo(struct xilinx_spi *xspi)
{
u8 sr;
+ u8 wsize;
+ if (xspi->bits_per_word == 8)
+ wsize = 1;
+ else if (xspi->bits_per_word == 16)
+ wsize = 2;
+ else
+ wsize = 4;
/* Fill the Tx FIFO with as many bytes as possible */
sr = xspi_read8(xspi, XSPI_SR_OFFSET);
- while ((sr & XSPI_SR_TX_FULL_MASK) == 0 && xspi->remaining_bytes > 0) {
+ while ((sr & XSPI_SR_TX_FULL_MASK) == 0 &&
+ xspi->remaining_bytes > 0) {
if (xspi->tx_ptr) {
- xspi_write8(xspi, XSPI_TXD_OFFSET, *xspi->tx_ptr++);
- } else {
+ if (wsize == 1)
+ xspi_write8(xspi, XSPI_TXD_OFFSET,
+ *xspi->tx_ptr);
+ else if (wsize == 2)
+ xspi_write16(xspi, XSPI_TXD_OFFSET,
+ *(u16 *)(xspi->tx_ptr));
+ else if (wsize == 4)
+ xspi_write32(xspi, XSPI_TXD_OFFSET,
+ *(u32 *)(xspi->tx_ptr));
+
+ xspi->tx_ptr += wsize;
+ } else
xspi_write8(xspi, XSPI_TXD_OFFSET, 0);
- }
- xspi->remaining_bytes--;
+ xspi->remaining_bytes -= wsize;
sr = xspi_read8(xspi, XSPI_SR_OFFSET);
}
}
@@ -283,6 +306,13 @@ static irqreturn_t xilinx_spi_irq(int irq, void *dev_id)
if (ipif_isr & XSPI_INTR_TX_EMPTY) { /* Transmission completed */
u16 cr;
u8 sr;
+ u8 rsize;
+ if (xspi->bits_per_word == 8)
+ rsize = 1;
+ else if (xspi->bits_per_word == 16)
+ rsize = 2;
+ else
+ rsize = 4;
/* A transmit has just completed. Process received data and
* check for more data to transmit. Always inhibit the
@@ -295,11 +325,22 @@ static irqreturn_t xilinx_spi_irq(int irq, void *dev_id)
/* Read out all the data from the Rx FIFO */
sr = xspi_read8(xspi, XSPI_SR_OFFSET);
while ((sr & XSPI_SR_RX_EMPTY_MASK) == 0) {
- u8 data;
+ u32 data;
+ if (rsize == 1)
+ data = xspi_read8(xspi, XSPI_RXD_OFFSET);
+ else if (rsize == 2)
+ data = xspi_read16(xspi, XSPI_RXD_OFFSET);
+ else
+ data = xspi_read32(xspi, XSPI_RXD_OFFSET);
- data = xspi_read8(xspi, XSPI_RXD_OFFSET);
if (xspi->rx_ptr) {
- *xspi->rx_ptr++ = data;
+ if (rsize == 1)
+ *xspi->rx_ptr = data & 0xff;
+ else if (rsize == 2)
+ *(u16 *)(xspi->rx_ptr) = data & 0xffff;
+ else
+ *((u32 *)(xspi->rx_ptr)) = data;
+ xspi->rx_ptr += rsize;
}
sr = xspi_read8(xspi, XSPI_SR_OFFSET);
}
@@ -323,7 +364,8 @@ static irqreturn_t xilinx_spi_irq(int irq, void *dev_id)
}
struct spi_master *xilinx_spi_init(struct device *dev, struct resource *mem,
- u32 irq, s16 bus_num, u16 num_chipselect, bool big_endian)
+ u32 irq, s16 bus_num, u16 num_chipselect, bool big_endian,
+ u8 bits_per_word)
{
struct spi_master *master;
struct xilinx_spi *xspi;
@@ -364,6 +406,7 @@ struct spi_master *xilinx_spi_init(struct device *dev, struct resource *mem,
xspi->mem = *mem;
xspi->irq = irq;
xspi->big_endian = big_endian;
+ xspi->bits_per_word = bits_per_word;
/* SPI controller initializations */
xspi_init_hw(xspi);
diff --git a/drivers/spi/xilinx_spi.h b/drivers/spi/xilinx_spi.h
index c381c4a..f73c35d 100644
--- a/drivers/spi/xilinx_spi.h
+++ b/drivers/spi/xilinx_spi.h
@@ -25,7 +25,8 @@
#define XILINX_SPI_NAME "xilinx_spi"
struct spi_master *xilinx_spi_init(struct device *dev, struct resource *mem,
- u32 irq, s16 bus_num, u16 num_chipselect, bool big_endian);
+ u32 irq, s16 bus_num, u16 num_chipselect, bool big_endian,
+ u8 bits_per_word);
void xilinx_spi_deinit(struct spi_master *master);
#endif
diff --git a/drivers/spi/xilinx_spi_of.c b/drivers/spi/xilinx_spi_of.c
index 83f23be..b9fa4e9 100644
--- a/drivers/spi/xilinx_spi_of.c
+++ b/drivers/spi/xilinx_spi_of.c
@@ -66,7 +66,7 @@ static int __init xilinx_spi_of_probe(struct of_device *ofdev,
return -EINVAL;
}
master = xilinx_spi_init(&ofdev->dev, r_mem, r_irq->start, -1, *prop,
- true);
+ true, 8);
if (IS_ERR(master))
return PTR_ERR(master);
^ permalink raw reply related
* [PATCH 2/4] xilinx_spi: Switch to iomem functions and support little endian.
From: Richard Röjfors @ 2009-11-11 14:38 UTC (permalink / raw)
To: spi-devel-general; +Cc: linuxppc-dev, Andrew Morton, dbrownell, John Linn
This patch changes the out_(be)(8|16|32) and in_(be)(8|16|32) calls to iowrite(8|16|32)
and ioread(8|16|32). This to be able to build on platforms not supporting the in/out calls
for instance x86.
Support is also added for little endian writes. In some systems the registers should be
accessed little endian rather than big endian.
Signed-off-by: Richard Röjfors <richard.rojfors@mocean-labs.com>
---
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index e60b264..9667650 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -236,7 +236,7 @@ config SPI_TXX9
config SPI_XILINX
tristate "Xilinx SPI controller"
- depends on EXPERIMENTAL
+ depends on HAS_IOMEM && EXPERIMENTAL
select SPI_BITBANG
help
This exposes the SPI controller IP from the Xilinx EDK.
diff --git a/drivers/spi/xilinx_spi.c b/drivers/spi/xilinx_spi.c
index 1562e9b..b00dabc 100644
--- a/drivers/spi/xilinx_spi.c
+++ b/drivers/spi/xilinx_spi.c
@@ -19,12 +19,12 @@
#include <linux/spi/spi_bitbang.h>
#include <linux/io.h>
-#define XILINX_SPI_NAME "xilinx_spi"
+#include "xilinx_spi.h"
/* Register definitions as per "OPB Serial Peripheral Interface (SPI) (v1.00e)
* Product Specification", DS464
*/
-#define XSPI_CR_OFFSET 0x62 /* 16-bit Control Register */
+#define XSPI_CR_OFFSET 0x60 /* 16-bit Control Register */
#define XSPI_CR_ENABLE 0x02
#define XSPI_CR_MASTER_MODE 0x04
@@ -36,7 +36,7 @@
#define XSPI_CR_MANUAL_SSELECT 0x80
#define XSPI_CR_TRANS_INHIBIT 0x100
-#define XSPI_SR_OFFSET 0x67 /* 8-bit Status Register */
+#define XSPI_SR_OFFSET 0x64 /* 8-bit Status Register */
#define XSPI_SR_RX_EMPTY_MASK 0x01 /* Receive FIFO is empty */
#define XSPI_SR_RX_FULL_MASK 0x02 /* Receive FIFO is full */
@@ -44,8 +44,8 @@
#define XSPI_SR_TX_FULL_MASK 0x08 /* Transmit FIFO is full */
#define XSPI_SR_MODE_FAULT_MASK 0x10 /* Mode fault error */
-#define XSPI_TXD_OFFSET 0x6b /* 8-bit Data Transmit Register */
-#define XSPI_RXD_OFFSET 0x6f /* 8-bit Data Receive Register */
+#define XSPI_TXD_OFFSET 0x68 /* 8-bit Data Transmit Register */
+#define XSPI_RXD_OFFSET 0x6c /* 8-bit Data Receive Register */
#define XSPI_SSR_OFFSET 0x70 /* 32-bit Slave Select Register */
@@ -83,23 +83,69 @@ struct xilinx_spi {
u8 *rx_ptr; /* pointer in the Tx buffer */
const u8 *tx_ptr; /* pointer in the Rx buffer */
int remaining_bytes; /* the number of bytes left to transfer */
+ bool big_endian; /* The device could be accessed big or little
+ * endian
+ */
};
-static void xspi_init_hw(void __iomem *regs_base)
+/* to follow are some functions that does little of big endian read and
+ * write depending on the config of the device.
+ */
+static inline void xspi_write8(struct xilinx_spi *xspi, u32 offs, u8 val)
+{
+ iowrite8(val, xspi->regs + offs + ((xspi->big_endian) ? 3 : 0));
+}
+
+static inline void xspi_write16(struct xilinx_spi *xspi, u32 offs, u16 val)
+{
+ if (xspi->big_endian)
+ iowrite16be(val, xspi->regs + offs + 2);
+ else
+ iowrite16(val, xspi->regs + offs);
+}
+
+static inline void xspi_write32(struct xilinx_spi *xspi, u32 offs, u32 val)
+{
+ if (xspi->big_endian)
+ iowrite32be(val, xspi->regs + offs);
+ else
+ iowrite32(val, xspi->regs + offs);
+}
+
+static inline u8 xspi_read8(struct xilinx_spi *xspi, u32 offs)
+{
+ return ioread8(xspi->regs + offs + ((xspi->big_endian) ? 3 : 0));
+}
+
+static inline u16 xspi_read16(struct xilinx_spi *xspi, u32 offs)
+{
+ if (xspi->big_endian)
+ return ioread16be(xspi->regs + offs + 2);
+ else
+ return ioread16(xspi->regs + offs);
+}
+
+static inline u32 xspi_read32(struct xilinx_spi *xspi, u32 offs)
+{
+ if (xspi->big_endian)
+ return ioread32be(xspi->regs + offs);
+ else
+ return ioread32(xspi->regs + offs);
+}
+
+static void xspi_init_hw(struct xilinx_spi *xspi)
{
/* Reset the SPI device */
- out_be32(regs_base + XIPIF_V123B_RESETR_OFFSET,
- XIPIF_V123B_RESET_MASK);
+ xspi_write32(xspi, XIPIF_V123B_RESETR_OFFSET, XIPIF_V123B_RESET_MASK);
/* Disable all the interrupts just in case */
- out_be32(regs_base + XIPIF_V123B_IIER_OFFSET, 0);
+ xspi_write32(xspi, XIPIF_V123B_IIER_OFFSET, 0);
/* Enable the global IPIF interrupt */
- out_be32(regs_base + XIPIF_V123B_DGIER_OFFSET,
- XIPIF_V123B_GINTR_ENABLE);
+ xspi_write32(xspi, XIPIF_V123B_DGIER_OFFSET, XIPIF_V123B_GINTR_ENABLE);
/* Deselect the slave on the SPI bus */
- out_be32(regs_base + XSPI_SSR_OFFSET, 0xffff);
+ xspi_write32(xspi, XSPI_SSR_OFFSET, 0xffff);
/* Disable the transmitter, enable Manual Slave Select Assertion,
* put SPI controller into master mode, and enable it */
- out_be16(regs_base + XSPI_CR_OFFSET,
+ xspi_write16(xspi, XSPI_CR_OFFSET,
XSPI_CR_TRANS_INHIBIT | XSPI_CR_MANUAL_SSELECT
| XSPI_CR_MASTER_MODE | XSPI_CR_ENABLE);
}
@@ -110,16 +156,15 @@ static void xilinx_spi_chipselect(struct spi_device *spi, int is_on)
if (is_on == BITBANG_CS_INACTIVE) {
/* Deselect the slave on the SPI bus */
- out_be32(xspi->regs + XSPI_SSR_OFFSET, 0xffff);
+ xspi_write32(xspi, XSPI_SSR_OFFSET, 0xffff);
} else if (is_on == BITBANG_CS_ACTIVE) {
/* Set the SPI clock phase and polarity */
- u16 cr = in_be16(xspi->regs + XSPI_CR_OFFSET)
- & ~XSPI_CR_MODE_MASK;
+ u16 cr = xspi_read16(xspi, XSPI_CR_OFFSET) & ~XSPI_CR_MODE_MASK;
if (spi->mode & SPI_CPHA)
cr |= XSPI_CR_CPHA;
if (spi->mode & SPI_CPOL)
cr |= XSPI_CR_CPOL;
- out_be16(xspi->regs + XSPI_CR_OFFSET, cr);
+ xspi_write16(xspi, XSPI_CR_OFFSET, cr);
/* We do not check spi->max_speed_hz here as the SPI clock
* frequency is not software programmable (the IP block design
@@ -127,7 +172,7 @@ static void xilinx_spi_chipselect(struct spi_device *spi, int is_on)
*/
/* Activate the chip select */
- out_be32(xspi->regs + XSPI_SSR_OFFSET,
+ xspi_write32(xspi, XSPI_SSR_OFFSET,
~(0x0001 << spi->chip_select));
}
}
@@ -174,15 +219,15 @@ static void xilinx_spi_fill_tx_fifo(struct xilinx_spi *xspi)
u8 sr;
/* Fill the Tx FIFO with as many bytes as possible */
- sr = in_8(xspi->regs + XSPI_SR_OFFSET);
+ sr = xspi_read8(xspi, XSPI_SR_OFFSET);
while ((sr & XSPI_SR_TX_FULL_MASK) == 0 && xspi->remaining_bytes > 0) {
if (xspi->tx_ptr) {
- out_8(xspi->regs + XSPI_TXD_OFFSET, *xspi->tx_ptr++);
+ xspi_write8(xspi, XSPI_TXD_OFFSET, *xspi->tx_ptr++);
} else {
- out_8(xspi->regs + XSPI_TXD_OFFSET, 0);
+ xspi_write8(xspi, XSPI_TXD_OFFSET, 0);
}
xspi->remaining_bytes--;
- sr = in_8(xspi->regs + XSPI_SR_OFFSET);
+ sr = xspi_read8(xspi, XSPI_SR_OFFSET);
}
}
@@ -204,18 +249,18 @@ static int xilinx_spi_txrx_bufs(struct spi_device *spi, struct spi_transfer *t)
/* Enable the transmit empty interrupt, which we use to determine
* progress on the transmission.
*/
- ipif_ier = in_be32(xspi->regs + XIPIF_V123B_IIER_OFFSET);
- out_be32(xspi->regs + XIPIF_V123B_IIER_OFFSET,
+ ipif_ier = xspi_read32(xspi, XIPIF_V123B_IIER_OFFSET);
+ xspi_write32(xspi, XIPIF_V123B_IIER_OFFSET,
ipif_ier | XSPI_INTR_TX_EMPTY);
/* Start the transfer by not inhibiting the transmitter any longer */
- cr = in_be16(xspi->regs + XSPI_CR_OFFSET) & ~XSPI_CR_TRANS_INHIBIT;
- out_be16(xspi->regs + XSPI_CR_OFFSET, cr);
+ cr = xspi_read16(xspi, XSPI_CR_OFFSET) & ~XSPI_CR_TRANS_INHIBIT;
+ xspi_write16(xspi, XSPI_CR_OFFSET, cr);
wait_for_completion(&xspi->done);
/* Disable the transmit empty interrupt */
- out_be32(xspi->regs + XIPIF_V123B_IIER_OFFSET, ipif_ier);
+ xspi_write32(xspi, XIPIF_V123B_IIER_OFFSET, ipif_ier);
return t->len - xspi->remaining_bytes;
}
@@ -232,8 +277,8 @@ static irqreturn_t xilinx_spi_irq(int irq, void *dev_id)
u32 ipif_isr;
/* Get the IPIF interrupts, and clear them immediately */
- ipif_isr = in_be32(xspi->regs + XIPIF_V123B_IISR_OFFSET);
- out_be32(xspi->regs + XIPIF_V123B_IISR_OFFSET, ipif_isr);
+ ipif_isr = xspi_read32(xspi, XIPIF_V123B_IISR_OFFSET);
+ xspi_write32(xspi, XIPIF_V123B_IISR_OFFSET, ipif_isr);
if (ipif_isr & XSPI_INTR_TX_EMPTY) { /* Transmission completed */
u16 cr;
@@ -244,20 +289,19 @@ static irqreturn_t xilinx_spi_irq(int irq, void *dev_id)
* transmitter while the Isr refills the transmit register/FIFO,
* or make sure it is stopped if we're done.
*/
- cr = in_be16(xspi->regs + XSPI_CR_OFFSET);
- out_be16(xspi->regs + XSPI_CR_OFFSET,
- cr | XSPI_CR_TRANS_INHIBIT);
+ cr = xspi_read16(xspi, XSPI_CR_OFFSET);
+ xspi_write16(xspi, XSPI_CR_OFFSET, cr | XSPI_CR_TRANS_INHIBIT);
/* Read out all the data from the Rx FIFO */
- sr = in_8(xspi->regs + XSPI_SR_OFFSET);
+ sr = xspi_read8(xspi, XSPI_SR_OFFSET);
while ((sr & XSPI_SR_RX_EMPTY_MASK) == 0) {
u8 data;
- data = in_8(xspi->regs + XSPI_RXD_OFFSET);
+ data = xspi_read8(xspi, XSPI_RXD_OFFSET);
if (xspi->rx_ptr) {
*xspi->rx_ptr++ = data;
}
- sr = in_8(xspi->regs + XSPI_SR_OFFSET);
+ sr = xspi_read8(xspi, XSPI_SR_OFFSET);
}
/* See if there is more data to send */
@@ -266,7 +310,7 @@ static irqreturn_t xilinx_spi_irq(int irq, void *dev_id)
/* Start the transfer by not inhibiting the
* transmitter any longer
*/
- out_be16(xspi->regs + XSPI_CR_OFFSET, cr);
+ xspi_write16(xspi, XSPI_CR_OFFSET, cr);
} else {
/* No more data to send.
* Indicate the transfer is completed.
@@ -279,7 +323,7 @@ static irqreturn_t xilinx_spi_irq(int irq, void *dev_id)
}
struct spi_master *xilinx_spi_init(struct device *dev, struct resource *mem,
- u32 irq, s16 bus_num, u16 num_chipselect)
+ u32 irq, s16 bus_num, u16 num_chipselect, bool big_endian)
{
struct spi_master *master;
struct xilinx_spi *xspi;
@@ -319,9 +363,10 @@ struct spi_master *xilinx_spi_init(struct device *dev, struct resource *mem,
xspi->mem = *mem;
xspi->irq = irq;
+ xspi->big_endian = big_endian;
/* SPI controller initializations */
- xspi_init_hw(xspi->regs);
+ xspi_init_hw(xspi);
/* Register for SPI Interrupt */
ret = request_irq(xspi->irq, xilinx_spi_irq, 0, XILINX_SPI_NAME, xspi);
diff --git a/drivers/spi/xilinx_spi.h b/drivers/spi/xilinx_spi.h
index 84c98ee..c381c4a 100644
--- a/drivers/spi/xilinx_spi.h
+++ b/drivers/spi/xilinx_spi.h
@@ -25,7 +25,7 @@
#define XILINX_SPI_NAME "xilinx_spi"
struct spi_master *xilinx_spi_init(struct device *dev, struct resource *mem,
- u32 irq, s16 bus_num, u16 num_chipselect);
+ u32 irq, s16 bus_num, u16 num_chipselect, bool big_endian);
void xilinx_spi_deinit(struct spi_master *master);
#endif
diff --git a/drivers/spi/xilinx_spi_of.c b/drivers/spi/xilinx_spi_of.c
index 5440253..83f23be 100644
--- a/drivers/spi/xilinx_spi_of.c
+++ b/drivers/spi/xilinx_spi_of.c
@@ -65,7 +65,8 @@ static int __init xilinx_spi_of_probe(struct of_device *ofdev,
dev_warn(&ofdev->dev, "no 'xlnx,num-ss-bits' property\n");
return -EINVAL;
}
- master = xilinx_spi_init(&ofdev->dev, r_mem, r_irq->start, -1, *prop);
+ master = xilinx_spi_init(&ofdev->dev, r_mem, r_irq->start, -1, *prop,
+ true);
if (IS_ERR(master))
return PTR_ERR(master);
^ permalink raw reply related
* [PATCH 1/4] xilinx_spi: Split into of driver and generic part.
From: Richard Röjfors @ 2009-11-11 14:38 UTC (permalink / raw)
To: spi-devel-general; +Cc: linuxppc-dev, Andrew Morton, dbrownell, John Linn
This patch splits the xilinx_spi driver into a generic part and a
OF driver part.
The reason for this is to later add in a platform driver as well.
Signed-off-by: Richard Röjfors <richard.rojfors@mocean-labs.com>
---
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 4b6f7cb..e60b264 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -236,7 +236,7 @@ config SPI_TXX9
config SPI_XILINX
tristate "Xilinx SPI controller"
- depends on (XILINX_VIRTEX || MICROBLAZE) && EXPERIMENTAL
+ depends on EXPERIMENTAL
select SPI_BITBANG
help
This exposes the SPI controller IP from the Xilinx EDK.
@@ -244,6 +244,12 @@ config SPI_XILINX
See the "OPB Serial Peripheral Interface (SPI) (v1.00e)"
Product Specification document (DS464) for hardware details.
+config SPI_XILINX_OF
+ tristate "Xilinx SPI controller OF device"
+ depends on SPI_XILINX && (XILINX_VIRTEX || MICROBLAZE)
+ help
+ This is the OF driver for the SPI controller IP from the Xilinx EDK.
+
#
# Add new SPI master controllers in alphabetical order above this line
#
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index 21a1182..97dee8f 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -31,6 +31,7 @@ obj-$(CONFIG_SPI_S3C24XX_GPIO) += spi_s3c24xx_gpio.o
obj-$(CONFIG_SPI_S3C24XX) += spi_s3c24xx.o
obj-$(CONFIG_SPI_TXX9) += spi_txx9.o
obj-$(CONFIG_SPI_XILINX) += xilinx_spi.o
+obj-$(CONFIG_SPI_XILINX_OF) += xilinx_spi_of.o
obj-$(CONFIG_SPI_SH_SCI) += spi_sh_sci.o
obj-$(CONFIG_SPI_STMP3XXX) += spi_stmp.o
# ... add above this line ...
diff --git a/drivers/spi/xilinx_spi.c b/drivers/spi/xilinx_spi.c
index 46b8c5c..1562e9b 100644
--- a/drivers/spi/xilinx_spi.c
+++ b/drivers/spi/xilinx_spi.c
@@ -14,11 +14,6 @@
#include <linux/module.h>
#include <linux/init.h>
#include <linux/interrupt.h>
-#include <linux/platform_device.h>
-
-#include <linux/of_platform.h>
-#include <linux/of_device.h>
-#include <linux/of_spi.h>
#include <linux/spi/spi.h>
#include <linux/spi/spi_bitbang.h>
@@ -78,7 +73,7 @@ struct xilinx_spi {
/* bitbang has to be first */
struct spi_bitbang bitbang;
struct completion done;
-
+ struct resource mem; /* phys mem */
void __iomem *regs; /* virt. address of the control registers */
u32 irq;
@@ -283,40 +278,17 @@ static irqreturn_t xilinx_spi_irq(int irq, void *dev_id)
return IRQ_HANDLED;
}
-static int __init xilinx_spi_of_probe(struct of_device *ofdev,
- const struct of_device_id *match)
+struct spi_master *xilinx_spi_init(struct device *dev, struct resource *mem,
+ u32 irq, s16 bus_num, u16 num_chipselect)
{
struct spi_master *master;
struct xilinx_spi *xspi;
- struct resource r_irq_struct;
- struct resource r_mem_struct;
-
- struct resource *r_irq = &r_irq_struct;
- struct resource *r_mem = &r_mem_struct;
- int rc = 0;
- const u32 *prop;
- int len;
+ int ret = 0;
- /* Get resources(memory, IRQ) associated with the device */
- master = spi_alloc_master(&ofdev->dev, sizeof(struct xilinx_spi));
-
- if (master == NULL) {
- return -ENOMEM;
- }
+ master = spi_alloc_master(dev, sizeof(struct xilinx_spi));
- dev_set_drvdata(&ofdev->dev, master);
-
- rc = of_address_to_resource(ofdev->node, 0, r_mem);
- if (rc) {
- dev_warn(&ofdev->dev, "invalid address\n");
- goto put_master;
- }
-
- rc = of_irq_to_resource(ofdev->node, 0, r_irq);
- if (rc == NO_IRQ) {
- dev_warn(&ofdev->dev, "no IRQ found\n");
- goto put_master;
- }
+ if (master == NULL)
+ return ERR_PTR(-ENOMEM);
/* the spi->mode bits understood by this driver: */
master->mode_bits = SPI_CPOL | SPI_CPHA;
@@ -329,128 +301,70 @@ static int __init xilinx_spi_of_probe(struct of_device *ofdev,
xspi->bitbang.master->setup = xilinx_spi_setup;
init_completion(&xspi->done);
- xspi->irq = r_irq->start;
-
- if (!request_mem_region(r_mem->start,
- r_mem->end - r_mem->start + 1, XILINX_SPI_NAME)) {
- rc = -ENXIO;
- dev_warn(&ofdev->dev, "memory request failure\n");
+ if (!request_mem_region(mem->start, resource_size(mem),
+ XILINX_SPI_NAME)) {
+ ret = -ENXIO;
goto put_master;
}
- xspi->regs = ioremap(r_mem->start, r_mem->end - r_mem->start + 1);
+ xspi->regs = ioremap(mem->start, resource_size(mem));
if (xspi->regs == NULL) {
- rc = -ENOMEM;
- dev_warn(&ofdev->dev, "ioremap failure\n");
- goto release_mem;
+ ret = -ENOMEM;
+ dev_warn(dev, "ioremap failure\n");
+ goto map_failed;
}
- xspi->irq = r_irq->start;
- /* dynamic bus assignment */
- master->bus_num = -1;
+ master->bus_num = bus_num;
+ master->num_chipselect = num_chipselect;
- /* number of slave select bits is required */
- prop = of_get_property(ofdev->node, "xlnx,num-ss-bits", &len);
- if (!prop || len < sizeof(*prop)) {
- dev_warn(&ofdev->dev, "no 'xlnx,num-ss-bits' property\n");
- goto unmap_io;
- }
- master->num_chipselect = *prop;
+ xspi->mem = *mem;
+ xspi->irq = irq;
/* SPI controller initializations */
xspi_init_hw(xspi->regs);
/* Register for SPI Interrupt */
- rc = request_irq(xspi->irq, xilinx_spi_irq, 0, XILINX_SPI_NAME, xspi);
- if (rc != 0) {
- dev_warn(&ofdev->dev, "irq request failure: %d\n", xspi->irq);
+ ret = request_irq(xspi->irq, xilinx_spi_irq, 0, XILINX_SPI_NAME, xspi);
+ if (ret != 0)
goto unmap_io;
- }
- rc = spi_bitbang_start(&xspi->bitbang);
- if (rc != 0) {
- dev_err(&ofdev->dev, "spi_bitbang_start FAILED\n");
+ ret = spi_bitbang_start(&xspi->bitbang);
+ if (ret != 0) {
+ dev_err(dev, "spi_bitbang_start FAILED\n");
goto free_irq;
}
- dev_info(&ofdev->dev, "at 0x%08X mapped to 0x%08X, irq=%d\n",
- (unsigned int)r_mem->start, (u32)xspi->regs, xspi->irq);
-
- /* Add any subnodes on the SPI bus */
- of_register_spi_devices(master, ofdev->node);
-
- return rc;
+ dev_info(dev, "at 0x%08X mapped to 0x%08X, irq=%d\n",
+ (u32)mem->start, (u32)xspi->regs, xspi->irq);
+ return master;
free_irq:
free_irq(xspi->irq, xspi);
unmap_io:
iounmap(xspi->regs);
-release_mem:
- release_mem_region(r_mem->start, resource_size(r_mem));
+map_failed:
+ release_mem_region(mem->start, resource_size(mem));
put_master:
spi_master_put(master);
- return rc;
+ return ERR_PTR(ret);
}
+EXPORT_SYMBOL(xilinx_spi_init);
-static int __devexit xilinx_spi_remove(struct of_device *ofdev)
+void xilinx_spi_deinit(struct spi_master *master)
{
struct xilinx_spi *xspi;
- struct spi_master *master;
- struct resource r_mem;
- master = platform_get_drvdata(ofdev);
xspi = spi_master_get_devdata(master);
spi_bitbang_stop(&xspi->bitbang);
free_irq(xspi->irq, xspi);
iounmap(xspi->regs);
- if (!of_address_to_resource(ofdev->node, 0, &r_mem))
- release_mem_region(r_mem.start, resource_size(&r_mem));
- dev_set_drvdata(&ofdev->dev, 0);
- spi_master_put(xspi->bitbang.master);
-
- return 0;
-}
-
-/* work with hotplug and coldplug */
-MODULE_ALIAS("platform:" XILINX_SPI_NAME);
-
-static int __exit xilinx_spi_of_remove(struct of_device *op)
-{
- return xilinx_spi_remove(op);
-}
-static struct of_device_id xilinx_spi_of_match[] = {
- { .compatible = "xlnx,xps-spi-2.00.a", },
- { .compatible = "xlnx,xps-spi-2.00.b", },
- {}
-};
-
-MODULE_DEVICE_TABLE(of, xilinx_spi_of_match);
-
-static struct of_platform_driver xilinx_spi_of_driver = {
- .owner = THIS_MODULE,
- .name = "xilinx-xps-spi",
- .match_table = xilinx_spi_of_match,
- .probe = xilinx_spi_of_probe,
- .remove = __exit_p(xilinx_spi_of_remove),
- .driver = {
- .name = "xilinx-xps-spi",
- .owner = THIS_MODULE,
- },
-};
-
-static int __init xilinx_spi_init(void)
-{
- return of_register_platform_driver(&xilinx_spi_of_driver);
+ release_mem_region(xspi->mem.start, resource_size(&xspi->mem));
+ spi_master_put(xspi->bitbang.master);
}
-module_init(xilinx_spi_init);
+EXPORT_SYMBOL(xilinx_spi_deinit);
-static void __exit xilinx_spi_exit(void)
-{
- of_unregister_platform_driver(&xilinx_spi_of_driver);
-}
-module_exit(xilinx_spi_exit);
MODULE_AUTHOR("MontaVista Software, Inc. <source@mvista.com>");
MODULE_DESCRIPTION("Xilinx SPI driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/spi/xilinx_spi.h b/drivers/spi/xilinx_spi.h
new file mode 100644
index 0000000..84c98ee
--- /dev/null
+++ b/drivers/spi/xilinx_spi.h
@@ -0,0 +1,31 @@
+/*
+ * xilinx_spi.h
+ * Copyright (c) 2009 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef _XILINX_SPI_H_
+#define _XILINX_SPI_H_ 1
+
+#include <linux/spi/spi.h>
+#include <linux/spi/spi_bitbang.h>
+
+#define XILINX_SPI_NAME "xilinx_spi"
+
+struct spi_master *xilinx_spi_init(struct device *dev, struct resource *mem,
+ u32 irq, s16 bus_num, u16 num_chipselect);
+
+void xilinx_spi_deinit(struct spi_master *master);
+#endif
diff --git a/drivers/spi/xilinx_spi_of.c b/drivers/spi/xilinx_spi_of.c
new file mode 100644
index 0000000..5440253
--- /dev/null
+++ b/drivers/spi/xilinx_spi_of.c
@@ -0,0 +1,126 @@
+/*
+ * xilinx_spi_of.c Support for Xilinx SPI OF devices
+ * Copyright (c) 2009 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* Supports:
+ * Xilinx SPI devices as OF devices
+ *
+ * Inspired by xilinx_spi.c, 2002-2007 (c) MontaVista Software, Inc.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+
+#include <linux/of_platform.h>
+#include <linux/of_device.h>
+#include <linux/of_spi.h>
+
+#include "xilinx_spi.h"
+
+
+static int __init xilinx_spi_of_probe(struct of_device *ofdev,
+ const struct of_device_id *match)
+{
+ struct resource r_irq_struct;
+ struct resource r_mem_struct;
+ struct spi_master *master;
+
+ struct resource *r_irq = &r_irq_struct;
+ struct resource *r_mem = &r_mem_struct;
+ int rc = 0;
+ const u32 *prop;
+ int len;
+
+ rc = of_address_to_resource(ofdev->node, 0, r_mem);
+ if (rc) {
+ dev_warn(&ofdev->dev, "invalid address\n");
+ return rc;
+ }
+
+ rc = of_irq_to_resource(ofdev->node, 0, r_irq);
+ if (rc == NO_IRQ) {
+ dev_warn(&ofdev->dev, "no IRQ found\n");
+ return -ENODEV;
+ }
+
+ /* number of slave select bits is required */
+ prop = of_get_property(ofdev->node, "xlnx,num-ss-bits", &len);
+ if (!prop || len < sizeof(*prop)) {
+ dev_warn(&ofdev->dev, "no 'xlnx,num-ss-bits' property\n");
+ return -EINVAL;
+ }
+ master = xilinx_spi_init(&ofdev->dev, r_mem, r_irq->start, -1, *prop);
+ if (IS_ERR(master))
+ return PTR_ERR(master);
+
+ dev_set_drvdata(&ofdev->dev, master);
+
+ /* Add any subnodes on the SPI bus */
+ of_register_spi_devices(master, ofdev->node);
+
+ return 0;
+}
+
+static int __devexit xilinx_spi_remove(struct of_device *ofdev)
+{
+ xilinx_spi_deinit(dev_get_drvdata(&ofdev->dev));
+ dev_set_drvdata(&ofdev->dev, 0);
+ return 0;
+}
+
+static int __exit xilinx_spi_of_remove(struct of_device *op)
+{
+ return xilinx_spi_remove(op);
+}
+
+static struct of_device_id xilinx_spi_of_match[] = {
+ { .compatible = "xlnx,xps-spi-2.00.a", },
+ { .compatible = "xlnx,xps-spi-2.00.b", },
+ {}
+};
+
+MODULE_DEVICE_TABLE(of, xilinx_spi_of_match);
+
+static struct of_platform_driver xilinx_spi_of_driver = {
+ .owner = THIS_MODULE,
+ .name = "xilinx-xps-spi",
+ .match_table = xilinx_spi_of_match,
+ .probe = xilinx_spi_of_probe,
+ .remove = __exit_p(xilinx_spi_of_remove),
+ .driver = {
+ .name = "xilinx-xps-spi",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init xilinx_spi_of_init(void)
+{
+ return of_register_platform_driver(&xilinx_spi_of_driver);
+}
+module_init(xilinx_spi_of_init);
+
+static void __exit xilinx_spi_of_exit(void)
+{
+ of_unregister_platform_driver(&xilinx_spi_of_driver);
+}
+module_exit(xilinx_spi_of_exit);
+
+MODULE_AUTHOR("Mocean Laboratories <info@mocean-labs.com>");
+MODULE_DESCRIPTION("Xilinx SPI OF driver");
+MODULE_LICENSE("GPL v2");
^ permalink raw reply related
* [PATCH 0/4] xilinx_spi: Split into platform and of driver, support new IP, platform independence.
From: Richard Röjfors @ 2009-11-11 14:37 UTC (permalink / raw)
To: spi-devel-general; +Cc: linuxppc-dev, Andrew Morton, dbrownell, John Linn
Hi,
To follow is a series of patches against the xilinx_spi driver.
The patchset is against 2.6.32-rc6
The patchset addresses several problems in the current driver:
* It's PPC only (OF and uses PPC specific memory operations)
* Only supporting big endian writes
* Supports only 8bit SPI, support for DS570 (also 16 or 32 bit) is added.
* A platform driver is introduced for non OF capable architectures.
These patches were posted as one big patch up to 4 iterations, not splitted into 4.
--Richard
^ permalink raw reply
* Re: [PATCH v5 0/4] pseries: Add cede support for cpu-offline
From: Peter Zijlstra @ 2009-11-11 13:25 UTC (permalink / raw)
To: Gautham R Shenoy
Cc: Benjamin Herrenschmidt, linux-kernel, Arun R Bharadwaj,
Andrew Morton, linuxppc-dev, Ingo Molnar
In-Reply-To: <20091030052106.25493.42109.stgit@sofia.in.ibm.com>
On Fri, 2009-10-30 at 10:52 +0530, Gautham R Shenoy wrote:
> Gautham R Shenoy (4):
> pseries: Serialize cpu hotplug operations during deactivate Vs deal=
locate
> pseries: Add code to online/offline CPUs of a DLPAR node.
> pSeries: Add hooks to put the CPU into an appropriate offline state
> pSeries: extended_cede_processor() helper function.
>=20
>=20
> Documentation/cpu-hotplug.txt | 6 +
> arch/powerpc/include/asm/lppaca.h | 9 +
> arch/powerpc/platforms/pseries/dlpar.c | 129 ++++++++++++++++
> arch/powerpc/platforms/pseries/hotplug-cpu.c | 182 +++++++++++++++++=
+++++-
> arch/powerpc/platforms/pseries/offline_states.h | 18 ++
> arch/powerpc/platforms/pseries/plpar_wrappers.h | 22 +++
> arch/powerpc/platforms/pseries/smp.c | 19 ++
> arch/powerpc/xmon/xmon.c | 3=20
> drivers/base/cpu.c | 2=20
> include/linux/cpu.h | 13 ++
> 10 files changed, 387 insertions(+), 16 deletions(-)
> create mode 100644 arch/powerpc/platforms/pseries/offline_states.h
Not quite sure how you solved the DLPAR communication but since pretty
much everything is under arch/powerpc/ I really don't have much to say.
^ permalink raw reply
* Re: [PATCH v5 0/4] pseries: Add cede support for cpu-offline
From: Gautham R Shenoy @ 2009-11-11 12:01 UTC (permalink / raw)
To: Peter Zijlstra, Nathan Fontenot, Benjamin Herrenschmidt
Cc: linux-kernel, Arun R Bharadwaj, Andrew Morton, linuxppc-dev,
Ingo Molnar
In-Reply-To: <20091030052106.25493.42109.stgit@sofia.in.ibm.com>
On Fri, Oct 30, 2009 at 10:52:43AM +0530, Gautham R Shenoy wrote:
> Hi,
Hi Peter,
Did you get a chance to look at this new design ?
>
> This is version 5 of patch series that provides a framework to choose the
> state a pseries CPU must be put to when it is offlined.
>
> Previous versions can be found here:
> Version 4: http://lkml.org/lkml/2009/10/9/59
> Version 3: http://lkml.org/lkml/2009/9/15/164
> Version 2: http://lkml.org/lkml/2009/8/28/102
> Version 1: http://lkml.org/lkml/2009/8/6/236
>
> Changes from the previous version include:
> - Rebased against Nathan Fontenot's latest "pseries kernel handling of dynamic
> logical paritioning v4" patches found here:
> http://lkml.org/lkml/2009/10/21/98
>
> - Added boot-time option to disable putting the offlined vcpus into an
> extended H_CEDE state.
>
> - Addressed Ben's comments regarding the if-else sequencing in
> pseries_mach_cpu_die().
>
> - Addition of comments for pseries_cpu_die() to distinguish it from
> pseries_mach_cpu_die()
>
> Also,
>
> - This approach addresses Peter Z's objections regarding layering
> violations. The user simply offlines the cpu and doesn't worry about what
> state the CPU should be put into. That part is automatically handled by the
> kernel.
>
> - It does not add any additional sysfs interface instead uses the existing
> sysfs interface to offline CPUs.
>
> - On platforms which do not have support for ceding the vcpu with a
> latency specifier value, the offlining mechanism defaults to the current
> method of calling rtas_stop_self().
>
> The patchset has been tested on the available pseries platforms and it works
> as per the expectations. I believe that the patch set is ready for inclusion.
> ---
>
> Gautham R Shenoy (4):
> pseries: Serialize cpu hotplug operations during deactivate Vs deallocate
> pseries: Add code to online/offline CPUs of a DLPAR node.
> pSeries: Add hooks to put the CPU into an appropriate offline state
> pSeries: extended_cede_processor() helper function.
>
>
> Documentation/cpu-hotplug.txt | 6 +
> arch/powerpc/include/asm/lppaca.h | 9 +
> arch/powerpc/platforms/pseries/dlpar.c | 129 ++++++++++++++++
> arch/powerpc/platforms/pseries/hotplug-cpu.c | 182 ++++++++++++++++++++++-
> arch/powerpc/platforms/pseries/offline_states.h | 18 ++
> arch/powerpc/platforms/pseries/plpar_wrappers.h | 22 +++
> arch/powerpc/platforms/pseries/smp.c | 19 ++
> arch/powerpc/xmon/xmon.c | 3
> drivers/base/cpu.c | 2
> include/linux/cpu.h | 13 ++
> 10 files changed, 387 insertions(+), 16 deletions(-)
> create mode 100644 arch/powerpc/platforms/pseries/offline_states.h
>
> --
> Thanks and Regards
> gautham.
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at http://www.tux.org/lkml/
>
--
Thanks and Regards
gautham
^ permalink raw reply
* Re: [spi-devel-general] [PATCH 3/3] mpc52xx_spi: add gpio chipselect
From: Wolfram Sang @ 2009-11-11 10:50 UTC (permalink / raw)
To: Luotao Fu; +Cc: spi-devel-general, David Brownell, linux-kernel, linuxppc-dev
In-Reply-To: <1257844329-20687-4-git-send-email-l.fu@pengutronix.de>
[-- Attachment #1: Type: text/plain, Size: 5363 bytes --]
On Tue, Nov 10, 2009 at 10:12:09AM +0100, Luotao Fu wrote:
> This one enables the mpc52xx_spi driver for usage of user defined gpio lines
> as chipselect. This way we can control some more spi devices than only one
>
> Signed-off-by: Luotao Fu <l.fu@pengutronix.de>
> ---
> drivers/spi/mpc52xx_spi.c | 57 +++++++++++++++++++++++++++++++++++++++++---
> 1 files changed, 53 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/spi/mpc52xx_spi.c b/drivers/spi/mpc52xx_spi.c
> index 79ba678..58c2ce5 100644
> --- a/drivers/spi/mpc52xx_spi.c
> +++ b/drivers/spi/mpc52xx_spi.c
> @@ -21,6 +21,7 @@
> #include <linux/spi/mpc52xx_spi.h>
> #include <linux/of_spi.h>
> #include <linux/io.h>
> +#include <linux/of_gpio.h>
> #include <asm/time.h>
> #include <asm/mpc52xx.h>
>
> @@ -79,7 +80,6 @@ struct mpc52xx_spi {
> spinlock_t lock;
> struct work_struct work;
>
> -
> /* Details of current transfer (length, and buffer pointers) */
> struct spi_message *message; /* current message */
> struct spi_transfer *transfer; /* current transfer */
> @@ -89,6 +89,8 @@ struct mpc52xx_spi {
> u8 *rx_buf;
> const u8 *tx_buf;
> int cs_change;
> + int gpio_cs_count;
> + unsigned int *gpio_cs;
> };
>
> /*
> @@ -96,7 +98,13 @@ struct mpc52xx_spi {
> */
> static void mpc52xx_spi_chipsel(struct mpc52xx_spi *ms, int value)
> {
> - out_8(ms->regs + SPI_PORTDATA, value ? 0 : 0x08);
> + int cs;
> +
> + if (ms->gpio_cs_count > 0) {
> + cs = ms->message->spi->chip_select;
> + gpio_direction_output(ms->gpio_cs[cs], value);
Use gpio_set_value() in combination with...
> + } else
> + out_8(ms->regs + SPI_PORTDATA, value ? 0 : 0x08);
> }
>
> /*
> @@ -390,8 +398,9 @@ static int __devinit mpc52xx_spi_probe(struct of_device *op,
> struct spi_master *master;
> struct mpc52xx_spi *ms;
> void __iomem *regs;
> - int rc;
> int ctrl1;
> + int rc, i = 0;
> + int gpio_cs;
>
> /* MMIO registers */
> dev_dbg(&op->dev, "probing mpc5200 SPI device\n");
> @@ -426,8 +435,8 @@ static int __devinit mpc52xx_spi_probe(struct of_device *op,
> rc = -ENOMEM;
> goto err_alloc;
> }
> +
> master->bus_num = -1;
> - master->num_chipselect = 1;
> master->setup = mpc52xx_spi_setup;
> master->transfer = mpc52xx_spi_transfer;
> master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LSB_FIRST;
> @@ -441,6 +450,39 @@ static int __devinit mpc52xx_spi_probe(struct of_device *op,
> ms->irq1 = irq_of_parse_and_map(op->node, 1);
> ms->state = mpc52xx_spi_fsmstate_idle;
> ms->ipb_freq = mpc5xxx_get_bus_frequency(op->node);
> + ms->gpio_cs_count = of_gpio_count(op->node);
> + if (ms->gpio_cs_count > 0) {
> + master->num_chipselect = ms->gpio_cs_count;
> + ms->gpio_cs = kmalloc(ms->gpio_cs_count * sizeof(unsigned int),
> + GFP_KERNEL);
> + if (!ms->gpio_cs) {
> + rc = -ENOMEM;
> + goto err_alloc;
> + }
> +
> + for (i = 0; i < ms->gpio_cs_count; i++) {
> + gpio_cs = of_get_gpio(op->node, i);
... with of_get_gpio_flags() to avoid initialisation jitter?
> + if (gpio_cs < 0) {
> + dev_err(&op->dev,
> + "could not parse the gpio field "
> + "in oftree\n");
> + rc = -ENODEV;
> + goto err_alloc;
That should go to err_gpio and unregister the previous allocated ones.
> + }
> +
> + ms->gpio_cs[i] = gpio_cs;
> + rc = gpio_request(ms->gpio_cs[i], dev_name(&op->dev));
> + if (rc) {
> + dev_err(&op->dev,
> + "can't request spi cs gpio #%d "
> + "on gpio line %d\n",
> + i, gpio_cs);
Last two lines could become one.
> + goto err_gpio;
> + }
> + }
> + } else
> + master->num_chipselect = 1;
> +
> spin_lock_init(&ms->lock);
> INIT_LIST_HEAD(&ms->queue);
> INIT_WORK(&ms->work, mpc52xx_spi_wq);
> @@ -477,6 +519,9 @@ static int __devinit mpc52xx_spi_probe(struct of_device *op,
> err_register:
> dev_err(&ms->master->dev, "initialization failed\n");
> spi_master_put(master);
> + err_gpio:
> + while (i-- > 0)
> + gpio_free(ms->gpio_cs[i]);
> err_alloc:
> err_init:
> iounmap(regs);
> @@ -487,10 +532,14 @@ static int __devexit mpc52xx_spi_remove(struct of_device *op)
> {
> struct spi_master *master = dev_get_drvdata(&op->dev);
> struct mpc52xx_spi *ms = spi_master_get_devdata(master);
> + int i;
>
> free_irq(ms->irq0, ms);
> free_irq(ms->irq1, ms);
>
> + for (i = 0; i < ms->gpio_cs_count; i++)
> + gpio_free(ms->gpio_cs[i]);
> +
> spi_unregister_master(master);
> spi_master_put(master);
> iounmap(ms->regs);
> --
> 1.6.5.2
>
>
> ------------------------------------------------------------------------------
> Let Crystal Reports handle the reporting - Free Crystal Reports 2008 30-Day
> trial. Simplify your report design, integration and deployment - and focus on
> what you do best, core application coding. Discover what's new with
> Crystal Reports now. http://p.sf.net/sfu/bobj-july
> _______________________________________________
> spi-devel-general mailing list
> spi-devel-general@lists.sourceforge.net
> https://lists.sourceforge.net/lists/listinfo/spi-devel-general
--
Pengutronix e.K. | Wolfram Sang |
Industrial Linux Solutions | http://www.pengutronix.de/ |
[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 197 bytes --]
^ permalink raw reply
* Re: [PATCH 2/3] mpc52xx_spi: add missing mode_bits definition
From: Wolfram Sang @ 2009-11-11 10:38 UTC (permalink / raw)
To: Luotao Fu; +Cc: spi-devel-general, David Brownell, linux-kernel, linuxppc-dev
In-Reply-To: <1257844329-20687-3-git-send-email-l.fu@pengutronix.de>
[-- Attachment #1: Type: text/plain, Size: 1202 bytes --]
On Tue, Nov 10, 2009 at 10:12:08AM +0100, Luotao Fu wrote:
> Signed-off-by: Luotao Fu <l.fu@pengutronix.de>
SPI_CS_HIGH should be dropped, otherwise:
Reviewed-by: Wolfram Sang <w.sang@pengutronix.de>
> ---
> drivers/spi/mpc52xx_spi.c | 2 ++
> 1 files changed, 2 insertions(+), 0 deletions(-)
>
> diff --git a/drivers/spi/mpc52xx_spi.c b/drivers/spi/mpc52xx_spi.c
> index 5b036f2..79ba678 100644
> --- a/drivers/spi/mpc52xx_spi.c
> +++ b/drivers/spi/mpc52xx_spi.c
> @@ -430,6 +430,8 @@ static int __devinit mpc52xx_spi_probe(struct of_device *op,
> master->num_chipselect = 1;
> master->setup = mpc52xx_spi_setup;
> master->transfer = mpc52xx_spi_transfer;
> + master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LSB_FIRST;
> +
> dev_set_drvdata(&op->dev, master);
>
> ms = spi_master_get_devdata(master);
> --
> 1.6.5.2
>
> _______________________________________________
> Linuxppc-dev mailing list
> Linuxppc-dev@lists.ozlabs.org
> https://lists.ozlabs.org/listinfo/linuxppc-dev
--
Pengutronix e.K. | Wolfram Sang |
Industrial Linux Solutions | http://www.pengutronix.de/ |
[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 197 bytes --]
^ permalink raw reply
* Re: [spi-devel-general] [PATCH v4] xilinx_spi: Splitted into generic, of and platform driver, added support for DS570
From: Richard Röjfors @ 2009-11-11 10:18 UTC (permalink / raw)
To: Grant Likely
Cc: spi-devel-general, Andrew Morton, dbrownell, John Linn,
linuxppc-dev
In-Reply-To: <fa686aa40911100844q19789297h8e6713e915185a7@mail.gmail.com>
Grant Likely wrote:
>
> Hi Richard. Please do another spin of this patch. I don't have any
> particular problem with the changes, but it needs to be in a more
> granular form so I can review it properly.
I will post an update today.
In the future, if you are "quasi responsible for everything Xilinx", please reply earlier to patches
on drivers for your IP:s.
--Richard
^ permalink raw reply
* Re: [PATCH 1/3] mpc52xx_spi: fix clearing status register
From: Wolfram Sang @ 2009-11-11 9:48 UTC (permalink / raw)
To: Luotao Fu; +Cc: spi-devel-general, David Brownell, linux-kernel, linuxppc-dev
In-Reply-To: <1257844329-20687-2-git-send-email-l.fu@pengutronix.de>
[-- Attachment #1: Type: text/plain, Size: 2659 bytes --]
On Tue, Nov 10, 2009 at 10:12:07AM +0100, Luotao Fu wrote:
> Before reading status register to check MODF failure, we have to clear it
> first since the MODF flag will be set after initializing the spi master,
> if the hardware comes up with a low SS. The processor datasheet reads:
> Mode Fault flag -- bit sets if SS input goes low while SPI is configured as a
> master. Flag is cleared automatically by an SPI status register read (with MODF
> set) followed by a SPI control register 1 write.
> Hence simply rereading the register is not sufficient to clear the flag. We
> redo the write also to make sure to clear the flag.
>
> Signed-off-by: Luotao Fu <l.fu@pengutronix.de>
Nit below, otherwise:
Acked-by: Wolfram Sang <w.sang@pengutronix.de>
> ---
> drivers/spi/mpc52xx_spi.c | 6 +++++-
> 1 files changed, 5 insertions(+), 1 deletions(-)
>
> diff --git a/drivers/spi/mpc52xx_spi.c b/drivers/spi/mpc52xx_spi.c
> index ef8379b..5b036f2 100644
> --- a/drivers/spi/mpc52xx_spi.c
> +++ b/drivers/spi/mpc52xx_spi.c
> @@ -391,6 +391,7 @@ static int __devinit mpc52xx_spi_probe(struct of_device *op,
> struct mpc52xx_spi *ms;
> void __iomem *regs;
> int rc;
> + int ctrl1;
u8?
>
> /* MMIO registers */
> dev_dbg(&op->dev, "probing mpc5200 SPI device\n");
> @@ -399,7 +400,8 @@ static int __devinit mpc52xx_spi_probe(struct of_device *op,
> return -ENODEV;
>
Sidenote to all: It was tested that simply moving the read here will not suffice.
> /* initialize the device */
> - out_8(regs+SPI_CTRL1, SPI_CTRL1_SPIE | SPI_CTRL1_SPE | SPI_CTRL1_MSTR);
> + ctrl1 = SPI_CTRL1_SPIE | SPI_CTRL1_SPE | SPI_CTRL1_MSTR;
> + out_8(regs + SPI_CTRL1, ctrl1);
> out_8(regs + SPI_CTRL2, 0x0);
> out_8(regs + SPI_DATADIR, 0xe); /* Set output pins */
> out_8(regs + SPI_PORTDATA, 0x8); /* Deassert /SS signal */
> @@ -409,6 +411,8 @@ static int __devinit mpc52xx_spi_probe(struct of_device *op,
> * on the SPI bus. This fault will also occur if the SPI signals
> * are not connected to any pins (port_config setting) */
> in_8(regs + SPI_STATUS);
> + out_8(regs + SPI_CTRL1, ctrl1);
> +
> in_8(regs + SPI_DATA);
> if (in_8(regs + SPI_STATUS) & SPI_STATUS_MODF) {
> dev_err(&op->dev, "mode fault; is port_config correct?\n");
> --
> 1.6.5.2
>
> _______________________________________________
> Linuxppc-dev mailing list
> Linuxppc-dev@lists.ozlabs.org
> https://lists.ozlabs.org/listinfo/linuxppc-dev
--
Pengutronix e.K. | Wolfram Sang |
Industrial Linux Solutions | http://www.pengutronix.de/ |
[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 197 bytes --]
^ permalink raw reply
* [PATCH] mpc5200/gpt: tiny fix for gpt period limitation
From: Albrecht Dreß @ 2009-11-11 8:49 UTC (permalink / raw)
To: grant.likely; +Cc: linuxppc-dev
This patch fixes a limitation of the 5200's period.
Signed-off-by: Albrecht Dre=DF <albrecht.dress@arcor.de>
---
arch/powerpc/include/asm/mpc52xx.h | 2 +-
arch/powerpc/platforms/52xx/mpc52xx_gpt.c | 6 +++---
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/arch/powerpc/include/asm/mpc52xx.h b/arch/powerpc/include/asm/=
mpc52xx.h
index 707ab75..933fb8f 100644
--- a/arch/powerpc/include/asm/mpc52xx.h
+++ b/arch/powerpc/include/asm/mpc52xx.h
@@ -279,7 +279,7 @@ extern void mpc52xx_restart(char *cmd);
/* mpc52xx_gpt.c */
struct mpc52xx_gpt_priv;
extern struct mpc52xx_gpt_priv *mpc52xx_gpt_from_irq(int irq);
-extern int mpc52xx_gpt_start_timer(struct mpc52xx_gpt_priv *gpt, int perio=
d,
+extern int mpc52xx_gpt_start_timer(struct mpc52xx_gpt_priv *gpt, u64 perio=
d,
int continuous);
extern void mpc52xx_gpt_stop_timer(struct mpc52xx_gpt_priv *gpt);
=20
diff --git a/arch/powerpc/platforms/52xx/mpc52xx_gpt.c b/arch/powerpc/platf=
orms/52xx/mpc52xx_gpt.c
index 2c3fa13..77572ab 100644
--- a/arch/powerpc/platforms/52xx/mpc52xx_gpt.c
+++ b/arch/powerpc/platforms/52xx/mpc52xx_gpt.c
@@ -378,12 +378,12 @@ EXPORT_SYMBOL(mpc52xx_gpt_from_irq);
/**
* mpc52xx_gpt_start_timer - Set and enable the GPT timer
* @gpt: Pointer to gpt private data structure
- * @period: period of timer
+ * @period: period of timer in ns; max. ~130s @ 33MHz IPB clock
* @continuous: set to 1 to make timer continuous free running
*
* An interrupt will be generated every time the timer fires
*/
-int mpc52xx_gpt_start_timer(struct mpc52xx_gpt_priv *gpt, int period,
+int mpc52xx_gpt_start_timer(struct mpc52xx_gpt_priv *gpt, u64 period,
int continuous)
{
u32 clear, set;
@@ -400,7 +400,7 @@ int mpc52xx_gpt_start_timer(struct mpc52xx_gpt_priv *gp=
t, int period,
* arithmatic is done here to preserve the precision until the valu=
e
* is scaled back down into the u32 range. Period is in 'ns', bus
* frequency is in Hz. */
- clocks =3D (u64)period * (u64)gpt->ipb_freq;
+ clocks =3D period * (u64)gpt->ipb_freq;
do_div(clocks, 1000000000); /* Scale it down to ns range */
=20
/* This device cannot handle a clock count greater than 32 bits */
Jetzt NEU: Do it youself E-Cards bei Arcor.de!
Stellen Sie Ihr eigenes Unikat zusammen und machen Sie dem Empf=E4nger eine=
ganz pers=F6nliche Freude!
E-Card Marke Eigenbau: HIER KLICKEN: http://www.arcor.de/rd/footer.ecard
^ permalink raw reply related
* [PATCH] mpc5200/gpt: tiny fix for gpt period limitation
From: Albrecht Dreß @ 2009-11-11 8:47 UTC (permalink / raw)
To: grant.likely; +Cc: linuxppc-dev
Jetzt NEU: Do it youself E-Cards bei Arcor.de!
Stellen Sie Ihr eigenes Unikat zusammen und machen Sie dem Empf=E4nger eine=
ganz pers=F6nliche Freude!
E-Card Marke Eigenbau: HIER KLICKEN: http://www.arcor.de/rd/footer.ecard
^ permalink raw reply
* Aw: Re: [PATCH 3/3] mpc52xx/wdt: WDT uses GPT api
From: Albrecht Dreß @ 2009-11-11 8:32 UTC (permalink / raw)
To: grant.likely; +Cc: linuxppc-dev, devicetree-discuss, wim
In-Reply-To: <fa686aa40911101307p12bba31s491ef1d8812ca033@mail.gmail.com>
Hi Grant:
O.k., thanks for your comments. If Wim doesn't have any objections to it, =
I will provide a merged patch. One consequence I forgot to mention is that=
we loose the ability to build the wdt support as module, but I don't think=
it's a real problem.
I think we still should keep the kernel config option enable/disable the wd=
t support, which would mask out the wdt code if disabled. Is that ok for y=
ou?
Thanks, Albrecht.
----- Original Nachricht ----
Von: Grant Likely <grant.likely@secretlab.ca>
An: Albrecht Dre=DF <albrecht.dress@arcor.de>
Datum: 10.11.2009 22:07
Betreff: Re: [PATCH 3/3] mpc52xx/wdt: WDT uses GPT api
> On Tue, Nov 10, 2009 at 1:26 PM, Albrecht Dre=DF <albrecht.dress@arcor.de=
>
> wrote:
> > Hi Grant:
> >
> > Am 10.11.09 20:59 schrieb(en) Grant Likely:
> >>
> >> On Tue, Nov 10, 2009 at 12:43 PM, Albrecht Dre=DF
> <albrecht.dress@arcor.de>
> >> wrote:
> >> > Use the MPC5200 GPT api for the WDT which drastically simplifies thi=
s
> >> > file.
> >> >
> >> > Signed-off-by: Albrecht Dre=DF <albrecht.dress@arcor.de>
> >> > ---
> >> >
> >> > =A0drivers/watchdog/mpc5200_wdt.c | =A0246
> >> > +++++++++++-----------------------------
> >> > =A01 files changed, 65 insertions(+), 181 deletions(-)
> >>
> >>
> >> Can the WDT functionality just be merged entirely into
> >> arch/powerpc/platforms/52xx/mpc52xx_gpt.c, eliminating the need for
> >> this file entirely? =A0I think I'd rather have all the GPT "built in"
> >> behaviour handled by a single driver.
> >
> > I also thought about it, as it has IMHO the cleaner code, and it would
> have
> > the extra benefit that the gpt-wdt api doesn't need to be public.
> >
> > However, the reasons I hesitated to do so are:
> > - I don't want to remove a file someone else wrote (even it doesn't
> work);
>=20
> Shouldn't be a problem, and I'll handle the fallout if it is.
>=20
> > - WDT code is shifted from drivers/watchdog to
> arch/powerpc/platforms/52xx
> > which might not be the "logical" place from the directory layout's pov;
>=20
> There is precedence of this in the past, particularly on arch or
> platform specific hardware drivers and multifunction devices. (Heck,
> that's almost entirely what arch/powerpc/sysdev is). I'm not
> concerned.
>=20
> > - a file living in arch/powerpc/platforms/52xx depends upon config
> options
> > set from drivers/watchdog/Kconfig which may be confusing.
>=20
> I'm not concerned about this either.
>=20
> > You see these are more political/cosmetical questions, so I would prefe=
r
> to
> > leave the decision to the maintainers (i.e. you and Wim). =A0Preparing =
a
> fully
> > merged driver is actually a matter of minutes!
>=20
> Do it. I'll champion getting it in. Wim, do you have any issues with
> this?
>=20
> g.
>=20
> --=20
> Grant Likely, B.Sc., P.Eng.
> Secret Lab Technologies Ltd.
> _______________________________________________
> Linuxppc-dev mailing list
> Linuxppc-dev@lists.ozlabs.org
> https://lists.ozlabs.org/listinfo/linuxppc-dev
>=20
Jetzt NEU: Do it youself E-Cards bei Arcor.de!
Stellen Sie Ihr eigenes Unikat zusammen und machen Sie dem Empf=E4nger eine=
ganz pers=F6nliche Freude!
E-Card Marke Eigenbau: HIER KLICKEN: http://www.arcor.de/rd/footer.ecard
^ permalink raw reply
* Re: [PATCH 2/3] mpc52xx/wdt: merge WDT code into the GPT
From: Albrecht Dreß @ 2009-11-11 8:27 UTC (permalink / raw)
To: grant.likely; +Cc: linuxppc-dev, devicetree-discuss, wim
Hi Grant:
Thanks a lot for your comments!
> On Tue, Nov 10, 2009 at 12:41 PM, Albrecht Dre=DF <albrecht.dress@arcor.d=
e>
> wrote:
> > Merge the WDT code into the GPT interface.
> >
> > Signed-off-by: Albrecht Dre=DF <albrecht.dress@arcor.de>
> > ---
>=20
> Hi Albrecht,
>=20
> Thanks for this work. Comments below.
>=20
> >
> > Notes:
> >
> > The maximum timeout for a 5200 GPT @ 33 MHz clock is ~130 seconds. =A0A=
s
> this
> > exceeds the range of an int, some api's had to be changed to u64.
> >
> > The WDT api is exported as to keep the WDT driver separated from the GP=
T
> > driver.
> >
> > If GPT0 is used as WDT, this prevents the use of any GPT0 GPT function
> (i.e.
> > they will fail with -EBUSY). =A0IOW, the safety function always has
> precedence
> > over the GPT function. =A0If the kernel has been compiled with
> > CONFIG_WATCHDOG_NOWAYOUT, this means that GPT0 is locked in WDT mode
> until
> > the next reboot - this may be a requirement in safety applications.
>=20
> This description would look great in the header comment block of the
> .c file.
O.k.
>=20
> Also, as I commented in patch 3; I'd rather see all the WDT
> functionality rolled into this driver. As long as you keep the
> functional code blocks logically arranged, I think the driver will be
> easier to maintain and understand that way.
>=20
> > =A0arch/powerpc/include/asm/mpc52xx.h =A0 =A0 =A0 =A0| =A0 18 ++-
> > =A0arch/powerpc/platforms/52xx/mpc52xx_gpt.c | =A0281
> ++++++++++++++++++++++++++---
> > =A02 files changed, 270 insertions(+), 29 deletions(-)
> >
> > diff --git a/arch/powerpc/platforms/52xx/mpc52xx_gpt.c
> b/arch/powerpc/platforms/52xx/mpc52xx_gpt.c
> > index 2c3fa13..8274ebb 100644
> > --- a/arch/powerpc/platforms/52xx/mpc52xx_gpt.c
> > +++ b/arch/powerpc/platforms/52xx/mpc52xx_gpt.c
> > @@ -60,9 +60,13 @@
> > =A0#include <asm/mpc52xx.h>
> >
> > =A0MODULE_DESCRIPTION("Freescale MPC52xx gpt driver");
> > -MODULE_AUTHOR("Sascha Hauer, Grant Likely");
> > +MODULE_AUTHOR("Sascha Hauer, Grant Likely, Albrecht Dre=DF");
> > =A0MODULE_LICENSE("GPL");
> >
> > +#if (defined(CONFIG_MPC5200_WDT)) ||
> (defined(CONFIG_MPC5200_WDT_MODULE))
> > +#define HAVE_MPC5200_WDT
> > +#endif
> > +
> > =A0/**
> > =A0* struct mpc52xx_gpt - Private data structure for MPC52xx GPT driver
> > =A0* @dev: pointer to device structure
> > @@ -70,6 +74,8 @@ MODULE_LICENSE("GPL");
> > =A0* @lock: spinlock to coordinate between different functions.
> > =A0* @of_gc: of_gpio_chip instance structure; used when GPIO is enabled
> > =A0* @irqhost: Pointer to irq_host instance; used when IRQ mode is
> supported
> > + * @wdt_mode: only used for gpt 0: 0 gpt-only timer, 1 can be used as =
a
> > + * =A0 =A0 =A0 =A0 =A0 =A0wdt, 2 currently used as wdt, cannot be used=
as gpt
> > =A0*/
> > =A0struct mpc52xx_gpt_priv {
> > =A0 =A0 =A0 =A0struct list_head list; =A0 =A0 =A0 =A0 =A0/* List of all=
GPT devices */
> > @@ -78,6 +84,9 @@ struct mpc52xx_gpt_priv {
> > =A0 =A0 =A0 =A0spinlock_t lock;
> > =A0 =A0 =A0 =A0struct irq_host *irqhost;
> > =A0 =A0 =A0 =A0u32 ipb_freq;
> > +#if defined(HAVE_MPC5200_WDT)
> > + =A0 =A0 =A0 u8 wdt_mode;
> > +#endif
>=20
> I wouldn't bother with the #if/#endif. I'm willing to sacrifice
> 32bits for the sake of readability of the code.
O.k., will remove it.
>=20
> > +#define NS_PER_SEC =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 1000000000L=
L
> > +
> > +#define MPC52xx_GPT_CAN_WDT =A0 =A0 =A0 =A0 =A0 =A0(1 << 0)
> > +#define MPC52xx_GPT_IS_WDT =A0 =A0 =A0 =A0 =A0 =A0 (1 << 1)
> > +
> > +
> > =A0/* -----------------------------------------------------------------=
----
> > =A0* Cascaded interrupt controller hooks
> > =A0*/
> > @@ -375,36 +393,22 @@ struct mpc52xx_gpt_priv *mpc52xx_gpt_from_irq(int
> irq)
> > =A0}
> > =A0EXPORT_SYMBOL(mpc52xx_gpt_from_irq);
> >
> > -/**
> > - * mpc52xx_gpt_start_timer - Set and enable the GPT timer
> > - * @gpt: Pointer to gpt private data structure
> > - * @period: period of timer
> > - * @continuous: set to 1 to make timer continuous free running
> > - *
> > - * An interrupt will be generated every time the timer fires
> > - */
> > -int mpc52xx_gpt_start_timer(struct mpc52xx_gpt_priv *gpt, int period,
> > - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0int continuous=
)
> > +/* Calculate the timer counter input register MBAR + 0x6n4 from the
> > + * period in ns. =A0The maximum period for 33 MHz IPB clock is ~130s. =
*/
> > +static int mpc52xx_gpt_calc_counter_input(u64 period, u64 ipb_freq,
> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =
=A0 =A0 =A0 u32 *reg_val)
> > =A0{
> > - =A0 =A0 =A0 u32 clear, set;
> > =A0 =A0 =A0 =A0u64 clocks;
> > =A0 =A0 =A0 =A0u32 prescale;
> > - =A0 =A0 =A0 unsigned long flags;
> > -
> > - =A0 =A0 =A0 clear =3D MPC52xx_GPT_MODE_MS_MASK | MPC52xx_GPT_MODE_CON=
TINUOUS;
> > - =A0 =A0 =A0 set =3D MPC52xx_GPT_MODE_MS_GPIO | MPC52xx_GPT_MODE_COUNT=
ER_ENABLE;
> > - =A0 =A0 =A0 if (continuous)
> > - =A0 =A0 =A0 =A0 =A0 =A0 =A0 set |=3D MPC52xx_GPT_MODE_CONTINUOUS;
> >
> > =A0 =A0 =A0 =A0/* Determine the number of clocks in the requested perio=
d. =A064 bit
> > =A0 =A0 =A0 =A0 * arithmatic is done here to preserve the precision unt=
il the
> value
> > - =A0 =A0 =A0 =A0* is scaled back down into the u32 range. =A0Period is=
in 'ns',
> bus
> > - =A0 =A0 =A0 =A0* frequency is in Hz. */
> > - =A0 =A0 =A0 clocks =3D (u64)period * (u64)gpt->ipb_freq;
> > - =A0 =A0 =A0 do_div(clocks, 1000000000); /* Scale it down to ns range =
*/
> > + =A0 =A0 =A0 =A0* is scaled back down into the u32 range. */
> > + =A0 =A0 =A0 clocks =3D period * ipb_freq;
> > + =A0 =A0 =A0 do_div(clocks, NS_PER_SEC); =A0 =A0 =A0 =A0 /* Scale it d=
own to ns range
> */
>=20
> Nit: I wouldn't bother with the NS_PER_SEC macro personally. ns per s
> is such a fundamental property that I'd rather see the actual number.
O.k.
>=20
> >
> > - =A0 =A0 =A0 /* This device cannot handle a clock count greater than 3=
2 bits
> */
> > - =A0 =A0 =A0 if (clocks > 0xffffffff)
> > + =A0 =A0 =A0 /* the maximum count is 0x10000 pre-scaler * 0xffff count=
*/
> > + =A0 =A0 =A0 if (clocks > 0xffff0000)
> > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0return -EINVAL;
>=20
> This a bug fix? Separate patch please.
Ooops, nope. That's wrong. Will remove it. The only *real* bug fix is th=
e fact that the period must be a 64-bit as the timer can serve longer perio=
ds than 2.1 seconds (int limit). I will provide a separate fix for that.
>=20
> > =A0 =A0 =A0 =A0/* Calculate the prescaler and count values from the clo=
cks value.
> > @@ -427,9 +431,47 @@ int mpc52xx_gpt_start_timer(struct mpc52xx_gpt_pri=
v
> *gpt, int period,
> > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0return -EINVAL;
> > =A0 =A0 =A0 =A0}
> >
> > + =A0 =A0 =A0 *reg_val =3D (prescale & 0xffff) << 16 | clocks;
> > + =A0 =A0 =A0 return 0;
> > +}
> > +
> > +/**
> > + * mpc52xx_gpt_start_timer - Set and enable the GPT timer
> > + * @gpt: Pointer to gpt private data structure
> > + * @period: period of timer in ns
> > + * @continuous: set to 1 to make timer continuous free running
> > + *
> > + * An interrupt will be generated every time the timer fires
> > + */
> > +int mpc52xx_gpt_start_timer(struct mpc52xx_gpt_priv *gpt, u64 period,
> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 int continuous)
> > +{
> > + =A0 =A0 =A0 u32 clear, set;
> > + =A0 =A0 =A0 u32 counter_reg;
> > + =A0 =A0 =A0 unsigned long flags;
> > +
> > +#if defined(HAVE_MPC5200_WDT)
> > + =A0 =A0 =A0 /* reject the operation if the timer is used as watchdog =
(gpt 0
> only) */
> > + =A0 =A0 =A0 spin_lock_irqsave(&gpt->lock, flags);
> > + =A0 =A0 =A0 if ((gpt->wdt_mode & MPC52xx_GPT_IS_WDT)) {
> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 spin_unlock_irqrestore(&gpt->lock, flags)=
;
> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return -EBUSY;
> > + =A0 =A0 =A0 }
> > + =A0 =A0 =A0 spin_unlock_irqrestore(&gpt->lock, flags);
> > +#endif
>=20
> I think the behaviour needs to be consistent, regardless of
> HAVE_MPC5200_WDT state. If the IS_WDT flag is set, then this needs
> to bail, even if WDT support is not enabled.
O.k., will do. It's a no-op if WDT support isn't enabled (i.e. IS_WDT is =
never set then), though, see below.
>=20
> Also, move this code block down into the spin lock/unlock block lower
> in the function. it doesn't make much sense to grab the spin lock
> twice in the same function, and the worst think that can happen if
> this test fails is that a few extra cycles are burned calculating what
> the counter_reg value should be.
Ouch. Yes, that's a source for races!
>=20
> > +
> > + =A0 =A0 =A0 clear =3D MPC52xx_GPT_MODE_MS_MASK | MPC52xx_GPT_MODE_CON=
TINUOUS;
> > + =A0 =A0 =A0 set =3D MPC52xx_GPT_MODE_MS_GPIO | MPC52xx_GPT_MODE_COUNT=
ER_ENABLE;
> > + =A0 =A0 =A0 if (continuous)
> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 set |=3D MPC52xx_GPT_MODE_CONTINUOUS;
> > +
> > + =A0 =A0 =A0 if (mpc52xx_gpt_calc_counter_input(period, (u64)gpt->ipb_=
freq,
> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =
=A0 =A0 =A0 =A0&counter_reg) !=3D 0)
> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return -EINVAL;
> > +
> > =A0 =A0 =A0 =A0/* Set and enable the timer */
> > =A0 =A0 =A0 =A0spin_lock_irqsave(&gpt->lock, flags);
> > - =A0 =A0 =A0 out_be32(&gpt->regs->count, prescale << 16 | clocks);
> > + =A0 =A0 =A0 out_be32(&gpt->regs->count, counter_reg);
> > =A0 =A0 =A0 =A0clrsetbits_be32(&gpt->regs->mode, clear, set);
> > =A0 =A0 =A0 =A0spin_unlock_irqrestore(&gpt->lock, flags);
> >
> > @@ -437,12 +479,175 @@ int mpc52xx_gpt_start_timer(struct mpc52xx_gpt_p=
riv
> *gpt, int period,
> > =A0}
> > =A0EXPORT_SYMBOL(mpc52xx_gpt_start_timer);
> >
> > -void mpc52xx_gpt_stop_timer(struct mpc52xx_gpt_priv *gpt)
> > +/**
> > + * mpc52xx_gpt_timer_period - Return the timer period in nanoseconds
> > + * Note: reads the timer config directly from the hardware
> > + */
> > +u64 mpc52xx_gpt_timer_period(struct mpc52xx_gpt_priv *gpt)
> > +{
> > + =A0 =A0 =A0 u64 period;
> > + =A0 =A0 =A0 u32 prescale;
> > + =A0 =A0 =A0 unsigned long flags;
> > +
> > + =A0 =A0 =A0 spin_lock_irqsave(&gpt->lock, flags);
> > + =A0 =A0 =A0 period =3D in_be32(&gpt->regs->count);
> > + =A0 =A0 =A0 spin_unlock_irqrestore(&gpt->lock, flags);
> > +
> > + =A0 =A0 =A0 prescale =3D period >> 16;
> > + =A0 =A0 =A0 period &=3D 0xffff;
> > + =A0 =A0 =A0 if (prescale =3D=3D 0)
> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 prescale =3D 0x10000;
> > + =A0 =A0 =A0 period =3D period * (u64) prescale * NS_PER_SEC;
> > + =A0 =A0 =A0 do_div(period, (u64)gpt->ipb_freq);
>=20
> Are the casts necessary? Maybe prescale should just be a u64 value?
Good point. Will do that.
>=20
> > + =A0 =A0 =A0 return period;
> > +}
> > +EXPORT_SYMBOL(mpc52xx_gpt_timer_period);
> > +
> > +int mpc52xx_gpt_stop_timer(struct mpc52xx_gpt_priv *gpt)
> > =A0{
> > + =A0 =A0 =A0 unsigned long flags;
> > +
> > + =A0 =A0 =A0 /* reject the operation if the timer is used as watchdog =
(gpt 0
> only) */
> > + =A0 =A0 =A0 spin_lock_irqsave(&gpt->lock, flags);
> > +#if defined(HAVE_MPC5200_WDT)
> > + =A0 =A0 =A0 if ((gpt->wdt_mode & MPC52xx_GPT_IS_WDT)) {
> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 spin_unlock_irqrestore(&gpt->lock, flags)=
;
> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return -EBUSY;
> > + =A0 =A0 =A0 }
> > +#endif
>=20
> Again, drop the #if/endif wrapper.
>=20
> > +/**
> > + * mpc52xx_gpt_wdt_release - Release the watchdog so it can be used as
> gpt
> > + * @gpt_wdt: the watchdog gpt
> > + *
> > + * Note: Stops the watchdog if CONFIG_WATCHDOG_NOWAYOUT is not defined=
.
> > + * the function does not protect itself form being called without a
> > + * timer or with a timer which cannot function as wdt.
> > + */
> > +int mpc52xx_gpt_wdt_release(struct mpc52xx_gpt_priv *gpt_wdt)
> > +{
> > +#if !defined(CONFIG_WATCHDOG_NOWAYOUT)
> > + =A0 =A0 =A0 unsigned long flags;
> > +
> > + =A0 =A0 =A0 spin_lock_irqsave(&gpt_wdt->lock, flags);
> > + =A0 =A0 =A0 clrbits32(&gpt_wdt->regs->mode,
> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 MPC52xx_GPT_MODE_COUNTER_ENABLE |
> MPC52xx_GPT_MODE_WDT_EN);
> > + =A0 =A0 =A0 gpt_wdt->wdt_mode &=3D ~MPC52xx_GPT_IS_WDT;
> > + =A0 =A0 =A0 spin_unlock_irqrestore(&gpt_wdt->lock, flags);
> > +#endif
> > + =A0 =A0 =A0 return 0;
> > +}
> > +EXPORT_SYMBOL(mpc52xx_gpt_wdt_release);
> > +
> > +#if !defined(CONFIG_WATCHDOG_NOWAYOUT)
> > +/**
> > + * mpc52xx_gpt_wdt_stop - Stop the watchdog
> > + * @gpt_wdt: the watchdog gpt
> > + *
> > + * Note: the function does not protect itself form being called withou=
t
> a
> > + * timer or with a timer which cannot function as wdt.
> > + */
> > +int mpc52xx_gpt_wdt_stop(struct mpc52xx_gpt_priv *gpt_wdt)
> > +{
> > + =A0 =A0 =A0 unsigned long flags;
> > +
> > + =A0 =A0 =A0 spin_lock_irqsave(&gpt_wdt->lock, flags);
> > + =A0 =A0 =A0 clrbits32(&gpt_wdt->regs->mode,
> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 MPC52xx_GPT_MODE_COUNTER_ENABLE |
> MPC52xx_GPT_MODE_WDT_EN);
> > + =A0 =A0 =A0 gpt_wdt->wdt_mode &=3D ~MPC52xx_GPT_IS_WDT;
> > + =A0 =A0 =A0 spin_unlock_irqrestore(&gpt_wdt->lock, flags);
> > + =A0 =A0 =A0 return 0;
> > +}
> > +EXPORT_SYMBOL(mpc52xx_gpt_wdt_stop);
> > +#endif =A0/* =A0CONFIG_WATCHDOG_NOWAYOUT =A0*/
> > +#endif =A0/* =A0HAVE_MPC5200_WDT =A0*/
> > +
> > =A0/* -----------------------------------------------------------------=
----
> > =A0* of_platform bus binding code
> > =A0*/
> > @@ -473,6 +678,30 @@ static int __devinit mpc52xx_gpt_probe(struct
> of_device *ofdev,
> > =A0 =A0 =A0 =A0list_add(&gpt->list, &mpc52xx_gpt_list);
> > =A0 =A0 =A0 =A0mutex_unlock(&mpc52xx_gpt_list_mutex);
> >
> > +#if defined(HAVE_MPC5200_WDT)
> > + =A0 =A0 =A0 /* check if this device could be a watchdog */
> > + =A0 =A0 =A0 if (of_get_property(ofdev->node, "fsl,has-wdt", NULL) ||
> > + =A0 =A0 =A0 =A0 =A0 of_get_property(ofdev->node, "has-wdt", NULL)) {
> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 const u32 *on_boot_wdt;
> > +
> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 gpt->wdt_mode =3D MPC52xx_GPT_CAN_WDT;
> > +
> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 /* check if the device shall be used as o=
n-boot watchdog
> */
> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 on_boot_wdt =3D of_get_property(ofdev->no=
de, "wdt,on-boot",
> NULL);
> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (on_boot_wdt) {
> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 gpt->wdt_mode |=3D MPC52x=
x_GPT_IS_WDT;
> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (*on_boot_wdt > 0 &&
> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 mpc52xx_gpt_wdt_s=
tart(gpt, *on_boot_wdt) =3D=3D
> 0)
> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 dev_info(=
gpt->dev,
> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =
=A0 =A0 =A0"running as wdt, timeout %us\n",
> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =
=A0 =A0 =A0*on_boot_wdt);
> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 else
> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 dev_info(=
gpt->dev, "reserved as wdt\n");
> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 } else
> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 dev_info(gpt->dev, "can f=
unction as wdt\n");
> > + =A0 =A0 =A0 }
> > +#endif
>=20
> Ditto on the #if/endif
I don't think it can be removed here. Think about the following:
- user has a dtb with fsl,has-wdt and fsl,wdt-on-boot properties and
- disables 5200 WDT in the kernel config.
Now the system refuses to use GPT0 as GPT, although the WDT functionality h=
as been disabled. Of course, the dts doesn't fit the kernel config now, bu=
t I think we should catch this case.
Actually I tried to implement your requirements from <http://lists.ozlabs.o=
rg/pipermail/linuxppc-dev/2009-August/074595.html>:
- fsl,has-wdt indicates that the GPT can serve as WDT;
- any access through the wdt api to a wdt-capable gpt actually reserves it =
as wdt;
- the new of property forces reserving the wdt before any gpt api function =
has chance to grab it as gpt.
Or did I get something wrong here?
Thanks,
Albrecht.
Jetzt NEU: Do it youself E-Cards bei Arcor.de!
Stellen Sie Ihr eigenes Unikat zusammen und machen Sie dem Empf=E4nger eine=
ganz pers=F6nliche Freude!
E-Card Marke Eigenbau: HIER KLICKEN: http://www.arcor.de/rd/footer.ecard
^ permalink raw reply
* Re: [PATCH 0/8] 8xx: Misc fixes for buggy insn
From: Scott Wood @ 2009-11-10 16:55 UTC (permalink / raw)
To: Joakim Tjernlund; +Cc: linuxppc-dev@ozlabs.org, Rex Feany
In-Reply-To: <4AF99695.800@freescale.com>
Scott Wood wrote:
> Joakim Tjernlund wrote:
>> Why does not pinning interact well with CPU15? If pinned, you never get
>> a TLB miss for kernel text so that should mitigate the CPU15 problem.
>
> The nature of the workaround for CPU15 is that we can't keep it pinned
> -- we have to take an ITLB miss on every page boundary crossing. If you
> try to pin, it'll just be invalidated by the workaround.
Except that the invalidation only happens when you take an ITLB miss on
an adjacent page, which means we'd likely never get CPU15 protection for
kernel code if pinning is enabled. :-(
-Scott
^ permalink raw reply
* [git pull] Please pull powerpc.git merge branch
From: Benjamin Herrenschmidt @ 2009-11-11 5:01 UTC (permalink / raw)
To: Linus Torvalds; +Cc: Andrew Morton, linuxppc-dev list, Linux Kernel list
Hi Linus !
This contains one code fix, 3 little device-tree fixes and a bunch of
defconfig updates for Freescale platforms for 2.6.32 along with one
defconfig update for PA-Semi.
Overall, a large diffstat but not much actual churn. If you are unhappy
with the defconfig updates at that stage of the process, let us know and
I'll ask my sub-maintainers to do them earlier.
Cheers,
Ben.
The following changes since commit 799dd75b1a8380a967c929a4551895788c374b31:
Linus Torvalds (1):
Merge branch 'i2c-for-linus' of git://git.kernel.org/.../jdelvare/staging
are available in the git repository at:
git://git.kernel.org/pub/scm/linux/kernel/git/benh/powerpc.git merge
Anton Vorontsov (2):
powerpc/85xx: Fix USB GPIOs for MPC8569E-MDS boards
powerpc/83xx: Fix u-boot partion size for MPC8377E-WLAN boards
Benjamin Herrenschmidt (1):
Merge commit 'kumar/merge' into merge
Kim Phillips (1):
powerpc/8xxx: enable IPsec ESP by default on mpc83xx/mpc85xx
Kumar Gala (1):
powerpc: 2.6.32 update of defconfigs for embedded 6xx/7xxx, 8xx, 8{3,5,6}xxx
Olof Johansson (1):
powerpc: pasemi_defconfig update
Paul Gortmaker (1):
powerpc/85xx: sbc8548 - fixup of PCI-e related DTS fields
Roel Kluin (1):
powerpc/82xx: kmalloc failure ignored in ep8248e_mdio_probe()
arch/powerpc/boot/dts/mpc8377_wlan.dts | 2 +-
arch/powerpc/boot/dts/mpc8569mds.dts | 4 +-
arch/powerpc/boot/dts/sbc8548.dts | 17 +-
arch/powerpc/configs/83xx/asp8347_defconfig | 60 ++-
arch/powerpc/configs/83xx/kmeter1_defconfig | 46 +-
arch/powerpc/configs/83xx/mpc8313_rdb_defconfig | 73 ++-
arch/powerpc/configs/83xx/mpc8315_rdb_defconfig | 76 ++-
arch/powerpc/configs/83xx/mpc832x_mds_defconfig | 62 ++-
arch/powerpc/configs/83xx/mpc832x_rdb_defconfig | 66 ++-
arch/powerpc/configs/83xx/mpc834x_itx_defconfig | 60 ++-
arch/powerpc/configs/83xx/mpc834x_itxgp_defconfig | 58 ++-
arch/powerpc/configs/83xx/mpc834x_mds_defconfig | 58 ++-
arch/powerpc/configs/83xx/mpc836x_mds_defconfig | 64 ++-
arch/powerpc/configs/83xx/mpc836x_rdk_defconfig | 60 ++-
arch/powerpc/configs/83xx/mpc837x_mds_defconfig | 64 ++-
arch/powerpc/configs/83xx/mpc837x_rdb_defconfig | 70 ++-
arch/powerpc/configs/83xx/sbc834x_defconfig | 61 ++-
arch/powerpc/configs/85xx/ksi8560_defconfig | 61 ++-
arch/powerpc/configs/85xx/mpc8540_ads_defconfig | 57 ++-
arch/powerpc/configs/85xx/mpc8560_ads_defconfig | 63 ++-
arch/powerpc/configs/85xx/mpc85xx_cds_defconfig | 58 ++-
arch/powerpc/configs/85xx/sbc8548_defconfig | 53 ++-
arch/powerpc/configs/85xx/sbc8560_defconfig | 57 ++-
arch/powerpc/configs/85xx/socrates_defconfig | 68 ++-
arch/powerpc/configs/85xx/stx_gp3_defconfig | 77 ++-
arch/powerpc/configs/85xx/tqm8540_defconfig | 59 ++-
arch/powerpc/configs/85xx/tqm8541_defconfig | 64 ++-
arch/powerpc/configs/85xx/tqm8548_defconfig | 64 ++-
arch/powerpc/configs/85xx/tqm8555_defconfig | 64 ++-
arch/powerpc/configs/85xx/tqm8560_defconfig | 64 ++-
arch/powerpc/configs/85xx/xes_mpc85xx_defconfig | 76 ++-
arch/powerpc/configs/86xx/gef_ppc9a_defconfig | 74 ++-
arch/powerpc/configs/86xx/gef_sbc310_defconfig | 72 ++-
arch/powerpc/configs/86xx/gef_sbc610_defconfig | 80 ++-
arch/powerpc/configs/86xx/mpc8610_hpcd_defconfig | 64 ++-
arch/powerpc/configs/86xx/mpc8641_hpcn_defconfig | 65 ++-
arch/powerpc/configs/86xx/sbc8641d_defconfig | 66 ++-
arch/powerpc/configs/adder875_defconfig | 48 +-
arch/powerpc/configs/c2k_defconfig | 77 ++-
arch/powerpc/configs/ep8248e_defconfig | 49 +-
arch/powerpc/configs/ep88xc_defconfig | 47 +-
arch/powerpc/configs/linkstation_defconfig | 72 ++-
arch/powerpc/configs/mgcoge_defconfig | 51 ++-
arch/powerpc/configs/mgsuvd_defconfig | 45 +-
arch/powerpc/configs/mpc7448_hpc2_defconfig | 58 ++-
arch/powerpc/configs/mpc8272_ads_defconfig | 52 ++-
arch/powerpc/configs/mpc83xx_defconfig | 90 ++-
arch/powerpc/configs/mpc85xx_defconfig | 98 ++--
arch/powerpc/configs/mpc85xx_smp_defconfig | 99 ++--
arch/powerpc/configs/mpc866_ads_defconfig | 54 ++-
arch/powerpc/configs/mpc86xx_defconfig | 70 ++-
arch/powerpc/configs/mpc885_ads_defconfig | 47 +-
arch/powerpc/configs/pasemi_defconfig | 628 +++++++++++++++------
arch/powerpc/configs/pq2fads_defconfig | 55 ++-
arch/powerpc/configs/prpmc2800_defconfig | 67 ++-
arch/powerpc/configs/storcenter_defconfig | 54 ++-
arch/powerpc/platforms/82xx/ep8248e.c | 15 +-
57 files changed, 2603 insertions(+), 1380 deletions(-)
^ permalink raw reply
* RE: [PATCH 3/6] gianfar: Fix build with CONFIG_PM=y
From: Kumar Gopalpet-B05799 @ 2009-11-11 4:27 UTC (permalink / raw)
To: Anton Vorontsov, David Miller
Cc: Jon Loeliger, netdev, linuxppc-dev, Fleming Andy-AFLEMING,
Stephen Hemminger, Lennert Buytenhek
In-Reply-To: <20091111001105.GC8817@oksana.dev.rtsoft.ru>
=20
>-----Original Message-----
>From: Anton Vorontsov [mailto:avorontsov@ru.mvista.com]=20
>Sent: Wednesday, November 11, 2009 5:41 AM
>To: David Miller
>Cc: Fleming Andy-AFLEMING; Jon Loeliger; Kumar=20
>Gopalpet-B05799; Lennert Buytenhek; Stephen Hemminger;=20
>netdev@vger.kernel.org; linuxppc-dev@ozlabs.org
>Subject: [PATCH 3/6] gianfar: Fix build with CONFIG_PM=3Dy
>
>commit fba4ed030cfae7efdb6b79a57b0c5a9d72c9 ("gianfar: Add=20
>Multiple Queue Support") introduced the following build failure:
>
> CC gianfar.o
>gianfar.c: In function 'gfar_restore':
>gianfar.c:1249: error: request for member 'napi' in something=20
>not a structure or union
>
>This patch fixes the issue.
>
>Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com>
>---
> drivers/net/gianfar.c | 2 +-
> 1 files changed, 1 insertions(+), 1 deletions(-)
>
>diff --git a/drivers/net/gianfar.c b/drivers/net/gianfar.c=20
>index 79c28f5..a5b0038 100644
>--- a/drivers/net/gianfar.c
>+++ b/drivers/net/gianfar.c
>@@ -1246,7 +1246,7 @@ static int gfar_restore(struct device *dev)
> phy_start(priv->phydev);
>=20
> netif_device_attach(ndev);
>- napi_enable(&priv->gfargrp.napi);
>+ enable_napi(priv);
>=20
> return 0;
> }
I am extreemely sorry for introducing this error, I missed it while
porting my last set of patches.
Thanks for fixing it.
--
Thanks
Sandeep
^ permalink raw reply
* RE: [PATCH 6/6] gianfar: Revive SKB recycling
From: Kumar Gopalpet-B05799 @ 2009-11-11 4:20 UTC (permalink / raw)
To: Anton Vorontsov, David Miller
Cc: Jon Loeliger, netdev, linuxppc-dev, Fleming Andy-AFLEMING,
Stephen Hemminger, Lennert Buytenhek
In-Reply-To: <20091111001110.GF8817@oksana.dev.rtsoft.ru>
=20
>-----Original Message-----
>From: Anton Vorontsov [mailto:avorontsov@ru.mvista.com]=20
>Sent: Wednesday, November 11, 2009 5:41 AM
>To: David Miller
>Cc: Fleming Andy-AFLEMING; Jon Loeliger; Kumar=20
>Gopalpet-B05799; Lennert Buytenhek; Stephen Hemminger;=20
>netdev@vger.kernel.org; linuxppc-dev@ozlabs.org
>Subject: [PATCH 6/6] gianfar: Revive SKB recycling
>
>Before calling gfar_clean_tx_ring() the driver grabs an=20
>irqsave spinlock, and then tries to recycle skbs. But since
>skb_recycle_check() returns 0 with IRQs disabled, we'll never=20
>recycle any skbs.
>
>It appears that gfar_clean_tx_ring() and gfar_start_xmit() are=20
>mostly idependent and can work in parallel, except when they=20
>modify num_txbdfree.
>
>So we can drop the lock from most sections and thus fix the=20
>skb recycling.
>
>Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com>
>---
> drivers/net/gianfar.c | 31 +++++++++++++++++++------------
> 1 files changed, 19 insertions(+), 12 deletions(-)
>
>diff --git a/drivers/net/gianfar.c b/drivers/net/gianfar.c=20
>index fde430a..16def13 100644
>--- a/drivers/net/gianfar.c
>+++ b/drivers/net/gianfar.c
>@@ -1928,14 +1928,11 @@ static int gfar_start_xmit(struct=20
>sk_buff *skb, struct net_device *dev)
> /* total number of fragments in the SKB */
> nr_frags =3D skb_shinfo(skb)->nr_frags;
>=20
>- spin_lock_irqsave(&tx_queue->txlock, flags);
>-
> /* check if there is space to queue this packet */
> if ((nr_frags+1) > tx_queue->num_txbdfree) {
> /* no space, stop the queue */
> netif_tx_stop_queue(txq);
> dev->stats.tx_fifo_errors++;
>- spin_unlock_irqrestore(&tx_queue->txlock, flags);
> return NETDEV_TX_BUSY;
> }
>=20
>@@ -1999,6 +1996,20 @@ static int gfar_start_xmit(struct=20
>sk_buff *skb, struct net_device *dev)
> lstatus |=3D BD_LFLAG(TXBD_CRC | TXBD_READY) | skb_headlen(skb);
>=20
> /*
>+ * We can work in parallel with gfar_clean_tx_ring(), except
>+ * when modifying num_txbdfree. Note that we didn't=20
>grab the lock
>+ * when we were reading the num_txbdfree and checking=20
>for available
>+ * space, that's because outside of this function it=20
>can only grow,
>+ * and once we've got needed space, it cannot suddenly=20
>disappear.
>+ *
>+ * The lock also protects us from gfar_error(), which can modify
>+ * regs->tstat and thus retrigger the transfers, which is why we
>+ * also must grab the lock before setting ready bit for=20
>the first
>+ * to be transmitted BD.
>+ */
>+ spin_lock_irqsave(&tx_queue->txlock, flags);
>+
>+ /*
> * The powerpc-specific eieio() is used, as wmb() has too strong
> * semantics (it requires synchronization between cacheable and
> * uncacheable mappings, which eieio doesn't provide=20
>and which we @@ -2225,6 +2236,8 @@ static int=20
>gfar_clean_tx_ring(struct gfar_priv_tx_q *tx_queue)
> skb_dirtytx =3D tx_queue->skb_dirtytx;
>=20
> while ((skb =3D tx_queue->tx_skbuff[skb_dirtytx])) {
>+ unsigned long flags;
>+
> frags =3D skb_shinfo(skb)->nr_frags;
> lbdp =3D skip_txbd(bdp, frags, base, tx_ring_size);
>=20
>@@ -2269,7 +2282,9 @@ static int gfar_clean_tx_ring(struct=20
>gfar_priv_tx_q *tx_queue)
> TX_RING_MOD_MASK(tx_ring_size);
>=20
> howmany++;
>+ spin_lock_irqsave(&tx_queue->txlock, flags);
> tx_queue->num_txbdfree +=3D frags + 1;
>+ spin_unlock_irqrestore(&tx_queue->txlock, flags);
> }
>=20
> /* If we freed a buffer, we can restart transmission,=20
>if necessary */ @@ -2548,7 +2563,6 @@ static int=20
>gfar_poll(struct napi_struct *napi, int budget)
> int tx_cleaned =3D 0, i, left_over_budget =3D budget;
> unsigned long serviced_queues =3D 0;
> int num_queues =3D 0;
>- unsigned long flags;
>=20
> num_queues =3D gfargrp->num_rx_queues;
> budget_per_queue =3D budget/num_queues;
>@@ -2568,14 +2582,7 @@ static int gfar_poll(struct napi_struct=20
>*napi, int budget)
> rx_queue =3D priv->rx_queue[i];
> tx_queue =3D priv->tx_queue[rx_queue->qindex];
>=20
>- /* If we fail to get the lock,
>- * don't bother with the TX BDs */
>- if=20
>(spin_trylock_irqsave(&tx_queue->txlock, flags)) {
>- tx_cleaned +=3D=20
>gfar_clean_tx_ring(tx_queue);
>- =09
>spin_unlock_irqrestore(&tx_queue->txlock,
>- flags);
>- }
>-
>+ tx_cleaned +=3D gfar_clean_tx_ring(tx_queue);
> rx_cleaned_per_queue =3D=20
>gfar_clean_rx_ring(rx_queue,
> =09
>budget_per_queue);
> rx_cleaned +=3D rx_cleaned_per_queue;
>--
Anton, we tried some experiments too at our end, and removing the
spinlocks did help improve the performance and recycling was effective
although, I don't have exact numbers to specify.
But overall I agree with you in removing the spinlocks from the
gfar_poll context.
--
Thanks
Sandeep
^ permalink raw reply
* Re: Interrupts not Firing on PPC405EX
From: Tonyliu @ 2009-11-11 2:07 UTC (permalink / raw)
To: Jonathan Haws; +Cc: linuxppc-dev@lists.ozlabs.org
In-Reply-To: <BB99A6BA28709744BF22A68E6D7EB51F0331030DBB@midas.usurf.usu.edu>
Jonathan Haws wrote:
> All,
>
> I am having some troubles getting interrupts to fire from my kernel module. I have connected the ISR with a call to request_irq() and have configured my device to generate interrupts. However, my ISR is called once when I connect the interrupt for the first time. After that it never is called again. It seems like that interrupt is getting stuck disabled, but that does not make sense as to why.
>
> The device is on the PCIE0 bus and works just fine in another OS (namely Vxworks - that is the driver I am working on porting to Linux).
>
> Here is how I am connecting the ISR and the ISR itself. Am I doing something stupid?
>
> Thanks for the help!
>
> Jonathan
>
> PS - Our hardware is a custom spun PPC405EX board based on the AMCC Kilauea board and uses the kilauea.dts with no modifications.
>
> A quick note - I realize that I am not checking if I was the one to interrupt the CPU. I am not worried about that right now - especially since I know there is nothing else that will interrupt the CPU on this IRQ right now anyway - it never fires.
>
>
> int fpga_open(struct inode *inode, struct file *filp)
> {
> int err = 0;
>
> /* Make sure we have successfully probed the device */
> if (NULL == fpga_drv.pcidev)
> {
> return -ENODEV;
> }
>
> /* Only one process at a time can have access to the FPGA */
> if (0 != atomic_read(&fpga_drv.openCount))
> {
> atomic_inc(&fpga_drv.openCount);
> printk(KERN_WARNING "FPGA: Could not open device: already open. \n");
> return -EBUSY;
> }
>
> /* If not already in use, state that we are */
> atomic_inc(&fpga_drv.openCount);
>
> /* Store a pointer to the PCI device structure */
> filp->private_data = fpga_drv.pcidev;
>
> /* Attach ISR to IRQ */
> if (request_irq(fpga_drv.pcidev->irq, &fpga_isr, IRQF_SHARED,
> FPGA_MODULE_NAME, fpga_drv.pcidev))
> {
> printk( KERN_ERR "FPGA: Unable to connect FPGA ISR (%d)!\n",
> fpga_drv.pcidev->irq);
> return -EPERM;
> }
>
> return 0;
> }
>
> /* Interrupt Service Routine */
> irqreturn_t fpga_isr(int irq, void *dev_id)
> {
> uint32_t status = 0;
>
> status = fpga_drv.cfg_ptr[FPGA_INTER_STATUS];
>
> printk(KERN_NOTICE "FPGA: Interrupt fired! (%#08x)\n", status);
> if (status & FPGA_INTERRUPT_SPI)
> {
> rt_sem_v(&fpga_drv.sarSem);
> }
>
Your ISR registry and handling is fine. you mentioned that the IRQ only
be trigged once.
Are you sure that nothing is needed to reenable the HW IRQ generation
such as clear status bit ...
Is the algorithm in linux same with it in Vxworks?
Tony
> /* Return HANDLED */
> return (IRQ_HANDLED);}
> _______________________________________________
> Linuxppc-dev mailing list
> Linuxppc-dev@lists.ozlabs.org
> https://lists.ozlabs.org/listinfo/linuxppc-dev
>
>
--
Tony Liu | Liu Bo
-------------------------------------------------------------
WIND RIVER | China Development Center
Tel: 86-10-8477-8542 ext: 8542 | Fax: 86-10-64790367
(M): 86-136-7117-3612
Address: 15/F, Wangjing TowerB, Chaoyang District, Beijing, P.R.China
^ permalink raw reply
* [Fwd: [PATCH] powerpc: _exception: kill the obsolete code under is_global_init()]
From: Benjamin Herrenschmidt @ 2009-11-11 1:13 UTC (permalink / raw)
To: linuxppc-dev
[-- Attachment #1: Type: text/plain, Size: 1 bytes --]
[-- Attachment #2: Forwarded message - [PATCH] powerpc: _exception: kill the obsolete code under is_global_init() --]
[-- Type: message/rfc822, Size: 4365 bytes --]
From: Oleg Nesterov <oleg@redhat.com>
To: Andrew Morton <akpm@linux-foundation.org>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>, Geert Uytterhoeven <Geert.Uytterhoeven@sonycom.com>, Kumar Gala <galak@kernel.crashing.org>, Liu Yu <yu.liu@freescale.com>, Roland McGrath <roland@redhat.com>, linux-kernel@vger.kernel.org
Subject: [PATCH] powerpc: _exception: kill the obsolete code under is_global_init()
Date: Tue, 10 Nov 2009 01:25:42 +0100
Message-ID: <20091110002542.GA1312@redhat.com>
The code under "if (is_global_init())" is bogus, and is_global_init()
itself is not right in mt case.
Contrary to what the comment says, nowadays force_sig_info() does kill
init even if the handler is SIG_DFL. Note that force_sig_info() clears
SIGNAL_UNKILLABLE exactly for this case.
Signed-off-by: Oleg Nesterov <oleg@redhat.com>
---
arch/powerpc/kernel/traps.c | 22 ----------------------
1 file changed, 22 deletions(-)
--- TH/arch/powerpc/kernel/traps.c~PPC_EXCEPTION_DONT_CK_INIT 2009-11-10 01:03:23.000000000 +0100
+++ TH/arch/powerpc/kernel/traps.c 2009-11-10 01:06:41.000000000 +0100
@@ -198,28 +198,6 @@ void _exception(int signr, struct pt_reg
info.si_code = code;
info.si_addr = (void __user *) addr;
force_sig_info(signr, &info, current);
-
- /*
- * Init gets no signals that it doesn't have a handler for.
- * That's all very well, but if it has caused a synchronous
- * exception and we ignore the resulting signal, it will just
- * generate the same exception over and over again and we get
- * nowhere. Better to kill it and let the kernel panic.
- */
- if (is_global_init(current)) {
- __sighandler_t handler;
-
- spin_lock_irq(¤t->sighand->siglock);
- handler = current->sighand->action[signr-1].sa.sa_handler;
- spin_unlock_irq(¤t->sighand->siglock);
- if (handler == SIG_DFL) {
- /* init has generated a synchronous exception
- and it doesn't have a handler for the signal */
- printk(KERN_CRIT "init has generated signal %d "
- "but has no handler for it\n", signr);
- do_exit(signr);
- }
- }
}
#ifdef CONFIG_PPC64
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
^ permalink raw reply
* [PATCH 6/6] gianfar: Revive SKB recycling
From: Anton Vorontsov @ 2009-11-11 0:11 UTC (permalink / raw)
To: David Miller
Cc: Jon Loeliger, Kumar Gopalpet-B05799, netdev, linuxppc-dev,
Andy Fleming, Stephen Hemminger, Lennert Buytenhek
In-Reply-To: <20091111001036.GA28576@oksana.dev.rtsoft.ru>
Before calling gfar_clean_tx_ring() the driver grabs an irqsave
spinlock, and then tries to recycle skbs. But since
skb_recycle_check() returns 0 with IRQs disabled, we'll never
recycle any skbs.
It appears that gfar_clean_tx_ring() and gfar_start_xmit() are
mostly idependent and can work in parallel, except when they
modify num_txbdfree.
So we can drop the lock from most sections and thus fix the skb
recycling.
Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com>
---
drivers/net/gianfar.c | 31 +++++++++++++++++++------------
1 files changed, 19 insertions(+), 12 deletions(-)
diff --git a/drivers/net/gianfar.c b/drivers/net/gianfar.c
index fde430a..16def13 100644
--- a/drivers/net/gianfar.c
+++ b/drivers/net/gianfar.c
@@ -1928,14 +1928,11 @@ static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev)
/* total number of fragments in the SKB */
nr_frags = skb_shinfo(skb)->nr_frags;
- spin_lock_irqsave(&tx_queue->txlock, flags);
-
/* check if there is space to queue this packet */
if ((nr_frags+1) > tx_queue->num_txbdfree) {
/* no space, stop the queue */
netif_tx_stop_queue(txq);
dev->stats.tx_fifo_errors++;
- spin_unlock_irqrestore(&tx_queue->txlock, flags);
return NETDEV_TX_BUSY;
}
@@ -1999,6 +1996,20 @@ static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev)
lstatus |= BD_LFLAG(TXBD_CRC | TXBD_READY) | skb_headlen(skb);
/*
+ * We can work in parallel with gfar_clean_tx_ring(), except
+ * when modifying num_txbdfree. Note that we didn't grab the lock
+ * when we were reading the num_txbdfree and checking for available
+ * space, that's because outside of this function it can only grow,
+ * and once we've got needed space, it cannot suddenly disappear.
+ *
+ * The lock also protects us from gfar_error(), which can modify
+ * regs->tstat and thus retrigger the transfers, which is why we
+ * also must grab the lock before setting ready bit for the first
+ * to be transmitted BD.
+ */
+ spin_lock_irqsave(&tx_queue->txlock, flags);
+
+ /*
* The powerpc-specific eieio() is used, as wmb() has too strong
* semantics (it requires synchronization between cacheable and
* uncacheable mappings, which eieio doesn't provide and which we
@@ -2225,6 +2236,8 @@ static int gfar_clean_tx_ring(struct gfar_priv_tx_q *tx_queue)
skb_dirtytx = tx_queue->skb_dirtytx;
while ((skb = tx_queue->tx_skbuff[skb_dirtytx])) {
+ unsigned long flags;
+
frags = skb_shinfo(skb)->nr_frags;
lbdp = skip_txbd(bdp, frags, base, tx_ring_size);
@@ -2269,7 +2282,9 @@ static int gfar_clean_tx_ring(struct gfar_priv_tx_q *tx_queue)
TX_RING_MOD_MASK(tx_ring_size);
howmany++;
+ spin_lock_irqsave(&tx_queue->txlock, flags);
tx_queue->num_txbdfree += frags + 1;
+ spin_unlock_irqrestore(&tx_queue->txlock, flags);
}
/* If we freed a buffer, we can restart transmission, if necessary */
@@ -2548,7 +2563,6 @@ static int gfar_poll(struct napi_struct *napi, int budget)
int tx_cleaned = 0, i, left_over_budget = budget;
unsigned long serviced_queues = 0;
int num_queues = 0;
- unsigned long flags;
num_queues = gfargrp->num_rx_queues;
budget_per_queue = budget/num_queues;
@@ -2568,14 +2582,7 @@ static int gfar_poll(struct napi_struct *napi, int budget)
rx_queue = priv->rx_queue[i];
tx_queue = priv->tx_queue[rx_queue->qindex];
- /* If we fail to get the lock,
- * don't bother with the TX BDs */
- if (spin_trylock_irqsave(&tx_queue->txlock, flags)) {
- tx_cleaned += gfar_clean_tx_ring(tx_queue);
- spin_unlock_irqrestore(&tx_queue->txlock,
- flags);
- }
-
+ tx_cleaned += gfar_clean_tx_ring(tx_queue);
rx_cleaned_per_queue = gfar_clean_rx_ring(rx_queue,
budget_per_queue);
rx_cleaned += rx_cleaned_per_queue;
--
1.6.3.3
^ permalink raw reply related
* [PATCH 5/6] gianfar: Fix race between gfar_error() and gfar_start_xmit()
From: Anton Vorontsov @ 2009-11-11 0:11 UTC (permalink / raw)
To: David Miller
Cc: Jon Loeliger, Kumar Gopalpet-B05799, netdev, linuxppc-dev,
Andy Fleming, Stephen Hemminger, Lennert Buytenhek
In-Reply-To: <20091111001036.GA28576@oksana.dev.rtsoft.ru>
gfar_error() can arrive at the middle of gfar_start_xmit() processing,
and so it can trigger transfers of BDs that we don't yet expect to
be transmitted.
Fix this by locking the tx queues in gfar_error().
Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com>
---
drivers/net/gianfar.c | 8 ++++++++
1 files changed, 8 insertions(+), 0 deletions(-)
diff --git a/drivers/net/gianfar.c b/drivers/net/gianfar.c
index a5b0038..fde430a 100644
--- a/drivers/net/gianfar.c
+++ b/drivers/net/gianfar.c
@@ -2943,14 +2943,22 @@ static irqreturn_t gfar_error(int irq, void *grp_id)
if (events & IEVENT_CRL)
dev->stats.tx_aborted_errors++;
if (events & IEVENT_XFUN) {
+ unsigned long flags;
+
if (netif_msg_tx_err(priv))
printk(KERN_DEBUG "%s: TX FIFO underrun, "
"packet dropped.\n", dev->name);
dev->stats.tx_dropped++;
priv->extra_stats.tx_underrun++;
+ local_irq_save(flags);
+ lock_tx_qs(priv);
+
/* Reactivate the Tx Queues */
gfar_write(®s->tstat, gfargrp->tstat);
+
+ unlock_tx_qs(priv);
+ local_irq_restore(flags);
}
if (netif_msg_tx_err(priv))
printk(KERN_DEBUG "%s: Transmit Error\n", dev->name);
--
1.6.3.3
^ permalink raw reply related
* [PATCH 4/6] gianfar: Fix thinko in gfar_set_rx_stash_index()
From: Anton Vorontsov @ 2009-11-11 0:11 UTC (permalink / raw)
To: David Miller
Cc: Jon Loeliger, Kumar Gopalpet-B05799, netdev, linuxppc-dev,
Andy Fleming, Stephen Hemminger, Lennert Buytenhek
In-Reply-To: <20091111001036.GA28576@oksana.dev.rtsoft.ru>
We obviously want to write a modified 'temp' value back to the
register, not the saved IRQ flags.
Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com>
---
drivers/net/gianfar_sysfs.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/drivers/net/gianfar_sysfs.c b/drivers/net/gianfar_sysfs.c
index 3724835..b31c9c8 100644
--- a/drivers/net/gianfar_sysfs.c
+++ b/drivers/net/gianfar_sysfs.c
@@ -186,7 +186,7 @@ static ssize_t gfar_set_rx_stash_index(struct device *dev,
temp = gfar_read(®s->attreli);
temp &= ~ATTRELI_EI_MASK;
temp |= ATTRELI_EI(index);
- gfar_write(®s->attreli, flags);
+ gfar_write(®s->attreli, temp);
out:
unlock_rx_qs(priv);
--
1.6.3.3
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox