* [PATCH v2 2/2] Register bootmem pages
From: Nathan Fontenot @ 2013-08-20 2:53 UTC (permalink / raw)
To: linuxppc-dev
In-Reply-To: <5212D989.8090103@linux.vnet.ibm.com>
Previous commit 46723bfa540... introduced a new config option
HAVE_BOOTMEM_INFO_NODE that ended up breaking memory hot-remove for ppc
when sparse vmemmap is not defined.
This patch defines HAVE_BOOTMEM_INFO_NODE for ppc and adds the call to
register_page_bootmem_info_node. Without this we get a BUG_ON for memory
hot remove in put_page_bootmem().
This also adds a stub for register_page_bootmem_memmap to allow ppc to build
with sparse vmemmap defined.
Signed-off-by: Nathan Fontenot <nfont@linux.vnet.ibm.com>
---
---
arch/powerpc/mm/init_64.c | 4 ++++
arch/powerpc/mm/mem.c | 9 +++++++++
mm/Kconfig | 2 +-
3 files changed, 14 insertions(+), 1 deletion(-)
Index: linux/arch/powerpc/mm/init_64.c
===================================================================
--- linux.orig/arch/powerpc/mm/init_64.c
+++ linux/arch/powerpc/mm/init_64.c
@@ -300,5 +300,9 @@ void vmemmap_free(unsigned long start, u
{
}
+void register_page_bootmem_memmap(unsigned long section_nr,
+ struct page *start_page, unsigned long size)
+{
+}
#endif /* CONFIG_SPARSEMEM_VMEMMAP */
Index: linux/arch/powerpc/mm/mem.c
===================================================================
--- linux.orig/arch/powerpc/mm/mem.c
+++ linux/arch/powerpc/mm/mem.c
@@ -297,12 +297,21 @@ void __init paging_init(void)
}
#endif /* ! CONFIG_NEED_MULTIPLE_NODES */
+static void __init register_page_bootmem_info(void)
+{
+ int i;
+
+ for_each_online_node(i)
+ register_page_bootmem_info_node(NODE_DATA(i));
+}
+
void __init mem_init(void)
{
#ifdef CONFIG_SWIOTLB
swiotlb_init(0);
#endif
+ register_page_bootmem_info();
high_memory = (void *) __va(max_low_pfn * PAGE_SIZE);
set_max_mapnr(max_pfn);
free_all_bootmem();
Index: linux/mm/Kconfig
===================================================================
--- linux.orig/mm/Kconfig
+++ linux/mm/Kconfig
@@ -183,7 +183,7 @@ config MEMORY_HOTPLUG_SPARSE
config MEMORY_HOTREMOVE
bool "Allow for memory hot remove"
select MEMORY_ISOLATION
- select HAVE_BOOTMEM_INFO_NODE if X86_64
+ select HAVE_BOOTMEM_INFO_NODE if (X86_64 || PPC64)
depends on MEMORY_HOTPLUG && ARCH_ENABLE_MEMORY_HOTREMOVE
depends on MIGRATION
^ permalink raw reply
* Re: [RFC PATCH 1/1] powerpc/embedded6xx: Add support for Motorola/Emerson MVME5100.
From: Stephen N Chivers @ 2013-08-20 4:05 UTC (permalink / raw)
To: Scott Wood; +Cc: Chris Proctor, linuxppc-dev, paulus, Stephen N Chivers
In-Reply-To: <1376959471.31636.395.camel@snotra.buserror.net>
Scott Wood <scottwood@freescale.com> wrote on 08/20/2013 10:44:31 AM:
> From: Scott Wood <scottwood@freescale.com>
> To: Stephen N Chivers <schivers@csc.com.au>
> Cc: Chris Proctor <cproctor@csc.com.au>, Kumar Gala
> <galak@kernel.crashing.org>, <linuxppc-dev@lists.ozlabs.org>,
> <paulus@samba.org>
> Date: 08/20/2013 10:44 AM
> Subject: Re: [RFC PATCH 1/1] powerpc/embedded6xx: Add support for
> Motorola/Emerson MVME5100.
>
> On Mon, 2013-08-19 at 07:58 +1100, Stephen N Chivers wrote:
> > The serial console setup in 'legacy_serial' does not support reg-shift
> > or reg-offset properties and so a preferred_console is not added by
it.
> > The DTS file for the board does specify the register shift so that
> > 'of_serial' will correctly setup the UARTS. But that is too late, the
> > preferred console will be tty0 as the .config derived from
> > ppc6xx_defconfig
> > has CONF_VT_CONSOLE set. In the test kernels I have built 'con_init'
in
> > the VT support is always called before serial8250_console_init.
>
> Are you setting console=ttyS0,<baud> on the kernel command line?
>
Yes, most certainly.
During the testing was specifying the command line in the dts
as doing so in the .config would be wrong.
When booting got:
Linux/PowerPC Load: console=ttyS0,9600 ip=dhcp root=/dev/nfs
So no problem there.
> > I did try modifying legacy_serial to correctly support the tsi-bridge
> > UARTS and add the preferred console but encountered even more problems
> > in 8250_core when registering the serial console (to the point of
> > having a silently panic'ing kernel).
> >
> > So I think for the moment, the board will need its own default config.
>
> Which config symbol is relevant here?
>
On reflection, maybe it wasn't panicing as in the configuration I was
using the boot times were quite protracted, something to do with the
network setup and e100 PHY initialisation.
(Embedding the e100 firmware in the kernel appears to fix that).
But at the point at which I gave up chasing the problem, the machine
was not showing any sign of life within what would be regarded as
"normal" times.
As to config symbol I don't know.
This may be the first attempt to
combine PPC6xx configurations with their embedded brethren.
What is certain that there is an incompatibility between what is
required for the "quirks" of the MVME-5100 onboard UARTS and the
configuration required for the non-embedded PPC6xx platforms.
> -Scott
>
>
>
^ permalink raw reply
* [PATCH v9 0/2] Add freescale S/PDIF CPU DAI and machine drivers
From: Nicolin Chen @ 2013-08-20 4:32 UTC (permalink / raw)
To: broonie, s.hauer
Cc: mark.rutland, devicetree, alsa-devel, lars, swarren, festevam,
timur, rob.herring, tomasz.figa, p.zabel, R65777, shawn.guo,
linuxppc-dev
Changelog:
v8->v9:
* Use bool instead of atomic_t.
* Use clk_prepare_enable() instead.
* Dropped dumpregs().
* Dropped unnecessary clk_round_rate().
* Dropped unused clock source enum.
* Revised dev_err() message.
v7->v8:
* Revised DT binding doc.
* Revised dev_err() error msg.
* Check return value of clk_set_rate().
v6->v7:
* Removed extra comma in array.
* Revised some comments.
* Added some dev_err().
* Check the return value of soft reset.
* Use bitrev8() instead of extra reverse_bits().
* Use cache_bypass as default for regmap.
v5->v6:
* Sorted out rxtx clk source in DT binding.
* Use devm_xxxx() functions.
* Use platform_get_resource() instead.
v4->v5:
* Dropped rx/tx-clksrc-names DT bindings.
* Use standard clock binding instead to pass the clock source list.
* Update the compatible list by using "imx35", the first SoC that has spdif.
v3->v4:
* Use regmap for CPU DAI driver.
* Use individual clock source for 32KHz, 44KHz, 48KHz playback.
* Determine clock source configuration from 'clocks' entry.
* Added imx53 to compatible list, merged imx6q and imx6dl in the list.
* Improve the algorism of reverse_bits().
* Dropped the unneeded clk_put().
v2->v3:
* Removed a wrong tag from the commit of patch-1.
v1->v2:
* Dropped one applied patch for spdif dummy codec drivers.
* Use generic DMA DT binding.
* Let spdif controller driver calculate the clock div.
* Added one optional clock source for spdif tx.
* Reivsed documentation accordingly.
Nicolin Chen (2):
ASoC: fsl: Add S/PDIF CPU DAI driver
ASoC: fsl: Add S/PDIF machine driver
.../devicetree/bindings/sound/fsl,spdif.txt | 54 +
.../devicetree/bindings/sound/imx-audio-spdif.txt | 29 +
sound/soc/fsl/Kconfig | 14 +
sound/soc/fsl/Makefile | 4 +
sound/soc/fsl/fsl_spdif.c | 1236 ++++++++++++++++++++
sound/soc/fsl/fsl_spdif.h | 191 +++
sound/soc/fsl/imx-spdif.c | 134 +++
7 files changed, 1662 insertions(+), 0 deletions(-)
create mode 100644 Documentation/devicetree/bindings/sound/fsl,spdif.txt
create mode 100644 Documentation/devicetree/bindings/sound/imx-audio-spdif.txt
create mode 100644 sound/soc/fsl/fsl_spdif.c
create mode 100644 sound/soc/fsl/fsl_spdif.h
create mode 100644 sound/soc/fsl/imx-spdif.c
^ permalink raw reply
* [PATCH v9 1/2] ASoC: fsl: Add S/PDIF CPU DAI driver
From: Nicolin Chen @ 2013-08-20 4:32 UTC (permalink / raw)
To: broonie, s.hauer
Cc: mark.rutland, devicetree, alsa-devel, lars, swarren, festevam,
timur, rob.herring, tomasz.figa, p.zabel, R65777, shawn.guo,
linuxppc-dev
In-Reply-To: <cover.1376972170.git.b42378@freescale.com>
This patch implements a device-tree-only CPU DAI driver for Freescale
S/PDIF controller that supports stereo playback and record feature.
Signed-off-by: Nicolin Chen <b42378@freescale.com>
---
.../devicetree/bindings/sound/fsl,spdif.txt | 54 +
sound/soc/fsl/Kconfig | 3 +
sound/soc/fsl/Makefile | 2 +
sound/soc/fsl/fsl_spdif.c | 1236 ++++++++++++++++++++
sound/soc/fsl/fsl_spdif.h | 191 +++
5 files changed, 1486 insertions(+), 0 deletions(-)
create mode 100644 Documentation/devicetree/bindings/sound/fsl,spdif.txt
create mode 100644 sound/soc/fsl/fsl_spdif.c
create mode 100644 sound/soc/fsl/fsl_spdif.h
diff --git a/Documentation/devicetree/bindings/sound/fsl,spdif.txt b/Documentation/devicetree/bindings/sound/fsl,spdif.txt
new file mode 100644
index 0000000..f2ae335
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/fsl,spdif.txt
@@ -0,0 +1,54 @@
+Freescale Sony/Philips Digital Interface Format (S/PDIF) Controller
+
+The Freescale S/PDIF audio block is a stereo transceiver that allows the
+processor to receive and transmit digital audio via an coaxial cable or
+a fibre cable.
+
+Required properties:
+
+ - compatible : Compatible list, must contain "fsl,imx35-spdif".
+
+ - reg : Offset and length of the register set for the device.
+
+ - interrupts : Contains the spdif interrupt.
+
+ - dmas : Generic dma devicetree binding as described in
+ Documentation/devicetree/bindings/dma/dma.txt.
+
+ - dma-names : Two dmas have to be defined, "tx" and "rx".
+
+ - clocks : Contains an entry for each entry in clock-names.
+
+ - clock-names : Includes the following entries:
+ "core" The core clock of spdif controller
+ "rxtx<0-7>" Clock source list for tx and rx clock.
+ This clock list should be identical to
+ the source list connecting to the spdif
+ clock mux in "SPDIF Transceiver Clock
+ Diagram" of SoC reference manual. It
+ can also be referred to TxClk_Source
+ bit of register SPDIF_STC.
+
+Example:
+
+spdif: spdif@02004000 {
+ compatible = "fsl,imx35-spdif";
+ reg = <0x02004000 0x4000>;
+ interrupts = <0 52 0x04>;
+ dmas = <&sdma 14 18 0>,
+ <&sdma 15 18 0>;
+ dma-names = "rx", "tx";
+
+ clocks = <&clks 197>, <&clks 3>,
+ <&clks 197>, <&clks 107>,
+ <&clks 0>, <&clks 118>,
+ <&clks 62>, <&clks 139>,
+ <&clks 0>;
+ clock-names = "core", "rxtx0",
+ "rxtx1", "rxtx2",
+ "rxtx3", "rxtx4",
+ "rxtx5", "rxtx6",
+ "rxtx7";
+
+ status = "okay";
+};
diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig
index 3a4808d..cd088cc 100644
--- a/sound/soc/fsl/Kconfig
+++ b/sound/soc/fsl/Kconfig
@@ -1,6 +1,9 @@
config SND_SOC_FSL_SSI
tristate
+config SND_SOC_FSL_SPDIF
+ tristate
+
config SND_SOC_FSL_UTILS
tristate
diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile
index d4b4aa8..4b5970e 100644
--- a/sound/soc/fsl/Makefile
+++ b/sound/soc/fsl/Makefile
@@ -12,9 +12,11 @@ obj-$(CONFIG_SND_SOC_P1022_RDK) += snd-soc-p1022-rdk.o
# Freescale PowerPC SSI/DMA Platform Support
snd-soc-fsl-ssi-objs := fsl_ssi.o
+snd-soc-fsl-spdif-objs := fsl_spdif.o
snd-soc-fsl-utils-objs := fsl_utils.o
snd-soc-fsl-dma-objs := fsl_dma.o
obj-$(CONFIG_SND_SOC_FSL_SSI) += snd-soc-fsl-ssi.o
+obj-$(CONFIG_SND_SOC_FSL_SPDIF) += snd-soc-fsl-spdif.o
obj-$(CONFIG_SND_SOC_FSL_UTILS) += snd-soc-fsl-utils.o
obj-$(CONFIG_SND_SOC_POWERPC_DMA) += snd-soc-fsl-dma.o
diff --git a/sound/soc/fsl/fsl_spdif.c b/sound/soc/fsl/fsl_spdif.c
new file mode 100644
index 0000000..42a4382
--- /dev/null
+++ b/sound/soc/fsl/fsl_spdif.c
@@ -0,0 +1,1236 @@
+/*
+ * Freescale S/PDIF ALSA SoC Digital Audio Interface (DAI) driver
+ *
+ * Copyright (C) 2013 Freescale Semiconductor, Inc.
+ *
+ * Based on stmp3xxx_spdif_dai.c
+ * Vladimir Barinov <vbarinov@embeddedalley.com>
+ * Copyright 2008 SigmaTel, Inc
+ * Copyright 2008 Embedded Alley Solutions, Inc
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/clk.h>
+#include <linux/clk-private.h>
+#include <linux/bitrev.h>
+#include <linux/regmap.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/of_irq.h>
+
+#include <sound/asoundef.h>
+#include <sound/soc.h>
+#include <sound/dmaengine_pcm.h>
+
+#include "fsl_spdif.h"
+#include "imx-pcm.h"
+
+#define FSL_SPDIF_TXFIFO_WML 0x8
+#define FSL_SPDIF_RXFIFO_WML 0x8
+
+#define INTR_FOR_PLAYBACK (INT_TXFIFO_RESYNC)
+#define INTR_FOR_CAPTURE (INT_SYM_ERR | INT_BIT_ERR | INT_URX_FUL | INT_URX_OV|\
+ INT_QRX_FUL | INT_QRX_OV | INT_UQ_SYNC | INT_UQ_ERR |\
+ INT_RXFIFO_RESYNC | INT_LOSS_LOCK | INT_DPLL_LOCKED)
+
+/* Index list for the values that has if (DPLL Locked) condition */
+static u8 srpc_dpll_locked[] = { 0x0, 0x1, 0x2, 0x3, 0x4, 0xa, 0xb };
+#define SRPC_NODPLL_START1 0x5
+#define SRPC_NODPLL_START2 0xc
+
+#define DEFAULT_RXCLK_SRC 1
+
+/*
+ * SPDIF control structure
+ * Defines channel status, subcode and Q sub
+ */
+struct spdif_mixer_control {
+ /* spinlock to access control data */
+ spinlock_t ctl_lock;
+
+ /* IEC958 channel tx status bit */
+ unsigned char ch_status[4];
+
+ /* User bits */
+ unsigned char subcode[2 * SPDIF_UBITS_SIZE];
+
+ /* Q subcode part of user bits */
+ unsigned char qsub[2 * SPDIF_QSUB_SIZE];
+
+ /* Buffer offset for U/Q */
+ u32 upos;
+ u32 qpos;
+
+ /* Ready buffer index of the two buffers */
+ u32 ready_buf;
+};
+
+struct fsl_spdif_priv {
+ struct spdif_mixer_control fsl_spdif_control;
+ struct snd_soc_dai_driver cpu_dai_drv;
+ struct platform_device *pdev;
+ struct regmap *regmap;
+ bool dpll_locked;
+ u8 txclk_div[SPDIF_TXRATE_MAX];
+ u8 txclk_src[SPDIF_TXRATE_MAX];
+ u8 rxclk_src;
+ struct clk *txclk[SPDIF_TXRATE_MAX];
+ struct clk *rxclk;
+ struct snd_dmaengine_dai_dma_data dma_params_tx;
+ struct snd_dmaengine_dai_dma_data dma_params_rx;
+
+ /* The name space will be allocated dynamically */
+ char name[0];
+};
+
+
+/* DPLL locked and lock loss interrupt handler */
+static void spdif_irq_dpll_lock(struct fsl_spdif_priv *spdif_priv)
+{
+ struct regmap *regmap = spdif_priv->regmap;
+ struct platform_device *pdev = spdif_priv->pdev;
+ u32 locked;
+
+ regmap_read(regmap, REG_SPDIF_SRPC, &locked);
+ locked &= SRPC_DPLL_LOCKED;
+
+ dev_dbg(&pdev->dev, "isr: Rx dpll %s \n",
+ locked ? "locked" : "loss lock");
+
+ spdif_priv->dpll_locked = locked ? true : false;
+}
+
+/* Receiver found illegal symbol interrupt handler */
+static void spdif_irq_sym_error(struct fsl_spdif_priv *spdif_priv)
+{
+ struct regmap *regmap = spdif_priv->regmap;
+ struct platform_device *pdev = spdif_priv->pdev;
+
+ dev_dbg(&pdev->dev, "isr: receiver found illegal symbol\n");
+
+ if (!spdif_priv->dpll_locked) {
+ /* DPLL unlocked seems no audio stream */
+ regmap_update_bits(regmap, REG_SPDIF_SIE, INT_SYM_ERR, 0);
+ }
+}
+
+/* U/Q Channel receive register full */
+static void spdif_irq_uqrx_full(struct fsl_spdif_priv *spdif_priv, char name)
+{
+ struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control;
+ struct regmap *regmap = spdif_priv->regmap;
+ struct platform_device *pdev = spdif_priv->pdev;
+ u32 *pos, size, val, reg;
+
+ switch (name) {
+ case 'U':
+ pos = &ctrl->upos;
+ size = SPDIF_UBITS_SIZE;
+ reg = REG_SPDIF_SRU;
+ break;
+ case 'Q':
+ pos = &ctrl->qpos;
+ size = SPDIF_QSUB_SIZE;
+ reg = REG_SPDIF_SRQ;
+ break;
+ default:
+ dev_err(&pdev->dev, "unsupported channel name\n");
+ return;
+ }
+
+ dev_dbg(&pdev->dev, "isr: %c Channel receive register full\n", name);
+
+ if (*pos >= size * 2) {
+ *pos = 0;
+ } else if (unlikely((*pos % size) + 3 > size)) {
+ dev_err(&pdev->dev, "User bit receivce buffer overflow\n");
+ return;
+ }
+
+ regmap_read(regmap, reg, &val);
+ ctrl->subcode[*pos++] = val >> 16;
+ ctrl->subcode[*pos++] = val >> 8;
+ ctrl->subcode[*pos++] = val;
+}
+
+/* U/Q Channel sync found */
+static void spdif_irq_uq_sync(struct fsl_spdif_priv *spdif_priv)
+{
+ struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control;
+ struct platform_device *pdev = spdif_priv->pdev;
+
+ dev_dbg(&pdev->dev, "isr: U/Q Channel sync found\n");
+
+ /* U/Q buffer reset */
+ if (ctrl->qpos == 0)
+ return;
+
+ /* Set ready to this buffer */
+ ctrl->ready_buf = (ctrl->qpos - 1) / SPDIF_QSUB_SIZE + 1;
+}
+
+/* U/Q Channel framing error */
+static void spdif_irq_uq_err(struct fsl_spdif_priv *spdif_priv)
+{
+ struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control;
+ struct regmap *regmap = spdif_priv->regmap;
+ struct platform_device *pdev = spdif_priv->pdev;
+ u32 val;
+
+ dev_dbg(&pdev->dev, "isr: U/Q Channel framing error\n");
+
+ /* Read U/Q data to clear the irq and do buffer reset */
+ regmap_read(regmap, REG_SPDIF_SRU, &val);
+ regmap_read(regmap, REG_SPDIF_SRQ, &val);
+
+ /* Drop this U/Q buffer */
+ ctrl->ready_buf = 0;
+ ctrl->upos = 0;
+ ctrl->qpos = 0;
+}
+
+/* Get spdif interrupt status and clear the interrupt */
+static u32 spdif_intr_status_clear(struct fsl_spdif_priv *spdif_priv)
+{
+ struct regmap *regmap = spdif_priv->regmap;
+ u32 val, val2;
+
+ regmap_read(regmap, REG_SPDIF_SIS, &val);
+ regmap_read(regmap, REG_SPDIF_SIE, &val2);
+
+ regmap_write(regmap, REG_SPDIF_SIC, val & val2);
+
+ return val;
+}
+
+static irqreturn_t spdif_isr(int irq, void *devid)
+{
+ struct fsl_spdif_priv *spdif_priv = (struct fsl_spdif_priv *)devid;
+ struct platform_device *pdev = spdif_priv->pdev;
+ u32 sis;
+
+ sis = spdif_intr_status_clear(spdif_priv);
+
+ if (sis & INT_DPLL_LOCKED)
+ spdif_irq_dpll_lock(spdif_priv);
+
+ if (sis & INT_TXFIFO_UNOV)
+ dev_dbg(&pdev->dev, "isr: Tx FIFO under/overrun\n");
+
+ if (sis & INT_TXFIFO_RESYNC)
+ dev_dbg(&pdev->dev, "isr: Tx FIFO resync\n");
+
+ if (sis & INT_CNEW)
+ dev_dbg(&pdev->dev, "isr: cstatus new\n");
+
+ if (sis & INT_VAL_NOGOOD)
+ dev_dbg(&pdev->dev, "isr: validity flag no good\n");
+
+ if (sis & INT_SYM_ERR)
+ spdif_irq_sym_error(spdif_priv);
+
+ if (sis & INT_BIT_ERR)
+ dev_dbg(&pdev->dev, "isr: receiver found parity bit error\n");
+
+ if (sis & INT_URX_FUL)
+ spdif_irq_uqrx_full(spdif_priv, 'U');
+
+ if (sis & INT_URX_OV)
+ dev_dbg(&pdev->dev, "isr: U Channel receive register overrun\n");
+
+ if (sis & INT_QRX_FUL)
+ spdif_irq_uqrx_full(spdif_priv, 'Q');
+
+ if (sis & INT_QRX_OV)
+ dev_dbg(&pdev->dev, "isr: Q Channel receive register overrun\n");
+
+ if (sis & INT_UQ_SYNC)
+ spdif_irq_uq_sync(spdif_priv);
+
+ if (sis & INT_UQ_ERR)
+ spdif_irq_uq_err(spdif_priv);
+
+ if (sis & INT_RXFIFO_UNOV)
+ dev_dbg(&pdev->dev, "isr: Rx FIFO under/overrun\n");
+
+ if (sis & INT_RXFIFO_RESYNC)
+ dev_dbg(&pdev->dev, "isr: Rx FIFO resync\n");
+
+ if (sis & INT_LOSS_LOCK)
+ spdif_irq_dpll_lock(spdif_priv);
+
+ /* FIXME: Write Tx FIFO to clear TxEm */
+ if (sis & INT_TX_EM)
+ dev_dbg(&pdev->dev, "isr: Tx FIFO empty\n");
+
+ /* FIXME: Read Rx FIFO to clear RxFIFOFul */
+ if (sis & INT_RXFIFO_FUL)
+ dev_dbg(&pdev->dev, "isr: Rx FIFO full\n");
+
+ return IRQ_HANDLED;
+}
+
+static int spdif_softreset(struct fsl_spdif_priv *spdif_priv)
+{
+ struct regmap *regmap = spdif_priv->regmap;
+ u32 val, cycle = 1000;
+
+ regmap_write(regmap, REG_SPDIF_SCR, SCR_SOFT_RESET);
+
+ /*
+ * RESET bit would be cleared after finishing its reset procedure,
+ * which typically lasts 8 cycles. 1000 cycles will keep it safe.
+ */
+ do {
+ regmap_read(regmap, REG_SPDIF_SCR, &val);
+ } while ((val & SCR_SOFT_RESET) && cycle--);
+
+ if (cycle)
+ return 0;
+ else
+ return -EBUSY;
+}
+
+static void spdif_set_cstatus(struct spdif_mixer_control *ctrl,
+ u8 mask, u8 cstatus)
+{
+ ctrl->ch_status[3] &= ~mask;
+ ctrl->ch_status[3] |= cstatus & mask;
+}
+
+static void spdif_write_channel_status(struct fsl_spdif_priv *spdif_priv)
+{
+ struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control;
+ struct regmap *regmap = spdif_priv->regmap;
+ struct platform_device *pdev = spdif_priv->pdev;
+ u32 ch_status;
+
+ ch_status = (bitrev8(ctrl->ch_status[0]) << 16) |
+ (bitrev8(ctrl->ch_status[1]) << 8) |
+ bitrev8(ctrl->ch_status[2]);
+ regmap_write(regmap, REG_SPDIF_STCSCH, ch_status);
+
+ dev_dbg(&pdev->dev, "STCSCH: 0x%06x\n", ch_status);
+
+ ch_status = bitrev8(ctrl->ch_status[3]) << 16;
+ regmap_write(regmap, REG_SPDIF_STCSCL, ch_status);
+
+ dev_dbg(&pdev->dev, "STCSCL: 0x%06x\n", ch_status);
+}
+
+/* Set SPDIF PhaseConfig register for rx clock */
+static int spdif_set_rx_clksrc(struct fsl_spdif_priv *spdif_priv,
+ enum spdif_gainsel gainsel, int dpll_locked)
+{
+ struct regmap *regmap = spdif_priv->regmap;
+ u8 clksrc = spdif_priv->rxclk_src;
+
+ if (clksrc >= SRPC_CLKSRC_MAX || gainsel >= GAINSEL_MULTI_MAX)
+ return -EINVAL;
+
+ regmap_update_bits(regmap, REG_SPDIF_SRPC,
+ SRPC_CLKSRC_SEL_MASK | SRPC_GAINSEL_MASK,
+ SRPC_CLKSRC_SEL_SET(clksrc) | SRPC_GAINSEL_SET(gainsel));
+
+ return 0;
+}
+
+static int spdif_set_sample_rate(struct snd_pcm_substream *substream,
+ int sample_rate)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(rtd->cpu_dai);
+ struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control;
+ struct regmap *regmap = spdif_priv->regmap;
+ struct platform_device *pdev = spdif_priv->pdev;
+ unsigned long csfs = 0;
+ u32 stc, mask, rate;
+ u8 clk, div;
+ int ret;
+
+ switch (sample_rate) {
+ case 32000:
+ rate = SPDIF_TXRATE_32000;
+ csfs = IEC958_AES3_CON_FS_32000;
+ break;
+ case 44100:
+ rate = SPDIF_TXRATE_44100;
+ csfs = IEC958_AES3_CON_FS_44100;
+ break;
+ case 48000:
+ rate = SPDIF_TXRATE_48000;
+ csfs = IEC958_AES3_CON_FS_48000;
+ break;
+ default:
+ dev_err(&pdev->dev, "unsupported sample rate %d\n", sample_rate);
+ return -EINVAL;
+ }
+
+ clk = spdif_priv->txclk_src[rate];
+ if (clk >= STC_TXCLK_SRC_MAX) {
+ dev_err(&pdev->dev, "tx clock source is out of range\n");
+ return -EINVAL;
+ }
+
+ div = spdif_priv->txclk_div[rate];
+ if (div == 0) {
+ dev_err(&pdev->dev, "the divisor can't be zero\n");
+ return -EINVAL;
+ }
+
+ /*
+ * The S/PDIF block needs a clock of 64 * fs * div. The S/PDIF block
+ * will divide by (div). So request 64 * fs * (div+1) which will
+ * get rounded.
+ */
+ ret = clk_set_rate(spdif_priv->txclk[rate], 64 * sample_rate * (div + 1));
+ if (ret) {
+ dev_err(&pdev->dev, "failed to set tx clock rate\n");
+ return ret;
+ }
+
+ dev_dbg(&pdev->dev, "expected clock rate = %d\n",
+ (64 * sample_rate * div));
+ dev_dbg(&pdev->dev, "actual clock rate = %ld\n",
+ clk_get_rate(spdif_priv->txclk[rate]));
+
+ /* set fs field in consumer channel status */
+ spdif_set_cstatus(ctrl, IEC958_AES3_CON_FS, csfs);
+
+ /* select clock source and divisor */
+ stc = STC_TXCLK_ALL_EN | STC_TXCLK_SRC_SET(clk) | STC_TXCLK_DIV(div);
+ mask = STC_TXCLK_ALL_EN_MASK | STC_TXCLK_SRC_MASK | STC_TXCLK_DIV_MASK;
+ regmap_update_bits(regmap, REG_SPDIF_STC, mask, stc);
+
+ dev_dbg(&pdev->dev, "set sample rate to %d\n", sample_rate);
+
+ return 0;
+}
+
+int fsl_spdif_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *cpu_dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(rtd->cpu_dai);
+ struct platform_device *pdev = spdif_priv->pdev;
+ struct regmap *regmap = spdif_priv->regmap;
+ u32 scr, mask, i;
+ int ret;
+
+ /* Reset module and interrupts only for first initialization */
+ if (!cpu_dai->active) {
+ ret = spdif_softreset(spdif_priv);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to soft reset\n");
+ return ret;
+ }
+
+ /* Disable all the interrupts */
+ regmap_update_bits(regmap, REG_SPDIF_SIE, 0xffffff, 0);
+ }
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ scr = SCR_TXFIFO_AUTOSYNC | SCR_TXFIFO_CTRL_NORMAL |
+ SCR_TXSEL_NORMAL | SCR_USRC_SEL_CHIP |
+ SCR_TXFIFO_FSEL_IF8;
+ mask = SCR_TXFIFO_AUTOSYNC_MASK | SCR_TXFIFO_CTRL_MASK |
+ SCR_TXSEL_MASK | SCR_USRC_SEL_MASK |
+ SCR_TXFIFO_FSEL_MASK;
+ for (i = 0; i < SPDIF_TXRATE_MAX; i++)
+ clk_prepare_enable(spdif_priv->txclk[i]);
+ } else {
+ scr = SCR_RXFIFO_FSEL_IF8 | SCR_RXFIFO_AUTOSYNC;
+ mask = SCR_RXFIFO_FSEL_MASK | SCR_RXFIFO_AUTOSYNC_MASK|
+ SCR_RXFIFO_CTL_MASK | SCR_RXFIFO_OFF_MASK;
+ clk_prepare_enable(spdif_priv->rxclk);
+ }
+ regmap_update_bits(regmap, REG_SPDIF_SCR, mask, scr);
+
+ /* Power up SPDIF module */
+ regmap_update_bits(regmap, REG_SPDIF_SCR, SCR_LOW_POWER, 0);
+
+ return 0;
+}
+
+static void fsl_spdif_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *cpu_dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(rtd->cpu_dai);
+ struct regmap *regmap = spdif_priv->regmap;
+ u32 scr, mask, i;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ scr = 0;
+ mask = SCR_TXFIFO_AUTOSYNC_MASK | SCR_TXFIFO_CTRL_MASK |
+ SCR_TXSEL_MASK | SCR_USRC_SEL_MASK |
+ SCR_TXFIFO_FSEL_MASK;
+ for (i = 0; i < SPDIF_TXRATE_MAX; i++)
+ clk_disable_unprepare(spdif_priv->txclk[i]);
+ } else {
+ scr = SCR_RXFIFO_OFF | SCR_RXFIFO_CTL_ZERO;
+ mask = SCR_RXFIFO_FSEL_MASK | SCR_RXFIFO_AUTOSYNC_MASK|
+ SCR_RXFIFO_CTL_MASK | SCR_RXFIFO_OFF_MASK;
+ clk_disable_unprepare(spdif_priv->rxclk);
+ }
+ regmap_update_bits(regmap, REG_SPDIF_SCR, mask, scr);
+
+ /* Power down SPDIF module only if tx&rx are both inactive */
+ if (!cpu_dai->active) {
+ spdif_intr_status_clear(spdif_priv);
+ regmap_update_bits(regmap, REG_SPDIF_SCR,
+ SCR_LOW_POWER, SCR_LOW_POWER);
+ }
+}
+
+static int fsl_spdif_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(rtd->cpu_dai);
+ struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control;
+ struct platform_device *pdev = spdif_priv->pdev;
+ u32 sample_rate = params_rate(params);
+ int ret = 0;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ ret = spdif_set_sample_rate(substream, sample_rate);
+ if (ret) {
+ dev_err(&pdev->dev, "%s: set sample rate failed: %d\n",
+ __func__, sample_rate);
+ return ret;
+ }
+ spdif_set_cstatus(ctrl, IEC958_AES3_CON_CLOCK,
+ IEC958_AES3_CON_CLOCK_1000PPM);
+ spdif_write_channel_status(spdif_priv);
+ } else {
+ /* Setup rx clock source */
+ ret = spdif_set_rx_clksrc(spdif_priv, SPDIF_DEFAULT_GAINSEL, 1);
+ }
+
+ return ret;
+}
+
+static int fsl_spdif_trigger(struct snd_pcm_substream *substream,
+ int cmd, struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(rtd->cpu_dai);
+ struct regmap *regmap = spdif_priv->regmap;
+ int is_playack = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
+ u32 intr = is_playack ? INTR_FOR_PLAYBACK : INTR_FOR_CAPTURE;
+ u32 dmaen = is_playack ? SCR_DMA_TX_EN : SCR_DMA_RX_EN;;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ regmap_update_bits(regmap, REG_SPDIF_SIE, intr, intr);
+ regmap_update_bits(regmap, REG_SPDIF_SCR, dmaen, dmaen);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ regmap_update_bits(regmap, REG_SPDIF_SCR, dmaen, 0);
+ regmap_update_bits(regmap, REG_SPDIF_SIE, intr, 0);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+struct snd_soc_dai_ops fsl_spdif_dai_ops = {
+ .startup = fsl_spdif_startup,
+ .hw_params = fsl_spdif_hw_params,
+ .trigger = fsl_spdif_trigger,
+ .shutdown = fsl_spdif_shutdown,
+};
+
+
+/*
+ * ============================================
+ * FSL SPDIF IEC958 controller(mixer) functions
+ *
+ * Channel status get/put control
+ * User bit value get/put control
+ * Valid bit value get control
+ * DPLL lock status get control
+ * User bit sync mode selection control
+ * ============================================
+ */
+
+static int fsl_spdif_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+ uinfo->count = 1;
+
+ return 0;
+}
+
+static int fsl_spdif_pb_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *uvalue)
+{
+ struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
+ struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai);
+ struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control;
+
+ uvalue->value.iec958.status[0] = ctrl->ch_status[0];
+ uvalue->value.iec958.status[1] = ctrl->ch_status[1];
+ uvalue->value.iec958.status[2] = ctrl->ch_status[2];
+ uvalue->value.iec958.status[3] = ctrl->ch_status[3];
+
+ return 0;
+}
+
+static int fsl_spdif_pb_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *uvalue)
+{
+ struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
+ struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai);
+ struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control;
+
+ ctrl->ch_status[0] = uvalue->value.iec958.status[0];
+ ctrl->ch_status[1] = uvalue->value.iec958.status[1];
+ ctrl->ch_status[2] = uvalue->value.iec958.status[2];
+ ctrl->ch_status[3] = uvalue->value.iec958.status[3];
+
+ spdif_write_channel_status(spdif_priv);
+
+ return 0;
+}
+
+/* Get channel status from SPDIF_RX_CCHAN register */
+static int fsl_spdif_capture_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
+ struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai);
+ struct regmap *regmap = spdif_priv->regmap;
+ u32 cstatus, val;
+
+ regmap_read(regmap, REG_SPDIF_SIS, &val);
+ if (!(val & INT_CNEW)) {
+ return -EAGAIN;
+ }
+
+ regmap_read(regmap, REG_SPDIF_SRCSH, &cstatus);
+ ucontrol->value.iec958.status[0] = (cstatus >> 16) & 0xFF;
+ ucontrol->value.iec958.status[1] = (cstatus >> 8) & 0xFF;
+ ucontrol->value.iec958.status[2] = cstatus & 0xFF;
+
+ regmap_read(regmap, REG_SPDIF_SRCSL, &cstatus);
+ ucontrol->value.iec958.status[3] = (cstatus >> 16) & 0xFF;
+ ucontrol->value.iec958.status[4] = (cstatus >> 8) & 0xFF;
+ ucontrol->value.iec958.status[5] = cstatus & 0xFF;
+
+ /* Clear intr */
+ regmap_write(regmap, REG_SPDIF_SIC, INT_CNEW);
+
+ return 0;
+}
+
+/*
+ * Get User bits (subcode) from chip value which readed out
+ * in UChannel register.
+ */
+static int fsl_spdif_subcode_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
+ struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai);
+ struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control;
+ unsigned long flags;
+ int ret = 0;
+
+ spin_lock_irqsave(&ctrl->ctl_lock, flags);
+ if (ctrl->ready_buf) {
+ int idx = (ctrl->ready_buf - 1) * SPDIF_UBITS_SIZE;
+ memcpy(&ucontrol->value.iec958.subcode[0],
+ &ctrl->subcode[idx], SPDIF_UBITS_SIZE);
+ } else {
+ ret = -EAGAIN;
+ }
+ spin_unlock_irqrestore(&ctrl->ctl_lock, flags);
+
+ return ret;
+}
+
+/* Q-subcode infomation. The byte size is SPDIF_UBITS_SIZE/8 */
+static int fsl_spdif_qinfo(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
+ uinfo->count = SPDIF_QSUB_SIZE;
+
+ return 0;
+}
+
+/* Get Q subcode from chip value which readed out in QChannel register */
+static int fsl_spdif_qget(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
+ struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai);
+ struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control;
+ unsigned long flags;
+ int ret = 0;
+
+ spin_lock_irqsave(&ctrl->ctl_lock, flags);
+ if (ctrl->ready_buf) {
+ int idx = (ctrl->ready_buf - 1) * SPDIF_QSUB_SIZE;
+ memcpy(&ucontrol->value.bytes.data[0],
+ &ctrl->qsub[idx], SPDIF_QSUB_SIZE);
+ } else {
+ ret = -EAGAIN;
+ }
+ spin_unlock_irqrestore(&ctrl->ctl_lock, flags);
+
+ return ret;
+}
+
+/* Valid bit infomation */
+static int fsl_spdif_vbit_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+
+ return 0;
+}
+
+/* Get valid good bit from interrupt status register */
+static int fsl_spdif_vbit_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
+ struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai);
+ struct regmap *regmap = spdif_priv->regmap;
+ u32 val;
+
+ val = regmap_read(regmap, REG_SPDIF_SIS, &val);
+ ucontrol->value.integer.value[0] = (val & INT_VAL_NOGOOD) != 0;
+ regmap_write(regmap, REG_SPDIF_SIC, INT_VAL_NOGOOD);
+
+ return 0;
+}
+
+/* DPLL lock infomation */
+static int fsl_spdif_rxrate_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 16000;
+ uinfo->value.integer.max = 96000;
+
+ return 0;
+}
+
+static u32 gainsel_multi[GAINSEL_MULTI_MAX] = {
+ 24, 16, 12, 8, 6, 4, 3,
+};
+
+/* Get RX data clock rate given the SPDIF bus_clk */
+static int spdif_get_rxclk_rate(struct fsl_spdif_priv *spdif_priv,
+ enum spdif_gainsel gainsel)
+{
+ struct regmap *regmap = spdif_priv->regmap;
+ struct platform_device *pdev = spdif_priv->pdev;
+ u64 tmpval64, busclk_freq = 0;
+ u32 freqmeas, phaseconf;
+ u8 clksrc;
+
+ regmap_read(regmap, REG_SPDIF_SRFM, &freqmeas);
+ regmap_read(regmap, REG_SPDIF_SRPC, &phaseconf);
+
+ clksrc = (phaseconf >> SRPC_CLKSRC_SEL_OFFSET) & 0xf;
+ if (srpc_dpll_locked[clksrc] && (phaseconf & SRPC_DPLL_LOCKED)) {
+ /* Get bus clock from system */
+ busclk_freq = clk_get_rate(spdif_priv->rxclk);
+ }
+
+ /* FreqMeas_CLK = (BUS_CLK * FreqMeas) / 2 ^ 10 / GAINSEL / 128 */
+ tmpval64 = (u64) busclk_freq * freqmeas;
+ do_div(tmpval64, gainsel_multi[gainsel] * 1024);
+ do_div(tmpval64, 128 * 1024);
+
+ dev_dbg(&pdev->dev, "FreqMeas: %d\n", freqmeas);
+ dev_dbg(&pdev->dev, "BusclkFreq: %lld\n", busclk_freq);
+ dev_dbg(&pdev->dev, "RxRate: %lld\n", tmpval64);
+
+ return (int)tmpval64;
+}
+
+/*
+ * Get DPLL lock or not info from stable interrupt status register.
+ * User application must use this control to get locked,
+ * then can do next PCM operation
+ */
+static int fsl_spdif_rxrate_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
+ struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai);
+ int rate = spdif_get_rxclk_rate(spdif_priv, SPDIF_DEFAULT_GAINSEL);
+
+ if (spdif_priv->dpll_locked)
+ ucontrol->value.integer.value[0] = rate;
+ else
+ ucontrol->value.integer.value[0] = 0;
+
+ return 0;
+}
+
+/* User bit sync mode info */
+static int fsl_spdif_usync_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+
+ return 0;
+}
+
+/*
+ * User bit sync mode:
+ * 1 CD User channel subcode
+ * 0 Non-CD data
+ */
+static int fsl_spdif_usync_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
+ struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai);
+ struct regmap *regmap = spdif_priv->regmap;
+ u32 val;
+
+ regmap_read(regmap, REG_SPDIF_SRCD, &val);
+ ucontrol->value.integer.value[0] = (val & SRCD_CD_USER) != 0;
+
+ return 0;
+}
+
+/*
+ * User bit sync mode:
+ * 1 CD User channel subcode
+ * 0 Non-CD data
+ */
+static int fsl_spdif_usync_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
+ struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai);
+ struct regmap *regmap = spdif_priv->regmap;
+ u32 val = ucontrol->value.integer.value[0] << SRCD_CD_USER_OFFSET;
+
+ regmap_update_bits(regmap, REG_SPDIF_SRCD, SRCD_CD_USER, val);
+
+ return 0;
+}
+
+/* FSL SPDIF IEC958 controller defines */
+static struct snd_kcontrol_new fsl_spdif_ctrls[] = {
+ /* Status cchanel controller */
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT),
+ .access = SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_WRITE |
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = fsl_spdif_info,
+ .get = fsl_spdif_pb_get,
+ .put = fsl_spdif_pb_put,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = SNDRV_CTL_NAME_IEC958("", CAPTURE, DEFAULT),
+ .access = SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = fsl_spdif_info,
+ .get = fsl_spdif_capture_get,
+ },
+ /* User bits controller */
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "IEC958 Subcode Capture Default",
+ .access = SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = fsl_spdif_info,
+ .get = fsl_spdif_subcode_get,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "IEC958 Q-subcode Capture Default",
+ .access = SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = fsl_spdif_qinfo,
+ .get = fsl_spdif_qget,
+ },
+ /* Valid bit error controller */
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "IEC958 V-Bit Errors",
+ .access = SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = fsl_spdif_vbit_info,
+ .get = fsl_spdif_vbit_get,
+ },
+ /* DPLL lock info get controller */
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "RX Sample Rate",
+ .access = SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = fsl_spdif_rxrate_info,
+ .get = fsl_spdif_rxrate_get,
+ },
+ /* User bit sync mode set/get controller */
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "IEC958 USyncMode CDText",
+ .access = SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_WRITE |
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = fsl_spdif_usync_info,
+ .get = fsl_spdif_usync_get,
+ .put = fsl_spdif_usync_put,
+ },
+};
+
+static int fsl_spdif_dai_probe(struct snd_soc_dai *dai)
+{
+ struct fsl_spdif_priv *spdif_private = snd_soc_dai_get_drvdata(dai);
+
+ dai->playback_dma_data = &spdif_private->dma_params_tx;
+ dai->capture_dma_data = &spdif_private->dma_params_rx;
+
+ snd_soc_add_dai_controls(dai, fsl_spdif_ctrls, ARRAY_SIZE(fsl_spdif_ctrls));
+
+ return 0;
+}
+
+struct snd_soc_dai_driver fsl_spdif_dai = {
+ .probe = &fsl_spdif_dai_probe,
+ .playback = {
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = FSL_SPDIF_RATES_PLAYBACK,
+ .formats = FSL_SPDIF_FORMATS_PLAYBACK,
+ },
+ .capture = {
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = FSL_SPDIF_RATES_CAPTURE,
+ .formats = FSL_SPDIF_FORMATS_CAPTURE,
+ },
+ .ops = &fsl_spdif_dai_ops,
+};
+
+static const struct snd_soc_component_driver fsl_spdif_component = {
+ .name = "fsl-spdif",
+};
+
+/*
+ * ================
+ * FSL SPDIF REGMAP
+ * ================
+ */
+
+static bool fsl_spdif_readable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case REG_SPDIF_SCR:
+ case REG_SPDIF_SRCD:
+ case REG_SPDIF_SRPC:
+ case REG_SPDIF_SIE:
+ case REG_SPDIF_SIS:
+ case REG_SPDIF_SRL:
+ case REG_SPDIF_SRR:
+ case REG_SPDIF_SRCSH:
+ case REG_SPDIF_SRCSL:
+ case REG_SPDIF_SRU:
+ case REG_SPDIF_SRQ:
+ case REG_SPDIF_STCSCH:
+ case REG_SPDIF_STCSCL:
+ case REG_SPDIF_SRFM:
+ case REG_SPDIF_STC:
+ return true;
+ default:
+ return false;
+ };
+}
+
+static bool fsl_spdif_writeable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case REG_SPDIF_SCR:
+ case REG_SPDIF_SRCD:
+ case REG_SPDIF_SRPC:
+ case REG_SPDIF_SIE:
+ case REG_SPDIF_SIC:
+ case REG_SPDIF_STL:
+ case REG_SPDIF_STR:
+ case REG_SPDIF_STCSCH:
+ case REG_SPDIF_STCSCL:
+ case REG_SPDIF_STC:
+ return true;
+ default:
+ return false;
+ };
+}
+
+static const struct regmap_config fsl_spdif_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+
+ .max_register = REG_SPDIF_STC,
+ .readable_reg = fsl_spdif_readable_reg,
+ .writeable_reg = fsl_spdif_writeable_reg,
+};
+
+static u32 fsl_spdif_txclk_caldiv(struct fsl_spdif_priv *spdif_priv,
+ struct clk *clk, u64 savesub,
+ enum spdif_txrate index)
+{
+ const u32 rate[] = { 32000, 44100, 48000 };
+ u64 rate_ideal, rate_actual, sub;
+ u32 div, arate;
+
+ for (div = 1; div <= 128; div++) {
+ rate_ideal = rate[index] * (div + 1) * 64;
+ rate_actual = clk_round_rate(clk, rate_ideal);
+
+ arate = rate_actual / 64;
+ arate /= div;
+
+ if (arate == rate[index]) {
+ /* We are lucky */
+ savesub = 0;
+ spdif_priv->txclk_div[index] = div;
+ break;
+ } else if (arate / rate[index] == 1) {
+ /* A little bigger than expect */
+ sub = (arate - rate[index]) * 100000;
+ do_div(sub, rate[index]);
+ if (sub < savesub) {
+ savesub = sub;
+ spdif_priv->txclk_div[index] = div;
+ }
+ } else if (rate[index] / arate == 1) {
+ /* A little smaller than expect */
+ sub = (rate[index] - arate) * 100000;
+ do_div(sub, rate[index]);
+ if (sub < savesub) {
+ savesub = sub;
+ spdif_priv->txclk_div[index] = div;
+ }
+ }
+ }
+
+ return savesub;
+}
+
+static int fsl_spdif_probe_txclk(struct fsl_spdif_priv *spdif_priv,
+ enum spdif_txrate index)
+{
+ const u32 rate[] = { 32000, 44100, 48000 };
+ struct platform_device *pdev = spdif_priv->pdev;
+ struct device *dev = &pdev->dev;
+ u64 savesub = 100000, ret;
+ struct clk *clk;
+ char tmp[16];
+ int i;
+
+ for (i = 0; i < STC_TXCLK_SRC_MAX; i++) {
+ sprintf(tmp, "rxtx%d", i);
+ clk = devm_clk_get(&pdev->dev, tmp);
+ if (IS_ERR(clk)) {
+ dev_err(dev, "no rxtx%d clock in devicetree\n", i);
+ return PTR_ERR(clk);
+ }
+ if (!clk_get_rate(clk))
+ continue;
+
+ ret = fsl_spdif_txclk_caldiv(spdif_priv, clk, savesub, index);
+ if (savesub == ret)
+ continue;
+
+ savesub = ret;
+ spdif_priv->txclk[index] = clk;
+ spdif_priv->txclk_src[index] = i;
+
+ /* To quick catch a divisor, we allow a 0.1% deviation */
+ if (savesub < 100)
+ break;
+ }
+
+ dev_dbg(&pdev->dev, "use rxtx%d as tx clock source for %dHz sample rate",
+ spdif_priv->txclk_src[index], rate[index]);
+ dev_dbg(&pdev->dev, "use divisor %d for %dHz sample rate",
+ spdif_priv->txclk_div[index], rate[index]);
+
+ return 0;
+}
+
+static int fsl_spdif_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct fsl_spdif_priv *spdif_priv;
+ struct spdif_mixer_control *ctrl;
+ struct resource *res;
+ void __iomem *regs;
+ int irq, ret, i;
+
+ if (!np)
+ return -ENODEV;
+
+ spdif_priv = devm_kzalloc(&pdev->dev,
+ sizeof(struct fsl_spdif_priv) + strlen(np->name) + 1,
+ GFP_KERNEL);
+ if (!spdif_priv)
+ return -ENOMEM;
+
+ strcpy(spdif_priv->name, np->name);
+
+ spdif_priv->pdev = pdev;
+
+ /* Initialize this copy of the CPU DAI driver structure */
+ memcpy(&spdif_priv->cpu_dai_drv, &fsl_spdif_dai, sizeof(fsl_spdif_dai));
+ spdif_priv->cpu_dai_drv.name = spdif_priv->name;
+
+ /* Get the addresses and IRQ */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (IS_ERR(res)) {
+ dev_err(&pdev->dev, "could not determine device resources\n");
+ return PTR_ERR(res);
+ }
+
+ regs = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(regs)) {
+ dev_err(&pdev->dev, "could not map device resources\n");
+ return PTR_ERR(regs);
+ }
+
+ spdif_priv->regmap = devm_regmap_init_mmio_clk(&pdev->dev,
+ "core", regs, &fsl_spdif_regmap_config);
+ if (IS_ERR(spdif_priv->regmap)) {
+ dev_err(&pdev->dev, "regmap init failed\n");
+ return PTR_ERR(spdif_priv->regmap);
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(&pdev->dev, "no irq for node %s\n", np->full_name);
+ return irq;
+ }
+
+ ret = devm_request_irq(&pdev->dev, irq, spdif_isr, 0,
+ spdif_priv->name, spdif_priv);
+ if (ret) {
+ dev_err(&pdev->dev, "could not claim irq %u\n", irq);
+ return ret;
+ }
+
+ /* Select clock source for rx/tx clock */
+ spdif_priv->rxclk = devm_clk_get(&pdev->dev, "rxtx1");
+ if (IS_ERR(spdif_priv->rxclk)) {
+ dev_err(&pdev->dev, "no rxtx1 clock in devicetree\n");
+ return PTR_ERR(spdif_priv->rxclk);
+ }
+ spdif_priv->rxclk_src = DEFAULT_RXCLK_SRC;
+
+ for (i = 0; i < SPDIF_TXRATE_MAX; i++) {
+ ret = fsl_spdif_probe_txclk(spdif_priv, i);
+ if (ret)
+ return ret;
+ }
+
+ /* Initial spinlock for control data */
+ ctrl = &spdif_priv->fsl_spdif_control;
+ spin_lock_init(&ctrl->ctl_lock);
+
+ /* Init tx channel status default value */
+ ctrl->ch_status[0] =
+ IEC958_AES0_CON_NOT_COPYRIGHT | IEC958_AES0_CON_EMPHASIS_5015;
+ ctrl->ch_status[1] = IEC958_AES1_CON_DIGDIGCONV_ID;
+ ctrl->ch_status[2] = 0x00;
+ ctrl->ch_status[3] =
+ IEC958_AES3_CON_FS_44100 | IEC958_AES3_CON_CLOCK_1000PPM;
+
+ spdif_priv->dpll_locked = false;
+
+ spdif_priv->dma_params_tx.maxburst = FSL_SPDIF_TXFIFO_WML;
+ spdif_priv->dma_params_rx.maxburst = FSL_SPDIF_RXFIFO_WML;
+ spdif_priv->dma_params_tx.addr = res->start + REG_SPDIF_STL;
+ spdif_priv->dma_params_rx.addr = res->start + REG_SPDIF_SRL;
+
+ /* Register with ASoC */
+ dev_set_drvdata(&pdev->dev, spdif_priv);
+
+ ret = snd_soc_register_component(&pdev->dev, &fsl_spdif_component,
+ &spdif_priv->cpu_dai_drv, 1);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to register DAI: %d\n", ret);
+ goto error_dev;
+ }
+
+ ret = imx_pcm_dma_init(pdev);
+ if (ret) {
+ dev_err(&pdev->dev, "imx_pcm_dma_init failed: %d\n", ret);
+ goto error_component;
+ }
+
+ return ret;
+
+error_component:
+ snd_soc_unregister_component(&pdev->dev);
+error_dev:
+ dev_set_drvdata(&pdev->dev, NULL);
+
+ return ret;
+}
+
+static int fsl_spdif_remove(struct platform_device *pdev)
+{
+ imx_pcm_dma_exit(pdev);
+ snd_soc_unregister_component(&pdev->dev);
+ dev_set_drvdata(&pdev->dev, NULL);
+
+ return 0;
+}
+
+static const struct of_device_id fsl_spdif_dt_ids[] = {
+ { .compatible = "fsl,imx35-spdif", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, fsl_spdif_dt_ids);
+
+static struct platform_driver fsl_spdif_driver = {
+ .driver = {
+ .name = "fsl-spdif-dai",
+ .owner = THIS_MODULE,
+ .of_match_table = fsl_spdif_dt_ids,
+ },
+ .probe = fsl_spdif_probe,
+ .remove = fsl_spdif_remove,
+};
+
+module_platform_driver(fsl_spdif_driver);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("Freescale S/PDIF CPU DAI Driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:fsl-spdif-dai");
diff --git a/sound/soc/fsl/fsl_spdif.h b/sound/soc/fsl/fsl_spdif.h
new file mode 100644
index 0000000..b126679
--- /dev/null
+++ b/sound/soc/fsl/fsl_spdif.h
@@ -0,0 +1,191 @@
+/*
+ * fsl_spdif.h - ALSA S/PDIF interface for the Freescale i.MX SoC
+ *
+ * Copyright (C) 2013 Freescale Semiconductor, Inc.
+ *
+ * Author: Nicolin Chen <b42378@freescale.com>
+ *
+ * Based on fsl_ssi.h
+ * Author: Timur Tabi <timur@freescale.com>
+ * Copyright 2007-2008 Freescale Semiconductor, Inc.
+ *
+ * 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 _FSL_SPDIF_DAI_H
+#define _FSL_SPDIF_DAI_H
+
+/* S/PDIF Register Map */
+#define REG_SPDIF_SCR 0x0 /* SPDIF Configuration Register */
+#define REG_SPDIF_SRCD 0x4 /* CDText Control Register */
+#define REG_SPDIF_SRPC 0x8 /* PhaseConfig Register */
+#define REG_SPDIF_SIE 0xc /* InterruptEn Register */
+#define REG_SPDIF_SIS 0x10 /* InterruptStat Register */
+#define REG_SPDIF_SIC 0x10 /* InterruptClear Register */
+#define REG_SPDIF_SRL 0x14 /* SPDIFRxLeft Register */
+#define REG_SPDIF_SRR 0x18 /* SPDIFRxRight Register */
+#define REG_SPDIF_SRCSH 0x1c /* SPDIFRxCChannel_h Register */
+#define REG_SPDIF_SRCSL 0x20 /* SPDIFRxCChannel_l Register */
+#define REG_SPDIF_SRU 0x24 /* UchannelRx Register */
+#define REG_SPDIF_SRQ 0x28 /* QchannelRx Register */
+#define REG_SPDIF_STL 0x2C /* SPDIFTxLeft Register */
+#define REG_SPDIF_STR 0x30 /* SPDIFTxRight Register */
+#define REG_SPDIF_STCSCH 0x34 /* SPDIFTxCChannelCons_h Register */
+#define REG_SPDIF_STCSCL 0x38 /* SPDIFTxCChannelCons_l Register */
+#define REG_SPDIF_SRFM 0x44 /* FreqMeas Register */
+#define REG_SPDIF_STC 0x50 /* SPDIFTxClk Register */
+
+
+/* SPDIF Configuration register */
+#define SCR_RXFIFO_CTL_OFFSET 23
+#define SCR_RXFIFO_CTL_MASK (1 << SCR_RXFIFO_CTL_OFFSET)
+#define SCR_RXFIFO_CTL_ZERO (1 << SCR_RXFIFO_CTL_OFFSET)
+#define SCR_RXFIFO_OFF_OFFSET 22
+#define SCR_RXFIFO_OFF_MASK (1 << SCR_RXFIFO_OFF_OFFSET)
+#define SCR_RXFIFO_OFF (1 << SCR_RXFIFO_OFF_OFFSET)
+#define SCR_RXFIFO_RST_OFFSET 21
+#define SCR_RXFIFO_RST_MASK (1 << SCR_RXFIFO_RST_OFFSET)
+#define SCR_RXFIFO_RST (1 << SCR_RXFIFO_RST_OFFSET)
+#define SCR_RXFIFO_FSEL_OFFSET 19
+#define SCR_RXFIFO_FSEL_MASK (0x3 << SCR_RXFIFO_FSEL_OFFSET)
+#define SCR_RXFIFO_FSEL_IF0 (0x0 << SCR_RXFIFO_FSEL_OFFSET)
+#define SCR_RXFIFO_FSEL_IF4 (0x1 << SCR_RXFIFO_FSEL_OFFSET)
+#define SCR_RXFIFO_FSEL_IF8 (0x2 << SCR_RXFIFO_FSEL_OFFSET)
+#define SCR_RXFIFO_FSEL_IF12 (0x3 << SCR_RXFIFO_FSEL_OFFSET)
+#define SCR_RXFIFO_AUTOSYNC_OFFSET 18
+#define SCR_RXFIFO_AUTOSYNC_MASK (1 << SCR_RXFIFO_AUTOSYNC_OFFSET)
+#define SCR_RXFIFO_AUTOSYNC (1 << SCR_RXFIFO_AUTOSYNC_OFFSET)
+#define SCR_TXFIFO_AUTOSYNC_OFFSET 17
+#define SCR_TXFIFO_AUTOSYNC_MASK (1 << SCR_TXFIFO_AUTOSYNC_OFFSET)
+#define SCR_TXFIFO_AUTOSYNC (1 << SCR_TXFIFO_AUTOSYNC_OFFSET)
+#define SCR_TXFIFO_FSEL_OFFSET 15
+#define SCR_TXFIFO_FSEL_MASK (0x3 << SCR_TXFIFO_FSEL_OFFSET)
+#define SCR_TXFIFO_FSEL_IF0 (0x0 << SCR_TXFIFO_FSEL_OFFSET)
+#define SCR_TXFIFO_FSEL_IF4 (0x1 << SCR_TXFIFO_FSEL_OFFSET)
+#define SCR_TXFIFO_FSEL_IF8 (0x2 << SCR_TXFIFO_FSEL_OFFSET)
+#define SCR_TXFIFO_FSEL_IF12 (0x3 << SCR_TXFIFO_FSEL_OFFSET)
+#define SCR_LOW_POWER (1 << 13)
+#define SCR_SOFT_RESET (1 << 12)
+#define SCR_TXFIFO_CTRL_OFFSET 10
+#define SCR_TXFIFO_CTRL_MASK (0x3 << SCR_TXFIFO_CTRL_OFFSET)
+#define SCR_TXFIFO_CTRL_ZERO (0x0 << SCR_TXFIFO_CTRL_OFFSET)
+#define SCR_TXFIFO_CTRL_NORMAL (0x1 << SCR_TXFIFO_CTRL_OFFSET)
+#define SCR_TXFIFO_CTRL_ONESAMPLE (0x2 << SCR_TXFIFO_CTRL_OFFSET)
+#define SCR_DMA_RX_EN_OFFSET 9
+#define SCR_DMA_RX_EN_MASK (1 << SCR_DMA_RX_EN_OFFSET)
+#define SCR_DMA_RX_EN (1 << SCR_DMA_RX_EN_OFFSET)
+#define SCR_DMA_TX_EN_OFFSET 8
+#define SCR_DMA_TX_EN_MASK (1 << SCR_DMA_TX_EN_OFFSET)
+#define SCR_DMA_TX_EN (1 << SCR_DMA_TX_EN_OFFSET)
+#define SCR_VAL_OFFSET 5
+#define SCR_VAL_MASK (1 << SCR_VAL_OFFSET)
+#define SCR_VAL_CLEAR (1 << SCR_VAL_OFFSET)
+#define SCR_TXSEL_OFFSET 2
+#define SCR_TXSEL_MASK (0x7 << SCR_TXSEL_OFFSET)
+#define SCR_TXSEL_OFF (0 << SCR_TXSEL_OFFSET)
+#define SCR_TXSEL_RX (1 << SCR_TXSEL_OFFSET)
+#define SCR_TXSEL_NORMAL (0x5 << SCR_TXSEL_OFFSET)
+#define SCR_USRC_SEL_OFFSET 0x0
+#define SCR_USRC_SEL_MASK (0x3 << SCR_USRC_SEL_OFFSET)
+#define SCR_USRC_SEL_NONE (0x0 << SCR_USRC_SEL_OFFSET)
+#define SCR_USRC_SEL_RECV (0x1 << SCR_USRC_SEL_OFFSET)
+#define SCR_USRC_SEL_CHIP (0x3 << SCR_USRC_SEL_OFFSET)
+
+/* SPDIF CDText control */
+#define SRCD_CD_USER_OFFSET 1
+#define SRCD_CD_USER (1 << SRCD_CD_USER_OFFSET)
+
+/* SPDIF Phase Configuration register */
+#define SRPC_DPLL_LOCKED (1 << 6)
+#define SRPC_CLKSRC_SEL_OFFSET 7
+#define SRPC_CLKSRC_SEL_MASK (0xf << SRPC_CLKSRC_SEL_OFFSET)
+#define SRPC_CLKSRC_SEL_SET(x) ((x << SRPC_CLKSRC_SEL_OFFSET) & SRPC_CLKSRC_SEL_MASK)
+#define SRPC_CLKSRC_SEL_LOCKED_OFFSET1 5
+#define SRPC_CLKSRC_SEL_LOCKED_OFFSET2 2
+#define SRPC_GAINSEL_OFFSET 3
+#define SRPC_GAINSEL_MASK (0x7 << SRPC_GAINSEL_OFFSET)
+#define SRPC_GAINSEL_SET(x) ((x << SRPC_GAINSEL_OFFSET) & SRPC_GAINSEL_MASK)
+
+#define SRPC_CLKSRC_MAX 16
+
+enum spdif_gainsel {
+ GAINSEL_MULTI_24 = 0,
+ GAINSEL_MULTI_16,
+ GAINSEL_MULTI_12,
+ GAINSEL_MULTI_8,
+ GAINSEL_MULTI_6,
+ GAINSEL_MULTI_4,
+ GAINSEL_MULTI_3,
+};
+#define GAINSEL_MULTI_MAX (GAINSEL_MULTI_3 + 1)
+#define SPDIF_DEFAULT_GAINSEL GAINSEL_MULTI_8
+
+/* SPDIF interrupt mask define */
+#define INT_DPLL_LOCKED (1 << 20)
+#define INT_TXFIFO_UNOV (1 << 19)
+#define INT_TXFIFO_RESYNC (1 << 18)
+#define INT_CNEW (1 << 17)
+#define INT_VAL_NOGOOD (1 << 16)
+#define INT_SYM_ERR (1 << 15)
+#define INT_BIT_ERR (1 << 14)
+#define INT_URX_FUL (1 << 10)
+#define INT_URX_OV (1 << 9)
+#define INT_QRX_FUL (1 << 8)
+#define INT_QRX_OV (1 << 7)
+#define INT_UQ_SYNC (1 << 6)
+#define INT_UQ_ERR (1 << 5)
+#define INT_RXFIFO_UNOV (1 << 4)
+#define INT_RXFIFO_RESYNC (1 << 3)
+#define INT_LOSS_LOCK (1 << 2)
+#define INT_TX_EM (1 << 1)
+#define INT_RXFIFO_FUL (1 << 0)
+
+/* SPDIF Clock register */
+#define STC_SYSCLK_DIV_OFFSET 11
+#define STC_SYSCLK_DIV_MASK (0x1ff << STC_TXCLK_SRC_OFFSET)
+#define STC_SYSCLK_DIV(x) ((((x) - 1) << STC_TXCLK_DIV_OFFSET) & STC_SYSCLK_DIV_MASK)
+#define STC_TXCLK_SRC_OFFSET 8
+#define STC_TXCLK_SRC_MASK (0x7 << STC_TXCLK_SRC_OFFSET)
+#define STC_TXCLK_SRC_SET(x) ((x << STC_TXCLK_SRC_OFFSET) & STC_TXCLK_SRC_MASK)
+#define STC_TXCLK_ALL_EN_OFFSET 7
+#define STC_TXCLK_ALL_EN_MASK (1 << STC_TXCLK_ALL_EN_OFFSET)
+#define STC_TXCLK_ALL_EN (1 << STC_TXCLK_ALL_EN_OFFSET)
+#define STC_TXCLK_DIV_OFFSET 0
+#define STC_TXCLK_DIV_MASK (0x7ff << STC_TXCLK_DIV_OFFSET)
+#define STC_TXCLK_DIV(x) ((((x) - 1) << STC_TXCLK_DIV_OFFSET) & STC_TXCLK_DIV_MASK)
+#define STC_TXCLK_SRC_MAX 8
+
+/* SPDIF tx rate */
+enum spdif_txrate {
+ SPDIF_TXRATE_32000 = 0,
+ SPDIF_TXRATE_44100,
+ SPDIF_TXRATE_48000,
+};
+#define SPDIF_TXRATE_MAX (SPDIF_TXRATE_48000 + 1)
+
+
+#define SPDIF_CSTATUS_BYTE 6
+#define SPDIF_UBITS_SIZE 96
+#define SPDIF_QSUB_SIZE (SPDIF_UBITS_SIZE / 8)
+
+
+#define FSL_SPDIF_RATES_PLAYBACK (SNDRV_PCM_RATE_32000 | \
+ SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000)
+
+#define FSL_SPDIF_RATES_CAPTURE (SNDRV_PCM_RATE_16000 | \
+ SNDRV_PCM_RATE_32000 | \
+ SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000 | \
+ SNDRV_PCM_RATE_64000 | \
+ SNDRV_PCM_RATE_96000)
+
+#define FSL_SPDIF_FORMATS_PLAYBACK (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+#define FSL_SPDIF_FORMATS_CAPTURE (SNDRV_PCM_FMTBIT_S24_LE)
+
+#endif /* _FSL_SPDIF_DAI_H */
--
1.7.1
^ permalink raw reply related
* [PATCH v9 2/2] ASoC: fsl: Add S/PDIF machine driver
From: Nicolin Chen @ 2013-08-20 4:32 UTC (permalink / raw)
To: broonie, s.hauer
Cc: mark.rutland, devicetree, alsa-devel, lars, swarren, festevam,
timur, rob.herring, tomasz.figa, p.zabel, R65777, shawn.guo,
linuxppc-dev
In-Reply-To: <cover.1376972170.git.b42378@freescale.com>
This patch implements a device-tree-only machine driver for Freescale
i.MX series Soc. It works with spdif_transmitter/spdif_receiver and
fsl_spdif.c drivers.
Signed-off-by: Nicolin Chen <b42378@freescale.com>
---
.../devicetree/bindings/sound/imx-audio-spdif.txt | 29 +++++
sound/soc/fsl/Kconfig | 11 ++
sound/soc/fsl/Makefile | 2 +
sound/soc/fsl/imx-spdif.c | 134 ++++++++++++++++++++
4 files changed, 176 insertions(+), 0 deletions(-)
create mode 100644 Documentation/devicetree/bindings/sound/imx-audio-spdif.txt
create mode 100644 sound/soc/fsl/imx-spdif.c
diff --git a/Documentation/devicetree/bindings/sound/imx-audio-spdif.txt b/Documentation/devicetree/bindings/sound/imx-audio-spdif.txt
new file mode 100644
index 0000000..9a3fa26
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/imx-audio-spdif.txt
@@ -0,0 +1,29 @@
+Freescale i.MX audio complex with S/PDIF transceiver
+
+Required properties:
+
+ - compatible : "fsl,imx-audio-spdif"
+
+ - model : The user-visible name of this sound complex
+
+ - spdif-controller : The phandle of the i.MX S/PDIF controller
+
+
+Optional properties:
+
+ - spdif-transmitter : The phandle of the spdif-transmitter dummy codec
+
+ - spdif-receiver : The phandle of the spdif-receiver dummy codec
+
+* Note: At least one of these two properties should be set in the DT binding.
+
+
+Example:
+
+sound-spdif {
+ compatible = "fsl,imx-audio-spdif";
+ model = "imx-spdif";
+ spdif-controller = <&spdif>;
+ spdif-transmitter = <&spdif_tx_codec>;
+ spdif-receiver = <&spdif_rx_codec>;
+};
diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig
index cd088cc..a708380 100644
--- a/sound/soc/fsl/Kconfig
+++ b/sound/soc/fsl/Kconfig
@@ -193,6 +193,17 @@ config SND_SOC_IMX_SGTL5000
Say Y if you want to add support for SoC audio on an i.MX board with
a sgtl5000 codec.
+config SND_SOC_IMX_SPDIF
+ tristate "SoC Audio support for i.MX boards with S/PDIF"
+ select SND_SOC_IMX_PCM_DMA
+ select SND_SOC_FSL_SPDIF
+ select SND_SOC_FSL_UTILS
+ select SND_SOC_SPDIF
+ help
+ SoC Audio support for i.MX boards with S/PDIF
+ Say Y if you want to add support for SoC audio on an i.MX board with
+ a S/DPDIF.
+
config SND_SOC_IMX_MC13783
tristate "SoC Audio support for I.MX boards with mc13783"
depends on MFD_MC13783 && ARM
diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile
index 4b5970e..e2aaff7 100644
--- a/sound/soc/fsl/Makefile
+++ b/sound/soc/fsl/Makefile
@@ -45,6 +45,7 @@ snd-soc-mx27vis-aic32x4-objs := mx27vis-aic32x4.o
snd-soc-wm1133-ev1-objs := wm1133-ev1.o
snd-soc-imx-sgtl5000-objs := imx-sgtl5000.o
snd-soc-imx-wm8962-objs := imx-wm8962.o
+snd-soc-imx-spdif-objs :=imx-spdif.o
snd-soc-imx-mc13783-objs := imx-mc13783.o
obj-$(CONFIG_SND_SOC_EUKREA_TLV320) += snd-soc-eukrea-tlv320.o
@@ -53,4 +54,5 @@ obj-$(CONFIG_SND_SOC_MX27VIS_AIC32X4) += snd-soc-mx27vis-aic32x4.o
obj-$(CONFIG_SND_MXC_SOC_WM1133_EV1) += snd-soc-wm1133-ev1.o
obj-$(CONFIG_SND_SOC_IMX_SGTL5000) += snd-soc-imx-sgtl5000.o
obj-$(CONFIG_SND_SOC_IMX_WM8962) += snd-soc-imx-wm8962.o
+obj-$(CONFIG_SND_SOC_IMX_SPDIF) += snd-soc-imx-spdif.o
obj-$(CONFIG_SND_SOC_IMX_MC13783) += snd-soc-imx-mc13783.o
diff --git a/sound/soc/fsl/imx-spdif.c b/sound/soc/fsl/imx-spdif.c
new file mode 100644
index 0000000..893f3d1
--- /dev/null
+++ b/sound/soc/fsl/imx-spdif.c
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2013 Freescale Semiconductor, Inc.
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <sound/soc.h>
+
+struct imx_spdif_data {
+ struct snd_soc_dai_link dai[2];
+ struct snd_soc_card card;
+};
+
+static int imx_spdif_audio_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct device_node *spdif_np, *codec_tx_np, *codec_rx_np;
+ struct platform_device *spdif_pdev;
+ struct imx_spdif_data *data;
+ int ret = 0, num_links = 0;
+
+ spdif_np = of_parse_phandle(np, "spdif-controller", 0);
+ if (!spdif_np) {
+ dev_err(&pdev->dev, "failed to find spdif-controller\n");
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ spdif_pdev = of_find_device_by_node(spdif_np);
+ if (!spdif_pdev) {
+ dev_err(&pdev->dev, "failed to find S/PDIF device\n");
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+ if (!data) {
+ dev_err(&pdev->dev, "failed to allocate memory\n");
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ codec_tx_np = of_parse_phandle(np, "spdif-transmitter", 0);
+ if (codec_tx_np) {
+ data->dai[num_links].name = "S/PDIF TX";
+ data->dai[num_links].stream_name = "S/PDIF PCM Playback";
+ data->dai[num_links].codec_dai_name = "dit-hifi";
+ data->dai[num_links].codec_of_node = codec_tx_np;
+ data->dai[num_links].cpu_of_node = spdif_np;
+ data->dai[num_links].platform_of_node = spdif_np;
+ num_links++;
+ }
+
+ codec_rx_np = of_parse_phandle(np, "spdif-receiver", 0);
+ if (codec_rx_np) {
+ data->dai[num_links].name = "S/PDIF RX";
+ data->dai[num_links].stream_name = "S/PDIF PCM Capture";
+ data->dai[num_links].codec_dai_name = "dir-hifi";
+ data->dai[num_links].codec_of_node = codec_rx_np;
+ data->dai[num_links].cpu_of_node = spdif_np;
+ data->dai[num_links].platform_of_node = spdif_np;
+ num_links++;
+ }
+
+ if (!num_links) {
+ dev_err(&pdev->dev, "no enabled S/PDIF DAI link\n");
+ goto fail;
+ }
+
+ data->card.dev = &pdev->dev;
+ data->card.num_links = num_links;
+ data->card.dai_link = data->dai;
+
+ ret = snd_soc_of_parse_card_name(&data->card, "model");
+ if (ret)
+ goto fail;
+
+ ret = snd_soc_register_card(&data->card);
+ if (ret) {
+ dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret);
+ goto fail;
+ }
+
+ platform_set_drvdata(pdev, data);
+
+fail:
+ if (codec_tx_np)
+ of_node_put(codec_tx_np);
+ if (codec_rx_np)
+ of_node_put(codec_rx_np);
+ if (spdif_np)
+ of_node_put(spdif_np);
+
+ return ret;
+}
+
+static int imx_spdif_audio_remove(struct platform_device *pdev)
+{
+ struct imx_spdif_data *data = platform_get_drvdata(pdev);
+
+ snd_soc_unregister_card(&data->card);
+
+ return 0;
+}
+
+static const struct of_device_id imx_spdif_dt_ids[] = {
+ { .compatible = "fsl,imx-audio-spdif", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, imx_spdif_dt_ids);
+
+static struct platform_driver imx_spdif_driver = {
+ .driver = {
+ .name = "imx-spdif",
+ .owner = THIS_MODULE,
+ .of_match_table = imx_spdif_dt_ids,
+ },
+ .probe = imx_spdif_audio_probe,
+ .remove = imx_spdif_audio_remove,
+};
+
+module_platform_driver(imx_spdif_driver);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("Freescale i.MX S/PDIF machine driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:imx-spdif");
--
1.7.1
^ permalink raw reply related
* Re: BUG_ON and gcc don't mix
From: Benjamin Herrenschmidt @ 2013-08-20 4:24 UTC (permalink / raw)
To: Anton Blanchard; +Cc: linuxppc-dev, paulus, wschmidt, Alan Modra
In-Reply-To: <20130820123750.7353f2f1@kryten>
On Tue, 2013-08-20 at 12:37 +1000, Anton Blanchard wrote:
> 0: 00 00 23 a1 lhz r9,0(r3)
> 4: 01 00 60 38 li r3,1
> 8: 20 00 a9 0c twlgei r9,32
> c: 20 00 80 4e blr
>
> Nice! I remember chasing this down before and the issue is we need the
> address of the trap instruction for our bug exception table. Maybe
> we need a gcc builtin in which we can get a label on the trap
> instruction. Would that be possible?
I suppose we can always just do a label before builtin_trap and have
the BUG code search a few instructions :-)
Cheers,
Ben.
^ permalink raw reply
* Re: BUG_ON and gcc don't mix
From: Alan Modra @ 2013-08-20 4:32 UTC (permalink / raw)
To: Anton Blanchard; +Cc: linuxppc-dev, paulus, wschmidt
In-Reply-To: <20130820123750.7353f2f1@kryten>
On Tue, Aug 20, 2013 at 12:37:50PM +1000, Anton Blanchard wrote:
> address of the trap instruction for our bug exception table. Maybe
> we need a gcc builtin in which we can get a label on the trap
> instruction. Would that be possible?
Not your actual _EMIT_BUG_ENTRY, but something like this ought to work.
The only trick here is not putting anything after __builtin_trap()..
#define BUG_ON(x) do { \
if (x) { \
__asm__ __volatile__ ("\n1:" \
"\t.section __bug_table,\"a\"" \
"\n\t.long 1b" \
"\n\t.previous"); \
__builtin_trap(); \
} \
} while (0)
int foo(unsigned int *bar)
{
unsigned int holder_cpu;
holder_cpu = *bar & 0xffff;
BUG_ON(holder_cpu >= 32);
return 1;
}
--
Alan Modra
Australia Development Lab, IBM
--
Alan Modra
Australia Development Lab, IBM
^ permalink raw reply
* Re: BUG_ON and gcc don't mix
From: Alan Modra @ 2013-08-20 4:45 UTC (permalink / raw)
To: Anton Blanchard; +Cc: linuxppc-dev, paulus, wschmidt
In-Reply-To: <20130820043211.GC3430@bubble.grove.modra.org>
On Tue, Aug 20, 2013 at 02:02:11PM +0930, Alan Modra wrote:
> On Tue, Aug 20, 2013 at 12:37:50PM +1000, Anton Blanchard wrote:
> > address of the trap instruction for our bug exception table. Maybe
> > we need a gcc builtin in which we can get a label on the trap
> > instruction. Would that be possible?
>
> Not your actual _EMIT_BUG_ENTRY, but something like this ought to work.
> The only trick here is not putting anything after __builtin_trap()..
Doh! I guess the whole point was to get the condition folded into the
trap, which is foiled by emitting an asm between the condition and
buildin_trap().
--
Alan Modra
Australia Development Lab, IBM
^ permalink raw reply
* Re: [PATCH v7 1/2] ASoC: fsl: Add S/PDIF CPU DAI driver
From: Shawn Guo @ 2013-08-20 5:19 UTC (permalink / raw)
To: Mark Rutland
Cc: devicetree@vger.kernel.org, alsa-devel@alsa-project.org,
lars@metafoo.de, swarren@wwwdotorg.org, festevam@gmail.com,
s.hauer@pengutronix.de, Nicolin Chen, timur@tabi.org,
rob.herring@calxeda.com, tomasz.figa@gmail.com,
broonie@kernel.org, p.zabel@pengutronix.de, R65777@freescale.com,
linuxppc-dev@lists.ozlabs.org
In-Reply-To: <20130819095433.GG3719@e106331-lin.cambridge.arm.com>
On Mon, Aug 19, 2013 at 10:54:33AM +0100, Mark Rutland wrote:
> > I guess it's better to drop the 'imx6q-spdif' here?
>
> That depends:
>
> * If the two IP blocks are identical, only the "imx35-spdif" name is
> necessary, and we can forget about "fsl,imx6q-spdif".
>
> * If "fsl,imx6q-spdif" is a strict superset of "fsl,imx35-spdif", having
> both names documented and in a compatible list for a "fsl,imx6q-spdif"
> device makes sense.
Practically, I found it's very useful to have "fsl,<soc>-<ip>" in the
device compatible property in <soc>.dtsi, even when device driver does
not match it right now. For this example, I still prefer to have the
following line for spdif device in imx6q.dtsi.
compatible = "fsl,imx6q-spdif", "fsl,imx35-spdif";
The reason for that is we usually do not see all the differences of an
IP block from one SoC to another when we firstly define the bindings
for the device by looking at hardware reference manual. Some
programming model differences are only identified when we're actually
programming. That said, if some day we find there is difference between
imx6q-spdif and imx35-spdif to be handled when we add something new to
the driver, we only need to add "fsl,imx6q-spdif" as a new compatible
into device driver and bindings document. The existing device tree
would need no update to work with the new kernel driver.
Shawn
>
> * If "fsl,imx6q-spdif" is a variation of "fsl,imx35-spdif", and the
> "fsl,imx6q-spdif" cannot always be treated identically to a
> "fsl,imx35-spdif", then it makes sense to have separate compatible
> strings, with a device being listed as either "fsl,imx6q-spdif" or
> "fsl,imx35-spdif".
^ permalink raw reply
* [PATCH] powerpc: Never handle VSX alignment exceptions from kernel
From: Anton Blanchard @ 2013-08-20 6:05 UTC (permalink / raw)
To: benh, paulus, mikey, amodra; +Cc: linuxppc-dev
The VSX alignment handler needs to write out the existing VSX
state to memory before operating on it (flush_vsx_to_thread()).
If we take a VSX alignment exception in the kernel bad things
will happen. It looks like we could write the kernel state out
to the user process, or we could handle the kernel exception
using data from the user process (depending if MSR_VSX is set
or not).
Worse still, if the code to read or write the VSX state causes an
alignment exception, we will recurse forever. I ended up with
hundreds of megabytes of kernel stack to look through as a result.
Floating point and SPE code have similar issues but already include
a user check. Add the same check to emulate_vsx().
Signed-off-by: Anton Blanchard <anton@samba.org>
---
Index: b/arch/powerpc/kernel/align.c
===================================================================
--- a/arch/powerpc/kernel/align.c
+++ b/arch/powerpc/kernel/align.c
@@ -651,6 +651,10 @@ static int emulate_vsx(unsigned char __u
int sw = 0;
int i, j;
+ /* userland only */
+ if (unlikely(!user_mode(regs)))
+ return 0;
+
flush_vsx_to_thread(current);
if (reg < 32)
^ permalink raw reply
* linux-next: build failure after merge of the final tree
From: Stephen Rothwell @ 2013-08-20 7:20 UTC (permalink / raw)
To: Ben Myers, David Chinner, xfs
Cc: cbe-oss-dev, Arnd Bergmann, Dwight Engen, linux-kernel,
linux-next, Jeremy Kerr, linuxppc-dev, Gao feng
[-- Attachment #1: Type: text/plain, Size: 929 bytes --]
Hi all,
After merging the final tree, today's linux-next build (powerpc
allyesconfig) failed like this:
arch/powerpc/platforms/cell/spufs/inode.c: In function 'spufs_parse_options':
arch/powerpc/platforms/cell/spufs/inode.c:623:16: error: incompatible types when assigning to type 'kuid_t' from type 'int'
root->i_uid = option;
^
arch/powerpc/platforms/cell/spufs/inode.c:628:16: error: incompatible types when assigning to type 'kgid_t' from type 'int'
root->i_gid = option;
^
Caused by commit d6970d4b726c ("enable building user namespace with xfs")
from the xfs tree (that was fun to find :-)).
I have reverted that commit for today. It could probably be replaced
with a patch that just changed XFS_FS to SPU_FS in the UIDGID_CONVERTED
config dependency - or someone could fix up SPU_FS.
--
Cheers,
Stephen Rothwell sfr@canb.auug.org.au
[-- Attachment #2: Type: application/pgp-signature, Size: 836 bytes --]
^ permalink raw reply
* Re: [PATCH v7 0/3] DMA: Freescale: Add support for 8-channel DMA engine
From: Hongbo Zhang @ 2013-08-20 8:33 UTC (permalink / raw)
To: Vinod Koul; +Cc: devicetree, linux-kernel, djbw, scottwood, linuxppc-dev
In-Reply-To: <20130729105901.GI29095@intel.com>
On 07/29/2013 06:59 PM, Vinod Koul wrote:
> On Mon, Jul 29, 2013 at 06:49:01PM +0800, hongbo.zhang@freescale.com wrote:
>> From: Hongbo Zhang <hongbo.zhang@freescale.com>
>>
>> Hi Vinod, Dan, Scott and Leo, please have a look at these V7 patches.
> The dma relates changes look okay to me.
>
> I need someone to review and ACK the DT bindings.
>
> ~Vinod
Vinod,
Are you using this tree?
http://git.infradead.org/users/vkoul/slave-dma.git
Did you merge these patches?
Thanks.
>> Freescale QorIQ T4 and B4 introduce new 8-channel DMA engines, this patch set
>> adds support this DMA engine.
>>
>> V6->V7 changes:
>> - only remove unnecessary "CHIP-dma" explanations in [1/3]
>>
>> V5->V6 changes:
>> - minor updates of descriptions in binding document and Kconfig
>> - remove [4/4], that should be another patch in future
>>
>> V4->V5 changes:
>> - update description in the dt binding document, to make it more resonable
>> - add new patch [4/4] to eliminate a compiling warning which already exists
>> for a long time
>>
>> V3->V4 changes:
>> - introduce new patch [1/3] to revise the legacy dma binding document
>> - and then add new paragraph to describe new dt node binding in [2/3]
>> - rebase to latest kernel v3.11-rc1
>>
>> V2->V3 changes:
>> - edit Documentation/devicetree/bindings/powerpc/fsl/dma.txt
>> - edit text string in Kconfig and the driver files, using "elo series" to
>> mention all the current "elo*"
>>
>> V1->V2 changes:
>> - removed the codes handling the register dgsr1, since it isn't used currently
>> - renamed the DMA DT compatible to "fsl,elo3-dma"
>> - renamed the new dts files to "elo3-dma-<n>.dtsi"
>>
>> Hongbo Zhang (3):
>> DMA: Freescale: revise device tree binding document
>> DMA: Freescale: Add new 8-channel DMA engine device tree nodes
>> DMA: Freescale: update driver to support 8-channel DMA engine
>>
>> .../devicetree/bindings/powerpc/fsl/dma.txt | 114 +++++++++++++++-----
>> arch/powerpc/boot/dts/fsl/b4si-post.dtsi | 4 +-
>> arch/powerpc/boot/dts/fsl/elo3-dma-0.dtsi | 81 ++++++++++++++
>> arch/powerpc/boot/dts/fsl/elo3-dma-1.dtsi | 81 ++++++++++++++
>> arch/powerpc/boot/dts/fsl/t4240si-post.dtsi | 4 +-
>> drivers/dma/Kconfig | 9 +-
>> drivers/dma/fsldma.c | 9 +-
>> drivers/dma/fsldma.h | 2 +-
>> 8 files changed, 264 insertions(+), 40 deletions(-)
>> create mode 100644 arch/powerpc/boot/dts/fsl/elo3-dma-0.dtsi
>> create mode 100644 arch/powerpc/boot/dts/fsl/elo3-dma-1.dtsi
>>
>> --
>> 1.7.9.5
>>
>>
>>
^ permalink raw reply
* RE: BUG_ON and gcc don't mix
From: David Laight @ 2013-08-20 8:36 UTC (permalink / raw)
To: Alan Modra, Anton Blanchard; +Cc: paulus, linuxppc-dev, wschmidt
In-Reply-To: <20130820044534.GD3430@bubble.grove.modra.org>
> On Tue, Aug 20, 2013 at 02:02:11PM +0930, Alan Modra wrote:
> > On Tue, Aug 20, 2013 at 12:37:50PM +1000, Anton Blanchard wrote:
> > > address of the trap instruction for our bug exception table. Maybe
> > > we need a gcc builtin in which we can get a label on the trap
> > > instruction. Would that be possible?
> >
> > Not your actual _EMIT_BUG_ENTRY, but something like this ought to =
work.
> > The only trick here is not putting anything after __builtin_trap()..
>
> Doh! I guess the whole point was to get the condition folded into the
> trap, which is foiled by emitting an asm between the condition and
> buildin_trap().
I was thinking that you could add the label after the trap and
then use '.long 1b-4'. But you'd have to put the asm outside the
conditional - so that wouldn't work if the condition was more
complicated and the trap had to be out of line.
If the trap is out of line, then it could be a long way from
the surrounding code block - so a label 'nearby' in the C isn't
any use.
With fix sized instructions the .text segment of the object files
could be scanned for trap instructions.
David
^ permalink raw reply
* Re: [PATCH v7 1/2] ASoC: fsl: Add S/PDIF CPU DAI driver
From: Mark Rutland @ 2013-08-20 8:47 UTC (permalink / raw)
To: Nicolin Chen
Cc: devicetree@vger.kernel.org, alsa-devel@alsa-project.org,
lars@metafoo.de, swarren@wwwdotorg.org, festevam@gmail.com,
s.hauer@pengutronix.de, timur@tabi.org, rob.herring@calxeda.com,
tomasz.figa@gmail.com, broonie@kernel.org, p.zabel@pengutronix.de,
R65777@freescale.com, shawn.guo@linaro.org,
linuxppc-dev@lists.ozlabs.org
In-Reply-To: <20130819113407.GA11489@MrMyself>
On Mon, Aug 19, 2013 at 12:34:08PM +0100, Nicolin Chen wrote:
> On Mon, Aug 19, 2013 at 12:13:49PM +0100, Mark Rutland wrote:
> > > > > > > + "rxtx<0-7>" Clock source list for tx and rx clock.
> > > > > > > + This clock list should be identical to
> > > > > > > + the source list connecting to the spdif
> > > > > > > + clock mux in "SPDIF Transceiver Clock
> > > > > > > + Diagram" of SoC reference manual. It
> > > > > > > + can also be referred to TxClk_Source
> > > > > > > + bit of register SPDIF_STC.
> > > Actually there's a clock mux for TxClk and it's connecting with 8 clock
> > > sources. So the TxClk_Source bit show the connection between its value
> > > with the correspond source on the clock mux:
> > >
> > > TxClk_Source 000 XTAL clk input
> > > 001 CCM spdif0_clk_root input
> > > 010 asrc_clk input
> > > 011 spdif_extclk input, from pads
> > > 100 esai_hckt input
> > > 101 frequency divided ipg_clk input
> > > 110 mlb_clk input
> > > 111 mlb phy clk input
> >
> > Ah. So are these the actual input names on the mux, or the names of the
> > outputs that are wired up to the mux? If the former, these may be better
> > clock-names than rxtx<0-7>.
>
> The clock names are actually different with different SoC. The list above
> is only for i.MX6Q. So we can't specify these names here, because the driver
> then would need to maintain a clock names list for different SoC as well.
Ok. I was working on the assumption there were some logical input names
on the mux that weren't just the names of the actual clocks wired into
them.
>
> > Is there a similar Rx mux?
>
> Both Tx and RX clocks can be derived from the Tx mux.
ok.
>
> > Given the "rxtx<0-7>" names you've given these clocks, are there 8 clock
> > inputs that get duplicated within the spdif block and fed into both
> > muxes, or are there 16 external inputs that happen to be two groups of 8
> > identical sets of clocks in systems so far?
>
> I think you can refer to the RM, since you just got it. The diagram doesn't
> show which one you mentioned is true. But I think we can understand in both
> ways.
>
> I'm going to send a v8. So I think I don't need to modify the description
> right?
>From the sounds of it, no.
Thanks,
Mark.
^ permalink raw reply
* Re: [PATCH v7 1/2] ASoC: fsl: Add S/PDIF CPU DAI driver
From: Mark Rutland @ 2013-08-20 8:54 UTC (permalink / raw)
To: Shawn Guo
Cc: devicetree@vger.kernel.org, alsa-devel@alsa-project.org,
lars@metafoo.de, swarren@wwwdotorg.org, festevam@gmail.com,
s.hauer@pengutronix.de, Nicolin Chen, timur@tabi.org,
rob.herring@calxeda.com, tomasz.figa@gmail.com,
broonie@kernel.org, p.zabel@pengutronix.de, R65777@freescale.com,
linuxppc-dev@lists.ozlabs.org
In-Reply-To: <20130820051947.GB29114@S2101-09.ap.freescale.net>
On Tue, Aug 20, 2013 at 06:19:49AM +0100, Shawn Guo wrote:
> On Mon, Aug 19, 2013 at 10:54:33AM +0100, Mark Rutland wrote:
> > > I guess it's better to drop the 'imx6q-spdif' here?
> >
> > That depends:
> >
> > * If the two IP blocks are identical, only the "imx35-spdif" name is
> > necessary, and we can forget about "fsl,imx6q-spdif".
> >
> > * If "fsl,imx6q-spdif" is a strict superset of "fsl,imx35-spdif", having
> > both names documented and in a compatible list for a "fsl,imx6q-spdif"
> > device makes sense.
>
> Practically, I found it's very useful to have "fsl,<soc>-<ip>" in the
> device compatible property in <soc>.dtsi, even when device driver does
> not match it right now. For this example, I still prefer to have the
> following line for spdif device in imx6q.dtsi.
>
> compatible = "fsl,imx6q-spdif", "fsl,imx35-spdif";
This is a good thing, but I was not referring to the driver. Nicolin
presumably has enough information about the hardware to know if the new
block is fully compatible, even if the driver doesn't yet poke the
hardware in such a way as to make that obvious.
>
> The reason for that is we usually do not see all the differences of an
> IP block from one SoC to another when we firstly define the bindings
> for the device by looking at hardware reference manual. Some
> programming model differences are only identified when we're actually
> programming. That said, if some day we find there is difference between
> imx6q-spdif and imx35-spdif to be handled when we add something new to
> the driver, we only need to add "fsl,imx6q-spdif" as a new compatible
> into device driver and bindings document. The existing device tree
> would need no update to work with the new kernel driver.
That's a valid concern, but I think that bindings should be documented
from the start (even if unused by the driver), otherwise we're in an
equally bad position later if we discover a device isn't backwards
compatible as we have no guarantee the new (previously undocumented)
string was picked up in all dts.
Thanks,
Mark.
^ permalink raw reply
* Re: [PATCH v7 0/3] DMA: Freescale: Add support for 8-channel DMA engine
From: Vinod Koul @ 2013-08-20 8:15 UTC (permalink / raw)
To: Hongbo Zhang; +Cc: devicetree, linux-kernel, djbw, scottwood, linuxppc-dev
In-Reply-To: <521329EA.3050900@freescale.com>
On Tue, Aug 20, 2013 at 04:33:46PM +0800, Hongbo Zhang wrote:
> On 07/29/2013 06:59 PM, Vinod Koul wrote:
> >On Mon, Jul 29, 2013 at 06:49:01PM +0800, hongbo.zhang@freescale.com wrote:
> >>From: Hongbo Zhang <hongbo.zhang@freescale.com>
> >>
> >>Hi Vinod, Dan, Scott and Leo, please have a look at these V7 patches.
> >The dma relates changes look okay to me.
> >
> >I need someone to review and ACK the DT bindings.
> >
> >~Vinod
> Vinod,
> Are you using this tree?
> http://git.infradead.org/users/vkoul/slave-dma.git
Yes
> Did you merge these patches?
No
As I said I would like someone who know DT and dma binding to ack them. I see
devicetree ML has been cced, can Arnd or someone else review these...
~Vinod
^ permalink raw reply
* Re: Build regressions/improvements in v3.11-rc6
From: Geert Uytterhoeven @ 2013-08-20 9:24 UTC (permalink / raw)
To: Linux Kernel Development; +Cc: Linux/PPC Development
In-Reply-To: <alpine.DEB.2.02.1308201120400.26995@ayla.of.borg>
On Tue, 20 Aug 2013, Geert Uytterhoeven wrote:
> JFYI, when comparing v3.11-rc6 to v3.11-rc5[3], the summaries are:
> - build errors: +6/-9
+ arch/powerpc/kvm/book3s_xics.c: error: implicit declaration of function 'get_tb' [-Werror=implicit-function-declaration]: => 812:3
powerpc-randconfig
+ error: No rule to make target /etc/sound/dsp001.ld: => N/A
i386-randconfig
> [1] http://kisskb.ellerman.id.au/kisskb/head/6556/ (all 120 configs)
> [3] http://kisskb.ellerman.id.au/kisskb/head/6532/ (all 120 configs)
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org
In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds
^ permalink raw reply
* [PATCH v4 01/19] microblaze: remove undefined of_get_cpu_node declaration
From: Sudeep KarkadaNagesha @ 2013-08-20 9:30 UTC (permalink / raw)
To: linux-arm-kernel, linux-kernel, linux-pm, devicetree,
linuxppc-dev
Cc: Jonas Bonn, Michal Simek, Greg Kroah-Hartman,
Sudeep KarkadaNagesha, Viresh Kumar, Rob Herring,
Rafael J. Wysocki, Grant Likely
In-Reply-To: <1376991021-12160-1-git-send-email-Sudeep.KarkadaNagesha@arm.com>
From: Sudeep KarkadaNagesha <sudeep.karkadanagesha@arm.com>
This patch removes the declaration of the function 'of_get_cpu_node'
which is not defined for microblaze. This is in preparation to move
it's definition from PPC to DT common code.
Michal Simek says: "it was just there because Microblaze
was based on powerpc code"
Acked-by: Michal Simek <monstr@monstr.eu>
Signed-off-by: Sudeep KarkadaNagesha <sudeep.karkadanagesha@arm.com>
---
arch/microblaze/include/asm/prom.h | 3 ---
1 file changed, 3 deletions(-)
diff --git a/arch/microblaze/include/asm/prom.h b/arch/microblaze/include/a=
sm/prom.h
index 20c5e8e..9977816 100644
--- a/arch/microblaze/include/asm/prom.h
+++ b/arch/microblaze/include/asm/prom.h
@@ -50,9 +50,6 @@ void of_parse_dma_window(struct device_node *dn, const vo=
id *dma_window_prop,
=20
extern void kdump_move_device_tree(void);
=20
-/* CPU OF node matching */
-struct device_node *of_get_cpu_node(int cpu, unsigned int *thread);
-
#endif /* __ASSEMBLY__ */
#endif /* __KERNEL__ */
=20
--=20
1.8.1.2
^ permalink raw reply related
* [PATCH v4 00/19] DT/core: update cpu device of_node
From: Sudeep KarkadaNagesha @ 2013-08-20 9:30 UTC (permalink / raw)
To: linux-arm-kernel, linux-kernel, linux-pm, devicetree,
linuxppc-dev
Cc: Jonas Bonn, Michal Simek, Greg Kroah-Hartman,
Sudeep KarkadaNagesha, Viresh Kumar, Rob Herring,
Rafael J. Wysocki, Grant Likely
In-Reply-To: <1374492747-13879-1-git-send-email-Sudeep.KarkadaNagesha@arm.com>
From: Sudeep KarkadaNagesha <sudeep.karkadanagesha@arm.com>
As more and more information is getting added into the cpu node, the number
of drivers needing to parse the device tree for CPU nodes are increasing.
Most of the time, the information needed from the cpu node is preferred
in the logical CPU order. Hence many drivers first parse and search the
CPU node, match them to logical index if needed and then search for the
required property inside a particular cpu node. Some of them assume the
logical and physical CPU ordering to be same which is incorrect.
This patch series initialises the of_node in all the cpu devices when
registering the CPU device.
1. This avoids different drivers having to parse the cpu nodes to obtain
different attributes like operating points, latency,...etc.
2. This handles different physical and logical cpu ordering which is not
the case in current code.
3. Also all the cpu nodes will have their of_node initialised correctly.
Currently different drivers assign them partially and incorrectly.
4. Removes all the reduntant parsing in various drivers.
Changes v3->v4:
1. There was a conflict with the generic definition of of_get_cpu_node
with the one defined in PPC with the same name but different arguments.
This version refactored the PPC definition to support other architecture=
s
before moving the code to DT core. This part was separately posted and
reviewed[1].
2. Updated the users of of_get_cpu_node with second argument(i.e. thread id
mostly NULL as its currently used only in PPC).
Changes v2->v3:
1. Added new OF helper to get of_node from the cpu logical index.
With the use of this help, removed lots of duplicated code from
cpufreq drivers.
2. Fixed issue with property length calculation in of_get_cpu_node.
(previously had assumed of_get_property returns number of cells)
3. Changed return type of arch_match_cpu_phys_id to bool(as suggested by Ni=
co)
4. Re-ordered patch 2 and 3, and few typo fixes.
5. Rebased on v3.11-rc2(to avoid any conflicts with __cpuinit* deletion)
Changes v1->v2:
1. Moved most of arch_of_get_cpu_node to OF/DT core as of_get_cpu_node
adding a provision for architecture specific hooks for matching
logical and physical ids.
2. Extended removal of DT cpu node parsing to PPC cpufreq drivers
3. Added Acks from Viresh and Shawn
Regards,
Sudeep
v1: https://lkml.org/lkml/2013/7/15/128
v2: https://lkml.org/lkml/2013/7/17/341
v3: https://lkml.org/lkml/2013/7/22/219
[1] https://lkml.org/lkml/2013/8/15/319 (PPC refactoring)
Sudeep KarkadaNagesha (19):
microblaze: remove undefined of_get_cpu_node declaration
openrisc: remove undefined of_get_cpu_node declaration
powerpc: refactor of_get_cpu_node to support other architectures
of: move of_get_cpu_node implementation to DT core library
ARM: DT/kernel: define ARM specific arch_match_cpu_phys_id
driver/core: cpu: initialize of_node in cpu's device struture
of/device: add helper to get cpu device node from logical cpu index
ARM: topology: remove hwid/MPIDR dependency from cpu_capacity
ARM: mvebu: remove device tree parsing for cpu nodes
drivers/bus: arm-cci: avoid parsing DT for cpu device nodes
cpufreq: imx6q-cpufreq: remove device tree parsing for cpu nodes
cpufreq: cpufreq-cpu0: remove device tree parsing for cpu nodes
cpufreq: highbank-cpufreq: remove device tree parsing for cpu nodes
cpufreq: spear-cpufreq: remove device tree parsing for cpu nodes
cpufreq: kirkwood-cpufreq: remove device tree parsing for cpu nodes
cpufreq: arm_big_little: remove device tree parsing for cpu nodes
cpufreq: maple-cpufreq: remove device tree parsing for cpu nodes
cpufreq: pmac64-cpufreq: remove device tree parsing for cpu nodes
cpufreq: pmac32-cpufreq: remove device tree parsing for cpu nodes
arch/arm/kernel/devtree.c | 5 ++
arch/arm/kernel/topology.c | 61 ++++++++----------------
arch/arm/mach-imx/mach-imx6q.c | 3 +-
arch/arm/mach-mvebu/platsmp.c | 51 +++++++++-----------
arch/microblaze/include/asm/prom.h | 3 --
arch/openrisc/include/asm/prom.h | 3 --
arch/powerpc/include/asm/prom.h | 3 --
arch/powerpc/kernel/prom.c | 43 +----------------
drivers/base/cpu.c | 2 +
drivers/bus/arm-cci.c | 28 +++--------
drivers/cpufreq/arm_big_little_dt.c | 40 ++++++----------
drivers/cpufreq/cpufreq-cpu0.c | 23 ++-------
drivers/cpufreq/highbank-cpufreq.c | 18 +++----
drivers/cpufreq/imx6q-cpufreq.c | 4 +-
drivers/cpufreq/kirkwood-cpufreq.c | 8 ++--
drivers/cpufreq/maple-cpufreq.c | 23 ++-------
drivers/cpufreq/pmac32-cpufreq.c | 5 +-
drivers/cpufreq/pmac64-cpufreq.c | 47 +++++-------------
drivers/cpufreq/spear-cpufreq.c | 4 +-
drivers/of/base.c | 95 +++++++++++++++++++++++++++++++++=
++++
include/linux/cpu.h | 1 +
include/linux/of.h | 7 +++
include/linux/of_device.h | 15 ++++++
23 files changed, 226 insertions(+), 266 deletions(-)
--=20
1.8.1.2
^ permalink raw reply
* [PATCH v4 03/19] powerpc: refactor of_get_cpu_node to support other architectures
From: Sudeep KarkadaNagesha @ 2013-08-20 9:30 UTC (permalink / raw)
To: linux-arm-kernel, linux-kernel, linux-pm, devicetree,
linuxppc-dev
Cc: Jonas Bonn, Michal Simek, Greg Kroah-Hartman,
Sudeep KarkadaNagesha, Viresh Kumar, Rob Herring,
Rafael J. Wysocki, Grant Likely
In-Reply-To: <1376991021-12160-1-git-send-email-Sudeep.KarkadaNagesha@arm.com>
From: Sudeep KarkadaNagesha <sudeep.karkadanagesha@arm.com>
Currently different drivers requiring to access cpu device node are
parsing the device tree themselves. Since the ordering in the DT need
not match the logical cpu ordering, the parsing logic needs to consider
that. However, this has resulted in lots of code duplication and in some
cases even incorrect logic.
It's better to consolidate them by adding support for getting cpu
device node for a given logical cpu index in DT core library. However
logical to physical index mapping can be architecture specific.
PowerPC has it's own implementation to get the cpu node for a given
logical index.
This patch refactors the current implementation of of_get_cpu_node.
This in preparation to move the implementation to DT core library.
It separates out the logical to physical mapping so that a default
matching of the physical id to the logical cpu index can be added
when moved to common code. Architecture specific code can override it.
Cc: Rob Herring <rob.herring@calxeda.com>
Cc: Grant Likely <grant.likely@linaro.org>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: Sudeep KarkadaNagesha <sudeep.karkadanagesha@arm.com>
---
arch/powerpc/kernel/prom.c | 76 ++++++++++++++++++++++++++++--------------=
----
1 file changed, 47 insertions(+), 29 deletions(-)
diff --git a/arch/powerpc/kernel/prom.c b/arch/powerpc/kernel/prom.c
index eb23ac9..f7b8c0b 100644
--- a/arch/powerpc/kernel/prom.c
+++ b/arch/powerpc/kernel/prom.c
@@ -865,45 +865,63 @@ static int __init prom_reconfig_setup(void)
__initcall(prom_reconfig_setup);
#endif
=20
+bool arch_match_cpu_phys_id(int cpu, u64 phys_id)
+{
+=09return (int)phys_id =3D=3D get_hard_smp_processor_id(cpu);
+}
+
+static bool __of_find_n_match_cpu_property(struct device_node *cpun,
+=09=09=09const char *prop_name, int cpu, unsigned int *thread)
+{
+=09const __be32 *cell;
+=09int ac, prop_len, tid;
+=09u64 hwid;
+
+=09ac =3D of_n_addr_cells(cpun);
+=09cell =3D of_get_property(cpun, prop_name, &prop_len);
+=09if (!cell)
+=09=09return false;
+=09prop_len /=3D sizeof(*cell);
+=09for (tid =3D 0; tid < prop_len; tid++) {
+=09=09hwid =3D of_read_number(cell, ac);
+=09=09if (arch_match_cpu_phys_id(cpu, hwid)) {
+=09=09=09if (thread)
+=09=09=09=09*thread =3D tid;
+=09=09=09return true;
+=09=09}
+=09=09cell +=3D ac;
+=09}
+=09return false;
+}
+
/* Find the device node for a given logical cpu number, also returns the c=
pu
* local thread number (index in ibm,interrupt-server#s) if relevant and
* asked for (non NULL)
*/
struct device_node *of_get_cpu_node(int cpu, unsigned int *thread)
{
-=09int hardid;
-=09struct device_node *np;
+=09struct device_node *cpun, *cpus;
=20
-=09hardid =3D get_hard_smp_processor_id(cpu);
+=09cpus =3D of_find_node_by_path("/cpus");
+=09if (!cpus) {
+=09=09pr_warn("Missing cpus node, bailing out\n");
+=09=09return NULL;
+=09}
=20
-=09for_each_node_by_type(np, "cpu") {
-=09=09const u32 *intserv;
-=09=09unsigned int plen, t;
+=09for_each_child_of_node(cpus, cpun) {
+=09=09if (of_node_cmp(cpun->type, "cpu"))
+=09=09=09continue;
=20
-=09=09/* Check for ibm,ppc-interrupt-server#s. If it doesn't exist
-=09=09 * fallback to "reg" property and assume no threads
+=09=09/* Check for non-standard "ibm,ppc-interrupt-server#s" property
+=09=09 * for thread ids on PowerPC. If it doesn't exist fallback to
+=09=09 * standard "reg" property.
=09=09 */
-=09=09intserv =3D of_get_property(np, "ibm,ppc-interrupt-server#s",
-=09=09=09=09&plen);
-=09=09if (intserv =3D=3D NULL) {
-=09=09=09const u32 *reg =3D of_get_property(np, "reg", NULL);
-=09=09=09if (reg =3D=3D NULL)
-=09=09=09=09continue;
-=09=09=09if (*reg =3D=3D hardid) {
-=09=09=09=09if (thread)
-=09=09=09=09=09*thread =3D 0;
-=09=09=09=09return np;
-=09=09=09}
-=09=09} else {
-=09=09=09plen /=3D sizeof(u32);
-=09=09=09for (t =3D 0; t < plen; t++) {
-=09=09=09=09if (hardid =3D=3D intserv[t]) {
-=09=09=09=09=09if (thread)
-=09=09=09=09=09=09*thread =3D t;
-=09=09=09=09=09return np;
-=09=09=09=09}
-=09=09=09}
-=09=09}
+=09=09if (__of_find_n_match_cpu_property(cpun,
+=09=09=09=09"ibm,ppc-interrupt-server#s", cpu, thread))
+=09=09=09return cpun;
+
+=09=09if (__of_find_n_match_cpu_property(cpun, "reg", cpu, thread))
+=09=09=09return cpun;
=09}
=09return NULL;
}
--=20
1.8.1.2
^ permalink raw reply related
* [PATCH v4 02/19] openrisc: remove undefined of_get_cpu_node declaration
From: Sudeep KarkadaNagesha @ 2013-08-20 9:30 UTC (permalink / raw)
To: linux-arm-kernel, linux-kernel, linux-pm, devicetree,
linuxppc-dev
Cc: Jonas Bonn, Michal Simek, Greg Kroah-Hartman,
Sudeep KarkadaNagesha, Viresh Kumar, Rob Herring,
Rafael J. Wysocki, Grant Likely
In-Reply-To: <1376991021-12160-1-git-send-email-Sudeep.KarkadaNagesha@arm.com>
From: Sudeep KarkadaNagesha <sudeep.karkadanagesha@arm.com>
This patch removes the declaration of the function 'of_get_cpu_node'
which is not defined for openrisc. This is in preparation to move
it's definition from PPC to DT common code.
Again it could be there as it was originally copied from powerpc.
Cc: Jonas Bonn <jonas@southpole.se>
Signed-off-by: Sudeep KarkadaNagesha <sudeep.karkadanagesha@arm.com>
---
arch/openrisc/include/asm/prom.h | 3 ---
1 file changed, 3 deletions(-)
diff --git a/arch/openrisc/include/asm/prom.h b/arch/openrisc/include/asm/p=
rom.h
index bbb34e5..eb59bfe 100644
--- a/arch/openrisc/include/asm/prom.h
+++ b/arch/openrisc/include/asm/prom.h
@@ -44,9 +44,6 @@ void of_parse_dma_window(struct device_node *dn, const vo=
id *dma_window_prop,
=20
extern void kdump_move_device_tree(void);
=20
-/* CPU OF node matching */
-struct device_node *of_get_cpu_node(int cpu, unsigned int *thread);
-
/* Get the MAC address */
extern const void *of_get_mac_address(struct device_node *np);
=20
--=20
1.8.1.2
^ permalink raw reply related
* [PATCH v4 04/19] of: move of_get_cpu_node implementation to DT core library
From: Sudeep KarkadaNagesha @ 2013-08-20 9:30 UTC (permalink / raw)
To: linux-arm-kernel, linux-kernel, linux-pm, devicetree,
linuxppc-dev
Cc: Jonas Bonn, Michal Simek, Greg Kroah-Hartman,
Sudeep KarkadaNagesha, Viresh Kumar, Rob Herring,
Rafael J. Wysocki, Grant Likely
In-Reply-To: <1376991021-12160-1-git-send-email-Sudeep.KarkadaNagesha@arm.com>
From: Sudeep KarkadaNagesha <sudeep.karkadanagesha@arm.com>
This patch moves the generalized implementation of of_get_cpu_node from
PowerPC to DT core library, thereby adding support for retrieving cpu
node for a given logical cpu index on any architecture.
The CPU subsystem can now use this function to assign of_node in the
cpu device while registering CPUs.
It is recommended to use these helper function only in pre-SMP/early
initialisation stages to retrieve CPU device node pointers in logical
ordering. Once the cpu devices are registered, it can be retrieved easily
from cpu device of_node which avoids unnecessary parsing and matching.
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: Grant Likely <grant.likely@linaro.org>
Acked-by: Rob Herring <rob.herring@calxeda.com>
Signed-off-by: Sudeep KarkadaNagesha <sudeep.karkadanagesha@arm.com>
---
arch/powerpc/include/asm/prom.h | 3 --
arch/powerpc/kernel/prom.c | 57 -------------------------
drivers/of/base.c | 95 +++++++++++++++++++++++++++++++++++++=
++++
include/linux/cpu.h | 1 +
include/linux/of.h | 7 +++
5 files changed, 103 insertions(+), 60 deletions(-)
diff --git a/arch/powerpc/include/asm/prom.h b/arch/powerpc/include/asm/pro=
m.h
index bc2da15..ac204e0 100644
--- a/arch/powerpc/include/asm/prom.h
+++ b/arch/powerpc/include/asm/prom.h
@@ -43,9 +43,6 @@ void of_parse_dma_window(struct device_node *dn, const vo=
id *dma_window_prop,
=20
extern void kdump_move_device_tree(void);
=20
-/* CPU OF node matching */
-struct device_node *of_get_cpu_node(int cpu, unsigned int *thread);
-
/* cache lookup */
struct device_node *of_find_next_cache_node(struct device_node *np);
=20
diff --git a/arch/powerpc/kernel/prom.c b/arch/powerpc/kernel/prom.c
index f7b8c0b..1c14cd4 100644
--- a/arch/powerpc/kernel/prom.c
+++ b/arch/powerpc/kernel/prom.c
@@ -870,63 +870,6 @@ bool arch_match_cpu_phys_id(int cpu, u64 phys_id)
=09return (int)phys_id =3D=3D get_hard_smp_processor_id(cpu);
}
=20
-static bool __of_find_n_match_cpu_property(struct device_node *cpun,
-=09=09=09const char *prop_name, int cpu, unsigned int *thread)
-{
-=09const __be32 *cell;
-=09int ac, prop_len, tid;
-=09u64 hwid;
-
-=09ac =3D of_n_addr_cells(cpun);
-=09cell =3D of_get_property(cpun, prop_name, &prop_len);
-=09if (!cell)
-=09=09return false;
-=09prop_len /=3D sizeof(*cell);
-=09for (tid =3D 0; tid < prop_len; tid++) {
-=09=09hwid =3D of_read_number(cell, ac);
-=09=09if (arch_match_cpu_phys_id(cpu, hwid)) {
-=09=09=09if (thread)
-=09=09=09=09*thread =3D tid;
-=09=09=09return true;
-=09=09}
-=09=09cell +=3D ac;
-=09}
-=09return false;
-}
-
-/* Find the device node for a given logical cpu number, also returns the c=
pu
- * local thread number (index in ibm,interrupt-server#s) if relevant and
- * asked for (non NULL)
- */
-struct device_node *of_get_cpu_node(int cpu, unsigned int *thread)
-{
-=09struct device_node *cpun, *cpus;
-
-=09cpus =3D of_find_node_by_path("/cpus");
-=09if (!cpus) {
-=09=09pr_warn("Missing cpus node, bailing out\n");
-=09=09return NULL;
-=09}
-
-=09for_each_child_of_node(cpus, cpun) {
-=09=09if (of_node_cmp(cpun->type, "cpu"))
-=09=09=09continue;
-
-=09=09/* Check for non-standard "ibm,ppc-interrupt-server#s" property
-=09=09 * for thread ids on PowerPC. If it doesn't exist fallback to
-=09=09 * standard "reg" property.
-=09=09 */
-=09=09if (__of_find_n_match_cpu_property(cpun,
-=09=09=09=09"ibm,ppc-interrupt-server#s", cpu, thread))
-=09=09=09return cpun;
-
-=09=09if (__of_find_n_match_cpu_property(cpun, "reg", cpu, thread))
-=09=09=09return cpun;
-=09}
-=09return NULL;
-}
-EXPORT_SYMBOL(of_get_cpu_node);
-
#if defined(CONFIG_DEBUG_FS) && defined(DEBUG)
static struct debugfs_blob_wrapper flat_dt_blob;
=20
diff --git a/drivers/of/base.c b/drivers/of/base.c
index 5c54279..605afa9 100644
--- a/drivers/of/base.c
+++ b/drivers/of/base.c
@@ -18,6 +18,7 @@
* 2 of the License, or (at your option) any later version.
*/
#include <linux/ctype.h>
+#include <linux/cpu.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/spinlock.h>
@@ -230,6 +231,100 @@ const void *of_get_property(const struct device_node =
*np, const char *name,
}
EXPORT_SYMBOL(of_get_property);
=20
+/*
+ * arch_match_cpu_phys_id - Match the given logical CPU and physical id
+ *
+ * @cpu: logical cpu index of a core/thread
+ * @phys_id: physical identifier of a core/thread
+ *
+ * CPU logical to physical index mapping is architecture specific.
+ * However this __weak function provides a default match of physical
+ * id to logical cpu index. phys_id provided here is usually values read
+ * from the device tree which must match the hardware internal registers.
+ *
+ * Returns true if the physical identifier and the logical cpu index
+ * correspond to the same core/thread, false otherwise.
+ */
+bool __weak arch_match_cpu_phys_id(int cpu, u64 phys_id)
+{
+=09return (u32)phys_id =3D=3D cpu;
+}
+
+/**
+ * Checks if the given "prop_name" property holds the physical id of the
+ * core/thread corresponding to the logical cpu 'cpu'. If 'thread' is not
+ * NULL, local thread number within the core is returned in it.
+ */
+static bool __of_find_n_match_cpu_property(struct device_node *cpun,
+=09=09=09const char *prop_name, int cpu, unsigned int *thread)
+{
+=09const __be32 *cell;
+=09int ac, prop_len, tid;
+=09u64 hwid;
+
+=09ac =3D of_n_addr_cells(cpun);
+=09cell =3D of_get_property(cpun, prop_name, &prop_len);
+=09if (!cell)
+=09=09return false;
+=09prop_len /=3D sizeof(*cell);
+=09for (tid =3D 0; tid < prop_len; tid++) {
+=09=09hwid =3D of_read_number(cell, ac);
+=09=09if (arch_match_cpu_phys_id(cpu, hwid)) {
+=09=09=09if (thread)
+=09=09=09=09*thread =3D tid;
+=09=09=09return true;
+=09=09}
+=09=09cell +=3D ac;
+=09}
+=09return false;
+}
+
+/**
+ * of_get_cpu_node - Get device node associated with the given logical CPU
+ *
+ * @cpu: CPU number(logical index) for which device node is required
+ * @thread: if not NULL, local thread number within the physical core is
+ * returned
+ *
+ * The main purpose of this function is to retrieve the device node for th=
e
+ * given logical CPU index. It should be used to initialize the of_node in
+ * cpu device. Once of_node in cpu device is populated, all the further
+ * references can use that instead.
+ *
+ * CPU logical to physical index mapping is architecture specific and is b=
uilt
+ * before booting secondary cores. This function uses arch_match_cpu_phys_=
id
+ * which can be overridden by architecture specific implementation.
+ *
+ * Returns a node pointer for the logical cpu if found, else NULL.
+ */
+struct device_node *of_get_cpu_node(int cpu, unsigned int *thread)
+{
+=09struct device_node *cpun, *cpus;
+
+=09cpus =3D of_find_node_by_path("/cpus");
+=09if (!cpus) {
+=09=09pr_warn("Missing cpus node, bailing out\n");
+=09=09return NULL;
+=09}
+
+=09for_each_child_of_node(cpus, cpun) {
+=09=09if (of_node_cmp(cpun->type, "cpu"))
+=09=09=09continue;
+=09=09/* Check for non-standard "ibm,ppc-interrupt-server#s" property
+=09=09 * for thread ids on PowerPC. If it doesn't exist fallback to
+=09=09 * standard "reg" property.
+=09=09 */
+=09=09if (IS_ENABLED(CONFIG_PPC) &&
+=09=09=09__of_find_n_match_cpu_property(cpun,
+=09=09=09=09"ibm,ppc-interrupt-server#s", cpu, thread))
+=09=09=09return cpun;
+=09=09if (__of_find_n_match_cpu_property(cpun, "reg", cpu, thread))
+=09=09=09return cpun;
+=09}
+=09return NULL;
+}
+EXPORT_SYMBOL(of_get_cpu_node);
+
/** Checks if the given "compat" string matches one of the strings in
* the device's "compatible" property
*/
diff --git a/include/linux/cpu.h b/include/linux/cpu.h
index ab0eade..3dfed2b 100644
--- a/include/linux/cpu.h
+++ b/include/linux/cpu.h
@@ -28,6 +28,7 @@ struct cpu {
extern int register_cpu(struct cpu *cpu, int num);
extern struct device *get_cpu_device(unsigned cpu);
extern bool cpu_is_hotpluggable(unsigned cpu);
+extern bool arch_match_cpu_phys_id(int cpu, u64 phys_id);
=20
extern int cpu_add_dev_attr(struct device_attribute *attr);
extern void cpu_remove_dev_attr(struct device_attribute *attr);
diff --git a/include/linux/of.h b/include/linux/of.h
index 1fd08ca..c0bb2f1 100644
--- a/include/linux/of.h
+++ b/include/linux/of.h
@@ -266,6 +266,7 @@ extern int of_device_is_available(const struct device_n=
ode *device);
extern const void *of_get_property(const struct device_node *node,
=09=09=09=09const char *name,
=09=09=09=09int *lenp);
+extern struct device_node *of_get_cpu_node(int cpu, unsigned int *thread);
#define for_each_property_of_node(dn, pp) \
=09for (pp =3D dn->properties; pp !=3D NULL; pp =3D pp->next)
=20
@@ -459,6 +460,12 @@ static inline const void *of_get_property(const struct=
device_node *node,
=09return NULL;
}
=20
+static inline struct device_node *of_get_cpu_node(int cpu,
+=09=09=09=09=09unsigned int *thread)
+{
+=09return NULL;
+}
+
static inline int of_property_read_u64(const struct device_node *np,
=09=09=09=09 const char *propname, u64 *out_value)
{
--=20
1.8.1.2
^ permalink raw reply related
* [PATCH v4 05/19] ARM: DT/kernel: define ARM specific arch_match_cpu_phys_id
From: Sudeep KarkadaNagesha @ 2013-08-20 9:30 UTC (permalink / raw)
To: linux-arm-kernel, linux-kernel, linux-pm, devicetree,
linuxppc-dev
Cc: Jonas Bonn, Michal Simek, Lorenzo Pieralisi, Russell King,
Greg Kroah-Hartman, Sudeep KarkadaNagesha, Viresh Kumar,
Rob Herring, Rafael J. Wysocki, Grant Likely
In-Reply-To: <1376991021-12160-1-git-send-email-Sudeep.KarkadaNagesha@arm.com>
From: Sudeep KarkadaNagesha <sudeep.karkadanagesha@arm.com>
OF/DT core library now provides architecture specific hook to match the
logical cpu index with the corresponding physical identifier. Most of the
cpu DT node parsing and initialisation is contained in devtree.c. So it's
better to define ARM specific arch_match_cpu_phys_id there.
This mainly helps to avoid replication of the code doing CPU node parsing
and physical(MPIDR) to logical mapping.
Cc: Russell King <linux@arm.linux.org.uk>
Cc: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Acked-by: Rob Herring <rob.herring@calxeda.com>
Acked-by: Nicolas Pitre <nico@linaro.org>
Signed-off-by: Sudeep KarkadaNagesha <sudeep.karkadanagesha@arm.com>
---
arch/arm/kernel/devtree.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/arch/arm/kernel/devtree.c b/arch/arm/kernel/devtree.c
index 5859c8b..2ee8a17 100644
--- a/arch/arm/kernel/devtree.c
+++ b/arch/arm/kernel/devtree.c
@@ -169,6 +169,11 @@ void __init arm_dt_init_cpu_maps(void)
=09}
}
=20
+bool arch_match_cpu_phys_id(int cpu, u64 phys_id)
+{
+=09return (phys_id & MPIDR_HWID_BITMASK) =3D=3D cpu_logical_map(cpu);
+}
+
/**
* setup_machine_fdt - Machine setup when an dtb was passed to the kernel
* @dt_phys: physical address of dt blob
--=20
1.8.1.2
^ permalink raw reply related
* [PATCH v4 06/19] driver/core: cpu: initialize of_node in cpu's device struture
From: Sudeep KarkadaNagesha @ 2013-08-20 9:30 UTC (permalink / raw)
To: linux-arm-kernel, linux-kernel, linux-pm, devicetree,
linuxppc-dev
Cc: Jonas Bonn, Michal Simek, Greg Kroah-Hartman,
Sudeep KarkadaNagesha, Viresh Kumar, Rob Herring,
Rafael J. Wysocki, Grant Likely
In-Reply-To: <1376991021-12160-1-git-send-email-Sudeep.KarkadaNagesha@arm.com>
From: Sudeep KarkadaNagesha <sudeep.karkadanagesha@arm.com>
CPUs are also registered as devices but the of_node in these cpu
devices are not initialized. Currently different drivers requiring
to access cpu device node are parsing the nodes themselves and
initialising the of_node in cpu device.
The of_node in all the cpu devices needs to be initialized properly
and at one place. The best place to update this is CPU subsystem
driver when registering the cpu devices.
The OF/DT core library now provides of_get_cpu_node to retrieve a cpu
device node for a given logical index by abstracting the architecture
specific details.
This patch uses of_get_cpu_node to assign of_node when registering the
cpu devices.
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Acked-by: Rob Herring <rob.herring@calxeda.com>
Signed-off-by: Sudeep KarkadaNagesha <sudeep.karkadanagesha@arm.com>
---
drivers/base/cpu.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/drivers/base/cpu.c b/drivers/base/cpu.c
index 4c358bc..4cf0717 100644
--- a/drivers/base/cpu.c
+++ b/drivers/base/cpu.c
@@ -14,6 +14,7 @@
#include <linux/slab.h>
#include <linux/percpu.h>
#include <linux/acpi.h>
+#include <linux/of.h>
=20
#include "base.h"
=20
@@ -289,6 +290,7 @@ int register_cpu(struct cpu *cpu, int num)
=09cpu->dev.release =3D cpu_device_release;
=09cpu->dev.offline_disabled =3D !cpu->hotpluggable;
=09cpu->dev.offline =3D !cpu_online(num);
+=09cpu->dev.of_node =3D of_get_cpu_node(num, NULL);
#ifdef CONFIG_ARCH_HAS_CPU_AUTOPROBE
=09cpu->dev.bus->uevent =3D arch_cpu_uevent;
#endif
--=20
1.8.1.2
^ permalink raw reply related
* [PATCH v4 07/19] of/device: add helper to get cpu device node from logical cpu index
From: Sudeep KarkadaNagesha @ 2013-08-20 9:30 UTC (permalink / raw)
To: linux-arm-kernel, linux-kernel, linux-pm, devicetree,
linuxppc-dev
Cc: Jonas Bonn, Michal Simek, Greg Kroah-Hartman,
Sudeep KarkadaNagesha, Viresh Kumar, Rob Herring,
Rafael J. Wysocki, Grant Likely
In-Reply-To: <1376991021-12160-1-git-send-email-Sudeep.KarkadaNagesha@arm.com>
From: Sudeep KarkadaNagesha <sudeep.karkadanagesha@arm.com>
Multiple drivers need to get the cpu device node from the cpu logical
index and then access the of_node.
This patch adds helper function to fetch the device node directly.
Acked-by: Rob Herring <rob.herring@calxeda.com>
Signed-off-by: Sudeep KarkadaNagesha <sudeep.karkadanagesha@arm.com>
---
include/linux/of_device.h | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/include/linux/of_device.h b/include/linux/of_device.h
index 9d27475..82ce324 100644
--- a/include/linux/of_device.h
+++ b/include/linux/of_device.h
@@ -1,6 +1,7 @@
#ifndef _LINUX_OF_DEVICE_H
#define _LINUX_OF_DEVICE_H
=20
+#include <linux/cpu.h>
#include <linux/platform_device.h>
#include <linux/of_platform.h> /* temporary until merge */
=20
@@ -43,6 +44,15 @@ static inline void of_device_node_put(struct device *dev=
)
=09of_node_put(dev->of_node);
}
=20
+static inline struct device_node *of_cpu_device_node_get(int cpu)
+{
+=09struct device *cpu_dev;
+=09cpu_dev =3D get_cpu_device(cpu);
+=09if (!cpu_dev)
+=09=09return NULL;
+=09return of_node_get(cpu_dev->of_node);
+}
+
#else /* CONFIG_OF */
=20
static inline int of_driver_match_device(struct device *dev,
@@ -67,6 +77,11 @@ static inline const struct of_device_id *of_match_device=
(
{
=09return NULL;
}
+
+static inline struct device_node *of_cpu_device_node_get(int cpu)
+{
+=09return NULL;
+}
#endif /* CONFIG_OF */
=20
#endif /* _LINUX_OF_DEVICE_H */
--=20
1.8.1.2
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox