LinuxPPC-Dev Archive on lore.kernel.org
 help / color / mirror / Atom feed
* Re: [PATCH v3 5/7] mtd: m25p80: add support to parse the SPI flash's partitions
From: Grant Likely @ 2010-09-30 21:34 UTC (permalink / raw)
  To: Mingkai Hu; +Cc: linuxppc-dev, kumar.gala, linux-mtd, spi-devel-general
In-Reply-To: <1285833646-12006-6-git-send-email-Mingkai.hu@freescale.com>

On Thu, Sep 30, 2010 at 5:00 PM, Mingkai Hu <Mingkai.hu@freescale.com> wrot=
e:
> Signed-off-by: Mingkai Hu <Mingkai.hu@freescale.com>
> ---
> v3:
> =A0- Move the SPI flash partition code to the probe function.
>
> =A0drivers/mtd/devices/m25p80.c | =A0 39 +++++++++++++++++++++++++++-----=
-------
> =A01 files changed, 27 insertions(+), 12 deletions(-)
>
> diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c
> index 6f512b5..47d53c7 100644
> --- a/drivers/mtd/devices/m25p80.c
> +++ b/drivers/mtd/devices/m25p80.c
> @@ -772,7 +772,7 @@ static const struct spi_device_id *__devinit jedec_pr=
obe(struct spi_device *spi)
> =A0static int __devinit m25p_probe(struct spi_device *spi)
> =A0{
> =A0 =A0 =A0 =A0const struct spi_device_id =A0 =A0 =A0*id =3D spi_get_devi=
ce_id(spi);
> - =A0 =A0 =A0 struct flash_platform_data =A0 =A0 =A0*data;
> + =A0 =A0 =A0 struct flash_platform_data =A0 =A0 =A0data, *pdata;
> =A0 =A0 =A0 =A0struct m25p =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 *flash=
;
> =A0 =A0 =A0 =A0struct flash_info =A0 =A0 =A0 =A0 =A0 =A0 =A0 *info;
> =A0 =A0 =A0 =A0unsigned =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0i;
> @@ -782,13 +782,27 @@ static int __devinit m25p_probe(struct spi_device *=
spi)
> =A0 =A0 =A0 =A0 * a chip ID, try the JEDEC id commands; they'll work for =
most
> =A0 =A0 =A0 =A0 * newer chips, even if we don't recognize the particular =
chip.
> =A0 =A0 =A0 =A0 */
> - =A0 =A0 =A0 data =3D spi->dev.platform_data;
> - =A0 =A0 =A0 if (data && data->type) {
> + =A0 =A0 =A0 pdata =3D spi->dev.platform_data;
> + =A0 =A0 =A0 if (!pdata && spi->dev.of_node) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 int nr_parts;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 struct mtd_partition *parts;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 struct device_node *np =3D spi->dev.of_node=
;
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 nr_parts =3D of_mtd_parse_partitions(&spi->=
dev, np, &parts);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (nr_parts) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 pdata =3D &data;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 memset(pdata, 0, sizeof(*pd=
ata));
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 pdata->parts =3D parts;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 pdata->nr_parts =3D nr_part=
s;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 }
> + =A0 =A0 =A0 }

Yes, this is the correct way to go about adding the partitions.
However, this patch can be made simpler by not renaming 'data' to
'pdata' and by moving the above code down to just before the partition
information is actually used.  in the OF case, only the parts and the
nr_parts values written into data, and those values aren't used until
the last part of the probe function.

Regardless, in principle this patch is correct:

Acked-by: Grant Likely <grant.likely@secretlab.ca>

> +
> + =A0 =A0 =A0 if (pdata && pdata->type) {
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0const struct spi_device_id *plat_id;
>
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0for (i =3D 0; i < ARRAY_SIZE(m25p_ids) - 1=
; i++) {
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0plat_id =3D &m25p_ids[i];
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (strcmp(data->type, plat=
_id->name))
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (strcmp(pdata->type, pla=
t_id->name))
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0continue;
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0break;
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0}
> @@ -796,7 +810,8 @@ static int __devinit m25p_probe(struct spi_device *sp=
i)
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0if (i < ARRAY_SIZE(m25p_ids) - 1)
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0id =3D plat_id;
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0else
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 dev_warn(&spi->dev, "unreco=
gnized id %s\n", data->type);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 dev_warn(&spi->dev, "unreco=
gnized id %s\n",
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
 =A0 pdata->type);
> =A0 =A0 =A0 =A0}
>
> =A0 =A0 =A0 =A0info =3D (void *)id->driver_data;
> @@ -847,8 +862,8 @@ static int __devinit m25p_probe(struct spi_device *sp=
i)
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0write_sr(flash, 0);
> =A0 =A0 =A0 =A0}
>
> - =A0 =A0 =A0 if (data && data->name)
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 flash->mtd.name =3D data->name;
> + =A0 =A0 =A0 if (pdata && pdata->name)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 flash->mtd.name =3D pdata->name;
> =A0 =A0 =A0 =A0else
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0flash->mtd.name =3D dev_name(&spi->dev);
>
> @@ -919,9 +934,9 @@ static int __devinit m25p_probe(struct spi_device *sp=
i)
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =
=A0 =A0part_probes, &parts, 0);
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0}
>
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (nr_parts <=3D 0 && data && data->parts)=
 {
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 parts =3D data->parts;
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 nr_parts =3D data->nr_parts=
;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (nr_parts <=3D 0 && pdata && pdata->part=
s) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 parts =3D pdata->parts;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 nr_parts =3D pdata->nr_part=
s;
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0}

As per my comment earlier; since parts and nr_parts isn't needed
before this point, this block could simply be:

if (nr_parts <=3D 0 && data && data->parts) {
        parts =3D data->parts;
        nr_parts =3D data->nr_parts;
}
if (nr_parts <=3D 0 && spi->dev.of_node)
  =A0 =A0 =A0 nr_parts =3D of_mtd_parse_partitions(&spi->dev, np, &parts);

And most of the other changes to this file goes away.  Simpler, yes?

g.

^ permalink raw reply

* Re: [PATCH v3 6/7] mtd: m25p80: add a read function to read page by page
From: Grant Likely @ 2010-09-30 21:41 UTC (permalink / raw)
  To: Mingkai Hu; +Cc: linuxppc-dev, kumar.gala, linux-mtd, spi-devel-general
In-Reply-To: <1285833646-12006-7-git-send-email-Mingkai.hu@freescale.com>

Hmmm.... for some reason the previous replies didn't get picked up by
patchwork, so I'm replying with my comment again for the public
record.

In this case the eSPI controller driver is buggy and needs to be
fixed.  If the hardware can only support small transfers, then it is
the responsibilty of the driver to chain up smaller chunks into one
big transfer, and make sure that the CS line doesn't go low in the
middle of it.

g.

On Thu, Sep 30, 2010 at 5:00 PM, Mingkai Hu <Mingkai.hu@freescale.com> wrot=
e:
> For Freescale's eSPI controller, the max transaction length one time
> is limitted by the SPCOM[TRANSLEN] field which is 0xFFFF. When used
> mkfs.ext2 command to create ext2 filesystem on the flash, the read
> length will exceed the max value of the SPCOM[TRANSLEN] field, so
> change the read function to read page by page.
>
> For other SPI flash driver, also needed to supply the read function
> if used the eSPI controller.
>
> Signed-off-by: Mingkai Hu <Mingkai.hu@freescale.com>
> ---
> v3:
> =A0- Add a quirks member for the SPI master to handle the contrains of th=
e
> =A0 SPI controller. I can't think of other method. :-(
>
> =A0drivers/mtd/devices/m25p80.c | =A0 78 ++++++++++++++++++++++++++++++++=
++++++++++
> =A0drivers/spi/spi_fsl_lib.c =A0 =A0| =A0 =A04 ++
> =A0include/linux/spi/spi.h =A0 =A0 =A0| =A0 =A05 +++
> =A03 files changed, 87 insertions(+), 0 deletions(-)
>
> diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c
> index 47d53c7..f65cca8 100644
> --- a/drivers/mtd/devices/m25p80.c
> +++ b/drivers/mtd/devices/m25p80.c
> @@ -377,6 +377,81 @@ static int m25p80_read(struct mtd_info *mtd, loff_t =
from, size_t len,
> =A0}
>
> =A0/*
> + * Read an address range from the flash chip page by page.
> + * Some controller has transaction length limitation such as the
> + * Freescale's eSPI controller can only trasmit 0xFFFF bytes one
> + * time, so we have to read page by page if the len is more than
> + * the limitation.
> + */
> +static int m25p80_page_read(struct mtd_info *mtd, loff_t from, size_t le=
n,
> + =A0 =A0 =A0 size_t *retlen, u_char *buf)
> +{
> + =A0 =A0 =A0 struct m25p *flash =3D mtd_to_m25p(mtd);
> + =A0 =A0 =A0 struct spi_transfer t[2];
> + =A0 =A0 =A0 struct spi_message m;
> + =A0 =A0 =A0 u32 i, page_size =3D 0;
> +
> + =A0 =A0 =A0 DEBUG(MTD_DEBUG_LEVEL2, "%s: %s %s 0x%08x, len %zd\n",
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 dev_name(&flash->spi->dev),=
 __func__, "from",
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 (u32)from, len);
> +
> + =A0 =A0 =A0 /* sanity checks */
> + =A0 =A0 =A0 if (!len)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return 0;
> +
> + =A0 =A0 =A0 if (from + len > flash->mtd.size)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return -EINVAL;
> +
> + =A0 =A0 =A0 spi_message_init(&m);
> + =A0 =A0 =A0 memset(t, 0, (sizeof t));
> +
> + =A0 =A0 =A0 /* NOTE:
> + =A0 =A0 =A0 =A0* OPCODE_FAST_READ (if available) is faster.
> + =A0 =A0 =A0 =A0* Should add 1 byte DUMMY_BYTE.
> + =A0 =A0 =A0 =A0*/
> + =A0 =A0 =A0 t[0].tx_buf =3D flash->command;
> + =A0 =A0 =A0 t[0].len =3D m25p_cmdsz(flash) + FAST_READ_DUMMY_BYTE;
> + =A0 =A0 =A0 spi_message_add_tail(&t[0], &m);
> +
> + =A0 =A0 =A0 t[1].rx_buf =3D buf;
> + =A0 =A0 =A0 spi_message_add_tail(&t[1], &m);
> +
> + =A0 =A0 =A0 /* Byte count starts at zero. */
> + =A0 =A0 =A0 if (retlen)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 *retlen =3D 0;
> +
> + =A0 =A0 =A0 mutex_lock(&flash->lock);
> +
> + =A0 =A0 =A0 /* Wait till previous write/erase is done. */
> + =A0 =A0 =A0 if (wait_till_ready(flash)) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 /* REVISIT status return?? */
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 mutex_unlock(&flash->lock);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return 1;
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 /* Set up the write data buffer. */
> + =A0 =A0 =A0 flash->command[0] =3D OPCODE_READ;
> +
> + =A0 =A0 =A0 for (i =3D page_size; i < len; i +=3D page_size) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 page_size =3D len - i;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (page_size > flash->page_size)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 page_size =3D flash->page_s=
ize;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 m25p_addr2cmd(flash, from + i, flash->comma=
nd);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 t[1].len =3D page_size;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 t[1].rx_buf =3D buf + i;
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 spi_sync(flash->spi, &m);
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 *retlen +=3D m.actual_length - m25p_cmdsz(f=
lash)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 - FAST_READ_DUMMY_BYTE;
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 mutex_unlock(&flash->lock);
> +
> + =A0 =A0 =A0 return 0;
> +}
> +
> +/*
> =A0* Write an address range to the flash chip. =A0Data must be written in
> =A0* FLASH_PAGESIZE chunks. =A0The address range may be any size provided
> =A0* it is within the physical boundaries.
> @@ -874,6 +949,9 @@ static int __devinit m25p_probe(struct spi_device *sp=
i)
> =A0 =A0 =A0 =A0flash->mtd.erase =3D m25p80_erase;
> =A0 =A0 =A0 =A0flash->mtd.read =3D m25p80_read;
>
> + =A0 =A0 =A0 if (spi->master->quirks & SPI_QUIRK_TRANS_LEN_LIMIT)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 flash->mtd.read =3D m25p80_page_read;
> +
> =A0 =A0 =A0 =A0/* sst flash chips use AAI word program */
> =A0 =A0 =A0 =A0if (info->jedec_id >> 16 =3D=3D 0xbf)
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0flash->mtd.write =3D sst_write;
> diff --git a/drivers/spi/spi_fsl_lib.c b/drivers/spi/spi_fsl_lib.c
> index 5cd741f..c8d8c2d 100644
> --- a/drivers/spi/spi_fsl_lib.c
> +++ b/drivers/spi/spi_fsl_lib.c
> @@ -135,6 +135,10 @@ int mpc8xxx_spi_probe(struct device *dev, struct res=
ource *mem,
> =A0 =A0 =A0 =A0master->cleanup =3D mpc8xxx_spi_cleanup;
> =A0 =A0 =A0 =A0master->dev.of_node =3D dev->of_node;
>
> + =A0 =A0 =A0 if (of_get_property(dev->of_node,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 "fsl,spi-qu=
irk-trans-len-limit", NULL))
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 master->quirks |=3D SPI_QUIRK_TRANS_LEN_LIM=
IT;
> +
> =A0 =A0 =A0 =A0mpc8xxx_spi =3D spi_master_get_devdata(master);
> =A0 =A0 =A0 =A0mpc8xxx_spi->dev =3D dev;
> =A0 =A0 =A0 =A0mpc8xxx_spi->get_rx =3D mpc8xxx_spi_rx_buf_u8;
> diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h
> index 92e52a1..4234dfd 100644
> --- a/include/linux/spi/spi.h
> +++ b/include/linux/spi/spi.h
> @@ -304,6 +304,11 @@ struct spi_master {
>
> =A0 =A0 =A0 =A0/* called on release() to free memory provided by spi_mast=
er */
> =A0 =A0 =A0 =A0void =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0(*cleanup)(str=
uct spi_device *spi);
> +
> + =A0 =A0 =A0 /* some constraints of the controller */
> + =A0 =A0 =A0 u16 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 quirks;
> +#define SPI_QUIRK_TRANS_LEN_LIMIT =A0 =A0 =A0BIT(0) =A0/* have trans len=
gth limit */
> +
> =A0};
>
> =A0static inline void *spi_master_get_devdata(struct spi_master *master)
> --
> 1.6.4
>
>
>



--=20
Grant Likely, B.Sc., P.Eng.
Secret Lab Technologies Ltd.

^ permalink raw reply

* [PATCH v4 2/4] fsldma: implement support for scatterlist to scatterlist copy
From: Ira W. Snyder @ 2010-09-30 21:46 UTC (permalink / raw)
  To: linux-kernel; +Cc: Dan Williams, linuxppc-dev
In-Reply-To: <1285883207-13761-1-git-send-email-iws@ovro.caltech.edu>

Now that the DMAEngine API has support for scatterlist to scatterlist
copy, implement support for the Freescale DMA controller.

Signed-off-by: Ira W. Snyder <iws@ovro.caltech.edu>
---
 drivers/dma/fsldma.c |  128 ++++++++++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 125 insertions(+), 3 deletions(-)

diff --git a/drivers/dma/fsldma.c b/drivers/dma/fsldma.c
index cea08be..1ed29d1 100644
--- a/drivers/dma/fsldma.c
+++ b/drivers/dma/fsldma.c
@@ -38,6 +38,8 @@
 #include <asm/fsldma.h>
 #include "fsldma.h"
 
+static const char msg_ld_oom[] = "No free memory for link descriptor\n";
+
 static void dma_init(struct fsldma_chan *chan)
 {
 	/* Reset the channel */
@@ -499,7 +501,7 @@ fsl_dma_prep_interrupt(struct dma_chan *dchan, unsigned long flags)
 
 	new = fsl_dma_alloc_descriptor(chan);
 	if (!new) {
-		dev_err(chan->dev, "No free memory for link descriptor\n");
+		dev_err(chan->dev, msg_ld_oom);
 		return NULL;
 	}
 
@@ -536,8 +538,7 @@ static struct dma_async_tx_descriptor *fsl_dma_prep_memcpy(
 		/* Allocate the link descriptor from DMA pool */
 		new = fsl_dma_alloc_descriptor(chan);
 		if (!new) {
-			dev_err(chan->dev,
-					"No free memory for link descriptor\n");
+			dev_err(chan->dev, msg_ld_oom);
 			goto fail;
 		}
 #ifdef FSL_DMA_LD_DEBUG
@@ -583,6 +584,125 @@ fail:
 	return NULL;
 }
 
+static struct dma_async_tx_descriptor *fsl_dma_prep_sg(struct dma_chan *dchan,
+	struct scatterlist *dst_sg, unsigned int dst_nents,
+	struct scatterlist *src_sg, unsigned int src_nents,
+	unsigned long flags)
+{
+	struct fsl_desc_sw *first = NULL, *prev = NULL, *new = NULL;
+	struct fsldma_chan *chan = to_fsl_chan(dchan);
+	size_t dst_avail, src_avail;
+	dma_addr_t dst, src;
+	size_t len;
+
+	/* basic sanity checks */
+	if (dst_nents == 0 || src_nents == 0)
+		return NULL;
+
+	if (dst_sg == NULL || src_sg == NULL)
+		return NULL;
+
+	/*
+	 * TODO: should we check that both scatterlists have the same
+	 * TODO: number of bytes in total? Is that really an error?
+	 */
+
+	/* get prepared for the loop */
+	dst_avail = sg_dma_len(dst_sg);
+	src_avail = sg_dma_len(src_sg);
+
+	/* run until we are out of scatterlist entries */
+	while (true) {
+
+		/* create the largest transaction possible */
+		len = min_t(size_t, src_avail, dst_avail);
+		len = min_t(size_t, len, FSL_DMA_BCR_MAX_CNT);
+		if (len == 0)
+			goto fetch;
+
+		dst = sg_dma_address(dst_sg) + sg_dma_len(dst_sg) - dst_avail;
+		src = sg_dma_address(src_sg) + sg_dma_len(src_sg) - src_avail;
+
+		/* allocate and populate the descriptor */
+		new = fsl_dma_alloc_descriptor(chan);
+		if (!new) {
+			dev_err(chan->dev, msg_ld_oom);
+			goto fail;
+		}
+#ifdef FSL_DMA_LD_DEBUG
+		dev_dbg(chan->dev, "new link desc alloc %p\n", new);
+#endif
+
+		set_desc_cnt(chan, &new->hw, len);
+		set_desc_src(chan, &new->hw, src);
+		set_desc_dst(chan, &new->hw, dst);
+
+		if (!first)
+			first = new;
+		else
+			set_desc_next(chan, &prev->hw, new->async_tx.phys);
+
+		new->async_tx.cookie = 0;
+		async_tx_ack(&new->async_tx);
+		prev = new;
+
+		/* Insert the link descriptor to the LD ring */
+		list_add_tail(&new->node, &first->tx_list);
+
+		/* update metadata */
+		dst_avail -= len;
+		src_avail -= len;
+
+fetch:
+		/* fetch the next dst scatterlist entry */
+		if (dst_avail == 0) {
+
+			/* no more entries: we're done */
+			if (dst_nents == 0)
+				break;
+
+			/* fetch the next entry: if there are no more: done */
+			dst_sg = sg_next(dst_sg);
+			if (dst_sg == NULL)
+				break;
+
+			dst_nents--;
+			dst_avail = sg_dma_len(dst_sg);
+		}
+
+		/* fetch the next src scatterlist entry */
+		if (src_avail == 0) {
+
+			/* no more entries: we're done */
+			if (src_nents == 0)
+				break;
+
+			/* fetch the next entry: if there are no more: done */
+			src_sg = sg_next(src_sg);
+			if (src_sg == NULL)
+				break;
+
+			src_nents--;
+			src_avail = sg_dma_len(src_sg);
+		}
+	}
+
+	new->async_tx.flags = flags; /* client is in control of this ack */
+	new->async_tx.cookie = -EBUSY;
+
+	/* Set End-of-link to the last link descriptor of new list */
+	set_ld_eol(chan, new);
+
+	return &first->async_tx;
+
+fail:
+	if (!first)
+		return NULL;
+
+	fsldma_free_desc_list_reverse(chan, &first->tx_list);
+	return NULL;
+}
+
 /**
  * fsl_dma_prep_slave_sg - prepare descriptors for a DMA_SLAVE transaction
  * @chan: DMA channel
@@ -1327,11 +1447,13 @@ static int __devinit fsldma_of_probe(struct platform_device *op,
 
 	dma_cap_set(DMA_MEMCPY, fdev->common.cap_mask);
 	dma_cap_set(DMA_INTERRUPT, fdev->common.cap_mask);
+	dma_cap_set(DMA_SG, fdev->common.cap_mask);
 	dma_cap_set(DMA_SLAVE, fdev->common.cap_mask);
 	fdev->common.device_alloc_chan_resources = fsl_dma_alloc_chan_resources;
 	fdev->common.device_free_chan_resources = fsl_dma_free_chan_resources;
 	fdev->common.device_prep_dma_interrupt = fsl_dma_prep_interrupt;
 	fdev->common.device_prep_dma_memcpy = fsl_dma_prep_memcpy;
+	fdev->common.device_prep_dma_sg = fsl_dma_prep_sg;
 	fdev->common.device_tx_status = fsl_tx_status;
 	fdev->common.device_issue_pending = fsl_dma_memcpy_issue_pending;
 	fdev->common.device_prep_slave_sg = fsl_dma_prep_slave_sg;
-- 
1.7.1

^ permalink raw reply related

* [PATCH v4 1/4] dma: add support for scatterlist to scatterlist copy
From: Ira W. Snyder @ 2010-09-30 21:46 UTC (permalink / raw)
  To: linux-kernel; +Cc: Dan Williams, linuxppc-dev
In-Reply-To: <1285883207-13761-1-git-send-email-iws@ovro.caltech.edu>

This adds support for scatterlist to scatterlist DMA transfers. A
similar interface is exposed by the fsldma driver (through the DMA_SLAVE
API) and by the ste_dma40 driver (through an exported function).

This patch paves the way for making this type of copy operation a part
of the generic DMAEngine API. Futher patches will add support in
individual drivers.

Signed-off-by: Ira W. Snyder <iws@ovro.caltech.edu>
---
 drivers/dma/dmaengine.c   |    2 ++
 include/linux/dmaengine.h |    6 ++++++
 2 files changed, 8 insertions(+), 0 deletions(-)

diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c
index 9d31d5e..db403b8 100644
--- a/drivers/dma/dmaengine.c
+++ b/drivers/dma/dmaengine.c
@@ -690,6 +690,8 @@ int dma_async_device_register(struct dma_device *device)
 		!device->device_prep_dma_memset);
 	BUG_ON(dma_has_cap(DMA_INTERRUPT, device->cap_mask) &&
 		!device->device_prep_dma_interrupt);
+	BUG_ON(dma_has_cap(DMA_SG, device->cap_mask) &&
+		!device->device_prep_dma_sg);
 	BUG_ON(dma_has_cap(DMA_SLAVE, device->cap_mask) &&
 		!device->device_prep_slave_sg);
 	BUG_ON(dma_has_cap(DMA_SLAVE, device->cap_mask) &&
diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h
index c61d4ca..7c44620 100644
--- a/include/linux/dmaengine.h
+++ b/include/linux/dmaengine.h
@@ -64,6 +64,7 @@ enum dma_transaction_type {
 	DMA_PQ_VAL,
 	DMA_MEMSET,
 	DMA_INTERRUPT,
+	DMA_SG,
 	DMA_PRIVATE,
 	DMA_ASYNC_TX,
 	DMA_SLAVE,
@@ -473,6 +474,11 @@ struct dma_device {
 		unsigned long flags);
 	struct dma_async_tx_descriptor *(*device_prep_dma_interrupt)(
 		struct dma_chan *chan, unsigned long flags);
+	struct dma_async_tx_descriptor *(*device_prep_dma_sg)(
+		struct dma_chan *chan,
+		struct scatterlist *dst_sg, unsigned int dst_nents,
+		struct scatterlist *src_sg, unsigned int src_nents,
+		unsigned long flags);
 
 	struct dma_async_tx_descriptor *(*device_prep_slave_sg)(
 		struct dma_chan *chan, struct scatterlist *sgl,
-- 
1.7.1

^ permalink raw reply related

* [PATCH v4 0/4] dma: add support for scatterlist to scatterlist copy
From: Ira W. Snyder @ 2010-09-30 21:46 UTC (permalink / raw)
  To: linux-kernel; +Cc: Dan Williams, linuxppc-dev

This series adds support for scatterlist to scatterlist copies to the
generic DMAEngine API. Both the fsldma and ste_dma40 drivers currently
implement a similar API using different, non-generic methods. This series
converts both of them to the new, standardized API.

By doing this as part of the core DMAEngine API, the individual drivers
have control over how to chain their descriptors together. In addition,
this makes graceful failure much easier to support.

The fsldma implementation has been tested on real hardware, using slightly
modified versions of the patches posted a few weeks ago, titled:
[PATCH RFCv2 0/5] CARMA Board Support

These patches will be re-submitted with the appropriate changes after we're
happy with this series.

Thanks to all that provided input!
Ira

Changes since v3:
- ste40_dma's implementation now returns NULL (thanks Dan!)
- fsldma now uses a custom command for external control
- fsldma now uses the standard struct dma_slave_config
- tested on real hardware

Ira W. Snyder (4):
  dma: add support for scatterlist to scatterlist copy
  fsldma: implement support for scatterlist to scatterlist copy
  fsldma: improved DMA_SLAVE support
  ste_dma40: implement support for scatterlist to scatterlist copy

 arch/powerpc/include/asm/fsldma.h |  137 ---------------
 drivers/dma/dmaengine.c           |    2 +
 drivers/dma/fsldma.c              |  328 ++++++++++++++++++-------------------
 drivers/dma/ste_dma40.c           |   17 ++
 include/linux/dmaengine.h         |    9 +
 5 files changed, 184 insertions(+), 309 deletions(-)
 delete mode 100644 arch/powerpc/include/asm/fsldma.h

^ permalink raw reply

* [PATCH v4 4/4] ste_dma40: implement support for scatterlist to scatterlist copy
From: Ira W. Snyder @ 2010-09-30 21:46 UTC (permalink / raw)
  To: linux-kernel; +Cc: Per Fridén, Linus Walleij, Dan Williams, linuxppc-dev
In-Reply-To: <1285883207-13761-1-git-send-email-iws@ovro.caltech.edu>

Now that the DMAEngine API has support for scatterlist to scatterlist
copy, implement support for the STE DMA40 DMA controller.

Cc: Linus Walleij <linus.ml.walleij@gmail.com>
Cc: Per Fridén <per.friden@stericsson.com>
Signed-off-by: Ira W. Snyder <iws@ovro.caltech.edu>
---
 drivers/dma/ste_dma40.c |   17 +++++++++++++++++
 1 files changed, 17 insertions(+), 0 deletions(-)

diff --git a/drivers/dma/ste_dma40.c b/drivers/dma/ste_dma40.c
index 17e2600..d5fd098 100644
--- a/drivers/dma/ste_dma40.c
+++ b/drivers/dma/ste_dma40.c
@@ -1857,6 +1857,18 @@ err:
 	return NULL;
 }
 
+static struct dma_async_tx_descriptor *
+d40_prep_sg(struct dma_chan *chan,
+	    struct scatterlist *dst_sg, unsigned int dst_nents,
+	    struct scatterlist *src_sg, unsigned int src_nents,
+	    unsigned long dma_flags)
+{
+	if (dst_nents != src_nents)
+		return NULL;
+
+	return stedma40_memcpy_sg(chan, dst_sg, src_sg, dst_nents, dma_flags);
+}
+
 static int d40_prep_slave_sg_log(struct d40_desc *d40d,
 				 struct d40_chan *d40c,
 				 struct scatterlist *sgl,
@@ -2281,6 +2293,7 @@ static int __init d40_dmaengine_init(struct d40_base *base,
 	base->dma_slave.device_alloc_chan_resources = d40_alloc_chan_resources;
 	base->dma_slave.device_free_chan_resources = d40_free_chan_resources;
 	base->dma_slave.device_prep_dma_memcpy = d40_prep_memcpy;
+	base->dma_slave.device_prep_dma_sg = d40_prep_sg;
 	base->dma_slave.device_prep_slave_sg = d40_prep_slave_sg;
 	base->dma_slave.device_tx_status = d40_tx_status;
 	base->dma_slave.device_issue_pending = d40_issue_pending;
@@ -2301,10 +2314,12 @@ static int __init d40_dmaengine_init(struct d40_base *base,
 
 	dma_cap_zero(base->dma_memcpy.cap_mask);
 	dma_cap_set(DMA_MEMCPY, base->dma_memcpy.cap_mask);
+	dma_cap_set(DMA_SG, base->dma_slave.cap_mask);
 
 	base->dma_memcpy.device_alloc_chan_resources = d40_alloc_chan_resources;
 	base->dma_memcpy.device_free_chan_resources = d40_free_chan_resources;
 	base->dma_memcpy.device_prep_dma_memcpy = d40_prep_memcpy;
+	base->dma_slave.device_prep_dma_sg = d40_prep_sg;
 	base->dma_memcpy.device_prep_slave_sg = d40_prep_slave_sg;
 	base->dma_memcpy.device_tx_status = d40_tx_status;
 	base->dma_memcpy.device_issue_pending = d40_issue_pending;
@@ -2331,10 +2346,12 @@ static int __init d40_dmaengine_init(struct d40_base *base,
 	dma_cap_zero(base->dma_both.cap_mask);
 	dma_cap_set(DMA_SLAVE, base->dma_both.cap_mask);
 	dma_cap_set(DMA_MEMCPY, base->dma_both.cap_mask);
+	dma_cap_set(DMA_SG, base->dma_slave.cap_mask);
 
 	base->dma_both.device_alloc_chan_resources = d40_alloc_chan_resources;
 	base->dma_both.device_free_chan_resources = d40_free_chan_resources;
 	base->dma_both.device_prep_dma_memcpy = d40_prep_memcpy;
+	base->dma_slave.device_prep_dma_sg = d40_prep_sg;
 	base->dma_both.device_prep_slave_sg = d40_prep_slave_sg;
 	base->dma_both.device_tx_status = d40_tx_status;
 	base->dma_both.device_issue_pending = d40_issue_pending;
-- 
1.7.1

^ permalink raw reply related

* [PATCH v4 3/4] fsldma: improved DMA_SLAVE support
From: Ira W. Snyder @ 2010-09-30 21:46 UTC (permalink / raw)
  To: linux-kernel; +Cc: Dan Williams, linuxppc-dev
In-Reply-To: <1285883207-13761-1-git-send-email-iws@ovro.caltech.edu>

Now that the generic DMAEngine API has support for scatterlist to
scatterlist copying, the device_prep_slave_sg() portion of the
DMA_SLAVE API is no longer necessary and has been removed.

However, the device_control() portion of the DMA_SLAVE API is still
useful to control device specific parameters, such as externally
controlled DMA transfers and maximum burst length.

A special dma_ctrl_cmd has been added to enable externally controlled
DMA transfers. This is currently specific to the Freescale DMA
controller, but can easily be made generic when another user is found.

Signed-off-by: Ira W. Snyder <iws@ovro.caltech.edu>
---
 arch/powerpc/include/asm/fsldma.h |  137 ----------------------
 drivers/dma/fsldma.c              |  226 +++++++-----------------------------
 include/linux/dmaengine.h         |    3 +
 3 files changed, 47 insertions(+), 319 deletions(-)
 delete mode 100644 arch/powerpc/include/asm/fsldma.h

diff --git a/arch/powerpc/include/asm/fsldma.h b/arch/powerpc/include/asm/fsldma.h
deleted file mode 100644
index debc5ed..0000000
--- a/arch/powerpc/include/asm/fsldma.h
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * Freescale MPC83XX / MPC85XX DMA Controller
- *
- * Copyright (c) 2009 Ira W. Snyder <iws@ovro.caltech.edu>
- *
- * This file is licensed under the terms of the GNU General Public License
- * version 2. This program is licensed "as is" without any warranty of any
- * kind, whether express or implied.
- */
-
-#ifndef __ARCH_POWERPC_ASM_FSLDMA_H__
-#define __ARCH_POWERPC_ASM_FSLDMA_H__
-
-#include <linux/slab.h>
-#include <linux/dmaengine.h>
-
-/*
- * Definitions for the Freescale DMA controller's DMA_SLAVE implemention
- *
- * The Freescale DMA_SLAVE implementation was designed to handle many-to-many
- * transfers. An example usage would be an accelerated copy between two
- * scatterlists. Another example use would be an accelerated copy from
- * multiple non-contiguous device buffers into a single scatterlist.
- *
- * A DMA_SLAVE transaction is defined by a struct fsl_dma_slave. This
- * structure contains a list of hardware addresses that should be copied
- * to/from the scatterlist passed into device_prep_slave_sg(). The structure
- * also has some fields to enable hardware-specific features.
- */
-
-/**
- * struct fsl_dma_hw_addr
- * @entry: linked list entry
- * @address: the hardware address
- * @length: length to transfer
- *
- * Holds a single physical hardware address / length pair for use
- * with the DMAEngine DMA_SLAVE API.
- */
-struct fsl_dma_hw_addr {
-	struct list_head entry;
-
-	dma_addr_t address;
-	size_t length;
-};
-
-/**
- * struct fsl_dma_slave
- * @addresses: a linked list of struct fsl_dma_hw_addr structures
- * @request_count: value for DMA request count
- * @src_loop_size: setup and enable constant source-address DMA transfers
- * @dst_loop_size: setup and enable constant destination address DMA transfers
- * @external_start: enable externally started DMA transfers
- * @external_pause: enable externally paused DMA transfers
- *
- * Holds a list of address / length pairs for use with the DMAEngine
- * DMA_SLAVE API implementation for the Freescale DMA controller.
- */
-struct fsl_dma_slave {
-
-	/* List of hardware address/length pairs */
-	struct list_head addresses;
-
-	/* Support for extra controller features */
-	unsigned int request_count;
-	unsigned int src_loop_size;
-	unsigned int dst_loop_size;
-	bool external_start;
-	bool external_pause;
-};
-
-/**
- * fsl_dma_slave_append - add an address/length pair to a struct fsl_dma_slave
- * @slave: the &struct fsl_dma_slave to add to
- * @address: the hardware address to add
- * @length: the length of bytes to transfer from @address
- *
- * Add a hardware address/length pair to a struct fsl_dma_slave. Returns 0 on
- * success, -ERRNO otherwise.
- */
-static inline int fsl_dma_slave_append(struct fsl_dma_slave *slave,
-				       dma_addr_t address, size_t length)
-{
-	struct fsl_dma_hw_addr *addr;
-
-	addr = kzalloc(sizeof(*addr), GFP_ATOMIC);
-	if (!addr)
-		return -ENOMEM;
-
-	INIT_LIST_HEAD(&addr->entry);
-	addr->address = address;
-	addr->length = length;
-
-	list_add_tail(&addr->entry, &slave->addresses);
-	return 0;
-}
-
-/**
- * fsl_dma_slave_free - free a struct fsl_dma_slave
- * @slave: the struct fsl_dma_slave to free
- *
- * Free a struct fsl_dma_slave and all associated address/length pairs
- */
-static inline void fsl_dma_slave_free(struct fsl_dma_slave *slave)
-{
-	struct fsl_dma_hw_addr *addr, *tmp;
-
-	if (slave) {
-		list_for_each_entry_safe(addr, tmp, &slave->addresses, entry) {
-			list_del(&addr->entry);
-			kfree(addr);
-		}
-
-		kfree(slave);
-	}
-}
-
-/**
- * fsl_dma_slave_alloc - allocate a struct fsl_dma_slave
- * @gfp: the flags to pass to kmalloc when allocating this structure
- *
- * Allocate a struct fsl_dma_slave for use by the DMA_SLAVE API. Returns a new
- * struct fsl_dma_slave on success, or NULL on failure.
- */
-static inline struct fsl_dma_slave *fsl_dma_slave_alloc(gfp_t gfp)
-{
-	struct fsl_dma_slave *slave;
-
-	slave = kzalloc(sizeof(*slave), gfp);
-	if (!slave)
-		return NULL;
-
-	INIT_LIST_HEAD(&slave->addresses);
-	return slave;
-}
-
-#endif /* __ARCH_POWERPC_ASM_FSLDMA_H__ */
diff --git a/drivers/dma/fsldma.c b/drivers/dma/fsldma.c
index 1ed29d1..286c3ac 100644
--- a/drivers/dma/fsldma.c
+++ b/drivers/dma/fsldma.c
@@ -35,7 +35,6 @@
 #include <linux/dmapool.h>
 #include <linux/of_platform.h>
 
-#include <asm/fsldma.h>
 #include "fsldma.h"
 
 static const char msg_ld_oom[] = "No free memory for link descriptor\n";
@@ -719,207 +718,70 @@ static struct dma_async_tx_descriptor *fsl_dma_prep_slave_sg(
 	struct dma_chan *dchan, struct scatterlist *sgl, unsigned int sg_len,
 	enum dma_data_direction direction, unsigned long flags)
 {
-	struct fsldma_chan *chan;
-	struct fsl_desc_sw *first = NULL, *prev = NULL, *new = NULL;
-	struct fsl_dma_slave *slave;
-	size_t copy;
-
-	int i;
-	struct scatterlist *sg;
-	size_t sg_used;
-	size_t hw_used;
-	struct fsl_dma_hw_addr *hw;
-	dma_addr_t dma_dst, dma_src;
-
-	if (!dchan)
-		return NULL;
-
-	if (!dchan->private)
-		return NULL;
-
-	chan = to_fsl_chan(dchan);
-	slave = dchan->private;
-
-	if (list_empty(&slave->addresses))
-		return NULL;
-
-	hw = list_first_entry(&slave->addresses, struct fsl_dma_hw_addr, entry);
-	hw_used = 0;
-
 	/*
-	 * Build the hardware transaction to copy from the scatterlist to
-	 * the hardware, or from the hardware to the scatterlist
+	 * This operation is not supported on the Freescale DMA controller
 	 *
-	 * If you are copying from the hardware to the scatterlist and it
-	 * takes two hardware entries to fill an entire page, then both
-	 * hardware entries will be coalesced into the same page
-	 *
-	 * If you are copying from the scatterlist to the hardware and a
-	 * single page can fill two hardware entries, then the data will
-	 * be read out of the page into the first hardware entry, and so on
+	 * However, we need to provide the function pointer to allow the
+	 * device_control() method to work.
 	 */
-	for_each_sg(sgl, sg, sg_len, i) {
-		sg_used = 0;
-
-		/* Loop until the entire scatterlist entry is used */
-		while (sg_used < sg_dma_len(sg)) {
-
-			/*
-			 * If we've used up the current hardware address/length
-			 * pair, we need to load a new one
-			 *
-			 * This is done in a while loop so that descriptors with
-			 * length == 0 will be skipped
-			 */
-			while (hw_used >= hw->length) {
-
-				/*
-				 * If the current hardware entry is the last
-				 * entry in the list, we're finished
-				 */
-				if (list_is_last(&hw->entry, &slave->addresses))
-					goto finished;
-
-				/* Get the next hardware address/length pair */
-				hw = list_entry(hw->entry.next,
-						struct fsl_dma_hw_addr, entry);
-				hw_used = 0;
-			}
-
-			/* Allocate the link descriptor from DMA pool */
-			new = fsl_dma_alloc_descriptor(chan);
-			if (!new) {
-				dev_err(chan->dev, "No free memory for "
-						       "link descriptor\n");
-				goto fail;
-			}
-#ifdef FSL_DMA_LD_DEBUG
-			dev_dbg(chan->dev, "new link desc alloc %p\n", new);
-#endif
-
-			/*
-			 * Calculate the maximum number of bytes to transfer,
-			 * making sure it is less than the DMA controller limit
-			 */
-			copy = min_t(size_t, sg_dma_len(sg) - sg_used,
-					     hw->length - hw_used);
-			copy = min_t(size_t, copy, FSL_DMA_BCR_MAX_CNT);
-
-			/*
-			 * DMA_FROM_DEVICE
-			 * from the hardware to the scatterlist
-			 *
-			 * DMA_TO_DEVICE
-			 * from the scatterlist to the hardware
-			 */
-			if (direction == DMA_FROM_DEVICE) {
-				dma_src = hw->address + hw_used;
-				dma_dst = sg_dma_address(sg) + sg_used;
-			} else {
-				dma_src = sg_dma_address(sg) + sg_used;
-				dma_dst = hw->address + hw_used;
-			}
-
-			/* Fill in the descriptor */
-			set_desc_cnt(chan, &new->hw, copy);
-			set_desc_src(chan, &new->hw, dma_src);
-			set_desc_dst(chan, &new->hw, dma_dst);
-
-			/*
-			 * If this is not the first descriptor, chain the
-			 * current descriptor after the previous descriptor
-			 */
-			if (!first) {
-				first = new;
-			} else {
-				set_desc_next(chan, &prev->hw,
-					      new->async_tx.phys);
-			}
-
-			new->async_tx.cookie = 0;
-			async_tx_ack(&new->async_tx);
-
-			prev = new;
-			sg_used += copy;
-			hw_used += copy;
-
-			/* Insert the link descriptor into the LD ring */
-			list_add_tail(&new->node, &first->tx_list);
-		}
-	}
-
-finished:
-
-	/* All of the hardware address/length pairs had length == 0 */
-	if (!first || !new)
-		return NULL;
-
-	new->async_tx.flags = flags;
-	new->async_tx.cookie = -EBUSY;
-
-	/* Set End-of-link to the last link descriptor of new list */
-	set_ld_eol(chan, new);
-
-	/* Enable extra controller features */
-	if (chan->set_src_loop_size)
-		chan->set_src_loop_size(chan, slave->src_loop_size);
-
-	if (chan->set_dst_loop_size)
-		chan->set_dst_loop_size(chan, slave->dst_loop_size);
-
-	if (chan->toggle_ext_start)
-		chan->toggle_ext_start(chan, slave->external_start);
-
-	if (chan->toggle_ext_pause)
-		chan->toggle_ext_pause(chan, slave->external_pause);
-
-	if (chan->set_request_count)
-		chan->set_request_count(chan, slave->request_count);
-
-	return &first->async_tx;
-
-fail:
-	/* If first was not set, then we failed to allocate the very first
-	 * descriptor, and we're done */
-	if (!first)
-		return NULL;
-
-	/*
-	 * First is set, so all of the descriptors we allocated have been added
-	 * to first->tx_list, INCLUDING "first" itself. Therefore we
-	 * must traverse the list backwards freeing each descriptor in turn
-	 *
-	 * We're re-using variables for the loop, oh well
-	 */
-	fsldma_free_desc_list_reverse(chan, &first->tx_list);
 	return NULL;
 }
 
 static int fsl_dma_device_control(struct dma_chan *dchan,
 				  enum dma_ctrl_cmd cmd, unsigned long arg)
 {
+	struct dma_slave_config *config;
 	struct fsldma_chan *chan;
 	unsigned long flags;
-
-	/* Only supports DMA_TERMINATE_ALL */
-	if (cmd != DMA_TERMINATE_ALL)
-		return -ENXIO;
+	int size;
 
 	if (!dchan)
 		return -EINVAL;
 
 	chan = to_fsl_chan(dchan);
 
-	/* Halt the DMA engine */
-	dma_halt(chan);
+	switch (cmd) {
+	case DMA_TERMINATE_ALL:
+		/* Halt the DMA engine */
+		dma_halt(chan);
 
-	spin_lock_irqsave(&chan->desc_lock, flags);
+		spin_lock_irqsave(&chan->desc_lock, flags);
 
-	/* Remove and free all of the descriptors in the LD queue */
-	fsldma_free_desc_list(chan, &chan->ld_pending);
-	fsldma_free_desc_list(chan, &chan->ld_running);
+		/* Remove and free all of the descriptors in the LD queue */
+		fsldma_free_desc_list(chan, &chan->ld_pending);
+		fsldma_free_desc_list(chan, &chan->ld_running);
 
-	spin_unlock_irqrestore(&chan->desc_lock, flags);
+		spin_unlock_irqrestore(&chan->desc_lock, flags);
+		return 0;
+
+	case DMA_SLAVE_CONFIG:
+		config = (struct dma_slave_config *)arg;
+
+		/* make sure the channel supports setting burst size */
+		if (!chan->set_request_count)
+			return -ENXIO;
+
+		/* we set the controller burst size depending on direction */
+		if (config->direction == DMA_TO_DEVICE)
+			size = config->dst_addr_width * config->dst_maxburst;
+		else
+			size = config->src_addr_width * config->src_maxburst;
+
+		chan->set_request_count(chan, size);
+		return 0;
+
+	case FSLDMA_EXTERNAL_START:
+
+		/* make sure the channel supports external start */
+		if (!chan->toggle_ext_start)
+			return -ENXIO;
+
+		chan->toggle_ext_start(chan, arg);
+		return 0;
+
+	default:
+		return -ENXIO;
+	}
 
 	return 0;
 }
diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h
index 7c44620..f92c139 100644
--- a/include/linux/dmaengine.h
+++ b/include/linux/dmaengine.h
@@ -120,12 +120,15 @@ enum dma_ctrl_flags {
  * configuration data in statically from the platform). An additional
  * argument of struct dma_slave_config must be passed in with this
  * command.
+ * @FSLDMA_EXTERNAL_START: this command will put the Freescale DMA controller
+ * into external start mode.
  */
 enum dma_ctrl_cmd {
 	DMA_TERMINATE_ALL,
 	DMA_PAUSE,
 	DMA_RESUME,
 	DMA_SLAVE_CONFIG,
+	FSLDMA_EXTERNAL_START,
 };
 
 /**
-- 
1.7.1

^ permalink raw reply related

* Re: [PATCH] PPC4xx: ADMA separating SoC specific functions
From: Dan Williams @ 2010-09-30 22:52 UTC (permalink / raw)
  To: Wolfgang Denk; +Cc: tmarri, linux-raid, linuxppc-dev, linux-crypto, yur
In-Reply-To: <20100930190814.52268D2B48C@gemini.denx.de>

On Thu, Sep 30, 2010 at 12:08 PM, Wolfgang Denk <wd@denx.de> wrote:
[snip other valid review comments]
>
> This is a header file, yet you add here literally thousands of lines of
> code.

Yes, these functions are entirely too large to be inlined.  It looks
like you are trying to borrow too heavily from the iop-adma model.
The differences between iop13xx and iop33x from a adma perspective are
just in descriptor format and channel capabilities.  If you look at
the routines implemented in:
arch/arm/include/asm/hardware/iop3xx-adma.h
arch/arm/mach-iop13xx/include/mach/adma.h
...they are just simple helpers that abstract the descriptor details.
For example:

iop_adma_prep_dma_xor()
{
[snip]
        spin_lock_bh(&iop_chan->lock);
        slot_cnt =3D iop_chan_xor_slot_count(len, src_cnt, &slots_per_op);
        sw_desc =3D iop_adma_alloc_slots(iop_chan, slot_cnt, slots_per_op);
        if (sw_desc) {
                grp_start =3D sw_desc->group_head;
                iop_desc_init_xor(grp_start, src_cnt, flags);
                iop_desc_set_byte_count(grp_start, iop_chan, len);
                iop_desc_set_dest_addr(grp_start, iop_chan, dma_dest);
                sw_desc->unmap_src_cnt =3D src_cnt;
                sw_desc->unmap_len =3D len;
                sw_desc->async_tx.flags =3D flags;
                while (src_cnt--)
                        iop_desc_set_xor_src_addr(grp_start, src_cnt,
                                                  dma_src[src_cnt]);
        }
        spin_unlock_bh(&iop_chan->lock);

        return sw_desc ? &sw_desc->async_tx : NULL;
}

Where  iop_adma_alloc_slots() is implemented differently between
iop13xx and iop3xx.  In this case why does ppc440spe-adma.h exist?  If
it has code specific to ppe440spe it should just live in the ppe440spe
C file.  If it is truly generic it should move to the base adma.c
implementation.  If you want to reuse a ppe440spe routine just link to
it.

> Selecting the architecture at build time is bad as it prevents using a
> sinlge kernel image across a wide range of boards. =A0You only replied
> "We select the architecture at build time." without any explanation if
> there is a pressing technical reason to do it this way, or if this was
> just a arbitrary decision.

I agree I have yet to see any indication that this driver needs to
have an architecture selected at build time.

A potential compromise a first step would be to have a common C file
that is shared between two driver modules until such point that they
can be unified into a common module.

--
Dan

^ permalink raw reply

* RE: [PATCH] PPC4xx: ADMA separating SoC specific functions
From: Tirumala Marri @ 2010-10-01  0:03 UTC (permalink / raw)
  To: Wolfgang Denk; +Cc: linux-raid, dan.j.williams, linuxppc-dev, linux-crypto, yur
In-Reply-To: <20100930190814.52268D2B48C@gemini.denx.de>

>
> When reposting a patch, please always indicate that this is new
> version by using something like "[PATCH v2]" in the Subject line.
[Marri] I know, but this patch is not modification of previous patch.
It is complete brand new from scratch again. In that case isn't this
 will be first version ?


> > ---
>
> Also, please include here (i. e. below the "---" line, i. e. in the
> comments section, a description of what was changed compared to the
> previous version of this patch.
>
> As is, you enforce us to rescan the whole patch again and check
> manually if you have reacted to any of the comments sent before, and
> how.  As is, you make reviewing your poatches harder than necessary.

[Marri} I will include comments in the further versions of this patch.

>
>
> > diff --git a/drivers/dma/ppc4xx/adma.c b/drivers/dma/ppc4xx/adma.c
> > index 0d58a4a..a1053cb 100644
> > --- a/drivers/dma/ppc4xx/adma.c
> > +++ b/drivers/dma/ppc4xx/adma.c
> ...
> > +#include "ppc440spe-adma.h"
> > +
> > +struct dma_async_tx_descriptor
> > +*ppc440spe_adma_prep_dma_pq(struct dma_chan *chan,
> > +			       dma_addr_t * dst,
> > +			       dma_addr_t * src,
> > +			       unsigned int src_cnt,
> > +			       const unsigned char *scf,
> > +			       size_t len,
> > +			       unsigned long flags);
> > +struct dma_async_tx_descriptor
> > +*ppc440spe_adma_prep_dma_pqzero_sum(struct dma_chan *chan,
>
> Should such 440SPe specific code not be removed here and placed into
> ppc440spe-adma.c instead?
[Marri] It is 440SPe specific. Definition is moved ppc440spe-adma.c


>
> > +#if 0
> >  static void prep_dma_pq_dbg(int id, dma_addr_t *dst, dma_addr_t
> *src,
> >  			    unsigned int src_cnt)
> >  {
> > @@ -213,8 +104,9 @@ static void prep_dma_pq_dbg(int id, dma_addr_t
> *dst, dma_addr_t *src,
> >  	for (i = 0; i < 2; i++)
> >  		pr_debug("\t0x%016llx ", dst[i]);
> >  }
> > +#endif
>
> Please do not add dead code - remove the whole "#if 0" block.
[Marri] Will remove it.


> *******/
>
> It seems youremove all code, but leave the (now empty) comment
> headers? This makes little sense to me.
>
[Marri] Will clean up in the next version.
> ...
> >  /**
> >   * ppc440spe_adma_free_slots - flags descriptor slots for reuse
> >   * @slot: Slot to free
> >   * Caller must hold &ppc440spe_chan->lock while calling this
> function
> >   */
>
> Again, all this is pretty low-level 440SPe specific code. Why do you
> keep this in the common drive rfile instead of moving it into the new
> 440SPe specific file?
[Marri]. With name change it can be used With any SoC ADMA driver.


>
>
> > diff --git a/drivers/dma/ppc4xx/ppc440spe-adma.c
> b/drivers/dma/ppc4xx/ppc440spe-adma.c
> > new file mode 100644
> > index 0000000..da467b4
> ...
> > +	/*  In the current implementation of ppc440spe ADMA driver it
> > +
> > +
> > +
> > +	 * makes sense to pick out only pq case, because it may be
>
> Formatting problems?
[Marri] Will fix in next version.

>
>
> > diff --git a/drivers/dma/ppc4xx/ppc440spe-adma.h
> b/drivers/dma/ppc4xx/ppc440spe-adma.h
> > new file mode 100644
> > index 0000000..81a1f46
> > --- /dev/null
> > +++ b/drivers/dma/ppc4xx/ppc440spe-adma.h
> ...
> > +/*
> > + * ppc440spe_get_group_entry - get group entry with index idx
> > + * @tdesc: is the last allocated slot in the group.
> > + */
> > +static struct ppc440spe_adma_desc_slot
> *ppc440spe_get_group_entry(struct
> > +
ppc440spe_adma_desc_slot
> > +							    *tdesc,
> > +							    u32 entry_idx)
> > +{
> > +	struct ppc440spe_adma_desc_slot *iter = tdesc->group_head;
> > +	int i = 0;
> > +
> > +	if (entry_idx < 0 || entry_idx >= (tdesc->src_cnt + tdesc-
> >dst_cnt)) {
> > +		printk("%s: entry_idx %d, src_cnt %d, dst_cnt %d\n",
> > +		       __func__, entry_idx, tdesc->src_cnt, tdesc-
> >dst_cnt);
> > +		BUG();
> > +	}
> > +
> > +	list_for_each_entry(iter, &tdesc->group_list, chain_node) {
> > +		if (i++ == entry_idx)
> > +			break;
> > +	}
> > +	return iter;
> > +}
>
> This is a header file, yet you add here literally thousands of lines of
> code.
>
>
> Note that more or less similar questions have been asked for the
> previous version of this patch, but I fail to find any good
> justification in your replies.
[Marri] Reason for some functions in lined is 1) They are small enough
To be in lined 2) If keep them in ppc440spe-adma.c I will have to make
them
Non static to avoid "Used but not defined warnings". With too many
functions
Non static might cause name space pollution in the kernel ?

>
>
> Selecting the architecture at build time is bad as it prevents using a
> sinlge kernel image across a wide range of boards.  You only replied
> "We select the architecture at build time." without any explanation if
> there is a pressing technical reason to do it this way, or if this was
> just a arbitrary decision.
[Marri] Build time separation is only for entirely different SoC DMA
engine.
For example 440spe and 460sx has engierely different DMA architecture.
Whereas 440spe and 460ex can be determined at run time.I am planning to do
the run time selection for similar SoCs. I discussed This with Yuri as
well.

>
> The same goes for putting so much source code in a header file - I
> really see no technical need for this (especially not if you build for
> a single architecture only).
[Marri] I explained this above.

>
> Also I wonder why you still keep so many 440SPe specific code in the
> common file, even though you just create new 440SPe specific header
> and source files.
>
>
> Please elucidate.
>
>
[Marri] With Dan's suggestion first I am working on separating SoC
specific functions
To avoid too many  changes. Next step is to re-name the functions in the
Adma.c to common name line ppc4xx_xxx().

Regards,
Marri

^ permalink raw reply

* RE: [PATCH] PPC4xx: ADMA separating SoC specific functions
From: Tirumala Marri @ 2010-10-01  0:16 UTC (permalink / raw)
  To: Dan Williams, Wolfgang Denk; +Cc: linux-raid, linuxppc-dev, linux-crypto, yur
In-Reply-To: <AANLkTi=CJOsayM1YnwE3a22D2S35aM+BTV6_YfXjcjiO@mail.gmail.com>

> On Thu, Sep 30, 2010 at 12:08 PM, Wolfgang Denk <wd@denx.de> wrote:
> [snip other valid review comments]
> >
> > This is a header file, yet you add here literally thousands of lines
> of
> > code.
>
> Yes, these functions are entirely too large to be inlined.  It looks
> like you are trying to borrow too heavily from the iop-adma model.
> The differences between iop13xx and iop33x from a adma perspective are
> just in descriptor format and channel capabilities.  If you look at
> the routines implemented in:
> arch/arm/include/asm/hardware/iop3xx-adma.h
> arch/arm/mach-iop13xx/include/mach/adma.h
> ...they are just simple helpers that abstract the descriptor details.
> For example:
>
> iop_adma_prep_dma_xor()
> {
> [snip]
>         spin_lock_bh(&iop_chan->lock);
>         slot_cnt =3D iop_chan_xor_slot_count(len, src_cnt,
> &slots_per_op);
>         sw_desc =3D iop_adma_alloc_slots(iop_chan, slot_cnt,
> slots_per_op);
>         if (sw_desc) {
>                 grp_start =3D sw_desc->group_head;
>                 iop_desc_init_xor(grp_start, src_cnt, flags);
>                 iop_desc_set_byte_count(grp_start, iop_chan, len);
>                 iop_desc_set_dest_addr(grp_start, iop_chan, dma_dest);
>                 sw_desc->unmap_src_cnt =3D src_cnt;
>                 sw_desc->unmap_len =3D len;
>                 sw_desc->async_tx.flags =3D flags;
>                 while (src_cnt--)
>                         iop_desc_set_xor_src_addr(grp_start, src_cnt,
>                                                   dma_src[src_cnt]);
>         }
>         spin_unlock_bh(&iop_chan->lock);
>
>         return sw_desc ? &sw_desc->async_tx : NULL;
> }
>
> Where  iop_adma_alloc_slots() is implemented differently between
> iop13xx and iop3xx.  In this case why does ppc440spe-adma.h exist?  If
> it has code specific to ppe440spe it should just live in the ppe440spe
> C file.  If it is truly generic it should move to the base adma.c
> implementation.  If you want to reuse a ppe440spe routine just link to
> it.
[Marri]That is how I started changing the code. And I see tons of warnings
Saying "Used but not defined" or "Defined but not used". How should I
suppress
Some functions from adma.c are used in ppc440spe-adma.c and some from
ppc440spe-adma.c
Are used in adma.c. So I created intermediate file ppc440spe-adma.h with
inlined
Functions. In future this will be converted into ppc4xx_adma.h and move
existing
SoC specific stuff to ppc440spe-adma.c file.

>
> > Selecting the architecture at build time is bad as it prevents using
> a
> > sinlge kernel image across a wide range of boards. =A0You only replied
> > "We select the architecture at build time." without any explanation
> if
> > there is a pressing technical reason to do it this way, or if this
> was
> > just a arbitrary decision.
>
> I agree I have yet to see any indication that this driver needs to
> have an architecture selected at build time.
>
> A potential compromise a first step would be to have a common C file
> that is shared between two driver modules until such point that they
> can be unified into a common module.

As I responded to Mr. Wolfgang in previous email, similar SoC DMA engines
will
Be resolved at run time. Whereas completely different architectures will
be
Resolved at build time.

Regards,
Marri

^ permalink raw reply

* Re: [PATCH] PPC4xx: ADMA separating SoC specific functions
From: Dan Williams @ 2010-10-01  0:57 UTC (permalink / raw)
  To: Tirumala Marri
  Cc: Wolfgang Denk, Greg KH, yur, linux-raid, linux-crypto,
	linuxppc-dev
In-Reply-To: <dc9b5d064d80ac2af2ccf932e94b2bb9@mail.gmail.com>

[ adding Greg ]

On Thu, Sep 30, 2010 at 5:16 PM, Tirumala Marri <tmarri@apm.com> wrote:
>> Where =A0iop_adma_alloc_slots() is implemented differently between
>> iop13xx and iop3xx. =A0In this case why does ppc440spe-adma.h exist? =A0=
If
>> it has code specific to ppe440spe it should just live in the ppe440spe
>> C file. =A0If it is truly generic it should move to the base adma.c
>> implementation. =A0If you want to reuse a ppe440spe routine just link to
>> it.
> [Marri]That is how I started changing the code. And I see tons of warning=
s
> Saying "Used but not defined" or "Defined but not used". How should I
> suppress
> Some functions from adma.c are used in ppc440spe-adma.c and some from
> ppc440spe-adma.c
> Are used in adma.c.

This is part of defining a common interface.  Maybe look at the
linkages of how the common ioat_probe() routine is used to support all
three versions of its dma hardware.

> So I created intermediate file ppc440spe-adma.h with
> inlined
> Functions. In future this will be converted into ppc4xx_adma.h and move
> existing
> SoC specific stuff to ppc440spe-adma.c file.

You definitely need to be able to resolve "used but not defined" and
"defined but not used" warnings before tackling a driver conversion
like this.  In light of this comment I wonder if it would be
appropriate to submit your original driver, that just duplicated
routines from the ppc440spe driver, to the -staging tree.  Then it
would be available for someone familiar with driver conversions to
take a shot at unifying.

Greg, is this an appropriate use of -staging?

--
Dan

^ permalink raw reply

* [PATCH 09/18] powerpc: Support device tree regardless of CPU endianness
From: Ian Munsie @ 2010-10-01  7:06 UTC (permalink / raw)
  To: linux-kernel, linuxppc-dev, benh
  Cc: Michal Simek, devicetree-discuss, paulus, Ian Munsie, Jeremy Kerr
In-Reply-To: <1285916771-18033-1-git-send-email-imunsie@au1.ibm.com>

From: Ian Munsie <imunsie@au1.ibm.com>

On PowerPC the device tree is always big endian, but the CPU could be
either, so add be32_to_cpu where appropriate and change the types of
device tree data to __be32 etc to allow sparse to locate endian issues.

Signed-off-by: Ian Munsie <imunsie@au1.ibm.com>
---
 arch/powerpc/kernel/prom.c |   60 ++++++++++++++++++++++----------------------
 1 files changed, 30 insertions(+), 30 deletions(-)

diff --git a/arch/powerpc/kernel/prom.c b/arch/powerpc/kernel/prom.c
index fed9bf6..9b9ebb2 100644
--- a/arch/powerpc/kernel/prom.c
+++ b/arch/powerpc/kernel/prom.c
@@ -188,16 +188,16 @@ static void __init check_cpu_pa_features(unsigned long node)
 #ifdef CONFIG_PPC_STD_MMU_64
 static void __init check_cpu_slb_size(unsigned long node)
 {
-	u32 *slb_size_ptr;
+	__be32 *slb_size_ptr;
 
 	slb_size_ptr = of_get_flat_dt_prop(node, "slb-size", NULL);
 	if (slb_size_ptr != NULL) {
-		mmu_slb_size = *slb_size_ptr;
+		mmu_slb_size = be32_to_cpup(slb_size_ptr);
 		return;
 	}
 	slb_size_ptr = of_get_flat_dt_prop(node, "ibm,slb-size", NULL);
 	if (slb_size_ptr != NULL) {
-		mmu_slb_size = *slb_size_ptr;
+		mmu_slb_size = be32_to_cpup(slb_size_ptr);
 	}
 }
 #else
@@ -252,11 +252,11 @@ static void __init check_cpu_feature_properties(unsigned long node)
 {
 	unsigned long i;
 	struct feature_property *fp = feature_properties;
-	const u32 *prop;
+	const __be32 *prop;
 
 	for (i = 0; i < ARRAY_SIZE(feature_properties); ++i, ++fp) {
 		prop = of_get_flat_dt_prop(node, fp->name, NULL);
-		if (prop && *prop >= fp->min_value) {
+		if (prop && be32_to_cpup(prop) >= fp->min_value) {
 			cur_cpu_spec->cpu_features |= fp->cpu_feature;
 			cur_cpu_spec->cpu_user_features |= fp->cpu_user_ftr;
 		}
@@ -269,8 +269,8 @@ static int __init early_init_dt_scan_cpus(unsigned long node,
 {
 	static int logical_cpuid = 0;
 	char *type = of_get_flat_dt_prop(node, "device_type", NULL);
-	const u32 *prop;
-	const u32 *intserv;
+	const __be32 *prop;
+	const __be32 *intserv;
 	int i, nthreads;
 	unsigned long len;
 	int found = 0;
@@ -297,9 +297,9 @@ static int __init early_init_dt_scan_cpus(unsigned long node,
 		 * version 2 of the kexec param format adds the phys cpuid of
 		 * booted proc.
 		 */
-		if (initial_boot_params && initial_boot_params->version >= 2) {
-			if (intserv[i] ==
-					initial_boot_params->boot_cpuid_phys) {
+		if (initial_boot_params && be32_to_cpu(initial_boot_params->version) >= 2) {
+			if (be32_to_cpu(intserv[i]) ==
+					be32_to_cpu(initial_boot_params->boot_cpuid_phys)) {
 				found = 1;
 				break;
 			}
@@ -324,9 +324,9 @@ static int __init early_init_dt_scan_cpus(unsigned long node,
 
 	if (found) {
 		DBG("boot cpu: logical %d physical %d\n", logical_cpuid,
-			intserv[i]);
+			be32_to_cpu(intserv[i]));
 		boot_cpuid = logical_cpuid;
-		set_hard_smp_processor_id(boot_cpuid, intserv[i]);
+		set_hard_smp_processor_id(boot_cpuid, be32_to_cpu(intserv[i]));
 
 		/*
 		 * PAPR defines "logical" PVR values for cpus that
@@ -343,8 +343,8 @@ static int __init early_init_dt_scan_cpus(unsigned long node,
 		 * it uses 0x0f000001.
 		 */
 		prop = of_get_flat_dt_prop(node, "cpu-version", NULL);
-		if (prop && (*prop & 0xff000000) == 0x0f000000)
-			identify_cpu(0, *prop);
+		if (prop && (be32_to_cpup(prop) & 0xff000000) == 0x0f000000)
+			identify_cpu(0, be32_to_cpup(prop));
 
 		identical_pvr_fixup(node);
 	}
@@ -365,7 +365,7 @@ static int __init early_init_dt_scan_cpus(unsigned long node,
 
 void __init early_init_dt_scan_chosen_arch(unsigned long node)
 {
-	unsigned long *lprop;
+	unsigned long *lprop; /* All these set by kernel, so no need to convert endian */
 
 #ifdef CONFIG_PPC64
 	/* check if iommu is forced on or off */
@@ -524,16 +524,16 @@ void __init early_init_dt_setup_initrd_arch(unsigned long start,
 static void __init early_reserve_mem(void)
 {
 	u64 base, size;
-	u64 *reserve_map;
+	__be64 *reserve_map;
 	unsigned long self_base;
 	unsigned long self_size;
 
-	reserve_map = (u64 *)(((unsigned long)initial_boot_params) +
-					initial_boot_params->off_mem_rsvmap);
+	reserve_map = (__be64 *)(((unsigned long)initial_boot_params) +
+			be32_to_cpu(initial_boot_params->off_mem_rsvmap));
 
 	/* before we do anything, lets reserve the dt blob */
 	self_base = __pa((unsigned long)initial_boot_params);
-	self_size = initial_boot_params->totalsize;
+	self_size = be32_to_cpu(initial_boot_params->totalsize);
 	memblock_reserve(self_base, self_size);
 
 #ifdef CONFIG_BLK_DEV_INITRD
@@ -547,13 +547,13 @@ static void __init early_reserve_mem(void)
 	 * Handle the case where we might be booting from an old kexec
 	 * image that setup the mem_rsvmap as pairs of 32-bit values
 	 */
-	if (*reserve_map > 0xffffffffull) {
+	if (be64_to_cpup(reserve_map) > 0xffffffffull) {
 		u32 base_32, size_32;
-		u32 *reserve_map_32 = (u32 *)reserve_map;
+		__be32 *reserve_map_32 = (__be32 *)reserve_map;
 
 		while (1) {
-			base_32 = *(reserve_map_32++);
-			size_32 = *(reserve_map_32++);
+			base_32 = be32_to_cpup(reserve_map_32++);
+			size_32 = be32_to_cpup(reserve_map_32++);
 			if (size_32 == 0)
 				break;
 			/* skip if the reservation is for the blob */
@@ -566,8 +566,8 @@ static void __init early_reserve_mem(void)
 	}
 #endif
 	while (1) {
-		base = *(reserve_map++);
-		size = *(reserve_map++);
+		base = be64_to_cpup(reserve_map++);
+		size = be64_to_cpup(reserve_map++);
 		if (size == 0)
 			break;
 		DBG("reserving: %llx -> %llx\n", base, size);
@@ -860,7 +860,7 @@ struct device_node *of_get_cpu_node(int cpu, unsigned int *thread)
 	hardid = get_hard_smp_processor_id(cpu);
 
 	for_each_node_by_type(np, "cpu") {
-		const u32 *intserv;
+		const __be32 *intserv;
 		unsigned int plen, t;
 
 		/* Check for ibm,ppc-interrupt-server#s. If it doesn't exist
@@ -869,10 +869,10 @@ struct device_node *of_get_cpu_node(int cpu, unsigned int *thread)
 		intserv = of_get_property(np, "ibm,ppc-interrupt-server#s",
 				&plen);
 		if (intserv == NULL) {
-			const u32 *reg = of_get_property(np, "reg", NULL);
+			const __be32 *reg = of_get_property(np, "reg", NULL);
 			if (reg == NULL)
 				continue;
-			if (*reg == hardid) {
+			if (be32_to_cpup(reg) == hardid) {
 				if (thread)
 					*thread = 0;
 				return np;
@@ -880,7 +880,7 @@ struct device_node *of_get_cpu_node(int cpu, unsigned int *thread)
 		} else {
 			plen /= sizeof(u32);
 			for (t = 0; t < plen; t++) {
-				if (hardid == intserv[t]) {
+				if (hardid == be32_to_cpu(intserv[t])) {
 					if (thread)
 						*thread = t;
 					return np;
@@ -900,7 +900,7 @@ static int __init export_flat_device_tree(void)
 	struct dentry *d;
 
 	flat_dt_blob.data = initial_boot_params;
-	flat_dt_blob.size = initial_boot_params->totalsize;
+	flat_dt_blob.size = be32_to_cpu(initial_boot_params->totalsize);
 
 	d = debugfs_create_blob("flat-device-tree", S_IFREG | S_IRUSR,
 				powerpc_debugfs_root, &flat_dt_blob);
-- 
1.7.1

^ permalink raw reply related

* [PATCH 13/18] powerpc 44x: Make DCR endianness agnostic
From: Ian Munsie @ 2010-10-01  7:06 UTC (permalink / raw)
  To: linux-kernel, linuxppc-dev, benh
  Cc: Peter Zijlstra, devicetree-discuss, Ian Munsie, paulus,
	Thomas Gleixner, Stefan Roese, Anton Blanchard
In-Reply-To: <1285916771-18033-1-git-send-email-imunsie@au1.ibm.com>

From: Ian Munsie <imunsie@au1.ibm.com>

The Device Control Register accesses parse the device tree and therefore
need to handle the possible differences of endianness between the CPU
and device tree.

Signed-off-by: Ian Munsie <imunsie@au1.ibm.com>
---
 arch/powerpc/sysdev/dcr.c        |   18 +++++++++---------
 arch/powerpc/sysdev/ppc4xx_soc.c |   16 ++++++++--------
 arch/powerpc/sysdev/uic.c        |    6 +++---
 3 files changed, 20 insertions(+), 20 deletions(-)

diff --git a/arch/powerpc/sysdev/dcr.c b/arch/powerpc/sysdev/dcr.c
index bb44aa9..7f91e8a 100644
--- a/arch/powerpc/sysdev/dcr.c
+++ b/arch/powerpc/sysdev/dcr.c
@@ -27,7 +27,7 @@
 static struct device_node *find_dcr_parent(struct device_node *node)
 {
 	struct device_node *par, *tmp;
-	const u32 *p;
+	const __be32 *p;
 
 	for (par = of_node_get(node); par;) {
 		if (of_get_property(par, "dcr-controller", NULL))
@@ -37,7 +37,7 @@ static struct device_node *find_dcr_parent(struct device_node *node)
 		if (p == NULL)
 			par = of_get_parent(par);
 		else
-			par = of_find_node_by_phandle(*p);
+			par = of_find_node_by_phandle(be32_to_cpup(p));
 		of_node_put(tmp);
 	}
 	return par;
@@ -128,24 +128,24 @@ unsigned int dcr_resource_start(const struct device_node *np,
 				unsigned int index)
 {
 	unsigned int ds;
-	const u32 *dr = of_get_property(np, "dcr-reg", &ds);
+	const __be32 *dr = of_get_property(np, "dcr-reg", &ds);
 
 	if (dr == NULL || ds & 1 || index >= (ds / 8))
 		return 0;
 
-	return dr[index * 2];
+	return be32_to_cpu(dr[index * 2]);
 }
 EXPORT_SYMBOL_GPL(dcr_resource_start);
 
 unsigned int dcr_resource_len(const struct device_node *np, unsigned int index)
 {
 	unsigned int ds;
-	const u32 *dr = of_get_property(np, "dcr-reg", &ds);
+	const __be32 *dr = of_get_property(np, "dcr-reg", &ds);
 
 	if (dr == NULL || ds & 1 || index >= (ds / 8))
 		return 0;
 
-	return dr[index * 2 + 1];
+	return be32_to_cpu(dr[index * 2 + 1]);
 }
 EXPORT_SYMBOL_GPL(dcr_resource_len);
 
@@ -156,7 +156,7 @@ u64 of_translate_dcr_address(struct device_node *dev,
 			     unsigned int *out_stride)
 {
 	struct device_node *dp;
-	const u32 *p;
+	const __be32 *p;
 	unsigned int stride;
 	u64 ret = OF_BAD_ADDR;
 
@@ -166,7 +166,7 @@ u64 of_translate_dcr_address(struct device_node *dev,
 
 	/* Stride is not properly defined yet, default to 0x10 for Axon */
 	p = of_get_property(dp, "dcr-mmio-stride", NULL);
-	stride = (p == NULL) ? 0x10 : *p;
+	stride = (p == NULL) ? 0x10 : be32_to_cpup(p);
 
 	/* XXX FIXME: Which property name is to use of the 2 following ? */
 	p = of_get_property(dp, "dcr-mmio-range", NULL);
@@ -176,7 +176,7 @@ u64 of_translate_dcr_address(struct device_node *dev,
 		goto done;
 
 	/* Maybe could do some better range checking here */
-	ret = of_translate_address(dp, p);
+	ret = of_translate_address(dp, be32_to_cpup(p));
 	if (ret != OF_BAD_ADDR)
 		ret += (u64)(stride) * (u64)dcr_n;
 	if (out_stride)
diff --git a/arch/powerpc/sysdev/ppc4xx_soc.c b/arch/powerpc/sysdev/ppc4xx_soc.c
index d3d6ce3..e5a7554 100644
--- a/arch/powerpc/sysdev/ppc4xx_soc.c
+++ b/arch/powerpc/sysdev/ppc4xx_soc.c
@@ -76,10 +76,10 @@ static int __init ppc4xx_l2c_probe(void)
 	u32 r;
 	unsigned long flags;
 	int irq;
-	const u32 *dcrreg;
+	const __be32 *dcrreg;
 	u32 dcrbase_isram;
 	int len;
-	const u32 *prop;
+	const __be32 *prop;
 	u32 l2_size;
 
 	np = of_find_compatible_node(NULL, NULL, "ibm,l2-cache");
@@ -93,7 +93,7 @@ static int __init ppc4xx_l2c_probe(void)
 		of_node_put(np);
 		return -ENODEV;
 	}
-	l2_size = prop[0];
+	l2_size = be32_to_cpu(prop[0]);
 
 	/* Map DCRs */
 	dcrreg = of_get_property(np, "dcr-reg", &len);
@@ -103,8 +103,8 @@ static int __init ppc4xx_l2c_probe(void)
 		of_node_put(np);
 		return -ENODEV;
 	}
-	dcrbase_isram = dcrreg[0];
-	dcrbase_l2c = dcrreg[2];
+	dcrbase_isram = be32_to_cpu(dcrreg[0]);
+	dcrbase_l2c = be32_to_cpu(dcrreg[2]);
 
 	/* Get and map irq number from device tree */
 	irq = irq_of_parse_and_map(np, 0);
@@ -198,7 +198,7 @@ void ppc4xx_reset_system(char *cmd)
 {
 	struct device_node *np;
 	u32 reset_type = DBCR0_RST_SYSTEM;
-	const u32 *prop;
+	const __be32 *prop;
 
 	np = of_find_node_by_type(NULL, "cpu");
 	if (np) {
@@ -210,8 +210,8 @@ void ppc4xx_reset_system(char *cmd)
 		 * 2 - PPC4xx chip reset
 		 * 3 - PPC4xx system reset (default)
 		 */
-		if ((prop) && ((prop[0] >= 1) && (prop[0] <= 3)))
-			reset_type = prop[0] << 28;
+		if ((prop) && ((be32_to_cpu(prop[0]) >= 1) && (be32_to_cpu(prop[0]) <= 3)))
+			reset_type = be32_to_cpu(prop[0]) << 28;
 	}
 
 	mtspr(SPRN_DBCR0, mfspr(SPRN_DBCR0) | reset_type);
diff --git a/arch/powerpc/sysdev/uic.c b/arch/powerpc/sysdev/uic.c
index 0038fb7..e2f7de8 100644
--- a/arch/powerpc/sysdev/uic.c
+++ b/arch/powerpc/sysdev/uic.c
@@ -253,7 +253,7 @@ uic_irq_ret:
 static struct uic * __init uic_init_one(struct device_node *node)
 {
 	struct uic *uic;
-	const u32 *indexp, *dcrreg;
+	const __be32 *indexp, *dcrreg;
 	int len;
 
 	BUG_ON(! of_device_is_compatible(node, "ibm,uic"));
@@ -269,7 +269,7 @@ static struct uic * __init uic_init_one(struct device_node *node)
 		       "cell-index property\n", node->full_name);
 		return NULL;
 	}
-	uic->index = *indexp;
+	uic->index = be32_to_cpup(indexp);
 
 	dcrreg = of_get_property(node, "dcr-reg", &len);
 	if (!dcrreg || (len != 2*sizeof(u32))) {
@@ -277,7 +277,7 @@ static struct uic * __init uic_init_one(struct device_node *node)
 		       "dcr-reg property\n", node->full_name);
 		return NULL;
 	}
-	uic->dcrbase = *dcrreg;
+	uic->dcrbase = be32_to_cpup(dcrreg);
 
 	uic->irqhost = irq_alloc_host(node, IRQ_HOST_MAP_LINEAR,
 				      NR_UIC_INTS, &uic_host_ops, -1);
-- 
1.7.1

^ permalink raw reply related

* [PATCH 14/18] powerpc, of_serial: Endianness issues setting up the serial ports
From: Ian Munsie @ 2010-10-01  7:06 UTC (permalink / raw)
  To: linux-kernel, linuxppc-dev, benh
  Cc: Michal Simek, Jiri Kosina, devicetree-discuss, Greg Kroah-Hartman,
	Stefan Weil, paulus, Ian Munsie, Sean MacLennan, David S. Miller
In-Reply-To: <1285916771-18033-1-git-send-email-imunsie@au1.ibm.com>

From: Ian Munsie <imunsie@au1.ibm.com>

The speed and clock of the serial ports is retrieved from the device
tree in both the PowerPC legacy serial code and the Open Firmware serial
driver, therefore they need to handle the fact that the device tree is
always big endian, while the CPU may not be.

Also fix other device tree references in the legacy serial code.

Signed-off-by: Ian Munsie <imunsie@au1.ibm.com>
---
 arch/powerpc/kernel/legacy_serial.c |   22 +++++++++++-----------
 drivers/serial/of_serial.c          |   12 ++++++------
 2 files changed, 17 insertions(+), 17 deletions(-)

diff --git a/arch/powerpc/kernel/legacy_serial.c b/arch/powerpc/kernel/legacy_serial.c
index c1fd0f9..c834757 100644
--- a/arch/powerpc/kernel/legacy_serial.c
+++ b/arch/powerpc/kernel/legacy_serial.c
@@ -52,14 +52,14 @@ static int __init add_legacy_port(struct device_node *np, int want_index,
 				  phys_addr_t taddr, unsigned long irq,
 				  upf_t flags, int irq_check_parent)
 {
-	const u32 *clk, *spd;
+	const __be32 *clk, *spd;
 	u32 clock = BASE_BAUD * 16;
 	int index;
 
 	/* get clock freq. if present */
 	clk = of_get_property(np, "clock-frequency", NULL);
 	if (clk && *clk)
-		clock = *clk;
+		clock = be32_to_cpup(clk);
 
 	/* get default speed if present */
 	spd = of_get_property(np, "current-speed", NULL);
@@ -109,7 +109,7 @@ static int __init add_legacy_port(struct device_node *np, int want_index,
 	legacy_serial_infos[index].taddr = taddr;
 	legacy_serial_infos[index].np = of_node_get(np);
 	legacy_serial_infos[index].clock = clock;
-	legacy_serial_infos[index].speed = spd ? *spd : 0;
+	legacy_serial_infos[index].speed = spd ? be32_to_cpup(spd) : 0;
 	legacy_serial_infos[index].irq_check_parent = irq_check_parent;
 
 	printk(KERN_DEBUG "Found legacy serial port %d for %s\n",
@@ -168,7 +168,7 @@ static int __init add_legacy_soc_port(struct device_node *np,
 static int __init add_legacy_isa_port(struct device_node *np,
 				      struct device_node *isa_brg)
 {
-	const u32 *reg;
+	const __be32 *reg;
 	const char *typep;
 	int index = -1;
 	u64 taddr;
@@ -181,7 +181,7 @@ static int __init add_legacy_isa_port(struct device_node *np,
 		return -1;
 
 	/* Verify it's an IO port, we don't support anything else */
-	if (!(reg[0] & 0x00000001))
+	if (!(be32_to_cpu(reg[0]) & 0x00000001))
 		return -1;
 
 	/* Now look for an "ibm,aix-loc" property that gives us ordering
@@ -202,7 +202,7 @@ static int __init add_legacy_isa_port(struct device_node *np,
 		taddr = 0;
 
 	/* Add port, irq will be dealt with later */
-	return add_legacy_port(np, index, UPIO_PORT, reg[1], taddr,
+	return add_legacy_port(np, index, UPIO_PORT, be32_to_cpu(reg[1]), taddr,
 			       NO_IRQ, UPF_BOOT_AUTOCONF, 0);
 
 }
@@ -251,9 +251,9 @@ static int __init add_legacy_pci_port(struct device_node *np,
 	 * we get to their "reg" property
 	 */
 	if (np != pci_dev) {
-		const u32 *reg = of_get_property(np, "reg", NULL);
-		if (reg && (*reg < 4))
-			index = lindex = *reg;
+		const __be32 *reg = of_get_property(np, "reg", NULL);
+		if (reg && (be32_to_cpup(reg) < 4))
+			index = lindex = be32_to_cpup(reg);
 	}
 
 	/* Local index means it's the Nth port in the PCI chip. Unfortunately
@@ -507,7 +507,7 @@ static int __init check_legacy_serial_console(void)
 	struct device_node *prom_stdout = NULL;
 	int i, speed = 0, offset = 0;
 	const char *name;
-	const u32 *spd;
+	const __be32 *spd;
 
 	DBG(" -> check_legacy_serial_console()\n");
 
@@ -547,7 +547,7 @@ static int __init check_legacy_serial_console(void)
 	}
 	spd = of_get_property(prom_stdout, "current-speed", NULL);
 	if (spd)
-		speed = *spd;
+		speed = be32_to_cpup(spd);
 
 	if (strcmp(name, "serial") != 0)
 		goto not_found;
diff --git a/drivers/serial/of_serial.c b/drivers/serial/of_serial.c
index 2af8fd1..17849dc 100644
--- a/drivers/serial/of_serial.c
+++ b/drivers/serial/of_serial.c
@@ -31,8 +31,8 @@ static int __devinit of_platform_serial_setup(struct platform_device *ofdev,
 {
 	struct resource resource;
 	struct device_node *np = ofdev->dev.of_node;
-	const unsigned int *clk, *spd;
-	const u32 *prop;
+	const __be32 *clk, *spd;
+	const __be32 *prop;
 	int ret, prop_size;
 
 	memset(port, 0, sizeof *port);
@@ -55,23 +55,23 @@ static int __devinit of_platform_serial_setup(struct platform_device *ofdev,
 	/* Check for shifted address mapping */
 	prop = of_get_property(np, "reg-offset", &prop_size);
 	if (prop && (prop_size == sizeof(u32)))
-		port->mapbase += *prop;
+		port->mapbase += be32_to_cpup(prop);
 
 	/* Check for registers offset within the devices address range */
 	prop = of_get_property(np, "reg-shift", &prop_size);
 	if (prop && (prop_size == sizeof(u32)))
-		port->regshift = *prop;
+		port->regshift = be32_to_cpup(prop);
 
 	port->irq = irq_of_parse_and_map(np, 0);
 	port->iotype = UPIO_MEM;
 	port->type = type;
-	port->uartclk = *clk;
+	port->uartclk = be32_to_cpup(clk);
 	port->flags = UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF | UPF_IOREMAP
 		| UPF_FIXED_PORT | UPF_FIXED_TYPE;
 	port->dev = &ofdev->dev;
 	/* If current-speed was set, then try not to change it. */
 	if (spd)
-		port->custom_divisor = *clk / (16 * (*spd));
+		port->custom_divisor = be32_to_cpup(clk) / (16 * (be32_to_cpup(spd)));
 
 	return 0;
 }
-- 
1.7.1

^ permalink raw reply related

* [PATCH 17/18] net: Fix endianess issues in IBM newemac driver
From: Ian Munsie @ 2010-10-01  7:06 UTC (permalink / raw)
  To: linux-kernel, linuxppc-dev, benh
  Cc: Jiri Pirko, netdev, devicetree-discuss, paulus, Ian Munsie,
	Sean MacLennan, Tejun Heo, David S. Miller
In-Reply-To: <1285916771-18033-1-git-send-email-imunsie@au1.ibm.com>

From: Ian Munsie <imunsie@au1.ibm.com>

This patch fixes all the device tree and ring buffer accesses in the IBM
newemac driver.

Signed-off-by: Ian Munsie <imunsie@au1.ibm.com>
---
 drivers/net/ibm_newemac/core.c |   68 ++++++++++++++++++++--------------------
 drivers/net/ibm_newemac/mal.c  |    6 ++--
 drivers/net/ibm_newemac/mal.h  |    6 ++--
 3 files changed, 40 insertions(+), 40 deletions(-)

diff --git a/drivers/net/ibm_newemac/core.c b/drivers/net/ibm_newemac/core.c
index 3506fd6..67238b8 100644
--- a/drivers/net/ibm_newemac/core.c
+++ b/drivers/net/ibm_newemac/core.c
@@ -981,12 +981,12 @@ static int emac_resize_rx_ring(struct emac_instance *dev, int new_mtu)
 	 * to simplify error recovery in the case of allocation failure later.
 	 */
 	for (i = 0; i < NUM_RX_BUFF; ++i) {
-		if (dev->rx_desc[i].ctrl & MAL_RX_CTRL_FIRST)
+		if (dev->rx_desc[i].ctrl & cpu_to_be16(MAL_RX_CTRL_FIRST))
 			++dev->estats.rx_dropped_resize;
 
 		dev->rx_desc[i].data_len = 0;
-		dev->rx_desc[i].ctrl = MAL_RX_CTRL_EMPTY |
-		    (i == (NUM_RX_BUFF - 1) ? MAL_RX_CTRL_WRAP : 0);
+		dev->rx_desc[i].ctrl = cpu_to_be16(MAL_RX_CTRL_EMPTY |
+		    (i == (NUM_RX_BUFF - 1) ? MAL_RX_CTRL_WRAP : 0));
 	}
 
 	/* Reallocate RX ring only if bigger skb buffers are required */
@@ -1005,9 +1005,9 @@ static int emac_resize_rx_ring(struct emac_instance *dev, int new_mtu)
 		dev_kfree_skb(dev->rx_skb[i]);
 
 		skb_reserve(skb, EMAC_RX_SKB_HEADROOM + 2);
-		dev->rx_desc[i].data_ptr =
+		dev->rx_desc[i].data_ptr = cpu_to_be32(
 		    dma_map_single(&dev->ofdev->dev, skb->data - 2, rx_sync_size,
-				   DMA_FROM_DEVICE) + 2;
+				   DMA_FROM_DEVICE) + 2);
 		dev->rx_skb[i] = skb;
 	}
  skip:
@@ -1067,7 +1067,7 @@ static void emac_clean_tx_ring(struct emac_instance *dev)
 		if (dev->tx_skb[i]) {
 			dev_kfree_skb(dev->tx_skb[i]);
 			dev->tx_skb[i] = NULL;
-			if (dev->tx_desc[i].ctrl & MAL_TX_CTRL_READY)
+			if (dev->tx_desc[i].ctrl & cpu_to_be16(MAL_TX_CTRL_READY))
 				++dev->estats.tx_dropped;
 		}
 		dev->tx_desc[i].ctrl = 0;
@@ -1104,12 +1104,12 @@ static inline int emac_alloc_rx_skb(struct emac_instance *dev, int slot,
 	dev->rx_desc[slot].data_len = 0;
 
 	skb_reserve(skb, EMAC_RX_SKB_HEADROOM + 2);
-	dev->rx_desc[slot].data_ptr =
+	dev->rx_desc[slot].data_ptr = cpu_to_be32(
 	    dma_map_single(&dev->ofdev->dev, skb->data - 2, dev->rx_sync_size,
-			   DMA_FROM_DEVICE) + 2;
+			   DMA_FROM_DEVICE) + 2);
 	wmb();
-	dev->rx_desc[slot].ctrl = MAL_RX_CTRL_EMPTY |
-	    (slot == (NUM_RX_BUFF - 1) ? MAL_RX_CTRL_WRAP : 0);
+	dev->rx_desc[slot].ctrl = cpu_to_be16(MAL_RX_CTRL_EMPTY |
+	    (slot == (NUM_RX_BUFF - 1) ? MAL_RX_CTRL_WRAP : 0));
 
 	return 0;
 }
@@ -1373,12 +1373,12 @@ static int emac_start_xmit(struct sk_buff *skb, struct net_device *ndev)
 	DBG2(dev, "xmit(%u) %d" NL, len, slot);
 
 	dev->tx_skb[slot] = skb;
-	dev->tx_desc[slot].data_ptr = dma_map_single(&dev->ofdev->dev,
+	dev->tx_desc[slot].data_ptr = cpu_to_be32(dma_map_single(&dev->ofdev->dev,
 						     skb->data, len,
-						     DMA_TO_DEVICE);
-	dev->tx_desc[slot].data_len = (u16) len;
+						     DMA_TO_DEVICE));
+	dev->tx_desc[slot].data_len = cpu_to_be16(len);
 	wmb();
-	dev->tx_desc[slot].ctrl = ctrl;
+	dev->tx_desc[slot].ctrl = cpu_to_be16(ctrl);
 
 	return emac_xmit_finish(dev, len);
 }
@@ -1399,9 +1399,9 @@ static inline int emac_xmit_split(struct emac_instance *dev, int slot,
 			ctrl |= MAL_TX_CTRL_WRAP;
 
 		dev->tx_skb[slot] = NULL;
-		dev->tx_desc[slot].data_ptr = pd;
-		dev->tx_desc[slot].data_len = (u16) chunk;
-		dev->tx_desc[slot].ctrl = ctrl;
+		dev->tx_desc[slot].data_ptr = cpu_to_be32(pd);
+		dev->tx_desc[slot].data_len = cpu_to_be16(chunk);
+		dev->tx_desc[slot].ctrl = cpu_to_be16(ctrl);
 		++dev->tx_cnt;
 
 		if (!len)
@@ -1442,9 +1442,9 @@ static int emac_start_xmit_sg(struct sk_buff *skb, struct net_device *ndev)
 	/* skb data */
 	dev->tx_skb[slot] = NULL;
 	chunk = min(len, MAL_MAX_TX_SIZE);
-	dev->tx_desc[slot].data_ptr = pd =
-	    dma_map_single(&dev->ofdev->dev, skb->data, len, DMA_TO_DEVICE);
-	dev->tx_desc[slot].data_len = (u16) chunk;
+	dev->tx_desc[slot].data_ptr = cpu_to_be32(pd =
+	    dma_map_single(&dev->ofdev->dev, skb->data, len, DMA_TO_DEVICE));
+	dev->tx_desc[slot].data_len = cpu_to_be16(chunk);
 	len -= chunk;
 	if (unlikely(len))
 		slot = emac_xmit_split(dev, slot, pd + chunk, len, !nr_frags,
@@ -1473,7 +1473,7 @@ static int emac_start_xmit_sg(struct sk_buff *skb, struct net_device *ndev)
 	if (dev->tx_slot == NUM_TX_BUFF - 1)
 		ctrl |= MAL_TX_CTRL_WRAP;
 	wmb();
-	dev->tx_desc[dev->tx_slot].ctrl = ctrl;
+	dev->tx_desc[dev->tx_slot].ctrl = cpu_to_be16(ctrl);
 	dev->tx_slot = (slot + 1) % NUM_TX_BUFF;
 
 	return emac_xmit_finish(dev, skb->len);
@@ -1541,7 +1541,7 @@ static void emac_poll_tx(void *param)
 		u16 ctrl;
 		int slot = dev->ack_slot, n = 0;
 	again:
-		ctrl = dev->tx_desc[slot].ctrl;
+		ctrl = be16_to_cpu(dev->tx_desc[slot].ctrl);
 		if (!(ctrl & MAL_TX_CTRL_READY)) {
 			struct sk_buff *skb = dev->tx_skb[slot];
 			++n;
@@ -1583,8 +1583,8 @@ static inline void emac_recycle_rx_skb(struct emac_instance *dev, int slot,
 
 	dev->rx_desc[slot].data_len = 0;
 	wmb();
-	dev->rx_desc[slot].ctrl = MAL_RX_CTRL_EMPTY |
-	    (slot == (NUM_RX_BUFF - 1) ? MAL_RX_CTRL_WRAP : 0);
+	dev->rx_desc[slot].ctrl = cpu_to_be16(MAL_RX_CTRL_EMPTY |
+	    (slot == (NUM_RX_BUFF - 1) ? MAL_RX_CTRL_WRAP : 0));
 }
 
 static void emac_parse_rx_error(struct emac_instance *dev, u16 ctrl)
@@ -1628,7 +1628,7 @@ static inline void emac_rx_csum(struct emac_instance *dev,
 static inline int emac_rx_sg_append(struct emac_instance *dev, int slot)
 {
 	if (likely(dev->rx_sg_skb != NULL)) {
-		int len = dev->rx_desc[slot].data_len;
+		int len = be16_to_cpu(dev->rx_desc[slot].data_len);
 		int tot_len = dev->rx_sg_skb->len + len;
 
 		if (unlikely(tot_len + 2 > dev->rx_skb_size)) {
@@ -1659,14 +1659,14 @@ static int emac_poll_rx(void *param, int budget)
 	while (budget > 0) {
 		int len;
 		struct sk_buff *skb;
-		u16 ctrl = dev->rx_desc[slot].ctrl;
+		u16 ctrl = be16_to_cpu(dev->rx_desc[slot].ctrl);
 
 		if (ctrl & MAL_RX_CTRL_EMPTY)
 			break;
 
 		skb = dev->rx_skb[slot];
 		mb();
-		len = dev->rx_desc[slot].data_len;
+		len = be16_to_cpu(dev->rx_desc[slot].data_len);
 
 		if (unlikely(!MAL_IS_SINGLE_RX(ctrl)))
 			goto sg;
@@ -1757,7 +1757,7 @@ static int emac_poll_rx(void *param, int budget)
 
 	if (unlikely(budget && test_bit(MAL_COMMAC_RX_STOPPED, &dev->commac.flags))) {
 		mb();
-		if (!(dev->rx_desc[slot].ctrl & MAL_RX_CTRL_EMPTY)) {
+		if (!(dev->rx_desc[slot].ctrl & cpu_to_be16(MAL_RX_CTRL_EMPTY))) {
 			DBG2(dev, "rx restart" NL);
 			received = 0;
 			goto again;
@@ -1783,7 +1783,7 @@ static int emac_peek_rx(void *param)
 {
 	struct emac_instance *dev = param;
 
-	return !(dev->rx_desc[dev->rx_slot].ctrl & MAL_RX_CTRL_EMPTY);
+	return !(dev->rx_desc[dev->rx_slot].ctrl & cpu_to_be16(MAL_RX_CTRL_EMPTY));
 }
 
 /* NAPI poll context */
@@ -1793,7 +1793,7 @@ static int emac_peek_rx_sg(void *param)
 
 	int slot = dev->rx_slot;
 	while (1) {
-		u16 ctrl = dev->rx_desc[slot].ctrl;
+		u16 ctrl = be16_to_cpu(dev->rx_desc[slot].ctrl);
 		if (ctrl & MAL_RX_CTRL_EMPTY)
 			return 0;
 		else if (ctrl & MAL_RX_CTRL_LAST)
@@ -2367,14 +2367,14 @@ static int __devinit emac_read_uint_prop(struct device_node *np, const char *nam
 					 u32 *val, int fatal)
 {
 	int len;
-	const u32 *prop = of_get_property(np, name, &len);
+	const __be32 *prop = of_get_property(np, name, &len);
 	if (prop == NULL || len < sizeof(u32)) {
 		if (fatal)
 			printk(KERN_ERR "%s: missing %s property\n",
 			       np->full_name, name);
 		return -ENODEV;
 	}
-	*val = *prop;
+	*val = be32_to_cpup(prop);
 	return 0;
 }
 
@@ -3013,7 +3013,7 @@ static void __init emac_make_bootlist(void)
 
 	/* Collect EMACs */
 	while((np = of_find_all_nodes(np)) != NULL) {
-		const u32 *idx;
+		const __be32 *idx;
 
 		if (of_match_node(emac_match, np) == NULL)
 			continue;
@@ -3022,7 +3022,7 @@ static void __init emac_make_bootlist(void)
 		idx = of_get_property(np, "cell-index", NULL);
 		if (idx == NULL)
 			continue;
-		cell_indices[i] = *idx;
+		cell_indices[i] = be32_to_cpup(idx);
 		emac_boot_list[i++] = of_node_get(np);
 		if (i >= EMAC_BOOT_LIST_SIZE) {
 			of_node_put(np);
diff --git a/drivers/net/ibm_newemac/mal.c b/drivers/net/ibm_newemac/mal.c
index d5717e2..9e4939e 100644
--- a/drivers/net/ibm_newemac/mal.c
+++ b/drivers/net/ibm_newemac/mal.c
@@ -524,7 +524,7 @@ static int __devinit mal_probe(struct platform_device *ofdev,
 	int err = 0, i, bd_size;
 	int index = mal_count++;
 	unsigned int dcr_base;
-	const u32 *prop;
+	const __be32 *prop;
 	u32 cfg;
 	unsigned long irqflags;
 	irq_handler_t hdlr_serr, hdlr_txde, hdlr_rxde;
@@ -550,7 +550,7 @@ static int __devinit mal_probe(struct platform_device *ofdev,
 		err = -ENODEV;
 		goto fail;
 	}
-	mal->num_tx_chans = prop[0];
+	mal->num_tx_chans = be32_to_cpu(prop[0]);
 
 	prop = of_get_property(ofdev->dev.of_node, "num-rx-chans", NULL);
 	if (prop == NULL) {
@@ -560,7 +560,7 @@ static int __devinit mal_probe(struct platform_device *ofdev,
 		err = -ENODEV;
 		goto fail;
 	}
-	mal->num_rx_chans = prop[0];
+	mal->num_rx_chans = be32_to_cpu(prop[0]);
 
 	dcr_base = dcr_resource_start(ofdev->dev.of_node, 0);
 	if (dcr_base == 0) {
diff --git a/drivers/net/ibm_newemac/mal.h b/drivers/net/ibm_newemac/mal.h
index 6608421..b8ee413 100644
--- a/drivers/net/ibm_newemac/mal.h
+++ b/drivers/net/ibm_newemac/mal.h
@@ -147,9 +147,9 @@ static inline int mal_tx_chunks(int len)
 
 /* MAL Buffer Descriptor structure */
 struct mal_descriptor {
-	u16 ctrl;		/* MAL / Commac status control bits */
-	u16 data_len;		/* Max length is 4K-1 (12 bits)     */
-	u32 data_ptr;		/* pointer to actual data buffer    */
+	__be16 ctrl;		/* MAL / Commac status control bits */
+	__be16 data_len;	/* Max length is 4K-1 (12 bits)     */
+	__be32 data_ptr;	/* pointer to actual data buffer    */
 };
 
 /* the following defines are for the MadMAL status and control registers. */
-- 
1.7.1

^ permalink raw reply related

* Introduce support for little endian PowerPC
From: Ian Munsie @ 2010-10-01  7:05 UTC (permalink / raw)
  To: linux-kernel, linuxppc-dev, benh; +Cc: paulus

Some PowerPC processors can be run in either big or little endian modes, some
others can map selected pages of memory as little endian, which allows the same
thing. Until now we have only supported the default big endian mode in Linux.
This patch set introduces little endian support for the 44x family of PowerPC
processors.

This patch set in combination with a patched GCC, binutils, uClibc and
buildroot has allowed for a full proof of concept little endian environment on
a 440 Taishan board, which was able to successfully run busybox, OpenSSH and a
handful of other userspace programs without problems.

I am not currently in a position to release my patches for the toolchain, but
in the meantime I thought I would send my patches on the kernel side for review
and feedback.

While my toolchain patches are necessary to support the powerpcle-linux target,
the kernel can still be built with an unpatched toolchain using the
powerpcle-elf target instead. Userspace programs, however, do require the
patched toolchain to build.

Since the processor still starts as big endian, the boot wrapper must still be
compiled with a big endian 32bit toolchain. This can be passed into the build
as CROSSBE_COMPILE in the same manner as the CROSS_COMPILE variable. When the
boot wrapper is finished it runs some platform specific assembly to switch to
little endian and execute the kernel.

This is not yet complete support for little endian PowerPC, some outstanding
issues that I am aware of are:
 * We only support 32bit PowerPC for now (and indeed, only 44x)
 * The vdso has not been fixed to be endian agnostic - any userspace program
   accessing it will get an unexpected result.
 * I have not touched PCI at all
 * Remaining device tree accesses still need to be examined to ensure they are
   correctly handling the endianess of the device tree.
 * Any other driver that uses the device tree is likely be broken for the same reason.
 * I've included a patch for the alignment handler, however it is as yet
   completely untested due to a property of the hardware I've been using for
   testing.

^ permalink raw reply

* [PATCH 01/18] powerpc: Add ability to build little endian kernels
From: Ian Munsie @ 2010-10-01  7:05 UTC (permalink / raw)
  To: linux-kernel, linuxppc-dev, benh
  Cc: Michal Marek, Albert Herranz, paulus, Ian Munsie, Andreas Schwab,
	Andrew Morton, Sam Ravnborg, Torez Smith
In-Reply-To: <1285916771-18033-1-git-send-email-imunsie@au1.ibm.com>

From: Ian Munsie <imunsie@au1.ibm.com>

This patch allows the kbuild system to successfully compile a kernel for
the little endian PowerPC architecture.

To build such a kernel a supported platform must be used and
CONFIG_CPU_LITTLE_ENDIAN must be set. CROSS_COMPILE must be set to a
suitable toolchain prefix (compiled for the powerpcle-elf target, or
patched to allow the powerpcle-linux target).

Since the system will always start in big endian mode, the zImage
wrapper must still be compiled with a big endian toolchain, which can be
specified via CROSS32_COMPILE.

Signed-off-by: Ian Munsie <imunsie@au1.ibm.com>
---
 arch/powerpc/Makefile                   |   22 +++++++++++++++++++---
 arch/powerpc/boot/Makefile              |    3 ++-
 arch/powerpc/kernel/vdso32/vdso32.lds.S |    4 ++++
 arch/powerpc/platforms/Kconfig.cputype  |   16 ++++++++++++++++
 4 files changed, 41 insertions(+), 4 deletions(-)

diff --git a/arch/powerpc/Makefile b/arch/powerpc/Makefile
index b7212b6..3eed685 100644
--- a/arch/powerpc/Makefile
+++ b/arch/powerpc/Makefile
@@ -56,11 +56,27 @@ endif
 
 UTS_MACHINE := $(OLDARCH)
 
+ifeq ($(CONFIG_CPU_LITTLE_ENDIAN),y)
+override CC	+= -mlittle-endian
+override AS	+= -mlittle-endian
+override LD	+= -EL
+LDEMULATION	:= lppc
+GNUTARGET	:= powerpcle
+MULTIPLEWORD	:= -mno-multiple
+else
+override CC	+= -mbig-endian
+override AS	+= -mbig-endian
+override LD	+= -EB
+LDEMULATION	:= ppc
+GNUTARGET	:= powerpc
+MULTIPLEWORD	:= -mmultiple
+endif
+
 ifeq ($(HAS_BIARCH),y)
 override AS	+= -a$(CONFIG_WORD_SIZE)
-override LD	+= -m elf$(CONFIG_WORD_SIZE)ppc
+override LD	+= -m elf$(CONFIG_WORD_SIZE)$(LDEMULATION)
 override CC	+= -m$(CONFIG_WORD_SIZE)
-override AR	:= GNUTARGET=elf$(CONFIG_WORD_SIZE)-powerpc $(AR)
+override AR	:= GNUTARGET=elf$(CONFIG_WORD_SIZE)-$(GNUTARGET) $(AR)
 endif
 
 LDFLAGS_vmlinux-yy := -Bstatic
@@ -68,7 +84,7 @@ LDFLAGS_vmlinux-$(CONFIG_PPC64)$(CONFIG_RELOCATABLE) := -pie
 LDFLAGS_vmlinux	:= $(LDFLAGS_vmlinux-yy)
 
 CFLAGS-$(CONFIG_PPC64)	:= -mminimal-toc -mtraceback=none  -mcall-aixdesc
-CFLAGS-$(CONFIG_PPC32)	:= -ffixed-r2 -mmultiple
+CFLAGS-$(CONFIG_PPC32)	:= -ffixed-r2 $(MULTIPLEWORD)
 KBUILD_CPPFLAGS	+= -Iarch/$(ARCH)
 KBUILD_AFLAGS	+= -Iarch/$(ARCH)
 KBUILD_CFLAGS	+= -msoft-float -pipe -Iarch/$(ARCH) $(CFLAGS-y)
diff --git a/arch/powerpc/boot/Makefile b/arch/powerpc/boot/Makefile
index fae8192..39f10a4 100644
--- a/arch/powerpc/boot/Makefile
+++ b/arch/powerpc/boot/Makefile
@@ -22,7 +22,8 @@ all: $(obj)/zImage
 BOOTCFLAGS    := -Wall -Wundef -Wstrict-prototypes -Wno-trigraphs \
 		 -fno-strict-aliasing -Os -msoft-float -pipe \
 		 -fomit-frame-pointer -fno-builtin -fPIC -nostdinc \
-		 -isystem $(shell $(CROSS32CC) -print-file-name=include)
+		 -isystem $(shell $(CROSS32CC) -print-file-name=include) \
+		 -mbig-endian
 BOOTAFLAGS	:= -D__ASSEMBLY__ $(BOOTCFLAGS) -traditional -nostdinc
 
 ifdef CONFIG_DEBUG_INFO
diff --git a/arch/powerpc/kernel/vdso32/vdso32.lds.S b/arch/powerpc/kernel/vdso32/vdso32.lds.S
index 0546bcd..f4c6676 100644
--- a/arch/powerpc/kernel/vdso32/vdso32.lds.S
+++ b/arch/powerpc/kernel/vdso32/vdso32.lds.S
@@ -4,7 +4,11 @@
  */
 #include <asm/vdso.h>
 
+#ifdef __LITTLE_ENDIAN__
+OUTPUT_FORMAT("elf32-powerpcle", "elf32-powerpcle", "elf32-powerpcle")
+#else
 OUTPUT_FORMAT("elf32-powerpc", "elf32-powerpc", "elf32-powerpc")
+#endif
 OUTPUT_ARCH(powerpc:common)
 ENTRY(_start)
 
diff --git a/arch/powerpc/platforms/Kconfig.cputype b/arch/powerpc/platforms/Kconfig.cputype
index d361f81..074ff12 100644
--- a/arch/powerpc/platforms/Kconfig.cputype
+++ b/arch/powerpc/platforms/Kconfig.cputype
@@ -329,3 +329,19 @@ config CHECK_CACHE_COHERENCY
 	bool
 
 endmenu
+
+config ARCH_SUPPORTS_LITTLE_ENDIAN
+	bool
+
+config CPU_LITTLE_ENDIAN
+	bool "Build little endian kernel"
+	depends on ARCH_SUPPORTS_LITTLE_ENDIAN && EXPERIMENTAL
+	default n
+	help
+	  This option selects whether a big endian or little endian kernel will
+	  be built.
+
+	  Note that if building a little endian kernel, CROSS_COMPILE must
+	  point to a toolchain capable of targetting little endian powerpc,
+	  while the toolchain specified by CROSS32_COMPILE must be capable of
+	  targetting *BIG* endian PowerPC.
-- 
1.7.1

^ permalink raw reply related

* [PATCH 02/18] powerpc: Add CROSSBE_COMPILE to build big endian boot wrapper
From: Ian Munsie @ 2010-10-01  7:05 UTC (permalink / raw)
  To: linux-kernel, linuxppc-dev, benh
  Cc: Michal Marek, Albert Herranz, paulus, Ian Munsie, Andrew Morton,
	Sam Ravnborg
In-Reply-To: <1285916771-18033-1-git-send-email-imunsie@au1.ibm.com>

From: Ian Munsie <imunsie@au1.ibm.com>

Since the boot wrapper must be built by a big endian 32bit toolchain
regardless of what the rest of the kernel is using introduce a new
parameter to specify that toolchain - CROSSBE_COMPILE.

We already have CROSS32_COMPILE which is already used for the boot
wrapper, but it is also used to build the 32bit vdso which should be
build in the same endianness as the rest of the kernel, so it is
necessary to be able to specify the toolchain to build the boot wrapper
separately from that used to build the vdso and again separately from
that used to build the main kernel.

CROSSBE_COMPILE should be pointed to a toolchain capable of targeting
32bit big endian powerpc, either specifically targetted at 32bit or
bi-arch 64 and 32bit. If CROSSBE_COMPILE is not specified it will fall
back to CROSS32_COMPILE to maintain compatibility with big endian
targets.

Signed-off-by: Ian Munsie <imunsie@au1.ibm.com>
---
 arch/powerpc/Makefile      |   19 +++++++++++++++++--
 arch/powerpc/boot/Makefile |   25 +++++++++++++++----------
 2 files changed, 32 insertions(+), 12 deletions(-)

diff --git a/arch/powerpc/Makefile b/arch/powerpc/Makefile
index 3eed685..ab69b0e 100644
--- a/arch/powerpc/Makefile
+++ b/arch/powerpc/Makefile
@@ -14,7 +14,7 @@
 
 HAS_BIARCH	:= $(call cc-option-yn, -m32)
 
-# Set default 32 bits cross compilers for vdso and boot wrapper
+# Set default 32 bits cross compilers for vdso
 CROSS32_COMPILE ?=
 
 CROSS32CC		:= $(CROSS32_COMPILE)gcc
@@ -27,7 +27,22 @@ CROSS32AR	:= GNUTARGET=elf32-powerpc $(AR)
 endif
 endif
 
-export CROSS32CC CROSS32AR
+# Set default big endian 32 bits cross compiler for boot wrapper
+ifeq ($(CROSSBE_COMPILE),)
+CROSSBECC	:= $(CROSS32CC)
+CROSSBEAR	:= $(CROSS32AR)
+else
+CROSSBECC	:= $(CROSSBE_COMPILE)gcc
+CROSSBEAR	:= $(CROSSBE_COMPILE)ar
+endif
+
+ifeq ($(call try-run,\
+	$(CROSSBECC) -m32 -c -xc /dev/null -o "$$TMP",y,n),y)
+CROSSBECC	:= $(CROSSBECC) -m32
+CROSSBEAR	:= GNUTARGET=elf32-powerpc $(CROSSBEAR)
+endif
+
+export CROSS32CC CROSS32AR CROSSBECC CROSSBEAR
 
 ifeq ($(CROSS_COMPILE),)
 KBUILD_DEFCONFIG := $(shell uname -m)_defconfig
diff --git a/arch/powerpc/boot/Makefile b/arch/powerpc/boot/Makefile
index 39f10a4..79d7e69 100644
--- a/arch/powerpc/boot/Makefile
+++ b/arch/powerpc/boot/Makefile
@@ -6,23 +6,24 @@
 # Based on coffboot by Paul Mackerras
 # Simplified for ppc64 by Todd Inglett
 #
-# NOTE:	this code is built for 32 bit in ELF32 format even though
-#	it packages a 64 bit kernel.  We do this to simplify the
+# NOTE:	this code is built for 32 bit big endian in ELF32 format even
+#	though it packages a 64 bit kernel.  We do this to simplify the
 #	bootloader and increase compatibility with OpenFirmware.
 #
-#	To this end we need to define BOOTCC, etc, as the tools
-#	needed to build the 32 bit image.  That's normally the same
+#	To this end we need to define BOOTCC, etc, as the tools needed
+#	to build the 32 bit big endian image.  That's normally the same
 #	compiler for the rest of the kernel, with the -m32 flag added.
 #	To make it easier to setup a cross compiler,
-#	CROSS32_COMPILE is setup as a prefix just like CROSS_COMPILE
-#	in the toplevel makefile.
+#	CROSSBE_COMPILE is setup as a prefix just like CROSS_COMPILE
+#	in the toplevel makefile. If building a big endian kernel,
+#	CROSS32_COMPILE may be used in it's place.
 
 all: $(obj)/zImage
 
 BOOTCFLAGS    := -Wall -Wundef -Wstrict-prototypes -Wno-trigraphs \
 		 -fno-strict-aliasing -Os -msoft-float -pipe \
 		 -fomit-frame-pointer -fno-builtin -fPIC -nostdinc \
-		 -isystem $(shell $(CROSS32CC) -print-file-name=include) \
+		 -isystem $(shell $(CROSSBECC) -print-file-name=include) \
 		 -mbig-endian
 BOOTAFLAGS	:= -D__ASSEMBLY__ $(BOOTCFLAGS) -traditional -nostdinc
 
@@ -122,13 +123,13 @@ clean-files := $(zlib) $(zlibheader) $(zliblinuxheader) \
 		empty.c zImage.coff.lds zImage.ps3.lds zImage.lds
 
 quiet_cmd_bootcc = BOOTCC  $@
-      cmd_bootcc = $(CROSS32CC) -Wp,-MD,$(depfile) $(BOOTCFLAGS) -c -o $@ $<
+      cmd_bootcc = $(CROSSBECC) -Wp,-MD,$(depfile) $(BOOTCFLAGS) -c -o $@ $<
 
 quiet_cmd_bootas = BOOTAS  $@
-      cmd_bootas = $(CROSS32CC) -Wp,-MD,$(depfile) $(BOOTAFLAGS) -c -o $@ $<
+      cmd_bootas = $(CROSSBECC) -Wp,-MD,$(depfile) $(BOOTAFLAGS) -c -o $@ $<
 
 quiet_cmd_bootar = BOOTAR  $@
-      cmd_bootar = $(CROSS32AR) -cr $@.$$$$ $(filter-out FORCE,$^); mv $@.$$$$ $@
+      cmd_bootar = $(CROSSBEAR) -cr $@.$$$$ $(filter-out FORCE,$^); mv $@.$$$$ $@
 
 $(obj-libfdt): $(obj)/%.o: $(srctree)/scripts/dtc/libfdt/%.c FORCE
 	$(call if_changed_dep,bootcc)
@@ -157,6 +158,9 @@ wrapperbits	:= $(extra-y) $(addprefix $(obj)/,addnote hack-coff mktree) \
 #############
 # Bits for building various flavours of zImage
 
+ifneq ($(CROSSBE_COMPILE),)
+CROSSWRAP := -C "$(CROSSBE_COMPILE)"
+else
 ifneq ($(CROSS32_COMPILE),)
 CROSSWRAP := -C "$(CROSS32_COMPILE)"
 else
@@ -164,6 +168,7 @@ ifneq ($(CROSS_COMPILE),)
 CROSSWRAP := -C "$(CROSS_COMPILE)"
 endif
 endif
+endif
 
 # args (to if_changed): 1 = (this rule), 2 = platform, 3 = dts 4=dtb 5=initrd
 quiet_cmd_wrap	= WRAP    $@
-- 
1.7.1

^ permalink raw reply related

* [PATCH 03/18] powerpc: Support parsing a little endian kernel from zImage wrapper
From: Ian Munsie @ 2010-10-01  7:05 UTC (permalink / raw)
  To: linux-kernel, linuxppc-dev, benh; +Cc: paulus, Ian Munsie
In-Reply-To: <1285916771-18033-1-git-send-email-imunsie@au1.ibm.com>

From: Ian Munsie <imunsie@au1.ibm.com>

This patch adds support to the PowerPC zImage wrapper (which always runs
in big endian mode) to detect if the zImage is little endian and parse
it's ELF header to enable it's successful extraction.

It also provides some infrastructure for executing a little endian
kernel - PowerPC platforms that support little endian should fill
platform_ops.le_kentry with a function pointer to the routine
responsible for switching the CPU to little endian and executing the
kernel. This routing takes the same arguments in the same positions as
kentry to allow them to easily be passed onto the kernel, with the
kentry pointer itself tacked on as argument 4.

Signed-off-by: Ian Munsie <imunsie@au1.ibm.com>
---
 arch/powerpc/boot/elf.h       |    2 +-
 arch/powerpc/boot/elf_util.c  |   48 +++++++++++++++++++++++++++++++++++++---
 arch/powerpc/boot/main.c      |   26 +++++++++++++++++-----
 arch/powerpc/boot/ops.h       |    3 ++
 arch/powerpc/boot/prpmc2800.c |    5 +++-
 arch/powerpc/boot/swab.h      |   26 ++++++++++++++++++++++
 6 files changed, 98 insertions(+), 12 deletions(-)
 create mode 100644 arch/powerpc/boot/swab.h

diff --git a/arch/powerpc/boot/elf.h b/arch/powerpc/boot/elf.h
index 1941bc5..9de8105 100644
--- a/arch/powerpc/boot/elf.h
+++ b/arch/powerpc/boot/elf.h
@@ -152,6 +152,6 @@ struct elf_info {
 	unsigned long elfoffset;
 };
 int parse_elf64(void *hdr, struct elf_info *info);
-int parse_elf32(void *hdr, struct elf_info *info);
+int parse_elf32(void *hdr, struct elf_info *info, int *little_endian);
 
 #endif				/* _PPC_BOOT_ELF_H_ */
diff --git a/arch/powerpc/boot/elf_util.c b/arch/powerpc/boot/elf_util.c
index 1567a0c..67bba80 100644
--- a/arch/powerpc/boot/elf_util.c
+++ b/arch/powerpc/boot/elf_util.c
@@ -14,6 +14,7 @@
 #include "page.h"
 #include "string.h"
 #include "stdio.h"
+#include "swab.h"
 
 int parse_elf64(void *hdr, struct elf_info *info)
 {
@@ -47,7 +48,35 @@ int parse_elf64(void *hdr, struct elf_info *info)
 	return 1;
 }
 
-int parse_elf32(void *hdr, struct elf_info *info)
+void byteswap_elf32(Elf32_Ehdr *elf32) {
+	Elf32_Phdr *elf32ph;
+
+	swab16s(&elf32->e_type);
+	swab16s(&elf32->e_machine);
+	swab32s(&elf32->e_version);
+	swab32s(&elf32->e_entry);
+	swab32s(&elf32->e_phoff);
+	swab32s(&elf32->e_shoff);
+	swab32s(&elf32->e_flags);
+	swab16s(&elf32->e_ehsize);
+	swab16s(&elf32->e_phentsize);
+	swab16s(&elf32->e_phnum);
+	swab16s(&elf32->e_shentsize);
+	swab16s(&elf32->e_shnum);
+	swab16s(&elf32->e_shstrndx);
+
+	elf32ph = (Elf32_Phdr *) ((unsigned long)elf32 + elf32->e_phoff);
+	swab32s(&elf32ph->p_type);
+	swab32s(&elf32ph->p_offset);
+	swab32s(&elf32ph->p_vaddr);
+	swab32s(&elf32ph->p_paddr);
+	swab32s(&elf32ph->p_filesz);
+	swab32s(&elf32ph->p_memsz);
+	swab32s(&elf32ph->p_flags);
+	swab32s(&elf32ph->p_align);
+}
+
+int parse_elf32(void *hdr, struct elf_info *info, int *little_endian)
 {
 	Elf32_Ehdr *elf32 = hdr;
 	Elf32_Phdr *elf32ph;
@@ -57,9 +86,20 @@ int parse_elf32(void *hdr, struct elf_info *info)
 	      elf32->e_ident[EI_MAG1]  == ELFMAG1	&&
 	      elf32->e_ident[EI_MAG2]  == ELFMAG2	&&
 	      elf32->e_ident[EI_MAG3]  == ELFMAG3	&&
-	      elf32->e_ident[EI_CLASS] == ELFCLASS32	&&
-	      elf32->e_ident[EI_DATA]  == ELFDATA2MSB	&&
-	      (elf32->e_type            == ET_EXEC ||
+	      elf32->e_ident[EI_CLASS] == ELFCLASS32))
+		return 0;
+	switch(elf32->e_ident[EI_DATA]) {
+		case ELFDATA2MSB:
+			*little_endian = 0;
+			break;
+		case ELFDATA2LSB:
+			*little_endian = 1;
+			byteswap_elf32(elf32);
+			break;
+		default:
+			return 0;
+	}
+	if (!((elf32->e_type            == ET_EXEC ||
 	       elf32->e_type            == ET_DYN)      &&
 	      elf32->e_machine         == EM_PPC))
 		return 0;
diff --git a/arch/powerpc/boot/main.c b/arch/powerpc/boot/main.c
index a28f021..36dd2b6 100644
--- a/arch/powerpc/boot/main.c
+++ b/arch/powerpc/boot/main.c
@@ -27,7 +27,7 @@ struct addr_range {
 
 #undef DEBUG
 
-static struct addr_range prep_kernel(void)
+static struct addr_range prep_kernel(int *little_endian)
 {
 	char elfheader[256];
 	void *vmlinuz_addr = _vmlinux_start;
@@ -40,8 +40,10 @@ static struct addr_range prep_kernel(void)
 	gunzip_start(&gzstate, vmlinuz_addr, vmlinuz_size);
 	gunzip_exactly(&gzstate, elfheader, sizeof(elfheader));
 
-	if (!parse_elf64(elfheader, &ei) && !parse_elf32(elfheader, &ei))
+	if (!parse_elf64(elfheader, &ei) && !parse_elf32(elfheader, &ei, little_endian))
 		fatal("Error: not a valid PPC32 or PPC64 ELF file!\n\r");
+	if (*little_endian && !platform_ops.le_kentry)
+		fatal("Little Endian kernel unsupported on this platform!");
 
 	if (platform_ops.image_hdr)
 		platform_ops.image_hdr(elfheader);
@@ -166,8 +168,10 @@ void start(void)
 {
 	struct addr_range vmlinux, initrd;
 	kernel_entry_t kentry;
+	le_kernel_entry_t le_kentry;
 	unsigned long ft_addr = 0;
 	void *chosen;
+	int little_endian = 0;
 
 	/* Do this first, because malloc() could clobber the loader's
 	 * command line.  Only use the loader command line if a
@@ -189,7 +193,7 @@ void start(void)
 	if (!chosen)
 		chosen = create_node(NULL, "chosen");
 
-	vmlinux = prep_kernel();
+	vmlinux = prep_kernel(&little_endian);
 	initrd = prep_initrd(vmlinux, chosen,
 			     loader_info.initrd_addr, loader_info.initrd_size);
 	prep_cmdline(chosen);
@@ -206,11 +210,21 @@ void start(void)
 		console_ops.close();
 
 	kentry = (kernel_entry_t) vmlinux.addr;
+	le_kentry = (le_kernel_entry_t)platform_ops.le_kentry;
 	if (ft_addr)
-		kentry(ft_addr, 0, NULL);
+		if (little_endian)
+			le_kentry(ft_addr, 0, NULL, kentry);
+		else
+			kentry(ft_addr, 0, NULL);
 	else
-		kentry((unsigned long)initrd.addr, initrd.size,
-		       loader_info.promptr);
+		if (little_endian)
+			le_kentry((unsigned long)initrd.addr, initrd.size,
+			       loader_info.promptr, kentry);
+		else
+			kentry((unsigned long)initrd.addr, initrd.size,
+			       loader_info.promptr);
+
+	kentry(ft_addr, 0, NULL);
 
 	/* console closed so printf in fatal below may not work */
 	fatal("Error: Linux kernel returned to zImage boot wrapper!\n\r");
diff --git a/arch/powerpc/boot/ops.h b/arch/powerpc/boot/ops.h
index b3218ce..cd6c7bf 100644
--- a/arch/powerpc/boot/ops.h
+++ b/arch/powerpc/boot/ops.h
@@ -20,6 +20,8 @@
 #define	MAX_PROP_LEN		256 /* What should this be? */
 
 typedef void (*kernel_entry_t)(unsigned long r3, unsigned long r4, void *r5);
+typedef void (*le_kernel_entry_t)(unsigned long r3, unsigned long r4, void *r5,
+				  kernel_entry_t kentry);
 
 /* Platform specific operations */
 struct platform_ops {
@@ -30,6 +32,7 @@ struct platform_ops {
 	void *	(*realloc)(void *ptr, unsigned long size);
 	void	(*exit)(void);
 	void *	(*vmlinux_alloc)(unsigned long size);
+	le_kernel_entry_t le_kentry;
 };
 extern struct platform_ops platform_ops;
 
diff --git a/arch/powerpc/boot/prpmc2800.c b/arch/powerpc/boot/prpmc2800.c
index da31d60..6bad899 100644
--- a/arch/powerpc/boot/prpmc2800.c
+++ b/arch/powerpc/boot/prpmc2800.c
@@ -519,6 +519,7 @@ void platform_init(unsigned long r3, unsigned long r4, unsigned long r5,
 	void *vmlinuz_addr = _vmlinux_start;
 	unsigned long vmlinuz_size = _vmlinux_end - _vmlinux_start;
 	char elfheader[256];
+	int little_endian;
 
 	if (dt_size <= 0) /* No fdt */
 		exit();
@@ -533,7 +534,9 @@ void platform_init(unsigned long r3, unsigned long r4, unsigned long r5,
 	gunzip_start(&gzstate, vmlinuz_addr, vmlinuz_size);
 	gunzip_exactly(&gzstate, elfheader, sizeof(elfheader));
 
-	if (!parse_elf32(elfheader, &ei))
+	if (!parse_elf32(elfheader, &ei, &little_endian))
+		exit();
+	if (little_endian)
 		exit();
 
 	heap_start = (char *)(ei.memsize + ei.elfoffset); /* end of kernel*/
diff --git a/arch/powerpc/boot/swab.h b/arch/powerpc/boot/swab.h
new file mode 100644
index 0000000..b122c2d
--- /dev/null
+++ b/arch/powerpc/boot/swab.h
@@ -0,0 +1,26 @@
+#ifndef _SWAB_H_
+#define _SWAB_H_
+
+#include "types.h"
+
+static __inline__ void st_le16(volatile u16 *addr, const u16 val)
+{
+	__asm__ __volatile__ ("sthbrx %1,0,%2" : "=m" (*addr) : "r" (val), "r" (addr));
+}
+
+static inline void swab16s(u16 *addr)
+{
+	st_le16(addr, *addr);
+}
+
+static __inline__ void st_le32(volatile u32 *addr, const u32 val)
+{
+	__asm__ __volatile__ ("stwbrx %1,0,%2" : "=m" (*addr) : "r" (val), "r" (addr));
+}
+
+static inline void swab32s(u32 *addr)
+{
+	st_le32(addr, *addr);
+}
+
+#endif
-- 
1.7.1

^ permalink raw reply related

* [PATCH 04/18] powerpc: Allow taishan platform to boot a little endian kernel
From: Ian Munsie @ 2010-10-01  7:05 UTC (permalink / raw)
  To: linux-kernel, linuxppc-dev, benh
  Cc: Albert Herranz, paulus, Ian Munsie, Torez Smith
In-Reply-To: <1285916771-18033-1-git-send-email-imunsie@au1.ibm.com>

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain, Size: 6195 bytes --]

From: Ian Munsie <imunsie@au1.ibm.com>

This adds code to the boot wrapper to allow 44x CPUs to boot little
endian kernels. Presumably all 44x PowerPC platforms should also be able
to use this code unmodified, but this patch only wires up the taishan
platform as that has been tested.

The boot wrapper is still run in 32bit big endian mode and must set the
E bit in the TLB entries that the kernel may use initially. Naturally
the code setting this up can't afford to change the E bit on it's own
TLB entry while it is executing so it sets up a trampoline in address
space 1 to affect the change on all address space 0 TLB entries.

Signed-off-by: Ian Munsie <imunsie@au1.ibm.com>
---
 arch/powerpc/boot/4xx.h                |    2 +
 arch/powerpc/boot/Makefile             |    2 +-
 arch/powerpc/boot/cuboot-taishan.c     |    1 +
 arch/powerpc/boot/le-44x.S             |   85 ++++++++++++++++++++++++++++++++
 arch/powerpc/platforms/Kconfig.cputype |    1 +
 5 files changed, 90 insertions(+), 1 deletions(-)
 create mode 100644 arch/powerpc/boot/le-44x.S

diff --git a/arch/powerpc/boot/4xx.h b/arch/powerpc/boot/4xx.h
index 7dc5d45..05bc068 100644
--- a/arch/powerpc/boot/4xx.h
+++ b/arch/powerpc/boot/4xx.h
@@ -29,5 +29,7 @@ void ibm440gx_fixup_clocks(unsigned int sys_clk, unsigned int ser_clk,
 			   unsigned int tmr_clk);
 void ibm440spe_fixup_clocks(unsigned int sys_clk, unsigned int ser_clk,
 			    unsigned int tmr_clk);
+void ibm44x_le_kentry(unsigned long r3, unsigned long r4, void *r5,
+		      kernel_entry_t kentry);
 
 #endif /* _POWERPC_BOOT_4XX_H_ */
diff --git a/arch/powerpc/boot/Makefile b/arch/powerpc/boot/Makefile
index 79d7e69..c4b8616 100644
--- a/arch/powerpc/boot/Makefile
+++ b/arch/powerpc/boot/Makefile
@@ -69,7 +69,7 @@ src-wlib := string.S crt0.S crtsavres.S stdio.c main.c \
 		gunzip_util.c elf_util.c $(zlib) devtree.c oflib.c ofconsole.c \
 		4xx.c ebony.c mv64x60.c mpsc.c mv64x60_i2c.c cuboot.c bamboo.c \
 		cpm-serial.c stdlib.c mpc52xx-psc.c planetcore.c uartlite.c \
-		fsl-soc.c mpc8xx.c pq2.c ugecon.c
+		fsl-soc.c mpc8xx.c pq2.c ugecon.c le-44x.S
 src-plat := of.c cuboot-52xx.c cuboot-824x.c cuboot-83xx.c cuboot-85xx.c holly.c \
 		cuboot-ebony.c cuboot-hotfoot.c treeboot-ebony.c prpmc2800.c \
 		ps3-head.S ps3-hvcall.S ps3.c treeboot-bamboo.c cuboot-8xx.c \
diff --git a/arch/powerpc/boot/cuboot-taishan.c b/arch/powerpc/boot/cuboot-taishan.c
index 9bc906a..7fdd614 100644
--- a/arch/powerpc/boot/cuboot-taishan.c
+++ b/arch/powerpc/boot/cuboot-taishan.c
@@ -52,6 +52,7 @@ void platform_init(unsigned long r3, unsigned long r4, unsigned long r5,
 	CUBOOT_INIT();
 
 	platform_ops.fixups = taishan_fixups;
+	platform_ops.le_kentry = ibm44x_le_kentry;
 	fdt_init(_dtb_start);
 	serial_console_init();
 }
diff --git a/arch/powerpc/boot/le-44x.S b/arch/powerpc/boot/le-44x.S
new file mode 100644
index 0000000..c8d2ee4
--- /dev/null
+++ b/arch/powerpc/boot/le-44x.S
@@ -0,0 +1,85 @@
+/*
+ * Copyright © 2010 Ian Munsie, IBM Corporation
+ *
+ * Assembly to begin executing a little endian kernel from a big endian boot
+ * wrapper. Uses a trampoline in address space 1 to update the E bit in each
+ * TLB entry of address space 0 before entering the kernel.
+ */
+
+#include "ppc_asm.h"
+
+#define __MASK(X)		(1<<(X))
+
+#define SPRN_SRR0		0x01A		/* Save/Restore Register 0 */
+#define SPRN_SRR1		0x01B		/* Save/Restore Register 1 */
+
+#define MSR_IS			__MASK(5)	/* Instruction Space */
+#define MSR_DS			__MASK(4)	/* Data Space */
+
+#define	PPC44x_TLB_PAGEID	0
+#define	PPC44x_TLB_XLAT		1
+#define	PPC44x_TLB_ATTRIB	2
+
+/* Page identification fields */
+#define	PPC44x_TLB_VALID	0x00000200      /* Valid flag */
+#define PPC44x_TLB_TS		0x00000100	/* Translation address space */
+
+/* Storage attribute and access control fields */
+#define PPC44x_TLB_E		0x00000080      /* Memory is little endian */
+
+	.text
+	.global ibm44x_le_kentry
+ibm44x_le_kentry:
+	/* Find an invalid TLB entry we can use */
+	li	r12,0				/* Start searching at TLB 0 */
+1:	tlbre	r10,r12,PPC44x_TLB_PAGEID	/* Read TLB page ID word */
+	andi.	r0,r10,PPC44x_TLB_VALID		/* Test if TLB is valid */
+	beq	2f				/* If not valid we are free to use it */
+	addi	r12,r12,1			/* If valid, increment */
+	cmpwi	r12,64				/* Have we reached the end of the TLBs? */
+	bne	1b				/* If not, continue searching */
+	blr					/* If so, no invalid TLB entries found :( Shouldn't happen AFAIK */
+
+	/* Locate TLB entry containing trampoline */
+2:	lis	r0,le_trampoline@h
+	ori	r0,r0,le_trampoline@l
+	tlbsx	r11,0,r0
+
+	/* Set free TLB to match our TLB, but with TS=1 */
+	tlbre	r10,r11,PPC44x_TLB_XLAT
+	tlbwe	r10,r12,PPC44x_TLB_XLAT
+	tlbre	r10,r11,PPC44x_TLB_ATTRIB
+	tlbwe	r10,r12,PPC44x_TLB_ATTRIB
+	tlbre	r10,r11,PPC44x_TLB_PAGEID
+	ori	r10,r10,PPC44x_TLB_TS
+	tlbwe	r10,r12,PPC44x_TLB_PAGEID
+
+	/* Goto trampoline in address space 1 */
+	mtspr	SPRN_SRR0,r0
+	mfmsr	r0
+	ori	r0,r0,MSR_IS | MSR_DS
+	mtspr	SPRN_SRR1,r0
+	rfi
+
+le_trampoline:
+	/* Set E bit on all valid TLB entries with TS=0 */
+	li	r12,0				/* Start searching at TLB 0 */
+1:	tlbre	r10,r12,PPC44x_TLB_PAGEID	/* Read TLB page ID word */
+	andi.	r0,r10,PPC44x_TLB_VALID		/* Test if TLB is valid */
+	beq	2f				/* If not valid, continue */
+	andi.	r0,r10,PPC44x_TLB_TS		/* If valid, test if TLB is TS=1 */
+	bne	2f				/* If TS=1, continue */
+	tlbre	r10,r12,PPC44x_TLB_ATTRIB	/* If TS=0, read TLB attributes */
+	ori	r10,r10,PPC44x_TLB_E		/* Set little endian bit */
+	tlbwe	r10,r12,PPC44x_TLB_ATTRIB	/* Write attributes back */
+2:	addi	r12,r12,1			/* Increment */
+	cmpwi	r12,64				/* Are we done? */
+	bne	1b				/* If not, continue searching */
+
+	/* Goto kentry in address space 0 */
+	mtspr	SPRN_SRR0,r6			/* arg 4 (kentry) */
+	mfmsr	r11
+	li	r12,MSR_IS | MSR_DS
+	andc	r11,r11,r12
+	mtspr	SPRN_SRR1,r11
+	rfi
diff --git a/arch/powerpc/platforms/Kconfig.cputype b/arch/powerpc/platforms/Kconfig.cputype
index 074ff12..8ba962e 100644
--- a/arch/powerpc/platforms/Kconfig.cputype
+++ b/arch/powerpc/platforms/Kconfig.cputype
@@ -49,6 +49,7 @@ config 44x
 	select 4xx_SOC
 	select PPC_PCI_CHOICE
 	select PHYS_64BIT
+	select ARCH_SUPPORTS_LITTLE_ENDIAN
 
 config E200
 	bool "Freescale e200"
-- 
1.7.1

^ permalink raw reply related

* [PATCH 05/18] powerpc: Wire up 44x little endian boot for remaining 44x targets
From: Ian Munsie @ 2010-10-01  7:05 UTC (permalink / raw)
  To: linux-kernel, linuxppc-dev, benh; +Cc: paulus, Ian Munsie
In-Reply-To: <1285916771-18033-1-git-send-email-imunsie@au1.ibm.com>

From: Ian Munsie <imunsie@au1.ibm.com>

I haven't tested booting a little endian kernel on any of these targets,
but they all claim to be 44x so my little endian trampoline should work
on all of them, so wire it up on:

bamboo
katmai
kilauea
rainer
sam440ep
sequoia
warp
yosemite
ebony

Signed-off-by: Ian Munsie <imunsie@au1.ibm.com>
---
 arch/powerpc/boot/bamboo.c          |    1 +
 arch/powerpc/boot/cuboot-katmai.c   |    1 +
 arch/powerpc/boot/cuboot-kilauea.c  |    1 +
 arch/powerpc/boot/cuboot-rainier.c  |    1 +
 arch/powerpc/boot/cuboot-sam440ep.c |    1 +
 arch/powerpc/boot/cuboot-sequoia.c  |    1 +
 arch/powerpc/boot/cuboot-warp.c     |    1 +
 arch/powerpc/boot/cuboot-yosemite.c |    1 +
 arch/powerpc/boot/ebony.c           |    1 +
 9 files changed, 9 insertions(+), 0 deletions(-)

diff --git a/arch/powerpc/boot/bamboo.c b/arch/powerpc/boot/bamboo.c
index b82cacb..547494b 100644
--- a/arch/powerpc/boot/bamboo.c
+++ b/arch/powerpc/boot/bamboo.c
@@ -41,6 +41,7 @@ void bamboo_init(void *mac0, void *mac1)
 {
 	platform_ops.fixups = bamboo_fixups;
 	platform_ops.exit = ibm44x_dbcr_reset;
+	platform_ops.le_kentry = ibm44x_le_kentry;
 	bamboo_mac0 = mac0;
 	bamboo_mac1 = mac1;
 	fdt_init(_dtb_start);
diff --git a/arch/powerpc/boot/cuboot-katmai.c b/arch/powerpc/boot/cuboot-katmai.c
index 5434d70..c72bdbf 100644
--- a/arch/powerpc/boot/cuboot-katmai.c
+++ b/arch/powerpc/boot/cuboot-katmai.c
@@ -52,6 +52,7 @@ void platform_init(unsigned long r3, unsigned long r4, unsigned long r5,
 	CUBOOT_INIT();
 
 	platform_ops.fixups = katmai_fixups;
+	platform_ops.le_kentry = ibm44x_le_kentry;
 	fdt_init(_dtb_start);
 	serial_console_init();
 }
diff --git a/arch/powerpc/boot/cuboot-kilauea.c b/arch/powerpc/boot/cuboot-kilauea.c
index 80cdad6..115048a 100644
--- a/arch/powerpc/boot/cuboot-kilauea.c
+++ b/arch/powerpc/boot/cuboot-kilauea.c
@@ -44,6 +44,7 @@ void platform_init(unsigned long r3, unsigned long r4, unsigned long r5,
 	CUBOOT_INIT();
 	platform_ops.fixups = kilauea_fixups;
 	platform_ops.exit = ibm40x_dbcr_reset;
+	platform_ops.le_kentry = ibm44x_le_kentry;
 	fdt_init(_dtb_start);
 	serial_console_init();
 }
diff --git a/arch/powerpc/boot/cuboot-rainier.c b/arch/powerpc/boot/cuboot-rainier.c
index 0a3fdde..7c1ad02 100644
--- a/arch/powerpc/boot/cuboot-rainier.c
+++ b/arch/powerpc/boot/cuboot-rainier.c
@@ -52,6 +52,7 @@ void platform_init(unsigned long r3, unsigned long r4, unsigned long r5,
 	CUBOOT_INIT();
 	platform_ops.fixups = rainier_fixups;
 	platform_ops.exit = ibm44x_dbcr_reset;
+	platform_ops.le_kentry = ibm44x_le_kentry;
 	fdt_init(_dtb_start);
 	serial_console_init();
 }
diff --git a/arch/powerpc/boot/cuboot-sam440ep.c b/arch/powerpc/boot/cuboot-sam440ep.c
index ec10a47..60761fd 100644
--- a/arch/powerpc/boot/cuboot-sam440ep.c
+++ b/arch/powerpc/boot/cuboot-sam440ep.c
@@ -44,6 +44,7 @@ void platform_init(unsigned long r3, unsigned long r4, unsigned long r5,
 	CUBOOT_INIT();
 	platform_ops.fixups = sam440ep_fixups;
 	platform_ops.exit = ibm44x_dbcr_reset;
+	platform_ops.le_kentry = ibm44x_le_kentry;
 	fdt_init(_dtb_start);
 	serial_console_init();
 }
diff --git a/arch/powerpc/boot/cuboot-sequoia.c b/arch/powerpc/boot/cuboot-sequoia.c
index caf8f2e..3e93748 100644
--- a/arch/powerpc/boot/cuboot-sequoia.c
+++ b/arch/powerpc/boot/cuboot-sequoia.c
@@ -52,6 +52,7 @@ void platform_init(unsigned long r3, unsigned long r4, unsigned long r5,
 	CUBOOT_INIT();
 	platform_ops.fixups = sequoia_fixups;
 	platform_ops.exit = ibm44x_dbcr_reset;
+	platform_ops.le_kentry = ibm44x_le_kentry;
 	fdt_init(_dtb_start);
 	serial_console_init();
 }
diff --git a/arch/powerpc/boot/cuboot-warp.c b/arch/powerpc/boot/cuboot-warp.c
index 806df69..8bdc383 100644
--- a/arch/powerpc/boot/cuboot-warp.c
+++ b/arch/powerpc/boot/cuboot-warp.c
@@ -33,6 +33,7 @@ void platform_init(unsigned long r3, unsigned long r4, unsigned long r5,
 
 	platform_ops.fixups = warp_fixups;
 	platform_ops.exit = ibm44x_dbcr_reset;
+	platform_ops.le_kentry = ibm44x_le_kentry;
 	fdt_init(_dtb_start);
 	serial_console_init();
 }
diff --git a/arch/powerpc/boot/cuboot-yosemite.c b/arch/powerpc/boot/cuboot-yosemite.c
index cc6e338..7fe0e05 100644
--- a/arch/powerpc/boot/cuboot-yosemite.c
+++ b/arch/powerpc/boot/cuboot-yosemite.c
@@ -39,6 +39,7 @@ void platform_init(unsigned long r3, unsigned long r4, unsigned long r5,
 	CUBOOT_INIT();
 	platform_ops.fixups = yosemite_fixups;
 	platform_ops.exit = ibm44x_dbcr_reset;
+	platform_ops.le_kentry = ibm44x_le_kentry;
 	fdt_init(_dtb_start);
 	serial_console_init();
 }
diff --git a/arch/powerpc/boot/ebony.c b/arch/powerpc/boot/ebony.c
index 5532ab3..6f302aa 100644
--- a/arch/powerpc/boot/ebony.c
+++ b/arch/powerpc/boot/ebony.c
@@ -85,6 +85,7 @@ void ebony_init(void *mac0, void *mac1)
 {
 	platform_ops.fixups = ebony_fixups;
 	platform_ops.exit = ibm44x_dbcr_reset;
+	platform_ops.le_kentry = ibm44x_le_kentry;
 	ebony_mac0 = mac0;
 	ebony_mac1 = mac1;
 	fdt_init(_dtb_start);
-- 
1.7.1

^ permalink raw reply related

* [PATCH 07/18] powerpc: Use generic bitops for little endian bitmap operations
From: Ian Munsie @ 2010-10-01  7:06 UTC (permalink / raw)
  To: linux-kernel, linuxppc-dev, benh; +Cc: paulus, Ian Munsie, Anton Blanchard
In-Reply-To: <1285916771-18033-1-git-send-email-imunsie@au1.ibm.com>

From: Ian Munsie <imunsie@au1.ibm.com>

With the little endian PowerPC Linux port, the CPU could be in either
mode. Rather than byte swapping and mapping the little endian bitmap
operations ourselves on the assumption that we are big endian, map them
to the generic macros that will automatically byte swap the bit number
only if we actually are big endian.

The generic bitops le.h already defines the macros/prototypes for the
generic_find_next_[zero_]le_bit appropriately for the current endian so
we can drop those prototypes altogether.

Signed-off-by: Ian Munsie <imunsie@au1.ibm.com>
---
 arch/powerpc/include/asm/bitops.h |   30 ++++++++++++------------------
 1 files changed, 12 insertions(+), 18 deletions(-)

diff --git a/arch/powerpc/include/asm/bitops.h b/arch/powerpc/include/asm/bitops.h
index 30964ae..066b2df 100644
--- a/arch/powerpc/include/asm/bitops.h
+++ b/arch/powerpc/include/asm/bitops.h
@@ -54,7 +54,6 @@
 
 #define BITOP_MASK(nr)		(1UL << ((nr) % BITS_PER_LONG))
 #define BITOP_WORD(nr)		((nr) / BITS_PER_LONG)
-#define BITOP_LE_SWIZZLE	((BITS_PER_LONG-1) & ~0x7)
 
 /* Macro for generating the ***_bits() functions */
 #define DEFINE_BITOP(fn, op, prefix, postfix)	\
@@ -272,34 +271,29 @@ static __inline__ int fls64(__u64 x)
 
 /* Little-endian versions */
 
-static __inline__ int test_le_bit(unsigned long nr,
-				  __const__ unsigned long *addr)
-{
-	__const__ unsigned char	*tmp = (__const__ unsigned char *) addr;
-	return (tmp[nr >> 3] >> (nr & 7)) & 1;
-}
+#include <asm-generic/bitops/le.h>
+
+#define test_le_bit(nr, addr) \
+	generic_test_le_bit(nr, addr)
 
 #define __set_le_bit(nr, addr) \
-	__set_bit((nr) ^ BITOP_LE_SWIZZLE, (addr))
+	generic__set_le_bit(nr, addr)
 #define __clear_le_bit(nr, addr) \
-	__clear_bit((nr) ^ BITOP_LE_SWIZZLE, (addr))
+	generic__clear_le_bit(nr, addr)
 
 #define test_and_set_le_bit(nr, addr) \
-	test_and_set_bit((nr) ^ BITOP_LE_SWIZZLE, (addr))
+	generic_test_and_set_le_bit(nr, addr)
 #define test_and_clear_le_bit(nr, addr) \
-	test_and_clear_bit((nr) ^ BITOP_LE_SWIZZLE, (addr))
+	generic_test_and_clear_le_bit(nr, addr)
 
 #define __test_and_set_le_bit(nr, addr) \
-	__test_and_set_bit((nr) ^ BITOP_LE_SWIZZLE, (addr))
+	generic__test_and_set_le_bit(nr, addr)
 #define __test_and_clear_le_bit(nr, addr) \
-	__test_and_clear_bit((nr) ^ BITOP_LE_SWIZZLE, (addr))
+	generic__test_and_clear_le_bit(nr, addr)
 
-#define find_first_zero_le_bit(addr, size) generic_find_next_zero_le_bit((addr), (size), 0)
-unsigned long generic_find_next_zero_le_bit(const unsigned long *addr,
-				    unsigned long size, unsigned long offset);
+#define find_first_zero_le_bit(addr, size) \
+	generic_find_first_zero_le_bit(addr, size)
 
-unsigned long generic_find_next_le_bit(const unsigned long *addr,
-				    unsigned long size, unsigned long offset);
 /* Bitmap functions for the ext2 filesystem */
 
 #define ext2_set_bit(nr,addr) \
-- 
1.7.1

^ permalink raw reply related

* [PATCH 06/18] powerpc 44x: Set E bit in TLBs and PTEs when CPU is in little endian mode
From: Ian Munsie @ 2010-10-01  7:05 UTC (permalink / raw)
  To: linux-kernel, linuxppc-dev, benh
  Cc: Stephen Rothwell, Dave Kleikamp, Ian Munsie, paulus, Torez Smith
In-Reply-To: <1285916771-18033-1-git-send-email-imunsie@au1.ibm.com>

From: Ian Munsie <imunsie@au1.ibm.com>

The endianness on the 44x CPUs is controlled by the E bit in the TLB
entries. If the kernel has been compiled for little endian this patch
sets this E bit wherever they are set - in the PTE base flags, the early
debugging, and TLB pinning.

It defines some _PAGE_CPUENDIAN and ..._TLB_CPUE macros which are set
if the CPU is little endian or 0 if the CPU is big endian so these can
be used to easily set the E bit only if the CPU is little endian.

Signed-off-by: Ian Munsie <imunsie@au1.ibm.com>
---
 arch/powerpc/include/asm/mmu-44x.h    |   12 ++++++++++++
 arch/powerpc/include/asm/pte-44x.h    |    4 ++++
 arch/powerpc/include/asm/pte-common.h |    9 ++++++---
 arch/powerpc/kernel/head_44x.S        |    6 +++---
 arch/powerpc/mm/44x_mmu.c             |    4 ++--
 5 files changed, 27 insertions(+), 8 deletions(-)

diff --git a/arch/powerpc/include/asm/mmu-44x.h b/arch/powerpc/include/asm/mmu-44x.h
index bf52d70..e96f5c3 100644
--- a/arch/powerpc/include/asm/mmu-44x.h
+++ b/arch/powerpc/include/asm/mmu-44x.h
@@ -42,6 +42,12 @@
 #define PPC44x_TLB_G		0x00000100      /* Memory is guarded */
 #define PPC44x_TLB_E		0x00000080      /* Memory is little endian */
 
+#ifdef __LITTLE_ENDIAN__
+#define PPC44x_TLB_CPUE		PPC44x_TLB_E
+#else
+#define PPC44x_TLB_CPUE		0
+#endif
+
 #define PPC44x_TLB_PERM_MASK	0x0000003f
 #define PPC44x_TLB_UX		0x00000020      /* User execution */
 #define PPC44x_TLB_UW		0x00000010      /* User write */
@@ -99,6 +105,12 @@
 #define PPC47x_TLB2_S_RW	(PPC47x_TLB2_SW | PPC47x_TLB2_SR)
 #define PPC47x_TLB2_IMG		(PPC47x_TLB2_I | PPC47x_TLB2_M | PPC47x_TLB2_G)
 
+#ifdef __LITTLE_ENDIAN__
+#define PPC47x_TLB2_CPUE	PPC47x_TLB2_E
+#else
+#define PPC47x_TLB2_CPUE	0
+#endif
+
 #ifndef __ASSEMBLY__
 
 extern unsigned int tlb_44x_hwater;
diff --git a/arch/powerpc/include/asm/pte-44x.h b/arch/powerpc/include/asm/pte-44x.h
index 4192b9b..9c79a85 100644
--- a/arch/powerpc/include/asm/pte-44x.h
+++ b/arch/powerpc/include/asm/pte-44x.h
@@ -89,6 +89,10 @@
 #define _PAGE_NO_CACHE	0x00000400		/* H: I bit */
 #define _PAGE_WRITETHRU	0x00000800		/* H: W bit */
 
+#ifdef __LITTLE_ENDIAN__
+#define _PAGE_CPUENDIAN	_PAGE_ENDIAN
+#endif
+
 /* TODO: Add large page lowmem mapping support */
 #define _PMD_PRESENT	0
 #define _PMD_PRESENT_MASK (PAGE_MASK)
diff --git a/arch/powerpc/include/asm/pte-common.h b/arch/powerpc/include/asm/pte-common.h
index f2b3701..b69609c 100644
--- a/arch/powerpc/include/asm/pte-common.h
+++ b/arch/powerpc/include/asm/pte-common.h
@@ -19,6 +19,9 @@
 #ifndef _PAGE_ENDIAN
 #define _PAGE_ENDIAN	0
 #endif
+#ifndef _PAGE_CPUENDIAN
+#define _PAGE_CPUENDIAN	0
+#endif
 #ifndef _PAGE_COHERENT
 #define _PAGE_COHERENT	0
 #endif
@@ -104,11 +107,11 @@ extern unsigned long bad_call_to_PMD_PAGE_SIZE(void);
  * pages. We always set _PAGE_COHERENT when SMP is enabled or
  * the processor might need it for DMA coherency.
  */
-#define _PAGE_BASE_NC	(_PAGE_PRESENT | _PAGE_ACCESSED | _PAGE_PSIZE)
+#define _PAGE_BASE_NC	(_PAGE_PRESENT | _PAGE_ACCESSED | _PAGE_PSIZE | _PAGE_CPUENDIAN)
 #if defined(CONFIG_SMP) || defined(CONFIG_PPC_STD_MMU)
-#define _PAGE_BASE	(_PAGE_BASE_NC | _PAGE_COHERENT)
+#define _PAGE_BASE	(_PAGE_BASE_NC | _PAGE_COHERENT | _PAGE_CPUENDIAN)
 #else
-#define _PAGE_BASE	(_PAGE_BASE_NC)
+#define _PAGE_BASE	(_PAGE_BASE_NC | _PAGE_CPUENDIAN)
 #endif
 
 /* Permission masks used to generate the __P and __S table,
diff --git a/arch/powerpc/kernel/head_44x.S b/arch/powerpc/kernel/head_44x.S
index 562305b..6198733 100644
--- a/arch/powerpc/kernel/head_44x.S
+++ b/arch/powerpc/kernel/head_44x.S
@@ -813,7 +813,7 @@ skpinv:	addi	r4,r4,1				/* Increment */
 	/* attrib fields */
 	/* Added guarded bit to protect against speculative loads/stores */
 	li	r5,0
-	ori	r5,r5,(PPC44x_TLB_SW | PPC44x_TLB_SR | PPC44x_TLB_SX | PPC44x_TLB_G)
+	ori	r5,r5,(PPC44x_TLB_SW | PPC44x_TLB_SR | PPC44x_TLB_SX | PPC44x_TLB_G | PPC44x_TLB_CPUE)
 
         li      r0,63                    /* TLB slot 63 */
 
@@ -850,7 +850,7 @@ skpinv:	addi	r4,r4,1				/* Increment */
 	ori	r4,r4,CONFIG_PPC_EARLY_DEBUG_44x_PHYSHIGH
 
 	/* attrib fields */
-	li	r5,(PPC44x_TLB_SW|PPC44x_TLB_SR|PPC44x_TLB_I|PPC44x_TLB_G)
+	li	r5,(PPC44x_TLB_SW|PPC44x_TLB_SR|PPC44x_TLB_I|PPC44x_TLB_G|PPC44x_TLB_CPUE)
         li      r0,62                    /* TLB slot 0 */
 
 	tlbwe	r3,r0,PPC44x_TLB_PAGEID
@@ -1068,7 +1068,7 @@ clear_utlb_entry:
 	ori	r4,r4,CONFIG_PPC_EARLY_DEBUG_44x_PHYSHIGH
 
 	/* Word 2 */
-	li	r5,(PPC47x_TLB2_S_RW | PPC47x_TLB2_IMG)
+	li	r5,(PPC47x_TLB2_S_RW | PPC47x_TLB2_IMG | PPC47x_TLB2_CPUE)
 
 	/* Bolted in way 0, bolt slot 5, we -hope- we don't hit the same
 	 * congruence class as the kernel, we need to make sure of it at
diff --git a/arch/powerpc/mm/44x_mmu.c b/arch/powerpc/mm/44x_mmu.c
index d8c6efb..70760e7 100644
--- a/arch/powerpc/mm/44x_mmu.c
+++ b/arch/powerpc/mm/44x_mmu.c
@@ -79,7 +79,7 @@ static void __init ppc44x_pin_tlb(unsigned int virt, unsigned int phys)
 #ifdef CONFIG_PPC47x
 	: "r" (PPC47x_TLB2_S_RWX),
 #else
-	: "r" (PPC44x_TLB_SW | PPC44x_TLB_SR | PPC44x_TLB_SX | PPC44x_TLB_G),
+	: "r" (PPC44x_TLB_SW | PPC44x_TLB_SR | PPC44x_TLB_SX | PPC44x_TLB_G | PPC44x_TLB_CPUE),
 #endif
 	  "r" (phys),
 	  "r" (virt | PPC44x_TLB_VALID | PPC44x_TLB_256M),
@@ -163,7 +163,7 @@ static void __cpuinit ppc47x_pin_tlb(unsigned int virt, unsigned int phys)
 		"tlbwe	%0,%3,2\n"
 		:
 		: "r" (PPC47x_TLB2_SW | PPC47x_TLB2_SR |
-		       PPC47x_TLB2_SX
+		       PPC47x_TLB2_SX |  PPC47x_TLB2_CPUE
 #ifdef CONFIG_SMP
 		       | PPC47x_TLB2_M
 #endif
-- 
1.7.1

^ permalink raw reply related

* [PATCH 08/18] powerpc: Include the appropriate endianness header
From: Ian Munsie @ 2010-10-01  7:06 UTC (permalink / raw)
  To: linux-kernel, linuxppc-dev, benh; +Cc: paulus, Ian Munsie
In-Reply-To: <1285916771-18033-1-git-send-email-imunsie@au1.ibm.com>

From: Ian Munsie <imunsie@au1.ibm.com>

This patch will have powerpc include the appropriate generic endianness
header depending on what the compiler reports.

Signed-off-by: Ian Munsie <imunsie@au1.ibm.com>
---
 arch/powerpc/include/asm/byteorder.h |    4 ++++
 1 files changed, 4 insertions(+), 0 deletions(-)

diff --git a/arch/powerpc/include/asm/byteorder.h b/arch/powerpc/include/asm/byteorder.h
index aa6cc4f..ca931d0 100644
--- a/arch/powerpc/include/asm/byteorder.h
+++ b/arch/powerpc/include/asm/byteorder.h
@@ -7,6 +7,10 @@
  * as published by the Free Software Foundation; either version
  * 2 of the License, or (at your option) any later version.
  */
+#ifdef __LITTLE_ENDIAN__
+#include <linux/byteorder/little_endian.h>
+#else
 #include <linux/byteorder/big_endian.h>
+#endif
 
 #endif /* _ASM_POWERPC_BYTEORDER_H */
-- 
1.7.1

^ permalink raw reply related

* [PATCH 10/18] powerpc: Support endian agnostic MMIO
From: Ian Munsie @ 2010-10-01  7:06 UTC (permalink / raw)
  To: linux-kernel, linuxppc-dev, benh; +Cc: paulus, Ian Munsie
In-Reply-To: <1285916771-18033-1-git-send-email-imunsie@au1.ibm.com>

From: Ian Munsie <imunsie@au1.ibm.com>

This patch maps the MMIO functions for 32bit PowerPC to their
appropriate instructions depending on CPU endianness.

The macros used to create the corresponding inline functions are also
renamed by this patch. Previously they had BE or LE in their names which
was misleading - they had nothing to do with endianness, but actually
created different instruction forms so their new names reflect the
instruction form they are creating (D-Form and X-Form).

Little endian 64bit PowerPC is not supported, so the lack of mappings
(and corresponding breakage) for that case is intentional to bring the
attention of anyone doing a 64bit little endian port. 64bit big endian
is unaffected.

Signed-off-by: Ian Munsie <imunsie@au1.ibm.com>
---
 arch/powerpc/include/asm/io.h |   51 ++++++++++++++++++++++++++--------------
 1 files changed, 33 insertions(+), 18 deletions(-)

diff --git a/arch/powerpc/include/asm/io.h b/arch/powerpc/include/asm/io.h
index 001f2f1..fd8e922 100644
--- a/arch/powerpc/include/asm/io.h
+++ b/arch/powerpc/include/asm/io.h
@@ -97,7 +97,7 @@ extern resource_size_t isa_mem_base;
 
 /* gcc 4.0 and older doesn't have 'Z' constraint */
 #if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ == 0)
-#define DEF_MMIO_IN_LE(name, size, insn)				\
+#define DEF_MMIO_IN_X(name, size, insn)				\
 static inline u##size name(const volatile u##size __iomem *addr)	\
 {									\
 	u##size ret;							\
@@ -106,7 +106,7 @@ static inline u##size name(const volatile u##size __iomem *addr)	\
 	return ret;							\
 }
 
-#define DEF_MMIO_OUT_LE(name, size, insn) 				\
+#define DEF_MMIO_OUT_X(name, size, insn) 				\
 static inline void name(volatile u##size __iomem *addr, u##size val)	\
 {									\
 	__asm__ __volatile__("sync;"#insn" %1,0,%2"			\
@@ -114,7 +114,7 @@ static inline void name(volatile u##size __iomem *addr, u##size val)	\
 	IO_SET_SYNC_FLAG();						\
 }
 #else /* newer gcc */
-#define DEF_MMIO_IN_LE(name, size, insn)				\
+#define DEF_MMIO_IN_X(name, size, insn)				\
 static inline u##size name(const volatile u##size __iomem *addr)	\
 {									\
 	u##size ret;							\
@@ -123,7 +123,7 @@ static inline u##size name(const volatile u##size __iomem *addr)	\
 	return ret;							\
 }
 
-#define DEF_MMIO_OUT_LE(name, size, insn) 				\
+#define DEF_MMIO_OUT_X(name, size, insn) 				\
 static inline void name(volatile u##size __iomem *addr, u##size val)	\
 {									\
 	__asm__ __volatile__("sync;"#insn" %1,%y0"			\
@@ -132,7 +132,7 @@ static inline void name(volatile u##size __iomem *addr, u##size val)	\
 }
 #endif
 
-#define DEF_MMIO_IN_BE(name, size, insn)				\
+#define DEF_MMIO_IN_D(name, size, insn)				\
 static inline u##size name(const volatile u##size __iomem *addr)	\
 {									\
 	u##size ret;							\
@@ -141,7 +141,7 @@ static inline u##size name(const volatile u##size __iomem *addr)	\
 	return ret;							\
 }
 
-#define DEF_MMIO_OUT_BE(name, size, insn)				\
+#define DEF_MMIO_OUT_D(name, size, insn)				\
 static inline void name(volatile u##size __iomem *addr, u##size val)	\
 {									\
 	__asm__ __volatile__("sync;"#insn"%U0%X0 %1,%0"			\
@@ -149,22 +149,37 @@ static inline void name(volatile u##size __iomem *addr, u##size val)	\
 	IO_SET_SYNC_FLAG();						\
 }
 
+DEF_MMIO_IN_D(in_8,     8, lbz);
+DEF_MMIO_OUT_D(out_8,   8, stb);
 
-DEF_MMIO_IN_BE(in_8,     8, lbz);
-DEF_MMIO_IN_BE(in_be16, 16, lhz);
-DEF_MMIO_IN_BE(in_be32, 32, lwz);
-DEF_MMIO_IN_LE(in_le16, 16, lhbrx);
-DEF_MMIO_IN_LE(in_le32, 32, lwbrx);
+#ifdef __BIG_ENDIAN__
+DEF_MMIO_IN_D(in_be16, 16, lhz);
+DEF_MMIO_IN_D(in_be32, 32, lwz);
+DEF_MMIO_IN_X(in_le16, 16, lhbrx);
+DEF_MMIO_IN_X(in_le32, 32, lwbrx);
 
-DEF_MMIO_OUT_BE(out_8,     8, stb);
-DEF_MMIO_OUT_BE(out_be16, 16, sth);
-DEF_MMIO_OUT_BE(out_be32, 32, stw);
-DEF_MMIO_OUT_LE(out_le16, 16, sthbrx);
-DEF_MMIO_OUT_LE(out_le32, 32, stwbrx);
+DEF_MMIO_OUT_D(out_be16, 16, sth);
+DEF_MMIO_OUT_D(out_be32, 32, stw);
+DEF_MMIO_OUT_X(out_le16, 16, sthbrx);
+DEF_MMIO_OUT_X(out_le32, 32, stwbrx);
+#else
+DEF_MMIO_IN_X(in_be16, 16, lhbrx);
+DEF_MMIO_IN_X(in_be32, 32, lwbrx);
+DEF_MMIO_IN_D(in_le16, 16, lhz);
+DEF_MMIO_IN_D(in_le32, 32, lwz);
+
+DEF_MMIO_OUT_X(out_be16, 16, sthbrx);
+DEF_MMIO_OUT_X(out_be32, 32, stwbrx);
+DEF_MMIO_OUT_D(out_le16, 16, sth);
+DEF_MMIO_OUT_D(out_le32, 32, stw);
+#endif /* __BIG_ENDIAN */
 
 #ifdef __powerpc64__
-DEF_MMIO_OUT_BE(out_be64, 64, std);
-DEF_MMIO_IN_BE(in_be64, 64, ld);
+
+#ifndef __LITTLE_ENDIAN__
+DEF_MMIO_OUT_D(out_be64, 64, std);
+DEF_MMIO_IN_D(in_be64, 64, ld);
+#endif
 
 /* There is no asm instructions for 64 bits reverse loads and stores */
 static inline u64 in_le64(const volatile u64 __iomem *addr)
-- 
1.7.1

^ permalink raw reply related


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